Play audio file in AudioApplication that has already some bufferToFill processing

projucer

#1

HI, I need to play a small audio sample in an AudioApplication in which I already have some processing of the bufferToFill inside the getNextAudioBlock() call.
I’ve followed the tutorial “Playing sound files” but when I add the code

transportSource.getNextAudioBlock (bufferToFill);

in that call, all the pre processed buffer are bypassed…
Plz help!


#3

Remember: we don’t see the code you are looking at.

It would be easier for people to understand what you mean and to help, if you would at least post the code of your AudioApplication::getNextAudioBlock(), or any snippet that could contribute to the problem or your anticipated solution.
Without that you probably can’t get great support.


#4

Thank you for the attention! The code is that:

void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override
{
    transportSource.getNextAudioBlock(bufferToFill);
    
    const float width = revWidthSlider.getValue();
    const float damp = revDampingSlider.getValue();
    const float size = revSizeSlider.getValue();
    const float revMix = revMixSlider.getValue();

    reverb.setWidth(width);
    reverb.setDamping(damp);
    reverb.setRoomSize(size);
    reverb.setEffectMix(revMix);
    
    const float radius = filterRadiusSlider.getValue();
    const float freq = filterFreqSlider.getValue();
    
    filter.setResonance(freq, radius);
    filter.setSweepRate(0.5);
    filter.setSweepTime(0.5);
    filter.setGain(1.0);
    
    const float master = masterSlider.getValue();
    
    const float* lInBuf = bufferToFill.buffer->getReadPointer(channels[0]);
    const float* rInBuf = bufferToFill.buffer->getReadPointer(channels[1]);
    
    float* lOutBuf = bufferToFill.buffer->getWritePointer(0);
    float* rOutBuf = bufferToFill.buffer->getWritePointer(1);

    for (int i = 0; i < bufferToFill.numSamples; ++i)
    {
        lOutBuf[i] = reverb.tick(filter.tick (lInBuf[i])) * master;
        rOutBuf[i] = reverb.tick(filter.tick(rInBuf[i])) * master;
    }
    
    rmsLevel = bufferToFill.buffer->getRMSLevel(channels[0], bufferToFill.startSample, bufferToFill.numSamples);
}

The rest of the code is as in the tutorial I mentioned above…


#5

I see, thanks. As you found out already, you have to fetch the samples from your transportSource, before you can apply the effects on it.
So I guess the problem is solved?

N.B.1: you may want to start the loop from the startSample instead of 0
N.B.2: you can help the processor processing each channel after another, so it doesn’t has to jump with the cache back and forth (although a modern compiler might optimise that anyway…)

for (int i = bufferToFill.startSample; i < bufferToFill.numSamples - bufferToFill.startSample; ++i)
    lOutBuf[i] = reverb.tick(filter.tick (lInBuf[i])) * master;
for (int i = bufferToFill.startSample; i < bufferToFill.numSamples - bufferToFill.startSample; ++i)
    rOutBuf[i] = reverb.tick(filter.tick(rInBuf[i])) * master;

#6

Thanks for the help but the problem is still here…
I’ve changed the code as you suggested but I hear only the play sample with noise and no process on the rest of my input audio…


#7

There also needs to be 2 instances of both the filter and the reverb, otherwise they are not going to work for stereo processing…(The reverb may have a way to process stereo signals, but you will need to feed it the samples in some other way.)


#8

Ok I’ll explain better…
I have an 8 input audio card and I capture the audio from there.
I have a function that enables the audio input channels by a combo box (storing the channel numbers in the channels[] array).
I need to process the audio from my card AND in the same configuration to play one sample shot to let the user customize the effect before starting the jam session in direct audio input…

The noise disappear if I process both of the channels in only one for loop… but I cannot hear the sounds from my audio card…


#9

Did you understand @xenakios answer? It is a common mistake to send different signals through the same processor/filter instance. About every third newcomer thread for DSP is about that mistake…

