MCI コマンド


コマンドによるマルチメディア操作

MCI コマンドを用いた、いわゆる MCI API は MCIWnd とは異なり
ウィンドウを介さず、直接マルチメディアコンテンツを制御する手法を用います
これも、基本的には MCI ウィンドウを用いた方法のように簡単に扱えますが
多くのコマンドや構造体、フラグなどが存在するため多少難しくなっています

MCI API は、MCI ウィンドウとは異なり、直接マルチメディアを制御します
これを用いれば MCIWnd ウィンドウのような、マルチメディアウィンドウを作れることから
MCI コマンドを用いた手法は中レベルのマルチメディア API に属すると考えられます

MCI コマンドとは、メッセージの様に要求を表すフラグと情報を送信することで
MCI デバイスを開いたり、再生や制御、情報の取得等、多彩な操作が可能です
MCI コマンドを送信するには mciSendCommand() 関数を使います
MCIERROR mciSendCommand(
	MCIDEVICEID IDDevice , UINT uMsg ,
	DWORD fdwCommand , DWORD dwParam
);
IDDevice には、コマンドメッセージを受ける MCI デバイスの識別子を指定します
MCI コマンドを送信するには、まず MCI デバイスを開かなければなりません
MCI デバイスの識別子は MCIDEVICEID 型で
MCI_OPEN コマンドを送信する時以外は、必ず指定しなければなりません

uMsg には、コマンドメッセージを指定します
fdwCommand は、コマンドの追加情報となるフラグを
dwParam は、コマンドの情報を格納した構造体のポインタを指定します
これらの引数は、用いるコマンドメッセージによって決定されます

成功すれば 0 が、失敗すれば 0 以外の値が返ります
失敗した時、下位ワードにはエラーコードが格納されています
上位ワードは、エラーコードがデバイス固有の場合に、ドライバの ID が格納されます
このエラーコードは MCIERROR 型として定義されています

fdwCommand で指定するフラグは、コマンドによって異なりますが
コマンドに関係なく、以下のフラグを組み合わせて指定することができます

定数解説
MCI_NOTIFY デバイスは、アクションを完了すると
親ウィンドウに MM_MCINOTIFY メッセージをポストする
このメッセージは、コマンドが処理を終了した時に送られてくる
MCI_TEST デジタルビデオデバイス、またはVCR デバイスに問い合わせ
コマンドを実行できるかどうかを求める
実行できない場合はエラーを返します
子のフラグが指定されたコマンドは、実際の処理を行いません
MCI_WAIT 要求されたアクションが終了するまで制御を返さない

MCI のエラーコードは数多くあり、数値を解析することで意味を知ることができますが
それよりも mciGetErrorString() 関数を使って処理する方が簡単でしょう
この関数は、エラーコードをエラー文字列に変換します
BOOL mciGetErrorString(
	DWORD fdwError,
	LPTSTR lpszErrorText , UINT cchErrorText
);
fdwError には、MCI API が返したエラーコードを指定します
lpszErrorText には、エラーコードの意味を表した文字列を格納するバッファへのポインタ
cchErrorText は lpszErrorText が指すバッファのサイズを指定します
成功すれば TRUE が、エラーコードが不明であれば FALSE が返ります

MCI が返す文字列は、最大で 128 文字になる可能性があります
筆者は、大事をとって 1024 バイトのバッファを用意していますが
使われる文字コードが確定しているのであれば、計算して最適化してもかまいません

まず、MCI コマンドを使うためには MCI デバイスを開かなくてはなりません
MCI デバイスを開いてコンテンツを再生するには MCI_OPEN コマンドを送ります

このコマンドを送る場合 fdwCommand 引数は以下の定数の組み合わせになります
フラグは、アニメーションやオーディオなど、MCI デバイスで異なるものもあります

