# HG changeset patch # User Omair Majid # Date 1222715775 14400 # Node ID 350bc72ba495ad86e0c076c56f5b1439b5317df9 # Parent f508adb1c908273e175e7a1fbbabe7cef7f32847 2008-09-29 Omair Majid * 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. diff -r f508adb1c908 -r 350bc72ba495 src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java --- 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 diff -r f508adb1c908 -r 350bc72ba495 src/java/org/classpath/icedtea/pulseaudio/Stream.java --- 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) { diff -r f508adb1c908 -r 350bc72ba495 src/native/org_classpath_icedtea_pulseaudio_Stream.c --- 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 diff -r f508adb1c908 -r 350bc72ba495 unittests/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLineTest.java --- 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));