dual arduino :: voice generator & manager
Looking for an original solution for a voice generator (instead of the Juno 82c54 solution), I decided to dedicate an entire Arduino to voice generation.
All voices start with a PWM wave and that’s just a switch from +5V to 0V for a precise number of times per second. This voice generator Arduino runs in 1 big counter loop, flipping four PWM outputs between HIGH and LOW. Each pin has its own switch value, determined by the desired PWM frequency for that pin. All values are subject to the speed of the counter loop. The faster the counter loop, the higher the switch values and the more precise the PWM frequencies.
The manager Arduino controls all other aspects of the synthesizer like filters etc. This Arduino is also responsible for MIDI control. MIDI-IN is interpreted and send to the voice generator via SPI (74hc595). The voice generator has a 8 bit parallel in for MIDI and voice data.
To archieve the voice generation, I combined this article http://jeremyblum.com/2010/09/05/driving-5-speakers-simultaneously-with-an-arduino/ [jeremy] with this article http://billgrundmann.wordpress.com/2009/03/03/to-use-or-not-use-writedigital/ [bill]. Using Bill’s article, I build a very fast counter loop. In pseudo code, the program is like this:
setup -> enable output pins and disable everything, except mainloop counter
setup -> enable parallel in on pins 6 – 13
loop -> for each voice -> check if this voice pin should be flipped
loop -> check if there’s a new note value for one of the voices -> yes? read -> no? continue
Here’s the code for the voice generator Arduino:
// arduino 4 voice square wave generator // all code resides in main loop // all delays are generated by main loop events // globals // toggle_steps -> after n steps, toggle voice output from HIGH to LOW, thus creating a squarewave int toggle_after_nrof_steps[] = { 132, 210, 220, 230, 240, 250, 260, 270, 280, 290, 300, 310, 320, 330, 340, 350, 360, 370, 380, 390, 400, 410, 420, 430, 440, 450, 460, 470, 480, 490, 500, 510, 520, 530, 540, 550, 560, 570, 580, 590, 600, 610, 620, 630, 640, 650, 660, 670, 680, 690, 700, 710, 720, 730, 740, 750, 760, 770, 780, 790, 800, 810, 820, 830, 840, 850, 860, 870, 880, 890, 900, 910, 920, 930, 940, 950, 960, 970, 980, 990, 1000, 1010, 1020, 1030, 1040, 1050, 1060, 1070, 1080, 1090, 1100, 1110, 1120, 1130, 1140, 1150, 1160, 1170, 1180, 1190 //100 }; long stepcounter1 = 0L; long stepcounter2 = 0L; long stepcounter3 = 0L; long stepcounter4 = 0L; int steps1 = 0; int steps2 = 1; int steps3 = 2; int steps4 = 3; int pinb_value= 0; int pind_value= 0; int pinb_value_old= 0; int pind_value_old= 0; boolean newnote = false; void setup() { pinMode(2, OUTPUT); pinMode(3, OUTPUT); pinMode(4, OUTPUT); pinMode(5, OUTPUT); //pinMode(1, OUTPUT); // dummy / buffer output // 6 - 13 input high int i; for(i=6; i<14;i++) { pinMode(i, INPUT); digitalWrite(i, HIGH); } bitWrite(ADCSRA, ADEN, LOW); // turn ADC off bitWrite(WDTCSR, WDE, LOW); // turn watchdog off bitWrite(WDTCSR, WDIE, LOW); // turn watchdog off bitWrite(TCCR0B, CS02, LOW); // turn timer0 off bitWrite(TCCR0B, CS01, LOW); // turn timer0 off bitWrite(TCCR0B, CS00, LOW); // turn timer0 off bitWrite(TCCR1B, CS12, LOW); // turn timer1 off bitWrite(TCCR1B, CS11, LOW); // turn timer1 off bitWrite(TCCR1B, CS10, LOW); // turn timer1 off bitWrite(TCCR2B, CS22, LOW); // turn timer2 off bitWrite(TCCR2B, CS21, LOW); // turn timer2 off bitWrite(TCCR2B, CS20, LOW); // turn timer2 off Serial.begin(9600); Serial.println("start"); } void loop() { while(1) { // for optimal processing (no sound hickups etc), the logic is divided over all voices // each voice has the same logic // if stepcounter reaches steps (note) value // switch digital pin // reset stepcounter // increase stepcounter // newnote? // check voice value // if this voice --> change steps value, reset stepscounter // voice 1----------------------------------------------------------------- if(stepcounter1 == toggle_after_nrof_steps[steps1]) { PIND |= _BV(2); // 250ns stepcounter1 = 0; } stepcounter1++; if(newnote) { if(pind_value == 0) {steps1=pinb_value;stepcounter1=0;} steps1=pinb_value;stepcounter1=0; } // voice 2----------------------------------------------------------------- if(stepcounter2 == toggle_after_nrof_steps[steps2]) { PIND |= _BV(3); stepcounter2 = 0; } stepcounter2++; if(newnote) { if(pind_value == 1) {steps2=pinb_value;stepcounter2=0;} } // voice 3----------------------------------------------------------------- if(stepcounter3 == toggle_after_nrof_steps[steps3]) { PIND |= _BV(4); stepcounter3 = 0; } stepcounter3++; if(newnote) { if(pind_value == 2) {steps3=pinb_value;stepcounter3=0;} } // voice 4----------------------------------------------------------------- if(stepcounter4 == toggle_after_nrof_steps[steps4]) { PIND |= _BV(5); stepcounter4 = 0; } stepcounter4++; if(newnote) { if(pind_value == 3) {steps4=pinb_value;stepcounter4=0;} } // reset newnote if (newnote) { pinb_value_old = pinb_value; newnote = false; } // if digital pins changed // raise new value flag // read digitalpin 6,7 (voice id) and digital pin 8,9,10,11,12,13 (data) pinb_value = PINB & B00111111; // dont read 14,15 pind_value = PIND & B11000000; // only read 6 and 7 if(pinb_value!=pinb_value_old) { newnote = true; } } }