From 048cfd6a3c3df143279b51d830eb24e6c9512ff6 Mon Sep 17 00:00:00 2001 From: jules Date: Tue, 23 Oct 2018 11:09:12 +0100 Subject: [PATCH] Handled some uncaught error cases in android openSL --- .../native/juce_android_OpenSL.cpp | 248 ++++++++++-------- 1 file changed, 135 insertions(+), 113 deletions(-) diff --git a/modules/juce_audio_devices/native/juce_android_OpenSL.cpp b/modules/juce_audio_devices/native/juce_android_OpenSL.cpp index fa610109b8..28a0592185 100644 --- a/modules/juce_audio_devices/native/juce_android_OpenSL.cpp +++ b/modules/juce_audio_devices/native/juce_android_OpenSL.cpp @@ -60,6 +60,22 @@ SLInterfaceID_ IntfIID::iid = { 0xc5657aa0, 0xdd SLInterfaceID_ IntfIID::iid = { 0x198e4940, 0xc5d7, 0x11df, 0xa2a6, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b} }; SLInterfaceID_ IntfIID::iid = { 0x89f6a7e0, 0xbeac, 0x11df, 0x8b5c, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b} }; +template +static void destroyObject (SLObjectType object) +{ + if (object != nullptr && *object != nullptr) + (*object)->Destroy (object); +} + +template <> +struct ContainerDeletePolicy +{ + static void destroy (SLObjectItf object) + { + destroyObject (object); + } +}; + //============================================================================== // Some life-time and type management of OpenSL objects class SlObjectRef @@ -67,27 +83,34 @@ class SlObjectRef public: //============================================================================== SlObjectRef() noexcept {} - SlObjectRef (const SlObjectRef& obj) noexcept : cb (obj.cb) {} + SlObjectRef (const SlObjectRef& obj) noexcept : cb (obj.cb) {} SlObjectRef (SlObjectRef&& obj) noexcept : cb (static_cast&&> (obj.cb)) { obj.cb = nullptr; } explicit SlObjectRef (SLObjectItf o) : cb (new ControlBlock (o)) {} //============================================================================== - SlObjectRef& operator=(const SlObjectRef& r) noexcept { cb = r.cb; return *this; } - SlObjectRef& operator=(SlObjectRef&& r) noexcept { cb = static_cast&&> (r.cb); r.cb = nullptr; return *this; } - SlObjectRef& operator=(std::nullptr_t) noexcept { cb = nullptr; return *this; } + SlObjectRef& operator= (const SlObjectRef& r) noexcept { cb = r.cb; return *this; } + SlObjectRef& operator= (SlObjectRef&& r) noexcept { cb = static_cast&&> (r.cb); r.cb = nullptr; return *this; } + SlObjectRef& operator= (std::nullptr_t) noexcept { cb = nullptr; return *this; } //============================================================================== - const SLObjectItf_* const operator*() noexcept { return *cb->ptr.get(); } - SLObjectItf operator->() noexcept { return (cb == nullptr ? nullptr : cb->ptr.get()); } - operator SLObjectItf() noexcept { return (cb == nullptr ? nullptr : cb->ptr.get()); } + const SLObjectItf_* operator*() noexcept { return *cb->ptr.get(); } + SLObjectItf operator->() noexcept { return (cb == nullptr ? nullptr : cb->ptr.get()); } + operator SLObjectItf() noexcept { return (cb == nullptr ? nullptr : cb->ptr.get()); } //============================================================================== - bool operator== (nullptr_t) const noexcept { return (cb == nullptr || cb->ptr == nullptr); } - bool operator!= (nullptr_t) const noexcept { return (cb != nullptr && cb->ptr != nullptr); } -private: + bool operator== (nullptr_t) const noexcept { return (cb == nullptr || cb->ptr == nullptr); } + bool operator!= (nullptr_t) const noexcept { return (cb != nullptr && cb->ptr != nullptr); } +private: //============================================================================== - struct ControlBlock : ReferenceCountedObject { std::unique_ptr ptr; ControlBlock() {} ControlBlock (SLObjectItf o) : ptr (o) {} }; + struct ControlBlock : ReferenceCountedObject + { + ControlBlock() = default; + ControlBlock (SLObjectItf o) : ptr (o) {} + + std::unique_ptr ptr; + }; + ReferenceCountedObjectPtr cb; }; @@ -96,53 +119,53 @@ class SlRef : public SlObjectRef { public: //============================================================================== - SlRef() noexcept : type (nullptr) {} - SlRef (SlRef& r) noexcept : SlObjectRef (r), type (r.type) {} + SlRef() noexcept {} + SlRef (const SlRef& r) noexcept : SlObjectRef (r), type (r.type) {} SlRef (SlRef&& r) noexcept : SlObjectRef (static_cast (r)), type (r.type) { r.type = nullptr; } //============================================================================== SlRef& operator= (const SlRef& r) noexcept { SlObjectRef::operator= (r); type = r.type; return *this; } - SlRef& operator= (SlRef&& r) noexcept { SlObjectRef::operator= (static_cast (r)); type = r.type; r.type = nullptr; return *this; } - SlRef& operator= (std::nullptr_t) noexcept { SlObjectRef::operator= (nullptr); type = nullptr; return *this; } + SlRef& operator= (SlRef&& r) noexcept { SlObjectRef::operator= (static_cast (r)); type = r.type; r.type = nullptr; return *this; } + SlRef& operator= (std::nullptr_t) noexcept { SlObjectRef::operator= (nullptr); type = nullptr; return *this; } //============================================================================== - T* const operator*() noexcept { return *type; } - T* const * operator->() noexcept { return type; } - operator T* const *() noexcept { return type; } + T* const operator*() noexcept { return *type; } + T* const* operator->() noexcept { return type; } + operator T* const*() noexcept { return type; } //============================================================================== - static SlRef cast (SlObjectRef& base) { return SlRef (base); } - static SlRef cast (SlObjectRef&& base) { return SlRef (static_cast (base)); } + static SlRef cast (SlObjectRef& base) { return SlRef (base); } + static SlRef cast (SlObjectRef&& base) { return SlRef (static_cast (base)); } + private: - //============================================================================== SlRef (SlObjectRef& base) : SlObjectRef (base) { - SLObjectItf obj = SlObjectRef::operator->(); - SLresult err = (*obj)->GetInterface (obj, &IntfIID::iid, &type); - if (type == nullptr || err != SL_RESULT_SUCCESS) - *this = nullptr; + if (auto obj = SlObjectRef::operator->()) + { + auto err = (*obj)->GetInterface (obj, &IntfIID::iid, &type); + + if (type != nullptr && err == SL_RESULT_SUCCESS) + return; + } + + *this = nullptr; } SlRef (SlObjectRef&& base) : SlObjectRef (static_cast (base)) { - SLObjectItf obj = SlObjectRef::operator->(); - SLresult err = (*obj)->GetInterface (obj, &IntfIID::iid, &type); - base = nullptr; + if (auto obj = SlObjectRef::operator->()) + { + auto err = (*obj)->GetInterface (obj, &IntfIID::iid, &type); + base = nullptr; - if (type == nullptr || err != SL_RESULT_SUCCESS) - *this = nullptr; - } - T* const * type; -}; + if (type != nullptr && err == SL_RESULT_SUCCESS) + return; + } -template <> -struct ContainerDeletePolicy -{ - static void destroy (SLObjectItf object) - { - if (object != nullptr) - (*object)->Destroy (object); + *this = nullptr; } + + T* const* type = nullptr; }; //============================================================================== @@ -281,8 +304,7 @@ public: numChannels (numChannelsToUse), nativeBuffer (static_cast (numChannels * owner.bufferSize * owner.numBuffers)), scratchBuffer (numChannelsToUse, owner.bufferSize), - sampleBuffer (scratchBuffer.getArrayOfWritePointers(), numChannelsToUse, owner.bufferSize), - nextBlock (0), numBlocksOut (0) + sampleBuffer (scratchBuffer.getArrayOfWritePointers(), numChannelsToUse, owner.bufferSize) {} ~OpenSLQueueRunner() @@ -297,6 +319,7 @@ public: bool init() { runner = crtp().createPlayerOrRecorder(); + if (runner == nullptr) return false; @@ -319,13 +342,13 @@ public: } queue = SlRef::cast (runner); + if (queue == nullptr) return false; return ((*queue)->RegisterCallback (queue, staticFinished, this) == SL_RESULT_SUCCESS); } - void clear() { nextBlock.set (0); @@ -376,7 +399,7 @@ public: HeapBlock nativeBuffer; AudioBuffer scratchBuffer, sampleBuffer; - Atomic nextBlock, numBlocksOut; + Atomic nextBlock { 0 }, numBlocksOut { 0 }; }; //============================================================================== @@ -385,35 +408,36 @@ public: { using Base = OpenSLQueueRunner, SLPlayItf_>; - enum { isPlayer = 1 }; - OpenSLQueueRunnerPlayer (OpenSLSessionT& sessionToUse, int numChannelsToUse) : Base (sessionToUse, numChannelsToUse) {} SlRef createPlayerOrRecorder() { - SLDataLocator_AndroidSimpleBufferQueue queueLocator = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, static_cast (Base::owner.numBuffers)}; - SLDataLocator_OutputMix outputMix = {SL_DATALOCATOR_OUTPUTMIX, Base::owner.outputMix}; + SLDataLocator_AndroidSimpleBufferQueue queueLocator = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, static_cast (Base::owner.numBuffers) }; + SLDataLocator_OutputMix outputMix = { SL_DATALOCATOR_OUTPUTMIX, Base::owner.outputMix }; PCMDataFormatEx dataFormat; BufferHelpers::initPCMDataFormat (dataFormat, Base::numChannels, Base::owner.sampleRate); - SLDataSource source = {&queueLocator, &dataFormat}; - SLDataSink sink = {&outputMix, nullptr}; + SLDataSource source = { &queueLocator, &dataFormat }; + SLDataSink sink = { &outputMix, nullptr }; SLInterfaceID queueInterfaces[] = { &IntfIID::iid, &IntfIID::iid }; SLboolean interfaceRequired[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE}; SLObjectItf obj = nullptr; - SLresult status = (*Base::owner.engine)->CreateAudioPlayer (Base::owner.engine, &obj, &source, &sink, 2, queueInterfaces, interfaceRequired); - if (status != SL_RESULT_SUCCESS || obj == nullptr || (*obj)->Realize (obj, 0) != SL_RESULT_SUCCESS) + if (auto e = *Base::owner.engine) { - if (obj != nullptr) - (*obj)->Destroy (obj); + auto status = e->CreateAudioPlayer (Base::owner.engine, &obj, &source, &sink, 2, + queueInterfaces, interfaceRequired); - return SlRef(); + if (status != SL_RESULT_SUCCESS || obj == nullptr || (*obj)->Realize(obj, 0) != SL_RESULT_SUCCESS) + { + destroyObject (obj); + return {}; + } } return SlRef::cast (SlObjectRef (obj)); @@ -423,44 +447,42 @@ public: }; template - struct OpenSLQueueRunnerRecorder : OpenSLQueueRunner, SLRecordItf_> + struct OpenSLQueueRunnerRecorder : public OpenSLQueueRunner, SLRecordItf_> { using Base = OpenSLQueueRunner, SLRecordItf_>; - enum { isPlayer = 0 }; - OpenSLQueueRunnerRecorder (OpenSLSessionT& sessionToUse, int numChannelsToUse) : Base (sessionToUse, numChannelsToUse) {} SlRef createPlayerOrRecorder() { - SLDataLocator_IODevice ioDeviceLocator = {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, SL_DEFAULTDEVICEID_AUDIOINPUT, nullptr}; - SLDataLocator_AndroidSimpleBufferQueue queueLocator = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, static_cast (Base::owner.numBuffers)}; + SLDataLocator_IODevice ioDeviceLocator = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, SL_DEFAULTDEVICEID_AUDIOINPUT, nullptr }; + SLDataLocator_AndroidSimpleBufferQueue queueLocator = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, static_cast (Base::owner.numBuffers) }; PCMDataFormatEx dataFormat; BufferHelpers::initPCMDataFormat (dataFormat, Base::numChannels, Base::owner.sampleRate); - SLDataSource source = {&ioDeviceLocator, nullptr}; - SLDataSink sink = {&queueLocator, &dataFormat}; + SLDataSource source = { &ioDeviceLocator, nullptr }; + SLDataSink sink = { &queueLocator, &dataFormat }; SLInterfaceID queueInterfaces[] = { &IntfIID::iid, &IntfIID::iid }; - SLboolean interfaceRequired[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE}; + SLboolean interfaceRequired[] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE }; SLObjectItf obj = nullptr; - SLresult status = (*Base::owner.engine)->CreateAudioRecorder (Base::owner.engine, &obj, &source, &sink, 2, queueInterfaces, interfaceRequired); - if (status != SL_RESULT_SUCCESS || obj == nullptr || (*obj)->Realize (obj, 0) != SL_RESULT_SUCCESS) + if (auto e = *Base::owner.engine) { - if (obj != nullptr) - (*obj)->Destroy (obj); + auto status = e->CreateAudioRecorder (Base::owner.engine, &obj, &source, &sink, 2, queueInterfaces, interfaceRequired); - return SlRef(); + if (status != SL_RESULT_SUCCESS || obj == nullptr || (*obj)->Realize (obj, 0) != SL_RESULT_SUCCESS) + { + destroyObject (obj); + return {}; + } } - SlRef recorder = SlRef::cast (SlObjectRef (obj)); - - return recorder; + return SlRef::cast (SlObjectRef (obj)); } bool setAudioPreprocessingEnabled (bool shouldEnable) @@ -468,13 +490,14 @@ public: if (Base::config != nullptr) { const bool supportsUnprocessed = (getEnv()->GetStaticIntField (AndroidBuildVersion, AndroidBuildVersion.SDK_INT) >= 25); + const SLuint32 recordingPresetValue = (shouldEnable ? SL_ANDROID_RECORDING_PRESET_GENERIC : (supportsUnprocessed ? SL_ANDROID_RECORDING_PRESET_UNPROCESSED : SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION)); - SLresult status = (*Base::config)->SetConfiguration (Base::config, SL_ANDROID_KEY_RECORDING_PRESET, - &recordingPresetValue, sizeof (recordingPresetValue)); + auto status = (*Base::config)->SetConfiguration (Base::config, SL_ANDROID_KEY_RECORDING_PRESET, + &recordingPresetValue, sizeof (recordingPresetValue)); return (status == SL_RESULT_SUCCESS); } @@ -482,7 +505,8 @@ public: return false; } - void setState (bool running) { (*Base::runner)->SetRecordState (Base::runner, running ? SL_RECORDSTATE_RECORDING : SL_RECORDSTATE_STOPPED); } + void setState (bool running) { (*Base::runner)->SetRecordState (Base::runner, running ? SL_RECORDSTATE_RECORDING + : SL_RECORDSTATE_STOPPED); } }; //============================================================================== @@ -494,21 +518,19 @@ public: double samleRateToUse, int bufferSizeToUse, int numBuffersToUse) : inputChannels (numInputChannels), outputChannels (numOutputChannels), - sampleRate (samleRateToUse), bufferSize (bufferSizeToUse), numBuffers (numBuffersToUse), - running (false), audioProcessingEnabled (true), callback (nullptr) + sampleRate (samleRateToUse), bufferSize (bufferSizeToUse), numBuffers (numBuffersToUse) { jassert (numInputChannels > 0 || numOutputChannels > 0); - if (CreateEngineFunc createEngine = (CreateEngineFunc) slLibraryToUse.getFunction ("slCreateEngine")) + if (auto createEngine = (CreateEngineFunc) slLibraryToUse.getFunction ("slCreateEngine")) { SLObjectItf obj = nullptr; + auto err = createEngine (&obj, 0, nullptr, 0, nullptr, nullptr); - SLresult err = createEngine (&obj, 0, nullptr, 0, nullptr, nullptr); - if (err != SL_RESULT_SUCCESS || obj == nullptr || (*obj)->Realize (obj, 0) != SL_RESULT_SUCCESS) + if (err != SL_RESULT_SUCCESS || obj == nullptr || *obj == nullptr + || (*obj)->Realize (obj, 0) != SL_RESULT_SUCCESS) { - if (obj != nullptr) - (*obj)->Destroy (obj); - + destroyObject (obj); return; } @@ -518,13 +540,12 @@ public: if (outputChannels > 0) { SLObjectItf obj = nullptr; + auto err = (*engine)->CreateOutputMix (engine, &obj, 0, nullptr, nullptr); - SLresult err = (*engine)->CreateOutputMix (engine, &obj, 0, nullptr, nullptr); - if (err != SL_RESULT_SUCCESS || obj == nullptr || (*obj)->Realize (obj, 0) != SL_RESULT_SUCCESS) + if (err != SL_RESULT_SUCCESS || obj == nullptr || *obj == nullptr + || (*obj)->Realize (obj, 0) != SL_RESULT_SUCCESS) { - if (obj != nullptr) - (*obj)->Destroy (obj); - + destroyObject (obj); return; } @@ -537,6 +558,7 @@ public: virtual bool openedOK() const { return (engine != nullptr && (outputChannels == 0 || (outputMix != nullptr))); } virtual void start() { stop(); jassert (callback.get() != nullptr); running = true; } virtual void stop() { running = false; } + virtual bool setAudioPreprocessingEnabled (bool shouldEnable) = 0; virtual bool supportsFloatingPoint() const noexcept = 0; virtual int getXRunCount() const noexcept = 0; @@ -553,9 +575,10 @@ public: jassert (callbackToUse != nullptr); // spin-lock until we can set the callback - while (true) + for (;;) { - AudioIODeviceCallback* old = callback.get(); + auto old = callback.get(); + if (old == callbackToUse) break; @@ -568,7 +591,7 @@ public: void process (const float** inputChannelData, float** outputChannelData) { - if (AudioIODeviceCallback* cb = callback.exchange(nullptr)) + if (auto* cb = callback.exchange (nullptr)) { cb->audioDeviceIOCallback (inputChannelData, inputChannels, outputChannelData, outputChannels, bufferSize); callback.set (cb); @@ -586,19 +609,19 @@ public: int numBuffersToUse); //============================================================================== - typedef SLresult (*CreateEngineFunc)(SLObjectItf*, SLuint32, const SLEngineOption*, SLuint32, const SLInterfaceID*, const SLboolean*); + using CreateEngineFunc = SLresult (*) (SLObjectItf*, SLuint32, const SLEngineOption*, + SLuint32, const SLInterfaceID*, const SLboolean*); //============================================================================== int inputChannels, outputChannels; double sampleRate; int bufferSize, numBuffers; - - bool running, audioProcessingEnabled; + bool running = false, audioProcessingEnabled = true; SlRef engine; SlRef outputMix; - Atomic callback; + Atomic callback { nullptr }; }; template @@ -609,7 +632,8 @@ public: int numInputChannels, int numOutputChannels, double samleRateToUse, int bufferSizeToUse, int numBuffersToUse) - : OpenSLSession (slLibraryToUse, numInputChannels, numOutputChannels, samleRateToUse, bufferSizeToUse, numBuffersToUse) + : OpenSLSession (slLibraryToUse, numInputChannels, numOutputChannels, + samleRateToUse, bufferSizeToUse, numBuffersToUse) { jassert (numInputChannels > 0 || numOutputChannels > 0); @@ -644,8 +668,8 @@ public: bool openedOK() const override { - return (OpenSLSession::openedOK() && (inputChannels == 0 || recorder != nullptr) - && (outputChannels == 0 || player != nullptr)); + return OpenSLSession::openedOK() && (inputChannels == 0 || recorder != nullptr) + && (outputChannels == 0 || player != nullptr); } void start() override @@ -762,11 +786,7 @@ public: }; //============================================================================== - OpenSLAudioIODevice (const String& deviceName) - : AudioIODevice (deviceName, openSLTypeName), - actualBufferSize (0), sampleRate (0), audioBuffersToEnqueue (0), - audioProcessingEnabled (true), - callback (nullptr) + OpenSLAudioIODevice (const String& deviceName) : AudioIODevice (deviceName, openSLTypeName) { // OpenSL has piss-poor support for determining latency, so the only way I can find to // get a number for this is by asking the AudioTrack/AudioRecord classes.. @@ -879,7 +899,9 @@ public: session.reset (OpenSLSession::create (slLibrary, numInputChannels, numOutputChannels, sampleRate, actualBufferSize, audioBuffersToEnqueue)); if (session != nullptr) + { session->setAudioPreprocessingEnabled (audioProcessingEnabled); + } else { if (numInputChannels > 0 && numOutputChannels > 0 && RuntimePermissions::isGranted (RuntimePermissions::recordAudio)) @@ -945,7 +967,7 @@ public: { if (session != nullptr && callback != newCallback) { - AudioIODeviceCallback* oldCallback = callback; + auto oldCallback = callback; if (newCallback != nullptr) newCallback->audioDeviceAboutToStart (this); @@ -1001,12 +1023,12 @@ private: //============================================================================== DynamicLibrary slLibrary; - int actualBufferSize, sampleRate, audioBuffersToEnqueue; + int actualBufferSize = 0, sampleRate = 0, audioBuffersToEnqueue = 0; int inputLatency, outputLatency; - bool deviceOpen, audioProcessingEnabled; + bool deviceOpen = false, audioProcessingEnabled = true; String lastError; BigInteger activeOutputChans, activeInputChans; - AudioIODeviceCallback* callback; + AudioIODeviceCallback* callback = nullptr; std::unique_ptr session; @@ -1210,12 +1232,12 @@ public: SLObjectItf obj = nullptr; auto err = createEngine (&obj, 0, nullptr, 0, nullptr, nullptr); - if (err != SL_RESULT_SUCCESS || obj == nullptr) + if (err != SL_RESULT_SUCCESS || obj == nullptr || *obj == nullptr) return; if ((*obj)->Realize (obj, 0) != SL_RESULT_SUCCESS) { - (*obj)->Destroy (obj); + destroyObject (obj); return; } @@ -1223,7 +1245,7 @@ public: if (engine == nullptr) { - (*obj)->Destroy (obj); + destroyObject (obj); return; } @@ -1232,7 +1254,7 @@ public: if (err != SL_RESULT_SUCCESS || obj == nullptr || (*obj)->Realize (obj, 0) != SL_RESULT_SUCCESS) { - (*obj)->Destroy (obj); + destroyObject (obj); return; } @@ -1240,7 +1262,7 @@ public: if (outputMix == nullptr) { - (*obj)->Destroy (obj); + destroyObject (obj); return; } @@ -1264,7 +1286,7 @@ public: if ((*obj)->Realize (obj, 0) != SL_RESULT_SUCCESS) { - (*obj)->Destroy (obj); + destroyObject (obj); return; } @@ -1272,7 +1294,7 @@ public: if (player == nullptr) { - (*obj)->Destroy (obj); + destroyObject (obj); return; }