描画ができたら次は音を再生したいと思うのが自然な流れです
サウンドを扱う場合は「winmm.lib」をリンクさせる必要があります
#pragma comment(lib,"winmm.lib")
ただ鳴らすだけなら
PlaySound関数一行で鳴らせますが・・・
PlaySound(WAVEファイル名, NULL, SND_FILENAME | SND_ASYNC);
ファイルの読み込みを毎回行うせいで遅い上
Waveファイル(.wav)しか読めません
細かい制御も利きません
細かい制御やmp3やAVIなどのマルチメディアを取り扱うには
MCI(Media Control Interface / Multimedia Control Interface)を使います
今回はメディアをオブジェクトとして扱いため、クラス化しています
今回以降はクラスを積極的に使っていく(というか使わざるを得ない)ので
不安な人はC++のクラスを勉強してください
すでに講座も作ってあります
今回は3つのコードファイルが必要です
・MCI.h
・MCI.cpp
・main.cpp
MCI.hです
WAV,MP3,MIDI,CD_AUDIO,AVI
の読み込みと再生、停止などを扱うことができます
//--------------------------------------------// // ファイル名:MCI.h // 概要: 音ファイル再生関係 // // 作成者:Daiki Terai // 作成日:2010/10/23 // 修正履歴: //--------------------------------------------// #ifndef _MCI_H_ #define _MCI_H_ #include <windows.h> #include <tchar.h> #pragma comment(lib, "winmm.lib") // メディアタイプ enum EMEDIA { WAVE, MIDI, MP3, CD_AUDIO, AVI }; // MCIクラス class CMCI { private: MCIDEVICEID m_ID; // ID public: CMCI(); virtual ~CMCI(); // メディアを開く bool Open(const TCHAR* FileName,EMEDIA Media); //-------------------------------------------------------------------// // 再生 // ループ再生するときは通知メッセージ処理するので // ウィンドウハンドルを渡すこと // // 引数: bLoop ループ再生フラグ // hWnd 処理終了通知メッセージを受け取るウィンドウハンドル //-------------------------------------------------------------------// void Play(bool bLoop = false,HWND hWnd = NULL); // 停止 void Stop(); // 巻き戻し void Rewind(); // 一時停止 void Pause(); // 一時停止解除 void Resume(); // 現在位置取得 DWORD GetPos(); // 再生時間取得 DWORD GetLength(); // 閉じる void Close(); }; #endif
MCI.cppです
ファイルオープン、(ループ)再生、停止などの基本的な機能の実装をしています
// MCI.cpp #define _CRT_SECURE_NO_WARNINGS // 警告抑制用 #define _CRT_NON_CONFORMING_SWPRINTFS // 警告抑制用 #include "MCI.h" CMCI::CMCI():m_ID(0) {} CMCI::~CMCI(){ this->Stop(); this->Close(); } // メディアを開く bool CMCI::Open(const TCHAR* FileName,EMEDIA Media){ MCI_OPEN_PARMS MCIOpen = {}; // 開く用 switch(Media){ case WAVE: { MCIOpen.lpstrDeviceType = _T("WaveAudio"); break; } case MIDI: { MCIOpen.lpstrDeviceType = _T("Sequencer"); break; } case MP3: { MCIOpen.lpstrDeviceType = _T("MPEGVideo"); break; } case CD_AUDIO: { MCIOpen.lpstrDeviceType = _T("CDAudio"); break; } case AVI: { MCIOpen.lpstrDeviceType = _T("avivideo"); break; } } MCIOpen.lpstrElementName = FileName; if(mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_ELEMENT, (DWORD_PTR)&MCIOpen) != 0){ TCHAR Buf[MAX_PATH] = {}; _stprintf(Buf,_T("%sが開けませんでした"),FileName); MessageBox(NULL,Buf,NULL,MB_OK); return false; } m_ID = MCIOpen.wDeviceID; // ミリ秒単位に設定 MCI_SET_PARMS MCISet = {}; MCISet.dwTimeFormat = MCI_FORMAT_MILLISECONDS; mciSendCommand(m_ID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD_PTR)&MCISet); return true; } //-------------------------------------------------------------------// // 再生 // ループ再生するときは通知メッセージ処理するので // ウィンドウハンドルを渡すこと // // 引数: bLoop ループ再生フラグ // hWnd 処理終了通知メッセージを受け取るウィンドウハンドル //-------------------------------------------------------------------// void CMCI::Play(bool bLoop,HWND hWnd) { if(bLoop){ MCI_PLAY_PARMS MCIPlay = {}; // MCI再生設定 MCIPlay.dwCallback = (DWORD_PTR)hWnd;// MM_NOTIFYメッセージを受け取るウィンドウ mciSendCommand(m_ID,MCI_PLAY, MCI_NOTIFY,(DWORD_PTR)&MCIPlay); } else{ mciSendCommand(m_ID,MCI_PLAY, 0,0); } } // 停止 void CMCI::Stop(){ mciSendCommand(m_ID,MCI_STOP,0,0); } // 巻き戻し void CMCI::Rewind(){ mciSendCommand(m_ID,MCI_SEEK,MCI_SEEK_TO_START,0); } // 一時停止 void CMCI::Pause(){ mciSendCommand(m_ID,MCI_PAUSE,0,0); } // 一時停止解除 void CMCI::Resume(){ mciSendCommand(m_ID,MCI_RESUME,0,0); } // 閉じる void CMCI::Close(){ MCI_GENERIC_PARMS MCIGeneric = {}; // データ mciSendCommand( m_ID, MCI_CLOSE, 0, (DWORD_PTR)&MCIGeneric); }
MCIを制御するにはmciSendCommand関数を使います
MCIERROR mciSendCommand( MCIDEVICEID IDDevice, // デバイス識別子 UINT uMsg, // コマンドメッセージ DWORD fdwCommand, // フラグ DWORD dwParam // パラメータを保持している構造体 );
デバイス識別子というのはメディアのIDのことです
コマンドメッセージはMCIの処理の種類を決めます
MCI_OPENを指定するとメディアを開きます
MCI_PLAYを指定するとメディアを再生します
MCI_STOPを指定するとメディアを停止します
MCI_SEEKを指定するとメディアのシークバー位置を変更します
MCI_PAUSEを指定するとメディアを一時停止します
MCI_RESUMEを指定するとメディアの一時停止を解除します
フラグはコマンドメッセージによって異なります
パラメータ構造体もコマンドメッセージによって異なります
メディアを開くには
MCI_OPEN_PARMS構造体を使います
lpstrDeviceTypeには開くメディアのタイプを文字列で指定します
lpstrElementNameにはファイル名を指定します
実際にメディアを開いているのはこの箇所です
if(mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_ELEMENT, (DWORD_PTR)&MCIOpen) != 0){ TCHAR Buf[MAX_PATH] = {}; _stprintf(Buf,_T("%sが開けませんでした"),FileName); MessageBox(NULL,Buf,NULL,MB_OK); return false; }
オープンに成功したら一意なIDが
wDeviceIDに格納されています
このIDは再生、停止などの制御に使うので覚えておきます
m_ID = MCIOpen.wDeviceID;
時間の単位をミリ秒に設定します
特に設定しなくても再生などはできますが設定すると制御がしやすくなります
// ミリ秒単位に設定 MCI_SET_PARMS MCISet = {}; MCISet.dwTimeFormat = MCI_FORMAT_MILLISECONDS; mciSendCommand(m_ID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD_PTR)&MCISet);
再生箇所は次のようになっています
//-------------------------------------------------------------------// // 再生 // ループ再生するときは通知メッセージ処理するので // ウィンドウハンドルを渡すこと // // 引数: bLoop ループ再生フラグ // hWnd 処理終了通知メッセージを受け取るウィンドウハンドル //-------------------------------------------------------------------// void CMCI::Play(bool bLoop,HWND hWnd) { if(bLoop){ MCI_PLAY_PARMS MCIPlay = {}; // MCI再生設定 MCIPlay.dwCallback = (DWORD_PTR)hWnd;// MM_NOTIFYメッセージを受け取るウィンドウ mciSendCommand(m_ID,MCI_PLAY, MCI_NOTIFY,(DWORD_PTR)&MCIPlay); } else{ mciSendCommand(m_ID,MCI_PLAY, 0,0); } }
ここでループ再生させるときは、ウィンドウにメディアの再生が終了したときを知らせなければなりません
MCI_PLAY_PARMS構造体のdwCallbackに処理終了メッセージを受け取るウィンドウのハンドルを指定します
main.cppです
メインコードに自作のMCI.hをインクルードさせる必要があります
また、CMCIクラス変数もグローバル変数として作成しておきましょう
(本当はグローバル変数は使いたくないんですが・・・
ウィンドウをクラス化すればこの問題は解決できます)
#include "MCI.h" #include <tchar.h> #include <stdio.h> #include <stdlib.h> #include <windows.h> #include <windowsx.h> #include <commctrl.h> #pragma comment(lib,"comctl32.lib") #pragma comment(lib,"winmm.lib") // MCI CMCI g_Media[2];
メッセージ処理部分です
//------------------------------------------------------// // ウィンドウ作成メッセージ処理 // // 引数: hWnd ウィンドウハンドル // lpCreateStruct ウィンドウ作成情報 //------------------------------------------------------// BOOL OnCreate(HWND hWnd,LPCREATESTRUCT lpCreateStruct){ g_Media[0].Open(_T("あぶないおにいちゃんです.mp3"),MP3); g_Media[1].Open(_T("ゆっくりしていってね.wav"),WAVE); g_Media[0].Play(true,hWnd); g_Media[1].Play(); return TRUE; } //------------------------------------------------------// // ウィンドウ破棄メッセージ処理 // // 引数: hWnd ウィンドウハンドル //------------------------------------------------------// void OnDestroy(HWND hWnd){ PostQuitMessage(0); } //-----------------------------------------------------// // ウィンドウプロシージャ // デフォルトの処理はDefWindowProc関数で行う // // 引数: hWnd ウィンドウハンドル // msg メッセージ // wParam パラメータ // lParam パラメータ // // 戻り値:処理結果 //-----------------------------------------------------// LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){ switch(msg){ HANDLE_MSG(hWnd,WM_CREATE,OnCreate); // MCI処理終了通知メッセージ case MM_MCINOTIFY:{ MCI_PLAY_PARMS mciPlay; // 通知メッセージ失敗 if (wParam != MCI_NOTIFY_SUCCESSFUL) return 0; // 初期位置に戻す mciSendCommand((MCIDEVICEID)lParam, MCI_SEEK, MCI_SEEK_TO_START, 0); mciPlay.dwCallback = (DWORD_PTR)hWnd; // ループ再生 mciSendCommand((MCIDEVICEID)lParam, MCI_PLAY, MCI_NOTIFY, (DWORD_PTR)&mciPlay); return 0; } HANDLE_MSG(hWnd,WM_DESTROY,OnDestroy); } return DefWindowProc(hWnd, msg, wParam, lParam); }
ウィンドウ作成時にWM_CREATEというメッセージが1度だけ送られてきます
ウィンドウに依存する変数の初期化などの処理を行います
メッセージクラッカーでOnCreate関数で初期化処理をします
HANDLE_MSG(hWnd,WM_CREATE,OnCreate);
今回は初期化時にメディアをオープンし、再生してます
Openメソッドの第2引数は、適切なメディアタイプを指定してください。MCI.hで定義しています
ループ再生にはPlayメソッドに、ウィンドウハンドルを渡す必要があります
g_Media[0].Open(_T("あぶないおにいちゃんです.mp3"),MP3); g_Media[1].Open(_T("ゆっくりしていってね.wav"),WAVE); g_Media[0].Play(true,hWnd); g_Media[1].Play();
再生終了したときループ再生フラグを立てているメディアには
MM_MCINOTIFYメッセージが送られてきます
これに関してはメッセージクラッカーは用意されていないので
ウィンドウプロシージャで直接処理しています
ループ処理自体はシークバー位置を初期位置に戻し
再び再生終了時にMM_MCINOTIFYメッセージを送ることで無限に再生ループをつくっています
// MCI処理終了通知メッセージ case MM_MCINOTIFY:{ MCI_PLAY_PARMS mciPlay; // 通知メッセージ失敗 if (wParam != MCI_NOTIFY_SUCCESSFUL) return 0; // 初期位置に戻す mciSendCommand((MCIDEVICEID)lParam, MCI_SEEK, MCI_SEEK_TO_START, 0); mciPlay.dwCallback = (DWORD_PTR)hWnd; // ループ再生 mciSendCommand((MCIDEVICEID)lParam, MCI_PLAY, MCI_NOTIFY, (DWORD_PTR)&mciPlay); return 0; }
全ソースコードと音ファイルは下からダウンロードしてください
main.zip
ソースファイルと音ファイルは
作成したプロジェクトと同じ場所においてください