Building A 25-Key Midi Pedalboard

Status
Not open for further replies.

debrad

Active member
Hello everyone!

After receiving positive encouragement in the "Project Guidance" board, I went ahead and purchased a Teensy++ 2.0 to serve as the basis for a 25-key Midi pedalboard which I hope to populate with 25 momentary stomp switches for keys, 2 stomp switches for "octave up" and "octave down", a slide potentiometer for "velocity", and 2 more momentary stomp switches for "modulation" and "sustain/damper/hold".

While waiting for my Teensy++ to arrive (1-week to the day I ordered!!!), I read through a number of related threads on forums, watched videos, studied "instructables", sent and received e-mails, and saved a bunch of sketches. As a result, I feel pretty comfortable with the basic concepts of what I'm trying to do and how to write code that will get me there.

Having said that, I already have some questions to ask "up front". Now, bear in mind that I am brand new to coding...both in terms of computer programming AND midi programming...so please be gentle on me if and when I ask incredibly lame questions but know in advance that I'm eager to learn and more than happy to navigate my way to existing resources when appropriate.

My first question relates to the fact that I have found several sketches that read digital "key" inputs and incorporate code for shifting octaves up and down; however, their approaches are different enough that I can't pull any commonalities from them. One used a basic approach similar to the "buttons" example (i.e. "rising edge"/"falling edge") but also incorporated a "flash table" that assigned a specific midi note value to each pin. Another sketch used "boolean" note-on states and appears to set pitches relative to the initial octave. A third sketch is reading notes from a chip on an IC2 bus and a number of other discussions talked about using stored arrays.

Anyway, what I'm SLOWLY getting around to asking is what recommendations you might have for writing code that would be effective and efficient for implementing "octave up" and "octave down" functions but would still be relatively easy to understand for a beginner like me? I like the "buttons" type code I mentioned because it is easy for me to follow...but maybe it makes the octave shift coding difficult (or impossible)?

The other questions I have right now involve the order of various steps within the sketch (i.e. do I define a "velocity" variable in the setup or the main loop, etc., etc.) but I suspect they would make more sense to ask once I've ironed out my basic approach to reading inputs.

Now, in case it impacts your advice, I did see a couple of interesting "features/functions" in these sketches that I would like to incorporate (if possible):

  • I'd like to always start with the lowest note of my 25 keys at C3 (note=48),
  • limit the octave shifts to 2 down (lowest playable note = C1 (note=24)) and 3 up (highest playable note = C8 (note=108),
  • illuminate a green LED when I've shifted BELOW the "base" octave and a red LED when I've shifted above the "base",
  • and have the ability to reset to the "base" octave by pressing the up and down switches simultaneously

I'm sure there are many other questions to come but I have definitely rambled on long enough already (my apologies for such a long first message).

I can provide copies of the sketches I'm considering but didn't want to stretch this first note even FURTHER!

I look forward to your feedback.

- brad -
 
Anyway, what I'm ... asking is what recommendations you might have for writing code ..... would still be relatively easy to understand for a beginner like me?

Avoid the temptation to over-think too much and attempt to do a huge project all at once!

Start with the known-good examples, like File > Examples > Teensy > USB_MIDI > Buttons. Even if you have no desire for Note On/Off messages, at least run it as-is and get it working.

Especially as a beginner, but even for experts, the best path to success is taking small steps and making incremental progress towards your goal. Usually developing your project in small pieces and testing each one thoroughly is a far more successful path than trying to do (and think of) everything all at once.

For the octave shift, you'll probably need to create a variable for the amount of shift, and change it when certain buttons are pressed. Maybe you'll add code that shows the amount of shift, using a LED or two? Then you'll need code in the note on and off messages, to calculate different note numbers.

Along the way, you'll need to print stuff to the Arduino Serial Monitor, to observe what your program is doing. Printing the numbers is the best way to check if your equations are working as expected, and to tell if the buttons are responding and changing the variable, and to generally see if things are working or doing something wrong.

I highly recommend doing this tutorial, if you've never printed anything to the serial monitor. It will show you how to connect buttons and print info. That skill is key to successfully diagnosing and fixing problems.

http://www.pjrc.com/teensy/tutorial3.html

But more than anything, the path to success involves making small steps and checking results as you go. Avoid the temptation to do too much at once. Build things up little by little, once small piece at a time, and thoroughly check results as you go.
 
Thanks Paul,

Your advice is well taken and I am definitely trying to follow this approach.

The reason why I was immediately thinking "big picture" is because I have multiple code examples, all of which COMBINE to cover my "wishlist" but none of which cover everything on their own. Realizing that I can't really follow my original plan of scavenging each example for the pieces I need and pasting them together because they all appear to use such diverse approaches to the basic "read pins for note on/off", I thought it would be wise to determine whether one approach might be more appropriate given my end goal rather than going off in one direction only to realize I have to go back and start all over with another approach to get where I really want to go.

I'm sure those of you with experience could implement my project with a variety of different coding approaches so I probably AM over-thinking...I was just hoping to get there a) without having to bother everyone for TOO much advice, b) with a full comprehension of what my code is doing, and c) without receiving TOO much of the chastising I saw on the Arduino forum when people didn't use or understand efficient coding.
 
I am plugging along with my Teensy++ 2 coding and hope to have things completed shortly...well, completed enough to share with everyone in hopes that any errors I encounter might be easily resolved.

One quick technical question that I have run into though...

When applying a process to several components, what is the proper form to ensure that EVERY component is included?

For example, I was basing some of my programing on some code that read:

Code:
//set pins 22 through 54 as input pins and set them high
  for(int i = 22; i < 54; i++) {
    pinMode(i,INPUT);
    digitalWrite(i, HIGH);

In my mind, this would only read pins 22 to 53 since the "<" sign would prevent the code from addressing pin 54; however, I have noticed the same type of approach in several lines of code.

To INCLUDE pin 54, should I be writing "< 54" or "<= 54"?

On a related note, I am also curious to know if using the same char "counter" in multiple FOR statements will work in my main loop or do I have to define something unique each time?

For example, will this code work or does "n" have to be a unique entity in each FOR statement?

Code:
          for(char n = 0; n < modCount; n++){                                                 //loop for the mod button
                modOn[n] = digitalRead(mod[n]);                                                 //read state of button
                if(modOn[n] == LOW && modLast[n] != modOn[n]){                     //if button is pressed
                                         midi(0xB0, 0x01, 0x28);                                     //channel 1 modulation ON with static 40 value
                                         modLast[n] = modOn[n];                                   //update button state
                                         }
                if(modLast[n] != modOn[n]){                                                      //if last button state changes
                                        midi(0xB0, 0x01, 0x00);                                     //channel 1 modulation OFF
                                        modLast[n] = modOn[n];                                   //update button state
                                        }
                }

          for(char n = 0; n < susCount; n++){                                               //loop for the sustain button
                susOn[n] = digitalRead(sus[n]);                                                //read state of button
                if(susOn[n] == LOW && susLast[n] != susOn[n]){                      //if button is pressed
                                        midi(0xB0, 0x40, 0x64);                                   //channel 1 sustain ON
                                        susLast[n] = susOn[n];                                    //update button state
                                        }
               if(susLast[n] != susOn[n]){                                                       //if last button state changes
                                       midi(0xB0, 0x40, 0x00);                                   //channel 1 sustain OFF
                                       susLast[n] = susOn[n];                                     //update button state
                                       }
              }
 
Well, i don't answer your Questions :)
The best way to learn these things is to DO it and try it. Learning by doing is sometimes the best approach.
For example, you could a add a println(n); statement in your for-loop and look what happens - does it count to 53 or 54 ?

But it's a good idea to have a good book - in my opinion, (very old!!, first edition 1978(?)) Kernighan & Ritchie "The C Programming Language" is GREAT (in a more recent edition)

Edit. You "arduino" is a bit different, for example with in-and output ("print"), so you can't use the examples in this book 1:1
 
Last edited:
Thanks Frank...good idea about "doing", I'll definitely play around a bit to see if I can find the answer based on what I'm seeing in my results.

On another tangent...

My basic approach to this project has been to use the "Buttons" example (which I have working just fine) and modify it where required to fit my needs.

Given the fact that I have 25 buttons (instead of 11) for keys and another 4 buttons for "octave up/down", "modulation", and "sustain", I have been trying to follow some advice I found online which went something like, "if you are repeating the same code over and over, you aren't coding correctly". For me, this meant trying to replace the multiple instances of:

Code:
Bounce button0 = Bounce(0, 5);

and

Code:
  pinMode(0, INPUT_PULLUP);

and

Code:
button0.update();

and ESPECIALLY

Code:
  if (button0.fallingEdge()) {
    usbMIDI.sendNoteOn(60, 99, channel);  // 60 = C4
  }
  if (button1.fallingEdge()) {
    usbMIDI.sendNoteOn(61, 99, channel);  // 61 = C#4
  }
...etc, etc.

with things like:

Code:
for(int i = 0; i < 45; i++) {                  //Bounce pins 0 through 45 to filter false triggers 
    Bounce button[i] = Bounce(i,5);                                 //set >5 (ms) if switches are too sensitive
   }

and

Code:
for(int i = 7; i < 37; i++) {                 //Set pins 7 through 37 as input pins and set them high
    pinMode(i,INPUT_PULLUP);
    digitalWrite(i, HIGH);
   }

and

Code:
for(int i = 0; i < 45; i++) { 
    button[i].update();
   }

and

Code:
for(int i = 7; i <= 31; i++ {
     if (button[i].fallingEdge()) {                                   // if button is pressed, play chosen note(s)
     usbMIDI.sendNoteOn(i + 41, vel, channel);                      // based on 1st button set to C3 (MIDI note = 48)
     }
  }

Unfortunately, most of my "efficiencies" produce errors (sigh...)

I am content to enter all the repetitious coding if it makes everything work; however, if anyone has suggestions on how I can use more efficient code AND make it work, I'd really love to educate myself! I know some of you will simply say, go look for examples...I have done that and either couldn't find examples similar enough to what I'm trying to do or (more likely) I was too ignorant to recognize the similarities.

- brad -
 
Just found the "Arrays" example in Arduino...looks like it might offer some addition help/guidance (stay tuned!!!)
 
OK...so I *TRIED* to apply what I was learning from the examples and all the other code I've found in order to make my sketch more efficient but, after spending quite a bit of time on editing, I still couldn't get things to work properly.

Taking Paul's advice, I went "back to first principles" and simply expanded the "Buttons" example to include my 25 buttons - SUCCESS! Then I added to buttons for "modulation" and "sustain" - SUCCESS! Then I got the onboard LED to blink when code was being sent and finally added code to shift my octaves - SUCCESS and SUCCESS!!!

Now I just have two features left to add and I'm really hoping that I can get some help because I haven't managed to find example of exactly what I'm trying to do (although they are basic enough that I'm sure the examples must be out there!).

First, I want to output my "usbMIDI.sendNoteOn" and "usbMIDI.sendControlChange" to a 5-pin MIDI jack attached to pin 3. I know this is a "serial" function but how do I add code so that these MIDI messages will be sent via the USB connector (as it is now) AND the serial port SIMULTANEOUSLY?

On a related note, do I have to alter any my pin setup code that currently has "pinMode(INPUT_PULLUP)" and a 5ms Bounce?

As for that SECOND feature? It seems to have slipped my mind as I typed the serial output question. Hopefully it comes back to me...


- brad -
 
...just remembered by "second feature":

If I have set pin0 and pin1 as "output" to light LEDs, do they require limiting resistors (or do the pull up resistors and/or PWM characteristics allow me to connect the pins direct to ground)?

Thanks!

- brad -
 
Well...I'm doing my best to try answering my own questions by searching online but hopefully someone will chime in with some help (especially if I'm going WAY off base).

For implementation of simultaneous output of my MIDI messages via USB and the 5-DIN MIDI jack I hope to attach to the Teensy++2.0, is it simply a matter of adding the following code in the approprate location(s)?

Code:
void setup() {
   //  Set MIDI baud rate:
   Serial.begin(31250);
 }
 
void loop() {
   // my main loop with 25 noteOn, 25 noteOff, 1 "modon", 1 "modoff", 1 "suson", and 1 "susoff"
...if (button7.fallingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(12 + (octave*12), vel, channel);  // C
    delay(d);
    digitalWrite(6,HIGH);
  }...etc...


  ...if (button7.risingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(12 + (octave*12), 0, channel);  // C
    delay(d);
    digitalWrite(6,HIGH);
  }...etc...

...if (button35.fallingEdge()) {
    usbMIDI.sendControlChange(64, 127, channel);      // (type=64=sus, value=127=on, MIDI channel)
  } 

 if (button34.fallingEdge()) {
    usbMIDI.sendControlChange(1, 64, channel);      // (type=1=mod, value=64, MIDI channel)
  }...etc...


   }
 }
 
