Build the mono synth

Now let’s build a synthesizer on the Arduino Nano platform.

The mono is a monophonic synth but has a paraphonic oscillator so you can play chords. The synth architecture is a basic 2 DCO, 1 DCF and 1 DCA.

Here is a demo video of the first tests.
A bit rough but very promising.

We’ll start with a donor keybed.
I’ve used the Swissonic49 4-octave full sized keyboard with pitchbend and modwheel.
While not a Fatar bed, is it quite a good quality full sized keyboard.

http://www.thomann.de/se/swissonic_easykey_49.htm

Swissonic49

It works just like the 37-keys used on the string synth but has an 8x7x2 matrix.

Swissonic49_2

New case worthy of the synth inside in progress.

newcase

 

We scan the keybed by reading the 8 rows from D2-D9 and outputing the 7 columns on D10, D12, D13, A0, A1, A2 and A3.

minimosch

//—————— Key scanner —————————–

if ((k&0x38)==(0x00<<3)) PORTB&=B11111011;
if ((k&0x38)==(0x01<<3)) PORTB&=B11101111;
if ((k&0x38)==(0x02<<3)) PORTB&=B11011111;
if ((k&0x38)==(0x03<<3)) PORTC&=B11111110;
if ((k&0x38)==(0x04<<3)) PORTC&=B11111101;
if ((k&0x38)==(0x05<<3)) PORTC&=B11111011;
if ((k&0x38)==(0x06<<3)) PORTC&=B11110111;

keytable[k]=digitalReadFast((k&7)+2);
PORTC|=0x0F;
PORTB|=0x34;
if (oldkeytable[k]!=keytable[k]) { //Handle keyevent
oldkeytable[k]=keytable[k];
if (keytable[k]==0) {
handleMIDINOTE(0x90,k+00,127);
}
else {
handleMIDINOTE(0x80,k+00,0);
}
}
k++;
if (k==56) {
k=0;
}
//—————————————————————

 

In the string synth we had the entire synth engine inside the Timer1 interrupt. Here we are going to use the same ringbuffer handler used in my drum chips:

ISR(TIMER1_COMPA_vect) {

//——————-  Ringbuffer handler ————————-

if (RingCount) {                            //If entry in FIFO..
OCR2A = Ringbuffer[(RingRead++)];          //Output to 8-bit DAC
RingCount–;
}

//—————————————————————–

}

This is a FIFO, First In First Out buffer. If there is a sample in the buffer it is output to the DAC. If not it does nothing.

The advantage is that context switching, happening when there is an interrupt, takes alot of extra time and we have to finish calculating one sample for the entire engine before the next interrupt happens.

Here we just have a simple ISR, always taking 3uS to execute, and can use the full cycles of the ATmega chip to calculate samples out of sync and just put these into the ringbuffer.

 

The signal path in the synth is 16-bit resolution with a 8-bit DAC and consists of the DCO, DCF and DCA.

 

The DCO uses the DDS NCO output as the waveshape.
This is what it looks like when and’ed with 0x1F:

DDS NCO Saw

A Sawwave. You can manipulate it with other logic operations to make other shapes, for instance and’ing it with 0x10 makes it a Square wave.

Code for the oscillator is:

//———- 8 DCO block ———————————–

DCO=0;
for (i=0;i<8;i++) {
DCOPH[i] += FREQ[i];                        //Add freq to phaseacc’s
DCO+=((DCOPH[i]>>16) & 0x1F)-0x10;
}

—————————–

 

After the oscillator comes the lowpass filter:

//———- DCF block ——————————-

//C implementation for a lowpass DCF with resonance:

for (i=0;i<6;i++) {
t[i] = ((t[i] * (255 – CUTOFF)) + (t[i+1] * CUTOFF))>>8;
}
t[6] = DCO-((t[0]*RESONANCE)>>8);  //resonance feedback
DCF=t[0]<<8;

//————————————————–

And last is the DCA for volume shaping:

//———- DCA block ——————————–

if ((ATTACK==255)&&(TRIG==1)) VCA=255;
if (!(envcnt–)) {
envcnt=20;
if (VCA<volume) VCA++;
if (VCA>volume) VCA–;
}

ENV=((DCF>>8)*VCA);

//————————————————-

You’ll notice that part of the code is similar to the string synth.
I’ll get more in depth on how the blocks work later on.

 

My work on these free synthesizers is based on donations from people.
If you find the code useful, please consider a $3 donation to keep future developments open source.

Donate $3

Chips for building DIY synthesizers can be found at:

http://www.dspsynth.eu


%d bloggers like this: