Stm32-monosynth is a monophonic digital synthesizer, controllable through a midi in port. It was developed using the STM32f407vg board, running a version of the Miosix operating system, for which a DAC support of the board (Cirrus Logic cs43l22dac) has been implemented.
The synthesizer is able to generate audio at the sampling rate 44.1Khz with 16 bit resolution, the rendering is at low latency (2.9ms in the tested configuration) and therefore allows the user to interact with it fluidly.
In order to compile the firmware and generate the binary file that needs to be flashed into the board, it’s necessary to follow the platform dependant instruction described in the Miosix wiki and to instal the toolchain supported by the operating system (https://miosix.org/wiki/index.php?title=Main_Page).
The compilation is performed using a Makefile, the CMakeList.txt file is instead provided only as a tool to enable indexing in an IDE driven environment and should not be used for compiling the project.
The synthesizer includes a low aliasing saw oscillator made using the Differentiated Parabolic Waveform (DPW) technique. This oscillator can be controlled in frequency through the midi in port, with a note message. The oscillator is then connected to a resonant 4 poles resonant lowpass filter, based on zero delay feedback technology, which filters its harmonics. A single attack-release exponential envelope is provided to simultaneously control the output gain and the cutoff frequency of the filter.
The stm32-monosynth hardware user interface includes 4 rotary pressure encoders, which allow you to control some of the synthesizer parameters. Encoders 2 and 4 offer alternative control in case they are pressed during rotation.
The following table illustrates the functions controlled by each encoder, the P symbol indicates that the encoder needs to be pressed to modify the parameter.
Encoder | Function |
---|---|
1 | glide time |
2 | base filter cutoff frequency |
2P | filter cutoff envelope amount |
3 | filter resonance |
4 | envelope release |
4P | envelope attack |
The software architecture is based on the use of the Miosix operating system (https://miosix.org), mainly used for concurrency management and for the interfaces to the serial peripherals it implements (which were necessary to implement the midi protocol).
The driver used to implement the encoders was written from scratch by managing the board registers. To allow future implementations on other boards of the stm32 family, it is possible to reimplement the core_util interface (whose header is found in include/drivers/core_util.h). This interface exposes a series of functions used to initialize the peripherals.
The interconnection of the hardware components with the various pins of the board are mediated through a configuration header (include/drivers/hardware_configuration.h), which allows the routing to be changed if necessary. You can check the default configuration in a table shown in one of the following sections.
Regarding the audio architecture aspect, the microaudio framework was used (https://github.com/FedericoDiMarzo/microaudio), which laid the foundation for the design of the audio driver and the dsp code used to build the audio engine.
Microaudio is an open source framework for developing audio applications aimed at embedded environments. It was used to facilitate and organize the development of the stm32-monosynth audio engine in a structured way. More details about the framework can be found on its github page.
https://github.com/FedericoDiMarzo/microaudio
To develop the audio driver of the board, a microaudio AudioDriver
was implemented. The runtime behavior of the driver for its different methods is shown below.
This particular implementation takes advantage of the board's DMA peripheral and its Cirrus Logic cs43l22dac digital-to-analog converter. The class miosix::BufferQueue
is used to copy the contents of the buffer to send to the DMA. When the DMA transfer is complete, the device notifies the start method to continue the audio processing.
The original repository used to develop the driver can be found following this link https://github.com/andreaco/miosix-audio .
Regarding the concurrency description of the software, stm32-monosynth is a multithreading application based on Miosix implementation of std::thread
. Four different threads can be distinguished:
- Audio thread: thread used to render the audio using the
AudioDriver
. - Midi thread: waits until a midi message reaches the system through the serial port, and then acts as a writer for a shared queue containing the midi messages.
- Synth thread: acts as a reader for the midi queue, and then applies a specific command to the synthetiser (note on, note off).
- Interface thread: periodically reads the values from the encoders and updates the synth parameters.
A runtime diagram is shown below for a more detailed analysis of the software execution.
All the shared resources are protected from race conditions using the miosix::FastMutex
locking mechanism offered by Miosix
Stm32-monosynth implements its audio architecture proposing different AudioModule
derived classes, defined in the microaudio framework.
Being stm32-monosynth a subtractive synthesizer, the sound synthesis chain starts from an oscillator capable of producing a rich harmonic spectrum. A low aliasing sawtooth oscillator is implemented as a microaudio AudioModule
exploiting the differentiated parabolic waveform technique [Valimaki 2006]. This method allows to produce a sawtooth waveform starting from a parabola, and differentiating the result employing the Euler backward method. A schematic is here shown to depict the process.
The output scaling gain g(f) is frequency dependent, and it's be calculated as:
where Fs is the sampling frequency (44100Hz for the synth) and f is the target frequency for the oscillator.
Another AudioModule
implemented and used in this project, is the Envelope
. Its design reflects the behaviour of an analog exponential envelope. To reach this goal, a state machine approach is employed; at each state change the envelope switches between different steady state values (1 for the attack state, 0 for the release state), to obtain the exponential evolution, the steady state constant output of the states is filtered with an exponential moving average filter, that smooths the transitions exponentially.
The envelope changes its state depending on the midi message received, a note-on message triggers the attack state and a note-off triggers the release. This design reacts fluidly to unfinished state changes; if for example a new note on is triggered before the envelope finishes the release state, the new attack phase will start from the point of interruption (and it wont restart from 0).
Stm32-monosynth contains an AudioModule
implementing a 4 poles resonant lowpass filter that was obtained from the discretization of a 4 poles analog ladder filter employing a zero delay feedback design [The art of VA Filter design, Vadim Zavalishin]. The filter consists of a series of four 1 pole lowpass filters with a feedback path.
The resonance of the filter is modified changing the feedback gain k, and it allows to approach the self oscillation state with k=4. Increasing further the value of k will bring to an unstable behaviour.
Each 1 pole lowpass is realized with a zero delay feedback topology. This topology preserves most of the audio quality of the filter even when its frequency is modulated. This is the schematic of the 1 pole lowpass section.
The cutoff frequency of the filter can be changed varying the gain g.