Adding toggle to convert notes to cc

Tano

Member
I've made midi controller with the Many buttons and knobs code.It's been working great with no problems. I would like to add a toggle to convert notes to cc commands. I've searched the forum and found a code for this but I couldn't get it to work. I'm using the teensy 4.1. I like the code I'm using because I'm new to coding it's pretty easy to understand and it has LED'S. Is there a solution to adding a toggle. Any help is greatly appreciated. Thanks


Code:
/* Use arrays to manage lists of knobs/pots and pushbuttons.

   By Leif Oddson
   https://forum.pjrc.com/threads/45376

   This more complex example demonstrates how to use arrays to
   manage a larger number of inputs, without duplicating your
   code for every signal.

   You must select MIDI from the "Tools > USB Type" menu

   This example code is in the public domain.
*/


//************LIBRARIES USED**************
// include the ResponsiveAnalogRead library for analog smoothing
#include <ResponsiveAnalogRead.h>
// include the Bounce library for 'de-bouncing' switches -- removing electrical chatter as contacts settle
#include <Bounce.h>
//usbMIDI.h library is added automatically when code is compiled as a MIDI device

// ******CONSTANT VALUES********
// customize code behaviour here!
const int channel = 1; // MIDI channel
const int A_PINS = 5; // number of Analog PINS
const int D_PINS = 6; // number of Digital PINS
const int ON_VELOCITY = 99; // note-one velocity sent from buttons (should be 65 to  127)

// define the pins you want to use and the CC ID numbers on which to send them..
const int ANALOG_PINS[A_PINS] = {A0,A1,A2,A3,A4,};
const int CCID[A_PINS] = {21,22,23,24,25,};

// define the pins and notes for digital events
const int DIGITAL_PINS[D_PINS] = {0,1,2,3,4,5};
const int ccid[D_PINS] = {60,61,62,63,64,65};
const int BOUNCE_TIME = 5; // 5 ms is usually sufficient
const boolean toggled = true;


//******VARIABLES***********
// a data array and a lagged copy to tell when MIDI changes are required
byte data[A_PINS];
byte dataLag[A_PINS]; // when lag and new are not the same then update MIDI CC value


//************INITIALIZE LIBRARY OBJECTS**************
// not sure if there is a better way... some way run a setup loop on global array??
// use comment tags to comment out unused portions of array definitions

// initialize the ReponsiveAnalogRead objects
ResponsiveAnalogRead analog[]{
  {ANALOG_PINS[0],true},
  {ANALOG_PINS[1],true},
  {ANALOG_PINS[2],true},
  {ANALOG_PINS[3],true},
  {ANALOG_PINS[4],true},/*
  {ANALOG_PINS[5],true},
  {ANALOG_PINS[6],true}
  {ANALOG_PINS[7],true},*/
};

// initialize the bounce objects
Bounce digital[] =   {
  Bounce(DIGITAL_PINS[0],BOUNCE_TIME),
  Bounce(DIGITAL_PINS[1], BOUNCE_TIME),
  Bounce(DIGITAL_PINS[2], BOUNCE_TIME),
  Bounce(DIGITAL_PINS[3], BOUNCE_TIME),
  Bounce(DIGITAL_PINS[4], BOUNCE_TIME),
  Bounce(DIGITAL_PINS[5], BOUNCE_TIME),/*
  Bounce(DIGITAL_PINS[6], BOUNCE_TIME),
  Bounce(DIGITAL_PINS[7], BOUNCE_TIME),*/
};

//************SETUP**************
void setup() {
// loop to configure input pins and internal pullup resisters for digital section
  for (int i=0;i<D_PINS;i++){
    pinMode(DIGITAL_PINS[i], INPUT_PULLUP);
  }
}

//************LOOP**************
void loop() {
  getAnalogData();
  getDigitalData();
  while (usbMIDI.read()) {
     // controllers must call .read() to keep the queue clear even if they are not responding to MIDI
  }
}


//************ANALOG SECTION**************
void getAnalogData(){ 
  for (int i=0;i<A_PINS;i++){
    // update the ResponsiveAnalogRead object every loop
    analog[i].update();
    // if the repsonsive value has change, print out 'changed'
    if(analog[i].hasChanged()) {
      data[i] = analog[i].getValue()>>3;
      if (data[i] != dataLag[i]){
        dataLag[i] = data[i];
        usbMIDI.sendControlChange(CCID[i], data[i], channel);
      }
    }
  }
}
 


