changeset 50:61cf6f811619

fixed problem in acquiring a SourceDataLine given only a Line.Info (instead of a DataLine.Info) some minor cleanup
author Omair Majid <omajid@redhat.com>
date Fri, 08 Aug 2008 16:24:38 -0400
parents bdc766c0c2ae
children 5c09d21c2d70
files src/org/classpath/icedtea/pulseaudio/EventLoop.java src/org/classpath/icedtea/pulseaudio/PulseAudioMixer.java src/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java unittests/org/classpath/icedtea/pulseaudio/PulseSourceDataLineTest.java
diffstat 4 files changed, 157 insertions(+), 99 deletions(-) [+]
line wrap: on
line diff
--- a/src/org/classpath/icedtea/pulseaudio/EventLoop.java	Fri Aug 08 14:54:18 2008 -0400
+++ b/src/org/classpath/icedtea/pulseaudio/EventLoop.java	Fri Aug 08 16:24:38 2008 -0400
@@ -63,12 +63,11 @@
 	private static EventLoop instance = null;
 
 	private List<ContextListener> contextListeners;
-	// private List<SourceDataLine> lines;
+
 	private String name;
 	private String serverString;
 
 	private int status;
-	// private boolean eventLoopIsRunning = false;
 
 	public Semaphore finished = new Semaphore(0);
 
@@ -76,7 +75,7 @@
 	 * JNI stuff
 	 * 
 	 * Do not synchronize the individual functions, synchronize
-	 * block/method/lines around the call
+	 * block/method/lines around the call using threadLock
 	 * 
 	 */
 
@@ -91,7 +90,6 @@
 	/*
 	 * These fields hold pointers
 	 * 
-	 * 
 	 */
 	@SuppressWarnings("unused")
 	private long contextPointer;
@@ -103,17 +101,16 @@
 	 */
 
 	static {
-		//try {
-			/*String library = new java.io.File(".").getCanonicalPath()
+		try {
+			String library = new java.io.File(".").getCanonicalPath()
 					+ java.io.File.separatorChar + "lib"
 					+ java.io.File.separatorChar
-					+ System.mapLibraryName("pulse-java");*/
-			String library = "/home/yyz/iivan/workspace/pulseaudio/lib/libpulse-java.so";
-			System.out.println(library);
+					+ System.mapLibraryName("pulse-java");
+			// System.out.println(library);
 			System.load(library);
-		/*} catch (IOException e) {
+		} catch (IOException e) {
 			assert ("Loading failed".endsWith("library"));
-		}*/
+		}
 	}
 
 	private EventLoop() {
@@ -156,12 +153,12 @@
 					native_shutdown();
 					// System.out.println(this.getClass().getName()
 					// + ": shutting down");
-					
+
 					// clean up the listeners
 					synchronized (contextListeners) {
 						contextListeners.clear();
 					}
-					
+
 					return;
 
 				}