定数解説
MCI_OPEN_ALIAS 所定の構造体の lpstrAlias メンバに別名が格納されている
MCI_OPEN_SHAREABLE デバイス、またはファイルを共有可能として開く
このフラグを指定せずに開かれたデバイス、またはファイルは
後続する MCI_OPEN で開くことができない
MCI_OPEN_TYPE 所定の構造体の lpstrDeviceType メンバに
デバイスタイプの名前、またはそれを表す定数が格納されている
定数を用いる場合は MCI_OPEN_TYPE_ID フラグも指定する
MCI_OPEN_TYPE_ID 所定の構造体の lpstrDeviceType メンバの下位ワードに
標準 MCI デバイスタイプ識別子が格納されている
必要に応じ、上位ワードにデバイスインデックスを格納できる
複合デバイス
MCI_OPEN_ELEMENT 所定の構造体の lpstrElementName メンバに
ファイル名が格納されている
自動タイプ選択を使うには、lpstrDeviceType メンバを NULL にする
MCI_OPEN_ELEMENT_ID 所定の構造体の lpstrElementName メンバを
ダブルワードの値として解釈する
アニメーションフラグ
MCI_ANIM_OPEN_NOSTATIC デバイスは、パレット内の静的システムカラーの数を
2つにしなければならない
MCI_ANIM_OPEN_PARENT 親ウィンドウのハンドルを MCI_ANIM_OPEN_PARMS 構造体の
hWndParent メンバで指定する
MCI_ANIM_OPEN_WS ウィンドウスタイルを MCI_ANIM_OPEN_PARMS 構造体の
dwStyle メンバで指定する
これは、アプリケーションがウィンドウを提供しない場合
ドライバが作成し、表示するウィンドウのスタイルです
デジタルビデオフラグ
MCI_ANIM_OPEN_NOSTATIC デバイスは、パレット内の静的システムカラーの数を
2つにしなければならない
MCI_DGV_OPEN_PARENT 親ウィンドウのハンドルを MCI_DGV_OPEN_PARMS 構造体の
hWndParent メンバで指定する
MCI_DGV_OPEN_WS ウィンドウスタイルを MCI_DGV_OPEN_PARMS 構造体の
dwStyle メンバで指定する
ビデオオーバレイフラグ
MCI_OVLY_OPEN_PARENT 親ウィンドウのハンドルを MCI_OVLY_OPEN_PARMS 構造体の
hWndParent メンバで指定する
MCI_OVLY_OPEN_WS ウィンドウスタイルを MCI_OVLY_OPEN_PARMS 構造体の
dwStyle メンバで指定する
ウェーブフォームオーディオフラグ
MCI_WAVE_OPEN_BUFFER MCI_WAVE_OPEN_PARMS 構造体の
dwBufferSeconds メンバでバッファの長さを指定する

多くのフラグがありますが、とりあえず今回は一番簡単な音声を扱うので
共通に使えるフラグと、複合デバイス用フラグに注目してください
次に dwParam に渡す構造体ですが、通常は MCI_OPEN_PARMS を使います
typedef struct {
    DWORD        dwCallback; 
    MCIDEVICEID  wDeviceID; 
    LPCSTR       lpstrDeviceType; 
    LPCSTR       lpstrElementName; 
    LPCSTR       lpstrAlias; 
} MCI_OPEN_PARMS;
dwCallback には、MCI_NOTIFY フラグを指定している場合
MM_MCINOTIFY メッセージを受けるウィンドウのハンドルを指定します

wDeviceID は、開いた MCI デバイスのデバイス識別子が格納されます
このメンバに格納された識別子を使って、今後コマンドを用います

lpstrDeviceType には、デバイスのタイプを表す名前、または定数を指定します
このメンバは、対象によって以下の名前、または定数のいずれかを指定します

名前定数解説
animation MCI_DEVTYPE_ANIMATION アニメーションデバイス
cdaudio MCI_DEVTYPE_CD_AUDIO CD オーディオデバイス
dat MCI_DEVTYPE_DAT デジタルオーディオデバイス
digitalvideo MCI_DEVTYPE_DIGITAL_VIDEO 非 GDI ベースのウィンドウ内デジタルビデオ
other MCI_DEVTYPE_OTHER 未定義デバイス
overlay MCI_DEVTYPE_OVERLAY オーバーレイデバイス
scanner MCI_DEVTYPE_SCANNER イメージスキャナ
sequencer MCI_DEVTYPE_SEQUENCER MIDI シーケンサデバイス
vcr MCI_DEVTYPE_VCR ビデオカセットレコーダー、またはプレーヤー
videodisc MCI_DEVTYPE_VIDEODISC ビデオディスクプレーヤー
waveaudio MCI_DEVTYPE_WAVEFORM_AUDIO ウェーブフォームオーディオデバイス

今回は音声ファイルを扱うため、ウェーブフォームオーディオデバイスを使います
音楽 CD を再生したい場合は MCI_DEVTYPE_CD_AUDIO を使えば良いでしょう

lpstrElementName には、デバイス要素名を指定します
デバイス要素名とは、すなわち再生するコンテンツのパス(ファイル名等)のことです
lpstrAlias は、デバイスに割り当てる別名を指定します
このメンバは必須ではなく、必要に応じて指定するオプションです

これらのフラグと構造体を MCI_OPEN コマンドとして送信すれば
成功すれば wDeviceID メンバに開いたデバイスの識別子が格納されます
そうすれば、このデバイス識別子に対して、コマンドを使って様々な操作を行えます

コンテンツを再生するには MCI_PLAY コマンドを送信します
fdwCommand 引数には、以下のフラグを組み合わせて指定します

定数解説
MCI_FROM 所定の構造体の dwFrom メンバに
現在の時間フォーマットでの開始位置が格納されている
MCI_TO 所定の構造体の dwTo メンバに
現在の時間フォーマットでの終了位置が格納されている
アニメーションフラグ
MCI_ANIM_PLAY_FAST 高速再生する
MCI_ANIM_PLAY_REVERSE 逆再生する
MCI_ANIM_PLAY_SCAN できる限り高速で再生する
MCI_ANIM_PLAY_SLOW 低速再生する
MCI_ANIM_PLAY_SPEED 再生速度が MCI_ANIM_PLAY_PARMS 構造体の
dwSpeed メンバに格納されている
デジタルビデオフラグ
MCI_DGV_PLAY_REPEAT 内容の最後に達したら、再び先頭から再生する
MCI_DGV_PLAY_REVERSE 逆再生する
MCI_MCIAVI_PLAY_FULLSCREEN 再生では、フルスクリーンディスプレイを使う
圧縮ファイル、または8ビットファイルの再生にのみ有効
MCI_MCIAVI_PLAY_WINDOW 再生は、デバイスインスタンスに
関連付けられているウィンドウで行う(デフォルト)
VCR フラグ
MCI_VCR_PLAY_AT MCI_VCR_PLAY_PARMS 構造体の
dwAt メンバに、コマンド全体を開始する時刻が格納されている
デバイスにキューを出した場合、MCI_CUE コマンドで指定された
開始位置に達する時刻が格納されている
MCI_VCR_PLAY_REVERSE 逆再生する
MCI_VCR_PLAY_SCAN できる限り高速で再生する
ビデオディスクフラグ
MCI_VD_PLAY_FAST 高速再生する
MCI_VD_PLAY_REVERSE 逆再生する
MCI_VD_PLAY_SCAN 高速スキャンする
MCI_VD_PLAY_SLOW 低速再生する
MCI_VD_PLAY_SPEED 再生速度が MCI_VD_PLAY_PARMS 構造体の
dwSpeed メンバに格納されている

dwParam 引数には、通常 MCI_PLAY_PARMS 構造体を指定します
この構造体は、次のように定義されています
typedef struct {
    DWORD dwCallback; 
    DWORD dwFrom; 
    DWORD dwTo; 
} MCI_PLAY_PARMS;
dwCallback は、メッセージを通知するウィンドウのハンドルを指定します
dwFrom には、現在の時間フォーマットでの再生開始位置を
dwTo は再生終了位置を指定します

このコマンドを送れば、指定されているデバイス識別子のコンテンツを再生します
とりあえず、アニメーションは後ほど紹介するので、まずは簡単な音声を再生しましょう

最後に、処理が終われば、デバイス、またはファイルへのアクセスを解放します
MCI デバイスを閉じずにアプリケーションを終了してしまえば
デバイスにアクセスできない状態のままになるなど、不具合が発生してしまいます

MCI デバイス、またはファイルのアクセスを解放するには MCI_CLOSE を使います
fdwCommand には、共通フラグのみを指定することができます
dwParam は MCI_GENERIC_PARMS 構造体へのポインタを指定します
typedef struct {
    DWORD dwCallback;  
} MCI_GENERIC_PARMS;
dwCallback には、メッセージを通知する親ウィンドウのハンドルを指定します
わざわざ構造体を使わずとも、ハンドルを直接渡したほうが手っ取り早いとも考えられます
#include <windows.h>
#define TITLE TEXT("Kitty on your lap")

PSTR strFile;

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	static MCI_OPEN_PARMS mop;
	int iErr;
	TCHAR strErr[1024];

	switch (msg) {
	case WM_DESTROY:
		mciSendCommand(mop.wDeviceID , MCI_CLOSE , 0 , 0);
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		mop.dwCallback = (DWORD)hWnd;
		mop.lpstrDeviceType = (LPCSTR)MCI_DEVTYPE_WAVEFORM_AUDIO;
		mop.lpstrElementName = strFile;

		iErr = mciSendCommand(0 , MCI_OPEN , MCI_OPEN_TYPE |
			MCI_OPEN_TYPE_ID | MCI_OPEN_ELEMENT , (DWORD)&mop);
		if (iErr) {
			mciGetErrorString(iErr , strErr , 1024);
			MessageBox(hWnd , strErr , NULL , MB_OK);
		}

		mciSendCommand(mop.wDeviceID , MCI_PLAY , 0 , 0);
		return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hWnd;
	MSG msg;
	WNDCLASS winc;

	strFile = lpCmdLine;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hWnd = CreateWindow(
			TEXT("KITTY") , TITLE ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);
	if (hWnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}
このプログラムは、コマンドライン引数で指定した WAVE ファイルを再生します
WAVE フォームオーディオデバイス以外のファイルを指定した場合
MCI_OPEN コマンドはエラーコードを返し、エラーダイアログを表示する仕組みになっています


アニメーション

音声の再生には、描画するウィンドウを必要としませんでしたが
アニメーションやデジタルビデオの再生には、描画するウィンドウが必要になります
しかし、MCI_OPEN や MCI_PLAY にそれを指定する情報はありませんでした
dwCallback は描画するウィンドウではなく、メッセージ送信のためのウィンドウです

実は、コマンドで指定する構造体は、フラグでも変化します
MCI_OPEN_PARMS 構造体は、全てのデバイスに共通する構造体であり
一種のテンプレートのようなもので、MCI デバイスによっては不十分な情報です

MCI_OPEN コマンドは MCI_OPEN_PARMS 以外に、次の構造体を渡すこともできます
MCI_ANIM_OPEN_PARMS 構造体はアニメーションデバイスの場合に
MCI_DGV_OPEN_PARMS 構造体はデジタルビデオデバイスの場合に
そして MCI_OVLY_OPEN_PARMS 構造体はビデオオーバレイデバイスの場合に
それぞれ dwParam 引数に渡すことができます
typedef struct {
    DWORD        dwCallback; 
    MCIDEVICEID  wDeviceID; 
    LPCSTR       lpstrDeviceType; 
    LPCSTR       lpstrElementName; 
    LPCSTR       lpstrAlias;
    DWORD        dwStyle;
    HWND         hWndParent;
} MCI_ANIM_OPEN_PARMS;

typedef struct {
    DWORD dwCallback; 
    UINT  wDeviceID; 
    LPSTR lpstrDeviceType; 
    LPSTR lpstrElementName; 
    LPSTR lpstrAlias; 
    DWORD dwStyle; 
    HWND  hWndParent; 
} MCI_DGV_OPEN_PARMS; 

typedef struct {
    DWORD  dwCallback; 
    MCIDEVICEID wDeviceID; 
    LPCSTR lpstrDeviceType; 
    LPCSTR lpstrElementName; 
    LPCSTR lpstrAlias; 
    DWORD  dwStyle; 
    DWORD  hWndParent; 
} MCI_OVLY_OPEN_PARMS;
見てのとおり、これらの構造体は名前と論理的な意味が異なるだけで
保有するメンバと意味は、基本的に同じものと考えてかまいません
dwCallback メンバから lpstrAlias メンバまでは MCI_OPEN_PARMS 構造体と同じです

dwStyle にはウィンドウスタイルを、hWndParent は親ウィンドウのハンドルを指定します
指定したウィンドウに、指定したウィンドウスタイルで描画されます
ただし、fdwCommand フラグで適切なフラグを指定していれば、です
MCI_ANIM_OPEN_PARMS 構造体を指定している場合
MCI_ANIM_OPEN_PARENT と MCI_ANIM_OPEN_WS フラグを指定して
はじめて dwStyle メンバと hWndParent メンバが有効になります

MCI_PLAY においても、細かい再生の制御を行いたい場合は
MCI_PALY_PARMS 構造体を用いるのではなく、専用の構造体を使います
MCI_ANIM_PLAY_PARMS 構造体は、アニメーションの再生において
MCI_VD_PLAY_PARMS 構造体はビデオディスクデバイスにおいて
再生速度を「フレーム数/秒」単位で指定することができます
typedef struct {
    DWORD dwCallback; 
    DWORD dwFrom; 
    DWORD dwTo; 
    DWORD dwSpeed; 
} MCI_ANIM_PLAY_PARMS;
 
typedef struct {
    DWORD dwCallback; 
    DWORD dwFrom; 
    DWORD dwTo; 
    DWORD dwSpeed; 
} MCI_VD_PLAY_PARMS;
dwCallback メンバから dwTo メンバまでは MCI_PLAY_PARMS 構造体と同じです
dwSpeed に、再生速度を指定することができます
もちろん、再生速度を有効にするには、適切なフラグも同時に指定する必要があります
#include <windows.h>
#define TITLE TEXT("Kitty on your lap")

PSTR strFile;

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	static MCI_ANIM_OPEN_PARMS maop;
	static MCI_ANIM_PLAY_PARMS mapp;
	int iErr;
	TCHAR strErr[1024];

	switch (msg) {
	case WM_DESTROY:
		mciSendCommand(maop.wDeviceID , MCI_CLOSE , 0 , 0);
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		maop.dwCallback = (DWORD)hWnd;
		maop.lpstrDeviceType = NULL;
		maop.lpstrElementName = strFile;
		maop.dwStyle = WS_CHILD | WS_VISIBLE;
		maop.hWndParent = hWnd;

		iErr = mciSendCommand(0 , MCI_OPEN , MCI_OPEN_ELEMENT |
			MCI_ANIM_OPEN_PARENT | MCI_ANIM_OPEN_WS , (DWORD)&maop);
		if (iErr) {
			mciGetErrorString(iErr , strErr , 1024);
			MessageBox(hWnd , strErr , NULL , MB_OK);
		}

		mapp.dwCallback = (DWORD)hWnd;
		mciSendCommand(maop.wDeviceID , MCI_PLAY , 0 , (DWORD)&mapp);
		return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hWnd;
	MSG msg;
	WNDCLASS winc;

	strFile = lpCmdLine;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hWnd = CreateWindow(
			TEXT("KITTY") , TITLE ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);
	if (hWnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}
このプログラムは、コマンドライン引数で指定した動画ファイルを再生します
この時、アニメーションは WS_CHILD | WS_VISIBLE スタイルの子ウィンドウとして
親ウィンドウとなる hWnd ウィンドウに表示されます
他のスタイルと組み合わせれば、ボーダーラインやタイトルバーも付加できます


mciSendCommand()

MCIERROR mciSendCommand(
	MCIDEVICEID IDDevice , UINT uMsg ,
	DWORD fdwCommand , DWORD dwParam
);
MCI デバイスにコマンドメッセージを送信します

IDDevice - コマンドメッセージを受ける MCI デバイスの識別子を指定します
uMsg - コマンドメッセージを指定します
fdwCommand - コマンドの追加情報となるフラグを指定します
dwParam - コマンドの情報を格納した構造体のポインタを指定します

戻り値 - 成功すれば 0 、失敗すれば 0 以外の値

mciGetErrorString()

BOOL mciGetErrorString(
	DWORD fdwError,
	LPTSTR lpszErrorText , UINT cchErrorText
);
エラーコードをエラー文字列に変換します

fdwError - MCI API が返したエラーコードを指定します
lpszErrorText - 文字列を格納するバッファへのポインタを指定します
cchErrorText - lpszErrorText が指すバッファのサイズを指定します

戻り値 - 成功すれば TRUE 、エラーコードが不明であれば FALSE



前のページへ戻る次のページへ