/* Arduino program for a musical robot Translated from Positron Basic code for PIC microcontrollers by dr.Godfried-Willem Raes implemented: Midi-out Midi-in with parsing 12-solenoid outputs timing system - velo pulse implemented, 1 ms resolution only need a better timer resolution. 4microsecond should be possible. */ // constants const int beat1 = 2; // this should define the pins on the Arduino board const int beat2 = 3; const int beat3 = 4; const int beat4 = 5; const int beat5 = 6; const int beat6 = 7; const int beat7 = 8; const int beat8 = 9; const int beat9 = 10; const int beat10 = 11; const int beat11 = 12; const int beat12 = 13; const int ana1 = A5; // constants for the midi-note mapping - notes 60 - 71 const byte noot0 = 60; const byte noot1 = 61; const byte noot2 = 62; const byte noot3 = 63; const byte noot4 = 64; const byte noot5 = 65; const byte noot6 = 66; const byte noot7 = 67; const byte noot8 = 68; const byte noot9 = 69; const byte noot10 = 70; const byte noot11 = 71; // constants for the midi system: const byte midichannel = 0; const byte noteoffstatus = 128 + midichannel; const byte noteonstatus = 144 + midichannel; const byte keypresstatus = 160 + midichannel; const byte ctrlstatus = 176 + midichannel; const byte progchangestatus = 192 + midichannel; const byte aftertouchstatus = 208 + midichannel; const byte pitchbendstatus = 224 + midichannel; const byte NrTasks = 12; const byte LastTask = NrTasks - 1; // Variables byte beat1state = LOW; // low on boot byte beat2state = LOW; byte beat3state = LOW; byte beat4state = LOW; byte beat5state = LOW; byte beat6state = LOW; byte beat7state = LOW; byte beat8state = LOW; byte beat9state = LOW; byte beat10state = LOW; byte beat11state = LOW; byte beat12state = LOW; // use "unsigned long" for variables that hold time // millis() will overflow after 50 hours (ca. 2 days...) // mtime will overflow after 70 seconds... unsigned long previousMillis = 0; // will store last time LED was updated unsigned long time = millis() ; unsigned long mtime = 0 ; unsigned long timvals[NrTasks]; byte resortflag = 0; byte idx = 255; unsigned long nxt = 0xFFFFFFFF ; byte i = 0; // midi related: byte inByte = 255; byte RingBuffer[256]; // should be a byte buffer Array for holding received midi messages byte inidx = 0; byte outidx = 0; byte statusbyte = 255; byte noteUit; // note off + release value byte release; byte noteAan; // note on + release value byte velo; byte notePres; // note pressure + pressure value byte pres; byte ctrl; // continuous controller + value byte value; byte prog; // program change + program-byte byte aft; // channel aftertouch byte pblsb; // pitch bend lsb byte pbmsb; // pitch bend msb byte st; byte b1; byte b2; // constants won't change: const long interval = 1000; // interval at which to blink (milliseconds) void setup() { // setup code here, to run once: // Set MIDI baud rate: Serial.begin(31250); // midi baud rate for TX and RX // set the digital pins as outputs: pinMode(beat1, OUTPUT); pinMode(beat2, OUTPUT); pinMode(beat3, OUTPUT); pinMode(beat4, OUTPUT); pinMode(beat5, OUTPUT); pinMode(beat6, OUTPUT); pinMode(beat7, OUTPUT); pinMode(beat8, OUTPUT); pinMode(beat9, OUTPUT); pinMode(beat10, OUTPUT); pinMode(beat11, OUTPUT); pinMode(beat12, OUTPUT); } void loop(){ // dit is de hoofdlus van het programma inByte = GetMidiIn(); time = millis(); mtime = micros(); // in 4 microsecond increments if (inByte > noteonstatus) { // in this case we disregard the byte //goto CheckTimers if (inByte < 254) { statusbyte = 0; // and we should do nothing... goto CheckTimers; } } midiparser: if (bitRead(inByte, 7) == 1) { // in this case we have a statusbyte, the MSB is set. switch (inByte){ case noteoffstatus:; statusbyte = inByte ; noteUit = 255; release = 255; break; case noteonstatus:; statusbyte = inByte; noteAan = 255; velo = 255; break; case keypresstatus:; statusbyte = inByte; break; case ctrlstatus:; statusbyte = inByte; break; case progchangestatus:; statusbyte = inByte; break; case aftertouchstatus:; statusbyte = inByte; break; case pitchbendstatus:; statusbyte = inByte; break; } } else { // 1 or 2 data bytes, status must be set switch (statusbyte) { case 0:; goto CheckTimers; break; // does not make sense here... case noteoffstatus:; if (noteUit = 255) { noteUit = inByte; } else { release = inByte; // message complete, so we can do the action... switch (noteUit) { case noot0:; digitalWrite (beat1, LOW) ; timvals[0]= 0xFFFFFFFF; noteOff (noot0, release); break; case noot1:; digitalWrite (beat2, LOW) ; timvals[1] = 0xFFFFFFFF; noteOff (noot1, release); break; case noot2:; digitalWrite (beat3, LOW) ; timvals[2] = 0xFFFFFFFF; noteOff (noot2, release); break; case noot3:; digitalWrite (beat4, LOW) ; timvals[3] = 0xFFFFFFFF; noteOff (noot3, release); break; case noot4:; digitalWrite (beat5, LOW) ; timvals[4] = 0xFFFFFFFF; noteOff (noot4, release); break; case noot5:; digitalWrite (beat6, LOW) ; timvals[5] = 0xFFFFFFFF; noteOff (noot5, release); break; case noot6:; digitalWrite (beat7, LOW) ; timvals[6] = 0xFFFFFFFF; noteOff (noot6, release); break; case noot7:; digitalWrite (beat8, LOW) ; timvals[7] = 0xFFFFFFFF; noteOff (noot7, release); break; case noot8:; digitalWrite (beat9, LOW) ; timvals[8] = 0xFFFFFFFF; noteOff (noot8, release); break; case noot9:; digitalWrite (beat10, LOW) ; timvals[9] = 0xFFFFFFFF; noteOff (noot9, release); break; case noot10:; digitalWrite (beat11, LOW) ; timvals[10] = 0xFFFFFFFF; noteOff (noot10, release); break; case noot11:; digitalWrite (beat12, LOW) ; timvals[11] = 0xFFFFFFFF; noteOff (noot11, release); break; } noteUit = 255; // reset break; } case noteonstatus:; if (noteAan = 255) { noteAan = inByte; } else { velo = inByte; // message complete // to do: implement case with velo = 0 switch (noteAan) { case noot0:; digitalWrite (beat1, HIGH) ; timvals[0] = time + velo; resortflag = 1; noteOn (noot0, velo); break; case noot1:; digitalWrite (beat2, HIGH) ; timvals[1] = time + velo; resortflag = 1 ; noteOn (noot1, velo); break; case noot2:; digitalWrite (beat3, HIGH) ; timvals[2] = time + velo; resortflag = 1 ; noteOn (noot2, velo); break; case noot3:; digitalWrite (beat4, HIGH) ; timvals[3] = time + velo; resortflag = 1 ; noteOn (noot3, velo); break; case noot4:; digitalWrite (beat5, HIGH) ; timvals[4] = time + velo; resortflag = 1 ; noteOn (noot4, velo); break; case noot5:; digitalWrite (beat6, HIGH) ; timvals[5] = time + velo; resortflag = 1 ; noteOn (noot5, velo); break; case noot6:; digitalWrite (beat7, HIGH) ; timvals[6] = time + velo; resortflag = 1 ; noteOn (noot6, velo); break; case noot7:; digitalWrite (beat8, HIGH) ; timvals[7] = time + velo; resortflag = 1 ; noteOn (noot7, velo); break; case noot8:; digitalWrite (beat9, HIGH) ; timvals[8] = time + velo; resortflag = 1 ; noteOn (noot8, velo); break; case noot9:; digitalWrite (beat10, HIGH) ; timvals[9] = time + velo; resortflag = 1 ; noteOn (noot9, velo); break; case noot10:; digitalWrite (beat11, HIGH) ; timvals[10] = time + velo; resortflag = 1 ; noteOn (noot10, velo); break; case noot11:; digitalWrite (beat12, HIGH) ; timvals[11] = time + velo; resortflag = 1 ; noteOn (noot11, velo); break; } noteAan = 255; // reset break; case keypresstatus:; if (notePres = 255){ notePres = inByte; } else { pres = inByte; // action code here... } notePres = 255; break; case ctrlstatus:; if (ctrl = 255){ ctrl = inByte; } else { value = inByte ; // action code here... } ctrl = 255; break; case progchangestatus:; prog = inByte; // action here... break; case aftertouchstatus:; aft = inByte; // action here break; case pitchbendstatus:; if (pblsb=255) { pblsb = inByte ; } else { pbmsb = inByte; // action here } pblsb = 255; break; } } } if (resortflag == 1){ idx = SortTimers (); } CheckTimers: // code as in the Basic version if (idx < NrTasks){ if (time >= nxt) { nxt = 0xFFFFFFFF; switch (idx){ case 0:; digitalWrite (beat1, LOW) ; timvals[0] = 0xFFFFFFFF; break; case 1:; digitalWrite (beat2, LOW) ; timvals[1] = 0xFFFFFFFF; break; case 2:; digitalWrite (beat3, LOW) ; timvals[2] = 0xFFFFFFFF; break; case 3:; digitalWrite (beat4, LOW) ; timvals[3] = 0xFFFFFFFF; break; case 4:; digitalWrite (beat5, LOW) ; timvals[4] = 0xFFFFFFFF; break; case 5:; digitalWrite (beat6, LOW) ; timvals[5] = 0xFFFFFFFF; break; case 6:; digitalWrite (beat7, LOW) ; timvals[6] = 0xFFFFFFFF; break; case 7:; digitalWrite (beat8, LOW) ; timvals[7] = 0xFFFFFFFF; break; case 8:; digitalWrite (beat9, LOW) ; timvals[8] = 0xFFFFFFFF; break; case 9:; digitalWrite (beat10, LOW) ; timvals[9] = 0xFFFFFFFF; break; case 10:; digitalWrite (beat11, LOW) ; timvals[10] = 0xFFFFFFFF; break; case 11:; digitalWrite (beat12, LOW) ; timvals[11] = 0xFFFFFFFF; break; } idx = SortTimers(); } else { // else code - check for timer overflow to eb done. } } /* // check to see if it's time to blink the LED; that is, if the difference // between the current time and last time you blinked the LED is bigger than // the interval at which you want to blink the LED. unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= interval) { // save the last time you blinked the LED previousMillis = currentMillis; // if the LED is off turn it on and vice-versa: // was: ledstate if (beat1state == LOW) { beat1state = HIGH; } else { beat1state = LOW; } // set the LED with the ledState of the variable: digitalWrite(beat1, beat1state); } */ /* some demo using midi out: play notes from F#-0 (0x1E) to F#-5 (0x5A): for (int note = 0x1E; note < 0x5A; note++) { //Note on channel 1 (0x90), some note value (note), middle velocity (0x45): noteOn(note, 0x45); delay(100); //Note on channel 1 (0x90), some note value (note), silent velocity (0x00): noteOff(note, 0x00); delay(100); } */ } // sluitaccolade hoofdlus byte GetMidiIn (){ /* should read the data from the midibuffer in our pic coding we did the serial-in in the ISR on this platform we do not know how to do interrupt handling. Zoals de kode nu is, heeft de midi buffer geen enkele zin. Dit zou eigenlijk in de interrupt routine moeten...: */ if (Serial.available()) { // in dit geval hebben we een byte beschikbaar... inByte = Serial.read(); // nu moeten we het naar de RingBuffer schrijven... inidx++ ; RingBuffer[inidx] = inByte; } // if no new data available, should return 255 if (inidx == outidx) { // in dit geval retourneren we 255 return 255; } else { outidx++; return RingBuffer[outidx]; } } byte SortTimers () { // sorting timers if resortflag is set idx = 255; nxt = 0xFFFFFFFF; do { /* basic code: ' look up the next smallest timer value in the Timvals array ' zoek de de volgende kleinste timer waarde: Set idx Set Nxt.31 ' nxt is set on entry. - for speed, we just set the highest bit For i = 0 To LastTask If Timvals[i] < Nxt Then Nxt = Timvals[i] ' NrTasks dword comparisons idx = i EndIf Next i Clear Resort_flag */ if (timvals[i] < nxt){ nxt = timvals[i] ; idx = i ; } }while (i < LastTask); resortflag = 0 ; return idx ; } // plays a MIDI note. Doesn't check to see that params are greater than 127, or that // data values are less than 127: void noteOn(byte pitch, byte velocity){ Serial.write(noteonstatus); Serial.write(pitch); Serial.write(velocity); } void noteOff(byte noot, byte release) { Serial.write(noteoffstatus); Serial.write(noot); Serial.write(release); } void keypress(byte noot, byte press) { Serial.write(keypresstatus); Serial.write(noot); Serial.write(press); } void controller(byte ctrl, byte value) { Serial.write(ctrlstatus); Serial.write(ctrl); Serial.write(value); }