Separately process filter for MixerAudioSource

Hi, i have two AudioSource which both added in the MixerAudioSource, how can i apply filter.process() on only one of them? Currently i can only call filter.process() on the buffer of the mixerSource. Thanks in advance

I would add a simple ProcessingSource inbetween. Something along these untested lines:

class ProcessingAudioSource : public juce::AudioSource
{
public:
    void getNextAudioBlock(const juce::AudioSourceChannelInfo& info) override
    {
        juce::AudioSourceChannelInfo readInfo (&readBuffer, 0, info.numSamples);
        source->getNextAudioBuffer (readInfo);

        // apply processing
        juce::dsp::AudioBlock<float> block (readBuffer);
        auto subBlock = block.getSubBlock (0, info.numSamples);
        filter.process (juce::dsp::ProcessorContextReplacing (subBlock));

        // copy channels to info.buffer
        // left as an exercise
    }
private:
    juce::AudioBuffer<float> readBuffer;
    std::unique_ptr<juce::AudioSource> source;
    // and whatever processor
};
1 Like

@daniel I’m really not familiar with buffer control but I’ve put it like this:

Am i doing it wrong somewhere? Because the output audio is completely broken, even when i commented the processing block, the output’s still broken:
image

Yes, you ignored the startSample when copying back.
Sadly you posted a screenshot, which means I have to type :wink:

In the future please post code as text enclosed in three backticks on a single line, that way it is formatted as code and people can easily copy/paste.

for (int i=0; i < readInfo.buffer->getNumChannels(); ++i)
    info.buffer->copyFrom (i, info.startSample, readInfo.buffer->getReadPointer(i), info.numSamples);
    //                        ^---------------

About the lambda in the processBlock, it is not advisable since it creates overhead and cannot be optimised that well, but on the other hand it is just one call per block, so it is not the first thing to optimise. I am just mentioning it for completeness.

@daniel Sorry for the inconvenience about the screenshot, I’ll copy the code from now, but the following code still provide me broken audio:

class TransportSourceProcessor : public AudioTransportSource {
public:
    void getNextAudioBlock(const AudioSourceChannelInfo &info) override {
        juce::AudioSourceChannelInfo readInfo (&readBuffer, 0, info.numSamples);
        AudioTransportSource::getNextAudioBlock(readInfo);


        auto channel = readInfo.buffer->getNumChannels();
        for (int i = 0; i < channel; ++i) {
            info.buffer->copyFrom(i, info.startSample, readInfo.buffer->getReadPointer(i),
                                  readInfo.buffer->getNumSamples());
        }
    }

    bool isPrimary = false;
private:
    juce::AudioBuffer<float> readBuffer;
};

Would love to know what have i done wrong. Thanks

No worries :slight_smile:

I think here you use the size of the buffer instead of the number of samples in the AudioSourceChannelInfo:

info.buffer->copyFrom(i, info.startSample, readInfo.buffer->getReadPointer(i), readInfo.numSamples);

But my pet peeve is the AudioTransportSource. It sounds like the golden bullet while there is actually only one legit use case:
when you want to connect your engine to the AudioIODevice, so you can start stop it.
But the AudioTransportSource is asynchronous, so it will start and stop with an undetermined delay and is discretised to block boundaries, which will sound very unmusical.

And another piece of advice:
Avoid to inherit like that, rather create an AudioSource owning the source. It is called:
“Prefer composition over inheritance”.

@daniel I’ve changed as you said, but the result is no better, and the reason i extend AudioTransportSource is that i need to control over the playback.The above TransportSourceProcessor is then passed to a MixerSource to mix several audio together.

Comment the code like this then it worked fine:

class TransportSourceProcessor : public AudioTransportSource {
public:
    void getNextAudioBlock(const AudioSourceChannelInfo &info) override {
//        juce::AudioSourceChannelInfo readInfo (&readBuffer, 0, info.numSamples);
        AudioTransportSource::getNextAudioBlock(info);


//        auto channel = readInfo.buffer->getNumChannels();
//        for (int i = 0; i < channel; ++i) {
//            info.buffer->copyFrom(i, info.startSample, readInfo.buffer->getReadPointer(i),
//                                  readInfo.numSamples);
//        }
    }

    bool isPrimary = false;
private:
    juce::AudioBuffer<float> readBuffer;
};

Did you forget to give the readBuffer a valid size by any chance?
You can use the numSamples from prepareToPlay, but figuring out the channel coount is a bit tricky, since the AudioSource doesn’t provide that information upfront.

I’ve fixed the channel count across my programm to 2, can i use that?

When you know it will always be stereo, sure. Why not?
Most programs start like that and only much later it will be generalised to cover other cases.

1 Like

It worked! Thanks for your patience :heart:

Great! Glad to hear that

1 Like