#include "MIDIUSB.h" // see https://github.com/arduino-libraries/MIDIUSB #include <math.h> #define DEBUG #undef DEBUG #define TUNE #undef TUNE #define MIDI_IN_PIN 0 #define OVERALL_VELOCITY_PIN 10 #define VALVE_0 4 // PD4 // 6mm #define VALVE_1 5 // PC6 // 6mm #define VALVE_2 6 // PD7 // 6mm #define VALVE_3 7 // PE6 // 4mm #define VALVE_4 8 // PB4 // 4mm #define VALVE_5 9 // PB5 // 4mm #define VALVE_6 3 // PD0 // 4mm #define VALVE_7 2 // PD1 // 4mm #define NUM_VALVES 8 // array of currently active notes, index 0 is the oldest one static int16_t notes[NUM_VALVES] = {-1, -1, -1, -1, -1, -1, -1, -1}; // array of the length of the tone period required to create the tone static uint16_t periods[NUM_VALVES] = {0, 0, 0, 0, 0, 0, 0, 0}; // array of the current state of the valve pins (for tone periods) static uint8_t states[NUM_VALVES] = {0, 0, 0, 0, 0, 0, 0, 0}; // array for the period timer helpers static uint64_t lastNoteMicros[NUM_VALVES] = {0, 0, 0, 0, 0, 0, 0, 0}; // array of the valve pin numbers static const uint8_t valves[NUM_VALVES] = {VALVE_0, VALVE_1, VALVE_2, VALVE_3, VALVE_4, VALVE_5, VALVE_6, VALVE_7}; static float noteToMidi[128]; static uint16_t noteToPeriod[128]; static uint16_t noteToPeriodDefault[128]; #ifdef TUNE uint8_t lastNote = 0; #endif bool enableTuning = true; // MIDI parameter overview // First parameter is the event type (0x0B = control change). // Second parameter is the event type, combined with the channel. // Third parameter is the control number number (0-119). // Fourth parameter is the control value (0-127). void setup() { #ifdef DEBUG Serial.begin(115200); Serial.println("Debug activated"); #endif #ifdef TUNE Serial.begin(115200); Serial.println("Tuning activated"); #endif Serial1.begin(31250); // MIDI baud rate // initialize valve output pins for (uint8_t i = 0; i < NUM_VALVES; i++) { pinMode(valves[i], OUTPUT); } pinMode(OVERALL_VELOCITY_PIN, OUTPUT); noteToMidi[127] = 12543.85; noteToPeriod[127] = 40; noteToPeriodDefault[127] = 40; noteToMidi[126] = 11839.82; noteToPeriod[126] = 42; noteToPeriodDefault[126] = 42; noteToMidi[125] = 11175.30; noteToPeriod[125] = 45; noteToPeriodDefault[125] = 45; noteToMidi[124] = 10548.08; noteToPeriod[124] = 47; noteToPeriodDefault[124] = 47; noteToMidi[123] = 9956.06; noteToPeriod[123] = 50; noteToPeriodDefault[123] = 50; noteToMidi[122] = 9397.27; noteToPeriod[122] = 53; noteToPeriodDefault[122] = 53; noteToMidi[121] = 8869.84; noteToPeriod[121] = 56; noteToPeriodDefault[121] = 56; noteToMidi[120] = 8372.02; noteToPeriod[120] = 60; noteToPeriodDefault[120] = 60; noteToMidi[119] = 7902.13; noteToPeriod[119] = 63; noteToPeriodDefault[119] = 63; noteToMidi[118] = 7458.62; noteToPeriod[118] = 67; noteToPeriodDefault[118] = 67; noteToMidi[117] = 7040.00; noteToPeriod[117] = 71; noteToPeriodDefault[117] = 71; noteToMidi[116] = 6644.88; noteToPeriod[116] = 75; noteToPeriodDefault[116] = 75; noteToMidi[115] = 6271.93; noteToPeriod[115] = 80; noteToPeriodDefault[115] = 80; noteToMidi[114] = 5919.91; noteToPeriod[114] = 84; noteToPeriodDefault[114] = 84; noteToMidi[113] = 5587.65; noteToPeriod[113] = 89; noteToPeriodDefault[113] = 89; noteToMidi[112] = 5274.04; noteToPeriod[112] = 95; noteToPeriodDefault[112] = 95; noteToMidi[111] = 4978.03; noteToPeriod[111] = 100; noteToPeriodDefault[111] = 100; noteToMidi[110] = 4698.64; noteToPeriod[110] = 106; noteToPeriodDefault[110] = 106; noteToMidi[109] = 4434.92; noteToPeriod[109] = 113; noteToPeriodDefault[109] = 113; noteToMidi[108] = 4186.01; noteToPeriod[108] = 119; noteToPeriodDefault[108] = 119; noteToMidi[107] = 3951.07; noteToPeriod[107] = 127; noteToPeriodDefault[107] = 127; noteToMidi[106] = 3729.31; noteToPeriod[106] = 134; noteToPeriodDefault[106] = 134; noteToMidi[105] = 3520.00; noteToPeriod[105] = 142; noteToPeriodDefault[105] = 142; noteToMidi[104] = 3322.44; noteToPeriod[104] = 150; noteToPeriodDefault[104] = 150; noteToMidi[103] = 3135.96; noteToPeriod[103] = 159; noteToPeriodDefault[103] = 159; noteToMidi[102] = 2959.96; noteToPeriod[102] = 169; noteToPeriodDefault[102] = 169; noteToMidi[101] = 2793.83; noteToPeriod[101] = 179; noteToPeriodDefault[101] = 179; noteToMidi[100] = 2637.02; noteToPeriod[100] = 190; noteToPeriodDefault[100] = 190; noteToMidi[99] = 2489.02; noteToPeriod[99] = 201; noteToPeriodDefault[99] = 201; noteToMidi[98] = 2349.32; noteToPeriod[98] = 213; noteToPeriodDefault[98] = 213; noteToMidi[97] = 2217.46; noteToPeriod[97] = 225; noteToPeriodDefault[97] = 225; noteToMidi[96] = 2093.00; noteToPeriod[96] = 239; noteToPeriodDefault[96] = 239; noteToMidi[95] = 1975.53; noteToPeriod[95] = 253; noteToPeriodDefault[95] = 253; noteToMidi[94] = 1864.66; noteToPeriod[94] = 268; noteToPeriodDefault[94] = 268; noteToMidi[93] = 1760.00; noteToPeriod[93] = 284; noteToPeriodDefault[93] = 284; noteToMidi[92] = 1661.22; noteToPeriod[92] = 301; noteToPeriodDefault[92] = 301; noteToMidi[91] = 1567.98; noteToPeriod[91] = 319; noteToPeriodDefault[91] = 319; noteToMidi[90] = 1479.98; noteToPeriod[90] = 338; noteToPeriodDefault[90] = 338; noteToMidi[89] = 1396.91; noteToPeriod[89] = 358; noteToPeriodDefault[89] = 358; noteToMidi[88] = 1318.51; noteToPeriod[88] = 379; noteToPeriodDefault[88] = 379; noteToMidi[87] = 1244.51; noteToPeriod[87] = 402; noteToPeriodDefault[87] = 402; noteToMidi[86] = 1174.66; noteToPeriod[86] = 426; noteToPeriodDefault[86] = 426; noteToMidi[85] = 1108.73; noteToPeriod[85] = 451; noteToPeriodDefault[85] = 451; noteToMidi[84] = 1046.50; noteToPeriod[84] = 478; noteToPeriodDefault[84] = 478; noteToMidi[83] = 987.77; noteToPeriod[83] = 506; noteToPeriodDefault[83] = 506; noteToMidi[82] = 932.33; noteToPeriod[82] = 536; noteToPeriodDefault[82] = 536; noteToMidi[81] = 880.00; noteToPeriod[81] = 568; noteToPeriodDefault[81] = 568; noteToMidi[80] = 830.61; noteToPeriod[80] = 602; noteToPeriodDefault[80] = 602; noteToMidi[79] = 783.99; noteToPeriod[79] = 638; noteToPeriodDefault[79] = 638; noteToMidi[78] = 739.99; noteToPeriod[78] = 676; noteToPeriodDefault[78] = 676; noteToMidi[77] = 698.46; noteToPeriod[77] = 716; noteToPeriodDefault[77] = 716; noteToMidi[76] = 659.26; noteToPeriod[76] = 758; noteToPeriodDefault[76] = 758; noteToMidi[75] = 622.25; noteToPeriod[75] = 804; noteToPeriodDefault[75] = 804; noteToMidi[74] = 587.33; noteToPeriod[74] = 851; noteToPeriodDefault[74] = 851; noteToMidi[73] = 554.37; noteToPeriod[73] = 902; noteToPeriodDefault[73] = 902; noteToMidi[72] = 523.25; noteToPeriod[72] = 956; noteToPeriodDefault[72] = 956; noteToMidi[71] = 493.88; noteToPeriod[71] = 995; // 995 noteToPeriodDefault[71] = 1012; noteToMidi[70] = 466.16; noteToPeriod[70] = 1073; noteToPeriodDefault[70] = 1073; noteToMidi[69] = 440.00; noteToPeriod[69] = 1136; noteToPeriodDefault[69] = 1136; noteToMidi[68] = 415.30; noteToPeriod[68] = 1204; noteToPeriodDefault[68] = 1204; noteToMidi[67] = 392.00; noteToPeriod[67] = 1276; noteToPeriodDefault[67] = 1276; noteToMidi[66] = 369.99; noteToPeriod[66] = 1351; noteToPeriodDefault[66] = 1351; noteToMidi[65] = 349.23; noteToPeriod[65] = 1403; // 1403 noteToPeriodDefault[65] = 1432; noteToMidi[64] = 329.63; noteToPeriod[64] = 1480; // 1480 noteToPeriodDefault[64] = 1517; noteToMidi[63] = 311.13; noteToPeriod[63] = 1560; // 1560 noteToPeriodDefault[63] = 1607; noteToMidi[62] = 293.66; noteToPeriod[62] = 1640; //1640 noteToPeriodDefault[62] = 1703; noteToMidi[61] = 277.18; noteToPeriod[61] = 1791; // 1791 noteToPeriodDefault[61] = 1804; noteToMidi[60] = 261.63; noteToPeriod[60] = 1872; // 1872 noteToPeriodDefault[60] = 1911; noteToMidi[59] = 246.94; noteToPeriod[59] = 2000; // 2000 noteToPeriodDefault[59] = 2025; noteToMidi[58] = 233.08; noteToPeriod[58] = 2106; // 2106 noteToPeriodDefault[58] = 2145; noteToMidi[57] = 220.00; noteToPeriod[57] = 2257; // 2257 noteToPeriodDefault[57] = 2273; noteToMidi[56] = 207.65; noteToPeriod[56] = 2408; // 2408 noteToPeriodDefault[56] = 2408; noteToMidi[55] = 196.00; noteToPeriod[55] = 2498; // 2498 noteToPeriodDefault[55] = 2551; noteToMidi[54] = 185.00; noteToPeriod[54] = 2651; // 2651 noteToPeriodDefault[54] = 2703; noteToMidi[53] = 174.61; noteToPeriod[53] = 2808; // 2808 noteToPeriodDefault[53] = 2864; noteToMidi[52] = 164.81; noteToPeriod[52] = 2969; // 2969 noteToPeriodDefault[52] = 3034; noteToMidi[51] = 155.56; noteToPeriod[51] = 3192; // 3192 noteToPeriodDefault[51] = 3214; noteToMidi[50] = 146.83; noteToPeriod[50] = 3355; // 3355 noteToPeriodDefault[50] = 3405; noteToMidi[49] = 138.59; noteToPeriod[49] = 3580; // 3580 noteToPeriodDefault[49] = 3608; noteToMidi[48] = 130.81; noteToPeriod[48] = 3810; // 3810 noteToPeriodDefault[48] = 3822; noteToMidi[47] = 123.47; noteToPeriod[47] = 4000; // 4000 noteToPeriodDefault[47] = 4050; noteToMidi[46] = 116.54; noteToPeriod[46] = 4250; // 4250 noteToPeriodDefault[46] = 4290; noteToMidi[45] = 110.00; noteToPeriod[45] = 4515; // 4515 noteToPeriodDefault[45] = 4545; noteToMidi[44] = 103.83; noteToPeriod[44] = 4756; // 4756 noteToPeriodDefault[44] = 4816; noteToMidi[43] = 98.00; noteToPeriod[43] = 5060; // 5060 noteToPeriodDefault[43] = 5102; noteToMidi[42] = 92.50; noteToPeriod[42] = 5371; // 5371 noteToPeriodDefault[42] = 5405; noteToMidi[41] = 87.31; noteToPeriod[41] = 5684; // 5684 noteToPeriodDefault[41] = 5727; noteToMidi[40] = 82.41; noteToPeriod[40] = 6000; // 6000 noteToPeriodDefault[40] = 6067; noteToMidi[39] = 77.78; noteToPeriod[39] = 6385; // 6385 noteToPeriodDefault[39] = 6428; noteToMidi[38] = 73.42; noteToPeriod[38] = 6775; // 6775 noteToPeriodDefault[38] = 6810; noteToMidi[37] = 69.30; noteToPeriod[37] = 7130; // 7130 noteToPeriodDefault[37] = 7215; noteToMidi[36] = 65.41; noteToPeriod[36] = 7644; // 7625 noteToPeriodDefault[36] = 7644; noteToMidi[35] = 61.74; noteToPeriod[35] = 8098; noteToPeriodDefault[35] = 8098; noteToMidi[34] = 58.27; noteToPeriod[34] = 8581; noteToPeriodDefault[34] = 8581; noteToMidi[33] = 55.00; noteToPeriod[33] = 9091; noteToPeriodDefault[33] = 9091; noteToMidi[32] = 51.91; noteToPeriod[32] = 9632; noteToPeriodDefault[32] = 9632; noteToMidi[31] = 49.00; noteToPeriod[31] = 10204; noteToPeriodDefault[31] = 10204; noteToMidi[30] = 46.25; noteToPeriod[30] = 10811; noteToPeriodDefault[30] = 10811; noteToMidi[29] = 43.65; noteToPeriod[29] = 11455; noteToPeriodDefault[29] = 11455; noteToMidi[28] = 41.20; noteToPeriod[28] = 12136; noteToPeriodDefault[28] = 12136; noteToMidi[27] = 38.89; noteToPeriod[27] = 12857; noteToPeriodDefault[27] = 12857; noteToMidi[26] = 36.71; noteToPeriod[26] = 13620; noteToPeriodDefault[26] = 13620; noteToMidi[25] = 34.65; noteToPeriod[25] = 14430; noteToPeriodDefault[25] = 14430; noteToMidi[24] = 32.70; noteToPeriod[24] = 15291; noteToPeriodDefault[24] = 15291; noteToMidi[23] = 30.87; noteToPeriod[23] = 16197; noteToPeriodDefault[23] = 16197; noteToMidi[22] = 29.14; noteToPeriod[22] = 17159; noteToPeriodDefault[22] = 17159; noteToMidi[21] = 27.50; noteToPeriod[21] = 18182; noteToPeriodDefault[21] = 18182; noteToMidi[20] = 25.96; noteToPeriod[20] = 19260; noteToPeriodDefault[20] = 19260; noteToMidi[19] = 24.50; noteToPeriod[19] = 20408; noteToPeriodDefault[19] = 20408; noteToMidi[18] = 23.12; noteToPeriod[18] = 21626; noteToPeriodDefault[18] = 21626; noteToMidi[17] = 21.83; noteToPeriod[17] = 22904; noteToPeriodDefault[17] = 22904; noteToMidi[16] = 20.60; noteToPeriod[16] = 24272; noteToPeriodDefault[16] = 24272; noteToMidi[15] = 19.45; noteToPeriod[15] = 25707; noteToPeriodDefault[15] = 25707; noteToMidi[14] = 18.35; noteToPeriod[14] = 27248; noteToPeriodDefault[14] = 27248; noteToMidi[13] = 17.32; noteToPeriod[13] = 28868; noteToPeriodDefault[13] = 28868; noteToMidi[12] = 16.35; noteToPeriod[12] = 30581; noteToPeriodDefault[12] = 30581; noteToMidi[11] = 15.43; noteToPeriod[11] = 32404; noteToPeriodDefault[11] = 32404; noteToMidi[10] = 14.57; noteToPeriod[10] = 34317; noteToPeriodDefault[10] = 34317; noteToMidi[9] = 13.75; noteToPeriod[9] = 36364; noteToPeriodDefault[9] = 36364; noteToMidi[8] = 12.98; noteToPeriod[8] = 38521; noteToPeriodDefault[8] = 38521; noteToMidi[7] = 12.25; noteToPeriod[7] = 40816; noteToPeriodDefault[7] = 40816; noteToMidi[6] = 11.56; noteToPeriod[6] = 43253; noteToPeriodDefault[6] = 43253; noteToMidi[5] = 10.91; noteToPeriod[5] = 45830; noteToPeriodDefault[5] = 45830; noteToMidi[4] = 10.30; noteToPeriod[4] = 48544; noteToPeriodDefault[4] = 48544; noteToMidi[3] = 9.72; noteToPeriod[3] = 51440; noteToPeriodDefault[3] = 51440; noteToMidi[2] = 9.18; noteToPeriod[2] = 54466; noteToPeriodDefault[2] = 54466; noteToMidi[1] = 8.66; noteToPeriod[1] = 57737; noteToPeriodDefault[1] = 57737; noteToMidi[0] = 8.18; noteToPeriod[0] = 61125; noteToPeriodDefault[0] = 61125; } // calculates the frequency according to the midi note number uint16_t midi2frq(uint8_t number) { #ifdef DEBUG Serial.print(number); Serial.print(" | "); #endif //double _n = (double)((double)number - (double)69); // #ifdef DEBUG // Serial.print(_n); // Serial.print(" | "); // #endif double d = 0; /*if (number < 48) { // c3 d = pow((double)2, _n / (double)12) * (double)440 * (double) 1.015; } else if (number < 57) { // c4 d = pow((double)2, _n / (double)12) * (double)440 * (double) 1.02; } else if (number < 60) { // c5 d = pow((double)2, _n / (double)12) * (double)440 * (double) 1.025; } else { d = pow((double)2, _n / (double)12) * (double)440 * (double) 1.04; }*/ //d = pow((double)2, _n / (double)12) * (double)440; d = noteToMidi[number]; #ifdef DEBUG Serial.println(d); #endif return (uint16_t)d; } // puts the note into the notes array or removes it if already present int8_t putNote(int8_t note, uint8_t state) { // set note for (uint8_t i = 0; i < NUM_VALVES; i++) { if (notes[i] == -1 && state == 1) { // note on notes[i] = note; return i; } else if (notes[i] == note && state == 0) { // note off //notes[i] = -1; for (uint8_t j = i; j < NUM_VALVES - 1; j++) { notes[j] = notes[j + 1]; periods[j] = periods[j + 1]; states[j] = states[j + 1]; lastNoteMicros[j] = lastNoteMicros[j + 1]; } notes[NUM_VALVES - 1] = -1; periods[NUM_VALVES - 1] = 0; states[NUM_VALVES - 1] = 0; lastNoteMicros[NUM_VALVES - 1] = 0; return i; } } if (state == 0) { return -1; } // remove oldest note and shift other notes for (int8_t i = 0; i < NUM_VALVES - 1; i++) { notes[i] = notes[i + 1]; } notes[NUM_VALVES - 1] = note; return NUM_VALVES - 1; } int counter = 0; // 0 = undefined // 1 = read status // 2 = read data 1 // 3 = read data 2 // 4 = handle note on/off int parseState = 1; uint8_t statusByte; uint8_t dataByte1; uint8_t dataByte2; void loop() { // handle Hardware MIDI IN if (Serial1.available() > 0 && parseState != 4) { uint8_t inp = Serial1.read(); if (inp == 0b11111110) { // ignore heartbeats #ifdef DEBUG Serial.println("ignoring active sensing"); #endif } else { switch (parseState) { case 0: break; case 1: #ifdef DEBUG Serial.print("parseState 1 inp: "); Serial.println(inp, BIN); #endif if ((inp & 0b10000000) == 0b10000000) { // first bit 1 => status byte statusByte = inp; parseState = 2; break; } else { #ifdef DEBUG Serial.print("Unexpected data byte (instead of status byte), assuming data byte 1: "); Serial.println(inp, BIN); #endif } case 2: #ifdef DEBUG Serial.print("parseState 2 inp: "); Serial.println(inp, BIN); #endif if ((inp & 0b10000000) != 0b10000000) { // first bit 0 => data byte parseState = 3; dataByte1 = inp; } else { #ifdef DEBUG Serial.print("Unexpected status byte (instead of data byte 1): "); Serial.println(inp, BIN); #endif parseState = 1; } break; case 3: #ifdef DEBUG Serial.print("parseState 3 inp: "); Serial.println(inp, BIN); #endif if ((inp & 0b10000000) != 0b10000000) { // first bit 0 => data byte parseState = 4; dataByte2 = inp; } else { #ifdef DEBUG Serial.print("Unexpected status byte (instead of data byte 2): "); Serial.println(inp, BIN); #endif parseState = 1; } break; default: Serial.print("parseState "); Serial.print(parseState); Serial.print(". Unexpected byte in status X inp: "); Serial.println(inp, BIN); break; } } } if (counter == 100) { counter = 0; // handle USB MIDI IN midiEventPacket_t rx; rx = MidiUSB.read(); if (rx.byte1 != 0) { #ifdef DEBUG Serial.print("Reading from USB MIDI: statusByte "); Serial.print(rx.byte1, BIN); Serial.print(" dataByte1 "); Serial.print(rx.byte2, BIN); Serial.print(" dataByte2 "); Serial.println(rx.byte3, BIN); char buffer[256]; sprintf(buffer, "header: 0x%X b1: %d b2: %d b3: %d", rx.header, rx.byte1, rx.byte2, rx.byte3); Serial.println(buffer); #endif if ((rx.byte1 & 0xF0) == 0x90 && rx.byte3 > 0 && rx.byte2 < 84) { // handle note on // 84 = C6 = 1046.5Hz int8_t noteIdx = putNote(rx.byte2, 1); //uint16_t frq = midi2frq(rx.byte2); //periods[noteIdx] = 1000000 / frq / 2; if (rx.byte2 == 72) { // todo: made this a CC event enableTuning = !enableTuning; } if (enableTuning) { periods[noteIdx] = noteToPeriod[rx.byte2]; } else { periods[noteIdx] = noteToPeriodDefault[rx.byte2]; } #ifdef DEBUG Serial.println("ONNNNNNN " + String(rx.byte2) + " @ " + String(noteIdx)); #endif } else if (((rx.byte1 & 0xF0) == 0x80 || ((rx.byte1 & 0xF0) == 0x90 && rx.byte3 == 0)) && rx.byte2 < 84) { // handle note off #ifdef DEBUG int8_t idx = 0; #endif for (int8_t i = 0; i < NUM_VALVES; i++) { if (notes[i] != -1 && notes[i] == rx.byte2) { putNote(rx.byte2, 0); //periods[i] = 0; #ifdef DEBUG idx = i; #endif #ifdef TUNE lastNote = rx.byte2; Serial.println("Tune note " + String(rx.byte2) + " current value: " + String(noteToPeriod[rx.byte2]) + " (" + String(noteToMidi[rx.byte2]) + "Hz)" + " (Default: " + noteToPeriodDefault[rx.byte2] + ")"); #endif break; } } #ifdef DEBUG Serial.println("OFFFFFFFF " + String(rx.byte2) + " @ " + String(idx)); #endif } else if ((rx.byte1 & 0xF0) == 0xB0) { // handle CC if (rx.byte2 == 7) { // CC 7 = Volume analogWrite(OVERALL_VELOCITY_PIN, 255 - (rx.byte3 * 2)); } } #ifdef DEBUG else { char buffer[256]; sprintf(buffer, "header: 0x%X b1: %d b2: %d b3: %d", rx.byte1, rx.byte2, rx.byte3); Serial.println(buffer); } #endif } // handle hardware MIDI IN // handle relevant data if (parseState == 4) { #ifdef DEBUG Serial.print("Reading from Serial1: statusByte "); Serial.print(statusByte, BIN); Serial.print(" dataByte1 "); Serial.print(dataByte1, BIN); Serial.print(" dataByte2 "); Serial.println(dataByte2, BIN); #endif if ((statusByte & 0xF0) == 0x90 && dataByte2 > 0 && dataByte1 < 84) { // handle note on int8_t noteIdx = putNote(dataByte1, 1); if (dataByte1 == 72) { // todo make this a CC event enableTuning = !enableTuning; } if (enableTuning) { periods[noteIdx] = noteToPeriod[dataByte1]; } else { periods[noteIdx] = noteToPeriodDefault[dataByte1]; } #ifdef DEBUG Serial.println("ONNNNNNN " + String(dataByte1) + " @ " + String(noteIdx)); #endif } else if ((statusByte & 0xF0) == 0x80 || ((statusByte & 0xF0) == 0x90 && dataByte2 == 0) && dataByte1 < 84) { // handle note off #ifdef DEBUG int8_t idx = 0; #endif for (int8_t i = 0; i < NUM_VALVES; i++) { if (notes[i] != -1 && notes[i] == dataByte1) { putNote(dataByte1, 0); #ifdef DEBUG idx = i; #endif #ifdef TUNE lastNote = dataByte1; Serial.println("Tune note " + String(dataByte1) + " current value: " + String(noteToPeriod[dataByte1]) + " (" + String(noteToMidi[dataByte1]) + "Hz)" + " (Default: " + noteToPeriodDefault[dataByte1] + ")"); #endif break; } } #ifdef DEBUG Serial.println("OFFFFFFFF " + String(dataByte1) + " @ " + String(idx)); #endif } parseState = 1; } } else { counter++; } // get current microseconds uint64_t nowMicros = micros(); // check for notes that require attention, either note on or note off for (uint8_t i = 0; i < NUM_VALVES; i++) { if (lastNoteMicros[i] + periods[i] < nowMicros && notes[i] != -1) { lastNoteMicros[i] = nowMicros; //states[i] ^= 1; //digitalWrite(valves[i], states[i]); switch (i) { case 0: // PD4 // 6mm PORTD ^= B00010000; break; case 1: // PC6 // 6mm PORTC ^= B01000000; break; case 2: // PD7 // 6mm PORTD ^= B10000000; break; case 3: // PE6 // 4mm PORTE ^= B01000000; break; case 4: // PB4 // 4mm PORTB ^= B00010000; break; case 5: // PB5 // 4mm PORTB ^= B00100000; break; case 6: // PD0 // 4mm PORTD ^= B00000001; break; case 7: // PD1 // 4mm PORTD ^= B00000010; break; } } else if (notes[i] == -1) { // be sure to end notes that should be ended digitalWrite(valves[i], LOW); } } #ifdef TUNE if (Serial.available()) { String input = Serial.readString(); if (input.startsWith("+")) { Serial.println("inc"); noteToPeriod[lastNote] = noteToPeriod[lastNote] + 1; } else if (input.startsWith("-")) { Serial.println("dec"); noteToPeriod[lastNote] = noteToPeriod[lastNote] - 1; } else { noteToPeriod[lastNote] = (uint16_t) input.toInt(); } Serial.println("Set note " + String(lastNote) + " to " + String(noteToPeriod[lastNote]) + " (Default: " + noteToPeriodDefault[lastNote] + ")"); } #endif }