From 5ec536f13fad7a16f536792697b6229f9f6d2399 Mon Sep 17 00:00:00 2001 From: attila Date: Thu, 18 Aug 2022 18:38:10 +0200 Subject: [PATCH] CoreAudio: Forward errors to callback during device initialisation --- .../audio_io/juce_AudioDeviceManager.cpp | 6 ++ .../native/juce_mac_CoreAudio.cpp | 68 +++++++++++++++++-- 2 files changed, 70 insertions(+), 4 deletions(-) diff --git a/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp b/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp index fa8a59c2d8..380636dba8 100644 --- a/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp +++ b/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp @@ -720,6 +720,12 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup currentDeviceType = currentAudioDevice->getTypeName(); currentAudioDevice->start (callbackHandler.get()); + + error = currentAudioDevice->getLastError(); + } + + if (error.isEmpty()) + { updateCurrentSetup(); for (int i = 0; i < availableDeviceTypes.size(); ++i) diff --git a/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp b/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp index c42148b1cc..c19d4847d8 100644 --- a/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp +++ b/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp @@ -1605,11 +1605,17 @@ public: stop(); fifos.clear(); - for (auto* d : devices) - d->start(); + { + ScopedErrorForwarder forwarder (*this, newCallback); - if (newCallback != nullptr) - newCallback->audioDeviceAboutToStart (this); + for (auto* d : devices) + d->start(); + + if (! forwarder.encounteredError() && newCallback != nullptr) + newCallback->audioDeviceAboutToStart (this); + else if (lastError.isEmpty()) + lastError = TRANS("Failed to initialise all requested devices."); + } const ScopedLock sl (callbackLock); previousCallback = callback = newCallback; @@ -2079,6 +2085,60 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DeviceWrapper) }; + /* If the current AudioIODeviceCombiner::callback is nullptr, it sets itself as the callback + and forwards error related callbacks to the provided callback + */ + class ScopedErrorForwarder : public AudioIODeviceCallback + { + public: + ScopedErrorForwarder (AudioIODeviceCombiner& ownerIn, AudioIODeviceCallback* cb) + : owner (ownerIn), + target (cb) + { + const ScopedLock sl (owner.callbackLock); + + if (owner.callback == nullptr) + owner.callback = this; + } + + ~ScopedErrorForwarder() override + { + const ScopedLock sl (owner.callbackLock); + + if (owner.callback == this) + owner.callback = nullptr; + } + + // We only want to be notified about error conditions when the owner's callback is nullptr. + // This class shouldn't be relied on for forwarding this call. + void audioDeviceAboutToStart (AudioIODevice*) override {} + + void audioDeviceStopped() override + { + if (target != nullptr) + target->audioDeviceStopped(); + + error = true; + } + + void audioDeviceError (const String& errorMessage) override + { + owner.lastError = errorMessage; + + if (target != nullptr) + target->audioDeviceError (errorMessage); + + error = true; + } + + bool encounteredError() const { return error; } + + private: + AudioIODeviceCombiner& owner; + AudioIODeviceCallback* target; + bool error = false; + }; + OwnedArray devices; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioIODeviceCombiner)