Mercurial > hg > pulseaudio
changeset 74:5f856aeca15a
2008-08-14 Omair Majid <omajid@redhat.com>
* build.xml: added Stream.java to list of files to generate jni headers
from
* src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java: initial
implementation using the Stream class
* src/java/org/classpath/icedtea/pulseaudio/Stream.java: new file
* src/native/Makefile.am: added Stream.c to list of files to package
* src/native/org_classpath_icedtea_pulseaudio_Stream.c
* unittests/org/classpath/icedtea/pulseaudio/PulseAudioClipTest.java:
added a few more tests
author | Omair Majid <omajid@redhat.com> |
---|---|
date | Thu, 14 Aug 2008 14:24:17 -0400 |
parents | 9ed589465932 |
children | a2034200b782 |
files | build.xml src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java src/java/org/classpath/icedtea/pulseaudio/Stream.java src/native/Makefile.am src/native/org_classpath_icedtea_pulseaudio_Stream.c unittests/org/classpath/icedtea/pulseaudio/PulseAudioClipTest.java |
diffstat | 6 files changed, 828 insertions(+), 22 deletions(-) [+] |
line wrap: on
line diff
--- a/build.xml Wed Aug 13 13:17:13 2008 -0400 +++ b/build.xml Thu Aug 14 14:24:17 2008 -0400 @@ -39,6 +39,7 @@ <class name="org.classpath.icedtea.pulseaudio.Operation"/> <class name="org.classpath.icedtea.pulseaudio.PulseAudioSourceDataLine"/> <class name="org.classpath.icedtea.pulseaudio.PulseAudioClip"/> + <class name="org.classpath.icedtea.pulseaudio.Stream"/> <class name="org.classpath.icedtea.pulseaudio.PulseAudioStreamVolumeControl"/> </javah> </target>
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java Wed Aug 13 13:17:13 2008 -0400 +++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java Thu Aug 14 14:24:17 2008 -0400 @@ -54,6 +54,8 @@ public class PulseAudioClip implements Clip { private byte[] data = null; + + // FIXME private int bufferSize = 0; // these are frame indices. so counted from 0 @@ -72,21 +74,20 @@ private List<LineListener> lineListeners = null; private static final int DEFAULT_BUFFER_SIZE = 0; - - @SuppressWarnings("unused") - private long streamPointer = 0; + public static final String DEFAULT_CLIP_NAME = "Clip"; - private native void native_open(); - - private native void native_close(); + private Stream stream; - private native void native_start(); - - private native void native_stop(); + private Thread clipLoop = new Thread() { + @Override + public void run() { + while (true) { - private native long native_drain(); + } - private native long native_flush(); + } + + }; static { try { @@ -117,20 +118,19 @@ @Override public void close() { // TODO Auto-generated method stub - native_close(); + stream.drain(); + stream.disconnect(); isOpen = false; } @Override public void drain() { - // TODO Auto-generated method stub - native_drain(); + stream.drain(); } @Override public void flush() { - // TODO Auto-generated method stub - native_flush(); + stream.flush(); } @Override @@ -234,9 +234,13 @@ @Override public void open(AudioFormat format, byte[] data, int offset, int bufferSize) throws LineUnavailableException { - // TODO Auto-generated method stub - - native_open(); + + + long contextPointer = EventLoop.getEventLoop().getContextPointer(); + int channels = 2; + int sampleRate = 22050; + stream = new Stream(contextPointer, DEFAULT_CLIP_NAME, + Stream.Format.PA_SAMPLE_U8, sampleRate, channels); isOpen = true; } @@ -294,12 +298,12 @@ @Override public void start() { - native_start(); + stream.cork(false); } @Override public void stop() { - native_stop(); + stream.cork(true); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java/org/classpath/icedtea/pulseaudio/Stream.java Thu Aug 14 14:24:17 2008 -0400 @@ -0,0 +1,441 @@ +package org.classpath.icedtea.pulseaudio; + +import javax.sound.sampled.AudioFormat; + +/** + * + * This class encapsulates a pa_stream object and provides easier access to the + * native functions + * + */ +public class Stream { + + public static enum State { + UNCONNECTED, CREATING, READY, FAILED, TERMINATED, + } + + public static enum Format { + 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, + } + + @SuppressWarnings("unused") + private long streamPointer; + + private native void native_pa_stream_new(long contextPointer, String name, + String format, int sampleRate, int channels); + + private native int native_pa_stream_get_state(); + + private native long native_pa_stream_get_context(); + + private native int native_pa_stream_get_index(); + + private native int native_pa_stream_get_device_index(); + + private native String native_pa_stream_get_device_name(); + + 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, + long sync_streamPointer); + + private native int native_pa_stream_connect_record(String device, + long buffer_attrPointer, int flags); + + private native int native_pa_stream_disconnect(); + + private native int native_pa_stream_write(byte[] data, int offset, + int length); + + /* + * private native int native_pa_stream_peek (pa_stream *p, const void + * **data, size_t *nbytes) Read the next fragment from the buffer (for + * recording). int pa_stream_drop (pa_stream *p) Remove the current fragment + * on record streams. + * + */ + + private native int native_pa_stream_writable_size(); + + private native int native_pa_stream_readable_size(); + + private native long native_pa_stream_drain(); + + /* + * Drain a playback stream. pa_operation pa_stream_update_timing_info + * (pa_stream *p, pa_stream_success_cb_t cb, void *userdata) Request a + * timing info structure update for a stream. void + * pa_stream_set_state_callback (pa_stream *s, pa_stream_notify_cb_t cb, + * void *userdata) Set the callback function that is called whenever the + * state of the stream changes. void pa_stream_set_write_callback (pa_stream + * *p, pa_stream_request_cb_t cb, void *userdata) Set the callback function + * that is called when new data may be written to the stream. void + * pa_stream_set_read_callback (pa_stream *p, pa_stream_request_cb_t cb, + * void *userdata) Set the callback function that is called when new data is + * available from the stream. void pa_stream_set_overflow_callback + * (pa_stream *p, pa_stream_notify_cb_t cb, void *userdata) Set the callback + * function that is called when a buffer overflow happens. void + * pa_stream_set_underflow_callback (pa_stream *p, pa_stream_notify_cb_t cb, + * void *userdata) Set the callback function that is called when a buffer + * underflow happens. void pa_stream_set_started_callback (pa_stream *p, + * pa_stream_notify_cb_t cb, void *userdata) Set the callback function that + * is called when a the server starts playback after an underrun or on + * initial startup. void pa_stream_set_latency_update_callback (pa_stream + * *p, pa_stream_notify_cb_t cb, void *userdata) Set the callback function + * that is called whenever a latency information update happens. void + * pa_stream_set_moved_callback (pa_stream *p, pa_stream_notify_cb_t cb, + * void *userdata) Set the callback function that is called whenever the + * stream is moved to a different sink/source. void + * pa_stream_set_suspended_callback (pa_stream *p, pa_stream_notify_cb_t cb, + * void *userdata) Set the callback function that is called whenever the + * sink/source this stream is connected to is suspended or resumed. + * + */ + + private native long native_pa_stream_cork(int b); + + private native long native_pa_stream_flush(); + + /* + * pa_operation pa_stream_prebuf (pa_stream *s, pa_stream_success_cb_t cb, + * void *userdata) Reenable prebuffering as specified in the pa_buffer_attr + * structure. + */ + + private native long native_pa_stream_trigger(); + + /* returns an operationPointer */ + private native long native_pa_stream_set_name(String name); + + /* + * Return the current playback/recording time private native int + * native_pa_stream_get_time (pa_usec_t r_usec); + */ + + /* + * Return the total stream latency private native int + * native_pa_stream_get_latency ( pa_usec_t *r_usec, int *negative); + */ + + /* + * 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. pa_operation * pa_stream_update_sample_rate + * (pa_stream *s, uint32_t rate, pa_stream_success_cb_t cb, void *userdata) + * Change the stream sampling rate during playback. pa_operation * + * pa_stream_proplist_update (pa_stream *s, pa_update_mode_t mode, + * pa_proplist *p, pa_stream_success_cb_t cb, void *userdata) Update the + * property list of the sink input/source output of this stream, adding new + * entries. pa_operation * pa_stream_proplist_remove (pa_stream *s, const + * char *const keys[], pa_stream_success_cb_t cb, void *userdata) Update the + * property list of the sink input/source output of this stream, remove + * entries. int pa_stream_set_monitor_stream (pa_stream *s, uint32_t + * sink_input_idx) For record streams connected to a monitor source: monitor + * only a very specific sink input of the sink. uint32_t + * pa_stream_get_monitor_stream (pa_stream *s) Return what has been set with + * pa_stream_set_monitor_stream() ebfore. + */ + + public Stream(long contextPointer, String name, Format format, + int sampleRate, int channels) { + System.out.println("format: "+ format.toString()); + native_pa_stream_new(contextPointer, name, format.toString(), + sampleRate, channels); + } + + public Stream.State getState() { + int state = native_pa_stream_get_state(); + switch (state) { + case 0: + return State.UNCONNECTED; + case 1: + return State.CREATING; + case 2: + return State.READY; + case 3: + return State.FAILED; + case 4: + return State.TERMINATED; + default: + throw new IllegalStateException("invalid stream state"); + } + + } + + public long getContextPointer() { + return native_pa_stream_get_context(); + } + + public int getSinkInputIndex() { + return native_pa_stream_get_index(); + } + + /** + * + * @return the index of the sink or source this stream is connected to in + * the server + */ + public int getDeviceIndex() { + return native_pa_stream_get_device_index(); + } + + /** + * + * @return the name of the sink or source this stream is connected to in the + * server + */ + public String getDeviceName() { + return native_pa_stream_get_device_name(); + } + + /** + * if the sink or source this stream is connected to has been suspended. + * + * @return + */ + public boolean isSuspended() { + return (native_pa_stream_is_suspended() != 0); + } + + /** + * Connect the stream to a sink + * + * @param deviceName + */ + public void connectForPlayback(String deviceName) { + + int returnValue = native_pa_stream_connect_playback(deviceName, 0, 0, + 0, 0); + } + + /** + * Connect the stream to a source. + * + */ + void connectForRecording(String deviceName) { + int returnValue = native_pa_stream_connect_record(deviceName, 0, 0); + } + + /** + * Disconnect a stream from a source/sink. + */ + void disconnect() { + int returnValue = native_pa_stream_disconnect(); + } + + /** + * Write data to the server + * + * @param data + * @param length + * @return + */ + int write(byte[] data, int offset, int length) { + return native_pa_stream_write(data, offset, length); + } + + /** + * Read the next fragment from the buffer (for recording). + * + * + * @param data + */ + public void peek(byte[] data) { + + } + + /** + * + * Remove the current fragment on record streams. + */ + void drop() { + + } + + /** + * Return the number of bytes that may be written using write(). + * + * @return + */ + public int getWritableSize() { + return native_pa_stream_writable_size(); + } + + /** + * Return the number of bytes that may be read using peek(). + * + * @return + */ + public int getReableSize() { + return native_pa_stream_readable_size(); + } + + /** + * Drain a playback stream + * + * @return + */ + Operation drain() { + Operation drainOperation = new Operation(native_pa_stream_drain()); + return drainOperation; + } + + /** + * this function is called whenever the state changes + */ + void stateCallback() { + + } + + void writeCallback() { + + } + + void readCallback() { + + } + + void overflowCallback() { + + } + + void underflowCallback() { + + } + + /** + * callback function that is called when a the server starts playback after + * an underrun or on initial startup + */ + void playbackStartedCallback() { + + } + + /** + * called whenever a latency information update happens + */ + void latencyUpdateCallback() { + + } + + /** + * whenever the stream is moved to a different sink/source + */ + void movedCallback() { + + } + + /** + * whenever the sink/source this stream is connected to is suspended or + * resumed + */ + void suspendedCallback() { + + } + + /** + * Pause (or resume) playback of this stream temporarily. + * + * @param cork + * @return + */ + Operation cork(boolean cork) { + int yes = cork ? 1 : 0; + Operation corkOperation = new Operation(native_pa_stream_cork(yes)); + return corkOperation; + } + + /** + * Flush the playback buffer of this stream. + * + * @return + */ + Operation flush() { + Operation flushOperation = new Operation(native_pa_stream_flush()); + return flushOperation; + } + + /* + * Operation pa_stream_prebuf (pa_stream *s, pa_stream_success_cb_t cb, void + * *userdata) + * + * Reenable prebuffering as specified in the pa_buffer_attr structure. + */ + + /** + * Request immediate start of playback on this stream. + */ + Operation triggerStart() { + Operation triggerOperation = new Operation(native_pa_stream_trigger()); + return triggerOperation; + } + + /** + * set the stream's name + * + * @param name + * @return + */ + Operation setName(String name) { + Operation setNameOperation = new Operation( + native_pa_stream_set_name(name)); + return setNameOperation; + } + + long getTimeInMicroseconds() { + return -1; + } + + // TODO: huh? + /** + * @returns the total stream latency + */ + int getLatency() { + return -1; + } + + /* + * const pa_timing_info * pa_stream_get_timing_info (pa_stream *s) Return + * the latest raw timing data structure. + * + */ + + public AudioFormat getSampleSpec() { + return null; + } + + /* + * 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. + * + */ + + /** + * Change the stream sampling rate during playback. + * + */ + /* + * Operation updateSampleRate(int rate) { Operation updateOp = new + * Operation(native_pa_stream_) } + */ +}
--- a/src/native/Makefile.am Wed Aug 13 13:17:13 2008 -0400 +++ b/src/native/Makefile.am Thu Aug 14 14:24:17 2008 -0400 @@ -12,7 +12,9 @@ org_classpath_icedtea_pulseaudio_Operation.h \ org_classpath_icedtea_pulseaudio_Operation.c \ org_classpath_icedtea_pulseaudio_PulseAudioClip.c \ - org_classpath_icedtea_pulseaudio_PulseAudioClip.h + org_classpath_icedtea_pulseaudio_PulseAudioClip.h \ + org_classpath_icedtea_pulseaudio_Stream.c \ + org_classpath_icedtea_pulseaudio_Stream.h AM_CFLAGS = -g -Wall -Werror $(PLATFORM_FLAGS) $(LIBPULSE_CFLAGS) AM_LDFLAGS = -g -Wall -Werror $(LIBPULSE_LIBS)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/native/org_classpath_icedtea_pulseaudio_Stream.c Thu Aug 14 14:24:17 2008 -0400 @@ -0,0 +1,324 @@ +#include "org_classpath_icedtea_pulseaudio_Stream.h" + +#include "jni-common.h" +#include <pulse/pulseaudio.h> +#include <string.h> + +/* + * Class: org_classpath_icedtea_pulseaudio_Stream + * Method: native_pa_stream_new + * Signature: (Ljava/lang/String;)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) { + + pa_context* context = convertJavaLongToPointer(contextPointer); + assert(context); + const char* name = NULL; + if (nameString) { + name = (*env)->GetStringUTFChars(env,nameString, NULL); + if (name == NULL) { + return; // oome thrown + } + } + + const char *encoding = (*env)->GetStringUTFChars(env, encodingString, NULL); + if( encoding == NULL) { + return; //oome thrown + } + + 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); + return; + } + + sample_spec.rate = sampleRate; + sample_spec.channels = channels; + + if ( !pa_sample_spec_valid(&sample_spec)) { + throwByName(env, "java/lang/IllegalArgumentException", "Invalid format"); + (*env)->ReleaseStringUTFChars(env, encodingString, encoding); + if (name) { + (*env)->ReleaseStringUTFChars(env, nameString,name); + } + return; + } + + pa_stream* stream = pa_stream_new(context, name, &sample_spec, NULL); + assert(stream); + if (name) { + (*env)->ReleaseStringUTFChars(env, nameString,name); + } + setJavaPointer(env, obj, "streamPointer", stream); + +} + +/* + * Class: org_classpath_icedtea_pulseaudio_Stream + * Method: native_pa_stream_get_state + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1get_1state +(JNIEnv* env, jobject obj) { + pa_stream* stream = (pa_stream*) getJavaPointer(env, obj, "streamPointer"); + assert(stream); + return pa_stream_get_state(stream); +} + +/* + * Class: org_classpath_icedtea_pulseaudio_Stream + * Method: native_pa_stream_get_context + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1get_1context +(JNIEnv* env, jobject obj) { + pa_stream* stream = (pa_stream*) getJavaPointer(env, obj, "streamPointer"); + assert(stream); + pa_context* context = pa_stream_get_context(stream); + assert(context); + return convertPointerToJavaLong(context); +} + +/* + * Class: org_classpath_icedtea_pulseaudio_Stream + * Method: native_pa_stream_get_index + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1get_1index +(JNIEnv* env, jobject obj) { + pa_stream* stream = (pa_stream*) getJavaPointer(env, obj, "streamPointer"); + assert(stream); + return pa_stream_get_index(stream); +} + +/* + * Class: org_classpath_icedtea_pulseaudio_Stream + * Method: native_pa_stream_get_device_index + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1get_1device_1index +(JNIEnv* env, jobject obj) { + pa_stream* stream = (pa_stream*) getJavaPointer(env, obj, "streamPointer"); + assert(stream); + return pa_stream_get_device_index(stream); +} + +/* + * Class: org_classpath_icedtea_pulseaudio_Stream + * Method: native_pa_stream_get_device_name + * Signature: ()Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1get_1device_1name +(JNIEnv* env, jobject obj) { + pa_stream* stream = (pa_stream*)getJavaPointer(env, obj, "streamPointer"); + assert(stream); + const char* name = pa_stream_get_device_name(stream); + assert(name); + return (*env)->NewStringUTF(env, name); +} + +/* + * Class: org_classpath_icedtea_pulseaudio_Stream + * Method: native_pa_stream_is_suspended + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1is_1suspended +(JNIEnv* env, jobject obj) { + pa_stream* stream = (pa_stream*) getJavaPointer(env, obj, "streamPointer"); + assert(stream); + return pa_stream_is_suspended(stream); +} + +/* + * Class: org_classpath_icedtea_pulseaudio_Stream + * Method: native_pa_stream_connect_playback + * 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) { + pa_stream* stream = (pa_stream*) getJavaPointer(env, obj, "streamPointer"); + const char* dev = NULL; + if (device != NULL) { + dev = (*env)->GetStringUTFChars(env, device, NULL); + if (dev == NULL) { + return -1; // oome thrown + } + } + int value = pa_stream_connect_playback(stream, dev, NULL, 0, NULL, NULL); + if (dev != NULL) { + (*env)->ReleaseStringUTFChars(env, device, dev); + dev = NULL; + } + return value; +} + +/* + * Class: org_classpath_icedtea_pulseaudio_Stream + * Method: native_pa_stream_connect_record + * Signature: (Ljava/lang/String;JI)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) { + pa_stream* stream = (pa_stream*)getJavaPointer(env, obj, "streamPointer"); + assert(stream); + const char* dev = NULL; + if (device != NULL) { + dev = (*env)->GetStringUTFChars(env, device, NULL); + if (dev == NULL) { + return -1; // oome thrown + } + } + pa_buffer_attr* buffer = convertJavaLongToPointer(bufferPointer); + assert(buffer == NULL); + int value = pa_stream_connect_record(stream, dev, buffer, flags); + if (dev != NULL) { + (*env)->ReleaseStringUTFChars(env, device, dev); + dev = NULL; + } + return value; + +} + +/* + * Class: org_classpath_icedtea_pulseaudio_Stream + * Method: native_pa_stream_disconnect + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1disconnect +(JNIEnv* env, jobject obj) { + pa_stream* stream = (pa_stream*) getJavaPointer(env, obj, "streamPointer"); + assert(stream); + return pa_stream_disconnect(stream); +} + +/* + * Class: org_classpath_icedtea_pulseaudio_Stream + * Method: native_pa_stream_write + * Signature: ([BI)I + */ +JNIEXPORT jint JNICALL Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1write +(JNIEnv* env, jobject obj, jbyteArray data, jint offset, jint data_length) { + pa_stream* stream = (pa_stream*)getJavaPointer(env, obj, "streamPointer"); + assert(stream); + jbyte* data_buffer = (*env)->GetByteArrayElements(env, data, NULL); + if (data_buffer == NULL) { + return -1; // oome thrown + } + jbyte* buffer_start = data_buffer + offset; + int value = pa_stream_write(stream, buffer_start, data_length, NULL, 0, PA_SEEK_RELATIVE); + (*env)->ReleaseByteArrayElements(env, data, data_buffer, 0); + return value; +} + +/* + * Class: org_classpath_icedtea_pulseaudio_Stream + * Method: native_pa_stream_writable_size + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1writable_1size +(JNIEnv* env, jobject obj) { + pa_stream* stream = (pa_stream*)getJavaPointer(env, obj, "streamPointer"); + size_t size = pa_stream_writable_size(stream); + return size; + +} + +/* + * Class: org_classpath_icedtea_pulseaudio_Stream + * Method: native_pa_stream_readable_size + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1readable_1size +(JNIEnv* env, jobject obj) { + pa_stream* stream = (pa_stream*)getJavaPointer(env, obj, "streamPointer"); + return pa_stream_readable_size(stream); +} + +/* + * Class: org_classpath_icedtea_pulseaudio_Stream + * Method: native_pa_stream_drain + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1drain +(JNIEnv* env, jobject obj) { + pa_stream* stream = (pa_stream*)getJavaPointer(env, obj, "streamPointer"); + pa_operation* operation = pa_stream_drain(stream, NULL, NULL); + return convertPointerToJavaLong(operation); +} + +/* + * Class: org_classpath_icedtea_pulseaudio_Stream + * Method: native_pa_stream_cork + * Signature: (I)J + */ +JNIEXPORT jlong JNICALL Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1cork +(JNIEnv* env, jobject obj, jint yes) { + pa_stream* stream = (pa_stream*)getJavaPointer(env, obj, "streamPointer"); + pa_operation* operation = pa_stream_cork(stream, yes, NULL, NULL); + return convertPointerToJavaLong(operation); +} + +/* + * Class: org_classpath_icedtea_pulseaudio_Stream + * Method: native_pa_stream_flush + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1flush +(JNIEnv* env, jobject obj) { + pa_stream* stream = (pa_stream*) getJavaPointer(env, obj, "streamPointer"); + pa_operation* operation = pa_stream_flush(stream, NULL, NULL); + return convertPointerToJavaLong(operation); +} + +/* + * Class: org_classpath_icedtea_pulseaudio_Stream + * Method: native_pa_stream_trigger + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1trigger +(JNIEnv* env, jobject obj) { + pa_stream* stream = (pa_stream*)getJavaPointer(env, obj, "streamPointer"); + pa_operation* operation = pa_stream_trigger(stream, NULL, NULL); + return convertPointerToJavaLong(operation); +} + +/* + * Class: org_classpath_icedtea_pulseaudio_Stream + * Method: native_pa_stream_set_name + * Signature: (Ljava/lang/String;)J + */ +JNIEXPORT jlong JNICALL Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1set_1name +(JNIEnv* env, jobject obj, jstring newName) { + pa_stream* stream = (pa_stream*)getJavaPointer(env, obj, "streamPointer"); + const char* name; + name = (*env)->GetStringUTFChars(env, newName, NULL); + if (name == NULL) { + return 0; // OutOfMemoryError already thrown + } + + pa_operation* operation = pa_stream_set_name(stream, name, NULL, NULL); + assert(operation); + (*env)->ReleaseStringUTFChars(env, newName, name); + + return convertPointerToJavaLong(operation); +} +
--- a/unittests/org/classpath/icedtea/pulseaudio/PulseAudioClipTest.java Wed Aug 13 13:17:13 2008 -0400 +++ b/unittests/org/classpath/icedtea/pulseaudio/PulseAudioClipTest.java Thu Aug 14 14:24:17 2008 -0400 @@ -37,11 +37,16 @@ package org.classpath.icedtea.pulseaudio; +import java.io.File; +import java.io.IOException; + +import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.Clip; import javax.sound.sampled.Line; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.Mixer; +import javax.sound.sampled.UnsupportedAudioFileException; import junit.framework.JUnit4TestAdapter; @@ -79,6 +84,35 @@ Assert.assertNotNull(clip); } + + @Test (expected=IllegalArgumentException.class) + public void testClipOpen() throws LineUnavailableException { + Clip clip = (Clip) mixer.getLine(new Line.Info(Clip.class)); + clip.open(); + } + + @Test + public void testClipOpens() throws LineUnavailableException, UnsupportedAudioFileException, IOException { + Clip clip = (Clip) mixer.getLine(new Line.Info(Clip.class)); + File soundFile = new File("testsounds/startup.wav"); + AudioInputStream audioInputStream = AudioSystem + .getAudioInputStream(soundFile); + clip.open(audioInputStream); + } + + @Test + public void testPlayClip() throws LineUnavailableException, IOException, UnsupportedAudioFileException { + Clip clip = (Clip) mixer.getLine(new Line.Info(Clip.class)); + File soundFile = new File("testsounds/startup.wav"); + AudioInputStream audioInputStream = AudioSystem + .getAudioInputStream(soundFile); + clip.open(audioInputStream); + clip.loop(1); + + + + } + @After public void tearDown() {