changeset 152:b185dd9217bb

2008-09-29 Omair Majid <omajid@redhat.com> * 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.
author Omair Majid <omajid@redhat.com>
date Mon, 29 Sep 2008 15:56:36 -0400
parents 350bc72ba495
children 5ab54b0ca4ea
files src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java unittests/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLineTest.java
diffstat 2 files changed, 120 insertions(+), 59 deletions(-) [+]
line wrap: on
line diff
--- 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));
--- 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();
 	}