MIDI device initialization
#pragma comment(lib, "winmm.lib")
int midiport = 0;
int flag = midiOutOpen(&device, midiport, 0, 0, CALLBACK_NULL);
if (flag != MMSYSERR_NOERROR)
cout << "Error opening MIDI Output" << endl;
MIDI device destruction
Bytes sent to a MIDI controller range between 0 and 255 (unsigned char). The difference between a MIDI command and data is the most significant bit of the btye. Zero signifies data, one signifies command. Thus, data is sent in the range from 0 to 127 and commands are sent in the range 128 to 255.
Command bytes are split into half. The most significant half contains the actual command and the second contains the MIDI channel it is targetted at. Example, 0x90 is the note on for the first MIDI channel, 0x91 for the second and so on.
Note that 0x80 in hex is 128 in decimal. 0x80 to 0xE0 are directed at channels as the second four bits (0x0n where n is [0:F]) specify the channel to target. 0xF0 to 0xFF are not directed at any channel, they are system messages.
A MIDI command along with its associated MIDI data is a MIDI message. The minimium size is 1 byte (A command byte, which is always required, but with no parameters) and the maximium is 3 bytes. (A command byte with 2 parameters).
Commands and their parameters
- 0x80 Note off
- 0x90 Note on
- 0xA0 Aftertouch
- 0xB0 Continuous controller
- 0xC0 Patch/instrument
- 0xD0 Channel pressure
- 0xE0 Pitch bend
- 0xF0 Misc commands to the device
As far as I have experimented, the third databyte is always zero, so the message itself only consists of thre.e significant bytes (cmd, p1, p2).
- 0x80 Note off - Key, velocity (must match the same parameters previously given to note on)
- 0x90 Note on - Key, velocity (velocity is the volume, it ranges from 0 to 0x7F)
- 0xA0 Aftertouch - Key, touch
- 0xB0 Continuous controller - Controller number ,value
- 0xC0 Patch/instrument - New instrument number
- 0xD0 Channel pressure - Pressure
- 0xE0 Pitch bend - MSB of the whole message affects the bend, LSB for channel
- 0xF0 Misc commands to the device - ??
enum MIDICommand : unsigned char
MIDI_StartNote = 0x90,
MIDI_StopNote = 0x80,
MIDI_ChangePatch = 0xC0,
int MIDICommand(MIDICommand cmd, char p1 = 0, char p2 = 0, char p3 = 0)
unsigned long word;
unsigned char data;
message.data = cmd;
message.data = p1;
message.data = p2;
message.data = p3;
return midiOutShortMsg(MIDIdevice, message.word);
The command exists in the upper half of the byte range, so its type is unsigned char. The parameters only exist in the lower half of the byte range, so I've left them signed chars in the function prototype as MIDI devices will ignore any parameters over 0x7F (127). This is different in the case of pitch bend and other commands which use the HSB and LSB of the whole message.
For example, Middle C may be played by
SendMIDICommand(MIDI_StartNote, 60, 0x7F);
But if you do not send a MIDI stop note before the next note is played, the falloff from this note may last a while. To do that,
SendMIDICommand(MIDI_StopNote, 60, 0x7F);