@@ -218,11 +215,11 @@
 	}
 
 	private void fireEvent(final ContextEvent e) {
-//		System.out.println(this.getClass().getName() + "firing event: "
-//				+ e.getType().toString());
+		// System.out.println(this.getClass().getName() + "firing event: "
+		// + e.getType().toString());
 
 		synchronized (contextListeners) {
-//			System.out.println(contextListeners.size());
+			// System.out.println(contextListeners.size());
 			for (ContextListener listener : contextListeners) {
 				listener.update(e);
 			}
--- a/src/org/classpath/icedtea/pulseaudio/PulseAudioMixer.java	Fri Aug 08 14:54:18 2008 -0400
+++ b/src/org/classpath/icedtea/pulseaudio/PulseAudioMixer.java	Fri Aug 08 16:24:38 2008 -0400
@@ -50,7 +50,6 @@
 import javax.sound.sampled.BooleanControl;
 import javax.sound.sampled.Control;
 import javax.sound.sampled.FloatControl;
-import javax.sound.sampled.DataLine;
 import javax.sound.sampled.Line;
 import javax.sound.sampled.LineEvent;
 import javax.sound.sampled.LineListener;
@@ -74,9 +73,6 @@
 	private List<PulseAudioSourceDataLine> sourceLines = new ArrayList<PulseAudioSourceDataLine>();
 	// private List<PulseAudioTargetDataLine> targetLines = null;
 
-	// private Line.Info targetDataLineInfo = new
-	// Line.Info(PulseAudioTargetDataLine.class);
-
 	List<LineListener> lineListeners = null;
 
 	private PulseAudioMixer() {
@@ -103,22 +99,24 @@
 		PulseAudioSourceDataLine sourceLine = null;
 		sourceLine = new PulseAudioSourceDataLine(eventLoop);
 		Line.Info sourceDataLineInfo = sourceLine.getLineInfo();
-		/*if (info instanceof DataLine.Info) {
-			if (info.matches(sourceDataLineInfo)) {
-				sourceLines.add(sourceLine);
-				return sourceLine;
-			}
-		}*/
-		
-		return sourceLine;
 
-		// if (info.matches(_targetDataLineInfo)) {
-		// PulseAudioTargetDataLine targetLine = new PulseAudioTargetDataLine();
-		// _targetLines.add(targetLine);
+		// TODO need to add special case code for 
+		// DataLine.Info case
+		if (info.matches(sourceDataLineInfo)) {
+			sourceLines.add(sourceLine);
+			return sourceLine;
+		}
+
+		// PulseAudioTargetDataLine targetLine = new
+		// PulseAudioTargetDataLine(eventLoop);
+		// Line.Info targetDataLineInfo = targetLine.getLineInfo();
+
+		// if (info.matches(targetDataLineInfo)) {
+		// targetLines.add(targetLine);
 		// return targetLine;
 		// }
 
-		//throw new IllegalArgumentException();
+		throw new IllegalArgumentException();
 	}
 
 	@Override
@@ -146,28 +144,31 @@
 	@Override
 	public javax.sound.sampled.Line.Info[] getSourceLineInfo(
 			javax.sound.sampled.Line.Info info) {
-		Line.Info sourceInfo = new Line.Info(PulseAudioSourceDataLine.class);
+		SourceDataLine sourceLine = new PulseAudioSourceDataLine(eventLoop);
+		Line.Info sourceInfo = sourceLine.getLineInfo();
+
 		if (info.matches(sourceInfo)) {
 			Line.Info[] sourceInfos = { sourceInfo, };
 			return sourceInfos;
-		} else {
-			Line.Info[] sourceInfos = {};
-			return sourceInfos;
+		}
 
-		}
+		return new Line.Info[] {};
+
 	}
 
 	@Override
 	public Line[] getSourceLines() {
-		// return (Line[]) _sourceLines.toArray();
-		return null;
-
+		return (Line[]) sourceLines.toArray();
 	}
 
 	@Override
 	public javax.sound.sampled.Line.Info[] getTargetLineInfo() {
-		Line.Info[] info = { new Line.Info(PulseAudioTargetDataLine.class), };
-		return info;
+		if (isOpen) {
+			PulseAudioTargetDataLine targetDataLine = new PulseAudioTargetDataLine();
+			return new Line.Info[] { targetDataLine.getLineInfo() };
+		}
+		// if not open, return an empty array
+		return new Line.Info[] {};
 	}
 
 	@Override
@@ -177,10 +178,10 @@
 		if (info.matches(sourceInfo)) {
 			Line.Info[] sourceInfos = { sourceInfo, };
 			return sourceInfos;
-		} else {
-			Line.Info[] sourceInfos = {};
-			return sourceInfos;
 		}
+
+		return new Line.Info[] {};
+
 	}
 
 	@Override
