OPL2 FM Synth Module, part 2 – Prototype

I wrote earlier about a new eurorack synth module I’m working on. I left off having made a test bed that made some sort of sound but wasn’t integrated with the synth. This weekend, I built out the test bed to a full prototype and really got to take it for a spin. Guess what? It’s awesome.

Lets start by talking about what I finally decided to choose as an interface. This part is probably the most insightful design choice of the whole project. Earlier, I realized that I didn’t need to worry about the envelope generators on the YM3812 and that I’d simply patch those in from traditional (and more versatile) eurorack modules. That leaves far less analog parameters:

  • Operator 1 multiplier
  • Operator 1 feedback
  • Operator 1 amplitude
  • Operator 1+2 pitch

There are then a few more parameters I’d describe as “digital” in that they are not some sort of continuous value. I put these inside the menu system.

  • Operator 1 waveform (sine, half sine, rectified sine, clipped rectified sine)
  • Operator 2 waveform
  • Algorithm (Op1*Op2 or Op1+Op2)

Finally, there are the eight other voices I want to use in some sort of polyphonic chord generator.

You’ll notice there are only four analog signals. That is a reasonable amount to have on a eurorack module without it getting overly complex. For example, the GS/Grains has three inputs and one output. The Synthrotek VC LFO has one input and three outputs.

Of course the three digital parameters are another issue. I solved that by using an Adafruit RGB LCD Pi Plate. It is a 2-line character LCD and five buttons connected by I2C. It’s originally for a the Raspberry Pi, but it works great with an Arduino if you’re willing to write some code. It won’t work for a finished module because it’s huge, but for now it lets me test things out.

The LCD simply displays a few numbers corresponding to each of the digital parameters. The buttons directly toggle through the options of each parameter – I didn’t actually write any sort of menu system yet.

To configure the polyphonic chord generator, I’ll need to create that menu system. There are simply too many parameters to map directly to buttons and too many values to keep in your head at once.

Let’s talk about some of the problems I faced.

First, there is the issue of the 10 bit ADCs on the Arduino Pro Mini I used. Ten bits are not enough to accurately sample a 1 volt per octave signal that spans from 0 to 5 volts. As far as my calculations show, I need more like 14 bits. The other control voltages (CV) are fine – most are controlling very low bit-width parameters on the YM3812 anyways.

To implement the 1v/oct pitch input CV, I decided to simply quantize the input. In theory, this works: each half step corresponds to so many millivolts which corresponds to so many counts of the ADC. It seemed great. What I didn’t realize is how non-linear the ADCs are. They’re just inaccurate enough that notes kept landing “in between” two quantization bins, resulting in two adjacent keys playing the same note.

At first I thought it was a scaling problem – perhaps 5v wasn’t measured as 1023. I attempted to tweak my map() from the ADC to the note lookup table with no avail. No matter what I tweaked it to, some part of the 5v range would be wrong. That’s when I realized it was a non-linearity issue. The only choice I had was to pick some portion of the range to “work” while the rest would not track correctly. To do this, I used another ADC to sample a potentiometer that simply varies how much “tweaking” I do to the map() scale. This allows the user to pick whichever portion of the 5v range they want to use, and correctly scale. Is it great? No. But it got the prototype working. I could reliably adjust the scaling knob to get 3v (ie 3 octaves) of range. That isn’t bad – the Synthrotek DS-M only has 3 or 4 octaves of usable range and it’s great.

The second trouble I ran into was the I2C-connected LCD and buttons. It is so slow! I have to send every bit change over I2C and to write a single character to the LCD takes a dozen or so bit changes because it uses the “4-bit” interface mode. Reading the button inputs isn’t as bad because I can read 8 bits at once. But this is all happening inside the main loop() of the sketch. That’s also where I’m doing some time-critical operations, like sampling the ADCs and updating the registers of the OPL2. This can cause weird glitches in the sound when some iteration of the loop() takes too long. Again – Is it great? No. But it got the prototype working. I got things fast enough to remove most of the glitches. If I could break up some of the slow operations to happen across multiple loop() calls and keep things more consistently speedy.

So all this implementation detail is great, but how does it sound? Completely awesome, frankly. Digital FM like the OPL2 is capable of so many different sounds! It is an excellent counterpoint to the traditional subtractive synthesis I have on the Behringer Neutron. I can make punchy 80s synth bass, chiming synth clangs, screamingly bright metallic wails, and utter chaos digital noise. And you can often vary across this whole spectrum with a single parameter sweep. I haven’t fully explored the range of sounds at all.

I connected the Korg SQ-1 in dual-channel mode to control the pitch and Operator 1 amplitude in 8 step patterns. It was wild – you could keep evolving more and more sounds and patterns. I haven’t even gotten to ADSR control of even of these yet, and that’s where things REALLY get crazy. And with everything being voltage controlled, I can do things the YM3812 couldn’t do otherwise – long LFO sweeps, inverse parameter envelopes, etc, etc.

On the home computers of the 80s, programmers learned the tricks to sequencing all these parameter changes in software to create really amazing sounds. I’ll be creating the same types of sounds and more, but live and with a wall of continuously editable and patch-able parameters on my modular.

Oh, and I haven’t even gotten to polyphony yet. 😉