First DCO prototype

This first DCO prototype uses the Arduino 16 bit timer (timer1) and the midi-in solution from Kuk on the Arduino forum

The Arduino has a hardware 16 bit timer (timer1). This timer offers 15 different counting modes. One of these modes is the so called “Clear Timer on Compare Match (CTC) Mode” (Mode 4):

When the timer, via OCR1A, reaches zero the output pin, via OC1A, is toggled thus creating a PWM wave with pulse-widths according to the timer values. The timer is easily updated by setting the value for OCR1A in code. The formula to calculate timings for frequencies is:

(CPU Clock / prescaler) / (required frequency * 2)

This formula combined with the frequency for midi-notes in excel shows the following:

midi notes (download entire sheet)

The arduino prescaler supports division rates 1,8,64,256 and so on. Since the counter is 16bit, the maximum value for OCR1A is 65.536. The first 11 midinotes are available through prescale 64, the next 35 are available with the highest precision using prescale 8 and the rest of the total midinote range is available using prescale 1.

Midi to Arduino: kuk on the Arduino forums

Hook up your midi keyboard and your headphones to pin 9 and GND. The code:

// sinneb.com
// sinneb sine polysynth
// 16 june 2010

// midi recognition code by Kuk (http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1187962258/)

// frequencies for midi notes 0 - 127
int musical_freqs[] = { 15289, 14431, 13621, 12857, 12135, 11454, 10811, 10204, 9631, 9091, 8581, 64792, 61156,
                        57724, 54484, 51426, 48540, 45815, 43244, 40817, 38526, 36364, 34323, 32396, 30578, 28862,
                        27242, 25713, 24270, 22908, 21622, 20408, 19263, 18182, 17161, 16198, 15289, 14431, 13621,
                        12856, 12135, 11454, 10811, 10204, 9631, 9091, 8581, 64793, 61156, 57724, 54484, 51426, 48540,
                        45815, 43244, 40817, 38526, 36364, 34323, 32396, 30578, 28862, 27242, 25713, 24270, 22908, 21622,
                        20408, 19263, 18182, 17161, 16198, 15289, 14431, 13621, 12856, 12135, 11454, 10811, 10204, 9631, 9091,
                        8581, 8099, 7645, 7215, 6810, 6428, 6067, 5727, 5405, 5102, 4816, 4545, 4290, 4050, 3822, 3608, 3405,
                        3214, 3034, 2863, 2703, 2551, 2408, 2273, 2145, 2025, 1911, 1804, 1703, 1607, 1517, 1432, 1351, 1276,
                        1204, 1136, 1073, 1012, 956, 902, 851, 804, 758, 716, 676, 638 };

// currnote = 69 = A 440Hz
int curr_midinote = 69;
int incomingByte = 0;   // for incoming serial data
byte note;
byte velocity;
int action=2; //0 =note off ; 1=note on ; 2= nada

void setup() {
  pinMode(9, OUTPUT);

  // Timer/Counter 1 :: registers TCCR1A and TCCR1B

  // Clear Timer on Compare Match (CTC) Mode
  bitWrite(TCCR1A, WGM10, 0);
  bitWrite(TCCR1A, WGM11, 0);
  bitWrite(TCCR1B, WGM12, 1);
  bitWrite(TCCR1B, WGM13, 0);

  // Toggle OC1A on Compare Match. p.134
  // arduino hw pin 9
  bitWrite(TCCR1A, COM1A0, 1);
  bitWrite(TCCR1A, COM1A1, 0);

  // prescaling
  set_prescaling(curr_midinote);

  // set output compare register A to curr_midinote (init: 69)
  OCR1A = musical_freqs[curr_midinote];

  // midi baudrate 31250 so use Python Serial Monitor
  Serial.begin(31250);
}

void set_prescaling (int midinote) {
  cli(); // interrupts off

  if (midinote < 11) {
    // prescaling * 64
    bitWrite(TCCR1B, CS10, 1);
    bitWrite(TCCR1B, CS11, 1);
    bitWrite(TCCR1B, CS12, 0);
    //Serial.println("prescale64");
  }
  if (midinote > 10 && midinote < 47) {
    // prescaling * 8
    bitWrite(TCCR1B, CS10, 0);
    bitWrite(TCCR1B, CS11, 1);
    bitWrite(TCCR1B, CS12, 0);
    //Serial.println("prescale8");
  }
  if (midinote > 46) {
    // prescaling * 1
    bitWrite(TCCR1B, CS10, 1);
    bitWrite(TCCR1B, CS11, 0);
    bitWrite(TCCR1B, CS12, 0);
    //Serial.println("prescale1");
  }

  sei(); // interrupts on
}

void stop_timer() {
  //TCCR1B = 0x00;
  //cli();
  //OCR1A = 1;
}

void loop() {
  if(Serial.available() > 0)
      {
        incomingByte = Serial.read();

        // wait for as status-byte, channel 1, note on or off
        if (incomingByte== 144){ // note on message starting starting
          action=1;
        }else if (incomingByte== 128){ // note off message starting
          action=0;
        }else if (incomingByte== 208){ // aftertouch message starting
           //not implemented yet
        }else if (incomingByte== 160){ // polypressure message starting
           //not implemented yet
        }else if ( (action==0)&&(note==0) ){ // if we received a "note off", we wait for which note (databyte)
           //removed
        }else if ( (action==1)&&(note==0) ){ // if we received a "note on", we wait for the note (databyte)
          note=incomingByte;
        }else if ( (action==1)&&(note!=0) ){ // ...and then the velocity
          velocity=incomingByte;

          if(velocity==0) { // note off
            stop_timer();
          } else {
            set_prescaling(note);
            OCR1A = musical_freqs[note];
          }

          Serial.println(note,DEC);
          note=0;
          velocity=0;
          action=0;
        }else{
          //nada
        }
      }
  }