changeset 78:3cf90f7c6931

2008-08-15 Omair Majid <omajid@redhat.com> * build.xml: added PulseAudioTargetDataLine as a javah target * src/java/org/classpath/icedtea/pulseaudio/ContextEvent.java: spacing changes (modified to fit with eclipse's guidelines * src/java/org/classpath/icedtea/pulseaudio/ContextListener.java: same * src/java/org/classpath/icedtea/pulseaudio/EventLoop.java: same * src/java/org/classpath/icedtea/pulseaudio/Operaiton.java: same * src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java: same * src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixer.java: merged * src/java/org/classpath/icedtea/pulseausio/PulseAUdioMixerInfo.java: spacing changes * src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixerProvider.java: same * src/java/org/classpath/icedtea/pulseaudio/PulseAudioMuteControl.java: same * src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java: merged file * src/java/org/classpath/icedtea/pulseaudio/PulseAudioStreamMuteControl.java: spacing changes * src/java/org/classpath/icedtea/pulseaudio/PulseAudioStreamVolumeControl.java: same * src/java/org/classpath/icedtea/pulseaudio/PulseAudioVolumeContro.java: same * src/java/org/classpath/icedtea/pulseaudio/PulseAudioRecorder.java: merged file * src/native/Makefile.am: merged file
author Omair Majid <omajid@redhat.com>
date Fri, 15 Aug 2008 12:03:05 -0400
parents 2e3dd470d097 (current diff) 20b37b004b8e (diff)
children 17b5e29d9a21
files build.xml src/java/org/classpath/icedtea/pulseaudio/ContextEvent.java src/java/org/classpath/icedtea/pulseaudio/ContextListener.java src/java/org/classpath/icedtea/pulseaudio/EventLoop.java src/java/org/classpath/icedtea/pulseaudio/Operation.java src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixer.java src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixerInfo.java src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixerProvider.java src/java/org/classpath/icedtea/pulseaudio/PulseAudioMuteControl.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/PulseAudioVolumeControl.java src/java/org/classpath/icedtea/pulseaudio/SimpleAudioRecorder.java src/native/Makefile.am
diffstat 20 files changed, 918 insertions(+), 558 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Fri Aug 15 10:51:54 2008 -0400
+++ b/ChangeLog	Fri Aug 15 12:03:05 2008 -0400
@@ -1,3 +1,24 @@
+2008-08-13 Ioana Ivan  <iivan@redhat.com>
+
+        * src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixer.java:
+	modified getLine() to allow us to obtain a TargetDataLine
+	* src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java: 
+	added open(), read() and close()
+	* src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java
+	marged the streamListeners into a single listener
+
+
+
+2008-08-13 Ioana Ivan  <iivan@redhat.com>
+
+        * src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixer.java: the
+	list of formats recognized by PulseAudio is being set here, so it can
+	be used by all DataLines. Also made some changes to get*LineInfo() and
+	isLineSupported()
+	* src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java:
+	changed the constructor 
+
+
 2008-08-11  Joshua Sumali  <jsumali@redhat.com>
 
 	* configure.ac: Add check for libpulse.
--- a/build.xml	Fri Aug 15 10:51:54 2008 -0400
+++ b/build.xml	Fri Aug 15 12:03:05 2008 -0400
@@ -38,6 +38,7 @@
 			<class name="org.classpath.icedtea.pulseaudio.EventLoop"/>
 			<class name="org.classpath.icedtea.pulseaudio.Operation"/>
 			<class name="org.classpath.icedtea.pulseaudio.Stream"/>
+			<class name="org.classpath.icedtea.pulseaudio.PulseAudioTargetDataLine"/>
 			<class name="org.classpath.icedtea.pulseaudio.PulseAudioStreamVolumeControl"/>
 		</javah>
 	</target>
--- a/src/java/org/classpath/icedtea/pulseaudio/ContextEvent.java	Fri Aug 15 10:51:54 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/ContextEvent.java	Fri Aug 15 12:03:05 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;
 
--- a/src/java/org/classpath/icedtea/pulseaudio/ContextListener.java	Fri Aug 15 10:51:54 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/ContextListener.java	Fri Aug 15 12:03:05 2008 -0400
@@ -33,12 +33,12 @@
 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;
 
 public interface ContextListener {
 
 	public void update(ContextEvent e);
-	
+
 }
--- a/src/java/org/classpath/icedtea/pulseaudio/EventLoop.java	Fri Aug 15 10:51:54 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/EventLoop.java	Fri Aug 15 12:03:05 2008 -0400
@@ -105,8 +105,8 @@
 	static {
 		try {
 			String library = new java.io.File(".").getCanonicalPath()
-					+ java.io.File.separatorChar
-					+ System.mapLibraryName("pulse-java");
+
+			+ java.io.File.separatorChar + System.mapLibraryName("pulse-java");
 			System.out.println(library);
 			System.load(library);
 		} catch (IOException e) {
@@ -154,12 +154,12 @@
 					native_shutdown();
 					// System.out.println(this.getClass().getName()
 					// + ": shutting down");
-					
+
 					// clean up the listeners
 					synchronized (contextListeners) {
 						contextListeners.clear();
 					}
-					
+
 					return;
 
 				}
@@ -216,11 +216,11 @@
 	}
 
 	private void fireEvent(final ContextEvent e) {
-//		System.out.println(this.getClass().getName() + "firing event: "
-//				+ e.getType().toString());
+		// System.out.println(this.getClass().getName() + "firing event: "
+		// + e.getType().toString());
 
 		synchronized (contextListeners) {
-//			System.out.println(contextListeners.size());
+			// System.out.println(contextListeners.size());
 			for (ContextListener listener : contextListeners) {
 				listener.update(e);
 			}
--- a/src/java/org/classpath/icedtea/pulseaudio/Operation.java	Fri Aug 15 10:51:54 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/Operation.java	Fri Aug 15 12:03:05 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;
 
@@ -43,7 +43,8 @@
  * Encapsulates a pa_operation object
  * 
  * 
- * This is really needed only so that we can deallocate the reference counted object
+ * This is really needed only so that we can deallocate the reference counted
+ * object
  * 
  * 
  */
@@ -100,9 +101,9 @@
 		}
 		return false;
 	}
-	
+
 	public State getState() {
-		assert (operationPointer != 0); 
+		assert (operationPointer != 0);
 		int state;
 		synchronized (eventLoop.threadLock) {
 			state = native_get_state();
@@ -120,11 +121,11 @@
 
 	}
 
-	public void waitForCompletion()  throws InterruptedException {
-		
+	public void waitForCompletion() throws InterruptedException {
+
 		synchronized (eventLoop.threadLock) {
 			eventLoop.threadLock.wait();
 		}
-		
+
 	}
 }
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java	Fri Aug 15 10:51:54 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java	Fri Aug 15 12:03:05 2008 -0400
@@ -234,8 +234,7 @@
 	@Override
 	public void open(AudioFormat format, byte[] data, int offset, int bufferSize)
 			throws LineUnavailableException {
-		
-		
+
 		long contextPointer = EventLoop.getEventLoop().getContextPointer();
 		int channels = 2;
 		int sampleRate = 22050;
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixer.java	Fri Aug 15 10:51:54 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixer.java	Fri Aug 15 12:03:05 2008 -0400
@@ -41,7 +41,10 @@
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.Semaphore;
 
 import javax.sound.sampled.AudioFormat;
@@ -57,6 +60,8 @@
 import javax.sound.sampled.LineUnavailableException;
 import javax.sound.sampled.Mixer;
 import javax.sound.sampled.SourceDataLine;
+import javax.sound.sampled.TargetDataLine;
+import javax.sound.sampled.AudioFormat.Encoding;
 import javax.sound.sampled.Control.Type;
 
 public class PulseAudioMixer implements javax.sound.sampled.Mixer {
@@ -65,16 +70,20 @@
 	public EventLoop eventLoop;
 	public Thread eventLoopThread;
 
+	private Line.Info[] sourceLineInfos;
+	private Line.Info[] targetLineInfos;
+
 	private static PulseAudioMixer _instance = null;
 
 	private static final String DEFAULT_APP_NAME = "Java App";
+	private static final String PULSEAUDIO_FORMAT_KEY = "PulseAudioFormatKey";
 
 	private boolean isOpen = false;
 
 	private List<PulseAudioSourceDataLine> sourceLines = new ArrayList<PulseAudioSourceDataLine>();
 	// private List<PulseAudioTargetDataLine> targetLines = null;
 	private List<PulseAudioClip> clips = new ArrayList<PulseAudioClip>();
-	
+
 	// private Line.Info targetDataLineInfo = new
 	// Line.Info(PulseAudioTargetDataLine.class);
 
@@ -83,6 +92,13 @@
 	private PulseAudioMixer() {
 		lineListeners = new ArrayList<LineListener>();
 		sourceLines = new ArrayList<PulseAudioSourceDataLine>();
+		AudioFormat[] formats = getSupportedFormats();
+		sourceLineInfos = new Line.Info[] { new DataLine.Info(
+				SourceDataLine.class, formats, AudioSystem.NOT_SPECIFIED,
+				AudioSystem.NOT_SPECIFIED) };
+		targetLineInfos = new Line.Info[] { new DataLine.Info(
+				TargetDataLine.class, formats, AudioSystem.NOT_SPECIFIED,
+				AudioSystem.NOT_SPECIFIED) };
 		// _targetLines = new ArrayList<PulseAudioTargetDataLine>();
 	}
 
@@ -93,6 +109,173 @@
 		return _instance;
 	}
 
+	private AudioFormat[] getSupportedFormats() {
+
+		List<AudioFormat> supportedFormats = new ArrayList<AudioFormat>();
+
+		Map<String, Object> properties;
+
+		int[] channelSizes = new int[] { 1, 2, 5 };
+		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
+					sampleSize, // sample size
+					channelSize, // channels
+					sampleSize / 8 * channelSize, // frame size in bytes
+					AudioSystem.NOT_SPECIFIED, // frame rate
+					false, // big endian?
+					properties);
+
+			supportedFormats.add(PA_SAMPLE_U8);
+		}
+
+		for (int channelSize : channelSizes) {
+			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
+					sampleSize, // sample size
+					channelSize, // channels
+					sampleSize / 8 * channelSize, // frame size
+					AudioSystem.NOT_SPECIFIED, // frame rate
+					false, // big endian?
+					properties);
+
+			supportedFormats.add(PA_SAMPLE_ALAW);
+		}
+
+		for (int channelSize : channelSizes) {
+			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
+					sampleSize, // sample size
+					channelSize, // channels
+					sampleSize / 8 * channelSize, // frame size
+					AudioSystem.NOT_SPECIFIED, // frame rate
+					false, // big endian?
+					properties);
+
+			supportedFormats.add(PA_SAMPLE_ULAW);
+		}
+
+		for (int channelSize : channelSizes) {
+			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
+					AudioSystem.NOT_SPECIFIED, // sample rate
+					sampleSize, // sample size
+					channelSize, // channels
+					sampleSize / 8 * channelSize, // frame size
+					AudioSystem.NOT_SPECIFIED, // frame rate
+					true, // big endian?
+					properties);
+
+			supportedFormats.add(PA_SAMPLE_S16BE);
+		}
+
+		for (int channelSize : channelSizes) {
+			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
+					AudioSystem.NOT_SPECIFIED, // sample rate
+					sampleSize, // sample size
+					channelSize, // channels
+					sampleSize / 8 * channelSize, // frame size
+					AudioSystem.NOT_SPECIFIED, // frame rate
+					false, // big endian?
+					properties);
+
+			supportedFormats.add(A_SAMPLE_S16LE);
+		}
+
+		for (int channelSize : channelSizes) {
+			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
+					AudioSystem.NOT_SPECIFIED, // sample rate
+					sampleSize, // sample size
+					channelSize, // channels
+					sampleSize / 8 * channelSize, // frame size
+					AudioSystem.NOT_SPECIFIED, // frame rate
+					true, // big endian?
+					properties);
+
+			supportedFormats.add(PA_SAMPLE_S32BE);
+		}
+
+		for (int channelSize : channelSizes) {
+			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
+					AudioSystem.NOT_SPECIFIED, // sample rate
+					sampleSize, // sample size
+					channelSize, // channels
+					sampleSize / 8 * channelSize, // frame size
+					AudioSystem.NOT_SPECIFIED, // frame rate
+					false, // big endian?
+					properties);
+
+			supportedFormats.add(PA_SAMPLE_S32LE);
+		}
+
+		return supportedFormats.toArray(new AudioFormat[0]);
+	}
+
 	@Override
 	public Line getLine(javax.sound.sampled.Line.Info info)
 			throws LineUnavailableException {
@@ -101,59 +284,54 @@
 			throw new LineUnavailableException();
 		}
 
-		PulseAudioSourceDataLine sourceLine = null;
-		sourceLine = new PulseAudioSourceDataLine(eventLoop);
-		Line.Info sourceDataLineInfo = sourceLine.getLineInfo();
+		if (!isLineSupported(info)) {
+			throw new IllegalArgumentException("Line unsupported: " + info);
+		}
 
-		if (DataLine.class.isAssignableFrom(info.getLineClass())
-				&& info instanceof DataLine.Info) {
-			System.out
-					.println("DEBUG: trying to find data line with last matching format");
+		AudioFormat[] formats = null;
+		AudioFormat defaultFormat = null;
 
-			DataLine.Info dataLineInfo = (DataLine.Info) info;
-			// need to find the DataLine with the last valid format
-			AudioFormat[] wantedFormats = dataLineInfo.getFormats();
-			AudioFormat[] availableFormats = ((DataLine.Info) sourceDataLineInfo)
+		if (DataLine.Info.class.isInstance(info)) {
+			ArrayList<AudioFormat> formatList = new ArrayList<AudioFormat>();
+			AudioFormat[] requestedFormats = ((DataLine.Info) info)
 					.getFormats();
-			for (int i = wantedFormats.length - 1; i > -1; i--) {
-				for (AudioFormat aFormat : availableFormats) {
-					if (wantedFormats[i].matches(aFormat)) {
-						System.out.println("DEBUG: found a matching format");
-						System.out.println("wanted: " + wantedFormats[i]);
-						System.out.println("available: " + aFormat);
-						sourceLine.setDefaultFormat(wantedFormats[i]);
-						sourceLines.add(sourceLine);
-						return sourceLine;
+			for (int i = 0; i < requestedFormats.length; i++) {
+				AudioFormat f1 = requestedFormats[i];
+				for (AudioFormat f2 : getSupportedFormats()) {
+
+					if (f1.matches(f2)) {
+						formatList.add(f2);
+						defaultFormat = f1;
 					}
 				}
 			}
+			formats = formatList.toArray(new AudioFormat[0]);
 
-			System.out.println("DEBUG: no matches found");
-			// no format matches, so return any line
-			throw new IllegalArgumentException("No matching format found");
-
+		} else {
+			formats = getSupportedFormats();
+			defaultFormat = new AudioFormat(Encoding.PCM_UNSIGNED, 22050, 8, 2,
+					2, AudioSystem.NOT_SPECIFIED, false);
 		}
 
-		if (info.matches(sourceDataLineInfo)) {
-			sourceLines.add(sourceLine);
-			return sourceLine;
+		if ((info.getLineClass() == SourceDataLine.class)) {
+			return new PulseAudioSourceDataLine(eventLoop, formats,
+					defaultFormat);
 		}
 
-		// if (info.matches(_targetDataLineInfo)) {
-		// PulseAudioTargetDataLine targetLine = new PulseAudioTargetDataLine();
-		// _targetLines.add(targetLine);
-		// return targetLine;
-		// }
+		if ((info.getLineClass() == TargetDataLine.class)) {
+			return new PulseAudioTargetDataLine(eventLoop, formats,
+					defaultFormat);
+		}
 
 		PulseAudioClip clip = new PulseAudioClip();
-		
+
 		if (info.matches(clip.getLineInfo())) {
 			clips.add(clip);
 			return clip;
 		}
-		
-		
+
 		throw new IllegalArgumentException();
+
 	}
 
 	@Override
@@ -166,30 +344,24 @@
 		return PulseAudioMixerInfo.getInfo();
 	}
 
-	@Override
 	public javax.sound.sampled.Line.Info[] getSourceLineInfo() {
-		if (isOpen) {
-			SourceDataLine sourceLine = new PulseAudioSourceDataLine(eventLoop);
-			Line.Info[] info = new Line.Info[] { sourceLine.getLineInfo() };
-			return info;
-		}
-
-		// if not open, then return empty array
-		return new Line.Info[] {};
+		Line.Info[] localArray = new Line.Info[sourceLineInfos.length];
+		System.arraycopy(sourceLineInfos, 0, localArray, 0,
+				sourceLineInfos.length);
+		return localArray;
 	}
 
 	@Override
 	public javax.sound.sampled.Line.Info[] getSourceLineInfo(
 			javax.sound.sampled.Line.Info info) {
-		Line.Info sourceInfo = new Line.Info(PulseAudioSourceDataLine.class);
-		if (info.matches(sourceInfo)) {
-			Line.Info[] sourceInfos = { sourceInfo, };
-			return sourceInfos;
-		} else {
-			Line.Info[] sourceInfos = {};
-			return sourceInfos;
+		ArrayList<javax.sound.sampled.Line.Info> infos = new ArrayList<javax.sound.sampled.Line.Info>();
 
+		for (Line.Info supportedInfo : sourceLineInfos) {
+			if (info.matches(supportedInfo)) {
+				infos.add(supportedInfo);
+			}
 		}
+		return infos.toArray(new Line.Info[infos.size()]);
 	}
 
 	@Override
@@ -201,21 +373,23 @@
 
 	@Override
 	public javax.sound.sampled.Line.Info[] getTargetLineInfo() {
-		Line.Info[] info = { new Line.Info(PulseAudioTargetDataLine.class), };
-		return info;
+		Line.Info[] localArray = new Line.Info[targetLineInfos.length];
+		System.arraycopy(targetLineInfos, 0, localArray, 0,
+				targetLineInfos.length);
+		return localArray;
 	}
 
 	@Override
 	public javax.sound.sampled.Line.Info[] getTargetLineInfo(
 			javax.sound.sampled.Line.Info info) {
-		Line.Info sourceInfo = new Line.Info(PulseAudioTargetDataLine.class);
-		if (info.matches(sourceInfo)) {
-			Line.Info[] sourceInfos = { sourceInfo, };
-			return sourceInfos;
-		} else {
-			Line.Info[] sourceInfos = {};
-			return sourceInfos;
+		ArrayList<javax.sound.sampled.Line.Info> infos = new ArrayList<javax.sound.sampled.Line.Info>();
+
+		for (Line.Info supportedInfo : targetLineInfos) {
+			if (info.matches(supportedInfo)) {
+				infos.add(supportedInfo);
+			}
 		}
+		return infos.toArray(new Line.Info[infos.size()]);
 	}
 
 	@Override
@@ -226,10 +400,21 @@
 
 	@Override
 	public boolean isLineSupported(javax.sound.sampled.Line.Info info) {
-		// if ( _sourceDataLineInfo.matches(info)) {
-		// return true;
-		// }
+		if (info != null) {
+			for (int i = 0; i < sourceLineInfos.length; i++) {
+				if (info.matches(sourceLineInfos[i])) {
+					return true;
+				}
+			}
+
+			for (int i = 0; i < targetLineInfos.length; i++) {
+				if (info.matches(targetLineInfos[i])) {
+					return true;
+				}
+			}
+		}
 		return false;
+
 	}
 
 	@Override
@@ -455,46 +640,22 @@
 			}
 		}
 
-		PulseAudioMixer selectedMixer = (PulseAudioMixer) AudioSystem
+		PulseAudioMixer mixer = (PulseAudioMixer) AudioSystem
 				.getMixer(selectedMixerInfo);
 
-		selectedMixer.open();
-		SourceDataLine line = (SourceDataLine) selectedMixer
-				.getLine(new Line.Info(PulseAudioSourceDataLine.class));
-
-		System.out.println("got a line");
-
-		// File soundFile = new File(new java.io.File(".").getCanonicalPath() +
-		// "/testsounds/logout.wav");
-		File soundFile = new File(
-				"/home/iivan/workspace/pulseaudio/testsounds/logout.wav");
+		mixer.open();
+		File soundFile = new File("testsounds/startup.wav");
 		AudioInputStream audioInputStream = AudioSystem
 				.getAudioInputStream(soundFile);
 		AudioFormat audioFormat = audioInputStream.getFormat();
-		line.open(audioFormat);
-		line.start();
-		PulseAudioStreamVolumeControl control = (PulseAudioStreamVolumeControl) line
-				.getControl(FloatControl.Type.VOLUME);
-		PulseAudioStreamMuteControl mute = (PulseAudioStreamMuteControl) line
-				.getControl(BooleanControl.Type.MUTE);
-		mute.setValue(true);
-		control.setValue(40000);
-		mute.setValue(false);
-		System.out.println("Volume set to " + control.getValue());
 
-		byte[] abData = new byte[1000];
-		int bytesRead = 0;
+		SourceDataLine line;
+		line = (SourceDataLine) mixer.getLine(new DataLine.Info(
+				SourceDataLine.class, audioFormat));
 
-		while (bytesRead >= 0) {
-			bytesRead = audioInputStream.read(abData, 0, abData.length);
-			if (bytesRead > 0) {
-				line.write(abData, 0, bytesRead);
-			}
-		}
-
-		System.out.println("about to close");
-		line.flush();
-		selectedMixer.close();
+		line.open();
+		System.out.println(line.getFormat().matches(audioFormat));
+		mixer.close();
 
 	}
 
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixerInfo.java	Fri Aug 15 10:51:54 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixerInfo.java	Fri Aug 15 12:03:05 2008 -0400
@@ -33,8 +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;
 
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixerProvider.java	Fri Aug 15 10:51:54 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixerProvider.java	Fri Aug 15 12:03:05 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;
 
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioMuteControl.java	Fri Aug 15 10:51:54 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioMuteControl.java	Fri Aug 15 12:03:05 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;
 
@@ -45,6 +45,4 @@
 		super(BooleanControl.Type.MUTE, false, "Volume muted", "Volume on");
 	}
 
-
 }
