changeset 135:c46f6e0e7959

2008-09-23 Omair Majid <omajid@redhat.com> * src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java Removed clipThreadStarted. Use clipThread.isAlive() etc. Removed unused clipSemaphore (ClipThread.run): Fixed to run without clipSemaphore. (close): Reset currentFrame and framesSinceOpen on close. (getFramePosition): Synchronized on framesSinceOpen. (loop): Replaced use of clipThreadStarted with clipThread.isAlive. (open): Initialize currentFrame, framesSinceOpen and endFrame. (stop): Remember which frame we stopped at. * src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java (open): Clean up properly on error. * src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java (write): On interrupt, return asap and mark the thread as being interrupted. * unittests/org/classpath/icedtea/pulseaudio/PulseAudioClipTest.java (testFramePosition): Renamed to testFramePositionBeforeClose. (testFramePositionWithStartStop): New function. Checks that starting and stopping doesnt change the frame counts. (testFramePositionWithLoop): Checks that looping doesnt interfere with the frame position. * unittests/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLineRawTest.java (ThreadWriter.run): Removed useless TODO comment. * unittests/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLineTest.java (ThreadWriter.run): Removed useless TODO. (setUp): Initialize listeners.
author Omair Majid <omajid@redhat.com>
date Tue, 23 Sep 2008 15:02:55 -0400
parents 381f212496eb
children 2d384ad19c3e 09bfaae2203b
files src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java unittests/org/classpath/icedtea/pulseaudio/PulseAudioClipTest.java unittests/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLineRawTest.java unittests/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLineTest.java
diffstat 6 files changed, 164 insertions(+), 44 deletions(-) [+]
line wrap: on
line diff
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java	Tue Sep 23 11:38:18 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java	Tue Sep 23 15:02:55 2008 -0400
@@ -38,7 +38,6 @@
 package org.classpath.icedtea.pulseaudio;
 
 import java.io.IOException;
-import java.util.concurrent.Semaphore;
 
 import javax.sound.sampled.AudioFormat;
 import javax.sound.sampled.AudioInputStream;
@@ -57,53 +56,57 @@
 	private float volume;
 
 	// these are frame indices. so counted from 0
+	// the current frame index
 	private int currentFrame = 0;
+
+	// total number of frames in this clip
 	private int frameCount = 0;
+
+	// the starting frame of the loop
 	private int startFrame = 0;
+	// the ending frame of the loop
 	private int endFrame = 0;
+
+	// the total number of frames played since this line was opened
 	private int framesSinceOpen = 0;
 
 	public static final String DEFAULT_CLIP_NAME = "Clip";
 
 	private Object clipLock = new Object();
-	private boolean clipThreadStarted;
 	private int loopsLeft;
