Nodachisoft Nodachi Sword Icon
  
@あまじ✎ 2021年3月22日に更新

第3章9 MCIを使って音を鳴らす

イチからゲーム作りで覚えるC言語
第3章8 PlaySound関数を使って音を鳴らす : PREV
NEXT : 第4章1 コンソール・ゲーム用ライブラリ :

概要

Windows API の MCI コマンド(MCI は Media Control Interface の略)を使って音声を鳴らす方法について確認します。

小さいファイルを単純に再生したいだけであれば、PlaySound 関数の方が簡単な場合もあります。

MCI コマンドを使った場合は、音の波形を直接編集してエフェクトをかける、というようなことはできませんが、 音の再生を一時停止したり巻き戻したり、という制御が簡単にできるのがメリットです。

wave 形式だけでなく、mp3 など、Windows OS 上で再生できる形式であれば再生できます。

簡単な音楽再生プレイヤ(コマンドメッセージ)

MCI コマンドを使って音の再生を制御するには、 コマンドメッセージ(Command-Message)を送信して制御する方法と、コマンド文字列(Command-String)を使って命令する方法があります。

まずはコマンドメッセージを送信することのできる、mciSendCommand 関数を使った、 1つの音声ファイル「sarasara.wav」を再生、停止、巻き戻し、一時停止などができる シンプルな音楽プレイヤを作ってみます。

 
mci_ex1.c
#include <stdio.h>
#include <windows.h>
#pragma comment(lib,"winmm")

int main ()  {
	MCI_OPEN_PARMS mParam;
	char inputKey;
	mParam.lpstrDeviceType = L"WaveAudio";
	mParam.lpstrElementName = L"sarasara.wav";
	mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_ELEMENT, (DWORD)&mParam);
	printf("はーぴー:音を流すので指示をだしてね♪\n");
	BOOL isLoopFlag = TRUE;
	while(isLoopFlag) {
		printf("\n");
		printf("  [e]nd : 終了      [p]lay : 再生      [s]top : 停止\n");
		printf("  [b]ack:巻き戻す  [w]ait : 一時停止   [g]o : 停止解除\n");
		printf("入力:");
		inputKey = getchar();
		switch (inputKey) {
		case 'p':	// play:再生
			mciSendCommand(mParam.wDeviceID, MCI_PLAY, 0, 0);
			printf("はーぴー:再生しますね!\n");
			break;
		case 's':	// stop:停止
			mciSendCommand(mParam.wDeviceID, MCI_STOP, 0, 0);
			printf("はーぴー:停止しますね!\n");
			break;
		case 'w':	// wait:一時停止
			mciSendCommand(mParam.wDeviceID, MCI_PAUSE, 0, 0);
			printf("はーぴー:一時停止しますね!\n");
			break;
		case 'g':	// go:一時停止解除
			mciSendCommand(mParam.wDeviceID, MCI_RESUME, 0, 0);
			printf("はーぴー:一時停止を解除しますね!\n");
			break;
		case 'b':	// back:巻き戻し
			mciSendCommand(mParam.wDeviceID, MCI_SEEK, MCI_SEEK_TO_START, 0);
			printf("はーぴー:巻き戻ししますね!\n");
			break;
		case 'e':	// end:再生を終了する
			mciSendCommand(mParam.wDeviceID, MCI_CLOSE, 0, 0);
			printf("はーぴー:終わりますね!\n");
			isLoopFlag = FALSE;
			break;
		}
		while (getchar() != '\n');
	}
}

実行すると、下のように「sarasara.wav」を再生したり 一時停止したりをキーボードから制御できる超簡易ミュージックプレイヤーとして動きます。

実行画面mciSendCommand

それでは、MCI コマンドメッセージを送信するために必要となる MCIOPENPARMS 構造体と mciSendCommand 関数について確認していきます。

MCIOPENPARMS 構造体

MCIOPENPARAMS 構造体は、再生したい音声ファイルの名前や種類を指定するときに使用します。

構造体は下のようなメンバを持っています。

