MIDI
A version of this page is also available for
4/8/2010
The following table shows the MIDI capabilities that are supported.
Capability | Description |
---|---|
Instruments |
Sine wave generation, no other instruments. |
Polyphony |
No limit on the number of midi streams. Number of concurrent notes per stream limited by the capabilities of the driver, and is set to 32 for all sample drivers. In practice, the total number of notes will be limited by the power of the CPU. Most devices should be capable of playing eight to ten notes concurrently. |
Messages |
Only note on, note off, and all notes off. |
Timing |
Sample accurate. |
Extensions |
Supports arbitrary frequency tone generation and tempo changes. |
The waveform audio API supports MIDI through the WAVE_FORMAT_MIDI wave format. To play MIDI notes, open the waveform audio device by calling the function waveOutOpen and providing a pointer to a WAVEFORMAT_MIDI structure and casting it to a WAVEFORMATEX structure. Use the value WAVE_FORMAT_MIDI to identify that the data being sent is MIDI data.
You then start passing buffers to the driver using waveOutWrite, just as you would for regular waveform audio data. Send MIDI data to the driver in a buffer that consists of an array of WAVEFORMAT_MIDI_MESSAGE structures.
The waveform audio driver will automatically take care of the timing and sequencing for each MIDI message. This approach should be satisfactory for most applications. If, however, you find that you need to do your own MIDI sequencing, then you can send MIDI messages directly to the audio driver by calling waveOutMessage and sending the deprecated message MM_MOM_MIDIMESSAGE. The driver handles MIDI messages send with MM_MOM_MIDIMESSAGE as soon as they are received. This means that the application is the sole source of timing and sequencing control for all MIDI messages sent in this way, which can be difficult to implement.
Code Example
The following code shows an application that plays a simple scale of eight MIDI notes.
#define DEFINE_GUIDS 1
#include "windows.h"
#include "stdio.h"
#include "wfmtmidi.h"
unsigned char Scale[8] = {63,65,67,68,70,72,74,75};
HANDLE hEvent;
void CALLBACK waveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD dwInstance,
DWORD dwParam1, DWORD dwParam2){
if (uMsg==WOM_DONE){
SetEvent(hEvent);
}
return;
}
int _tmain(int argc, TCHAR *argv[]) {
// Code to play a simple 8-note scale.
// Create an event to signal when playback is done
hEvent=CreateEvent( NULL,TRUE,FALSE,NULL);
// Build a MIDI waveformat header
WAVEFORMAT_MIDI wfm;
memset(&wfm,0,sizeof(wfm));
wfm.wfx.wFormatTag=WAVE_FORMAT_MIDI;
wfm.wfx.nChannels=1;
wfm.wfx.nBlockAlign=sizeof(WAVEFORMAT_MIDI_MESSAGE);
wfm.wfx.cbSize=WAVEFORMAT_MIDI_EXTRASIZE;
// These fields adjust the interpretation of DeltaTicks,
// and thus the rate of playback
wfm.USecPerQuarterNote=1000000; // Set to 1 second. Note driver
// will default to 500000 if we set
// this to 0
wfm.TicksPerQuarterNote=100; // Set to 100. Note driver will
// default to 96 if we set this to 0
MMRESULT Result;
HWAVEOUT hWaveOut;
// Open the waveout device
Result = waveOutOpen(&hWaveOut, WAVE_MAPPER, (LPWAVEFORMATEX)&wfm,
(DWORD)waveOutProc, 0, CALLBACK_FUNCTION);
if (Result!=MMSYSERR_NOERROR){
return -1;
}
// Build a MIDI buffer with 16 MIDI messages.
int i,j;
WAVEFORMAT_MIDI_MESSAGE MidiMessage[16];
for (i=0,j=0;i<8;i++,j+=2) {
// Wait 1 second :
// (DeltaTicks * (UsecPerQuarterNote / TicksPerQuarterNote))
MidiMessage[j].DeltaTicks=100;
MidiMessage[j].MidiMsg=0x1F0090 | ((Scale[i])<<8); // Note on
MidiMessage[j+1].DeltaTicks=100; // Wait 1 second
MidiMessage[j+1].MidiMsg=0x1F0080 | ((Scale[i])<<8); // Note off
}
WAVEHDR WaveHdr;
WaveHdr.lpData = (LPSTR)&MidiMessage;
WaveHdr.dwBufferLength = sizeof(MidiMessage);
WaveHdr.dwFlags = 0;
Result = waveOutPrepareHeader(hWaveOut,&WaveHdr,sizeof(WaveHdr));
// Play the data
Result = waveOutWrite(hWaveOut,&WaveHdr,sizeof(WaveHdr));
// Wait for playback to complete
WaitForSingleObject(hEvent,INFINITE);
// Cleanup
Result = waveOutUnprepareHeader(hWaveOut,&WaveHdr,sizeof(WaveHdr));
Result = waveOutClose(hWaveOut);
return 0;
}