Creating an audio plugin that plays sounds from a pre-determined file

Hello -
I’m quite new to JUCE (and C++), so apologies for the novice question. I am trying to create an audio plugin that essentially ignores incoming audio and instead plays sound from a pre-determined wav file (e.g. one that will be shipped with the plugin, not one that the user will choose). I gather from other forum posts that one way to do this is to make the wav file available as BinaryData. I’ve been looking at the looping audio tutorial as well (and the forum), but I’m struggling to get this working for an audio plugin rather than an audio app. I would greatly appreciate any advice or pointers.

Currently, I am reading the audio file by doing something like this in the AudioProcessor constructor (this may not be the right place for this – open to advice here):

input = new juce::MemoryInputStream (BinaryData::testaudio_wav, BinaryData::testaudio_wavSize, false);
audioFormatReader = audioFormatManager.createReaderFor(std::unique_ptr <juce::InputStream> (input));
audioFormatReaderSource = new juce::AudioFormatReaderSource(audioFormatReader, true);

I’m now trying to figure out how to use the audioFormatReader or the audioFormatReaderSource in the processBlock function to get this test audio to play, but the examples I’m finding seem to be for audio applications rather than plugins (at least I think). I found one user who seemed to be taking this approach and then calling the audioFormatReaderSource->getNextAudioBlock in the processBlock function, but I’m not sure what to pass as an argument.

Am I at all on the right track? If not, does anyone know of any sample code that I could look at for reference?

Thank you!

Another approach would be to read from an actual audio file. We use a background thread to read audio in advance of needing it processBlock(). This requires some code to detect if the playback loops/jumps/stops, but it’s working fine. We keep at least two (16k) buffers in memory at a time, and when one buffer reaches its end and we start using the next one, we tell the background thread to fetch us the buffer after that, so that we always stay ahead of the read position by at least a buffer.

1 Like

You need to make an AudioSourceChannelInfo from the buffer you are given for the processBlock call.

Something like :

void MyPlugin::processBlock(AudioBuffer<float>& buffer, MidiBuffer...)
{
  AudioSourceChannelInfo chaninfo(buffer);
  readerSource->getNextAudioBlock(chaninfo);
}
1 Like

Thanks @xenakios, that is very helpful. Now I’m seeing that I didn’t realize that the return type of readerSource->getNextAudioBlock(chaninfo) is void… I thought it might given me a pointer to the next “block” of audio from the source. Is there a different way I should be using it? What I’m currently doing, in an attempt to essentially ignore the incoming audio and play the sound from the file, is something like:

void NewProjectAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& 
   midiMessages)
{
buffer.clear();
juce::AudioSourceChannelInfo channelInfo(buffer);

int numChannels = buffer.getNumChannels();

for (int ch = 0; ch < numChannels; ch++) {
    buffer.copyFrom(xxxx what to put here?)
}
}

Any advice would (again) be much appreciated!

@HowardAntares Thanks - yours sounds like a great approach. I’m trying to get a rough POC done this weekend though, and this BinaryData strategy seemed like the easiest approach that I could find examples of. Open to suggestions on how to implement this by reading from the file though. I’m not currently aware of the “correct” way to add a .wav file to a project and read from it.

Ok, I think I understand better what getNextAudioBlock does now. I have something sort of working – the wav file is getting played, but it sounds like it’s sped up by a lot (and maybe has some light clicking associated with it). Posting what I have so far below. Maybe I need to resample the wav file to set the wav file’s sample rate equal to the one in the daw? If so, how might I do that

// in the constructor: 
audioFormatManager.registerBasicFormats();
input = new juce::MemoryInputStream (BinaryData::testaudiocantina_wav, BinaryData::testaudiocantina_wavSize, false);
audioFormatReader = audioFormatManager.createReaderFor(std::unique_ptr<juce::InputStream> (input));

if (audioFormatReader != nullptr)
{
    audioFormatReaderSource = new juce::AudioFormatReaderSource(audioFormatReader, true);
    audioFormatReaderSource->setLooping(true);
}

//...
void NewProjectAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& 
midiMessages)
{
    juce::AudioSourceChannelInfo channelInfo(buffer);
    audioFormatReaderSource->getNextAudioBlock(channelInfo);
}
void NewProjectAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
    audioFormatReaderSource->prepareToPlay(samplesPerBlock, sampleRate);
}