Hey everyone!
I asked for some help several months ago and now I'm ready to show what I've been working on. As said in the title it's an all in one sequencer / synth.
More info below the video, please wear headphones or use a half decent sound system
If you're curious it involves:
- An old Teensy 3.1 I've had lying around for years
- An old PicoADK (small batch of overclocked RP2040 boards with a lovely DAC and abysmal documentation)
- A 128x64 yellow OLED, 8 encoders and 5 tactile switches.
DISCLAIMER: I did get AI help. I'm not a big fan of genAI but my area of expertise is woodwork and this is my first real foray into C++ (more like C with classes) so I would've probably given up without it. I've got a month of Claude Code to refactor my initial UI spaghetti code. "We" discussed the general architecture and laid the foundations. I've learnt loads in the process and I'm now pretty independent. In fact the whole DSP side is completely AI-less. I'm not trying to brag (too much), mostly I don't want anyone to think this is just the result of an AI prompt. It's about 4 months of swearing, sweat and tears!
The Teensy handles the front panel inputs, the UI and the sequencing (including chord generation). Pretty much all of its pins are in use as I'm not multiplexing the encoders nor their switches (which I will regret not doing when I want to add pads to this thing). Out of 5, 4 of the tactile switches are multiplexed using a voltage divider (I only had analog pins left!) and the "shift" button has its own digital pin. The UI renders at 20fps but will throttle itself if anything else needs CPU time. It's never had to though, I'm amazed at how much the 3.1 can handle and it is a pleasure to program. Communication between classes is mostly based on an event queue system except the transport (timing) which uses callbacks for a tight clock. Every loop calls an update() method for all the components in which they check if an event that matters to them has been emitted (front panel input, cache updates, midi messages...). Apart from the odd temporary variable, very little is created at runtime to keep RAM usage as deterministic as possible. No interrupts either to keep control on when things happen. The 8 patterns can be edited in real time as they're playing. I'd like to add a proper pattern bank and oscillator presets but RAM is getting pretty scarce and there are a lot of parameters (48 per track accross all modes) and notes (64 per pattern per track) to store. I'm booting at 72% usage.
The PicoADK handles everything DSP at 44KHz. I would've loved to use a Teensy 4 instead but I had the ADK already. All 8 tracks oscillators, filter, envelopes and delay are generated onboard, the USB cables you see poking out are just for programming. Each track is monophonic (one note) and mono (one channel, but I'm hoping to add panning to the mixer in the future). The ADK handles way more than it should so polyphony is not an option but I do manage to sound chords using a trick inspired by an old Casio patent. In the patent they use half a cycle to generate a base waveform and the other half features a simulated resonant filter. The filter here is real but the samples of the 4 voices of the chord are interleaved in the same way. The illusion is suprisingly good but I do get a fair bit of artefact in the frequencies that overlap with my tenitis. I filter them with a low pass but the younger ones amongst you might still hear some
The oscillators mix 4 wavetables using an X/Y coordinate system. I will add more exotic waveforms but for now the usual suspects are all here, including noise.
Communication between the two MCUs is via UART MIDI. I can save patterns and patches via Sysex to a computer using a little cli tool. Thinking of using the Pico as non volatile storage instead so the unit is fully standalone.
The Teensy code (UI and sequencer)
The PicoADK code (audio)
The CLI backup tool
Thanks for checking this out, looking forward to get some feedback. As you can tell I'm proud like a daddy but will do my best to handle (constructive) criticism
All the best!
I asked for some help several months ago and now I'm ready to show what I've been working on. As said in the title it's an all in one sequencer / synth.
More info below the video, please wear headphones or use a half decent sound system
If you're curious it involves:
- An old Teensy 3.1 I've had lying around for years
- An old PicoADK (small batch of overclocked RP2040 boards with a lovely DAC and abysmal documentation)
- A 128x64 yellow OLED, 8 encoders and 5 tactile switches.
DISCLAIMER: I did get AI help. I'm not a big fan of genAI but my area of expertise is woodwork and this is my first real foray into C++ (more like C with classes) so I would've probably given up without it. I've got a month of Claude Code to refactor my initial UI spaghetti code. "We" discussed the general architecture and laid the foundations. I've learnt loads in the process and I'm now pretty independent. In fact the whole DSP side is completely AI-less. I'm not trying to brag (too much), mostly I don't want anyone to think this is just the result of an AI prompt. It's about 4 months of swearing, sweat and tears!
The Teensy handles the front panel inputs, the UI and the sequencing (including chord generation). Pretty much all of its pins are in use as I'm not multiplexing the encoders nor their switches (which I will regret not doing when I want to add pads to this thing). Out of 5, 4 of the tactile switches are multiplexed using a voltage divider (I only had analog pins left!) and the "shift" button has its own digital pin. The UI renders at 20fps but will throttle itself if anything else needs CPU time. It's never had to though, I'm amazed at how much the 3.1 can handle and it is a pleasure to program. Communication between classes is mostly based on an event queue system except the transport (timing) which uses callbacks for a tight clock. Every loop calls an update() method for all the components in which they check if an event that matters to them has been emitted (front panel input, cache updates, midi messages...). Apart from the odd temporary variable, very little is created at runtime to keep RAM usage as deterministic as possible. No interrupts either to keep control on when things happen. The 8 patterns can be edited in real time as they're playing. I'd like to add a proper pattern bank and oscillator presets but RAM is getting pretty scarce and there are a lot of parameters (48 per track accross all modes) and notes (64 per pattern per track) to store. I'm booting at 72% usage.
The PicoADK handles everything DSP at 44KHz. I would've loved to use a Teensy 4 instead but I had the ADK already. All 8 tracks oscillators, filter, envelopes and delay are generated onboard, the USB cables you see poking out are just for programming. Each track is monophonic (one note) and mono (one channel, but I'm hoping to add panning to the mixer in the future). The ADK handles way more than it should so polyphony is not an option but I do manage to sound chords using a trick inspired by an old Casio patent. In the patent they use half a cycle to generate a base waveform and the other half features a simulated resonant filter. The filter here is real but the samples of the 4 voices of the chord are interleaved in the same way. The illusion is suprisingly good but I do get a fair bit of artefact in the frequencies that overlap with my tenitis. I filter them with a low pass but the younger ones amongst you might still hear some
Communication between the two MCUs is via UART MIDI. I can save patterns and patches via Sysex to a computer using a little cli tool. Thinking of using the Pico as non volatile storage instead so the unit is fully standalone.
The Teensy code (UI and sequencer)
The PicoADK code (audio)
The CLI backup tool
Thanks for checking this out, looking forward to get some feedback. As you can tell I'm proud like a daddy but will do my best to handle (constructive) criticism
All the best!