トップ 戻る

MIDIデバイスの操作

現在、ほとんどのWindowsパソコンには、ソフトウエアMIDIデバイスがインストールされています。このMIDIデバイスを操作すると、指定した音色で指定した音を自由に出すことができるので、シーケンサやゲームのBGM再生システムなどを作ることができます。

MIDIデバイスの発音処理

WindowsアプリケーションからMIDIデバイスを制御する処理は、最初にデバイスをオープンし、続いてデバイスの設定や音色変更、発音・消音といったいくつかのコマンドを送ってデバイスを使用した後に解放する、という形で行います。オープンや解放は専用のAPIがあり、また発音や消音、音色の変更などのコマンドもコマンドを32ビットの数値にまとめて送るAPIがあるので、特に複雑な処理は必要ありません。

実際にMIDIの処理を行う時に中心になるのは、発音と消音ですね。MIDIでは、16本のチャネルが用意され、チャネルごとに音色を設定し発音することができます。発音(ノートオン)を行う場合は、チャネルと音程、音の強さ(ベロシティ)を組み込んだ32ビットのメッセージを作成し、それをmidiOutShortMsg()で送信します。音色やベロシティに指定できる数値の範囲は7ビット(0-127)で、チャネル番号は実際の番号から1を引いて0-15で指定します。

発音メッセージの実際の32ビットの数値は、以下のようになっています。

24-31ビット 0
23-16ビット ベロシティ
15- 8ビット ノート番号
 8- 1ビット 0xC0 + チャネル番号(-1)

もともとのMIDIの規格では、チャネル番号n(+1)のノートオンを行う0xCnというコマンドがあり、そのコマンドの配列が、0xCn、ノート番号、ベロシティ、というものでした。MIDI機器につないだケーブルを通してこのコマンドを送ると、音を出すことができたのです。midiOutShortMsg()に渡す32ビットの数値も、メモリ上の配置では、0xCn、ノート番号、ベロシティ、0、とMIDIコマンドの配列とまったく同じになる(バイトオーダーに注意)ので、midiOutShortMsg()はちょうどMIDIコマンドをそのままMIDIデバイスに流すケーブルの役割を果たすわけですね。

MIDIデバイスにメッセージを送るには、事前にデバイスをオープンし、デバイスのハンドルを取得する必要があります。ハンドルを取得したら、あとはそのハンドルとメッセージを引数にmidiOutShortMsg()を呼び出し、デバイスを使い終わったら、midiOutClose()でデバイスを閉じる、というのが処理の流れです。
たとえば、MIDIデバイスを初期化してオクターブ4のCの音を0x40の強さで発音、1000ミリ秒後に消音し、デバイスを解放、という処理は以下のようになります。

MIDIデバイスで音を出す例
MIDIHDR mhMidi;

/* MIDIデバイスオープン */
midiOutOpen(&g_hMidi, MIDIMAPPER, 0, 0, 0);

/* MIDIチャネル1でノート番号0x3cの音をベロシティ0x40で発音 */
midiOutShortMsg(g_hMidi, 0x00403c90);

Sleep(1000);

/* MIDIチャネル1でノート番号0x3cの音を消音 */
midiOutShortMsg(g_hMidi, 0x00003c90);

/* MIDIデバイスクローズ */
midiOutClose(g_hMidi);

消音に関しては専用のメッセージもありますが、ベロシティを0にすることでも実現できます。

システムエクスクルーシブメッセージ送信

MIDIデバイスに送るメッセージには、音を鳴らすショートメッセージのほかに、デバイスの設定を行うシステムエクスクルーシブメッセージというものがあります。といっても、このメッセージを送る機会は通常それほどあるわけではなく、普通はデバイスを初期化するときに念のためGM音源として初期化するGMリセットメッセージを送る、という場面で使うことがある程度でしょう。
ただ、このメッセージはデバイスにやや長いバイト列を送信することもあって手順が少し複雑になります。まず、バッファを用意してそこに送信するメッセージを書き込み、そのバッファのアドレスやメッセージの長さを格納したMIDIHDR構造体を作成。続いてmidiOutPrepareHeader()midiOutLongMsg()midiOutUnprepareHeader()の順に呼び出してメッセージを送信します。
注意が必要なのは、midiOutLongMsg()で実際にメッセージを送信した後、メッセージ送信を行うデバイスドライバの処理が終わるのを待つ必要がある点。送信終了時に呼び出されるコールバック関数を登録するか、送信終了時にセットされるMIDIHDR構造体のフラグを監視して終了を待ちます。

エクスクルーシブメッセージ送信例
MIDIHDR mhMidi;

/* GMリセット用データ */
BYTE abyGMReset[] = {0xf0, 0x7e, 0x7f, 0x09, 0x01, 0xf7};

ZeroMemory(&mhMidi, sizeof(mhMidi));

/* GMリセット送信用バッファ設定 */
mhMidi.lpData = (LPSTR)abyGMReset;
mhMidi.dwBufferLength = 6;
mhMidi.dwBytesRecorded = 6;

midiOutPrepareHeader(g_hMidi, &mhMidi, sizeof(mhMidi));

/* GMリセットメッセージ送信 */
midiOutLongMsg(g_hMidi, &mhMidi, sizeof(mhMidi));

/* GMリセット完了待機 */
while ((mhMidi.dwFlags & MHDR_DONE) == 0);

midiOutUnprepareHeader(g_hMidi, &mhMidi, sizeof(mhMidi));

上の例では、メッセージ送信後デバイスドライバの処理が終わると設定されるMIDIHDR構造体のMHDR_DONEフラグが立つのを待って、次の処理に移っています。

プログラム

プログラムを実行すると、MIDIデバイスをオープン・GMリセットします。その後、C/Dボタンをクリックすると、500ms音を鳴らします。

プログラムソース表示 プログラムダウンロード


トップ 戻る