RackAFX single sample to JUCE audio buffer example?


#1

Not sure if there are many people on this forum who have purchased Will Pirkle’s audio plugin books or are users of his RackAFX software. I was currently on working through his synth book and trying to recreate the different projects in JUCE as learning exercises. One thing that is confusing me is how to go from the single sample output that RackAFX uses to fill up a JUCE audio buffer. Does anyone happen to have an example for how to go about doing this? Anyone else ported RackAFX projects into JUCE?


#2

Just write a for loop on all the samples in the loop, and call your code on each sample.

Yet another example why the book is awful.


#3

Right. Okay I’m working on the for loop right now. However, the other thing that is confusing me is the fact that the processBlock function in the PluginProcessor does not explicitly have StartSample or NumSamples inputs. I have handleNoteOn and handleNoteOff functions in my PluginEditor which listen for changes in the MidiKeyboard component I am using. However, those functions don’t explicitly deal with audio buffers so I’m a bit confused on how to link all these functions together. Should I just call the processBlock from the handleNoteOn function in the PluginEditor every time a new MIDI note is triggered? Guessing there is no relation between the default buffer size and MIDI note duration. Sorry for the noob questions but it would be very informative to get some clarity on this issue since it seems critical.


#4

That book is alright but the code is bad, just use the DSP concepts, ditch the rest.

PluginProcessor receives an AudioBuffer. you can call getNumSamples() & getNumChannels()

for (int channel = 0; channel < totalNumInputChannels; ++channel)
{
    float* channelData = buffer.getWritePointer (channel);

    // ..do something to the data...

    for (int sample = 0; sample < buffer.getNumSamples(); sample++) {
        
        channelData[sample] = DSP_OUTPUT;
    }
}

#5

Jacob thats helpful but if the plugin is a synth, does the PluginProcessor have to receive an AudioBuffer? Can it not just create an AudioBuffer? The reason I ask that is because I’m confused how the handleNoteOn and handleNoteOff in the PluginEditor which communicates with the MidiKeyboardComponent syncs with the AudioBuffers in the PluginProcessor. Those functions don’t have inputs which are AudioBuffers, just MIDI related inputs. Do you just create a new AudioBuffer every time a new MIDI note is triggered?


#6

The AudioBuffer passed in will contain input data (which you can ignore), but you also write into the same buffer and this is what is output to the host. This is often referred to as process replacing, i.e. you are replacing the input with your output. In your case you may not care about the input which is fine. But even some synths may want input, for example if your synth is also a vocoder if might be useful to have the input.


#7
void handleNoteOn (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) override
{
    MidiBuffer incomingMidi;
    processor.midiNoteOn(midiChannel, midiNoteNumber, velocity);
    processor.processBlock(AudioBuffer<float>& buffer, incomingMidi);
}

^The above function is in my PluginEditor for responding to notes from the MidiKeyboardComponent. Is this the proper way to communicate between the keyboardcomponent, editor, and processor? Right now, the editor is generating an error related to passing the audiobuffer. Should I inherent from “AudioProcessor” in my PluginEditor and then pass a buffer to the PluginProcessor from there every time a note is triggered as I have above?


#8

The easiest way to implement a synth in Juce is to use the synthesiser class. In order to implement it you also need to use the synthesiserVoice and synthesiserSound classes. These will come with a lot of the built in functionality you’ll need for your synth.


#9

Joshua, I’ll give that a shot. It might be tricky since I’m importing code from RackAFX projects but perhaps that might be the easiest way to go. Thanks for the tip.


#10

Maybe this will help you get in the right direction


#11

Joshua, I didn’t realize that you were the one behind the AudioProgrammer videos/channel on youtube. It’s an incredibly helpful resource. Thanks for your contributions! Watching the above tutorial right now.


#12

Joshua, quick question. I’m working through your tutorial on building a synthesiser using JUCE. I’m on the second video where you are adding MIDI functionality. However, I’m having an issue. After adding the following code:

mySynth.clearVoices();

for (int i =0; i < 16; i++)
{
    mySynth.addVoice(new SynthVoice());
}

mySynth.clearSounds();
mySynth.addSound(new SynthSound());

There is an error saying allocating an object of abstract class type. I know this is because of the two virtual functions, appliesToNote and appliesToChannel. However, I wasn’t sure if I was supposed to somehow specify them in the PluginProcessor or even how to specify them. Could you perhaps provide a bit of example code of where and how to instantiate these functions?


#13

I answered here:


#14

When I try to add SynthesiserSound::appliesToNote (int) and SynthesiserSound::appliesToChannel (int) in my PluginProcessor.h file as follow:

SynthesiserSound::appliesToNote(int);
SynthesiserSound::appliesToChannel (int);

I get the following error:

"Non-friend class member "appliesToChannel"cannot have a qualified name. "

Therefore, I think I’m missing something about where to implement these functions.


#15

It should look like:

class SynthSound : public SynthesizerSound {
// ...
void appliesToNote(int) override { return true; }
void appliesToChannel (int) override { return true; }
// ...

HTH


#16

Daniel, thanks so much. That did the trick! So is the lesson to be learned here that whenever I inherent from a class with pure virtual functions, I just use “override” on those functions in the derived class?


#17

Yes, and tripple check spelling… :wink:

override makes the compilation fail, if there wasn’t a virtual method in one of the inherited classes to override.

Unfortunately the override keyword was added later, so if you never use override, it might also compile, depending on some compiler flags, warning levels etc.


#18

The “override” keyword doesn’t do anything else but give you a compile time error if the function actually doesn’t override a virtual (or pure virtual) method in a parent class. So it’s useful to add when overriding methods to avoid mistakes with method name typos or wrong function signatures.