//  plays a MIDI note.  Doesn't check to see that
 //  cmd is greater than 127, or that data values are  less than 127:
 void noteOn(int cmd, int pitch, int velocity) {
   Serial.write(cmd);
   Serial.write(pitch);
   Serial.write(velocity);
 }

void ControlChange(int cmd, int value, int channel){
   Serial.write(cmd);
   Serial.write(value);
   Serial.write(channel);
}
}

That can't be right though because my "noteON" messages contain ("pitch", "velocity", "channel") as opposed to ("cmd","pitch","velocity")...am I at least getting CLOSE?!?!?!?

- brad -
 
First, I want to output my "usbMIDI.sendNoteOn" and "usbMIDI.sendControlChange" to a 5-pin MIDI jack attached to pin 3.

You probably want to use this library:

http://www.pjrc.com/teensy/td_libs_MIDI.html

but how do I add code so that these MIDI messages will be sent via the USB connector (as it is now) AND the serial port SIMULTANEOUSLY?

Using that library, you'd write something like this:

Code:
  usbMIDI.sendNoteOn(note, 127, 1);                // sends to USB
  MIDI.sendControlChange(something, value, 1);     // sends to TX pin

You can also try writing directly to the serial port. But you must use Serial1. If use Serial, it sends to the Arduino Serial Monitor, which probably isn't what you want (except perhaps for testing). Use Serial1 to send to the TX pin.

If you use the MIDI library, it'll automatically use Serial1. It also gives you the same functions, so it's simpler than trying to compose your MIDI messages as raw bytes.
 
Thanks Paul.

So...am I safe in assuming the the "usb.MIDI" commands will ONLY send to USB and the "MIDI" commands will ONLY send to SERIAL (regardless of whether I have the MIDI library included)? Does that mean if I include that MIDI library in my sketch, to get simultaneous output to USB and SERIAL, I have to write similar "MIDI" lines for all of the "usbMIDI.sendNoteOn" and "usbMIDI.sendControlChange" lines in my sketch that are currently working to send MIDI via the USB?

ie:

Code:
 if (button7.fallingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(12 + (octave*12), vel, channel);       // C on USB
    MIDI.sendNoteOn(12 + (octave*12), vel, channel);           // C on serial MIDI
    delay(d);
    digitalWrite(6,HIGH);
  }

and

Code:
if (button35.fallingEdge()) {
    usbMIDI.sendControlChange(64, 127, channel);      // (type=64=sus, value=127=on, MIDI channel)
    MIDI.sendControlChange(64, 127, channel);          //  "sus" on serial MIDI
  }


If so, would it be more efficient to use the "serial write" method to write to Serial1? I have to admit that I still don't fully understand the serial write code that I have seen because everyone seems to create a void loop that defines the 2 or 3 parts of the MIDI message and then writes those three parts. There is only one line for, say, the "noteON" message or the "controlChange" message and yet that somehow sends ALL of the note or Control Change messages?
 
Last edited:
By the way, I just realized I have failed to follow the bold "forum rule" at the top of the page.

