Creating Synthesizer Sounds Using CircuitPython Synthio With Pimoroni PGA2350 or Cytron Maker Pi Pico
by kevinjwalters in Circuits > Audio
629 Views, 15 Favorites, 0 Comments
Creating Synthesizer Sounds Using CircuitPython Synthio With Pimoroni PGA2350 or Cytron Maker Pi Pico
This article shows the process to create a synthesizer patch step-by-step in code using the synthio library in CircuitPython. The synthio library provides a flexible way to create patches (sounds) with polyphonic playback on a wide range of microcontrollers using DAC, I2S or PWM audio output. The patch created is loosely based on one originally made on an analogue synthesizer in the typical subtractive style.
The patch is played monophonically using a MIDI keyboard controller and the new Pimoroni PGA2350 with some external circuitry. This custom hardware will be covered in a future article. The Cytron Maker Pi Pico is an ideal alternative as it has the audio filtering for PWM output and a 3.5mm stereo audio socket.
The background in the video used to accompany this article is VIRTUAL JAPAN's Tokyo Rainy Night Walk in Shinjuku on YouTube.
Supplies
- A computer for routing MIDI over USB.
- A MIDI controller or alternative MIDI source.
- A suitable microcontroller (first two boards have 3.5mm sockets for PWM-based audio output).
- Cytron Pi Pico: The PiHut | DigiKey
- Cytron EDU PICO. The PiHut | DigiKey
- Other microcontrollers may be usable too, check Adafruit Guide.
- The Pimoroni PGA2350 with unusual use of stripboard will be described in a subsequent article. The main attraction of this board is the RP2350B chip, the larger variant with all of the GPIO including 8 ADC but it isn't really suitable for use with stripboard.
- Affordable effect units.
- Korg NTS-1 kit - Korg provide an SDK if you wish to create your own oscillators or effects - simple self-assembly required.
- Korg Mini Kaoss Pad 2s - cheaper, older models are available second-hand - check battery compartment for leakage before purchase.
- Korg NTS-3 kaoss pad kit - a programmable effects unit with x-y pad - simple self-assembly required.
- Zoom MultiStomp pads and Guitar Effects - MS-70CDR is often used with synthesizers.
Synthesizer Evolution - One Hundred Years to Synthio
Synthesizers have evolved over more than 100 years with digital synthesizers appearing in the late 1970s and software synthesis becoming common in the 1990s. The 120 Years of Electronic Music has a thorough list of synthesizers over time and Google Arts & Culture: Instrument history: the synthesizer is a nice visual summary.
A few interesting examples are listed below in time order.
- 1922 Theremin: 120 Years
- 1929 Ondes-Martenot - 120 Years
- 1951 RCA Synthesizer - 120 Years
- 1959 Siemens Modular Synthesizer - 120 Years - photograph above from the Deutsches Museum.
- 1967 Stylophone: Wikipedia
- 1969 Electronic Music Studios (EMS) VCS3: VintageSynth | Wikipedia
- 1970 Moog Minimoog (Model D) - VintageSynth | Wikipedia
- 1977 Yamaha CS-80: VintageSynth | Wikipedia
- 1978 ARP Odyssey MkIII: VintageSynth | Wikipedia
- 1979 Maplin 5600s (kit): Trevor Marshall | Synth DB
- 1979 Fairlight CMI: VintageSynth | Wikipedia
- 1981 Roland TB-303: VintageSynth | Wikipedia
- 1983 MIDI standard
- 1983 Yamaha DX-7: VintageSynth | Wikipedia
- 1984 E-mu Emulator II: VintageSynth | Wikipedia
- 1984 Casio CZ-101: VintageSynth | Wikipedia
- 1988 Akai MPC60: VintageSynth | Wikipedia
- 1988 Korg M1: VintageSynth | Wikipedia
- 1989 Steinberg Cubase (DAW) on Atari ST: Wikipedia
- 1995 Eurorack standard
- 1996 Virtual Studio Technology (VST) interface standard
- 2011 Teenage Engineering OP-1: Wikipedia
- 2012 Artutia MiniBrute: VintageSynth | Wikipedia
- 2013 Novation Bass Station II: VintageSynth
- 2013 Korg Volca Keys: VintageSynth | Wikipedia
- 2014 Serum (VST): Sound on Sound
- 2021 CircuitPython original synthio: Github
- 2023 CircuitPython synthio: Adafruit Blog - image above is todbot's Adafruit QT Py RP2040 and PCM5102 I2S DAC wavetable synth.
The synthio library was originally developed by Artyom Skrobov in 2021 for playing MIDI files using square waves (beeps). Jeff Epler significantly enhanced it with the help of others to provide an advanced, polyphonic synthesizer with "semi-modular" flexibility. John Park has written many of the Adafruit articles about it and Tod Kurt (todbot) has created lots of interesting synthesizer code and hardware using it.
Starting With a Single Oscillator Playing a Sawtooth Wave
The synthio.Note object is used to create a playable tone. The waveform can be specified and here is based on a sawtooth waveform - this is commonly used in analogue synthesizers as it's rich in harmonics. The amplitude is reduced from 1.0 to reduce the chance of the output reaching its maximum positive or negative value. It is important to avoid clipping as this form of distortion tends to sound terrible.
The midi_to_hz() function turns the MIDI note number into the (fundamental) frequency of the note. The note A from the fourth octave is note 69 which translates to 440.0Hz. In synthesizer terminology the components creating repeating waveforms are referred to as oscillators.
An analogue synthesizer uses voltage-controlled oscillators (VCO), synthio is more akin to a digitally-controlled oscillator (DCO). There is also a more rarely-used term, numerically-controlled oscillator (NCO).
The addition of a small random value is an attempt to mimic the inaccuracies of an analogue synthesizer. It may be ineffectual as the pitches are likely to be quantized to some extent due to the hardware limitations of the waveform sample playing.
The sawtooth waveform is created from a short ulab array of 16bit signed integers. The Note object interpolates the values by default leading to a less steppy waveform than might be expected.
PWM audio output places a limit on the resolution of output levels. There is a trade-off between sample output rate and the resolution of the duty cycle which translates into the number of discrete output levels. This is discussed for the nRF52840 microcontroller in the PWM As a Substitute for DAC page of Instructables: Make a Component Tester With Adafruit CLUE and Kitronik Inventor's Kit.
Adding a Second Oscillator at a Slightly Different Frequency
A second perfect, synchronised oscillator would simply increase the amplitude (volume) of the first one but an intentional frequency offset can create a pleasing sound. The offset here is a random one between 0.034 and 0.036 semitones, AKA 3.4-3.6 cents.
At certain debug levels the program will print the patch number from a Program Change midi message. This allows a MIDI controller to select the patch. The previous patch is numbered 0, this one is numbered 1. This can be seen on the terminal window in the video as "PC: 1"
Adding a Third Oscillator
A third oscillator is added at -4.5 cents from the central frequency to further enrichen the sound.
Shaping the Volume With an ADSR Amplitude Envelope
Up until now the note abruptly turns on and then off as the key is pressed and released. In modular synthesis the "gate" signal controls this on/off. An amplitude envelope allows the signal level (equivalent to volume) to be controlled and can provide a closer emulation of real instruments and playing techniques. The ADSR envelope is a classic way to specify the parameters of an envelope. In an analogue filter the signal level is controlled with a voltage-controlled amplifier (VCA).
This is assigned to the envelope property on all of the oscillators.
Unlike a piano this particular envelope does not vary the sound with the speed of the key presses.
Changing the Timbre With a Low-pass Filter
A low-pass filter can be used to remove the higher harmonics in the sawtooth. A resonant filter can also boost certain harmonics. The filter cutoff frequency is normally set based on the note frequency. In an analogue synthesizer this non-fixed filter may be referred to as a voltage-controlled filter (VCF). The type of filter in an analogue synthesizer is a key part of its character.
This is assigned to the filter property on all of the oscillators.
The default offset applied to the note number is high enough for the filter to have a subtle effect here.
Adjusting the Envelope Based on Key Velocity Plus Adding Vibrato
Velocity Sensitivity
The MIDI note on message can provide a velocity between 0 and 127. This is used here to vary the overall amplitude slightly and to more dramatically vary the attack on the envelope. A slow key press will make a gradually increasing note and a fast one will create a slightly louder more instance one.
This is assigned to all of the oscillators.
Key-triggered Vibrato with delayed LFO
This component of the pitch variation uses the mathematical functions in the synthio library. The lfo_note_vibrato low frequency oscillator (LFO) is used to change the pitch with a default triangle wave. LFOs are just oscillators which work below audible rates, i.e. below 20Hz. In this case the oscillator is started then then runs constantly, it's a "free-running" oscillator.
The vibrato effect is gradually introduced with a delay by multiplying it with the lfo_slowstart_note_vibrato which slowly ramps up after being started (triggered) with each key press.
This is assigned to the bend property on all of the oscillators.
The lfo_slowstart_note_vibrato can be seen below. It starts with two zeros creating the delay and then slowly ramps up to the maximum waveform value of 32767. The once value is an important difference here. It stops the "oscillator" on the final value meaning it's not really a true oscillator. In synthesizer terminology these are called one-shot or single-cycle LFOs.
The video shows this working but there's no real control over the degree of the vibrato effect and the chosen level may be too much.
Mod Wheel Control of Vibrato
It's common for synthesizer keyboards and MIDI controllers to have a modulation wheel and a self-centring pitch bend wheel next to the keybed on the left side. The "mod wheel" can be used to provide control over the vibrato by placing the value passed in MIDI continuous controller (cc) messages in modwheel_math.
The synthio.Note object only has one parameter, bend, for pitch bending hence the summation of this value with pitch_bend_control_math to enable the pitch bend wheel. The Math operations take three parameters with the second parameter defaulting to 0.0 and third to 1.0. It's very important for the SUM below to specify 0.0 for the third parameter - if this were omitted then the code would appear to work but 1.0 would get added resulting in an octave increase for this use!
The Math values are initiated with 0.0 (shown below) and then the a properties are set in the main loop based on MIDI messages (not shown).
Adding Vibrato Using Aftertouch
The M-Audio Axiom range support a feature commonly known as aftertouch. When a key is pressed, any further additional pressure causes a MIDI channel pressure message to be sent with a value between 0 and 127. Intuitively, this value would be per key but it's actually a global value and only per key on modern MIDI Polyphonic Expression (MPE) controllers.
The aftertouch pressure is used here as an alternative way to control the vibrato. The mod wheel can still be used with the two values simple being added together.
The value of 0.8 is scaling down the effect of aftertouch. This value was chosen based on some testing with the keyboard as the normal maximum value (equivalent to 1.0) sounded too extreme.
Programmed Pitch Slides
The previous page showed the lfo_pitch_slide value as the third parameter to the synthio Math SUM.
This lfo_pitch_slide is another one-shot LFO with the scale and rate varied based on the desired direction (pitch going up or down) and speed of the slide. This is a crude way to emulate a far more flexible ribbon controller. The pitch slides triggered by pressing a pad and holding it down on the controller work surprisingly well for this case.
A ribbon is a rare feature on a keyboard, the Yamaha CS-80 has one and the Ondes Martenot has a ring on a wire with similar functionality. There are third-party MIDI ribbon controllers which can be placed on/near an existing keyboard.
Installing CircuitPython and Synthesizer Program
If you are not familiar with CircuitPython then it's worth reading the Welcome to CircuitPython guide first.
- Install the latest version of CircuitPython (9.2.0-beta.1 October 2024 - this is a beta because the RP2350 microcontroller is new) from https://circuitpython.org/ - this process is similar to the one described in Introducing Adafruit Feather RP2040: Installing CircuitPython.
- Verify the installation by connecting to the serial console over USB. The REPL prompt shows the version number. The version can also be checked by inspecting the boot_out.txt file on the CIRCUITPY drive.
- Install this library from a recent bundle from https://circuitpython.org/libraries into the lib directory on CIRCUITPY:
- adafruit_midi.
- Download the example program to CIRCUITPY by clicking Save link as... on synthio-patch-development.py
- Rename or delete any existing code.py file on CIRCUITPY, then rename the synthio-patch-development.py to code.py. This file is run when the CircuitPython interpreter starts or reloads.
The versions used for this article were:
- CircuitPython: 9.2.0-beta.1
- CircuitPython library bundle: adafruit-circuitpython-bundle-9.x-mpy-20241019
USB MIDI Controller to USB CircuitPython Message Routing
The (free) Tomarus Internet Media Web MIDI Utility can be used in a Web MIDI compatible browser to route the MIDI messages on a computer from a controller to a CircuitPython program. The Router panel is shown above with channels 1 and 10 being sent from the Axiom 25 to CircuitPython.
Putting It All Together With Effects
"Man has made his patch... now it's time to play".
The artificial sounds created by a synthesizer do not have the natural sound effects from the subtle sound reflections from playing in a room, concert hall or church. These reverberation effects can be added with additional processing and are often exaggerated to enhance the sound. The processing to add reverberation requires computational and memory resources which aren't available on a microcontroller that's already synthesizing sounds using synthio.
The Korg NTS-1 was used in the final part of the video above to post-process the sound from the PGA2350 board adding the hall reverb effect with maximum delay and depth plus a tiny bit of stereo delay.
The M-Audio Axiom 25 MkII keyboard controller is set to MIDI channel 1 for keys and channel 10 for its eight pads. The latter are used for pitch slides up and down at four speeds.
Ground Noise
There are a few approaches to avoid ground loops and minimise noise from poor grounding:
- Use equipment with balanced audio outputs.
- Use linear power supplies with traditional transformers. Weight is a primitive indicator - a light wall-wart power supply will use a noisy switched-mode power supply. USB power from a desktop computer tends to have a high degree of noise on it.
- Use a USB hub with ground isolation - these are niche products and tend to be expensive.
- Battery power equipment.
- Use ground (loop) isolators between equipment.
- Keep cables short and away from mains cables.
- Avoid low quality DIN to USB MIDI converters - anything below 20 USD/GBP/EUR should be viewed with suspicion.
The NTS-1 was powered by a power bank and the Zoyi ZT-703s multimeter/oscilloscope was left on battery power. An inexpensive ground (loop) isolator was used in between the output of the PGA2350 and NTS-1 to prevent ground noise from the USB-C power from the desktop computer contaminating the audio.
A Closer Look at PWM Audio Output Using Cytron EDU PICO
The RP2040 and RP2350 microcontrollers do not have digital-to-analogue converters (DAC). The only way to produce an audio signal without additional hardware is to produce a high frequency PWM signal, hardware filter this to produce an analogue signal and then use a coupling capacitor to produce an AC audio signal without a DC offset. This should not be confused with an LFO pulse-width modulating a square waveform oscillator which is a common technique for audio synthesis.
The left channel of the PWM audio output from a Cytron EDU PICO is shown above on a Zoyi ZT703-S multimeter/oscilloscope. The EDU PICO board was used to check the code works on the lower specification RP2040-based microcontroller. The code works fine as long as the display isn't used. If a previous program has used displayio then the serial console printing will be shown and these updates will interfere with synthio.
The two images are middle C (C4, MIDI note 60) on the left and silence on the right. The sound is a sawtooth-like waveform approximately 380mV peak to peak and the silence is a crude triangle waveform 280mV pk-pk, not much less! Middle C should be 261.626Hz - 7.7 divisions at 500us is 259.7Hz - this is as accurate as counting tenths of a division visually will allow. The silence has 8.2 divisions at 2us equating to 121.9kHz - this must be the PWM (carrier) frequency on the RP2040 PWM audio implementation. Digital oscilloscopes may visually misrepresent this high frequency with the timebase set to 500us per division.
At a guess the implementation will be dividing the 125MHz Pi Pico RP2040 clock by 1024 or 1000 equating to 122.0703125kHz or 125kHz, respectively. The EDU PICO has filtering to remove frequencies above audio range but the simple low-pass (single-pole) RC filter (the triangle-like waveform for silence is the square wave charging and discharging the capacitor) only attenuates with a slope of 6dB per octave leaving the surprisingly large carrier frequency in the inaudible ultrasound range. This noise in the signal could affect some downstream audio equipment. In this case the Korg NTS-1 and the desktop PC line input, they do not seem affected although there is a ground (loop) isolator in the path which will further attenuate very high frequencies to some degree.
Going Further
Some issues around this patch to explore:
- The patch has a mildly unpleasant screech starting around the sixth octave.
- The filter with certain parameters can turn the sound into complete noise! This is worth investigating, understanding and preventing. This was discussed in a forum and is caused if the filter frequency is above half the sample output rate, the nyquist frequency.
- It would be useful to be able to increase the ADSR release for a playing note, i.e. after a key has been pressed and while it's being held down.
- Check the accuracy of the note frequencies and the quanitzation of pitch in synthio on the RP2040 and RP2350.
- If using an RP2350 keep an eye out for bugs, this is a new port of CircuitPython.
This article may have helped you to create your own synthio sounds. Here are some other articles, videos, products and books which may help or inspire.
Some CircuitPython audio synthesis and MIDI projects:
- synthio
- Adafruit Learn: Audio Synthesis with CircuitPython synthio
- Adafruit Learn: Faderwave Synthesizer
- Adafruit Learn: Tyrell Desktop Synthesizer
- Adafruit Learn: Guitar Synth with CircuitPython SynthIO
- Adafruit Learn: Circle of Fifths Euclidean Synth with synthio and CircuitPython
- Adafruit Learn: Computer Perfection Synthesizer
- todbot
- eighties dystopia in synthio (YouTube)
- Pygamer wavetable renderer + synth for CircuitPython synthio (YouTube)
- Mozzi "derpnote2" -- THX Deep Note recreation attempt #2 (YouTube)
- ReLiC: CircuitPython Synth Voice Library Deep Dive (YouTube) - a CircuitPython synthvoice library to simplify synthio patch creation.
- other
- Adafruit Learn: Circuit Playground Express USB MIDI Controller and Synthesizer - this includes a discussion on and demonstration of sawtooth and square waves construction from sine waves.
- Adafruit Learn: MIDI Melody Maker
- Adafruit Learn: CLUE BLE MIDI Glove
- Adafruit Learn: Cyber Cat MIDI Keyboard
- Adafruit Learn: Circuit Playground Express: Piano in the Key of Lime.
Examples of creating synthesizer sounds, many similar, some with details on how to create the patches:
- Doctor Mix (YouTube)
- Blade Runner Synth: The Vangelis BRASS SOUND Explained
- Vangelis "Blade Runner - End Titles" Recreated
- I Feel Love: Full Reconstruction Pt. 2/2 - SYNTHESIZERS
- Darude "Sandstorm" Deconstructed
- Orchid's Void: Blade Runner Inspired Ambient Jam | Korg Volca Keys (YouTube) - there's some limited descriptions of the settings in the comments.
- Microflat: Blade Runner (1982) | Vangelis on Hydrasynth | Soundtrack | A004 Blade Titles PS Patch (YouTube) - this synthesizer comes with patch A004 called "Blade Titles" which is played here.
Synthesizers, boards and kits designed for experimentations and learning:
- littleBits Korg Synth Kit - lots of fun, musical with some effort, sadly no longer sold but available second-hand.
- Synthux Academy - easy building of custom synthesizers using a prototyping system.
- Axoloti audio board for software modular synthesis - might no longer be sold.
- PJRC Teensy microcontroller boards - commonly used for audio projects.
- Bela microcontroller boards - designed for audio use.
- Electrosmith Daisy boards - designed for audio use.
- Soulsby miniATMEGATRON - an Arduino UNO synth similar to the ATMEGATRON pushing the limits of its limited processing power.
- Microrack on Kickstarter - an interesting looking breadboard based modular-style synthesizer product - all campaigns like this carry some degree of risk.
Further reading:
- Adafruit Industries: Beeps and Boops with synthio: A CircuitPython Day Panel Discussion hosted by Paul Cutler (YouTube)
- Simon Crab's 120 Years of Electronic Music
- Sonicstate: Top 20 Greatest Synths and Top 20 Greatest Synths (YouTube)
- The Synthesizer Academy (online guides).
- The Curious Cases of Rutherford & Fry: An Instrumental Case: Why do instruments sound different? (podcast, click on yellow Download button, main programme up to 00:21:30).
- Gizmodo: A Beginner's Guide to the Synth.
- Collin's Lab: MIDI (YouTube video with transcript).
- Coursera: Audio Signal Processing for Music Applications
- Books
- Bjooks - awesome, detailed hardback books on synthesizers with great photography.
- Analog Days by Trevor Pinch
- Vintage Synthesizers: Pioneering Designers, Groundbreaking Instruments, Collecting Tips, Mutants of Technology by Mark Vail
- Bits and Pieces: A History of Chiptunes by Kenneth B. McAlpine
- Music: a Mathematical Offering by David J. Benson - a book available as a free pdf.
- Steve W. Smith: The Scientist and Engineer's Guide to Digital Signal Processing - an in-depth book available for free online.
- Moog Foundation:
- Synthesis Education playlist (YouTube)
- Other interesting synthesizer-related YouTube channels
- Moritz Klein
- Hainbach
- Alex Ball
- BoBeats
- Andertons Synths, Keys and Tech
- Sweetwater
- Bygonebytes: Maplin 5600 Synthesiser - recreating the Maplin kit synthesizer based on Trevor Marshall's designs published in early 1970s in Electronics Today International (ETI) magazine.
- Using PWM to produce an analogue audio signal
- ForceTronics: Converting an Arduino PWM Output to a DAC Output (YouTube)
- Gadget Reboot: Teensy Audio without the Shield - Using ADC, DAC, DUAL PWM (YouTube) - later parts show a more elaborate combined dual PWM audio output using an 88kHz carrier.
- YouTube smalin - interesting animated visualisations of music, a recent one was borrowed for the background for the image above.
- Black Corporation: Deckard's Dream MK2 - follow-up to Deckard's Dream synthesizer module.