You could use the Control Surface library I maintain. You can find the links to the documentation and “Getting Started” guide here. It supports button matrices and potentiometers out of the box, it debounces the buttons and filters the analog inputs so you don't have to worry about that.
For the button matrix, see the NoteButtonMatrix.ino example. For the potentiometers, see the Control-Change-Potentiometer.ino example for the potentiometers.
For the resistor ladder of your buttons, you'll have to write some code yourself. You can send MIDI messages using
Control_Surface.sendNoteOn(notenumber, velocity); // default channel 1
Control_Surface.sendNoteOff(notenumber, velocity);
Control_Surface.sendNoteOn({notenumber, CHANNEL_10}, velocity); // specific channel
You can find a full list of MIDI sending functions in the MIDI_Sender documentation.
If you want to change the octave of the notes of the buttons, you can use the Bankable::NoteButtonMatrix with a Transposer that you control yourself in your main loop:
#include <Control_Surface.h>
USBMIDI_Interface midi;
// Transpose up to 2 octaves down or up (steps of 12 semitones = 1 octave)
Transposer<-2, +2> transposer{12};
// The note numbers corresponding to the buttons in the matrix
const AddressMatrix<4, 3> addresses = {{
{0x3C, 0x3D, 0x3E},
{0x3F, 0x40, 0x41},
{0x42, 0x43, 0x44},
{0x45, 0x46, 0x47},
}};
Bankable::NoteButtonMatrix<4, 3> buttons {
transposer, // determines the offset
{2, 3, 4, 5}, // row pins
{6, 7, 8}, // column pins
addresses, // address matrix
CHANNEL_1, // channel and optional cable number
};
void setup() {
Control_Surface.begin();
}
void loop() {
Control_Surface.loop();
if (/* some button pressed */) {
// You can set the transposition using transposer.select(i),
// where i is the index of the octave to select: i = 0 is
// the lowest octave (-2 in this case), i = 1 is the second
// octave (-1 here), i = 2 is the third octave (no transposition
// here) etc.
transposer.select(0); // selects transposition of -2
}
}
Pieter