Here is my current code...it's not pretty and it's not efficient, but it's doing 99% of what I want to accomplish and I feel like Paul's latest tips on the MIDI library and/or writing to Serial1 can probably get me the rest of the way there once I understand how to incorporate them into this sketch:

Code:
/*
25 Key Midi Footpedal with
Octave Up/Down, Mod, Sustain, and Volume
Created by Brad Hill on 19-06-2015
based primarily on Teensy "Buttons" with snippets from
MidiHacker's MultiButtonMIDIOctave.ino,
Graham Wykes' Miditzer Pedalboard Scanner for Teensy2.0++ board,
Lionel Cassin's "midi-bass-pedals-using-arduino.html",
G333T's video (https://www.youtube.com/watch?v=9J_tGVLD7UQ),
and various Teensy/Arduino definitions and examples.
This code is released into the Public Domain.
*/


#include <Bounce.h>

const int channel = 1;        // the MIDI channel number to send messages
const int ms = 5;             // bounce time in ms (increase for more sensitve switches)
const int d = 25;             // default delay time (ms) for "activity" LED

int LEDU = 0;                 // pin for octave up LED
int LEDD = 1;                 // pin for ocatave down LED

int octaveup = 32;            // pin for octave up switch
int octavedown = 33;          // pin for octave down switch
int octave = 3;               // set base octave

int vpot = 38;                // pin setup for volume pot 
int vol;                      // place holder for vpot reading
int vel = 99;                 // fixed note velocity

// Create Bounce objects for each button.  The Bounce object
// automatically deals with contact chatter or "bounce", and
// it makes detecting changes very simple.

Bounce button0 = Bounce(0, ms);
Bounce button1 = Bounce(1, ms);  // 5 = 5 ms debounce time
Bounce button2 = Bounce(2, ms);  // which is appropriate for good
Bounce button3 = Bounce(3, ms);  // quality mechanical pushbuttons
Bounce button4 = Bounce(4, ms);
Bounce button5 = Bounce(5, ms);  // if a button is too "sensitive"
Bounce button6 = Bounce(6, ms);  // to rapid touch, you can
Bounce button7 = Bounce(7, ms);  // increase this time.
Bounce button8 = Bounce(8, ms);
Bounce button9 = Bounce(9, ms);
Bounce button10 = Bounce(10, ms);
Bounce button11 = Bounce(11, ms);
Bounce button12 = Bounce(12, ms);
Bounce button13 = Bounce(13, ms);  
Bounce button14 = Bounce(14, ms);  
Bounce button15 = Bounce(15, ms);  
Bounce button16 = Bounce(16, ms);
Bounce button17 = Bounce(17, ms);  
Bounce button18 = Bounce(18, ms);  
Bounce button19 = Bounce(19, ms);  
Bounce button20 = Bounce(20, ms);
Bounce button21 = Bounce(21, ms);
Bounce button22 = Bounce(22, ms);
Bounce button23 = Bounce(23, ms);
Bounce button24 = Bounce(24, ms);
Bounce button25 = Bounce(25, ms);  
Bounce button26 = Bounce(26, ms);  
Bounce button27 = Bounce(27, ms);  
Bounce button28 = Bounce(28, ms);
Bounce button29 = Bounce(29, ms);  
Bounce button30 = Bounce(30, ms);  
Bounce button31 = Bounce(31, ms);  
Bounce button32 = Bounce(32, ms);
Bounce button33 = Bounce(33, ms);
Bounce button34 = Bounce(34, ms);
Bounce button35 = Bounce(35, ms);
Bounce button36 = Bounce(36, ms);
Bounce button37 = Bounce(37, ms);  
Bounce button38 = Bounce(38, ms);  
Bounce button39 = Bounce(39, ms);  
Bounce button40 = Bounce(40, ms);
Bounce button41 = Bounce(41, ms);  
Bounce button42 = Bounce(42, ms);  
Bounce button43 = Bounce(43, ms);  
Bounce button44 = Bounce(44, ms);
Bounce button45 = Bounce(45, ms);



//--------------------------SETUP-----------------------------------------------
//------------------------------------------------------------------------------

void setup() {
  
  // Configure the pins for input mode with pullup resistors.
  // The pushbuttons connect from each pin to ground.  When
  // the button is pressed, the pin reads LOW because the button
  // shorts it to ground.  When released, the pin reads HIGH
  // because the pullup resistor connects to +5 volts inside
  // the chip.  LOW for "on", and HIGH for "off" may seem
  // backwards, but using the on-chip pullup resistors is very
  // convenient.  The scheme is called "active low", and it's
  // very commonly used in electronics... so much that the chip
  // has built-in pullup resistors!
  
  pinMode(0, OUTPUT);              // LED for "octave up"
  pinMode(1, OUTPUT);              // LED for "octave down"
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);        // TX output for MIDI Jack
  pinMode(4, INPUT_PULLUP);
  pinMode(5, INPUT_PULLUP);
  pinMode(6, OUTPUT);              // Teensy++ 2.0 LED, may need 1k resistor pullup
  pinMode(7, INPUT_PULLUP);
  pinMode(8, INPUT_PULLUP);
  pinMode(9, INPUT_PULLUP);
  pinMode(10, INPUT_PULLUP);
  pinMode(11, INPUT_PULLUP);
  pinMode(12, INPUT_PULLUP);
  pinMode(13, INPUT_PULLUP);
  pinMode(14, INPUT_PULLUP);
  pinMode(15, INPUT_PULLUP);
  pinMode(16, INPUT_PULLUP);
  pinMode(17, INPUT_PULLUP);
  pinMode(18, INPUT_PULLUP);
  pinMode(19, INPUT_PULLUP);
  pinMode(20, INPUT_PULLUP);
  pinMode(21, INPUT_PULLUP);
  pinMode(22, INPUT_PULLUP);
  pinMode(23, INPUT_PULLUP);
  pinMode(24, INPUT_PULLUP);
  pinMode(25, INPUT_PULLUP);
  pinMode(26, INPUT_PULLUP);
  pinMode(27, INPUT_PULLUP);
  pinMode(28, INPUT_PULLUP);
  pinMode(29, INPUT_PULLUP);
  pinMode(30, INPUT_PULLUP);
  pinMode(31, INPUT_PULLUP);
  pinMode(32, INPUT_PULLUP);
  pinMode(33, INPUT_PULLUP);
  pinMode(34, INPUT_PULLUP);
  pinMode(35, INPUT_PULLUP);
  pinMode(36, INPUT_PULLUP);
  pinMode(37, INPUT_PULLUP);
  pinMode(38, INPUT_PULLUP);
  pinMode(39, INPUT_PULLUP);
  pinMode(40, INPUT_PULLUP);
  pinMode(41, INPUT_PULLUP);
  pinMode(42, INPUT_PULLUP);
  pinMode(43, INPUT_PULLUP);
  pinMode(44, INPUT_PULLUP);
  pinMode(45, INPUT_PULLUP);
  
  digitalWrite(6,HIGH);                 // Set Teensy++ fixed LED to "on"
  digitalWrite(0,LOW);                  // Set "octave up" LED to "off"
  digitalWrite(1,LOW);                  // Set "octave down" LED to "off
}



//--------------------------MAIN LOOP-------------------------------------
//------------------------------------------------------------------------

