Hey there,
I’ve encountered a latency compensation bug in the JUCE AudioProcessorGraph.
It happens when a node’s output channel is connected to multiple destination nodes with a larger delay, and the destination input channel indices are greater than the numOuts the respective destination nodes.
In this scenario, the audio graph does not add a copyChannelOp and applies latency compensation for each destination node. As addChannelDelayOp is an in-place op, this results in a delay applied twice to the second node, resulting in misaligned audio.
I’ve put together a simple reproducible example in this gist.
The graph is constructed as follows:
- A
ModStepnode generates a step signal (value 0.4) between samples 30 and 60. - The
ModStepnode output channel 0 is connected to bothNode1(ch 1) andNode2(ch 1). Node0introduces 100 samples of latency.
The signal path is:
Input → Node0 → Node1 → Node2 → Output
with the ModStep signal injected into Node1 and Node2 in parallel.
All the nodes (but ModStep) simply add all their input channels together in the first output channel.
The expected output is a step of amplitude 0.8 between sample 130 and 160 (30 to 60, + 100 latency) in the output buffer:
Index: 0 --- 129 130 --- 159 160 ---
Value: 0.00 --- 0.00 0.80 --- 0.80 0.00 ---
But instead, we get two copies of the original step signal, one at the expected position, and one with another 100 samples delay:
Index: 0 --- 129 130 --- 159 160 --- 229 230 --- 259 260 ---
Value: 0.00 --- 0.00 0.40 --- 0.40 0.00 --- 0.00 0.40 --- 0.40 0.00 ---
When different output channels are used, there’s no issue, and we get the expected behavior (use_same_out_channel_for_step = false in the example). Also, it works just fine when there’s no latency compensation to apply.
I don’t think I saw in the documentation that one should use different output channels for each destination node, so I believe this is a bug.
I modified the JUCE graph code on my end to add a copyChannelOp in this case (and mark the used buffer as assigned, even if inputChan >= numOuts), and it fixes the issue by doing some more copies (some could be avoided though).
But I think it should be fixed in the JUCE codebase itself. Let me know if you need more details or if I can help in any way.
