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

Using encoder to scroll tracks and change banks #836

Open 0restman opened 1 year ago

0restman commented 1 year ago

Hi there:) First of all thank you a ton for this library and the energy to answer so many questions!!!!!

I am building a midi console to control ableton with. I want to have some banks so i can map my potentiometers to different settings depending on the bank selected. Also I want to be able to scroll through my tracks(this was solved by using remotify to generate a python script).

Is it possible to use my encoder in the following way:

When connected on the computer and ableton, due to the script installed, by rotating the encocder i scroll through my tracks. When the push button of the encoder is pressed, my encoder exits scrolling mode and enters bank mode, meaning when i rotate my encoder it scrolls through the different banks. When the push button of the encoder is pressed again the encoder goes back to scrolling through the tracks

I saw this issue https://github.com/tttapa/Control-Surface/issues/448 and I thought maybe with the help of borrowed encoder classes I could solve my issue. I haven't figured a way yet to modify the code below so I can change the banks as well... Any help appreciated:)

#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);
BorrowedCCRotaryEncoder::disable(Volumeenc);

}

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 1 year ago

In more complex cases like this one, it is often easier to handle the encoder logic in your main sketch so you have full control over the behavior. You can manually change the bank by using bank.select(), and you can manually send MIDI using Control_Surface.sendControlChange({ccnumber, channel}, value).

0restman commented 1 year ago

Thank you a ton for your answer. I hope 2023 found you with a lot of daily reasons to smile:)!

I apologize for not replying, my head and schedule is so full I forget even my name some days...

Well I got it to work with the code below so every potentiometer, encoder, push button and LED seems to work....BUT!!! For some reason some of the potentiometers will generate random MIDI pulses (messages? I don't own 100% the terminology yet). even when no sensor is triggered... the LED on the arduino pro Micro that indicates a MIDI message being sent from a sensor is lighting on and off for no apparent reason...

I checked the connections and they seem pretty solid to me... I attach my code below. If sb can spot anything in the code that may be causing this problem it is over the clouds appreciated!!!

#include <Control_Surface.h>

USBMIDI_Interface midi;

using namespace ExtIO;
using namespace MIDI_Notes;

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

//CREATE MUX

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

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

// DECLARE PHYSICAL ENCODERS

CCRotaryEncoder left_enc {//the left encoder.
  {2,3},
  {MIDIAddress(21, CHANNEL_1)}, // MIDI address (CC number + optional channel)
  1, 
};
CCRotaryEncoder right_enc {// right encoder. will be used for track scroll
  {4,5},
   {MIDIAddress(22, CHANNEL_1)}, // MIDI address (CC number + optional channel)
  1, 
};

///////////////MUTE and ARM LEDs /////////////
NoteLED muteLED {
  14,                 // Pin of built-in LED
  {MIDI_Notes::C(4), CHANNEL_1}, // Note C4 on MIDI channel 1
};
NoteLED armLED {
  15,
  {MIDI_Notes::C(3),CHANNEL_1},
};
//////// MUTE AND ARM BUTTONS ////////////////////

CCButton mute_btn {
  mux.pin(0),
  {MIDI_CC::General_Purpose_Controller_1, CHANNEL_1},
};

CCButton arm_btn{
  mux.pin(1),
  {MIDI_CC::General_Purpose_Controller_1,CHANNEL_2},
};

////////// ENCODER BUTTON ////////////////

// Left Encoder button is declared directly to Switch Selector (see code below) so the button will change between the 2 banks.
//const pin_t left_enc_btn = mux.pin(0);// the button of the encoder that is connected to pin 0 of the mux. 
//Button Left_encoder_click = (left_enc_btn);//the button of the encoder. it will be used to change banks

//Right Encoders button
CCButton right_enc_btn {// will be used for soloing the selected track
  mux.pin(2),
  {MIDI_CC::General_Purpose_Controller_1, CHANNEL_3},
};

///////////////BANKS//////////////
// Bank with 2 settings in total, increments of 1 address at a time
Bank<2> bank(12);

///// BANK SELECTOR //////
Button left_enc_btn = 10;
IncrementSelector<2> selector {
  bank, // Bank to manage
  left_enc_btn,    // push button pin
};

// The potentiometer that sends MIDI messages
// the address (controller number) depends on the bank setting
Bankable:: CCPotentiometer pots[]  {
  {{bank, BankType::CHANGE_ADDRESS}, mux.pin(3) ,{MIDI_CC::Channel_Volume, CHANNEL_4}}, // Bank configuration, Analog pin for potentiometer, Base CC address
  {{bank, BankType::CHANGE_ADDRESS}, mux.pin(4),{MIDI_CC::Channel_Volume, CHANNEL_5}}, 
  {{bank, BankType::CHANGE_ADDRESS}, mux.pin(5),{MIDI_CC::Channel_Volume, CHANNEL_6}}, 
  {{bank, BankType::CHANGE_ADDRESS}, mux.pin(6),{MIDI_CC::Channel_Volume, CHANNEL_7}}, 
   {{bank, BankType::CHANGE_ADDRESS}, mux.pin(7),{MIDI_CC::Channel_Volume, CHANNEL_8}},
    {{bank, BankType::CHANGE_ADDRESS}, mux.pin(8),{MIDI_CC::Channel_Volume, CHANNEL_9}},

};

//The pans are under the faders.
Bankable:: CCPotentiometer pans[]{
  {{bank, BankType::CHANGE_ADDRESS}, mux.pin(9) ,{MIDI_CC::Channel_Volume, CHANNEL_10}}, // Bank configuration, Analog pin for potentiometer, Base CC address 
  {{bank, BankType::CHANGE_ADDRESS}, mux.pin(10),{MIDI_CC::Channel_Volume, CHANNEL_11}}, 
  {{bank, BankType::CHANGE_ADDRESS}, mux.pin(11),{MIDI_CC::Channel_Volume, CHANNEL_12}}, 
  {{bank, BankType::CHANGE_ADDRESS}, mux.pin(12),{MIDI_CC::Channel_Volume, CHANNEL_13}}, 

};

// Eliminating dead zones of the potentiometers //
//
// The filtered value read when potentiometer is at the 0% position
constexpr analog_t minimumValue = 255;
// The filtered value read when potentiometer is at the 100% position
constexpr analog_t maximumValue = 16383 - 255;

// A mapping function to eliminate the dead zones of the potentiometer:
// Some potentiometers don't output a perfect zero signal when you move them to
// the zero position, they will still output a value of 1 or 2, and the same
// goes for the maximum position.
analog_t mappingFunction(analog_t raw) {
  // make sure that the analog value is between the minimum and maximum
  raw = constrain(raw, minimumValue, maximumValue);
  // map the value from [minimumValue, maximumValue] to [0, 16383]
  return map(raw, minimumValue, maximumValue, 0, 16383);
  // Note: 16383 = 2¹⁴ - 1 (the maximum value that can be represented by
  // a 14-bit unsigned number
}

/// Variables //////
bool toggle = 1;
void setup()
{
  Control_Surface.begin();
  for(int i = 0; i < 6; i++){
    pots[i].map(mappingFunction);

  }
  for(int i = 0; i < 4; i++){

    pans[i].map(mappingFunction);
  }

}

void loop() {
  Control_Surface.loop();

  if (left_enc_btn.update() == Button::Falling) {
    toggle = !toggle;
    digitalWrite (16, !toggle); //LED FEEDBACK bank toggle stateisa[i]i
  };

};