diff --git a/lib/native/linux-64/libjportaudio.so b/lib/native/linux-64/libjportaudio.so index 54cea33d6..87c3bb1e4 100755 Binary files a/lib/native/linux-64/libjportaudio.so and b/lib/native/linux-64/libjportaudio.so differ diff --git a/lib/native/linux/libjportaudio.so b/lib/native/linux/libjportaudio.so index 1a39507c4..3855879a7 100755 Binary files a/lib/native/linux/libjportaudio.so and b/lib/native/linux/libjportaudio.so differ diff --git a/lib/native/mac/libjportaudio.jnilib b/lib/native/mac/libjportaudio.jnilib index 70c657471..daef9a694 100755 Binary files a/lib/native/mac/libjportaudio.jnilib and b/lib/native/mac/libjportaudio.jnilib differ diff --git a/lib/native/windows/jportaudio.dll b/lib/native/windows/jportaudio.dll index 8ec47f214..106fa0bec 100644 Binary files a/lib/native/windows/jportaudio.dll and b/lib/native/windows/jportaudio.dll differ diff --git a/src/native/portaudio/net_java_sip_communicator_impl_neomedia_portaudio_PortAudio.c b/src/native/portaudio/net_java_sip_communicator_impl_neomedia_portaudio_PortAudio.c index efe66f0af..26b5ec022 100644 --- a/src/native/portaudio/net_java_sip_communicator_impl_neomedia_portaudio_PortAudio.c +++ b/src/native/portaudio/net_java_sip_communicator_impl_neomedia_portaudio_PortAudio.c @@ -6,14 +6,15 @@ */ #include "net_java_sip_communicator_impl_neomedia_portaudio_PortAudio.h" +#include #include -#include #include #include #include -#include +#include +#include +//#include /* used on Windows */ #include -//#include //used on windows typedef struct { @@ -24,14 +25,14 @@ typedef struct typedef struct { - PaStream *stream; - jobject streamCallback; - JavaVM *vm; - JNIEnv *env; - jmethodID streamCallbackMethodID; - jmethodID streamFinishedCallbackMethodID; - long inputFrameSize; - long outputFrameSize; + PaStream *stream; + jobject streamCallback; + JavaVM *vm; + JNIEnv *env; + jmethodID streamCallbackMethodID; + jmethodID streamFinishedCallbackMethodID; + long inputFrameSize; + long outputFrameSize; double samplerate; SpeexResamplerState *outputResampler; @@ -49,34 +50,38 @@ typedef struct Buffer *first; Buffer *last; -} -PortAudioStream; +} PortAudioStream; #define DEFAULT_SAMPLE_RATE 44100.0 static void PortAudio_throwException(JNIEnv *env, PaError errorCode); static PaStreamParameters * PortAudio_fixInputParametersSuggestedLatency( - PaStreamParameters *inputParameters); + PaStreamParameters *inputParameters); static PaStreamParameters * PortAudio_fixOutputParametersSuggestedLatency( - PaStreamParameters *outputParameters); + PaStreamParameters *outputParameters); static long PortAudio_getFrameSize(PaStreamParameters *streamParameters); static int PortAudioStream_callback( - const void *input, - void *output, - unsigned long frameCount, - const PaStreamCallbackTimeInfo *timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData); + const void *input, + void *output, + unsigned long frameCount, + const PaStreamCallbackTimeInfo *timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData); static void PortAudioStream_finishedCallback(void *userData); static void PortAudioStream_free(JNIEnv *env, PortAudioStream *stream); static PortAudioStream * PortAudioStream_new( - JNIEnv *env, jobject streamCallback); + JNIEnv *env, jobject streamCallback); +static PaError PortAudioStream_write( + PortAudioStream *stream, + jbyte *buffer, + jlong frames); static void clear(PortAudioStream *st) { int cleared = 0; Buffer *curr = st->first; + while(curr != NULL) { Buffer *n = curr->next; @@ -85,12 +90,6 @@ static void clear(PortAudioStream *st) curr = n; cleared++; } - -/* - if(cleared > 0) - printf("cleared %d\n", cleared);fflush(stdout); -*/ - st->first = NULL; st->last = NULL; } @@ -117,21 +116,21 @@ static Buffer* getBuffer(PortAudioStream *st, int time) Buffer *res = st->first; st->first = st->first->next; - if(st->first == NULL) - { st->last = NULL; - } - return res; } JNIEXPORT void JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_setEchoCancelParams - (JNIEnv *env, jclass clazz, - jlong instream, jlong outstream, - jboolean enableDenoise, - jboolean enableEchoCancel, jint frameSize, jint filterLength) +Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_setEchoCancelParams( + JNIEnv *env, + jclass clazz, + jlong instream, + jlong outstream, + jboolean enableDenoise, + jboolean enableEchoCancel, + jint frameSize, + jint filterLength) { PortAudioStream *inAudioStream = (PortAudioStream *) instream; PortAudioStream *outAudioStream = (PortAudioStream *) outstream; @@ -184,7 +183,7 @@ Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1GetDefaultO JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1CloseStream( - JNIEnv *env, jclass clazz, jlong stream) + JNIEnv *env, jclass clazz, jlong stream) { PortAudioStream *portAudioStream = (PortAudioStream *) stream; @@ -245,86 +244,84 @@ Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1AbortStream PaError errorCode = Pa_AbortStream(((PortAudioStream *) stream)->stream); if (paNoError != errorCode) - PortAudio_throwException(env, errorCode); + PortAudio_throwException(env, errorCode); } JNIEXPORT jint JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1GetDeviceCount( - JNIEnv *env, jclass clazz) + JNIEnv *env, jclass clazz) { - PaDeviceIndex deviceCount = Pa_GetDeviceCount(); + PaDeviceIndex deviceCount = Pa_GetDeviceCount(); - if (deviceCount < 0) - PortAudio_throwException(env, deviceCount); - return deviceCount; + if (deviceCount < 0) + PortAudio_throwException(env, deviceCount); + return deviceCount; } JNIEXPORT jlong JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1GetDeviceInfo( - JNIEnv *env, jclass clazz, jint deviceIndex) + JNIEnv *env, jclass clazz, jint deviceIndex) { - return (jlong) Pa_GetDeviceInfo(deviceIndex); + return (jlong) Pa_GetDeviceInfo(deviceIndex); } JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1Initialize( - JNIEnv *env, jclass clazz) + JNIEnv *env, jclass clazz) { - PaError errorCode = Pa_Initialize(); + PaError errorCode = Pa_Initialize(); - if (paNoError != errorCode) - PortAudio_throwException(env, errorCode); + if (paNoError != errorCode) + PortAudio_throwException(env, errorCode); } JNIEXPORT jlong JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1OpenStream( - JNIEnv *env, - jclass clazz, - jlong inputParameters, - jlong outputParameters, - jdouble sampleRate, - jlong framesPerBuffer, - jlong streamFlags, - jobject streamCallback) -{ - PortAudioStream *stream = PortAudioStream_new(env, streamCallback); - PaError errorCode; - PaStreamParameters *inputStreamParameters - = (PaStreamParameters *) inputParameters; - PaStreamParameters *outputStreamParameters - = (PaStreamParameters *) outputParameters; - - if (!stream) - return 0; - - double defSampleRate = DEFAULT_SAMPLE_RATE; - - /* - * Obay default sample rate of the device. some devices has - * default 44.1 kHz and some 48 kHz. - */ - if(outputStreamParameters) - { - defSampleRate = - Pa_GetDeviceInfo(outputStreamParameters->device)->defaultSampleRate; - } - else if(inputStreamParameters) - { - defSampleRate = - Pa_GetDeviceInfo(inputStreamParameters->device)->defaultSampleRate; - } + JNIEnv *env, + jclass clazz, + jlong inputParameters, + jlong outputParameters, + jdouble sampleRate, + jlong framesPerBuffer, + jlong streamFlags, + jobject streamCallback) +{ + PortAudioStream *stream = PortAudioStream_new(env, streamCallback); + PaError errorCode; + PaStreamParameters *inputStreamParameters + = (PaStreamParameters *) inputParameters; + PaStreamParameters *outputStreamParameters + = (PaStreamParameters *) outputParameters; + + if (!stream) + return 0; - errorCode - = Pa_OpenStream( - &(stream->stream), - PortAudio_fixInputParametersSuggestedLatency(inputStreamParameters), - PortAudio_fixOutputParametersSuggestedLatency( - outputStreamParameters), - defSampleRate, - framesPerBuffer, - streamFlags, - streamCallback ? PortAudioStream_callback : NULL, - stream); + double defSampleRate = DEFAULT_SAMPLE_RATE; + + /* + * Obay default sample rate of the device. some devices has default 44.1kHz + * and some 48kHz. + */ + if(outputStreamParameters) + defSampleRate + = Pa_GetDeviceInfo(outputStreamParameters->device) + ->defaultSampleRate; + else if(inputStreamParameters) + defSampleRate + = Pa_GetDeviceInfo(inputStreamParameters->device) + ->defaultSampleRate; + + errorCode + = Pa_OpenStream( + &(stream->stream), + PortAudio_fixInputParametersSuggestedLatency(inputStreamParameters), + PortAudio_fixOutputParametersSuggestedLatency( + outputStreamParameters), + defSampleRate, + framesPerBuffer, + streamFlags, + streamCallback ? PortAudioStream_callback : NULL, + stream); stream->samplerate = sampleRate; @@ -356,139 +353,98 @@ Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1OpenStream( stream->inputChannelCount, defSampleRate, sampleRate, 3, NULL); stream->inputFrameSize - = PortAudio_getFrameSize(inputStreamParameters); + = PortAudio_getFrameSize(inputStreamParameters); } } else stream->inputResampleFactor = 1.0; - if (paNoError == errorCode) - { - stream->outputFrameSize - = PortAudio_getFrameSize(outputStreamParameters); + if (paNoError == errorCode) + { stream->inputFrameSize - = PortAudio_getFrameSize(inputStreamParameters); - - if (streamCallback) - { - stream->inputFrameSize - = PortAudio_getFrameSize(inputStreamParameters); - stream->outputFrameSize - = PortAudio_getFrameSize(outputStreamParameters); - - errorCode - = Pa_SetStreamFinishedCallback( - stream->stream, - PortAudioStream_finishedCallback); - } - - return (jlong) stream; - } - else - { - PortAudioStream_free(env, stream); - PortAudio_throwException(env, errorCode); - return 0; - } + = PortAudio_getFrameSize(inputStreamParameters); + stream->outputFrameSize + = PortAudio_getFrameSize(outputStreamParameters); + + if (streamCallback) + errorCode + = Pa_SetStreamFinishedCallback( + stream->stream, + PortAudioStream_finishedCallback); + + return (jlong) stream; + } + else + { + PortAudioStream_free(env, stream); + PortAudio_throwException(env, errorCode); + return 0; + } } JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1StartStream( - JNIEnv *env, jclass clazz, jlong stream) + JNIEnv *env, jclass clazz, jlong stream) { - PaError errorCode = Pa_StartStream(((PortAudioStream *) stream)->stream); + PaError errorCode = Pa_StartStream(((PortAudioStream *) stream)->stream); - if (paNoError != errorCode) - PortAudio_throwException(env, errorCode); + if (paNoError != errorCode) + PortAudio_throwException(env, errorCode); } JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1StopStream( - JNIEnv *env, jclass clazz, jlong stream) + JNIEnv *env, jclass clazz, jlong stream) { - PaError errorCode = Pa_StopStream(((PortAudioStream *) stream)->stream); + PaError errorCode = Pa_StopStream(((PortAudioStream *) stream)->stream); - if (paNoError != errorCode) - PortAudio_throwException(env, errorCode); + if (paNoError != errorCode) + PortAudio_throwException(env, errorCode); } JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1WriteStream( - JNIEnv *env, jclass clazz, jlong stream, jbyteArray buffer, jlong frames) + JNIEnv *env, + jclass clazz, + jlong stream, + jbyteArray buffer, + jint offset, + jlong frames, + jint numberOfWrites) { - jbyte* data = (*env)->GetByteArrayElements(env, buffer, NULL); - - PortAudioStream *outStream = (PortAudioStream *) stream; - if (data) - { - PaError errorCode; - - if(outStream->outputResampleFactor != 1) - { - spx_uint32_t out_len; - out_len = lrint( - frames - *outStream->outputChannelCount - *outStream->outputResampleFactor); - - short res[out_len]; - speex_resampler_process_interleaved_int( - outStream->outputResampler, - data, - &frames, - res, - &out_len); - - errorCode = Pa_WriteStream( - outStream->stream, - res, - out_len); - - - if(outStream->connectedToStream != NULL && - ((PortAudioStream *)outStream->connectedToStream)->echoState != NULL && - outStream->startCaching == 1) - { - struct timeval tv; - gettimeofday(&tv,NULL); - - Buffer *b; - b = (Buffer*)malloc(sizeof(Buffer)); - b->time = tv.tv_sec*1000 + tv.tv_usec/1000; - - b->data = malloc(sizeof(short)*frames); - memcpy(b->data, data, sizeof(short)*frames); + jbyte *bufferBytes; + jbyte* data; + PortAudioStream *portAudioStream; + long frameSize; + PaError errorCode; + jint i; + + bufferBytes = (*env)->GetByteArrayElements(env, buffer, NULL); + if (!bufferBytes) + return; - b->next = NULL; + data = bufferBytes + offset; + portAudioStream = (PortAudioStream *) stream; + frameSize = portAudioStream->outputFrameSize; - addBuffer(outStream, b); - } - } + for (i = 0; i < numberOfWrites; i++) + { + errorCode = PortAudioStream_write(portAudioStream, data, frames); + if ((paNoError != errorCode) && (errorCode != paOutputUnderflowed)) + break; else - { - errorCode = Pa_WriteStream( - outStream->stream, - data, - frames); - } - - (*env)->ReleaseByteArrayElements(env, buffer, data, 0); + data += frames * frameSize; + } - if (paNoError != errorCode && errorCode != paOutputUnderflowed) - PortAudio_throwException(env, errorCode); + (*env)->ReleaseByteArrayElements(env, buffer, bufferBytes, 0); -/* - if(errorCode == paOutputUnderflowed) - { - printf("OutputUnderflowed\n");fflush(stdout); - } -*/ - } + if ((paNoError != errorCode) && (errorCode != paOutputUnderflowed)) + PortAudio_throwException(env, errorCode); } JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1ReadStream - (JNIEnv *env, jclass clazz, jlong stream, jbyteArray buffer, jlong frames) + (JNIEnv *env, jclass clazz, jlong stream, jbyteArray buffer, jlong frames) { jbyte* data = (*env)->GetByteArrayElements(env, buffer, NULL); @@ -639,26 +595,26 @@ Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1IsFormatSup JNIEXPORT jint JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaDeviceInfo_1getMaxInputChannels( - JNIEnv *env, jclass clazz, jlong deviceInfo) + JNIEnv *env, jclass clazz, jlong deviceInfo) { - return ((PaDeviceInfo *) deviceInfo)->maxInputChannels; + return ((PaDeviceInfo *) deviceInfo)->maxInputChannels; } JNIEXPORT jint JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaDeviceInfo_1getMaxOutputChannels( - JNIEnv *env, jclass clazz, jlong deviceInfo) + JNIEnv *env, jclass clazz, jlong deviceInfo) { - return ((PaDeviceInfo *) deviceInfo)->maxOutputChannels; + return ((PaDeviceInfo *) deviceInfo)->maxOutputChannels; } JNIEXPORT jstring JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaDeviceInfo_1getName( - JNIEnv *env, jclass clazz, jlong deviceInfo) + JNIEnv *env, jclass clazz, jlong deviceInfo) { - const char *name = ((PaDeviceInfo *) deviceInfo)->name; + const char *name = ((PaDeviceInfo *) deviceInfo)->name; - return name ? (*env)->NewStringUTF(env, name) : NULL; + return name ? (*env)->NewStringUTF(env, name) : NULL; } JNIEXPORT jdouble JNICALL @@ -749,30 +705,30 @@ Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaHostApiInfo_1 JNIEXPORT jlong JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaStreamParameters_1new( - JNIEnv *env, - jclass clazz, - jint deviceIndex, - jint channelCount, - jlong sampleFormat, + JNIEnv *env, + jclass clazz, + jint deviceIndex, + jint channelCount, + jlong sampleFormat, jdouble suggestedLatency) { - PaStreamParameters *streamParameters - = (PaStreamParameters *) malloc(sizeof(PaStreamParameters)); + PaStreamParameters *streamParameters + = (PaStreamParameters *) malloc(sizeof(PaStreamParameters)); - if (streamParameters) - { - streamParameters->device = deviceIndex; - streamParameters->channelCount = channelCount; - streamParameters->sampleFormat = sampleFormat; - streamParameters->suggestedLatency = suggestedLatency; - streamParameters->hostApiSpecificStreamInfo = NULL; - } - return (jlong) streamParameters; + if (streamParameters) + { + streamParameters->device = deviceIndex; + streamParameters->channelCount = channelCount; + streamParameters->sampleFormat = sampleFormat; + streamParameters->suggestedLatency = suggestedLatency; + streamParameters->hostApiSpecificStreamInfo = NULL; + } + return (jlong) streamParameters; } static PaStreamParameters * PortAudio_fixInputParametersSuggestedLatency( - PaStreamParameters *inputParameters) + PaStreamParameters *inputParameters) { if (inputParameters) { @@ -796,13 +752,13 @@ PortAudio_fixInputParametersSuggestedLatency( static PaStreamParameters * PortAudio_fixOutputParametersSuggestedLatency( - PaStreamParameters *outputParameters) + PaStreamParameters *outputParameters) { - if (outputParameters) - { - PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(outputParameters->device); + if (outputParameters) + { + PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(outputParameters->device); - if (deviceInfo) + if (deviceInfo) { if(outputParameters->suggestedLatency == net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_LATENCY_LOW) @@ -814,173 +770,173 @@ PortAudio_fixOutputParametersSuggestedLatency( outputParameters->suggestedLatency = deviceInfo->defaultHighOutputLatency; } - } - return outputParameters; + } + return outputParameters; } static long PortAudio_getFrameSize(PaStreamParameters *streamParameters) { - if (streamParameters) - { - PaError sampleSize = Pa_GetSampleSize(streamParameters->sampleFormat); + if (streamParameters) + { + PaError sampleSize = Pa_GetSampleSize(streamParameters->sampleFormat); - if (paSampleFormatNotSupported != sampleSize) - return sampleSize * streamParameters->channelCount; - } - return 0; + if (paSampleFormatNotSupported != sampleSize) + return sampleSize * streamParameters->channelCount; + } + return 0; } static void PortAudio_throwException(JNIEnv *env, PaError errorCode) { - jclass clazz - = (*env) - ->FindClass( - env, - "net/java/sip/communicator/impl/neomedia/portaudio/PortAudioException"); - - if (clazz) - (*env)->ThrowNew(env, clazz, Pa_GetErrorText(errorCode)); + jclass clazz + = (*env) + ->FindClass( + env, + "net/java/sip/communicator/impl/neomedia/portaudio/PortAudioException"); + + if (clazz) + (*env)->ThrowNew(env, clazz, Pa_GetErrorText(errorCode)); } static int PortAudioStream_callback( - const void *input, - void *output, - unsigned long frameCount, - const PaStreamCallbackTimeInfo *timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData) -{ - PortAudioStream *stream = (PortAudioStream *) userData; - jobject streamCallback = stream->streamCallback; - JNIEnv *env; - jmethodID streamCallbackMethodID; - - if (!streamCallback) - return paContinue; - - env = stream->env; - if (!env) - { - JavaVM *vm = stream->vm; - - if ((*vm)->AttachCurrentThreadAsDaemon(vm, (void **) &env, NULL) < 0) - return paAbort; - else - stream->env = env; - } - streamCallbackMethodID = stream->streamCallbackMethodID; - if (!streamCallbackMethodID) - { - jclass streamCallbackClass - = (*env)->GetObjectClass(env, streamCallback); - - streamCallbackMethodID - = (*env) - ->GetMethodID( - env, - streamCallbackClass, - "callback", - "(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)I"); - if (streamCallbackMethodID) - stream->streamCallbackMethodID = streamCallbackMethodID; - else - return paAbort; - } + const void *input, + void *output, + unsigned long frameCount, + const PaStreamCallbackTimeInfo *timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData) +{ + PortAudioStream *stream = (PortAudioStream *) userData; + jobject streamCallback = stream->streamCallback; + JNIEnv *env; + jmethodID streamCallbackMethodID; + + if (!streamCallback) + return paContinue; + + env = stream->env; + if (!env) + { + JavaVM *vm = stream->vm; + + if ((*vm)->AttachCurrentThreadAsDaemon(vm, (void **) &env, NULL) < 0) + return paAbort; + else + stream->env = env; + } + streamCallbackMethodID = stream->streamCallbackMethodID; + if (!streamCallbackMethodID) + { + jclass streamCallbackClass + = (*env)->GetObjectClass(env, streamCallback); + + streamCallbackMethodID + = (*env) + ->GetMethodID( + env, + streamCallbackClass, + "callback", + "(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)I"); + if (streamCallbackMethodID) + stream->streamCallbackMethodID = streamCallbackMethodID; + else + return paAbort; + } return - (*env) - ->CallIntMethod( - env, - streamCallback, - streamCallbackMethodID, - input - ? (*env) - ->NewDirectByteBuffer( - env, - input, - frameCount * stream->inputFrameSize) - : NULL, - output - ? (*env) - ->NewDirectByteBuffer( - env, - output, - frameCount * stream->outputFrameSize) - : NULL); + (*env) + ->CallIntMethod( + env, + streamCallback, + streamCallbackMethodID, + input + ? (*env) + ->NewDirectByteBuffer( + env, + input, + frameCount * stream->inputFrameSize) + : NULL, + output + ? (*env) + ->NewDirectByteBuffer( + env, + output, + frameCount * stream->outputFrameSize) + : NULL); } static void PortAudioStream_finishedCallback(void *userData) { - PortAudioStream *stream = (PortAudioStream *) userData; - jobject streamCallback = stream->streamCallback; - JNIEnv *env; - jmethodID streamFinishedCallbackMethodID; - - if (!streamCallback) - return; - - env = stream->env; - if (!env) - { - JavaVM *vm = stream->vm; - - if ((*vm)->AttachCurrentThreadAsDaemon(vm, (void **) &env, NULL) < 0) - return; - else - stream->env = env; - } - streamFinishedCallbackMethodID = stream->streamFinishedCallbackMethodID; - if (!streamFinishedCallbackMethodID) - { - jclass streamCallbackClass - = (*env)->GetObjectClass(env, streamCallback); - - streamFinishedCallbackMethodID - = (*env) - ->GetMethodID( - env, - streamCallbackClass, - "finishedCallback", - "()V"); - if (streamFinishedCallbackMethodID) - stream->streamFinishedCallbackMethodID - = streamFinishedCallbackMethodID; - else - return; - } - - (*env)->CallVoidMethod(env, streamCallback, streamFinishedCallbackMethodID); + PortAudioStream *stream = (PortAudioStream *) userData; + jobject streamCallback = stream->streamCallback; + JNIEnv *env; + jmethodID streamFinishedCallbackMethodID; + + if (!streamCallback) + return; + + env = stream->env; + if (!env) + { + JavaVM *vm = stream->vm; + + if ((*vm)->AttachCurrentThreadAsDaemon(vm, (void **) &env, NULL) < 0) + return; + else + stream->env = env; + } + streamFinishedCallbackMethodID = stream->streamFinishedCallbackMethodID; + if (!streamFinishedCallbackMethodID) + { + jclass streamCallbackClass + = (*env)->GetObjectClass(env, streamCallback); + + streamFinishedCallbackMethodID + = (*env) + ->GetMethodID( + env, + streamCallbackClass, + "finishedCallback", + "()V"); + if (streamFinishedCallbackMethodID) + stream->streamFinishedCallbackMethodID + = streamFinishedCallbackMethodID; + else + return; + } + + (*env)->CallVoidMethod(env, streamCallback, streamFinishedCallbackMethodID); } static void PortAudioStream_free(JNIEnv *env, PortAudioStream *stream) { - if (stream->streamCallback) - { - (*env)->DeleteGlobalRef(env, stream->streamCallback); - stream->streamCallback = NULL; - } - stream->streamCallbackMethodID = NULL; - stream->streamFinishedCallbackMethodID = NULL; + if (stream->streamCallback) + { + (*env)->DeleteGlobalRef(env, stream->streamCallback); + stream->streamCallback = NULL; + } + stream->streamCallbackMethodID = NULL; + stream->streamFinishedCallbackMethodID = NULL; - free(stream); + free(stream); } static PortAudioStream * PortAudioStream_new(JNIEnv *env, jobject streamCallback) { - PortAudioStream *stream = malloc(sizeof(PortAudioStream)); + PortAudioStream *stream = malloc(sizeof(PortAudioStream)); - if (!stream) - { - PortAudio_throwException(env, paInsufficientMemory); - return NULL; - } - stream->stream = NULL; + if (!stream) + { + PortAudio_throwException(env, paInsufficientMemory); + return NULL; + } + stream->stream = NULL; stream->preprocessor = NULL; stream->echoState = NULL; stream->first = NULL; @@ -988,32 +944,95 @@ PortAudioStream_new(JNIEnv *env, jobject streamCallback) stream->connectedToStream = NULL; stream->startCaching = 0; - if (streamCallback) - { - if ((*env)->GetJavaVM(env, &(stream->vm)) < 0) - { - free(stream); - PortAudio_throwException(env, paInternalError); - return NULL; - } - - stream->streamCallback = (*env)->NewGlobalRef(env, streamCallback); - if (!(stream->streamCallback)) - { - free(stream); - PortAudio_throwException(env, paInsufficientMemory); - return NULL; - } - } - else - { - stream->vm = NULL; - stream->streamCallback = NULL; - } - - stream->env = NULL; - stream->streamCallbackMethodID = NULL; - stream->streamFinishedCallbackMethodID = NULL; - - return stream; + if (streamCallback) + { + if ((*env)->GetJavaVM(env, &(stream->vm)) < 0) + { + free(stream); + PortAudio_throwException(env, paInternalError); + return NULL; + } + + stream->streamCallback = (*env)->NewGlobalRef(env, streamCallback); + if (!(stream->streamCallback)) + { + free(stream); + PortAudio_throwException(env, paInsufficientMemory); + return NULL; + } + } + else + { + stream->vm = NULL; + stream->streamCallback = NULL; + } + + stream->env = NULL; + stream->streamCallbackMethodID = NULL; + stream->streamFinishedCallbackMethodID = NULL; + + return stream; +} + +static PaError +PortAudioStream_write( + PortAudioStream *stream, + jbyte *buffer, + jlong frames) +{ + PaError errorCode; + + if(stream->outputResampleFactor != 1) + { + spx_uint32_t out_len; + out_len = lrint( + frames + *stream->outputChannelCount + *stream->outputResampleFactor); + + short res[out_len]; + speex_resampler_process_interleaved_int( + stream->outputResampler, + buffer, + &frames, + res, + &out_len); + + errorCode = Pa_WriteStream(stream->stream, res, out_len); + + if(stream->connectedToStream + && ((PortAudioStream *)stream->connectedToStream) + ->echoState + && (stream->startCaching == 1)) + { + Buffer *b; + + b = malloc(sizeof(Buffer)); + if (b) + { + struct timeval tv; + + gettimeofday(&tv,NULL); + b->time = tv.tv_sec*1000 + tv.tv_usec/1000; + + b->data = malloc(sizeof(short)*frames); + if (b->data) + { + memcpy(b->data, buffer, sizeof(short)*frames); + b->next = NULL; + addBuffer(stream, b); + } + else + { + free(b); + errorCode = paInsufficientMemory; + } + } + else + errorCode = paInsufficientMemory; + } + } + else + errorCode = Pa_WriteStream(stream->stream, buffer, frames); + return errorCode; } diff --git a/src/native/portaudio/net_java_sip_communicator_impl_neomedia_portaudio_PortAudio.h b/src/native/portaudio/net_java_sip_communicator_impl_neomedia_portaudio_PortAudio.h index 662b360a7..59b8fa756 100644 --- a/src/native/portaudio/net_java_sip_communicator_impl_neomedia_portaudio_PortAudio.h +++ b/src/native/portaudio/net_java_sip_communicator_impl_neomedia_portaudio_PortAudio.h @@ -104,10 +104,10 @@ JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_Po /* * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio * Method: Pa_WriteStream - * Signature: (J[BJ)V + * Signature: (J[BIJI)V */ JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1WriteStream - (JNIEnv *, jclass, jlong, jbyteArray, jlong); + (JNIEnv *, jclass, jlong, jbyteArray, jint, jlong, jint); /* * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio diff --git a/src/net/java/sip/communicator/impl/neomedia/device/DeviceConfiguration.java b/src/net/java/sip/communicator/impl/neomedia/device/DeviceConfiguration.java index 2abaa1f52..548153285 100644 --- a/src/net/java/sip/communicator/impl/neomedia/device/DeviceConfiguration.java +++ b/src/net/java/sip/communicator/impl/neomedia/device/DeviceConfiguration.java @@ -891,10 +891,10 @@ public void setEchoCancel(boolean enabled, boolean save) { try { - PortAudioManager.getInstance().setEchoCancel( - enabled, - PortAudioManager.getInstance().getFrameSize(), - PortAudioManager.getInstance().getFilterLength()); + PortAudioManager portAudioManager = PortAudioManager.getInstance(); + + portAudioManager + .setEchoCancel(enabled, portAudioManager.getFilterLength()); if(save) NeomediaActivator.getConfigurationService() diff --git a/src/net/java/sip/communicator/impl/neomedia/device/PortAudioAuto.java b/src/net/java/sip/communicator/impl/neomedia/device/PortAudioAuto.java index aaea7be92..4ee2b7a52 100644 --- a/src/net/java/sip/communicator/impl/neomedia/device/PortAudioAuto.java +++ b/src/net/java/sip/communicator/impl/neomedia/device/PortAudioAuto.java @@ -65,7 +65,7 @@ public class PortAudioAuto // if PortAudio has a problem initializing like missing native // components it will trow exception here and PortAudio rendering will // not be inited. - PortAudioManager.getInstance(); + PortAudioManager portAudioManager = PortAudioManager.getInstance(); // enable jmf logging, so we can track codec chains and formats if(logger.isDebugEnabled()) @@ -128,32 +128,32 @@ public class PortAudioAuto boolean echoCancelEnabled = config.getBoolean( DeviceConfiguration.PROP_AUDIO_ECHOCANCEL_ENABLED, - PortAudioManager.getInstance().isEnabledEchoCancel()); + portAudioManager.isEnabledEchoCancel()); if(echoCancelEnabled) { int echoCancelTail = config.getInt( DeviceConfiguration.PROP_AUDIO_ECHOCANCEL_TAIL, - PortAudioManager.getInstance().getFilterLength()); - PortAudioManager.getInstance().setEchoCancel( + portAudioManager.getFilterLength()); + portAudioManager.setEchoCancel( echoCancelEnabled, - PortAudioManager.getInstance().getFrameSize(), echoCancelTail); } boolean denoiseEnabled = config.getBoolean( DeviceConfiguration.PROP_AUDIO_DENOISE_ENABLED, - PortAudioManager.getInstance().isEnabledDeNoise()); - PortAudioManager.getInstance().setDeNoise(denoiseEnabled); + portAudioManager.isEnabledDeNoise()); + portAudioManager.setDeNoise(denoiseEnabled); // suggested latency is saved in configuration as // milliseconds but PortAudioManager use it as seconds + int defaultAudioLatency + = (int) (PortAudioManager.getSuggestedLatency()*1000); int audioLatency = config.getInt( DeviceConfiguration.PROP_AUDIO_LATENCY, - (int)(PortAudioManager.getSuggestedLatency()*1000)); - if(audioLatency != - (int)PortAudioManager.getSuggestedLatency()*1000) + defaultAudioLatency); + if(audioLatency != defaultAudioLatency) PortAudioManager.setSuggestedLatency( (double)audioLatency/1000d); } diff --git a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/portaudio/DSAudioStream.java b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/portaudio/DSAudioStream.java index 6cf93a529..e2e89c440 100644 --- a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/portaudio/DSAudioStream.java +++ b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/portaudio/DSAudioStream.java @@ -6,9 +6,6 @@ */ package net.java.sip.communicator.impl.neomedia.jmfext.media.protocol.portaudio; -import net.java.sip.communicator.impl.neomedia.portaudio.*; -import net.java.sip.communicator.impl.neomedia.portaudio.streams.*; - import java.io.*; import javax.media.*; @@ -16,6 +13,8 @@ import javax.media.protocol.*; import net.java.sip.communicator.impl.neomedia.control.*; +import net.java.sip.communicator.impl.neomedia.portaudio.*; +import net.java.sip.communicator.impl.neomedia.portaudio.streams.*; /** * The stream used by jmf, wraps our InputPortAudioStream, which wraps @@ -28,8 +27,8 @@ public class DSAudioStream extends ControlsAdapter implements PullBufferStream { - private final static ContentDescriptor cd = - new ContentDescriptor(ContentDescriptor.RAW); + private static final ContentDescriptor cd + = new ContentDescriptor(ContentDescriptor.RAW); private final int deviceIndex; @@ -55,8 +54,7 @@ void start() { if(stream == null) { - AudioFormat audioFormat = - (AudioFormat)DataSource.getCaptureFormat(); + AudioFormat audioFormat = DataSource.getCaptureFormat(); stream = PortAudioManager.getInstance().getInputStream(deviceIndex, audioFormat.getSampleRate(), audioFormat.getChannels()); @@ -106,7 +104,7 @@ public void read(Buffer buffer) buffer.setData(bytebuff); buffer.setLength(bytebuff.length); - buffer.setFlags(0); + buffer.setFlags(Buffer.FLAG_SYSTEM_TIME); buffer.setFormat(getFormat()); buffer.setHeader(null); diff --git a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/portaudio/DataSource.java b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/portaudio/DataSource.java index 5160dd902..b869fb244 100644 --- a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/portaudio/DataSource.java +++ b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/portaudio/DataSource.java @@ -73,24 +73,24 @@ public class DataSource /** * The format of the media captured by the datasource. */ - private static AudioFormat captureAudioFormat = - new AudioFormat( + private static AudioFormat captureAudioFormat + = new AudioFormat( AudioFormat.LINEAR, - 8000, - 16, - 1, - AudioFormat.LITTLE_ENDIAN, - AudioFormat.SIGNED, - 16, - Format.NOT_SPECIFIED, - Format.byteArray); + 8000, + 16, + 1, + AudioFormat.LITTLE_ENDIAN, + AudioFormat.SIGNED, + 16, + Format.NOT_SPECIFIED, + Format.byteArray); /** * Return the formats supported by the datasource. * * @return the supported formats. */ - public static Format getCaptureFormat() + public static AudioFormat getCaptureFormat() { return captureAudioFormat; } diff --git a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/renderer/audio/PortAudioRenderer.java b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/renderer/audio/PortAudioRenderer.java index 54d3b88ef..457a1f91c 100644 --- a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/renderer/audio/PortAudioRenderer.java +++ b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/renderer/audio/PortAudioRenderer.java @@ -127,26 +127,22 @@ public synchronized void stop() /** * Processes the data and renders it * to the output device represented by this Renderer. - * @param inputBuffer the input data. + * @param buffer the input data. * @return BUFFER_PROCESSED_OK if the processing is successful. */ - public synchronized int process(Buffer inputBuffer) + public synchronized int process(Buffer buffer) { - byte[] buff = new byte[inputBuffer.getLength()]; - System.arraycopy( - (byte[])inputBuffer.getData(), - inputBuffer.getOffset(), - buff, - 0, - buff.length); - try { - stream.write(buff); + stream + .write( + (byte[]) buffer.getData(), + buffer.getOffset(), + buffer.getLength()); } - catch (PortAudioException e) + catch (PortAudioException paex) { - logger.error("Error writing to device", e); + logger.error("Error writing to device", paex); } return BUFFER_PROCESSED_OK; diff --git a/src/net/java/sip/communicator/impl/neomedia/notify/PortAudioClipImpl.java b/src/net/java/sip/communicator/impl/neomedia/notify/PortAudioClipImpl.java index 08ca56282..952402c26 100644 --- a/src/net/java/sip/communicator/impl/neomedia/notify/PortAudioClipImpl.java +++ b/src/net/java/sip/communicator/impl/neomedia/notify/PortAudioClipImpl.java @@ -37,7 +37,7 @@ public class PortAudioClipImpl private final URL url; - private Object syncObject = new Object(); + private final Object syncObject = new Object(); /** * Creates the audio clip and initialize the listener used from the @@ -110,6 +110,7 @@ private class PlayThread implements Runnable { private final byte[] buffer = new byte[1024]; + private OutputPortAudioStream portAudioStream = null; public void run() @@ -124,18 +125,22 @@ public void run() if (portAudioStream == null) { - int deviceIndex = - PortAudioUtils.getDeviceIndexFromLocator( + int deviceIndex + = PortAudioUtils.getDeviceIndexFromLocator( audioNotifier.getDeviceConfiguration(). getAudioNotifyDevice().getLocator()); - portAudioStream = PortAudioManager.getInstance(). - getOutputStream( - deviceIndex, - audioStreamFormat.getSampleRate(), - audioStreamFormat.getChannels(), - PortAudioUtils.getPortAudioSampleFormat( - audioStreamFormat.getSampleSizeInBits())); + portAudioStream + = PortAudioManager + .getInstance() + .getOutputStream( + deviceIndex, + audioStreamFormat.getSampleRate(), + audioStreamFormat.getChannels(), + PortAudioUtils + .getPortAudioSampleFormat( + audioStreamFormat + .getSampleSizeInBits())); portAudioStream.start(); } @@ -146,8 +151,12 @@ public void run() return; } - while(started && audioStream.read(buffer) != -1) - portAudioStream.write(buffer); + int bufferLength; + + while(started + && ((bufferLength = audioStream.read(buffer)) + != -1)) + portAudioStream.write(buffer, 0, bufferLength); if(!isLooping()) { @@ -158,12 +167,14 @@ public void run() else { synchronized(syncObject) { - if (started) { - try { + if (started) + try + { syncObject.wait(getLoopInterval()); - } catch (InterruptedException e) { } - } + catch (InterruptedException e) + { + } } } } diff --git a/src/net/java/sip/communicator/impl/neomedia/portaudio/PortAudio.java b/src/net/java/sip/communicator/impl/neomedia/portaudio/PortAudio.java index ae5ca7229..41768a60b 100644 --- a/src/net/java/sip/communicator/impl/neomedia/portaudio/PortAudio.java +++ b/src/net/java/sip/communicator/impl/neomedia/portaudio/PortAudio.java @@ -8,7 +8,9 @@ /** * PortAudio functions. + * * @author Lubomir Marinov + * @author Damian Minkov */ public final class PortAudio { @@ -262,10 +264,39 @@ public static native void Pa_StopStream(long stream) * @param frames The number of frames to be written from buffer. * @throws PortAudioException */ + public static void Pa_WriteStream(long stream, byte[] buffer, long frames) + throws PortAudioException + { + Pa_WriteStream(stream, buffer, 0, frames, 1); + } + + /** + * Writes samples to an output stream. Does not return until the specified + * samples have been consumed - this may involve waiting for the operating + * system to consume the data. + *