const int D_LEDS = 6; // How many Pins for LEDS
const int DIGITAL_LEDS[D_LEDS] = {7,8,9,10,11,12}; // Numbers of the LED Pins


//************DIGITAL SECTION**************
void getDigitalData(){
  for (int i=0;i<D_PINS;i++){
  digital[i].update();
    if (digital[i].fallingEdge()) {
      usbMIDI.sendNoteOn(ccid[i], ON_VELOCITY, channel);
      digitalWrite(DIGITAL_LEDS[0], LOW); // TURNS OFF DIGITAL_LEDS 1 - PIN 7
      digitalWrite(DIGITAL_LEDS[1], LOW); // TURNS OFF DIGITAL_LEDS 2 - PIN 8
      digitalWrite(DIGITAL_LEDS[2], LOW); // TURNS OFF DIGITAL_LEDS 3 - PIN 9
      digitalWrite(DIGITAL_LEDS[3], LOW); // TURNS OFF DIGITAL_LEDS 4 - PIN 10
      digitalWrite(DIGITAL_LEDS[4], LOW); // TURNS OFF DIGITAL_LEDS 5 - PIN 11
      digitalWrite(DIGITAL_LEDS[5], LOW); // TURNS OFF DIGITAL_LEDS 6 - PIN 12
    }
    // Note Off messages when each button is released
    if (digital[i].risingEdge()) {
      usbMIDI.sendNoteOff(ccid[i], 0, channel);
      digitalWrite(DIGITAL_LEDS[i], HIGH); // TURN ON THE PRESSED BUTTON EQUIVALENT LED WHEN BUTTON IS RELEASED.
      Serial.print(" Note OFF ");
    }
  }
}
 
Here is a code that I got off the forum which I modified to what I wanted. It compiles. When I tried to add LED'S ,I couldn't make it work so I removed that part of the code. I had copied and pasted the LED part of the code from "Many buttons and Knobs" and tried to modify it to reflect whats in this code but it failed. As I stated I'm new to coding but I'm starting to understand a little of it. Enough to modify what works.So I thought I would just go back to the code which I actually made a controller with (the one posted above) and see if adding a toggle could be done.
As stated in my first sentence I'm posting a different code in which I tried to add the LED code.This code has a toggle and compiles on the teensy 4.1


Code:
/* Works on T3.2, Usb type = Midi
 * Based on Many_Buttons_Knobs example by Leif Oddson
 * https://forum.pjrc.com/threads/45376
 * and some bits nicked from Notes and Volts
 *
 * With some additions by MatrixRat to provide some Per-element definitions
 * Pots (or other analog sensors) can send CCs, MSB/LSB CCs, or NRPNs
 * Analog inputs scalable for sensors that do not emit full 0v - 3.3v range.
 *
 * Buttons can send NoteOn/NoteOff, CC# on press, CC# on press/release or Toggle CC#.
 * Button Pressed or Released values configurable per element
 * No NRPN support for buttons as have not needed 'em.
 */
#include <ResponsiveAnalogRead.h>
#include <Bounce.h>

const int      NUM_POTS  = 5; // number of Analog PINS
const int      POT_PINS             [NUM_POTS] = {  A0,    A1,   A2,   A3,  A4}; // Specify which pins are connected to Pots
const uint16_t POT_CONTROLLER_NUMBER[NUM_POTS] = {  22,    23,   24,   25,   26}; // CC#, Note# or we might be sending NRPN and need a 14-bit value here.
const uint16_t POT_INPUT_CENTER     [NUM_POTS] = { 400,  511,   511,   511, 511}; // Useful for Center detent pots were detent spot is not at electrical center.
const uint16_t POT_DEADZONE_WIDTH   [NUM_POTS] = {  40,    0,     0,      0,  0}; // Allows a dead zone to give us room to climb out of the detent before value changes.
const uint16_t POT_INPUT_MINIMUM    [NUM_POTS] = {   0,    0,     0,      0,   0}; // Useful for pots where wiper does not reach track Min.
const uint16_t POT_INPUT_MAXIMUM    [NUM_POTS] = {1023, 1023,  1023,   1020,  1020}; //  "     "   "     "     "     "   "    "     "   Max.
const uint16_t POT_OUTPUT_MINIMUM   [NUM_POTS] = {   0,    0,     0,      0,  0}; // Specify Output range Minimum.
const uint16_t POT_OUTPUT_MAXIMUM   [NUM_POTS] = { 127,  127,   255,   1023,  255}; // Specify output range Maximum
const byte     POT_OUTPUT_TYPE      [NUM_POTS] = {   1,    1,   1,    2,      3}; // 0=Muted, 1=7-bitCC, 2=MSB LSB CC, 3= NRPN.
const byte     POT_OUTPUT_CHANNEL   [NUM_POTS] = {  1,   1,    1,     1,   1};
//******VARIABLES***********
uint16_t data   [NUM_POTS];
uint16_t dataLag[NUM_POTS]; // when lag and new are not the same then update MIDI CC value

