# HG changeset patch # User Omair Majid # Date 1222718196 14400 # Node ID b185dd9217bb275c80a252b647dbaf76249f6ae3 # Parent 350bc72ba495ad86e0c076c56f5b1439b5317df9 2008-09-29 Omair Majid * src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java New class variables flushed and drained. (close): Now synchronized. (open): Likewise. (open): Likewise. (read): Likewise. Return when flush()ed, drain()ed, stop()ed or close()d. (drain): Synchronized. Sets drained to true. (flush): Sets flushed to true. (start): Synchronized. (stop): Synchronized. * unittests/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLineTest.java (testReadAndDrain): Fixed test and made it active. diff -r 350bc72ba495 -r b185dd9217bb src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java --- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java Mon Sep 29 15:16:15 2008 -0400 +++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java Mon Sep 29 15:56:36 2008 -0400 @@ -52,6 +52,13 @@ */ byte[] fragmentBuffer; + /* + * these are set to true only by the respective functions (flush(), drain()) + * set to false only by read() + */ + boolean flushed = false; + boolean drained = false; + public PulseAudioTargetDataLine(EventLoop eventLoop, AudioFormat[] formats, AudioFormat defaultFormat) { supportedFormats = formats; @@ -62,7 +69,7 @@ } @Override - public void close() { + synchronized public void close() { if (!isOpen) { throw new IllegalStateException( "Line cant be closed if it isnt open"); @@ -75,7 +82,7 @@ } @Override - public void open(AudioFormat format, int bufferSize) + synchronized public void open(AudioFormat format, int bufferSize) throws LineUnavailableException { if (isOpen) { throw new IllegalStateException("already open"); @@ -90,7 +97,8 @@ } @Override - public void open(AudioFormat format) throws LineUnavailableException { + synchronized public void open(AudioFormat format) + throws LineUnavailableException { open(format, DEFAULT_BUFFER_SIZE); } @@ -140,72 +148,98 @@ /* on first read() of the line, fragmentBuffer is null */ if (fragmentBuffer != null) { + synchronized (this) { - boolean fragmentBufferSmaller = fragmentBuffer.length < length; - int smallerBufferLength = Math.min(fragmentBuffer.length, length); - System.arraycopy(fragmentBuffer, 0, data, position, - smallerBufferLength); + boolean fragmentBufferSmaller = fragmentBuffer.length < length; + int smallerBufferLength = Math.min(fragmentBuffer.length, + length); + System.arraycopy(fragmentBuffer, 0, data, position, + smallerBufferLength); + framesSinceOpen += smallerBufferLength + / currentFormat.getFrameSize(); - if (!fragmentBufferSmaller) { - /* - * if fragment was larger, then we already have all the data we - * need. clean up the buffer before returning. Make a new - * fragmentBuffer from the remaining bytes - */ - int remainingBytesInFragment = (fragmentBuffer.length - length); - byte[] newFragmentBuffer = new byte[remainingBytesInFragment]; - System.arraycopy(fragmentBuffer, length, newFragmentBuffer, 0, - newFragmentBuffer.length); - fragmentBuffer = newFragmentBuffer; - return length; + if (!fragmentBufferSmaller) { + /* + * if fragment was larger, then we already have all the data + * we need. clean up the buffer before returning. Make a new + * fragmentBuffer from the remaining bytes + */ + int remainingBytesInFragment = (fragmentBuffer.length - length); + byte[] newFragmentBuffer = new byte[remainingBytesInFragment]; + System.arraycopy(fragmentBuffer, length, newFragmentBuffer, + 0, newFragmentBuffer.length); + fragmentBuffer = newFragmentBuffer; + return length; + } + + /* done with fragment buffer, remove it */ + bytesRead = smallerBufferLength; + sizeRead += bytesRead; + position += bytesRead; + remainingLength -= bytesRead; + fragmentBuffer = null; + } - - /* done with fragment buffer, remove it */ - bytesRead = smallerBufferLength; - sizeRead += bytesRead; - position += bytesRead; - remainingLength -= bytesRead; - fragmentBuffer = null; } /* * if we need to read more data, then we read from PulseAudio's buffer */ while (remainingLength != 0) { - byte[] currentFragment; - synchronized (eventLoop.threadLock) { + synchronized (this) { + + if (!isOpen || !isStarted) { + return sizeRead; + } - /* read a fragment, and drop it from the server */ - currentFragment = stream.peek(); - stream.drop(); - if (currentFragment == null) { - System.out - .println("DEBUG: PulseAudioTargetDataLine:read(): error in stream.peek()"); + if (flushed) { + flushed = false; + return sizeRead; + } + + if (drained) { + drained = false; return sizeRead; } - bytesRead = Math.min(currentFragment.length, remainingLength); + byte[] currentFragment; + synchronized (eventLoop.threadLock) { + + /* read a fragment, and drop it from the server */ + currentFragment = stream.peek(); + stream.drop(); + if (currentFragment == null) { + System.out + .println("DEBUG: PulseAudioTargetDataLine:read(): error in stream.peek()"); + return sizeRead; + } + + bytesRead = Math.min(currentFragment.length, + remainingLength); - /* - * we read more than we required, save the rest of the data in - * the fragmentBuffer - */ - if (bytesRead < currentFragment.length) { - /* allocate a buffer to store unsaved data */ - fragmentBuffer = new byte[currentFragment.length - - bytesRead]; - /* copy over the unsaved data */ - System.arraycopy(currentFragment, bytesRead, - fragmentBuffer, 0, fragmentBuffer.length - - bytesRead); + /* + * we read more than we required, save the rest of the data + * in the fragmentBuffer + */ + if (bytesRead < currentFragment.length) { + /* allocate a buffer to store unsaved data */ + fragmentBuffer = new byte[currentFragment.length + - bytesRead]; + + /* copy over the unsaved data */ + System.arraycopy(currentFragment, bytesRead, + fragmentBuffer, 0, currentFragment.length + - bytesRead); + } + + System.arraycopy(currentFragment, 0, data, position, + bytesRead); + + sizeRead += bytesRead; + position += bytesRead; + remainingLength -= bytesRead; + framesSinceOpen += bytesRead / currentFormat.getFrameSize(); } - - System.arraycopy(currentFragment, 0, data, position, bytesRead); - - sizeRead += bytesRead; - position += bytesRead; - remainingLength -= bytesRead; - framesSinceOpen += bytesRead / currentFormat.getFrameSize(); } } @@ -227,9 +261,18 @@ throw new IllegalStateException("must call open() before drain()"); } + synchronized (this) { + drained = true; + } + // blocks when there is data on the line // http://www.jsresources.org/faq_audio.html#stop_drain_tdl - while (isStarted) { + while (true) { + synchronized (this) { + if (!isStarted || !isOpen) { + break; + } + } try { Thread.sleep(100); } catch (InterruptedException e) { @@ -251,6 +294,10 @@ operation.waitForCompletion(); operation.releaseReference(); + synchronized (this) { + flushed = true; + } + } public int available() { @@ -282,14 +329,14 @@ */ @Override - public void start() { + synchronized public void start() { super.start(); fireLineEvent(new LineEvent(this, LineEvent.Type.START, framesSinceOpen)); } @Override - public void stop() { + synchronized public void stop() { super.stop(); fireLineEvent(new LineEvent(this, LineEvent.Type.STOP, framesSinceOpen)); diff -r 350bc72ba495 -r b185dd9217bb unittests/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLineTest.java --- a/unittests/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLineTest.java Mon Sep 29 15:16:15 2008 -0400 +++ b/unittests/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLineTest.java Mon Sep 29 15:56:36 2008 -0400 @@ -299,7 +299,6 @@ // this is kind of messed up // drain should hang on a started data line // but read should return - @Ignore @Test public void testReadAndDrain() throws LineUnavailableException, InterruptedException { @@ -320,12 +319,27 @@ Assert.assertTrue(reader.isAlive()); - targetDataLine.drain(); + Thread drainer = new Thread() { + + @Override + public void run() { + targetDataLine.drain(); + + } + + }; + + drainer.start(); Thread.sleep(100); Assert.assertFalse(reader.isAlive()); + targetDataLine.stop(); + + Thread.sleep(100); + Assert.assertFalse(drainer.isAlive()); + targetDataLine.close(); }