Gin & Juce
Introduction
Over the past 13 years as an independent contractor I’ve worked on JUCE based projects for companies like Intel, reFX, QSC, Neyrinck Audio and Tracktion. During this time I’ve had to solve the same problems multiple times, so I came up with Gin, a collection of classes and utilities that compliment JUCE and allow me to work faster. Some parts of Gin have been well tested widely deployed in popular plugins, others are for my personal projects or ideas that went nowhere. Unfinished classes are marked with a comment, but feel free to ask me if something is ‘production ready’ before you use it. The following sections will cover some of what’s in Gin and why you might want to use it. This is not a complete list, but just the highlights, please see the online documentation for a full list.
Licensing
I’m not a lawyer or a master of Open Source licenses, but the intention is that for anybody who has a license to use JUCE (either GPL3 or Commercial) and also use Gin free or charge and without restrictions. Therefore I’ve released Gin under the BSD license. As parts of Gin are derivates of JUCE, you also need to abide by JUCE’s license terms. Gin also includes 3rd party code (either BSD, MIT or similar license). I have left the authors original copyright notifications in these files.
While I do not require your changes to be shared back to the main branch, it is appreciated. If you fixed bugs or have new features, please open a pull request.
Example Code
The Gin repository include a Demo Application that uses as many classes as possible with visual demos. There is also a Synth Demo that is a basic subtractive synth.
gin
The core gin module only depends on juce_core and juce_data_structures and is designed to be used from command line apps as well a GUI apps and plugins.
Catenary
A catenary is the shape a wire, cable, rope etc. makes when it hangs. This class is useful for drawing wire connections in a modular synth. Note that in this class the y axis inverted compared to the y axis in juce::Graphics so you will need to invert the y coordinates.
Ellipse
This class provides a set of functions to fine where lines intersect ellipses, points on ellipses, points inside and outside ellipses. Along with Catenary it can be useful for drawing wires with rounded ends.
DownloadManager
DownloadManager is useful for downloading multiple files in the background. Maximum number of concurrent downloads and retry limits can be set. Supports gzip compressed downloads, which JUCE doesn’t automatically support on Windows.
EquationParser
EquationParser takes strings like “6 * 3 + 5” and calculates the resulting value. Supports variables, constants, and functions. Parsing is cached and stored in byte code, so it is very fast if only variables change between evaluations. Based on muParser.
FileSystemWatcher
FileSystemWatcher monitors the file system for changes to files, much more efficient than polling for changes. Useful for file browsers to know when to refresh or watching for new presets and samples in a plugin. Supports Windows, macOS & Linux.
RIFFParser
RIFFParser parses files like WAV and AVI that are based on the RIFF format. Useful for getting metadata chunks that aren’t supported by the JUCE WavAudioFormat. Used internally by the free function gin::getWavetableSize().
gin_dsp
gin_dsp is split into DSP algorithms and Components which are used to display the output of or control those algorithms.
ADSR
A simple linear ADSR, best used as a modulation source.
AnalogADSR
A analog style ADSR based on the algorithm from Will Pirkle’s book. Much better for controlling auto levels than the other ADSR.
AudioEquationParser
Similar to the gin::EquationParser, this subclass adds audio related functions. And since these functions have state, they aren’t true functions without side effects anymore. However, it does let the user type things like (hp12(inL, 5000, 0.707) + hp12(inR, 5000, 0.707)) and it will filter the left and right channels and add them together. Since there is state, make sure you have a separate gin::AudioEquationParser for the left and right channel. For an example of how this can be used, see the Maths plugin.
AudioFifo
A simple wrapper around juce::AbstractFifo, that lets you push and pop from a juce::AudioSampleBuffer.
BandLimitedLookupTable
A collection of lookup tables for an oscillator, with a different lookup table for every few notes. This allows the oscillator to play back notes without aliasing. The tables can either be created from functions like saw, triangle, since, square and pulse. Or they can be loaded from a single cycle wave file, and then the higher frequencies will be removed with an FFT. These tables are used by the gin::StereoOscillator ∫and gin::WTOscillator.
DelayLine
Simple delay with multiple tabs. Either linear or Lagrange interpolation.
Dynamics
Compressor, limiter, expander and gate based on Will Pirkle’s book.
EQ
Bundles up a number of filters to make it easy to have multi channel, multi band EQ.
Filter
Bundles up a number of SVF filters to have multichannel filters with either 12 or 24 dB slope.
GateEffect
Tracegate style effect
LFO
Oscillator with 17 different waveforms, great as a modulation source.
MidiFifo
If your plug-in has latency, and you need to keep the midi in sync with the audio, then a gin::MidiFifo will do just that.
Modulation
Audio modulation, for a chorus or phaser etc
StereoOscillator
Audio rate oscillator with sine, saw, ramp, square, pulse and noise waves. Uses gin::BandLimitedLookupTables to avoid aliasing.
VoicedStereoOscillator
A template class that can take an Oscillator and add multiple voices, detune and spread.
PlateReverb
An implementation of the classic plate reverb algorithm described by Jon Dattorro.
ResamplingFifo
An audio fifo where you push ay one sample rate and pop at another. Uses SecretRabbitCode internally.
SampleOscillator
Not really an oscillator, but it plays audio files at various pitches, so it can be used to create a sample based instrument.
Synthesiser
A subclass of juce::MPESynthesiser but with much better voice handling. Adds fast kill support so there are no clicks when you run out of voices. Supports mono, legato, glissando and portamento. Number of voices can be changed with just a parameter, rather than having to construct and delete Voice objects.
ValueSmoother
A value smoother that only works on values between 0.0 and 1.0, but smooths at a fix rate, rather than a fixed time from current value to new value.
WTOscillator
Wave table oscillator with formant and bend phase distortion. Uses gin::BandLimitedLookupTables to avoid aliasing.
TriggeredScope
Triggered Scope that it can be set to start on a rising or falling signal. This makes it extremely useful for very zoomed-in waveform viewing.
WavetableComponent
Draws a sweet 3D looking wavetable.
gin_graphics & gin_webp
The gin_graphics module extends the juce_graphics module with 2 new image file formats: webp and bmp.
There are also several free functions for applying image effect to juce::Image including vignette, sepia, soften, sharpen, game, contrast, brightness, stack blur, higher quality AVIR resampling, Photoshop inspired blend modes.
gin_gui
The gin_gui module extends juce_gui_basics with the following new classes:
ComponentViewer
A useful tool for debugging, however the mouse over components to get the name, class, size, hierarchy, etc. Superseded by Melatonin Inspector.
MapViewer
Display an OpenStreetMap zoomable and scrollable map.
FilePropertyComponent / ColourPropertyComponent
juce::PropertyComponent subclasses for File and Colour
CoalescedTimer
A way to combine juce::Timers to ensure all timers with the same rate trigger at the same time. Usually for making sure all Component update from the same 60 Hz timer for example.
ElevatedFileCopy
For macOS and Windows, all the user to copy files into folders that require admin access.
Layout
This class probably deserves it’s own article. Allows Component layouts to be set from json, supports hot reloading to build layouts at runtime. Component positions are set by name, x, y, width and height. Functions are available to get the positions of other components. gin::EquationParser is used internally so mathematical expressions are supported. Square brackets can be used to layout several components at once.
The following code does the layout for components name lfo1, lfo2 and lfo3. The y position for lfo1 is prevB()+1 while lfo2 and lfo3 use prevY() for their y position. All other coordinates are the same for each. If you are using this class with gin_plugin, each knob, select or switch component will be named after the short name of the parameter it is controlling.
{ "id": "lfo[1..3]", "x": 0, "y": "prevB()+1,prevY()", "w": 280, "h": 163, "children": [ { "id": "Sync", "x": 0, "y": 23, "w": 56, "h": 70 }, { "id": "Beat", "x": "prevR()", "y": 23, "w": 56, "h": 70 }, { "id": "Rate", "x": "prevX()", "y": 23, "w": 56, "h": 70 }, ] },
For full examples see Organ or Wavetable.
AsyncDownload
Downloads on a background thread, but until the similar juce functionality, doesn’t do the DNS lookup on the main thread, which can lock up your UI.
gin_location
This is a work in process module for getting the current location from the device GPS. It is nowhere near ready for use.
gin_metadata
Loads EXIF, IPTC, XMP, and comment metadata from JPEG and PNG images. If you are displaying a JPEG that came from a camera, you’ll need to get the orientation metadata to ensure the image is displayed the correct way up. GPS location, focal length, aperture, ISO may also be useful to know.
gin_network
SecureStreamingSocket
A drop in replacement for juce::StreamingSocket but it supports SSL.
WebSocket
Webscocket client, supports HTTPS thanks to gin::SecureStreamingSocket
gin_plugin
This module really needs an article on it’s own. It provides subclasses of juce::AudioProcessor, juce::AudioProcessorParameter and juce::AudioProcessorEditor that make it much faster and easier to get started writing a effect or synth plugin. Preset loading and saving is handled. The editor has a titlebar for loading / saving presets and a patch browser. The plugin editor layout can either be done via json or snapped to a large grid.
See the HugeGain plugin for a simple example, or Wavetable for a full featured synth.
The following code:
PluginEditor::PluginEditor (PluginProcessor& p) : gin::ProcessorEditor (p), proc (p) { for (auto pp : p.getPluginParameters()) { ParamComponent* pc; if (pp->isOnOff()) pc = new Switch (pp); else pc = new Knob (pp); addAndMakeVisible (pc); controls.add (pc); } setGridSize (6, 1); } void PluginEditor::resized() { gin::ProcessorEditor::resized(); componentForId (PARAM_GAIN_L)->setBounds (getGridArea (1, 0)); componentForId (PARAM_GAIN_S)->setBounds (getGridArea (2, 0)); componentForId (PARAM_GAIN_R)->setBounds (getGridArea (3, 0)); componentForId (PARAM_CLIP)->setBounds (getGridArea (4, 0)); }
Creates the following UI:
The gin::Parameter class supports optional smoothing to avoid zipper effect. Modulation via gin::ModMatrix.
Setting up the parameters is as follows:
gainl = addExtParam (PARAM_GAIN_L, "Left", "", "dB", {-100.0f, 100.0f, 0.0f, 5.0f}, 0.0f, 0.1f); gains = addExtParam (PARAM_GAIN_S, "Both", "", "dB", {-100.0f, 100.0f, 0.0f, 5.0f}, 0.0f, 0.1f); gainr = addExtParam (PARAM_GAIN_R, "Right", "", "dB", {-100.0f, 100.0f, 0.0f, 5.0f}, 0.0f, 0.1f); clipp = addExtParam (PARAM_CLIP, "Clip", "", "", { 0.0f, 1.0f, 1.0f, 1.0f}, 1.0f, 0.1f, onOffTextFunction); gainl->conversionFunction = [] (float in) { return Decibels::decibelsToGain (in); }; gains->conversionFunction = [] (float in) { return Decibels::decibelsToGain (in); }; gainr->conversionFunction = [] (float in) { return Decibels::decibelsToGain (in); };
And then the process block function is as follows, doing it sample by sample if smoothing is currently active or by blocks if parameters have changed recently:
void PluginProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer&) { int numSamples = buffer.getNumSamples(); if (isSmoothing()) { int pos = 0; while (pos < numSamples) { auto workBuffer = sliceBuffer (buffer, pos, 1); workBuffer.applyGain (0, 0, 1, gainl->getProcValue (1)); workBuffer.applyGain (1, 0, 1, gainr->getProcValue (1)); workBuffer.applyGain (gains->getProcValue (1)); pos++; } } else { buffer.applyGain (0, 0, numSamples, gainl->getProcValue (numSamples)); buffer.applyGain (1, 0, numSamples, gainr->getProcValue (numSamples)); buffer.applyGain (gains->getProcValue (numSamples)); } if (clipp->getUserValue() != 0.0f) clip (buffer, -1.0f, 1.0f); }
There are also numerous components for the plugin editor including LFO display, envelope display, patch browser, mod matrix, modulation source draggers, step lfo editor. The Wavetable synth uses no custom components, other than gin::ParamBox subclasses to group components, everything else is from Gin.
Conclusion
That is a high level overview of the Gin library. In future posts, I’ll give into some of the classes in more detail. If you have any questions or bugs, join the discussion or issues on GitHub or ask on my Discord.