ResponsiveAnalogRead analog[] {
  {POT_PINS[0], true},
  {POT_PINS[1], true},
  {POT_PINS[2], true},
  {POT_PINS[3], true},
  {POT_PINS[4], true},
};

//************************************************************

const int BOUNCE_TIME = 7; // 5 ms is usually sufficient
const int NUM_BUTTONS = 6; // number of Buttons connected to digital PINS

const int  BUTTON_PINS             [NUM_BUTTONS] = { 0,1,2,3,4,5};// Which Pin is a Button on?
const byte BUTTON_CHANNEL          [NUM_BUTTONS] = { 1,1,1,1,1,1};// What channel is the button sending on?
const int  BUTTON_CONTROLLER_NUMBER[NUM_BUTTONS] = {32,27,28,29,30,31};// Could be CC# or Note#
const int  BUTTON_ON_VALUE         [NUM_BUTTONS] = {127, 127, 127,  127, 127,127};// Specify a button pressed value
const int  BUTTON_OFF_VALUE        [NUM_BUTTONS] = {  0,   0,   0,    0, 0,0};// Specify a button released value
const byte BUTTON_OUTPUT_TYPE      [NUM_BUTTONS] = {  1,   1,  3,  3, 3, 3};// 0=Muted, 1=Note On/Off, 2=CC Press, 3=CC Press/Release, 4=Toggle
int        BUTTON_TOGGLED          [NUM_BUTTONS]; //Array to hold toggled state

// initialize the bounce objects
  Bounce digital[] =   {
  Bounce(BUTTON_PINS[0], BOUNCE_TIME),
  Bounce(BUTTON_PINS[1], BOUNCE_TIME),
  Bounce(BUTTON_PINS[2], BOUNCE_TIME),
  Bounce(BUTTON_PINS[3], BOUNCE_TIME),
  Bounce(BUTTON_PINS[4], BOUNCE_TIME),
  Bounce(BUTTON_PINS[5], BOUNCE_TIME),

};

//*******************************************************************

void setup() {
  // loop to configure input pins and internal pullup resisters for digital section
  for (int i = 0; i < NUM_BUTTONS; i++) {
    pinMode(BUTTON_PINS[i], INPUT_PULLUP);
  }
}
//**********************************************

void loop() {
  while (usbMIDI.read()) {
    // controllers must call .read() to keep the queue clear even if they are not responding to MIDI
  }
  getDigitalData();
  getAnalogData();
}

