Beginner advice on midi controller

Status
Not open for further replies.
Hi, I'm looking to build what should be a pretty simple midi controller for MAX MSP. The controller would consist of several joysticks that would transmit CC messages. I plan on using these... https://www.adafruit.com/product/3102

I'd also like to have a keypad matrix switch that I can use to type in commands. For instance if I typed in '127' it would then send a message (what kind I don't know, I'm currently thinking program change) that would trigger some sort of event in Max like 'start playing this file' or 'load this preset' etc etc

And lastly I'd like some sort of LCD display that told me what I was doing. IE if I moved a joystick it would display what two CC values I was transmitting.

I've downloaded and installed the MIDIcontroller-master library. To get started I opened up the MIDIpot example file in arduino IDE, but I am unable to verify and compile the sketch.

This is the error message.

Arduino: 1.8.9 (Mac OS X), TD: 1.46, Board: "Teensy 2.0, MIDI, 16 MHz, US English"

/Users/nate/Documents/Arduino/libraries/MIDIcontroller-master/MIDIcapSens.cpp: In member function 'int MIDIcapSens::read()':
/Users/nate/Documents/Arduino/libraries/MIDIcontroller-master/MIDIcapSens.cpp:47:31: error: 'touchRead' was not declared in this scope
int newValue = touchRead(pin);
^
Error compiling for board Teensy 2.0.

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.



I am very new to arduino/teensy and have no idea what this means. Any help with this specific problem or general advice on my project would be greatly appreciated.

Thanks!

The code is simply the MIDIpot exmaple, but here it is anyways,


#include "MIDIcontroller.h"

byte MIDIchannel = 5;
const int potPin = A0; // Change this to the ANALOG pin you want to use

// Pot parameters are: pin, CC number, KILL switch enabled
// When KILL is enabled, separate CC messages (with a different number) will be sent
// when you turn the pot all the way down and when you start turning it up again.
// Simply omit the "KILL" argument if you don't want that.
MIDIpot myPot(potPin, 22, KILL);

// OPTIONAL: use outputRange() to limit the min/max MIDI output values
// mysensor.outputRange(12, 90);

void setup(){
}

void loop(){
myPot.send();
}
 
I'm just building my first controller too, but I *think* that appears to be an error message from the MIDIcapSens.cpp sketch rather than the MIDIpot one, so maybe you opened the wrong one first go?

I think it's saying that in line 47 of "MIDIcapSens.cpp"

"int newValue = touchRead(pin);"

you haven't defined a value for "pin"

but I'm pretty sure that's irrelevant as it's wrong sketch!
In the "MIDIpot" sketch you posted you should not have the same issue as it defines "Potpin" as "A0" in the third line.
Maybe try this again?

I am building a controller using this libary instead, which I found very simple:

https://github.com/tttapa/MIDI_controller

but it is designed for Teensy 3.1/3.2 - although this suggests there is an amended code for 2.0

https://github.com/tttapa/MIDI_controller/issues/20
 
Thanks, duckchild

I just ran the sketch again and got this error message, which is completely different...

Code:
Arduino: 1.8.9 (Mac OS X), TD: 1.46, Board: "Teensy 2.0, MIDI, 16 MHz, US English"

