How can I synchronise the playback of several ogg files in Java? - java

I have a song that has the different tracks in separate ogg files. I can play these ogg files together using Vorbis SPI to get the complete song. However I'm doing this by playing each ogg in a separate thread and I have no protection against the playback of each ogg file getting out of sync.
How would you go about ensuring they stay in sync. Ideally all tracks would pause if there was a delay reading any.
Once I've got this working I'll also be wanting to sync with graphics being displayed on screen.
I feel like what I need is some sort of time/location record so I know how far through each of the ogg playbacks have got to. I could then delay other threads until everything is back in sync.
I'm in early days so I'm happy to consider alternative playback libraries.
EDIT :
Following JimmyB's advice I'm now able to read into 4 byte buffers. I then add the buffers together using the following code :
for (int i = 0; i < byteLength; i++) {
data[0][i] = data[0][i];
for (int j = 1; j < numInputs; j++) {
data[0][i] += data[j][i];
}
}
This does work, but the output is distorted. I suspect clipping is occuring. How can I combine the sound bytes without this effect?
EDIT 2:
Dividing the byte by the number of inputs stops the clipping, but greatly reduces the quality of the playback.
EDIT 3:
I think I'm also losing sync between the tracks. My read method looks like :
// return true if there is more to come
private boolean fillBuffer(AudioInputStream audioInputStream, byte[] soundBytes) throws IOException {
byte empty = 0;
int offset = 0;
while (offset != soundBytes.length) {
int bytesRead;
try {
bytesRead = audioInputStream.read(soundBytes, offset, soundBytes.length - offset);
offset += bytesRead;
if (bytesRead == -1) {
Arrays.fill(soundBytes, offset, soundBytes.length, empty);
return false;
}
} catch (Exception e) {
e.printStackTrace();
LOGGER.info("Boom");
}
}
return true;
}
It seems like the relationship between bytes read and time may not be linear? Perhaps that's in my head though.

Related

Java Inflater will loop infinitely sometimes

In my application, I'm trying to compress/decompress byte array using java's Inflater/Deflater class.
Here's part of the code I used at first:
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
byte[] buffer = new byte[1024];
while (!inflater.finished()) {
int count = inflater.inflate(buffer);
outputStream.write(buffer, 0, count);
}
Then after I deployed the code it'll randomly (very rare) cause the whole application hang, and when I took a thread dump, I can identify that one thread hanging
at java.util.zip.Inflater.inflateBytes(Native Method)
at java.util.zip.Inflater.inflate(Inflater.java:259)
- locked java.util.zip.ZStreamRef#fc71443
at java.util.zip.Inflater.inflate(Inflater.java:280)
It doesn't happen very often. Then I googled everywhere and found out it could be some empty byte data passed in the inflater and finished() will never return true.
So I used a workaround, instead of using
while (!inflater.finished())
to determine if it's finished, I used
while (inflater.getRemaining() > 0)
But it happened again.
Now it makes me wonder what's the real reason that causes the issue. There shouldn't be any empty array passed in the inflater, even if it did, how come getRemaining() method did not break the while loop?
Can anybody help pls? It's really bugging me.
Confused by the same problem, I find this page.
This is my workaround for this, it may helps:
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
while (!inflater.finished()) {
int i = inflater.inflate(buffer);
if (i == 0) {
break;
}
byteArrayOutputStream.write(buffer, 0, i);
}
The javadoc of inflate:
Uncompresses bytes into specified buffer. Returns actual number of bytes uncompressed. A return value of 0 indicates that needsInput() or needsDictionary() should be called in order to determine if more input data or a preset dictionary is required. In the latter case, getAdler() can be used to get the Adler-32 value of the dictionary required.
So #Wildo Luo was certainly right to check for 0 being returned.
byte[] buffer = new byte[1024];
while (!inflater.finished()) {
int count = inflater.inflate(buffer);
if (count != 0 ) {
outputStream.write(buffer, 0, count);
} else {
if (inflater.needsInput()) { // Not everything read
inflater.setInput(...);
} else if (inflater.needsDictionary()) { // Dictionary to be loaded
inflater.setDictionary(...);
}
}
}
inflater.end();
I can only imagine that elsewhere the code is not entirely right, maybe on the compression size. Better first check the general code. There is the Inflater(boolean nowrap) requiring an extra byte, the end() call. Exception handling (try-finally). Etcetera.
For unkown data, unknown occurrences: using a try-catch, find compressed data to check whether it is a data based error, and for testing any solution.
Having the same problem...
What I'm sure about:
I'm having an infinite loop, assured with logs printed.
inflater.inflate returns 0, and the output buffer size is 0.
My loop is like this (Hive ORC code):
while (!(inflater.finished() || inflater.needsDictionary() ||
inflater.needsInput())) {
try {
int count = inflater.inflate(out.array(),
out.arrayOffset() + out.position(),
out.remaining());
out.position(count + out.position());
} catch (DataFormatException dfe) {
throw new IOException("Bad compression data", dfe);
}
}
After the out buffer is consumed and its remaining size is 0, the loop will infinitely run.
But I'm not sure about whether it's orc or zlib caused this. On orc side, it fills original data with the same compression buffer size then do the compression, so theoretically it's not possible I get an compressed chunk larger than the buffer size. Possibilities may be zlib or hardware.
That being said, break the loop when count == 0 is dangerous, since there may be still uncompressed data in the inflator.

