MIDI keyboard on arduino micro note off error

Hello,

I use an Arduino micro to run a self made MIDI bass pedal board with individual transposable keys. It has 5 on/off pedals without any velocity functionality and for every pedal there are 2 buttons to decrement/increment the note number.
Additionally there are 2 buttons and 3 potentiometers to give cc changes into my DAW.

The code at the end does it's job very well. Thanks to the members of this forum that helped me last year :slight_smile:

Now i wanna change a specific detail...

Every time while playing one pedal of the board with one foot, when a second pedal is pressed with the other foot and the first pedal is not released yet, the note event of the first pedal is holded as long as the second pedal is released. Independendly of releasing the first pedal.

So in this "overlapping" playing situation it seems that there is no "note off" event created for the first pedal.
The result is that two notes sound together while only holding the second pedal.
I wanna change this!

I hope I could explain this detail in an understandable way...

One circumstance that I could life with could be to turn the code into a monophonic operating module. I don't need polyphonic ability in my bass pedal.
But what do i have to change in my code for that solution?
I'm a really beginner in arduino programming, so please when you have an idea talk to me how you would talk to a 10 year old child :wink:

Sascha

// Pedalboard
// 5 Pedale, die vordefinierte Midi-Noten abspielen, gedrückt: NoteOn, losgelassen: NoteOff
// Jedes der 5 Pedale hat zwei Tranpose-Buttons, wodurch in Halbtonschritten jedes Pedal eingestellt werden kann
// Zusätzlich 3 Potis und 2 Buttons

#include <Control_Surface.h> // Include the Control Surface library
 
// Instantiate a MIDI over USB interface.
USBMIDI_Interface midi;

// Abfrage der 3 Potis
CCPotentiometer pot1 { A1, {7, CHANNEL_1} }; // Poti 1 am Eingang A1 fungiert als MIDI-Controller 7
CCPotentiometer pot2 { A2, {8, CHANNEL_1} }; // Poti 2 am Eingang A2 fungiert als MIDI-Controller 8 
CCPotentiometer pot3 { A3, {9, CHANNEL_1} }; // Poti 3 am Eingang A3 fungiert als MIDI-Controller 9

using namespace MIDI_Notes;
 
// Array of transposers
Transposer<-12, +12> transposers[7]; // Definition von 7 Buttons

// Array of buttons using these transposers
Bankable::NoteButton buttons[] {
  {
    transposers[6], // use the 1. transposer for this button
    23,             // pin 23 mit Button 2 verbunden
    note(C, 7),     // MIDI address to send to when pressed/released
  }, {   
    transposers[5], // use the 1. transposer for this button
    22,             // pin 22 mit Button 1 verbunden
    note(D, 7),     // MIDI address to send to when pressed/released
  }, {   
    transposers[4], // use the 1. transposer for this button
    4,              // pin connected to the pedal Nr 1
    note(C, 3),     // MIDI address to send to when pressed/released
  }, {
    transposers[3], // use the 2. transposer for this button
    1,              // pin connected to the pedal Nr 2
    note(C, 3),     // MIDI address to send to when pressed/released
  }, {
    transposers[2], // Use the 3. transposer for this button
    3,              // pin connected to the pedal Nr 3
    note(C, 3),    // MIDI address to send to when pressed/released
  }, {
    transposers[1], // Use the 4. transposer for this button
    14,              // pin connected to the pedal Nr 4
    note(C, 3),    // MIDI address to send to when pressed/released
  }, {
    transposers[0], // Use the 5. transposer for this button
    15,              // pin connected to the pedal Nr 5
    note(C, 3),    // MIDI address to send to when pressed/released
  },
 };

 IncrementDecrementSelector<transposers[0].getNumberOfBanks()> selectors[] {
  {
    transposers[0], // control the 1. transposer
    {5, 6}, // using the switches connected to these pins (pin +, pin -)
    Wrap::Clamp,
  }, {
    transposers[1], // control the 2. transposer
    {7, 8}, // using the switches connected to these pins (pin +, pin -)
    Wrap::Clamp,
  }, {
    transposers[2], // control the 3. transposer
    {9, 10}, // using the switches connected to these pins (pin +, pin -)
    Wrap::Clamp,
  }, {
    transposers[3], // control the 4. transposer
    {11, 12}, // using the switches connected to these pins (pin +, pin -)
    Wrap::Clamp,
  }, {
    transposers[4], // control the 5. transposer
    {13, 18}, // using the switches connected to these pins (pin +, pin -)
    Wrap::Clamp,
  }
};

void setup() {
  Control_Surface.begin(); // Initialize Control Surface
}
 
void loop() {
  Control_Surface.loop(); // Update the Control Surface
}

Stop messing with daddy's MIDI peddle, that is how MIDI works. :grin:

The problem is that by using the control library you have painted yourself into a corner and as I understand it there is no way round this. Although there is a member that specialises in this library so he might be along in a short time. @PieterP

If you had not used this library then the solution would be simple. You would just issue a notes all off control message before you issued the midi not on message.

Hi Grumpy_mike,

thanks for your fast response! :slight_smile:

That makes me think of another solution since I found a function inside my DAW that gives me the possibility of individually tranposing my pedals.
This function inside my DAW would make the transpose function of my pedal board unnecessary.

So in that case I could reprogram my device without using the control library
and as you said that would give me a simple solution for my request.
Did I get that right?

Last year before I searched for the transpose function I had this simple code below on the micro.
Would my request be possible with that version?
And in simple steps how would I now put the notes all off control message inside this sketch? :open_mouth:

 */
#include <MIDI_Controller.h>

const uint8_t velocity = 0b1111111;
const uint8_t C4 = 60;

Digital button1(2, C4, 1, velocity);
Digital button2(3, Csharp4, 1, velocity);
Digital button3(4, D4, 1, velocity);
Digital button4(5, E, 1, velocity);
Digital button5(6, F, 1, velocity);

void setup() {

}

void loop() {
  // put your main code here, to run repeatedly:
  MIDI_Controller.refresh();

}

If you, @PieterP have a solution with my first sketch version, that would be also very fine. Because than I would have both possibilities, tranpsose function inside my DAW and inside my arduino...

Could you post the concrete output you get when doing this? You can replace USBMIDI_Interface by USBDebugMIDI_Interface in your code to print the MIDI messages to the serial monitor (115200 baud).
E.g.

press pedal 1
Note On    Channel: 1    Data 1: 0x3C    Data 2: 0x7F
press pedal 2
Note On    Channel: 1    Data 1: 0x3E    Data 2: 0x7F
release pedal 1
Note Off   Channel: 1    Data 1: 0x3C    Data 2: 0x7F
release pedal 2
Note Off   Channel: 1    Data 1: 0x3E    Data 2: 0x7F

I'm not sure I understand your description correctly, because the Bankable::NoteButton class sends a Note Off event as soon as the button is released, independent of any other buttons:

Either way, if you want more control over what's going on in your sketch, you can do the same thing manually:

// Example with only two buttons.

#include <Control_Surface.h>
template <class T, size_t N> constexpr size_t len(const T (&)[N]) { return N; }

USBMIDI_Interface midi;

Button buttons[] {2, 3};
const MIDIAddress notes[] {MIDI_Notes::C(4), MIDI_Notes::Db(4)};
MIDIAddress locked_addresses[len(buttons)];
using MyTransposer = Transposer<-12, +12>;
MyTransposer transposers[len(buttons)];
IncrementDecrementSelector<MyTransposer::getNumberOfBanks()> selectors[] {
    {transposers[0], {5, 6}, Wrap::Clamp},
    {transposers[1], {7, 8}, Wrap::Clamp},
};
const uint8_t velocity = 0x7F;

static_assert(len(notes) == len(buttons), "");
static_assert(len(selectors) == len(buttons), "");

void setup() {
    Control_Surface.begin();
    for (auto &btn : buttons)
        btn.begin();
}

void handle_button_press(size_t i) {
    // Compute the transposed address
    auto address = notes[i] + transposers[i].getTranspositionSemitones();
    // Check if any other buttons are already playing this same note
    auto already_playing = std::find(std::begin(locked_addresses),
                                     std::end(locked_addresses), address);
    // If not already playing, safe to send a note on message
    if (already_playing == std::end(locked_addresses)) {
        Control_Surface.sendNoteOn(address, velocity);
        // Save and lock the address to make sure we send a matching
        // note off message later, regardless of transposition
        locked_addresses[i] = address;
    }
}

void handle_button_release(size_t i) {
    // Get the address of the last note on message this button sent
    auto &address = locked_addresses[i];
    // If a note on message was previously sent
    if (address) {
        // Send the matching note off message
        Control_Surface.sendNoteOff(address, velocity);
        // and clear the locked address since it's no longer playing
        address = {};
    }
}

void loop() {
    Control_Surface.loop();

    for (size_t i = 0; i < len(buttons); ++i) {
        auto state = buttons[i].update();
        if (state == Button::Falling)
            handle_button_press(i);
        else if (state == Button::Rising)
            handle_button_release(i);
    }
}

Hi PieterP,

thanks for your remark! After I read this I put my device into my second PC with another rack configuration inside my DAW. And here I could figure out, that not my arduino is the problem, but one devicec in my virtual rack that didn't let through the note off message in my explained playing situation.
I switched off this virtual device and now my arduino does exactly what I want!
You really helped me (not in the expected way), but now the problem is solved :slight_smile:

Sascha

Glad to hear that it works now!

The manual approach I posted might still be of use if you want to avoid sending multiple consecutive note on/off messages for the same note. This can occur when multiple pedals are transposed to the same note. It's an edge case, but it could confuse the DAW.

I just tested that specific situation and found out that for my needs it is not necessary to transpose two pedals on the same note number. In my sketch I now programmed the 5 pedals in the row of a normal keyboard: C, C#, D, D#, E and that works for me exactly in the way I want. But nevertheless thanks for your deep knowledge, perhaps one time there is another question :slight_smile: