changeset 96:9a4c1d255bc6

2008-08-25 Omair Majid <omajid@redhat.com> * src/java/org/classpath/icedtea/pulseaudio/Operation.java (Operation): Added a check for value pointer * src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java Formatting fixes using eclipse's format (connectLine): Parameter bufferSize is now used to chose buffer attributes. (close): Added a synchronized block to protect shared variables. (flush): Likewise. * src/java/org/classpath/icedtea/pulseaudio/PusleAudioDataLine.java (open): Synchronized stream operations. Pass buffer sizes to the connectLine function. Also added checks to ensure that the stream actually connected. (start): Syncrhonized stream operations. (connectLine): changed signature from connectLine() to connectLine(int bufferSize). * src/java/org/classpath/icedtea/pusleaudio/PulseAudioSourceDataLine.java (connectLine): Changed buffer values. Some buffer attributes now guessed from the max buffer size. Synchronized stream operations. * src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java (connectLine): Likewise * src/java/org/classpath/icedtea/pulseaudio/Stream.java (native_pa_stream_connect_record): Added additional parameters for buffers. (addOverflowListener): Synchronized operation. (removeOverflowListener): Likewise. (addUnderflowListener): Likewise. (removeUnderflowListener): Likewise. (addPlaybackStartedListener): Likewise. (removePlaybackStartedListener): Likewise. (addLatencyUpdateListener): Likewise. (removeLatencyUpdateListener): Likewise. (addMovedListener): Likewise. (removeMovedListener): Likewise. (addSuspenedListener): Likewise. (removeSuspenedListener): Likewise. (connectForRecording): Added a paramter to pass in StreamBufferAttributes. * src/java/org/classpath/icedtea/pulseaudio/StreamBufferAttributes.java Fixed SANE_DEFAULT to be a valid numer. Added extra variables to indicate max and min values. * src/native/org_classpath_icedtea_pulseaudio_Stream.c (stream_state_callback): Commented out debug output. (stream_overflow_callback): Likewise. (stream_latency_update_callback): Likewise. (stream_moved_callback): Likewise. (stream_suspeneded_callback): Likewise. (Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1connect_1playback): Using bufferAttributes passed in to connnect the stream. Commented out debug info. Added a check for return value. (Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1connect_1record): Likewise. (Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1get_1buffer_1attr): Fixed constructor method id. Added checks to fail if this happens again. (set_buffer_attr_callback): Checking the new buffer_attr (Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1set_1buffer_1attr): Added tests to check if everything works. Added some debug info. * unittests/org/classpath/icedtea/pusleaudio/PulseSourceDataLineTest.java: (testPlay): Added some debug output for the test
author Omair Majid <omajid@redhat.com>
date Mon, 25 Aug 2008 12:07:04 -0400
parents 23c52715cfb2
children 2e4a2a022ffb
files src/java/org/classpath/icedtea/pulseaudio/Operation.java src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java src/java/org/classpath/icedtea/pulseaudio/Stream.java src/java/org/classpath/icedtea/pulseaudio/StreamBufferAttributes.java src/native/org_classpath_icedtea_pulseaudio_Stream.c unittests/org/classpath/icedtea/pulseaudio/PulseSourceDataLineTest.java
diffstat 9 files changed, 273 insertions(+), 169 deletions(-) [+]
line wrap: on
line diff
--- a/src/java/org/classpath/icedtea/pulseaudio/Operation.java	Wed Aug 20 14:27:45 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/Operation.java	Mon Aug 25 12:07:04 2008 -0400
@@ -77,6 +77,7 @@
 	private native int native_get_state();
 
 	public Operation(long operationPointer) {
+		assert(operationPointer != 0);
 		this.operationPointer = operationPointer;
 		this.eventLoop = EventLoop.getEventLoop();
 	}
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java	Wed Aug 20 14:27:45 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java	Mon Aug 25 12:07:04 2008 -0400
@@ -71,68 +71,64 @@
 	private static final AudioFormat DEFAULT_FORMAT = new AudioFormat(
 			AudioFormat.Encoding.PCM_UNSIGNED, 22050, 8, 2, 2, 22050 / 2, false);
 
-
-
 	private static final int DEFAULT_BUFFER_SIZE = 0;
 	public static final String DEFAULT_CLIP_NAME = "Clip";
 
 	private Object clipLock = new Object();
 	private boolean clipThreadStarted;
 	private int loopsLeft;