How do you create a tone generator whose tones can be manipulated "live" or on the fly in java?

I would like to make a basic tone generator in java which can be manipulated in real time (just pitch to start off with).
I would like to start simple and then add on more complicated tone generation and effects to end up with some kind of basic synthesizer.
I found a helpful post on this site featuring some sample code in an applet Beeper.java.
It generated a tone and saved it to a clip. It then played that clip back on a loop when required. Relevant tone generation bit:
/** Generates a tone, and assigns it to the Clip. */
public void generateTone()
throws LineUnavailableException {
if ( clip!=null ) {
clip.stop();
clip.close();
} else {
clip = AudioSystem.getClip();
}
boolean addHarmonic = harmonic.isSelected();
int intSR = ((Integer)sampleRate.getSelectedItem()).intValue();
int intFPW = framesPerWavelength.getValue();
float sampleRate = (float)intSR;
// oddly, the sound does not loop well for less than
// around 5 or so, wavelengths
int wavelengths = 20;
byte[] buf = new byte[2*intFPW*wavelengths];
AudioFormat af = new AudioFormat(
sampleRate,
8, // sample size in bits
2, // channels
true, // signed
false // bigendian
);
int maxVol = 127;
for(int i=0; i<intFPW*wavelengths; i++){
double angle = ((float)(i*2)/((float)intFPW))*(Math.PI);
buf[i*2]=getByteValue(angle);
if(addHarmonic) {
buf[(i*2)+1]=getByteValue(2*angle);
} else {
buf[(i*2)+1] = buf[i*2];
}
}
try {
byte[] b = buf;
AudioInputStream ais = new AudioInputStream(
new ByteArrayInputStream(b),
af,
buf.length/2 );
clip.open( ais );
} catch(Exception e) {
e.printStackTrace();
}
}
Loop bit:
/** Loops the current Clip until a commence false is passed. */
public void loopSound(boolean commence) {
if ( commence ) {
clip.setFramePosition(0);
clip.loop( Clip.LOOP_CONTINUOUSLY );
} else {
clip.stop();
}
}
I tried to wrangle this so that I would create another clip in the background and quickly change one for the other when I wanted to change pitch but of course there is a noticeable click as one clip starts and the other stops.
So I'm guessing I need some kind of clever buffering to do this that matches the end of one wave seamlessly with another?
Or is it just not possible with a pre-generated clip? If so how should I go about this?
As a relevant aside, how do software synthesizers work? Do they generate all their sounds and effects continuously or do they pregenerate "clips" and loop like Beeper.java?
Thanks!
It sounds like you want to implement a numerically controlled oscillator (NCO), implemented with a phase accumulator, in Java.
Basically, you need to figure out the delta-phase for the frequency you want, and then keep adding this, modulo 2pi, to the accumulator. The value of the accumulator is used as the value to sin() to generate the sample value.
When you want to change frequency, you update the delta-phase. This ensures continuity in the samples (no abrupt breaks in the wave). I suspect this is what was causing your clicks. If you want better changes, then you need to gradually change the delta-phase over a bunch of samples.

How do I read x bytes from a stream?

