changeset in /hg/pulseaudio: Ioana Ivan <iivan at redhat.com>

Ioana Ivan iivan at redhat.com
Tue Sep 23 14:09:50 PDT 2008


changeset 2d384ad19c3e in /hg/pulseaudio
details: http://icedtea.classpath.org/hg/pulseaudio?cmd=changeset;node=2d384ad19c3e
description:
	Ioana Ivan <iivan at redhat.com>
	        * src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java:
	                -split open() into createStream(), addStreamListeners() and
	                connect(), which can be reused when reconnecting the line for
	                synchronization
	                -added recconectForSynchronization()
	                -made some changes to stop()/start() in order to send
	                START/STOP events both when corking and in case of underflow
	        * src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixer.java:
	                -changes to synchronize()
	        * src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java:
	                 -changed connectLine to take the masterStream as a
	                 parameter in case we want to synchronize the Line
	        * src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java:
	                 -changed connectLine to take the masterStream as a
	                 parameter in case we want to synchronize the Line

diffstat:

11 files changed, 407 insertions(+), 111 deletions(-)
ChangeLog                                                                       |   20 +
src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java                   |   12 
src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java               |  190 ++++++++--
src/java/org/classpath/icedtea/pulseaudio/PulseAudioLine.java                   |    4 
src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixer.java                  |  141 ++++++-
src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java         |   55 +-
src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java         |    2 
src/java/org/classpath/icedtea/pulseaudio/Stream.java                           |   32 +
src/native/org_classpath_icedtea_pulseaudio_Stream.c                            |   24 +
unittests/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLineRawTest.java |   28 -
unittests/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLineTest.java    |   10 

diffs (truncated from 914 to 500 lines):

diff -r c46f6e0e7959 -r 2d384ad19c3e ChangeLog
--- a/ChangeLog	Tue Sep 23 15:02:55 2008 -0400
+++ b/ChangeLog	Tue Sep 23 17:01:26 2008 -0400
@@ -1,3 +1,23 @@ 2008-08-13 Ioana Ivan  <iivan at redhat.com
+2008-09-23 Ioana Ivan <iivan at redhat.com>
+	* src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java:
+		-split open() into createStream(), addStreamListeners() and
+		connect(), which can be reused when reconnecting the line for
+		synchronization
+		-added recconectForSynchronization()
+		-made some changes to stop()/start() in order to send
+		START/STOP events both when corking and in case of underflow
+	* src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixer.java:
+		-changes to synchronize()
+	* src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java:
+	         -changed connectLine to take the masterStream as a
+		 parameter in case we want to synchronize the Line
+	* src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java:
+		 -changed connectLine to take the masterStream as a
+		 parameter in case we want to synchronize the Line
+						                          
+
+			
+
 2008-08-13 Ioana Ivan  <iivan at redhat.com>
 	* src/java/org/classpath/icedtea/pulseaudio/PulseAudioPort.java:
 		superclass for TargetPort and SourcePort
diff -r c46f6e0e7959 -r 2d384ad19c3e src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java	Tue Sep 23 15:02:55 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java	Tue Sep 23 17:01:26 2008 -0400
@@ -192,11 +192,19 @@ public class PulseAudioClip extends Puls
 
 	}
 
