MIDI音源の利用



HSPで作成するゲームなどのBGMで使用する一般的なMIDIですが、midファイルを使用しなくてもMIDI音源に収録されている音を出すことが出来ます。
そもそもmidファイルはMIDIデバイスに送るメッセージの羅列なので、そのメッセージを直接MIDIデバイスに送ってやっても音を出してくれるわけです。

MIDIのAPIのライブラリはwinmm.dllです。winmm.dllにはMCIの関数もあります。llmodはロードしてくれないので自分でロードする必要があります。
単純に音を鳴らすだけなら、3つの関数で可能です。

#include "llmod.as"

#module

#deffunc deviceopen	;MIDIデバイスオープン
	ll_libload winmm,"winmm.dll"	;winmm.dllのロード

	getptr phdevice,hdevice
	prm=phdevice,-1,0,0,0
	dllproc "midiOutOpen",prm,5,winmm	;MIDIデバイスオープン
return

#deffunc instrument int,int	;楽器の設定
	mref channel,0	;設定するチャンネル
	mref inst,1	;音色番号

	prm=hdevice,inst<<8+$C0+channel
	dllproc "midiOutShortMsg",prm,2,winmm	;楽器の設定
return

#deffunc sound int,int,int,int	;音を鳴らす・消す
	mref channel,0	;鳴らすチャンネル
	mref scale,1	;音程
	mref volume,2	;音量
	mref silence,3	;消音の場合は1を指定

	prm=hdevice,volume<<16+(scale<<8)+($90-(silence*$10))+channel
	dllproc "midiOutShortMsg",prm,2,winmm
return

#deffunc deviceclose onexit	;MIDIデバイスクローズ
	dllproc "midiOutClose",hdevice,1,winmm

	ll_libfree winmm
return

#global
	deviceopen	;MIDIデバイスをオープン
	instrument 0,0	;楽器No.1(グランドピアノ?)
	sound 0,60,127 : wait 30 : sound 0,60,,1	;ド
	sound 0,62,127 : wait 30 : sound 0,62,,1	;レ
	sound 0,64,127 : wait 30 : sound 0,64,,1	;ミ
	sound 0,65,127 : wait 30 : sound 0,65,,1	;ファ
	sound 0,67,127 : wait 30 : sound 0,67,,1	;ソ
	sound 0,69,127 : wait 30 : sound 0,69,,1	;ラ
	sound 0,71,127 : wait 30 : sound 0,71,,1	;シ
	sound 0,72,127 : wait 30 : sound 0,72,,1	;ド

midiOutOpen…MIDI出力デバイスのオープン
(MIDIデバイスハンドルを格納する変数のポインタ,
MIDI出力デバイスの識別子。-1を指定すればいい,
コールバック関数、イベントハンドル、ウィンドウハンドル、使えないので0を指定,
コールバック関数に渡す付加パラメータ。0を指定,
コールバックのタイプ。0を指定)

midiOutShortMsg…MIDI出力デバイスに短いメッセージを送る
(MIDIデバイスハンドル,
送るMIDIメッセージ)

midiOutClose…MIDI出力デバイスのクローズ
(MIDIデバイスハンドル)

上のスクリプトを実行すると、ドレミファソラシドと音が鳴るはず。
関数使用の手順は、

midiOutOpenでMIDIデバイスをオープン

midiOutShortMsgでチャンネルに音色番号を設定

midiOutShortMsgで音を鳴らす

midiOutCloseでMIDIデバイスをクローズ

という流れになります。
midiOutOpenとmidiOutCloseは値がほぼ決まっているからいいとして、問題はmidiOutShortMsgです。
第2引数のMIDIメッセージは音量や音程を1バイトごとに格納した32ビット数値(つまり普通の数値型)です。ただ実際には3バイトしか使用しません。
1バイト目はステータスバイトといい、命令コードとチャンネル番号を格納します。
命令コードとはその名のとおり、MIDIデバイスへどういう処理をするかを命令するコードです。1バイトのうち上位4ビットに指定します。

0xC0音色を変える
0x90音を鳴らす
0x80音を消す

とりあえずこれが命令コードの基本の表です。他にもありますが、これで十分です。

チャンネルとはいわば演奏者のことで、ひとつのチャンネルに一つの楽器を割り当てることが出来ます。
チャンネルは全部で16個あり、ステータスバイトの下位4ビットに指定します。
チャンネル番号9だけはドラムメンバ専用なので他のチャンネルとは違う感じになります。

例)
$C2…チャンネル番号2に楽器を設定
$92…チャンネル番号2の楽器で音を鳴らす。

という感じです。

2バイト目以降をデータバイトといい、付加パラメータを格納します。
形式は命令コードによって異なります。

1バイト目(nはチャンネル番号)2バイト目3バイト目
0xCn音色番号(0〜127)なし
0x9n音階(0〜127)音量(0〜127)
0x8n音階(0〜127)なし

という感じです。

例)
$0AC2…チャンネル番号2に音色番号11(オルゴール?)を設定
$7F3C92…チャンネル番号2で音量127(最大)でドの音を鳴らす
$3C82…チャンネル番号2のドの音を消す

音階は全部で128個あり、黒鍵盤部分も含みます。
音を消す場合、命令コードを$80にする他に音量を0にしても同じことになります。
MIDIデバイスがクローズされれば全て音を消してくれるので、消し忘れの心配とかはしなくても大丈夫です。

MIDIデバイスに直接メッセージを送るのは十分に高速なので、ゲームの効果音としても使えるかもしれません。
ただし環境によって音が変わってくる可能性がありますので、その辺を気にしないならですけど。
これをうまく使えばHSPプログラムコンテストのショートプログラム部門でも効果音を使うことが出来るかも…(ただし使い勝手は悪い)。


戻る