Problem with copying AudioSampleBuffer's


#1

I am developing a simple audio application to try and get to grips with Juce, similar to that of the voice memo's app on an iphone, but with 4 layers of audio. (also with Mute and Solo buttons etc.) I can record and playback each layer, however I am struggling to play back all the layers at the same time. I have a class called TransportBar which has a master "Play" button, which I want to play all 4 layers at the same time.

To do this, I made an AudioSampleBuffer called "masterBuffer", which makes a copy of all of the layerBuffer's. I created a getter function for this as the Layers and Transport bar are created in different classes:

 

In the Layer.cpp file:

AudioSampleBuffer Layer::getLayerBuffer() const

{

    return layerBuffer;

}

I then made a reference to the Layer class in the TransportBar class, and wrote the following code to copy the layerBuffer data to a masterBuffer:

float TransportBar::processSample(float input)
{

    float masterOutput = 0.f;
    if (playState.get() == true)

    {

        //play
        masterBuffer.makeCopyOf(layerRef.getAudioSampleBuffer());
        masterOutput = *(masterBuffer.getReadPointer(0, bufferPosition));

        //create click
        if ((bufferPosition % (bufferSize / 16)) == 0)
            masterOutput += 0.25f;

        ++bufferPosition;

        if (bufferPosition == bufferSize)
        {
            bufferPosition = 0;
            playState = false;
        }
    }

    return masterOutput;
}

This should make a copy of the layerBuffer's to a masterBuffer so I can use it for the output.

Finally, in my audioDeviceIOCallback, I have written this block of code to assign the right buffer:

  if (layer.getPlayState() == true || layer2.getPlayState() == true || layer3.getPlayState() == true || layer4.getPlayState() == true )

        {

            output = layer.processSample (mix) + layer2.processSample(mix) + layer3.processSample(mix) + layer4.processSample(mix);

        }

        else if (transportBar.getPlayState() == true)

        {

            layer.processSample(mix);

            output = transportBar.processSample(mix);

        }

When I press the play buttons on the layers, they playback the audio perfectly fine, however when I press play on the transportBar, all I can hear is a quick click then nothing. Any ideas how to fix this?


#2

if (layer.getPlayState() == true || layer2.getPlayState() == true || layer3.getPlayState() == true || layer4.getPlayState() == true || transportBar.getPlayState() == true)
{ 
    output = layer.processSample (mix) + layer2.processSample(mix) + layer3.processSample(mix) + 
    layer4.processSample(mix);
}

wouldn't that work?

I don't see why you need to copy the buffers, and have a separate process function in the TransportBar class.

 


#3

masterBuffer.makeCopyOf(layerRef.getAudioSampleBuffer());
 


i would hazard a very large guess that you don't want to be copying whole buffers on every sample!
 


#4

else if (transportBar.getPlayState() == true) 
{ 
    layer.processSample(mix); 

    output = transportBar.processSample(mix); 
}

layer.processSample(mix) seems to be doing nothing here?


#5

...just a pointer, AudioSampleBuffer is the same as a AudioBuffer<float>. So for copying, moving and gain adjusting samples check the documentation of AudioBuffer, there are all the methods you need to process samples. Try to avoid iterating over samples, as it's horrible inefficient. AudioBuffer::copyFrom and AudioBuffer::addFrom are vectorized and therefore much faster: http://www.juce.com/doc/classAudioBuffer

In your example I don't understand, what you try to achieve, but I see, that the "float input" of your processSample seems not to be used at all.

And in your Layer::getLayerBuffer() method: AFAIK if you return the AudioSampleBuffer not as a reference, the buffer is copied each time you call it, which is much data (if there is no implicit datasharing in the class, but I think it's not).

Allocating space for that is time consuming and should be avoided in the audio thread (same as in my example output.setSize(), this should be done in the constructor, if the buffersize is known at that point already, or in audioDeviceAboutToStart: http://www.juce.com/doc/classAudioIODeviceCallback#a1bec2ae89543a93e31461991f7404aec

Maybe something like this works for your case as well:


AudioSampleBuffer& Layer::getLayerBuffer() const 
{ 
    return layerBuffer; 
}

[...]

AudioSampleBuffer output;
output.setSize (1, layer.getNumSamples());

output.copyFrom (0, 0, layer.getReadPointer (0), layer.getNumSamples(), 0.33); 
// suggestion: reduce gain by 1/3 to mix three sources
output.addFrom (0, 0, layer1.getReadPointer(0), layer1.getNumSamples(), 0.33);
output.addFrom (0, 0, layer2.getReadPointer(0), layer2.getNumSamples(), 0.33);

[...]

...not tested, but I use it similar every day...

You will need to check, that layer, layer1 and layer2 have the same getNumSamples(), otherwise you run out of bounds...

HTH, daniel