EternalWindows
MCI / Windows Multimedia

Windows 3.1の頃、マルチメディアファイル再生用のAPIとしてWindows Multimediaが登場しました。 このAPIはファイルの再生に加えて、コーデックを利用した圧縮や解凍、 さらにファイルの入出力や高精度なタイマを操作する関数を提供し、 現在のWindowsでも依然として利用することができます。 次に、ファイルの再生を担当するWindows Multimedia APIの階層を示します。 基本的に、上位に存在するAPIが下位のAPIを呼び出していると考えて問題ありません。

アプリケーションは、マルチメディアファイルを再生する場合に 上記のAPIのいずれかを呼び出すことになります。 たとえば、WAVEファイルを再生したい場合は、waveOut関数を呼び出すことができます。 また、MIDIファイルを再生したい場合はmidiOut関数、 動画を表示したい場合はVFWの関数群を呼び出すことができます。 しかし、ファイル毎に呼び出すべきAPIを理解するのは大変ですから、 どのようなファイルでも統一的に再生できるAPIが用意されています。 それがMCIであり、このAPIはファイルに応じて呼び出すべきAPIを内部で適切に選択して、 再生を行うようになっています。 つまり、マルチメディアファイルの再生に本来必要な作業をアプリケーションから抽象化します。 このようなAPIは高水準APIと呼ばれ、各種ファイルについての知識が不要な点に加え、 比較的少ないコード量でファイルを再生できるという利点があります。 また、MCIWndはMCIよりもさらに高水準なAPIであり、ファイルを再生するためのUIまで提供します。 一方、waveOutやmidiOut、そしてVFWは低水準APIと呼ばれ、 ファイルを実際に再生するまでに必要なコード量は、 MCIやMCIWndと比べて圧倒的に多いものになります。

高水準APIの方が再生が簡単であるのにも関わらず、低水準APIを呼び出す意味はあるのでしょうか。 そもそも、このような高低を表す単語は、機能の優劣を示すものではありません。 高水準APIの目的の1つは、どのようなファイルでも同じように扱えるようにすることですが、 これは悪く言い換えれば、どのようなファイルでも起こり得る操作しかサポートしないということになります。 つまり、MCIやMCIWndで可能なのは、再生や一時停止といった基本的な操作のみであり、 ファイル特有の高度な処理は行うことができないのです。 こうした操作を行う場合に呼び出すのが、低水準APIになります。 そして、ファイル特有の高度な操作を行うには、そのファイルについての知識も必要になりますから、 結果として低水準APIを扱ったコードが難解になるのです。 また、低水準APIを呼び出すといっても、WAVEやMP3の場合ならばwaveOut関数だけを呼び出すわけではありません。 ファイルから目的のデータを取得するためにmmio関数を呼び出すことになりますし、 MP3の場合ならば圧縮を解凍するためにacm関数を呼び出す必要もあります。 低水準APIを呼び出せば、アプリケーションが行える操作の範囲は増えることになりますが、 それと同時に多くの知識とコード量も要求されることになります。

上図には含まれていませんでしたが、 マルチメディアファイルの再生はPlaySoundという関数でも行うことができます。 この関数もWindows Multimedia APIの1つであり、 WAVEファイルしか再生できないという欠点はあるものの、 その呼び出しはMCIよりも簡単となっています。 このため、効果音として手軽に再生したい場合などは、便利な関数といえるでしょう。 次に、PlaySoundの定義を示します。

BOOL PlaySound(
  LPCTSTR pszSound,  
  HMODULE hmod,     
  DWORD fdwSound    
);

pszSoundは、サウンドのファイル名、またはサウンドのデータを維持しているバッファを指定します。 前者の場合は、fdwSoundにSND_FILENAMEを指定し、後者の場合はSND_MEMORYを指定します。 hmodは、サウンドをリソースとして含むモジュールのハンドルを指定します。 ハンドルを指定する場合は、fdwSoundにSND_RESOURCEを指定します。 fdwSoundは、サウンドに再生に関する定数を指定します。 SND_ASYNCを指定した場合、サウンドの再生が非同期になり、 再生が終了する前に関数が制御を返すことになります。 戻り値は、関数が成功した場合にTRUEが返ります。 存在しないファイル名を指定してもTRUEが返るので注意してください。

今回のプログラムは、PlaySoundによってWAVEファイルを再生します。 Windows Multimedia APIを呼び出す場合は、 winmm.libへのリンクが必要になります。

#include <windows.h>

#pragma comment(lib, "winmm.lib")

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	PlaySound(TEXT("sample.wav"), NULL, SND_FILENAME);

	MessageBox(NULL, TEXT("再生を終了しました。"), TEXT("OK"), MB_OK);

	return 0;
}

PlaySoundの第1引数にsample.wavと指定しているため、 カレントディレクトリに存在するsample.wavが再生されることになります。 第1引数にファイル名を指定する場合は、第3引数にSND_FILENAMEを指定することになります。 また、第2引数はNULLで問題ありません。 PlaySoundを次のように呼び出した場合は、再生が終了する前に関数が制御を返すことになります。

PlaySound(TEXT("sample.wav"), NULL, SND_FILENAME | SND_ASYNC);

SND_ASYNCを指定した場合は、再生が非同期的に実行されるため、 PlaySoundから直ちに制御を返ることになります。 これは、再生が終了するまで待機したくない場合に役立ちます。 ちなみに、次のようなコードを記述すると、 サウンドを任意の時間だけ再生することができます。

PlaySound(TEXT("sample.wav"), NULL, SND_FILENAME | SND_ASYNC);

Sleep(3000);

PlaySound(NULL, NULL, 0);

1回目のPlaySoundによってサウンドを再生し、 関数から制御が返った後にSleepで3秒間待機します。 そしてSleepから制御が返った後に、第1引数にNULLを指定してPlaySoundを呼び出すことで、 再生中のサウンドを停止します。 この結果、ユーザーには開始3秒の音しか聞こえないことになります。

Windows Vista以降のマルチメディアアプリケーションの開発では、 Windows Multimediaよりも、WASAPIやMedia FoundationというAPIを使用するのがよいとされています。 事実、Windows Multimediaは内部でWASAPIを呼び出すようになっているため、 WASAPIを呼び出した方が速度的にも好ましいと思われます。 ただし、WASAPIやMedia FoundationはMIDIの再生をサポートしていないと思われるため、 この場合はWindows MultimediaのmidiOut関数が役に立つでしょう。 また、ゲームのようなアプリケーションではWindows XPから追加されたXAudio2を使用しますが、 このAPIにはMP3のような圧縮データを直接指定することはできません。 よって、こうした場合はWindows Multimediaのacm関数を使用して、データの展開処理を行うことになるでしょう。


戻る