From b41951bc4b64d8998ac3bf502ee6e09e4f58e568 Mon Sep 17 00:00:00 2001 From: reuk Date: Thu, 22 Oct 2020 20:28:51 +0100 Subject: [PATCH] AudioProcessorGraph: Ensure nodes are prepared with correct precision Previously, if `AudioProcessorGraph::prepareToPlay` was called twice, interspersed with calls to `setProcessingPrecision`, the graph would consider the nodes 'prepared' on the second call, and wouldn't re-prepare the inner nodes with the new precision setting. graph.setProcessingPrecision (juce::AudioProcessor::singlePrecision); graph.prepareToPlay (44100, 512); graph.setProcessingPrecision (juce::AudioProcessor::doublePrecision); graph.prepareToPlay (44100, 512); // this wouldn't update the nodes Now, we always explicitly unprepare all nodes at the beginning of prepareToPlay, so that they'll always receive the newest settings. --- .../processors/juce_AudioProcessorGraph.cpp | 31 ++++++++++++++++--- .../processors/juce_AudioProcessorGraph.h | 21 +++++++++++++ 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp b/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp index fd8c358798..17c5af30c0 100644 --- a/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp +++ b/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp @@ -1265,6 +1265,22 @@ void AudioProcessorGraph::prepareToPlay (double sampleRate, int estimatedSamples { const ScopedLock sl (getCallbackLock()); setRateAndBufferSizeDetails (sampleRate, estimatedSamplesPerBlock); + + const auto newPrepareSettings = [&] + { + PrepareSettings settings; + settings.precision = getProcessingPrecision(); + settings.sampleRate = sampleRate; + settings.blockSize = estimatedSamplesPerBlock; + settings.valid = true; + return settings; + }(); + + if (prepareSettings != newPrepareSettings) + { + unprepare(); + prepareSettings = newPrepareSettings; + } } clearRenderingSequence(); @@ -1277,16 +1293,23 @@ bool AudioProcessorGraph::supportsDoublePrecisionProcessing() const return true; } +void AudioProcessorGraph::unprepare() +{ + prepareSettings.valid = false; + + isPrepared = 0; + + for (auto* n : nodes) + n->unprepare(); +} + void AudioProcessorGraph::releaseResources() { const ScopedLock sl (getCallbackLock()); cancelPendingUpdate(); - isPrepared = 0; - - for (auto* n : nodes) - n->unprepare(); + unprepare(); if (renderSequenceFloat != nullptr) renderSequenceFloat->releaseBuffers(); diff --git a/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h b/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h index a16e35df07..a60e67d845 100644 --- a/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h +++ b/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h @@ -403,6 +403,24 @@ public: void setStateInformation (const void* data, int sizeInBytes) override; private: + struct PrepareSettings + { + ProcessingPrecision precision = ProcessingPrecision::singlePrecision; + double sampleRate = 0.0; + int blockSize = 0; + bool valid = false; + + using Tied = std::tuple; + + Tied tie() const noexcept { return std::tie (precision, sampleRate, blockSize, valid); } + + bool operator== (const PrepareSettings& other) const noexcept { return tie() == other.tie(); } + bool operator!= (const PrepareSettings& other) const noexcept { return tie() != other.tie(); } + }; + //============================================================================== ReferenceCountedArray nodes; NodeID lastNodeID = {}; @@ -412,11 +430,14 @@ private: std::unique_ptr renderSequenceFloat; std::unique_ptr renderSequenceDouble; + PrepareSettings prepareSettings; + friend class AudioGraphIOProcessor; std::atomic isPrepared { false }; void topologyChanged(); + void unprepare(); void handleAsyncUpdate() override; void clearRenderingSequence(); void buildRenderingSequence();