I want to read exactly n bytes from a Socket at a time. How can I achieve that?
DataInputStream.readFully()
Of course it may block for an arbitrarily long time...
You can create a helper method to completely fill a buffer. Something like this:
public int fillBufferCompletely(InputStream is, byte[] bytes) throws IOException {
int size = bytes.length;
int offset = 0;
while (offset < size) {
int read = is.read(bytes, offset, size - offset);
if (read == -1) {
if ( offset == 0 ) {
return -1;
} else {
return offset;
}
} else {
offset += read;
}
}
return size;
}
Then you just need to pass in a buffer of size x.
Edit
Michael posted a link to a function which does essentially the same thing. The only difference with mine is that it does have the ability to return less than the buffer length, but only on the condition of an end-of-stream. DataInputStream.readFully would throw a runtime exception in this scenario.
So I'll leave my answer up in case an example of that behaviour is useful.
DataInputStream.readFully() throws an exception on EOF, as Mark Peters points out. But there are two other methods who don't: Commons IO's IOUtils.read() and Guavas ByteStreams.read(). These both try to read up to N bytes, stopping only at EOF, and return how many they actually read.
This is impossible. The underlying platforms cannot guarantee this, so neither can Java. You can attempt to read n bytes, but you always have to be prepared that you get less than what was requested.

How to measure internet bandwidth

I have a problem and can't find answers. I would like to measure internet bandwidth with java, but I donĀ“t know how.
It would be great to get some hints; I know that I have to open a socket and send it to a defined server, get it back and then use the time.
But how would I code this?
Well I'd implement this simply by downloading a fixed size file. Not tested, but something along these lines should work just fine
byte[] buffer = new byte[BUFFERSIZE];
Socket s = new Socket(urlOfKnownFile);
InputStream is = s.getInputStream();
long start = System.nanoTime();
while (is.read(buffer) != -1) continue;
long end = System.nanoTime();
long time = end-start;
// Now we know that it took about time ns to download <filesize>.
// If you don't know the correct filesize you can obviously use the total of all is.read() calls.
How about fixing an arbitrary amount of time and send the data respecting it?
For example, let's say i want my server to limit it's bandwidth usage to 100Bytes/s.
So i fix 1 second and send the data as long as it does not goes beyond 1 second and 100 Bytes.
Here's some pseudocode to show what I'm talking about:
timer_get (a);
sent_data = 0;
while (not_finished_sending_data)
{
timer_get (b);
if ((b - a) < 1 ) // 1 second
{
if (sent_data < 100) // 100 bytes
{
// We actually send here
sent_data += send();
}
}
else
{
timer_get (a);
sent_data = 0;
}
}

Microphone level in Java

I'm trying to access the level of the mic through Java.
I don't need to record anything, I just want to know a relative scale of sound level.
Is this possible in real-time?
If it's impossible, maybe this could work:
Start recording when the level is over a certain value, stop when the level drops under a certain level for a certain time
Recording bits of a quarter second and reading it's volume, and if it's under the threshold stop recording.
Thanks in advance
http://www.technogumbo.com/tutorials/Java-Microphone-Selection-And-Level-Monitoring/Java-Microphone-Selection-And-Level-Monitoring.php
Pretty good article on this. Helped me out a lot.
From what i can tell, this uses the root mean squared stuff talked about in #Nick's answer
Basically:
public int calculateRMSLevel(byte[] audioData)
{
long lSum = 0;
for(int i=0; i < audioData.length; i++)
lSum = lSum + audioData[i];
double dAvg = lSum / audioData.length;
double sumMeanSquare = 0d;
for(int j=0; j < audioData.length; j++)
sumMeanSquare += Math.pow(audioData[j] - dAvg, 2d);
double averageMeanSquare = sumMeanSquare / audioData.length;
return (int)(Math.pow(averageMeanSquare,0.5d) + 0.5);
}
and the usage:
int level = 0;
byte tempBuffer[] = new byte[6000];
stopCapture = false;
try {
while (!stopCapture) {
if (targetRecordLine.read(tempBuffer, 0, tempBuffer.length) > 0) {
level = calculateRMSLevel(tempBuffer);
}
}
targetRecordLine.close();
} catch (Exception e) {
System.out.println(e);
System.exit(0);
}
You can access microphones through the Sound API, but it won't give you a simple loudness level. You'll just have to capture the data and make your own decision about how loud it is.
http://download.oracle.com/javase/tutorial/sound/capturing.html
Recording implies saving the data, but here you can discard the data once you've finished determining its loudness.
The root mean squared method is a good way of calculating the amplitude of a section of wave data.
In answer to your comment, yes, you'd capture a short length of data (maybe just a few milliseconds worth) and calculate the amplitude of that. Repeat this periodically depending on how often you need updates. If you want to keep track of previous loudnesses and compare them, that's up to you - at this point it's just comparing numbers. You might use the average of recent loudnesses to calculate the ambient loudness of the room, so you can detect sudden increases in noise.
I don't know how much overhead there is in turning audio capture on and off, but you may be better off keeping the TargetDataLine open all the time, and just calculating the loudness when you need it. While the line is open you do need to keep calling read() on it though, otherwise the application will hang waiting for you to read data.

Categories