@@ -191,9 +192,15 @@
 
 	@Override
 	public boolean isLineSupported(javax.sound.sampled.Line.Info info) {
-		// if ( _sourceDataLineInfo.matches(info)) {
-		// return true;
-		// }
+		if (isOpen) {
+			PulseAudioSourceDataLine sourceLine = new PulseAudioSourceDataLine(
+					eventLoop);
+			Line.Info sourceLineInfo = sourceLine.getLineInfo();
+
+			if (info.matches(sourceLineInfo)) {
+				return true;
+			}
+		}
 		return false;
 	}
 
@@ -242,10 +249,9 @@
 		fireEvent(new LineEvent(this, LineEvent.Type.CLOSE,
 				AudioSystem.NOT_SPECIFIED));
 
-		/*
-		 * FIXME need to clean up the listeners on close without a race
-		 * condition
-		 */
+		synchronized (lineListeners) {
+			lineListeners.clear();
+		}
 
 	}
 
@@ -294,7 +300,8 @@
 		try {
 			openRemote(appName, null);
 		} catch (UnknownHostException e) {
-			// not possible
+			assert ("opening a local connection cant result in unknownhost"
+					.length() == 0);
 		}
 	}
 
@@ -393,7 +400,9 @@
 
 	/*
 	 * Should this method be synchronized? I had a few reasons, but i forgot
-	 * them Pros: - Thread safety?
+	 * them :(
+	 * 
+	 * Pros: - Thread safety?
 	 * 
 	 * Cons: - eventListeners are run from other threads, if those then call
 	 * fireEvent while a method is waiting on a listener, this synchronized
@@ -430,21 +439,25 @@
 
 		System.out.println("got a line");
 
-		//File soundFile = new File(new java.io.File(".").getCanonicalPath() + "/testsounds/logout.wav");
-		File soundFile = new File( "/home/iivan/workspace/pulseaudio/testsounds/logout.wav");
+		// File soundFile = new File(new java.io.File(".").getCanonicalPath()
+		// + "/testsounds/logout.wav");
+		File soundFile = new File(
+				"/home/iivan/workspace/pulseaudio/testsounds/logout.wav");
 		AudioInputStream audioInputStream = AudioSystem
 				.getAudioInputStream(soundFile);
 		AudioFormat audioFormat = audioInputStream.getFormat();
+		System.out.println();
 		line.open(audioFormat);
 		line.start();
-		PulseAudioStreamVolumeControl control = (PulseAudioStreamVolumeControl) line.getControl(FloatControl.Type.VOLUME);
-		PulseAudioStreamMuteControl mute = (PulseAudioStreamMuteControl) line.getControl(BooleanControl.Type.MUTE);
+		PulseAudioStreamVolumeControl control = (PulseAudioStreamVolumeControl) line
+				.getControl(FloatControl.Type.VOLUME);
+		PulseAudioStreamMuteControl mute = (PulseAudioStreamMuteControl) line
+				.getControl(BooleanControl.Type.MUTE);
 		mute.setValue(true);
 		control.setValue(40000);
 		mute.setValue(false);
 		System.out.println("Volume set to " + control.getValue());
-	
-		
+
 		byte[] abData = new byte[1000];
 		int bytesRead = 0;
 
--- a/src/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java	Fri Aug 08 14:54:18 2008 -0400
+++ b/src/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java	Fri Aug 08 16:24:38 2008 -0400
@@ -47,7 +47,6 @@
 
 import javax.sound.sampled.AudioFormat;
 import javax.sound.sampled.AudioSystem;
-import javax.sound.sampled.BooleanControl;
 import javax.sound.sampled.Control;
 import javax.sound.sampled.DataLine;
 import javax.sound.sampled.LineEvent;
@@ -74,7 +73,7 @@
 	private AudioFormat currentFormat = null;
 
 	private List<LineListener> listeners;
-	
+
 	private Control[] controls = new Control[2];
 	private PulseAudioStreamMuteControl muteControl;
 	private PulseAudioStreamVolumeControl volumeControl;
@@ -124,19 +123,11 @@
 
 	}
 
-	
-	
-	
-	
-
-
 	public PulseAudioSourceDataLine(EventLoop eventLoop) {
 		this.eventLoop = eventLoop;
 		this.listeners = new ArrayList<LineListener>();
 		this.volume = 65536;
 
-
-
 		/*
 		 * FIXME puselaudio supports any sample rate (it can covert between
 		 * sample rates without a problem). it calculates the frame size and the
@@ -153,9 +144,9 @@
 		 * 
 		 * 
 		 */
