MIDI.h dynamic memory usage

In an attempt to keep my dynamic memory usage low (since I want to add SD.h and eventually, Adafruit GFX), I've been going through my sketch and getting an estimate of how much dynamic memory each library uses.

I've found that by simply including MIDI.h and then calling MIDI_CREATE_DEFAULT_INSTANCE() uses approximately 356 bytes.

Is there a MIDI "lite" version anywhere that would use less memory? Any suggestions or tips? Or am I SOL here?

Thanks in advance,
Kyle

You could easily create your own MIDI out function, MIDI in is a bit trickier, but still doable, especially if you only need 'normal' events, without sysex etc.

This is the code I used for a simple MIDI controller with 6 potentiometers, for example. If you don't understand what is happening, just ask. (It will probably be much more readable if you copy and paste it in the IDE.)

/* These are constants (actually preprocessor macros): whenever we put one of these 3 words (the ones in capitals) in our code, the precompiler will replace it by the 0xsomething after the word. 
 This makes our code more readable, instead of typing the meaningless 0x90, we can now just type NOTE_ON, if we want a note to be on. */
#define NOTE_OFF       0x80
#define NOTE_ON        0x90
#define CC             0xB0

/* This is also a constant value (preprocessor macro). If you want to change the number of analog inputs, you can simply do it once on this line, instead of changing it everywhere in your code.*/ 
#define NUMBER_OF_ANALOG_INPUTS  6 // The Uno has 6 analog inputs, we'll use all of them in this example. If you only need 4, change this to 4, and you'll be able to use A4 & A5 as normal I/O pins. 
// NOTE: if you change this value, also change the controllers array, to match the number of inputs and the number of controllers.

#define CHANNEL  1 // Send all messages on channel 1.

/* The list with the corresponding controller numbers: for example, the values of the potentiometer on A0 will be sent as the first controller number in this list, A1 as the second, etc... 
 Here's the list with all controller numbers:  http://midi.org/techspecs/midimessages.php#3  You can change them if you want.*/
int controllers[NUMBER_OF_ANALOG_INPUTS] = { 
  0x10, 0x11, 0x12, 0x13, 0x14, 0x15
};  

int analogVal[NUMBER_OF_ANALOG_INPUTS];  // Declare an array for the values from the analog inputs

int analogOld[NUMBER_OF_ANALOG_INPUTS];  // Declare an array for the previous analog values. (To know if the knob has been turned since the last measurement)


/* The format of the message to send via serial. We create a new data structure, that can store 3 bytes at once.  This will be easier to send as MIDI. */
typedef struct {
  uint8_t status;   // first  byte   : status message (NOTE_ON, NOTE_OFF or CC (controlchange) and midi channel (0-15)
  uint8_t data1;    // second byte   : first value (0-127), controller number or note number
  uint8_t data2;    // third  byte   : second value (0-127), controller value or velocity
} 
t_midiMsg;          // We call this structure 't_midiMsg'

void setup() // The setup runs only once, at startup.
{
  pinMode(13, OUTPUT);   // Set pin 13 (the one with the LED) to output
  digitalWrite(13, LOW); // Turn off the LED
  for(int i = 0; i < NUMBER_OF_ANALOG_INPUTS; i++){  // We make all values of analogOld -1, so it will always be different from any possible analog reading.
    analogOld[i]=-1;
  }
  Serial.begin(31250);  // Start a serial connection @31250 baud or pulses per second on digital pin 0 and 1, 31250 baud is the original MIDI speed. The MIDI output is on pin 1 (TX)
  digitalWrite(13, HIGH);// Turn on the LED, when the loop is about to start.
}

void loop() // The loop keeps on repeating forever.
{
  t_midiMsg msg;                                     // create a variable 'msg' of data type 't_midiMsg' we just created
  for(int i = 0; i < NUMBER_OF_ANALOG_INPUTS; i++){  // Repeat this procedure for every analog input.

    analogVal[i] = analogRead(i+A0)/8;               // The resolution of the Arduino's ADC is 10 bit, and the MIDI message has only 7 bits, 10 - 7 = 3, so we divide by 2^3, or 8.
    if(analogVal[i] != analogOld[i]){                // Only send the value, if it is a different value than last time.
      msg.status = CC;                               // Controll Change
      msg.status = msg.status | CHANNEL-1;           // Channels are zero based (0 = ch1, and F = ch16). Bitwise or to add the status message (s) and channel (c) together: 
                                                    /* status     = 0bssss0000 
                                                     * channel    = 0b0000cccc 
                                                     * | --------------------- (bitwise or)
                                                     * msg.status = 0bsssscccc       
                                                     */
      msg.data1   = controllers[i];                  // Get the controller number from the array above.
      msg.data2   = analogVal[i];                    // Get the value of the analog input from the analogVal array.
      Serial.write((uint8_t *)&msg, sizeof(msg));    // Send the MIDI message. (send three bytes, starting at the address of msg)
      analogOld[i] = analogVal[i];                   // Put the analog values in the array for old analog values, so we can compare the new values with the previous ones.
      delay(10);                                     // Wait for 10ms, so it doesn't flood the computer/synth with MIDI-messages
    }
  }
}

or using bit fields:

/* The format of the message to send via serial. We create a new data structure, that can store 3 bytes at once.  This will be easier to send as MIDI. */
typedef struct {
  unsigned int channel : 4;   // second nibble : midi channel (0-15) (channel and status are swapped, because Arduino is Little Endian)
  unsigned int status : 4;    // first  nibble : status message (NOTE_ON, NOTE_OFF or CC (controlchange) 
  uint8_t data1;              // second byte   : first value (0-127), controller number or note number
  uint8_t data2;              // third  byte   : second value (0-127), controller value or velocity
} 
t_midiMsg;          // We call this structure 't_midiMsg'

void MIDISend(uint8_t status, uint8_t channel, uint8_t data1, uint8_t data2) {
  t_midiMsg msg;
  msg.status = status & 0xF;
  msg.channel = (channel-1) & 0xF; // channels are 0-based
  msg.data1 = data1 & 0x7F;
  msg.data2 = data2 & 0x7F;
  Serial.write((uint8_t *)&msg,3);
}

Thanks, Pieter!

I was investigating this at one point, and wasn't having the best luck. I'll give this a shot and see if it helps.

I will need MIDI In at some point. However, I think all I want to handle is CC/PC messages. When I get a little further down the road, I'll dig deeper into this.

I appreciate you giving me your time and some direction.

Kyle

If you're using program change, it is important to know that it consists of only two bytes, compared to three for most other MIDI events.

The general notation for Control Change, for example, is 0b1011 cccc 0nnn nnnn 0vvv vvvv,
where c = channel, n = number of the controller, and v = the value of the controller. This is 1 status byte (MSB = 1) and two data bytes (MSB = 0).
Program Change, on the other hand, uses: 0b1100 cccc 0ppp pppp
Where c = channel, and p = new program. As you can see, there's the status byte, and only one data byte.

You can't send a second data byte after 0ppp pppp, because MIDI uses running statuses: this means that all messages after the Program Change status byte (1100 cccc) will be interpreted as program changes, until a different status byte (e.g. control change) is received.
This means that if you would send "Program Change: channel 5, 63, 0", it will be interpreted as "Change channel 5 to program 63, and change channel 5 to program 0". This is not what you want.

The solution is to send only two bytes whenever you need a program change:
Serial.write((uint8_t *)&msg,2);

Also, as stated above, all status bytes start with a 1 at bit 7 (MSB), whereas other (data) bytes have a 0. This makes interpreting incoming MIDI messages relatively easy.