-
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java	Fri Aug 15 10:51:54 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java	Fri Aug 15 12:03:05 2008 -0400
@@ -67,7 +67,7 @@
 	private boolean isOpen = false;
 	private boolean isPaused = false;
 
-	private List<AudioFormat> supportedFormats = null;
+	private AudioFormat[] supportedFormats = null;
 	private AudioFormat currentFormat = null;
 	private AudioFormat defaultFormat = null;
 
@@ -79,196 +79,20 @@
 	private boolean muted;
 	private float volume;
 
+	private Semaphore semaphore = new Semaphore(0);
 	private long currentFramePosition = 0;
 
 	private Stream stream;
 
-	public PulseAudioSourceDataLine(EventLoop eventLoop) {
+	public PulseAudioSourceDataLine(EventLoop eventLoop, AudioFormat[] formats,
+			AudioFormat defaultFormat) {
 
+		this.supportedFormats = formats;
 		this.eventLoop = eventLoop;
 		this.lineListeners = new ArrayList<LineListener>();
 		this.volume = PulseAudioVolumeControl.MAX_VOLUME;
-
-		/*
-		 * FIXME puselaudio supports any sample rate (it can covert between
-		 * sample rates without a problem). it calculates the frame size and the
-		 * frame rate based on that.
-		 * 
-		 * Java's AudioSystem interface accepts NOT_SPECIFIED only for sample
-		 * rate and frame rate. eg: cant say that it supports any number of
-		 * audio channels
-		 * 
-		 * sample size in bytes [PA_SAMPLE_U8] = 1, [PA_SAMPLE_ULAW] = 1,
-		 * [PA_SAMPLE_ALAW] = 1, [PA_SAMPLE_S16LE] = 2, [PA_SAMPLE_S16BE] = 2,
-		 * [PA_SAMPLE_FLOAT32LE] = 4, [PA_SAMPLE_FLOAT32BE] = 4,
-		 * [PA_SAMPLE_S32LE] = 4, [PA_SAMPLE_S32BE] = 4,
-		 * 
-		 * 
-		 */
-
-		supportedFormats = new LinkedList<AudioFormat>();
-
-		Map<String, Object> properties;
-
-		int[] channelSizes = new int[] { 1, 2, 5 };
-		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
-					sampleSize, // sample size
-					channelSize, // channels
-					sampleSize / 8 * channelSize, // frame size in bytes
-					AudioSystem.NOT_SPECIFIED, // frame rate
-					false, // big endian?
-					properties);
-
-			supportedFormats.add(PA_SAMPLE_U8);
-		}
-
-		for (int channelSize : channelSizes) {
-			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
-					sampleSize, // sample size
-					channelSize, // channels
-					sampleSize / 8 * channelSize, // frame size
-					AudioSystem.NOT_SPECIFIED, // frame rate
-					false, // big endian?
-					properties);
-
-			supportedFormats.add(PA_SAMPLE_ALAW);
-		}
-
-		for (int channelSize : channelSizes) {
-			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
-					sampleSize, // sample size
-					channelSize, // channels
-					sampleSize / 8 * channelSize, // frame size
-					AudioSystem.NOT_SPECIFIED, // frame rate
-					false, // big endian?
-					properties);
-
-			supportedFormats.add(PA_SAMPLE_ULAW);
-		}
-
-		for (int channelSize : channelSizes) {
-			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
-					AudioSystem.NOT_SPECIFIED, // sample rate
-					sampleSize, // sample size
-					channelSize, // channels
-					sampleSize / 8 * channelSize, // frame size
-					AudioSystem.NOT_SPECIFIED, // frame rate
-					true, // big endian?
-					properties);
-
-			supportedFormats.add(PA_SAMPLE_S16BE);
-		}
-
-		for (int channelSize : channelSizes) {
-			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
-					AudioSystem.NOT_SPECIFIED, // sample rate
-					sampleSize, // sample size
-					channelSize, // channels
-					sampleSize / 8 * channelSize, // frame size
-					AudioSystem.NOT_SPECIFIED, // frame rate
-					false, // big endian?
-					properties);
-
-			supportedFormats.add(A_SAMPLE_S16LE);
-		}
-
-		for (int channelSize : channelSizes) {
-			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
-					AudioSystem.NOT_SPECIFIED, // sample rate
-					sampleSize, // sample size
-					channelSize, // channels
-					sampleSize / 8 * channelSize, // frame size
-					AudioSystem.NOT_SPECIFIED, // frame rate
-					true, // big endian?
-					properties);
-
-			supportedFormats.add(PA_SAMPLE_S32BE);
-		}
-
-		for (int channelSize : channelSizes) {
-			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
-					AudioSystem.NOT_SPECIFIED, // sample rate
-					sampleSize, // sample size
-					channelSize, // channels
-					sampleSize / 8 * channelSize, // frame size
-					AudioSystem.NOT_SPECIFIED, // frame rate
-					false, // big endian?
-					properties);
-
-			supportedFormats.add(PA_SAMPLE_S32LE);
-		}
-
-		currentFormat = null;
+		this.defaultFormat = defaultFormat;
+		this.currentFormat = defaultFormat;
 
 	}
 