-		
+
 		supportedFormats = new LinkedList<AudioFormat>();
-		
+
 		Map<String, Object> properties;
 
 		int[] channelSizes = new int[] { 1, 2, 5 };
@@ -168,8 +159,7 @@
 			// as soon as they change something
 			// FIXME ^
 			int sampleSize = 8; // in bits
-			AudioFormat PA_SAMPLE_U8 = new AudioFormat(
-					Encoding.PCM_UNSIGNED, // encoding
+			AudioFormat PA_SAMPLE_U8 = new AudioFormat(Encoding.PCM_UNSIGNED, // encoding
 					AudioSystem.NOT_SPECIFIED, // sample rate
 					sampleSize, // sample size
 					channelSize, // channels
@@ -319,32 +309,31 @@
 
 		currentFormat = null;
 
-
 	}
 
 	protected boolean isMuted() {
 		return muted;
 	}
-	
+
 	protected void setMuted(boolean value) {
 		muted = value;
 	}
-	
+
 	protected float getVolume() {
 		return this.volume;
 	}
-	
+
 	protected void setVolume(float value) {
 		this.volume = value;
-		
+
 	}
-	
+
 	public void open(AudioFormat format, int bufferSize)
 			throws LineUnavailableException {
 		if (isOpen) {
 			throw new IllegalStateException("Line is already open");
 		}
-		
+
 		// ignore suggested buffer size
 
 		for (AudioFormat myFormat : supportedFormats) {
@@ -358,7 +347,9 @@
 			}
 		}
 
-		//throw new IllegalArgumentException("invalid format");
+		if (!isOpen) {
+			throw new IllegalArgumentException("Unsupported format");
+		}
 
 		final Semaphore semaphore = new Semaphore(0);
 
@@ -382,11 +373,9 @@
 		try {
 			semaphore.acquire();
 		} catch (InterruptedException e) {
-			// throw new LineUnavailableException("unable to prepare
-			// stream");
+			throw new LineUnavailableException("unable to prepare stream");
 		}
-		System.out.println(this.getClass().getName() + "stream is ready");
-		
+
 		volumeControl = new PulseAudioStreamVolumeControl(this);
 		controls[0] = volumeControl;
 		muteControl = new PulseAudioStreamMuteControl(this);
@@ -446,16 +435,16 @@
 	}
 
 	public void start() {
-			if (isPaused) {
-				native_resume();
-				isPaused = false;
-			}
+		if (isPaused) {
+			native_resume();
+			isPaused = false;
+		}
 
 		/*
 		 * for(LineListener l :listeners) { l.update(new LineEvent(this,
 		 * LineEvent.Type.START, 0)); }
 		 */
-	
+
 	}
 
 	public void stop() {
@@ -530,7 +519,6 @@
 		return 0;
 	}
 