//************ANALOG SECTION**************
void getAnalogData()
{
  for (int i = 0; i < NUM_POTS; i++) {
    analog[i].update();
    if (!analog[i].hasChanged())
      continue;
//With some help from @NominalAnimal

    const int32_t  OUTPUT_CENTER = (POT_OUTPUT_MINIMUM[i] + POT_OUTPUT_MAXIMUM[i]) / 2;
    const int32_t  input = analog[i].getValue();

    if (input <= POT_INPUT_MINIMUM[i]) {
      data[i] = POT_OUTPUT_MINIMUM[i];
    } else if (input < POT_INPUT_CENTER[i] - POT_DEADZONE_WIDTH[i]) {
      data[i] = map(input, POT_INPUT_MINIMUM[i], POT_INPUT_CENTER[i] - POT_DEADZONE_WIDTH[i], POT_OUTPUT_MINIMUM[i], OUTPUT_CENTER);
    } else if (input <= POT_INPUT_CENTER[i] + POT_DEADZONE_WIDTH[i]) {
      data[i] = OUTPUT_CENTER;
    } else if (input < POT_INPUT_MAXIMUM[i]) {
      data[i] = map(input, POT_INPUT_CENTER[i] + POT_DEADZONE_WIDTH[i], POT_INPUT_MAXIMUM[i], OUTPUT_CENTER, POT_OUTPUT_MAXIMUM[i]);
    } else {
      data[i] = POT_OUTPUT_MAXIMUM[i];
    }
    if (data[i] != dataLag[i]) {
      dataLag[i] = data[i];
     
      uint16_t  potmessage = data[i];      //has what the pot has just given us
  // Note we're using a uint16_t because we might be sending values higher than 127 with MSB/LSB CCs or NRPNs
  // and the following dance pulls out MSB/LSB and tosses out bits 14 and 15  
      byte     valueLSB   = (potmessage & 0b0000000001111111);
      byte     valueMSB   = (potmessage & 0b0011111110000000)  >> 7;
  // Similarly, we might be sending an NRPN 14-bit Controller number  
      byte     controlLSB = (POT_CONTROLLER_NUMBER[i]    & 0b0000000001111111);// POT_CONTROLLER_NUMBER[i], a uint16_t holds Control#
      byte     controlMSB = (POT_CONTROLLER_NUMBER[i]    & 0b0011111110000000) >> 7;

      switch (POT_OUTPUT_TYPE[i])// from POT_OUTPUT_TYPE definition
      {
        case 0:
          //muted, do nothing
          break;
        case 1:   // Sending 7-bit CC
          usbMIDI.sendControlChange   (controlLSB, valueLSB, (POT_OUTPUT_CHANNEL[i]));
          break;
        case 2:
          usbMIDI.sendControlChange    (controlLSB, valueMSB, (POT_OUTPUT_CHANNEL[i]));
          usbMIDI.sendControlChange    (controlLSB + 32, valueLSB, (POT_OUTPUT_CHANNEL[i]));
          break;
        case 3:
          usbMIDI.sendControlChange(99, controlMSB, (POT_OUTPUT_CHANNEL[i]));
          usbMIDI.sendControlChange(98, controlLSB, (POT_OUTPUT_CHANNEL[i]));
          usbMIDI.sendNrpnValue(data[i], (POT_OUTPUT_CHANNEL[i]));
          break;
      }
    }
  }
}
//************DIGITAL SECTION**************

void getDigitalData() {

  for (int i = 0; i < NUM_BUTTONS; i++) {
    digital[i].update();
    if (digital[i].fallingEdge()) {
      // Button is pressed
      switch (BUTTON_OUTPUT_TYPE[i]) {
        case 0:
          //Do nothing, MUTED
          break;
        case 1: //Note
          usbMIDI.sendNoteOn (BUTTON_CONTROLLER_NUMBER[i], (BUTTON_ON_VALUE[i]), (BUTTON_CHANNEL[i]));
          break;
        case 2: //PRESS
          usbMIDI.sendControlChange   (BUTTON_CONTROLLER_NUMBER[i], (BUTTON_ON_VALUE[i]), (BUTTON_CHANNEL[i]));
          break;
        case 3: //CC PRESS - RELEASE
          usbMIDI.sendControlChange   (BUTTON_CONTROLLER_NUMBER[i], (BUTTON_ON_VALUE[i]), (BUTTON_CHANNEL[i]));
          break;
        case 4: //Toggle
          if (BUTTON_TOGGLED[i] == 0) {
            usbMIDI.sendControlChange   (BUTTON_CONTROLLER_NUMBER[i], (BUTTON_ON_VALUE[i]), (BUTTON_CHANNEL[i]));
            BUTTON_TOGGLED[i] = 1;
          }
          else if (BUTTON_TOGGLED[i] == 1)
          {
            usbMIDI.sendControlChange   (BUTTON_CONTROLLER_NUMBER[i], (BUTTON_OFF_VALUE[i]), (BUTTON_CHANNEL[i]));
            BUTTON_TOGGLED[i] = 0;
          }
          break;
      }
    }
    if (digital[i].risingEdge()) {
      switch (BUTTON_OUTPUT_TYPE[i]) {
        case 0:
          //Do nothing - MUTED
          break;
        case 1:// Send Note Off
          usbMIDI.sendNoteOff (BUTTON_CONTROLLER_NUMBER[i], (BUTTON_OFF_VALUE[i]), (BUTTON_CHANNEL[i]));
          break;
        case 2: // Don't Send CC Release
          // MUTED
          break;
        case 3:  //  Send CC - RELEASE
          usbMIDI.sendControlChange   (BUTTON_CONTROLLER_NUMBER[i], (BUTTON_OFF_VALUE[i]), (BUTTON_CHANNEL[i]));
          break;
        case 4: // Dummy for TOGGLE
          break;
      }
    }
  }
}
 
Back
Top