Pi Pico Dual Voice Voltage Controlled Wavetable Module for Eurorack Synthesizers
by baritonomarchetto in Circuits > Microcontrollers
25 Views, 0 Favorites, 0 Comments
Pi Pico Dual Voice Voltage Controlled Wavetable Module for Eurorack Synthesizers
One of my very first projects for synthesizers was a simple wavetable oscillator. It was built around an Arduino nano and used Mozzi v1 library for audio generation.
The project had one-single main goal: generate a decent, free-running wave for my in-development DIY modular synthesizer at its first stages. Given the goal and being a first try, it missed some features now obvious to me.
Recently I landed on Mozzi library documentation and got aware that it now supports Raspberry Pi Pico, a microcontroller board that I like very much. It is a very capable microcontroller board, with way higher RAM amount and CPU speed with respect to the classic arduino nano. A no brainer for a project like this!
The time had then come to rise the wavetable oscillator project back from it's grave and give it a good refresh (oh, well: it ended being a full restart, actually!).
In this instructable I will show you my brand new, dual voice, wavetable module for eurorack synthesizers. I will describe the main circuits I adopted and their working principle. I will share with you the files to have PCBs (circuit and faceplate) manufactured.
There's also a video of the module running down this article...
Let's go!
Supplies
In the following the bill of materials for main board and front board. Please notice that there's a DAC, but this Instructables PCB pictures show no DAC on the main board!
This is because my prototype was built to use straight PWM, but after some testing I was not happy with the sound quality. I ended by adopting a 16-bit DAC in the final version (more info later on). The DAC version is the one shared.
Front Board
1x TL072P generic dual op amp
2x 220 resistor
2x 1K resistor
16x 10K resistor
4x 33K resistor
4x 100K resistor
8x 100nF ceramic capacitor (non polarized)
2x BAT42 Schottky diode
2x LED, 3mm
4x PJ301M female MONO Jack Connector 3.5 mm
4x Rotary Encoder EC11 6mm shaft
28x pinheader (male)
Main Board
1x Raspberry Pi Pico microcontroller board (standard pinout)
1x PT8211 16-bit DAC
1x TL072P generic op-amp
1x 6N138 opto coupler
3x 220 resistor
1x 330 resistor
2x 1K resistor
1x 10K resistor
8x 15K resistor
3x 100nF ceramic capacitor (non polarized)
4x 1nF capacitor (non polarized, non ceramic if possible)
3x 10uF/25V electrolitic capacitors
2x 4u7/25V electrolitic capacitor
1x 1N4148 diode
1x SB120 Schottky diode
1x IDC connector 08X2
32x pinheader (female)
You are also in the need for a soldering station, solder wire, a magnifying glass, and some time to spare :)
Features
Here is a list of features of this module:
- Two independent voices with two, detunable, oscillators each
- Fully programmable
- Stream-wave wavetables (more on this later)
- V/octave control voltage standard (input range 0 - 12V)
- MIDI-ready hardware
- Small footprint (only 10 HP for two, dual-oscillator voices!)
- Open source firmware
Hardware Design
The wavetable oscillator module is made of three PCBs:
- Front panel (aluminum)
- Front board
- Main board
The module has been layed down in a stacked-PCBs design. I generally prefer to adopt flag-mount design, but I like how these stacked-boards modules turns out, even if they call for more design work on my side.
The front panel, or face plate, is the white board you see when looking to the assembled module's front. It's made of alluminum alloy, not FR4.
The front board hosts female jacks, rotary encoders and LEDS. It puts them in electrical contact with the main board. It also hosts the control voltage input circuit and the rotary encoders slave circuitry.
The main board hosts active and passive circuits and components. MIDI IN and OUT circuits, the microcontroller board, the power input filters and connections are all hosted on the main board.
In the following a description of the components and circuits used for this project.
Circuits
Microcontroller board
The use of a Raspberry Pi Pico microcontroller board brings a series of advantages over an arduino nano.
First of all, we have a default audio output with 11 bits of resolution instead of 7. This means that we can have a well defined wave even whitout the need to adopt dual PWM.
Second, the increased CPU speed makes it possible to output more than one waves with ease, e.g. one on the right audio channel, the other on the left one.
Third, "more RAM" means "more wavetables in memory".
Fourth, the higher ADC resolution extends analog readings to the 0 - 4095 range. This augmented resolution makes it possible to, poorly speaking, "concentrate" a higher amount of notes withing it's 0-3V range of analog input, without overlapping them.
Pi Pico also has some disadvantages with respect to an evergreen Arduino nano.
One is the limited number of analog inputs. I have obviated to this by keeping analog inputs only for control voltages and adopting rotary encoders for direct user interations.
Another limit is the more complex coding language (circuit phyton is not for me, yet. Shame on me.). Luckily, as far as we keep things "simple", we can use Arduino IDE as alternative.
Rotary Encoders
Rotaries are user main devices for communication with the microcontroller board of the module. There are four of them, each with it's own filter circuit to clean the signal and make the microcontroller work easier. Filter design has been taken from Bourns rotary encoders datasheet. Please notice that I have not used such exact encoders, but generic (and vastly available) encodes from a Chinese supplier (see "Supplies" Step for details).
Rotaries have a push-button on the z axis, a common (and very welcome when dealing with small spaces) feature this module takes advantage from.
Output Stage
Outputs of each voice are low-pass filtered to limit digital artifacts at higher frequencies (noises and whines).
The filter adopted is not the "classical" RC filter, but a Sallen-Key low-pass filter. This is a simple-but-effective, second-order active filter I had already adopted for a project of mine in the past.
The filter is amplified with a X2 gain in order to reach the 6.6Vpp ballpark. I know, I know, it's not a line level voltage, but my DIY synth is built around 3340 VCOs modules with hot outputs (approx 8Vpp). The hardware can be modified at assemblying phase for 1X gain.
An AC coupling capacitor and a current limiting series resistor complete che output stage for each voice.
Control Voltage
Each voice has its own indipendent control voltage for pitch in the V/octave format (0-10V range).
The RP2040 microcontroller GPIOs work at 3.3V level and are not 5V tolerant. The solution I have adopted here is to "compress" incoming signals in the 0/+3.3V range by an op-amp in differential voltage confiuration (see >>this<< reference if you want to go deeper).
CV inputs are Schottky diode protected against possible damages due to the application of negative voltages.
I designed the circuit with the help of Falstad's CircuitJS. >>HERE<< is a link to the circuit simulation, if you want to toy with it ;)
MIDI IN and OUT
MIDI IN and MIDI OUT circuits are also present. These are well tested circuits I have already used in many of my previous projects (see >>this one<< in example).
The MIDI IN circuit is an opto-isolated circuit respecting the MIDI association specifications. It makes use of a single channel Darlinghton optocoupler (6N138), and some other slave components. Very basic, but functional, circuit.
Please notice that MIDI is not implemented in the current sketch. I wanted the circuit to be there for future upgrades, but the actual project makes no use of it.
Main Board Pinout
This project comes with a functional sketch to have it working as a free running, dual voice, wavetable module. Even if developed with this final application in mind, user can modify the code and turn it in something different.
Just to cite a possible upgrade, we could take advantage from the whole lot of digital modulation sources Mozzi library puts in our hands and output enveloped voices instead of free running voices.
A completely different use could be a dual, multi-sectors digital envelope generator.
So, it is necessary to explain a little all the internal connections of the module such that you can turn it in whatever you need/want :)
As I wrote in the "Supplies" Step, even if my prototype board (the one pictured) use straight PWM out on default GPIOs 0 and 1, the shared version adopts a DAC for better audio. DAC is directly connected to Pi Pico for I2S communication (GPIOs 2, 3 and 4).
DAC's left and right channels go trough the Sallen-Key low pass filter, decoupling circuit and final series limiting resistor.
Pi Pico has only three analog inputs. Two of them (GP27 and GP28, internal ADC1 and ADC2 respectively) are used for CV inputs. These two receive the "compressed" voltage coming from the differential voltage amplifier.
The four rotary encoders need two GPIOs each to work. These go through a filter circuit (one for each GPIO), then to pins GP9, GP10, GP11, GP12, GP13, GP20, GP21 AND GP22 of the Raspberry Pi Pico.
Each rotary has a Z-axis momentary push button. These have direct connections to the Pico (GP14, GP15, GP18, GP19).
Six GPIOs are left unused (GP0, GP1, GP5, GP6 and the analog GP26). They are anyway made available trough dedicated headers for future use.
Take a look at the attached table for a detailed module pinout.
The Code
The code generates two indipendent, free-running voices with two wavetable oscillators each.
This very special (and complex) part of the code is made simple by the use of Mozzy library, a very nice sound synthesis library for Arduino.
Mozzy is a vast library and I am still skratching the top of it's ice. If you are willing to improve this sketch, my suggestion is to give a good reading at Mozzi documentation since there are very interesting features a sound generation project like this can take advantage from.
The most "exciting" (at least for me) part of the code I wrote regards how wavetables are handled. Commonly projects load one or more wavetables from those available and reproduce them. In my sketch wavetables are instead stored as a single looong wavetable which is actually a series of smaller wavetables in the same array. I called this a "streamwave", but I could have called it a "transwave" (this is for the older of you, I suppose :) ).
Loading a streamwave makes it possible not only to freely cycle between wavetables constituting the stream, but also reproduce a mix of adiacent wavetables. Being the wave array fixed lenght, by warying the starting point of the streamwave you end up with mixed (and unpredictably sounding) wavetables. Cool :)
Wave samples (or wavetables) are, in general, characterized by two main factors: sample resolution and bit-depth. In very simple (and inaccurate) words, sample resolution is the number of points (cells) for a complete cycle of a waveform (AKA "frame") and is here represented by the wavetable array lenght. Bit-depth is the "amplitude" of the single wave point. Typical values for Mozzi wavetables are 512 ~ 2048 cells and 8 bit (one byte) of depth (range -127 to 127).
After various testings, I have here adopted a sample resolution of 4096. I kept the resolution at 8-bit given that Mozzi library shift the wavetable amplitude to that value anyway, for compatibility reasons.
Fourteen wavetables are available in the moment I am writing, and defined in file Stream_DATA_4096_8bit. Included wavetables come from some single cycle wav file I found on the internet. I am not sure about how they were recorded, but had evocative names so I could not resist :)
More wavetables to come since there's still a lot of free space available! To help you increase the number by youself in the meantime, I wrote a brief "How To": take a look at "Wavetables From Audio File" Step!
Pitches are set as a function of incoming control voltages in the V/oct format at the dedicated inputs (input range 0 to 10V). This part of the code saw a complete rewrite after the first try, because direct convertion of incoming signals gave me a +/-20 cents of detuning: unacceptable! I obtained way (WAY) better results with a tabulated approach: frequencies withing one cent from the ideal!
Rotary encoders give user control over voice octave, fine pitch, absolute gain, wavetable ("straight" and "mixed") and oscillators relative detune.
Each rotary has a Z-button. These are used for voice selection and to toggle between rotary functions. More on this in the "How To Use" Step.
Software dependencies:
- Earle Phil Hower's Arduino Pi Pico core
- Mozzi Team's Mozzi library V2
- (Forty Seven Effects MIDI library)
All these libraries can be directly downloaded from arduino's IDE.
Please notice that MIDI is not implemented in the current sketch. MIDI circuits are there for future upgrades, but the current project makes no use of MIDI.
How to Use
Ok, we are now in front of our dual wavetable module, firmly installed in our eurorack case and ready to rock!
The module starts with both LEDs on. This means that the two voices are linked and any knob turning will affect both.
The first encored (upper left) controls main octave (function one). Press the Z-button and now the encoder affect fine pitch (function two).
The second encoder (upper right) controls the relative detune between oscillators. Press Z-button to set detune to zero.
The third encoder (lower left) controls digital gain. The z-button has the very important task to cycle between voices: voice #1, voice #2 or both at the same time. LEDs indicates which voice is active.
The fourth encoder cycles among available wavetables. By default, you will cicle between "pure" waveforms. If you press the Z-button once, you will now cycle between mixed waveforms. Press it again to reset waveforms and go back to pure cycling.
Easy no? :)
Take a look at the embedded video for a brief test. Dry signal, no effects!
Wavetables From Audio Files
After toying with basic "analog" wavetables you will likely want to explore some new wavetable or create your own. It is full of wav files one can take as a base for wavetables out there, so having a way to get raw data from them and convert into something Mozzi can read is definitely a valuable addition to this Instructables.
In the following I will share with you some of my findings about the possibility to convert audio files into wavetables. Nothing too fancy or technical, maybe not the best way to do it: just a simple, working method for the task.
I was interested in single cycle waves ("frame" in the following) to loop, but the method works also for one-shot samples.
To start, we are in the need for the evergreen Audacity software. We are obviously also in the need for a wav file to convert.
Open the file in audacity and select a single cycle of the waveform of your interest (or the single sound you want to sample). You can convert the file to mono or keep it stereo: in the end we will end up with a single wavetable anyway.
By zooming enought, you will notice that you can actually see every single point the wave is made of. We are now going to export those values in a txt file.
Please notice that the number of points is a function of the rate at which the file has been recorded. If the file is oversampled, with audacity you can downsample it to your needs or keep the file at it's resolution and manipulate it later on.
Go to "Tools" -> "Export data" and set the following relevant parameters:
- Limit output to first 4096 samples (the number being a function of the frame dimension)
- Measurement scale Linear
- Index Sample Count
- Channel layout for Stereo: L Channel First
Then click "Apply".
Your text file containing wavetable/sample data is now available!
Wave amplitude data are not in the right format for Mozzi. Using 8-bit depth tables we have now to convert those raw amplitude data to the 127 to -127 range. This is a simple proportion calculation one can use softwares like win Excel or LibreOffice Calc to perform.
Module Improvements After PWM Prototype
When I started working on my very first wavetable oscillator module, my only need was a "MIDI controlled something" oscillating at audible rates. No matter how it sounded like (and it ended being good enought for me at the time), the project made it's job.
Now that my ears are a little bit more trained and demanding, in total honesty I don't like how pure PWM audio sound. It's not Mozzi's fault (that's actually an enabling software for this project, so I must give it due credits) but likely PWM technology vs my ears.
I made variuos tests, from adopting wavetables with higher frame dimension and increased bit-depth, to the use of custom wavetables: nothing was satisfactory enough (note to self: on PWM, resolutions higher than 2048 makes no difference to my ears).
Anyway, I don't give up easily. I really wanted to enjoy my new module and started digging for a different approach.
Mozzi is a currently developed library, with a set of impressive features already there. Using it's PWM capabilities is only the top of it's surface and the fastest way to give a kick to a musical project, but other approaches are possible. The one I ended using to improve audio quality is the adoption of external DAC. There are plenty of DACs out there, but the one mostly documented with Mozzi is PT8211. This is a 16 bits, I2S, dual channel DAC perfectly working with Mozzi.
It took little time to have a sketch ready (many thanks to Tom Combriat for his help) and then install it on my current hardware for a fly test. The module sounded definitely better after that! Way less interference and noise, especially on mixed wavetables, less interference between the two channels (even if not null on low sample resolution waves), and a sensible difference in quality by using higher resolution tables.
No brainer: I implemented it permanently on Gerber! In particular, I drawn a new main board, the face plate and front board being left unchanged.
Unfortunately I have not the new hardware manufactured, so pictures in this Instructables show the PWM version. The new version with DAC is the one shared on my Github repository (see next Step for the link). It's untested (again, I don't have it on my hands yet, only tested with a DAC soldered on my prototype through flying wires) so use it at your own risk.
Acknowledgments
Many thanks to the nice girls and guys at JLCPCB for sponsoring the manufacturing of PCBs for this module. Without their contribution this project would have never seen the light.
JLCPCB is a high-tech manufacturer specialized in the production of high-reliable and cost-effective PCBs. They offer a flexible PCB assembly service with a huge library of more than 350.000 components in stock.
3D printing is part of their portfolio of services so one could create a full finished product, all in one place!
By registering at JLCPCB site via THIS LINK (affiliated link) you will receive a series of coupons for your orders. Registering costs nothing, so it could be the right opportunity to give their service a due try ;)
All Gerber files and sketches I realized for this project are stored >>HERE<< (Github). The repository hosts my first wavetable oscillator (VERSION_1_OLD) and the new version (VERSION_2).
My projects are free and for everybody. You are anyway welcome if you want to donate some change to help me cover components costs and push the development of new projects (I have a new one on a third wavetable oscillator module that could be interesting for some of you... :) )
>>HERE<< is my paypal donation page, just in case ;)