void loop() {

  // Update all the buttons.  There should not be any long
  // delays in loop(), so this runs repetitively at a rate
  // faster than the buttons could be pressed and released.

  button0.update();
  button1.update();
  button2.update();
  button3.update();
  button4.update();
  button5.update();
  button6.update();
  button7.update();
  button8.update();
  button9.update();
  button10.update();
  button11.update();
  button12.update();
  button13.update();
  button14.update();
  button15.update();
  button16.update();
  button17.update();
  button18.update();
  button19.update();
  button20.update();
  button21.update();
  button22.update();
  button23.update();
  button24.update();
  button25.update();
  button26.update();
  button27.update();
  button28.update();
  button29.update();
  button30.update();
  button31.update();
  button32.update();
  button33.update();
  button34.update();
  button35.update();
  button36.update();
  button37.update();
  button38.update();
  button39.update();
  button40.update();
  button41.update();
  button42.update();
  button43.update();
  button44.update();
  button45.update();
  

//---------------------------------SET OCTAVE --------------------------------------------

  int up = digitalRead(octaveup);                     // alias "octaveup" pin read as "up"        
  int down = digitalRead(octavedown);                 // alias "octavedown" pin read as "down"
 
  if(up == LOW){                                      // if "up" button pressed
    if(octave < 6){       octave++;                   // constrain highest note to C8 (108)
                     }
     while(up == LOW){                                // wait until button is released
       up = digitalRead(octaveup);
       delay(20);
     }
  }
   if(down == LOW){                                   // if "down" button pressed
     if(octave > 1){      octave--;                   // constrain lowest note to C1 (24)
                      }
    while(down == LOW){                               // wait until button is released
      down = digitalRead(octavedown);
      delay(20);
    }
   }
   
   if(up && down == LOW){                             // if "up" and "down" buttons both pressed
     octave = 3;                                      // return to base octave
   }
   
   if(octave > 3){                                    // if the octave is above base
     digitalWrite(LEDU,HIGH);                         // turn on "octave up" LED
   } else {                                           // otherwise leave it off
     digitalWrite(LEDU,LOW);
   }
   if(octave < 3){                                    // if the current octave is below base
     digitalWrite(LEDD,HIGH);                         // turn on the "octave down" LED
   } else {                                           // otherwise leave it off
     digitalWrite(LEDD,LOW);
   }
     
     
  vol = analogRead(vpot);                             // Read velocity from the volume pot                               
  vel = map(vol, 0, 1023, 0, 127);                    // change the velocity range
                                                      // from "pot range" (0-1023)
                                                      // to "MIDI range" (0-127)


//----------------------READ BUTTON PINS FOR NOTE ON--------------------------------------------------

  // Check each button from 7 - 31 for "falling" edge.
  // Send a MIDI Note On message when each button presses
  // Update the Joystick buttons only upon changes.
  // falling = high (not pressed - voltage from pullup resistor)
  //           to low (pressed - button connects pin to ground)
 
  if (button7.fallingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(12 + (octave*12), vel, channel);  // C
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button8.fallingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(13 + (octave*12), vel, channel);  // C#
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button9.fallingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(14 + (octave*12), vel, channel);  // D
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button10.fallingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(15 + (octave*12), vel, channel);  // Eb
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button11.fallingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(16 + (octave*12), vel, channel);  // E
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button12.fallingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(17 + (octave*12), vel, channel);  // F
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button13.fallingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(18 + (octave*12), vel, channel);  // F
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button14.fallingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(19 + (octave*12), vel, channel);  // G
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button15.fallingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(20 + (octave*12), vel, channel);  // G#
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button16.fallingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(21 + (octave*12), vel, channel);  // A
    digitalWrite(6,HIGH);
  }
  if (button17.fallingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(22 + (octave*12), vel, channel);  // B
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button18.fallingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(23 + (octave*12), vel, channel);  // B
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button19.fallingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(24 + (octave*12), vel, channel);  // C
    digitalWrite(6,HIGH);
  }
  if (button20.fallingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(25 + (octave*12), vel, channel);  // C#
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button21.fallingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(26 + (octave*12), vel, channel);  // D
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button22.fallingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(27 + (octave*12), vel, channel);  // Eb
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button23.fallingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(28 + (octave*12), vel, channel);  // E
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button24.fallingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(29 + (octave*12), vel, channel);  // F
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button25.fallingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(30 + (octave*12), vel, channel);  // F#
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button26.fallingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(31 + (octave*12), vel, channel);  // G
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button27.fallingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(32 + (octave*12), vel, channel);  // G#
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button28.fallingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(33 + (octave*12), vel, channel);  // A
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button29.fallingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(34 + (octave*12), vel, channel);  // Bb
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button30.fallingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(35 + (octave*12), vel, channel);  // B
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button31.fallingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(36 + (octave*12), vel, channel);  // C
    delay(d);
    digitalWrite(6,HIGH);
  }


//----------------------READ BUTTON PINS FOR SUSTAIN AND MODULATION--------------

  // Check sus button for "falling" edge.
  // Send a MIDI CC message when button is pressed
  // Update the Joystick buttons only upon changes.
  // falling = high (not pressed - voltage from pullup resistor)
  //           to low (pressed - button connects pin to ground)
 
  if (button35.fallingEdge()) {
    usbMIDI.sendControlChange(64, 127, channel);      // (type=64=sus, value=127=on, MIDI channel)
  }  

  // Check mod button for "falling" edge.
  // Send a MIDI CC message when the button is pressed
  // Update the Joystick buttons only upon changes.
  // falling = high (not pressed - voltage from pullup resistor)
  //           to low (pressed - button connects pin to ground)
 
  if (button34.fallingEdge()) {
    usbMIDI.sendControlChange(1, 64, channel);      // (type=1=mod, value=64, MIDI channel)
  }  

  // Check mod button for "rising" edge.
  // Send a MIDI CC message when button is not pressed
  // Update the Joystick buttons only upon changes.
  // falling = high (not pressed - voltage from pullup resistor)
  //           to low (pressed - button connects pin to ground)
 
  if (button34.risingEdge()) {
    usbMIDI.sendControlChange(1, 0, channel);      // (type=1=mod, value=0=off, MIDI channel)
  }  

  // Check sus button for "rising" edge.
  // Send a MIDI CC message when button is not pressed
  // Update the Joystick buttons only upon changes.
  // rising = low (pressed - button connects pin to ground)
  //          to high (not pressed - voltage from pullup resistor)
  
  if (button35.risingEdge()) {
    usbMIDI.sendControlChange(64, 0, channel);      // (type=64=sus, value=0=off, MIDI channel)
  }  


//---------------READ BUTTON PINS FOR NOTE OFF-----------------------------------------

  // Check each button for "rising" edge
  // Send a MIDI Note Off message when each button releases
  // For many types of projects, you only care when the button
  // is pressed and the release isn't needed.
  // rising = low (pressed - button connects pin to ground)
  //          to high (not pressed - voltage from pullup resistor)
  
  if (button7.risingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(12 + (octave*12), 0, channel);  // C
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button8.risingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(13 + (octave*12), 0, channel);  // C#
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button9.risingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(14 + (octave*12), 0, channel);  // D
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button10.risingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(15 + (octave*12), 0, channel);  // Eb
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button11.risingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(16 + (octave*12), 0, channel);  // E
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button12.risingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(17 + (octave*12), 0, channel);  // F
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button13.risingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(18 + (octave*12), 0, channel);  // F#
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button14.risingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(19 + (octave*12), 0, channel);  // G
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button15.risingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(20 + (octave*12), 0, channel);  // G#
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button16.risingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(21 + (octave*12), 0, channel);  // A
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button17.risingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(22 + (octave*12), 0, channel);  // Bb
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button18.risingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(23 + (octave*12), 0, channel);  // B
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button19.risingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(24 + (octave*12), 0, channel);  // C
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button20.risingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(25 + (octave*12), 0, channel);  // C#
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button21.risingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(26 + (octave*12), 0, channel);  // D
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button22.risingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(27 + (octave*12), 0, channel);  // Eb
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button23.risingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(28 + (octave*12), 0, channel);  // E
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button24.risingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(29 + (octave*12), 0, channel);  // F
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button25.risingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(30 + (octave*12), 0, channel);  // F#
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button26.risingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(31 + (octave*12), 0, channel);  // G
    digitalWrite(6,HIGH);
  }
  if (button27.risingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(32 + (octave*12), 0, channel);  // G#
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button28.risingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(33 + (octave*12), 0, channel);  // A
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button29.risingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(34 + (octave*12), 0, channel);  // Bb
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button30.risingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(35 + (octave*12), 0, channel);  // B
    delay(d);
    digitalWrite(6,HIGH);
  }
  if (button31.risingEdge()) {
    digitalWrite(6,LOW);
    usbMIDI.sendNoteOn(36 + (octave*12), 0, channel);  // C
    delay(d);
    digitalWrite(6,HIGH);
  }

  // MIDI Controllers should discard incoming MIDI messages.
  // http://forum.pjrc.com/threads/24179-Teensy-3-Ableton-Analog-CC-causes-midi-crash
  
  while (usbMIDI.read()) {               // ignore incoming messages
  }
}
 
