diff --git a/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp b/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp index 5ec04dab7f..5c39d3841c 100644 --- a/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp +++ b/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp @@ -167,6 +167,10 @@ public: jassert (deviceID != 0); updateDetailsFromDevice(); + JUCE_COREAUDIOLOG ("Creating CoreAudioInternal\n" + << (isInputDevice ? (" inputDeviceId " + String (deviceID) + "\n") : "") + << (isOutputDevice ? (" outputDeviceId " + String (deviceID) + "\n") : "") + << getDeviceDetails().joinIntoString ("\n ")); AudioObjectPropertyAddress pa; pa.mSelector = kAudioObjectPropertySelectorWildcard; @@ -463,6 +467,33 @@ public: return true; } + StringArray getDeviceDetails() + { + StringArray result; + + String availableSampleRates ("Available sample rates:"); + + for (auto& s : sampleRates) + availableSampleRates << " " << s; + + result.add (availableSampleRates); + result.add ("Sample rate: " + String (sampleRate)); + String availableBufferSizes ("Available buffer sizes:"); + + for (auto& b : bufferSizes) + availableBufferSizes << " " << b; + + result.add (availableBufferSizes); + result.add ("Buffer size: " + String (bufferSize)); + result.add ("Bit depth: " + String (bitDepth)); + result.add ("Input latency: " + String (inputLatency)); + result.add ("Output latency: " + String (outputLatency)); + result.add ("Input channel names: " + inChanNames.joinIntoString (" ")); + result.add ("Output channel names: " + outChanNames.joinIntoString (" ")); + + return result; + } + //============================================================================== StringArray getSources (bool input) { @@ -676,9 +707,7 @@ public: callback = nullptr; } - if (started - && (deviceID != 0) - && ! leaveInterruptRunning) + if (started && (deviceID != 0) && ! leaveInterruptRunning) { OK (AudioDeviceStop (deviceID, audioIOProc)); OK (AudioDeviceDestroyIOProcID (deviceID, audioProcID)); @@ -772,7 +801,7 @@ public: // called by callbacks void deviceDetailsChanged() { - if (callbacksAllowed) + if (callbacksAllowed.get() == 1) startTimer (100); } @@ -812,7 +841,7 @@ private: HeapBlock audioBuffer; int numInputChans = 0; int numOutputChans = 0; - bool callbacksAllowed = true; + Atomic callbacksAllowed { 1 }; const bool isInputDevice, isOutputDevice; Array inputChannelInfo, outputChannelInfo; @@ -850,7 +879,7 @@ private: break; case kAudioObjectPropertyOwnedObjects: - intern->stop (false); + intern->owner.restart(); intern->owner.deviceType.triggerAsyncAudioDeviceListChange(); break; @@ -921,6 +950,7 @@ public: isStarted (false) { CoreAudioInternal* device = nullptr; + if (outputDeviceId == 0 || outputDeviceId == inputDeviceId) { jassert (inputDeviceId != 0); @@ -930,8 +960,8 @@ public: { device = new CoreAudioInternal (*this, outputDeviceId, false, true); } - jassert (device != nullptr); + jassert (device != nullptr); internal = device; AudioObjectPropertyAddress pa; @@ -985,17 +1015,16 @@ public: double sampleRate, int bufferSizeSamples) override { isOpen_ = true; - internal->xruns = 0; + if (bufferSizeSamples <= 0) bufferSizeSamples = getDefaultBufferSize(); lastError = internal->reopen (inputChannels, outputChannels, sampleRate, bufferSizeSamples); - JUCE_COREAUDIOLOG ("Opened: " << getName()); - JUCE_COREAUDIOLOG ("Latencies: " << getInputLatencyInSamples() << ' ' << getOutputLatencyInSamples()); isOpen_ = lastError.isEmpty(); + return lastError; } @@ -1069,10 +1098,16 @@ public: void restart() { - JUCE_COREAUDIOLOG ("Restarting"); - AudioIODeviceCallback* oldCallback = internal->callback; - stop(); - start (oldCallback); + if (deviceWrapperRestartCallback != nullptr) + { + deviceWrapperRestartCallback(); + } + else + { + AudioIODeviceCallback* oldCallback = internal->callback; + stop(); + start (oldCallback); + } } bool setCurrentSampleRate (double newSampleRate) @@ -1080,6 +1115,11 @@ public: return internal->setNominalSampleRate (newSampleRate); } + void setDeviceWrapperRestartCallback (const std::function& cb) + { + deviceWrapperRestartCallback = cb; + } + CoreAudioIODeviceType& deviceType; int inputIndex, outputIndex; @@ -1087,6 +1127,7 @@ private: ScopedPointer internal; bool isOpen_, isStarted; String lastError; + std::function deviceWrapperRestartCallback = nullptr; static OSStatus hardwareListenerProc (AudioDeviceID /*inDevice*/, UInt32 /*inLine*/, const AudioObjectPropertyAddress* pa, void* inClientData) { @@ -1110,7 +1151,8 @@ private: //============================================================================== class AudioIODeviceCombiner : public AudioIODevice, - private Thread + private Thread, + private Timer { public: AudioIODeviceCombiner (const String& deviceName, CoreAudioIODeviceType& deviceType) @@ -1247,6 +1289,11 @@ public: const BigInteger& outputChannels, double sampleRate, int bufferSize) override { + inputChannelsRequested = inputChannels; + outputChannelsRequested = outputChannels; + sampleRateRequested = sampleRate; + bufferSizeRequested = bufferSize; + close(); active = true; @@ -1313,6 +1360,21 @@ public: d->close(); } + void restart() + { + auto* oldCallback = callback; + + close(); + open (inputChannelsRequested, outputChannelsRequested, + sampleRateRequested, bufferSizeRequested); + start (oldCallback); + } + + void restartAsync() + { + startTimer (100); + } + BigInteger getActiveOutputChannels() const override { BigInteger chans; @@ -1409,6 +1471,10 @@ private: float** fifoWritePointers = nullptr; WaitableEvent threadInitialised; + BigInteger inputChannelsRequested, outputChannelsRequested; + double sampleRateRequested = 44100; + int bufferSizeRequested = 512; + void run() override { auto numSamples = currentBufferSize; @@ -1467,6 +1533,12 @@ private: } } + void timerCallback() override + { + stopTimer(); + restart(); + } + void shutdown (const String& error) { AudioIODeviceCallback* lastCallback = nullptr; @@ -1623,6 +1695,7 @@ private: : owner (cd), device (d), useInputs (useIns), useOutputs (useOuts) { + d->setDeviceWrapperRestartCallback ([this]() { owner.restartAsync(); }); } ~DeviceWrapper()