Midi learn on arduino

Hello.

I am looking for an easy way to have a midi learn function on arduino or rpi pico.

I would like to build a simple module with midi learn to assign any midi note input to at least 4 jacks that would only trigger from the note on note off that would have been learned.

The idea is to have a super simple way to sequence drum voices from any midi hardware.

Thanks a lot. You are the bests

It would be simple enough, a single button or even a midi Cc number to trigger the learning process, a count that reads all four inputs and then stops learning, store them in eeprom and recall on startup so you don’t have to relearn. 4 led outputs to indicate each trigger has been received.

Hi @didordure, a mashup of the ShedSynth MIDI ARPEGGIATOR and PROGRAMMABLE CLOCK DIVIDER modules might offer some hints about Arduino hardware/software for MIDI input and drum triggers.
Best of luck, and it’ll be great to see what you come up with.

#include <MIDI.h>

#include <EEPROM.h>

MIDI_CREATE_DEFAULT_INSTANCE();

const int learnButtonPin = 2;

const int learnLedPin = 3;

const int trigPins[4] = {4, 5, 6, 7};

const int ledPins[4] = {8, 9, 10, 11};

int learnedNotes[4];

bool learning = false;

int learnedCount = 0;

unsigned long lastTriggerTime[4];

const unsigned long triggerLength = 20; // milliseconds

void setup() {

pinMode(learnButtonPin, INPUT_PULLUP);

pinMode(learnLedPin, OUTPUT);

for (int i = 0; i < 4; i++) {

pinMode(trigPins\[i\], OUTPUT);

pinMode(ledPins\[i\], OUTPUT);

digitalWrite(trigPins\[i\], LOW);

digitalWrite(ledPins\[i\], LOW);

learnedNotes\[i\] = EEPROM.read(i);

}

MIDI.begin(MIDI_CHANNEL_OMNI);

MIDI.setHandleNoteOn(handleNoteOn);

MIDI.setHandleNoteOff(handleNoteOff);

}

void loop() {

MIDI.read();

// Handle Learn button

static bool lastButtonState = HIGH;

bool buttonState = digitalRead(learnButtonPin);

if (lastButtonState == HIGH && buttonState == LOW) {

startLearning();

}

lastButtonState = buttonState;

// Flash LED while learning

if (learning) {

static unsigned long lastBlink = 0;

static bool ledState = false;

if (millis() - lastBlink > 300) {

  ledState = !ledState;

  digitalWrite(learnLedPin, ledState);

  lastBlink = millis();

}

}

// Auto reset outputs after trigger length

unsigned long now = millis();

for (int i = 0; i < 4; i++) {

if (digitalRead(trigPins\[i\]) && now - lastTriggerTime\[i\] > triggerLength) {

  digitalWrite(trigPins\[i\], LOW);

  digitalWrite(ledPins\[i\], LOW);

}

}

}

void handleNoteOn(byte channel, byte note, byte velocity) {

if (learning) {

learnedNotes\[learnedCount++\] = note;

EEPROM.write(learnedCount - 1, note);

if (learnedCount >= 4) stopLearning();

} else {

for (int i = 0; i < 4; i++) {

  if (note == learnedNotes\[i\]) {

    triggerOutput(i);

  }

}

}

}

void handleNoteOff(byte channel, byte note, byte velocity) {

// Optional: could make trigger last for note duration instead of fixed time

}

void startLearning() {

learning = true;

learnedCount = 0;

digitalWrite(learnLedPin, HIGH);

}

void stopLearning() {

learning = false;

digitalWrite(learnLedPin, LOW);

}

void triggerOutput(int index) {

digitalWrite(trigPins[index], HIGH);

digitalWrite(ledPins[index], HIGH);

lastTriggerTime[index] = millis();

}

1 Like

The trigger output length is 20mS here, you can increase this or add more code to determine note off and then the trigger length will be how long you hold the notes.

Use a standard 6n138 opto isolator on the RX input for the midi in, hundreds of schematics around for that and a push button for learn to ground, if you need I can probably sketch it out quickly.

Wow. Thanks a lot.

Its a big step forward. I will try that in the next days and keep you informed.

:heart:

If you start with three back quotes and then insert some formatted text, it will be shown formatted. E.g. (using some arbitrary code):

        // Return the state of the delay used to hold back the gate.
        bool gateOnDelayFinishedOnce() {
            return(bs_gateOnDelay.finishedOnce());
        }

        // Start the delay function that is used to hold back the gate.
        void gateOnDelayStart() {
            bs_gateOnDelay.start();
        }

        // Set the duration for holding back the gate.
        void setGateOnDelay(unsigned long someDelayTime) {
            bs_gateOnDelay.set(someDelayTime);
        }