Mercurial > hg > pulseaudio
changeset 107:8ddfb8d274c7
2008-09-03 Omair Majid <omajid@redhat.com>
* src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java
Moved controls to PulseAudioDataLine.
(PulseAudioClip): Removed lineListeners and controls - They are in a
superclass of PulseAudioClip.
(getBufferSize): Moved method to PulseAudioDataLine.
(getControl): Moved method to PulseAudioLine.
(getControls): Likewise.
(getFormat): Moved method to PulseAudioDataLine.
(getLevel): Likewise.
(getLineInfo): Likewise.
(isControlSupported): Moved to PulseAudioLine.
(isOpen): Likewise.
(isRunning): Moved to PulseAudioDataLine.
(removeLineListeners): Moved to PulseAudioLine.
* src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java
(open): Now sets isEngagedInIo when playback starts or stops.
(close): Calls super.close and throws an exception if interrupted.
(start): Removed comments.
(stop): Likewise.
(getBufferSize): New function. Moved from derived class to this class.
(getLineInfo): Likewise.
(getFormat): Likewise.
(getLevel): Likewise.
* src/java/org/classpath/icedtea/pulseaudio/PulseAudioLine.java
Added controls.
(close): New function. Moved from derived class to this class.
(getControl): Likewise.
(getControls): Likewise.
(isControlSupported): Likewise.
(open): Likewise.
* src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixer.java
(getSupportedFormats): Removed redundant comments.
(getLine): Removed unused variables.
(addTargetLine): Renamed from addTargetDataLine.
(removeTargetLine): Renamed from removeTargetDataLine.
* src/java/org/classpath/icedtea/pulseaudio/PulseAduioPort.java
Removed controls
(open): Use the controls List from the parent class.
(getControl): Moved to superclass.
(getControls): Likewise.
(isControlSupported): Likewise.
* src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java
Removed controls array. Uses the one from parent class.
(open): Fixed to use the controls List.
(write): Added an extra check.
(getBufferSize): Moved to parent class.
(getFormat): Likewise.
(getLevel): Likewise.
(getControl): Likewise.
(getControls): Likewise.
(getLineInfo): Likewise.
(isControlSupported): Likewise.
* src/java/org/classpath/icedate/pulseaudio/PulseAudioTargetDataLine.java
(PulseAudioTargetDataLine): Use lineListeners from parent class.
(close): Fixed function name to call in Mixer.
(open): Likewise.
(read): Added an extra check.
(getBufferSize): Moved to parent class.
(getFormat): Likewise.
(getLevel): Likewise.
(getControl): Likewise.
(getControls): Likewise.
(getLIneInfo): Likewise.
(isControlSupported): Likewise.
* src/java/org/classpath/icedtea/pulseaudio/PulseAudioVolumeControl.java
Formatting fixes.
* src/java/org/classpath/icedtea/pulseaudio/Stream.java
(underflowCallback): Removed debug output.
* unittests/org/classpath/icedtea/pulseaudio/PulseAudioClipTest.java
(testOpenEvent): New test. Checks the OPEN event from PulseAudioClip
(testCloseEvent): New test. Check the CLOSE event from PulseAudioClip
(testPlayTwoClips): Removed redundant try/catch block.
(testSupportedControls): New test. Checks that PulseAudioClip supports at
least two controls.
(testMixerKnowsAboutOpenClips): New test. Checks that open clips are added
to the mixer's list of open source lines.
* unittests/org/classpath/icedtea/pulseaudio/PulseAudioMixerRawTest.java
Fixed formatting.
* unittests/org/classpath/icedtea/pulseaudio/PulseAudioMixerTest.java
(testOpeneingAgain): Fixed test to detect the IllegalStateException
thrown.
* unittests/org/classpath/icedtea/pulseaudio/PulseAudioSourcePortTest.java
(tearDown): New function. Close the mixer at the end of the test.
* unittests/org/classpath/icedtea/pulseaudio/PulseSourceDataLineTest.java
(testMixerKnowsAoubtOpenLines): Modified the test to check that the lines
are actually the same.
line wrap: on
line diff
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java Wed Sep 03 10:47:27 2008 -0400 +++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java Wed Sep 03 15:11:46 2008 -0400 @@ -38,21 +38,13 @@ package org.classpath.icedtea.pulseaudio; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; import java.util.concurrent.Semaphore; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; -import javax.sound.sampled.BooleanControl; import javax.sound.sampled.Clip; -import javax.sound.sampled.Control; -import javax.sound.sampled.DataLine; -import javax.sound.sampled.FloatControl; -import javax.sound.sampled.LineListener; import javax.sound.sampled.LineUnavailableException; -import javax.sound.sampled.Control.Type; import org.classpath.icedtea.pulseaudio.Stream.WriteListener; @@ -73,8 +65,6 @@ public static final String DEFAULT_CLIP_NAME = "Clip"; - private List<Control> controls = null; - private Object clipLock = new Object(); private boolean clipThreadStarted; private int loopsLeft; @@ -176,12 +166,11 @@ AudioFormat defaultFormat) { supportedFormats = formats; this.eventLoop = eventLoop; - this.lineListeners = new ArrayList<LineListener>(); this.defaultFormat = defaultFormat; this.currentFormat = defaultFormat; + this.volume = PulseAudioVolumeControl.MAX_VOLUME; + clipThread = new ClipThread(); - this.volume = PulseAudioVolumeControl.MAX_VOLUME; - controls = new ArrayList<Control>(); } @@ -241,46 +230,6 @@ } @Override - public int getBufferSize() { - if (!isOpen) { - return DEFAULT_BUFFER_SIZE; - } - return bufferSize; - } - - @Override - public Control getControl(Type control) { - if (isOpen) { - if (control.getClass() == BooleanControl.Type.MUTE.getClass()) { - return controls.get(1); - } - - if (control.getClass() == FloatControl.Type.VOLUME.getClass()) { - return controls.get(0); - } - } - throw new IllegalArgumentException(control.toString() - + " not supported"); - } - - @Override - public Control[] getControls() { - if (!isOpen) { - return new Control[] {}; - } - - return (Control[]) controls.toArray(new Control[0]); - } - - @Override - public AudioFormat getFormat() { - if (!isOpen) { - return defaultFormat; - } - return currentFormat; - } - - @Override public int getFrameLength() { return frameCount; } @@ -291,18 +240,6 @@ } @Override - public float getLevel() { - return AudioSystem.NOT_SPECIFIED; - } - - @Override - public javax.sound.sampled.Line.Info getLineInfo() { - return new DataLine.Info(this.getClass(), supportedFormats, - StreamBufferAttributes.MIN_VALUE, - StreamBufferAttributes.MAX_VALUE); - } - - @Override public long getLongFramePosition() { synchronized (clipLock) { return framesSinceOpen; @@ -327,22 +264,6 @@ } @Override - public boolean isControlSupported(Type control) { - return false; - } - - @Override - public boolean isOpen() { - return isOpen; - } - - @Override - public boolean isRunning() { - // really confused about what this is supposed to do - return isActive(); - } - - @Override public void loop(int count) { System.out.println("Loop " + count + " called"); @@ -429,11 +350,6 @@ } @Override - public void removeLineListener(LineListener listener) { - lineListeners.remove(listener); - } - - @Override public void setFramePosition(int frames) { if (frames > frameCount) {
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java Wed Sep 03 10:47:27 2008 -0400 +++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java Wed Sep 03 15:11:46 2008 -0400 @@ -53,9 +53,6 @@ protected String streamName = "Java Stream"; - // true between open() and close(). ie represents when a line has acquire - // resources - // true between start() and stop() protected boolean isStarted = false; @@ -76,9 +73,6 @@ public void open(AudioFormat format, int bufferSize) throws LineUnavailableException { - if (isOpen) { - throw new IllegalStateException("Line is already open"); - } for (AudioFormat myFormat : supportedFormats) { if (format.matches(myFormat)) { @@ -90,7 +84,7 @@ } currentFormat = format; - isOpen = true; + super.open(); } } // no matches found @@ -122,6 +116,7 @@ Stream.UnderflowListener stoppedListener = new Stream.UnderflowListener() { @Override public void update() { + isEngagedInIo = false; fireLineEvent(new LineEvent(PulseAudioDataLine.this, LineEvent.Type.STOP, AudioSystem.NOT_SPECIFIED)); } @@ -131,6 +126,7 @@ Stream.PlaybackStartedListener startedListener = new Stream.PlaybackStartedListener() { @Override public void update() { + isEngagedInIo = true; fireLineEvent(new LineEvent(PulseAudioDataLine.this, LineEvent.Type.START, AudioSystem.NOT_SPECIFIED)); } @@ -170,51 +166,31 @@ } public void close() { - // FIXME what should be done here - assert (isOpen); + + super.close(); synchronized (eventLoop.threadLock) { - // drain(); + drain(); stream.disconnect(); } try { semaphore.acquire(); } catch (InterruptedException e) { - // throw new LineUnavailableException("unable to prepare - // stream"); + throw new RuntimeException("unable to prepare stream"); } - isOpen = false; - } public void start() { - // if (isPaused) { - // synchronized (eventLoop.threadLock) { - // stream.cork(false); - // } - // isPaused = false; - // } - isStarted = true; - - /* - * for(LineListener l :listeners) { l.update(new LineEvent(this, - * LineEvent.Type.START, 0)); } - */ - } public void stop() { - // synchronized (eventLoop.threadLock) { - // stream.cork(true); - // } - // isPaused = true; + isStarted = false; + } - isStarted = false; - - } + // A BIG FIXME ! /* * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4791152 : @@ -245,4 +221,30 @@ return stream; } + @Override + public int getBufferSize() { + if (!isOpen) { + return DEFAULT_BUFFER_SIZE; + } + return bufferSize; + } + + public javax.sound.sampled.Line.Info getLineInfo() { + return new DataLine.Info(this.getClass(), supportedFormats, + StreamBufferAttributes.MIN_VALUE, + StreamBufferAttributes.MAX_VALUE); + } + + @Override + public AudioFormat getFormat() { + if (!isOpen) { + return defaultFormat; + } + return currentFormat; + } + + public float getLevel() { + return AudioSystem.NOT_SPECIFIED; + } + }
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioLine.java Wed Sep 03 10:47:27 2008 -0400 +++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioLine.java Wed Sep 03 15:11:46 2008 -0400 @@ -40,20 +40,33 @@ import java.util.ArrayList; import java.util.List; +import javax.sound.sampled.Control; +import javax.sound.sampled.Line; import javax.sound.sampled.LineEvent; import javax.sound.sampled.LineListener; +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.Control.Type; -abstract class PulseAudioLine { +abstract class PulseAudioLine implements Line { protected List<LineListener> lineListeners = new ArrayList<LineListener>(); + protected List<Control> controls = new ArrayList<Control>(); + + // true between open() and close(). ie represents when a line has acquire + // resources protected boolean isOpen = false; public void addLineListener(LineListener listener) { this.lineListeners.add(listener); } - public void removeLineListener(LineListener listener) { - this.lineListeners.remove(listener); + @Override + public void close() { + if (!isOpen) { + throw new IllegalStateException("Line is not open"); + } + + isOpen = false; } protected void fireLineEvent(LineEvent e) { @@ -62,8 +75,51 @@ } } + @Override + public Control getControl(Type control) { + if (isOpen) { + for (Control aControl : controls) { + if (aControl.getType() == control) { + return aControl; + } + } + } + throw new IllegalArgumentException(control.toString() + + " not supported"); + } + + @Override + public Control[] getControls() { + if (!isOpen) { + return new Control[] {}; + } + + return (Control[]) controls.toArray(new Control[0]); + } + + public boolean isControlSupported(Type control) { + for (Control myControl : controls) { + if (myControl.getType().getClass() == control.getClass()) { + return true; + } + } + return false; + } + public boolean isOpen() { return isOpen; } + @Override + public void open() throws LineUnavailableException { + if (isOpen) { + throw new IllegalStateException("Line is already open"); + } + isOpen = true; + } + + public void removeLineListener(LineListener listener) { + lineListeners.remove(listener); + } + }
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixer.java Wed Sep 03 10:47:27 2008 -0400 +++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixer.java Wed Sep 03 15:11:46 2008 -0400 @@ -119,15 +119,17 @@ Map<String, Object> properties; - int[] channelSizes = new int[] { 1, 2, 5 }; + /* + * frameSize = sample size (in bytes, not bits) x # of channels ^ From + * PulseAudio's sources + * http://git.0pointer.de/?p=pulseaudio.git;a=blob;f=src/pulse/sample.c;h=93da2465f4301e27af4976e82737c3a048124a68;hb=82ea8dde8abc51165a781c69bc3b38034d62d969#l63 + */ + + int[] channelSizes = new int[] { 1, 2, 5, 6, 8 }; for (int channelSize : channelSizes) { properties = new HashMap<String, Object>(); properties.put(PULSEAUDIO_FORMAT_KEY, "PA_SAMPLE_U8"); - // frameSize = sample size (in bytes, not bits) x # of channels - // ^ that's from PulseAudio sources, so it will pretty much break - // as soon as they change something - // FIXME ^ int sampleSize = 8; // in bits AudioFormat PA_SAMPLE_U8 = new AudioFormat(Encoding.PCM_UNSIGNED, // encoding AudioSystem.NOT_SPECIFIED, // sample rate @@ -145,11 +147,6 @@ properties = new HashMap<String, Object>(); properties.put(PULSEAUDIO_FORMAT_KEY, "PA_SAMPLE_ALAW"); - // frameSize = sample size (in bytes, not bits) x # of channels - // ^ that's from PulseAudio sources, so it will pretty much break - // as soon as they change something - // FIXME ^ - int sampleSize = 8; final AudioFormat PA_SAMPLE_ALAW = new AudioFormat(Encoding.ALAW, // encoding AudioSystem.NOT_SPECIFIED, // sample rate @@ -167,11 +164,6 @@ properties = new HashMap<String, Object>(); properties.put(PULSEAUDIO_FORMAT_KEY, "PA_SAMPLE_ULAW"); - // frameSize = sample size (in bytes, not bits) x # of channels - // ^ that's from PulseAudio sources, so it will pretty much break - // as soon as they change something - // FIXME ^ - int sampleSize = 8; final AudioFormat PA_SAMPLE_ULAW = new AudioFormat(Encoding.ULAW, // encoding AudioSystem.NOT_SPECIFIED, // sample rate @@ -189,11 +181,6 @@ properties = new HashMap<String, Object>(); properties.put(PULSEAUDIO_FORMAT_KEY, "PA_SAMPLE_S16BE"); - // frameSize = sample size (in bytes, not bits) x # of channels - // ^ that's from PulseAudio sources, so it will pretty much break - // as soon as they change something - // FIXME ^ - int sampleSize = 16; final AudioFormat PA_SAMPLE_S16BE = new AudioFormat( Encoding.PCM_SIGNED, // encoding @@ -212,11 +199,6 @@ properties = new HashMap<String, Object>(); properties.put(PULSEAUDIO_FORMAT_KEY, "PA_SAMPLE_S16LE"); - // frameSize = sample size (in bytes, not bits) x # of channels - // ^ that's from PulseAudio sources, so it will pretty much break - // as soon as they change something - // FIXME ^ - int sampleSize = 16; final AudioFormat A_SAMPLE_S16LE = new AudioFormat( Encoding.PCM_SIGNED, // encoding @@ -235,11 +217,6 @@ properties = new HashMap<String, Object>(); properties.put(PULSEAUDIO_FORMAT_KEY, "PA_SAMPLE_S32BE"); - // frameSize = sample size (in bytes, not bits) x # of channels - // ^ that's from PulseAudio sources, so it will pretty much break - // as soon as they change something - // FIXME ^ - int sampleSize = 32; final AudioFormat PA_SAMPLE_S32BE = new AudioFormat( Encoding.PCM_SIGNED, // encoding @@ -258,11 +235,6 @@ properties = new HashMap<String, Object>(); properties.put(PULSEAUDIO_FORMAT_KEY, "PA_SAMPLE_S32LE"); - // frameSize = sample size (in bytes, not bits) x # of channels - // ^ that's from PulseAudio sources, so it will pretty much break - // as soon as they change something - // FIXME ^ - int sampleSize = 32; final AudioFormat PA_SAMPLE_S32LE = new AudioFormat( Encoding.PCM_SIGNED, // encoding @@ -284,7 +256,7 @@ public Line getLine(javax.sound.sampled.Line.Info info) throws LineUnavailableException { - if (!this.isOpen) { + if (!isOpen) { throw new LineUnavailableException(); } @@ -331,12 +303,8 @@ return new PulseAudioClip(eventLoop, formats, defaultFormat); } - String portName; - boolean isSource; - if (Port.Info.class.isInstance(info)) { Port.Info portInfo = (Port.Info) info; - portName = portInfo.getName(); if (portInfo.isSource()) { return new PulseAudioSourcePort(portInfo.getName(), eventLoop); } else { @@ -344,7 +312,7 @@ } } - throw new IllegalArgumentException(); + throw new IllegalArgumentException("No matching lines found"); } @@ -519,7 +487,7 @@ @Override public boolean isOpen() { - return this.isOpen; + return isOpen; } @Override @@ -568,8 +536,9 @@ synchronized public void openRemote(String appName, String host, Integer port) throws UnknownHostException, LineUnavailableException { - if (this.isOpen) { - return; + + if (isOpen) { + throw new IllegalStateException("Mixer is already open"); } InetAddress addr = InetAddress.getAllByName(host)[0]; @@ -719,11 +688,11 @@ sourceLines.remove(line); } - void addTargetDataLine(PulseAudioLine line) { + void addTargetLine(PulseAudioLine line) { targetLines.add(line); } - void removeTargetDataLine(PulseAudioLine line) { + void removeTargetLine(PulseAudioLine line) { targetLines.remove(line); }
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioPort.java Wed Sep 03 10:47:27 2008 -0400 +++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioPort.java Wed Sep 03 15:11:46 2008 -0400 @@ -38,24 +38,28 @@ package org.classpath.icedtea.pulseaudio; import javax.sound.sampled.AudioSystem; -import javax.sound.sampled.Control; -import javax.sound.sampled.FloatControl; import javax.sound.sampled.LineEvent; -import javax.sound.sampled.LineListener; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.Port; -import javax.sound.sampled.Control.Type; public abstract class PulseAudioPort extends PulseAudioLine implements Port, PulseAudioPlaybackLine { private String name; + + /* + * Variable used in native code + */ + @SuppressWarnings("unused") private long contextPointer; + @SuppressWarnings("unused") + private int channels; + private EventLoop eventLoop; - private int channels; + private float volume; private boolean muted; - private Control[] controls = null; + private PulseAudioMuteControl muteControl; private PulseAudioVolumeControl volumeControl; @@ -65,11 +69,10 @@ this.eventLoop = eventLoop; updateVolumeInfo(); - controls = new Control[2]; volumeControl = new PulseAudioVolumeControl(this, eventLoop); - controls[0] = volumeControl; + controls.add(volumeControl); muteControl = new PulseAudioMuteControl(this, volumeControl); - controls[1] = muteControl; + controls.add(muteControl); isOpen = true; System.out.println("Opened Target Port " + name); @@ -119,41 +122,9 @@ AudioSystem.NOT_SPECIFIED)); } - public Control getControl(Type control) { - if (!isOpen) { - throw new IllegalArgumentException( - "Controls only supported when line is open"); - } - - for (int i = 0; i < controls.length; i++) { - if (controls[i].getType().getClass() == control.getClass()) { - return controls[i]; - } - } - throw new IllegalArgumentException("Unsupported control type"); - } - - public Control[] getControls() { - if (isOpen) { - return controls; - } else { - return new Control[] {}; - } - - } - @Override public abstract javax.sound.sampled.Line.Info getLineInfo(); - public boolean isControlSupported(Type control) { - for (Control myControl : controls) { - if (myControl.getType().getClass() == control.getClass()) { - return true; - } - } - return false; - } - @Override public void open() throws LineUnavailableException { native_setVolume(volume);
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java Wed Sep 03 10:47:27 2008 -0400 +++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java Wed Sep 03 15:11:46 2008 -0400 @@ -40,20 +40,15 @@ import java.util.ArrayList; import javax.sound.sampled.AudioFormat; -import javax.sound.sampled.AudioSystem; -import javax.sound.sampled.Control; -import javax.sound.sampled.DataLine; import javax.sound.sampled.LineListener; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.SourceDataLine; -import javax.sound.sampled.Control.Type; import org.classpath.icedtea.pulseaudio.Stream.WriteListener; public class PulseAudioSourceDataLine extends PulseAudioDataLine implements SourceDataLine, PulseAudioPlaybackLine { - private Control[] controls = null; private PulseAudioMuteControl muteControl; private PulseAudioVolumeControl volumeControl; private boolean muted; @@ -76,11 +71,11 @@ throws LineUnavailableException { super.open(format, bufferSize); - controls = new Control[2]; + volumeControl = new PulseAudioVolumeControl(this, eventLoop); - controls[0] = volumeControl; + controls.add(volumeControl); muteControl = new PulseAudioMuteControl(this, volumeControl); - controls[1] = muteControl; + controls.add(muteControl); PulseAudioMixer parentMixer = PulseAudioMixer.getInstance(); parentMixer.addSourceLine(this); @@ -120,6 +115,10 @@ @Override public int write(byte[] data, int offset, int length) { + if (!isOpen) { + throw new IllegalStateException("must call open() before write()"); + } + if (!isStarted) { throw new IllegalStateException("must call start() before write()"); } @@ -207,28 +206,10 @@ } }; - public int getBufferSize() { - if (!isOpen) { - return DEFAULT_BUFFER_SIZE; - } - return bufferSize; - } - - public AudioFormat getFormat() { - if (!isOpen) { - return defaultFormat; - } - return currentFormat; - } - public int getFramePosition() { return (int) currentFramePosition; } - public float getLevel() { - return AudioSystem.NOT_SPECIFIED; - } - public long getLongFramePosition() { return currentFramePosition; } @@ -241,44 +222,6 @@ return microseconds; } - public Control getControl(Type control) { - if (!isOpen) { - throw new IllegalArgumentException( - "Controls only supported when line is open"); - } - - for (int i = 0; i < controls.length; i++) { - if (controls[i].getType().getClass() == control.getClass()) { - return controls[i]; - } - } - throw new IllegalArgumentException("Unsupported control type"); - } - - public Control[] getControls() { - if (isOpen) { - return controls; - } else { - return new Control[] {}; - } - - } - - public javax.sound.sampled.Line.Info getLineInfo() { - return new DataLine.Info(this.getClass(), supportedFormats, - StreamBufferAttributes.MIN_VALUE, - StreamBufferAttributes.MAX_VALUE); - } - - public boolean isControlSupported(Type control) { - for (Control myControl : controls) { - if (myControl.getType().getClass() == control.getClass()) { - return true; - } - } - return false; - } - @Override public void drain() { Operation operation;
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java Wed Sep 03 10:47:27 2008 -0400 +++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java Wed Sep 03 15:11:46 2008 -0400 @@ -37,16 +37,9 @@ package org.classpath.icedtea.pulseaudio; -import java.util.ArrayList; - import javax.sound.sampled.AudioFormat; -import javax.sound.sampled.AudioSystem; -import javax.sound.sampled.Control; -import javax.sound.sampled.DataLine; -import javax.sound.sampled.LineListener; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.TargetDataLine; -import javax.sound.sampled.Control.Type; public class PulseAudioTargetDataLine extends PulseAudioDataLine implements TargetDataLine { @@ -60,7 +53,6 @@ AudioFormat defaultFormat) { supportedFormats = formats; this.eventLoop = eventLoop; - this.lineListeners = new ArrayList<LineListener>(); this.defaultFormat = defaultFormat; this.currentFormat = defaultFormat; @@ -69,7 +61,7 @@ @Override public void close() { PulseAudioMixer parentMixer = PulseAudioMixer.getInstance(); - parentMixer.removeTargetDataLine(this); + parentMixer.removeTargetLine(this); super.close(); } @@ -80,7 +72,7 @@ super.open(format, bufferSize); PulseAudioMixer parentMixer = PulseAudioMixer.getInstance(); - parentMixer.addTargetDataLine(this); + parentMixer.addTargetLine(this); } protected void connectLine(int bufferSize) { @@ -93,8 +85,13 @@ @Override public int read(byte[] data, int offset, int length) { + + if (!isOpen) { + throw new IllegalStateException("must call open() before read()"); + } + if (!isStarted) { - throw new IllegalStateException("must call open before read()"); + throw new IllegalStateException("must call start() before read()"); } int frameSize = currentFormat.getFrameSize(); @@ -145,14 +142,12 @@ @Override public void drain() { - // TODO Auto-generated method stub - + // FIXME how do we drain a target data line? } @Override public void flush() { // TODO Auto-generated method stub - } public int available() { @@ -161,25 +156,10 @@ } } - public int getBufferSize() { - return bufferSize; - } - - public AudioFormat getFormat() { - if (!isOpen) { - return defaultFormat; - } - return currentFormat; - } - public int getFramePosition() { return (int) currentFramePosition; } - public float getLevel() { - return AudioSystem.NOT_SPECIFIED; - } - public long getLongFramePosition() { return currentFramePosition; } @@ -188,23 +168,4 @@ return (long) (currentFramePosition / currentFormat.getFrameRate()); } - public Control getControl(Type control) { - throw new IllegalArgumentException( - "PulseAudioTargetDataLine does not support any controls"); - } - - public Control[] getControls() { - return new Control[] {}; - } - - public javax.sound.sampled.Line.Info getLineInfo() { - return new DataLine.Info(this.getClass(), supportedFormats, - StreamBufferAttributes.MIN_VALUE, - StreamBufferAttributes.MAX_VALUE); - } - - public boolean isControlSupported(Type control) { - return false; - } - } \ No newline at end of file
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioVolumeControl.java Wed Sep 03 10:47:27 2008 -0400 +++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioVolumeControl.java Wed Sep 03 15:11:46 2008 -0400 @@ -46,14 +46,15 @@ public static final int MAX_VOLUME = 65536; public static final int MIN_VOLUME = 0; - protected PulseAudioVolumeControl(PulseAudioPlaybackLine line, EventLoop eventLoop) { - super(FloatControl.Type.VOLUME, MIN_VOLUME, MAX_VOLUME, 1, -1, line.getVolume(), - "pulseaudio units", "Volume Off", + protected PulseAudioVolumeControl(PulseAudioPlaybackLine line, + EventLoop eventLoop) { + super(FloatControl.Type.VOLUME, MIN_VOLUME, MAX_VOLUME, 1, -1, line + .getVolume(), "pulseaudio units", "Volume Off", "Default Volume", "Full Volume"); this.line = line; this.eventLoop = eventLoop; } - + @SuppressWarnings("unused") private long streamPointer; @@ -65,8 +66,7 @@ String library = new java.io.File(".").getCanonicalPath() + java.io.File.separatorChar + System.mapLibraryName("pulse-java"); - System.out.println(PulseAudioVolumeControl.class - .getCanonicalName() + System.out.println(PulseAudioVolumeControl.class.getCanonicalName() + ": " + library); System.load(library); } catch (IOException e) { @@ -75,12 +75,11 @@ } public synchronized void setValue(float newValue) { - if (newValue > MAX_VOLUME - || newValue < MIN_VOLUME) { + if (newValue > MAX_VOLUME || newValue < MIN_VOLUME) { throw new IllegalArgumentException("invalid value"); } - - if(!line.isOpen()) { + + if (!line.isOpen()) { return; } @@ -106,7 +105,4 @@ return line.getVolume(); } - ; } - -
--- a/src/java/org/classpath/icedtea/pulseaudio/Stream.java Wed Sep 03 10:47:27 2008 -0400 +++ b/src/java/org/classpath/icedtea/pulseaudio/Stream.java Wed Sep 03 15:11:46 2008 -0400 @@ -559,7 +559,6 @@ @SuppressWarnings("unused") private void underflowCallback() { - System.out.println("DEBUG: underflowCallback called"); synchronized (underflowListeners) { for (UnderflowListener listener : underflowListeners) { listener.update();
--- a/unittests/org/classpath/icedtea/pulseaudio/PulseAudioClipTest.java Wed Sep 03 10:47:27 2008 -0400 +++ b/unittests/org/classpath/icedtea/pulseaudio/PulseAudioClipTest.java Wed Sep 03 15:11:46 2008 -0400 @@ -39,13 +39,15 @@ import java.io.File; import java.io.IOException; -import java.util.concurrent.Semaphore; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.Clip; +import javax.sound.sampled.Control; 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.UnsupportedAudioFileException; @@ -158,6 +160,81 @@ } + int opened = 0; + + @Test + public void testOpenEvent() 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); + + opened = 0; + + LineListener openListener = new LineListener() { + + @Override + public void update(LineEvent event) { + if (event.getType() == LineEvent.Type.OPEN) { + opened++; + } + } + + }; + + clip.addLineListener(openListener); + clip.open(audioInputStream); + clip.close(); + clip.removeLineListener(openListener); + + Assert.assertEquals(1, opened); + + } + + int closed = 0; + + @Test + public void testCloseEvent() throws LineUnavailableException, + UnsupportedAudioFileException, IOException { + + System.out.println("This tests the CLOSE event"); + + Clip clip = (Clip) mixer.getLine(new Line.Info(Clip.class)); + File soundFile = new File("testsounds/startup.wav"); + AudioInputStream audioInputStream = AudioSystem + .getAudioInputStream(soundFile); + + closed = 0; + + LineListener closeListener = new LineListener() { + + @Override + public void update(LineEvent event) { + if (event.getType() == LineEvent.Type.CLOSE) { + closed++; + } + } + + }; + + clip.addLineListener(closeListener); + clip.open(audioInputStream); + clip.close(); + + try { + // wait a little while before removing the listener + // to ensure it is called before we remove it + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + clip.removeLineListener(closeListener); + + Assert.assertEquals(1, closed); + + } + @Test public void testLoop0Clip() throws LineUnavailableException, IOException, UnsupportedAudioFileException { @@ -194,35 +271,64 @@ */ @Test - public void testPlayTwoClips() { - try { - Clip clip1 = (Clip) mixer.getLine(new Line.Info(Clip.class)); - File soundFile1 = new File("testsounds/startup.wav"); - AudioInputStream audioInputStream1 = AudioSystem - .getAudioInputStream(soundFile1); - clip1.open(audioInputStream1); + public void testPlayTwoClips() throws LineUnavailableException, + UnsupportedAudioFileException, IOException { + Clip clip1 = (Clip) mixer.getLine(new Line.Info(Clip.class)); + File soundFile1 = new File("testsounds/startup.wav"); + AudioInputStream audioInputStream1 = AudioSystem + .getAudioInputStream(soundFile1); + clip1.open(audioInputStream1); - Clip clip2 = (Clip) mixer.getLine(new Line.Info(Clip.class)); - File soundFile2 = new File("testsounds/logout.wav"); - AudioInputStream audioInputStream2 = AudioSystem - .getAudioInputStream(soundFile2); - clip2.open(audioInputStream2); + Clip clip2 = (Clip) mixer.getLine(new Line.Info(Clip.class)); + File soundFile2 = new File("testsounds/logout.wav"); + AudioInputStream audioInputStream2 = AudioSystem + .getAudioInputStream(soundFile2); + clip2.open(audioInputStream2); + + clip1.start(); + clip2.start(); + + clip1.drain(); + clip2.drain(); + + clip1.close(); + clip2.close(); + + } - Semaphore done = new Semaphore(0); + @Test + public void testSupportedControls() throws LineUnavailableException, + UnsupportedAudioFileException, IOException { + Clip clip = (Clip) mixer.getLine(new Line.Info(Clip.class)); + File soundFile1 = new File("testsounds/startup.wav"); + AudioInputStream audioInputStream1 = AudioSystem + .getAudioInputStream(soundFile1); + clip.open(audioInputStream1); + + Control[] controls = clip.getControls(); + Assert.assertNotNull(controls); + Assert.assertTrue(controls.length >= 2); + for (Control control : controls) { + Assert.assertNotNull(control); + } - clip1.start(); - clip2.start(); - - clip1.drain(); - clip2.drain(); - - clip1.close(); - clip2.close(); - + clip.close(); + } - } catch (Exception e) { - e.printStackTrace(); - } + @Test + public void testMixerKnowsAboutOpenClips() throws LineUnavailableException, + UnsupportedAudioFileException, IOException { + Clip clip = (Clip) mixer.getLine(new Line.Info(Clip.class)); + File soundFile1 = new File("testsounds/startup.wav"); + AudioInputStream audioInputStream1 = AudioSystem + .getAudioInputStream(soundFile1); + + Assert.assertEquals(0, mixer.getSourceLines().length); + clip.open(audioInputStream1); + Assert.assertEquals(1, mixer.getSourceLines().length); + Assert.assertEquals(clip, mixer.getSourceLines()[0]); + clip.close(); + Assert.assertEquals(0, mixer.getSourceLines().length); }
--- a/unittests/org/classpath/icedtea/pulseaudio/PulseAudioMixerRawTest.java Wed Sep 03 10:47:27 2008 -0400 +++ b/unittests/org/classpath/icedtea/pulseaudio/PulseAudioMixerRawTest.java Wed Sep 03 15:11:46 2008 -0400 @@ -33,7 +33,7 @@ this exception to your version of the library, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. -*/ + */ package org.classpath.icedtea.pulseaudio; @@ -47,24 +47,22 @@ public class PulseAudioMixerRawTest { Mixer mixer = null; - + @Before public void setUp() { mixer = PulseAudioMixer.getInstance(); } - - + @Test public void testOpen() throws LineUnavailableException { mixer.open(); - + mixer.close(); + } - - - + @After public void tearDown() { - + } - + }
--- a/unittests/org/classpath/icedtea/pulseaudio/PulseAudioMixerTest.java Wed Sep 03 10:47:27 2008 -0400 +++ b/unittests/org/classpath/icedtea/pulseaudio/PulseAudioMixerTest.java Wed Sep 03 15:11:46 2008 -0400 @@ -244,9 +244,8 @@ selectedMixer.getLine(Port.Info.MICROPHONE); } - @Test - public void testOpeningAgain() throws LineUnavailableException, - UnsupportedOperationException { + @Test(expected = IllegalStateException.class) + public void testOpeningAgain() throws LineUnavailableException { selectedMixer.open(); selectedMixer.open(); }
--- a/unittests/org/classpath/icedtea/pulseaudio/PulseAudioSourcePortTest.java Wed Sep 03 10:47:27 2008 -0400 +++ b/unittests/org/classpath/icedtea/pulseaudio/PulseAudioSourcePortTest.java Wed Sep 03 15:11:46 2008 -0400 @@ -1,26 +1,31 @@ package org.classpath.icedtea.pulseaudio; -import javax.sound.sampled.*; +import javax.sound.sampled.BooleanControl; +import javax.sound.sampled.FloatControl; +import javax.sound.sampled.Line; +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.Mixer; +import javax.sound.sampled.Port; + +import org.junit.After; import org.junit.Before; import org.junit.Test; public class PulseAudioSourcePortTest { - + Mixer mixer; - - @Before public void setUp() throws Exception { mixer = PulseAudioMixer.getInstance(); mixer.open(); } - + @Test - public void testClose() throws LineUnavailableException{ + public void testClose() throws LineUnavailableException { Line.Info[] lineInfos = mixer.getSourceLineInfo(); - for(Line.Info info : lineInfos) { + for (Line.Info info : lineInfos) { if (info.getLineClass() == Port.class) { System.out.println(info.toString()); Port port = (Port) mixer.getLine(info); @@ -28,22 +33,29 @@ } } } - - + @Test - public void testControls() throws LineUnavailableException{ + public void testControls() throws LineUnavailableException { Line.Info[] lineInfos = mixer.getSourceLineInfo(); - for(Line.Info info : lineInfos) { + for (Line.Info info : lineInfos) { if (info.getLineClass() == Port.class) { System.out.println(info.toString()); Port port = (Port) mixer.getLine(info); - FloatControl volumeControl = (FloatControl) port.getControl(FloatControl.Type.VOLUME); + FloatControl volumeControl = (FloatControl) port + .getControl(FloatControl.Type.VOLUME); volumeControl.setValue(60000); - BooleanControl muteControl = (BooleanControl) port.getControl(BooleanControl.Type.MUTE); + BooleanControl muteControl = (BooleanControl) port + .getControl(BooleanControl.Type.MUTE); muteControl.setValue(true); muteControl.setValue(false); } } } + @After + public void tearDown() { + mixer.close(); + + } + }
--- a/unittests/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLineTest.java Wed Sep 03 10:47:27 2008 -0400 +++ b/unittests/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLineTest.java Wed Sep 03 15:11:46 2008 -0400 @@ -43,6 +43,7 @@ mixer = AudioSystem.getMixer(wantedMixerInfo); Assert.assertNotNull(mixer); mixer.open(); + targetDataLine = null; } @@ -127,25 +128,36 @@ targetDataLine.removeLineListener(closeListener); } - - + @Test public void testMixerKnowsAboutOpenLines() throws LineUnavailableException { targetDataLine = (TargetDataLine) mixer.getLine(new Line.Info( TargetDataLine.class)); - + Assert.assertEquals(0, mixer.getTargetLines().length); targetDataLine.open(); Assert.assertEquals(1, mixer.getTargetLines().length); targetDataLine.close(); Assert.assertEquals(0, mixer.getTargetLines().length); - + } - + @Test public void testAllTargetLinesClosed() { Assert.assertEquals(0, mixer.getTargetLines().length); - + + } + + @Test + public void testTargetLineHasNoControls() throws LineUnavailableException { + targetDataLine = (TargetDataLine) mixer.getLine(new Line.Info( + TargetDataLine.class)); + + targetDataLine.open(); + + Assert.assertEquals(0, targetDataLine.getControls().length); + + targetDataLine.close(); } @After
--- a/unittests/org/classpath/icedtea/pulseaudio/PulseAudioTargetPortTest.java Wed Sep 03 10:47:27 2008 -0400 +++ b/unittests/org/classpath/icedtea/pulseaudio/PulseAudioTargetPortTest.java Wed Sep 03 15:11:46 2008 -0400 @@ -1,26 +1,31 @@ package org.classpath.icedtea.pulseaudio; -import javax.sound.sampled.*; +import javax.sound.sampled.BooleanControl; +import javax.sound.sampled.FloatControl; +import javax.sound.sampled.Line; +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.Mixer; +import javax.sound.sampled.Port; + +import org.junit.After; import org.junit.Before; import org.junit.Test; public class PulseAudioTargetPortTest { - + Mixer mixer; - - @Before public void setUp() throws Exception { mixer = PulseAudioMixer.getInstance(); mixer.open(); } - + @Test - public void testClose() throws LineUnavailableException{ + public void testClose() throws LineUnavailableException { Line.Info[] lineInfos = mixer.getTargetLineInfo(); - for(Line.Info info : lineInfos) { + for (Line.Info info : lineInfos) { if (info.getLineClass() == Port.class) { System.out.println(info.toString()); Port port = (Port) mixer.getLine(info); @@ -28,22 +33,28 @@ } } } - - + @Test - public void testControls() throws LineUnavailableException{ + public void testControls() throws LineUnavailableException { Line.Info[] lineInfos = mixer.getTargetLineInfo(); - for(Line.Info info : lineInfos) { + for (Line.Info info : lineInfos) { if (info.getLineClass() == Port.class) { System.out.println(info.toString()); Port port = (Port) mixer.getLine(info); - FloatControl volumeControl = (FloatControl) port.getControl(FloatControl.Type.VOLUME); + FloatControl volumeControl = (FloatControl) port + .getControl(FloatControl.Type.VOLUME); volumeControl.setValue(60000); - BooleanControl muteControl = (BooleanControl) port.getControl(BooleanControl.Type.MUTE); + BooleanControl muteControl = (BooleanControl) port + .getControl(BooleanControl.Type.MUTE); muteControl.setValue(true); muteControl.setValue(false); } } } + @After + public void tearDown() { + mixer.close(); + } + }
--- a/unittests/org/classpath/icedtea/pulseaudio/PulseSourceDataLineTest.java Wed Sep 03 10:47:27 2008 -0400 +++ b/unittests/org/classpath/icedtea/pulseaudio/PulseSourceDataLineTest.java Wed Sep 03 15:11:46 2008 -0400 @@ -301,8 +301,6 @@ public void testVolumeAndMute() throws Exception { Mixer selectedMixer = mixer; - - selectedMixer.open(); SourceDataLine line = (SourceDataLine) selectedMixer .getLine(new Line.Info(SourceDataLine.class)); @@ -346,7 +344,6 @@ Mixer selectedMixer = mixer; - selectedMixer.open(); SourceDataLine line = (SourceDataLine) selectedMixer .getLine(new Line.Info(SourceDataLine.class)); @@ -543,6 +540,7 @@ Assert.assertEquals(0, mixer.getSourceLines().length); sourceDataLine.open(); Assert.assertEquals(1, mixer.getSourceLines().length); + Assert.assertEquals(sourceDataLine, mixer.getSourceLines()[0]); sourceDataLine.close(); Assert.assertEquals(0, mixer.getSourceLines().length);