In this assignment you are asked to create a spectrum analysis plug-in. Through this assignment you will learn how to:

- Create a plugin using JUCE
- Compute the FFT magnitude using JUCE's dsp library
- Create a GUI in JUCE using PluginGUIMagic
- Connect parameters to make plugins controllable

Feel free to go wild with your GUI and have fun! However, your implementation must have the following components:

- Your plugin must display the
**real time**spectrum of your input signal.*There will be some delay, of course :)* - Your plugin must be controllable and have, at least, two parameters
- Smoothing Time (ms)
- Another parameter of your choice
- Your plugin must compute and display the
*spectral centroid*

In Projucer create a new audio-plugin project with PGM support like you did in 0.5.

Once you've set up your project, grab the starter code from the
class repository. Review
the differences between the
Projucer-generated `PluginProcessor.h/cpp`

and
the starter code, which now inherits
from `foleys::MagicProcessor`

instead
of `juce::AudioProcessor`

directly. In the
starter code we have hooked up your `fftData`

and `frequencies`

arrays to a new and incomplete
`Visualizer`

class. Upon building and
running your plugin, you should see the PGM GUI editor,
but if you try to plot your `fftPlot`

, nothing
happens yet.

In your starter code you have been provided with the files `Visualizer.h`

and
`Visualizer.cpp`

that implement the class `Visualizer`

. This class is derived
from `foleys::MagicPlotSource`

. The documentation for the parent class can be found here.

The `Visualizer`

class has two private members
which are `float`

pointers: `data`

and `x`

.

`void createPlotPaths (juce::Path& path, juce::Path& filledPath, juce::Rectangle` bounds, foleys::MagicPlotComponent& component);

which generates the plot of `data`

vs `x`

.
The function `createPlotPaths`

updates the
arguments `path`

and `filledPath`

,
and these updates are used
in `foleys::MagicPlotComponent`

's `paint`

method.

The `bounds`

argument is
a `juce::Rectangle`

containing the bounding
box of your plot. Methods such as `getX()`

and `getY()`

will be useful for getting its
location on the screen and drawing your plot. The
`juce::Rectangle`

class
reference documentation is useful. Every JUCE
`Component`

,
which represents a rectangular region on the screen,
uses `juce::Rectangle`

to hold its bounds.
See the
Point, Line, and Rectangle Classes Tutorial if
anything is not clear from the reference doc
for `Rectangle`

.

**It is up to you to implement this method.** A helpful example is the implementation of `foleys::MagicFilterPlot`

.

If you choose to do a straightforward plot, please
plot `data`

versus `x`

on a log-log
scale with your magnitude spectra in dB. This will make
your plot look more like what we hear.

After implementing your method, check it using
the `frequencies`

array generated
in `prepareToPlay`

```
for (int i = 0; i < FFT_SIZE/2; ++i)
frequencies[i] = i;
```

and the `fftData`

array generated in `processBlock`

```
for (int i = 0; i < FFT_SIZE/2; ++i)
fftData[i] = std::pow(10, 3)*std::sin(24.8* i/FFT_SIZE);
```

are now plotting in your GUI. Does your plot make sense given the generated data?
Now that you have a working plot, we want to feed it real-world data by using the JUCE library FFT. Carefully read the class documentation and review this tutorial.

After capturing your FFT data, plot (at least) the magnitude spectrum in a way that is clear, and please make sure to window your data!

Allocate the necessary buffers in `PluginProcessor.h`

and implement your signal processing code in `PluginProcessor.cpp`

in the `processBlock`

method. Update the `fftData`

array with your FFT data. Change the `freq`

array instantiation with the correct frequency lines values.

At this point you should have a working spectrum analyzer! But it could be better... it could be controllable!

In this section you will add parameters to your plugin, which will allow you to control the time smoothing of your FFT plot and display the spectral centroid. See this tutorial for a more in depth guide on how to add parameter and parameter listeners.

