Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 10 of 10

Thread: Example code for MIDI controllers with Pots and Buttons

  1. #1
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Isle in the Salish Sea
    Posts
    1,052

    Example code for MIDI controllers with Pots and Buttons

    Here is my code for a simple MIDI controller with both potentiometers and buttons using ResponsiveAnalogRead and BOUNCE libraries to deal with signal stability.

    I hope it will be helpful for those looking to implement basic USB-MIDI controller projects and don't want to write their own code as yet.

    I'm looking for suggestions and a general vetting for stupidity before pointing noobs at it...

    Code:
    //************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 = 6; // number of Analog PINS
    const int D_PINS = 3; // 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,A5};
    const int CCID[A_PINS] = {21,22,23,24,25,26};
    
    // define the pins and notes for digital events
    const int DIGITAL_PINS[D_PINS] = {0,1,2};
    const int note[D_PINS] = {60,61,62};
    const int BOUNCE_TIME = 7; // 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);
          }
        }
      }
    }
    
    
    
    //************DIGITAL SECTION**************
    void getDigitalData(){
      for (int i=0;i<D_PINS;i++){
      digital[i].update();
        if (digital[i].fallingEdge()) {
          usbMIDI.sendNoteOn(note[i], ON_VELOCITY, channel);  
        }
        // Note Off messages when each button is released
        if (digital[i].risingEdge()) {
          usbMIDI.sendNoteOff(note[i], 0, channel);  
        }
      }
    }
    Last edited by oddson; 07-15-2017 at 08:30 PM.

  2. #2
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    19,532
    Would it be ok to include this in the examples that ship with Teensyduino?

    I'd like to give you credit in the comments. How best to do that? "oddson"?

    The other examples have "This example code is in the public domain". Is that ok? Or would you prefer it to have an open source license?

  3. #3
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Isle in the Salish Sea
    Posts
    1,052
    PD is fine... 'oddson' or "Leif Oddson" if you want to credit.

    Is there a better way to initialize an arbitrary number of BOUNCE or RAR objects that the comment trick in my code?

  4. #4
    Junior Member
    Join Date
    Jan 2019
    Posts
    3
    Hello! Thanks for sharing this @oddson, it's the best example I have found for my needs.
    I have made a few mofications to suit my needs however I think the way I coded things is a bit inneficient and I was wondering if i could get some help.


    My objective is to have an LED per Button, every time a button is pressed its corresponding LED turns On and stays On until another button is pressed and the rest turns Off, basically a Ableton Live Scene trigger where LED's On is just a visual indicatior of what Scene is active.

    Adding the LED info
    Code:
    const int D_LEDS = 2; // How many Pins for LEDS 
    const int DIGITAL_LEDS[D_LEDS] = {11,12}; // Numbers of the LED Pins
    Digital Section:
    Code:
    //************DIGITAL SECTION**************
    //Modificaions : Changed the order of fallingEdge and risingEdge because note off was trigeering the note instead of note on.
    void getDigitalData(){
      for (int i=0;i<D_PINS;i++){
      digital[i].update();
        if (digital[i].risingEdge()) {
          usbMIDI.sendNoteOn(note[i], ON_VELOCITY, channel);
          digitalWrite(DIGITAL_LEDS[0], LOW); // TURNS OFF DIGITAL_LEDS 1 - PIN 11
          digitalWrite(DIGITAL_LEDS[1], LOW); // TURNS OFF DIGITAL_LEDS 2 - PIN 12
        }
        // Note Off messages when each button is released
        if (digital[i].fallingEdge()) {
          usbMIDI.sendNoteOff(note[i], 0, channel);
          digitalWrite(DIGITAL_LEDS[i], HIGH); // TURN ON THE PRESSED BUTTON EQUIVALENT LED WHEN BUTTON IS RELEASED.
          Serial.print(" Note OFF ");
        }
      }
    }
    It works fine but as i add more LED's using "DIGITAL_LEDS[number], LOW" a bunch of times seems inneficint, is there a better way to go about this?

  5. #5
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Isle in the Salish Sea
    Posts
    1,052
    If there is always one, and only one mode active you can store it as a byte/integer variable.

    (This may be easier than checking a pin output when you try use the mode info... what do you use it for?)

    Just have the falling edge turn off the old and turn on the new and set the mode to the new index. But for a small number of modes you could just set all to low before setting the new value.

    Re swapping rising/falling... are you using active low switching?

  6. #6
    Junior Member
    Join Date
    Jan 2019
    Posts
    3
    Oh man, I am a big noob, I just started learning 2 days ago, tbh i dont know most of what you're asking.

    Quote Originally Posted by oddson View Post
    If there is always one, and only one mode active you can store it as a byte/integer variable.
    (This may be easier than checking a pin output when you try use the mode info... what do you use it for?)
    Yes, only one active at a time.
    I'll do some research on byte/integer variables to learn what it's about and how it works.
    I think i left it like that by mistake after testing some stuff and forgot about it.


    Quote Originally Posted by oddson View Post
    Just have the falling edge turn off the old and turn on the new and set the mode to the new index. But for a small number of modes you could just set all to low before setting the new value.
    So bacasically this:
    void getDigitalData(){
    for (int i=0;i<D_PINS;i++){
    digital[i].update();
    if (digital[i].risingEdge()) {
    usbMIDI.sendNoteOn(note[i], ON_VELOCITY, channel);
    }
    // Note Off messages when each button is released
    if (digital[i].fallingEdge()) {
    usbMIDI.sendNoteOff(note[i], 0, channel);
    digitalWrite(11, LOW); // turn off list
    digitalWrite(12, LOW); // turn off list
    digitalWrite(DIGITAL_LEDS[i], HIGH);
    }
    }
    }
    ?
    I wont ever use many Leds so i guess that works.


    Quote Originally Posted by oddson View Post
    Re swapping rising/falling... are you using active low switching?
    I have no idea, if i am is that bad? How do i check if I am? Sorry for the dumb question.

  7. #7
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Isle in the Salish Sea
    Posts
    1,052
    See https://www.pjrc.com/teensy/td_digital.html for active low explanation.

  8. #8
    Junior Member
    Join Date
    Jan 2019
    Posts
    3
    Quote Originally Posted by oddson View Post
    See https://www.pjrc.com/teensy/td_digital.html for active low explanation.
    Thanks Sir, i appreciate your time and pointing me in the right direction.
    Time for me to do some reading.

  9. #9
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Isle in the Salish Sea
    Posts
    1,052
    Set

    digitalWrite(DIGITAL_LEDS[i], HIGH );

    on falling edge.

    Set the previous high to low

    digitalWrite(DIGITAL_LEDS[lastHigh], LOW);

    Record the current pin as last high

    lastHigh = i ;

    So you can turn it off on the next.

    If I'm following you

  10. #10
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Isle in the Salish Sea
    Posts
    1,052
    Actually set new to HIGH after turning old LOW to avoid turning LED off if selected twice in sequence.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •