diff --git a/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp b/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp index aeedc0d23f..90d54e382b 100644 --- a/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp +++ b/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp @@ -1695,6 +1695,7 @@ private: CriticalSection closeLock; int targetLatency = 0; std::atomic xruns { -1 }; + std::atomic lastValidReadPosition { invalidSampleTime }; BigInteger inputChannelsRequested, outputChannelsRequested; double sampleRateRequested = 44100; @@ -1792,8 +1793,9 @@ private: } auto currentWritePos = writePos.load(); + const auto nextWritePos = currentWritePos + static_cast (n); - writePos.compare_exchange_strong (currentWritePos, currentWritePos + static_cast (n)); + writePos.compare_exchange_strong (currentWritePos, nextWritePos); if (currentWritePos == invalidSampleTime) return; @@ -1817,6 +1819,11 @@ private: scratchBuffer.getReadPointer (args.channel, args.inputPos), args.nItems); }); + + { + auto invalid = invalidSampleTime; + lastValidReadPosition.compare_exchange_strong (invalid, nextWritePos); + } } void outputAudioCallback (float* const* channels, int numChannels, int n) noexcept @@ -1839,16 +1846,29 @@ private: } } - accessFifo (currentReadPos, numChannels, n, [&] (const auto& args) + // If there was an xrun, we want to output zeros until we're sure that there's some valid + // input for us to read. + const auto longN = static_cast (n); + const auto nextReadPos = currentReadPos + longN; + const auto validReadPos = lastValidReadPosition.load(); + const auto sanitisedValidReadPos = validReadPos != invalidSampleTime ? validReadPos : nextReadPos; + const auto numZerosToWrite = sanitisedValidReadPos <= currentReadPos + ? 0 + : jmin (longN, sanitisedValidReadPos - currentReadPos); + + for (auto i = 0; i < numChannels; ++i) + std::fill (channels[i], channels[i] + numZerosToWrite, 0.0f); + + accessFifo (currentReadPos + numZerosToWrite, numChannels, static_cast (longN - numZerosToWrite), [&] (const auto& args) { - FloatVectorOperations::copy (channels[args.channel] + args.inputPos, + FloatVectorOperations::copy (channels[args.channel] + args.inputPos + numZerosToWrite, fifo.getReadPointer (args.channel, args.fifoPos), args.nItems); }); // use compare exchange here as we need to avoid the case // where we overwrite readPos being equal to invalidSampleTime - readPos.compare_exchange_strong (currentReadPos, currentReadPos + static_cast (n)); + readPos.compare_exchange_strong (currentReadPos, nextReadPos); } void xrun() noexcept @@ -1987,7 +2007,7 @@ private: auto copy = invalidSampleTime; if (sampleTime.compare_exchange_strong (copy, callbackSampleTime) && (! input)) - owner.fifo.clear(); + owner.lastValidReadPosition = invalidSampleTime; } bool isInput() const { return input; }