@@ -319,10 +143,12 @@
 				if (stream.getState() == Stream.State.READY) {
 					fireLineEvent(new LineEvent(PulseAudioSourceDataLine.this,
 							LineEvent.Type.OPEN, AudioSystem.NOT_SPECIFIED));
+					semaphore.release();
 				} else if (stream.getState() == Stream.State.TERMINATED
 						|| stream.getState() == Stream.State.FAILED) {
 					fireLineEvent((new LineEvent(PulseAudioSourceDataLine.this,
 							LineEvent.Type.CLOSE, AudioSystem.NOT_SPECIFIED)));
+					semaphore.release();
 				}
 			}
 
@@ -330,19 +156,8 @@
 
 		stream.addStateListener(openCloseListener);
 
-		final Semaphore semaphore = new Semaphore(0);
-
 		synchronized (eventLoop.threadLock) {
 
-			stream.addStateListener(new Stream.StateListener() {
-				@Override
-				public void update() {
-					if (stream.getState() == Stream.State.READY) {
-						semaphore.release();
-					}
-				}
-			});
-
 			stream.connectForPlayback(null);
 		}
 
@@ -470,20 +285,6 @@
 	public void close() {
 		assert (isOpen);
 
-		final Semaphore semaphore = new Semaphore(0);
-
-		Stream.StateListener closeListener = new Stream.StateListener() {
-			@Override
-			public void update() {
-				if (stream.getState() == Stream.State.TERMINATED
-						|| stream.getState() == Stream.State.FAILED) {
-					semaphore.release();
-				}
-			}
-		};
-
-		stream.addStateListener(closeListener);
-
 		synchronized (eventLoop.threadLock) {
 			stream.drain();
 			stream.disconnect();
@@ -491,8 +292,6 @@
 
 		try {
 			semaphore.acquire();
-			System.out.println("stream closed");
-			stream.removeStateListener(closeListener);
 		} catch (InterruptedException e) {
 			// throw new LineUnavailableException("unable to prepare
 			// stream");
@@ -571,8 +370,8 @@
 	}
 
 	public javax.sound.sampled.Line.Info getLineInfo() {
-		return new DataLine.Info(SourceDataLine.class, supportedFormats
-				.toArray(new AudioFormat[0]), 0, 100000);
+		return new DataLine.Info(SourceDataLine.class, supportedFormats, 0,
+				100000);
 	}
 
 	public boolean isControlSupported(Type control) {
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioStreamMuteControl.java	Fri Aug 15 10:51:54 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioStreamMuteControl.java	Fri Aug 15 12:03:05 2008 -0400
@@ -33,24 +33,24 @@
 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.volumeControl = (PulseAudioStreamVolumeControl) line
+				.getControl(FloatControl.Type.VOLUME);
 		this.line = line;
 	}
-	
-	public synchronized void setValue(boolean value){
+
+	public synchronized void setValue(boolean value) {
 		if (value == true) {
 			line.setMuted(true);
 			volumeControl.setStreamVolume(0);
@@ -60,7 +60,7 @@
 			volumeControl.setStreamVolume(newValue);
 		}
 	}
-	
+
 	public synchronized boolean getValue() {
 		return line.isMuted();
 	}
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioStreamVolumeControl.java	Fri Aug 15 10:51:54 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioStreamVolumeControl.java	Fri Aug 15 12:03:05 2008 -0400
@@ -6,19 +6,19 @@
 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");
+
+			+ java.io.File.separatorChar + System.mapLibraryName("pulse-java");
+
 			System.out.println(library);
 			System.load(library);
 		} catch (IOException e) {
@@ -32,41 +32,40 @@
 		this.eventLoop = line.getEventLoop();
 		this.line = line;
 	}
-	
+
 	public synchronized void setValue(float newValue) {
-		if ( newValue > PulseAudioVolumeControl.MAX_VOLUME || newValue < PulseAudioStreamVolumeControl.MIN_VOLUME) {
+		if (newValue > PulseAudioVolumeControl.MAX_VOLUME
+				|| newValue < PulseAudioStreamVolumeControl.MIN_VOLUME) {
 			throw new IllegalArgumentException("invalid value");
 		}
-		
-		if(!line.isMuted()) {
+
+		if (!line.isMuted()) {
 			setStreamVolume(newValue);
 		}
-		
+
 		line.setVolume(newValue);
 	}
-	
+
 	protected synchronized void setStreamVolume(float newValue) {
 		int operationPointer;
 		int operationState;
-		synchronized(eventLoop.threadLock) {
+		synchronized (eventLoop.threadLock) {
 			operationPointer = native_setValue(newValue);
 			operationState = native_getOperationState(operationPointer);
 		}
-		while(operationState != 1) {
+		while (operationState != 1) {
 			synchronized (eventLoop.threadLock) {
 				operationState = native_getOperationState(operationPointer);
 			}
 		}
-		
+
 	}
-	
+
 	public synchronized float getValue() {
 		return line.getVolume();
 	}
 
+	private native int native_getOperationState(int operationState);
 
-	private native int native_getOperationState(int operationState);
 	public native int native_setValue(float newValue);
 }
-
-
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java	Fri Aug 15 10:51:54 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java	Fri Aug 15 12:03:05 2008 -0400
@@ -37,27 +37,51 @@
 
 package org.classpath.icedtea.pulseaudio;
 
+import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Semaphore;
 
 import javax.sound.sampled.*;
+import javax.sound.sampled.AudioFormat.Encoding;
 import javax.sound.sampled.Control.Type;
+import javax.sound.sampled.Port.Info;
 
 public class PulseAudioTargetDataLine implements TargetDataLine {
 
-	protected long contextPointer;
-	protected long mainLoopPointer;
-	protected long streamPointer;
+	
+	
 	protected boolean isOpen = false;
 	protected boolean isPaused = false;
-	protected int defaultBufferSize;
+
+	private AudioFormat[] supportedFormats = null;
+	private AudioFormat currentFormat = null;
+	private AudioFormat defaultFormat = null;
+
+	private List<LineListener> lineListeners;
+	
+	private List<StreamListener> streamListeners = new ArrayList<StreamListener>();
+	
+	private String streamName = "Java Stream";
+	private static final int DEFAULT_BUFFER_SIZE = 1000;
+	private static final String PULSEAUDIO_FORMAT_KEY = "PulseAudioFormatKey";
+	
+	private EventLoop eventLoop = null;
+	
 	protected ArrayList<LineListener> listeners;
+	
+	private Semaphore semaphore = new Semaphore(0);
+	
+	@SuppressWarnings("unused")
+	private long streamPointer;
 
 	static {
 		try {
-			String library = new java.io.File(".").getCanonicalPath()
-					+ java.io.File.separatorChar + "lib"
-					+ java.io.File.separatorChar
+			String library = new java.io.File(".").getCanonicalPath()	+ java.io.File.separatorChar
 					+ System.mapLibraryName("pulse-java");
 			System.out.println(library);
 			System.load(library);
@@ -65,35 +89,149 @@
 			assert ("Loading failed".endsWith("library"));
 		}
 	}
+	
+	private native void native_open(long contextPointer, String streamName,
+			String encoding, int sampleRate, int channels, int bufferSize);
+	
+	private native void native_start();
+	
+	private native int native_get_readable_size();
+	
+	private native void native_close();
+	
+	private native int native_read(byte[] array, int remaininglength, int position);
+	
+	public PulseAudioTargetDataLine(EventLoop eventLoop, AudioFormat[] formats, AudioFormat defaultFormat) {
+		supportedFormats = formats;
+		this.eventLoop = eventLoop;
+		this.lineListeners = new ArrayList<LineListener>();
+		this.defaultFormat = defaultFormat; 
+		this.currentFormat = defaultFormat;
+
+	}
 
 	public void open(AudioFormat format, int bufferSize)
 			throws LineUnavailableException {
-		isOpen = true;
+		System.out.println("OPEn CALLED");
+		if (isOpen) {
+			throw new IllegalStateException("Line is already open");
+		}
+
+		// ignore suggested buffer size
+
+		for (AudioFormat myFormat : supportedFormats) {
+			if (format.matches(myFormat)) {
+				native_open(eventLoop.getContextPointer(), streamName,
+						(String) myFormat.getProperty(PULSEAUDIO_FORMAT_KEY),
+						(int) format.getSampleRate(), format.getChannels(),
+						bufferSize);
+				currentFormat = format;
+				isOpen = true;
+			}
+		}
+		// no matches found
+		if (!isOpen) {
+			throw new IllegalArgumentException("Invalid format");
+		}
+
+		StreamListener openCloseListener = new StreamListener() {
 
-		int channels = format.getChannels();
-		float rate = format.getSampleRate();
-		int sampleSize = format.getSampleSizeInBits();
-		String encoding = format.getEncoding().toString();
-		boolean bigEndian = format.isBigEndian();
-		openStream(encoding, rate, sampleSize, channels, bigEndian, bufferSize);
+			@Override
+			public void update(StreamEvent e) {
+				if (e.getType() == StreamEvent.Type.READY) {
+					fireLineEvent(new LineEvent(PulseAudioTargetDataLine.this,
+							LineEvent.Type.OPEN, AudioSystem.NOT_SPECIFIED));
+					System.out.println("IN HERE");
+					semaphore.release();
+				} else if (e.getType() == StreamEvent.Type.TERMINATED
+						|| e.getType() == StreamEvent.Type.FAILED) {
+					fireLineEvent((new LineEvent(PulseAudioTargetDataLine.this,
+							LineEvent.Type.CLOSE, AudioSystem.NOT_SPECIFIED)));
+					semaphore.release();
+				}
+			}
+
+		};
+
+		addStreamListener(openCloseListener);
+
+
+		
+		synchronized (eventLoop.threadLock) {
+
+			native_start();
+		}
+
+		try {
+			semaphore.acquire();
+		} catch (InterruptedException e) {
+			// throw new LineUnavailableException("unable to prepare
+			// stream");
+		}
 	}
 
 	public void open(AudioFormat format) throws LineUnavailableException {
-		open(format, defaultBufferSize);
+		open(format, DEFAULT_BUFFER_SIZE);
 
 	}
 
 	public void open() throws LineUnavailableException {
-		openStream("PCM_SIGNED", 44100, 16, 2, false, defaultBufferSize);
+		// pick a random format
+		if (defaultFormat == null) {
+			defaultFormat = new AudioFormat(Encoding.PCM_UNSIGNED, 22050, 8, 2,
+					2, AudioSystem.NOT_SPECIFIED, false);
+		}
+
+		open(defaultFormat, DEFAULT_BUFFER_SIZE);
 	}
 
-	private native void openStream(String encoding, float rate, int size,
-			int channels, boolean bigEndian, int bufferSize);
 
 	@Override
-	public int read(byte[] b, int off, int len) {
-		readFromStream(b, off, len);
-		return len;
+	public int read(byte[] data, int offset, int length) {
+		int frameSize = currentFormat.getFrameSize();
+		
+		if (length % frameSize != 0) {
+			throw new IllegalArgumentException(
+					"amount of data to write does not represent an integral number of frames");
+		}
+		
+		if (length < 0) {
+			throw new IllegalArgumentException("length is negative");
+		}
+
+		if (length + offset > data.length) {
+			throw new ArrayIndexOutOfBoundsException(length + offset);
+		}
+
+		int position = offset;
+		int remainingLength = length;
+		int availableSize;
+
+		int sizeRead = 0;
+
+		while (remainingLength != 0) {
+
+			synchronized (eventLoop.threadLock) {
+				availableSize = available();
+				int toRead = native_read(data, remainingLength,  position);
+
+				sizeRead += toRead;
+				position += toRead;
+				remainingLength -= toRead;
+				
+
+			}
+		}
+
+		// all the data should have been played by now
+		assert (sizeRead == length);
+		//currentFramePosition += (sizeWritten/getFormat().getFrameSize());
+		/*
+		 * FIXME when the stream is flushed() etc, instead of returning length
+		 * this should unblock and return the the size of data written so far
+		 */
+		return sizeRead;
+	
 	}
 
 	private native void readFromStream(byte[] b, int off, int len);
@@ -102,17 +240,17 @@
 		if (isPaused) {
 			resumeStream();
 			isPaused = false;
-		} else {
+		} /*else {
 			startStream();
-		}
+		}*/
 
-		for (LineListener l : listeners) {
+		/*for (LineListener l : listeners) {
 			l.update(new LineEvent(this, LineEvent.Type.START, 0));
-		}
+		}*/
 	}
 
 	public void stop() {
-		pauseStream();
+		//pauseStream();
 		isPaused = true;
 
 	}
@@ -147,12 +285,26 @@
 		return isOpen;
 	}
 
-	public native int available();
+	public int available() {
+		synchronized (eventLoop.threadLock) {
+			return native_get_readable_size();
+		}
+	}
 
 	public void close() {
-		closeStream();
-		for (LineListener l : listeners) {
-			l.update(new LineEvent(this, LineEvent.Type.CLOSE, 0));
+		assert (isOpen);
+
+
+		synchronized (eventLoop.threadLock) {
+			native_close();
+		}
+
+		try {
+			semaphore.acquire();
+			System.out.println("stream closed");
+		} catch (InterruptedException e) {
+			// throw new LineUnavailableException("unable to prepare
+			// stream");
 		}
 
 	}
@@ -165,8 +317,7 @@
 	}
 
 	public AudioFormat getFormat() {
-		// TODO Auto-generated method stub
-		return null;
+		return currentFormat;
 	}
 
 	public int getFramePosition() {
@@ -217,5 +368,56 @@
 		// TODO Auto-generated method stub
 		return false;
 	}
+	
+	public void update(int status) {
+		synchronized (eventLoop.threadLock) {
+			switch (status) {
+			case 0:
+				fireStreamEvent(new StreamEvent(StreamEvent.Type.UNCONNECTED));
+				break;
+			case 1:
+				fireStreamEvent(new StreamEvent(StreamEvent.Type.CREATING));
+				break;
+			case 2:
+				fireStreamEvent(new StreamEvent(StreamEvent.Type.READY));
+				break;
+			case 3:
+				fireStreamEvent(new StreamEvent(StreamEvent.Type.FAILED));
+				break;
+			case 4:
+				fireStreamEvent(new StreamEvent(StreamEvent.Type.TERMINATED));
+				break;
+			default:
+				assert ("not supposed to happen".indexOf("false") >= 0);
+			}
+		}
+	}
 
-}
+	private void fireLineEvent(LineEvent e) {
+		for (LineListener lineListener : lineListeners) {
+			lineListener.update(e);
+		}
+	}
+
+	private void fireStreamEvent(StreamEvent e) {
+		synchronized (streamListeners) {
+			for (StreamListener streamListener : streamListeners) {
+				streamListener.update(e);
+			}
+		}
+	}
+	
+	
+	private void addStreamListener(StreamListener listener) {
+		synchronized (streamListeners) {
+			this.streamListeners.add(listener);
+		}
+	}
+
+	private void removeStreamListener(StreamListener listener) {
+		synchronized (streamListeners) {
+			this.streamListeners.remove(listener);
+		}
+	}
+
+}
\ No newline at end of file
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioVolumeControl.java	Fri Aug 15 10:51:54 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioVolumeControl.java	Fri Aug 15 12:03:05 2008 -0400
@@ -33,21 +33,21 @@
 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;
 
 abstract 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", "Default Volume", "Full Volume");
+		super(FloatControl.Type.VOLUME, MIN_VOLUME, MAX_VOLUME, 1, -1, line
+				.getVolume(), "pulseaudio units", "Volume Off",
+				"Default Volume", "Full Volume");
 	}
 
-
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java/org/classpath/icedtea/pulseaudio/SimpleAudioRecorder.java	Fri Aug 15 12:03:05 2008 -0400
@@ -0,0 +1,208 @@
+package org.classpath.icedtea.pulseaudio;
+
+/*
+ *	SimpleAudioRecorder.java
+ *
+ *	This file is part of jsresources.org
+ */
+
+/*
+ * Copyright (c) 1999 - 2003 by Matthias Pfisterer
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ |<---            this code is formatted to fit into 80 columns             --->|
+ */
+
+import java.io.IOException;
+import java.io.File;
+
+import javax.sound.sampled.DataLine;
+import javax.sound.sampled.Mixer;
+import javax.sound.sampled.TargetDataLine;
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.LineUnavailableException;
+import javax.sound.sampled.AudioFileFormat;
+
+public class SimpleAudioRecorder extends Thread {
+	private TargetDataLine m_line;
+	private AudioFileFormat.Type m_targetType;
+	private AudioInputStream m_audioInputStream;
+	private File m_outputFile;
+
+	public SimpleAudioRecorder(TargetDataLine line,
+			AudioFileFormat.Type targetType, File file) {
+		m_line = line;
+		m_audioInputStream = new AudioInputStream(line);
+		m_targetType = targetType;
+		m_outputFile = file;
+	}
+
+	/**
+	 * Starts the recording. To accomplish this, (i) the line is started and
+	 * (ii) the thread is started.
+	 */
+	public void start() {
+		/*
+		 * Starting the TargetDataLine. It tells the line that we now want to
+		 * read data from it. If this method isn't called, we won't be able to
+		 * read data from the line at all.
+		 */
+		m_line.start();
+
+		/*
+		 * Starting the thread. This call results in the method 'run()' (see
+		 * below) being called. There, the data is actually read from the line.
+		 */
+		super.start();
+	}
+
+	/**
+	 * Stops the recording.
+	 * 
+	 * Note that stopping the thread explicitely is not necessary. Once no more
+	 * data can be read from the TargetDataLine, no more data be read from our
+	 * AudioInputStream. And if there is no more data from the AudioInputStream,
+	 * the method 'AudioSystem.write()' (called in 'run()' returns. Returning
+	 * from 'AudioSystem.write()' is followed by returning from 'run()', and
+	 * thus, the thread is terminated automatically.
+	 * 
+	 * It's not a good idea to call this method just 'stop()' because stop() is
+	 * a (deprecated) method of the class 'Thread'. And we don't want to
+	 * override this method.
+	 */
+	public void stopRecording() {
+		m_line.stop();
+		m_line.close();
+		super.stop();
+	}
+
+	/**
+	 * Main working method. You may be surprised that here, just
+	 * 'AudioSystem.write()' is called. But internally, it works like this:
+	 * AudioSystem.write() contains a loop that is trying to read from the
+	 * passed AudioInputStream. Since we have a special AudioInputStream that
+	 * gets its data from a TargetDataLine, reading from the AudioInputStream
+	 * leads to reading from the TargetDataLine. The data read this way is then
+	 * written to the passed File. Before writing of audio data starts, a header
+	 * is written according to the desired audio file type. Reading continues
+	 * untill no more data can be read from the AudioInputStream. In our case,
+	 * this happens if no more data can be read from the TargetDataLine. This,
+	 * in turn, happens if the TargetDataLine is stopped or closed (which
+	 * implies stopping). (Also see the comment above.) Then, the file is closed
+	 * and 'AudioSystem.write()' returns.
+	 */
+	public void run() {
+		try {
+			AudioSystem.write(m_audioInputStream, m_targetType, m_outputFile);
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+
+	public static void main(String[] args) throws Exception {
+		Mixer.Info mixerInfos[] = AudioSystem.getMixerInfo();
+		Mixer.Info selectedMixerInfo = null;
+		// int i = 0;
+		for (Mixer.Info info : mixerInfos) {
+			// System.out.println("Mixer Line " + i++ + ": " + info.getName() +
+			// " " + info.getDescription());
+			if (info.getName().contains("PulseAudio")) {
+				selectedMixerInfo = info;
+				System.out.println(selectedMixerInfo);
+			}
+		}
+
+		PulseAudioMixer mixer = (PulseAudioMixer) AudioSystem
+				.getMixer(selectedMixerInfo);
+
+		mixer.open();
+		File outputFile = new File("recordingFile");
+		AudioFormat audioFormat = new AudioFormat(
+				AudioFormat.Encoding.PCM_SIGNED, 44100.0F, 16, 2, 4, 44100.0F,
+				false);
+		DataLine.Info info = new DataLine.Info(TargetDataLine.class,
+				audioFormat);
+		TargetDataLine targetDataLine = (TargetDataLine) mixer.getLine(info);
+		targetDataLine.open();
+
+		AudioFileFormat.Type targetType = AudioFileFormat.Type.WAVE;
+		SimpleAudioRecorder recorder = new SimpleAudioRecorder(targetDataLine,
+				targetType, outputFile);
+
+		/*
+		 * We are waiting for the user to press ENTER to start the recording.
+		 * (You might find it inconvenient if recording starts immediately.)
+		 */
+		out("Press ENTER to start the recording.");
+		try {
+			System.in.read();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		/*
+		 * Here, the recording is actually started.
+		 */
+		recorder.start();
+		out("Recording...");
+
+		/*
+		 * And now, we are waiting again for the user to press ENTER, this time
+		 * to signal that the recording should be stopped.
+		 */
+		out("Press ENTER to stop the recording.");
+		try {
+			System.in.read();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+
+		/*
+		 * Here, the recording is actually stopped.
+		 */
+		recorder.stopRecording();
+		out("Recording stopped.");
+
+		mixer.close();
+	}
+
+	private static void printUsageAndExit() {
+		out("SimpleAudioRecorder: usage:");
+		out("\tjava SimpleAudioRecorder -h");
+		out("\tjava SimpleAudioRecorder <audiofile>");
+		System.exit(0);
+	}
+
+	private static void out(String strMessage) {
+		System.out.println(strMessage);
+	}
+}
+
+/** * SimpleAudioRecorder.java ** */
+
--- a/src/native/Makefile.am	Fri Aug 15 10:51:54 2008 -0400
+++ b/src/native/Makefile.am	Fri Aug 15 12:03:05 2008 -0400
@@ -5,6 +5,8 @@
 	jni-common.h \
 	org_classpath_icedtea_pulseaudio_EventLoop.c \
 	org_classpath_icedtea_pulseaudio_EventLoop.h \
+	org_classpath_icedtea_pulseaudio_PulseAudioTargetDataLine.c \
+	org_classpath_icedtea_pulseaudio_PulseAudioTargetDataLine.h \
 	org_classpath_icedtea_pulseaudio_PulseAudioStreamVolumeControl.c \
 	org_classpath_icedtea_pulseaudio_PulseAudioStreamVolumeControl.h \
 	org_classpath_icedtea_pulseaudio_Operation.h \
--- a/src/native/org_classpath_icedtea_pulseaudio_Operation.c	Fri Aug 15 10:51:54 2008 -0400
+++ b/src/native/org_classpath_icedtea_pulseaudio_Operation.c	Fri Aug 15 12:03:05 2008 -0400
@@ -72,7 +72,7 @@
  * Signature: ()I
  */
 JNIEXPORT jint JNICALL Java_org_classpath_icedtea_pulseaudio_Operation_native_1get_1state
-(JNIEnv* env, jobject obj) {
+(JNIEnv *env, jobject obj) {
 
 	pa_operation* operation = (pa_operation*) getJavaPointer(env, obj, "operationPointer");
 	int state = pa_operation_get_state(operation);
--- a/src/native/org_classpath_icedtea_pulseaudio_PulseAudioTargetDataLine.c	Fri Aug 15 10:51:54 2008 -0400
+++ b/src/native/org_classpath_icedtea_pulseaudio_PulseAudioTargetDataLine.c	Fri Aug 15 12:03:05 2008 -0400
@@ -47,7 +47,10 @@
 #include "org_classpath_icedtea_pulseaudio_PulseAudioTargetDataLine.h"
 #include "jni-common.h"
 
-static void stream_read_cb(pa_stream* stream, size_t length, void* userdata) {
+/* defined in EventLoop.c */
+extern JNIEnv* pulse_thread_env;
+
+/*static void stream_read_cb(pa_stream* stream, size_t length, void* userdata) {
 	pa_threaded_mainloop *mainloop = userdata;
 	pa_threaded_mainloop_signal(mainloop, 0);
 }
@@ -56,212 +59,179 @@
 		void* userdata) {
 	pa_threaded_mainloop *mainloop = userdata;
 	pa_threaded_mainloop_signal(mainloop, 0);
+}*/
+
+static void stream_state_change_callback(pa_stream* stream, void* userdata) {
+	assert(stream);
+	assert(userdata);
+
+	//	printf("entering stream_state_change_callback\n");
+
+	java_context_t* java_context = (java_context_t*)userdata;
+	JNIEnv* env;
+
+	/* needed so we can create a stream from another thread
+	 */
+	if (pa_stream_get_state(stream) == PA_STREAM_CREATING) {
+		env = java_context->env;
+	} else {
+		env = pulse_thread_env;
+	}
+
+	jobject obj = java_context->obj;
+
+	// printf("stream state changed to %d\n", pa_stream_get_state(stream));
+
+	/* Call the 'update' method in java
+	 * to handle all java-side events
+	 */
+	jclass cls = (*env)->GetObjectClass(env, obj);
+	if (cls == NULL) {
+		printf("unable to get class of object");
+		return;
+	}
+	jmethodID mid = (*env)->GetMethodID(env, cls, "update", "(I)V");
+	if (mid == NULL) {
+		printf("unable to get callback method\n");
+		return;
+
+	}
+	//printf("calling update on java\n");
+	(*env)->CallVoidMethod(env, obj, mid, pa_stream_get_state(stream));
+
+	//printf("returning form stream_state_change_callback\n");
+	return;
+
 }
 
-static void stream_state_cb(pa_stream* stream, void* userdata) {
-	assert(stream);
-	pa_threaded_mainloop *mainloop = userdata;
-	printf("stream state changed to %d\n", pa_stream_get_state(stream));
-	switch (pa_stream_get_state(stream)) {
-	case PA_STREAM_READY:
-	case PA_STREAM_FAILED:
-	case PA_STREAM_TERMINATED:
-		pa_threaded_mainloop_signal(mainloop, 0);
-		break;
-
-	default:
-		/* do nothing */
-		break;
-	}
-}
-
-JNIEXPORT void JNICALL Java_org_openjdk_sound_PulseAudioSourceDataLine_openStream
-(JNIEnv * env, jobject obj, jstring string, jfloat rate, jint size, jint channels, jboolean bigEndian, jint bufferSize) {
-
-	printf("entering native_open\n");
-
-	pa_context* context = (pa_context*) contextPointer;
-	assert(context != NULL);
-
-	obj = (*env)->NewGlobalRef(env, obj);
-
-	java_context_t* java_context = malloc(sizeof(java_context));
-	java_context->env = env;
-	java_context->obj = obj;
+/*
+ * Class:     org_classpath_icedtea_pulseaudio_PulseAudioSourceDataLine
+ * Method:    native_open
+ * Signature: (JLjava/lang/String;Ljava/lang/String;III)V
+ */
+JNIEXPORT void JNICALL Java_org_classpath_icedtea_pulseaudio_PulseAudioTargetDataLine_native_1open
+(JNIEnv* env, jobject obj, jlong contextPointer, jstring name, jstring encodingString, jint sampleRate, jint channels, jint bufferSize) {
 
 	//TODO: Need to deal with the buffer size. Currently ignored
 
+	//	printf("entering native_open\n");
+	java_context_t* java_context = malloc(sizeof(java_context));
+	java_context->env = env;
+	java_context->obj = (*env)->NewGlobalRef(env, obj);
+
+	pa_context* context = (pa_context*) convertJavaLongToPointer(contextPointer);
+	assert(context != NULL);
+
 	pa_sample_spec sample_spec;
 
-	char *encoding = GetStringUTFChars(env, string, NULL);
+	const char *encoding = (*env)->GetStringUTFChars(env, encodingString, NULL);
 
-	if( (strcmp(encoding, "PCM_UNSIGNED") == 0) && (size == 8)) {
+	if (strcmp(encoding, "PA_SAMPLE_U8") == 0) {
 		sample_spec.format = PA_SAMPLE_U8;
-	} else if( (strcmp(encoding, "ALAW") == 0) && (size == 8)) {
+	} else if (strcmp(encoding, "PA_SAMPLE_ALAW") == 0) {
 		sample_spec.format = PA_SAMPLE_ALAW;
-	} else if( (strcmp(encoding, "ULAW") == 0) && (size == 8)) {
+	} else if (strcmp(encoding, "PA_SAMPLE_ULAW;") == 0) {
 		sample_spec.format = PA_SAMPLE_ULAW;
-	} else if ( (strcmp(encoding, "PCM_SIGNED") == 0) && (size == 16) && (bigEndian == 1)) {
+	} else if (strcmp(encoding, "PA_SAMPLE_S16BE") == 0) {
 		sample_spec.format = PA_SAMPLE_S16BE;
-	} else if ( (strcmp(encoding, "PCM_SIGNED") == 0) && (size == 16) && (bigEndian == 0)) {
+	} else if (strcmp(encoding, "PA_SAMPLE_S16LE") == 0) {
 		sample_spec.format = PA_SAMPLE_S16LE;
-	} else if ( (strcmp(encoding, "PCM_SIGNED") == 0) && (size == 32) && (bigEndian == 1)) {
+	} else if (strcmp(encoding, "PA_SAMPLE_S32BE") == 0) {
 		sample_spec.format = PA_SAMPLE_S32BE;
-	} else if ( (strcmp(encoding, "PCM_SIGNED") == 0) && (size == 32) && (bigEndian == 0)) {
+	} else if (strcmp(encoding, "PA_SAMPLE_S32LE") == 0) {
 		sample_spec.format = PA_SAMPLE_S32LE;
 	} else {
-		//TODO: Invalid format :throw Exception;
+		printf("error in open: encoding is : %s\n", encoding);
+		throwByName(env, "java/lang/IllegalArgumentException", "Invalid format");
+		/* clean up */
+		free(java_context);
+		(*env)->DeleteGlobalRef(env, obj);
+		(*env)->ReleaseStringUTFChars(env, encodingString, encoding);
+		return;
 	}
 
-	sample_spec.rate = rate;
+	sample_spec.rate = sampleRate;
 	sample_spec.channels = channels;
 
+	printf("sample_spec.rate = %d\n", sample_spec.rate);
+	printf("sample_spec.channels = %d\n", sample_spec.channels);
+
+	if ( !pa_sample_spec_valid(&sample_spec)) {
+		printf("error: invalid format\n");
+		throwByName(env, "java/lang/IllegalArgumentException", "Invalid format");
+		/* clean up */
+		free(java_context);
+		(*env)->DeleteGlobalRef(env, obj);
+		(*env)->ReleaseStringUTFChars(env, encodingString, encoding);
+		return;
+	}
+
+	(*env)->ReleaseStringUTFChars(env, encodingString, encoding);
+
 	/* obtain the server from the caller */
-	const jbyte* stream_name = NULL;
+	const char* stream_name = NULL;
 	stream_name = (*env)->GetStringUTFChars(env, name, NULL);
 	if (stream_name == NULL) {
 		return; /* OutOfMemoryError */
 	}
-	printf("About to create stream: %s\n", stream_name);
+	//	printf("About to create stream: %s\n", stream_name);
 	pa_stream* stream = pa_stream_new(context, stream_name, &sample_spec, NULL);
 	assert(stream != NULL);
 	(*env)->ReleaseStringUTFChars(env, name, stream_name);
 
 	pa_stream_set_state_callback(stream, stream_state_change_callback, java_context);
 
-	jclass cls = (*env)->GetObjectClass(env,obj);
-	jfieldID fid = (*env)->GetFieldID(env, cls, "streamPointer", "I");
-	(*env)->SetIntField(env, obj, fid, (jint) stream);
-
-	printf("returning from native_open\n");
+	//	printf("seeting stream pointer: %d\n", (int)stream);
+	setJavaPointer(env, obj, "streamPointer", stream);
+	//	printf("returning from native_open\n");
 
 }
 
-JNIEXPORT void JNICALL
-Java_org_openjdk_sound_PulseAudioSourceDataLine_startStream(JNIEnv *env, jobject obj) {
-	pa_threaded_mainloop *mainloop = getJavaLongField (env, obj, "mainLoopPointer");
-	pa_stream *stream = getJavaLongField(env, obj, "streamPointer");
-	pa_threaded_mainloop_lock(mainloop);
-	pa_stream_connect_playback(stream, NULL, NULL, 0, NULL, NULL);
-	pa_threaded_mainloop_wait(mainloop);
-	if ( pa_stream_get_state(stream) != PA_STREAM_READY ) {
-		printf("stream initialization failed\n");
-	}
-	pa_threaded_mainloop_unlock(mainloop);
-}
-
-JNIEXPORT void Java_org_openjdk_sound_PulseAudioSourceDataLine_resumeStream(
-		JNIEnv *env, jobject obj) {
-	pa_threaded_mainloop *mainloop = getJavaLongField(env, obj,
-			"mainLoopPointer");
-	pa_stream *stream = getJavaLongField(env, obj, "streamPointer");
-	pa_threaded_mainloop_lock(mainloop);
-	pa_operation *o = pa_stream_cork(stream, 0, stream_operation_complete_cb,
-			mainloop);
-	while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
-		pa_threaded_mainloop_wait(mainloop);
-	}
-	pa_operation_unref(o);
-	pa_threaded_mainloop_unlock(mainloop);
+/*
+ * Class:     org_classpath_icedtea_pulseaudio_PulseAudioSourceDataLine
+ * Method:    native_start
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_org_classpath_icedtea_pulseaudio_PulseAudioTargetDataLine_native_1start
+(JNIEnv *env, jobject obj) {
+	pa_stream *stream = (pa_stream*)getJavaPointer(env, obj, "streamPointer");
+	assert(stream);
+	pa_stream_connect_record(stream, NULL, NULL, 0);
 
 }
 
-JNIEXPORT void Java_org_openjdk_sound_PulseAudioSourceDataLine__pauseStream(
-		JNIEnv *env, jobject obj) {
-	pa_threaded_mainloop *mainloop = getJavaLongField(env, obj,
-			"mainLoopPointer");
-	pa_stream *stream = getJavaLongField(env, obj, "streamPointer");
-	pa_threaded_mainloop_lock(mainloop);
-	pa_operation *o = pa_stream_cork(stream, 1, stream_operation_complete_cb,
-			mainloop);
-	while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
-		pa_threaded_mainloop_wait(mainloop);
-	}
-	pa_operation_unref(o);
-	pa_threaded_mainloop_unlock(mainloop);
-}
+
+JNIEXPORT jint JNICALL Java_org_classpath_icedtea_pulseaudio_PulseAudioTargetDataLine_native_1get_1readable_1size
+(JNIEnv* env, jobject obj) {
+
 
-J
 
-JNIEXPORT void JNICALL
-Java_org_openjdk_sound_PulseAudioSourceDataLine__closeStream(JNIEnv *env, jobject obj, jint channels, jfloat rate) {
-
-	pa_stream *stream = getJavaLongField(env, obj, "streamPointer");
-	pa_strean_disconnect(stream);
-}
-
-JNIEXPORT jint JNICALL Java_org_openjdk_sound_PulseAudioSourceDataLine_available
-(JNIEnv *env, jobject obj) {
-	pa_threaded_mainloop *mainloop = getJavaLongField (env, obj, "mainLoopPointer");
-	pa_stream *stream = getJavaLongField(env, obj, "streamPointer");
-	pa_threaded_mainloop_lock(mainloop);
-	int available = pa_stream_writable_size(stream);
-	pa_threaded_mainloop_unlock(mainloop);
+	pa_stream *stream = (pa_stream*) getJavaPointer(env, obj, "streamPointer");
+	assert(stream);
+	int available = pa_stream_readable_size(stream);
 	return available;
 }
 
-JNIEXPORT void JNICALL Java_org_openjdk_sound_PulseAudioSourceDataLine_drain
-(JNIEnv *env, jobject obj) {
-	pa_threaded_mainloop *mainloop = getJavaLongField (env, obj, "mainLoopPointer");
-	pa_stream *stream = getJavaLongField(env, obj, "streamPointer");
-	pa_threaded_mainloop_lock(mainloop);
-	pa_operation *o = pa_stream_drain(stream, stream_operation_complete_cb, mainloop);
-	while(pa_operation_get_state(o) != PA_OPERATION_DONE) {
-		pa_threaded_mainloop_wait(mainloop);
+JNIEXPORT int JNICALL Java_org_classpath_icedtea_pulseaudio_PulseAudioTargetDataLine_native_1read
+  (JNIEnv *env, jobject obj, jbyteArray array, jint length,  jint offset) {
+	pa_stream *stream = getJavaPointer(env, obj, "streamPointer");
+	const void *read_data = NULL;
+	size_t  read_length = 0;
+	pa_stream_peek(stream, &read_data, &read_length);
+	if (length < read_length) {
+		read_length = length;
 	}
-	pa_operation_unref(o);
-	pa_threaded_mainloop_unlock(mainloop);
-}
 
-JNIEXPORT void JNICALL Java_org_openjdk_sound_PulseAudioSourceDataLine_flush
-(JNIEnv *env, jobject obj) {
-	pa_threaded_mainloop *mainloop = getJavaLongField (env, obj, "mainLoopPointer");
-	pa_stream *stream = getJavaLongField(env, obj, "streamPointer");
-	pa_threaded_mainloop_lock(mainloop);
-	pa_operation *o = pa_stream_flush(stream, stream_operation_complete_cb, mainloop);
-	while(pa_operation_get_state(o) != PA_OPERATION_DONE) {
-		pa_threaded_mainloop_wait(mainloop);
-	}
-	pa_operation_unref(o);
-	pa_threaded_mainloop_unlock(mainloop);
+	(*env)->SetByteArrayRegion(env, array, offset, read_length, read_data);
+	pa_stream_drop(stream);
+	return read_length;
 }
 
-JNIEXPORT void JNICALL Java_org_openjdk_sound_PulseAudioTargetDataLine_readFromStream
-(JNIEnv * env, jobject obj, jbyteArray array, jint length, jint offset);
-pa_threaded_mainloop *mainloop = getJavaLongField(env, obj, "mainLoopPointer");
-pa_stream *stream = getJavaLongField(env, obj, "streamPointer");
-pa_threaded_mainloop_lock(mainloop);
-char[length] data;
-while(length> 0) {
-	size_t l;
-	while(!read_data) {
-		int r = pa_stream_peek(_stream, &read_data, &read_length);
-		if(!read_data) {
-			pa_threaded_mainloop_wait(mainloop);
-		} else {
-			read_index = 0;
-		}
-	}
+JNIEXPORT void JNICALL Java_org_classpath_icedtea_pulseaudio_PulseAudioTargetDataLine_native_1close
+(JNIEnv* env, jobject obj) {
+	pa_stream* stream = (pa_stream*) getJavaPointer(env, obj, "streamPointer");
+	pa_stream_disconnect(stream);
 
-	l = read_length < length ? read_length : length;
-	memcpy(data, read_data+read_index, l);
-
-	data = data + l;
-	length -= l;
-	read_index +=l;
-	read_length-=l;
-
-	if(! read_length) {
-		int r = pa_stream_drop(stream);
-		read_data = NULL;
-		read_length = 0;
-		read_index = 0;
-
-	}
 
 }
 
-pa_threaded_mainloop_unlock(mainloop);
-SetByteArrayRegion(env, array, offset, initialLength, data);
-}
-