| @@ -22,20 +22,16 @@ | |||
| ============================================================================== | |||
| */ | |||
| AudioIODevice::AudioIODevice (const String& deviceName, const String& typeName_) | |||
| : name (deviceName), | |||
| typeName (typeName_) | |||
| AudioIODevice::AudioIODevice (const String& deviceName, const String& deviceTypeName) | |||
| : name (deviceName), typeName (deviceTypeName) | |||
| { | |||
| } | |||
| AudioIODevice::~AudioIODevice() | |||
| { | |||
| } | |||
| AudioIODevice::~AudioIODevice() {} | |||
| bool AudioIODevice::hasControlPanel() const | |||
| { | |||
| return false; | |||
| } | |||
| void AudioIODeviceCallback::audioDeviceError (const String&) {} | |||
| bool AudioIODevice::setAudioPreprocessingEnabled (bool) { return false; } | |||
| bool AudioIODevice::hasControlPanel() const { return false; } | |||
| bool AudioIODevice::showControlPanel() | |||
| { | |||
| @@ -43,6 +39,3 @@ bool AudioIODevice::showControlPanel() | |||
| // their hasControlPanel() method. | |||
| return false; | |||
| } | |||
| //============================================================================== | |||
| void AudioIODeviceCallback::audioDeviceError (const String&) {} | |||
| @@ -289,6 +289,11 @@ public: | |||
| */ | |||
| virtual bool showControlPanel(); | |||
| /** On devices which support it, this allows automatic gain control or other | |||
| mic processing to be disabled. | |||
| If the device doesn't support this operation, it'll return false. | |||
| */ | |||
| virtual bool setAudioPreprocessingEnabled (bool shouldBeEnabled); | |||
| //============================================================================== | |||
| protected: | |||
| @@ -125,6 +125,7 @@ | |||
| #if JUCE_USE_ANDROID_OPENSLES | |||
| #include <SLES/OpenSLES.h> | |||
| #include <SLES/OpenSLES_Android.h> | |||
| #include <SLES/OpenSLES_AndroidConfiguration.h> | |||
| #endif | |||
| #endif | |||
| @@ -165,29 +165,9 @@ public: | |||
| oldCallback->audioDeviceStopped(); | |||
| } | |||
| void run() override | |||
| bool setAudioPreprocessingEnabled (bool enable) override | |||
| { | |||
| if (recorder != nullptr) recorder->start(); | |||
| if (player != nullptr) player->start(); | |||
| while (! threadShouldExit()) | |||
| { | |||
| if (player != nullptr) player->writeBuffer (outputBuffer, *this); | |||
| if (recorder != nullptr) recorder->readNextBlock (inputBuffer, *this); | |||
| const ScopedLock sl (callbackLock); | |||
| if (callback != nullptr) | |||
| { | |||
| callback->audioDeviceIOCallback (numInputChannels > 0 ? inputBuffer.getArrayOfReadPointers() : nullptr, numInputChannels, | |||
| numOutputChannels > 0 ? outputBuffer.getArrayOfWritePointers() : nullptr, numOutputChannels, | |||
| actualBufferSize); | |||
| } | |||
| else | |||
| { | |||
| outputBuffer.clear(); | |||
| } | |||
| } | |||
| return recorder != nullptr && recorder->setAudioPreprocessingEnabled (enable); | |||
| } | |||
| private: | |||
| @@ -212,6 +192,31 @@ private: | |||
| return oldCallback; | |||
| } | |||
| void run() override | |||
| { | |||
| if (recorder != nullptr) recorder->start(); | |||
| if (player != nullptr) player->start(); | |||
| while (! threadShouldExit()) | |||
| { | |||
| if (player != nullptr) player->writeBuffer (outputBuffer, *this); | |||
| if (recorder != nullptr) recorder->readNextBlock (inputBuffer, *this); | |||
| const ScopedLock sl (callbackLock); | |||
| if (callback != nullptr) | |||
| { | |||
| callback->audioDeviceIOCallback (numInputChannels > 0 ? inputBuffer.getArrayOfReadPointers() : nullptr, numInputChannels, | |||
| numOutputChannels > 0 ? outputBuffer.getArrayOfWritePointers() : nullptr, numOutputChannels, | |||
| actualBufferSize); | |||
| } | |||
| else | |||
| { | |||
| outputBuffer.clear(); | |||
| } | |||
| } | |||
| } | |||
| //================================================================================================== | |||
| struct Engine | |||
| { | |||
| @@ -230,6 +235,7 @@ private: | |||
| SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (SLInterfaceID*) library.getFunction ("SL_IID_ANDROIDSIMPLEBUFFERQUEUE"); | |||
| SL_IID_PLAY = (SLInterfaceID*) library.getFunction ("SL_IID_PLAY"); | |||
| SL_IID_RECORD = (SLInterfaceID*) library.getFunction ("SL_IID_RECORD"); | |||
| SL_IID_ANDROIDCONFIGURATION = (SLInterfaceID*) library.getFunction ("SL_IID_ANDROIDCONFIGURATION"); | |||
| check ((*engineObject)->Realize (engineObject, SL_BOOLEAN_FALSE)); | |||
| check ((*engineObject)->GetInterface (engineObject, *SL_IID_ENGINE, &engineInterface)); | |||
| @@ -271,6 +277,7 @@ private: | |||
| SLInterfaceID* SL_IID_ANDROIDSIMPLEBUFFERQUEUE; | |||
| SLInterfaceID* SL_IID_PLAY; | |||
| SLInterfaceID* SL_IID_RECORD; | |||
| SLInterfaceID* SL_IID_ANDROIDCONFIGURATION; | |||
| private: | |||
| DynamicLibrary library; | |||
| @@ -434,7 +441,8 @@ private: | |||
| struct Recorder | |||
| { | |||
| Recorder (int numChannels, int sampleRate, Engine& engine) | |||
| : recorderObject (nullptr), recorderRecord (nullptr), recorderBufferQueue (nullptr), | |||
| : recorderObject (nullptr), recorderRecord (nullptr), | |||
| recorderBufferQueue (nullptr), configObject (nullptr), | |||
| bufferList (numChannels) | |||
| { | |||
| jassert (numChannels == 1); // STEREO doesn't always work!! | |||
| @@ -466,6 +474,7 @@ private: | |||
| { | |||
| check ((*recorderObject)->GetInterface (recorderObject, *engine.SL_IID_RECORD, &recorderRecord)); | |||
| check ((*recorderObject)->GetInterface (recorderObject, *engine.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &recorderBufferQueue)); | |||
| check ((*recorderObject)->GetInterface (recorderObject, *engine.SL_IID_ANDROIDCONFIGURATION, &configObject)); | |||
| check ((*recorderBufferQueue)->RegisterCallback (recorderBufferQueue, staticCallback, this)); | |||
| check ((*recorderRecord)->SetRecordState (recorderRecord, SL_RECORDSTATE_STOPPED)); | |||
| @@ -532,10 +541,20 @@ private: | |||
| } | |||
| } | |||
| bool setAudioPreprocessingEnabled (bool enable) | |||
| { | |||
| SLuint32 mode = enable ? SL_ANDROID_RECORDING_PRESET_GENERIC | |||
| : SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION; | |||
| return configObject != nullptr | |||
| && check ((*configObject)->SetConfiguration (configObject, SL_ANDROID_KEY_RECORDING_PRESET, &mode, sizeof (mode))); | |||
| } | |||
| private: | |||
| SLObjectItf recorderObject; | |||
| SLRecordItf recorderRecord; | |||
| SLAndroidSimpleBufferQueueItf recorderBufferQueue; | |||
| SLAndroidConfigurationItf configObject; | |||
| BufferList bufferList; | |||
| @@ -208,6 +208,12 @@ public: | |||
| bool isPlaying() override { return isRunning && callback != nullptr; } | |||
| String getLastError() override { return lastError; } | |||
| bool setAudioPreprocessingEnabled (bool enable) override | |||
| { | |||
| return setSessionUInt32Property (kAudioSessionProperty_Mode, enable ? kAudioSessionMode_Default | |||
| : kAudioSessionMode_Measurement); | |||
| } | |||
| private: | |||
| //================================================================================================== | |||
| CriticalSection callbackLock; | |||
| @@ -433,12 +439,12 @@ private: | |||
| static OSStatus processStatic (void* client, AudioUnitRenderActionFlags* flags, const AudioTimeStamp* time, | |||
| UInt32 /*busNumber*/, UInt32 numFrames, AudioBufferList* data) | |||
| { | |||
| return static_cast <iOSAudioIODevice*> (client)->process (flags, time, numFrames, data); | |||
| return static_cast<iOSAudioIODevice*> (client)->process (flags, time, numFrames, data); | |||
| } | |||
| static void routingChangedStatic (void* client, AudioSessionPropertyID, UInt32 /*inDataSize*/, const void* propertyValue) | |||
| { | |||
| static_cast <iOSAudioIODevice*> (client)->routingChanged (propertyValue); | |||
| static_cast<iOSAudioIODevice*> (client)->routingChanged (propertyValue); | |||
| } | |||
| //================================================================================================== | |||
| @@ -530,9 +536,9 @@ private: | |||
| return AudioSessionGetProperty (propID, &valueSize, &result); | |||
| } | |||
| static void setSessionUInt32Property (AudioSessionPropertyID propID, UInt32 v) noexcept { AudioSessionSetProperty (propID, sizeof (v), &v); } | |||
| static void setSessionFloat32Property (AudioSessionPropertyID propID, Float32 v) noexcept { AudioSessionSetProperty (propID, sizeof (v), &v); } | |||
| static void setSessionFloat64Property (AudioSessionPropertyID propID, Float64 v) noexcept { AudioSessionSetProperty (propID, sizeof (v), &v); } | |||
| static bool setSessionUInt32Property (AudioSessionPropertyID propID, UInt32 v) noexcept { AudioSessionSetProperty (propID, sizeof (v), &v) == kAudioSessionNoError; } | |||
| static bool setSessionFloat32Property (AudioSessionPropertyID propID, Float32 v) noexcept { AudioSessionSetProperty (propID, sizeof (v), &v) == kAudioSessionNoError; } | |||
| static bool setSessionFloat64Property (AudioSessionPropertyID propID, Float64 v) noexcept { AudioSessionSetProperty (propID, sizeof (v), &v) == kAudioSessionNoError; } | |||
| JUCE_DECLARE_NON_COPYABLE (iOSAudioIODevice) | |||
| }; | |||