Browse Source

AudioProcessorGraph: Avoid data race when setting parent on AudioGraphIOProcessor

v7.0.9
reuk 3 years ago
parent
commit
3d09a98826
No known key found for this signature in database GPG Key ID: FCB43929F012EE5C
1 changed files with 148 additions and 99 deletions
  1. +148
    -99
      modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp

+ 148
- 99
modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp View File

@@ -500,8 +500,17 @@ struct GraphRenderSequence
{
using Node = AudioProcessorGraph::Node;
struct GlobalIO
{
AudioBuffer<FloatType>& audioIn;
AudioBuffer<FloatType>& audioOut;
MidiBuffer& midiIn;
MidiBuffer& midiOut;
};
struct Context
{
GlobalIO globalIO;
AudioPlayHead* audioPlayHead;
int numSamples;
};
@@ -540,7 +549,12 @@ struct GraphRenderSequence
currentMidiOutputBuffer.clear();
{
const Context context { audioPlayHead, numSamples };
const Context context { { *currentAudioInputBuffer,
currentAudioOutputBuffer,
*currentMidiInputBuffer,
currentMidiOutputBuffer },
audioPlayHead,
numSamples };
for (const auto& op : renderOps)
op->process (context);
@@ -748,7 +762,30 @@ struct GraphRenderSequence
int totalNumChans,
int midiBuffer)
{
renderOps.push_back (std::make_unique<ProcessOp> (node, audioChannelsUsed, totalNumChans, midiBuffer));
auto op = [&]() -> std::unique_ptr<NodeOp>
{
if (auto* ioNode = dynamic_cast<const AudioProcessorGraph::AudioGraphIOProcessor*> (node->getProcessor()))
{
switch (ioNode->getType())
{
case AudioProcessorGraph::AudioGraphIOProcessor::audioInputNode:
return std::make_unique<AudioInOp> (node, audioChannelsUsed, totalNumChans, midiBuffer);
case AudioProcessorGraph::AudioGraphIOProcessor::audioOutputNode:
return std::make_unique<AudioOutOp> (node, audioChannelsUsed, totalNumChans, midiBuffer);
case AudioProcessorGraph::AudioGraphIOProcessor::midiInputNode:
return std::make_unique<MidiInOp> (node, audioChannelsUsed, totalNumChans, midiBuffer);
case AudioProcessorGraph::AudioGraphIOProcessor::midiOutputNode:
return std::make_unique<MidiOutOp> (node, audioChannelsUsed, totalNumChans, midiBuffer);
}
}
return std::make_unique<ProcessOp> (node, audioChannelsUsed, totalNumChans, midiBuffer);
}();
renderOps.push_back (std::move (op));
}
void prepareBuffers (int blockSize)
@@ -796,12 +833,12 @@ private:
virtual void process (const Context&) = 0;
};
struct ProcessOp : public RenderOp
struct NodeOp : public RenderOp
{
ProcessOp (const Node::Ptr& n,
const Array<int>& audioChannelsUsed,
int totalNumChans,
int midiBufferIndex)
NodeOp (const Node::Ptr& n,
const Array<int>& audioChannelsUsed,
int totalNumChans,
int midiBufferIndex)
: node (n),
processor (*n->getProcessor()),
audioChannelsToUse (audioChannelsUsed),
@@ -812,7 +849,7 @@ private:
audioChannelsToUse.add (0);
}
void prepare (FloatType* const* renderBuffer, MidiBuffer* buffers) override
void prepare (FloatType* const* renderBuffer, MidiBuffer* buffers) final
{
for (size_t i = 0; i < audioChannels.size(); ++i)
audioChannels[i] = renderBuffer[audioChannelsToUse.getUnchecked ((int) i)];
@@ -820,7 +857,7 @@ private:
midiBuffer = buffers + midiBufferToUse;
}
void process (const Context& c) override
void process (const Context& c) final
{
processor.setPlayHead (c.audioPlayHead);
@@ -835,59 +872,125 @@ private:
AudioBuffer<FloatType> buffer { audioChannels.data(), numAudioChannels, c.numSamples };
const ScopedLock lock (processor.getCallbackLock());
if (processor.isSuspended())
{
buffer.clear();
}
else
callProcess (buffer, *midiBuffer);
{
const auto bypass = node->isBypassed() && processor.getBypassParameter() == nullptr;
processWithBuffer (c.globalIO, bypass, buffer, *midiBuffer);
}
}
virtual void processWithBuffer (const GlobalIO&, bool bypass, AudioBuffer<FloatType>& audio, MidiBuffer& midi) = 0;
const Node::Ptr node;
AudioProcessor& processor;
MidiBuffer* midiBuffer = nullptr;
Array<int> audioChannelsToUse;
std::vector<FloatType*> audioChannels;
const int midiBufferToUse;
};
struct ProcessOp : public NodeOp
{
using NodeOp::NodeOp;
void processWithBuffer (const GlobalIO&, bool bypass, AudioBuffer<FloatType>& audio, MidiBuffer& midi) final
{
callProcess (bypass, audio, midi);
}
void callProcess (AudioBuffer<float>& buffer, MidiBuffer& midi)
void callProcess (bool bypass, AudioBuffer<float>& buffer, MidiBuffer& midi)
{
if (processor.isUsingDoublePrecision())
if (this->processor.isUsingDoublePrecision())
{
tempBufferDouble.makeCopyOf (buffer, true);
process (*node, tempBufferDouble, midi);
processImpl (bypass, this->processor, tempBufferDouble, midi);
buffer.makeCopyOf (tempBufferDouble, true);
}
else
{
process (*node, buffer, midi);
processImpl (bypass, this->processor, buffer, midi);
}
}
void callProcess (AudioBuffer<double>& buffer, MidiBuffer& midi)
void callProcess (bool bypass, AudioBuffer<double>& buffer, MidiBuffer& midi)
{
if (processor.isUsingDoublePrecision())
if (this->processor.isUsingDoublePrecision())
{
process (*node, buffer, midi);
processImpl (bypass, this->processor, buffer, midi);
}
else
{
tempBufferFloat.makeCopyOf (buffer, true);
process (*node, tempBufferFloat, midi);
processImpl (bypass, this->processor, tempBufferFloat, midi);
buffer.makeCopyOf (tempBufferFloat, true);
}
}
template <typename Value>
static void process (const Node& node, AudioBuffer<Value>& audio, MidiBuffer& midi)
static void processImpl (bool bypass, AudioProcessor& p, AudioBuffer<Value>& audio, MidiBuffer& midi)
{
if (node.isBypassed() && node.getProcessor()->getBypassParameter() == nullptr)
node.getProcessor()->processBlockBypassed (audio, midi);
if (bypass)
p.processBlockBypassed (audio, midi);
else
node.getProcessor()->processBlock (audio, midi);
p.processBlock (audio, midi);
}
const Node::Ptr node;
AudioProcessor& processor;
MidiBuffer* midiBuffer = nullptr;
Array<int> audioChannelsToUse;
std::vector<FloatType*> audioChannels;
AudioBuffer<float> tempBufferFloat, tempBufferDouble;
const int midiBufferToUse;
};
struct MidiInOp : public NodeOp
{
using NodeOp::NodeOp;
void processWithBuffer (const GlobalIO& g, bool bypass, AudioBuffer<FloatType>& audio, MidiBuffer& midi) final
{
if (! bypass)
midi.addEvents (g.midiIn, 0, audio.getNumSamples(), 0);
}
};
struct MidiOutOp : public NodeOp
{
using NodeOp::NodeOp;
void processWithBuffer (const GlobalIO& g, bool bypass, AudioBuffer<FloatType>& audio, MidiBuffer& midi) final
{
if (! bypass)
g.midiOut.addEvents (midi, 0, audio.getNumSamples(), 0);
}
};
struct AudioInOp : public NodeOp
{
using NodeOp::NodeOp;
void processWithBuffer (const GlobalIO& g, bool bypass, AudioBuffer<FloatType>& audio, MidiBuffer&) final
{
if (bypass)
return;
for (int i = jmin (g.audioIn.getNumChannels(), audio.getNumChannels()); --i >= 0;)
audio.copyFrom (i, 0, g.audioIn, i, 0, audio.getNumSamples());
}
};
struct AudioOutOp : public NodeOp
{
using NodeOp::NodeOp;
void processWithBuffer (const GlobalIO& g, bool bypass, AudioBuffer<FloatType>& audio, MidiBuffer&) final
{
if (bypass)
return;
for (int i = jmin (g.audioOut.getNumChannels(), audio.getNumChannels()); --i >= 0;)
g.audioOut.addFrom (i, 0, audio, i, 0, audio.getNumSamples());
}
};
std::vector<std::unique_ptr<RenderOp>> renderOps;
@@ -1430,15 +1533,6 @@ public:
jassertfalse; // Not prepared for this audio format!
}
template <typename FloatType>
void processIO (AudioGraphIOProcessor& io, AudioBuffer<FloatType>& audio, MidiBuffer& midi)
{
if (auto* s = std::get_if<GraphRenderSequence<FloatType>> (&sequence.sequence))
processIOBlock (io, *s, audio, midi);
else
jassertfalse; // Not prepared for this audio format!
}
int getLatencySamples() const { return sequence.latencySamples; }
PrepareSettings getSettings() const { return settings; }
@@ -1457,47 +1551,6 @@ private:
visitRenderSequence (*this, [&] (auto& seq) { seq.prepareBuffers (settings.blockSize); });
}
template <typename FloatType, typename SequenceType>
static void processIOBlock (AudioGraphIOProcessor& io,
SequenceType& sequence,
AudioBuffer<FloatType>& buffer,
MidiBuffer& midiMessages)
{
switch (io.getType())
{
case AudioGraphIOProcessor::audioOutputNode:
{
auto&& currentAudioOutputBuffer = sequence.currentAudioOutputBuffer;
for (int i = jmin (currentAudioOutputBuffer.getNumChannels(), buffer.getNumChannels()); --i >= 0;)
currentAudioOutputBuffer.addFrom (i, 0, buffer, i, 0, buffer.getNumSamples());
break;
}
case AudioGraphIOProcessor::audioInputNode:
{
auto* currentInputBuffer = sequence.currentAudioInputBuffer;
for (int i = jmin (currentInputBuffer->getNumChannels(), buffer.getNumChannels()); --i >= 0;)
buffer.copyFrom (i, 0, *currentInputBuffer, i, 0, buffer.getNumSamples());
break;
}
case AudioGraphIOProcessor::midiOutputNode:
sequence.currentMidiOutputBuffer.addEvents (midiMessages, 0, buffer.getNumSamples(), 0);
break;
case AudioGraphIOProcessor::midiInputNode:
midiMessages.addEvents (*sequence.currentMidiInputBuffer, 0, buffer.getNumSamples(), 0);
break;
default:
break;
}
}
PrepareSettings settings;
SequenceAndLatency sequence;
};
@@ -2000,20 +2053,16 @@ bool AudioProcessorGraph::AudioGraphIOProcessor::supportsDoublePrecisionProcessi
return true;
}
void AudioProcessorGraph::AudioGraphIOProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
void AudioProcessorGraph::AudioGraphIOProcessor::processBlock (AudioBuffer<float>&, MidiBuffer&)
{
jassert (graph != nullptr);
if (auto* state = graph->pimpl->getAudioThreadState())
state->processIO (*this, buffer, midiMessages);
// The graph should never call this!
jassertfalse;
}
void AudioProcessorGraph::AudioGraphIOProcessor::processBlock (AudioBuffer<double>& buffer, MidiBuffer& midiMessages)
void AudioProcessorGraph::AudioGraphIOProcessor::processBlock (AudioBuffer<double>&, MidiBuffer&)
{
jassert (graph != nullptr);
if (auto* state = graph->pimpl->getAudioThreadState())
state->processIO (*this, buffer, midiMessages);
// The graph should never call this!
jassertfalse;
}
double AudioProcessorGraph::AudioGraphIOProcessor::getTailLengthSeconds() const
@@ -2051,15 +2100,15 @@ void AudioProcessorGraph::AudioGraphIOProcessor::setParentGraph (AudioProcessorG
{
graph = newGraph;
if (graph != nullptr)
{
setPlayConfigDetails (type == audioOutputNode ? graph->getTotalNumOutputChannels() : 0,
type == audioInputNode ? graph->getTotalNumInputChannels() : 0,
getSampleRate(),
getBlockSize());
if (graph == nullptr)
return;
updateHostDisplay();
}
setPlayConfigDetails (type == audioOutputNode ? newGraph->getTotalNumOutputChannels() : 0,
type == audioInputNode ? newGraph->getTotalNumInputChannels() : 0,
getSampleRate(),
getBlockSize());
updateHostDisplay();
}
//==============================================================================


Loading…
Cancel
Save