-	private Semaphore clipSemaphore = new Semaphore(1);
+
+	// private Semaphore clipSemaphore = new Semaphore(1);
 
 	private class ClipThread extends Thread {
 		@Override
 		public void run() {
 
-			clipThreadStarted = true;
 			while (loopsLeft >= 0) {
 				writeFrames(currentFrame, endFrame + 1);
 				if (Thread.interrupted()) {
 					// Thread.currentThread().interrupt();
-					clipThreadStarted = false;
+					// System.out.println("returned from interrupted
+					// writeFrames");
 					break;
 				}
 
-				try {
-					// if loop(0) has been called from the mainThread,
-					// wait until loopsLeft has been set
-					clipSemaphore.acquire();
-					if (loopsLeft == 0) {
-						System.out.println("Reading to the end of the file");
-						writeFrames(endFrame, getFrameLength());
-						break;
-					} else {
-						synchronized (clipLock) {
-							currentFrame = startFrame;
-							loopsLeft--;
-						}
+				// if loop(0) has been called from the mainThread,
+				// wait until loopsLeft has been set
+				if (loopsLeft == 0) {
+					// System.out.println("Reading to the end of the file");
+					// System.out.println("endFrame: " + endFrame);
+					writeFrames(endFrame, getFrameLength());
+					break;
+				} else {
+					synchronized (clipLock) {
+						currentFrame = startFrame;
+						loopsLeft--;
 					}
-					clipSemaphore.release();
-				} catch (InterruptedException e) {
-					break;
 				}
 
 			}
 
+			// drain
 			Operation operation;
 
 			synchronized (eventLoop.threadLock) {
@@ -145,6 +148,9 @@
 						try {
 							eventLoop.threadLock.wait();
 						} catch (InterruptedException e) {
+							// System.out
+							// .println("interrupted while waiting for
+							// getWritableSize");
 							// clean up and return
 							Thread.currentThread().interrupt();
 							stream.removeWriteListener(writeListener);
@@ -160,16 +166,18 @@
 						framesToWrite * getFormat().getFrameSize());
 				remainingFrames -= framesToWrite;
 				currentFrame += framesToWrite;
+				framesSinceOpen += framesToWrite;
 				if (Thread.interrupted()) {
 					Thread.currentThread().interrupt();
 					break;
 				}
 				// System.out.println("remaining frames" + remainingFrames);
+				// System.out.println("currentFrame: " + currentFrame);
+				// System.out.println("framesSinceOpen: " + framesSinceOpen);
 			}
 		}
 
 		stream.removeWriteListener(writeListener);
-
 	}
 
 	public PulseAudioClip(EventLoop eventLoop, AudioFormat[] formats,
@@ -210,6 +218,9 @@
 			e.printStackTrace();
 		}
 
+		currentFrame = 0;
+		framesSinceOpen = 0;
+
 		PulseAudioMixer mixer = PulseAudioMixer.getInstance();
 		mixer.removeSourceLine(this);
 
@@ -235,8 +246,7 @@
 			try {
 				clipThread.join();
 			} catch (InterruptedException e) {
-				// TODO Auto-generated catch block
-				e.printStackTrace();
+				// ignore
 			}
 		}
 
@@ -280,8 +290,9 @@
 		if (!isOpen) {
 			throw new IllegalStateException("Line not open");
 		}
-
-		return (int) framesSinceOpen;
+		synchronized (clipLock) {
+			return (int) framesSinceOpen;
+		}
 	}
 
 	@Override
@@ -324,7 +335,7 @@
 
 		System.out.println("Loop " + count + " called");
 
-		if (clipThreadStarted && count != 0) {
+		if (clipThread.isAlive() && count != 0) {
 			// Do nothing; behavior not specified by the Java API
 			return;
 		}
@@ -358,7 +369,9 @@
 		this.data = new byte[bufferSize];
 		System.arraycopy(data, offset, this.data, 0, bufferSize);
 		frameCount = bufferSize / format.getFrameSize();
-
+		currentFrame = 0;
+		framesSinceOpen = 0;
+		endFrame = frameCount - 1;
 		PulseAudioVolumeControl volumeControl = new PulseAudioVolumeControl(
 				this, eventLoop);
 		PulseAudioMuteControl muteControl = new PulseAudioMuteControl(this,
@@ -373,6 +386,20 @@
 
 	}
 
+	// FIXME This is not exposed by the Clip interface, but becomes available if
+	// using PulseAudioClip
+	// @Override
+	// public void open(AudioFormat format, int bufferSize)
+	// throws LineUnavailableException {
+	// throw new IllegalArgumentException("open(AudioFormat, int) on a Clip is
+	// not allowed");
+	// }
+
+	@Override
+	// public void open(AudioFormat format) throws LineUnavailableException {
+	// throw new IllegalArgumentException("open(AudioFormat) on a Clip is not
+	// allowed");
+	// }
 	public byte[] native_setVolume(float value) {
 		return stream.native_setVolume(value);
 	}
@@ -428,7 +455,7 @@
 		}
 
 		if (end == -1) {
-			end = frameCount;
+			end = frameCount - 1;
 		}
 
 		if (end < start) {
@@ -496,7 +523,6 @@
 			e.printStackTrace();
 		}
 		synchronized (clipLock) {
-			currentFrame = 0;
 			loopsLeft = 0;
 		}
 
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java	Tue Sep 23 11:38:18 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java	Tue Sep 23 15:02:55 2008 -0400
@@ -160,7 +160,11 @@
 
 		} catch (LineUnavailableException e) {
 			// error connecting to the server!
-			// FIXME clean up
+			stream.removePlaybackStartedListener(startedListener);
+			stream.removeUnderflowListener(stoppedListener);
+			stream.removeStateListener(openCloseListener);
+			stream.free();
+			stream = null;
 			throw e;
 		}
 		this.bufferSize = bufferSize;
