mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-02-02 03:20:06 +00:00
AudioProcessorGraph: Ensure graph is rebuilt if any node latencies change
This commit is contained in:
parent
5ee9d24e36
commit
b8f3030e0b
1 changed files with 75 additions and 9 deletions
|
|
@ -1046,12 +1046,13 @@ private:
|
|||
|
||||
enum { readOnlyEmptyBufferIndex = 0 };
|
||||
|
||||
HashMap<uint32, int> delays;
|
||||
std::unordered_map<uint32, int> delays;
|
||||
int totalLatency = 0;
|
||||
|
||||
int getNodeDelay (NodeID nodeID) const noexcept
|
||||
{
|
||||
return delays[nodeID.uid];
|
||||
const auto iter = delays.find (nodeID.uid);
|
||||
return iter != delays.end() ? iter->second : 0;
|
||||
}
|
||||
|
||||
int getInputLatencyForNode (const Connections& c, NodeID nodeID) const
|
||||
|
|
@ -1379,7 +1380,7 @@ private:
|
|||
auto totalChans = jmax (numIns, numOuts);
|
||||
|
||||
Array<int> audioChannelsToUse;
|
||||
auto maxLatency = getInputLatencyForNode (c, node.nodeID);
|
||||
const auto maxInputLatency = getInputLatencyForNode (c, node.nodeID);
|
||||
|
||||
for (int inputChan = 0; inputChan < numIns; ++inputChan)
|
||||
{
|
||||
|
|
@ -1390,7 +1391,7 @@ private:
|
|||
node,
|
||||
inputChan,
|
||||
ourRenderingIndex,
|
||||
maxLatency);
|
||||
maxInputLatency);
|
||||
jassert (index >= 0);
|
||||
|
||||
audioChannelsToUse.add (index);
|
||||
|
|
@ -1413,10 +1414,11 @@ private:
|
|||
if (processor.producesMidi())
|
||||
midiBuffers.getReference (midiBufferToUse).channel = { node.nodeID, midiChannelIndex };
|
||||
|
||||
delays.set (node.nodeID.uid, maxLatency + processor.getLatencySamples());
|
||||
const auto thisNodeLatency = maxInputLatency + processor.getLatencySamples();
|
||||
delays[node.nodeID.uid] = thisNodeLatency;
|
||||
|
||||
if (numOuts == 0)
|
||||
totalLatency = maxLatency;
|
||||
totalLatency = jmax (totalLatency, thisNodeLatency);
|
||||
|
||||
sequence.addProcessOp (node, audioChannelsToUse, totalChans, midiBufferToUse);
|
||||
}
|
||||
|
|
@ -1548,6 +1550,25 @@ private:
|
|||
SequenceAndLatency sequence;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/* Holds information about the properties of a graph node at the point it was prepared.
|
||||
|
||||
If the bus layout or latency of a given node changes, the graph should be rebuilt so
|
||||
that channel connections are ordered correctly, and the graph's internal delay lines have
|
||||
the correct delay.
|
||||
*/
|
||||
class NodeAttributes
|
||||
{
|
||||
auto tie() const { return std::tie (layout, latencySamples); }
|
||||
|
||||
public:
|
||||
AudioProcessor::BusesLayout layout;
|
||||
int latencySamples = 0;
|
||||
|
||||
bool operator== (const NodeAttributes& other) const { return tie() == other.tie(); }
|
||||
bool operator!= (const NodeAttributes& other) const { return tie() != other.tie(); }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/* Holds information about a particular graph configuration, without sharing ownership of any
|
||||
graph nodes. Can be checked for equality with other RenderSequenceSignature instances to see
|
||||
|
|
@ -1565,7 +1586,7 @@ public:
|
|||
bool operator!= (const RenderSequenceSignature& other) const { return tie() != other.tie(); }
|
||||
|
||||
private:
|
||||
using NodeMap = std::map<AudioProcessorGraph::NodeID, AudioProcessor::BusesLayout>;
|
||||
using NodeMap = std::map<AudioProcessorGraph::NodeID, NodeAttributes>;
|
||||
|
||||
static NodeMap getNodeMap (const Nodes& n)
|
||||
{
|
||||
|
|
@ -1573,7 +1594,12 @@ private:
|
|||
NodeMap result;
|
||||
|
||||
for (const auto& node : nodeRefs)
|
||||
result.emplace (node->nodeID, node->getProcessor()->getBusesLayout());
|
||||
{
|
||||
auto* proc = node->getProcessor();
|
||||
result.emplace (node->nodeID,
|
||||
NodeAttributes { proc->getBusesLayout(),
|
||||
proc->getLatencySamples() });
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -1912,7 +1938,6 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
AudioProcessorGraph* owner = nullptr;
|
||||
Nodes nodes;
|
||||
Connections connections;
|
||||
|
|
@ -2204,6 +2229,42 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
beginTest ("rebuilding the graph recalculates overall latency");
|
||||
{
|
||||
AudioProcessorGraph graph;
|
||||
|
||||
const auto nodeA = graph.addNode (BasicProcessor::make (BasicProcessor::getStereoProperties(), MidiIn::no, MidiOut::no))->nodeID;
|
||||
const auto nodeB = graph.addNode (BasicProcessor::make (BasicProcessor::getStereoProperties(), MidiIn::no, MidiOut::no))->nodeID;
|
||||
const auto final = graph.addNode (BasicProcessor::make (BasicProcessor::getInputOnlyProperties(), MidiIn::no, MidiOut::no))->nodeID;
|
||||
|
||||
expect (graph.addConnection ({ { nodeA, 0 }, { nodeB, 0 } }));
|
||||
expect (graph.addConnection ({ { nodeA, 1 }, { nodeB, 1 } }));
|
||||
expect (graph.addConnection ({ { nodeB, 0 }, { final, 0 } }));
|
||||
expect (graph.addConnection ({ { nodeB, 1 }, { final, 1 } }));
|
||||
|
||||
expect (graph.getLatencySamples() == 0);
|
||||
|
||||
// Graph isn't built, latency is 0 if prepareToPlay hasn't been called yet
|
||||
const auto nodeALatency = 100;
|
||||
graph.getNodeForId (nodeA)->getProcessor()->setLatencySamples (nodeALatency);
|
||||
graph.rebuild();
|
||||
expect (graph.getLatencySamples() == 0);
|
||||
|
||||
graph.prepareToPlay (44100, 512);
|
||||
|
||||
expect (graph.getLatencySamples() == nodeALatency);
|
||||
|
||||
const auto nodeBLatency = 200;
|
||||
graph.getNodeForId (nodeB)->getProcessor()->setLatencySamples (nodeBLatency);
|
||||
graph.rebuild();
|
||||
expect (graph.getLatencySamples() == nodeALatency + nodeBLatency);
|
||||
|
||||
const auto finalLatency = 300;
|
||||
graph.getNodeForId (final)->getProcessor()->setLatencySamples (finalLatency);
|
||||
graph.rebuild();
|
||||
expect (graph.getLatencySamples() == nodeALatency + nodeBLatency + finalLatency);
|
||||
}
|
||||
|
||||
beginTest ("large render sequence can be built");
|
||||
{
|
||||
AudioProcessorGraph graph;
|
||||
|
|
@ -2275,6 +2336,11 @@ private:
|
|||
return std::make_unique<BasicProcessor> (layout, midiIn, midiOut);
|
||||
}
|
||||
|
||||
static BusesProperties getInputOnlyProperties()
|
||||
{
|
||||
return BusesProperties().withInput ("in", AudioChannelSet::stereo());
|
||||
}
|
||||
|
||||
static BusesProperties getStereoProperties()
|
||||
{
|
||||
return BusesProperties().withInput ("in", AudioChannelSet::stereo())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue