tttapa / Control-Surface

Arduino library for creating MIDI controllers and other MIDI devices.
GNU General Public License v3.0
1.25k stars 140 forks source link

Change NoteButton parameter in a Switch/Case #220

Open zeysh opened 4 years ago

zeysh commented 4 years ago

Hi,

I wonder if it is possible to change a parameter for a NoteButton in a loop. I try to select a mode with one button, and activate it with another one.

Final goal is to

But I'm not sure I can declare/configure a NoteButton in a Switch/Case in a loop().

Does anyone have an idea ? Many thanks

  /**
   * @brief 
   *
   * Select track with MCU::CHANNEL_LEFT and MCU::CHANNEL_RIGHT,
   * A button can select action and an other button trigger the selected action.
   * Actions can be: mute, solo, record.
   * PIN 5 : switch to left channel
   * PIN 6 : switch to right channel
   * PIN 10: select mode
   * PIN 7 : activate the selected mode
   */

  // This must be done before the Control Surface library.
  #include <Control_Surface.h> // Include the Control Surface library

  USBMIDI_Interface midi;

  // Create a Button object that reads a push button connected to pin 10
  Button pushbutton = {10};
  // Modes: REC, MUTE, SOLO

  String mode;

  // Init count button count
  int count = 0;

  // Instantiate the buttons for switch between tracks
  NoteButtonLatched track_control[] = {
      {5, MCU::CHANNEL_LEFT},
      {6, MCU::CHANNEL_RIGHT},
    //  {7, MCU::REC_RDY_1},
    //  {8, MCU::MUTE_1},
    //  {9, MCU::SOLO_1}, 
  };

  // trigger the LED if MUTE is enable on the track
  NoteValueLED mute_led[] = {
    {LED_BUILTIN, MCU::MUTE_1},
  };

  // --------------------------------- Setup ---------------------------------- //
  // ========================================================================== //

  void setup() {
      Serial.begin(115200);
      pushbutton.begin();
      RelativeCCSender::setMode(MACKIE_CONTROL_RELATIVE); // Correct mode for                                                     
      Control_Surface.begin(); // Initialize Control Surface
  }

  // ---------------------------------- Loop ---------------------------------- //
  // ========================================================================== //

  void loop() {
    Control_Surface.loop(); // Refresh all elements
    // Detect button update
    if (pushbutton.update() == Button::Falling) {
      // increment count
      count++;
      // reset count to 0, reset mode selection
      if (count >= 3) {
        count = 0;
      }
    }
    // Mode detection
    switch (count) {
        // If button 10 was not pressed yet, the selected track is ARM
        case 0:
        {
          mode = String("REC");
          NoteButtonLatched action = {
            7,
            {MCU::REC_RDY_1},
          };
        }
        break;
        case 1:
        {
          mode = String("MUTE");
          NoteButtonLatched action = {
            7,
            {MCU::MUTE_1},
          };
        }
        break;
        case 2:
        {
          mode = String("SOLO");
          NoteButtonLatched action = {
            7,
            {MCU::SOLO_1},
          };
        }
        break;
      }
    Serial.println(mode);  
  }
tttapa commented 4 years ago

The reason your code doesn't work is the scope of the action objects. It is declared inside of the switch cases, so it is destroyed as soon as you leave that case.

The idiomatic way to change the address of a MIDI element is to use Banks. If you need a fixed offset between addresses, you can use the MIDI elements in the Bankable namespace, if you need arbitrary addresses, you can use the Bankable::ManyAddresses namespace.

As you'll see, ManyAddresses isn't complete yet, simply because I haven't had much time, and it's not that high on my TODO list, but if you need a ManyAddresses version of one of the other MIDI elements, just let me know. I noticed you were using the NoteButtonLatched class, for instance. (Usually, a non-Latched NoteButton is the right choice for MCU controllers.)

An example using ManyAddresses:

#include <Control_Surface.h> // Include the Control Surface library

USBMIDI_Interface midi;

// Instantiate the buttons for switch between tracks
NoteButtonLatched track_control[] = {
  {5, MCU::CHANNEL_LEFT},
  {6, MCU::CHANNEL_RIGHT},
};

// trigger the LED if MUTE is enable on the track
NoteValueLED mute_led[] = {
  {LED_BUILTIN, MCU::MUTE_1},
};

// Create a Bank object to select one of three modes (REC, MUTE, SOLO)
Bank<3> bank;

// Create a Selector object that reads a push button connected to pin 10
// to select which mode is active.
IncrementSelector<3> pushbutton = {bank, 10};

Bankable::ManyAddresses::NoteButton<3> button = {
  bank, // bank selects active address
  7,    // push button pin
  {{    // addresses
    MCU::REC_RDY_1,
    MCU::MUTE_1,
    MCU::SOLO_1,
  }},
};

// --------------------------------- Setup ---------------------------------- //

void setup() {
  Serial.begin(115200);
  Control_Surface.begin(); // Initialize Control Surface
}

// ---------------------------------- Loop ---------------------------------- //

void loop() {
  Control_Surface.loop(); // Refresh all elements
  static setting_t prevSetting = -1;
  setting_t setting = bank.getSelection();
  if (setting != prevSetting) {
    const char *mode;
    switch (setting) {
      case 0: mode = "REC"; break;
      case 1: mode = "MUTE"; break;
      case 2: mode = "SOLO"; break;
      default: mode = "<invalid>";
    }
    Serial.println(mode);
    prevSetting = setting;
  }
}
zeysh commented 4 years ago

That's work perfectly ! Many thanks.

The reason your code doesn't work is the scope of the action objects. It is declared inside of the switch cases, so it is destroyed as soon as you leave that case.

Makes sens thanks!

As you'll see, ManyAddresses isn't complete yet, simply because I haven't had much time, and it's not that high on my TODO list, but if you need a ManyAddresses version of one of the other MIDI elements, just let me know. I noticed you were using the NoteButtonLatched class, for instance. (Usually, a non-Latched NoteButton is the right choice for MCU controllers.)

I've tested my code each time with Ableton Live and Ardour. And the reason I use the NoteButtonLatched class is because it's working with Ardour. I don't understand why actually... NoteOn enable MUTE (for example), but NoteOff disable it.

I'll probably ask to the Ardour community, if I don't find anything on their forum.

Thanks again for your help and your time.

zeysh commented 4 years ago

Just posted on Ardour forum about the "Latched" behavior.

Wait and see...

zeysh commented 4 years ago

Seems that I have to use Bankable::ManyAddresses::NoteButton with Ardour. I'll try to do my best and submit a PR :)

zeysh commented 4 years ago

I pushed a draft (not working ATM) https://github.com/tttapa/Control-Surface/pull/237