/Users/nate/Documents/Arduino/libraries/MIDIcontroller-master/MIDIpot.cpp: In member function 'int MIDIpot::read()':
/Users/nate/Documents/Arduino/libraries/MIDIcontroller-master/MIDIpot.cpp:82:16: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
   if (newValue >= inHi && value != outHi){ // Assign hi analog to hi MIDI
                ^
/Users/nate/Documents/Arduino/libraries/MIDIcontroller-master/MIDIpot.cpp:86:21: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
   else if (newValue <= inLo && value != outLo){ // Assign low analog to low MIDI
                     ^
/Users/nate/Documents/Arduino/libraries/MIDIcontroller-master/MIDIdrum.cpp: In member function 'int MIDIdrum::send(int)':
/Users/nate/Documents/Arduino/libraries/MIDIcontroller-master/MIDIdrum.cpp:69:27: warning: third operand of conditional expression has no effect [-Wunused-value]
     constrain(vel, 1, 127);
                           ^
/Users/nate/Documents/Arduino/libraries/MIDIcontroller-master/MIDIcapSens.cpp: In member function 'int MIDIcapSens::read()':
/Users/nate/Documents/Arduino/libraries/MIDIcontroller-master/MIDIcapSens.cpp:47:31: error: 'touchRead' was not declared in this scope
   int newValue = touchRead(pin);
                               ^
Error compiling for board Teensy 2.0.

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

I don't know, I've been working with some other examples from a few of the instructables out there that are so far working ok. I'll check out the library you posted too.

This has been my favorite so far...
https://forum.pjrc.com/threads/45376-Example-code-for-MIDI-controllers-with-Pots-and-Buttons
 
...I just ran the sketch again and got this error message, which is completely different...
That's actually the same fatal error plus some new warnings.

You don't need an external MIDI controller library (and you won't really find support for them here as none of the regulars use them).

The joysticks should be fairly simple as they are just two potentiometers each so you need two analog pins for each. The 'many knobs and buttons' example code's analog section can handle this.

Reading a keypad should be not too hard by following this
https://www.pjrc.com/teensy/td_libs_Keypad.html

...but interpreting codes and performing actions will be up to you to work out.

I'm not expert at displays but there are plenty of people here who are.

I strongly recommend you try the tutorials to get some basics first.
https://www.pjrc.com/teensy/tutorial.html

It's possible to cobble code together from examples without understanding all the gory details but you need to have some understanding of the basics to have any chance of succeeding. And you have specific plans that will require you to work out the logic so you will need to learn to code these yourself.
 
Hi, I've made some great progress but could use some advice for taking the next step.

I've got the matrix keypad working and sending midi note on/off and velocity values for each of the 16 keys (0-9,#,*, A,B,C,D). What I'd like to do is have the 0-9 keys send the midi note messages but A,B,C,D keys act as octave change buttons when held. For instance say I have it set so that when I press 1 it sends note# 40, I'd like to be able to hold the A key press 1 and have it transmit note# 52.

Any advice on how to proceed?

here is the code so far...

Code:
#include "MIDIUSB.h"

/* Settings for Keypad */
const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
const int NB_BUTTONS = ROWS * COLS;


int notes[NB_BUTTONS] = {37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52};

int keymodifier;
/* Keypad Init */
//https://github.com/Nullkraft/Keypad
#include <Keypad.h>
char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

//Keypad pins
byte rowPins[ROWS] = {0, 1, 2, 3}; //connect to the row pinouts of the kpd
byte colPins[COLS] = {4, 5, 6, 7}; //connect to the column pinouts of the kpd
Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

//Keypad state : 1 = DOWN 3 = UP (there are other state which are unused here)
unsigned int buttons_state[NB_BUTTONS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};


void setup() {
  Serial.begin(115200);
  kpd.setHoldTime(250); // THIS IS WHERE I SET HOLD TIME NPF
}



void loop() {
  if (kpd.getKeys()) {
    for (int i = 0; i < LIST_MAX; i++) // Scan the whole key list.
    {
      if ( kpd.key[i].stateChanged )   // Only find keys that have changed state.
      {
        buttons_state[kpd.key[i].kcode] = kpd.key[i].kstate;

        if (kpd.key[i].kstate == 1) { //DOWN
          Serial.print(kpd.key[i].kcode);
          Serial.print(":");
          Serial.println("1");

        
           
            key_down(kpd.key[i].kcode);

          
        }
              
              
        if (kpd.key[i].kstate == 3) { //UP
          Serial.print(kpd.key[i].kcode);
          Serial.print(":");
          Serial.println("0");
          // If key is up we send a midi note off
          key_up(kpd.key[i].kcode);
        }
      }
    }
  }
}

//Key down (pressed)
void key_down(int key) {
  //Serial.print(buttons_state[velocity_button]);
  noteOn(0, (notes[key] + keymodifier), 127);

  //Display note and velocity on screen
  MidiUSB.flush();
}

//Key up (unpressed)
void key_up(int key) {

    noteOff(0, notes[key], 127);
    
  MidiUSB.flush();
}

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);
}

void controlChange(byte channel, byte control, byte value) {
  midiEventPacket_t event = {0x0B, 0xB0 | channel, control, value};
  MidiUSB.sendMIDI(event);
}
 
Do you mean an option selector that is only active while held... like a shift key?

Search MIDI and BANK together for examples. Thay mostly use a variable to store the selected bank states but using the actual pad button state should be simple enough.

The main trick is to go to a two dimensional array (matrix) for notes where the additional dimension is the bank number.

The other complication is having some keys represent modifiers and some events. It might be easier to have an intermediary step pass the matrix states into separate data structures for event and modifier buttons to allow indicator varables work in a loop that scans through the event key states on an outer loop and the modifiers on an inner loop.

BTW: What is that Library? I don't know if you're even calling it in your code as I've only scanned it so far but I don't think you want to use it if you are compilingTeensy for USB MIDI as the appropriate library is included automatically.


....knocked this off quickly on my tablet.... excuse spelling and typos....
 
You can retain the single loop structure if you have two paths within the loop, one if 'i' represents an event button and another if it is a modifier.

Then set modifier variables when not an event.

When it is an event, send with the note based on current modifier state from the most recent pass.

As long as the modifier is set first this will work but longer debounce times MIGHT start to make it feel sluggish....
 
Thanks, it'll take me a while to parse all this.

Do you mean an option selector that is only active while held... like a shift key?
Yes, exactly

I'm not sure if this is what you're getting at but I was thinking that maybe the matrix should just be for notes and incorporate additional buttons to act as the modifiers. I mean, I don't need to trigger that many events, even 16 is probably fine for most performances.
 
What is to be gained from using the default library?
it works, and doesn't need a flush or prepacked variables.

Separete bank selectors would be slightly easier but you should be able to use the pad if you want.
 
....I should mention that, years ago, Teensy was the only way to do USB MIDI without fairly onerous kludges needed to get around how the device appears to the host for programing vs in operation. Teensy's bootloader made a much nicer experience possible for both the programmer and the user (by providing a MIDI compliant device to the USB interface).

I honestly don't know where Aurdiuno is now as I only do Teensy.
 
Last edited:
Status
Not open for further replies.
Back
Top