Brad,
I've modified your code to use for loops in a few places which reduces the size of the code and should make it more readable.
Code:
/*
  https://forum.pjrc.com/threads/28816
25 Key Midi Footpedal with
Octave Up/Down, Mod, Sustain, and Volume
Created by Brad Hill on 19-06-2015
based primarily on Teensy "Buttons" with snippets from
MidiHacker's MultiButtonMIDIOctave.ino,
Graham Wykes' Miditzer Pedalboard Scanner for Teensy2.0++ board,
Lionel Cassin's "midi-bass-pedals-using-arduino.html",
G333T's video (https://www.youtube.com/watch?v=9J_tGVLD7UQ),
and various Teensy/Arduino definitions and examples.
This code is released into the Public Domain.
*/


#include <Bounce.h>

const int channel = 1;        // the MIDI channel number to send messages
const int ms = 5;             // bounce time in ms (increase for more sensitve switches)
const int d = 25;             // default delay time (ms) for "activity" LED

int LEDU = 0;                 // pin for octave up LED
int LEDD = 1;                 // pin for ocatave down LED

int octaveup = 32;            // pin for octave up switch
int octavedown = 33;          // pin for octave down switch
int octave = 3;               // set base octave

int vpot = 38;                // pin setup for volume pot
int vol;                      // place holder for vpot reading
int vel = 99;                 // fixed note velocity

// Create Bounce objects for each button.  The Bounce object
// automatically deals with contact chatter or "bounce", and
// it makes detecting changes very simple.

Bounce button0 = Bounce(0, ms);
Bounce button1 = Bounce(1, ms);  // 5 = 5 ms debounce time
Bounce button2 = Bounce(2, ms);  // which is appropriate for good
Bounce button3 = Bounce(3, ms);  // quality mechanical pushbuttons
Bounce button4 = Bounce(4, ms);
Bounce button5 = Bounce(5, ms);  // if a button is too "sensitive"
Bounce button6 = Bounce(6, ms);  // to rapid touch, you can
Bounce button7 = Bounce(7, ms);  // increase this time.
Bounce button8 = Bounce(8, ms);
Bounce button9 = Bounce(9, ms);
Bounce button10 = Bounce(10, ms);
Bounce button11 = Bounce(11, ms);
Bounce button12 = Bounce(12, ms);
Bounce button13 = Bounce(13, ms);
Bounce button14 = Bounce(14, ms);
Bounce button15 = Bounce(15, ms);
Bounce button16 = Bounce(16, ms);
Bounce button17 = Bounce(17, ms);
Bounce button18 = Bounce(18, ms);
Bounce button19 = Bounce(19, ms);
Bounce button20 = Bounce(20, ms);
Bounce button21 = Bounce(21, ms);
Bounce button22 = Bounce(22, ms);
Bounce button23 = Bounce(23, ms);
Bounce button24 = Bounce(24, ms);
Bounce button25 = Bounce(25, ms);
Bounce button26 = Bounce(26, ms);
Bounce button27 = Bounce(27, ms);
Bounce button28 = Bounce(28, ms);
Bounce button29 = Bounce(29, ms);
Bounce button30 = Bounce(30, ms);
Bounce button31 = Bounce(31, ms);
Bounce button32 = Bounce(32, ms);
Bounce button33 = Bounce(33, ms);
Bounce button34 = Bounce(34, ms);
Bounce button35 = Bounce(35, ms);
Bounce button36 = Bounce(36, ms);
Bounce button37 = Bounce(37, ms);
Bounce button38 = Bounce(38, ms);
Bounce button39 = Bounce(39, ms);
Bounce button40 = Bounce(40, ms);
Bounce button41 = Bounce(41, ms);
Bounce button42 = Bounce(42, ms);
Bounce button43 = Bounce(43, ms);
Bounce button44 = Bounce(44, ms);
Bounce button45 = Bounce(45, ms);

// Declaring the addresses of the button objects in this array
// allows us to deal with them in a for loop
Bounce *buttons[46] = {
  &button0, &button1, &button2, &button3, &button4, &button5, &button6, &button7, &button8, &button9,
  &button10, &button11, &button12, &button13, &button14, &button15, &button16, &button17, &button18, &button19,
  &button20, &button21, &button22, &button23, &button24, &button25, &button26, &button27, &button28, &button29,
  &button30, &button31, &button32, &button33, &button34, &button35, &button36, &button37, &button38, &button39,
  &button40, &button41, &button42, &button43, &button44, &button45
};



//--------------------------SETUP-----------------------------------------------
//------------------------------------------------------------------------------

void setup() {

  // Configure the pins for input mode with pullup resistors.
  // The pushbuttons connect from each pin to ground.  When
  // the button is pressed, the pin reads LOW because the button
  // shorts it to ground.  When released, the pin reads HIGH
  // because the pullup resistor connects to +5 volts inside
  // the chip.  LOW for "on", and HIGH for "off" may seem
  // backwards, but using the on-chip pullup resistors is very
  // convenient.  The scheme is called "active low", and it's
  // very commonly used in electronics... so much that the chip
  // has built-in pullup resistors!

  pinMode(0, OUTPUT);              // LED for "octave up"
  pinMode(1, OUTPUT);              // LED for "octave down"
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);        // TX output for MIDI Jack
  pinMode(4, INPUT_PULLUP);
  pinMode(5, INPUT_PULLUP);
  pinMode(6, OUTPUT);              // Teensy++ 2.0 LED, may need 1k resistor pullup

  // pins 7 to 45 are all INPUT_PULLUP
  for (int i = 7; i <= 45; i++) {
    pinMode(i, INPUT_PULLUP);
  }

  digitalWrite(6, HIGH);                // Set Teensy++ fixed LED to "on"
  digitalWrite(0, LOW);                 // Set "octave up" LED to "off"
  digitalWrite(1, LOW);                 // Set "octave down" LED to "off
}



//--------------------------MAIN LOOP-------------------------------------
//------------------------------------------------------------------------

