The ManyAddresses elements are objects that can send MIDI output to one of multiple addresses. You can select which address is used by using a Bank.
For example, if you have a button that you can use as either a "solo" button, a "mute" button or an "record" button, you could use something like this:
Bankable::ManyAddresses::NoteButton<3> myMultiFunctionButton = {
bank,
5, // input pin
{
MCU::SOLO_1, // Address to use when 1st bank is selected
MCU::MUTE_1, // Address to use when 2nd bank is selected
MCU::REC_RDY_1, // Address to use when 3rd bank is selected
},
};
If you want to send different kinds of messages (Control Change and Program Change), there are two main approaches.
- Create both a CCButton and a PCButton, and give them no address for some of the banks:
#include <Control_Surface.h>
// MIDI interface to use
USBDebugMIDI_Interface midi;
// Instantiate three Banks
Bank<3> bank;
// Instantiate a Bank selector to control which one of the three Banks is active.
IncrementDecrementSelector<3> selector = {
bank, // Bank to manage
{2, 3}, // push button pins (increment, decrement)
Wrap::Wrap, // Wrap around
};
// An empty address never sends any MIDI data
const MIDIAddress disabled = {};
Bankable::ManyAddresses::CCButton<3> myMultiFunctionButtonCC = {
bank,
5, // input pin
{
0x07, // Controller Nº to use when 1st bank is selected
0x0A, // Controller Nº to use when 2nd bank is selected
disabled, // Disable this button when 3rd bank is selected
},
};
Bankable::ManyAddresses::PCButton<3> myMultiFunctionButtonPC = {
bank,
5, // input pin (same one as the CCButton)
{
disabled, // Disable this button when 1st bank is selected
disabled, // Disable this button when 2nd bank is selected
0x0D, // Program Nº to use when 3rd bank is selected
},
};
void setup() {
Control_Surface.begin();
}
void loop() {
Control_Surface.loop();
}
This piece of code has the following behavior:
If you press the button when the first bank is selected, it sends a MIDI CC message for controller 0x07,
if you press the button when the second bank is selected, it sends a MIDI CC message for controller 0x0A,
if you press the button when the third bank is selected, it sends a MIDI PC message for program 0x0D.
- Write the code manually, without using the existing CCButton and PCButton classes. This requires a bit more code, but it's a cleaner solution in the long run.
#include <Control_Surface.h>
// --------------------------------------------------------------------------------------- //
// Class for a single button that sends either a MIDI control change or MIDI program
// change when pressed. A bank is used to select which address (controller number or
// program number) is used, and whether the message type is control change or program
// change.
//
// @tparam NumAddr
// The number of different addresses this button supports. This is the same as
// the number of banks.
template <uint8_t NumAddr>
class CC_PC_Button : public MIDIOutputElement {
public:
// Constructor.
//
// @param bank
// The bank that determines the address that the MIDI messgaes of this button
// will be sent to, and what type of MIDI messages is used.
// @param pin
// The digital input pin with the push button or switch connected.
// @param addresses
// A list of addresses to use. The first element is the address used when the
// first bank is selected, the second element is used when the second bank is
// selected, etc.
// @param types
// The MIDI message types to use. The first element is the message type used
// when the first bank is selected, the second element is used when the second
// bank is selected, etc.
// Allowed values are ‘MIDIMessageType::CONTROL_CHANGE’ and
// ‘MIDIMessageType::PROGRAM_CHANGE’
CC_PC_Button(const Bank<NumAddr> &bank,
pin_t pin,
const Array<MIDIAddress, NumAddr> &addresses,
const Array<MIDIMessageType, NumAddr> &types)
: address{bank, addresses}, button{pin}, types{types} {}
// Initialization (called in ‘setup’)
void begin() override {
button.begin();
}
// Continuously read the button state and send MIDI messages (called in ‘loop’)
void update() override {
Button::State state = button.update();
// Button is pressed
if (state == Button::Falling) {
address.lock(); // prevent changing of the bank while the button is pressed
auto activeBank = address.getSelection();
MIDIMessageType type = types[activeBank]; // get the type for the active bank
// Send the appropriate MIDI message
if (type == MIDIMessageType::CONTROL_CHANGE)
Control_Surface.sendCC(address.getActiveAddress(), 0x7F);
else if (type == MIDIMessageType::PROGRAM_CHANGE)
Control_Surface.sendPC(address.getActiveAddress());
}
// Button is released
else if (state == Button::Rising) {
auto activeBank = address.getSelection();
MIDIMessageType type = types[activeBank]; // get the type for the active bank
// Send the appropriate MIDI message
if (type == MIDIMessageType::CONTROL_CHANGE)
Control_Surface.sendCC(address.getActiveAddress(), 0x00);
else if (type == MIDIMessageType::PROGRAM_CHANGE)
; // nothing
address.unlock(); // now that the button is released, we can unlock the bank
}
}
private:
// The ManyAddresses class keeps track of the active bank, and returns the selected
// address.
Bankable::ManyAddresses::ManyAddresses<NumAddr> address;
Button button;
Array<MIDIMessageType, NumAddr> types;
};
// --------------------------------------------------------------------------------------- //
// MIDI interface to use
USBDebugMIDI_Interface midi;
// Instantiate three Banks
Bank<3> bank;
// Instantiate a Bank selector to control which one of the three Banks is active.
IncrementDecrementSelector<3> selector = {
bank, // Bank to manage
{2, 3}, // push button pins (increment, decrement)
Wrap::Wrap, // Wrap around
};
CC_PC_Button<3> button = {
bank,
5, // input pin
{
0x07, // Controller Nº to use when 1st bank is selected
0x0A, // Controller Nº to use when 2nd bank is selected
0x0D, // Program Nº to use when 3rd bank is selected
},
{
MIDIMessageType::CONTROL_CHANGE, // Message type 1st bank
MIDIMessageType::CONTROL_CHANGE, // Message type 2nd bank
MIDIMessageType::PROGRAM_CHANGE, // Message type 3rd bank
},
};
void setup() {
Control_Surface.begin();
}
void loop() {
Control_Surface.loop();
}
Pieter