Introduction: Post-Box Synthesizer

Picture of Post-Box Synthesizer

In this project, take an old post-box and an Arduino, to create an incredibly functional monophonic synthesizer. This synthesizer includes such features as:
- Dual oscillators
- 6 wave forms (Sin, Triangle, Left Saw, Right Saw, Square, Flat)
- Noise feature on the main oscillator
- Adjustable mixing of the two oscillators
- Adjustable cents, semitone, and octave for the second oscillator
- LFO from 0 to 10 Hz
- Routing the LFO to semitone, cents and octave control of the second oscillator
- 20 note arpeggio feature with adjustable speed from 0 to 50Hz.
- 5 banks for saving presets
- Internal speaker and 3.5mm aux output with volume control
- LCD
- MIDI input
- UART input


Parts
Filter:
- 2X 4.7mH inductor
- 2X 47nF capacitor
- 1X 100nF capacitor
- 2X 270 ohm resistor 
- PC board

MIDI Input:
- 1X Female MIDI connector
- 1X 6N138 opto-isolator
- 1X 220 ohm resistor
- 1X 270 ohm resistor
- 1X 1N194 diode

Audio Output:
- 1X 3.5mm Female audio jack
- 1X 8 ohm speaker
- 1X SPDT switch
- 1X Amplifier (For this I used a ready made breakout from SparkFun https://www.sparkfun.com/products/11044)
- 1X 10k ohm potentiometer

User Input/Output:
- 1X Serial Enabled LCD (20x4 Character LCD from SparkFun https://www.sparkfun.com/products/9568)
- 6X Tactile Switches
- 4X 10k ohm potentiometers

Misc:
- 1X DC Barrel Jack
- 1X 7805 voltage regulator
- 1X 5 pin male header
- 1X 10k ohm resistor
- 1X push switch (for the reset)

Step 1: Synthesis Method

Picture of Synthesis Method

The synthesis method used in this project is called DDS, direct digital synthesis. With this method, a digital signal, 1's and 0's, can be turned into an analog signal without the addition of a DAC, digital to analog converter. In fact with DDS, there are very few extra components are actually required; only a low pass filter.

The method works by creating a PWM, pulse width modulation, signal and modulating the duty cycle, the amount of time the signal stays on, in proportion to the amplitude of a wave form at a given time. So in the code there is a wave table of one period for various wave forms. The program then steps through the table at different speeds to create different frequencies. The output of the PWM is shown in the image below. As the duty cycle increase, the amplitude of the output wave increases. The filter removes the carrier frequency, the square wave, and leave the clean wave form from the table.

Step 2: The Filter

Picture of The Filter

There are a couple ways to create a filter. You can make an RC or LC filter, as long as it's built in a lowpass configuration with a 12.5 kHz cutoff frequency. I used a 2nd Order Chebyshef filter which removes the carrier frequency extremely well, and leaves a smooth sound for the output signal. The schematic is fairly simple, even though it requires inductors, and only needs 7 components.

First I tried to just solder the leads together, but then I used a PC board to make is easier, and look a little more professional. It makes connecting the input and output easier and keeps all the components for the filter nice and segmented.

Step 3: PWM Code

The first step is to create the wave table. The table is saved into the Atmega328 RAM using the pgmspace library. Each wave table has 256 values from 0 to 255, so each value can be mapped to a byte data type. The sine wave definition is shown below. Each value is the amplitude of the wave at a specific time. This represents one period of the wave. The higher the frequency that is played, the faster the program steps through the table.

#include "avr/pgmspace.h"

//Waveform definitions
PROGMEM  prog_uchar waveTable[]  = {
  //sine wave
  0,0,0,0,1,1,1,2,2,3,4,5,5,6,7,9,10,11,12,14,
15,16,18,20,21,23,25,27,29,31,33,35,37,39,
42,44,46,49,51,54,56,59,62,64,67,70,73,76,
78,81,84,87,90,93,96,99,102,105,108,111,115,
118,121,124,127,130,133,136,139,143,146,149,
152,155,158,161,164,167,170,173,176,178,181,
184,187,190,192,195,198,200,203,205,208,210,
212,215,217,219,221,223,225,227,229,231,233,
234,236,238,239,240,242,243,244,245,247,248,
249,249,250,251,252,252,253,253,253,254,254,
254,254,254,254,254,253,253,253,252,252,251,
250,249,249,248,247,245,244,243,242,240,239,
238,236,234,233,231,229,227,225,223,221,219,
217,215,212,210,208,205,203,200,198,195,192,
190,187,184,181,178,176,173,170,167,164,161,
158,155,152,149,146,143,139,136,133,130,127,
124,121,118,115,111,108,105,102,99,96,93,90,
87,84,81,78,76,73,70,67,64,62,59,56,54,51,49,
46,44,42,39,37,35,33,31,29,27,25,23,21,20,18,
16,15,14,12,11,10,9,7,6,5,5,4, 3,2,2,1,1,1,0,0,0,
};

To get the Arduino to create the PWM signal, the timer has to be properly initialized. For this I used the C method to setup the timer so that I can better control it. The timer is created so that we have a 32 kHz sampling rate for our audio and the output of the signal is put on 11 of the Arduino. I also enable an overflow interrupt, so that when the timer value goes over 255, the interrupt triggers.

void Setup_timer2() {

  // Timer2 Clock Prescaler to : 1
  sbi (TCCR2B, CS20);
  cbi (TCCR2B, CS21);
  cbi (TCCR2B, CS22);

  // Timer2 PWM Mode set to Phase Correct PWM
  cbi (TCCR2A, COM2A0);  // clear Compare Match
  sbi (TCCR2A, COM2A1);

  sbi (TCCR2A, WGM20);  // Mode 1  / Phase Correct PWM
  cbi (TCCR2A, WGM21);
  cbi (TCCR2B, WGM22);
}

This is the overflow interrupt. When the interrupt occurs I calculate the next value that should be pulled from the wave table and write that value to pin 11. A variable called the phase accumulator keeps track of where the program is in the table.

ISR(TIMER2_OVF_vect)
{

  phaccu=phaccu+tword_m; // soft DDS, phase accu with 32 bits
  icnt=phaccu >> 24;     // use upper 8 bits for phase accu as frequency information
                         // read value fron ROM sine table and send to PWM DAC

  OCR2A=pgm_read_byte_near(waveTable + icnt + (waveSelect << 8));   

  if(icnt1++ == 125) {  // increment variable c4ms all 4 milliseconds
    c4ms++;
    icnt1=0;
   }  
}

That value is calculated using a tuning word which is found by dividing the frequency you want by a reference clock, in this case the 32kHz reference clock.

const double refclk=31376.6;      // measured
tword_m=pow(2,32)*dfreq/refclk;  // calulate DDS new tuning word

Step 4: Note Effects

The note values are stored to an array. You can find the values here: http://en.wikipedia.org/wiki/Piano_key_frequencies

double keyFreq[] = {
  27.5, 29.1352, 30.8677,     //Octave 0
  32.7032, 34.6478, 36.7081, 38.8909, 41.2034, 43.6535, 46.2493, 48.9994, 51.9131, 55, 58.2075, 61.7354,     //Octave 1
  65.4064, 69.2957, 73.4162, 77.7817, 82.4069, 87.3071, 92.4986, 97.9989, 103.826, 110, 116.541, 123.471,    //Octave 2
  130.813, 138.591, 146.832, 155.563, 164.814, 174.614, 184.997, 195.998, 207.652, 220, 233.082, 246.942,    //Octave 3
  261.626, 277.183, 293.665, 311.127, 329.628, 349.228, 369.994, 394.995, 415.305, 440, 466.164, 493.883,    //Octave 4
  523.251, 554.365, 587.330, 622.254, 659.255, 698.456, 739.989, 783.991, 830.609, 880, 932.328, 987.767,    //Octave 5
  1406.50, 1108.73, 1174.66, 1244.51, 1318.51, 1396.91, 1479.98, 1567.98, 1661.22, 1760, 1864.66, 1975.53,   //Octave 6
  2093.00, 2217.46, 2349.32, 2489.02, 2637.02, 2793.83, 2959.96, 3135.96, 3322.44, 3520, 3729.31, 3951.07,   //Octave 7
  4186.01                                                                                                    //Octave 8
};
So notes sent from the MIDI or over UART have an appropriate value, instead of having to be calculated on the fly.

The second oscillator can be detuned from the first in 3 ways.
1. Is using a system called cents, which are fractions of a note.  Calculated like this:
centMultiplier = pow(2.0,(cents + dC)/1200.0);
That value is then multiplied to the note frequency.
2. Is using a system called semi, which are full note shifts from -1 to +1 octave
3. Finally by full octaves from -3 to +3

The two oscillators are then mixed by using an adjustable weight.
byte osc1 = ((pgm_read_byte(waveTable + icnt1 + (osc1WaveForm<<8))*weight1)/MAX_WEIGHT); //first osc
byte osc2 = ((pgm_read_byte(waveTable + icnt2 + (osc2WaveForm<<8))*weight2)/MAX_WEIGHT); //second osc
The two values are then summed. The weight value goes from 0 to 16. So you can have entirely the first oscillator, entirely the second, or some mixture in between.

The LFO adjusts the detuning of the second oscillator by adjusting the values in proportion to the amplitude of the wave.So it works in a similar way to the first 2 oscillators but instead of creating sound, it tweaks values.

Step 5: Arpeggiator

The arpeggiator is a system that creates an arpeggio based upon the notes played when in arpeggio mode. When arpeggio mode begins, you play a note. That note becomes the root key. Every key hit afterward is saved to an array, of a max of 20 notes. The value stored to the array is the difference between the note played and the root key.

if(appMode) //add notes to the app array
      {
        if(appMaxCount == 0) //if just starting app mode
        {
          rootKey = note - MIDI_OFFSET; //get new root key, all notes in array are relative to this value
        }
        else
        {
          app[appMaxCount - 1] = noteSelect - rootKey; //calculate relative note
        }
        appMaxCount++; //increment number of notes in app array

        if(appMaxCount > MAX_APP_NOTES)
        {
          appMode = false;
          appUpdate();
        }
      }

When playing, the arpeggio array is stepped through at a speed depending on the value from one of the control potentiometers. The value in the array is added to the note being played.

noteSelect = rootKey + app[appCount];
    appTimer = millisecs;

    appCount++; //move through the array

    if(appCount >= appMaxCount)
    {
      appCount = 0;
    }

Step 6: Control

Picture of Control

To start, wire the MIDI connector according to the schematic. It's important to note that the MIDI connector is probably upside down in the schematic, make note before you start soldering. The point of the opto-isolator is to keep the signal from the MIDI controller from damaging the control board. The output from the opto-isolator is connected to the serial input, RX, pin on the Arduino.

The MIDI in is serial at 32150 baud. The system is 3 bytes. The first byte is whether or not the note is on or off. The second is the note value and the third is the velocity, but I ignore that.
I handle it with a serial event.

void serialEvent()
{
  if(Serial.available() >= 3) //messages in 3 byte packets
  {
    byte cmd = Serial.read();
    byte note = Serial.read();
    byte vel = Serial.read();

    if(cmd >= 0x80 && cmd <= 0x8F && (rootKey == note - MIDI_OFFSET || noteSelect == note - MIDI_OFFSET)) //note off
    {     
      notePlaying = false;
    }
    else if(cmd >= 0x90 && cmd <= 0x9F) //note on
    {
      noteSelect = note - MIDI_OFFSET;

      notePlaying = true;
    } 
  }
}


Because the synthesizer is mono, I connected the left and right channels of the audio jack together.

The SPDT switch is used to switch between audio output to the jack or the speaker. The center pin is where the signal from the amplifier is connected. The right pin goes to the audio jack and the left to the speaker. The ground of the audio jack, the center pin, is connected to one of the pins of the speaker, then both are connected to ground.

Step 7: User Control

Picture of User Control

The user control is composed of 3 parts, the LCD, the switches, and the potentiometers.

If you look at the schematic, all the switches are connected via a common ground. Luckily I had a switch array from an old computer monitor that already had the right number of switches, all connected by common ground. It even had an LED, which isn't necessary but I included it anyway. Without this array each switch would have had to be connected together manually. One side of all the switches is connected to ground, then each switch's other side is connected to a pin on the Arduino. Each of the pins on the Arduino then has an internal pull-up enabled.

The LCD is serial enabled, but because the MIDI in takes the main serial connection, the LCD requires a software serial connection. The software serial is enabled on pin 13, so that is connected to the receiving pin on the LCD. The LCD is also connected to the power and ground on the main board.

The potentiometers are connected to the Arduino's analog input pins 0 through 3. The Arduino's AREF pin is connected to the 5 volts.

In order to avoid sacrificing an entire Arduino board for this project,  I programmed the chip first, then remove it to a separate board with a separate crystal. This requires a PC board for the chip and crystal. Now this becomes the control board, having rails for power and ground and all the pins broken out.

Step 8: Putting It Together

Picture of Putting It Together
First step is lay out the parts, mark spaces, then cut the holes. Using generous helping of hot glue I put the LCD and button array into place on the lid of the box. Then using the nuts and washers that came with the potentiometers, and attach them through the lid.

Next I wire up the amplifier. I connect the power to the main power on the control board. Then I wire the volume potentiometer to the three spaces on the amp. The nice thing about the breakout board is all of the connections are appropriately labeled. I take the volume potentiometer and connect it through the left side of the box. The output from the filter is connected to the input on the amplifier. The output of the amplifier is connected to the switch. Only the positive output from the output on the amplifier is connected to the middle pin on the audio switch. 

I added an external reset switch just in case, next to the volume control potentiometer. It helps when reprogramming the board, or if the synthesizer get stuck.

Wire up the power supply. I used a DC barrel jack and a 7805 voltage regulator. The back of the DC barrel is the positive, so by the schematic, that is connected to the input pin on the 7805. The control board and barrel jack share a common ground. The output from the voltage regulator is then run to the 5 volt line on the control board. The DC jack is glued to the back of the box. I only recommend putting in 9V to the jack, maximum.

The FTDI connector is 5 male header pins connected as shown in the schematic. This allows for serial communication to the synthesizer if you don't have a MIDI controller.

Using the speaker, I marked a space. Then using a compass, I created concentric circles to drill holes for the sound to come through.

Once everything is properly wired, use that hot glue again to secure everything down. I put the MIDI In/Audio Out in the upper right hand corner, the control board in the upper left, speaker lower right, and the filter and amplifier in toward the center.

Add a little paint, and that's it.

Now a little demo...

Comments

GabrielP20 (author)2015-09-09

Could anyone please post the complete code so I can Learn how to mount it by assimilating the explanation to the result? That would be really helpful for beginners like myself.. Thanks as bunch this is a great project!

rczarnik (author)2013-08-07

You're getting some really great sounds here. Especially when you detune the oscillators. Sounds very comparable to an analog. Good job!

Josehf Murchison (author)2013-07-31

Now that is neat a cardboard proto box.

About This Instructable

9,265views

51favorites

License:

More by humanHardDrive:Post-Box Synthesizer
Add instructable to: