changeset 98:220882a984dd

2008-08-25 Omair Majid <omajid@redhat.com> * src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java (getBufferSize): Return the default buffer size if not opened. (isActive): Removed method; parent class PulseAudioDataLine has this. (start): Call parent class' start. (stop): Likewise. * src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java Added isStarted and isEngagedInIo. Commented out isPaused. (open): Use the default format supplied in the constructor. (close): Set isOpen to false. (start): Commented out isPaused stuff, seems to work ok. Set isStarted to true. (stop): Commented out isPaused. Set isStarted to false. (isActive): New function. (isRunning): Likewise. * src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java (write): Added a check that the line has been open()ed. (getBufferSize): Return the default buffer size if the line hasnt been opened. (setDefaultFormat): Removed obsolete function. (isActive): Moved function to parent class (isRunning): Likewise. * src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java Removed duplicate variables isOpen and isPaused. (read): Added a check that the line has been open()ed. (isActive): Moved functino to parent class. (isRunning): Likewise. * unittests/org/classpath/icedtea/pulseaudio/PulseAudioClipTest.java (testIsActiveAndIsOpen): New function. * unittests/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLineTest.java (testIsActiveAndIsOpen): New function. * unittests/org/classpath/icedtea/pulseaudio/PulseSourceDataLineTest.java (testOpenAndClose): New function. (testIsActiveAndIsOpen): Likewise. (testPlayLessThanFrameSize): Added calls to start() and stop(). (testBufferSizes): New function. (testHasADefaultFormat): Likewise. (testDefaultFormatWithGetLine): Likewise. (testDefaultBufferSize): Likewise. (messWithStreams): Likewise.
author Omair Majid <omajid@redhat.com>
date Mon, 25 Aug 2008 17:27:52 -0400
parents 2e4a2a022ffb
children 548fa22ff716
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 src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java unittests/org/classpath/icedtea/pulseaudio/PulseAudioClipTest.java unittests/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLineTest.java unittests/org/classpath/icedtea/pulseaudio/PulseSourceDataLineTest.java
diffstat 7 files changed, 252 insertions(+), 81 deletions(-) [+]
line wrap: on
line diff
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java	Mon Aug 25 14:51:13 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java	Mon Aug 25 17:27:52 2008 -0400
@@ -193,6 +193,9 @@
 
 	@Override
 	public int getBufferSize() {
+		if (!isOpen) {
+			return DEFAULT_BUFFER_SIZE;
+		}
 		return bufferSize;
 	}
 
@@ -262,12 +265,6 @@
 	}
 
 	@Override
-	public boolean isActive() {
-		// TODO Auto-generated method stub
-		return false;
-	}
-
-	@Override
 	public boolean isControlSupported(Type control) {
 		return false;
 	}
@@ -378,6 +375,8 @@
 
 	@Override
 	public void start() {
+		super.start();
+
 		if (!clipThread.isAlive()) {
 			synchronized (clipLock) {
 				loopsLeft = 0;
@@ -388,6 +387,7 @@
 	}
 
 	public void stop() {
+
 		if (clipThread.isAlive()) {
 			clipThread.interrupt();
 		}
@@ -400,6 +400,8 @@
 			currentFrame = 0;
 			loopsLeft = 0;
 		}
+
+		super.stop();
 	}
 
 }
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java	Mon Aug 25 14:51:13 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java	Mon Aug 25 17:27:52 2008 -0400
@@ -10,7 +10,6 @@
 import javax.sound.sampled.LineEvent;
 import javax.sound.sampled.LineListener;
 import javax.sound.sampled.LineUnavailableException;
-import javax.sound.sampled.AudioFormat.Encoding;
 
 public abstract class PulseAudioDataLine implements Line {
 
@@ -19,14 +18,24 @@
 
 	protected String streamName = "Java Stream";
 
+	// true between open() and close(). ie represents when a line has acquire
+	// resources
 	protected boolean isOpen = false;
-	private boolean isPaused = false;
+
+	// true between start() and stop()
+	protected boolean isStarted = false;
+
+	protected boolean isEngagedInIo = false;
+
+	// true if a stream has been paused
+	// protected boolean isPaused = false;
+
 	protected AudioFormat[] supportedFormats = null;
 	protected AudioFormat currentFormat = null;
 	protected AudioFormat defaultFormat = null;
 
 	protected int bufferSize = 0;
-	
+
 	protected List<LineListener> lineListeners = new ArrayList<LineListener>();;
 
 	protected EventLoop eventLoop = null;
@@ -103,16 +112,13 @@
 	}
 
 	public void open() throws LineUnavailableException {
-		// pick a random format
-		if (defaultFormat == null) {
-			defaultFormat = new AudioFormat(Encoding.PCM_UNSIGNED, 44100, 8, 2,
-					2, AudioSystem.NOT_SPECIFIED, false);
-		}
+		assert (defaultFormat != null);
 
 		open(defaultFormat, DEFAULT_BUFFER_SIZE);
 	}
 
 	public void close() {
+		// FIXME what should be done here
 		assert (isOpen);
 
 		synchronized (eventLoop.threadLock) {
@@ -127,15 +133,19 @@
 			// stream");
 		}
 
+		isOpen = false;
+
 	}
 
 	public void start() {
-		if (isPaused) {
-			synchronized (eventLoop.threadLock) {
-				stream.cork(false);
-			}
-			isPaused = false;
-		}
+		// if (isPaused) {
+		// synchronized (eventLoop.threadLock) {
+		// stream.cork(false);
+		// }
+		// isPaused = false;
+		// }
+
+		isStarted = true;
 
 		/*
 		 * for(LineListener l :listeners) { l.update(new LineEvent(this,
@@ -145,11 +155,35 @@
 	}
 
 	public void stop() {
-		synchronized (eventLoop.threadLock) {
-			stream.cork(true);
-		}
-		isPaused = true;
+		// synchronized (eventLoop.threadLock) {
+		// stream.cork(true);
+		// }
+		// isPaused = true;
+
+		isStarted = false;
+
+	}
 
+	/*
+	 * 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,
+	 * active means that the line is ready to take or give data. Running is
+	 * tightly bound to data flow in the line. I.e. when you start a
+	 * SourceDataLine but never write data to it, the line should not be
+	 * running. This also means that a line should become not running on buffer
+	 * underrun/overflow.
+	 * 
+	 * 
+	 */
+
+	public boolean isActive() {
+		return isStarted;
+	}
+
+	public boolean isRunning() {
+		// FIXME Auto-generated method stub
+		return false;
 	}
 
 	public void addLineListener(LineListener listener) {
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java	Mon Aug 25 14:51:13 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java	Mon Aug 25 17:27:52 2008 -0400
@@ -111,6 +111,9 @@
 
 	@Override
 	public int write(byte[] data, int offset, int length) {
+		if (!isStarted) {
+			throw new IllegalStateException("must call start() before write()");
+		}
 
 		int frameSize = currentFormat.getFrameSize();
 		if (length % frameSize != 0) {
@@ -168,6 +171,9 @@
 	};
 
 	public int getBufferSize() {
+		if (!isOpen) {
+			return DEFAULT_BUFFER_SIZE;
+		}
 		return bufferSize;
 	}
 
@@ -178,14 +184,6 @@
 		return currentFormat;
 	}
 
-	public void setDefaultFormat(AudioFormat format) {
-		for (AudioFormat aFormat : supportedFormats) {
-			if (format.matches(aFormat)) {
-				defaultFormat = format;
-			}
-		}
-	}
-
 	public int getFramePosition() {
 		return (int) currentFramePosition;
 	}
@@ -206,16 +204,6 @@
 		return microseconds;
 	}
 
-	public boolean isActive() {
-		// TODO Auto-generated method stub
-		return false;
-	}
-
-	public boolean isRunning() {
-		// TODO Auto-generated method stub
-		return false;
-	}
-
 	public Control getControl(Type control) {
 		if (!isOpen) {
 			throw new IllegalArgumentException(
@@ -240,7 +228,8 @@
 	}
 
 	public javax.sound.sampled.Line.Info getLineInfo() {
-		return new DataLine.Info(this.getClass(), supportedFormats, StreamBufferAttributes.MIN_VALUE,
+		return new DataLine.Info(this.getClass(), supportedFormats,
+				StreamBufferAttributes.MIN_VALUE,
 				StreamBufferAttributes.MAX_VALUE);
 	}
 
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java	Mon Aug 25 14:51:13 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java	Mon Aug 25 17:27:52 2008 -0400
@@ -50,9 +50,6 @@
 public class PulseAudioTargetDataLine extends PulseAudioDataLine implements
 		TargetDataLine {
 
-	protected boolean isOpen = false;
-	protected boolean isPaused = false;
-
 	private long currentFramePosition = 0;
 
 	@SuppressWarnings("unused")
@@ -78,6 +75,10 @@
 
 	@Override
 	public int read(byte[] data, int offset, int length) {
+		if (!isStarted) {
+			throw new IllegalStateException("must call open before read()");
+		}
+		
 		int frameSize = currentFormat.getFrameSize();
 
 		if (length % frameSize != 0) {
@@ -169,16 +170,6 @@
 		return (long) (currentFramePosition / currentFormat.getFrameRate());
 	}
 
-	public boolean isActive() {
-		// TODO Auto-generated method stub
-		return false;
-	}
-
-	public boolean isRunning() {
-		// TODO Auto-generated method stub
-		return false;
-	}
-
 	public Control getControl(Type control) {
 		throw new IllegalArgumentException(
 				"PulseAudioTargetDataLine does not support any controls");
--- a/unittests/org/classpath/icedtea/pulseaudio/PulseAudioClipTest.java	Mon Aug 25 14:51:13 2008 -0400
+++ b/unittests/org/classpath/icedtea/pulseaudio/PulseAudioClipTest.java	Mon Aug 25 17:27:52 2008 -0400
@@ -40,12 +40,15 @@
 import java.io.File;
 import java.io.IOException;
 
+import javax.sound.sampled.AudioFormat;
 import javax.sound.sampled.AudioInputStream;
 import javax.sound.sampled.AudioSystem;
 import javax.sound.sampled.Clip;
+import javax.sound.sampled.DataLine;
 import javax.sound.sampled.Line;
 import javax.sound.sampled.LineUnavailableException;
 import javax.sound.sampled.Mixer;
+import javax.sound.sampled.SourceDataLine;
 import javax.sound.sampled.UnsupportedAudioFileException;
 
 import junit.framework.JUnit4TestAdapter;
@@ -58,6 +61,8 @@
 public class PulseAudioClipTest {
 
 	Mixer mixer;
+	AudioFormat aSupportedFormat = new AudioFormat(
+			AudioFormat.Encoding.PCM_UNSIGNED, 44100f, 8, 1, 1, 10, true);
 
 	public static junit.framework.Test suite() {
 		return new JUnit4TestAdapter(PulseAudioClipTest.class);
@@ -84,31 +89,33 @@
 		Assert.assertNotNull(clip);
 	}
 
-	
-	@Test (expected=IllegalArgumentException.class)
+	@Test(expected = IllegalArgumentException.class)
 	public void testClipOpen() throws LineUnavailableException {
 		Clip clip = (Clip) mixer.getLine(new Line.Info(Clip.class));
 		clip.open();
 	}
-	
+
 	@Test
-	public void testClipOpens() throws LineUnavailableException, UnsupportedAudioFileException, IOException {
+	public void testClipOpens() throws LineUnavailableException,
+			UnsupportedAudioFileException, IOException {
 		Clip clip = (Clip) mixer.getLine(new Line.Info(Clip.class));
 		File soundFile = new File("testsounds/startup.wav");
 		AudioInputStream audioInputStream = AudioSystem
 				.getAudioInputStream(soundFile);
 		clip.open(audioInputStream);
 	}
-	
+
 	@Test
-	public void testLoopStopStartClip() throws LineUnavailableException, IOException, UnsupportedAudioFileException {
+	public void testLoopStopStartClip() throws LineUnavailableException,
+			IOException, UnsupportedAudioFileException {
 		Clip clip = (Clip) mixer.getLine(new Line.Info(Clip.class));
 		File soundFile = new File("testsounds/startup.wav");
 		AudioInputStream audioInputStream = AudioSystem
 				.getAudioInputStream(soundFile);
 		clip.open(audioInputStream);
-		
-		clip.setLoopPoints((int) (clip.getFrameLength() / 4), (int) (clip.getFrameLength() / 2));
+
+		clip.setLoopPoints((int) (clip.getFrameLength() / 4), (int) (clip
+				.getFrameLength() / 2));
 		clip.loop(4);
 		try {
 			Thread.sleep(2000);
@@ -124,16 +131,44 @@
 		clip.start();
 		clip.close();
 	}
-	
+
 	@Test
-	public void testLoop0Clip() throws LineUnavailableException, IOException, UnsupportedAudioFileException {
+	public void testIsActiveAndIsOpen() throws LineUnavailableException,
+			UnsupportedAudioFileException, IOException {
+
+		Clip clip = (Clip) mixer.getLine(new Line.Info(Clip.class));
+		File soundFile = new File("testsounds/startup.wav");
+		AudioInputStream audioInputStream = AudioSystem
+				.getAudioInputStream(soundFile);
+
+		Assert.assertFalse(clip.isActive());
+		Assert.assertFalse(clip.isOpen());
+		clip.open(audioInputStream);
+		Assert.assertTrue(clip.isOpen());
+		Assert.assertFalse(clip.isActive());
+		clip.start();
+		Assert.assertTrue(clip.isOpen());
+		Assert.assertTrue(clip.isActive());
+		clip.stop();
+		Assert.assertTrue(clip.isOpen());
+		Assert.assertFalse(clip.isActive());
+		clip.close();
+		Assert.assertFalse(clip.isOpen());
+		Assert.assertFalse(clip.isActive());
+
+	}
+
+	@Test
+	public void testLoop0Clip() throws LineUnavailableException, IOException,
+			UnsupportedAudioFileException {
 		Clip clip = (Clip) mixer.getLine(new Line.Info(Clip.class));
 		File soundFile = new File("testsounds/startup.wav");
 		AudioInputStream audioInputStream = AudioSystem
 				.getAudioInputStream(soundFile);
 		clip.open(audioInputStream);
-		
-		clip.setLoopPoints((int) (clip.getFrameLength() / 4), (int) (clip.getFrameLength() / 2));
+
+		clip.setLoopPoints((int) (clip.getFrameLength() / 4), (int) (clip
+				.getFrameLength() / 2));
 		clip.loop(4);
 		try {
 			Thread.sleep(2000);
@@ -143,14 +178,14 @@
 		clip.loop(0);
 		clip.close();
 	}
-	
-	@Test (expected=IllegalArgumentException.class)
+
+	@Test(expected = IllegalArgumentException.class)
 	public void testOpenWrongUse() throws LineUnavailableException {
 		Clip clip = (Clip) mixer.getLine(new Line.Info(Clip.class));
 		clip.open();
-		
+
 	}
-	
+
 	@After
 	public void tearDown() {
 
--- a/unittests/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLineTest.java	Mon Aug 25 14:51:13 2008 -0400
+++ b/unittests/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLineTest.java	Mon Aug 25 17:27:52 2008 -0400
@@ -1,11 +1,14 @@
 package org.classpath.icedtea.pulseaudio;
 
+import javax.sound.sampled.AudioFormat;
 import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.DataLine;
 import javax.sound.sampled.Line;
 import javax.sound.sampled.LineEvent;
 import javax.sound.sampled.LineListener;
 import javax.sound.sampled.LineUnavailableException;
 import javax.sound.sampled.Mixer;
+import javax.sound.sampled.SourceDataLine;
 import javax.sound.sampled.TargetDataLine;
 
 import junit.framework.JUnit4TestAdapter;
@@ -17,13 +20,16 @@
 
 public class PulseAudioTargetDataLineTest {
 
+	private Mixer mixer;
+	private TargetDataLine targetDataLine;
+
+	AudioFormat aSupportedFormat = new AudioFormat(
+			AudioFormat.Encoding.PCM_UNSIGNED, 44100f, 8, 1, 1, 10, true);
+
 	public static junit.framework.Test suite() {
 		return new JUnit4TestAdapter(PulseAudioTargetDataLineTest.class);
 	}
 
-	private Mixer mixer;
-	private TargetDataLine targetDataLine;
-
 	@Before
 	public void setUp() throws LineUnavailableException {
 		Mixer.Info[] mixerInfos = AudioSystem.getMixerInfo();
@@ -50,6 +56,29 @@
 	}
 
 	@Test
+	public void testIsActiveAndIsOpen() throws LineUnavailableException {
+
+		SourceDataLine line = (SourceDataLine) mixer.getLine(new DataLine.Info(
+				SourceDataLine.class, aSupportedFormat, 1000));
+
+		Assert.assertFalse(line.isActive());
+		Assert.assertFalse(line.isOpen());
+		line.open();
+		Assert.assertTrue(line.isOpen());
+		Assert.assertFalse(line.isActive());
+		line.start();
+		Assert.assertTrue(line.isOpen());
+		Assert.assertTrue(line.isActive());
+		line.stop();
+		Assert.assertTrue(line.isOpen());
+		Assert.assertFalse(line.isActive());
+		line.close();
+		Assert.assertFalse(line.isOpen());
+		Assert.assertFalse(line.isActive());
+
+	}
+
+	@Test
 	public void testOpenEvents() throws LineUnavailableException {
 		targetDataLine = (TargetDataLine) mixer.getLine(new Line.Info(
 				TargetDataLine.class));
@@ -71,7 +100,7 @@
 		targetDataLine.open();
 		targetDataLine.removeLineListener(openListener);
 		targetDataLine.close();
-		
+
 	}
 
 	@Test
@@ -92,15 +121,13 @@
 
 		};
 
-
 		targetDataLine.open();
 		targetDataLine.addLineListener(closeListener);
 		targetDataLine.close();
 		targetDataLine.removeLineListener(closeListener);
-		
+
 	}
-	
-	
+
 	@After
 	public void tearDown() {
 		mixer.close();
--- a/unittests/org/classpath/icedtea/pulseaudio/PulseSourceDataLineTest.java	Mon Aug 25 14:51:13 2008 -0400
+++ b/unittests/org/classpath/icedtea/pulseaudio/PulseSourceDataLineTest.java	Mon Aug 25 17:27:52 2008 -0400
@@ -67,6 +67,9 @@
 
 	private int listenerCalled = 0;
 
+	AudioFormat aSupportedFormat = new AudioFormat(
+			AudioFormat.Encoding.PCM_UNSIGNED, 44100f, 8, 1, 1, 10, true);
+
 	public static junit.framework.Test suite() {
 		return new JUnit4TestAdapter(PulseSourceDataLineTest.class);
 	}
@@ -79,6 +82,42 @@
 	}
 
 	@Test
+	public void testOpenAndClose() throws LineUnavailableException {
+		SourceDataLine line = (SourceDataLine) mixer.getLine(new Line.Info(
+				SourceDataLine.class));
+
+		line.open();
+		Assert.assertTrue(line.isOpen());
+
+		line.close();
+		Assert.assertFalse(line.isOpen());
+
+	}
+
+	@Test
+	public void testIsActiveAndIsOpen() throws LineUnavailableException {
+
+		SourceDataLine line = (SourceDataLine) mixer.getLine(new DataLine.Info(
+				SourceDataLine.class, aSupportedFormat, 1000));
+
+		Assert.assertFalse(line.isActive());
+		Assert.assertFalse(line.isOpen());
+		line.open();
+		Assert.assertTrue(line.isOpen());
+		Assert.assertFalse(line.isActive());
+		line.start();
+		Assert.assertTrue(line.isOpen());
+		Assert.assertTrue(line.isActive());
+		line.stop();
+		Assert.assertTrue(line.isOpen());
+		Assert.assertFalse(line.isActive());
+		line.close();
+		Assert.assertFalse(line.isOpen());
+		Assert.assertFalse(line.isActive());
+
+	}
+
+	@Test
 	public void testPlay() throws LineUnavailableException,
 			UnsupportedAudioFileException, IOException {
 		System.out.println("This test plays a file");
@@ -132,8 +171,9 @@
 		data[0] = (byte) 'a';
 
 		line.open();
+		line.start();
 		line.write(data, 0, 1);
-
+		line.stop();
 		line.close();
 
 	}
@@ -387,6 +427,59 @@
 		line.close();
 	}
 
+	@Test
+	public void testBufferSizes() throws LineUnavailableException {
+		SourceDataLine line = (SourceDataLine) mixer.getLine(new Line.Info(
+				SourceDataLine.class));
+		line.open(aSupportedFormat, 10000);
+		Assert.assertEquals(10000, line.getBufferSize());
+		line.close();
+	}
+
+	@Test
+	public void testHasADefaultFormat() throws LineUnavailableException {
+		SourceDataLine line = (SourceDataLine) mixer.getLine(new Line.Info(
+				SourceDataLine.class));
+		Assert.assertNotNull(line.getFormat());
+		System.out.println(line.getFormat());
+
+	}
+
+	@Test
+	public void testDefaultFormatWithGetLine() throws LineUnavailableException {
+		SourceDataLine line = (SourceDataLine) mixer.getLine(new DataLine.Info(
+				SourceDataLine.class, aSupportedFormat, 1000));
+		Assert.assertEquals(aSupportedFormat, line.getFormat());
+
+	}
+
+	@Test
+	public void testDefaultBufferSize() throws LineUnavailableException {
+		SourceDataLine line = (SourceDataLine) mixer.getLine(new DataLine.Info(
+				SourceDataLine.class, aSupportedFormat, 1000));
+		Assert.assertEquals(StreamBufferAttributes.SANE_DEFAULT, line
+				.getBufferSize());
+	}
+
+	@Test
+	public void messWithStreams() throws LineUnavailableException {
+		System.out
+				.println("This test tries to cork(false) a stream which hasnt been corked");
+
+		PulseAudioSourceDataLine line = (PulseAudioSourceDataLine) mixer
+				.getLine(new DataLine.Info(SourceDataLine.class,
+						aSupportedFormat, 1000));
+
+		line.open();
+		line.start();
+		Stream s = line.getStream();
+		Operation o;
+		synchronized (EventLoop.getEventLoop().threadLock) {
+			o = s.unCork();
+		}
+		o.waitForCompletion();
+	}
+
 	@After
 	public void tearDown() throws Exception {