The first parameter you will implement is a leaky integrator that smooths the output of your FFT data by computing a running mean. The leaky integrator is a one-pole lowpass filter with unity gain at dc and is defined by a smoothing time-constant \(\tau\) in seconds.

Determine the leakage factor \( \lambda \) (which is the pole of your filter) as a function of \(\tau \), the sampling rate and your FFT buffer size. Your FFT update equation is then \[ FFT_{old} = \lambda * FFT_{old} + (1 - \lambda)* FFT_{new}\] Where \( FFT_{old} \) stores updated the time-averaged FFT plot and \( FFT_{new} \) is the incoming FFT data.

After implementing your leaky integrator, try hard-coding a \( \tau = \) 10 ms into you code. After compiling your code you should see less jitter and a smoother plot in your spectrum analyzer.

To change the value of \( \tau \) you will need to instantiate an `AudioParameter`

in the provided `createParameterLayout`

function.

`layout.add(std::make_unique< juce::AudioParameterFloat >(paramID, paramName, juce::NormalisableRange< float >(min, max, step), default));`

This line of code adds a new floating point audio parameter `juce::AudioParameterFloat`

with a unique string ID `paramID`

and string name `paramName`

. Note that `paramID`

must be a unique string and is used by the value tree to correctly identify parameters. `paramName`

will be the name that comes up in your PGM editor. See more documentation on AudioParameters and the different types of parameters here.

`juce::NormalisableRange<float>`

creates a floating point range based on a minimum value, maximum value, and step size. The `default`

value is the value the parameter is initialized as. Documentation on the range class can be found here.

After creating your parameter, it is important to add a listener to your value tree state. This listener will notify the plugin when the value of your parameter is externally changed. Implement the listener in your plugin's constructor.

`treeState.addListener(paramID, this)`

`paramID`

is once again your parameter's unique string identifier. `this`

is a pointer to the current class and does not need to be edited.
When your parameter changes, the function `parameterChanged(const juce::String ¶meterID, float newValue)`

is automatically called. The `newValue`

for a certain parameter with unqiue `parameterID`

is passed into this function. Based on what parameter string is passed we can update the behavior of our plugin.

In the case of our leaky integrator, say we get a new value of \( \tau \) from our GUI, in `parameterChanged`

we then update the value of \( \lambda \) that is used in our FFT update equation.

```
if (parameterID == "tau")
{
// Update lambda
}
```

You are now going to compute and print the spectral centroid in your plugin. The spectral centoid is defined as \[ \text{Spectral Centroid} = \frac{\sum_{i=0}^N f_i * |X_i|}{\sum_{i=0}^N|X_i|} \] Where \( f_i\) is the frequency value and \( |X_i| \) is the FFT magnitude coefficient at index \( i \).

Add a spectral centroid parameter to your parameter layout, but **do not** create a listener. This is because we don't want our spectral centroid value to change as the result of an external control. Parameters can be manually updated using the following code:

```
juce::Value paramToEdit = treeState.getParameterAsValue(paramID);
paramToEdit = myCalculatedValue;
```

This code creates a `juce::Value`

object that can be can be updated internally and will then update the `AudioParameter`

value in our GUI.
In your `processBlock`

function, compute your spectral centroid after computing your FFT. Use `getParameterAsValue()`

to create a `juce::Value`

item based spectral centroid `paramID`

. By assigning your computed spectral centroid value to the `juce::Value`

item you will update the GUI parameter.

Use the PGM `Label`

item and attach it to your spectral centroid parameter to print the spectral centroid in your plugin.

Now that you have a working smoothing paramter add some new parameters of your choice! Some ideas:

- dB Offset
- Window Type
- FFT Size

Congrats! You now have your own working spectrum analyzer. Play around with it and submit a video of you having some fun. Make sure that you can clearly correlate what you are doing audio-wise to what is happening on screen.

- Spectral centroid reference
- MagicPlotSource reference
- MagicPlotComponent Implementation
- JUCE Rectangle class reference
- MagicFilterPlot Implementation