Do you find yourself struggling to tighten the nuts on those 3.5mm Thonk Jacks? Do they break your nails and hurt your fingers, forcing you to resort to pliers that inevitably slip off and scrape your beautiful PCB front panels? Do you have a 3D printer?
Take 14 minutes and print this file out. It fits snugly around the nut and allows you to screw it in with ease. You can tighten using your fingers, or use the hole in the top with that 4mm hex adapter in your screwdriver set. Enjoy, and I hope it saves you as much grief as it did me!
I’ve been meaning to build a noise generator for a while now—especially to make hi-hats, claps, and other percussive sounds. So when my noise2 chip arrived from electricdruid.com, I was eager to dig in. As I looked through the datasheet schematic, I was really surprised by the simplicity of the module design.
The chip has outputs for pink and white noise, so short of powering things, you just need to route the pink and white noise outputs through voltage followers for protection, maybe add a low pass filter on the white noise output, and that’s basically it!
Have you ever wondered about the difference between pink and white noise? All noise is made up of a combination of sine waves of different frequencies. Those waves are added together to create sounds of different timbres. White noise contains sine waves of every frequency all at equal volume. Now, because the frequency of a pitch increases exponentially for every octave, there are more frequencies in higher octaves than in lower ones. For this reason, white noise—to our human ears—sounds high pitched. Pink noise however, equalizes the volume of the frequencies so that each octave has the same total volume.
Adding a Filter
Seeing as there wasn’t much to the noise circuit, I decided to integrate the noise2 chip into the VC Tone Filter experiment that was occupying my breadboard at the time. This experiment uses a TDA1524 tone chip (designed for Treble / Bass / Volume adjustments in cheap stereos), to create something like a VCF/VCA combo.
After adding the noise chip, I experimented to see what kinds of sounds it could generate. I added an envelope generator on loop and, with a little fiddling, got everything from ocean waves to howling winds, to hi-hats, cymbals, claps, footsteps, and base drums. Here is a sample of some of the sounds. It’s about 5 minutes of tweaking settings—feel free to skip around.
It was clear to me that this thing needed the full module treatment.
Module Design
After a bit of consideration, I came up with a list of features to include. Most of these are pretty standard for any module:
Output buffering / protection
CV inputs that you can attenuate
Selectable voltages mixed with CV inputs (so you can override)
Adjustable Resonance / Feedback
Selectable pink noise / white noise / no noise
A buffered input signal (which really just allows you to use the module as a combo VCA/VCF instead of the noise)
Eurorack power connector
The Filter
The filter circuit stayed largely the same as the version in my previous experiment. I used the TDA1524 as the core of the filter, and because the chip supports two channels (left and right) I fed the output of the right channel into the input of the left channel. This doubled the effect of the filters.
While the components are largely the same, I did tweak a couple of values—lowering the capacitance of C4 and C8, and raising the capacitance of C10 and C13. Because the same signal flows through both the left and right channels, the balance control acts as a choke. When balance is turned all the way left, the signal gets amplified before being attenuated on the right side (and visa versa). The signal is only loudest when it is centered. So, by having different capacitors on the left and right side, you get an imbalance between the channels, and this gives you very different sounds as you adjust the balance control.
Inputs & Feedback Loop
The feedback loop mixes together the input signal, the white/pink noise, and the filter’s output using an inverting buffer (with unity gain) to create the filter’s input signal. The feedback potentiometer adjusts the attenuation of the output signal to determine the amount of resonance. Note, for the noise switch, you will want to use one of those three-position “on-off-on” switches so you can turn off the noise.
CV Input Mixers
All of the CV inputs (except balance) follow a standard active mixing design:
The CV input attenuates through a potentiometer before being mixed together with a voltage divider. The two inverting amplifiers (unity gain) ensure that the signals mix without cross-talk and the Zener diode (D4) ensures that the CV gets clipped before it can damage the TDA1524 chip.
Full Schematic
Here is the full schematic that also includes the power filtering components. Nothing super crazy there, although as a best practice, I do include low voltage-drop rectifier diodes on the power input to prevent frying the module by plugging it in backwards.
PCB Layout
After a bunch of fiddling in Kicad, I came up with a rather densely packed single-board PCB layout. The 12u width (60.6mm) feels quite usable and not too cramped. Of course, if creating the most tightly packed Eurorack device is your thing, I bet you could make it smaller by stacking two boards. Personally, I only try to do that when the savings is closer to 40% or so. Here, given the number of physical controls, and the fact you do want SOME space between them, I think you’d probably only save maybe 20% on the board width. Also, hey this is a White/Pink Noise Generator, VCF, and VCA all in one, so 12 units seemed like a pretty good value to me.
One more note, I added M3 holes at the top of the module for front-panel stand-offs because the pots I’m using are super cheap ones without panel mounts. Without better pots, you need something at the top to fasten on the front panel. Also, keep an eye on those caps sitting between the potentiometers. If they get too tall, they might run into the front-panel. This isn’t too big of a deal because there is plenty of room to bend them down against the board if you have to.
Module Demo Time
The module is extremely versatile and can be used to create lots of effects. To demonstrate as much as possible, I broke the video into two parts. The first part uses the module purely to alter an input signal with the noise turned off. In essence, it acts as a filter—even resonating at one point. In the second video, I turned the noise source on and showed how it can be shaped by the filter and even mixed with the input signal.
As always, if you have any ideas on how to push this module further, please leave a comment! The device is a blast to play with and I will keep trying to improve it. Maybe starting with a proper faceplate.
When starting out in the DIY synth space, one of the first projects people typically tackle after creating their first oscillator is an analog sequencer—like the “baby-8.” It’s a simple circuit that steps through a sequence of adjustable voltages. And if you use those voltages to control an oscillator you’ll get a little tune that repeats over and over.
But what if we turned the clock speed up? Like… WAY up? And, instead of using the output as a control voltage, what if we used those adjustable voltages to directly form a sound wave? Well, that’s what this experiment is about—creating, controlling and visualizing new waveforms.
Grab a bread board and follow along—or just read on—either way, let’s dive down the rabbit hole to figure out how this all works.
The CD4017 Decade Counter
At the heart of the Waveform Generator is a CD4017 Decade Counter:
Simply put, this chip turns on one of the Q[0-9] outputs at a time, rotating through them with each tick of the clock input pin. In the “on” state, the Q pin will output VCC which can be anything from 3v to 18v.
There are also a couple of control functions to the chip:
Reset will flip back to Q0 when it goes high
Enable turns the chip’s output on and off. I tied this to ground so the chip stays on.
Carry is an output pin that goes high whenever the CD4017 ticks from Q9 back to Q0. If you were chaining multiple counter chips together, you might use this pin as the next chip’s clock to get a second counting digit. I’m not doing that—but this pin will still be important later…
Creating a Sound Wave
When the clock starts ticking, the CD4017 cycles through pins Q0 through Q9 turning each one on in sequence. And the 5 volts I’m using to power the chip (VCC) gets outputted onto the Q pin that’s turned on.
Now, sound waves are just changes in voltage over time. So if each Q pin generated a different voltage and I combined those outputs together, then as the clock ticks through the different Q pins, I would get a sound wave that looks something like this:
In order to get different voltages for each step in the sequence, I added 10k pots connected from the Q pin to ground. The center pin acts as a voltage divider allowing you to select a value between ground and 5v:
I combined the 10 voltage dividers together through diodes to ensure that none of the signal routes back into the CD4017 chip. Here is the schematic:
It may look a little complicated, but it’s really just a repeating pattern.
As for the other pins on the CD4017, I tied both the Enable pin (13) and Reset pin (15) to ground. I connected the clock pin to pin D6 on an Arduino Nano, and the Carry-out pin to…
Wait… An Arduino Nano Clock?
Converting a control voltage into a frequency is probably one of the most temperamental parts of any analog synthesizer. Synth designers use some highly temperature-dependent analog circuitry tricks to accommodate the exponential relationship of a 1v/octave control voltage and the Oscillator’s Frequency.
For this circuit, I needed a way to drive the CD4017 clock input using a musically accurate square wave that goes 10x as fast as the desired. The Arduino Nano just so happens to have the ability to generate a very fast, very accurate clock signal using the tone() function. You can set it from 32 Hz all of the way up to 65,535 Hz. Even if you divide that by 10—for each of the 10 steps in the waveform—6,535 Hz is higher than the highest note on the piano (4,186 Hz). Moreover, the Arduino Nano has analog inputs that can read a control voltage and do the exponential conversion in code.
Now I know what you are thinking… isn’t using an Arduino cheating? Won’t it contaminate the purity of all of our analog goodness? Well, the clock signal needs to be a digital signal anyway. And, with the exacting frequency control, temperature stability, the ability to use both CV and MIDI input… it seemed like a good trade-off. Lastly, since the timer is hardware-driven, that leaves most of the processing power on the Arduino for… other things…
Finnessing the Signal
Let’s take another look at the output of our circuit so far:
Notice how the voltage levels appear to be disconnected line segments? When the CD4017 flips from one Q output to the next, the voltage will instantly change. A waveform like this is going to be really grainy and harsh, with lots of overtones—like a square wave. This might be what you want, but if it isn’t, you’ll need a way to round those corners off so the wave looks a bit more like this:
Rounding waves with filters
To round off the signal, I used a simple low-pass filter:
First, I buffered the signal coming from the CD4017 to remove any interaction between the RC Filter and the voltage dividers. This ensured that those square edges remained in the output signal when I turned the filter totally off.
After buffering the signal, I used three low-pass Resistor-Capacitor (RC) filters in series. They each apply a small amount of filtering to round off the output signal without attenuating it too much. Still, they do attenuate it a bit, so after filtering the signal, I added an amplification stage.
Amplifying the signal
The amplification stage has two parts. A level potentiometer allows you to attenuate the signal coming from the filter between 0% and 100%. That signal is then amplified 3x by the inverting amplifier. This means that the level pot is really amplifying the signal between 0% and 300%.
Note: While this filter & amplification stage will get the job done, it is still pretty primitive and has one significant disadvantage: the filter cutoff doesn’t change as the oscillator’s pitch increases. That’s going to make the sound more hollow as you move up the scale (until you manually adjust the filter accordingly). To fix this, you’d need a much more complicated CV-controlled filter that can adjust the cut-off using a 1v/oct scale. But for now, let’s keep this simple. It works pretty well for an octave or two anyway. Maybe a future article will address this…
Blocking the DC Offset
The audio signal is almost ready to use, but I had to do one more thing first. Right now, every segment of the waveform sits between 0v and 5v. This means that the center-line of the wave is effectively 2.5v. Audio waves need to center around ground (0v), and the difference between these center lines is a “DC offset.” You really don’t want a DC offset. Thankfully, it’s pretty easy to get rid of this: just add a large capacitor in series before the output. The capacitor only allows changes in voltage to make it through while blocking any direct current.
A Quick Sound Demo
Now that we know how to generate the sound, let’s take a listen:
Visualizing the Sound
Ok, so we now have a waveform assembled from 10 different segments, filtered with three filters, and then amplified. I’m not sure about you, but visualizing a waveform made from fourteen pots in my head is a pretty steep ask. If only there was a way to visualize this… Wait. Don’t we have a completely underutilized Arduino just waiting for something to do? Why, yes. Yes, we do.
So… an Arduino Nano Oscilloscope?
This seems like a super cheap hack, but honestly… it works surprisingly well. After all, I only need a visual reference and not a precision piece of bench equipment. Here’s a peek at the output (with a few test indicators on top of it):
To build this, I basically took the output signal (before it goes into that DC blocking capacitor) and fed it into an analog input on the Arduino. The Arduino captures the waveform and displays it on an OLED screen. There are of course a couple of catches…
Making the signal Arduino-safe
The Arduino’s analog pins can only handle between 0v and 5v. If you go over that, you can easily destroy the analog pins—if not the entire Arduino. And, thanks to that 3x amplifier, the signal can definitely breach that threshold. So I needed a way of clipping the signal:
Let’s break this clipping circuit into a few parts:
The Op-amp (in voltage following mode) just buffers the incoming signal—it doesn’t change it at all. This ensures that the clipping part of the circuit doesn’t interact with the sound output.
The Zener diode (D12) only conducts when the incoming signal goes above 5.1v or below 0v. This ensures that the point between R5 and R7 stays within the 0v to 5.1v range. And, since R7 is connected to that point, it will never exceed those voltage boundaries either, making it safe to go into the Arduino. R7 also limits the current going into the Arduino—which would otherwise fry it.
The left side of R5 connects directly to the incoming signal, and the right side connects to the clipped signal. Whenever the signal is within the clipping bounds, the voltage on both sides will be the same. BUT if the signal voltage exceeds the clipping boundaries, then there will be a potential difference across R5. I used that to power a clipping indicator LED (D11) through a current limiting resistor (R6) when the signal exceeds 5v.
Adding CV Input
In order to add a frequency control CV input, I used the same clipping circuit, but added a second diode in reverse to indicate negative voltage clipping. On the first circuit, this wasn’t necessary since the voltage could never go negative. But here, in the wilds of modular, anything is possible.
Hooking up the Arduino
Now let’s take a look at how to hook up the Arduino Nano:
The clock output that goes into the CD4017 connects to pin D6. Though this could be any digital pin.
The clipped waveform signal that we want to read into our “oscilloscope” connects to pin A3.
The CV input signal connects to pin A2.
The 128×32 pixel OLED screen—which uses an I2C bus—connects to A4 (SDA) and A5 (SCL). These pins are fixed on the Arduino Nano.
Remember when I said the Carry pin on the CD4017 would be important? Well, that connects to D5 and triggers the Arduino to refresh the Oscilloscope reading right at the beginning of a waveform.
Lastly, I added MIDI input through an optocoupler (6n138). A switch selects between programming the Arduino and using MIDI input because they both use the RX pin. I typically use 1/8″ stereo jacks for my MIDI connectors (vs. DIN) because they are smaller and cheaper. I follow the Korg pinout because that’s the adapter I happen to have.
Full Schematic
That’s really all there is to the hardware piece of this project. Here is the full schematic. Let’s move onto the software
On to the Software!
One thing I love about the Arduino platform is that there are tons of libraries out there that do all of the heavy lifting. In this case, Adafruit’s GFX library allows us to write to the screen, and the MIDI library manages incoming MIDI messages.
With the hard stuff out of the way, my code primarily focuses on two key things:
Combining the input CV and current MIDI note into a clock frequency.
Reading the incoming audio signal, and writing it to the display.
Frequency Lookup Tables
The most difficult part of this programming exercise was figuring out how to combine the current MIDI note with the control voltage input.
In my first attempt, I translated MIDI notes into their corresponding frequencies using a lookup table. This is easy and works great—until you try to combine it with the control voltage input. You can’t just convert a 1v/oct CV into a frequency and add it to the frequency of the current midi note. Let’s say we had a MIDI note of 440hz, and our CV input was 1v. A 1v CV would increase the MIDI note by one octave (adding 440 Hz). But, with a MIDI note of 880 Hz, that same 1v CV would have to add 880 Hz to raise the MIDI note by an octave. Unfortunately, the look-up table was a non-starter.
Calculating the Frequency
In order to abandon the lookup table, I needed a formula that calculates the clock frequency based on the input CV and the current MIDI note. Here is how I got there…
We know that the frequency doubles with every octave increase—e.g. the note an octave above A 440 Hz is A 880 Hz. In this way, you could think of the Frequency Control Voltage (which is 1v/oct) as being the number of times we want to double the current pitch (Base Frequency). Mathematically, you could write this as:
(Where “Base Frequency” is the frequency of the lowest note.)
In the MIDI standard, note 0 starts at 8.1758 Hz and goes through note 127 (12,543 Hz). So we can use 8.1758 as our base frequency. Since there are 12 MIDI notes (half steps) in each Octave, you can convert the current MIDI note into a “1 unit per octave” equivalent by dividing the note number by 12.
Now, since both the CV and MIDI note are on the same linear scale, we can add it to the Control Voltage in the formula above, and this gives us our new formula:
And that’s it. If use the input CV (scaled to 0v-5v) as “ControlVoltage”, and the current midi note as “MidiNote” then the result will be the combined equivalent frequency. Lastly, because there are 10 steps in our waveform, we have to multiply our base note by 10. I used 81.758.
Ok, that’s enough maths.
Coding the Oscilloscope
In order to keep the oscilloscope as responsive as possible, I render the screen in three steps.
Collect Readings The first step collects 128 readings from the analog input into an array. Keeping the data collection loop extremely simple ensures that readings are collected as fast as possible—providing most granularity.
Draw Screen Before drawing any pixels, I clear out the display. Then, for each column of pixels, I translate the analog value collected in the array (which ranges from 0 – 1024) to a row on the screen (which ranges from 0 – 32). Lastly, I write a white pixel to the corresponding row and column.
Add any debug info After rendering the wave, I found it super helpful to add a few other interesting indicators. For example, the currently selected MIDI note, the Input CV value, and of course, the current frequency being played. You can pretty easily turn these on or off to suit your needs. Last thing, don’t forget to run display.display(). This function moves everything from the hidden screen buffer we’ve been writing to onto the actual display.
One more timing consideration
Rendering the screen takes a relatively long time. And, while the clock frequency driving the wave is hardware-driven—and thus unaffected by the screen rendering—changes to the clock frequency ARE affected. This means that if you change the CV input, the corresponding frequency change won’t happen until the screen finishes rendering. This creates audible stuttering in the sound as you change the CV—almost like it is being quantized.
In order to fix this, I only render the screen once every 100 milliseconds, and if the CV Input changes more than a small amount, I wait another 100 milliseconds before updating the screen again.
The trade-off is that if you update the CV continuously the screen won’t update, but the frequency changes will be buttery smooth. As soon as the CV stops changing—even for a split second—the screen will refresh.
Enough talk…
Here’s the code
#include<Adafruit_GFX.h>#include<Adafruit_SSD1306.h>#include<MIDI.h>/**********************
* Screen Definitions *
**********************/#defineSCREEN_WIDTH128//OLED display width, in pixels#defineSCREEN_HEIGHT32//OLED display height, in pixels//My screen doesn't have a reset pin... so I just picked a random one#defineOLED_RESET4//Reset pin (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT,&Wire, OLED_RESET);/****************************
* Oscilloscope Definitions *
***************************/#defineWAVE_START_PIN5//CD4017 carry pin goes high at start of wave#defineTONE_PIN6//Output clock pin that drives the tone#defineCV_PINA2 //Analog CV input pin#defineOSC_IN_PINA3 //Analog input pin for oscilloscope measurement#defineREFRESH_DELAY100//Milliseconds between OLED screen refreshes//This is where we will track the values for oscilloscope measurement//We will collect one value for every column of the displayint vals[SCREEN_WIDTH];unsignedlong next_refresh =0;//Tracks time between screen refreshes/********************
* MIDI Definitions *
********************/MIDI_CREATE_DEFAULT_INSTANCE();
byte current_note =23;//Contains the currently played midi note. //Defaults to 23 so the CV input of 0v will start //at C (32.7 hz) even if MIDI input is not used.float last_CV =0.0;//Sotres last CV value. This is used to determnine //change in CV over time (for OLED refresh)//MIDI note on handler:voidhandleNoteOn(byte channel, byte note, byte velocity){
current_note = note;}//MIDI note off handler: (nothing to do here really)voidhandleNoteOff(byte channel, byte note, byte velocity){}/******************
* SETUP FUNCTION *
******************/voidsetup(){pinMode(WAVE_START_PIN, INPUT);//Set Wave Start pin as an input// SSD1306 Display Setupif(!display.begin(SSD1306_SWITCHCAPVCC,0x3C)){//Addr. 0x3C for 128x32for(;;);//Fault. Stop here...}
display.setTextSize(1);//Set Text Size to be really small
display.setTextColor(SSD1306_WHITE);//Make Text white
next_refresh =millis();//Set next refresh time to be ASAP//MIDI Setup
MIDI.setHandleNoteOn(handleNoteOn);//Add event handlers for Note On
MIDI.setHandleNoteOff(handleNoteOff);//Add event handlers for Note Off
MIDI.begin();//Initiate Miditone(6,880);//Set a tone to start with}/*****************
* LOOP FUNCTION *
*****************/voidloop(){//--- SET THE TONE CLOCK FREQUENCY ---////Step 1: Read CV pin and convert it to between 0 and 5//Analogread returns a value [0 to 1024], we need a value [0 to 5]//You can simply multiply the reading by (5 / 1024) = 0.0048828125float current_CV =float(analogRead(CV_PIN))*0.0048828125;//Step 2: Convert MIDI note to an equivalent CV and add to current_CV//Because there are 12 notes per octave, and one volt represents one//octave, all we need to do is divide the current MIDI note by 12.
current_CV +=float(current_note)/12;//Step 3: Convert Control Voltage into a frequency//We will assume that the zero'th MIDI note is 8.1758hz which is "C"//Because there are 10 steps per waveform, we multiply this by 10float freq =81.758*pow(2,current_CV);//BaseFreq x 2 ^ CV tone( TONE_PIN, freq );//Tone function sets CD4017 clock pin//--- UPDATE THE SCREEN ---////Step 4: Determine whether it is safe to update the OLED screen//When fast CV changes occur, it sounds like the tone quantizes.//This is because the Arduino can't update the tone value while it //is updating the OLED screen. By checking CV delta, we can see how//much it changed and then skip the screen refresh to keep things//buttery smooth.if(abs(current_CV - last_CV)>.015)
next_refresh =millis()+REFRESH_DELAY;
last_CV = current_CV;//save the current CV for the next loop//Step 5: See if it is time to refresh the screen:if((millis()> next_refresh)){//Step 6: Wait for the start of a new wave by repeatedly //checking the WAVE_START_PIN pinwhile(digitalRead(WAVE_START_PIN)== LOW ){}
next_refresh =millis()+ REFRESH_DELAY;//update refresh timer//Step 7: Collect analog values for each column of the display//We want the timing to be as fast and consistent as possible, //hence we just read and store the valuesfor( byte i=0; i<SCREEN_WIDTH; i++){
vals[i]=analogRead(OSC_IN_PIN);}//Step 8: Clear the OLED screen and plot each pixel on the screen//Since the screen is only 32 pixels tall, we have to map the analog//input value that goes 0-1024 to 0-32. Because both 32 and 1024 are//powers of 2, we can right-shift the analog input by 5 bits leaving//only the most significant 5 bits left over. Those remaining 5 bits//go from 0-32. This method is extremely fast, but if you have a //different screen height, you will need to adjust this function.
display.clearDisplay();for( byte i=0; i<SCREEN_WIDTH; i++){
display.drawPixel(i, vals[i]>>5, SSD1306_WHITE);}//Step 9: OPTIONAL - Display some values on the screen
display.setCursor(0,25);//Current MIDI note in the bottom left
display.print(current_note);
display.setCursor(0,0);//Combined CV in the top left
display.print(current_CV);
display.print(" ");//Clock freq after the CV value
display.print(float(freq)/10);
display.print("hz");//Step 10: Update the display//Since all changes were made to a hidden display buffer, we have to//tell the screen update the display based on the buffered changes
display.display();}
MIDI.read();//Update MIDI to see if any signals came through}
One day I was flipping through random electronics parts on AliExpress and came across a chip I hadn’t seen before—the TDA1524. After popping open the datasheet, it appeared to be a tone control chip with voltage-controlled inputs for Bass, Treble, Volume, and Balance. Apparently they use this chip in car stereos, karaoke equipment and other places where cheap tone control is needed. Best of all, it was a mere $1.30 for 10 pieces!
While patiently waiting the month-long shipping, I pondered whether this chip could form the basis of a voltage controlled filter / amplifier combo. Would it work? How would it sound? How long does it really take for 10 chips to pass customs?
Lucky you, you don’t have to wait.
Will it Filter? The results.
Of course it ISN’T a traditional VCF, because the cutoff frequencies for bass and treble seem to be fixed at 40hz and 16khz. That said, what makes this circuit unusual is that the chip both filters (through a 2-pole LP filter) and boosts the levels of the bass and treble depending on the control voltages. As such, when you turn up the bass, you definitely get the “Waa Waa” effect of a Low Pass filter as well as some interesting resonances and overtones.
I used a simple ramp wave as the input for this video. Towards the end I cranked up the volume to make it super hot, and that definitely gives you different sound control.
All of this creates some really interesting timbres, adding all kinds of overtones, etc. I tried to demo as many different sounds as possible—some great, some horrible, but I think there’s a lot of potential here.
Pots from left to right:
Feedback Attenuation / Resonance
Input Attenuation
Balance CV (0-5v)
Bass CV (0-5v)
Treble CV (0-5v)
Volume CV (0-5v)
Hacking the TDA1524
The chip is designed to control both left and right channels, but to enhance the effect, I fed the right channel’s output into the left channel’s input, and then use the left channel output as the signal out. This created something that sounds like a 4-pole low-pass filter, while the treble control gives you options for band-pass, high-pass, and band-stop filters as well.
The chip is designed for a much smaller input signal than a 10vpp, so I use a potentiometer to voltage divide the input.
Lastly, I mixed the left output signal with the input through a TL072 and sent it back into the right input to create a resonance feedback loop. This causes the filter to self oscillate. And while that oscillation can be made to sound really harsh, you can also get much softer and more musical sounds out of it as well. One strange effect I noticed was that the resonance seems to quantize itself to the harmonic series (you can see it around 1:23 in the video above). I’d never heard that one in a filter before.
VC Tone Filter Schematic
The schematic uses surprisingly few parts. The basic filter portion follows the datasheet, but pin 8 (right output) routes into pin 15 (left input) to double up the filter. I’m guessing you could probably remove one of those capacitors (C1 or C12). Work in progress
Because the left channel feeds into the right channel, the balance CV needs to stay around 2.5v to produce sound. Otherwise, you are effectively turning the right channel up and the left channel down, or visa-versa. Either way, you attenuate the output. Small changes around that 2.5v mark do create some interesting sound variation though, especially if you swap in different capacitor values for C4/C5/C8/C10/C11/C13. If the left/right channels aren’t balanced, then you will get tone variation as you change how the two blend together.
One last thing, the datasheet shows that the TDA1524 has an internal voltage regulator that you can use to drive your CV pots. I deliberately used an external regulator here in order to demonstrate that an external voltage can be applied instead.
Watching LMNC build the Sega Mega Drive Synthesizer inspired me to build an FM Synth of my own. I loved how all of the controls were broken out and that you could adjust the sound on the fly. I wanted to replicate that in a Eurorack-sized module, and this is how the Smooth Operator came to be.
The Smooth Operator is simply a controller for a sound module daughterboard. It breaks out all of the sound settings into 34 potentiometers and 18 switches—no menu-diving required!
For the sound processing daughterboard, I used the OPL-3 module from the MidiBox Hardware Platform. This module houses a YMF262 sound processor, YAC512 DACs, and all of the related analog circuitry. While the YMF262 is slightly different from the YMF2612 used in the Sega Mega Drive, they are both 4-operator FM Synth chips and sound very similar.
The OPL-3 module has a compact design that provides low-noise, high-quality sound output. And, because the project is open-source, the schematics and PCB layouts are freely available. You can even buy a printed board from modular addict.
Smooth Operator Features
A 128×160 pixel TFTdisplay provides a readout of all settings and a visualization of the four operator envelopes.
All four output channels output through two stereo jacks.
Midi In/Out allows you to connect your keyboard or Midi controller
Gate/Trigger outputs allow you to connect other Eurorack modules for external audio processing.
An Arduino Nano provides the brains of the operation controlling everything while keeping the device easy to program.
Before taking on this project, I first built a similar device using Yamaha’s YM3812 OPL-2 sound processor, and I highly recommend this inexpensive kit from Cheerful Electronic as a way to get started. Having only two operators to control simplifies the physical interface and programming. And, since the OPL-3 is backward-compatible with OPL-2, you can leverage most of your OPL-2 code in the OPL-3 device. In fact, I drew heavily from DhrBaksteen’s OPL-2 code library to write my OPL-3 Arduino library.
YMF262 Sound Chip Features
This FM synth chip powered many of the sound cards from the early 1990s including the SoundBlaster 16, Pro AudioSpectrum, and probably 20 others. It defined the new OPL-3 standard that replaced OPL-2— which powered the original Adlib and Sound Blaster music cards.
Operators form the basic building blocks of FM Synthesis. Each operator includes an oscillator, ADSR envelope, amplifier, and low-frequency oscillator (for vibrato/tremolo effects). The OPL-3 chip includes 36 operators and allows you to configure them in a variety of ways.
You can mix the sound from each operator directly or use one operator to modulate the frequency of another. And when a high-frequency oscillator modulates the frequency of another high-frequency oscillator, magical things start to happen. This chip allows you to combine up to 4 operators through different algorithms to form a “voice.” In 4-Op mode, you can play 6 simultaneous voices, and in 2-Op-mode you can have up to 18!
In the 4-Op, 6 voice mode as well as the 2-Op, 15 voice mode, you also get five rhythm sounds to build some sweet drum tracks.
The oscillators of the YMF262 support 8 different waveforms including a square wave, sine wave, and a bunch of other variants. These provide tons of flexibility in creating different sound timbres.
You can also mix the voices together into any of the four output channels. This provides some rudimentary surround sound functionality, but I could imagine repurposing it for a Eurorack integration that independently processes pieces of sounds and recombines them later. Sky’s the limit!
Hardware Overview
Smooth Operator hardware breaks down into 5 key sections—all controlled by an Arduino Nano. We will go through each in detail:
A grid of potentiometers & Switches and buttons that allows you to adjust the settings for each of the operators and control the menus
Controller interface for the MBHP OPL-3 sound module
A TFT display for visual feedback of the settings
A Midi In/Out Interface circuit
A circuit to output Gate and Trigger signals
Demo Time
Helpful Links
Before we go deeper, here are a few links that I found extremely helpful in my journey:
YMF262 Datasheet – This provides tons of information about the chip, how to communicate with it, and how to set everything up. Print this out, and keep it close. You will need it!
YAC512 Datasheet – This provides the information you need to build out the Digital to Analog Converter circuitry that connects to the YMF262. If you use the OPL-3 module kit above, you probably won’t need to refer to this much.
OPL3 Programming Guide – Wow was this site helpful. It goes way beyond the datasheet, and provides detailed explanations of all the YMF262 registers.
A beginner’s guide to FM Synthesis – This primer on FM synthesis explains how operators, carriers, and modulators work together to create sound.
Pots & Switches
As it turns out, the Arduino makes it pretty easy to wire up and read a potentiometer. You just put a wire from one side of the pot to ground and the other side to +5v and then the middle wire directly into an analog input. The potentiometer acts as a “voltage divider” and depending on how far you turn the dial, outputs a voltage between 0v and 5v. The Arduino reads this voltage and uses its digital-to-analog converter to turn it into a number between 0 and 1,024. Easy breezy.
Similarly, a switch alternates between two different connection states: ground (0v) or 5v. The Arduino turns this into 0 or 1,024 (all off or all on). Incidentally, this type of switch is called a “single pole, double throw.” It has one connector (pole) that can be connected to one of two other connectors (throws). If you look for these switches online, search for “SPDT” or “1P2T.”
Reading Many Analog Inputs
If you look closely at the Arduino Nano, you will see only 8 analog inputs. Our device needs 56! How are we going to read 34 pots, 18 switches and 4 buttons with only 8 analog inputs??
Enter the CD4051 Analog Multiplexer. You can think of this like a switch, but instead of 2 throws it has 8, and instead of using your finger to flip it, we use a binary value. Here is a block diagram of the chip:
At the top are all of the “Channel In/Out” pins. Each of these connects to the middle pin of a different potentiometer or a switch.
On the right is the “Common Out/In” pin. This pin connects to the Arduino.
The CD4051 allows you to select which of the channels at the top connects to the common pin on the right. And it does this using the A/B/C pins on the left. These three pins use a binary number from 0 (all pins off) to 7 (all pins on) to select one of the channel lines.
What about that INH pin? Well this handy little guy disconnects all of the channels from the common pin. Why would you want to do that? Well, right now we only have 8 channel inputs, and we need 56. That means we need a bunch of these chips as well as a way to turn them on and off.
A Giant 56-input Multiplexer
The above schematic shows just the connections between the Arduino and the 4051 multiplexers. Notice that the first three address lines are all connected together across all 7 of the 4051s. The fourth address line connects directly to the INH pin of the top four chips, but then inverts before connecting to INH on the bottom three chips using a 74HC14 hex inverter chip.
The output of each of the four 4051s on top goes to one of four analog inputs on the Arduino. And, because the top and bottom rows will never be connected at the same time, the bottom row feeds into the same analog input pins.
When we get to the software side of things, you will see that putting a 4-bit binary address on the Arduino’s D2-D5 pins results in being able to read 4 analog values on pins A4-A7.
One thing to note here… In case you were thinking about choosing pins A0-A3 on the Arduino, it turns out that pins A6 and A7 can ONLY be used as analog inputs. Since that is exactly what we need in this case, I used pins A4-A7 to ensure the more flexible pins are available for other purposes.
Connecting Pots & Switches
Now, see all of those connectors marked X0-X7 on each of the 4051 chips? Those are the inputs that will connect to our pots and switches.
Connecting the pots is pretty self explanatory. One side ties to ground, the other to +5v and the middle goes to one of the inputs on the 4051. Above is an example showing one Operator’s worth of pots. I used 10k pots because they seem to allow enough current to drive the A/D converter in the Arduino. I have had mixed success with 100k pots.
Switches are just as easy as the pots. One side goes to ground, the other to +5v and it works!
Experiment gone wrong (a brief aside) On my first attempt, I thought it would be great to power the pots and switches with a nice clean +5v power rail to ensure that the Arduino’s A/D readings would be more accurate. So I created a separate +5v power rail and tied all of the pots and switches to it. I still powered the 4051s (and all of the other chips) off of the Arduino’s +5v. Theoretically, that should be awesome. But in practice, the Arduino’s +5v sometimes dropped below the other (less loaded) power rail, and when the pot’s output exceeded the 4051 chip’s power, it got hot and died. After replacing all seven of the surface mounted 4051s, I combined the two +5v rails into one and everything worked great. LESSON: don’t get fancy, just use the Arduino’s 5v rail for everything.
Debouncing Buttons
You’d think buttons would work the same way as switches, but of course, they don’t. In practice they tend to “bounce” between on and off as you press them. This makes navigating menus impossible, because every button press registers as multiple different presses. This sends you frustratingly passed the option you were looking for. But never fear, WE HAVE THE TECHNOLOGY, and we can fix this with a bit of “debounce” circuitry.
The bouncing comes from a brief moment where the button just starts to make contact. During this moment (also known as an eternity to a micro controller operating at millions of cycles per second) the button goes back and forth between making contact and not making contact. We need to filter out that noise during the transition period and only detect the fully-on and fully-off states.
As far as debouncing circuits go, I made this one fairly complex, but also very reliable. If all of this looks like magic, let’s re-draw the circuit and break it down into its individual parts:
Pull-up Resistor:
The first part of the circuit ensures the button signal is either “on” (5v) or “off” (GND). Before you press the button, the output connects to 5v through a “pull-up” resistor. When you press the button, the output shorts to ground. This ensures a reliable output voltage of either 5v or 0v and no state in which the output “floats”—creating unpredictable voltages.
Even though this first part of the circuit ensures the output is either all on or all off, the output signal will flutter back and forth until it stabilizes in the on or off position. To fix that, we need a way to filter out this high frequency noise. We need a “Low-pass RC Filter.”
Low-pass RC Filter:
An RC Filter contains a resistor and a capacitor—hence “RC.” In our case, we use a 10k resistor and .1uF capacitor. With these values, the filter will smooth out any bouncing noise over 160 hz. The filter will also elongate the transition from high to low. To get a sharp transition, we need one more piece…
Inverting Schmitt Trigger:
The third part of the circuit inverts the output. This way, when you press the button, the output goes high and when released, it goes low. Of course, the 74HC14 isn’t just an inverter, it also contains a Schmitt Trigger. This means that for the input to go from low to high, it has to go above the high threshold (~2.0v) and to go from high to low it has to drop below the low threshold (~1.3v). Having two thresholds instead of one dramatically decreases the effect of any remaining noise and reduces the elongated input from the filter into a nice sharp transition.
The OPL3 Connector
The MidiBox Hardware Platform (MBHP) OPL-3 module sits at the heart of the Smooth Operator. It houses the YMF262, the D/A converters, and all of the analog circuitry needed to produce sound. We can control the device through an 8-bit data bus and a few control pins.
Note: By consolidating the audio hardware like this, we can actually build all kinds of hardware modules that can plug into the Smooth Operator. As long as they have the same form factor and use the same control circuitry they should be compatible.
Without getting into the details of the module itself, let’s instead look at how to connect to it. These connections come in four types:
Power: +5, +12, -12 and GND power connections
Audio: 4 audio output channels (both signal & ground)
Data: 8-bit bus (D0-D7) for sending data
Control: Signals that tell the module when and where to write the data
Transmitting Data
First, we need a way to control the 8 data pins without using up all of the pins on the Arduino Nano. To do that, we make use of the Arduino’s SPI bus. This serial port is very fast and we can convert the serial signal to an 8-bit parallel one using a 74HC595 shift register.
The shift register reads serial data coming into SER (pin 14) using SRCLK (pin 11) for timing. Inside the chip, there are two sets of 8 flip-flops. The first set stores the data as it comes in, shifting over one bit at a time with every pulse on SRCLK. The second set of flip-flops latches the output of the first set as soon as RCLK pulses high.
This lines up really nicely with the SPI bus on the Arduino. Connect the MOSI pin (master out) to the SER pin on the 74HC595, and the SCK pin (Serial Clock) to the SRCLK pin. Now the 74HC595 will capture the serial data from the SPI bus. In order to see that data on the output (QA-QH), we also need to latch the data by pulsing the RCLK pin. So, connect RCLK to a data pin on the Arduino—I chose D6, but it doesn’t really matter.
OPL-3 Module Control Pins
What about those control pins? Well, let’s take a closer look:
/WR: tells the module to write the data on the bus into the YMF262. Note, the “/” means that a transition from positive to ground signals a write.
A0: tells the module whether we are selecting a register or writing data to the currently selected register.
A1: tells the module to which register set we are writing. We need this because the YMF262, contains two different register sets. This was an improvement over the OPL-2 standard which only had one set.
Reset: tells the YMF262 to reset everything.
A0, A1 and Reset can all be managed in the software on the Arduino. So you can connect them to any pin that can output a digital signal—I used D7, D8 and A1.
74HC595 Control Pins
As for the other pins on the 74HC595, tie /SRCLR high—we don’t need to clear the serial data. And you can tie /OE (operation enable) to ground—we always want to listen to the incoming serial data. QH’ is used for chaining together multiple 74HC595 shift registers, so we can leave that unattached.
We only trigger the /WR pin after loading data into the 74HC595. And as it turns out, we can use the same signal to latch the data onto the shift register and tell the module to read the data bus. The caveat is that /WR needs a negative pulse instead of a positive one. No problem. If we send the signal through the 74HC14 inverter, we get just what we need.
One final note: The module’s audio channel outputs each come with their own ground connection. While the output jacks sit on the Smooth Operator PCB, these ground connections are NOT connected to the ground plane of the Smooth Operator board. This would create a ground loop and cause all kinds of unwanted noise. You gotta keep ’em separated.
Are you thirsty for more?
The TFT Display
The TFT display is a critical component of the Smooth Operator. It provides visual feedback for all 50 of the patch settings as well as a graphical representation of the four envelopes. The beauty of organizing everything on one screen is that you don’t have to menu-dive to see how things change AND if you want to “save” a patch, you can just take a picture of the screen.
The Screen Module:
The screen module itself is a 1.8″ 128×160 TFT display that I purchased from AliExpress. There are lots of different types of these screens with different interfaces, so make sure yours has all 8 pins like the image above. Those pins are (GND / VCC / SCL / SDA / RST / DC / CS / BLK). A lot of similar screens don’t expose the Chip Select (CS) pin, but you really need it. The screen shares the SPI bus with the sound module, so we need to tell it when it is time to listen.
In case you were curious, here are the screen dimensions. These came in handy throughout the design process:
Hooking up the screen:
To connect the screen to the Arduino, I used the same SPI bus as the OPL3 module. I also connected the reset, chip select, and DC pins to their own digital pins on the Arduino (D10, A0, D9).
Note that the screen operates at 3.3v instead of 5v. Luckily the Arduino has a 3.3v regulated power output that we can use to power the screen. However, the data pins still operate at 5v and we need to use voltage dividers to lower them to 3.3v.
A voltage divider consists of two resistors in series with one side connected to the signal, and the other side connected to ground. The output signal comes from the point between the two resistors which allows about 2/3 of the 5v signal (or 3.3v) to pass into the screen.
MIDI Communication
MIDI connections are really just serial connection similar to the RX/TX pins on the Arduino. In fact, the only reason we don’t just directly connect the two together is because we want to ensure any unanticipated incompatible connections don’t harm the larger circuit.
To do this, we isolate incoming messages using an “opto-isolator.” The 6N138, contains a light emitting diode (LED) and photo sensor embedded inside of it. When the light turns on, the photo sensor allows current to flow through the circuit which passes the message on to the Arduino.
Note: because the Arduino Nano uses the TX/RX pins for its USB connection, I included a dual-pole, dual-throw switch to flip between USB mode (for programming the Arduino) and MIDI mode. Since this isn’t something you interact with frequently, I just used one of those micro switches.
A trip down the rabbit hole…
To really understand how the MIDI circuit works, it might make sense to re-draw it so MIDI Out and MIDI In are connected through a cable. Imagine that TX is connected to one Arduino and RX is connected to another Arduino:
The circuit on the left side of the 6N138 is pretty simple. +5V comes in from the power rail, through (R1), through the MIDI cable and into (R3). It then passes through the LED inside the 6N138, goes back through the MIDI cable, through another resistor (R2) and into the Arduino’s TX pin.
When transmitting data, the Arduino brings the TX pin HIGH shutting off current flow (because both sides of the LED are at 5v) and LOW which turns on the LED, sinking the current into the Arduino’s TX pin. The diode (D1) ensures that if the cable was inverted, current would flow around the 6N138 without damaging it.
On the right side of the circuit, the photo diode receives the signal and amplifies it through the two transistors contained in the 6N138. These transistors switch the signal, so when the LED is on, RX goes low, and visa versa.
In the end, when TX goes Low, so does RX, and visa versa.
Gate & Trigger
Not much to the gate and trigger outputs really. A diode prevents current from flowing into the Arduino from an external source, and the resistor is very likely unnecessary. I suppose it would ensure that there is a connection to ground even if A2/A3 on the Arduino were to be placed in a floating/input state.
Final Thoughts
Honestly, if you have read all the way through this, bless you. I sincerely hope the article was useful. This was my first project using Kicad and printing PCBs and, as you can imagine, I learned a huge amount along the way. Even now, there are probably a hundred ways to make this module better. And, if you have some of those ideas, I’d love to hear them!
The most important lesson I learned in this process was, to plan on getting it wrong. No matter how hard I tried, there was always some improperly sized footprint, an incorrect trace, or some other bug. Finding it was next to impossible before printing the PCB, and perfectly trivial afterwards. So, if you find yourself meticulously inspecting every wire for the 5th time, just stop, order and assemble the PCB and see why it doesn’t work. It’s soooooo much faster that way.