Rubberband with MixerAudioSource

I’m working on a college project which aims at building an application which can together Play/Pause several audio sample files in a loop. To solve this purpose I’m creating a TransportSource for each audio file and then merging them together into a single MixerAudioSource.

The code so far solves the above purpose but, now I’m being tasked to add a funtionality which can manipulate the Pitch and Tempo of the output audio (from MixerAudioSource). For this purpose I’m using the RubberBandStretcher upon referring to this video.

The problem with the current code is that the changes(in pitch/tempo) are not being reflected on the output and it is just playing audio in a loop as it was doing earlier.

Being a beginner with JUCE, I had to struggle a lot to reach here, but now I need a little push to proceed. Here is the current code:-

#pragma once

//==============================================================================
class MainContentComponent   : public juce::AudioAppComponent,
                               public juce::AudioProcessor,
                               public juce::ChangeListener
{
public:
    MainContentComponent() 
                              
    {
        addAndMakeVisible (warning);

        addAndMakeVisible (tanpura.button);
        tanpura.button.setButtonText ("Play Tanpura");
        tanpura.button.setColour(juce::TextButton::buttonColourId, juce::Colours::red);
        tanpura.button.onClick = [this] { buttonClicked(tanpura);};
        tanpura.curState = Paused;

        addAndMakeVisible (tabla.button);
        tabla.button.setButtonText ("Play Tabla");
        tabla.button.setColour(juce::TextButton::buttonColourId, juce::Colours::red);
        tabla.button.onClick = [this] { buttonClicked(tabla);};
        tabla.curState = Paused;
        
        transportSource.addChangeListener (this);

        formatManager.registerBasicFormats();

        auto file = juce::File::getCurrentWorkingDirectory().getChildFile("../../../Resources/tabla.wav");

        if (file == juce::File{})
            return;

        auto *reader  = formatManager.createReaderFor (file);

        auto newSource = std::make_unique<juce::AudioFormatReaderSource> (reader, true);

        if (newSource.get() == nullptr) {
            
            std::cerr<<file.getFullPathName()<<" could not found!!";
            return;

        }else {
            auto duration = (float) reader->lengthInSamples / reader->sampleRate;               // [3]
            if (duration < 10)
            {
                newSource->setLooping(true);
                transportSource.setSource (newSource.get(), 0, nullptr, reader->sampleRate);       // [12]
                readerSource.reset (newSource.release());
            }
            else
            {
                // std::cerr<<"Audio size exceeds 4 sec!!\n";
                warning.setText("Audio size exceeds 10 sec!!",juce::dontSendNotification);
            }
        }

        mixSource.addInputSource(&transportSource,false);   
        
        setSize (300, 200);

        setAudioChannels(0,2);
    }

    ~MainContentComponent() override
    {
        shutdownAudio();
    }

    void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override
    {
        mixSource.prepareToPlay (samplesPerBlockExpected , sampleRate);
    }
    
    void getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill) override
    {
        mixSource.getNextAudioBlock(bufferToFill);
    }

    void changeListenerCallback (juce::ChangeBroadcaster* source) override {   }

    void prepareToPlay	(double sampleRate,int 	maximumExpectedSamplesPerBlock) override 
    {
        fileBuffer.setSize(getTotalNumOutputChannels(), maximumExpectedSamplesPerBlock);
        
        // DBG(maxSamplingRate);

        rb = std::make_unique<RubberBand::RubberBandStretcher>(sampleRate,
                                                               2,
                                                               RubberBand::RubberBandStretcher::PresetOption::DefaultOptions,
                                                               0.5,
                                                               0.5);
        rb->reset();
    }

    void processBlock (AudioBuffer< float > &buffer, MidiBuffer &midiMessages) override 
    {   
        while(rb->available() < buffer.getNumSamples()) 
        {
            transportSource.getNextAudioBlock(AudioSourceChannelInfo(fileBuffer));

            rb->process(fileBuffer.getArrayOfReadPointers(),
                        fileBuffer.getNumSamples(),
                        false);
                        
        }
        // DBG(rb->available());
        
        rb->retrieve(buffer.getArrayOfWritePointers(),
                     buffer.getNumSamples());   
    }

    void releaseResources() override
    {
        mixSource.releaseResources();
    }

    void resized() override
    {
        warning.setBounds(getWidth()/2 - 20, 60, getWidth(), 20);
        tanpura.button.setBounds(10, 90, getWidth()-20, 20);
        tabla.button.setBounds(10, 120, getWidth()-20, 20);
    }

    const String getName() const override { return {};}

    double getTailLengthSeconds()const override { return 1;}

    bool acceptsMidi () const override { return false;}

    bool producesMidi() const override { return false;}

    AudioProcessorEditor* createEditor () override { return NULL;}

    bool hasEditor () const  override { return false;}

    int getNumPrograms () override { return 1;}

    int getCurrentProgram () override { return 1;}

    void setCurrentProgram (int index) override {}

    const String getProgramName (int index) override {return {};}

    void changeProgramName (int index, const String &newName) override {}

    void getStateInformation (juce::MemoryBlock &destData) override {}

    void setStateInformation (const void *data, int sizeInBytes) {} 
    //==========================================================================
private:

    enum State
    {
        Playing,
        Paused
    };

    struct Instrument {
        State curState;
        juce::TextButton button;
    }tanpura, tabla;

    void buttonClicked(Instrument &ins) {
        if(ins.curState == Paused) {
            ins.button.setColour(juce::TextButton::buttonColourId, juce::Colours::green);
            ins.button.setButtonText ("Playing...");
            ins.curState = Playing;
            transportSource.start();
        }else {
            ins.button.setColour(juce::TextButton::buttonColourId, juce::Colours::red);
            ins.button.setButtonText ("Paused");
            transportSource.stop();
            ins.curState = Paused;
        }
    }

    juce::Label warning;
    juce::MixerAudioSource mixSource;
    juce::AudioSampleBuffer fileBuffer;
    juce::AudioFormatManager formatManager;
    juce::AudioTransportSource transportSource;
    std::unique_ptr<RubberBand::RubberBandStretcher> rb;
    std::unique_ptr<juce::AudioFormatReaderSource> readerSource;
    
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)
};