1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

AudioProcessorGraph: Remove unnecessary precision conversion buffer

The double-precision buffer is unnecessary because internal nodes should
be prepared using the same precision as the graph itself, and all
AudioProcessors *must* support single-precision processing. Therefore,
if the graph is prepared to use single-precision, then all inner nodes
*must* also use single-precision.

This change also shares the remaining temporary buffer.
This commit is contained in:
reuk 2025-03-31 12:39:49 +01:00
parent a31cadaa18
commit 5297df9995
No known key found for this signature in database

View file

@ -788,7 +788,11 @@ struct GraphRenderSequence
}
}
return std::make_unique<ProcessOp> (node, audioChannelsUsed, totalNumChans, midiBuffer);
return std::make_unique<ProcessOp> (node,
audioChannelsUsed,
totalNumChans,
midiBuffer,
*precisionConversionBuffer);
}();
renderOps.push_back (std::move (op));
@ -801,6 +805,8 @@ struct GraphRenderSequence
currentAudioOutputBuffer.setSize (numBuffersNeeded + 1, blockSize);
currentAudioOutputBuffer.clear();
precisionConversionBuffer->setSize (numBuffersNeeded, blockSize);
currentMidiOutputBuffer.clear();
midiBuffers.clearQuick();
@ -898,7 +904,14 @@ private:
struct ProcessOp final : public NodeOp
{
using NodeOp::NodeOp;
ProcessOp (const Node::Ptr& n,
const Array<int>& audioChannelsUsed,
int totalNumChans,
int midiBufferIndex,
AudioBuffer<float>& tempBuffer)
: NodeOp (n, audioChannelsUsed, totalNumChans, midiBufferIndex),
temporaryBuffer (tempBuffer)
{}
void processWithBuffer (const GlobalIO&, bool bypass, AudioBuffer<FloatType>& audio, MidiBuffer& midi) final
{
@ -909,9 +922,14 @@ private:
{
if (this->processor.isUsingDoublePrecision())
{
tempBufferDouble.makeCopyOf (buffer, true);
processImpl (bypass, this->processor, tempBufferDouble, midi);
buffer.makeCopyOf (tempBufferDouble, true);
// The graph is processing in single-precision, but this node is expecting a
// double-precision buffer. If the graph is using single-precision, it
// should also have set its internal nodes to use single-precision
// during prepareToPlay(). You should avoid calling setProcessingPrecision()
// directly on processors within an AudioProcessorGraph.
jassertfalse;
buffer.clear();
midi.clear();
}
else
{
@ -927,9 +945,11 @@ private:
}
else
{
tempBufferFloat.makeCopyOf (buffer, true);
processImpl (bypass, this->processor, tempBufferFloat, midi);
buffer.makeCopyOf (tempBufferFloat, true);
// This branch will be taken if the graph is configured for double-precision but
// this node only supports single-precision.
temporaryBuffer.makeCopyOf (buffer, true);
processImpl (bypass, this->processor, temporaryBuffer, midi);
buffer.makeCopyOf (temporaryBuffer, true);
}
}
@ -942,7 +962,7 @@ private:
p.processBlock (audio, midi);
}
AudioBuffer<float> tempBufferFloat, tempBufferDouble;
AudioBuffer<float>& temporaryBuffer;
};
struct MidiInOp final : public NodeOp
@ -996,6 +1016,8 @@ private:
};
std::vector<std::unique_ptr<RenderOp>> renderOps;
std::unique_ptr<AudioBuffer<float>> precisionConversionBuffer = std::make_unique<AudioBuffer<float>>();
};
//==============================================================================
@ -2246,6 +2268,63 @@ public:
expect (graph.getLatencySamples() == nodeALatency + nodeBLatency + finalLatency);
}
beginTest ("nodes use double precision if supported");
{
AudioProcessorGraph graph;
constexpr auto blockSize = 512;
AudioBuffer<float> bufferFloat (2, blockSize);
AudioBuffer<double> bufferDouble (2, blockSize);
MidiBuffer midi;
auto processorOwner = BasicProcessor::make (BasicProcessor::getStereoProperties(), MidiIn::no, MidiOut::no);
auto* processor = processorOwner.get();
graph.addNode (std::move (processorOwner));
// Process in single-precision
{
graph.setProcessingPrecision (AudioProcessor::singlePrecision);
graph.prepareToPlay (44100.0, blockSize);
graph.processBlock (bufferFloat, midi);
expect (processor->getProcessingPrecision() == AudioProcessor::singlePrecision);
expect (processor->getLastBlockPrecision() == AudioProcessor::singlePrecision);
graph.releaseResources();
}
// Process in double-precision
{
graph.setProcessingPrecision (AudioProcessor::doublePrecision);
graph.prepareToPlay (44100.0, blockSize);
graph.processBlock (bufferDouble, midi);
expect (processor->getProcessingPrecision() == AudioProcessor::doublePrecision);
expect (processor->getLastBlockPrecision() == AudioProcessor::doublePrecision);
graph.releaseResources();
}
// Process in double-precision when node only supports single-precision
{
processor->setSupportsDoublePrecisionProcessing (false);
graph.setProcessingPrecision (AudioProcessor::doublePrecision);
graph.prepareToPlay (44100.0, blockSize);
graph.processBlock (bufferDouble, midi);
expect (processor->getProcessingPrecision() == AudioProcessor::singlePrecision);
expect (processor->getLastBlockPrecision() == AudioProcessor::singlePrecision);
graph.releaseResources();
}
// It's not possible for the node to *only* support double-precision.
// It's also not possible to prepare the graph in single-precision mode, and then
// to set an individual node into double-precision mode. This would require calling
// prepareToPlay() on an individual node after preparing the graph as a whole, which is
// not a supported usage pattern.
}
beginTest ("large render sequence can be built");
{
AudioProcessorGraph graph;
@ -2302,15 +2381,22 @@ private:
void setStateInformation (const void*, int) override {}
void prepareToPlay (double, int) override {}
void releaseResources() override {}
void processBlock (AudioBuffer<float>&, MidiBuffer&) override {}
bool supportsDoublePrecisionProcessing() const override { return true; }
bool supportsDoublePrecisionProcessing() const override { return doublePrecisionSupported; }
bool isMidiEffect() const override { return {}; }
void reset() override {}
void setNonRealtime (bool) noexcept override {}
using AudioProcessor::processBlock;
void processBlock (AudioBuffer<float>&, MidiBuffer&) override
{
blockPrecision = singlePrecision;
}
static std::unique_ptr<AudioProcessor> make (const BusesProperties& layout,
void processBlock (AudioBuffer<double>&, MidiBuffer&) override
{
blockPrecision = doublePrecision;
}
static std::unique_ptr<BasicProcessor> make (const BusesProperties& layout,
MidiIn midiIn,
MidiOut midiOut)
{
@ -2334,9 +2420,15 @@ private:
.withOutput ("out", AudioChannelSet::discreteChannels (numChannels));
}
void setSupportsDoublePrecisionProcessing (bool x) { doublePrecisionSupported = x; }
ProcessingPrecision getLastBlockPrecision() const { return blockPrecision; }
private:
MidiIn midiIn;
MidiOut midiOut;
ProcessingPrecision blockPrecision = ProcessingPrecision (-1); // initially invalid
bool doublePrecisionSupported = true;
};
};