Mercurial > hg > pulseaudio
changeset 90:0ab3515d5074
2008-08-19 Omair Majid <omajid@redhat.com>
* src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java
(connectLine): Added support for buffer attributes.
* src/java/org/classpath/icedtea/pulseaudio/Stream.java:
(native_pa_stream_connect_playback): Likeswise.
(native_pa_stream_get_sample_spec): New function.
(native_pa_stream_get_buffer_attr): Likewise.
(native_pa_stream_set_buffer_attr): Likewise.
(Stream): Uses SampleSpecification objects now.
(connectForPlayback): Added support for buffer attributes.
(getBufferAttributes): New function.
(setBufferAtrributes): Likewise.
* src/native/org_classpath_icedtea_pulseaudio_Stream.c:
(getStringFromFormat): New function.
(getFormatFromString): New function.
(Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1new): Uses
getFormatFromString now.
(Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1connect_1playback):
New function.
(Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1get_1sample_1spec):
Likewise.
(Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1get_1buffer_1attr):
Likewise.
(Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1set_1buffer_1attr):
Likewise.
* unittests/org/classpath/icedtea/pulseaudio/PulseSourceDataLineTest.java
(testVolumeChanging): New function.
author | Omair Majid <omajid@redhat.com> |
---|---|
date | Tue, 19 Aug 2008 17:04:11 -0400 |
parents | 52b513a7e5e9 |
children | 675a5044aecb |
files | src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java src/java/org/classpath/icedtea/pulseaudio/Stream.java src/native/org_classpath_icedtea_pulseaudio_Stream.c unittests/org/classpath/icedtea/pulseaudio/PulseSourceDataLineTest.java |
diffstat | 4 files changed, 239 insertions(+), 66 deletions(-) [+] |
line wrap: on
line diff
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java Tue Aug 19 11:35:27 2008 -0400 +++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java Tue Aug 19 17:04:11 2008 -0400 @@ -46,11 +46,10 @@ import javax.sound.sampled.LineListener; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.SourceDataLine; -import javax.sound.sampled.AudioFormat.Encoding; import javax.sound.sampled.Control.Type; -public class PulseAudioSourceDataLine extends PulseAudioDataLine implements SourceDataLine { - +public class PulseAudioSourceDataLine extends PulseAudioDataLine implements + SourceDataLine { private Control[] controls = null; private PulseAudioStreamMuteControl muteControl; @@ -60,8 +59,6 @@ private long currentFramePosition = 0; - - public PulseAudioSourceDataLine(EventLoop eventLoop, AudioFormat[] formats, AudioFormat defaultFormat) { @@ -93,7 +90,7 @@ public void open(AudioFormat format, int bufferSize) throws LineUnavailableException { - + super.open(format, bufferSize); controls = new Control[2]; volumeControl = new PulseAudioStreamVolumeControl(this); @@ -102,12 +99,16 @@ controls[1] = muteControl; } - + protected void connectLine() { - stream.connectForPlayback(null); + StreamBufferAttributes bufferAttributes = new StreamBufferAttributes( + StreamBufferAttributes.SANE_DEFAULT, StreamBufferAttributes.SANE_DEFAULT, + StreamBufferAttributes.SANE_DEFAULT, StreamBufferAttributes.SANE_DEFAULT, + StreamBufferAttributes.SANE_DEFAULT); + + stream.connectForPlayback(null,bufferAttributes); } - @Override public int write(byte[] data, int offset, int length) { @@ -160,14 +161,12 @@ return sizeWritten; } - public int available() { synchronized (eventLoop.threadLock) { return stream.getWritableSize(); } }; - public int getBufferSize() { // FIXME! return 10000; @@ -290,7 +289,6 @@ } - protected EventLoop getEventLoop() { return this.eventLoop; }
--- a/src/java/org/classpath/icedtea/pulseaudio/Stream.java Tue Aug 19 11:35:27 2008 -0400 +++ b/src/java/org/classpath/icedtea/pulseaudio/Stream.java Tue Aug 19 17:04:11 2008 -0400 @@ -86,7 +86,9 @@ private native int native_pa_stream_is_suspended(); private native int native_pa_stream_connect_playback(String name, - long buffer_attrPointer, int flags, long volumePointer, + int bufferMaxLength, int bufferTargetLength, + int bufferPreBuffering, int bufferMinimumRequest, + int bufferFragmentSize, int flags, long volumePointer, long sync_streamPointer); private native int native_pa_stream_connect_record(String device, @@ -139,17 +141,20 @@ /* * const pa_timing_info * pa_stream_get_timing_info (pa_stream *s) Return - * the latest raw timing data structure. const pa_sample_spec * - * pa_stream_get_sample_spec (pa_stream *s) Return a pointer to the stream's - * sample specification. const pa_channel_map * pa_stream_get_channel_map - * (pa_stream *s) Return a pointer to the stream's channel map. const - * pa_buffer_attr * pa_stream_get_buffer_attr (pa_stream *s) Return the - * per-stream server-side buffer metrics of the stream. pa_operation * - * pa_stream_set_buffer_attr (pa_stream *s, const pa_buffer_attr *attr, - * pa_stream_success_cb_t cb, void *userdata) Change the buffer metrics of - * the stream during playback. + * the latest raw timing data structure. */ + native StreamSampleSpecification native_pa_stream_get_sample_spec(); + + /* + * const pa_channel_map * pa_stream_get_channel_map (pa_stream *s) Return a + * pointer to the stream's channel map. const + * + */ + native StreamBufferAttributes native_pa_stream_get_buffer_attr(); + + native long native_pa_stream_set_buffer_attr(StreamBufferAttributes info); + private native long native_pa_stream_update_sample_rate(int rate); /* @@ -182,8 +187,11 @@ this.format = format; - native_pa_stream_new(contextPointer, name, format.toString(), + StreamSampleSpecification spec = new StreamSampleSpecification(format, sampleRate, channels); + + native_pa_stream_new(contextPointer, name, spec.getFormat().toString(), + spec.getRate(), spec.getChannels()); } public void addStateListener(StateListener listener) { @@ -330,10 +338,14 @@ * * @param deviceName */ - public void connectForPlayback(String deviceName) { + public void connectForPlayback(String deviceName, + StreamBufferAttributes bufferAttributes) { - int returnValue = native_pa_stream_connect_playback(deviceName, 0, 0, - 0, 0); + int returnValue = native_pa_stream_connect_playback(deviceName, + bufferAttributes.getMaxLength(), bufferAttributes + .getTargetLength(), bufferAttributes.getPreBuffering(), + bufferAttributes.getMinimumRequest(), bufferAttributes + .getFragmentSize(), 0, 0, 0); assert (returnValue == 0); } @@ -575,8 +587,6 @@ return native_pa_stream_get_time(); } - - /** * @returns the total stream latency in microseconds */ @@ -599,18 +609,13 @@ * pointer to the stream's channel map. */ - /* - * const pa_buffer_attr * pa_stream_get_buffer_attr (pa_stream *s) Return - * the per-stream server-side buffer metrics of the stream. - * - */ + public StreamBufferAttributes getBufferAttributes() { + return native_pa_stream_get_buffer_attr(); + } - /* - * pa_operation * pa_stream_set_buffer_attr (pa_stream *s, const - * pa_buffer_attr *attr, pa_stream_success_cb_t cb, void *userdata) Change - * the buffer metrics of the stream during playback. - * - */ + public Operation setBufferAtrributes(StreamBufferAttributes attr) { + return new Operation(native_pa_stream_set_buffer_attr(attr)); + } /** * Change the stream sampling rate during playback.
--- a/src/native/org_classpath_icedtea_pulseaudio_Stream.c Tue Aug 19 11:35:27 2008 -0400 +++ b/src/native/org_classpath_icedtea_pulseaudio_Stream.c Tue Aug 19 17:04:11 2008 -0400 @@ -11,6 +11,56 @@ extern JNIEnv* pulse_thread_env; +const char* getStringFromFormat(pa_sample_format_t format) { + + const char* value; + + if (format == PA_SAMPLE_U8) { + value = "PA_SAMPLE_U8"; + } else if (format == PA_SAMPLE_ALAW) { + value = "PA_SAMPLE_ALAW"; + } else if (format == PA_SAMPLE_ULAW) { + value = "PA_SAMPLE_ULAW"; + } else if (format == PA_SAMPLE_S16BE) { + value = "PA_SAMPLE_S16BE"; + } else if (format == PA_SAMPLE_S16LE) { + value = "PA_SAMPLE_S16LE"; + } else if (format == PA_SAMPLE_S32BE) { + value = "PA_SAMPLE_S32BE"; + } else if (format == PA_SAMPLE_S32LE) { + value = "PA_SAMPLE_S32LE"; + } else { + value = "PA_SAMPLE_INVALID"; + } + + return value; +} + +pa_sample_format_t getFormatFromString(const char* encoding) { + + pa_sample_format_t format; + + if (strcmp(encoding, "PA_SAMPLE_U8") == 0) { + format = PA_SAMPLE_U8; + } else if (strcmp(encoding, "PA_SAMPLE_ALAW") == 0) { + format = PA_SAMPLE_ALAW; + } else if (strcmp(encoding, "PA_SAMPLE_ULAW;") == 0) { + format = PA_SAMPLE_ULAW; + } else if (strcmp(encoding, "PA_SAMPLE_S16BE") == 0) { + format = PA_SAMPLE_S16BE; + } else if (strcmp(encoding, "PA_SAMPLE_S16LE") == 0) { + format = PA_SAMPLE_S16LE; + } else if (strcmp(encoding, "PA_SAMPLE_S32BE") == 0) { + format = PA_SAMPLE_S32BE; + } else if (strcmp(encoding, "PA_SAMPLE_S32LE") == 0) { + format = PA_SAMPLE_S32LE; + } else { + format = PA_SAMPLE_INVALID; + } + + return format; +} + static void stream_state_callback(pa_stream* stream, void *userdata) { printf("stream_state_callback called\n"); @@ -168,11 +218,14 @@ /* * Class: org_classpath_icedtea_pulseaudio_Stream * Method: native_pa_stream_new - * Signature: (Ljava/lang/String;)V + * Signature: (JLjava/lang/String;Ljava/lang/String;IIIIIII)V */ JNIEXPORT void JNICALL Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1new -(JNIEnv* env, jobject obj, jlong contextPointer, jstring nameString, jstring encodingString, jint sampleRate, jint channels) { +(JNIEnv* env, jobject obj, jlong contextPointer, jstring nameString, + jstring encodingString, jint sampleRate, jint channels) { + printf("creating a new PulseAudio stream\n"); + java_context* j_context = malloc(sizeof(java_context)); assert(j_context); j_context->env = env; @@ -180,6 +233,7 @@ pa_context* context = convertJavaLongToPointer(contextPointer); assert(context); + const char* name = NULL; if (nameString) { name = (*env)->GetStringUTFChars(env,nameString, NULL); @@ -197,30 +251,7 @@ pa_sample_spec sample_spec; - if (strcmp(encoding, "PA_SAMPLE_U8") == 0) { - sample_spec.format = PA_SAMPLE_U8; - } else if (strcmp(encoding, "PA_SAMPLE_ALAW") == 0) { - sample_spec.format = PA_SAMPLE_ALAW; - } else if (strcmp(encoding, "PA_SAMPLE_ULAW;") == 0) { - sample_spec.format = PA_SAMPLE_ULAW; - } else if (strcmp(encoding, "PA_SAMPLE_S16BE") == 0) { - sample_spec.format = PA_SAMPLE_S16BE; - } else if (strcmp(encoding, "PA_SAMPLE_S16LE") == 0) { - sample_spec.format = PA_SAMPLE_S16LE; - } else if (strcmp(encoding, "PA_SAMPLE_S32BE") == 0) { - sample_spec.format = PA_SAMPLE_S32BE; - } else if (strcmp(encoding, "PA_SAMPLE_S32LE") == 0) { - sample_spec.format = PA_SAMPLE_S32LE; - } else { - printf("error in open: encoding is : %s\n", encoding); - throwByName(env, "java/lang/IllegalArgumentException", "Invalid format"); - /* clean up */ - (*env)->ReleaseStringUTFChars(env, encodingString, encoding); - free(j_context); - (*env)->DeleteGlobalRef(env, obj); - return; - } - + sample_spec.format = getFormatFromString(encoding); sample_spec.rate = sampleRate; sample_spec.channels = channels; @@ -342,8 +373,20 @@ * Signature: (Ljava/lang/String;JIJJ)I */ JNIEXPORT jint JNICALL Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1connect_1playback -(JNIEnv* env, jobject obj, jstring device, jlong pointer, jint anotherPointer, jlong yetAnotherPointer, jlong TooManyPointers) { +(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"); + + pa_buffer_attr buffer_attributes; + + buffer_attributes.maxlength = bufferMaxLength; + buffer_attributes.tlength = bufferTargetLength; + buffer_attributes.prebuf = bufferPreBuffering; + buffer_attributes.minreq = bufferMinimumRequest; + buffer_attributes.fragsize = bufferFragmentSize; + const char* dev = NULL; if (device != NULL) { dev = (*env)->GetStringUTFChars(env, device, NULL); @@ -600,6 +643,92 @@ /* * Class: org_classpath_icedtea_pulseaudio_Stream + * Method: native_pa_stream_get_sample_spec + * Signature: ()Lorg/classpath/icedtea/pulseaudio/StreamSampleSpecification; + */ +JNIEXPORT jobject JNICALL Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1get_1sample_1spec +(JNIEnv* env, jobject obj) { + pa_stream* stream = (pa_stream*)getJavaPointer(env, obj, "streamPointer"); + const pa_sample_spec* sample_spec = pa_stream_get_sample_spec(stream); + + char* name = "Lorg/classpath/icedtea/pulseaudio/StreamSampleSpecification;"; + jclass cls = (*env)->FindClass(env, name); + jmethodID constructor_mid = (*env)->GetMethodID(env, cls, "<init>", "V"); + + const char* formatString = getStringFromFormat(sample_spec->format); + int rate = sample_spec->rate; + int channels = sample_spec->channels; + + jstring format = (*env)->NewStringUTF(env, formatString); + + jobject return_object = (*env)->NewObject(env, cls, constructor_mid, format, rate, channels); + + return return_object; +} + +/* + * Class: org_classpath_icedtea_pulseaudio_Stream + * Method: native_pa_stream_get_buffer_attr + * Signature: ()Lorg/classpath/icedtea/pulseaudio/StreamBufferAttributes; + */ +JNIEXPORT jobject JNICALL Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1get_1buffer_1attr +(JNIEnv* env, jobject obj) { + pa_stream* stream = (pa_stream*)getJavaPointer(env, obj, "streamPointer"); + assert(stream); + const pa_buffer_attr* buffer = pa_stream_get_buffer_attr(stream); + + 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"); + + jint maxLength = buffer->maxlength; + jint targetLength = buffer->tlength; + jint preBuffering = buffer->prebuf; + jint minimumRequest = buffer->minreq; + jint fragmentSize = buffer->fragsize; + + jobject return_object = (*env)->NewObject(env, cls, constructor_mid, maxLength, targetLength, + preBuffering, minimumRequest, fragmentSize); + + return return_object; +} + +/* + * Class: org_classpath_icedtea_pulseaudio_Stream + * Method: native_pa_stream_set_buffer_attr + * Signature: (Lorg/classpath/icedtea/pulseaudio/Stream/BufferAttributes;)J + */ +JNIEXPORT jlong JNICALL Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1set_1buffer_1attr +(JNIEnv* env, jobject obj, jobject bufferAttributeObject) { + + pa_stream* stream = (pa_stream*)getJavaPointer(env, obj, "streamPointer"); + assert(stream); + + jclass cls = (*env)->GetObjectClass(env, bufferAttributeObject); + + pa_buffer_attr* buffer = malloc(sizeof(pa_buffer_attr)); + + jmethodID getMaxLengthID = (*env)->GetMethodID(env,cls,"getMaxLength","()I"); + buffer->maxlength = (*env)->CallIntMethod(env, bufferAttributeObject, getMaxLengthID); + + jmethodID getTargetLengthID = (*env)->GetMethodID(env,cls,"getTargetLength","()I"); + buffer->tlength = (*env)->CallIntMethod(env, bufferAttributeObject, getTargetLengthID); + + jmethodID getPreBufferingID = (*env)->GetMethodID(env,cls,"getPreBuffering","()I"); + buffer->prebuf = (*env)->CallIntMethod(env, bufferAttributeObject, getPreBufferingID); + + jmethodID getMinimumRequestID = (*env)->GetMethodID(env,cls,"getMinimumRequest ","()I"); + buffer->minreq = (*env)->CallIntMethod(env, bufferAttributeObject, getMinimumRequestID ); + + jmethodID getFragmentSizeID = (*env)->GetMethodID(env,cls,"getFragmentSize","()I"); + buffer->fragsize = (*env)->CallIntMethod(env, bufferAttributeObject, getFragmentSizeID ); + + pa_operation* operation = pa_stream_set_buffer_attr(stream, buffer, NULL, NULL); + assert(operation); + return convertPointerToJavaLong(operation); +} +/* + * Class: org_classpath_icedtea_pulseaudio_Stream * Method: native_pa_stream_update_sample_rate * Signature: (I)J */
--- a/unittests/org/classpath/icedtea/pulseaudio/PulseSourceDataLineTest.java Tue Aug 19 11:35:27 2008 -0400 +++ b/unittests/org/classpath/icedtea/pulseaudio/PulseSourceDataLineTest.java Tue Aug 19 17:04:11 2008 -0400 @@ -225,6 +225,47 @@ } @Test + public void testVolumeChanging() throws LineUnavailableException, + IOException, UnsupportedAudioFileException { + + Mixer selectedMixer = mixer; + + selectedMixer.open(); + SourceDataLine line = (SourceDataLine) selectedMixer + .getLine(new Line.Info(SourceDataLine.class)); + + File soundFile = new File(new java.io.File(".").getCanonicalPath() + + "/testsounds/logout.wav"); + AudioInputStream audioInputStream = AudioSystem + .getAudioInputStream(soundFile); + AudioFormat audioFormat = audioInputStream.getFormat(); + + line.open(audioFormat); + line.start(); + PulseAudioStreamVolumeControl volume = (PulseAudioStreamVolumeControl) line + .getControl(FloatControl.Type.VOLUME); + PulseAudioStreamMuteControl mute = (PulseAudioStreamMuteControl) line + .getControl(BooleanControl.Type.MUTE); + + volume.setValue(PulseAudioStreamVolumeControl.MIN_VOLUME); + + byte[] abData = new byte[1000]; + int bytesRead = 0; + + while (bytesRead >= 0) { + bytesRead = audioInputStream.read(abData, 0, abData.length); + if (bytesRead > 0) { + line.write(abData, 0, bytesRead); + volume.setValue(volume.getValue() + 100); + } + } + + line.flush(); + selectedMixer.close(); + + } + + @Test public void testFindControl() throws LineUnavailableException { SourceDataLine sourceLine = (SourceDataLine) mixer .getLine(new Line.Info(SourceDataLine.class));