-	protected void connectLine(int bufferSize) throws LineUnavailableException {
+	protected void connectLine(int bufferSize, Stream masterStream) throws LineUnavailableException{
 		StreamBufferAttributes bufferAttributes = new StreamBufferAttributes(
 				bufferSize, bufferSize / 2, bufferSize / 2, bufferSize / 2, 0);
 
-		stream.connectForPlayback(Stream.DEFAULT_DEVICE, bufferAttributes);
+		if(masterStream != null) {
+			synchronized (eventLoop.threadLock) {
+				stream.connectForPlayback(Stream.DEFAULT_DEVICE, bufferAttributes, masterStream.getStreamPointer());
+			}
+		} else {
+			synchronized (eventLoop.threadLock) {
+				stream.connectForPlayback(Stream.DEFAULT_DEVICE, bufferAttributes, null);
+			}
+		}
 	}
 
 	@Override
diff -r c46f6e0e7959 -r 2d384ad19c3e src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java	Tue Sep 23 15:02:55 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java	Tue Sep 23 17:01:26 2008 -0400
@@ -37,6 +37,7 @@ exception statement from your version.
 
 package org.classpath.icedtea.pulseaudio;
 
+import java.util.ArrayList;
 import java.util.concurrent.Semaphore;
 
 import javax.sound.sampled.AudioFormat;
@@ -45,6 +46,8 @@ import javax.sound.sampled.LineEvent;
 import javax.sound.sampled.LineEvent;
 import javax.sound.sampled.LineUnavailableException;
 
+import org.classpath.icedtea.pulseaudio.Stream.WriteListener;
+
 public abstract class PulseAudioDataLine extends PulseAudioLine implements
 		DataLine {
 
@@ -56,7 +59,8 @@ public abstract class PulseAudioDataLine
 	// true between start() and stop()
 	protected boolean isStarted = false;
 
-	protected boolean isEngagedInIo = false;
+	protected boolean dataWritten = false;
+	protected boolean corked = true;
 
 	// true if a stream has been paused
 	// protected boolean isPaused = false;
@@ -64,12 +68,14 @@ public abstract class PulseAudioDataLine
 	protected AudioFormat[] supportedFormats = null;
 	protected AudioFormat currentFormat = null;
 	protected AudioFormat defaultFormat = null;
+	protected boolean sendEvents = true;
 
 	protected int bufferSize = 0;
 
 	protected EventLoop eventLoop = null;
 	protected Semaphore semaphore = new Semaphore(0);
 	protected Stream stream;
+	private ArrayList<PulseAudioDataLine> synchronizedLines;
 
 	public void open(AudioFormat format, int bufferSize)
 			throws LineUnavailableException {
@@ -77,6 +83,16 @@ public abstract class PulseAudioDataLine
 		if (isOpen) {
 			throw new IllegalStateException("DataLine is already open");
 		}
+
+		createStream(format);
+
+		addStreamListeners();
+		connect(null, bufferSize);
+
+	}
+
+	public void createStream(AudioFormat format)
+			throws LineUnavailableException {
 
 		for (AudioFormat myFormat : supportedFormats) {
 			if (format.matches(myFormat)) {
@@ -91,11 +107,16 @@ public abstract class PulseAudioDataLine
 				super.open();
 			}
 		}
-		// no matches found
+
 		if (!isOpen) {
 			throw new IllegalArgumentException("Invalid format");
 		}
 
+		System.out.println("Stream " + stream + " created");
+
+	}
+
+	public void addStreamListeners() {
 		Stream.StateListener openCloseListener = new Stream.StateListener() {
 
 			@Override
@@ -116,14 +137,24 @@ public abstract class PulseAudioDataLine
 					 */
 
 					if (stream.getState() == Stream.State.READY) {
-						fireLineEvent(new LineEvent(PulseAudioDataLine.this,
-								LineEvent.Type.OPEN, AudioSystem.NOT_SPECIFIED));
+						if (sendEvents) {
+							fireLineEvent(new LineEvent(
+									PulseAudioDataLine.this,
+									LineEvent.Type.OPEN,
+									AudioSystem.NOT_SPECIFIED));
+						}
 						semaphore.release();
+
 					} else if (stream.getState() == Stream.State.TERMINATED
 							|| stream.getState() == Stream.State.FAILED) {
-						fireLineEvent((new LineEvent(PulseAudioDataLine.this,
-								LineEvent.Type.CLOSE, AudioSystem.NOT_SPECIFIED)));
+						if (sendEvents) {
+							fireLineEvent((new LineEvent(
+									PulseAudioDataLine.this,
+									LineEvent.Type.CLOSE,
+									AudioSystem.NOT_SPECIFIED)));
+						}
 						semaphore.release();
+
 					}
 				}
 			}
@@ -134,9 +165,13 @@ public abstract class PulseAudioDataLine
 		Stream.UnderflowListener stoppedListener = new Stream.UnderflowListener() {
 			@Override
 			public void update() {
-				isEngagedInIo = false;
-				fireLineEvent(new LineEvent(PulseAudioDataLine.this,
-						LineEvent.Type.STOP, AudioSystem.NOT_SPECIFIED));
+				dataWritten = false;
+				
+				if (!corked) {
+					fireLineEvent(new LineEvent(PulseAudioDataLine.this,
+							LineEvent.Type.STOP, AudioSystem.NOT_SPECIFIED));
+				}
+
 			}
 		};
 		stream.addUnderflowListener(stoppedListener);
@@ -144,34 +179,64 @@ public abstract class PulseAudioDataLine
 		Stream.PlaybackStartedListener startedListener = new Stream.PlaybackStartedListener() {
 			@Override
 			public void update() {
-				isEngagedInIo = true;
-				fireLineEvent(new LineEvent(PulseAudioDataLine.this,
-						LineEvent.Type.START, AudioSystem.NOT_SPECIFIED));
-			}
-
+
+				dataWritten = true;
+				if (!corked) {
+					fireLineEvent(new LineEvent(PulseAudioDataLine.this,
+							LineEvent.Type.START, AudioSystem.NOT_SPECIFIED));
+				}
+
+			}
 		};
 
 		stream.addPlaybackStartedListener(startedListener);
 
+		WriteListener writeNotifier = new WriteListener() {
+
+			@Override
+			public void update() {
+				// System.out.println("can write");
+				eventLoop.threadLock.notifyAll();
+			}
+
+		};
+		stream.addWriteListener(writeNotifier);
+
+		Stream.CorkListener corkListener = new Stream.CorkListener() {
+
+			@Override
+			public void update() {
+
+				eventLoop.threadLock.notifyAll();
+			}
+
+		};
+		stream.addCorkListener(corkListener);
+	}
+
+	public void connect(Stream masterStream, int bufferSize)
+			throws LineUnavailableException {
+
 		try {
 			synchronized (eventLoop.threadLock) {
-				connectLine(bufferSize);
-			}
-
+				connectLine(bufferSize, masterStream);
+			}
 		} catch (LineUnavailableException e) {
 			// error connecting to the server!
-			stream.removePlaybackStartedListener(startedListener);
-			stream.removeUnderflowListener(stoppedListener);
-			stream.removeStateListener(openCloseListener);
+			//stream.removePlaybackStartedListener(startedListener);
+			//stream.removeUnderflowListener(stoppedListener);
+			//stream.removeStateListener(openCloseListener);
 			stream.free();
 			stream = null;
 			throw e;
+
 		}
 		this.bufferSize = bufferSize;
 		try {
 			semaphore.acquire();
 			synchronized (eventLoop.threadLock) {
 				if (stream.getState() != Stream.State.READY) {
+					System.out.println(stream.getState());
 					stream.disconnect();
 					stream.free();
 					throw new LineUnavailableException(
@@ -200,6 +265,8 @@ public abstract class PulseAudioDataLine
 					"Line must be open for close() to work");
 		}
 
+		drain();
+
 		synchronized (eventLoop.threadLock) {
 			stream.disconnect();
 		}
@@ -218,21 +285,88 @@ public abstract class PulseAudioDataLine
 
 	}
 
+	public void reconnectforSynchronization(Stream masterStream) {
+		sendEvents = false;
+		drain();
+
+		synchronized (eventLoop.threadLock) {
+			stream.disconnect();
+		}
+		try {
+			semaphore.acquire();
+		} catch (InterruptedException e) {
+			throw new RuntimeException("unable to prepare stream");
+		}
+		try {
+			createStream(getFormat());
+		} catch (LineUnavailableException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		addStreamListeners();
+		try {
+			connect(masterStream, getBufferSize());
+		} catch (LineUnavailableException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		sendEvents = true;
+	}
+
 	public void start() {
 		if (!isOpen) {
 			throw new IllegalStateException(
 					"Line must be open()ed before it can be start()ed");
 		}
 
+		if (!corked) {
+			System.out.println("Already started, returning");
+			return;
+		}
+
+		corked = false;
+		Operation op;
+		synchronized (eventLoop.threadLock) {
+			op = stream.unCork();
+		}
+
+		op.waitForCompletion();
+		op.releaseReference();
 		isStarted = true;
-	}
-
-	public void stop() {
+		
+		/*if (dataWritten) {
+			fireLineEvent(new LineEvent(PulseAudioDataLine.this,
+					LineEvent.Type.START, AudioSystem.NOT_SPECIFIED));
+		}*/
+
+	}
+
+	public synchronized void stop() {
 		if (!isOpen) {
 			throw new IllegalStateException(
 					"Line must be open()ed before it can be start()ed");
-		}
+
+		}
+		if (corked) {
+			return;
+		}
+		corked = true;
+		Operation op;
+		synchronized (eventLoop.threadLock) {
+			op = stream.cork();
+		}
+
+		op.waitForCompletion();
+		op.releaseReference();
+
 		isStarted = false;
+		if (dataWritten) {
+			fireLineEvent(new LineEvent(PulseAudioDataLine.this,
+					LineEvent.Type.STOP, AudioSystem.NOT_SPECIFIED));
+		}
+
+		isStarted = false;
+
 	}
 
 	/*
@@ -257,10 +391,10 @@ public abstract class PulseAudioDataLine
 	}
 
 	public boolean isRunning() {
-		return isEngagedInIo;
-	}
-
-	protected abstract void connectLine(int bufferSize)
+		return !corked && dataWritten;
+	}
+
+	protected abstract void connectLine(int bufferSize, Stream masterStream)
 			throws LineUnavailableException;
 
 	public abstract void drain();
diff -r c46f6e0e7959 -r 2d384ad19c3e src/java/org/classpath/icedtea/pulseaudio/PulseAudioLine.java
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioLine.java	Tue Sep 23 15:02:55 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioLine.java	Tue Sep 23 17:01:26 2008 -0400
@@ -114,9 +114,9 @@ abstract class PulseAudioLine implements
 
 	@Override
 	public void open() throws LineUnavailableException {
-		if (isOpen) {
+		/*if (isOpen) {
 			throw new IllegalStateException("Line is already open");
-		}
+		}*/
 		isOpen = true;
 	}
 
diff -r c46f6e0e7959 -r 2d384ad19c3e src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixer.java
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixer.java	Tue Sep 23 15:02:55 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixer.java	Tue Sep 23 17:01:26 2008 -0400
@@ -38,6 +38,7 @@ package org.classpath.icedtea.pulseaudio
 package org.classpath.icedtea.pulseaudio;
 
 import java.io.File;
+import java.io.IOException;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
@@ -61,6 +62,7 @@ import javax.sound.sampled.Port;
 import javax.sound.sampled.Port;
 import javax.sound.sampled.SourceDataLine;
 import javax.sound.sampled.TargetDataLine;
+import javax.sound.sampled.UnsupportedAudioFileException;
 import javax.sound.sampled.AudioFormat.Encoding;
 import javax.sound.sampled.Control.Type;
 
@@ -393,13 +395,32 @@ public class PulseAudioMixer implements 
 	@Override
 	public boolean isSynchronizationSupported(Line[] lines, boolean maintainSync) {
 		// FIXME
-		return false;
+		return true;
 	}
 
 	@Override
 	public void synchronize(Line[] lines, boolean maintainSync) {
-		// FIXME pulse audio supports this
-		throw new IllegalArgumentException();
+		Line masterStream = null;
+		for (Line line : lines) {
+			if (line.isOpen()) {
+				masterStream = line;
+				break;
+			}
+		}
+		if(masterStream == null) {
+			//for now, can't synchronize lines if none of them is open (no stream pointer to pass)
+			//will see what to do about this later
+			throw new IllegalArgumentException();
+		}
+		
+		for(Line line : lines) {
+			if(line != masterStream) {
+			
+					((PulseAudioDataLine) line).reconnectforSynchronization(((PulseAudioDataLine) masterStream).getStream());
+				
+			}
+		}
+	
 	}
 
 	@Override
@@ -631,6 +652,10 @@ public class PulseAudioMixer implements 
 		}
 	}
 
