MIDI decoder sketch

I was playing with the attached sketch to work out exactly what my MIDI keyboard was sending.

Example output:

   1191616: Control change channel = 1, General Purpose Controller 1 (MSB): 0x40
      5532: Note off channel = 1, note = D (5) , velocity = 99
      2740: Note  on channel = 1, note = E (0) , velocity = 127
      4528: Note  on channel = 1, note = D (4) , velocity = 63
     46184: After-touch pressure channel = 1, value = 31
     15328: After-touch pressure channel = 1, value = 127
     29776: Note off channel = 1, note = D (5) , velocity = 64
      5708: After-touch pressure channel = 1, value = 49
     10664: After-touch pressure channel = 1, value = 18
     13256: After-touch pressure channel = 1, value = 9

Changing instruments:

   1115248: Control change channel = 1, Bank Select (MSB): 0x3F
      1964: Control change channel = 1, Bank Select (LSB): 0x4
      2064: Program change channel = 1, program = 42
      3832: System: Exclusive (in hex), vendor ID = 66
30 7A 40 0 0 44 61 6E 63 65 20 4C 0 65 61 64 20 20 20 20 20 20 20 A 3C 0 30 14 50 27 0 14 5F 
48 2 14 2 28 19 55 0 31 40 2 20 0 40 0 7F 7F 4E 23 0 40

Another tune from the device:

    486436: Note off channel = 1, note = C (4) , velocity = 64
      2036: Note  on channel = 1, note = G (4) , velocity = 105
      2120: Note off channel = 1, note = D (4) , velocity = 114
      4532: Note  on channel = 1, note = A#(3) , velocity = 105
     75416: Note off channel = 1, note = A (3) , velocity = 64
      6988: Note off channel = 1, note = F (4) , velocity = 64
    503724: Note  on channel = 1, note = A (4) , velocity = 102
      2120: Note  on channel = 1, note = E (4) , velocity = 117
      2212: Note  on channel = 1, note = C (4) , velocity = 99
      4444: Note  on channel = 1, note = B (3) , velocity = 99
     10344: Note off channel = 1, note = B (3) , velocity = 0
     76212: Note off channel = 1, note = D (4) , velocity = 64
      2036: Note off channel = 1, note = A#(3) , velocity = 64
     41684: Note off channel = 1, note = G (4) , velocity = 64
    872724: Note off channel = 1, note = C (4) , velocity = 64
    147928: Note off channel = 1, note = A (4) , velocity = 64

Pitch wheel changes:

 136802728: Note  on channel = 1, note = D#(4) , velocity = 111
     24208: After-touch pressure channel = 1, value = 0
    106028: Pitch wheel change channel = 1, value = 9560
     13056: Pitch wheel change channel = 1, value = 16383
    133788: Note off channel = 1, note = D#(4) , velocity = 64
     19176: Pitch wheel change channel = 1, value = 16045
     11352: Pitch wheel change channel = 1, value = 12053
     11644: Pitch wheel change channel = 1, value = 8192
     72888: After-touch pressure channel = 1, value = 0
     94608: Note  on channel = 1, note = G#(4) , velocity = 105
    217200: Note off channel = 1, note = G#(4) , velocity = 64
      8080: Note  on channel = 1, note = A#(4) , velocity = 105
     38220: After-touch pressure channel = 1, value = 6
     11336: After-touch pressure channel = 1, value = 127
     35416: After-touch pressure channel = 1, value = 42
     11356: After-touch pressure channel = 1, value = 0
     34140: Note off channel = 1, note = A#(4) , velocity = 64
    264208: Note  on channel = 1, note = C#(5) , velocity = 102
    244728: After-touch pressure channel = 1, value = 5
     13324: After-touch pressure channel = 1, value = 21
     11352: After-touch pressure channel = 1, value = 42
     15388: After-touch pressure channel = 1, value = 0
     11360: After-touch pressure channel = 1, value = 80
     11624: After-touch pressure channel = 1, value = 101
     11080: After-touch pressure channel = 1, value = 127
    404408: After-touch pressure channel = 1, value = 42
     11060: After-touch pressure channel = 1, value = 3
     15400: After-touch pressure channel = 1, value = 0
    144136: Note off channel = 1, note = C#(5) , velocity = 64
      4180: Note  on channel = 1, note = A#(4) , velocity = 99
     52116: After-touch pressure channel = 1, value = 15
      9592: After-touch pressure channel = 1, value = 21
     13120: After-touch pressure channel = 1, value = 2
     11332: After-touch pressure channel = 1, value = 0
     64000: Note off channel = 1, note = A#(4) , velocity = 64
    204316: Note  on channel = 1, note = G#(4) , velocity = 96

As written, you connect your MIDI device up to pin D2 (for NewSoftSerial). It uses hardware serial at 115200 to send the above message to your monitor window. I had to fiddle slightly with NewSoftSerial to make it compatible with version 1.0 of the IDE, but if you grab the latest version that probably won't be necessary.

