MIDI音楽を演奏する「mciSendString」

VBには音楽を演奏する機能はありません。

と、言う事で、カスタムコントロールを使う、 またはWindows.APIを使って演奏します。 Visual BasicにはMCIコントロールがありますので、 このコントロールを使用する事で音楽の演奏が可能になります。

夢の女神戦ではMCIコントロールは使用せずに、 Windows.APIを使ってMIDI演奏をしました。 ここではAPIによる演奏を書きます。

宣言

APIを使う場合、宣言しないと使えません。

Declare Function mciSendString Lib "winmm.dll" Alias "mciSendStringA" (ByVal lpstrCommand As String, ByVal lpstrReturnString As String, ByVal uReturnLength As Long, ByVal hwndCallback As Long) As Long

演奏

exec = mciSendString("stop midifile ", "", 0, 0)
exec = mciSendString("close midifile", "", 0, 0)
exec = mciSendString("open " & FileName & " type sequencer alias midifile", "", 0, 0)
exec = mciSendString("play midifile from 0", "", 0, 0)

FileNameはパスを含めたファイル名です。前の演奏をいったん止めて(stop)、前のファイルを閉じて(close)、 演奏するファイルを開いて(open)、演奏スタート(play)って感じですね。

落とし穴

Open時のファイルネームにスペースがあると、正しくファイルを見つける事が出来ません。 どうやら、mciSendStringはスペースをファイル名の区切りと見ているようです。

例えば・・
FileName = A:\Program files\Game\Test.mid
exec = mciSendString("open " & FileName & " type sequencer alias midifile", "", 0, 0)
これだとファイルが見つかりません。

Openする時のファイル指定では、スペースを混ぜてはいけません。


解決法。

ファイル名にスペースは入れない。

実行時のパス名(フォルダ名)にはスペースが混ざっても対処法があります。
'パス名の取得
FilePath = App.Path
If (Right$(FilePath, 1) <> "\") Then
   FilePath = FilePath + "\"
End If

'カレントドライブ、及びパスの移動
ChDrive FilePath
ChDir FilePath

こうしておけば、mciSendString実行時にファイル名だけ書けばいいので解決します。
FileName = Test.mid
exec = mciSendString("open " & FileName & " type sequencer alias midifile", "", 0, 0)
こんな感じです。私は3週間ほど詰まりました。


もう一つの解決法。

スペースが混ざっても大丈夫な方法を教えてもらいました。
ファイルの名前を""""で囲みます。

exec = mciSendString("open " & """" & FileName & """" & " type sequencer alias midifile", "", 0, 0)
こんな感じかな?


タイマーコントロールでループ。

音楽ループでは、タイマーコントロールを使いました。 一秒毎に音楽を演奏しているか調べて、止まっていれば再演奏をかけます。

'戻り値の長さ
   StatusLen = 15
'status格納変数にスペース代入
   mciStatus = String$(StatusLen + 1, " ")
'チェック!
   checking = mciSendString("status midifile mode", mciStatus, StatusLen, 0)
'debugウィンドウに状態を表示
   Debug.Print "mciStatus: ["; checking; "], "; mciStatus
'デバイスがないときは、この処理を中止する
   If checking = 263 Then
      Exit Sub
   End If
'停止中のときは、演奏を再開する
   If (UCase$(Left$(mciStatus, 7)) = "STOPPED") Then
   'で、再度演奏
      checking = mciSendString("play midifile from 0", "", 0, 0)
   End If