From 9be03bc99e17dc788379a2e88f6bcf9974533a01 Mon Sep 17 00:00:00 2001 From: reuk Date: Wed, 27 Aug 2025 16:51:07 +0100 Subject: [PATCH] Android Oboe: Reopen streams correctly after device disconnection Even if the output stream is the only one receiving callbacks, it's possible that the input device got disconnected, in which case we'll still need to recreate both streams. --- .../native/juce_Oboe_android.cpp | 119 +++++++++--------- 1 file changed, 60 insertions(+), 59 deletions(-) diff --git a/modules/juce_audio_devices/native/juce_Oboe_android.cpp b/modules/juce_audio_devices/native/juce_Oboe_android.cpp index 08d868ea0f..8507db5d92 100644 --- a/modules/juce_audio_devices/native/juce_Oboe_android.cpp +++ b/modules/juce_audio_devices/native/juce_Oboe_android.cpp @@ -666,44 +666,9 @@ private: sampleRate (sampleRateToUse), bufferSize (bufferSizeToUse), streamFormat (streamFormatToUse), - bitDepth (bitDepthToUse), - outputStream (new OboeStream (outputDeviceId, - oboe::Direction::Output, - oboe::SharingMode::Exclusive, - numOutputChannels, - streamFormatToUse, - sampleRateToUse, - bufferSizeToUse, - this)) + bitDepth (bitDepthToUse) { - if (numInputChannels > 0) - { - inputStream.reset (new OboeStream (inputDeviceId, - oboe::Direction::Input, - oboe::SharingMode::Exclusive, - numInputChannels, - streamFormatToUse, - sampleRateToUse, - bufferSizeToUse, - nullptr)); - - if (inputStream->openedOk() && outputStream->openedOk()) - { - const auto getSampleRate = [] (auto nativeStream) - { - return nativeStream != nullptr ? nativeStream->getSampleRate() : 0; - }; - // Input & output sample rates should match! - jassert (getSampleRate (inputStream->getNativeStream()) - == getSampleRate (outputStream->getNativeStream())); - } - - checkStreamSetup (inputStream.get(), inputDeviceId, numInputChannels, - sampleRate, bufferSize, streamFormat); - } - - checkStreamSetup (outputStream.get(), outputDeviceId, numOutputChannels, - sampleRate, bufferSize, streamFormat); + openStreams(); } // Not strictly required as these should not change, but recommended by Google anyway @@ -735,6 +700,47 @@ private: return 0; } + void openStreams() + { + outputStream = std::make_unique (outputDeviceId, + oboe::Direction::Output, + oboe::SharingMode::Exclusive, + numOutputChannels, + streamFormat, + sampleRate, + bufferSize, + static_cast (this)); + + checkStreamSetup (outputStream.get(), outputDeviceId, numOutputChannels, + sampleRate, bufferSize, streamFormat); + + if (numInputChannels <= 0) + return; + + inputStream = std::make_unique (inputDeviceId, + oboe::Direction::Input, + oboe::SharingMode::Exclusive, + numInputChannels, + streamFormat, + sampleRate, + bufferSize, + nullptr); + + checkStreamSetup (inputStream.get(), inputDeviceId, numInputChannels, + sampleRate, bufferSize, streamFormat); + + if (! inputStream->openedOk() || ! outputStream->openedOk()) + return; + + const auto getSampleRate = [] (auto nativeStream) + { + return nativeStream != nullptr ? nativeStream->getSampleRate() : 0; + }; + // Input & output sample rates should match! + jassert (getSampleRate (inputStream->getNativeStream()) + == getSampleRate (outputStream->getNativeStream())); + } + OboeAudioIODevice& owner; int inputDeviceId, outputDeviceId; int numInputChannels, numOutputChannels; @@ -782,8 +788,7 @@ private: { const SpinLock::ScopedLockType lock { audioCallbackMutex }; - inputStream = nullptr; - outputStream = nullptr; + destroyStreams(); } int getOutputLatencyInSamples() override { return outputLatency; } @@ -958,30 +963,26 @@ private: JUCE_OBOE_LOG ("Oboe stream onErrorAfterClose(): " + getOboeString (error)); - if (error == oboe::Result::ErrorDisconnected) - { - const SpinLock::ScopedTryLockType streamRestartLock { streamRestartMutex }; + const SpinLock::ScopedTryLockType streamRestartLock { streamRestartMutex }; - if (streamRestartLock.isLocked()) - { - // Close, recreate, and start the stream, not much use in current one. - // Use default device id, to let the OS pick the best ID (since our was disconnected). + if (! streamRestartLock.isLocked()) + return; - const SpinLock::ScopedLockType audioCallbackLock { audioCallbackMutex }; + const SpinLock::ScopedLockType audioCallbackLock { audioCallbackMutex }; - outputStream = nullptr; - outputStream.reset (new OboeStream (oboe::kUnspecified, - oboe::Direction::Output, - oboe::SharingMode::Exclusive, - numOutputChannels, - streamFormat, - sampleRate, - bufferSize, - this)); + destroyStreams(); - outputStream->start(); - } - } + if (error != oboe::Result::ErrorDisconnected) + return; + + openStreams(); + start(); + } + + void destroyStreams() + { + inputStream = nullptr; + outputStream = nullptr; } std::vector inputStreamNativeBuffer;