Midi Guitar Pedal with Arduino Midi USB Library

With the sketch provided from the tutorial: MIDI Device that uses the library Midi USB I was able to play the mapped notes, with the pot acting as intensity of the note, which is packaged and sent as a Note on/Note off command. Exactly what it is intended for.

The Guitar Pro Software Has a Learn Button to capture the notes and assign to functions, such as enable/disable custom effects. This already works fine.
One thing I noticed is that when the pot is set to minimum the buttons do not work, so the software is recording the note intensity value learned.

What I want to achieve is to make the potentiometer to control the volume slider or wah wah pedal, the software learns when a button is pressed but just goes on or off, the note intensity pot does not drive the gradative slider. The provided sketch example does not include a proper control for that.

Previous Repply from Library dev:
I see, you have to develop a program (your starting example is fine) that convert analog input (i.e. your pot) to a range that can be sent via the controlChange() function in your example, not the note on/off functions. The value you would read from say, the analog arduino port A0, would be sent via this function using the ‘value’ parameter that now would be your analog input value (converted to a range suitable for the midi cc “Control Change” range).

Can Someone Help me?

Midi Message Commands:
Specs

Example Sketch from:

/*
   This examples shows how to make a simple seven keys MIDI keyboard with volume control

   Created: 4/10/2015
   Author: Arturo Guadalupi <a.guadalupi@arduino.cc>
   
   http://www.arduino.cc/en/Tutorial/MidiDevice
*/

#include "MIDIUSB.h"
#include "PitchToNote.h"
#define NUM_BUTTONS  7

const uint8_t button1 = 2;
const uint8_t button2 = 3;
const uint8_t button3 = 4;
const uint8_t button4 = 5;
const uint8_t button5 = 6;
const uint8_t button6 = 7;
const uint8_t button7 = 8;

const int intensityPot = 0;  //A0 input

const uint8_t buttons[NUM_BUTTONS] = {button1, button2, button3, button4, button5, button6, button7};
const byte notePitches[NUM_BUTTONS] = {C3, D3, E3, F3, G3, A3, B3};

uint8_t notesTime[NUM_BUTTONS];
uint8_t pressedButtons = 0x00;
uint8_t previousButtons = 0x00;
uint8_t intensity;

void setup() {
  for (int i = 0; i < NUM_BUTTONS; i++)
    pinMode(buttons[i], INPUT_PULLUP);
}


void loop() {
  readButtons();
  readIntensity();
  playNotes();
}

// First parameter is the event type (0x0B = control change).
// Second parameter is the event type, combined with the channel.
// Third parameter is the control number number (0-119).
// Fourth parameter is the control value (0-127).

void controlChange(byte channel, byte control, byte value) {
  midiEventPacket_t event = {0x0B, 0xB0 | channel, control, value};
  MidiUSB.sendMIDI(event);
}

void readButtons()
{
  for (int i = 0; i < NUM_BUTTONS; i++)
  {
    if (digitalRead(buttons[i]) == LOW)
    {
      bitWrite(pressedButtons, i, 1);
      delay(50);
    }
    else
      bitWrite(pressedButtons, i, 0);
  }
}

void readIntensity()
{
  int val = analogRead(intensityPot);
  intensity = (uint8_t) (map(val, 0, 1023, 0, 127));
}

void playNotes()
{
  for (int i = 0; i < NUM_BUTTONS; i++)
  {
    if (bitRead(pressedButtons, i) != bitRead(previousButtons, i))
    {
      if (bitRead(pressedButtons, i))
      {
        bitWrite(previousButtons, i , 1);
        noteOn(0, notePitches[i], intensity);
        MidiUSB.flush();
      }
      else
      {
        bitWrite(previousButtons, i , 0);
        noteOff(0, notePitches[i], 0);
        MidiUSB.flush();
      }
    }
  }
}

// First parameter is the event type (0x09 = note on, 0x08 = note off).
// Second parameter is note-on/note-off, combined with the channel.
// Channel can be anything between 0-15. Typically reported to the user as 1-16.
// Third parameter is the note number (48 = middle C).
// Fourth parameter is the velocity (64 = normal, 127 = fastest).

void noteOn(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOn);
}

void noteOff(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOff);
}

You just need to send a Control Change event. The midi.org link you posted contains information on how to construct a CC packet: Byte 1: 0b1011 nnnn where cccc is the (zero-based) MIDI channel number. Byte 2: 0b0ccc cccc where ccccccc is the controller number (from 0 to 119). Byte 3: 0b0vvv vvvv where vvvvvvv is the value (0 to 127).