+	public static void debug(String string) {
+		System.out.println("DEBUG: " + string);
+	}
+	
 	public static void main(String[] args) throws Exception {
 		Mixer.Info mixerInfos[] = AudioSystem.getMixerInfo();
 		Mixer.Info selectedMixerInfo = null;
@@ -648,27 +673,97 @@ public class PulseAudioMixer implements 
 				.getMixer(selectedMixerInfo);
 
 		mixer.open();
-		File soundFile = new File("testsounds/startup.wav");
-		AudioInputStream audioInputStream = AudioSystem
-				.getAudioInputStream(soundFile);
-		AudioFormat audioFormat = audioInputStream.getFormat();
-
-		SourceDataLine line;
-		line = (SourceDataLine) mixer.getLine(new DataLine.Info(
-				SourceDataLine.class, audioFormat));
-
-		line.open();
-		Port.Info info = new Port.Info(Port.class,
-				"alsa_output.pci_8086_24d5_sound_card_0_alsa_playback_0", false);
-		Port port = (Port) mixer.getLine(info);
-		FloatControl control = (FloatControl) port
-				.getControl(FloatControl.Type.VOLUME);
-		control.getValue();
-		control.setValue(0);
-		control.getValue();
-		System.out.println(control.getValue());
+
+		String fileName1 = "testsounds/startup.wav";
+		PulseAudioSourceDataLine line1;
+		line1 = (PulseAudioSourceDataLine) mixer.getLine(new Line.Info(
+				SourceDataLine.class));
+		line1.setName("Line 1");
+
+		String fileName2 = "testsounds/logout.wav";
+		PulseAudioSourceDataLine line2;
+		line2 = (PulseAudioSourceDataLine) mixer.getLine(new Line.Info(
+				SourceDataLine.class));
+		line2.setName("Line 2");
+		
+
+		ThreadWriter writer1 = mixer.new ThreadWriter(line1, fileName1);
+		ThreadWriter writer2 = mixer.new ThreadWriter(line2, fileName2);
+		//line2.start();
+	//	line1.start();
+		
+		Line[] lines = { line1, line2 };
+		mixer.synchronize(lines, true);
+		
+	//	line2.stop();
+		
+		debug("PulseAudioMixer: " + line1.getName() + " and " + line2.getName()



More information about the distro-pkg-dev mailing list