@@ -169,6 +173,7 @@
 			synchronized (eventLoop.threadLock) {
 				if (stream.getState() != Stream.State.READY) {
 					stream.disconnect();
+					stream.free();
 					throw new LineUnavailableException(
 							"unable to obtain a line");
 				}
@@ -230,9 +235,9 @@
 		isStarted = false;
 	}
 
-	// A BIG FIXME !
-
 	/*
+	 * TODO
+	 * 
 	 * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4791152 :
 	 * 
 	 * a line is active in between calls to start() and stop(). In that sense,
@@ -243,6 +248,8 @@
 	 * underrun/overflow.
 	 * 
 	 * 
+	 * HOWEVER, the javadocs say the opposite thing!
+	 * 
 	 */
 
 	public boolean isActive() {
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java	Tue Sep 23 11:38:18 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java	Tue Sep 23 15:02:55 2008 -0400
@@ -151,6 +151,8 @@
 		};
 		stream.addWriteListener(writeNotifier);
 
+		boolean interrupted = false;
+
 		while (remainingLength != 0) {
 
 			synchronized (eventLoop.threadLock) {
@@ -164,8 +166,8 @@
 						try {
 							eventLoop.threadLock.wait(100);
 						} catch (InterruptedException e) {
-							// FIXME
-							assert (false);
+							// ignore for now
+							interrupted = true;
 						}
 
 					}
@@ -195,7 +197,9 @@
 		 */
 
 		stream.removeWriteListener(writeNotifier);
-
+		if (interrupted) {
+			Thread.currentThread().interrupt();
+		}
 		return sizeWritten;
 	}
 
--- a/unittests/org/classpath/icedtea/pulseaudio/PulseAudioClipTest.java	Tue Sep 23 11:38:18 2008 -0400
+++ b/unittests/org/classpath/icedtea/pulseaudio/PulseAudioClipTest.java	Tue Sep 23 15:02:55 2008 -0400
@@ -168,6 +168,8 @@
 	@Test
 	public void testOpenEvent() throws LineUnavailableException,
 			UnsupportedAudioFileException, IOException {
+		System.out
+				.println("This tests the OPEN event. You should see an OPEN on the next line");
 		Clip clip = (Clip) mixer.getLine(new Line.Info(Clip.class));
 		File soundFile = new File("testsounds/startup.wav");
 		AudioInputStream audioInputStream = AudioSystem
@@ -327,8 +329,8 @@
 	}
 
 	@Test
