The Red Penguin

Add audio playback functionality


Friday 31 July 2020

We now need to add audio playback functionality to the DJAudioPlayer class. Mostly, this will involve refactoring - copying code from the main component or rather moving code from the main component class, into the DJAudioPlayer class.

1. Implement LoadURL - this is in the MainComponent class. I need to copy and paste this code in from MC.cpp:
    auto* reader = formatManager.createReaderFor(audioURL.createInputStream(false));
if (reader != nullptr) // good file!
{
std::unique_ptr newSource(new juce::AudioFormatReaderSource(reader, true));
transportSource.setSource(newSource.get(), 0, nullptr, reader->sampleRate);
readerSource.reset(newSource.release());

}

We need to declare some variables that it uses in private: in DJAudioPlayer.h: These were previously declared in MainComponent.h so I just copied them over.
        juce::AudioFormatManager formatManager;
std::unique_ptr readerSource;
juce::AudioTransportSource transportSource;

2. Implement the audio life cycle - this has three functions - prepareToPlay, getNextAudioBlock and releaseResources.
Again we take the code from MainComponent.cpp and add it to DJAudioPlayer.cpp:
void DJAudioPlayer::prepareToPlay(int samplesPerBlockExpected, double sampleRate)
{
formatManager.registerBasicFormats();
transportSource.prepareToPlay(samplesPerBlockExpected, sampleRate);
}

void DJAudioPlayer::getNextAudioBlock(const juce::AudioSourceChannelInfo& bufferToFill)
{
transportSource.getNextAudioBlock(bufferToFill);
// should it not be resampleSource.getNextAudioBlock(bufferToFill); ?
}

void DJAudioPlayer::releaseResources()
{
transportSource.releaseResources();
}

3. Our next step is to connect the audio life cycle into the MainComponent. So remember, the main component is still responsible ultimately for delivering the audio content. We need to go to MainComponent::prepareToPlay in MainComponent.cpp and type this
player1.prepareToPlay(samplesPerBlockExpected, sampleRate);

Underneath in MainComponent::getNextAudioBlock only have this:
player1.getNextAudioBlock(bufferToFill);

Then in MainComponent::releaseResources only have this:
player1.releaseResources();

4. The final step is to implement the stop and start. So we go to DJAudioPlayer.cpp and type this in the start and stop functions:
void DJAudioPlayer::start()
{
transportSource.start();
}

void DJAudioPlayer::stop()
{
transportSource.stop();
}

5. We now need to hook up the buttons so they call events in DJAudioPlayer and not MainComponent. So we need to find the event listeners in MainComponent.cpp - they are buttonClicked and sliderValueChanged. Change transportSource to player 1 in the first two and prefix loadURL with player1. in the loadButton too.

Once you do a build and compile, it still works (the gain and speed aren't hooked up yet).

6. The final step is to clear out all the junk from the MainComponent class.

Removed from private: in MainComponent.h:
    std::unique_ptr readerSource;

juce::AudioTransportSource transportSource;
juce::ResamplingAudioSource resampleSource{&transportSource, false, 2};

void loadURL(juce::URL audioURL);

juce::Random rand;
double phase;
double dphase;

Removed from MainComponent.cpp:
    phase = 0.0;
dphase = 0.0001;

Removed from the slider function in MainComponent.cpp:
transportSource.setGain(slider->getValue());
resampleSource.setResamplingRatio(slider->getValue());

We can also remove the whole of void MainComponent::loadURL(juce::URL audioURL)

And now we can build and compile it and it still works!