void loop() {

  // Update all the buttons.  There should not be any long
  // delays in loop(), so this runs repetitively at a rate
  // faster than the buttons could be pressed and released.

  for (int i = 0; i <= 45; i++) {
    buttons[i]->update();
  }


  //---------------------------------SET OCTAVE --------------------------------------------

  int up = digitalRead(octaveup);                     // alias "octaveup" pin read as "up"
  int down = digitalRead(octavedown);                 // alias "octavedown" pin read as "down"

  if (up == LOW) {                                    // if "up" button pressed
    if (octave < 6) {
      octave++;                   // constrain highest note to C8 (108)
    }
    while (up == LOW) {                              // wait until button is released
      up = digitalRead(octaveup);
      delay(20);
    }
  }
  if (down == LOW) {                                 // if "down" button pressed
    if (octave > 1) {
      octave--;                   // constrain lowest note to C1 (24)
    }
    while (down == LOW) {                             // wait until button is released
      down = digitalRead(octavedown);
      delay(20);
    }
  }

  if (up && down == LOW) {                           // if "up" and "down" buttons both pressed
    octave = 3;                                      // return to base octave
  }

  if (octave > 3) {                                  // if the octave is above base
    digitalWrite(LEDU, HIGH);                        // turn on "octave up" LED
  } else {                                           // otherwise leave it off
    digitalWrite(LEDU, LOW);
  }
  if (octave < 3) {                                  // if the current octave is below base
    digitalWrite(LEDD, HIGH);                        // turn on the "octave down" LED
  } else {                                           // otherwise leave it off
    digitalWrite(LEDD, LOW);
  }


  vol = analogRead(vpot);                             // Read velocity from the volume pot
  vel = map(vol, 0, 1023, 0, 127);                    // change the velocity range
  // from "pot range" (0-1023)
  // to "MIDI range" (0-127)


  //----------------------READ BUTTON PINS FOR NOTE ON--------------------------------------------------

  // Check each button from 7 - 31 for "falling" edge.
  // Send a MIDI Note On message when each button presses
  // Update the Joystick buttons only upon changes.
  // falling = high (not pressed - voltage from pullup resistor)
  //           to low (pressed - button connects pin to ground)

  for (int i = 7; i < 31; i++) {
    if (buttons[i]->fallingEdge()) {
      digitalWrite(6, LOW);
      usbMIDI.sendNoteOn(i + 5 + (octave * 12), vel, channel); // C
      delay(d);
      digitalWrite(6, HIGH);
    }
  }


  //----------------------READ BUTTON PINS FOR SUSTAIN AND MODULATION--------------

  // Check sus button for "falling" edge.
  // Send a MIDI CC message when button is pressed
  // Update the Joystick buttons only upon changes.
  // falling = high (not pressed - voltage from pullup resistor)
  //           to low (pressed - button connects pin to ground)

  if (button35.fallingEdge()) {
    usbMIDI.sendControlChange(64, 127, channel);      // (type=64=sus, value=127=on, MIDI channel)
  }

  // Check mod button for "falling" edge.
  // Send a MIDI CC message when the button is pressed
  // Update the Joystick buttons only upon changes.
  // falling = high (not pressed - voltage from pullup resistor)
  //           to low (pressed - button connects pin to ground)

  if (button34.fallingEdge()) {
    usbMIDI.sendControlChange(1, 64, channel);      // (type=1=mod, value=64, MIDI channel)
  }

  // Check mod button for "rising" edge.
  // Send a MIDI CC message when button is not pressed
  // Update the Joystick buttons only upon changes.
  // falling = high (not pressed - voltage from pullup resistor)
  //           to low (pressed - button connects pin to ground)

  if (button34.risingEdge()) {
    usbMIDI.sendControlChange(1, 0, channel);      // (type=1=mod, value=0=off, MIDI channel)
  }

  // Check sus button for "rising" edge.
  // Send a MIDI CC message when button is not pressed
  // Update the Joystick buttons only upon changes.
  // rising = low (pressed - button connects pin to ground)
  //          to high (not pressed - voltage from pullup resistor)

  if (button35.risingEdge()) {
    usbMIDI.sendControlChange(64, 0, channel);      // (type=64=sus, value=0=off, MIDI channel)
  }


  //---------------READ BUTTON PINS FOR NOTE OFF-----------------------------------------

  // Check each button for "rising" edge
  // Send a MIDI Note Off message when each button releases
  // For many types of projects, you only care when the button
  // is pressed and the release isn't needed.
  // rising = low (pressed - button connects pin to ground)
  //          to high (not pressed - voltage from pullup resistor)


  for (int i = 7; i < 31; i++) {
    if (buttons[i]->risingEdge()) {
      digitalWrite(6, LOW);
      usbMIDI.sendNoteOn(i + 5 + (octave * 12), vel, channel); // C
      delay(d);
      digitalWrite(6, HIGH);
    }
  }


  // MIDI Controllers should discard incoming MIDI messages.
  // http://forum.pjrc.com/threads/24179-Teensy-3-Ableton-Analog-CC-causes-midi-crash

  while (usbMIDI.read()) {               // ignore incoming messages
  }
}

It compiles but I have no way to test this. You'll have to try it out to see if functions in the same way as your version.

But there are a couple of other things to look at:
- pins 0, 1 and 6 are LED outputs, pin 3 is the Tx MIDI output and pin 38 is an analog input so there's no point declaring Bounce objects for them because they aren't digital inputs. There may be other pins which don't need to be Bounce objects too.
- I don't think you are handling the "SET OCTAVE" code properly. If we consider just the "up" variable, you have this code:
Code:
  int up = digitalRead(octaveup);                     // alias "octaveup" pin read as "up"
  int down = digitalRead(octavedown);                 // alias "octavedown" pin read as "down"

  if (up == LOW) {                                    // if "up" button pressed
    if (octave < 6) {
      octave++;                   // constrain highest note to C8 (108)
    }
    while (up == LOW) {                              // wait until button is released
      up = digitalRead(octaveup);
      delay(20);
    }
  }

If the up pin is HIGH, then the result of this code is that "up" will be HIGH. But if it is LOW, you execute a while loop which doesn't exit until it has set "up" HIGH.
So, either way "up" will always be HIGH after this segment of code. The same thing happens to "down". Then when you get to this segment:
Code:
  if (up && down == LOW) {                           // if "up" and "down" buttons both pressed
    octave = 3;                                      // return to base octave
  }
octave can never be set to 3 because both up and down are always HIGH.

@anyone
I don't know C++ very well. Is there any way to abbreviate all these instantiations:
Code:
Bounce button0 = Bounce(0, ms);
Bounce button1 = Bounce(1, ms);  // 5 = 5 ms debounce time
Bounce button2 = Bounce(2, ms);  // which is appropriate for good
Bounce button3 = Bounce(3, ms);  // quality mechanical pushbuttons
Bounce button4 = Bounce(4, ms);
.
.
.
etc.
so that you don't have to write out every single one of them?

Pete
 
hey, I'm anyone, tho I wouldn't claim expertise in any particular language; I do not see how, one way or another you need to instantiate the object(s) and one way or another you have to let the objects know the pins (and other detail(s)) to use.

If the object has a constructor with no arguments then you could instantiate an array of them but then you need to 'tell' them the basic info they need to do their job anyway - if the pins really are contiguous (suitable array of pin numbers allows this too tho) then you might be able to 'load' them in a loop, if you can instantiate them as an array.

The Audio Library has quite a few objects that can be instantiated as an array, the AudioFilterBiquad is one I (reasonably) regularly make an array of.

Of course my non-expertise leaves the question open in my own mind and I have partially given my understanding to subscribe the thread so I will notice if anybody comes along with better information :)


I hate that bounce library, I have my own I spun up last time that one disappointed me, I should add it to my github account and see if anyone else likes it any better than that original.
 
P.S. Brad,
It is easy to fix the problem with the "up" and "down". Move the if statement which tests their state and sets "octave" so that it is immediately after the pins are read. But also, correct the way that their state is tested. You didn't have it quite right:
Code:
  int up = digitalRead(octaveup);                     // alias "octaveup" pin read as "up"
  int down = digitalRead(octavedown);                 // alias "octavedown" pin read as "down"

  if (up == LOW && down == LOW) {                           // if "up" and "down" buttons both pressed
    octave = 3;                                      // return to base octave
  }

Pete
 
Thanks guys...I REALLY appreciate the feedback.

I have to admit that the "octave" portion of the code is the one spot where I didn't fully understand all the code (particularly the "while" section!). On top of that, I took the "up" and "down" portion from one source and the "reset with both buttons pushed" from another source so I'm not THAT surprised if they weren't 100% compatible the way I had them.