You need a left reverb instance, only processing data from the left channel, and another right channel instance, only processing data from the right channel. Same for the filter. The filter has to be confused, when all the sudden the signal jumps from left data to right data. And the reverb would create artefacts on the wrong channel.

For your routing problem some pseudo code:

ChannelRemappingAudioSource* inputMapper;
ChannelRemappingAudioSource* outputMapper;
AudioMixerSource*            mixer;
AudioTransportSource*        playback;

// setup
inputMapper = new ChannelRemappingAudioSource (this /*AudioAppComponent*/, false);
inputMapper->setNumberOfChannelsToProduce (2);
inputMapper->setOutputChannelMapping (leftIn, 0);
inputMapper->setOutputChannelMapping (rightIn, 1);

mixer->addInputSource (inputMapper, false);
mixer->addInputSource (transportSource, false);
// all prepareToPlay and all that jazz

outputMapper = new ChannelRemappingAudioSource (mixer, false);
outputMapper->setOutputChannelMapping (0, leftOut);
outputMapper->setOutputChannelMapping (1, rightOut);

When that works, you can think about writing a custom MixerSource, where you add custom processors in each source etc. It is up to you, if you want to write all by hand in the mixer’s getNextAudioBlock, or have a look at AudioProcessorGraph. And there are more options to choose from.

HTH


#10

Excuse me for the late… Where do I place the pseudo code above?
How can I send the two inputs in the mixer?


#11

There are a lot of ways to do it, depending, how flexible you want your app and routing to be. I would

  1. set the routing in the constructor (basically the calls above),
  2. call prepareToPlay for all sources in the AudioAppComponent’s prepareToPlay, and
  3. in the getNextAudioBlock only call outputMapper->getNextAudioBlock.

The outputMapper will automatically fetch the block from it’s source, which is the mixerSource. This will fetch audio from it’s sources, which is the transportSource and the inputMapper, which again fetches the data from the input channels of the AudioAppComponent. That’s all.

When this is working, you can have a thought on how to call processors on the audio blocks. You can either plug an AudioSource in between or build a customised version of AudioMixerSource, or something completely different…


#12

Ok I think I have understood… I’ll try your solution this afternoon.
Thank you so much!


#13

Ok, I have tried and…
I had to add mixer = new MixerAudioSource(); because the app crashed at the launch.
But if I add the inputMapper to the mixer, the app crashes at the launch…
In the prepareToPlay() have I to call the

inputMapper->prepareToPlay(…);

with the transportSource.prepareToPlay(…) only?
And what have I to do with the playback* AudioTransportSource?

Basically I think that the inputMapper don’t work…
Because if I comment the line

mixer->addInputSource (inputMapper);

The app runs… And I ear the transportSource.


#14

Sorry, that was not meant to be complete code, just to get the idea across.

I try to give an complete example:

// private member variables:
ScopedPointer<ChannelRemappingAudioSource> inputMapper;
ScopedPointer<ChannelRemappingAudioSource* outputMapper;
ScopedPointer<AudioMixerSource>            mixer;
ScopedPointer<AudioTransportSource>        playback;

// In the constructor
inputMapper = new ChannelRemappingAudioSource (this /*AudioAppComponent*/, false);
inputMapper->setNumberOfChannelsToProduce (2);
inputMapper->setOutputChannelMapping (leftIn, 0);
inputMapper->setOutputChannelMapping (rightIn, 1);
playback = new AudioTransportSource ();
mixer = new AudioMixerSource ();
mixer->addInputSource (inputMapper, false);
mixer->addInputSource (transportSource, false);
outputMapper = new ChannelRemappingAudioSource (mixer, false);
outputMapper->setNumberOfChannelsToProduce (2);
outputMapper->setOutputChannelMapping (0, leftOut);
outputMapper->setOutputChannelMapping (1, rightOut);

// in prepareToPlay
inputMapper->prepareToPlay (samplesPerBlockExpected, sampleRate);
playback->prepareToPlay (samplesPerBlockExpected, sampleRate);
inputMapper->prepareToPlay (samplesPerBlockExpected, sampleRate);
outputMapper->prepareToPlay (samplesPerBlockExpected, sampleRate);

