🎛️ making a simple EQ with juce
Summary
While not the most glamorous audio application to start off with, I think an EQ will be a great start to learning the JUCE framework! I’ll be following the above tutorial as a springboard for learning and implementing the JUCE framework here.
Part 1 - Introduction
- Goal is to create a simple EQ, with a high and low cut (with gain and CF), and a central peak (with gain, Q, and freq)
- JUCE is a framework built on C++ for developing audio applications and plugins.
- Projucer is used to create projects and automatically populating the classes for you.
Part 2 - Setting up the Project
- Follow steps here
- Audio plugins in JUCE have two main
.cpp / .h
pairs,PluginProcessor
andPluginEditor
.-
PluginProcessor
handles our DSP and audio manipulation code -
PluginEditor
handles the GUI
-
Part 3 - Creating Audio Parameters
PluginProcessor.h
holds our class, which is pre-populated with a few class methods, the most important being
-
void prepareToPlay (double sampleRate, int samplesPerBlock) override;
docs page void processBlock (juce::AudioBuffer<float>&, juce::MidiBuffer&) override;
We’re going to start by creating what is called a AudioProcessorValueStateTree
, in our PluginProcessor
header file, a class that will hold all of the values for our plugin parameters. However, one of the values it requires in its construction is a parameter layout, which we must first declare, and then define in our PluginProccessor.cpp
file (snippet shown below).
// PluginProcessor.cpp
// Define the create layout function declared in the header file which returns a parameter layout
juce::AudioProcessorValueTreeState::ParameterLayout SimpleEQAudioProcessor::createParameterLayout()
{
// Create new parameter layout object
juce::AudioProcessorValueTreeState::ParameterLayout layout;
// Add parameters to layout object
// New parameter for low cut frequency
layout.add(std::make_unique<juce::AudioParameterFloat>(
"LowCut Freq",
"LowCut Freq",
juce::NormalisableRange<float>(20.0f, 20000.0f,1.0f,1.0f),
20.0f
));
// Create a string array to hold the dB/Oct choices
juce::StringArray slopeArray;
for (int i=0; i < 4; i++)
{
// Create new string
juce::String str;
// Store iterated value e.g. 12 + 0*12 = 12..24..36..48
str << (12 + i * 12);
str << " dB/Oct";
slopeArray.add(str);
}
// New parameter for Low Cut Slope
layout.add(std::make_unique<juce::AudioParameterChoice>(
"LowCut Slope", // parameter ID
"LowCut Slope", // parmeter name
slopeArray, // array of choices
0 // default index
));
// return the layout
return layout;
}
// PluginProcessor.h (in public)
// Declare parameter layout function that returns a parmateter layout
static juce::AudioProcessorValueTreeState::ParameterLayout createParameterLayout();
// Declare ValueStateTree object that holds all of the parameter values
juce::AudioProcessorValueTreeState apvts {*this, nullptr, "Parameters", createParameterLayout()};
Creating the parameter layout meant using some JUCE types, notably the juce::AudioProcessorValueTreeState::ParameterLayout
, juce::AudioParameterFloat>
, and juce::AudioParameterChoice
types. For the latter two, they were initialised as unique pointers using std::make_unique<datatype>()
keyword. Additionally, juce::NormalisableRange<float>()
was encountered, which is a in-built JUCE function that provides a range of values, it requires min, max, interval, and slew (linearity).