barkowsky
Published © LGPL

MIDIfication of Church Organ Pedal Board

IR-sensors detect motion of pedal keys and Arduino Due generates MIDI messages.

AdvancedFull instructions provided2,790
MIDIfication of Church Organ Pedal Board

Things used in this project

Hardware components

Arduino Due
Arduino Due
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Code

MIDi signals from a pedal board

C/C++
This code will turn an Arduino Due into a MIDI USB host, that will translate IR-sensor signals into MIDI note events.
#include <MIDIUSB.h>
#include <ArduinoQueue.h>

/*
Program for MIDIfying a 30-key organ pedal board with Arduino Due as Midi controller and IR-sensors TCRT5000
Original code by Prof. David Musker, June 2020
Adapted by Johannes Barkowsky, April 2022
Free for use if source is acknowledged
*/

//=================================================================
const byte KeyboardSize = 30;                                  // size of pedalboard here 30 keys, can be set to 27, 25 or whatever
const 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
                                                               // so 30 pins now are pin3 to 32. StartNotePin can be set to 5 or whatever. It moves all pins up then.
const byte NoteOff = B10000000;
const byte NoteOn  = B10010000;

const 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
const byte LowestNote = 24;                                    // Midi note number of lowest note, set to C1

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

//=================================================================
unsigned long KeyReadTime = 0;
unsigned long DebounceTime = 20;                               // 20 ms in real life for notes - might be set lower
unsigned long LastMIDISentTime;                                // track when last MIDI message sent
unsigned long MidiLag = 50;                                    // delay between MIDI messages - greater than 25 ms and less than say 500 ms to avoid latency

byte NoteCount = 0;                                            // notes on
byte NoteCountOld = 0;                                         // notes on at last scan
byte NotesChanged = 0;                                         // note changes from last scan
byte Velocity;

bool KeyState[KeyboardSize] = {true};                          // orig. false, but set here to true, because IR-sensors are HIGH, when pedals are up
bool NoteState[KeyboardSize] [3] = {false};                    // current and last note states and a change flag
unsigned long KeyLastTime [KeyboardSize] = {0};                // times since last key transitions

//Queue creation:
ArduinoQueue<midiEventPacket_t> MidiChannelNoteQueue(20);      // 20 here is the maximum number of items

//=================================================================
void setup () {
delay(1000); // remedy for Arduino Due R3 startup reset issue - perhaps unnecessary

Serial.begin(31250); // MIDI transmission speed is 31250 baud
  for (int i = 0; i < KeyboardSize; i++) {
    pinMode((StartNotePin+i), INPUT_PULLUP); // digital input pins with pullup resistor
  }
}

//=================================================================
void loop () {

KeyRead();
NoteRead();
NoteSend();
TransmitMIDI();

} //main loop ends here

//=================================================================
void KeyRead() {
KeyReadTime = millis();
  for (int i = 0; i < KeyboardSize; i++) { // note reading loop
    KeyState[i] = !digitalRead(KeyPin[i]+StartNotePin); // indirect addressing via keypin array; keystate is here reversed (!), because IR-sensors are HIGH by default
  }
}

//=================================================================
void NoteRead() {
NoteCountOld = NoteCount;
NotesChanged = 0;
 for (int i = 0; i < KeyboardSize; i++ ) { 
  if ((KeyReadTime - KeyLastTime [i]) > DebounceTime) { // only look at key state if it is more than DebounceTime since last change in note, otherwise skip
    KeyLastTime [i] = KeyReadTime;
    NoteState[i][3] = false;
    NoteState[i][2] = NoteState[i][1]; // update previous note state
    if (((KeyState[i] == false) && (NoteState[i][1] == true)) || ((KeyState[i] == true) && (NoteState[i][1] == false))){ // key state has changed
        NoteState[i][1] = KeyState[i]; // update current note state
        NoteState[i][3] = true; // set a changed state flag
        NotesChanged++;
        if (NoteState[i][1] == true) { // note is on
         NoteCount++; // increment number of notes on
         }
        else {NoteCount--;} // decrement number of notes on
    }    
   }
  }
}
/*
At the end of this loop, we have:
NoteState[i][1] - current note states, 
NoteState[i][2] - previous note states, and 
NoteState[i][3] - change flags 
NoteCount - a count of the number of notes ON
NotesChanged - a count of the number of notes changed 
*/

//=================================================================
void NoteSend() {
if (NotesChanged > 0) { // something has changed
    for (int i = 0; i < (KeyboardSize); i++) {
      if (NoteState[i][3] == true){ // note state has changed
        if (NoteState[i][1] == true){ // note is ON
          sendNoteOn((i+ LowestNote), MidiChannel, 127); // velocity is here fixed at 127
          }
          else {
            sendNoteOff((i+ LowestNote), MidiChannel, 0);} // note is OFF
            }
      }
   }
}
  
//=================================================================
void sendNoteOn (byte Pitch, byte MidiChannel, byte Velocity) {
  MIDImessage(NoteOn, MidiChannel, Pitch, Velocity);
}

//=================================================================
void sendNoteOff (byte Pitch, byte MidiChannel, byte Velocity) {
  MIDImessage(NoteOff, MidiChannel, Pitch, Velocity);
}

//=================================================================
void MIDImessage(int byte1, int byte2, int byte3, int byte4) { // sends a 3 byte MIDI message via the serial port and queues it
  // one could use the Arduino Midi library command instead
  byte cmd = (byte1 + byte2);
  Serial.write(cmd); // send note on or note off command
  Serial.write(byte3); // send pitch
  Serial.write(byte4); //send velocity
  midiEventPacket_t midiMsg = {cmd >> 4, cmd, byte3, byte4};
  MidiChannelNoteQueue.enqueue(midiMsg);
}

//=================================================================
void TransmitMIDI() {
if ((micros() - LastMIDISentTime) > MidiLag) { // send only if the delay time has expired
   if (!MidiChannelNoteQueue.isEmpty()) { // something is in the main channel queue
      MidiUSB.sendMIDI(MidiChannelNoteQueue.dequeue());
      MidiUSB.flush();
      LastMIDISentTime = micros();
      }
  }
}

Credits

barkowsky

barkowsky

0 projects • 2 followers

Comments