// in getNextAudioBlock
if (outputMapper)
    outputMapper->getNextAudioBlock (bufferToFill);
else
    bufferToFill.clearActiveBufferRegion();

I hope that helps you to get it going…


#15

I have understood the routing, but the app still crashes…
Is it correct to insert “this” in the constructor of ChannelRemappingAudioSource of the inputMapper?
I think that’s the problem…

I get BAD_EXC_ACCESS on the line

outputMapper->prepareToPlay(…);


#16

With that code:

    // Mixer Settings
    testMapper = new ChannelRemappingAudioSource(&transportSource, false);
    testMapper->setNumberOfChannelsToProduce(2);
    testMapper->setOutputChannelMapping(0, 0);
    testMapper->setOutputChannelMapping(1, 1);
    
    mixer = new MixerAudioSource();
    mixer->addInputSource(testMapper, false);
    
    outputMapper = new ChannelRemappingAudioSource(mixer, false);
    outputMapper->setOutputChannelMapping(channels[0], 0);
    outputMapper->setOutputChannelMapping(channels[1], 1);

in the constructor, and that in prepareToPlay():

    testMapper->prepareToPlay(samplesPerBlockExpected, sampleRate);
    mixer->prepareToPlay(samplesPerBlockExpected, sampleRate);
    outputMapper->prepareToPlay(samplesPerBlockExpected, sampleRate);

and that in getNextAudioBlock():

    outputMapper->getNextAudioBlock(bufferToFill);

I receive a EXC_BAD_ACCESS in the line:

    testMapper->prepareToPlay(samplesPerBlockExpected, sampleRate);


#17

That is needed. The AudioAppComponent inherits AudioSource, so the ChannelMapper pulls the samples from the AudioAppComponent.

But I tried it now myself, and it seems, when calling setAudioChannels (2, 2); it gets into an infinite loop of prepareToPlay() calls.
Maybe somebody else can chime in with an idea…


#18

Mhm… if I comment the audio sources prepare to play, the apps runs fine, but when I add the inputMapper with “this” audio source at the mixer, xCode has an

EXC_BAD_ACCESS in the line 65 of the juce_ScopedLock.h header file…

The constructor code is:

    // Mixer Settings
    inputMapper = new ChannelRemappingAudioSource(this, false);
    inputMapper->setNumberOfChannelsToProduce(2);
    inputMapper->setOutputChannelMapping(channels[0], 0);
    inputMapper->setOutputChannelMapping(channels[1], 1);
    
    testMapper = new ChannelRemappingAudioSource(&transportSource, false);
    testMapper->setNumberOfChannelsToProduce(2);
    testMapper->setOutputChannelMapping(channels[0], 0);
    testMapper->setOutputChannelMapping(channels[1], 1);
    
    mixer = new MixerAudioSource();
    mixer->addInputSource(inputMapper, false);
    mixer->addInputSource(testMapper, false);
    
    outputMapper = new ChannelRemappingAudioSource(mixer, false);
    outputMapper->setOutputChannelMapping(0, 0);
    outputMapper->setOutputChannelMapping(1, 1);

#19

I’ve noticed that even the call at

outputMapper->getNextAudioBlock(...)

is called in an infinite loop…

However this happens only when I add to the mixer object the inputMapper object…

And the compiler pass me an “non virtual thunk to MainContentComponent::getNextAudioBlock::AudioSourceChannelsInfo const&”

That means that the code don’t implement a defined virtual function…
Is it a JUCE error?
Bitwise the error “non virtual thunk…” is in an infinite loop…


#20

I came up with an hack solution that is enough for me:

I use only the transportSource AudioTransportSource and without mixer or routing, using an if statement to check if the transportSound has finished playing, then when it stops I Set the source to .stop() and the getNextBlock() continues to process the main input.

The only problem is that you can ear one source at a time…


#21

I got it!

Here is my App:

v-Drums Player

Please left me a feedback!