Control_Surface library ManyAddresses< NumBanks > help

Hello, I'd like to learn what is this for.

I'd like to have 1 of 4 banks of 6 CC buttons in which 2 buttons will be used for Program Change instead of CC like in other banks.

I hope my English is good enough to let you understand what I mean. :confused:

I'd like to see an example sketch because the one I found Control Surface: ManyAddressesPCButton.ino doesn't resolve my doubts.
Every help will be appreciated!

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.

  1. 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.

  1. 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

Thank you very much!
It is exactly what I was asking for and your help is always really precious and kind.
I will use the first option for now for it's semplicity, but I'm going to go deeper in the learning of Control_Surface library, so I'll study your second example too.

Thanks once again, Luca.