MCI_OPEN_PARMS構造体
typedef struct tagMCI_OPEN_PARMSW {
    DWORD_PTR   dwCallback;       // コールバックウィンドウのハンドル
    MCIDEVICEID wDeviceID;        // デバイスのID
    LPCTSTR     lpstrDeviceType;  // デバイスの形式
    LPCTSTR     lpstrElementName; // ファイル名
    LPCTSTR     lpstrAlias;
} MCI_OPEN_PARMS; 
  • dwCallback(コールバックウィンドウのハンドル)・・・ コンソールアプりから上記を扱う場合は無視して大丈夫です。
  • wDeviceID・・・後ほど、MCIコマンドを使ってファイルを開いたとき、再生に使う機器(MCI デバイス)のIDがセットされます。
  • lpstrDeviceType・・・ Wave形式なら「WaveAudio」、MP3形式なら「MPEGVideo」などを指定します。
  • lpstrElementName ・・・ ファイル名を指定します。
  • lpstrAlias・・・デバイスに割り当てる別名です。オプションなので指定しなくても大丈夫です。

今回のプログラムでは、下のように2つのメンバのみ設定しています。

 
mci_ex1.c
	mParam.lpstrDeviceType = L"WaveAudio";
	mParam.lpstrElementName = L"sarasara.wav";

Wave 形式の音声ファイル「sarasara.wav」を開いて欲しい、というパラメータを作成しています。

mciSendCommand 関数

続いて mciSendCommand 関数の使い方を確認していきます。

mciSendCommand 関数は下のように定義されています。

mciSendCommand関数の定義
MCIERROR mciSendCommand(
    MCIDEVICEID IDDevice,   // 命令を送る先の MCI デバイス番号
    UINT        uMsg,       // 命令の種類(メッセージ)
    DWORD_PTR   fdwCommand, // 命令のフラグ
    DWORD_PTR   dwParam     // 命令のパラメータ構造体
);
  • IDDevice には命令を送る先のデバイス番号が入ります。
  • uMsg には、「音声ファイル開いて」「再生して」とか「停止して」などの様々な命令の種類が入ります。
  • fdwCommand には命令を送る際のフラグが入ります。
  • dwParam には命令の詳細パラメータが渡されます。さきほどのMCIOPENPARMS構造体を渡したりします。

使用例

コードを追って確認していきます。

MCI コマンドで音声ファイルを再生するには、まず音を再生するのに使うデバイスと 再生するファイルを指定します。

9 行目の mciSendCommand 関数で、MCIデバイスを開いています。 また、直前に作成した MCIOPENPARMS 構造体を渡して、 指定した音声ファイルを開いています。

 
mci_ex1.c
int main ()  {
	MCI_OPEN_PARMS mParam;
	char inputKey;
	mParam.lpstrDeviceType = L"WaveAudio";
	mParam.lpstrElementName = L"sarasara.wav";
	mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_ELEMENT, (DWORD)&mParam);

MCIコマンドが成功すると、渡した MCIOPENPARMS 構造体の変数 mParam.wDeviceID に、 再生に使用する機器のID(MCI デバイスID)がセットされます。

再生する機器が一つついている、よくあるパソコンの構成でしたら、1 がセットされるかと思います。

以降、入力されるキーボードの文字によって、開いた音声ファイルの制御を mciSendCommand を使って行っています。

下のように音声ファイルを制御できます。

mciSendCommandの例
mciSendCommand(mParam.wDeviceID, MCI_PLAY, 0, 0);    // 再生     
mciSendCommand(mParam.wDeviceID, MCI_STOP, 0, 0);    // 停止
mciSendCommand(mParam.wDeviceID, MCI_PAUSE, 0, 0);   // 一時停止
mciSendCommand(mParam.wDeviceID, MCI_RESUME, 0, 0);  // 一時停止の解除
mciSendCommand(mParam.wDeviceID, MCI_SEEK, MCI_SEEK_TO_START, 0); // 再生位置を最初に戻す
mciSendCommand(mParam.wDeviceID, MCI_CLOSE, 0, 0);   // MCIデバイス使用を終了

命令一つで簡単に制御できてとても便利です!

MCIERR 構造体

mciSendCommand 関数の戻り値に MCIERROR が得られます。 この MCIERROR の数値を確認することで、MCI命令が成功したか、失敗したかを確認できます。

MCIERROR が 0 なら、ちゃんと mciSendCommand 実行に成功しています。

エラーの種類は様々なものがありますが、例えば、”指定したファイルが存在しない” というエラーであれば、 MCIERRFILENOT_FOUND という定数と比較することでチェックできます。

 
mci_ex2.c
#include <stdio.h>
#include <windows.h>
#pragma comment(lib,"winmm")

