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