avr-midi

Hi, this is some code I wrote in order to send and receive midi with an atmel avr chip. It uses avr-libc. It is is licenced with the GPL v3 licence.

It provides convenient functions for both sending and receiving most (if not all) of the message types specified by the midi spec. It also provides a function for making sure that one can safely send midi data during a midi merge. Finally it provides functions to pack and unpack 8 bit bytes into/out of 7 bit midi bytes, allowing for users to send and receive large chunks of data in system exclusive (SYSEX) messages.

So far it has only been used on an atmega16, but should work on many other avr chips with little or no modification.

By Alex Norman.

Download:

Get it here: code dir

Donate:

If you find that this project is useful I would appreciate your support. Please go to my donations page for more information.

Example:

Below is an example program that uses this code. It sends midi data based on button inputs.


/*
 
  Here is an example of how to use avr-midi
  this was written for an atmega16 using a 12Mhz clock in an stk500
  the switch inputs of the STK500 are connected to PORTC

  switch 0 and 1 increment and decrement [respectively] a midi program and
  send the appropriate midi program change

  switch 2 sends a note on message when the button is pushed and a note off
  when the button is released

  the rest of the switches send CC messages, a 1 when the button is down and a
  0 when the button is released

*/

#include 
#include 
#include 
#include "midi.h"

#define MIDI_CLOCK_RATE MIDI_CLOCK_12MHZ_OSC

//how many times to we read the buttons before we say they're valid
#define INPUT_HISTORY 4

//this is the midi channel we'll be sending on.. 0 is midi channel 1
#define MIDI_CHAN 0

int main(void) {
  //index for looping
  uint8_t i, j;
  bool consistent;
  //is the button up
  bool up;
  //midi program 
  uint8_t program = 0;
  //button inputs, for debouncing
  uint8_t inputs[INPUT_HISTORY];
  uint8_t curInput = 0;
  uint8_t lastInput = 0;
  uint8_t historyIndex = 0;

  //PORTC is in input with internal pullups
  DDRC = 0x00;
  PORTC = 0xFF;

  //init the button history
  for(i = 0; i < INPUT_HISTORY; i++)
    inputs[i] = 0;

  //init midi, give the clock rate setting, indicate that we want only output
  midiInit(MIDI_CLOCK_RATE, true, false);

  while(1){

    //grab the inputs
    inputs[historyIndex] = PINC;
    curInput = lastInput;

    //debounce inputs
    //for each input
    for(i = 0; i < 8; i++){
      consistent = true;
      //the STK500 has 1 for up and 0 for down if you're using the on board buttons
      up = (bool)(0x1 & (inputs[0] >> i));
      //check the history to make sure we have the same value for this input
      //for the whole history
      for(j = 1; j < INPUT_HISTORY; j++){
        if(up != (bool)(0x1 & (inputs[j] >> i))){
          consistent = false;
          break;
        }
      }
      //if we have the same value for the whole history then store it in
      //curInput
      if(consistent){
        if(up) {
          //store the current input state
          curInput |= (1 << i);
          //if the last input was a 0 and the current is a 1
          if(((lastInput >> i) & 0x1) == 0){
            //we don't do anything on up for the first two buttons
            //send the appropriate midi data
            if(i == 2){
              midiSendNoteOff(MIDI_CHAN, 64, 30);
            } else if(i > 1)
              midiSendCC(MIDI_CHAN, i - 3, 0);
          }
        } else {
          //store the current input state
          curInput &= ~(1 << i);
          //if the last input was a 1 and the current is a 0
          if(((lastInput >> i) & 0x1) == 1){
            //send the appropriate midi data
            switch(i){
              case 0:
                program = (program + 1) % 128;
                midiSendProgramChange(MIDI_CHAN, program);
                break;
              case 1:
                program = (program - 1) % 128;
                midiSendProgramChange(MIDI_CHAN, program);
                break;
              case 2:
                midiSendNoteOn(MIDI_CHAN, 64, 127);
                break;
              default:
                midiSendCC(MIDI_CHAN, i - 3, 1);
                break;
            }
          }
        }
      }
    }

    //update the history index
    historyIndex = (historyIndex + 1) % INPUT_HISTORY;
    //store the curInput as lastInput for the next loop
    lastInput = curInput;
  }

  return 0;
}