-	public void testLoop0InterruptsPlayback() throws LineUnavailableException, IOException,
-			UnsupportedAudioFileException {
+	public void testLoop0InterruptsPlayback() throws LineUnavailableException,
+			IOException, UnsupportedAudioFileException {
 		Clip clip = (Clip) mixer.getLine(new Line.Info(Clip.class));
 		File soundFile = new File("testsounds/startup.wav");
 		AudioInputStream audioInputStream = AudioSystem
@@ -410,7 +412,7 @@
 	}
 
 	@Test
-	public void testFramePosition() throws LineUnavailableException,
+	public void testFramePositionBeforeClose() throws LineUnavailableException,
 			UnsupportedAudioFileException, IOException {
 		System.out
 				.println("This tests if the Clip provides the correct frame position");
@@ -430,7 +432,89 @@
 		clip.close();
 
 		long expected = 136703;
-		long granularity = 100;
+		long granularity = 5;
+		System.out.println("Frames in " + fileName + ": " + expected);
+		System.out.println("Frame position in clip :" + pos);
+		Assert.assertTrue("Expected: " + expected + " got " + pos, Math
+				.abs(expected - pos) < granularity);
+
+	}
+
+	@Test
+	public void testFramePositionWithStartStop()
+			throws LineUnavailableException, UnsupportedAudioFileException,
+			IOException, InterruptedException {
+
+		System.out
+				.println("This tests if the Clip provides the correct frame position");
+		Clip clip = (Clip) mixer.getLine(new Line.Info(Clip.class));
+		String fileName = "testsounds/logout.wav";
+		File soundFile1 = new File(fileName);
+		AudioInputStream audioInputStream1 = AudioSystem
+				.getAudioInputStream(soundFile1);
+		clip.open(audioInputStream1);
+
+		clip.start();
+
+		Thread.sleep(500);
+		clip.stop();
+		Thread.sleep(5000);
+		clip.start();
+
+		clip.drain();
+
+		long pos = clip.getFramePosition();
+
+		clip.close();
+
+		long expected = 136703;
+		long granularity = 5;
+		System.out.println("Frames in " + fileName + ": " + expected);
+		System.out.println("Frame position in clip :" + pos);
+		Assert.assertTrue("Expected: " + expected + " got " + pos, Math
+				.abs(expected - pos) < granularity);
+
+	}
+
+	@Test
+	public void testFramePositionWithLoop() throws LineUnavailableException,
+			UnsupportedAudioFileException, IOException, InterruptedException {
+		System.out.println("This tests if the Clip provides the correct frame "
+				+ "position with a bit of looping in the clip");
+		Clip clip = (Clip) mixer.getLine(new Line.Info(Clip.class));
+		String fileName = "testsounds/logout.wav";
+		File soundFile1 = new File(fileName);
+		AudioInputStream audioInputStream1 = AudioSystem
+				.getAudioInputStream(soundFile1);
+		clip.open(audioInputStream1);
+
+		clip.setLoopPoints(0, -1);
+		clip.loop(1);
+		Thread.sleep(500);
+		clip.stop();
+		Thread.sleep(2000);
+		clip.start();
+		Thread.sleep(100);
+		clip.stop();
+		Thread.sleep(4000);
+		clip.start();
+		Thread.sleep(100);
+		clip.stop();
+		Thread.sleep(2000);
+		clip.start();
+		Thread.sleep(100);
+		clip.stop();
+		Thread.sleep(3000);
+		clip.start();
+
+		clip.drain();
+
+		long pos = clip.getFramePosition();
+
+		clip.close();
+
+		long expected = 136703 * 1;
+		long granularity = 5;
 		System.out.println("Frames in " + fileName + ": " + expected);
 		System.out.println("Frame position in clip :" + pos);
 		Assert.assertTrue("Expected: " + expected + " got " + pos, Math
@@ -451,6 +535,7 @@
 				.getAudioInputStream(soundFile1);
 		clip.open(audioInputStream1);
 
+		clip.setLoopPoints(0, -1);
 		clip.loop(1);
 
 		clip.drain();
@@ -460,7 +545,7 @@
 		clip.close();
 
 		long expected = 136703 * 2;
-		long granularity = 100;
+		long granularity = 5;
 		System.out.println("Frames in " + fileName + ": " + expected);
 		System.out.println("Frame position in clip :" + pos);
 		Assert.assertTrue("Expected: " + expected + " got " + pos, Math
--- a/unittests/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLineRawTest.java	Tue Sep 23 11:38:18 2008 -0400
+++ b/unittests/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLineRawTest.java	Tue Sep 23 15:02:55 2008 -0400
@@ -113,7 +113,6 @@
 			} catch (LineUnavailableException e) {
 				e.printStackTrace();
 			} catch (IOException e) {
-				// TODO Auto-generated catch block
 				e.printStackTrace();
 			}
 		}
--- a/unittests/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLineTest.java	Tue Sep 23 11:38:18 2008 -0400
+++ b/unittests/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLineTest.java	Tue Sep 23 15:02:55 2008 -0400
@@ -119,7 +119,6 @@
 			} catch (LineUnavailableException e) {
 				e.printStackTrace();
 			} catch (IOException e) {
-				// TODO Auto-generated catch block
 				e.printStackTrace();
 			}
 		}
@@ -152,7 +151,7 @@
 		stopped = 0;
 		opened = 0;
 		closed = 0;
-
+		listenerCalled = 0;
 	}
 
 	@Test