Thursday, November 3, 2011

MIDI controller - part 1


I recently got a Micro B II from Voce!
It's an organ module, with 36 organ presets and a bunch of knobs to change key click, overdrive, decay, harmonics, rotor speed... It has beautiful, realistic sounds. Of course that doesn't replace the drawbars on my Hammond, but it's just really cool to carry around all these organs in a tiny box. The best part of it is it's controlled by MIDI, so I can turn almost any keyboard with its built-in sounds into an awesome sounding organ.
So why build an extra MIDI controller? Well, 14 of these sounds and a few controls can only be accessed through MIDI commands - like rotating speaker acceleration time and chorus. Besides, to get a real organ feel, it'd be nice to have a volume pedal.
I have an Arduino Duemilanove that's been lying around, so I will put it to good use. I'll start putting the project together when I get the male 5-DIN I ordered online.

First off, here is how MIDI works.
A midi message is composed of 3 bytes: a status byte followed by 2 data bytes.
  • The status byte specifies what type of action we ask the system to perform (play a note, change program...) and which MIDI channel is selected (if you have one controller and one instrument, you only have 1 channel).
  • The data bytes contain the information to perform that action: withing 'play a note', the data bytes would specify which note, and how loud the note should be played. Within 'change program', the data bytes would specify which program to change (Vibrato, Chorus, Rotor speed...), and by how much (Set rotor speed to max or min or what have you).
Here is what it would like with the Arduino (serial communication):
void midi(byte stat, byte data1, byte data2) {
  Serial.print(stat, BYTE);
  Serial.print(data1, BYTE);
  Serial.print(data2, BYTE);
}

If you look up a MIDI chart, you'll see that for pressing a key:
  1. the status byte must look like 1001nnnn, where the n's represent the channel in bits.
  2. the data1 byte must look like 0kkkkkkk, where the k's represent the key in bits
  3. the data2 byte must look like 0vvvvvvv, where the v's represent the velocity (or pressure) in bits.
 Therefore a 'press a key' function can be written as
void noteOn(byte channel, byte note, byte velocity) {
  midi( (0x90 | channel), note, velocity);
}


 Here is something I wrote out of boredom. I didn't have a keyboard, just the MIDI module and my Arduino. So I hardcoded Amazing Grace.

 #define CHANNEL 1

 void setup() {
   //  Set MIDI baud rate:
   Serial.begin(31250);
 }

  void loop() {
    //63 65  67 69 70...
    //fa sol la si do...
  
    press(65, 1);
    delay(500);
    press(65, 1);
    noteOn(CHANNEL, 58, 80);
    press(70, 4);//do
    press(74, 1);
    press(72, 1);
    press(70, 1);
    press(74, 5);
    press(72, 1);
    noteOff(CHANNEL, 58, 0);
    noteOn(CHANNEL, 51, 80);
    press(70, 5);
  
    press(67, 1);
    noteOff(CHANNEL, 51, 0);
    noteOn(CHANNEL, 58, 80);
    press(65, 6);
    noteOff(CHANNEL, 58, 0);
     
    press(65, 1);
    delay(500);
    press(65, 1);
    noteOn(CHANNEL, 58, 80);
    press(70, 4);
  
    press(74, 1);
    press(72, 1);
    press(70, 1);
    press(74, 5);
    press(72, 2);
    press(74, 1);
    noteOff(CHANNEL, 58, 0);
    noteOn(CHANNEL, 53, 80);
    noteOn(CHANNEL, 69, 80);
    press(77, 5);
    noteOff(CHANNEL, 53, 0);
    noteOff(CHANNEL, 69, 0);  
  }

void press(byte note, int wait)
{
  noteOn(CHANNEL, note, 100);
  delay(wait*500);
  noteOff(CHANNEL, note, 30);
}

 // Send a MIDI note-on message.  Like pressing a piano key
// channel ranges from 0-15
void noteOn(byte channel, byte note, byte velocity) {
  midi( (0x90 | channel), note, velocity);
}

// Send a MIDI note-off message.  Like releasing a piano key
void noteOff(byte channel, byte note, byte velocity) {
  midi( (0x80 | channel), note, velocity);
}

// Send a general MIDI message
void midi(byte stat, byte data1, byte data2) {
  Serial.print(stat, BYTE);
  Serial.print(data1, BYTE);
  Serial.print(data2, BYTE);
}

No comments:

Post a Comment