-	
 	public boolean isActive() {
 		// TODO Auto-generated method stub
 		return false;
@@ -543,7 +531,7 @@
 
 	public Control getControl(Type control) {
 		for (int i = 0; i < controls.length; i++) {
-			if (controls[i].getType() == control){
+			if (controls[i].getType() == control) {
 
 				return controls[i];
 			}
@@ -556,7 +544,7 @@
 	}
 
 	public javax.sound.sampled.Line.Info getLineInfo() {
-		return new DataLine.Info(SourceDataLine.class,
+		return new DataLine.Info(PulseAudioSourceDataLine.class,
 				supportedFormats.toArray(new AudioFormat[0]), 0, 100000);
 	}
 
@@ -626,12 +614,10 @@
 			streamListener.update(e);
 		}
 	}
-	
+
 	protected EventLoop getEventLoop() {
 		return this.eventLoop;
 	}
-	
-
 
 	public long getStreamPointer() {
 		return streamPointer;
--- a/unittests/org/classpath/icedtea/pulseaudio/PulseSourceDataLineTest.java	Fri Aug 08 14:54:18 2008 -0400
+++ b/unittests/org/classpath/icedtea/pulseaudio/PulseSourceDataLineTest.java	Fri Aug 08 16:24:38 2008 -0400
@@ -43,7 +43,10 @@
 import javax.sound.sampled.AudioFormat;
 import javax.sound.sampled.AudioInputStream;
 import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.BooleanControl;
 import javax.sound.sampled.DataLine;
+import javax.sound.sampled.FloatControl;
+import javax.sound.sampled.Line;
 import javax.sound.sampled.LineUnavailableException;
 import javax.sound.sampled.Mixer;
 import javax.sound.sampled.SourceDataLine;
@@ -77,13 +80,12 @@
 		AudioInputStream audioInputStream = AudioSystem
 				.getAudioInputStream(soundFile);
 		AudioFormat audioFormat = audioInputStream.getFormat();
-		
+
 		SourceDataLine line;
 		line = (PulseAudioSourceDataLine) mixer.getLine(new DataLine.Info(
 				SourceDataLine.class, audioFormat));
 		Assert.assertNotNull(line);
 
-
 		line.open(audioFormat);
 		line.start();
 
@@ -112,6 +114,66 @@
 
 	}
 
+	@Test (expected = IllegalArgumentException.class)
+	public void testFindLineWithWrongFormat() throws LineUnavailableException {
+		SourceDataLine line = (SourceDataLine) mixer.getLine(new DataLine.Info(
+				SourceDataLine.class, new AudioFormat(
+						AudioFormat.Encoding.PCM_UNSIGNED, 44100, 100000, 1, 1, 10,
+						true)));
+		line.open();
+
+	}
+	
+	@Test
+	public void testVolumeAndMute() throws Exception {
+		Mixer.Info mixerInfos[] = AudioSystem.getMixerInfo();
+		Mixer.Info selectedMixerInfo = null;
+		for (Mixer.Info info : mixerInfos) {
+			if (info.getName().contains("PulseAudio")) {
+				selectedMixerInfo = info;
+				System.out.println(selectedMixerInfo);
+			}
+		}
+
+		Mixer selectedMixer = AudioSystem.getMixer(selectedMixerInfo);
+
+		selectedMixer.open();
+		SourceDataLine line = (SourceDataLine) selectedMixer
+				.getLine(new Line.Info(SourceDataLine.class));
+
+		File soundFile = new File(new java.io.File(".").getCanonicalPath()
+				+ "/testsounds/logout.wav");
+		AudioInputStream audioInputStream = AudioSystem
+				.getAudioInputStream(soundFile);
+		AudioFormat audioFormat = audioInputStream.getFormat();
+
+		line.open(audioFormat);
+		line.start();
+		PulseAudioStreamVolumeControl volume = (PulseAudioStreamVolumeControl) line
+				.getControl(FloatControl.Type.VOLUME);
+		PulseAudioStreamMuteControl mute = (PulseAudioStreamMuteControl) line
+				.getControl(BooleanControl.Type.MUTE);
+		
+		mute.setValue(true);
+		volume.setValue(40000);
+		
+		mute.setValue(false);
+
+		byte[] abData = new byte[1000];
+		int bytesRead = 0;
+
+		while (bytesRead >= 0) {
+			bytesRead = audioInputStream.read(abData, 0, abData.length);
+			if (bytesRead > 0) {
+				line.write(abData, 0, bytesRead);
+			}
+		}
+
+		line.flush();
+		selectedMixer.close();
+
+	}
+
 	@After
 	public void tearDown() throws Exception {