Transferring audio data between AudioIOCallbacks

Hello there! I have two classes that inherit from juce::AudioSource, responsible for processing input sound. These classes are added to different AudioDeviceManager instances as AudioDeviceIOCallback, to play on two different output devices simultaneously.

I have two AudioDeviceManagers devman1, devman2
and two AudioSourcePlayers sp1, sp2.

sp1’s source is audioSource1, and sp1 is registered to devman1.
sp2’s source is audioSource2 and sp2 is registered to devman2.

The sound processing is done in audioSource1. Now, I need to figure out how to copy a block of audio that is processed by audioSource1 to audioSource2. Can you guide me on how to achieve this?

Create a super-class derived from AudioSource and include a shared memory segment that both instances can use to copy data quickly to each other? Just an idea…

2 Likes

I think both AudioIODevices will most likely run on different threads.

What I would do is to declare one to be my master (as mental model), which has an input and output FIFO where the other device can push and pull from.
And you need to have a strategy to allow drift for the clocks and resampling, when they run on different sample rates.

3 Likes

Thanks! That’s the way how I’ve just solved the problem exactly

Thank you very much for your suggestion. Yes, I’ll be working on that as the next step.

Thanks mate

No need for FIFO latency, threads can easily have a common shared memory region for their purposes …

So data races of different threads accessing the same memory is fake news? :thinking:

I don’t think this is good advice

1 Like

Of course its not good news to have a data race … this is why you use standard practices to guard access … mutex’s, etc. and ensure that your threads are well-behaved, as is your responsibility as a developer. Straw man argument = fake news.

This isn’t just good advice, its kind of a fundamental principle of modern software architecture. Your words wouldn’t be making its way across the ether to pollute my eyeballs if it wasn’t already a well established practice to have two well-behaved threads sharing memory with each other …

Apologies for my wording.

So to get back from personal:

Do you happen to know if different audio drivers run on different threads?

I also don’t think, that this is a good advice! Avoid system calls all together when handling with realtime trait is one of the most essential tips anyone will ever give you when working with the audio threads.
I also don’t grasp the concept on what FIFO latency you are referring to. Stuff gets copied by the input source, and as soon as the copy operation is done, the output source may grab it. I wouldn’t speak of latency neither with a FIFO nor with mutex. When nothing is available in the FIFO when the output wants to play audio data, then nothing is being played or when the input is still writing and holding the mutex lock while the output would like to access the data, the output waits for the input to finish. I don’t see any actually defined latency being involved.

In addition, a FIFO is a perfectly good example of handling shared memory. The JUCE AbstractFifo actually makes the perfect case for that. The class itself does even hold the actual data being stored or read. It just tells the callees which regions of the data is save to be read from and written to, all while ensuring there will be no race conditions, exactly the same the mutex would be doing expect it is lock free.
And then finally: even when using mutexex, you’d still be better off with a FIFO structure nonetheless (even when it is not lock free). A FIFO Queue is an excellent choice for what we are trying to achieve here. There is stuff coming in at unknown times, and there should stuff be flushed at unknown times in uncertain intervals. We want to make sure what comes in first, is flushed first and we’d like a little bit of buffer, when input was called ahead of time, or output was not picked up caused by a lag of some sort. Making it a ring buffer would also make total sense, assuring that the input is allowed to overwrite the most outdated data when the output is behind by a lot of calls.

There is a similar discussion going on here: Using different audio devices at the same time? - #4 by Rincewind