From afb27d05c34100aa15d1ae8c8d6ecce7a00a5699 Mon Sep 17 00:00:00 2001 From: Lyubomir Marinov Date: Tue, 6 Nov 2012 12:25:30 +0000 Subject: [PATCH] - Prevents a NullPointerException in ice4j's Connector#stop() which appears quite often with unknown side effects. - Prevents a NullPointerException in ToolsMenu related to the Jitsi VideoBridge menu item which causes of long chain of other exceptions and, eventually, prevents the application from completing its startup. - Moves the C source code of JNI PortAudio library from Jitsi/net.java.sip.communicator to libjitsi/org.jitsi. Removes a couple of Java method and C function implementations each of which repeats three times. Fixes possible cases of leaving native callback threads which have been attached to the JVM in undetermined states. --- lib/installer-exclude/ice4j.jar | Bin 450513 -> 451241 bytes .../portaudio/AudioQualityImprovement.c | 911 --------- .../portaudio/AudioQualityImprovement.h | 165 -- src/native/portaudio/ConditionVariable.h | 95 - src/native/portaudio/Makefile | 50 - src/native/portaudio/Mutex.h | 82 - src/native/portaudio/README | 40 - ...icator_impl_neomedia_portaudio_PortAudio.c | 1649 ----------------- ...icator_impl_neomedia_portaudio_PortAudio.h | 323 ---- .../portaudio/pa_linux_alsa.c-by-Werner.patch | 71 - ...pa_linux_alsa.c-fix-blocked-renderer.patch | 39 - .../portaudio/portaudio-hotplug-os.patch | 1212 ------------ .../impl/gui/main/menus/ToolsMenu.java | 177 +- 13 files changed, 95 insertions(+), 4719 deletions(-) delete mode 100644 src/native/portaudio/AudioQualityImprovement.c delete mode 100644 src/native/portaudio/AudioQualityImprovement.h delete mode 100644 src/native/portaudio/ConditionVariable.h delete mode 100644 src/native/portaudio/Makefile delete mode 100644 src/native/portaudio/Mutex.h delete mode 100644 src/native/portaudio/README delete mode 100644 src/native/portaudio/net_java_sip_communicator_impl_neomedia_portaudio_PortAudio.c delete mode 100644 src/native/portaudio/net_java_sip_communicator_impl_neomedia_portaudio_PortAudio.h delete mode 100644 src/native/portaudio/pa_linux_alsa.c-by-Werner.patch delete mode 100644 src/native/portaudio/pa_linux_alsa.c-fix-blocked-renderer.patch delete mode 100644 src/native/portaudio/portaudio-hotplug-os.patch diff --git a/lib/installer-exclude/ice4j.jar b/lib/installer-exclude/ice4j.jar index 3a24b28767450bd44c950bd725db376bc100fb95..213ae54e782b2d32a34649257bb4617b06342b51 100644 GIT binary patch delta 71114 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 Pq`-uXF)+e>k(d7m;xW2X delta 70507 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 -#include -#include -#include -#include -#include - -#define MIN_SOUND_PRESSURE_LEVEL 40 -#define MAX_SOUND_PRESSURE_LEVEL 85 - -/** - * - * @param aqi - * @param samples - * @param count the number of samples specified in samples - * @return the sound pressure level in dB of the specified samples - */ -static float AudioQualityImprovement_calculateSoundPressureLevel - (AudioQualityImprovement *aqi, spx_int16_t *samples, spx_uint32_t count); - -/** - * - * @param aqi - * @param buffer - * @param length the length of buffer in bytes - * @return the sound pressure level in dB of the playback which has been matched - * to the specified capture buffer for the purposes of echo - * cancellation if echo suppression is enabled; otherwise, 0 - */ -static float AudioQualityImprovement_cancelEchoFromPlay - (AudioQualityImprovement *aqi, - void *buffer, unsigned long length); -static void AudioQualityImprovement_free(AudioQualityImprovement *aqi); -static AudioQualityImprovement *AudioQualityImprovement_new - (const char *stringID, jlong longID, AudioQualityImprovement *next); -static void AudioQualityImprovement_popFromPlay - (AudioQualityImprovement *aqi, spx_uint32_t sampleCount); -static void AudioQualityImprovement_resampleInPlay - (AudioQualityImprovement *aqi, - double sampleRate, unsigned long sampleSizeInBits, int channels, - void *buffer, unsigned long length); -static void AudioQualityImprovement_retain(AudioQualityImprovement *aqi); -static void AudioQualityImprovement_setFrameSize - (AudioQualityImprovement *aqi, jint frameSize); -static void AudioQualityImprovement_setInputLatency - (AudioQualityImprovement *aqi, jlong inputLatency); -static void AudioQualityImprovement_setOutputLatency - (AudioQualityImprovement *aqi, jlong outputLatency); -static void AudioQualityImprovement_suppressEcho - (AudioQualityImprovement *aqi, - spx_int16_t *buffer, spx_uint32_t length, - float spl); - -/** - * Updates the indicator of the specified AudioQualityImprovement which - * determines whether AudioQualityImprovement#play delays the access to - * it from AudioQualityImprovement#echo. - * - * @param aqi the AudioQualityImprovement of which to update the - * indicator which determines whether AudioQualityImprovement#play - * delays the access to it from AudioQualityImprovement#echo - */ -static void AudioQualityImprovement_updatePlayDelay - (AudioQualityImprovement *aqi); -static void AudioQualityImprovement_updatePlayIsDelaying - (AudioQualityImprovement *aqi); -static void AudioQualityImprovement_updatePreprocess - (AudioQualityImprovement *aqi); - -/** - * Returns the current time in milliseconds (akin to - * java.lang.System#currentTimeMillis()). - * - * @return the current time in milliseconds - */ -static jlong System_currentTimeMillis(); - -static Mutex *AudioQualityImprovement_sharedInstancesMutex = NULL; -static AudioQualityImprovement *AudioQualityImprovement_sharedInstances = NULL; - -/** - * - * @param aqi - * @param samples - * @param count the number of samples specified in samples - * @return the sound pressure level in dB of the specified samples - */ -static float -AudioQualityImprovement_calculateSoundPressureLevel - (AudioQualityImprovement *aqi, spx_int16_t *samples, spx_uint32_t count) -{ - spx_uint32_t i; - float rms; - float spl; - - if (!count) - return 0; - - rms = 0; - for (i = 0; i < count; i++) - { - float sample = samples[i] / (float) SHRT_MAX; - - rms += sample * sample; - } - rms = sqrtf(rms / count); - - spl = (rms > 0) ? 20 * log10f(rms / 0.00002) : -MAX_SOUND_PRESSURE_LEVEL; - return spl; -} - -/** - * - * @param aqi - * @param buffer - * @param length the length of buffer in bytes - * @return the sound pressure level in dB of the playback which has been matched - * to the specified capture buffer for the purposes of echo - * cancellation if echo suppression is enabled; otherwise, 0 - */ -static float -AudioQualityImprovement_cancelEchoFromPlay - (AudioQualityImprovement *aqi, - void *buffer, unsigned long length) -{ - spx_uint32_t sampleCount; - float spl; - - if (aqi->playIsDelaying == JNI_TRUE) - return 0; - - sampleCount = length / sizeof(spx_int16_t); - if (aqi->playLength < sampleCount) - return 0; - - /* - * Ensure that out exists and is large enough to receive the result of the - * echo cancellation. - */ - if (!(aqi->out) || (aqi->outCapacity < length)) - { - spx_int16_t *newOut = realloc(aqi->out, length); - - if (newOut) - { - aqi->out = newOut; - aqi->outCapacity = length; - } - else - return 0; - } - - /* Perform the echo cancellation and return the result in buffer. */ - speex_echo_cancellation(aqi->echo, buffer, aqi->play, aqi->out); - memcpy(buffer, aqi->out, length); - - /* - * Calculate the sound pressure level in dB to be returned (if echo - * suppression is enabled and, thus, needs it). - */ - spl - = (JNI_TRUE == aqi->suppressEcho) - ? AudioQualityImprovement_calculateSoundPressureLevel( - aqi, - aqi->play, sampleCount) - : 0; - - AudioQualityImprovement_popFromPlay(aqi, sampleCount); - - return spl; -} - -static void -AudioQualityImprovement_free(AudioQualityImprovement *aqi) -{ - /* mutex */ - Mutex_free(aqi->mutex); - /* preprocess */ - if (aqi->preprocess) - speex_preprocess_state_destroy(aqi->preprocess); - /* echo */ - if (aqi->echo) - speex_echo_state_destroy(aqi->echo); - /* out */ - if (aqi->out) - free(aqi->out); - /* play */ - if (aqi->play) - free(aqi->play); - /* resampler */ - if (aqi->resampler) - speex_resampler_destroy(aqi->resampler); - /* stringID */ - free(aqi->stringID); - - free(aqi); -} - -AudioQualityImprovement * -AudioQualityImprovement_getSharedInstance(const char *stringID, jlong longID) -{ - AudioQualityImprovement *theSharedInstance = NULL; - - if (!Mutex_lock(AudioQualityImprovement_sharedInstancesMutex)) - { - AudioQualityImprovement *aSharedInstance - = AudioQualityImprovement_sharedInstances; - - while (aSharedInstance) - { - if ((aSharedInstance->longID == longID) - && ((aSharedInstance->stringID == stringID) - || (0 == strcmp(aSharedInstance->stringID, stringID)))) - { - theSharedInstance = aSharedInstance; - break; - } - aSharedInstance = aSharedInstance->next; - } - if (theSharedInstance) - AudioQualityImprovement_retain(theSharedInstance); - else - { - theSharedInstance - = AudioQualityImprovement_new( - stringID, - longID, - AudioQualityImprovement_sharedInstances); - if (theSharedInstance) - AudioQualityImprovement_sharedInstances = theSharedInstance; - } - Mutex_unlock(AudioQualityImprovement_sharedInstancesMutex); - } - return theSharedInstance; -} - -/** Loads the AudioQualityImprovement class. */ -void -AudioQualityImprovement_load() -{ - AudioQualityImprovement_sharedInstancesMutex = Mutex_new(NULL); -} - -static AudioQualityImprovement * -AudioQualityImprovement_new - (const char *stringID, jlong longID, AudioQualityImprovement *next) -{ - AudioQualityImprovement *aqi = calloc(1, sizeof(AudioQualityImprovement)); - - if (aqi) - { - /* stringID */ - size_t slen = strlen(stringID); - aqi->stringID = malloc(slen + 1); - - if (!(aqi->stringID)) - { - AudioQualityImprovement_free(aqi); - return NULL; - } - strncpy(aqi->stringID, stringID, slen); - aqi->stringID[slen] = 0x00; - - /* mutex */ - aqi->mutex = Mutex_new(NULL); - if (!(aqi->mutex)) - { - AudioQualityImprovement_free(aqi); - return NULL; - } - - aqi->inputLatency = -1; - aqi->longID = longID; - aqi->next = next; - aqi->outputLatency = -1; - aqi->retainCount = 1; - aqi->suppressEcho = JNI_TRUE; - } - return aqi; -} - -static void -AudioQualityImprovement_popFromPlay - (AudioQualityImprovement *aqi, spx_uint32_t sampleCount) -{ - spx_uint32_t i; - spx_uint32_t sampleCountToMove = aqi->playLength - sampleCount; - spx_int16_t *playNew = aqi->play; - spx_int16_t *playOld = aqi->play + sampleCount; - - for (i = 0; i < sampleCountToMove; i++) - *playNew++ = *playOld++; - aqi->playLength -= sampleCount; -} - -/** - * - * @param aqi - * @param sampleOrigin - * @param sampleRate - * @param sampleSizeInBits - * @param channels - * @param latency the latency of the stream associated with buffer in - * milliseconds - * @param buffer - * @param length the length of buffer in bytes - */ -void -AudioQualityImprovement_process - (AudioQualityImprovement *aqi, - AudioQualityImprovementSampleOrigin sampleOrigin, - double sampleRate, unsigned long sampleSizeInBits, int channels, - jlong latency, - void *buffer, unsigned long length) -{ - if ((sampleSizeInBits == 16) && (channels == 1) && !Mutex_lock(aqi->mutex)) - { - switch (sampleOrigin) - { - case AUDIO_QUALITY_IMPROVEMENT_SAMPLE_ORIGIN_INPUT: - if (sampleRate == aqi->sampleRate) - { - AudioQualityImprovement_setFrameSize(aqi, length); - if (aqi->preprocess) - { - float spl; - jboolean suppressEcho; - - AudioQualityImprovement_setInputLatency(aqi, latency); - - if (aqi->echo && aqi->play && aqi->playLength) - { - spl - = AudioQualityImprovement_cancelEchoFromPlay( - aqi, - buffer, length); - suppressEcho = aqi->suppressEcho; - } - else - { - spl = 0; - /* - * Let the echo suppression fade out if it's enabled and - * there hasn't been recent playback. - */ - suppressEcho - = (aqi->suppressEcho && !(aqi->playLength)) - ? JNI_TRUE - : JNI_FALSE; - } - - speex_preprocess_run(aqi->preprocess, buffer); - - if (JNI_TRUE == suppressEcho) - AudioQualityImprovement_suppressEcho( - aqi, - buffer, length / sizeof(spx_int16_t), - spl); - } - } - break; - - case AUDIO_QUALITY_IMPROVEMENT_SAMPLE_ORIGIN_OUTPUT: - if (aqi->preprocess && aqi->echo) - { - AudioQualityImprovement_setOutputLatency(aqi, latency); - AudioQualityImprovement_resampleInPlay( - aqi, - sampleRate, sampleSizeInBits, channels, - buffer, length); - } - break; - } - Mutex_unlock(aqi->mutex); - } -} - -void -AudioQualityImprovement_release(AudioQualityImprovement *aqi) -{ - if (!Mutex_lock(AudioQualityImprovement_sharedInstancesMutex)) - { - if (!Mutex_lock(aqi->mutex)) - { - --(aqi->retainCount); - if (aqi->retainCount < 1) - { - if (aqi == AudioQualityImprovement_sharedInstances) - { - AudioQualityImprovement_sharedInstances - = AudioQualityImprovement_sharedInstances->next; - } - else - { - AudioQualityImprovement *prevSharedInstance - = AudioQualityImprovement_sharedInstances; - - while (prevSharedInstance) - { - AudioQualityImprovement *nextSharedInstance - = prevSharedInstance->next; - - if (aqi == nextSharedInstance) - { - prevSharedInstance->next = aqi->next; - break; - } - prevSharedInstance = nextSharedInstance; - } - } - - Mutex_unlock(aqi->mutex); - AudioQualityImprovement_free(aqi); - } - else - Mutex_unlock(aqi->mutex); - } - Mutex_unlock(AudioQualityImprovement_sharedInstancesMutex); - } -} - -/** - * - * @param aqi - * @param sampleRate - * @param sampleSizeInBits - * @param channels - * @param buffer - * @param length the length of buffer in bytes - */ -static void -AudioQualityImprovement_resampleInPlay - (AudioQualityImprovement *aqi, - double sampleRate, unsigned long sampleSizeInBits, int channels, - void *buffer, unsigned long length) -{ - spx_uint32_t playSize; - spx_uint32_t playCapacity; - spx_uint32_t playLength;; - spx_int16_t *play; - - if (sampleRate == aqi->sampleRate) - playSize = length; - else if (length * aqi->sampleRate == aqi->frameSize * sampleRate) - { - if (aqi->resampler) - { - speex_resampler_set_rate( - aqi->resampler, - (spx_uint32_t) sampleRate, (spx_uint32_t) (aqi->sampleRate)); - playSize = aqi->frameSize; - } - else - { - aqi->resampler - = speex_resampler_init( - channels, - (spx_uint32_t) sampleRate, (spx_uint32_t) (aqi->sampleRate), - SPEEX_RESAMPLER_QUALITY_VOIP, - NULL); - if (aqi->resampler) - playSize = aqi->frameSize; - else - { - aqi->playIsDelaying = JNI_TRUE; - aqi->playLength = 0; - return; - } - } - } - else - { - /* - * The specified buffer neither is in the format of the audio capture - * nor can be resampled to it. - */ - aqi->playIsDelaying = JNI_TRUE; - aqi->playLength = 0; - return; - } - - /* Ensure that play exists and is large enough. */ - playCapacity - = ((1 + aqi->playDelay) + 1) * (aqi->frameSize / sizeof(spx_int16_t)); - playLength = playSize / sizeof(spx_int16_t); - if (playCapacity < playLength) - playCapacity = playLength; - if (!(aqi->play) || (aqi->playCapacity < playCapacity)) - { - spx_int16_t *newPlay; - - newPlay = realloc(aqi->play, playCapacity * sizeof(spx_int16_t)); - if (newPlay) - { - if (!(aqi->play)) - { - aqi->playIsDelaying = JNI_TRUE; - aqi->playLength = 0; - } - - aqi->play = newPlay; - aqi->playCapacity = playCapacity; - } - else - { - aqi->playIsDelaying = JNI_TRUE; - aqi->playLength = 0; - return; - } - } - - /* Ensure that there is room for buffer in play. */ - if (aqi->playLength + playLength > aqi->playCapacity) - { - aqi->playIsDelaying = JNI_TRUE; - aqi->playLength = 0; - /* - * We don't have enough room in play for buffer which means that we'll - * have to throw some samples away. But it'll effectively mean that - * we'll enlarge the drift which will disrupt the echo cancellation. So - * it seems the least of two evils to just reset the echo cancellation. - */ - speex_echo_state_reset(aqi->echo); - } - - /* Place buffer in play. */ - play = aqi->play + aqi->playLength; - if (length == aqi->frameSize) - memcpy(play, buffer, playSize); - else - { - unsigned long sampleSizeInBytes = sampleSizeInBits / 8; - spx_uint32_t bufferSampleCount = length / sampleSizeInBytes; - - speex_resampler_process_interleaved_int( - aqi->resampler, - buffer, &bufferSampleCount, play, &playLength); - } - aqi->playLength += playLength; - - /* Take into account the latency. */ - if (aqi->playIsDelaying == JNI_TRUE) - AudioQualityImprovement_updatePlayIsDelaying(aqi); -} - -static void -AudioQualityImprovement_retain(AudioQualityImprovement *aqi) -{ - if (!Mutex_lock(aqi->mutex)) - { - ++(aqi->retainCount); - Mutex_unlock(aqi->mutex); - } -} - -/** - * Sets the indicator which determines whether noise suppression is to be - * performed by the specified AudioQualityImprovement (for captured - * audio). - * - * @param aqi the AudioQualityImprovement on which to set the indicator - * which determines whether it is to perform noise suppression (for captured audio) - * @param denoise JNI_TRUE if the specified aqi is to perform - * noise suppression (for captured audio); otherwise, JNI_FALSE - */ -void -AudioQualityImprovement_setDenoise - (AudioQualityImprovement *aqi, jboolean denoise) -{ - if (!Mutex_lock(aqi->mutex)) - { - if (aqi->denoise != denoise) - { - aqi->denoise = denoise; - AudioQualityImprovement_updatePreprocess(aqi); - } - Mutex_unlock(aqi->mutex); - } -} - -/** - * Sets the filter length in milliseconds of the echo cancellation - * implementation of the specified AudioQualityImprovement. The - * recommended filter length is approximately the third of the room - * reverberation time. For example, in a small room, reverberation time is in - * the order of 300 ms, so a filter length of 100 ms is a good choice (800 - * samples at 8000 Hz sampling rate). - * - * @param aqi the AudioQualityImprovement to set the filter length of - * @param echoFilterLengthInMillis the filter length in milliseconds of the echo - * cancellation of aqi - */ -void -AudioQualityImprovement_setEchoFilterLengthInMillis - (AudioQualityImprovement *aqi, jlong echoFilterLengthInMillis) -{ - if (echoFilterLengthInMillis < 0) - echoFilterLengthInMillis = 0; - if (!Mutex_lock(aqi->mutex)) - { - if (aqi->echoFilterLengthInMillis != echoFilterLengthInMillis) - { - aqi->echoFilterLengthInMillis = echoFilterLengthInMillis; - AudioQualityImprovement_updatePreprocess(aqi); - } - Mutex_unlock(aqi->mutex); - } -} - -static void -AudioQualityImprovement_setFrameSize - (AudioQualityImprovement *aqi, jint frameSize) -{ - if (aqi->frameSize != frameSize) - { - aqi->frameSize = frameSize; - AudioQualityImprovement_updatePreprocess(aqi); - } -} - -static void -AudioQualityImprovement_setInputLatency - (AudioQualityImprovement *aqi, jlong inputLatency) -{ - if (aqi->inputLatency != inputLatency) - { - aqi->inputLatency = inputLatency; - AudioQualityImprovement_updatePlayDelay(aqi); - } -} - -static void -AudioQualityImprovement_setOutputLatency - (AudioQualityImprovement *aqi, jlong outputLatency) -{ - if (aqi->outputLatency != outputLatency) - { - aqi->outputLatency = outputLatency; - AudioQualityImprovement_updatePlayDelay(aqi); - } -} - -void -AudioQualityImprovement_setSampleRate - (AudioQualityImprovement *aqi, int sampleRate) -{ - if (!Mutex_lock(aqi->mutex)) - { - if (aqi->sampleRate != sampleRate) - { - aqi->sampleRate = sampleRate; - AudioQualityImprovement_updatePlayDelay(aqi); - AudioQualityImprovement_updatePreprocess(aqi); - } - Mutex_unlock(aqi->mutex); - } -} -static void -AudioQualityImprovement_suppressEcho - (AudioQualityImprovement *aqi, - spx_int16_t *buffer, spx_uint32_t length, - float spl) -{ - float amplifier; - spx_int16_t i; - - if (spl < MIN_SOUND_PRESSURE_LEVEL) - spl = MIN_SOUND_PRESSURE_LEVEL; - else if (spl > MAX_SOUND_PRESSURE_LEVEL) - spl = MAX_SOUND_PRESSURE_LEVEL; - - /* Decay #suppressEchoSPL. */ - if (aqi->suppressEchoSPLTime - && (aqi->suppressEchoSPL > MIN_SOUND_PRESSURE_LEVEL)) - { - aqi->suppressEchoSPL - -= (System_currentTimeMillis() - aqi->suppressEchoSPLTime) - * aqi->suppressEchoSPLDecay; - if (aqi->suppressEchoSPL <= MIN_SOUND_PRESSURE_LEVEL) - { - aqi->suppressEchoSPLDecay = 0; - aqi->suppressEchoSPLTime = 0; - } - } - - if (spl < aqi->suppressEchoSPL) - spl = aqi->suppressEchoSPL; - else - { - aqi->suppressEchoSPL = spl; - aqi->suppressEchoSPLDecay - = ((MIN_SOUND_PRESSURE_LEVEL == spl) - ? 1 - : (spl - MIN_SOUND_PRESSURE_LEVEL)) - / 1000.0; - aqi->suppressEchoSPLTime = System_currentTimeMillis(); - } - - amplifier - = 1 - - (spl - MIN_SOUND_PRESSURE_LEVEL) - / (float) (MAX_SOUND_PRESSURE_LEVEL - MIN_SOUND_PRESSURE_LEVEL); - - for (i = 0; i < length; i++) - buffer[i] = (spx_int16_t) (amplifier * buffer[i]); -} - -/** Unloads the AudioQualityImprovement class. */ -void -AudioQualityImprovement_unload() -{ - if (AudioQualityImprovement_sharedInstancesMutex) - { - Mutex_free(AudioQualityImprovement_sharedInstancesMutex); - AudioQualityImprovement_sharedInstancesMutex = NULL; - } -} - -static void -AudioQualityImprovement_updatePlayDelay(AudioQualityImprovement *aqi) -{ - spx_uint32_t playDelay; - - if ((aqi->inputLatency < 0) - || (aqi->outputLatency < 0) - || !(aqi->frameSize) - || !(aqi->sampleRate)) - { - playDelay = MIN_PLAY_DELAY_IN_FRAMES; - } - else - { - playDelay - = (aqi->outputLatency * aqi->sampleRate) - / ((aqi->frameSize / sizeof(spx_int16_t)) * 1000); - if (playDelay < MIN_PLAY_DELAY_IN_FRAMES) - playDelay = MIN_PLAY_DELAY_IN_FRAMES; - } - - if (aqi->playDelay != playDelay) - { - aqi->playDelay = playDelay; - if (aqi->play && (aqi->playIsDelaying == JNI_TRUE)) - AudioQualityImprovement_updatePlayIsDelaying(aqi); - } -} - -/** - * Updates the indicator of the specified AudioQualityImprovement which - * determines whether AudioQualityImprovement#play delays the access to - * it from AudioQualityImprovement#echo. - * - * @param aqi the AudioQualityImprovement of which to update the - * indicator which determines whether AudioQualityImprovement#play - * delays the access to it from AudioQualityImprovement#echo - */ -static void -AudioQualityImprovement_updatePlayIsDelaying(AudioQualityImprovement *aqi) -{ - spx_uint32_t playDelay - = aqi->playDelay * (aqi->frameSize / sizeof(spx_int16_t)); - - aqi->playIsDelaying - = ((aqi->playLength < playDelay) && (playDelay <= aqi->playCapacity)) - ? JNI_TRUE - : JNI_FALSE; -} - -static void -AudioQualityImprovement_updatePreprocess(AudioQualityImprovement *aqi) -{ - if (aqi->echo) - { - int frameSize = 0; - - if ((aqi->echoFilterLengthInMillis > 0) - && (aqi->sampleRate > 0) - && speex_echo_ctl( - aqi->echo, - SPEEX_ECHO_GET_FRAME_SIZE, &frameSize)) - frameSize = 0; - if (frameSize - && (aqi->frameSize - == (frameSize * (16 /* sampleSizeInBits */ / 8)))) - { - int echoFilterLength - = (int) - ((aqi->sampleRate * aqi->echoFilterLengthInMillis) - / 1000); - - if (aqi->filterLengthOfEcho != echoFilterLength) - frameSize = 0; - } - else - frameSize = 0; - if (frameSize < 1) - { - if (aqi->preprocess) - { - speex_preprocess_ctl( - aqi->preprocess, - SPEEX_PREPROCESS_SET_ECHO_STATE, NULL); - } - speex_echo_state_destroy(aqi->echo); - aqi->echo = NULL; - } - } - if (aqi->preprocess - && ((aqi->frameSize != aqi->frameSizeOfPreprocess) - || (aqi->sampleRate != aqi->sampleRateOfPreprocess))) - { - speex_preprocess_state_destroy(aqi->preprocess); - aqi->preprocess = NULL; - } - if ((aqi->frameSize > 0) && (aqi->sampleRate > 0)) - { - if (aqi->echoFilterLengthInMillis > 0) - { - if (!(aqi->echo)) - { - int echoFrameSize - = aqi->frameSize / (16 /* sampleSizeInBits */ / 8); - int echoFilterLength - = (int) - ((aqi->sampleRate * aqi->echoFilterLengthInMillis) - / 1000); - - aqi->echo - = speex_echo_state_init(echoFrameSize, echoFilterLength); - aqi->filterLengthOfEcho = echoFilterLength; - /* - * Since echo has just been (re)created, make sure that the - * delay in play will happen again taking into consideration the - * latest frameSize. - */ - if (aqi->play) - AudioQualityImprovement_updatePlayIsDelaying(aqi); - } - if (aqi->echo) - { - speex_echo_ctl( - aqi->echo, - SPEEX_ECHO_SET_SAMPLING_RATE, &(aqi->sampleRate)); - } - } - if (aqi->denoise || aqi->echo) - { - if (!(aqi->preprocess)) - { - aqi->preprocess - = speex_preprocess_state_init( - aqi->frameSize / (16 /* sampleSizeInBits */ / 8), - aqi->sampleRate); - aqi->frameSizeOfPreprocess = aqi->frameSize; - aqi->sampleRateOfPreprocess = aqi->sampleRate; - if (aqi->preprocess) - { - int on = 1; - - speex_preprocess_ctl( - aqi->preprocess, - SPEEX_PREPROCESS_SET_DEREVERB, &on); - speex_preprocess_ctl( - aqi->preprocess, - SPEEX_PREPROCESS_SET_VAD, &on); - } - } - if (aqi->preprocess) - { - int denoise = (aqi->denoise == JNI_TRUE) ? 1 : 0; - - speex_preprocess_ctl( - aqi->preprocess, - SPEEX_PREPROCESS_SET_DENOISE, &denoise); - if (aqi->echo) - { - speex_preprocess_ctl( - aqi->preprocess, - SPEEX_PREPROCESS_SET_ECHO_STATE, aqi->echo); - } - } - } - } -} - -/** - * Returns the current time in milliseconds (akin to - * java.lang.System#currentTimeMillis()). - * - * @return the current time in milliseconds - */ -static jlong -System_currentTimeMillis() -{ - struct timeval tv; - - return - (gettimeofday(&tv, NULL) == 0) - ? ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)) - : -1; -} diff --git a/src/native/portaudio/AudioQualityImprovement.h b/src/native/portaudio/AudioQualityImprovement.h deleted file mode 100644 index 6aa2f83b3..000000000 --- a/src/native/portaudio/AudioQualityImprovement.h +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Distributable under LGPL license. - * See terms of license at gnu.org. - */ - -#ifndef _NET_JAVA_SIP_COMMUNICATOR_IMPL_NEOMEDIA_PORTAUDIO_AUDIOQUALITYIMPROVEMENT_H_ -#define _NET_JAVA_SIP_COMMUNICATOR_IMPL_NEOMEDIA_PORTAUDIO_AUDIOQUALITYIMPROVEMENT_H_ - -#include - -/** - * The minimum number of frames by which the playback is to be delayed before it - * is accessible to the echo cancellation/capture. - */ -#define MIN_PLAY_DELAY_IN_FRAMES 2 - -#ifndef AUDIO_QUALITY_IMPROVEMENT_IMPLEMENTATION -typedef void *AudioQualityImprovement; -#else /* #ifndef AUDIO_QUALITY_IMPROVEMENT_IMPLEMENTATION */ - -#include "Mutex.h" -#include -#include -#include - -typedef struct _AudioQualityImprovement -{ - jboolean denoise; - SpeexEchoState *echo; - jlong echoFilterLengthInMillis; - - /** The length of the echo cancelling filter of #echo in samples. */ - int filterLengthOfEcho; - jint frameSize; - int frameSizeOfPreprocess; - - /** The capture latency in milliseconds. */ - jlong inputLatency; - jlong longID; - Mutex *mutex; - struct _AudioQualityImprovement *next; - - /** - * The intermediate buffer into which the result of echo cancellation is - * written for a specific buffer of captured audio. - */ - spx_int16_t *out; - - /** The capacity of #out in bytes. */ - spx_uint32_t outCapacity; - - /** The playback latency in milliseconds. */ - jlong outputLatency; - spx_int16_t *play; - - /** - * The number of samples allocated to #play regardless of whether they are - * valid or not. - */ - spx_uint32_t playCapacity; - - /** The number of frames to delay playback with. */ - spx_uint32_t playDelay; - - /** - * The indicator which determines whether #play is currently delaying the - * access to it from #echo. - */ - jboolean playIsDelaying; - - /** The number of valid samples written into #play. */ - spx_uint32_t playLength; - SpeexPreprocessState *preprocess; - SpeexResamplerState *resampler; - int retainCount; - int sampleRate; - int sampleRateOfPreprocess; - char *stringID; - - /** The indicator which determines whether echo suppression is enabled. */ - jboolean suppressEcho; - - /** - * The sound pressure level in dB depending on which the echo suppression, - * if enabled, decreases the sound volume of the captured oudio. - */ - float suppressEchoSPL; - - /** The decay in dB per millisecond of #suppressEchoSPL. */ - float suppressEchoSPLDecay; - - /** - * The time in milliseconds at which #suppressEchoSPL has last been assigned - * a value and thus determines how much #suppressEchoSPLDecay is to be - * applied to #suppressEchoSPL at a certain point in time. - */ - jlong suppressEchoSPLTime; -} AudioQualityImprovement; - -#endif /* #ifndef AUDIO_QUALITY_IMPROVEMENT_IMPLEMENTATION */ - -typedef enum -{ - /** - * The constant which indicates that the associated samples have originated - * from an input stream i.e. capture. - */ - AUDIO_QUALITY_IMPROVEMENT_SAMPLE_ORIGIN_INPUT, - - /** - * The constant which indicates that the associated samples have originated - * from an output stream i.e. playback. - */ - AUDIO_QUALITY_IMPROVEMENT_SAMPLE_ORIGIN_OUTPUT -} AudioQualityImprovementSampleOrigin; - -AudioQualityImprovement *AudioQualityImprovement_getSharedInstance - (const char *stringID, jlong longID); - -/** Loads the AudioQualityImprovement class. */ -void AudioQualityImprovement_load(); -void AudioQualityImprovement_process - (AudioQualityImprovement *aqi, - AudioQualityImprovementSampleOrigin sampleOrigin, - double sampleRate, unsigned long sampleSizeInBits, int channels, - jlong latency, - void *buffer, unsigned long length); -void AudioQualityImprovement_release(AudioQualityImprovement *aqi); - -/** - * Sets the indicator which determines whether noise suppression is to be - * performed by the specified AudioQualityImprovement (for captured - * audio). - * - * @param aqi the AudioQualityImprovement on which to set the indicator - * which determines whether it is to perform noise suppression (for captured audio) - * @param denoise JNI_TRUE if the specified aqi is to perform - * noise suppression (for captured audio); otherwise, JNI_FALSE - */ -void AudioQualityImprovement_setDenoise - (AudioQualityImprovement *aqi, jboolean denoise); - -/** - * Sets the filter length in milliseconds of the echo cancellation - * implementation of the specified AudioQualityImprovement. The - * recommended filter length is approximately the third of the room - * reverberation time. For example, in a small room, reverberation time is in - * the order of 300 ms, so a filter length of 100 ms is a good choice (800 - * samples at 8000 Hz sampling rate). - * - * @param aqi the AudioQualityImprovement to set the filter length of - * @param echoFilterLengthInMillis the filter length in milliseconds of the echo - * cancellation of aqi - */ -void AudioQualityImprovement_setEchoFilterLengthInMillis - (AudioQualityImprovement *aqi, jlong echoFilterLengthInMillis); -void AudioQualityImprovement_setSampleRate - (AudioQualityImprovement *aqi, int sampleRate); - -/** Unloads the AudioQualityImprovement class. */ -void AudioQualityImprovement_unload(); - -#endif /* #ifndef _NET_JAVA_SIP_COMMUNICATOR_IMPL_NEOMEDIA_PORTAUDIO_AUDIOQUALITYIMPROVEMENT_H_ */ diff --git a/src/native/portaudio/ConditionVariable.h b/src/native/portaudio/ConditionVariable.h deleted file mode 100644 index 30a65502f..000000000 --- a/src/native/portaudio/ConditionVariable.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Distributable under LGPL license. - * See terms of license at gnu.org. - */ - -#ifndef _NET_JAVA_SIP_COMMUNICATOR_IMPL_NEOMEDIA_CONDITIONVARIABLE_H_ -#define _NET_JAVA_SIP_COMMUNICATOR_IMPL_NEOMEDIA_CONDITIONVARIABLE_H_ - -#include "Mutex.h" - -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include - -typedef HANDLE ConditionVariable; - -static inline void ConditionVariable_free(ConditionVariable *condVar) -{ - if (CloseHandle(*condVar)) - free(condVar); -} - -static inline ConditionVariable *ConditionVariable_new(void *attr) -{ - ConditionVariable *condVar = malloc(sizeof(ConditionVariable)); - - if (condVar) - { - HANDLE event = CreateEvent(NULL, FALSE, FALSE, NULL); - - if (event) - *condVar = event; - else - { - free(condVar); - condVar = NULL; - } - } - return condVar; -} - -static inline int ConditionVariable_notify(ConditionVariable *condVar) -{ - return SetEvent(*condVar) ? 0 : GetLastError(); -} - -static inline int ConditionVariable_wait - (ConditionVariable *condVar, Mutex *mutex) -{ - DWORD waitForSingleObject; - - LeaveCriticalSection(mutex); - waitForSingleObject = WaitForSingleObject(*condVar, INFINITE); - EnterCriticalSection(mutex); - return waitForSingleObject; -} - -#else /* #ifdef _WIN32 */ -#include - -typedef pthread_cond_t ConditionVariable; - -static inline void ConditionVariable_free(ConditionVariable *condVar) -{ - if (!pthread_cond_destroy(condVar)) - free(condVar); -} - -static inline ConditionVariable *ConditionVariable_new(void *attr) -{ - ConditionVariable *condVar = malloc(sizeof(ConditionVariable)); - - if (condVar && pthread_cond_init(condVar, attr)) - { - free(condVar); - condVar = NULL; - } - return condVar; -} - -static inline int ConditionVariable_notify(ConditionVariable *condVar) -{ - return pthread_cond_signal(condVar); -} - -static inline int ConditionVariable_wait - (ConditionVariable *condVar, Mutex *mutex) -{ - return pthread_cond_wait(condVar, mutex); -} -#endif /* #ifdef _WIN32 */ - -#endif /* _NET_JAVA_SIP_COMMUNICATOR_IMPL_NEOMEDIA_CONDITIONVARIABLE_H_ */ diff --git a/src/native/portaudio/Makefile b/src/native/portaudio/Makefile deleted file mode 100644 index f83a7ebed..000000000 --- a/src/native/portaudio/Makefile +++ /dev/null @@ -1,50 +0,0 @@ -PORTAUDIO_HOME?=../../../../portaudio-20101214 -SPEEX_HOME?=../../../../speex-1.2rc1 - -CC=gcc -TARGET_BASENAME=jnportaudio - -OS=$(shell $(CC) -dumpmachine | sed -e s/.*-apple-.*/mac/ -e s/.*-linux-.*/linux/ -e s/.*-.*-mingw32/windows/) -ifeq "$(OS)" "mac" - JAVA_HOME?=/Developer/SDKs/MacOSX10.4u.sdk/System/Library/Frameworks/JavaVM.framework/Versions/1.5 - CC:=$(CC) -arch i386 -arch ppc -arch x86_64 -mmacosx-version-min=10.4 - CPPFLAGS=-I$(JAVA_HOME)/Headers - LDFLAGS=-dynamiclib - LIBS=-framework AudioToolbox -framework AudioUnit -framework CoreAudio -framework Carbon \ - -lpthread -pthread -lm -dynamic -lportaudio -lspeexdsp - TARGET=../../../lib/native/mac/lib$(TARGET_BASENAME).jnilib -else - ARCH=$(shell $(CC) -dumpmachine | sed -e s/x86_64-.*/-64/ -e s/i.86-.*//) - LDFLAGS=-shared - ifeq "$(OS)" "linux" - JAVA_HOME?=/usr/lib/jvm/java-6-sun - CPPFLAGS=-I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux - LIBS=-Wl,-Bstatic -lportaudio -lspeexdsp -Wl,-Bdynamic -lrt -lasound -lm -lpthread - TARGET=../../../lib/native/$(OS)$(ARCH)/lib$(TARGET_BASENAME).so - else ifeq "$(OS)" "windows" - DXSDK_HOME?=/c/Users/lyubomir/Downloads/DXSDK_Jun10 - ifeq "$(ARCH)" "-64" - JAVA_HOME?=C:/PROGRA~1/jdk - LIBS=-L$(DXSDK_HOME)/Lib/x64 - else - JAVA_HOME?=C:/PROGRA~2/jdk - LIBS=-L$(DXSDK_HOME)/Lib/x86 - endif - CPPFLAGS=-I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/win32 -I$(DXSDK_HOME)/Include - LDFLAGS:=-Wl,--kill-at $(LDFLAGS) - LIBS:=$(LIBS) -static -lportaudio -lspeexdsp -lwinmm -ldsound -lm -lstdc++ -lole32 -luuid - TARGET=../../../lib/native/$(OS)$(ARCH)/$(TARGET_BASENAME).dll - endif -endif - -CPPFLAGS:=-D_JNI_IMPLEMENTATION_ \ - -I$(SPEEX_HOME)/include -I$(PORTAUDIO_HOME)/include \ - -O2 \ - -Wall \ - $(CPPFLAGS) -LDFLAGS:=-fPIC $(LDFLAGS) -LIBS:=-L$(PORTAUDIO_HOME)/lib/.libs -L$(SPEEX_HOME)/libspeex/.libs $(LIBS) - -$(TARGET): net_java_sip_communicator_impl_neomedia_portaudio_PortAudio.c AudioQualityImprovement.c - $(CC) $(CPPFLAGS) $^ $(LDFLAGS) -o $@ $(LIBS) - -strip $(TARGET) diff --git a/src/native/portaudio/Mutex.h b/src/native/portaudio/Mutex.h deleted file mode 100644 index 93ab3ec79..000000000 --- a/src/native/portaudio/Mutex.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Distributable under LGPL license. - * See terms of license at gnu.org. - */ - -#ifndef _NET_JAVA_SIP_COMMUNICATOR_IMPL_NEOMEDIA_PORTAUDIO_MUTEX_H_ -#define _NET_JAVA_SIP_COMMUNICATOR_IMPL_NEOMEDIA_PORTAUDIO_MUTEX_H_ - -#include - -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include - -typedef CRITICAL_SECTION Mutex; - -static inline void Mutex_free(Mutex* mutex) -{ - DeleteCriticalSection(mutex); - free(mutex); -} - -static inline int Mutex_lock(Mutex* mutex) -{ - EnterCriticalSection(mutex); - return 0; -} - -static inline Mutex *Mutex_new(void* attr) -{ - Mutex *mutex = malloc(sizeof(Mutex)); - - (void) attr; - - if (mutex) - InitializeCriticalSection(mutex); - return mutex; -} - -static inline int Mutex_unlock(Mutex* mutex) -{ - LeaveCriticalSection(mutex); - return 0; -} - -#else /* #ifdef _WIN32 */ -#include - -typedef pthread_mutex_t Mutex; - -static inline void Mutex_free(Mutex* mutex) -{ - if (!pthread_mutex_destroy(mutex)) - free(mutex); -} - -static inline int Mutex_lock(Mutex* mutex) -{ - return pthread_mutex_lock(mutex); -} - -static inline Mutex *Mutex_new(void* attr) -{ - Mutex *mutex = malloc(sizeof(Mutex)); - - if (mutex && pthread_mutex_init(mutex, attr)) - { - free(mutex); - mutex = NULL; - } - return mutex; -} - -static inline int Mutex_unlock(Mutex* mutex) -{ - return pthread_mutex_unlock(mutex); -} -#endif /* #ifdef _WIN32 */ - -#endif /* #ifndef _NET_JAVA_SIP_COMMUNICATOR_IMPL_NEOMEDIA_PORTAUDIO_MUTEX_H_ */ diff --git a/src/native/portaudio/README b/src/native/portaudio/README deleted file mode 100644 index 32249088c..000000000 --- a/src/native/portaudio/README +++ /dev/null @@ -1,40 +0,0 @@ -1. portaudio - - Get portaudio-hotplug branch and apply the portaudio-hotplug-os patch: - $ svn -r 1821 co https://subversion.assembla.com/svn/portaudio/portaudio/branches/hotplug - $ patch -p0 < portaudio-hotplug-os.patch - $ autoreconf -i (OS X and Linux only) - - - Linux/FreeBSD - pa_linux_alsa.c-by-Werner.patch (already in portaudio-hotplug-os.patch) - pa_linux_alsa.c-fix-blocked-renderer.patch (already in portaudio-hotplug-os.patch) - $ ./configure --disable-shared --enable-static --with-pic --with-jack=no && make/gmake - - - Mac OS X - $ export MACOSX_DEPLOYMENT_TARGET=10.4 - $ ./configure --disable-shared --enable-static --with-pic && make - - - Windows - Use msys and mingw (A gui installer, use latest - http://sf.net/projects/mingw/files/Installer/mingw-get-inst/ this can - only compile 32bit). - Download directx devpack for MinGW at http://www.dgrigoriadis.net/post/2004/06/26/DirectXDevPak-for-Dev-Cpp.aspx and - extract it. (May neeto to remove the define for WAVEFORMATEXTENSIBLE - (#ifndef _WAVEFORMATEXTENSIBLE_ .....) include/ksmedia.h) - - $ DXDIR=/path/to/directx_dev_pack_directory - $ CFLAGS="-DWINVER=0x501" ./configure --disable-shared --enable-static --with-pic --with-dxdir=$DXDIR --with-winapi=wmme,directx,wdmks && make - -2. speex - - Linux/FreeBSD, Windows - $ ./configure --disable-shared --enable-static --with-pic && make/gmake - - - Mac OS X - $ export MACOSX_DEPLOYMENT_TARGET=10.4 - $ export CC="gcc -arch i386 -arch ppc -arch x86_64 -mmacosx-version-min=10.4" - $ export CPP="gcc -E" - $ ./configure --disable-shared --enable-static --with-pic && make - -3. jportaudio - - Linux/FreeBSD, Mac OS X, Windows - $ make/gmake 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 deleted file mode 100644 index 629025b80..000000000 --- a/src/native/portaudio/net_java_sip_communicator_impl_neomedia_portaudio_PortAudio.c +++ /dev/null @@ -1,1649 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Distributable under LGPL license. - * See terms of license at gnu.org. - */ - -#include "net_java_sip_communicator_impl_neomedia_portaudio_PortAudio.h" - -#include "AudioQualityImprovement.h" -#include "ConditionVariable.h" -#include "Mutex.h" -#include -#include -#include -#include -#include -#include - -typedef struct -{ - AudioQualityImprovement *audioQualityImprovement; - int channels; - JNIEnv *env; - jboolean finished; - - /** - * The value specified as the framesPerBuffer argument to the - * Pa_OpenStream function call which has opened #stream. - */ - unsigned long framesPerBuffer; - void *input; - size_t inputCapacity; - ConditionVariable *inputCondVar; - long inputFrameSize; - - /** The input latency of #stream. */ - jlong inputLatency; - size_t inputLength; - Mutex *inputMutex; - Mutex *mutex; - void *output; - size_t outputCapacity; - ConditionVariable *outputCondVar; - long outputFrameSize; - - /** The output latency of #stream. */ - jlong outputLatency; - size_t outputLength; - Mutex *outputMutex; - - /** - * The indicator which determines whether this PortAudioStream - * implements the blocking stream interface on top of the non-blocking - * stream interface. - */ - jboolean pseudoBlocking; - jlong retainCount; - double sampleRate; - int sampleSizeInBits; - PaStream *stream; - jobject streamCallback; - jmethodID streamCallbackMethodID; - jmethodID streamFinishedCallbackMethodID; - JavaVM *vm; -} PortAudioStream; - -static void PortAudio_devicesChangedCallback(void *userData); -static PaStreamParameters *PortAudio_fixInputParametersSuggestedLatency - (PaStreamParameters *inputParameters, - jdouble sampleRate, jlong framesPerBuffer, - PaHostApiTypeId hostApiType); -static PaStreamParameters *PortAudio_fixOutputParametersSuggestedLatency - (PaStreamParameters *outputParameters, - jdouble sampleRate, jlong framesPerBuffer, - PaHostApiTypeId hostApiType); -static PaStreamParameters *PortAudio_fixStreamParametersSuggestedLatency - (PaStreamParameters *streamParameters, - jdouble sampleRate, jlong framesPerBuffer, - PaHostApiTypeId hostApiType); -static long PortAudio_getFrameSize(PaStreamParameters *streamParameters); -static unsigned long PortAudio_getSampleSizeInBits - (PaStreamParameters *streamParameters); -static void PortAudio_throwException(JNIEnv *env, PaError errorCode); - -/** - * Allocates (and initializes) the memory and its associated variables for a - * specific buffer to be used by the pseudo-blocking stream interface - * implementation of a PortAudioStream. - * - * @param capacity the number of bytes to be allocated to the buffer - * @param bufferPtr a pointer which specifies where the location of the - * allocated buffer is to be stored - * @param bufferLengthPtr a pointer which specifies where the initial length - * (i.e. zero) is to be stored - * @param bufferCapacityPtr a pointer which specifies where the capacity of the - * allocated buffer is to be stored - * @param bufferMutexPtr a pointer which specifies where the Mute to - * synchronize the access to the allocated buffer is to be stored - * @param bufferCondVarPtr a pointer which specifies where the - * ConditionVariable to synchronize the access to the allocated buffer - * is to be stored - * @return the location of the allocated buffer upon success; otherwise, - * NULL - */ -static void *PortAudioStream_allocPseudoBlockingBuffer - (size_t capacity, - void **bufferPtr, size_t *bufferLengthPtr, size_t *bufferCapacityPtr, - Mutex **bufferMutexPtr, ConditionVariable **bufferCondVarPtr); -static void PortAudioStream_free(JNIEnv *env, PortAudioStream *stream); -static int PortAudioStream_javaCallback - (const void *input, - void *output, - unsigned long frameCount, - const PaStreamCallbackTimeInfo *timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData); -static void PortAudioStream_javaFinishedCallback(void *userData); -static PortAudioStream * PortAudioStream_new - (JNIEnv *env, jobject streamCallback); -static void PortAudioStream_popFromPseudoBlockingBuffer - (void *buffer, size_t length, size_t *bufferLengthPtr); -static int PortAudioStream_pseudoBlockingCallback - (const void *input, - void *output, - unsigned long frameCount, - const PaStreamCallbackTimeInfo *timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData); -static void PortAudioStream_pseudoBlockingFinishedCallback(void *userData); -static void PortAudioStream_release(PortAudioStream *stream); -static void PortAudioStream_retain(PortAudioStream *stream); - -static const char *AUDIO_QUALITY_IMPROVEMENT_STRING_ID = "portaudio"; -#define LATENCY_HIGH net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_LATENCY_HIGH -#define LATENCY_LOW net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_LATENCY_LOW -#define LATENCY_UNSPECIFIED net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_LATENCY_UNSPECIFIED - -static jclass PortAudio_devicesChangedCallbackClass = 0; -static jmethodID PortAudio_devicesChangedCallbackMethodID = 0; -static JavaVM* PortAudio_vm = 0; - -JNIEXPORT void JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_free - (JNIEnv *env, jclass clazz, jlong ptr) -{ - free((void *) (intptr_t) ptr); -} - -JNIEXPORT void JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1AbortStream - (JNIEnv *env, jclass clazz, jlong stream) -{ - PaError errorCode - = Pa_AbortStream(((PortAudioStream *) (intptr_t) stream)->stream); - - if (paNoError != errorCode) - PortAudio_throwException(env, errorCode); -} - -JNIEXPORT void JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1CloseStream - (JNIEnv *env, jclass clazz, jlong stream) -{ - PortAudioStream *portAudioStream = (PortAudioStream *) (intptr_t) stream; - PaError errorCode = Pa_CloseStream(portAudioStream->stream); - - if (paNoError != errorCode) - PortAudio_throwException(env, errorCode); - else if (portAudioStream->pseudoBlocking) - PortAudioStream_release(portAudioStream); - else - PortAudioStream_free(env, portAudioStream); -} - -JNIEXPORT jint JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1GetDefaultInputDevice - (JNIEnv *env, jclass clazz) -{ - return Pa_GetDefaultInputDevice(); -} - -JNIEXPORT jint JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1GetDefaultOutputDevice - (JNIEnv *env, jclass clazz) -{ - return Pa_GetDefaultOutputDevice(); -} - -JNIEXPORT jint JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1GetDeviceCount( - JNIEnv *env, jclass clazz) -{ - PaDeviceIndex deviceCount = Pa_GetDeviceCount(); - - 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) -{ - return (jlong) (intptr_t) Pa_GetDeviceInfo(deviceIndex); -} - -JNIEXPORT jlong JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1GetHostApiInfo - (JNIEnv *env , jclass clazz, jint hostApiIndex) -{ - return (jlong) (intptr_t) Pa_GetHostApiInfo(hostApiIndex); -} - -JNIEXPORT jint JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1GetSampleSize - (JNIEnv *env, jclass clazz, jlong format) -{ - return Pa_GetSampleSize(format); -} - -JNIEXPORT jlong JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1GetStreamReadAvailable - (JNIEnv *env, jclass clazz, jlong stream) -{ - return - Pa_GetStreamReadAvailable( - ((PortAudioStream *) (intptr_t) stream)->stream); -} - -JNIEXPORT jlong JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1GetStreamWriteAvailable - (JNIEnv *env, jclass clazz, jlong stream) -{ - return - Pa_GetStreamWriteAvailable( - ((PortAudioStream *) (intptr_t) stream)->stream); -} - -JNIEXPORT void JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1Initialize - (JNIEnv *env, jclass clazz) -{ - PaError errorCode = Pa_Initialize(); - - if (paNoError == errorCode) - { - jclass devicesChangedCallbackClass - = (*env)->FindClass( - env, - "net/java/sip/communicator/impl/neomedia/portaudio/PortAudio"); - - if (devicesChangedCallbackClass) - { - devicesChangedCallbackClass - = (*env)->NewGlobalRef(env, devicesChangedCallbackClass); - if (devicesChangedCallbackClass) - { - jmethodID devicesChangedCallbackMethodID - = (*env)->GetStaticMethodID( - env, - devicesChangedCallbackClass, - "devicesChangedCallback", - "()V"); - - if (devicesChangedCallbackMethodID) - { - PortAudio_devicesChangedCallbackClass - = devicesChangedCallbackClass; - PortAudio_devicesChangedCallbackMethodID - = devicesChangedCallbackMethodID; - Pa_SetDevicesChangedCallback( - NULL, - PortAudio_devicesChangedCallback); - } - } - } - } - else - PortAudio_throwException(env, errorCode); -} - -JNIEXPORT jboolean JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1IsFormatSupported - (JNIEnv *env, jclass clazz, - jlong inputParameters, jlong outputParameters, jdouble sampleRate) -{ - if (Pa_IsFormatSupported( - (PaStreamParameters *) (intptr_t) inputParameters, - (PaStreamParameters *) (intptr_t) outputParameters, - sampleRate) - == paFormatIsSupported) - return JNI_TRUE; - else - return JNI_FALSE; -} - -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); - PaStreamCallback *effectiveStreamCallback; - PaStreamFinishedCallback *effectiveStreamFinishedCallback; - unsigned long effectiveFramesPerBuffer = framesPerBuffer; - PaHostApiTypeId hostApiType = paInDevelopment; - PaError errorCode; - PaStreamParameters *inputStreamParameters - = (PaStreamParameters *) (intptr_t) inputParameters; - PaStreamParameters *outputStreamParameters - = (PaStreamParameters *) (intptr_t) outputParameters; - - if (!stream) - return 0; - - if (streamCallback) - { - effectiveStreamCallback = PortAudioStream_javaCallback; - effectiveStreamFinishedCallback = PortAudioStream_javaFinishedCallback; - stream->pseudoBlocking = JNI_FALSE; - } - else - { - /* - * Some host APIs such as DirectSound don't really implement the - * blocking stream interface. If we're to ever be able to try them out, - * we'll have to implement the blocking stream interface on top of the - * non-blocking stream interface. - */ - - effectiveStreamCallback = NULL; - effectiveStreamFinishedCallback = NULL; - stream->pseudoBlocking = JNI_FALSE; - - /* - * TODO It should be possible to implement the blocking stream interface - * without a specific framesPerBuffer. - */ - if ((paFramesPerBufferUnspecified != framesPerBuffer) - && (framesPerBuffer > 0)) - { - PaDeviceIndex device; - - if (outputStreamParameters) - device = outputStreamParameters->device; - else if (inputStreamParameters) - device = inputStreamParameters->device; - else - device = paNoDevice; - if (device != paNoDevice) - { - const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(device); - - if (deviceInfo) - { - const PaHostApiInfo *hostApiInfo - = Pa_GetHostApiInfo(deviceInfo->hostApi); - - if (hostApiInfo) - { - switch (hostApiInfo->type) - { - case paCoreAudio: - /* - * If we are to ever succeed in requesting a higher - * latency in - * PortAudio_fixOutputParametersSuggestedLatency, we - * have to specify paFramesPerBufferUnspecified. - * Otherwise, the CoreAudio implementation of - * PortAudio will ignore our suggestedLatency. - */ - if (outputStreamParameters - && ((LATENCY_HIGH - == outputStreamParameters - ->suggestedLatency) - || (LATENCY_UNSPECIFIED - == outputStreamParameters - ->suggestedLatency))) - { - effectiveFramesPerBuffer - = paFramesPerBufferUnspecified; - hostApiType = hostApiInfo->type; - } - if (inputStreamParameters - && ((LATENCY_HIGH - == inputStreamParameters - ->suggestedLatency) - || (LATENCY_UNSPECIFIED - == inputStreamParameters - ->suggestedLatency))) - { - effectiveFramesPerBuffer - = paFramesPerBufferUnspecified; - hostApiType = hostApiInfo->type; - } - break; - case paDirectSound: - effectiveStreamCallback - = PortAudioStream_pseudoBlockingCallback; - effectiveStreamFinishedCallback - = PortAudioStream_pseudoBlockingFinishedCallback; - stream->pseudoBlocking = JNI_TRUE; - break; - default: - break; - } - } - } - } - } - } - - if (JNI_TRUE == stream->pseudoBlocking) - { - stream->mutex = Mutex_new(NULL); - errorCode = (stream->mutex) ? paNoError : paInsufficientMemory; - } - else - errorCode = paNoError; - - if (paNoError == errorCode) - { - errorCode - = Pa_OpenStream( - &(stream->stream), - PortAudio_fixInputParametersSuggestedLatency( - inputStreamParameters, - sampleRate, framesPerBuffer, - hostApiType), - PortAudio_fixOutputParametersSuggestedLatency( - outputStreamParameters, - sampleRate, framesPerBuffer, - hostApiType), - sampleRate, - effectiveFramesPerBuffer, - streamFlags, - effectiveStreamCallback, - stream); - } - - if (paNoError == errorCode) - { - stream->framesPerBuffer = effectiveFramesPerBuffer; - stream->inputFrameSize - = PortAudio_getFrameSize(inputStreamParameters); - stream->outputFrameSize - = PortAudio_getFrameSize(outputStreamParameters); - stream->sampleRate = sampleRate; - - if (effectiveStreamFinishedCallback) - { - errorCode - = Pa_SetStreamFinishedCallback( - stream->stream, - effectiveStreamFinishedCallback); - } - - stream->audioQualityImprovement - = AudioQualityImprovement_getSharedInstance( - AUDIO_QUALITY_IMPROVEMENT_STRING_ID, - 0); - if (inputStreamParameters) - { - stream->sampleSizeInBits - = PortAudio_getSampleSizeInBits(inputStreamParameters); - stream->channels = inputStreamParameters->channelCount; - - /* - * Prepare whatever is necessary for the pseudo-blocking stream - * interface implementation. For example, allocate its memory early - * because doing it in the stream callback may introduce latency. - */ - if (stream->pseudoBlocking - && !PortAudioStream_allocPseudoBlockingBuffer( - 2 * framesPerBuffer * (stream->inputFrameSize), - &(stream->input), - &(stream->inputLength), - &(stream->inputCapacity), - &(stream->inputMutex), - &(stream->inputCondVar))) - { - Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1CloseStream( - env, clazz, - (jlong) (intptr_t) stream); - if (JNI_FALSE == (*env)->ExceptionCheck(env)) - { - PortAudio_throwException(env, paInsufficientMemory); - return 0; - } - } - - if (stream->audioQualityImprovement) - { - AudioQualityImprovement_setSampleRate( - stream->audioQualityImprovement, - (int) sampleRate); - - if (stream->pseudoBlocking) - { - const PaStreamInfo *streamInfo; - - streamInfo = Pa_GetStreamInfo(stream->stream); - if (streamInfo) - { - stream->inputLatency - = (jlong) (streamInfo->inputLatency * 1000); - } - } - } - } - if (outputStreamParameters) - { - stream->sampleSizeInBits - = PortAudio_getSampleSizeInBits(outputStreamParameters); - stream->channels = outputStreamParameters->channelCount; - - if (stream->pseudoBlocking - && !PortAudioStream_allocPseudoBlockingBuffer( - 2 * framesPerBuffer * (stream->outputFrameSize), - &(stream->output), - &(stream->outputLength), - &(stream->outputCapacity), - &(stream->outputMutex), - &(stream->outputCondVar))) - { - Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1CloseStream( - env, clazz, - (jlong) (intptr_t) stream); - if (JNI_FALSE == (*env)->ExceptionCheck(env)) - { - PortAudio_throwException(env, paInsufficientMemory); - return 0; - } - } - - if (stream->audioQualityImprovement) - { - const PaStreamInfo *streamInfo; - - streamInfo = Pa_GetStreamInfo(stream->stream); - if (streamInfo) - { - stream->outputLatency - = (jlong) (streamInfo->outputLatency * 1000); - } - } - } - - if (stream->pseudoBlocking) - PortAudioStream_retain(stream); - - return (jlong) (intptr_t) 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_1ReadStream - (JNIEnv *env, jclass clazz, jlong stream, jbyteArray buffer, jlong frames) -{ - jbyte* data = (*env)->GetByteArrayElements(env, buffer, NULL); - - if (data) - { - PortAudioStream *portAudioStream - = (PortAudioStream *) (intptr_t) stream; - PaError errorCode; - jlong framesInBytes = frames * portAudioStream->inputFrameSize; - - if (portAudioStream->pseudoBlocking) - { - if (Mutex_lock(portAudioStream->inputMutex)) - errorCode = paInternalError; - else - { - jlong bytesRead = 0; - - errorCode = paNoError; - while (bytesRead < framesInBytes) - { - jlong bytesToRead; - - if (JNI_TRUE == portAudioStream->finished) - { - errorCode = paStreamIsStopped; - break; - } - if (!(portAudioStream->inputLength)) - { - ConditionVariable_wait( - portAudioStream->inputCondVar, - portAudioStream->inputMutex); - continue; - } - - bytesToRead = framesInBytes - bytesRead; - if (bytesToRead > portAudioStream->inputLength) - bytesToRead = portAudioStream->inputLength; - memcpy( - data + bytesRead, - portAudioStream->input, - bytesToRead); - PortAudioStream_popFromPseudoBlockingBuffer( - portAudioStream->input, - bytesToRead, - &(portAudioStream->inputLength)); - bytesRead += bytesToRead; - } - Mutex_unlock(portAudioStream->inputMutex); - } - - /* Improve the audio quality of the input if possible. */ - if ((paNoError == errorCode) - && portAudioStream->audioQualityImprovement) - { - AudioQualityImprovement_process( - portAudioStream->audioQualityImprovement, - AUDIO_QUALITY_IMPROVEMENT_SAMPLE_ORIGIN_INPUT, - portAudioStream->sampleRate, - portAudioStream->sampleSizeInBits, - portAudioStream->channels, - portAudioStream->inputLatency, - data, framesInBytes); - } - } - else - { - errorCode = Pa_ReadStream(portAudioStream->stream, data, frames); - if ((paNoError == errorCode) || (paInputOverflowed == errorCode)) - { - errorCode = paNoError; - - if (portAudioStream->audioQualityImprovement) - { - AudioQualityImprovement_process( - portAudioStream->audioQualityImprovement, - AUDIO_QUALITY_IMPROVEMENT_SAMPLE_ORIGIN_INPUT, - portAudioStream->sampleRate, - portAudioStream->sampleSizeInBits, - portAudioStream->channels, - portAudioStream->inputLatency, - data, framesInBytes); - } - } - } - - if (paNoError == errorCode) - (*env)->ReleaseByteArrayElements(env, buffer, data, 0); - else - { - (*env)->ReleaseByteArrayElements(env, buffer, data, JNI_ABORT); - PortAudio_throwException(env, errorCode); - } - } -} - -JNIEXPORT void JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1StartStream - (JNIEnv *env, jclass clazz, jlong stream) -{ - PortAudioStream *portAudioStream = (PortAudioStream *) (intptr_t) stream; - PaError errorCode; - - if (portAudioStream->pseudoBlocking) - { - PortAudioStream_retain(portAudioStream); - if (Mutex_lock(portAudioStream->mutex)) - errorCode = paInternalError; - else - { - portAudioStream->finished = JNI_FALSE; - errorCode = Pa_StartStream(portAudioStream->stream); - if (paNoError != errorCode) - portAudioStream->finished = JNI_TRUE; - Mutex_unlock(portAudioStream->mutex); - } - if (paNoError != errorCode) - PortAudioStream_release(portAudioStream); - } - else - errorCode = Pa_StartStream(portAudioStream->stream); - 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) -{ - PaError errorCode - = Pa_StopStream(((PortAudioStream *) (intptr_t) stream)->stream); - - 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, jint offset, jlong frames, - jint numberOfWrites) -{ - jbyte *bufferBytes; - jbyte* data; - PortAudioStream *portAudioStream; - jint i; - PaError errorCode = paNoError; - jlong framesInBytes; - AudioQualityImprovement *audioQualityImprovement; - double sampleRate; - unsigned long sampleSizeInBits; - int channels; - jlong outputLatency; - - bufferBytes = (*env)->GetByteArrayElements(env, buffer, NULL); - if (!bufferBytes) - return; - data = bufferBytes + offset; - - portAudioStream = (PortAudioStream *) (intptr_t) stream; - framesInBytes = frames * portAudioStream->outputFrameSize; - audioQualityImprovement = portAudioStream->audioQualityImprovement; - sampleRate = portAudioStream->sampleRate; - sampleSizeInBits = portAudioStream->sampleSizeInBits; - channels = portAudioStream->channels; - outputLatency = portAudioStream->outputLatency; - - if (portAudioStream->pseudoBlocking) - { - for (i = 0; i < numberOfWrites; i++) - { - if (Mutex_lock(portAudioStream->outputMutex)) - errorCode = paInternalError; - else - { - jlong bytesWritten = 0; - - errorCode = paNoError; - while (bytesWritten < framesInBytes) - { - size_t outputCapacity - = portAudioStream->outputCapacity - - portAudioStream->outputLength; - jlong bytesToWrite; - - if (JNI_TRUE == portAudioStream->finished) - { - errorCode = paStreamIsStopped; - break; - } - if (outputCapacity < 1) - { - ConditionVariable_wait( - portAudioStream->outputCondVar, - portAudioStream->outputMutex); - continue; - } - - bytesToWrite = framesInBytes - bytesWritten; - if (bytesToWrite > outputCapacity) - bytesToWrite = outputCapacity; - memcpy( - ((jbyte *) portAudioStream->output) - + portAudioStream->outputLength, - data + bytesWritten, - bytesToWrite); - - portAudioStream->outputLength += bytesToWrite; - bytesWritten += bytesToWrite; - } - Mutex_unlock(portAudioStream->outputMutex); - } - - if (paNoError == errorCode) - { - if (audioQualityImprovement) - { - AudioQualityImprovement_process( - audioQualityImprovement, - AUDIO_QUALITY_IMPROVEMENT_SAMPLE_ORIGIN_OUTPUT, - sampleRate, sampleSizeInBits, channels, - outputLatency, - data, framesInBytes); - } - - data += framesInBytes; - } - } - } - else - { - PaStream *paStream = portAudioStream->stream; - - for (i = 0; i < numberOfWrites; i++) - { - errorCode = Pa_WriteStream(paStream, data, frames); - if ((paNoError != errorCode) && (paOutputUnderflowed != errorCode)) - break; - else - { - if (audioQualityImprovement) - { - AudioQualityImprovement_process( - audioQualityImprovement, - AUDIO_QUALITY_IMPROVEMENT_SAMPLE_ORIGIN_OUTPUT, - sampleRate, sampleSizeInBits, channels, - outputLatency, - data, framesInBytes); - } - data += framesInBytes; - } - } - } - - (*env)->ReleaseByteArrayElements(env, buffer, bufferBytes, JNI_ABORT); - - if ((paNoError != errorCode) && (paOutputUnderflowed != errorCode)) - PortAudio_throwException(env, errorCode); -} - -JNIEXPORT jdouble JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaDeviceInfo_1getDefaultHighInputLatency - (JNIEnv *env, jclass clazz, jlong deviceInfo) -{ - return ((PaDeviceInfo *) (intptr_t) deviceInfo)->defaultHighInputLatency; -} - -JNIEXPORT jdouble JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaDeviceInfo_1getDefaultHighOutputLatency - (JNIEnv *env, jclass clazz, jlong deviceInfo) -{ - return ((PaDeviceInfo *) (intptr_t) deviceInfo)->defaultHighOutputLatency; -} - -JNIEXPORT jdouble JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaDeviceInfo_1getDefaultLowInputLatency - (JNIEnv *env, jclass clazz, jlong deviceInfo) -{ - return ((PaDeviceInfo *) (intptr_t) deviceInfo)->defaultLowInputLatency; -} - -JNIEXPORT jdouble JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaDeviceInfo_1getDefaultLowOutputLatency - (JNIEnv *env, jclass clazz, jlong deviceInfo) -{ - return ((PaDeviceInfo *) (intptr_t) deviceInfo)->defaultLowOutputLatency; -} - -JNIEXPORT jdouble JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaDeviceInfo_1getDefaultSampleRate - (JNIEnv *env, jclass clazz, jlong deviceInfo) -{ - return ((PaDeviceInfo *) (intptr_t) deviceInfo)->defaultSampleRate; -} - -JNIEXPORT jint JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaDeviceInfo_1getHostApi - (JNIEnv *env, jclass clazz, jlong deviceInfo) -{ - return ((PaDeviceInfo *) (intptr_t) deviceInfo)->hostApi; -} - -JNIEXPORT jint JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaDeviceInfo_1getMaxInputChannels - (JNIEnv *env, jclass clazz, jlong deviceInfo) -{ - return ((PaDeviceInfo *) (intptr_t) deviceInfo)->maxInputChannels; -} - -JNIEXPORT jint JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaDeviceInfo_1getMaxOutputChannels - (JNIEnv *env, jclass clazz, jlong deviceInfo) -{ - return ((PaDeviceInfo *) (intptr_t) deviceInfo)->maxOutputChannels; -} - -JNIEXPORT jbyteArray JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaDeviceInfo_1getNameBytes - (JNIEnv *env, jclass clazz, jlong deviceInfo) -{ - const char *name = ((PaDeviceInfo *) (intptr_t) deviceInfo)->name; - jbyteArray nameBytes; - - if (name) - { - size_t nameLength = strlen(name); - - nameBytes = (*env)->NewByteArray(env, nameLength); - if (nameBytes && nameLength) - { - (*env)->SetByteArrayRegion( - env, - nameBytes, 0, nameLength, - (jbyte *) name); - } - } - else - nameBytes = NULL; - return nameBytes; -} - -JNIEXPORT jbyteArray JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaDeviceInfo_1getTransportTypeBytes - (JNIEnv *env, jclass clazz, jlong deviceInfo) -{ - jbyteArray typeBytes = NULL; - if(((PaDeviceInfo *) (intptr_t) deviceInfo)->structVersion >= 3) - { - const char *type - = ((PaDeviceInfo *) (intptr_t) deviceInfo)->transportType; - - if(type != NULL) - { - size_t typeLength = strlen(type); - - typeBytes = (*env)->NewByteArray(env, typeLength); - if (typeBytes && typeLength) - { - (*env)->SetByteArrayRegion( - env, - typeBytes, 0, typeLength, - (jbyte *) type); - } - } - } - - return typeBytes; -} - -JNIEXPORT jbyteArray JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaDeviceInfo_1getDeviceUIDBytes - (JNIEnv *env, jclass clazz, jlong deviceInfo) -{ - jbyteArray uidBytes = NULL; - if(((PaDeviceInfo *) (intptr_t) deviceInfo)->structVersion >= 3) - { - const char *uid - = ((PaDeviceInfo *) (intptr_t) deviceInfo)->deviceUID; - - if (uid) - { - size_t uidLength = strlen(uid); - - uidBytes = (*env)->NewByteArray(env, uidLength); - if (uidBytes && uidLength) - { - (*env)->SetByteArrayRegion( - env, - uidBytes, 0, uidLength, - (jbyte *) uid); - } - } - } - - return uidBytes; -} - -JNIEXPORT jint JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaHostApiInfo_1getDefaultInputDevice - (JNIEnv *env, jclass clazz, jlong hostApi) -{ - return ((PaHostApiInfo *) (intptr_t) hostApi)->defaultInputDevice; -} - -JNIEXPORT jint JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaHostApiInfo_1getDefaultOutputDevice - (JNIEnv *env, jclass clazz, jlong hostApi) -{ - return ((PaHostApiInfo *) (intptr_t) hostApi)->defaultOutputDevice; -} - -JNIEXPORT jint JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaHostApiInfo_1getDeviceCount - (JNIEnv *env, jclass clazz, jlong hostApi) -{ - return ((PaHostApiInfo *) (intptr_t) hostApi)->deviceCount; -} - -JNIEXPORT jstring JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaHostApiInfo_1getName - (JNIEnv *env, jclass clazz, jlong hostApi) -{ - const char *name = ((PaHostApiInfo *) (intptr_t) hostApi)->name; - - /* PaHostApiInfo_GetName has been deprected in the Java source code. */ - return name ? (*env)->NewStringUTF(env, name) : NULL; -} - -JNIEXPORT jint JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaHostApiInfo_1getType - (JNIEnv *env, jclass clazz, jlong hostApi) -{ - return ((PaHostApiInfo *) (intptr_t) hostApi)->type; -} - -JNIEXPORT jlong JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaStreamParameters_1new - (JNIEnv *env, jclass clazz, - jint deviceIndex, - jint channelCount, - jlong sampleFormat, - jdouble suggestedLatency) -{ - 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) (intptr_t) streamParameters; -} - -JNIEXPORT void JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_setDenoise - (JNIEnv *env, jclass clazz, jlong stream, jboolean denoise) -{ - AudioQualityImprovement *audioQualityImprovement - = ((PortAudioStream *) (intptr_t) stream)->audioQualityImprovement; - - if (audioQualityImprovement) - AudioQualityImprovement_setDenoise(audioQualityImprovement, denoise); -} - -JNIEXPORT void JNICALL -Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_setEchoFilterLengthInMillis - (JNIEnv *env, jclass clazz, jlong stream, jlong echoFilterLengthInMillis) -{ - AudioQualityImprovement *audioQualityImprovement - = ((PortAudioStream *) (intptr_t) stream)->audioQualityImprovement; - - if (audioQualityImprovement) - { - AudioQualityImprovement_setEchoFilterLengthInMillis( - audioQualityImprovement, - echoFilterLengthInMillis); - } -} - -JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_updateAvailableDeviceList - (JNIEnv *env, jclass clazz) -{ - Pa_UpdateAvailableDeviceList(); -} - -JNIEXPORT jint JNICALL -JNI_OnLoad(JavaVM *vm, void *reserved) -{ - PortAudio_vm = vm; - - AudioQualityImprovement_load(); - - return JNI_VERSION_1_4; -} - -JNIEXPORT void JNICALL -JNI_OnUnload(JavaVM *vm, void *reserved) -{ - AudioQualityImprovement_unload(); - - PortAudio_vm = NULL; -} - -static void -PortAudio_devicesChangedCallback(void *userData) -{ - JavaVM *vm = PortAudio_vm; - JNIEnv *env; - - (void) userData; - - if (!vm - || ((*vm)->AttachCurrentThreadAsDaemon(vm, (void **) &env, NULL) - < 0)) - { - fprintf(stderr, "AttachCurrentThreadAsDaemon\n" ); - fflush(stderr); - } - else - { - jclass clazz = PortAudio_devicesChangedCallbackClass; - jmethodID methodID = PortAudio_devicesChangedCallbackMethodID; - - if (clazz && methodID) - (*env)->CallStaticVoidMethod(env, clazz, methodID); - } -} - -static PaStreamParameters * -PortAudio_fixInputParametersSuggestedLatency - (PaStreamParameters *inputParameters, - jdouble sampleRate, jlong framesPerBuffer, - PaHostApiTypeId hostApiType) -{ - if (inputParameters) - { - const PaDeviceInfo *deviceInfo - = Pa_GetDeviceInfo(inputParameters->device); - - if (deviceInfo) - { - PaTime suggestedLatency = inputParameters->suggestedLatency; - - if (suggestedLatency == LATENCY_LOW) - { - inputParameters->suggestedLatency - = deviceInfo->defaultLowInputLatency; - } - else if ((suggestedLatency == LATENCY_HIGH) - || (suggestedLatency == LATENCY_UNSPECIFIED)) - { - inputParameters->suggestedLatency - = deviceInfo->defaultHighInputLatency; - - /* - * When the input latency is too low, we do not have a great - * chance to perform echo cancellation using it. Since the - * caller does not care about the input latency, try to request - * an input latency which increases our chances. - */ - PortAudio_fixStreamParametersSuggestedLatency( - inputParameters, - sampleRate, framesPerBuffer, - hostApiType); - } - } - } - return inputParameters; -} - -static PaStreamParameters * -PortAudio_fixOutputParametersSuggestedLatency( - PaStreamParameters *outputParameters, - jdouble sampleRate, jlong framesPerBuffer, - PaHostApiTypeId hostApiType) -{ - if (outputParameters) - { - const PaDeviceInfo *deviceInfo - = Pa_GetDeviceInfo(outputParameters->device); - - if (deviceInfo) - { - PaTime suggestedLatency = outputParameters->suggestedLatency; - - if (suggestedLatency == LATENCY_LOW) - { - outputParameters->suggestedLatency - = deviceInfo->defaultLowOutputLatency; - } - else if ((suggestedLatency == LATENCY_HIGH) - || (suggestedLatency == LATENCY_UNSPECIFIED)) - { - outputParameters->suggestedLatency - = deviceInfo->defaultHighOutputLatency; - - /* - * When the output latency is too low, we do not have a great - * chance to perform echo cancellation using it. Since the - * caller does not care about the output latency, try to request - * an output latency which increases our chances. - */ - PortAudio_fixStreamParametersSuggestedLatency( - outputParameters, - sampleRate, framesPerBuffer, - hostApiType); - } - } - } - return outputParameters; -} - -static PaStreamParameters * -PortAudio_fixStreamParametersSuggestedLatency - (PaStreamParameters *streamParameters, - jdouble sampleRate, jlong framesPerBuffer, - PaHostApiTypeId hostApiType) -{ - if ((paCoreAudio == hostApiType) - && sampleRate - && (paFramesPerBufferUnspecified != framesPerBuffer)) - { - PaTime minLatency - = (MIN_PLAY_DELAY_IN_FRAMES - * streamParameters->channelCount - * framesPerBuffer) - / (2 * sampleRate); - - if (streamParameters->suggestedLatency < minLatency) - streamParameters->suggestedLatency = minLatency; - } - return streamParameters; -} - -static long -PortAudio_getFrameSize(PaStreamParameters *streamParameters) -{ - if (streamParameters) - { - PaError sampleSize = Pa_GetSampleSize(streamParameters->sampleFormat); - - if (paSampleFormatNotSupported != sampleSize) - return sampleSize * streamParameters->channelCount; - } - return 0; -} - -static unsigned long -PortAudio_getSampleSizeInBits(PaStreamParameters *streamParameters) -{ - if (streamParameters) - { - PaError sampleSize = Pa_GetSampleSize(streamParameters->sampleFormat); - - if (paSampleFormatNotSupported != sampleSize) - return sampleSize * 8; - } - return 0; -} - -static void -PortAudio_throwException(JNIEnv *env, PaError errorCode) -{ - jclass clazz - = (*env)->FindClass( - env, - "net/java/sip/communicator/impl/neomedia/portaudio/PortAudioException"); - - if(errorCode == paUnanticipatedHostError) - { - // throw new exception with host error info - const PaHostErrorInfo* herr = Pa_GetLastHostErrorInfo(); - if (herr) - { - jmethodID methodID - = (*env)->GetMethodID( - env, - clazz, - "", - "(Ljava/lang/String;IJLjava/lang/String;)V"); - - if (methodID) - { - jstring jmessage; - if (herr->errorText) - jmessage = (*env)->NewStringUTF(env, herr->errorText); - else - jmessage = (*env)->NewStringUTF(env, ""); - - if (jmessage) - { - jobject t - = (*env)->NewObject( - env, - clazz, - methodID, - Pa_GetErrorText(errorCode), - herr->hostApiType, - herr->errorCode, - jmessage); - - if (t) - { - (*env)->Throw(env, (jthrowable) t); - return; - } - } - } - } - } - - if (clazz) - (*env)->ThrowNew(env, clazz, Pa_GetErrorText(errorCode)); -} - -/** - * Allocates (and initializes) the memory and its associated variables for a - * specific buffer to be used by the pseudo-blocking stream interface - * implementation of a PortAudioStream. - * - * @param capacity the number of bytes to be allocated to the buffer - * @param bufferPtr a pointer which specifies where the location of the - * allocated buffer is to be stored - * @param bufferLengthPtr a pointer which specifies where the initial length - * (i.e. zero) is to be stored - * @param bufferCapacityPtr a pointer which specifies where the capacity of the - * allocated buffer is to be stored - * @param bufferMutexPtr a pointer which specifies where the Mute to - * synchronize the access to the allocated buffer is to be stored - * @param bufferCondVarPtr a pointer which specifies where the - * ConditionVariable to synchronize the access to the allocated buffer - * is to be stored - * @return the location of the allocated buffer upon success; otherwise, - * NULL - */ -static void * -PortAudioStream_allocPseudoBlockingBuffer - (size_t capacity, - void **bufferPtr, size_t *bufferLengthPtr, size_t *bufferCapacityPtr, - Mutex **bufferMutexPtr, ConditionVariable **bufferCondVarPtr) -{ - void *buffer = malloc(capacity); - - if (buffer) - { - Mutex *mutex = Mutex_new(NULL); - - if (mutex) - { - ConditionVariable *condVar = ConditionVariable_new(NULL); - - if (condVar) - { - if (bufferPtr) - *bufferPtr = buffer; - if (bufferLengthPtr) - *bufferLengthPtr = 0; - if (bufferCapacityPtr) - *bufferCapacityPtr = capacity; - *bufferMutexPtr = mutex; - *bufferCondVarPtr = condVar; - } - else - { - Mutex_free(mutex); - free(buffer); - buffer = NULL; - } - } - else - { - free(buffer); - buffer = NULL; - } - } - return buffer; -} - -static void -PortAudioStream_free(JNIEnv *env, PortAudioStream *stream) -{ - if (stream->streamCallback) - (*env)->DeleteGlobalRef(env, stream->streamCallback); - - if (stream->inputMutex && !Mutex_lock(stream->inputMutex)) - { - if (stream->input) - free(stream->input); - ConditionVariable_free(stream->inputCondVar); - Mutex_unlock(stream->inputMutex); - Mutex_free(stream->inputMutex); - } - - if (stream->outputMutex && !Mutex_lock(stream->outputMutex)) - { - if (stream->output) - free(stream->output); - ConditionVariable_free(stream->outputCondVar); - Mutex_unlock(stream->outputMutex); - Mutex_free(stream->outputMutex); - } - - if (stream->audioQualityImprovement) - AudioQualityImprovement_release(stream->audioQualityImprovement); - - if (stream->mutex) - Mutex_free(stream->mutex); - - free(stream); -} - -static int -PortAudioStream_javaCallback - (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, - (void *) input, - frameCount * stream->inputFrameSize) - : NULL, - output - ? (*env)->NewDirectByteBuffer( - env, - output, - frameCount * stream->outputFrameSize) - : NULL); -} - -static void -PortAudioStream_javaFinishedCallback(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); -} - -static PortAudioStream * -PortAudioStream_new(JNIEnv *env, jobject streamCallback) -{ - PortAudioStream *stream = calloc(1, sizeof(PortAudioStream)); - - if (!stream) - { - PortAudio_throwException(env, paInsufficientMemory); - return NULL; - } - - 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; - } - } - - return stream; -} - -static void -PortAudioStream_popFromPseudoBlockingBuffer - (void *buffer, size_t length, size_t *bufferLengthPtr) -{ - size_t i; - size_t newLength = *bufferLengthPtr - length; - jbyte *oldBuffer = (jbyte *) buffer; - jbyte *newBuffer = ((jbyte *) buffer) + length; - - for (i = 0; i < newLength; i++) - *oldBuffer++ = *newBuffer++; - *bufferLengthPtr = newLength; -} - -static int -PortAudioStream_pseudoBlockingCallback - (const void *input, - void *output, - unsigned long frameCount, - const PaStreamCallbackTimeInfo *timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData) -{ - PortAudioStream *stream = (PortAudioStream *) userData; - - if (input && stream->inputMutex && !Mutex_lock(stream->inputMutex)) - { - size_t inputLength = frameCount * stream->inputFrameSize; - size_t newInputLength; - void *inputInStream; - - /* - * Remember the specified input so that it can be retrieved later on in - * our pseudo-blocking Pa_ReadStream(). - */ - newInputLength = stream->inputLength + inputLength; - if (newInputLength > stream->inputCapacity) - { - PortAudioStream_popFromPseudoBlockingBuffer( - stream->input, - newInputLength - stream->inputCapacity, - &(stream->inputLength)); - } - inputInStream = ((jbyte *) (stream->input)) + stream->inputLength; - memcpy(inputInStream, input, inputLength); - stream->inputLength += inputLength; - - ConditionVariable_notify(stream->inputCondVar); - Mutex_unlock(stream->inputMutex); - } - if (output && stream->outputMutex && !Mutex_lock(stream->outputMutex)) - { - size_t outputLength = frameCount * stream->outputFrameSize; - size_t availableOutputLength = outputLength; - - if (availableOutputLength > stream->outputLength) - availableOutputLength = stream->outputLength; - memcpy(output, stream->output, availableOutputLength); - PortAudioStream_popFromPseudoBlockingBuffer( - stream->output, - availableOutputLength, - &(stream->outputLength)); - if (availableOutputLength < outputLength) - { - memset( - ((jbyte *) output) + availableOutputLength, - 0, - outputLength - availableOutputLength); - } - - ConditionVariable_notify(stream->outputCondVar); - Mutex_unlock(stream->outputMutex); - } - return paContinue; -} - -static void -PortAudioStream_pseudoBlockingFinishedCallback(void *userData) -{ - PortAudioStream *stream = (PortAudioStream *) userData; - - if (!Mutex_lock(stream->mutex)) - { - stream->finished = JNI_TRUE; - if (stream->inputMutex && !Mutex_lock(stream->inputMutex)) - { - ConditionVariable_notify(stream->inputCondVar); - Mutex_unlock(stream->inputMutex); - } - if (stream->outputMutex && !Mutex_lock(stream->outputMutex)) - { - ConditionVariable_notify(stream->outputCondVar); - Mutex_unlock(stream->outputMutex); - } - Mutex_unlock(stream->mutex); - } - PortAudioStream_release(stream); -} - -static void -PortAudioStream_release(PortAudioStream *stream) -{ - if (!Mutex_lock(stream->mutex)) - { - --(stream->retainCount); - if (stream->retainCount < 1) - { - Mutex_unlock(stream->mutex); - PortAudioStream_free(NULL, stream); - } - else - Mutex_unlock(stream->mutex); - } -} - -static void -PortAudioStream_retain(PortAudioStream *stream) -{ - if (!Mutex_lock(stream->mutex)) - { - ++(stream->retainCount); - Mutex_unlock(stream->mutex); - } -} 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 deleted file mode 100644 index e4c941d15..000000000 --- a/src/native/portaudio/net_java_sip_communicator_impl_neomedia_portaudio_PortAudio.h +++ /dev/null @@ -1,323 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -/* Header for class net_java_sip_communicator_impl_neomedia_portaudio_PortAudio */ - -#ifndef _Included_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio -#define _Included_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio -#ifdef __cplusplus -extern "C" { -#endif -#undef net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_LATENCY_HIGH -#define net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_LATENCY_HIGH -1.0 -#undef net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_LATENCY_LOW -#define net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_LATENCY_LOW -2.0 -#undef net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_LATENCY_UNSPECIFIED -#define net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_LATENCY_UNSPECIFIED 0.0 -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: free - * Signature: (J)V - */ -JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_free - (JNIEnv *, jclass, jlong); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: Pa_AbortStream - * Signature: (J)V - */ -JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1AbortStream - (JNIEnv *, jclass, jlong); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: Pa_CloseStream - * Signature: (J)V - */ -JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1CloseStream - (JNIEnv *, jclass, jlong); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: Pa_GetDefaultInputDevice - * Signature: ()I - */ -JNIEXPORT jint JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1GetDefaultInputDevice - (JNIEnv *, jclass); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: Pa_GetDefaultOutputDevice - * Signature: ()I - */ -JNIEXPORT jint JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1GetDefaultOutputDevice - (JNIEnv *, jclass); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: Pa_GetDeviceCount - * Signature: ()I - */ -JNIEXPORT jint JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1GetDeviceCount - (JNIEnv *, jclass); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: Pa_GetDeviceInfo - * Signature: (I)J - */ -JNIEXPORT jlong JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1GetDeviceInfo - (JNIEnv *, jclass, jint); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: Pa_GetHostApiInfo - * Signature: (I)J - */ -JNIEXPORT jlong JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1GetHostApiInfo - (JNIEnv *, jclass, jint); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: Pa_GetSampleSize - * Signature: (J)I - */ -JNIEXPORT jint JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1GetSampleSize - (JNIEnv *, jclass, jlong); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: Pa_GetStreamReadAvailable - * Signature: (J)J - */ -JNIEXPORT jlong JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1GetStreamReadAvailable - (JNIEnv *, jclass, jlong); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: Pa_GetStreamWriteAvailable - * Signature: (J)J - */ -JNIEXPORT jlong JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1GetStreamWriteAvailable - (JNIEnv *, jclass, jlong); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: Pa_Initialize - * Signature: ()V - */ -JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1Initialize - (JNIEnv *, jclass); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: Pa_IsFormatSupported - * Signature: (JJD)Z - */ -JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1IsFormatSupported - (JNIEnv *, jclass, jlong, jlong, jdouble); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: Pa_OpenStream - * Signature: (JJDJJLnet/java/sip/communicator/impl/neomedia/portaudio/PortAudioStreamCallback;)J - */ -JNIEXPORT jlong JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1OpenStream - (JNIEnv *, jclass, jlong, jlong, jdouble, jlong, jlong, jobject); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: Pa_ReadStream - * Signature: (J[BJ)V - */ -JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1ReadStream - (JNIEnv *, jclass, jlong, jbyteArray, jlong); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: Pa_StartStream - * Signature: (J)V - */ -JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1StartStream - (JNIEnv *, jclass, jlong); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: Pa_StopStream - * Signature: (J)V - */ -JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1StopStream - (JNIEnv *, jclass, jlong); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: Pa_WriteStream - * Signature: (J[BIJI)V - */ -JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_Pa_1WriteStream - (JNIEnv *, jclass, jlong, jbyteArray, jint, jlong, jint); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: PaDeviceInfo_getDefaultHighInputLatency - * Signature: (J)D - */ -JNIEXPORT jdouble JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaDeviceInfo_1getDefaultHighInputLatency - (JNIEnv *, jclass, jlong); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: PaDeviceInfo_getDefaultHighOutputLatency - * Signature: (J)D - */ -JNIEXPORT jdouble JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaDeviceInfo_1getDefaultHighOutputLatency - (JNIEnv *, jclass, jlong); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: PaDeviceInfo_getDefaultLowInputLatency - * Signature: (J)D - */ -JNIEXPORT jdouble JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaDeviceInfo_1getDefaultLowInputLatency - (JNIEnv *, jclass, jlong); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: PaDeviceInfo_getDefaultLowOutputLatency - * Signature: (J)D - */ -JNIEXPORT jdouble JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaDeviceInfo_1getDefaultLowOutputLatency - (JNIEnv *, jclass, jlong); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: PaDeviceInfo_getDefaultSampleRate - * Signature: (J)D - */ -JNIEXPORT jdouble JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaDeviceInfo_1getDefaultSampleRate - (JNIEnv *, jclass, jlong); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: PaDeviceInfo_getHostApi - * Signature: (J)I - */ -JNIEXPORT jint JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaDeviceInfo_1getHostApi - (JNIEnv *, jclass, jlong); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: PaDeviceInfo_getMaxInputChannels - * Signature: (J)I - */ -JNIEXPORT jint JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaDeviceInfo_1getMaxInputChannels - (JNIEnv *, jclass, jlong); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: PaDeviceInfo_getMaxOutputChannels - * Signature: (J)I - */ -JNIEXPORT jint JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaDeviceInfo_1getMaxOutputChannels - (JNIEnv *, jclass, jlong); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: PaDeviceInfo_getNameBytes - * Signature: (J)[B - */ -JNIEXPORT jbyteArray JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaDeviceInfo_1getNameBytes - (JNIEnv *, jclass, jlong); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: PaDeviceInfo_getTransportTypeBytes - * Signature: (J)[B - */ -JNIEXPORT jbyteArray JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaDeviceInfo_1getTransportTypeBytes - (JNIEnv *, jclass, jlong); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: PaDeviceInfo_getDeviceUIDBytes - * Signature: (J)[B - */ -JNIEXPORT jbyteArray JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaDeviceInfo_1getDeviceUIDBytes - (JNIEnv *, jclass, jlong); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: PaHostApiInfo_getDefaultInputDevice - * Signature: (J)I - */ -JNIEXPORT jint JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaHostApiInfo_1getDefaultInputDevice - (JNIEnv *, jclass, jlong); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: PaHostApiInfo_getDefaultOutputDevice - * Signature: (J)I - */ -JNIEXPORT jint JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaHostApiInfo_1getDefaultOutputDevice - (JNIEnv *, jclass, jlong); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: PaHostApiInfo_getDeviceCount - * Signature: (J)I - */ -JNIEXPORT jint JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaHostApiInfo_1getDeviceCount - (JNIEnv *, jclass, jlong); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: PaHostApiInfo_getName - * Signature: (J)Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaHostApiInfo_1getName - (JNIEnv *, jclass, jlong); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: PaHostApiInfo_getType - * Signature: (J)I - */ -JNIEXPORT jint JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaHostApiInfo_1getType - (JNIEnv *, jclass, jlong); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: PaStreamParameters_new - * Signature: (IIJD)J - */ -JNIEXPORT jlong JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_PaStreamParameters_1new - (JNIEnv *, jclass, jint, jint, jlong, jdouble); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: setDenoise - * Signature: (JZ)V - */ -JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_setDenoise - (JNIEnv *, jclass, jlong, jboolean); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: setEchoFilterLengthInMillis - * Signature: (JJ)V - */ -JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_setEchoFilterLengthInMillis - (JNIEnv *, jclass, jlong, jlong); - -/* - * Class: net_java_sip_communicator_impl_neomedia_portaudio_PortAudio - * Method: updateAvailableDeviceList - * Signature: ()V - */ -JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_neomedia_portaudio_PortAudio_updateAvailableDeviceList - (JNIEnv *, jclass); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/native/portaudio/pa_linux_alsa.c-by-Werner.patch b/src/native/portaudio/pa_linux_alsa.c-by-Werner.patch deleted file mode 100644 index 5c3c60e56..000000000 --- a/src/native/portaudio/pa_linux_alsa.c-by-Werner.patch +++ /dev/null @@ -1,71 +0,0 @@ ---- pa_linux_alsa.c 2009-06-03 21:57:56.000000000 +0300 -+++ pa_linux_alsa.c 2010-04-22 14:26:41.081552093 +0300 -@@ -2735,8 +2735,19 @@ - snd_pcm_sframes_t framesAvail = snd_pcm_avail_update( self->pcm ); - *xrunOccurred = 0; - -- if( -EPIPE == framesAvail ) -- { -+ /* Get pcm_state and check for xrun condition. On playback I often see -+ * xrun but avail_update does not return -EPIPE but framesAvail larger -+ * than bufferSize. In case of xrun status set xrun flag, leave framesize -+ * as reported by avail_update, will be fixed below. In case avail_update -+ * returns -EPIPE process as usual. wd-xxx -+ */ -+ snd_pcm_state_t state = snd_pcm_state( self->pcm ); /* wd-xxx */ -+ if (state == SND_PCM_STATE_XRUN) { -+ // printf("xrun, fav %d\n", framesAvail); fflush(stdout); // DEBUG-WD -+ *xrunOccurred = 1; -+ } -+ if( -EPIPE == framesAvail) { -+ // printf("xrun-1, fav %d\n", framesAvail); fflush(stdout); // DEBUG-WD - *xrunOccurred = 1; - framesAvail = 0; - } -@@ -2745,6 +2756,11 @@ - ENSURE_( framesAvail, paUnanticipatedHostError ); - } - -+ /* Fix frames avail, should not be bigger than bufferSize wd-xxx */ -+ if (framesAvail > self->bufferSize) { -+ // printf("xrun-2, fav %d\n", framesAvail); fflush(stdout); // DEBUG-WD -+ framesAvail = self->bufferSize; -+ } - *numFrames = framesAvail; - - error: -@@ -3457,9 +3472,24 @@ - while( frames > 0 ) - { - int xrun = 0; -- PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) ); -- framesGot = PA_MIN( framesAvail, frames ); -+ PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) ); -+ /* -+ * In case of overrun WaitForFrames leaves the capture stream in STATE_PREPARED -+ * most of the time. handleXrun() restarts the ALSA stream only in case -+ * snd_pcm_recover() fails, which usually does not happen. -+ * Here we start the pcm stream again and go for another try. Another -+ * option is: set result to paOverrun and return to caller. Then -+ * the caller needs to call ReadStream again. This takes more time and -+ * we lose even more frames. -+ */ -+ if (xrun) { /* wd-xxx */ -+ if( snd_pcm_state( stream->capture.pcm ) == SND_PCM_STATE_PREPARED ) { -+ ENSURE_( snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError ); -+ } -+ continue; -+ } - -+ framesGot = PA_MIN( framesAvail, frames ); - PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) ); - if( framesGot > 0 ) - { -@@ -3580,6 +3610,7 @@ - - PA_ENSURE( PaAlsaStream_HandleXrun( stream ) ); - savail = snd_pcm_avail_update( stream->playback.pcm ); -+ // printf("GSWA xrun, sav: %ld (%lx)\n", savail, savail); fflush(stdout); // DEBUG-WD - - /* savail should not contain -EPIPE now, since PaAlsaStream_HandleXrun will only prepare the pcm */ - ENSURE_( savail, paUnanticipatedHostError ); diff --git a/src/native/portaudio/pa_linux_alsa.c-fix-blocked-renderer.patch b/src/native/portaudio/pa_linux_alsa.c-fix-blocked-renderer.patch deleted file mode 100644 index 1f131bd50..000000000 --- a/src/native/portaudio/pa_linux_alsa.c-fix-blocked-renderer.patch +++ /dev/null @@ -1,39 +0,0 @@ -Index: pa_linux_alsa.c -=================================================================== ---- pa_linux_alsa.c (revision 1418) -+++ pa_linux_alsa.c (working copy) -@@ -315,7 +315,10 @@ - } - - PaUtil_FreeMemory( alsaHostApi ); -- snd_config_update_free_global(); -+// damencho, removed fo compability with pulseaudio versions before 0.9.16 -+// segfault application: -+// bugtrack alsa: 0002124: snd_config_update_free_global kills applications using user space alsa plugins -+// snd_config_update_free_global(); - } - - /** Determine max channels and default latencies. -@@ -1364,7 +1367,7 @@ - - ENSURE_( snd_pcm_sw_params_set_avail_min( self->pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_sw_params_set_xfer_align( self->pcm, swParams, 1 ), paUnanticipatedHostError ); -- ENSURE_( snd_pcm_sw_params_set_tstamp_mode( self->pcm, swParams, SND_PCM_TSTAMP_ENABLE ), paUnanticipatedHostError ); -+ ENSURE_( snd_pcm_sw_params_set_tstamp_mode( self->pcm, swParams, SND_PCM_TSTAMP_MMAP ), paUnanticipatedHostError ); - - /* Set the parameters! */ - ENSURE_( snd_pcm_sw_params( self->pcm, swParams ), paUnanticipatedHostError ); -@@ -2788,6 +2807,13 @@ - - *shouldPoll = 0; - } -+ else -+ { -+ // not actually used -+ unsigned long framesAvail = 0; -+ // now check for xrun -+ PaAlsaStreamComponent_GetAvailableFrames(self, &framesAvail, xrun ); -+ } - - error: - return result; diff --git a/src/native/portaudio/portaudio-hotplug-os.patch b/src/native/portaudio/portaudio-hotplug-os.patch deleted file mode 100644 index 0938066ff..000000000 --- a/src/native/portaudio/portaudio-hotplug-os.patch +++ /dev/null @@ -1,1212 +0,0 @@ -Index: test/patest_update_available_device_list.c -=================================================================== ---- test/patest_update_available_device_list.c (revision 1799) -+++ test/patest_update_available_device_list.c (working copy) -@@ -1,5 +1,6 @@ - #include - #include -+#include - - #include "portaudio.h" - -@@ -33,9 +34,10 @@ - - for(;;){ - printDevices(); -- -+ - printf( "press [enter] to update the device list. or q + [enter] to quit.\n" ); -- if( getchar() == 'q' ) -+ char ch = getchar(); -+ if( ch == 'q' ) - break; - - Pa_UpdateAvailableDeviceList(); -Index: configure -=================================================================== ---- configure (revision 1799) -+++ configure (working copy) -@@ -11341,7 +11341,7 @@ - fi - SHARED_FLAGS="$LIBS -dynamiclib $mac_arches $mac_sysroot $mac_version_min" - CFLAGS="-std=c99 $CFLAGS $mac_arches $mac_sysroot $mac_version_min" -- OTHER_OBJS="src/os/unix/pa_unix_hostapis.o src/os/unix/pa_unix_util.o src/hostapi/coreaudio/pa_mac_core.o src/hostapi/coreaudio/pa_mac_core_utilities.o src/hostapi/coreaudio/pa_mac_core_blocking.o src/common/pa_ringbuffer.o" -+ OTHER_OBJS="src/os/unix/pa_unix_hostapis.o src/os/unix/pa_unix_util.o src/hostapi/coreaudio/pa_mac_core.o src/hostapi/coreaudio/pa_mac_core_utilities.o src/hostapi/coreaudio/pa_mac_core_blocking.o src/common/pa_ringbuffer.o src/os/mac_osx/pa_osx_hotplug.o" - PADLL="libportaudio.dylib" - ;; - -@@ -11373,7 +11373,7 @@ - - if [ "x$with_wdmks" = "xyes" ]; then - DXDIR="$with_dxdir" -- add_objects src/hostapi/wdmks/pa_win_wdmks.o src/os/win/pa_win_hostapis.o src/os/win/pa_win_util.o -+ add_objects src/hostapi/wdmks/pa_win_wdmks.o src/os/win/pa_win_hostapis.o src/os/win/pa_win_util.o src/os/win/pa_win_wdmks_utils.o - LIBS="-lwinmm -lm -luuid -lsetupapi -lole32" - DLL_LIBS="${DLL_LIBS} -lwinmm -lm -L$DXDIR/lib -luuid -lsetupapi -lole32" - #VC98="\"/c/Program Files/Microsoft Visual Studio/VC98/Include\"" -@@ -11610,7 +11610,7 @@ - if [ "$have_alsa" = "yes" ] && [ "$with_alsa" != "no" ] ; then - DLL_LIBS="$DLL_LIBS -lasound" - LIBS="$LIBS -lasound" -- OTHER_OBJS="$OTHER_OBJS src/hostapi/alsa/pa_linux_alsa.o" -+ OTHER_OBJS="$OTHER_OBJS src/hostapi/alsa/pa_linux_alsa.o src/os/unix/pa_linux_hotplug.o" - INCLUDES="$INCLUDES pa_linux_alsa.h" - $as_echo "#define PA_USE_ALSA 1" >>confdefs.h - -Index: Makefile.in -=================================================================== ---- Makefile.in (revision 1799) -+++ Makefile.in (working copy) -@@ -107,6 +107,7 @@ - bin/patest_wire \ - bin/patest_write_sine \ - bin/patest_write_sine_nonint \ -+ bin/patest_update_available_device_list \ - bin/pa_devs \ - bin/pa_fuzz \ - bin/pa_minlat -@@ -146,6 +147,7 @@ - src/hostapi/wdmks \ - src/hostapi/wmme \ - src/os/unix \ -+ src/os/mac_osx \ - src/os/win - - SUBDIRS = -Index: configure.in -=================================================================== ---- configure.in (revision 1799) -+++ configure.in (working copy) -@@ -216,7 +216,7 @@ - fi - SHARED_FLAGS="$LIBS -dynamiclib $mac_arches $mac_sysroot $mac_version_min" - CFLAGS="-std=c99 $CFLAGS $mac_arches $mac_sysroot $mac_version_min" -- OTHER_OBJS="src/os/unix/pa_unix_hostapis.o src/os/unix/pa_unix_util.o src/hostapi/coreaudio/pa_mac_core.o src/hostapi/coreaudio/pa_mac_core_utilities.o src/hostapi/coreaudio/pa_mac_core_blocking.o src/common/pa_ringbuffer.o" -+ OTHER_OBJS="src/os/unix/pa_unix_hostapis.o src/os/unix/pa_unix_util.o src/hostapi/coreaudio/pa_mac_core.o src/hostapi/coreaudio/pa_mac_core_utilities.o src/hostapi/coreaudio/pa_mac_core_blocking.o src/common/pa_ringbuffer.o src/os/mac_osx/pa_osx_hotplug.o" - PADLL="libportaudio.dylib" - ;; - -@@ -230,7 +230,7 @@ - - if [[ "x$with_directx" = "xyes" ]]; then - DXDIR="$with_dxdir" -- add_objects src/hostapi/dsound/pa_win_ds.o src/hostapi/dsound/pa_win_ds_dynlink.o src/os/win/pa_win_hostapis.o src/os/win/pa_win_util.o src/os/win/pa_win_waveformat.o -+ add_objects src/hostapi/dsound/pa_win_ds.o src/hostapi/dsound/pa_win_ds_dynlink.o src/os/win/pa_win_hostapis.o src/os/win/pa_win_util.o src/os/win/pa_win_waveformat.o src/os/win/pa_win_hotplug.o - LIBS="-lwinmm -lm -ldsound -lole32" - DLL_LIBS="${DLL_LIBS} -lwinmm -lm -L$DXDIR/lib -ldsound -lole32" - #VC98="\"/c/Program Files/Microsoft Visual Studio/VC98/Include\"" -@@ -249,7 +249,7 @@ - - if [[ "x$with_wdmks" = "xyes" ]]; then - DXDIR="$with_dxdir" -- add_objects src/hostapi/wdmks/pa_win_wdmks.o src/os/win/pa_win_hostapis.o src/os/win/pa_win_util.o -+ add_objects src/hostapi/wdmks/pa_win_wdmks.o src/os/win/pa_win_hostapis.o src/os/win/pa_win_util.o src/os/win/pa_win_wdmks_utils.o - LIBS="-lwinmm -lm -luuid -lsetupapi -lole32" - DLL_LIBS="${DLL_LIBS} -lwinmm -lm -L$DXDIR/lib -luuid -lsetupapi -lole32" - #VC98="\"/c/Program Files/Microsoft Visual Studio/VC98/Include\"" -@@ -319,7 +319,7 @@ - if [[ "$have_alsa" = "yes" ] && [ "$with_alsa" != "no" ]] ; then - DLL_LIBS="$DLL_LIBS -lasound" - LIBS="$LIBS -lasound" -- OTHER_OBJS="$OTHER_OBJS src/hostapi/alsa/pa_linux_alsa.o" -+ OTHER_OBJS="$OTHER_OBJS src/hostapi/alsa/pa_linux_alsa.o src/os/unix/pa_linux_hotplug.o" - INCLUDES="$INCLUDES pa_linux_alsa.h" - AC_DEFINE(PA_USE_ALSA) - fi -Index: src/os/unix/pa_linux_hotplug.c -=================================================================== ---- src/os/unix/pa_linux_hotplug.c (revision 0) -+++ src/os/unix/pa_linux_hotplug.c (revision 0) -@@ -0,0 +1,147 @@ -+ -+#include "pa_util.h" -+#include "pa_debugprint.h" -+#include "pa_allocation.h" -+#include "pa_linux_alsa.h" -+#include "pa_hostapi.h" -+ -+#include -+ -+#include -+#include -+#include -+ -+/* Implemented in pa_front.c -+ @param first 0 = unknown, 1 = insertion, 2 = removal -+ @param second Host specific device change info (in windows it is the (unicode) device path) -+*/ -+extern void PaUtil_DevicesChanged(unsigned, void*); -+ -+static pthread_t g_thread_id; -+static pthread_mutex_t g_mutex; -+static volatile sig_atomic_t g_run = 0; -+ -+static int device_list_size(void) -+{ -+ snd_ctl_t *handle; -+ int card, err, dev, idx; -+ int nb = 0; -+ snd_ctl_card_info_t *info; -+ snd_pcm_info_t *pcminfo; -+ snd_ctl_card_info_alloca(&info); -+ snd_pcm_info_alloca(&pcminfo); -+ -+ card = -1; -+ if (snd_card_next(&card) < 0 || card < 0) -+ { -+ return nb; -+ } -+ -+ while (card >= 0) -+ { -+ char name[32]; -+ -+ sprintf(name, "hw:%d", card); -+ if ((err = snd_ctl_open(&handle, name, 0)) < 0) -+ { -+ goto next_card; -+ } -+ if ((err = snd_ctl_card_info(handle, info)) < 0) -+ { -+ snd_ctl_close(handle); -+ goto next_card; -+ } -+ dev = -1; -+ while (1) -+ { -+ unsigned int count; -+ int hasPlayback = 0; -+ int hasCapture = 0; -+ -+ snd_ctl_pcm_next_device(handle, &dev); -+ -+ if (dev < 0) -+ break; -+ snd_pcm_info_set_device(pcminfo, dev); -+ snd_pcm_info_set_subdevice(pcminfo, 0); -+ snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_CAPTURE); -+ if ((err = snd_ctl_pcm_info(handle, pcminfo)) >= 0) -+ { -+ hasCapture = 1; -+ } -+ -+ snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_PLAYBACK); -+ if ((err = snd_ctl_pcm_info(handle, pcminfo)) >= 0) -+ { -+ hasPlayback = 1; -+ -+ count = snd_pcm_info_get_subdevices_count(pcminfo); -+ } -+ -+ if(hasPlayback == 0 && hasCapture == 0) -+ continue; -+ -+ nb++; -+ } -+ snd_ctl_close(handle); -+next_card: -+ if (snd_card_next(&card) < 0) -+ { -+ break; -+ } -+ } -+ return nb; -+} -+ -+static void* thread_fcn(void* data) -+{ -+ int currentDevices = 0; -+ -+ currentDevices = device_list_size(); -+ -+ while(g_run) -+ { -+ int count = 0; -+ -+ sleep(1); -+ count = device_list_size(); -+ if(count != currentDevices) -+ { -+ /* 1 = add device, 2 = remove device */ -+ int add = (count > currentDevices) ? 1 : 2; -+ -+ currentDevices = count; -+ -+ PaUtil_DevicesChanged(add, NULL); -+ } -+ } -+ -+ return NULL; -+} -+ -+void PaUtil_InitializeHotPlug() -+{ -+ pthread_mutex_init(&g_mutex, NULL); -+ g_run = 1; -+ pthread_create(&g_thread_id, NULL, thread_fcn, NULL); -+} -+ -+void PaUtil_TerminateHotPlug() -+{ -+ void* ret = NULL; -+ -+ g_run = 0; -+ pthread_join(g_thread_id, &ret); -+ pthread_mutex_destroy(&g_mutex); -+} -+ -+void PaUtil_LockHotPlug() -+{ -+ pthread_mutex_lock(&g_mutex); -+} -+ -+void PaUtil_UnlockHotPlug() -+{ -+ pthread_mutex_unlock(&g_mutex); -+} -+ -Index: src/os/mac_osx/pa_osx_hotplug.c -=================================================================== ---- src/os/mac_osx/pa_osx_hotplug.c (revision 0) -+++ src/os/mac_osx/pa_osx_hotplug.c (revision 0) -@@ -0,0 +1,76 @@ -+ -+#include "pa_util.h" -+#include "pa_debugprint.h" -+#include "pa_allocation.h" -+ -+#include -+ -+#include -+#include -+ -+/* Implemented in pa_front.c -+ @param first 0 = unknown, 1 = insertion, 2 = removal -+ @param second Host specific device change info (in windows it is the (unicode) device path) -+*/ -+extern void PaUtil_DevicesChanged(unsigned, void*); -+ -+/* Callback for audio hardware property changes. */ -+static OSStatus audioPropertyCallback(AudioHardwarePropertyID inPropertyID, -+ void *refCon) -+{ -+ (void)refCon; -+ switch (inPropertyID) -+ { -+ /* -+ * These are the other types of notifications we might receive, however, they are beyond -+ * the scope of this sample and we ignore them. -+ */ -+ case kAudioHardwarePropertyDefaultInputDevice: -+ PA_DEBUG(("audioPropertyCallback: default input device changed\n")); -+ break; -+ case kAudioHardwarePropertyDefaultOutputDevice: -+ PA_DEBUG(("audioPropertyCallback: default output device changed\n")); -+ break; -+ case kAudioHardwarePropertyDefaultSystemOutputDevice: -+ PA_DEBUG(("audioPropertyCallback: default system output device changed\n")); -+ break; -+ case kAudioHardwarePropertyDevices: -+ PA_DEBUG(("audioPropertyCallback: device list changed\n")); -+ PaUtil_DevicesChanged(1, NULL); -+ break; -+ default: -+ PA_DEBUG(("audioPropertyCallback: unknown message id=%08lx\n", inPropertyID)); -+ break; -+ } -+ -+ return noErr; -+} -+ -+void PaUtil_InitializeHotPlug() -+{ -+ AudioHardwareAddPropertyListener(kAudioHardwarePropertyDevices, -+ audioPropertyCallback, NULL); -+ AudioHardwareAddPropertyListener(kAudioHardwarePropertyDefaultInputDevice, -+ audioPropertyCallback, NULL); -+ AudioHardwareAddPropertyListener(kAudioHardwarePropertyDefaultOutputDevice, -+ audioPropertyCallback, NULL); -+} -+ -+void PaUtil_TerminateHotPlug() -+{ -+ AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDevices, -+ audioPropertyCallback); -+ AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDefaultInputDevice, -+ audioPropertyCallback); -+ AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDefaultOutputDevice, -+ audioPropertyCallback); -+} -+ -+void PaUtil_LockHotPlug() -+{ -+} -+ -+void PaUtil_UnlockHotPlug() -+{ -+} -+ -Index: src/os/win/pa_win_hotplug.c -=================================================================== ---- src/os/win/pa_win_hotplug.c (revision 1799) -+++ src/os/win/pa_win_hotplug.c (working copy) -@@ -186,6 +186,8 @@ - { - PA_DEBUG(("Device inserted : %S\n", ptr->dbcc_name)); - InsertDeviceIntoCache(pInfo, ptr->dbcc_name); -+ /* yield some seconds because added device may not be completely configured */ -+ Sleep(2000); - PaUtil_DevicesChanged(1, ptr->dbcc_name); - } - } -@@ -202,6 +204,7 @@ - if (RemoveDeviceFromCache(pInfo, ptr->dbcc_name)) - { - PA_DEBUG(("Device removed : %S\n", ptr->dbcc_name)); -+ Sleep(2000); - PaUtil_DevicesChanged(2, ptr->dbcc_name); - } - } -Index: src/os/win/pa_win_hostapis.c -=================================================================== ---- src/os/win/pa_win_hostapis.c (revision 1799) -+++ src/os/win/pa_win_hostapis.c (working copy) -@@ -81,7 +81,7 @@ - #endif - - #if PA_USE_WDMKS -- PaWinWdm_Initialize, -+ //PaWinWdm_Initialize, - #endif - - #if PA_USE_SKELETON -Index: src/hostapi/alsa/pa_linux_alsa.c -=================================================================== ---- src/hostapi/alsa/pa_linux_alsa.c (revision 1799) -+++ src/hostapi/alsa/pa_linux_alsa.c (working copy) -@@ -668,6 +668,15 @@ - } - PaAlsaDeviceInfo; - -+/* used for tranferring device infos during scanning / rescanning */ -+typedef struct PaLinuxScanDeviceInfosResults -+{ -+ PaDeviceInfo **deviceInfos; -+ PaDeviceIndex defaultInputDevice; -+ PaDeviceIndex defaultOutputDevice; -+ int deviceCount; -+} PaLinuxScanDeviceInfosResults; -+ - /* prototypes for functions declared in this file */ - - static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); -@@ -692,10 +701,17 @@ - static PaError IsStreamActive( PaStream *stream ); - static PaTime GetStreamTime( PaStream *stream ); - static double GetStreamCpuLoad( PaStream* stream ); --static PaError BuildDeviceList( PaAlsaHostApiRepresentation *hostApi ); -+static PaError BuildDeviceList( PaAlsaHostApiRepresentation *hostApi, void** scanResults, int* deviceCount ); - static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate ); - static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate ); - -+static PaError ScanDeviceInfos(struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, -+ void **newDeviceInfos, int *newDeviceCount ); -+static PaError CommitDeviceInfos(struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, -+ void *deviceInfos, int deviceCount); -+static PaError DisposeDeviceInfos(struct PaUtilHostApiRepresentation *hostApi, void *deviceInfos, -+ int deviceCount ); -+ - /* Callback prototypes */ - static void *CallbackThreadFunc( void *userData ); - -@@ -722,6 +738,8 @@ - { - PaError result = paNoError; - PaAlsaHostApiRepresentation *alsaHostApi = NULL; -+ void* scanResults = NULL; -+ int deviceCount = 0; - - /* Try loading Alsa library. */ - if (!PaAlsa_LoadLibrary()) -@@ -733,20 +751,29 @@ - alsaHostApi->hostApiIndex = hostApiIndex; - - *hostApi = (PaUtilHostApiRepresentation*)alsaHostApi; -+ (*hostApi)->deviceInfos = NULL; - (*hostApi)->info.structVersion = 1; - (*hostApi)->info.type = paALSA; - (*hostApi)->info.name = "ALSA"; - -+ (*hostApi)->ScanDeviceInfos = NULL; -+ (*hostApi)->CommitDeviceInfos = NULL; -+ (*hostApi)->DisposeDeviceInfos = NULL; - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; -+ (*hostApi)->ScanDeviceInfos = ScanDeviceInfos; -+ (*hostApi)->CommitDeviceInfos = CommitDeviceInfos; -+ (*hostApi)->DisposeDeviceInfos = DisposeDeviceInfos; - - /** If AlsaErrorHandler is to be used, do not forget to unregister callback pointer in - Terminate function. - */ - /*ENSURE_( snd_lib_error_set_handler(AlsaErrorHandler), paUnanticipatedHostError );*/ - -- PA_ENSURE( BuildDeviceList( alsaHostApi ) ); -+ ScanDeviceInfos(&alsaHostApi->baseHostApiRep, hostApiIndex, &scanResults, -+ &deviceCount); -+ CommitDeviceInfos(&alsaHostApi->baseHostApiRep, hostApiIndex, scanResults, deviceCount); - - PaUtil_InitializeStreamInterface( &alsaHostApi->callbackStreamInterface, - CloseStream, StartStream, -@@ -1075,7 +1105,7 @@ - } - - static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* deviceName, int blocking, -- PaAlsaDeviceInfo* devInfo, int* devIdx ) -+ PaAlsaDeviceInfo* devInfo, int* devIdx, PaLinuxScanDeviceInfosResults* out ) - { - PaError result = 0; - PaDeviceInfo *baseDeviceInfo = &devInfo->baseDeviceInfo; -@@ -1128,20 +1158,20 @@ - if( baseDeviceInfo->maxInputChannels > 0 || baseDeviceInfo->maxOutputChannels > 0 ) - { - /* Make device default if there isn't already one or it is the ALSA "default" device */ -- if( (baseApi->info.defaultInputDevice == paNoDevice || !strcmp(deviceName->alsaName, -+ if( (out->defaultInputDevice == paNoDevice || !strcmp(deviceName->alsaName, - "default" )) && baseDeviceInfo->maxInputChannels > 0 ) - { -- baseApi->info.defaultInputDevice = *devIdx; -+ out->defaultInputDevice = *devIdx; - PA_DEBUG(("Default input device: %s\n", deviceName->name)); - } -- if( (baseApi->info.defaultOutputDevice == paNoDevice || !strcmp(deviceName->alsaName, -+ if( (out->defaultOutputDevice == paNoDevice || !strcmp(deviceName->alsaName, - "default" )) && baseDeviceInfo->maxOutputChannels > 0 ) - { -- baseApi->info.defaultOutputDevice = *devIdx; -+ out->defaultOutputDevice = *devIdx; - PA_DEBUG(("Default output device: %s\n", deviceName->name)); - } - PA_DEBUG(("%s: Adding device %s: %d\n", __FUNCTION__, deviceName->name, *devIdx)); -- baseApi->deviceInfos[*devIdx] = (PaDeviceInfo *) devInfo; -+ out->deviceInfos[*devIdx] = (PaDeviceInfo *) devInfo; - (*devIdx) += 1; - } - else -@@ -1154,9 +1184,10 @@ - } - - /* Build PaDeviceInfo list, ignore devices for which we cannot determine capabilities (possibly busy, sigh) */ --static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) -+static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi, void** scanResults, int* count) - { - PaUtilHostApiRepresentation *baseApi = &alsaApi->baseHostApiRep; -+ PaLinuxScanDeviceInfosResults *outArgument = NULL; - PaAlsaDeviceInfo *deviceInfoArray; - int cardIdx = -1, devIdx = 0; - snd_ctl_card_info_t *cardInfo; -@@ -1171,13 +1202,11 @@ - #ifdef PA_ENABLE_DEBUG_OUTPUT - PaTime startTime = PaUtil_GetTime(); - #endif -+ PaLinuxScanDeviceInfosResults* out = NULL; - - if( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) && atoi( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) ) ) - blocking = 0; - -- /* These two will be set to the first working input and output device, respectively */ -- baseApi->info.defaultInputDevice = paNoDevice; -- baseApi->info.defaultOutputDevice = paNoDevice; - - /* Gather info about hw devices - -@@ -1341,8 +1370,14 @@ - else - PA_DEBUG(( "%s: Iterating over ALSA plugins failed: %s\n", __FUNCTION__, alsa_snd_strerror( res ) )); - -+ out = (PaLinuxScanDeviceInfosResults *) PaUtil_GroupAllocateMemory( -+ alsaApi->allocations, sizeof(PaLinuxScanDeviceInfosResults) ); -+ -+ out->defaultInputDevice = paNoDevice; -+ out->defaultOutputDevice = paNoDevice; -+ - /* allocate deviceInfo memory based on the number of devices */ -- PA_UNLESS( baseApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( -+ PA_UNLESS( out->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( - alsaApi->allocations, sizeof(PaDeviceInfo*) * (numDeviceNames) ), paInsufficientMemory ); - - /* allocate all device info structs in a contiguous block */ -@@ -1367,7 +1402,7 @@ - continue; - } - -- PA_ENSURE( FillInDevInfo( alsaApi, hwInfo, blocking, devInfo, &devIdx ) ); -+ PA_ENSURE( FillInDevInfo( alsaApi, hwInfo, blocking, devInfo, &devIdx, out ) ); - } - assert( devIdx < numDeviceNames ); - /* Now inspect 'dmix' and 'default' plugins */ -@@ -1381,11 +1416,13 @@ - } - - PA_ENSURE( FillInDevInfo( alsaApi, hwInfo, blocking, devInfo, -- &devIdx ) ); -+ &devIdx, out ) ); - } - free( hwDevInfos ); - -- baseApi->info.deviceCount = devIdx; /* Number of successfully queried devices */ -+ out->deviceCount = devIdx; /* Number of successfully queried devices */ -+ *scanResults = out; -+ *count = out->deviceCount; - - #ifdef PA_ENABLE_DEBUG_OUTPUT - PA_DEBUG(( "%s: Building device list took %f seconds\n", __FUNCTION__, PaUtil_GetTime() - startTime )); -@@ -3693,6 +3754,10 @@ - framesAvail, &xrun ) ); - if( xrun ) - { -+ if(*framesAvail == 0) -+ { -+ result = paInternalError; -+ } - goto end; - } - -@@ -3740,6 +3805,7 @@ - Pa_Sleep( 1 ); /* avoid hot loop */ - continue; - } -+ result = paInternalError; - - /* TODO: Add macro for checking system calls */ - PA_ENSURE( paInternalError ); -@@ -3769,6 +3835,7 @@ - xrun = 1; /* try recovering device */ - - PA_DEBUG(( "%s: poll timed out\n", __FUNCTION__, timeouts )); -+ result = paTimedOut; - goto end;/*PA_ENSURE( paTimedOut );*/ - } - } -@@ -4546,3 +4628,75 @@ - busyRetries_ = retries; - return paNoError; - } -+ -+static PaError ScanDeviceInfos(struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex hostApiIndex, -+ void **scanResults, int *newDeviceCount) -+{ -+ PaAlsaHostApiRepresentation* alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi; -+ PaError result = paNoError; -+ PA_ENSURE(BuildDeviceList( alsaHostApi, scanResults, newDeviceCount )); -+ -+ return paNoError; -+ -+error: -+ return result; -+} -+ -+static PaError CommitDeviceInfos(struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, -+ void *scanResults, int deviceCount) -+{ -+ PaAlsaHostApiRepresentation* alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi; -+ PaError result = paNoError; -+ -+ /* These two will be set to the first working input and output device, respectively */ -+ hostApi->info.defaultInputDevice = paNoDevice; -+ hostApi->info.defaultOutputDevice = paNoDevice; -+ -+ /* Free any old memory which might be in the device info */ -+ if( hostApi->deviceInfos ) -+ { -+ /* all device info structs are allocated in a block so we can destroy them here */ -+ PaUtil_GroupFreeMemory( alsaHostApi->allocations, hostApi->deviceInfos[0] ); -+ PaUtil_GroupFreeMemory( alsaHostApi->allocations, hostApi->deviceInfos ); -+ hostApi->deviceInfos = NULL; -+ } -+ -+ if( scanResults != NULL ) -+ { -+ PaLinuxScanDeviceInfosResults *scanDeviceInfosResults = ( PaLinuxScanDeviceInfosResults * ) scanResults; -+ -+ if( deviceCount > 0 ) -+ { -+ /* use the array allocated in ScanDeviceInfos() as our deviceInfos */ -+ hostApi->deviceInfos = scanDeviceInfosResults->deviceInfos; -+ hostApi->info.defaultInputDevice = scanDeviceInfosResults->defaultInputDevice; -+ hostApi->info.defaultOutputDevice = scanDeviceInfosResults->defaultOutputDevice; -+ hostApi->info.deviceCount = deviceCount; -+ } -+ -+ PaUtil_GroupFreeMemory( alsaHostApi->allocations, scanDeviceInfosResults ); -+ } -+ -+ return result; -+} -+ -+static PaError DisposeDeviceInfos(struct PaUtilHostApiRepresentation *hostApi, void *scanResults, int deviceCount) -+{ -+ PaAlsaHostApiRepresentation *alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi; -+ -+ if( scanResults != NULL ) -+ { -+ PaLinuxScanDeviceInfosResults *scanDeviceInfosResults = ( PaLinuxScanDeviceInfosResults * ) scanResults; -+ if( scanDeviceInfosResults->deviceInfos ) -+ { -+ /* all device info structs are allocated in a block so we can destroy them here */ -+ PaUtil_GroupFreeMemory( alsaHostApi->allocations, scanDeviceInfosResults->deviceInfos[0] ); -+ PaUtil_GroupFreeMemory( alsaHostApi->allocations, scanDeviceInfosResults->deviceInfos ); -+ } -+ -+ PaUtil_GroupFreeMemory(alsaHostApi->allocations, scanDeviceInfosResults ); -+ } -+ -+ return paNoError; -+} -+ -Index: src/hostapi/oss/pa_unix_oss.c -=================================================================== ---- src/hostapi/oss/pa_unix_oss.c (revision 1799) -+++ src/hostapi/oss/pa_unix_oss.c (working copy) -@@ -256,6 +256,9 @@ - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; -+ (*hostApi)->ScanDeviceInfos = NULL; -+ (*hostApi)->CommitDeviceInfos = NULL; -+ (*hostApi)->DisposeDeviceInfos = NULL; - - PA_ENSURE( BuildDeviceList( ossHostApi ) ); - -Index: src/hostapi/skeleton/pa_hostapi_skeleton.c -=================================================================== ---- src/hostapi/skeleton/pa_hostapi_skeleton.c (revision 1799) -+++ src/hostapi/skeleton/pa_hostapi_skeleton.c (working copy) -@@ -206,9 +206,15 @@ - } - } - -+ (*hostApi)->ScanDeviceInfos = NULL; -+ (*hostApi)->CommitDeviceInfos = NULL; -+ (*hostApi)->DisposeDeviceInfos = NULL; - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; -+ (*hostApi)->ScanDeviceInfos = NULL; -+ (*hostApi)->CommitDeviceInfos = NULL; -+ (*hostApi)->DisposeDeviceInfos = NULL; - - PaUtil_InitializeStreamInterface( &skeletonHostApi->callbackStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, -Index: src/hostapi/wasapi/pa_win_wasapi.c -=================================================================== ---- src/hostapi/wasapi/pa_win_wasapi.c (revision 1799) -+++ src/hostapi/wasapi/pa_win_wasapi.c (working copy) -@@ -1386,6 +1386,9 @@ - } - } - -+ (*hostApi)->ScanDeviceInfos = NULL; -+ (*hostApi)->CommitDeviceInfos = NULL; -+ (*hostApi)->DisposeDeviceInfos = NULL; - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; -Index: src/hostapi/wdmks/pa_win_wdmks.c -=================================================================== ---- src/hostapi/wdmks/pa_win_wdmks.c (revision 1799) -+++ src/hostapi/wdmks/pa_win_wdmks.c (working copy) -@@ -1885,6 +1885,9 @@ - - (*hostApi)->info.deviceCount = deviceCount; - -+ (*hostApi)->ScanDeviceInfos = NULL; -+ (*hostApi)->CommitDeviceInfos = NULL; -+ (*hostApi)->DisposeDeviceInfos = NULL; - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; -@@ -3305,4 +3308,4 @@ - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - PA_LOGL_; - return 0; --} -\ No newline at end of file -+} -Index: src/hostapi/jack/pa_jack.c -=================================================================== ---- src/hostapi/jack/pa_jack.c (revision 1799) -+++ src/hostapi/jack/pa_jack.c (working copy) -@@ -749,6 +749,9 @@ - - /* Register functions */ - -+ (*hostApi)->ScanDeviceInfos = NULL; -+ (*hostApi)->CommitDeviceInfos = NULL; -+ (*hostApi)->DisposeDeviceInfos = NULL; - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; -Index: src/hostapi/coreaudio/pa_mac_core.c -=================================================================== ---- src/hostapi/coreaudio/pa_mac_core.c (revision 1799) -+++ src/hostapi/coreaudio/pa_mac_core.c (working copy) -@@ -80,7 +80,28 @@ - /* prototypes for functions declared in this file */ - - PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); -+static PaError ScanDeviceInfos(struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, -+ void **newDeviceInfos, int *newDeviceCount ); -+static PaError CommitDeviceInfos(struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, -+ void *deviceInfos, int deviceCount); -+static PaError DisposeDeviceInfos(struct PaUtilHostApiRepresentation *hostApi, void *deviceInfos, -+ int deviceCount ); - -+/* structures */ -+ -+/* used for tranferring device infos during scanning / rescanning */ -+typedef struct PaMacScanDeviceInfosResults -+{ -+ PaDeviceInfo **deviceInfos; -+ PaDeviceIndex defaultInputDevice; -+ PaDeviceIndex defaultOutputDevice; -+ -+ AudioDeviceID* devIds; -+ int devCount; -+ AudioDeviceID devInputDevice; -+ AudioDeviceID devOutputDevice; -+} PaMacScanDeviceInfosResults; -+ - /* - * Function declared in pa_mac_core.h. Sets up a PaMacCoreStreamInfoStruct - * with the requested flags and initializes channel map. -@@ -132,7 +153,6 @@ - return NULL; - PaMacAUHAL *macCoreHostApi = (PaMacAUHAL*)hostApi; - AudioDeviceID hostApiDevice = macCoreHostApi->devIds[device]; -- - UInt32 size = 0; - - error = AudioDeviceGetPropertyInfo( hostApiDevice, -@@ -311,46 +331,57 @@ - } - - --/*currently, this is only used in initialization, but it might be modified -- to be used when the list of devices changes.*/ --static PaError gatherDeviceInfo(PaMacAUHAL *auhalHostApi) -+//static PaError gatherDeviceInfo(PaMacAUHAL *auhalHostApi) -+static PaError gatherDeviceInfo(PaMacAUHAL* auhalHostApi, void** scanResults, int* count) - { -- UInt32 size; -- UInt32 propsize; -+ UInt32 propsize = 0; -+ UInt32 size = sizeof(AudioDeviceID); -+ PaMacScanDeviceInfosResults *outArgument = NULL; -+ PaError result = paNoError; -+ - VVDBUG(("gatherDeviceInfo()\n")); -- /* -- free any previous allocations -- */ -- if( auhalHostApi->devIds ) -- PaUtil_GroupFreeMemory(auhalHostApi->allocations, auhalHostApi->devIds); -- auhalHostApi->devIds = NULL; -- - /* -- figure out how many devices there are -- */ - AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices, - &propsize, - NULL ); -- auhalHostApi->devCount = propsize / sizeof( AudioDeviceID ); -+ *count = (propsize / sizeof(AudioDeviceID)); - -- VDBUG( ( "Found %ld device(s).\n", auhalHostApi->devCount ) ); -+ VDBUG( ( "Found %ld device(s).\n", *count ) ); - -+ if(*count == 0) -+ return paNoError; -+ -+ /* Allocate the out param for all the info we need */ -+ outArgument = (PaMacScanDeviceInfosResults *) PaUtil_GroupAllocateMemory( -+ auhalHostApi->allocations, sizeof(PaMacScanDeviceInfosResults) ); -+ -+ if( !outArgument ) -+ { -+ result = paInsufficientMemory; -+ return result; -+ } -+ -+ outArgument->devCount = *count; -+ - /* -- copy the device IDs -- */ -- auhalHostApi->devIds = (AudioDeviceID *)PaUtil_GroupAllocateMemory( -+ outArgument->devIds = (AudioDeviceID *)PaUtil_GroupAllocateMemory( - auhalHostApi->allocations, - propsize ); -- if( !auhalHostApi->devIds ) -+ if( !outArgument->devIds ) - return paInsufficientMemory; - AudioHardwareGetProperty( kAudioHardwarePropertyDevices, - &propsize, -- auhalHostApi->devIds ); -+ outArgument->devIds ); - #ifdef MAC_CORE_VERBOSE_DEBUG - { - int i; -- for( i=0; idevCount; ++i ) -- printf( "Device %d\t: %ld\n", i, auhalHostApi->devIds[i] ); -+ for( i=0; idevCount; ++i ) -+ printf( "Device %d\t: %ld\n", i, outArgument->devIds[i] ); - } - #endif - -- size = sizeof(AudioDeviceID); -- auhalHostApi->defaultIn = kAudioDeviceUnknown; -- auhalHostApi->defaultOut = kAudioDeviceUnknown; -+ outArgument->devInputDevice = kAudioDeviceUnknown; -+ outArgument->devOutputDevice = kAudioDeviceUnknown; - - /* determine the default device. */ - /* I am not sure how these calls to AudioHardwareGetProperty() -@@ -358,42 +389,44 @@ - device as the default. */ - if( 0 != AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, - &size, -- &auhalHostApi->defaultIn) ) { -+ &outArgument->devInputDevice) ) { - int i; -- auhalHostApi->defaultIn = kAudioDeviceUnknown; -+ outArgument->devInputDevice = kAudioDeviceUnknown; - VDBUG(("Failed to get default input device from OS.")); - VDBUG((" I will substitute the first available input Device.")); -- for( i=0; idevCount; ++i ) { -+ -+ for( i=0; i< outArgument->devCount; ++i ) { - PaDeviceInfo devInfo; - if( 0 != GetChannelInfo( auhalHostApi, &devInfo, -- auhalHostApi->devIds[i], TRUE ) ) -+ outArgument->devIds[i], TRUE ) ) - if( devInfo.maxInputChannels ) { -- auhalHostApi->defaultIn = auhalHostApi->devIds[i]; -+ outArgument->devInputDevice = outArgument->devIds[i]; - break; - } - } - } - if( 0 != AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, - &size, -- &auhalHostApi->defaultOut) ) { -+ &outArgument->devOutputDevice) ) { - int i; -- auhalHostApi->defaultIn = kAudioDeviceUnknown; -+ outArgument->devOutputDevice = kAudioDeviceUnknown; - VDBUG(("Failed to get default output device from OS.")); - VDBUG((" I will substitute the first available output Device.")); -- for( i=0; idevCount; ++i ) { -+ for( i=0; idevCount; ++i ) { - PaDeviceInfo devInfo; - if( 0 != GetChannelInfo( auhalHostApi, &devInfo, -- auhalHostApi->devIds[i], FALSE ) ) -+ outArgument->devIds[i], FALSE ) ) - if( devInfo.maxOutputChannels ) { -- auhalHostApi->defaultOut = auhalHostApi->devIds[i]; -+ outArgument->devOutputDevice = outArgument->devIds[i]; - break; - } - } - } - -- VDBUG( ( "Default in : %ld\n", auhalHostApi->defaultIn ) ); -- VDBUG( ( "Default out: %ld\n", auhalHostApi->defaultOut ) ); -+ VDBUG( ( "Default in : %ld\n", outArgument->devInputDevice ) ); -+ VDBUG( ( "Default out: %ld\n", outArgument->devOutputDevice ) ); - -+ *scanResults = outArgument; - return paNoError; - } - -@@ -402,9 +435,9 @@ - AudioDeviceID macCoreDeviceId, - int isInput) - { -- UInt32 propSize; -+ UInt32 propSize = 0; - PaError err = paNoError; -- UInt32 i; -+ UInt32 i = 0; - int numChannels = 0; - AudioBufferList *buflist = NULL; - UInt32 frameLatency; -@@ -528,10 +561,10 @@ - PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) - { - PaError result = paNoError; -- int i; - PaMacAUHAL *auhalHostApi = NULL; -- PaDeviceInfo *deviceInfoArray; - int unixErr; -+ void* scanResults = NULL; -+ int deviceCount = 0; - - VVDBUG(("PaMacCore_Initialize(): hostApiIndex=%d\n", hostApiIndex)); - -@@ -572,65 +605,29 @@ - auhalHostApi->devIds = NULL; - auhalHostApi->devCount = 0; - -- /* get the info we need about the devices */ -- result = gatherDeviceInfo( auhalHostApi ); -- if( result != paNoError ) -- goto error; -- - *hostApi = &auhalHostApi->inheritedHostApiRep; - (*hostApi)->info.structVersion = 1; - (*hostApi)->info.type = paCoreAudio; - (*hostApi)->info.name = "Core Audio"; -- -+ -+ (*hostApi)->deviceInfos = NULL; -+ - (*hostApi)->info.defaultInputDevice = paNoDevice; - (*hostApi)->info.defaultOutputDevice = paNoDevice; -- - (*hostApi)->info.deviceCount = 0; - -- if( auhalHostApi->devCount > 0 ) -- { -- (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( -- auhalHostApi->allocations, sizeof(PaDeviceInfo*) * auhalHostApi->devCount); -- if( !(*hostApi)->deviceInfos ) -- { -- result = paInsufficientMemory; -- goto error; -- } -+ (*hostApi)->ScanDeviceInfos = ScanDeviceInfos; -+ (*hostApi)->CommitDeviceInfos = CommitDeviceInfos; -+ (*hostApi)->DisposeDeviceInfos = DisposeDeviceInfos; - -- /* allocate all device info structs in a contiguous block */ -- deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory( -- auhalHostApi->allocations, sizeof(PaDeviceInfo) * auhalHostApi->devCount ); -- if( !deviceInfoArray ) -- { -- result = paInsufficientMemory; -- goto error; -- } -+ result = ScanDeviceInfos(&auhalHostApi->inheritedHostApiRep, hostApiIndex, &scanResults, -+ &deviceCount); - -- for( i=0; i < auhalHostApi->devCount; ++i ) -- { -- int err; -- err = InitializeDeviceInfo( auhalHostApi, &deviceInfoArray[i], -- auhalHostApi->devIds[i], -- hostApiIndex ); -- if (err == paNoError) -- { /* copy some info and set the defaults */ -- (*hostApi)->deviceInfos[(*hostApi)->info.deviceCount] = &deviceInfoArray[i]; -- if (auhalHostApi->devIds[i] == auhalHostApi->defaultIn) -- (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount; -- if (auhalHostApi->devIds[i] == auhalHostApi->defaultOut) -- (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount; -- (*hostApi)->info.deviceCount++; -- } -- else -- { /* there was an error. we need to shift the devices down, so we ignore this one */ -- int j; -- auhalHostApi->devCount--; -- for( j=i; jdevCount; ++j ) -- auhalHostApi->devIds[j] = auhalHostApi->devIds[j+1]; -- i--; -- } -- } -- } -+ if(result != paNoError) -+ goto error; -+ -+ /* FIXME for now we ignore the result of CommitDeviceInfos(), it should probably be an atomic non-failing operation */ -+ CommitDeviceInfos( &auhalHostApi->inheritedHostApiRep, hostApiIndex, scanResults, deviceCount ); - - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; -@@ -2468,3 +2465,148 @@ - - return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); - } -+ -+static PaError ScanDeviceInfos(struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex hostApiIndex, -+ void **scanResults, int *newDeviceCount) -+{ -+ PaMacAUHAL* auhalHostApi = (PaMacAUHAL*)hostApi; -+ PaDeviceInfo *deviceInfoArray = NULL; -+ PaError result = paNoError; -+ int i = 0; -+ PaMacScanDeviceInfosResults* out = NULL; -+ -+ /* get the info we need about the devices */ -+ result = gatherDeviceInfo( auhalHostApi, scanResults, newDeviceCount ); -+ -+ if( result != paNoError ) -+ return result; -+ -+ out = (PaMacScanDeviceInfosResults*)*scanResults; -+ -+ if( out->devCount > 0 ) -+ { -+ int count = 0; -+ -+ /* allocate array for pointers to PaDeviceInfo structs */ -+ out->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( -+ auhalHostApi->allocations, sizeof(PaDeviceInfo*) * out->devCount); -+ if( !out->deviceInfos ) -+ { -+ result = paInsufficientMemory; -+ return result; -+ } -+ -+ /* allocate all device info structs in a contiguous block */ -+ deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory( -+ auhalHostApi->allocations, sizeof(PaDeviceInfo) * out->devCount ); -+ if( !deviceInfoArray ) -+ { -+ result = paInsufficientMemory; -+ return result; -+ } -+ -+ for( i=0; i < out->devCount; ++i ) -+ { -+ int err; -+ err = InitializeDeviceInfo( auhalHostApi, &deviceInfoArray[i], -+ out->devIds[i], -+ hostApiIndex ); -+ if (err == paNoError) -+ { -+ /* copy some info and set the defaults */ -+ out->deviceInfos[count] = &deviceInfoArray[i]; -+ -+ if (out->devIds[i] == out->devInputDevice) -+ { -+ out->defaultInputDevice = count; -+ } -+ if (out->devIds[i] == out->devOutputDevice) -+ { -+ out->defaultOutputDevice = count; -+ } -+ count++; -+ } -+ else -+ { -+ /* there was an error. we need to shift the devices down, so we ignore this one */ -+ int j; -+ out->devCount--; -+ for( j=i; jdevCount; ++j ) -+ out->devIds[j] = out->devIds[j+1]; -+ i--; -+ } -+ } -+ } -+ *newDeviceCount = out->devCount; -+ -+ return paNoError; -+} -+ -+static PaError CommitDeviceInfos(struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, -+ void *scanResults, int deviceCount) -+{ -+ PaMacAUHAL* auhalHostApi = (PaMacAUHAL*)hostApi; -+ PaError result = paNoError; -+ -+ hostApi->info.deviceCount = 0; -+ hostApi->info.defaultInputDevice = paNoDevice; -+ hostApi->info.defaultOutputDevice = paNoDevice; -+ -+ /* -- free any previous allocations -- */ -+ if( auhalHostApi->devIds ) -+ { -+ PaUtil_GroupFreeMemory(auhalHostApi->allocations, auhalHostApi->devIds); -+ } -+ auhalHostApi->devIds = NULL; -+ -+ /* Free any old memory which might be in the device info */ -+ if( hostApi->deviceInfos ) -+ { -+ PaUtil_GroupFreeMemory( auhalHostApi->allocations, hostApi->deviceInfos[0] ); -+ PaUtil_GroupFreeMemory( auhalHostApi->allocations, hostApi->deviceInfos ); -+ hostApi->deviceInfos = NULL; -+ } -+ -+ if( scanResults != NULL ) -+ { -+ PaMacScanDeviceInfosResults *scanDeviceInfosResults = ( PaMacScanDeviceInfosResults * ) scanResults; -+ -+ if( deviceCount > 0 ) -+ { -+ /* use the array allocated in ScanDeviceInfos() as our deviceInfos */ -+ hostApi->deviceInfos = scanDeviceInfosResults->deviceInfos; -+ hostApi->info.defaultInputDevice = scanDeviceInfosResults->defaultInputDevice; -+ hostApi->info.defaultOutputDevice = scanDeviceInfosResults->defaultOutputDevice; -+ hostApi->info.deviceCount = deviceCount; -+ auhalHostApi->devIds = scanDeviceInfosResults->devIds; -+ auhalHostApi->devCount = scanDeviceInfosResults->devCount; -+ auhalHostApi->defaultIn = scanDeviceInfosResults->devInputDevice; -+ auhalHostApi->defaultOut = scanDeviceInfosResults->devOutputDevice; -+ } -+ -+ PaUtil_GroupFreeMemory( auhalHostApi->allocations, scanDeviceInfosResults ); -+ } -+ -+ return result; -+} -+ -+static PaError DisposeDeviceInfos(struct PaUtilHostApiRepresentation *hostApi, void *scanResults, int deviceCount) -+{ -+ PaMacAUHAL *auhalHostApi = (PaMacAUHAL*)hostApi; -+ -+ if( scanResults != NULL ) -+ { -+ PaMacScanDeviceInfosResults *scanDeviceInfosResults = ( PaMacScanDeviceInfosResults * ) scanResults; -+ if( scanDeviceInfosResults->deviceInfos ) -+ { -+ /* all device info structs are allocated in a block so we can destroy them here */ -+ PaUtil_GroupFreeMemory( auhalHostApi->allocations, scanDeviceInfosResults->deviceInfos[0] ); -+ PaUtil_GroupFreeMemory( auhalHostApi->allocations, scanDeviceInfosResults->deviceInfos ); -+ } -+ -+ PaUtil_GroupFreeMemory(auhalHostApi->allocations, scanDeviceInfosResults ); -+ } -+ -+ return paNoError; -+} -+ -Index: src/hostapi/asio/pa_asio.cpp -=================================================================== ---- src/hostapi/asio/pa_asio.cpp (revision 1799) -+++ src/hostapi/asio/pa_asio.cpp (working copy) -@@ -1315,6 +1315,9 @@ - } - - -+ (*hostApi)->ScanDeviceInfos = NULL; -+ (*hostApi)->CommitDeviceInfos = NULL; -+ (*hostApi)->DisposeDeviceInfos = NULL; - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; -Index: src/common/pa_front.c -=================================================================== ---- src/common/pa_front.c (revision 1799) -+++ src/common/pa_front.c (working copy) -@@ -737,13 +737,12 @@ - for( i = 0 ; i < paInternalInfo_.hostApisCount_ ; ++i ) - { - PaUtilHostApiRepresentation *hostApi = paInternalInfo_.hostApis_[i]; -+ PA_DEBUG(( "Scanning new device list for host api %d.\n",i)); - if( hostApi->ScanDeviceInfos == NULL ) - continue; - -- PA_DEBUG(( "Scanning new device list for host api %d.\n",i)); - if( hostApi->ScanDeviceInfos( hostApi, i, &scanResults[ i ], &deviceCounts[ i ] ) != paNoError ) - break; -- - } - - /* Check the result of the scan operation */ diff --git a/src/net/java/sip/communicator/impl/gui/main/menus/ToolsMenu.java b/src/net/java/sip/communicator/impl/gui/main/menus/ToolsMenu.java index 556f1e6fe..8c8c4e03b 100644 --- a/src/net/java/sip/communicator/impl/gui/main/menus/ToolsMenu.java +++ b/src/net/java/sip/communicator/impl/gui/main/menus/ToolsMenu.java @@ -30,6 +30,8 @@ import net.java.sip.communicator.util.skin.*; import net.java.sip.communicator.util.swing.*; +import org.jitsi.service.configuration.*; +import org.jitsi.service.resources.*; import org.osgi.framework.*; /** @@ -37,7 +39,7 @@ * contains "New account". * * @author Yana Stamcheva - * @author Lubomir Marinov + * @author Lyubomir Marinov * @author Adam Netocny */ public class ToolsMenu @@ -92,16 +94,16 @@ public class ToolsMenu * Creates an instance of FileMenu. * @param parentWindow The parent ChatWindow. */ - public ToolsMenu(MainFrame parentWindow) { - - super(GuiActivator.getResources().getI18NString("service.gui.TOOLS")); + public ToolsMenu(MainFrame parentWindow) + { + ResourceManagementService r = GuiActivator.getResources(); - this.setMnemonic( - GuiActivator.getResources().getI18nMnemonic("service.gui.TOOLS")); + setText(r.getI18NString("service.gui.TOOLS")); + setMnemonic(r.getI18nMnemonic("service.gui.TOOLS")); - this.registerMenuItems(); + registerMenuItems(); - this.initPluginComponents(); + initPluginComponents(); } /** @@ -174,13 +176,16 @@ else if (itemName.equals("conference")) confInviteDialog.setVisible(true); } else + { + ResourceManagementService r = GuiActivator.getResources(); + new ErrorDialog( - null, - GuiActivator.getResources().getI18NString( - "service.gui.WARNING"), - GuiActivator.getResources().getI18NString( - "service.gui.NO_ONLINE_CONFERENCING_ACCOUNT")) - .showDialog(); + null, + r.getI18NString("service.gui.WARNING"), + r.getI18NString( + "service.gui.NO_ONLINE_CONFERENCING_ACCOUNT")) + .showDialog(); + } } else if (itemName.equals("showHideOffline")) { @@ -287,36 +292,38 @@ private void registerMenuItems() { // We only add the options button if the property SHOW_OPTIONS_WINDOW // specifies so or if it's not set. + ConfigurationService cfg = GuiActivator.getConfigurationService(); Boolean showOptionsProp - = GuiActivator.getConfigurationService() - .getBoolean(ConfigurationFrame.SHOW_OPTIONS_WINDOW_PROPERTY, - true); + = cfg.getBoolean( + ConfigurationFrame.SHOW_OPTIONS_WINDOW_PROPERTY, + true); if (showOptionsProp.booleanValue()) { UIService uiService = GuiActivator.getUIService(); - if ((uiService == null) || !uiService.useMacOSXScreenMenuBar() - || !registerConfigMenuItemMacOSX()) + + if ((uiService == null) + || !uiService.useMacOSXScreenMenuBar() + || !registerConfigMenuItemMacOSX()) { registerConfigMenuItemNonMacOSX(); } } - conferenceMenuItem = new JMenuItem( - GuiActivator.getResources().getI18NString( - "service.gui.CREATE_CONFERENCE_CALL")); + ResourceManagementService r = GuiActivator.getResources(); - conferenceMenuItem.setMnemonic(GuiActivator.getResources() - .getI18nMnemonic("service.gui.CREATE_CONFERENCE_CALL")); + conferenceMenuItem + = new JMenuItem( + r.getI18NString("service.gui.CREATE_CONFERENCE_CALL")); + conferenceMenuItem.setMnemonic( + r.getI18nMnemonic("service.gui.CREATE_CONFERENCE_CALL")); conferenceMenuItem.setName("conference"); conferenceMenuItem.addActionListener(this); - this.add(conferenceMenuItem); + add(conferenceMenuItem); initVideoBridgeMenu(); - if(!GuiActivator.getConfigurationService().getBoolean( - AUTO_ANSWER_MENU_DISABLED_PROP, - false)) + if(!cfg.getBoolean(AUTO_ANSWER_MENU_DISABLED_PROP, false)) { if(ConfigurationManager.isAutoAnswerDisableSubmenu()) { @@ -337,25 +344,20 @@ private void registerMenuItems() ? "service.gui.HIDE_OFFLINE_CONTACTS" : "service.gui.SHOW_OFFLINE_CONTACTS"; - hideOfflineMenuItem = new JMenuItem( - GuiActivator.getResources().getI18NString(offlineTextKey)); - - hideOfflineMenuItem.setMnemonic(GuiActivator.getResources() - .getI18nMnemonic(offlineTextKey)); + hideOfflineMenuItem = new JMenuItem(r.getI18NString(offlineTextKey)); + hideOfflineMenuItem.setMnemonic(r.getI18nMnemonic(offlineTextKey)); hideOfflineMenuItem.setName("showHideOffline"); hideOfflineMenuItem.addActionListener(this); this.add(hideOfflineMenuItem); // Sound on/off menu item. - String soundTextKey = GuiActivator.getAudioNotifier().isMute() - ? "service.gui.SOUND_ON" - : "service.gui.SOUND_OFF"; - - soundMenuItem = new JMenuItem( - GuiActivator.getResources().getI18NString(soundTextKey)); + String soundTextKey + = GuiActivator.getAudioNotifier().isMute() + ? "service.gui.SOUND_ON" + : "service.gui.SOUND_OFF"; - soundMenuItem.setMnemonic(GuiActivator.getResources() - .getI18nMnemonic(soundTextKey)); + soundMenuItem = new JMenuItem(r.getI18NString(soundTextKey)); + soundMenuItem.setMnemonic(r.getI18nMnemonic(soundTextKey)); soundMenuItem.setName("sound"); soundMenuItem.addActionListener(this); this.add(soundMenuItem); @@ -398,7 +400,7 @@ private List getVideoBridgeProviders() */ private void initVideoBridgeMenu() { - // If already created remove the previous menu in order to reinitialize + // If already created, remove the previous menu in order to reinitialize // it. if (videoBridgeMenuItem != null) { @@ -427,47 +429,53 @@ public void menuCanceled(MenuEvent arg0) {} = getVideoBridgeProviders(); // Add a service listener in order to be notified when a new protocol - // privder is added or removed and the list should be refreshed. + // provider is added or removed and the list should be refreshed. GuiActivator.bundleContext.addServiceListener(this); - if (videoBridgeProviders == null || videoBridgeProviders.size() <= 0) + int videoBridgeProviderCount + = (videoBridgeProviders == null) ? 0 : videoBridgeProviders.size(); + ResourceManagementService r = GuiActivator.getResources(); + + if (videoBridgeProviderCount <= 0) { - videoBridgeMenuItem = new VideoBridgeProviderMenuItem( - GuiActivator.getResources().getI18NString( - "service.gui.CREATE_VIDEO_BRIDGE"), null); + videoBridgeMenuItem + = new VideoBridgeProviderMenuItem( + r.getI18NString("service.gui.CREATE_VIDEO_BRIDGE"), + null); videoBridgeMenuItem.setEnabled(false); } - else if (videoBridgeProviders.size() == 1) + else if (videoBridgeProviderCount == 1) { - videoBridgeMenuItem = new VideoBridgeProviderMenuItem( - GuiActivator.getResources().getI18NString( - "service.gui.CREATE_VIDEO_BRIDGE"), - videoBridgeProviders.get(0)); + videoBridgeMenuItem + = new VideoBridgeProviderMenuItem( + r.getI18NString("service.gui.CREATE_VIDEO_BRIDGE"), + videoBridgeProviders.get(0)); videoBridgeMenuItem.setName("videoBridge"); videoBridgeMenuItem.addActionListener(this); } - else if (videoBridgeProviders.size() > 1) + else if (videoBridgeProviderCount > 1) { - videoBridgeMenuItem = new SIPCommMenu( - GuiActivator.getResources().getI18NString( - "service.gui.CREATE_VIDEO_BRIDGE_MENU")); + videoBridgeMenuItem + = new SIPCommMenu( + r.getI18NString( + "service.gui.CREATE_VIDEO_BRIDGE_MENU")); for (ProtocolProviderService videoBridgeProvider - : videoBridgeProviders) + : videoBridgeProviders) { VideoBridgeProviderMenuItem videoBridgeItem = new VideoBridgeProviderMenuItem(videoBridgeProvider); ((JMenu) videoBridgeMenuItem).add(videoBridgeItem); videoBridgeItem.setIcon( - ImageLoader.getAccountStatusImage(videoBridgeProvider)); + ImageLoader.getAccountStatusImage(videoBridgeProvider)); } } - videoBridgeMenuItem.setIcon(GuiActivator.getResources().getImage( - "service.gui.icons.VIDEO_BRIDGE")); - videoBridgeMenuItem.setMnemonic(GuiActivator.getResources() - .getI18nMnemonic("service.gui.CREATE_VIDEO_BRIDGE")); + videoBridgeMenuItem.setIcon( + r.getImage("service.gui.icons.VIDEO_BRIDGE")); + videoBridgeMenuItem.setMnemonic( + r.getI18nMnemonic("service.gui.CREATE_VIDEO_BRIDGE")); insert(videoBridgeMenuItem, 1); } @@ -487,14 +495,15 @@ private boolean registerConfigMenuItemMacOSX() */ private void registerConfigMenuItemNonMacOSX() { - configMenuItem = new JMenuItem( - GuiActivator.getResources().getI18NString("service.gui.SETTINGS"), - GuiActivator.getResources().getImage( - "service.gui.icons.CONFIGURE_ICON")); - - this.add(configMenuItem); - configMenuItem.setMnemonic(GuiActivator.getResources() - .getI18nMnemonic("service.gui.SETTINGS")); + ResourceManagementService r = GuiActivator.getResources(); + + configMenuItem + = new JMenuItem( + r.getI18NString("service.gui.SETTINGS"), + r.getImage("service.gui.icons.CONFIGURE_ICON")); + add(configMenuItem); + configMenuItem.setMnemonic( + r.getI18nMnemonic("service.gui.SETTINGS")); configMenuItem.setName("config"); configMenuItem.addActionListener(this); } @@ -504,19 +513,23 @@ private void registerConfigMenuItemNonMacOSX() */ public void loadSkin() { - conferenceMenuItem.setIcon(GuiActivator.getResources().getImage( - "service.gui.icons.CONFERENCE_CALL")); - videoBridgeMenuItem.setIcon(GuiActivator.getResources().getImage( - "service.gui.icons.VIDEO_BRIDGE")); - hideOfflineMenuItem.setIcon(GuiActivator.getResources().getImage( - "service.gui.icons.SHOW_HIDE_OFFLINE_ICON")); - soundMenuItem.setIcon(GuiActivator.getResources().getImage( - "service.gui.icons.SOUND_MENU_ICON")); - - if(configMenuItem != null) + ResourceManagementService r = GuiActivator.getResources(); + + conferenceMenuItem.setIcon( + r.getImage("service.gui.icons.CONFERENCE_CALL")); + if (configMenuItem != null) + { + configMenuItem.setIcon( + r.getImage("service.gui.icons.CONFIGURE_ICON")); + } + hideOfflineMenuItem.setIcon( + r.getImage("service.gui.icons.SHOW_HIDE_OFFLINE_ICON")); + soundMenuItem.setIcon( + r.getImage("service.gui.icons.SOUND_MENU_ICON")); + if (videoBridgeMenuItem != null) { - configMenuItem.setIcon(GuiActivator.getResources().getImage( - "service.gui.icons.CONFIGURE_ICON")); + videoBridgeMenuItem.setIcon( + r.getImage("service.gui.icons.VIDEO_BRIDGE")); } }