Mercurial > hg > pulseaudio
changeset 101:f489a16be6f1
ports added
line wrap: on
line diff
--- a/ChangeLog Wed Aug 27 14:22:14 2008 -0400 +++ b/ChangeLog Thu Aug 28 15:57:05 2008 -0400 @@ -1,3 +1,12 @@ +2008-08-13 Ioana Ivan <iivan@redhat.com> + + * src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetPort.java: + new class representing a targetport + * some refactoring in the control classes (controls for the target + port are now supported) + + + 2008-08-13 Ioana Ivan <iivan@redhat.com> * src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java:
--- a/build.xml Wed Aug 27 14:22:14 2008 -0400 +++ b/build.xml Thu Aug 28 15:57:05 2008 -0400 @@ -41,6 +41,9 @@ <class name="org.classpath.icedtea.pulseaudio.PulseAudioTargetDataLine"/> <class name="org.classpath.icedtea.pulseaudio.PulseAudioStreamVolumeControl"/> <class name="org.classpath.icedtea.pulseaudio.PulseAudioDataLine"/> + <class name="org.classpath.icedtea.pulseaudio.PulseAudioSourcePort"/> + <class name="org.classpath.icedtea.pulseaudio.PulseAudioTargetPort"/> + <class name="org.classpath.icedtea.pulseaudio.PulseAudioTargetPortVolumeControl"/> </javah> </target>
--- a/src/java/org/classpath/icedtea/pulseaudio/EventLoop.java Wed Aug 27 14:22:14 2008 -0400 +++ b/src/java/org/classpath/icedtea/pulseaudio/EventLoop.java Thu Aug 28 15:57:05 2008 -0400 @@ -71,6 +71,9 @@ // private boolean eventLoopIsRunning = false; public Semaphore finished = new Semaphore(0); + + private List<String> targetPortNameList = new ArrayList(); + private List<String> sourcePortNameList = new ArrayList(); /* * JNI stuff @@ -240,5 +243,48 @@ public long getMainLoopPointer() { return mainloopPointer; } + + private native int nativeUpdateTargetPortNameList(); + private native int nativeUpdateSourcePortNameList(); + + protected synchronized List<String> updateTargetPortNameList() { + targetPortNameList = new ArrayList<String>(); + Operation op; + synchronized (this.threadLock) { + op = new Operation(nativeUpdateTargetPortNameList()); + } + + op.waitForCompletion(); + + assert (op.getState() == Operation.State.Done); + + op.releaseReference(); + return targetPortNameList; + } + + protected synchronized List<String> updateSourcePortNameList() { + sourcePortNameList = new ArrayList<String>(); + Operation op; + synchronized (this.threadLock) { + op = new Operation(nativeUpdateSourcePortNameList()); + } + + op.waitForCompletion(); + + assert (op.getState() == Operation.State.Done); + + op.releaseReference(); + return sourcePortNameList; + } + + + public void source_callback(String name ) { + sourcePortNameList.add(name); + } + + + public void sink_callback(String name ) { + targetPortNameList.add(name); + } }
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java Wed Aug 27 14:22:14 2008 -0400 +++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java Thu Aug 28 15:57:05 2008 -0400 @@ -45,17 +45,22 @@ 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; -public class PulseAudioClip extends PulseAudioDataLine implements Clip { +public class PulseAudioClip extends PulseAudioDataLine implements Clip, PulseAudioPlaybackLine { private byte[] data = null; + private boolean muted; + private float volume; + // these are frame indices. so counted from 0 private int currentFrame = 0; private int frameCount = 0; @@ -138,6 +143,8 @@ this.defaultFormat = defaultFormat; this.currentFormat = defaultFormat; clipThread = new ClipThread(); + this.volume = PulseAudioVolumeControl.MAX_VOLUME; + controls = new ArrayList<Control>(); } @@ -203,7 +210,13 @@ @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"); @@ -328,6 +341,35 @@ System.arraycopy(data, offset, this.data, 0, bufferSize); frameCount = bufferSize / format.getFrameSize(); isOpen = true; + + PulseAudioVolumeControl volumeControl = new PulseAudioVolumeControl( + this, eventLoop); + PulseAudioMuteControl muteControl = new PulseAudioMuteControl( + this, volumeControl); + controls.add(volumeControl); + controls.add(muteControl); + + } + + public int native_setVolume(float value) { + return stream.native_setVolume(value); + } + + public boolean isMuted() { + return muted; + } + + public void setMuted(boolean value) { + muted = value; + } + + public float getVolume() { + return this.volume; + } + + public void setVolume(float value) { + this.volume = value; + } @Override
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java Wed Aug 27 14:22:14 2008 -0400 +++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java Thu Aug 28 15:57:05 2008 -0400 @@ -11,7 +11,7 @@ import javax.sound.sampled.LineListener; import javax.sound.sampled.LineUnavailableException; -public abstract class PulseAudioDataLine implements DataLine { +public abstract class PulseAudioDataLine extends PulseAudioLine implements DataLine { protected static final int DEFAULT_BUFFER_SIZE = StreamBufferAttributes.SANE_DEFAULT; protected static final String PULSEAUDIO_FORMAT_KEY = "PulseAudioFormatKey"; @@ -20,7 +20,7 @@ // true between open() and close(). ie represents when a line has acquire // resources - protected boolean isOpen = false; + // true between start() and stop() protected boolean isStarted = false; @@ -36,8 +36,6 @@ protected int bufferSize = 0; - protected List<LineListener> lineListeners = new ArrayList<LineListener>();; - protected EventLoop eventLoop = null; protected Semaphore semaphore = new Semaphore(0); protected Stream stream; @@ -185,26 +183,16 @@ return isEngagedInIo; } - public void addLineListener(LineListener listener) { - this.lineListeners.add(listener); - } - public void removeLineListener(LineListener listener) { - this.lineListeners.remove(listener); - } - - private void fireLineEvent(LineEvent e) { - for (LineListener lineListener : lineListeners) { - lineListener.update(e); - } - } protected abstract void connectLine(int bufferSize); public abstract void drain(); - public boolean isOpen() { - return isOpen; + + + public Stream getStream() { + return stream; } - + }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioLine.java Thu Aug 28 15:57:05 2008 -0400 @@ -0,0 +1,43 @@ +package org.classpath.icedtea.pulseaudio; + +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 { + + protected List<LineListener> lineListeners = new ArrayList<LineListener>(); + protected boolean isOpen = false; + + + public void addLineListener(LineListener listener) { + this.lineListeners.add(listener); + } + + public void removeLineListener(LineListener listener) { + this.lineListeners.remove(listener); + } + + protected void fireLineEvent(LineEvent e) { + for (LineListener lineListener : lineListeners) { + lineListener.update(e); + } + } + + public boolean isOpen() { + return isOpen; + } + + + + + + +}
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixer.java Wed Aug 27 14:22:14 2008 -0400 +++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixer.java Thu Aug 28 15:57:05 2008 -0400 @@ -52,15 +52,18 @@ import javax.sound.sampled.Clip; import javax.sound.sampled.Control; import javax.sound.sampled.DataLine; +import javax.sound.sampled.FloatControl; 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.Port; import javax.sound.sampled.SourceDataLine; import javax.sound.sampled.TargetDataLine; import javax.sound.sampled.AudioFormat.Encoding; import javax.sound.sampled.Control.Type; +import javax.sound.sampled.Port.Info; public class PulseAudioMixer implements javax.sound.sampled.Mixer { // singleton @@ -87,6 +90,11 @@ private PulseAudioMixer() { AudioFormat[] formats = getSupportedFormats(); + List<Line.Info> sourceLineInfoList = new ArrayList<Line.Info>(); + sourceLineInfoList.add(new DataLine.Info( + SourceDataLine.class, formats, + StreamBufferAttributes.MIN_VALUE, + StreamBufferAttributes.MAX_VALUE)); sourceLineInfos = new Line.Info[] { new DataLine.Info( SourceDataLine.class, formats, StreamBufferAttributes.MIN_VALUE, @@ -327,6 +335,17 @@ clips.add((PulseAudioClip) clip); return clip; } + + if ((info.getLineClass() == Port.class) ) { + Port.Info portInfo = (Port.Info) info; + if(portInfo.isSource()){ + return new PulseAudioSourcePort(portInfo.getName(), eventLoop); + } else { + return new PulseAudioTargetPort(portInfo.getName(), eventLoop); + } + } + + throw new IllegalArgumentException(); @@ -500,6 +519,23 @@ @Override public void open() throws LineUnavailableException { openLocal(); + //the sourceLineInfo and targetLineInfo arrays need to be updated with + //port infos, which can only be obtained after EventLoop had started + + ArrayList<Line.Info> sourceLineInfoList = new ArrayList<Line.Info>(); + sourceLineInfoList.add(sourceLineInfos[0]); + for(String portName : eventLoop.updateSourcePortNameList()){ + sourceLineInfoList.add(new Port.Info(Port.class, portName, true)); + } + sourceLineInfos = sourceLineInfoList.toArray(new Line.Info[0]); + + ArrayList<Line.Info> targetLineInfoList = new ArrayList<Line.Info>(); + targetLineInfoList.add(targetLineInfos[0]); + for(String portName : eventLoop.updateTargetPortNameList()){ + targetLineInfoList.add(new Port.Info(Port.class, portName, false)); + } + targetLineInfos = targetLineInfoList.toArray(new Line.Info[0]); + } public void open(String appName, String host) throws UnknownHostException, @@ -650,16 +686,24 @@ AudioInputStream audioInputStream = AudioSystem .getAudioInputStream(soundFile); AudioFormat audioFormat = audioInputStream.getFormat(); - + + SourceDataLine line; line = (SourceDataLine) mixer.getLine(new DataLine.Info( SourceDataLine.class, audioFormat)); line.open(); - System.out.println(line.getFormat().matches(audioFormat)); + 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()); mixer.close(); - + } + void addSourceDataLine(PulseAudioSourceDataLine line) { sourceLines.add(line);
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioMuteControl.java Wed Aug 27 14:22:14 2008 -0400 +++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioMuteControl.java Thu Aug 28 15:57:05 2008 -0400 @@ -39,10 +39,40 @@ import javax.sound.sampled.BooleanControl; -abstract class PulseAudioMuteControl extends BooleanControl { +class PulseAudioMuteControl extends BooleanControl { + + private PulseAudioVolumeControl volumeControl; + private PulseAudioPlaybackLine line; + + protected PulseAudioMuteControl(PulseAudioPlaybackLine line, PulseAudioVolumeControl volumeControl) { + super(BooleanControl.Type.MUTE, false, "Volume muted", "Volume on"); + this.volumeControl = volumeControl; + this.line = line; + } + + + + - protected PulseAudioMuteControl() { - super(BooleanControl.Type.MUTE, false, "Volume muted", "Volume on"); + public synchronized void setValue(boolean value) { + if(!line.isOpen()) { + return; + } + + if (value == true) { + line.setMuted(true); + volumeControl.setStreamVolume(0); + } else { + line.setMuted(false); + float newValue = volumeControl.getValue(); + volumeControl.setStreamVolume(newValue); + } + } + + public synchronized boolean getValue() { + return line.isMuted(); + } + } -} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioPlaybackLine.java Thu Aug 28 15:57:05 2008 -0400 @@ -0,0 +1,17 @@ +package org.classpath.icedtea.pulseaudio; + +interface PulseAudioPlaybackLine { + + public int native_setVolume(float value); + + boolean isMuted(); + + void setMuted(boolean mute); + + float getVolume(); + + void setVolume(float volume); + + boolean isOpen(); + +}
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java Wed Aug 27 14:22:14 2008 -0400 +++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java Thu Aug 28 15:57:05 2008 -0400 @@ -45,15 +45,16 @@ import javax.sound.sampled.DataLine; import javax.sound.sampled.LineListener; import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.Port; import javax.sound.sampled.SourceDataLine; import javax.sound.sampled.Control.Type; public class PulseAudioSourceDataLine extends PulseAudioDataLine implements - SourceDataLine { + SourceDataLine, PulseAudioPlaybackLine{ private Control[] controls = null; - private PulseAudioStreamMuteControl muteControl; - private PulseAudioStreamVolumeControl volumeControl; + private PulseAudioMuteControl muteControl; + private PulseAudioVolumeControl volumeControl; private boolean muted; private float volume; @@ -65,44 +66,49 @@ this.supportedFormats = formats; this.eventLoop = eventLoop; this.lineListeners = new ArrayList<LineListener>(); - this.volume = PulseAudioVolumeControl.MAX_VOLUME; this.defaultFormat = defaultFormat; this.currentFormat = defaultFormat; - - } - - protected boolean isMuted() { - return muted; + this.volume = PulseAudioVolumeControl.MAX_VOLUME; } - 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 { super.open(format, bufferSize); controls = new Control[2]; - volumeControl = new PulseAudioStreamVolumeControl(this); + volumeControl = new PulseAudioVolumeControl(this, eventLoop); controls[0] = volumeControl; - muteControl = new PulseAudioStreamMuteControl(this); + muteControl = new PulseAudioMuteControl(this, volumeControl); controls[1] = muteControl; - + PulseAudioMixer parentMixer = PulseAudioMixer.getInstance(); parentMixer.addSourceDataLine(this); System.out.println("PulseAudioSourceDataLine: adding to mixer"); } + + public int native_setVolume(float value) { + return stream.native_setVolume(value); + } + + public boolean isMuted() { + return muted; + } + + public void setMuted(boolean value) { + muted = value; + } + + public float getVolume() { + return this.volume; + } + + public void setVolume(float value) { + this.volume = value; + + } protected void connectLine(int bufferSize) { StreamBufferAttributes bufferAttributes = new StreamBufferAttributes( @@ -115,6 +121,7 @@ @Override public int write(byte[] data, int offset, int length) { + if (!isStarted) { throw new IllegalStateException("must call start() before write()"); } @@ -271,14 +278,6 @@ } - protected EventLoop getEventLoop() { - return this.eventLoop; - } - - public Stream getStream() { - return stream; - } - @Override public void close() { PulseAudioMixer parent = PulseAudioMixer.getInstance(); @@ -287,5 +286,7 @@ super.close(); } + + }
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioStreamMuteControl.java Wed Aug 27 14:22:14 2008 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,68 +0,0 @@ -/* PulseAudioStreamMuteControl.java - Copyright (C) 2008 Red Hat, Inc. - -This file is part of IcedTea. - -IcedTea is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License as published by -the Free Software Foundation, version 2. - -IcedTea is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. - -You should have received a copy of the GNU General Public License -along with IcedTea; see the file COPYING. If not, write to -the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -02110-1301 USA. - -Linking this library statically or dynamically with other modules is -making a combined work based on this library. Thus, the terms and -conditions of the GNU General Public License cover the whole -combination. - -As a special exception, the copyright holders of this library give you -permission to link this library with independent modules to produce an -executable, regardless of the license terms of these independent -modules, and to copy and distribute the resulting executable under -terms of your choice, provided that you also meet, for each linked -independent module, the terms and conditions of the license of that -module. An independent module is a module which is not derived from -or based on this library. If you modify this library, you may extend -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; - -import javax.sound.sampled.FloatControl; - -public class PulseAudioStreamMuteControl extends PulseAudioMuteControl { - - private PulseAudioStreamVolumeControl volumeControl; - private PulseAudioSourceDataLine line; - - public PulseAudioStreamMuteControl(PulseAudioSourceDataLine line) { - this.volumeControl = (PulseAudioStreamVolumeControl) line - .getControl(FloatControl.Type.VOLUME); - this.line = line; - } - - public synchronized void setValue(boolean value) { - if (value == true) { - line.setMuted(true); - volumeControl.setStreamVolume(0); - } else { - line.setMuted(false); - float newValue = volumeControl.getValue(); - volumeControl.setStreamVolume(newValue); - } - } - - public synchronized boolean getValue() { - return line.isMuted(); - } - -}
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioStreamVolumeControl.java Wed Aug 27 14:22:14 2008 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -package org.classpath.icedtea.pulseaudio; - -import java.io.IOException; - -import org.classpath.icedtea.pulseaudio.EventLoop; -import org.classpath.icedtea.pulseaudio.PulseAudioVolumeControl; - -public class PulseAudioStreamVolumeControl extends PulseAudioVolumeControl { - - @SuppressWarnings("unused") - private long streamPointer; - - private EventLoop eventLoop; - private PulseAudioSourceDataLine line; - - static { - try { - String library = new java.io.File(".").getCanonicalPath() - + java.io.File.separatorChar - + System.mapLibraryName("pulse-java"); - System.out.println(PulseAudioStreamVolumeControl.class - .getCanonicalName() - + ": " + library); - System.load(library); - } catch (IOException e) { - assert ("Loading failed".endsWith("library")); - } - } - - protected PulseAudioStreamVolumeControl(PulseAudioSourceDataLine line) { - super(line); - this.streamPointer = line.getStream().getStreamPointer(); - this.eventLoop = line.getEventLoop(); - this.line = line; - } - - public synchronized void setValue(float newValue) { - if (newValue > PulseAudioVolumeControl.MAX_VOLUME - || newValue < PulseAudioStreamVolumeControl.MIN_VOLUME) { - throw new IllegalArgumentException("invalid value"); - } - - if (!line.isMuted()) { - setStreamVolume(newValue); - } - - line.setVolume(newValue); - } - - protected synchronized void setStreamVolume(float newValue) { - Operation op; - synchronized (eventLoop.threadLock) { - op = new Operation(native_setValue(newValue)); - } - - op.waitForCompletion(); - op.releaseReference(); - - } - - public synchronized float getValue() { - return line.getVolume(); - } - - public native int native_setValue(float newValue); -}
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java Wed Aug 27 14:22:14 2008 -0400 +++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java Thu Aug 28 15:57:05 2008 -0400 @@ -206,5 +206,7 @@ public boolean isControlSupported(Type control) { return false; } + + } \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetPort.java Thu Aug 28 15:57:05 2008 -0400 @@ -0,0 +1,140 @@ +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 class PulseAudioTargetPort extends PulseAudioLine implements Port, PulseAudioPlaybackLine { + + private String name; + private long contextPointer; + private EventLoop eventLoop; + private int channels; + private float volume; + private boolean muted; + private Control[] controls = null; + private PulseAudioMuteControl muteControl; + private PulseAudioVolumeControl volumeControl; + + + public PulseAudioTargetPort(String name, EventLoop eventLoop) { + this.name = name; + this.contextPointer = eventLoop.getContextPointer(); + this.eventLoop = eventLoop; + updateVolumeInfo(); + + controls = new Control[2]; + volumeControl = new PulseAudioVolumeControl(this, eventLoop); + controls[0] = volumeControl; + muteControl = new PulseAudioMuteControl(this, volumeControl); + controls[1] = muteControl; + isOpen = true; + + System.out.println("Opened Target Port " + name); + } + + public native int native_setVolume(float newValue); + + public boolean isMuted() { + return muted; + } + + public void setMuted(boolean value) { + muted = value; + } + + public float getVolume() { + return this.volume; + } + + public void setVolume(float value) { + this.volume = value; + + } + + public synchronized native int native_updateVolumeInfo(); + + public synchronized void updateVolumeInfo() { + Operation op; + synchronized (eventLoop.threadLock) { + op = new Operation(native_updateVolumeInfo()); + } + + op.waitForCompletion(); + op.releaseReference(); + } + + public void update_channels_and_volume(int channels, float volume) { + this.channels = channels; + this.volume = volume; + } + + + @Override + public void close() { + native_setVolume((float)0); + isOpen = false; + fireLineEvent(new LineEvent(this,LineEvent.Type.CLOSE, 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 javax.sound.sampled.Line.Info getLineInfo() { + return new Port.Info(Port.class, name, false); + } + + 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); + isOpen = true; + fireLineEvent(new LineEvent(this,LineEvent.Type.OPEN, AudioSystem.NOT_SPECIFIED)); + } + + + + + + +} + + + +
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioVolumeControl.java Wed Aug 27 14:22:14 2008 -0400 +++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioVolumeControl.java Thu Aug 28 15:57:05 2008 -0400 @@ -37,17 +37,76 @@ package org.classpath.icedtea.pulseaudio; +import java.io.IOException; + import javax.sound.sampled.FloatControl; -abstract class PulseAudioVolumeControl extends FloatControl { +class PulseAudioVolumeControl extends FloatControl { public static final int MAX_VOLUME = 65536; public static final int MIN_VOLUME = 0; - protected PulseAudioVolumeControl(PulseAudioSourceDataLine line) { - 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; + + private EventLoop eventLoop; + private PulseAudioPlaybackLine line; + + static { + try { + String library = new java.io.File(".").getCanonicalPath() + + java.io.File.separatorChar + + System.mapLibraryName("pulse-java"); + System.out.println(PulseAudioVolumeControl.class + .getCanonicalName() + + ": " + library); + System.load(library); + } catch (IOException e) { + assert ("Loading failed".endsWith("library")); + } } + public synchronized void setValue(float newValue) { + if (newValue > MAX_VOLUME + || newValue < MIN_VOLUME) { + throw new IllegalArgumentException("invalid value"); + } + + if(!line.isOpen()) { + return; + } + + if (!line.isMuted()) { + setStreamVolume(newValue); + } + + line.setVolume(newValue); + } + + protected synchronized void setStreamVolume(float newValue) { + Operation op; + synchronized (eventLoop.threadLock) { + op = new Operation(line.native_setVolume(newValue)); + } + + op.waitForCompletion(); + op.releaseReference(); + + } + + public synchronized float getValue() { + return line.getVolume(); + } + + ; } + +
--- a/src/java/org/classpath/icedtea/pulseaudio/Stream.java Wed Aug 27 14:22:14 2008 -0400 +++ b/src/java/org/classpath/icedtea/pulseaudio/Stream.java Thu Aug 28 15:57:05 2008 -0400 @@ -58,6 +58,8 @@ public static final String DEFAULT_DEVICE = null; + + @SuppressWarnings("unused") private long streamPointer; @@ -175,6 +177,9 @@ native long native_pa_stream_set_buffer_attr(StreamBufferAttributes info); private native long native_pa_stream_update_sample_rate(int rate); + + public native int native_setVolume(float newValue); + /* * pa_operation pa_stream_proplist_update (pa_stream *s, pa_update_mode_t @@ -206,6 +211,7 @@ this.format = format; + StreamSampleSpecification spec = new StreamSampleSpecification(format, sampleRate, channels);
--- a/src/native/Makefile.am Wed Aug 27 14:22:14 2008 -0400 +++ b/src/native/Makefile.am Thu Aug 28 15:57:05 2008 -0400 @@ -5,12 +5,17 @@ jni-common.h \ org_classpath_icedtea_pulseaudio_EventLoop.c \ org_classpath_icedtea_pulseaudio_EventLoop.h \ - org_classpath_icedtea_pulseaudio_PulseAudioStreamVolumeControl.c \ - org_classpath_icedtea_pulseaudio_PulseAudioStreamVolumeControl.h \ org_classpath_icedtea_pulseaudio_Operation.h \ org_classpath_icedtea_pulseaudio_Operation.c \ org_classpath_icedtea_pulseaudio_Stream.c \ - org_classpath_icedtea_pulseaudio_Stream.h + org_classpath_icedtea_pulseaudio_Stream.h \ + org_classpath_icedtea_pulseaudio_PulseAudioSourcePort.c \ + org_classpath_icedtea_pulseaudio_PulseAudioSourcePort.h \ + org_classpath_icedtea_pulseaudio_PulseAudioTargetPort.c \ + org_classpath_icedtea_pulseaudio_PulseAudioTargetPort.h + + + AM_CFLAGS = -g -Wall -Werror $(PLATFORM_FLAGS) $(LIBPULSE_CFLAGS) AM_LDFLAGS = -g -Wall -Werror $(LIBPULSE_LIBS)
--- a/src/native/org_classpath_icedtea_pulseaudio_EventLoop.c Wed Aug 27 14:22:14 2008 -0400 +++ b/src/native/org_classpath_icedtea_pulseaudio_EventLoop.c Thu Aug 28 15:57:05 2008 -0400 @@ -49,6 +49,32 @@ JNIEnv* pulse_thread_env = NULL; +void sink_list_success_cb(pa_context *context, const pa_sink_info *i, int eol, void *userdata) { + + if (eol == 0) { + jclass cls = (*pulse_thread_env)->GetObjectClass(pulse_thread_env, java_context->obj); + jstring name = (*pulse_thread_env)->NewStringUTF(pulse_thread_env, i->name); + jmethodID mid1 = (*pulse_thread_env)->GetMethodID(pulse_thread_env, cls, "sink_callback", "(Ljava/lang/String;)V"); + (*pulse_thread_env)->CallVoidMethod(pulse_thread_env, java_context->obj, mid1, name) ; + } else { + notifyWaitingOperations(pulse_thread_env); + } + +} + +void source_list_success_cb(pa_context *context, const pa_source_info *i, int eol, void *userdata) { + + if (eol == 0) { + jclass cls = (*pulse_thread_env)->GetObjectClass(pulse_thread_env, java_context->obj); + jstring name = (*pulse_thread_env)->NewStringUTF(pulse_thread_env, i->name); + jmethodID mid1 = (*pulse_thread_env)->GetMethodID(pulse_thread_env, cls, "source_callback", "(Ljava/lang/String;)V"); + (*pulse_thread_env)->CallVoidMethod(pulse_thread_env, java_context->obj, mid1, name) ; + } else { + notifyWaitingOperations(pulse_thread_env); + } + +} + static void context_change_callback(pa_context* context, void* userdata) { assert(context); assert(userdata == NULL); @@ -187,6 +213,18 @@ } +JNIEXPORT jint JNICALL Java_org_classpath_icedtea_pulseaudio_EventLoop_nativeUpdateTargetPortNameList(JNIEnv *env, jobject obj) { + pa_context* context = (pa_context*) getJavaPointer(env, obj, "contextPointer"); + pa_operation *o = pa_context_get_sink_info_list(context, sink_list_success_cb, NULL); + return (jint) o; + } + + JNIEXPORT jint JNICALL Java_org_classpath_icedtea_pulseaudio_EventLoop_nativeUpdateSourcePortNameList(JNIEnv *env, jobject obj) { + pa_context* context = (pa_context*) getJavaPointer(env, obj, "contextPointer"); + pa_operation *o = pa_context_get_source_info_list(context, source_list_success_cb, NULL); + return (jint) o; + } + static void context_drain_complete_callback(pa_context* context, void* userdata) { pa_context_disconnect(context);
--- a/src/native/org_classpath_icedtea_pulseaudio_PulseAudioStreamVolumeControl.c Wed Aug 27 14:22:14 2008 -0400 +++ b/src/native/org_classpath_icedtea_pulseaudio_PulseAudioStreamVolumeControl.c Thu Aug 28 15:57:05 2008 -0400 @@ -41,6 +41,7 @@ #include "jni-common.h" #include "org_classpath_icedtea_pulseaudio_PulseAudioStreamVolumeControl.h" + extern JNIEnv* pulse_thread_env; static void set_sink_input_volume_callback(pa_context* context, int success,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/native/org_classpath_icedtea_pulseaudio_PulseAudioTargetPort.c Thu Aug 28 15:57:05 2008 -0400 @@ -0,0 +1,77 @@ +#include "org_classpath_icedtea_pulseaudio_PulseAudioTargetPort.h" + +#include "jni-common.h" +#include <pulse/pulseaudio.h> +#include <string.h> + +typedef struct java_context { + JNIEnv* env; + jobject obj; +} java_context; + +extern JNIEnv* pulse_thread_env; + +void sink_callback(pa_context *context, int success, void *userdata) { + notifyWaitingOperations(pulse_thread_env); +} + +void get_sink_volume_callback(pa_context *context, const pa_sink_info *i, int eol, void *userdata) { + if(eol == 0) { + printf("%s\n", i->name); + jobject obj = (jobject) userdata; + jclass cls = (*pulse_thread_env)->GetObjectClass(pulse_thread_env, obj); + jmethodID mid1 = (*pulse_thread_env)->GetMethodID(pulse_thread_env, cls, "update_channels_and_volume", "(IF)V"); + (*pulse_thread_env)->CallVoidMethod(pulse_thread_env, obj, mid1, (int) (i->volume).channels, (float) (i->volume).values[0]) ; + } else { + notifyWaitingOperations(pulse_thread_env); + } + + +} + + + +JNIEXPORT jint JNICALL Java_org_classpath_icedtea_pulseaudio_PulseAudioTargetPort_nativeOpen(JNIEnv *env, jobject obj) { + jclass cls = (*env)->GetObjectClass(env, obj); + jfieldID fid = (*env)->GetFieldID(env, cls, "name", "Ljava/lang/String;"); + jstring jstr = (*env)->GetObjectField(env, obj, fid); + const char *name = (*env)->GetStringUTFChars(env, jstr, NULL); + pa_context* context = (pa_context*) getJavaPointer(env, obj, "contextPointer"); + pa_operation *o= pa_context_suspend_sink_by_name(context, (char *) name, 0, sink_callback, NULL); + return (jint) o; +} + +JNIEXPORT jint JNICALL Java_org_classpath_icedtea_pulseaudio_PulseAudioTargetPort_nativeClose(JNIEnv *env, jobject obj) { + jclass cls = (*env)->GetObjectClass(env, obj); + jfieldID fid = (*env)->GetFieldID(env, cls, "name", "Ljava/lang/String;"); + jstring jstr = (*env)->GetObjectField(env, obj, fid); + const char *name = (*env)->GetStringUTFChars(env, jstr, NULL); + pa_context* context = (pa_context*) getJavaPointer(env, obj, "contextPointer"); + pa_operation *o= pa_context_suspend_sink_by_name(context, (char *) name, 1, sink_callback, NULL); + return (jint) o; +} + +JNIEXPORT jint JNICALL Java_org_classpath_icedtea_pulseaudio_PulseAudioTargetPort_native_1updateVolumeInfo(JNIEnv *env, jobject obj) { + jclass cls = (*env)->GetObjectClass(env, obj); + jfieldID fid = (*env)->GetFieldID(env, cls, "name", "Ljava/lang/String;"); + jstring jstr = (*env)->GetObjectField(env, obj, fid); + const char *name = (*env)->GetStringUTFChars(env, jstr, NULL); + pa_context* context = (pa_context*) getJavaPointer(env, obj, "contextPointer"); + obj = (*env)->NewGlobalRef(env, obj); + pa_operation *o = pa_context_get_sink_info_by_name (context, (char*) name, get_sink_volume_callback, obj); + return (jint) o; +} + +JNIEXPORT jint JNICALL Java_org_classpath_icedtea_pulseaudio_PulseAudioTargetPort_native_1setVolume(JNIEnv *env, jobject obj, jfloat value) { + jclass cls = (*env)->GetObjectClass(env, obj); + jfieldID fid = (*env)->GetFieldID(env, cls, "name", "Ljava/lang/String;"); + jstring jstr = (*env)->GetObjectField(env, obj, fid); + const char *name = (*env)->GetStringUTFChars(env, jstr, NULL); + pa_context* context = (pa_context*) getJavaPointer(env, obj, "contextPointer"); + obj = (*env)->NewGlobalRef(env, obj); + fid = (*env)->GetFieldID(env, cls, "channels", "I"); + jint channels = (*env)->GetIntField(env, obj, fid); + pa_cvolume cv; + pa_operation *o = pa_context_set_sink_volume_by_name (context, (char*) name,pa_cvolume_set(&cv, channels, value), sink_callback, obj); + return (jint) o; +}
--- a/src/native/org_classpath_icedtea_pulseaudio_Stream.c Wed Aug 27 14:22:14 2008 -0400 +++ b/src/native/org_classpath_icedtea_pulseaudio_Stream.c Thu Aug 28 15:57:05 2008 -0400 @@ -11,6 +11,13 @@ extern JNIEnv* pulse_thread_env; +static void set_sink_input_volume_callback(pa_context* context, int success,void* userdata) { + notifyWaitingOperations(pulse_thread_env); + +} + + + const char* getStringFromFormat(pa_sample_format_t format) { const char* value; @@ -852,3 +859,13 @@ } +JNIEXPORT jint JNICALL Java_org_classpath_icedtea_pulseaudio_Stream_native_1setVolume(JNIEnv *env, jobject obj, jfloat new_volume) { + pa_stream *stream = getJavaPointer(env, obj, "streamPointer"); + pa_context *context = pa_stream_get_context(stream); + int stream_id = pa_stream_get_index(stream); + int channels = pa_stream_get_sample_spec(stream)->channels; + pa_cvolume cv; + return (jint) pa_context_set_sink_input_volume(context, stream_id, pa_cvolume_set(&cv, channels, new_volume), set_sink_input_volume_callback, NULL); + +} +
--- a/unittests/org/classpath/icedtea/pulseaudio/PulseSourceDataLineTest.java Wed Aug 27 14:22:14 2008 -0400 +++ b/unittests/org/classpath/icedtea/pulseaudio/PulseSourceDataLineTest.java Thu Aug 28 15:57:05 2008 -0400 @@ -249,13 +249,13 @@ line.open(audioFormat); line.start(); - PulseAudioStreamVolumeControl volume = (PulseAudioStreamVolumeControl) line + PulseAudioVolumeControl volume = (PulseAudioVolumeControl) line .getControl(FloatControl.Type.VOLUME); - PulseAudioStreamMuteControl mute = (PulseAudioStreamMuteControl) line + PulseAudioMuteControl mute = (PulseAudioMuteControl) line .getControl(BooleanControl.Type.MUTE); mute.setValue(true); - volume.setValue(PulseAudioStreamVolumeControl.MAX_VOLUME); + volume.setValue(PulseAudioVolumeControl.MAX_VOLUME); mute.setValue(false); @@ -293,10 +293,10 @@ line.open(audioFormat); line.start(); - PulseAudioStreamVolumeControl volume = (PulseAudioStreamVolumeControl) line + PulseAudioVolumeControl volume = (PulseAudioVolumeControl) line .getControl(FloatControl.Type.VOLUME); - volume.setValue(PulseAudioStreamVolumeControl.MIN_VOLUME); + volume.setValue(PulseAudioVolumeControl.MIN_VOLUME); byte[] abData = new byte[1000]; int bytesRead = 0;