changeset 151:350bc72ba495

2008-09-29 Omair Majid <omajid@redhat.com> * src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java fragmentBuffer is a new class variable that stores partial fragments. (read): Modified function to work mostly in java. Also takes care of partial fragments that occur when PulseAudio's fragment size does not match the buffer length provided by java. * src/java/org/classpath/icedtea/pulseaudio/Stream.java (native_pa_stream_read): Removed function. (read): Removed function. * src/native/org_classpath_icedtea_pulseaudio_Stream.c (Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1read): Commented out function. * unittests/org/classpath/icedtea/pulseadio/PulseAudioTargetDataLineTest.java (testOpenWithFormat): Fixed output message.
author Omair Majid <omajid@redhat.com>
date Mon, 29 Sep 2008 15:16:15 -0400
parents f508adb1c908
children b185dd9217bb
files src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java src/java/org/classpath/icedtea/pulseaudio/Stream.java src/native/org_classpath_icedtea_pulseaudio_Stream.c unittests/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLineTest.java
diffstat 4 files changed, 83 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java	Mon Sep 29 13:50:48 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java	Mon Sep 29 15:16:15 2008 -0400
@@ -45,6 +45,13 @@
 public class PulseAudioTargetDataLine extends PulseAudioDataLine implements
 		TargetDataLine {
 
+	/*
+	 * This contains the data from the PulseAudio buffer that has since been
+	 * dropped. If 20 bytes of a fragment of size 200 are read, the other 180
+	 * are dumped in this
+	 */
+	byte[] fragmentBuffer;
+
 	public PulseAudioTargetDataLine(EventLoop eventLoop, AudioFormat[] formats,
 			AudioFormat defaultFormat) {
 		supportedFormats = formats;
@@ -89,8 +96,9 @@
 
 	protected void connectLine(int bufferSize, Stream masterStream)
 			throws LineUnavailableException {
+		int fragmentSize = bufferSize / 10 > 500 ? bufferSize / 10 : 500;
 		StreamBufferAttributes bufferAttributes = new StreamBufferAttributes(
-				bufferSize, 0, 0, 0, bufferSize / 10);
+				bufferSize, 0, 0, 0, fragmentSize);
 		synchronized (eventLoop.threadLock) {
 			stream.connectForRecording(Stream.DEFAULT_DEVICE, bufferAttributes);
 		}
@@ -123,24 +131,87 @@
 		int remainingLength = length;
 		int sizeRead = 0;
 
+		/* bytes read on each iteration of loop */
+		int bytesRead;
+
+		/*
+		 * to read, we first take stuff from the fragmentBuffer
+		 */
+
+		/* on first read() of the line, fragmentBuffer is null */
+		if (fragmentBuffer != null) {
+
+			boolean fragmentBufferSmaller = fragmentBuffer.length < length;
+			int smallerBufferLength = Math.min(fragmentBuffer.length, length);
+			System.arraycopy(fragmentBuffer, 0, data, position,
+					smallerBufferLength);
+
+			if (!fragmentBufferSmaller) {
+				/*
+				 * if fragment was larger, then we already have all the data we
+				 * need. clean up the buffer before returning. Make a new
+				 * fragmentBuffer from the remaining bytes
+				 */
+				int remainingBytesInFragment = (fragmentBuffer.length - length);
+				byte[] newFragmentBuffer = new byte[remainingBytesInFragment];
+				System.arraycopy(fragmentBuffer, length, newFragmentBuffer, 0,
+						newFragmentBuffer.length);
+				fragmentBuffer = newFragmentBuffer;
+				return length;
+			}
+
+			/* done with fragment buffer, remove it */
+			bytesRead = smallerBufferLength;
+			sizeRead += bytesRead;
+			position += bytesRead;
+			remainingLength -= bytesRead;
+			fragmentBuffer = null;
+		}
+
+		/*
+		 * if we need to read more data, then we read from PulseAudio's buffer
+		 */
 		while (remainingLength != 0) {
-
+			byte[] currentFragment;
 			synchronized (eventLoop.threadLock) {
-				int bytesRead = stream.read(data, remainingLength, position);
-				if (bytesRead < 0) {
+
+				/* read a fragment, and drop it from the server */
+				currentFragment = stream.peek();
+				stream.drop();
+				if (currentFragment == null) {
+					System.out
+							.println("DEBUG: PulseAudioTargetDataLine:read(): error in stream.peek()");
 					return sizeRead;
 				}
+
+				bytesRead = Math.min(currentFragment.length, remainingLength);
+
+				/*
+				 * we read more than we required, save the rest of the data in
+				 * the fragmentBuffer
+				 */
+				if (bytesRead < currentFragment.length) {
+					/* allocate a buffer to store unsaved data */
+					fragmentBuffer = new byte[currentFragment.length
+							- bytesRead];
+					/* copy over the unsaved data */
+					System.arraycopy(currentFragment, bytesRead,
+							fragmentBuffer, 0, fragmentBuffer.length
+									- bytesRead);
+				}
+
+				System.arraycopy(currentFragment, 0, data, position, bytesRead);
+
 				sizeRead += bytesRead;
 				position += bytesRead;
 				remainingLength -= bytesRead;
 				framesSinceOpen += bytesRead / currentFormat.getFrameSize();
-
 			}
 		}
 
 		// 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
--- a/src/java/org/classpath/icedtea/pulseaudio/Stream.java	Mon Sep 29 13:50:48 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/Stream.java	Mon Sep 29 15:16:15 2008 -0400
@@ -54,7 +54,7 @@
 	public interface StateListener {
 		public void update();
 	}
-	
+
 	public interface CorkListener {
 		public void update();
 	}
@@ -164,9 +164,6 @@
 	private native int native_pa_stream_write(byte[] data, int offset,
 			int length);
 
-	private native int native_pa_stream_read(byte[] array, int length,
-			int position);
-
 	private native byte[] native_pa_stream_peek();
 
 	private native int native_pa_stream_drop();
@@ -323,7 +320,7 @@
 			underflowListeners.remove(listener);
 		}
 	}
-	
+
 	public void addCorkListener(CorkListener listener) {
 		synchronized (corkListeners) {
 			corkListeners.add(listener);
@@ -501,10 +498,6 @@
 		return native_pa_stream_write(data, offset, length);
 	}
 
-	public int read(byte[] array, int length, int position) {
-		return native_pa_stream_read(array, length, position);
-	}
-
 	/**
 	 * Read the next fragment from the buffer (for recording).
 	 * 
@@ -636,7 +629,7 @@
 			}
 		}
 	}
-	
+
 	@SuppressWarnings("unused")
 	private void corkCallback() {
 		synchronized (corkListeners) {
--- a/src/native/org_classpath_icedtea_pulseaudio_Stream.c	Mon Sep 29 13:50:48 2008 -0400
+++ b/src/native/org_classpath_icedtea_pulseaudio_Stream.c	Mon Sep 29 15:16:15 2008 -0400
@@ -525,6 +525,7 @@
 	return value;
 }
 
+/*
 JNIEXPORT jint JNICALL Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1read
 (JNIEnv *env, jobject obj, jbyteArray array, jint length, jint offset) {
 	pa_stream *stream = getJavaPointer(env, obj, STREAM_POINTER);
@@ -540,6 +541,7 @@
 	pa_stream_drop(stream);
 	return read_length;
 }
+*/
 
 /*
  * Class:     org_classpath_icedtea_pulseaudio_Stream
--- a/unittests/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLineTest.java	Mon Sep 29 13:50:48 2008 -0400
+++ b/unittests/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLineTest.java	Mon Sep 29 15:16:15 2008 -0400
@@ -170,7 +170,7 @@
 
 	@Test
 	public void testOpenWithFormat() throws LineUnavailableException {
-		System.out.println("This test checks that read() sort of wroks");
+		System.out.println("This test checks that open(AudioFormat) works");
 
 		targetDataLine = (TargetDataLine) mixer.getLine(new Line.Info(
 				TargetDataLine.class));