How to process and render multiple sources with AudioProcessor

Hi folks!

I’m currently developing a vst where I’ve to process multi audio source audio and I’m wondering what is the best architecture to do it.

I’ve 2 subclasses of AudioTransportSource, one for the vocal and one for the background.

Each of them has its own AudioProcessor (its custom processor chain). The process is applied in getNextAudioBlock overriding.

This 2 tracks are added to a MixerAudioSource.

Everything works great in real time.

The final result, which is the rendered vocal track with the applied effect works well too.

However, now, I will have to do a offline preprocessing effect before rendering the vocal track. This effect needs to get the whole audio data array and the 2 tracks. I need to process the background track to extract some feature and use them in the vocal track AudioProcessor as parameter. It would be like this:

backgroundData -> offlineBackgroundAudioProcessor -> processedBackgroundDataInformation

vocalData + processedBackgroundDataInformation -> offlineVocalAudioProcessor -> final audio

My question is how can I manage to do it?

My first solution would be to do 2 offline audio processors, BackgroundAudioProcessor and VocalAudioProcessor. And enable the BackgroundAudioProcessor to give an getter to its result. Also an offline renderer mixer will take care of the processing queue.

It will look like this:

AudioFormatReader* backgroundReader, 
AudioProcessor* backgroundAudioProcessor,
AudioFormatReader* vocalReader, 
AudioProcessor* vocalAudioProcessor
) {

  //… all stuff to create audio buffers and prepare the processors

  let backgroundInformation = backgroundAudioProcessor.getResult();

  // vocalBuffer is the result of the process

What do you think about it? I feel that a better solution could be found but I cannot manage to know which one

After looking for other solution, I came to an other usage. Using an AudioBlock that will contain the vocal and the background data.

auto backgroundBuffer = getAudioBufferFromReader(backgroundReader);
auto vocalBuffer = getAudioBufferFromReader(vocalReader);
auto mergedBuffer = mergeBuffers(backgroundBuffer, vocalBuffer);
dsp::AudioBlock<float> mergedBlock (buffer);
// Here background range will be 0 to n, and vocal range, n+1 to m

I could then pass only audioBlock into a single processor that will handle everything on its side.
It will look like this

void MyProcessor::process(const Context& context) noexcept {
  if (context.isBypassed) { return; }

  auto inputBlock = context.getInputBlock();
  auto vocalBlock = inputBlock.getSubBlock(parameters->vocalStartSample, subBlockSamples);

  auto backgroundBlock = inputBlock.getSubBlock(parameters->backgroundSartSample, subBlockSamples);

  // ...

Btw is AudioBlock a common approach to merge audio signal?
I’m looking for correct pattern but I’m exploring the AudioBlock right now.