So Pete, are you saying that I can just move the "return to base" section on top of the "up button" and "down button" section and everything should work OK (given the edit to "if (up == LOW && down == LOW)" of course? Do I need to put the three octave sections ("both LOW", "up LOW", "down LOW") into some kind of of "if+else if" code to prevent my code from carrying out two scenarios (i.e....code sees BOTH buttons pushed which tells it to set octave=3 but also sees "up LOW" which tells it to increase the octave)?

I will also give the "for loops" a try. That is definitely the idea I was getting at; however, when I tried to implement that approach in an earlier version of my code, it wouldn't even compile let alone function properly (although I'm sure I simply didn't write the "for loops" properly)! The good news is that my "inefficient" code seems to be working quite well so, if I can't get the loops working or...as robsoles says...if they just mean that I have to provide all of the same information in another place, then I'm content to live with "inefficient" code...mostly because it works but also because my feeble brain understands what it is doing.

- brad -
 
@debrad. First, please just run the code I posted in #14 and compare how it works with what you have. I want to make sure that I haven't introduced any new bugs before processing.
If it works the same as yours, then we can start fixing bugs.

Pete
 
@debrad. First, please just run the code I posted in #14 and compare how it works with what you have. I want to make sure that I haven't introduced any new bugs before processing.
If it works the same as yours, then we can start fixing bugs.

Pete,

Your code tested just fine...with one SMALL exception:

I noticed that my highest note on Pin31 was not playing. Took a quick look and noticed you had written:

Code:
for (int i = 7; i < 31; i++) {

so I changed that to read:

Code:
for (int i = 7; i <= 31; i++) {

and I got my highest note back (plus I got the answer to one of my earlier questions!).

Love how the new code is a little more concise...especially the NoteOn/NoteOff sections. If all I have to do is add a similar "Midi.sendNoteON" and "Midi.sendNoteOff" message while using the Midi Library to send simultaneous MIDI messages to a serial 5-DIN MIDI jack then this will help immensely!

Unfortunately, I can't fully test the octave section because I don't have my switches connected to the Teensy yet. I do have a couple switches that I can quickly connect though...

- brad -
 
Last edited:
Oooops. That error occurs twice - in the noteon and noteoff loops. AND there's a cut-and-paste error in the noteoff "for" loop at the end of the loop() function. The sendNoteOn should send a velocity of zero - not "vel":
Code:
      usbMIDI.sendNoteOn(i + 5 + (octave * 12), 0, channel);

Pete
 
Last edited:
I'd suggest you handle the usbMidi. and Midi. by adding your own function to handle sendNoteOn. Something like this:
Code:
void do_sendNoteOn(unsigned char note,unsigned char velocity,unsigned char channel)
{
  usbMIDI.sendNoteOn(note, velocity, channel);
}
Then change each occurrence of usbMIDI.sendNoteOn(...) to do_sendNoteOn(...). Do the same kind of thing for sendControlChange. Then if you want to add an output to Midi. as well as to usbMIDI you only need change the functions, e.g. to do this:
Code:
do_sendNoteOn(unsigned char note,unsigned char velocity,unsigned char channel)
{
  usbMIDI.sendNoteOn(note, velocity, channel);
  Midi.sendNoteOn(note, velocity, channel);
}
and do the same for sendControlChange.

Pete
 
Last edited:
I wish I could say I caught ALL of those...but, at best, I might have caught both "<=" (I'll double check...), but I'll find and fix the "vel".

Your suggestions for usb and serial "NoteOn" functions are EXACTLY what I keep seeing in other sketches. I think I understand the point of that coding but I'm struggling to understand how/where to place it in my sketch...would they go somewhere AFTER my "edge detect" code? would they REPLACE part(s) of my "edge detect" code? are they in the "void loop"? are they in that bundle of "void" coding that I sometimes see AFTER the "void loop".

My apologies for being so dense on this one...unfortunately, this is where I probably need the most "hand holding"...

Say, on a somewhat related tangent...

If I am currently sending MIDI via usb, is it possible for me to connect one of those MIDI/USB cables so that the USB end is connected to the Teensy and the MIDI is connected to the 5-DIN MIDI "in" jack on whatever device I want to send my MIDI messages to (basically the OPPOSITE way I am using the same cable to connect my Casio keyboard to my PC)? If this would work, then I really don't need to worry about the serial MIDI jack...I could send USB directly from the Teensy to my PC and send via this cable to devices that only have 5-DIN jacks...just a thought...

- brad -


- brad -
 
the do_sendNoteOn and do_sendControlChange functions can go before the setup() function.
Note that I've edited the code for the do_sendNoteOn to include the void type specifier before the function name because the function doesn't return a value.

Pete
 
Thanks Pete.

I really appreciate all your help with this...plus I apologize for not staying 100% on track with my questioning. You sorted out my "verbose" code issue with the "for loops" (not sure I would have EVER figured out that I need those arrows!) and we were talking about the "octave" section but I guess I should sort out this "simultaneous USB + serial MIDI" part while we are talking about it and then tackle the octave (...a) because that "reset to octave 3" function is really just a "nice to have"...and b) because I got the feeling it was a pretty quick/easy fix).

OK...so based on your "do" function definition, would my code then read something like this (I've added the MIDI Library "include" as well as the "MIDI.begin" line in the Setup loop...hopefully that is correct):

Code:
/*
https://forum.pjrc.com/threads/28816
25 Key Midi Footpedal with
Octave Up/Down, Mod, Sustain, and Volume
Created by Brad Hill on 19-06-2015
based primarily on Teensy "Buttons" with snippets from
MidiHacker's MultiButtonMIDIOctave.ino,
Graham Wykes' Miditzer Pedalboard Scanner for Teensy2.0++ board,
Lionel Cassin's "midi-bass-pedals-using-arduino.html",
G333T's video (https://www.youtube.com/watch?v=9J_tGVLD7UQ),
and various Teensy/Arduino definitions and examples.
This code is released into the Public Domain.
*/


#include <MIDI.h>
#include <Bounce.h>

const int channel = 1;        // the MIDI channel number to send messages
const int ms = 5;             // bounce time in ms (increase for more sensitve switches)
const int d = 25;             // default delay time (ms) for "activity" LED

int LEDU = 0;                 // pin for octave up LED
int LEDD = 1;                 // pin for ocatave down LED

int octaveup = 32;            // pin for octave up switch
int octavedown = 33;          // pin for octave down switch
int octave = 3;               // set base octave

int vpot = 38;                // pin setup for volume pot
int vol;                      // place holder for vpot reading
int vel = 99;                 // fixed note velocity

// Create Bounce objects for each button.  The Bounce object
// automatically deals with contact chatter or "bounce", and
// it makes detecting changes very simple.

Bounce button0 = Bounce(0, ms);
Bounce button1 = Bounce(1, ms);  // 5 = 5 ms debounce time
Bounce button2 = Bounce(2, ms);  // which is appropriate for good
Bounce button3 = Bounce(3, ms);  // quality mechanical pushbuttons
Bounce button4 = Bounce(4, ms);
Bounce button5 = Bounce(5, ms);  // if a button is too "sensitive"
Bounce button6 = Bounce(6, ms);  // to rapid touch, you can
Bounce button7 = Bounce(7, ms);  // increase this time.
Bounce button8 = Bounce(8, ms);
Bounce button9 = Bounce(9, ms);
Bounce button10 = Bounce(10, ms);
Bounce button11 = Bounce(11, ms);
Bounce button12 = Bounce(12, ms);
Bounce button13 = Bounce(13, ms);
Bounce button14 = Bounce(14, ms);
Bounce button15 = Bounce(15, ms);
Bounce button16 = Bounce(16, ms);
Bounce button17 = Bounce(17, ms);
Bounce button18 = Bounce(18, ms);
Bounce button19 = Bounce(19, ms);
Bounce button20 = Bounce(20, ms);
Bounce button21 = Bounce(21, ms);
Bounce button22 = Bounce(22, ms);
Bounce button23 = Bounce(23, ms);
Bounce button24 = Bounce(24, ms);
Bounce button25 = Bounce(25, ms);
Bounce button26 = Bounce(26, ms);
Bounce button27 = Bounce(27, ms);
Bounce button28 = Bounce(28, ms);
Bounce button29 = Bounce(29, ms);
Bounce button30 = Bounce(30, ms);
Bounce button31 = Bounce(31, ms);
Bounce button32 = Bounce(32, ms);
Bounce button33 = Bounce(33, ms);
Bounce button34 = Bounce(34, ms);
Bounce button35 = Bounce(35, ms);
Bounce button36 = Bounce(36, ms);
Bounce button37 = Bounce(37, ms);
Bounce button38 = Bounce(38, ms);
Bounce button39 = Bounce(39, ms);
Bounce button40 = Bounce(40, ms);
Bounce button41 = Bounce(41, ms);
Bounce button42 = Bounce(42, ms);
Bounce button43 = Bounce(43, ms);
Bounce button44 = Bounce(44, ms);
Bounce button45 = Bounce(45, ms);


// Declaring the addresses of the button objects in this array
// allows us to deal with them in a for loop
Bounce *buttons[46] = {
  &button0, &button1, &button2, &button3, &button4, &button5, &button6, &button7, &button8, &button9,
  &button10, &button11, &button12, &button13, &button14, &button15, &button16, &button17, &button18, &button19,
  &button20, &button21, &button22, &button23, &button24, &button25, &button26, &button27, &button28, &button29,
  &button30, &button31, &button32, &button33, &button34, &button35, &button36, &button37, &button38, &button39,
  &button40, &button41, &button42, &button43, &button44, &button45
};


// Create functions to send usbMIDI and serial MIDI messages

void do_sendNoteOn(unsigned char note,unsigned char velocity,unsigned char channel)
{
  usbMIDI.sendNoteOn(note, velocity, channel);
  MIDI.sendNoteOn(note, velocity, channel);
}

void do_sendControlChange(unsigned char cmd,unsigned char value,unsigned char channel)
{
  usbMIDI.sendControlChange(cmd, value, channel);
  MIDI.sendControlChange(cmd, value, channel);
}



//--------------------------SETUP-----------------------------------------------
//------------------------------------------------------------------------------

void setup() {

 MIDI.begin();  

  // Configure the pins for input mode with pullup resistors.
  // The pushbuttons connect from each pin to ground.  When
  // the button is pressed, the pin reads LOW because the button
  // shorts it to ground.  When released, the pin reads HIGH
  // because the pullup resistor connects to +5 volts inside
  // the chip.  LOW for "on", and HIGH for "off" may seem
  // backwards, but using the on-chip pullup resistors is very
  // convenient.  The scheme is called "active low", and it's
  // very commonly used in electronics... so much that the chip
  // has built-in pullup resistors!

  pinMode(0, OUTPUT);              // LED for "octave up"
  pinMode(1, OUTPUT);              // LED for "octave down"
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);        // TX output for MIDI Jack
  pinMode(4, INPUT_PULLUP);
  pinMode(5, INPUT_PULLUP);
  pinMode(6, OUTPUT);              // Teensy++ 2.0 LED, may need 1k resistor pullup

  // pins 7 to 45 are all INPUT_PULLUP
  for (int i = 7; i <= 45; i++) {
    pinMode(i, INPUT_PULLUP);
  }

  digitalWrite(6, HIGH);                // Set Teensy++ fixed LED to "on"
  digitalWrite(0, LOW);                 // Set "octave up" LED to "off"
  digitalWrite(1, LOW);                 // Set "octave down" LED to "off
}



//--------------------------MAIN LOOP-------------------------------------
//------------------------------------------------------------------------

void loop() {

  // Update all the buttons.  There should not be any long
  // delays in loop(), so this runs repetitively at a rate
  // faster than the buttons could be pressed and released.

  for (int i = 0; i <= 45; i++) {
    buttons[i]->update();
  }


  //---------------------------------SET OCTAVE --------------------------------------------

  int up = digitalRead(octaveup);                     // alias "octaveup" pin read as "up"
  int down = digitalRead(octavedown);                 // alias "octavedown" pin read as "down"

  if (up == LOW) {                                    // if "up" button pressed
    if (octave < 6) {
      octave++;                   // constrain highest note to C8 (108)
    }
    while (up == LOW) {                              // wait until button is released
      up = digitalRead(octaveup);
      delay(20);
    }
  }
  if (down == LOW) {                                 // if "down" button pressed
    if (octave > 1) {
      octave--;                   // constrain lowest note to C1 (24)
    }
    while (down == LOW) {                             // wait until button is released
      down = digitalRead(octavedown);
      delay(20);
    }
  }

  if (up && down == LOW) {                           // if "up" and "down" buttons both pressed
    octave = 3;                                      // return to base octave
  }

  if (octave > 3) {                                  // if the octave is above base
    digitalWrite(LEDU, HIGH);                        // turn on "octave up" LED
  } else {                                           // otherwise leave it off
    digitalWrite(LEDU, LOW);
  }
  if (octave < 3) {                                  // if the current octave is below base
    digitalWrite(LEDD, HIGH);                        // turn on the "octave down" LED
  } else {                                           // otherwise leave it off
    digitalWrite(LEDD, LOW);
  }


  vol = analogRead(vpot);                             // Read velocity from the volume pot
  vel = map(vol, 0, 1023, 0, 127);                    // change the velocity range
  // from "pot range" (0-1023)
  // to "MIDI range" (0-127)


  //----------------------READ BUTTON PINS FOR NOTE ON--------------------------------------------------

  // Check each button from 7 - 31 for "falling" edge.
  // Send a MIDI Note On message when each button presses
  // Update the Joystick buttons only upon changes.
  // falling = high (not pressed - voltage from pullup resistor)
  //           to low (pressed - button connects pin to ground)

  for (int i = 7; i <= 31; i++) {
    if (buttons[i]->fallingEdge()) {
      digitalWrite(6, LOW);
      do_sendNoteOn(i + 5 + (octave * 12), vel, channel); // C
      delay(d);
      digitalWrite(6, HIGH);
    }
  }


  //----------------------READ BUTTON PINS FOR SUSTAIN AND MODULATION--------------

  // Check sus button for "falling" edge.
  // Send a MIDI CC message when button is pressed
  // Update the Joystick buttons only upon changes.
  // falling = high (not pressed - voltage from pullup resistor)
  //           to low (pressed - button connects pin to ground)

  if (button35.fallingEdge()) {
    do_sendControlChange(64, 127, channel);      // (type=64=sus, value=127=on, MIDI channel)
  }

  // Check mod button for "falling" edge.
  // Send a MIDI CC message when the button is pressed
  // Update the Joystick buttons only upon changes.
  // falling = high (not pressed - voltage from pullup resistor)
  //           to low (pressed - button connects pin to ground)

  if (button34.fallingEdge()) {
    do_sendControlChange(1, 64, channel);      // (type=1=mod, value=64, MIDI channel)
  }

  // Check mod button for "rising" edge.
  // Send a MIDI CC message when button is not pressed
  // Update the Joystick buttons only upon changes.
  // falling = high (not pressed - voltage from pullup resistor)
  //           to low (pressed - button connects pin to ground)

  if (button34.risingEdge()) {
    do_sendControlChange(1, 0, channel);      // (type=1=mod, value=0=off, MIDI channel)
  }

  // Check sus button for "rising" edge.
  // Send a MIDI CC message when button is not pressed
  // Update the Joystick buttons only upon changes.
  // rising = low (pressed - button connects pin to ground)
  //          to high (not pressed - voltage from pullup resistor)

  if (button35.risingEdge()) {
    do_sendControlChange(64, 0, channel);      // (type=64=sus, value=0=off, MIDI channel)
  }


  //---------------READ BUTTON PINS FOR NOTE OFF-----------------------------------------

  // Check each button for "rising" edge
  // Send a MIDI Note Off message when each button releases
  // For many types of projects, you only care when the button
  // is pressed and the release isn't needed.
  // rising = low (pressed - button connects pin to ground)
  //          to high (not pressed - voltage from pullup resistor)


  for (int i = 7; i <= 31; i++) {
    if (buttons[i]->risingEdge()) {
      digitalWrite(6, LOW);
      do_sendNoteOn(i + 5 + (octave * 12), 0, channel); // C
      delay(d);
      digitalWrite(6, HIGH);
    }
  }


  // MIDI Controllers should discard incoming MIDI messages.
  // http://forum.pjrc.com/threads/24179-Teensy-3-Ableton-Analog-CC-causes-midi-crash

  while (usbMIDI.read()) {               // ignore incoming messages
  }
}


So far so good? If so, do I now just add serial output in the forum of "serial1 begin" (with the MIDI baud rate) and using "serial1.Write" code?
 
If my insertion of the "do_SendNoteOn" and "do_SendControlChange" functions are correct, do they alone send simultaneous midi messages to the USB and hardware serial ports or do I need some additional code?
 
Status
Not open for further replies.
Back
Top