tttapa / Control-Surface

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

Toggle between Bank & direct CCRotaryEncoder with pushButton #448

Open basichumantastes opened 3 years ago

basichumantastes commented 3 years ago

Hello there ! First of all , thanks a lot for this amazing library , without which , as a non programmer, I would not be able to enter this world of creation .

The project i'm workin on is a Midi controller with one rotary encoder and some buttons to select what the encoder is doing .

I successfully used Bankable::CCRotaryEncoder with a ManyButtonsSelectorLEDS to switch between the encoder's functions , changing the MIDIAddress .

What I would like to do now and which is giving me a headache for 1 week, is that :

-6 buttons in a ManyButtonsSelectorLEDs to select the MIDI Address of the rotary encoder from CC21 to CC26 -1 button (RotarySW) witch toggles between MIDI CC20 and the last ManyButtonsSelectorLEDs.

 I hope i'am clear, it's a little bit tricky to explain in english for me . 

 Here is the code i'm at the moment 

Thanks for helping !

#include <FastLED.h>
#include <Arduino_Helpers.h>
#include <Encoder.h>
#include <Control_Surface.h>

USBMIDI_Interface midi;

using namespace ExtIO;
using namespace MIDI_Notes;

//#define NUM_LEDS 12
//#define DATA_PIN 13
//
//CRGB leds[NUM_LEDS];

////////////////////////////////////SHIFT REGISTER/////////////////////////////

// Instantiate a shift register with the SPI slave select pin as latch pin, most
// significant bit first, and a total of 8 outputs.
SPIShiftRegisterOut<16> sreg = {SS, MSBFIRST};

//DECLARE SHIFT REGISTER PINS
const pin_t PlayLED = sreg.pin(0);
const pin_t RecLED = sreg.pin(1);
const pin_t LoopLED = sreg.pin(2);
const pin_t OverdubLED = sreg.pin(3);
const pin_t VolumeLED = sreg.pin(4);
const pin_t PanLED = sreg.pin(5);
const pin_t SendALED = sreg.pin(6);
const pin_t SendBLED = sreg.pin(7);
const pin_t SendCLED = sreg.pin(8);
const pin_t SendDLED = sreg.pin(9);
const pin_t MuteLED = sreg.pin(10);
const pin_t SoloLED = sreg.pin(11);
const pin_t ArmLED = sreg.pin(12);
const pin_t RotaryLED = sreg.pin(13);

//////////////////////////////////MULTIPLEXER/////////////////////////////////

//CREATE MUX
CD74HC4067 mux = {
  4,       // Common Digital pin
  {5, 6, 7, 8} // Address pins S0, S1, S2, S3
};

//DECLARE MUXBUTTONS
const pin_t RotarySW = mux.pin(0);
const pin_t VolumeSW = mux.pin(1);
const pin_t PanSW = mux.pin(2);
const pin_t SendASW = mux.pin(3);
const pin_t SendBSW = mux.pin(4);
const pin_t SendCSW = mux.pin(5);
const pin_t SendDSW = mux.pin(6);
const pin_t MuteSW = mux.pin(7);
const pin_t SoloSW = mux.pin(8);
const pin_t ArmSW = mux.pin(9);
const pin_t PlaySW = mux.pin(10);
const pin_t StopSW = mux.pin(11);
const pin_t RecSW = mux.pin(12);
const pin_t LoopSW = mux.pin(13);
const pin_t OverdubSW = mux.pin(14);

//////DECLARE VARIABLES
//int VolumeValue = CCValue(21, CHANNEL_1);

/////////////////////////////////BANK////////////////////////////////////////
//CREATE BANK
Bank<7> bank(1);
//Bank<7> FastLEDbank(1);

//CREATE BANK MANY SELECTOR
ManyButtonsSelectorLEDs<7> selector = {
  bank,
  {{RotarySW,VolumeSW,PanSW,SendASW,SendBSW,SendCSW,SendDSW}},
  {{RotaryLED,VolumeLED,PanLED,SendALED,SendBLED,SendCLED,SendDLED}},
};

//CREATE BANKABLE CCROTARY ENCODER

// CC20 : Track Select ! CC23 : Send A  ! CC26 : Send D
// CC21 : Volume       ! CC24 : Send B  !
// CC22 : Pan          ! CC25 : Send C  !

Bankable::CCRotaryEncoder enc = {
  {bank, BankType::CHANGE_ADDRESS},
  {2, 3},       // pins
  {MIDIAddress(20, CHANNEL_1)}, // MIDI address (CC number + optional channel)
  1,            // optional multiplier if the control isn't fast enough
};

//// Instantiate TRACK SELECT rotary Encoder
//CCRotaryEncoder SelectENC = {
//  {2, 3},       // pins
//  {MIDIAddress(20, CHANNEL_1)}, // MIDI address (CC number + optional channel)
//  1,            // optional multiplier if the control isn't fast enough
//};

//////////////////////////////////////////////////////////////////////////
// DECLARE GLOBAL CONTROL BUTTONS
NoteButton button [] = {
  {PlaySW, {note(C, 4), CHANNEL_1}}, //PLAY
  {StopSW, {note(Db, 4), CHANNEL_1}}, // STOP
  {RecSW, {note(D, 4), CHANNEL_1}}, // REC
  {LoopSW, {note(Eb, 4), CHANNEL_1}}, // LOOP
  {OverdubSW, {note(E, 4), CHANNEL_1}}, // OVERDUB
  {MuteSW, {note(F, 4), CHANNEL_1}}, // CHANNEL MUTE
  {SoloSW, {note(Gb, 4), CHANNEL_1}}, // CHANNEL SOLO
  {ArmSW, {note(G, 4), CHANNEL_1}}, // CHANNEL ARM
};

