soundsynth
soundsynth copied to clipboard
Sound synthesis in Clojure
Sound synthesis from the scratch
Wavetable sound synthesis engine.
It's a proof of concept helping understaing the topic and eventually implementing similar synthesizer on STM32 ARM chip.
What's included:
- BPM Clock with trigger and gate
- Sequencer x3
- ADSR envelope for amplitude and filter
- State Variable Filter
- Phase modulation
- Waveform generators + collection imported from Plaits
- Waveform oscillator
- Waveshaper based on waveforms, bipolar and unipolar
Live sound synthesis
To play a sound load and run sound.core
(use toggle-playing
to start/stop sound generation).
sound.patch
defines complete configuration of the synthesizer you can change it live(!).
Example song
Listen to the Lost Woods from Zelda - Koji Kondo generated by syntesizer. This is what is defined in sound.patch
Implementation notes
Implementation is highly based on Mutable Instruments source code and enables easy translation to C++ (custom types and class-like modularity).
Signal path
Singal is created by following path:
- Clock / Sequencer generates MIDI note + information about the gate (open/close + trigger)
- Both ADSRs (amplitude and filter) are triggered
- Oscillator generates sample from wavetable (actually two wavetables mixed together) and phase which is calculated from note frequency and phase modulator (the other oscillator)
- Signal can be shaped by another oscillator
- Signal is filtered by filter controlled by ADSR
- Amplitude ADSR is applied
- Above procedure is applied for two other sequencers and mixed (added) together
- Finally, hard clipping is applied + quantized to 8-bit depth
Every step generates new sample which are gathered in small buffer and send to audio engine.
Waveforms
Waveforms are bandlimited (generated as sum of sines or as FFT/IFFT interpolation) wavetables of 256 samples. Waveform is stored as integrated signal (according to Higher-Order Integrated Wavetable Synthesis) as 16 bit signed integer (short
type). This representation + following procedure generates sample with minimized aliasing.
Sample generation follows the procedure:
- For given frequency scaling factor, gain and one-pole low pass filter coefficien are calculated
- Current phase is taken from previous phase and step (taken from given frequency)
- Integrated sample is read from wavetable using 4-point hermite interpolation for given phase
- Difference with previous value + one-pole low pass filter + scaling + gain gives the sample
State Variable Filter
State Variable Filter is used here because it's simple and doesn't require trigonometry (actually it's partly true, tan
is required but some interpolations can be used instead) functions to calculate the parametrization and the result. The second: 3 filtered samples are generated at once: low pass, high pass and band pass.
Phase modulation and waveshaping/folding
There is an option to modulate the phase (which is cheap replacement for frequency modulation (FM) - but works well enough) by other oscillator. Also there is an option to shape wave by any defined waveform.
Phase modulation
It works by adding value of one oscillator (multiplied by amount) to the phase of second oscillator.
Shaping/folding
Amplitude of the signal is used as a phase of the shaping oscillator. Both values are mixed together (you can control amount)
ADSR
Attack-Delay-Sustain-Release shaper is used to control amplitude of the signal and frequency of the filter. Slopes are exponential.
Patch
Patch, as a configuration of every module in the path, are kept as global variables to allow live coding. Playing is done in separated thread as future
. Thread is stopped via playing?
atom.
Sound quality
- Sample rate: 44100.0Hz
- Bit depth: 8 (-128 to 127)
- mono
- 3 independent tracks (sequencers) mixed together.
TODO
- echo / delay
- resonator (implemented, not tested)
- build full collection of waveforms
- write blog post about this implementation
- translate to C++
License
Copyright © 2020 GenerateMe
This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0.
This Source Code may also be made available under the following Secondary Licenses when the conditions for such availability set forth in the Eclipse Public License, v. 2.0 are satisfied: GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version, with the GNU Classpath Exception which is available at https://www.gnu.org/software/classpath/license.html.