For example, sending a value of 100% (vvvvvvv = 0b111 1111) on MIDI channel 1 (nnnn = 0b0000) for controller 7 (channel volume, ccccccc = 0b0000111): 0b1011 0000, 0000 0111, 0111 1111.

MIDI over USB uses 4-byte packets. The first byte contains the message type again (i.e. 1011 for Control Change): 0b0000 1011, 1011 0000, 0000 0111, 0111 1111

The resolution of the Arduino's analog input is 10 bits, but the resolution of the CC value (vvvvvvv) can only be 7 bits. So to map the potentiometer range to fit inside the MIDI event, you have to shift the 10-bit analog value 3 bits to the right to get a 7-bit value.

uint8_t CC_val = analogRead(pin) >> 3;

You might be interested in my MIDI Controller library. It makes creating a MIDI controller really easy, and has extra functions like averaging. Take a look at the ReadMe and the examples to get an idea.

Pieter

Excellent Pieter. I will give a try on you library. I think it’s what I was looking for. I’ll give you a feedback.

Thanks!
Celso

Wow. Your library is really good! The pot is already working, I had to comment all the other componnents that were not connected because they give a false reading. Maybe using the analog array it may me advisable to use a resistor load on the unused pins, maybe because of average readings the port keeps transmitting commands. I had to change to "Digital" class for momentary switches on the midi controller example. Sweet, now everything is working, well Done!

Thanks A Lot! Celso

Glad to hear that you got it working!

cferrarini:
The pot is already working, I had to comment all the other componnents that were not connected because they give a false reading. Maybe using the analog array it may me advisable to use a resistor load on the unused pins, maybe because of average readings the port keeps transmitting commands.

You don’t have to use te Analog array example. You can just use as many Analog objects as you like.

Analog name(pin, controller, channel);

This creates one Analog object, that reads a single analog input (pin). The Analog array example creates an Analog object for every analog pin on the Arduino, but you don’t have to do that if you’re not using all analog pins.
Take a look at the other Analog examples to get an idea.

Here’s another example that uses arrays, but let’s you choose what pins it uses:

#include <MIDI_controller.h> // include the library

const uint8_t digitalPins[] = { 2,  3,  4,  5,            // change these pins to the actual pins
                                6,  7,  8,  9,            // you have buttons connected to
                                10, 11, 12, 13,
                                14, 15, 16, 17 };

const uint8_t analogPins[]  = { A0, A1, A2, A3, A4, A5 }; // change these pins to the actual pins 
                                                          // you have potentiometers connected to

const static size_t analogAverage = 8; // Use the average of 8 samples to get smooth transitions and prevent noise
const static byte velocity = 127; // the maximum velocity, since MIDI uses a 7-bit number for velocity.

//________________________________________________________________________________________________________________________________

Analog* potentiometers[sizeof(analogPins)]; // create an empty array of pointers to Analog objects
Digital* buttons[sizeof(digitalPins)]; // create an empty array of pointers to Digital objects

//________________________________________________________________________________________________________________________________

void setup(){
  USBMidiController.blink(LED_BUILTIN);  // flash the built-in LED (pin 13 on most boards) on every message
  USBMidiController.setDelay(5);  // wait 5 ms after each message not to flood the connection
  USBMidiController.begin();
  delay(1000); // Wait a second...
  for(unsigned int i = 0; i < sizeof(analogPins); i++) {
    potentiometers[i] = new Analog(analogPins[i], i, 1); // create a new Analog object and store it in the array
    potentiometers[i]->average(analogAverage); // Use the average of 8 samples to get smooth transitions and prevent noise
  }
  for(unsigned int i = 0; i < sizeof(digitalPins); i++) {
    buttons[i] = new Digital(digitalPins[i], i, 1, velocity); // create a new Digital object and store it in the array
  }
}

//________________________________________________________________________________________________________________________________

void loop(){
  for(unsigned int i = 0; i < sizeof(analogPins); i++) { // refresh all Analog inputs
    potentiometers[i]->refresh();
  }
  for(unsigned int i = 0; i < sizeof(digitalPins); i++) { // refresh all Digital inputs
    buttons[i]->refresh();
  }
}

If you want a simpler (but longer) program, take a look at MIDI_Controller_finished_example. It doesn’t use any arrays or for-loops, so it’s really easy to see what’s going on. Once you understand how that’s working, you’ll notice that there are many lines that are more or less repeated. You can avoid these unnecessary repetitions by putting all Analog and Digital(Latch) objects in arrays. That’s what the code above does.

Pieter