Dradique version 2.0

Dradique version 2.0 is a digital synthesizer heavily based on the Casio CZ series of synthesizers.

The entire synth engine runs on an Arduino Due, making extensive use of the Thumb2 instruction set as well as the ARM processor's 32-bit-ness :)

Demo video can be watched here.

The Arduino project files can be downloaded here.
I advice you to have a look at the project files first so you can get a general idea of the structure.
Keep in mind that I haven't done much polishing yet, so it may be a bit messy still.

The Software

The digital audio is generated through Direct Digital Synthesis (DDS) using 512-value, 10-bit wavetables (-512 to 512).
The core DSP is in mono, the FX is in stereo. This saves a lot of processing and sounds just fine.

I've tried to keep the actual DSP code as simple as possible and the Due seems to not break a sweat at 10-note polyphony, so I can probably push it a lot more still. If you are experimenting with my code feel free to try this out yourself :)

The Casio CZ synths have a couple of oddities that I've implemented in Dradique to get that CZ vibe, though I did add my own flavouring to the recipe.

the Phase Distortion
Instead of modulating the phase such as in FM, PD distorts the phase and thus distorts the shape of the waveform. There are already a lot of cool websites explaining this, so use Google if you want to know the details on how this works exactly.
Here's an image from the Electric Druid website that illustrates this:

Casio mainly used a sine and cosine waveform, you can see the effect on a sine in the picture, but I've also allowed other waveforms to be selected for more different sounds. These include triangle, sawtooth and square waves.

the DCO
Dradique technically has 20 oscillators, however I made each DCO in a way so that it can generate 2 phase distorted waveforms by combining both the two carrier and two modulator wavetables into two wavetables twice the size and then iterating with a single counter.
The reason I did this is because of one of the oddities that the Casio CZ synths have: when layering two DCO's the signals are actually not mixed together, which is the usual way of doing it.
Rather, the each DCO plays it's cycle one after the other. I.e. first one cycle of DCO 1 plays, then one cycle of DCO 2 plays, then DCO 1 again etc... This creates an interesting sub-octave effect when certain waveforms are combined.
Dradique just does this all the time, allowing for easy realtime mixing of waveforms to create all sorts of vibes, while limiting the actual calculations needed by combining two DCO's into one.
The Dual mode in Dradique actually layers two DCO's normally, mixing their signals together and tuning the second DCO down one octave.

The resonance in the CZ synths has nothing to do with phase distortion at all. It's nothing more but a sine wave at a frequency relative to the DCW "cutoff" frequency, hard-synced to the DCO. This sine wave is amplitude modulated by the inverse of the oscillator's phase. This is called using a 'window function'. This also means you can use a waveform as the window function, thus creating various resonant waveforms. In Dradique I just used the phase as the window function since that sounded really nice.

This picture from WikiPedia illustrates the process:

(a) The base frequency counter (window function), wrapping around every period.

(b) The resonance frequency counter at a (slightly) higher frequency, being reset (or "synced") when the base counter wraps around.

(c) The resonance frequency counter used as a sine wave readout. Note the nasty sudden jump at the reset!

(d) The inverted base frequency counter (window function).

(e) Multiplying c by d. The sudden jump in c is now leveled out. It now sounds a lot like resonance :)

Here's the struct and DSP code for a single DCO:

//'oscs' is an array of structs
//here's the struct:
struct DCO
  //property values, used in main loop
  byte note;
  bool gate;
  bool env_stage;
  bool mod_stage;
  bool busy;

  int16_t envelope;
  int16_t modulation;
  int16_t resofreq;

  //processing values used in DSP code
  volatile uint16_t freq;         //phase increments
  volatile uint16_t counter;      //phase counter

  volatile uint16_t resocounter;  //CZ style resonance

  volatile int16_t waveform[1024];  //carrier waveforms * 2
  volatile int16_t modulator[1024]; //modulator waveforms * 2 (distorts phase)

  volatile int16_t mod_depth;      //modulation depth 0 - 63
  volatile int16_t env_val;        //envelope value 0 - 63

//and here's the DSP:
//increment DCO phase
oscs[0].counter += oscs[0].freq;

//create an index offset in the phase and use it in the wavetable
uint16_t index = oscs[0].counter + (oscs[0].modulator[oscs[0].counter>>6] * oscs[0].mod_depth);
int32_t result = oscs[0].waveform[index>>6] * oscs[0].env_val;

//only update the volume when the amplitude is small, this reduces zipper noise a lot
if(result < 1024 && result > -1024)
	oscs[0].env_val = oscs[0].envelope;
outvalL = result;

//resonance output, only for the second waveform, CZ style :)
//so keep the resonance phase at zero the first half. This also creates the hard sync
//that is needed for CZ style resonance
if(oscs[0].counter < 32768)
	oscs[0].resocounter = 0;
	oscs[0].resocounter += oscs[0].resofreq;
	outvalL += (tab_sine[oscs[0].resocounter>>7] * (65535-oscs[0].counter)>>9) * mod_resonance.value;
the DCW ("filter")
The DCW of an oscillator is the magnitude of distortion that is applied to the reading angle of that oscillator's selected waveform. The DCW can be modified over time using an ADSR envelope, thus changing the timbre of the sound over time. This acts much like a low pass filter cutoff hence the analog vibe of the Casio CZ synths.

In Dradique I implemented the phase distortion by actually adding a waveform to the phase incrementer itself, this creates the exact same effect of phase distortion as the Casio CZ do. Again, a picture from Electric Druid illustrates this really well:

the DCA
The DCA, which modulates the DCO's amplitude has a simple ADSR envelope. In Dradique this is simply a multiplier on the DCO output.
The DCW and DCA in the CZ also had a "key follow" feature; which determined how much higher notes affected a sound, making the DCW have a more dull sound with less harmonics with higher notes, and making the DCA envelope faster for higher notes. This is something I've not yet implemented in Dradique.

Stereo Chorus
The chorus is just a very, very standard one. It's build on the example code by ElectroSmash for their Arduino guitar FX pedal. Go check them out, it's pretty cool stuff!

Patch Storage
The user patches are actually stored on the Flash memory, since the Due doesn't have an EEPROM and I didn't have one of a proper size handy either. Only 8 user presets is not much :P

I used the DueFlashStorage library for this, you can find info here.

Using the library is super simple. It used bank 1 by default so if your code is less than 256Kb you don't ever have to worry about overwriting stuff.
Here's the entire declaration, writing and reading code:

DueFlashStorage dueFlashStorage; //init library

dueFlashStorage.write(index, byte); //write a byte to flash index. Starts at flash bank 1.
byte dueFlashStorage.write(index); //read a byte from flash index. Starts at flash bank 1.
Not very riveting at all :)

The Hardware

I've made use of the two DACs on the Due, to create a smooth sounding stereo output. The two audio signals go through simple RC low pass filters, through simple voltage-follower opamps through a DC blocking capacitor to the output jack. The left channel also goes through a second opamp voltage-follower to buffer it for a 386 amplifier which drives the internal speaker.
A digital pin of the Due is also connected to the 386's input through a resistor to generate beeps when the user presses a button.

That's it really, all keyboard and front panel connections are directly attached to the Due with 4.7k pull-down resistors. No matrix scanning is done since the Due has pins a plenty! And it makes things a bit faster :)

Copyright 2018 Evil Turtle Productions. All Rights Reserved.