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 } } }