soundsynth icon indicating copy to clipboard operation
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:

  1. BPM Clock with trigger and gate
  2. Sequencer x3
  3. ADSR envelope for amplitude and filter
  4. State Variable Filter
  5. Phase modulation
  6. Waveform generators + collection imported from Plaits
  7. Waveform oscillator
  8. 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:

  1. Clock / Sequencer generates MIDI note + information about the gate (open/close + trigger)
  2. Both ADSRs (amplitude and filter) are triggered
  3. Oscillator generates sample from wavetable (actually two wavetables mixed together) and phase which is calculated from note frequency and phase modulator (the other oscillator)
  4. Signal can be shaped by another oscillator
  5. Signal is filtered by filter controlled by ADSR
  6. Amplitude ADSR is applied
  7. Above procedure is applied for two other sequencers and mixed (added) together
  8. 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:

  1. For given frequency scaling factor, gain and one-pole low pass filter coefficien are calculated
  2. Current phase is taken from previous phase and step (taken from given frequency)
  3. Integrated sample is read from wavetable using 4-point hermite interpolation for given phase
  4. 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.