int main ()  {
	MCI_OPEN_PARMS mParam;
	mParam.lpstrDeviceType = L"WaveAudio";
	mParam.lpstrElementName = L"notfound.wav";  // 存在しないファイル
	MCIERROR e = mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_ELEMENT, (DWORD)&mParam);
    if ( e == 0 ) {
	    printf("はーぴー:音声ファイルを開くことが出来ましたー♪");
    } else if ( e == MCIERR_FILE_NOT_FOUND ) {
        printf("はーぴー:音声ファイルが開けませんでしたー!");
    } else {
        printf("はーぴー:なんかダメでした。エラーコードは %lu です!", e);
    }
}

こんな感じで、音声ファイルがちゃんと開けたかどうかのエラーチェックが出来ます。

簡単な音楽再生プレイヤ(コマンド文字列)

ここまではコマンドメッセージ(mciSendCommand関数)を使って音声ファイルを制御してきました。

続いて、コマンド文字列を使って MCI の制御を行うプログラムを確認してみます。

コマンド文字列を使うには mciSendString 関数を使います。関数は下のように定義されています。

mciSendString関数の定義
MCIERROR mciSendString(
   LPCTSTR lpszCommand,       // MCIコマンド文字列
   LPTSTR  lpszReturnString,  // コマンドの結果を受け取る文字列へのポインタ
   UINT    cchReturn,         // lpszReturnString のサイズ
   HANDLE  hwndCallback       // コールバックウィンドウのハンドル
);
  • 第1引数 lpszCommand … MCIコマンド文字列を指定します。
  • 第2引数 lpszReturnString … コマンドの結果を受け取る文字列へのポインタを指定します。結果がいらないなら NULL でOKです。
  • 第3引数 cchReturn … lpszReturnString のサイズを指定します。
  • 第4引数 hwndCallback … コンソールアプりから利用する時は無視して大丈夫です。NULL を指定します。

この関数を使用した、超簡単な音楽再生プレイヤープログラムは下のようになります。

 
mcistr_ex1.c
#include <stdio.h>
#include <windows.h>
#pragma comment(lib,"winmm")

int main() {
	char inputKey;
	mciSendString(TEXT("open happy.mp3 type Mpegvideo alias happy"), NULL, 0, 0);
	printf("はーぴー:音を流すので指示をだしてね♪\n");
	BOOL isLoopFlag = TRUE;
	while (isLoopFlag) {
		printf("\n");
		printf("  [e]nd : 終了      [p]lay : 再生      [s]top : 停止\n");
		printf("  [b]ack:巻き戻す  [w]ait : 一時停止   [g]o : 停止解除\n");
		printf("入力:");
		inputKey = getchar();
		switch (inputKey) {
		case 'p':	// play:再生
			mciSendString(TEXT("play happy"), NULL, 0, 0);
			printf("はーぴー:再生しますね!\n");
			break;
		case 's':	// stop:停止
			mciSendString(TEXT("stop happy"), NULL, 0, 0);
			printf("はーぴー:停止しますね!\n");
			break;
		case 'w':	// wait:一時停止
			mciSendString(TEXT("pause happy"), NULL, 0, 0);
			printf("はーぴー:一時停止しますね!\n");
			break;
		case 'g':	// go:一時停止解除
			mciSendString(TEXT("resume happy"), NULL, 0, 0);
			printf("はーぴー:一時停止を解除しますね!\n");
			break;
		case 'b':	// back:巻き戻し
			mciSendString(TEXT("seek happy to 00 : 00 : 00"), NULL, 0, 0);
			printf("はーぴー:巻き戻ししますね!\n");
			break;
		case 'e':	// end:再生を終了する
			mciSendString(TEXT("close happy"), NULL, 0, 0);
			printf("はーぴー:終わりますね!\n");
			isLoopFlag = FALSE;
			break;
		}
		while (getchar() != '\n');
	}
}

実行すると、先ほどのプログラム同様、再生や巻き戻しなどをキーボードから入力して 音楽ファイルの再生を制御できます。

mciSendString にコマンドを渡して、再生や停止などを制御しています。 コマンドの文字列は TEXT("文字列") の形でTEXTマクロを使って適切な型に変換してから渡しています。

MCIコマンドの種類は沢山ありますが、音声再生でつかう代表的なものを下に一覧にしておきます。

MCIコマンド 用途 使い方の例
open MCIデバイスを開き音声ファイルを読み込む。
音声ファイル種類は type xxx で指定する。
alias xxx で開いたMCIに別名を割り当てできる。
open happy.mp3 type Mpegvideo alias happy
close MCIデバイスの使用を終了する close happy
play 音声を再生する play happy
stop 音声を停止する stop happy
pause 音声を一時停止する pause happy
resume 音声の一時停止を解除 resume happy
seek 再生位置を指定 seek happy to 00 : 00 : 00
setaudio 設定変更
音量調整(0~1000)
setaudio happy volume to 500

