Components and supplies
Arduino Due
Apps and platforms
Arduino IDE
Project description
Code
MIDi signals from a pedal board
c_cpp
This code will turn an Arduino Due into a MIDI USB host, that will translate IR-sensor signals into MIDI note events.
1#include <MIDIUSB.h> 2#include <ArduinoQueue.h> 3 4/* 5Program for MIDIfying a 30-key organ pedal board with Arduino Due as Midi controller and IR-sensors TCRT5000 6Original code by Prof. David Musker, June 2020 7Adapted by Johannes Barkowsky, April 2022 8Free for use if source is acknowledged 9*/ 10 11//================================================================= 12const byte KeyboardSize = 30; // size of pedalboard here 30 keys, can be set to 27, 25 or whatever 13const byte StartNotePin = 2; // first digital input pin for note lowest note is set to pin 2, leaving digital pin 1 and 0 for other use 14 // so 30 pins now are pin3 to 32. StartNotePin can be set to 5 or whatever. It moves all pins up then. 15const byte NoteOff = B10000000; 16const byte NoteOn = B10010000; 17 18const byte MidiChannel = 3; // 3 sets Midi channel to channel 4; according to midi_Defs.h a 0 translates to MIDI_CHANNEL_OMNI, not sure though 19const byte LowestNote = 24; // Midi note number of lowest note, set to C1 20 21const byte KeyPin [KeyboardSize] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29}; 22 23//================================================================= 24unsigned long KeyReadTime = 0; 25unsigned long DebounceTime = 20; // 20 ms in real life for notes - might be set lower 26unsigned long LastMIDISentTime; // track when last MIDI message sent 27unsigned long MidiLag = 50; // delay between MIDI messages - greater than 25 ms and less than say 500 ms to avoid latency 28 29byte NoteCount = 0; // notes on 30byte NoteCountOld = 0; // notes on at last scan 31byte NotesChanged = 0; // note changes from last scan 32byte Velocity; 33 34bool KeyState[KeyboardSize] = {true}; // orig. false, but set here to true, because IR-sensors are HIGH, when pedals are up 35bool NoteState[KeyboardSize] [3] = {false}; // current and last note states and a change flag 36unsigned long KeyLastTime [KeyboardSize] = {0}; // times since last key transitions 37 38//Queue creation: 39ArduinoQueue<midiEventPacket_t> MidiChannelNoteQueue(20); // 20 here is the maximum number of items 40 41//================================================================= 42void setup () { 43delay(1000); // remedy for Arduino Due R3 startup reset issue - perhaps unnecessary 44 45Serial.begin(31250); // MIDI transmission speed is 31250 baud 46 for (int i = 0; i < KeyboardSize; i++) { 47 pinMode((StartNotePin+i), INPUT_PULLUP); // digital input pins with pullup resistor 48 } 49} 50 51//================================================================= 52void loop () { 53 54KeyRead(); 55NoteRead(); 56NoteSend(); 57TransmitMIDI(); 58 59} //main loop ends here 60 61//================================================================= 62void KeyRead() { 63KeyReadTime = millis(); 64 for (int i = 0; i < KeyboardSize; i++) { // note reading loop 65 KeyState[i] = !digitalRead(KeyPin[i]+StartNotePin); // indirect addressing via keypin array; keystate is here reversed (!), because IR-sensors are HIGH by default 66 } 67} 68 69//================================================================= 70void NoteRead() { 71NoteCountOld = NoteCount; 72NotesChanged = 0; 73 for (int i = 0; i < KeyboardSize; i++ ) { 74 if ((KeyReadTime - KeyLastTime [i]) > DebounceTime) { // only look at key state if it is more than DebounceTime since last change in note, otherwise skip 75 KeyLastTime [i] = KeyReadTime; 76 NoteState[i][3] = false; 77 NoteState[i][2] = NoteState[i][1]; // update previous note state 78 if (((KeyState[i] == false) && (NoteState[i][1] == true)) || ((KeyState[i] == true) && (NoteState[i][1] == false))){ // key state has changed 79 NoteState[i][1] = KeyState[i]; // update current note state 80 NoteState[i][3] = true; // set a changed state flag 81 NotesChanged++; 82 if (NoteState[i][1] == true) { // note is on 83 NoteCount++; // increment number of notes on 84 } 85 else {NoteCount--;} // decrement number of notes on 86 } 87 } 88 } 89} 90/* 91At the end of this loop, we have: 92NoteState[i][1] - current note states, 93NoteState[i][2] - previous note states, and 94NoteState[i][3] - change flags 95NoteCount - a count of the number of notes ON 96NotesChanged - a count of the number of notes changed 97*/ 98 99//================================================================= 100void NoteSend() { 101if (NotesChanged > 0) { // something has changed 102 for (int i = 0; i < (KeyboardSize); i++) { 103 if (NoteState[i][3] == true){ // note state has changed 104 if (NoteState[i][1] == true){ // note is ON 105 sendNoteOn((i+ LowestNote), MidiChannel, 127); // velocity is here fixed at 127 106 } 107 else { 108 sendNoteOff((i+ LowestNote), MidiChannel, 0);} // note is OFF 109 } 110 } 111 } 112} 113 114//================================================================= 115void sendNoteOn (byte Pitch, byte MidiChannel, byte Velocity) { 116 MIDImessage(NoteOn, MidiChannel, Pitch, Velocity); 117} 118 119//================================================================= 120void sendNoteOff (byte Pitch, byte MidiChannel, byte Velocity) { 121 MIDImessage(NoteOff, MidiChannel, Pitch, Velocity); 122} 123 124//================================================================= 125void MIDImessage(int byte1, int byte2, int byte3, int byte4) { // sends a 3 byte MIDI message via the serial port and queues it 126 // one could use the Arduino Midi library command instead 127 byte cmd = (byte1 + byte2); 128 Serial.write(cmd); // send note on or note off command 129 Serial.write(byte3); // send pitch 130 Serial.write(byte4); //send velocity 131 midiEventPacket_t midiMsg = {cmd >> 4, cmd, byte3, byte4}; 132 MidiChannelNoteQueue.enqueue(midiMsg); 133} 134 135//================================================================= 136void TransmitMIDI() { 137if ((micros() - LastMIDISentTime) > MidiLag) { // send only if the delay time has expired 138 if (!MidiChannelNoteQueue.isEmpty()) { // something is in the main channel queue 139 MidiUSB.sendMIDI(MidiChannelNoteQueue.dequeue()); 140 MidiUSB.flush(); 141 LastMIDISentTime = micros(); 142 } 143 } 144} 145
Comments
Only logged in users can leave comments