Saturday, April 6, 2013

Foot-switch MIDI Controller

Recently I've been playing around more with MIDI control of audio programs like Ableton Live and POD Farm effects program on OS X. Creating my own foot-switch controller seemed like an easy weekend project.

Ingredients:

  1. A footswitch ( https://www.sparkfun.com/products/11192 )
  2. Someway to get input into my computer. Teensy 3.0 ( http://pjrc.com/store/teensy3.html )
  3. Unnecessary number of LEDs ( https://www.sparkfun.com/products/11492 and all the bits that go with that: breakout board, LED rotary encoder )


Instructions:

  • Foot-switch goes into the teensy: simple pull-up resistor
  • Wire all up the rotary encoder breakout board pins to the teensy
  • Write some arduino code to read from/write to the serial port
  • Write an OS X program to read from/write to the teensy board via the serial port
Source Excerpts:

On the teensy I attach an interrupt on the pin the footswitch is attached to:

void setup() {
  attachInterrupt(foot1Pin, footSwInterrupt,CHANGE );
}

static int foot1PinDown = 0;
void footSwInterrupt() {
  foot1PinDown = (digitalRead(foot1Pin) == LOW);
}

void loop() {
  static int last_state = 0;
  
  if( last_state != foot1PinDown ) {
      uint8_t buf[5];
      buf[0] = 0x01;  // COMMAND
      buf[1] = (foot1PinDown) ? 0x1 : 0x0;
      buf[2] = 0;
      buf[3] = 0;
      buf[4] = 0;
      Serial.write(buf, 5);
      
      last_state = foot1PinDown;
    }
}


I don't use the rotary encoder for anything but the "Encoder" library provided by pjrc works well with the sparkfun breakout board/rotary encoder.

The ring-o-LEDs is just two 8-bit shifts out:

void updateRing( uint16_t v ) {
  digitalWrite(ringLatch, LOW);  

  shiftOut(ringDat, ringClk, MSBFIRST, v>>8);
  shiftOut(ringDat, ringClk, MSBFIRST, v&0xFF);

  digitalWrite(ringLatch, HIGH);
}


The most interesting part for me was figuring out how OS X's midi library works. This turns out to be pretty simple after looking at maxbok's CoreMIDITemplate.

On app startup register as a MIDI source. I.e. other MIDI programs that "read" MIDI messages will show "Footswitch MIDI Source" as one of the control surfaces:

MIDIClientCreate((CFStringRef)@"Footswitch MIDI Client", NULL, NULL, &_client);
MIDISourceCreate(_client,(CFStringRef)@"Footswitch MIDI Source", &_endpointRef );
MIDIOutputPortCreate(_client, (CFStringRef)@"Footswitch Output Port", &_outputPort);

When you want to send a note message:
- (void)sendNote:(uint)note on:(BOOL)on {
    const UInt8 data[]  = { on ? 0x90 : 0x80, note, 127 };
    ByteCount size = sizeof(data);
    
    Byte packetBuffer[sizeof(MIDIPacketList)];
    MIDIPacketList *packetList = (MIDIPacketList *)packetBuffer;
    
    MIDIPacketListAdd(packetList,
                      sizeof(packetBuffer),
                      MIDIPacketListInit(packetList),
                      AudioGetCurrentHostTime(),
                      size,
                      data);

MIDIReceived( _endpointRef, packetList );
}


The rest of the code is pretty teensy specific for opening it's USB serial port and reading from it in an asynchronous mode:

- (void)startSerialReader {

struct termios settings;
__block int fsw1_state = 0;
int port = open("/dev/cu.usbmodem12341", O_RDWR);
if (port < 0) {
fprintf(stderr, "Unable to open %s\n", "/dev/cu.usbmodem12341");
return;
}
__weak typeof(self) weak_self = self;
// Configure the port
tcgetattr(port, &settings);
cfmakeraw(&settings);
tcsetattr(port, TCSANOW, &settings);
_serialHandle = [[NSFileHandle alloc] initWithFileDescriptor:port closeOnDealloc:TRUE];
__block NSMutableData *comm_buf = [[NSMutableData alloc] init];
_serialHandle.readabilityHandler = ^(NSFileHandle *fh) {
// DO INTERESTING THINGS WITH SERIAL DATA FROM TEENSY HERE!
};
}



Results:

Here is a short video of the rotary encoder's RGB LED's cycling through colors while I push the footswitch down.


The hardware overall works great. You kinda get what-you-expect from the $5 foot-switch from sparkfun. It works but it has no tactile feel and you need to push it down quite hard to ensure it's actually switched. I'd like to experiment with some sort of optical switch arrangement instead of this cheap mechanical one.