MCIコマンドで音声ファイルを再生する時は、 再生に必要となる部分のデータを徐々に読み込んで再生していきますので、 メモリの使用量は少なくて済みます。大きなバックミュージックなどを再生する時などに使いやすいです。

MCIコマンドで音声を再生したときのメモリ使用料

補足:MCI や PlaySound 以外での音声ファイル再生

MCI コマンドや PlaySound 関数を使う以外にも、音声ファイルを再生する方法は沢山あります。 他にどんな方法があるのかを補足しておきます。

Media Foundation について

MCI コマンドは Windows 3.1 から導入された仕組みで、非常に古くからある機能です。 Windows Vista 以降からはより音声データや動画の再生二特化した Media Foundation 、略して MF という機能が使用できます。

Media Foundation は Windows SDK と呼ばれる、開発者向けのライブラリをインストールすると利用できます。 今回は Media Foundation については割愛します。

Windows に標準でインストールされている Windows Media Player も Media Foundation を使用しています。

音声にフィルタやエフェクトを付けたり、立体音響の計算ができるなど、非常に高機能です。

Windows Multimedia について

Windows API には音声ファイルをもっと詳細に制御するための Windows Multimedia と呼ばれる 動画や音楽を制御する原始的な API(関数)が用意されています。

これらは mmeapi.h というヘッダをインクルードすることで使用できます。

WaveOutXxx / WaveInXxx などの関数群があり、音声データを生成したりエフェクトをかけたりすることが可能です。

DirectX について

DirectX とは Microsoft 社が公開している、ゲームなどでよく使われる音声や動画、3Dデータ処理などを高速に処理できる 機能群です。

Windows10 であれば、DirectX の中には音声を再生することに特化している、DirectX Audio2 という機能があり、 DirectX Audio2 を使用することで、 MIDI や音声を制御する機能が用意されています。 具体的には XBox 360 などのゲーム機内部で使用されています。

DirectX Audio2(細かくはバージョン 2.9)では、先ほど挙げた Media Foundation 機能を使って制御することが推奨されています。

参考文献

イチからゲーム作りで覚えるC言語
第3章8 PlaySound関数を使って音を鳴らす : PREV
NEXT : 第4章1 コンソール・ゲーム用ライブラリ :
 
 
送信しました!

コメント、ありがとうございます。

なんかエラーでした

ごめんなさい。エラーでうまく送信できませんでした。ご迷惑をおかけします。しばらくおいてから再度送信を試していただくか、以下から DM などでご連絡頂ければと思います。

Twitter:@NodachiSoft_jp
お名前:
 
連絡先:
 
メッセージ:
 
戻る
内容の確認!

以下の内容でコメントを送信します。よろしければ、「送信」を押してください。修正する場合は「戻る」を押してください

お名前:
 
連絡先:
 
メッセージ:
 
Roboto からの操作ではないという確認のため確認キーを入れてください。
確認キー=95
戻る
 / 
送信確認へ
コメント欄
コメント送信確認へ

関連ありそうな記事(5件)です!

第1章01 Visual Studio Community 2019 のインストール手順

#C11仕様#C言語#ゲームプログラミング✎ 2021-08-08
C言語でプログラミングをするために、無料で使える Visual Studio Community を使った開発環境を揃えていく手順や注意点をお話しています。
目次
第3章9 MCIを使って音を鳴らす
第3章9 MCIを使って音を鳴らす
概要
概要
簡単な音楽再生プレイヤ(コマンドメッセージ)
簡単な音楽再生プレイヤ(コマンドメッセージ)
MCI_OPEN_PARMS 構造体
MCI_OPEN_PARMS 構造体
mciSendCommand 関数
mciSendCommand 関数
使用例
使用例
MCIERR 構造体
MCIERR 構造体
簡単な音楽再生プレイヤ(コマンド文字列)
簡単な音楽再生プレイヤ(コマンド文字列)
補足:MCI や PlaySound 以外での音声ファイル再生
補足:MCI や PlaySound 以外での音声ファイル再生
Media Foundation について
Media Foundation について
Windows Multimedia について
Windows Multimedia について
DirectX について
DirectX について
参考文献
参考文献
Nodachisoft © 2021