changeset 101:f489a16be6f1

ports added
author iivan@town.yyz.redhat.com
date Thu, 28 Aug 2008 15:57:05 -0400
parents 1a1a426b17cc
children cf375df7c7b7
files ChangeLog build.xml src/java/org/classpath/icedtea/pulseaudio/EventLoop.java src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java src/java/org/classpath/icedtea/pulseaudio/PulseAudioLine.java src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixer.java src/java/org/classpath/icedtea/pulseaudio/PulseAudioMuteControl.java src/java/org/classpath/icedtea/pulseaudio/PulseAudioPlaybackLine.java src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java src/java/org/classpath/icedtea/pulseaudio/PulseAudioStreamMuteControl.java src/java/org/classpath/icedtea/pulseaudio/PulseAudioStreamVolumeControl.java src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetPort.java src/java/org/classpath/icedtea/pulseaudio/PulseAudioVolumeControl.java src/java/org/classpath/icedtea/pulseaudio/Stream.java src/native/Makefile.am src/native/org_classpath_icedtea_pulseaudio_EventLoop.c src/native/org_classpath_icedtea_pulseaudio_PulseAudioStreamVolumeControl.c src/native/org_classpath_icedtea_pulseaudio_PulseAudioTargetPort.c src/native/org_classpath_icedtea_pulseaudio_Stream.c unittests/org/classpath/icedtea/pulseaudio/PulseSourceDataLineTest.java
diffstat 22 files changed, 639 insertions(+), 205 deletions(-) [+]
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;