AudioApp parallel buffer processing


#1

Hi… thanks to @daniel I’ve understood my problem:
I have an audio app in which I need to apply parallel effects on the main buffer channels 0 and 1.
I’ ve rea about a technique like copying the buffer to another clean one, but it doesn’t work. What is the correct method to split the buffer into four different operations?
(if I use the buffer in getNextAudioBlock once, it is occupied for the other effects)


#2

Why doesn’t it work? The general approach would be:

  • declaring an AudioBuffer<float> copyBuffer; in the header
  • in the prepare section, resize the buffer according to the number of copies and samplesPerBlock (e.g. copyBuffer.setSize(4, numSamples);
  • use copyBuffer.copyFrom(...) methods in the processing

After processing each copy you would merge them to your main buffer.


#3

It produces noise…
How can I declare the size of thecopyBuffer in prepareToPlay (I don’t have access to buffer.getNumSamples())?
Now I have declared and setted the copy buffers in header and prepareToPlay() and that in getNextAudioBlock():

const auto numSamples = bufferToFill.buffer->getNumSamples();

pitchABuf.copyFrom(0, 0, *bufferToFill.buffer, 0, 0, numSamples);


pitchABuf.copyFrom(1, 0, *bufferToFill.buffer, 1, 0, numSamples);

if (pitchOn)
{
    pitchA.process(pitchABuf);
    //pitchB.process(pitchBBuf);
}

bufferToFill.buffer->copyFrom(0, 0, pitchABuf, 0, 0, numSamples);
bufferToFill.buffer->copyFrom(1, 0, pitchABuf, 1, 0, numSamples);

It works but make terrible noise…


#4

Ok, now both effects work with this code in getNextAudioBlock();

const auto numSamples = bufferToFill.buffer->getNumSamples();

if (pitchOn)
{
    effectsBuffer.copyFrom(0, 0, *bufferToFill.buffer, 0, 0, numSamples);
    effectsBuffer.copyFrom(1, 0, *bufferToFill.buffer, 1, 0, numSamples);

    effectsBuffer.copyFrom(2, 0, *bufferToFill.buffer, 0, 0, numSamples);
    effectsBuffer.copyFrom(3, 0, *bufferToFill.buffer, 1, 0, numSamples);

    pitchA.process(effectsBuffer);
    pitchB.process(effectsBuffer);

    bufferToFill.buffer->copyFrom(0, 0, effectsBuffer, 0, 0, numSamples);
    bufferToFill.buffer->copyFrom(1, 0, effectsBuffer, 1, 0, numSamples);
    
    bufferToFill.buffer->copyFrom(0, 0, effectsBuffer, 2, 0, numSamples);
    bufferToFill.buffer->copyFrom(1, 0, effectsBuffer, 3, 0, numSamples);
}

But it makes a lot of noise… I think the buffer size is wrong, isn’t it?

That code in prepareToPlay():

effectsBuffer.setSize(4, samplesPerBlockExpected);
effectsBuffer.clear();

#5

Ok I’ve arrived: no more noise, only the pitch effect:
In getNextAudioBlock():

if (pitchOn)
{
    pitchABuffer.copyFrom(0, 0, *bufferToFill.buffer, 0, 0, numSamples);
    
    pitchA.process(pitchABuffer);

    bufferToFill.buffer->copyFrom(0, 0, pitchABuffer, 0, 0, numSamples);
    bufferToFill.buffer->copyFrom(1, 0, pitchABuffer, 0, 0, numSamples);

    pitchBBuffer.copyFrom(0, 0, *bufferToFill.buffer, 0, 0, numSamples);
    
    pitchB.process(pitchBBuffer);
    
    bufferToFill.buffer->copyFrom(0, 0, pitchBBuffer, 0, 0, numSamples);
    bufferToFill.buffer->copyFrom(1, 0, pitchBBuffer, 0, 0, numSamples);
}

in prepareToPlay():

pitchABuffer.setSize(1, samplesPerBlockExpected);
pitchBBuffer.setSize(1, samplesPerBlockExpected);

now… how can I pan each effect to left and right channels?


#6

Without knowing anything about your actual processing algorithm, there are two things that I think are not your intention. First:

    bufferToFill.buffer->copyFrom(0, 0, effectsBuffer, 0, 0, numSamples);
    bufferToFill.buffer->copyFrom(1, 0, effectsBuffer, 1, 0, numSamples);
    
    bufferToFill.buffer->copyFrom(0, 0, effectsBuffer, 2, 0, numSamples);
    bufferToFill.buffer->copyFrom(1, 0, effectsBuffer, 3, 0, numSamples);

So you have a four channel buffer (effectsBuffer) and copy the first two channels of it to the output buffer. This operation will replace all previous content in the buffer and replace it by the content of the selected effectsBuffer channels. After that, you do the same thing with channels three and four which means that you will again replace the whole buffer content. So you are overwriting the data you just copied two lines before. I don’t think you want that. Maybe you want to mix both signals? Then addFrom is the right call for the second pair of channels.

Second:
As I said I know nothing about the internals of your algorithm. But

pitchA.process(effectsBuffer);
pitchB.process(effectsBuffer);

looks like you are processing the buffer sequentially rather then parallel? So you pass the data processed by pitchA over to pitchB to be processed again. So what happens inside those calls?


#7

I see I came a few seconds to late with my last post :wink:

But the same applies to your last post. Currently you’ll only hear the result of pitchB, as you replace the content that came from pitchA with the copyFrom call. You definitively want to use addFrom in the last two lines to hear the blended results of both.

How do you expect your panning to work? Basically instead of simply copying data you could scale it by a different gain for the left and right channel. Both copyFrom and addFrom allow you to apply a gain too. So for an example to pan one unit more to the right, the gain for channel 1 has to be higher than the one for channel 0. For the actual gain values, there are various approaches out there, google is your friend here, I don’t think JUCE has something for panning, but I might be wrong.


#8

Great!! I feel a bit stupid… I was trying for hours with copyFrom without thinking that it overwrites all the buffer…now with addFrom it works great!
For the panning I use the gain value of copyFrom and addFrom with a function that @daniel suggest me:

auto angleA =  jmap(params[2] / 50.0, -1.0, 1.0, 0.0, float_Pi * 0.5);
    
    bufferToFill.buffer->copyFrom(0, 0, pitchABuffer.getReadPointer(0), numSamples, std::cos(angleA));
    bufferToFill.buffer->copyFrom(1, 0, pitchABuffer.getReadPointer(0), numSamples, std::sin(angleA));

where params[2] is a value from a slider that has a value between -50.0 and 50.0

Thanks Guys!! Now I can return to my app and I know how to do all!


#9

Excuse me, but I’m using the same technique in an AudioPlugin instead of an AudioApp, and I get noise and then stop sounding (with a delay from stk CCRMA toolkit)