//////////////////////////////////////////////////////////////////////////
// Instantiate a LEDs object
NoteValueLED led [] = {
  {PlayLED, {note(C, 4), CHANNEL_1}}, // PLAY
  //{StopLED, {note(Db, 4), CHANNEL_1}},
  {RecLED, {note(D, 4), CHANNEL_1}}, // REC
  {LoopLED, {note(Eb, 4), CHANNEL_1}}, //LOOP
  {OverdubLED, {note(E, 4), CHANNEL_1}}, // OVERDUB
  {MuteLED, {note(F, 4), CHANNEL_1}}, // CHANNEL MUTE
  {SoloLED, {note(Gb, 4), CHANNEL_1}}, // CHANNEL SOLO
  {ArmLED, {note(G, 4), CHANNEL_1}}, // CHANNEL ARM
};

void setup()
{

  // See FastLED examples and documentation for more information.
//  FastLED.addLeds<NEOPIXEL, ledpin>(leds.data, leds.length);
//  FastLED.setCorrection(TypicalPixelString);
//  midiled.setBrightness(10);
   Control_Surface.begin();

  RelativeCCSender::setMode(relativeCCmode::TWOS_COMPLEMENT);

  sreg.begin(); //Initialize Shift Register

  //currentButtonState = RotarySW.getState();

  //Bankable::CCRotaryEncoder::disable(enc);

  //CCRotaryEncoder::enable(SelectENC);

   // Initialize Control Surface
}

void loop() {
  Control_Surface.loop(); // Update the Control Surface
  FastLED.show();
}
tttapa commented 3 years ago

This is not as trivial as it might seem. Because encoders use interrupts, you cannot have multiple instances on the same pins. You'll have to use the borrowed encoder classes that store a reference to the Encoder object, not just a copy of the Encoder itself.

Next, when you enable one encoder and disable the other one, you have to update the MIDI element's encoder offset and remainder: https://tttapa.github.io/Control-Surface-doc/new-input/Doxygen/db/d50/Abstract_2MIDIRotaryEncoder_8hpp_source.html#l00075 Otherwise, the enabled encoder will jump to the position of the one that you just disabled. There is currently no standard way to do this, so you'll have to add some getters and setters to the GenericMIDIRotaryEncoder class. I think overwriting the deltaOffset and remainder of the encoder to be enabled by the ones from the encoder to be disabled should work.

basichumantastes commented 3 years ago

thanks a lot for those informations ! Let's get back to work :)

basichumantastes commented 3 years ago

I managed to use the borrowed class for the first CC control of the encoder . I had to switch to the new input branch to get that workin and it did (at least for the non banked one).

I edited a simplified version of my code to work on this feature because my board crashes each time I click the EncoderClick Button to toggle the state of the encoder, and it still does .

Any idea on what I am doing wrong ? here is the simplified code :

thanks for your time !

#include <Arduino_Helpers.h>
#include <Control_Surface.h>

USBMIDI_Interface midi;

using namespace ExtIO;
using namespace MIDI_Notes;

//////////////////////////////////MULTIPLEXER/////////////////////////////////

//CREATE MUX

CD74HC4067 mux = {
  4,       // Common Digital pin
  {5, 6, 7, 8} // Address pins S0, S1, S2, S3
};

//DECLARE MUXBUTTONS

const pin_t RotarySW = mux.pin(0);

Button EncoderClick = (RotarySW);

////////////////////////////////////////ENCODER//////////////////

// DECLARE PHYSICAL ENCODER

Encoder enc (2, 3); 

//DECLARE VIRTUAL ENCODER FROM PHYSICAL

//enc - used as track selector 

BorrowedCCRotaryEncoder TrackSelectenc = {
  enc,
  {MIDIAddress(20, CHANNEL_1)},
  1,
  };

//Volumeenc - used as Volume Control

BorrowedCCRotaryEncoder Volumeenc = {
  enc,
  {MIDIAddress(21, CHANNEL_1)},
  1,
  };

  ///////////////VARIABLES/////////
  bool toggle = 1;

  void setup()
{

   Control_Surface.begin();

   EncoderClick.begin();

   RelativeCCSender::setMode(relativeCCmode::TWOS_COMPLEMENT);

}

void loop() {
 Control_Surface.loop();
 //EncoderClick.invert();

if (EncoderClick.update()==Button::Falling){
  toggle = !toggle;
   if (toggle) {
    BorrowedCCRotaryEncoder::enable(Volumeenc);
    BorrowedCCRotaryEncoder::disable(TrackSelectenc);
   } else {
    BorrowedCCRotaryEncoder::enable(TrackSelectenc);
    BorrowedCCRotaryEncoder::disable(Volumeenc);
   }
   digitalWrite (27, toggle); //LED FEEDBACK toggle state
    };

};
tttapa commented 3 years ago

I think the problem could be that both encoders start off enabled, so when you press the button, you try to enable an encoder that was enabled already. Try disabling one of the encoders in your setup.

If you define DEBUG_OUT as Serial, it'll print the error to the Serial port.

You shouldn't include Arduino_Helpers.h, Control_Surface.h should be sufficient.