You need to compiler under version 1.0+ of the IDE to get the "F" macro which is used to save SRAM by keeping literal strings in program memory.

The number on the left shows the number of microseconds between each message, so you can see which ones come closely together.

Note values are interpreted, eg. C(4) is Middle C.

As far as possible I interpreted incoming messages as per the tables on this page:

You need the "streaming" library for the sketch to compile:

http://arduiniana.org/libraries/streaming/

MIDI_decoder.ino (13.2 KB)

Example of it in operation:

hi nick, i have run over ide 1.0
according this web: NewSoftSerial | Arduiniana
"if you have 1.0 or later, you should not download this library. To port your code to 1.0, simply change all NewSoftSerial references to SoftwareSerial."
this is the code with the changes:

//  MIDI_decoder
//  
//  Author:  Nick Gammon
//  Date:    8th April 2012.
//  Version: 1.0
//  Released into the public domain.

#include <SoftwareSerial.h>
#include <Streaming.h>

// Plug MIDI into pin D2 (MIDI in serial)

NewSoftSerial midi (2, 3);  // Rx, Tx

this is the error:
'NewSoftSerial' does not name a type

Well, so they say, but I found SoftwareSerial to be a bit sluggish. You could change:

NewSoftSerial midi (2, 3);  // Rx, Tx

to:

SoftwareSerial midi (2, 3);  // Rx, Tx

now it's ok :slight_smile:

Hi Nick,

Sorry new to this stuff, my question maybe way off topic so I am sorry if it is irritating.
I was wondering if your program decodes incoming midi streams only?
My interest is in decoding a midi file from an SD card and playing it back through a Synth.
Can I use all or part of your program to tackle this problem? I already have some very good midi code, but as it is far from working, I need to learn more and your code may help.
The video above is very impressive, great seeing a program in "in action". Very clever.

Cheers, Rob

That's fine. :slight_smile:

The program only decodes incoming MIDI, its intention was not to send MIDI out. I haven't looked at the exact format of MIDI files yet, so I can't say how easy that would be to do. I agree that it would be very useful.

The intention really was just to see "in real time" what happens when you hit keys on your keyboard. For example: note on, note off, program change etc.

Amended sketch (attached version 1.1).

When testing on a different device I was getting strange sequences that I initially assumed were an electrical problem. However they persisted. It turns out that (rather unclearly from the specs) you can have multiple "command" messages followed by parameters. For example, Channel 1 Note on is 0x90 followed by the note and the pressure, eg.

0x90 0x45 0x32

However it appears (to save space) you can have multiple notes, like this:

0x90 0x45 0x32 0x46 0x32 0x47 0x32

The amended code checks if an incoming byte has the high-bit set, and if not, uses the last byte with the high-bit set as a "repeat last command" idea.

MIDI_decoder.ino (13.6 KB)

I have been confused by the explanations of this phenomena as well, your example is bit more concrete which I appreciate, however I think if you have a look at this site OpenStax and scroll down to "Running Status" it will help. Not having programmed anything to deal with it, I would not claim to understand it, but at least they seem to have a handle on the philosophy behind it.

cheers, Rob

Yes, that is more or less what I deduced from observing the data streams. You can tell a "command" byte by the fact that it has the high bit set, and then other (parameter) bytes follow in fixed amounts (eg. two, for note and velocity). So it makes sense to omit repeated commands.

Hello Nick,
I am in the process of building a digital organ using the amazing hauptwerk software. Typically an organ has stops that the organist pulls out or pushes in to alter the sound of the instrument. Most of this stops are made from a push-pull solenoid or they might just be the type that illuminates when engaged like what I am building.

Currently I have all the stops connected to keyers from a old casio keyboard and I use the keyboard to send out messages to haupwerk. SO if the organist wants the trumpet 8 sound all you do is push the stop, the light comes on at the console for trumpet 8 and the sound comes out when you play. And vise versa, when you push the stop back in, the light goes off and the trumpet no longer sounds. However hauptwerk also needs to be able to send some messages back to the organ so only certain stops stay engaged when the Organist uses preset pistons(a preset piston is a combinations of stops, you simple press on button to pull the sound of 10 instruments at once).My casio send the message properly to hauptwerk and those presets sound, however the stops that are sounding are not illuminated on the console.

This is where I need a MIDI decoder to interprete the mesages from hauptwerk regarding the stops to be engaged/disengaged and illuminate or de-illuminate those stops on my organ. In the case of my build some stops will be illuminated while others will be off.

Please any help will be high appreciated.

jeremalian:
Typically an organ has stops that the organist pulls out or pushes in to alter the sound of the instrument.

Yes, I learnt the organ at school.

Please any help will be high appreciated.

I'm not sure what you are asking. Does the sketch not show the messages that get sent when you change instruments?

@jeremalian: Please don't cross post. I'm locking this thread.

http://arduino.cc/forum/index.php/topic,107173