-	private Semaphore clipSemaphore= new Semaphore(1);
+	private Semaphore clipSemaphore = new Semaphore(1);
 
 	private class ClipThread extends Thread {
 		@Override
 		public void run() {
 			clipThreadStarted = true;
-			while(loopsLeft >= 0) {
+			while (loopsLeft >= 0) {
 				writeFrames(currentFrame, endFrame + 1);
-				if(Thread.interrupted()) {
+				if (Thread.interrupted()) {
 					//Thread.currentThread().interrupt();
 					clipThreadStarted = false;
 					return;
 				}
-			
-				
-		
-			try {
-				//if loop(0) has been called from the mainThread,
-				//wait until loopsLeft has been set
-				clipSemaphore.acquire();
-				if(loopsLeft == 0){
+
+				try {
+					//if loop(0) has been called from the mainThread,
+					//wait until loopsLeft has been set
+					clipSemaphore.acquire();
+					if (loopsLeft == 0) {
 						System.out.println("Reading to the end of the file");
-						writeFrames( endFrame, getFrameLength());
+						writeFrames(endFrame, getFrameLength());
 						return;
-				} else {
-					synchronized (clipLock) {
-						currentFrame = startFrame;
-						loopsLeft--;
+					} else {
+						synchronized (clipLock) {
+							currentFrame = startFrame;
+							loopsLeft--;
+						}
 					}
+					clipSemaphore.release();
+				} catch (InterruptedException e) {
+					return;
 				}
-				clipSemaphore.release();
-			} catch (InterruptedException e) {
-					return;
-			}
-					
-					
+
 			}
 		}
 	}
-	
-	private ClipThread clipThread; 
-			
-	
+
+	private ClipThread clipThread;
+
 	private void writeFrames(int startingFrame, int lastFrame) {
 
 		int remainingFrames = lastFrame - startingFrame - 1;
-		while(remainingFrames > 0) {
+		while (remainingFrames > 0) {
 			synchronized (eventLoop.threadLock) {
 				int availableSize = stream.getWritableSize();
-				int framesToWrite = Math.min(remainingFrames, availableSize / getFormat().getFrameSize());
-				stream.write(data, currentFrame * getFormat().getFrameSize(), framesToWrite * getFormat().getFrameSize());
+				int framesToWrite = Math.min(remainingFrames, availableSize
+						/ getFormat().getFrameSize());
+				stream.write(data, currentFrame * getFormat().getFrameSize(),
+						framesToWrite * getFormat().getFrameSize());
 				remainingFrames -= framesToWrite;
 				currentFrame += framesToWrite;
-				if(Thread.interrupted()) {
+				if (Thread.interrupted()) {
 					Thread.currentThread().interrupt();
 					break;
 				}
@@ -141,7 +137,6 @@
 		}
 	}
 
-	
 	static {
 		try {
 			String library = new java.io.File(".").getCanonicalPath()
@@ -154,27 +149,23 @@
 		}
 	}
 
-	public PulseAudioClip(EventLoop eventLoop, AudioFormat[] formats, AudioFormat defaultFormat) {
+	public PulseAudioClip(EventLoop eventLoop, AudioFormat[] formats,
+			AudioFormat defaultFormat) {
 		supportedFormats = formats;
 		this.eventLoop = eventLoop;
 		this.lineListeners = new ArrayList<LineListener>();
-		this.defaultFormat = defaultFormat; 
+		this.defaultFormat = defaultFormat;
 		this.currentFormat = defaultFormat;
 		clipThread = new ClipThread();
 
 	}
-	
-	protected void connectLine() {
+
+	protected void connectLine(int bufferSize) {
 		StreamBufferAttributes bufferAttributes = new StreamBufferAttributes(
-				StreamBufferAttributes.SANE_DEFAULT,
-				StreamBufferAttributes.SANE_DEFAULT,
-				StreamBufferAttributes.SANE_DEFAULT,
-				StreamBufferAttributes.SANE_DEFAULT,
-				StreamBufferAttributes.SANE_DEFAULT);
+				bufferSize, bufferSize / 2, bufferSize / 2, bufferSize / 2, 0);
 
-		stream.connectForPlayback(null, bufferAttributes);
+		stream.connectForPlayback(Stream.DEFAULT_DEVICE, bufferAttributes);
 	}
-	
 
 	@Override
 	public int available() {
@@ -188,10 +179,12 @@
 		} catch (InterruptedException e) {
 			e.printStackTrace();
 		}
-		
-		stream.drain();
-		stream.disconnect();
-		isOpen = false;
+
+		synchronized (eventLoop.threadLock) {
+			drain();
+			stream.disconnect();
+			isOpen = false;
+		}
 	}
 
 	@Override
@@ -209,7 +202,9 @@
 
 	@Override
 	public void flush() {
-		stream.flush();
+		synchronized (eventLoop.threadLock) {
+			stream.flush();
+		}
 	}
 
 	@Override
@@ -260,7 +255,7 @@
 
 	@Override
 	public long getLongFramePosition() {
-		synchronized(clipLock) {
+		synchronized (clipLock) {
 			return framesSinceOpen;
 		}
 	}
@@ -270,14 +265,14 @@
 		if (!isOpen) {
 			return AudioSystem.NOT_SPECIFIED;
 		}
-		synchronized(clipLock) {
+		synchronized (clipLock) {
 			return frameCount / currentFormat.getFrameSize();
 		}
 	}
 
 	@Override
 	public long getMicrosecondPosition() {
-		synchronized(clipLock) {
+		synchronized (clipLock) {
 			return framesSinceOpen / currentFormat.getFrameSize();
 		}
 	}
@@ -308,22 +303,22 @@
 	@Override
 	public void loop(int count) {
 		System.out.println("Loop " + count + " called");
-		if(clipThreadStarted && count != 0) {
+		if (clipThreadStarted && count != 0) {
 			//Do nothing; behavior not specified by the Java API 
 			return;
 		}
-		synchronized(clipLock) {
-			if(currentFrame > endFrame) {
+		synchronized (clipLock) {
+			if (currentFrame > endFrame) {
 				loopsLeft = 0;
 			} else {
-				loopsLeft = count; 
+				loopsLeft = count;
 			}
 		}
 		if (!clipThread.isAlive()) {
 			clipThread = new ClipThread();
 			clipThread.start();
-		} 
-			
+		}
+
 	}
 
 	@Override
@@ -338,7 +333,7 @@
 		super.open(format);
 		this.data = new byte[bufferSize];
 		System.arraycopy(data, offset, this.data, 0, bufferSize);
-		frameCount =  bufferSize / format.getFrameSize();
+		frameCount = bufferSize / format.getFrameSize();
 		isOpen = true;
 	}
 
@@ -350,7 +345,6 @@
 		stream.read(buffer, 0, buffer.length);
 
 		open(stream.getFormat(), buffer, 0, buffer.length);
-	
 
 	}
 
@@ -365,8 +359,8 @@
 		if (frames > frameCount) {
 			throw new IllegalArgumentException("incorreft frame value");
 		}
-		
-		synchronized(clipLock) {
+
+		synchronized (clipLock) {
 			currentFrame = frames;
 		}
 
@@ -383,7 +377,7 @@
 					"ending point must be greater than or equal to the starting point");
 		}
 
-		synchronized(clipLock) {
+		synchronized (clipLock) {
 			startFrame = start;
 			endFrame = end;
 		}
@@ -393,7 +387,7 @@
 	@Override
 	public void setMicrosecondPosition(long microseconds) {
 		float frameIndex = microseconds * currentFormat.getFrameRate();
-		synchronized(clipLock) {
+		synchronized (clipLock) {
 			currentFrame = (int) frameIndex;
 		}
 
@@ -401,18 +395,17 @@
 
 	@Override
 	public void start() {
-		if(!clipThread.isAlive()) {
-			synchronized(clipLock) {
+		if (!clipThread.isAlive()) {
+			synchronized (clipLock) {
 				loopsLeft = 0;
 			}
 			clipThread = new ClipThread();
 			clipThread.start();
 		}
 	}
-	
-	
+
 	public void stop() {
-		if(clipThread.isAlive()) {
+		if (clipThread.isAlive()) {
 			clipThread.interrupt();
 		}
 		try {
@@ -420,7 +413,7 @@
 		} catch (InterruptedException e) {
 			e.printStackTrace();
 		}
-		synchronized(clipLock) {
+		synchronized (clipLock) {
 			currentFrame = 0;
 			loopsLeft = 0;
 		}
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java	Wed Aug 20 14:27:45 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java	Mon Aug 25 12:07:04 2008 -0400
@@ -14,40 +14,38 @@
 
 public abstract class PulseAudioDataLine implements Line {
 
-	protected static final int DEFAULT_BUFFER_SIZE = 1000;
+	protected static final int DEFAULT_BUFFER_SIZE = StreamBufferAttributes.SANE_DEFAULT;
 	protected static final String PULSEAUDIO_FORMAT_KEY = "PulseAudioFormatKey";
 
 	protected String streamName = "Java Stream";
-	
+
 	protected boolean isOpen = false;
 	private boolean isPaused = false;
 	protected AudioFormat[] supportedFormats = null;
 	protected AudioFormat currentFormat = null;
 	protected AudioFormat defaultFormat = null;
-	
-	
+
 	protected List<LineListener> lineListeners = new ArrayList<LineListener>();;
-	
+
 	protected EventLoop eventLoop = null;
 	protected Semaphore semaphore = new Semaphore(0);
 	protected Stream stream;
-	
-	
-	
+
 	public void open(AudioFormat format, int bufferSize)
-	throws LineUnavailableException {
+			throws LineUnavailableException {
 		if (isOpen) {
 			throw new IllegalStateException("Line is already open");
 		}
 
-		// ignore suggested buffer size
-
 		for (AudioFormat myFormat : supportedFormats) {
 			if (format.matches(myFormat)) {
-				stream = new Stream(eventLoop.getContextPointer(), streamName,
-						Stream.Format.valueOf((String) myFormat
-								.getProperty(PULSEAUDIO_FORMAT_KEY)),
-								(int) format.getSampleRate(), format.getChannels());
+				synchronized (eventLoop.threadLock) {
+					stream = new Stream(eventLoop.getContextPointer(),
+							streamName, Stream.Format.valueOf((String) myFormat
+									.getProperty(PULSEAUDIO_FORMAT_KEY)),
+							(int) format.getSampleRate(), format.getChannels());
+
+				}
 				currentFormat = format;
 				isOpen = true;
 			}
@@ -61,40 +59,47 @@
 
 			@Override
 			public void update() {
-				if (stream.getState() == Stream.State.READY) {
-					fireLineEvent(new LineEvent(PulseAudioDataLine.this,
-							LineEvent.Type.OPEN, AudioSystem.NOT_SPECIFIED));
-					semaphore.release();
-				} else if (stream.getState() == Stream.State.TERMINATED
-						|| stream.getState() == Stream.State.FAILED) {
-					fireLineEvent((new LineEvent(PulseAudioDataLine.this,
-							LineEvent.Type.CLOSE, AudioSystem.NOT_SPECIFIED)));
-					semaphore.release();
+				synchronized (eventLoop.threadLock) {
+					if (stream.getState() == Stream.State.READY) {
+						fireLineEvent(new LineEvent(PulseAudioDataLine.this,
+								LineEvent.Type.OPEN, AudioSystem.NOT_SPECIFIED));
+						semaphore.release();
+					} else if (stream.getState() == Stream.State.TERMINATED
+							|| stream.getState() == Stream.State.FAILED) {
+						fireLineEvent((new LineEvent(PulseAudioDataLine.this,
+								LineEvent.Type.CLOSE, AudioSystem.NOT_SPECIFIED)));
+						semaphore.release();
+					}
 				}
 			}
-
 		};
 
 		stream.addStateListener(openCloseListener);
 
 		synchronized (eventLoop.threadLock) {
-
-			connectLine();
+			connectLine(bufferSize);
 		}
 
 		try {
 			semaphore.acquire();
+			synchronized (eventLoop.threadLock) {
+				if (stream.getState() != Stream.State.READY) {
+					stream.disconnect();
+					throw new LineUnavailableException(
+							"unable to obtain a line");
+				}
+			}
 		} catch (InterruptedException e) {
-			// throw new LineUnavailableException("unable to prepare
-			// stream");
+			throw new LineUnavailableException("unable to prepare stream");
 		}
+
 	}
-	
+
 	public void open(AudioFormat format) throws LineUnavailableException {
 		open(format, DEFAULT_BUFFER_SIZE);
 
 	}
-	
+
 	public void open() throws LineUnavailableException {
 		// pick a random format
 		if (defaultFormat == null) {
@@ -105,12 +110,11 @@
 		open(defaultFormat, DEFAULT_BUFFER_SIZE);
 	}
 
-	
 	public void close() {
 		assert (isOpen);
 
 		synchronized (eventLoop.threadLock) {
-			//drain();
+			// drain();
 			stream.disconnect();
 		}
 
@@ -122,10 +126,12 @@
 		}
 
 	}
-	
+
 	public void start() {
 		if (isPaused) {
-			stream.cork(false);
+			synchronized (eventLoop.threadLock) {
+				stream.cork(false);
+			}
 			isPaused = false;
 		}
 
@@ -143,7 +149,7 @@
 		isPaused = true;
 
 	}
-	
+
 	public void addLineListener(LineListener listener) {
 		this.lineListeners.add(listener);
 	}
@@ -151,20 +157,19 @@
 	public void removeLineListener(LineListener listener) {
 		this.lineListeners.remove(listener);
 	}
-	
+
 	private void fireLineEvent(LineEvent e) {
 		for (LineListener lineListener : lineListeners) {
 			lineListener.update(e);
 		}
 	}
 
-	abstract void connectLine();
+	abstract void connectLine(int bufferSize);
+
 	abstract void drain();
-	
+
 	public boolean isOpen() {
 		return isOpen;
 	}
 
-	
-
 }
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java	Wed Aug 20 14:27:45 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java	Mon Aug 25 12:07:04 2008 -0400
@@ -100,15 +100,13 @@
 
 	}
 
-	protected void connectLine() {
+	protected void connectLine(int bufferSize) {
 		StreamBufferAttributes bufferAttributes = new StreamBufferAttributes(
-				StreamBufferAttributes.SANE_DEFAULT,
-				StreamBufferAttributes.SANE_DEFAULT,
-				StreamBufferAttributes.SANE_DEFAULT,
-				StreamBufferAttributes.SANE_DEFAULT,
-				StreamBufferAttributes.SANE_DEFAULT);
+				bufferSize, bufferSize / 4, bufferSize / 4, bufferSize / 10, 0);
 
-		stream.connectForPlayback(null, bufferAttributes);
+		synchronized (eventLoop.threadLock) {
+			stream.connectForPlayback(Stream.DEFAULT_DEVICE, bufferAttributes);
+		}
 	}
 
 	@Override
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java	Wed Aug 20 14:27:45 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java	Mon Aug 25 12:07:04 2008 -0400
@@ -68,8 +68,12 @@
 
 	}
 
-	protected void connectLine() {
-		stream.connectForRecording(null);
+	protected void connectLine(int bufferSize) {
+		StreamBufferAttributes bufferAttributes = new StreamBufferAttributes(
+				bufferSize, 0, 0, 0, bufferSize / 10);
+		synchronized (eventLoop.threadLock) {
+			stream.connectForRecording(Stream.DEFAULT_DEVICE, bufferAttributes);
+		}
 	}
 
 	@Override
--- a/src/java/org/classpath/icedtea/pulseaudio/Stream.java	Wed Aug 20 14:27:45 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/Stream.java	Mon Aug 25 12:07:04 2008 -0400
@@ -55,6 +55,8 @@
 		PA_SAMPLE_U8, PA_SAMPLE_ULAW, PA_SAMPLE_ALAW, PA_SAMPLE_S16LE, PA_SAMPLE_S16BE, PA_SAMPLE_FLOAT32LE, PA_SAMPLE_FLOAT32BE, PA_SAMPLE_S32LE, PA_SAMPLE_S32BE
 	}
 
+	public static final String DEFAULT_DEVICE = null;
+
 	@SuppressWarnings("unused")
 	private long streamPointer;
 
@@ -91,8 +93,11 @@
 			int bufferFragmentSize, int flags, long volumePointer,
 			long sync_streamPointer);
 
-	private native int native_pa_stream_connect_record(String device,
-			long buffer_attrPointer, int flags);
+	private native int native_pa_stream_connect_record(String name,
+			int bufferMaxLength, int bufferTargetLength,
+			int bufferPreBuffering, int bufferMinimumRequest,
+			int bufferFragmentSize, int flags, long volumePointer,
+			long sync_streamPointer);
 
 	private native int native_pa_stream_disconnect();
 
@@ -232,51 +237,75 @@
 	}
 
 	public void addOverflowListener(OverflowListener listener) {
-		overflowListeners.add(listener);
+		synchronized (overflowListeners) {
+			overflowListeners.add(listener);
+		}
 	}
 
 	public void removeOverflowListener(OverflowListener listener) {
-		overflowListeners.remove(listener);
+		synchronized (overflowListeners) {
+			overflowListeners.remove(listener);
+		}
 	}
 
 	public void addUnderflowListener(UnderflowListener listener) {
-		underflowListeners.add(listener);
+		synchronized (underflowListeners) {
+			underflowListeners.add(listener);
+		}
 	}
 
 	public void removeUnderflowListener(UnderflowListener listener) {
-		underflowListeners.remove(listener);
+		synchronized (underflowListeners) {
+			underflowListeners.remove(listener);
+		}
 	}
 
 	public void addPlaybackStartedListener(PlaybackStartedListener listener) {
-		playbackStartedListeners.add(listener);
+		synchronized (playbackStartedListeners) {
+			playbackStartedListeners.add(listener);
+		}
 	}
 
 	public void removePlaybackStartedListener(PlaybackStartedListener listener) {
-		playbackStartedListeners.remove(listener);
+		synchronized (playbackStartedListeners) {
+			playbackStartedListeners.remove(listener);
+		}
 	}
 
 	public void addLatencyUpdateListener(LatencyUpdateListener listener) {
-		latencyUpdateListeners.add(listener);
+		synchronized (latencyUpdateListeners) {
+			latencyUpdateListeners.add(listener);
+		}
 	}
 
 	public void removeLatencyUpdateListener(LatencyUpdateListener listener) {
-		latencyUpdateListeners.remove(listener);
+		synchronized (playbackStartedListeners) {
+			latencyUpdateListeners.remove(listener);
+		}
 	}
 
 	public void addMovedListener(MovedListener listener) {
-		movedListeners.add(listener);
+		synchronized (movedListeners) {
+			movedListeners.add(listener);
+		}
 	}
 
 	public void removeMovedListener(MovedListener listener) {
-		movedListeners.remove(listener);
+		synchronized (movedListeners) {
+			movedListeners.remove(listener);
+		}
 	}
 
 	public void addSuspendedListener(SuspendedListener listener) {
-		suspendedListeners.add(listener);
+		synchronized (suspendedListeners) {
+			suspendedListeners.add(listener);
+		}
 	}
 
 	public void removeSuspendedListener(SuspendedListener listener) {
-		suspendedListeners.remove(listener);
+		synchronized (suspendedListeners) {
+			suspendedListeners.remove(listener);
+		}
 	}
 
 	public Stream.State getState() {
@@ -337,6 +366,8 @@
 	 * Connect the stream to a sink
 	 * 
 	 * @param deviceName
+	 *            the device to connect to. use
+	 *            <code>null</code for the default device
 	 */
 	public void connectForPlayback(String deviceName,
 			StreamBufferAttributes bufferAttributes) {
@@ -353,8 +384,14 @@
 	 * Connect the stream to a source.
 	 * 
 	 */
-	public void connectForRecording(String deviceName) {
-		int returnValue = native_pa_stream_connect_record(deviceName, 0, 0);
+	public void connectForRecording(String deviceName,
+			StreamBufferAttributes bufferAttributes) {
+
+		int returnValue = native_pa_stream_connect_record(deviceName,
+				bufferAttributes.getMaxLength(), bufferAttributes
+						.getTargetLength(), bufferAttributes.getPreBuffering(),
+				bufferAttributes.getMinimumRequest(), bufferAttributes
+						.getFragmentSize(), 0, 0, 0);
 		assert (returnValue == 0);
 	}
 
@@ -459,6 +496,7 @@
 
 	@SuppressWarnings("unused")
 	private void overflowCallback() {
+		System.out.println("overflowCallback called");
 		synchronized (overflowListeners) {
 			for (OverflowListener listener : overflowListeners) {
 				listener.update();
--- a/src/java/org/classpath/icedtea/pulseaudio/StreamBufferAttributes.java	Wed Aug 20 14:27:45 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/StreamBufferAttributes.java	Mon Aug 25 12:07:04 2008 -0400
@@ -2,8 +2,14 @@
 
 public class StreamBufferAttributes {
 
-	public static final int SANE_DEFAULT = -1;
+	public static final int SANE_DEFAULT = 50000;
 
+	
+	// FIXME need to set these to proper values
+	// integer.max_value will crash the program!
+	public static final int MAX_VALUE = Integer.MAX_VALUE;
+	public static final int MIN_VALUE = 0;
+	
 	private int maxLength;
 	private int targetLength;
 	private int preBuffering;
--- a/src/native/org_classpath_icedtea_pulseaudio_Stream.c	Wed Aug 20 14:27:45 2008 -0400
+++ b/src/native/org_classpath_icedtea_pulseaudio_Stream.c	Mon Aug 25 12:07:04 2008 -0400
@@ -62,7 +62,7 @@
 }
 
 static void stream_state_callback(pa_stream* stream, void *userdata) {
-	printf("stream_state_callback called\n");
+	//printf("stream_state_callback called\n");
 
 	java_context* context = userdata;
 	assert(stream);
@@ -114,7 +114,7 @@
 }
 
 static void stream_overflow_callback(pa_stream *stream, void *userdata) {
-	printf("stream_overflow_callback called\n");
+	//printf("stream_overflow_callback called\n");
 
 	java_context* context = userdata;
 	assert(stream);
@@ -165,7 +165,7 @@
  */
 
 static void stream_latency_update_callback(pa_stream *stream, void *userdata) {
-	printf("stream_latency_update_callback called\n");
+	//	printf("stream_latency_update_callback called\n");
 
 	java_context* context = userdata;
 	assert(stream);
@@ -182,7 +182,7 @@
 }
 
 static void stream_moved_callback(pa_stream *stream, void *userdata) {
-	printf("stream_moved_callback called\n");
+	//	printf("stream_moved_callback called\n");
 
 	java_context* context = userdata;
 	assert(stream);
@@ -199,7 +199,7 @@
 }
 
 static void stream_suspended_callback(pa_stream *stream, void *userdata) {
-	printf("stream_suspended_callback called\n");
+	//	printf("stream_suspended_callback called\n");
 
 	java_context* context = userdata;
 	assert(stream);
@@ -224,7 +224,7 @@
 (JNIEnv* env, jobject obj, jlong contextPointer, jstring nameString,
 		jstring encodingString, jint sampleRate, jint channels) {
 
-	printf("creating a new PulseAudio stream\n");
+	//	printf("creating a new PulseAudio stream\n");
 
 	java_context* j_context = malloc(sizeof(java_context));
 	assert(j_context);
@@ -379,13 +379,22 @@
 		jlong volumePointer, jlong sync_streamPointer) {
 	pa_stream* stream = (pa_stream*) getJavaPointer(env, obj, "streamPointer");
 
-	pa_buffer_attr buffer_attributes;
+	pa_buffer_attr buffer_attr;
+
+	memset(&buffer_attr, 0, sizeof(buffer_attr));
 
-	buffer_attributes.maxlength = bufferMaxLength;
-	buffer_attributes.tlength = bufferTargetLength;
-	buffer_attributes.prebuf = bufferPreBuffering;
-	buffer_attributes.minreq = bufferMinimumRequest;
-	buffer_attributes.fragsize = bufferFragmentSize;
+	buffer_attr.maxlength = (uint32_t) bufferMaxLength;
+	buffer_attr.tlength = (uint32_t) bufferTargetLength;
+	buffer_attr.prebuf = (uint32_t) bufferPreBuffering;
+	buffer_attr.minreq = (uint32_t) bufferMinimumRequest;
+
+	/*
+	 printf("buffer maxlength: %u\n", buffer_attr.maxlength);
+	 printf("buffer tlength: %u\n", buffer_attr.tlength);
+	 printf("buffer prebuf: %u\n", buffer_attr.prebuf);
+	 printf("buffer minreq: %u\n", buffer_attr.minreq);
+	 printf("buffer fragsize: %u\n", buffer_attr.fragsize);
+	 */
 
 	const char* dev = NULL;
 	if (device != NULL) {
@@ -394,7 +403,8 @@
 			return -1; // oome thrown
 		}
 	}
-	int value = pa_stream_connect_playback(stream, dev, NULL, 0, NULL, NULL);
+	int value = pa_stream_connect_playback(stream, dev, &buffer_attr, 0, NULL, NULL);
+	assert(value >= 0);
 	if (dev != NULL) {
 		(*env)->ReleaseStringUTFChars(env, device, dev);
 		dev = NULL;
@@ -405,12 +415,30 @@
 /*
  * Class:     org_classpath_icedtea_pulseaudio_Stream
  * Method:    native_pa_stream_connect_record
- * Signature: (Ljava/lang/String;JI)I
+ * Signature: (Ljava/lang/String;IIIIIIJJ)I
  */
 JNIEXPORT jint JNICALL Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1connect_1record
-(JNIEnv* env, jobject obj, jstring device, jlong bufferPointer, jint flags) {
+(JNIEnv* env, jobject obj, jstring device, jint bufferMaxLength,
+		jint bufferTargetLength, jint bufferPreBuffering,
+		jint bufferMinimumRequest, jint bufferFragmentSize, jint flags,
+		jlong volumePointer, jlong sync_streamPointer) {
+
 	pa_stream* stream = (pa_stream*)getJavaPointer(env, obj, "streamPointer");
 	assert(stream);
+
+	pa_buffer_attr buffer_attr;
+	memset(&buffer_attr, 0 , sizeof(buffer_attr));
+	buffer_attr.maxlength = (uint32_t) bufferMaxLength;
+	buffer_attr.fragsize = (uint32_t) bufferFragmentSize;
+
+	/*
+	 printf("buffer maxlength: %u\n", buffer_attr.maxlength);
+	 printf("buffer tlength: %u\n", buffer_attr.tlength);
+	 printf("buffer prebuf: %u\n", buffer_attr.prebuf);
+	 printf("buffer minreq: %u\n", buffer_attr.minreq);
+	 printf("buffer fragsize: %u\n", buffer_attr.fragsize);
+	 */
+
 	const char* dev = NULL;
 	if (device != NULL) {
 		dev = (*env)->GetStringUTFChars(env, device, NULL);
@@ -418,9 +446,10 @@
 			return -1; // oome thrown
 		}
 	}
-	pa_buffer_attr* buffer = convertJavaLongToPointer(bufferPointer);
-	assert(buffer == NULL);
-	int value = pa_stream_connect_record(stream, dev, buffer, flags);
+
+	int value = pa_stream_connect_record(stream, dev, &buffer_attr, flags);
+	assert(value >= 0);
+
 	if (dev != NULL) {
 		(*env)->ReleaseStringUTFChars(env, device, dev);
 		dev = NULL;
@@ -709,14 +738,19 @@
  */
 JNIEXPORT jobject JNICALL Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1get_1buffer_1attr
 (JNIEnv* env, jobject obj) {
+
+	//	printf("in native_pa_stream_get_buffer_attributes");
+
 	pa_stream* stream = (pa_stream*)getJavaPointer(env, obj, "streamPointer");
 	assert(stream);
 	const pa_buffer_attr* buffer = pa_stream_get_buffer_attr(stream);
+	assert(buffer);
 
 	const char* class_name = "Lorg/classpath/icedtea/pulseaudio/StreamBufferAttributes;";
 	jclass cls = (*env)->FindClass(env, class_name);
-	jmethodID constructor_mid = (*env)->GetMethodID(env, cls, "<init>", "V");
-
+	assert(cls);
+	jmethodID constructor_mid = (*env)->GetMethodID(env, cls, "<init>", "(IIIII)V");
+	assert(constructor_mid);
 	jint maxLength = buffer->maxlength;
 	jint targetLength = buffer->tlength;
 	jint preBuffering = buffer->prebuf;
@@ -725,18 +759,23 @@
 
 	jobject return_object = (*env)->NewObject(env, cls, constructor_mid, maxLength, targetLength,
 			preBuffering, minimumRequest, fragmentSize);
+	assert(return_object);
 
 	return return_object;
 }
 
-static void set_buffer_attr_callback(pa_stream* stream, int success, void* userdata) {
+static void set_buffer_attr_callback(pa_stream* stream, int success,
+		void* userdata) {
+
+	const pa_buffer_attr* buffer = pa_stream_get_buffer_attr(stream);
+	assert(buffer);
+
 	assert(success);
 	JNIEnv* env = pulse_thread_env;
 	notifyWaitingOperations(env);
 
 }
 
-
 /*
  * Class:     org_classpath_icedtea_pulseaudio_Stream
  * Method:    native_pa_stream_set_buffer_attr
@@ -749,30 +788,46 @@
 	assert(stream);
 
 	jclass cls = (*env)->GetObjectClass(env, bufferAttributeObject);
+	assert(cls);
 
-	pa_buffer_attr* buffer = malloc(sizeof(pa_buffer_attr));
+	pa_buffer_attr buffer;
 
 	jmethodID getMaxLengthID = (*env)->GetMethodID(env,cls,"getMaxLength","()I");
-	buffer->maxlength = (*env)->CallIntMethod(env, bufferAttributeObject, getMaxLengthID);
+	assert(getMaxLengthID);
+	buffer.maxlength = (uint32_t) (*env)->CallIntMethod(env, bufferAttributeObject, getMaxLengthID);
 
 	jmethodID getTargetLengthID = (*env)->GetMethodID(env,cls,"getTargetLength","()I");
-	buffer->tlength = (*env)->CallIntMethod(env, bufferAttributeObject, getTargetLengthID);
+	assert(getTargetLengthID);
+	buffer.tlength = (uint32_t) (*env)->CallIntMethod(env, bufferAttributeObject, getTargetLengthID);
 
 	jmethodID getPreBufferingID = (*env)->GetMethodID(env,cls,"getPreBuffering","()I");
-	buffer->prebuf = (*env)->CallIntMethod(env, bufferAttributeObject, getPreBufferingID);
+	assert(getPreBufferingID);
+	buffer.prebuf = (uint32_t) (*env)->CallIntMethod(env, bufferAttributeObject, getPreBufferingID);
 
-	jmethodID getMinimumRequestID = (*env)->GetMethodID(env,cls,"getMinimumRequest ","()I");
-	buffer->minreq = (*env)->CallIntMethod(env, bufferAttributeObject, getMinimumRequestID );
+	jmethodID getMinimumRequestID = (*env)->GetMethodID(env, cls, "getMinimumRequest", "()I");
+	assert(getMinimumRequestID);
+	buffer.minreq = (uint32_t) (*env)->CallIntMethod(env, bufferAttributeObject, getMinimumRequestID );
 
 	jmethodID getFragmentSizeID = (*env)->GetMethodID(env,cls,"getFragmentSize","()I");
-	buffer->fragsize = (*env)->CallIntMethod(env, bufferAttributeObject, getFragmentSizeID );
+	assert(getFragmentSizeID);
+	buffer.fragsize = (uint32_t) (*env)->CallIntMethod(env, bufferAttributeObject, getFragmentSizeID );
+
+	/*
+	 const pa_buffer_attr* old_buffer = pa_stream_get_buffer_attr(stream);
 
-	pa_operation* operation = pa_stream_set_buffer_attr(stream, buffer, set_buffer_attr_callback, NULL);
+	 printf("old buffer values: %u %u %u %u %u\n", old_buffer->maxlength, old_buffer->tlength, old_buffer->prebuf, old_buffer->minreq, old_buffer->fragsize);
+
+	 printf("want these values: %u %u %u %u %u\n", buffer.maxlength, buffer.tlength, buffer.prebuf, buffer.minreq, buffer.fragsize);
+	 */
+
+	pa_operation* operation = pa_stream_set_buffer_attr(stream, &buffer, set_buffer_attr_callback, NULL);
+
 	assert(operation);
 	return convertPointerToJavaLong(operation);
 }
 
-static void update_sample_rate_callback(pa_stream* stream, int success, void* userdata) {
+static void update_sample_rate_callback(pa_stream* stream, int success,
+		void* userdata) {
 	assert(success);
 	JNIEnv* env = pulse_thread_env;
 	notifyWaitingOperations(env);
--- a/unittests/org/classpath/icedtea/pulseaudio/PulseSourceDataLineTest.java	Wed Aug 20 14:27:45 2008 -0400
+++ b/unittests/org/classpath/icedtea/pulseaudio/PulseSourceDataLineTest.java	Mon Aug 25 12:07:04 2008 -0400
@@ -94,8 +94,9 @@
 		Assert.assertNotNull(line);
 
 		line.open(audioFormat);
+		System.out.println("opened");
 		line.start();
-
+		System.out.println("started");
 		byte[] abData = new byte[1000];
 		int bytesRead = 0;
 
@@ -105,9 +106,12 @@
 				line.write(abData, 0, bytesRead);
 			}
 		}
+		System.out.println("done");
 
 		line.flush();
+		System.out.println("flushed");
 		line.close();
+		System.out.println("closed");
 
 	}