Audio Router 4096x4096 channels - Best approach

Hi there. I will appreciate very much your view on what the best strategies, methods or classes would be for a large number of channels being routed.

I am more interested on the architecture of such an application in terms of it being efficient processing these many channels and being able to make or delete connections at runtime.

More specifically, what classes or methods would prove to be more efficient for copying the buffers from the input to the output. Will vectorization help with the copy function?

A little background: 48 kHz, 32f and fixed sample size. Buffers can be of up to 64 channels each.

Here is a picture for better clarity and thanks so much for chiming in.

1 Like

For efficient copying, use juce::FloatVectorOperations::copy.

For an example of a dynamically patchable routing system, look at the AudioProcessorGraph class.

I’m not sure if JUCE supports handling this many input and output channels at once, I’ve never tried it.

I would recommend always using the VectorOperations for audio operations; at worst, it’s memcpy under the hood, but it will leverage explicit SIMD accelerated instructions on some systems.

You could build a ‘CommandList’ structure when the user changes the configuration. It could be a POD containing the destination, source pointers, and copy length info. Then, your audio callback would just become a for loop that ‘blasts’ a load of copies in sequence.

The plus side with a CommandList is you can calculate dependencies and split the workload between workers in a thread pool.

Thanks so much replying!

I will dive in the documentation about vector operations.

I had found another class that is supposedly using SIMD instructions too, and seems very specificly geared towards copying channels.

What do you think about using AudioBuffer::copyFrom instead of vector operations?

https://docs.juce.com/master/classAudioBuffer.html#a9ec751bfa23564c011bf3940ca17b743

Thanks!

Pablo

Good choice. It will call internally the FloatVectorOperations:

1 Like

The fastest would be to avoid copying to output buffers and have an array of output pointers instead.

Wow @stenzel sounds interesting. Any caveats?

This would eliminate the need to copy when you are not processing the audio. Why isn’t the norm with channel mappings for instance?

There is not enough information in the OP, but you cannot change the pointers you are given in the processBlock of a plugin, neither the pointers in the AudioIODeviceCallback, so I don’t think that strategy is an option here.

@daniel one additional piece of information: this is a standalone app, and we will not use the AudioIO to receive or send the audio, but another network transport. Is this a different scenario where the pointers might work?

That totally depends with what you are interfacing, but the usual flow is the driver hands you an array of sample array pointers where it expects you to place your signal. I am not aware of any system where you tell the outside where to find the audio buffers, but it might exist.

Ok everyone. I have managed to do a quick demo of using the getReadPointers from the input buffers channels, rearrange them and create the output buffer.

The input buffers are being created using the AudioBuffer::copyFrom method extracting the data from the input sources (not via processBlock). Then, using the getReadPointer method I create an output array of the pointers to the input buffers, but rearranging the channels. That new array of pointers is being fed to an AudioBuffer to create the outputBuffer.

My question now is, would I need to implement a FIFO on the input buffers to avoid any issues between copyFrom writing and getSample reading the data from this outputBuffer which is created from an array of pointers to the input buffers?