+ * Provides better efficiency than achieved through multiple consecutive + * calls to {@link #Pa_WriteStream(long, byte[], long)} with one and the + * same buffer because the JNI access to the bytes of the buffer which is + * likely to copy the whole buffer is only performed once. + *

+ * + * @param stream the pointer to the PortAudio stream to write the samples to + * @param buffer the buffer containing the samples to be written + * @param offset the byte offset in buffer at which the samples to + * be written start + * @param frames the number of frames from buffer starting at + * offset are to be written with a single write + * @param numberOfWrites the number of writes each writing frames + * number of frames to be performed + * @throws PortAudioException if anything goes wrong while writing + */ public static native void Pa_WriteStream( long stream, byte[] buffer, - long frames) + int offset, + long frames, + int numberOfWrites) throws PortAudioException; /** diff --git a/src/net/java/sip/communicator/impl/neomedia/portaudio/PortAudioManager.java b/src/net/java/sip/communicator/impl/neomedia/portaudio/PortAudioManager.java index bbb9928f7..99eb5a767 100644 --- a/src/net/java/sip/communicator/impl/neomedia/portaudio/PortAudioManager.java +++ b/src/net/java/sip/communicator/impl/neomedia/portaudio/PortAudioManager.java @@ -16,13 +16,17 @@ * them. * * @author Damian Minkov + * @author Lubomir Marinov */ public class PortAudioManager { + /** - * 20ms in 8kHz is 160 samples. + * The number of frames to be read from or written to a native PortAudio + * stream in a single transfer of data. The current value is based on 20ms + * of audio with 8kHz frame rate which is equal to 160 frames. */ - public static final int NUM_SAMPLES = 160; + private static final int FRAMES_PER_BUFFER = 160; /** * The static instance of portaudio manager. @@ -55,10 +59,11 @@ public class PortAudioManager private boolean enabledDeNoise = true; /** - * The default value for the frame size we use to read and write - * by portaudio. Currently 20 ms. + * The number of frames to be read from or written to a native PortAudio + * stream in a single transfer of data. The current value is based on 20ms + * of audio with 8kHz frame rate which is equal to 160 frames. */ - private int frameSize = NUM_SAMPLES; + private final int framesPerBuffer = FRAMES_PER_BUFFER; /** * The default value for number of samples of echo to cancel. @@ -113,24 +118,32 @@ public InputPortAudioStream getInputStream( throws PortAudioException { MasterPortAudioStream st = inputStreams.get(deviceIndex); + if(st == null) { st = new MasterPortAudioStream(deviceIndex, sampleRate, channels); inputStreams.put(deviceIndex, st); - // if there is a output streams, get the latest one - // and connect them - // todo: we must link input to all outputs ??? - if(isEnabledEchoCancel() && outputStreams.size() > 0) - { - OutputPortAudioStream out = outputStreams.get( - outputStreams.size() - 1); - st.setParams(out, isEnabledDeNoise(), - isEnabledEchoCancel(), getFrameSize(), getFilterLength()); - } + /* + * If there are output streams, get the latest one and connect them. + */ + // TODO We must link input to all outputs??? + boolean echoCancelIsEnabled = isEnabledEchoCancel(); + int outputStreamCount; + OutputPortAudioStream out; + + if(echoCancelIsEnabled + && ((outputStreamCount = outputStreams.size()) > 0)) + out = outputStreams.get(outputStreamCount - 1); else - st.setParams(null, isEnabledDeNoise(), - isEnabledEchoCancel(), getFrameSize(), getFilterLength()); + out = null; + + st.setParams( + out, + isEnabledDeNoise(), + echoCancelIsEnabled, + getFramesPerBuffer(), + getFilterLength()); } return new InputPortAudioStream(st); @@ -152,17 +165,26 @@ public OutputPortAudioStream getOutputStream(int deviceIndex, { OutputPortAudioStream out = new OutputPortAudioStream(deviceIndex, sampleRate, channels); + outputStreams.add(out); - // if there are input streams created, get the first one - // and link it to this output - // TODO what to do with the others - if(isEnabledEchoCancel() && inputStreams.size() > 0) + /* + * If there are input streams created, get the first one and link it to + * this output. + */ + // TODO What to do with the others? + boolean echoCancelIsEnabled = isEnabledEchoCancel(); + + if (echoCancelIsEnabled && (inputStreams.size() > 0)) { MasterPortAudioStream st = inputStreams.values().iterator().next(); - st.setParams(out, isEnabledEchoCancel(), - isEnabledDeNoise(), getFrameSize(), getFilterLength()); + st.setParams( + out, + echoCancelIsEnabled, + isEnabledDeNoise(), + getFramesPerBuffer(), + getFilterLength()); } return out; @@ -206,16 +228,13 @@ public OutputPortAudioStream getOutputStream( /** * Enables or disables echo cancel. - * @param enabled should we enable or disable echo cancelation - * @param frameSize Number of samples to process at one time - * (should correspond to 10-20 ms) + * @param enabled should we enable or disable echo cancellation * @param filterLength Number of samples of echo to cancel * (should generally correspond to 100-500 ms) */ - public void setEchoCancel(boolean enabled, int frameSize, int filterLength) + public void setEchoCancel(boolean enabled, int filterLength) { this.enabledEchoCancel = enabled; - this.frameSize = frameSize; this.filterLength = filterLength; } @@ -274,12 +293,14 @@ public boolean isEnabledDeNoise() } /** - * Number of samples to process at one time (should correspond to 10-20 ms). - * @return the frameSize. + * Gets the number of frames to process at a time (should correspond to + * 10-20ms). + * + * @return the number of frames to process at a time */ - public int getFrameSize() + public int getFramesPerBuffer() { - return frameSize; + return framesPerBuffer; } /** diff --git a/src/net/java/sip/communicator/impl/neomedia/portaudio/streams/MasterPortAudioStream.java b/src/net/java/sip/communicator/impl/neomedia/portaudio/streams/MasterPortAudioStream.java index 291931ec9..203132e60 100644 --- a/src/net/java/sip/communicator/impl/neomedia/portaudio/streams/MasterPortAudioStream.java +++ b/src/net/java/sip/communicator/impl/neomedia/portaudio/streams/MasterPortAudioStream.java @@ -38,7 +38,19 @@ public class MasterPortAudioStream /** * The frame size we use. */ - private int frameSize; + private final int frameSize; + + /** + * The number of frames to read from a native PortAudio stream in a single + * invocation. + */ + private final int framesPerBuffer; + + /** + * The number of bytes to read from a native PortAudio stream in a single + * invocation. Based on {@link #framesPerBuffer} and {@link #frameSize}. + */ + private final int bytesPerBuffer; /** * The sample rate for the current stream. @@ -85,6 +97,8 @@ public MasterPortAudioStream( frameSize = PortAudio.Pa_GetSampleSize(PortAudio.SAMPLE_FORMAT_INT16) * channels; + framesPerBuffer = PortAudioManager.getInstance().getFramesPerBuffer(); + bytesPerBuffer = frameSize * framesPerBuffer; } /** @@ -203,12 +217,11 @@ public synchronized byte[] read() if(!started) return new byte[0]; - byte[] bytebuff = new byte[PortAudioManager.NUM_SAMPLES*frameSize]; + byte[] bytebuff = new byte[bytesPerBuffer]; synchronized(connectedToStreamSync) { - PortAudio.Pa_ReadStream( - stream, bytebuff, PortAudioManager.NUM_SAMPLES); + PortAudio.Pa_ReadStream(stream, bytebuff, framesPerBuffer); } for(InputPortAudioStream slave : slaves) diff --git a/src/net/java/sip/communicator/impl/neomedia/portaudio/streams/OutputPortAudioStream.java b/src/net/java/sip/communicator/impl/neomedia/portaudio/streams/OutputPortAudioStream.java index 7b1cea133..96e6e29e8 100644 --- a/src/net/java/sip/communicator/impl/neomedia/portaudio/streams/OutputPortAudioStream.java +++ b/src/net/java/sip/communicator/impl/neomedia/portaudio/streams/OutputPortAudioStream.java @@ -39,22 +39,42 @@ public class OutputPortAudioStream */ private final int frameSize; + /** + * The number of frames to write to the native PortAudioStream represented + * by this instance with a single invocation. + */ + private final int framesPerBuffer; + + /** + * The number of bytes to write to a native PortAudio stream with a single + * invocation. Based on {@link #framesPerBuffer} and {@link #frameSize}. + */ + private final int bytesPerBuffer; + /** * The stream pointer we are using or 0 if stopped and not initialized. */ private long stream; + /** * Whether this stream is started. */ private boolean started = false; /** - * Buffer left for writing from previous write, - * as everything is split into parts of PortAudioManager.NUM_SAMPLES, - * this is what has left. + * The audio samples left unwritten by a previous call to + * {@link #write(byte[], int, int)}. As {@link #bytesPerBuffer} number of + * bytes are always written, the number of the unwritten audio samples is + * always less than that. */ private byte[] bufferLeft = null; + /** + * The number of bytes in {@link #bufferLeft} representing unwritten audio + * samples. + */ + private int bufferLeftLength = 0; + /** * We use this object to sync input stream reads with this output stream * closes, if this output stream is connected to input stream(when using @@ -86,7 +106,7 @@ public OutputPortAudioStream( * @throws PortAudioException if stream fails to open. */ public OutputPortAudioStream( - int deviceIndex, double sampleRate, int channels, long sampleFormat) + int deviceIndex, double sampleRate, int channels, long sampleFormat) throws PortAudioException { this.deviceIndex = deviceIndex; @@ -95,6 +115,9 @@ public OutputPortAudioStream( this.sampleFormat = sampleFormat; frameSize = PortAudio.Pa_GetSampleSize(sampleFormat)*channels; + framesPerBuffer = PortAudioManager.getInstance().getFramesPerBuffer(); + bytesPerBuffer = frameSize * framesPerBuffer; + stream = createStream(); } @@ -149,77 +172,89 @@ private long createStream() } /** - * We will split everything into parts of PortAudioManager.NUM_SAMPLES. - * If something is left we will save it for next write and use it than. + * Writes a specific byte buffer of audio samples into the native + * PortAudio stream represented by this instance. + *

+ * Splits the specified buffer and performs multiple writes with + * {@link PortAudioManager#getFramesPerBuffer()} number of frames at a time. + * If any bytes from the specified buffer remain unwritten, they are + * retained for the next write to be prepended to its buffer. + *

* - * @param buffer the current buffer. - * @throws PortAudioException error while writing to device. + * @param buffer the byte buffer to the written into the native + * PortAudio stream represented by this instance + * @param offset the offset in buffer at which the audio samples to + * be written begin + * @param length the length of the audio samples in buffer to be + * written + * @throws PortAudioException if anything goes wrong while writing */ - public synchronized void write(byte[] buffer) + public synchronized void write(byte[] buffer, int offset, int length) throws PortAudioException { if((stream == 0) || !started) return; - int numSamples = PortAudioManager.NUM_SAMPLES*frameSize; + /* + * If there are audio samples left unwritten from a previous write, + * prepend them to the specified buffer. If it's possible to write them + * now, do it. + */ + if ((bufferLeft != null) && (bufferLeftLength > 0)) + { + int numberOfBytesInBufferLeftToBytesPerBuffer + = bytesPerBuffer - bufferLeftLength; + int numberOfBytesToCopyToBufferLeft + = (numberOfBytesInBufferLeftToBytesPerBuffer < length) + ? numberOfBytesInBufferLeftToBytesPerBuffer + : length; - int currentIx = 0; + System + .arraycopy( + buffer, + offset, + bufferLeft, + bufferLeftLength, + numberOfBytesToCopyToBufferLeft); + offset += numberOfBytesToCopyToBufferLeft; + length -= numberOfBytesToCopyToBufferLeft; + bufferLeftLength += numberOfBytesToCopyToBufferLeft; - // if there are bytes from previous run - if(bufferLeft != null && bufferLeft.length > 0) - { - if(buffer.length + bufferLeft.length >= numSamples) + if (bufferLeftLength == bytesPerBuffer) { - byte[] tmp = new byte[numSamples]; - System.arraycopy(bufferLeft, 0, tmp, 0, bufferLeft.length); - System.arraycopy(buffer, currentIx, tmp, - bufferLeft.length, numSamples - bufferLeft.length); - currentIx += numSamples - bufferLeft.length; - bufferLeft = null; - - PortAudio.Pa_WriteStream( - stream,tmp, tmp.length/frameSize); - } - else - { - // not enough bytes even with previous left - // so let store everything - byte[] tmp = new byte[numSamples]; - System.arraycopy(bufferLeft, 0, tmp, 0, bufferLeft.length); - System.arraycopy(buffer, currentIx, tmp, - bufferLeft.length, numSamples - bufferLeft.length); - bufferLeft = null; - return; + PortAudio.Pa_WriteStream(stream, bufferLeft, framesPerBuffer); + bufferLeftLength = 0; } } - // now use all the current buffer - if(buffer.length > numSamples) + // Write the audio samples from the specified buffer. + int numberOfWrites = length / bytesPerBuffer; + + if (numberOfWrites > 0) { - while(currentIx <= buffer.length - numSamples) - { - byte[] tmp = new byte[numSamples]; - System.arraycopy(buffer, currentIx, tmp, 0, numSamples); + PortAudio + .Pa_WriteStream( + stream, + buffer, + offset, + framesPerBuffer, + numberOfWrites); - PortAudio.Pa_WriteStream( - stream,tmp, tmp.length/frameSize); - currentIx += numSamples; - } + int bytesWritten = numberOfWrites * bytesPerBuffer; - if(currentIx < buffer.length) - { - bufferLeft = new byte[buffer.length - currentIx]; - System.arraycopy(buffer, currentIx, bufferLeft, 0, bufferLeft.length); - } - } - else if(buffer.length < numSamples) - { - bufferLeft = buffer; + offset += bytesWritten; + length -= bytesWritten; } - else + + // If anything was left unwritten, remember it for next time. + if (length > 0) { - PortAudio.Pa_WriteStream( - stream,buffer, buffer.length/frameSize); + if (bufferLeft == null) + { + bufferLeft = new byte[bytesPerBuffer]; + } + System.arraycopy(buffer, offset, bufferLeft, 0, length); + bufferLeftLength = length; } }