This guide documents the process of using the Graphical Module (GM) to build an interface that controls a continuous synth. Its purpose is to illustrate how to setup GM classes with a concrete example. It can also be used as a copy-paste template to build other synths interfaces.
The full example code is located at the end of this guide.
The synth I'm using is a simple continuous synth which uses frequency modulation.
It has 4 overall parameters :
First, each control is nested inside a container using layouts. This allows to provide both the control(s) and a GMTextView that identifies what the control does :
Before instanciating GM classes, I first setup a GMStyle that will define how widgets are drawn :
This allows to unify style properties management within a single code block.
As you can see, I'm using deepCopy
to quickly define a second style that inherits from the first style properties. This style will be applied to controls GMTextView so that their design is smoother and does not catch the eye as much as controls do (this is mostly about reducing border sizes).
When instanciating a GM object, the style must be applied to it using the style
method :
The amplitude control uses GMFaderSlider defaults value : a linear scale from 0
to 1
. Its action
just sets the synths \amp
parameter to the chosen values.
Helpers are displayed to ease the volume selection. They are configured so that there's 3 big helpers, and 4 small helpers inbetween each helpers pairs. This means there's a total of 11 helpers, allowing to snap the value to a multiple of 0.1 when holding the SHIFT key down.
Mod steps are optionnal, but allow to refine the control behavior.
The first mod step, that is used when holding only the CTRL key down, allows to smoothly increment and decrement the volume. This allows, for example, to simulate a LFO with the mouse.
The second mod step, that is used when holding both CTRL + SHIFT keys down, behaves the same, but with a bigger pixel range. This means it take more mouse movement to increment / decrement the value. This allows to fine tune the volume very precisely.
Spread allows to detune stereo channels, using this formula :
The expected range therefore begins at 1, and 1.25 is a good maximum. Values close to 1 give a nice stereo effect that make the sound 'oscillate'. Higher value will render the offset between the two channels more audible, thus sounding 'detuned'.
The chosen scale is a curved one, meaning that at first, the slider will increment the spread value really slowly, allowing to fine tune the stereo effect. Higher values will increment more rapidly, because the detune effect doesn't need to be as precise. When specifying a number as scale
parameter, the lincurve
mapping method will be used.
The spread parameter is a ratio, so I find it better expressed as a percentage. This is what the display function does, adapting the displayed value to a percentage equivalent, without affecting the argument passed to the synth :
Helpers are drawn mostly for esthetic purposes, and mod steps have nothing particular other than allowing to play with the control smoothly.
This time, the base frequency parameter uses two distinct controls. This allows to either pick any frequency within an octave range, or to specifically select a note.
The continuous frequency selector is a simple slider, that will round the displayed value for clarity :
A GMMultiButton allows to select a particular 'named' note. Both note names and their respective frequencies are stored into arrays, and the multi-button acts as a 'bridge' between them :
Since both widgets modify the same control, they do not update the synth directly : they call a Function which is responsible for the Synth update, then updates both widgets according to the new frequency :
Then, an other GMMultiButton simply allows to select a frequency multiplier that acts as an octave control.
The synth plays two different signals. The first is a sinusoïd oscillating at the specified frequency. The second is an other sinusoïd which frequency is modulated by an other sinusoïd.
Frequency modulation uses two distinct parameters : a frequency, and an index. This frequency is often expressed as a ratio (in this case, relative to the base frequency).
Using a GMSymbol2DSlider allows to play with both controls at once.
The index parameter, on the horizontal axis, has no special property. It is a simple linear parameter that changes the texture of the sound.
The frequency ratio parameter, however, has an interesting property. When its value is a power of 2 (0.5, 1, 2, 4, etc), both the first and second signal will be in sync, with the second signal playing two sinusoïds which frequencies are octaves of the first signal. When its value isn't a power of 2, signals won't be synced, thus being detuned (unless the value ends up producing harmonics of the first signal, which is unlikely).
By setting up mod steps carefully, this allows to interact with the synth in two different ways : the normal behavior will allow to detune the synth and to play with the full range of frequency modulation. Holding CTRL + ALT keys down while moving the mouse will instead force the value to snap to a power of two : in this case, the synth will always be tuned.
When a particular frequency ratio has been chosen, it becomes possible to only play with the index by pressing the CTRL key down.
The following code allows to setup a fullscreen switch when the user presses CTRL + F :
Now that every container is defined, they are distributed inside the window using layouts.
The window is also setup so that closing the window, pressing ESC
or using CTRL + .
will free the synth and close the window if needed.