概要

PlaySound APIを使用しwavファイルの再生を行います。
サウンドイベントの再生・wavファイルの再生・繰返再生・停止・非同期再生と終了検出について解説しています。

テスト環境

コンパイラ

Visual C++ 2008 Standard 32/64bit
Visual C++ 2013 Express 32/64bit

実行環境

Windows 8.1 Enterprise 64bit
Windows 7 EnterPrise Service Pack 1 64bit
Windows Vista Ultimate Service Pack 2 32bit
Windows XP Professional Service Pack 3 32bit

定義済みのサウンド識別子を使用してwavファイルを再生

例えば、ログオン時やエラーが発生時に発生する音は、サウンドイベントといいコントロールパネルのサウンドなどで変更できる。

直接wavファイル名を指定しないので、テーマにより音を変えても正常に動作する。
これらの音を発生されるにはPlaySound APIを使用する。音の種類は、定義済みのサウンド識別子で指定できる。
定義済みのサウンド識別子を使用するには、第1引数に定義済みのサウンド識別子、第3引数にSND_ALIASを付加します。
SND_NODEFAULTを付加しない場合、wavファイルが見つからない場合に標準の音を再生します。
SND_SYNCは音の再生が終わるまでPlaySound APIは制御を渡しません。
エラーが発生した場合、FALSEを返します。
例えば、Windowsのログオン時の音を再生させるためのソースコードは以下の通りです。

//      PlaySound API サンプル1
//      定義済みのサウンド識別子により音を指定

#include <windows.h>
#include <tchar.h>

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

int WINAPI _tWinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, TCHAR* CmdLine, int nCmdShow){
        if (PlaySound(_TEXT("WindowsLogon"), NULL, SND_ALIAS | SND_SYNC | SND_NODEFAULT) == FALSE)
                MessageBox(0,_TEXT("再生できませんでした。"),_TEXT("エラー"),MB_OK);
        return 0;
}
ダウンロード playsound_alias.zip(21.2kByte)
ZIPファイルに含まれるファイル

playsound_alias.cpp
playsound_alias.exe
サウンドイベントに対するwavファイルはレジストリで定義されています。
例えばWindowsに対するレジストリキーは以下の通りです。

¥HKEY_CURRENT_USER¥AppEvents¥Schemes¥Apps¥.Default¥イベント名¥.Current
手持ちのWindowsについてレジストリを抽出して調べたところ下表のとおりでした。
該当Windowsの列で○がある場合はサウンドイベントがレジストリに登録されていることを示します。
イベント名 定義済みのサウンド識別子 XP Vista 7 8.1
一般の警告音 .Default
プログラムエラー AppGPFault
選択 CCSelect
Windowsテーマの変更 ChangeTheme
プログラムの終了 Close
バッテリー切れアラーム CriticalBatteryAlarm
デバイスの接続 DeviceConnect
デバイスの切断 DeviceDisconnect
デバイスの接続の失敗 DeviceFail
新着FAXの通知 FaxBeep
バッテリー低下アラーム LowBatteryAlarm
新着メールの通知 MailBeep
最大化 Maximize
メニューコマンド MenuCommand
メニューポップアップ MenuPopup
メッセージのシェイク MessageNudge
最小化 Minimize
通知 Notification.Default
インスタントメッセージ通知 Notification.IM
アラームサンド1 Notification.Looping.Alarm
アラームサンド10 Notification.Looping.Alarm10
アラームサンド2 Notification.Looping.Alarm2
アラームサンド3 Notification.Looping.Alarm3
アラームサンド4 Notification.Looping.Alarm4
アラームサンド5 Notification.Looping.Alarm5
アラームサンド6 Notification.Looping.Alarm6
アラームサンド7 Notification.Looping.Alarm7
アラームサンド8 Notification.Looping.Alarm8
アラームサンド9 Notification.Looping.Alarm9
電話着信サウンド1 Notification.Looping.Call
電話着信サウンド10 Notification.Looping.Call10
電話着信サウンド2 Notification.Looping.Call2
電話着信サウンド3 Notification.Looping.Call3
電話着信サウンド4 Notification.Looping.Call4
電話着信サウンド5 Notification.Looping.Call5
電話着信サウンド6 Notification.Looping.Call6
電話着信サウンド7 Notification.Looping.Call7
電話着信サウンド8 Notification.Looping.Call8
電話着信サウンド9 Notification.Looping.Call9
Notification.Mail
NFP完了 Notification.Proximity
予定表のアラーム Notification.Reminder
Notification.SMS
プログラムの起動 Open
印刷完了 PrintComplete
NFP接続 ProximityConnection
元に戻す(縮小) RestoreDown
元に戻す(拡大) RestoreUp
ツールバーバインドの表示 ShowBand
メッセージ(情報) SystemAsterisk
メッセージ(警告) SystemExclamation
Windowsの終了 SystemExit
システムエラー SystemHand
システム通知 SystemNotification
メッセージ(問い合わせ) SystemQuestion
Windowsの起動 SystemStart
Windowsログオフ WindowsLogoff
Windowsログオン WindowsLogon
Windowsユーザーアカウント制御 WindowsUAC
WindowsUnlock
Windows XP、Windows Vista、Windows 7 のサウンドイベントに対するwavファイルは、Cドライブにwindowsをインストールした場合、C:¥windows¥mediaに保存されています。
Windows 7でC:¥windows¥mediaを見ると

のように、日本語のファイル名になっていますが、コマンドプロンプトのDIRコマンドで見ると以下の様に英語になっています。
ファイル名を指定してPlay Sound APIを使用する場合は、英語名で使用する必要がある。レジストリも英語名で登録されている。

Windows Default.wav
Windows Ding.wav
Windows Error.wav
Windows Exclamation.wav
Windows Feed Discovered.wav
Windows Hardware Fail.wav
Windows Hardware Insert.wav
Windows Hardware Remove.wav
Windows Information Bar.wav

wavファイルの名前を指定して再生

直接wavファイル名を指定して再生します。
PlaySound APIの第1引数にファイル名、第3引数にSND_FILENAMEを付加します。
SND_NODEFAULTを付加しない場合、wavファイルが見つからない場合に標準の音を再生します。
SND_SYNCは音の再生が終わるまでPlaySound APIは制御を渡しません。
エラーが発生した場合、FALSEを返します。
例えば、C:¥WINDOWS¥media¥Windows Battery Critical.wav を再生させるためのソースコードは以下の通りです。

//      PlaySound API サンプル2
//      wavファイル名を指定して再生

#include <windows.h>
#include <tchar.h>

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

int WINAPI _tWinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, TCHAR* CmdLine, int nCmdShow){
        if (PlaySound(_TEXT("C:¥¥WINDOWS¥¥media¥¥Windows Battery Critical.wav"), NULL, SND_FILENAME | SND_SYNC | SND_NODEFAULT) == FALSE)
                MessageBox(0,_TEXT("再生できませんでした。"),_TEXT("エラー"),MB_OK);
        return 0;
}
ダウンロード playsound_file.zip(21.2kByte)
ZIPファイルに含まれるファイル

playsound_file.cpp
playsound_file.exe

wavファイルの名前を指定して再生(ループ再生・停止・終了検出)


カレントフォルダーのtest.wavを再生します。
(本プログラムをテストするときは、test.wavをカレントフォルダーに用意してください。)

使用方法

再生ボタン

クリックすると非同期で1回再生します。

繰返再生ボタン

クリックすると非同期で繰返再生します。

停止ボタン

クリックすると非同期の再生している場合停止します。

終了検出再生ボタン

クリックすると新規にスレッドを立ち上げスレッドの中で同期再生を行います。再生中は、秒数が表示されます。再生中は終了ボタン以外はクリックできないようになります。再生が終了するとほかのボタンが押せるようになります。

終了ボタン

クリックすると再生中でもプログラムを終了させます。
同期再生の場合、再生が終了するまでPlaySoundが制御を返さず、非同期再生の場合再生準備ができたら直ちに制御を返す点が異なります。

再生中のサウンドを停止させる方法

非同期で再生されている場合、PlaySound(NULL,NULL,NULL);を呼び出すと停止できます。 同期再生の場合プログラム自身を終了させます。

プログラムソースの説明

自動的に繰り返してサウンドを再生する方法

PlaySoundの第3引数にSND_LOOPを付加します。
非同期で再生させますのでPlaySound(NULL,NULL,NULL);で停止させます。

PlaySound APIで再生が終了したことを知る方法

同期再生の場合、呼び出したPlaySound APIから制御が戻れば再生が終了したことがわかります。
非同期再生の場合、再生が始まれば制御が戻るため、再生の終了がわかりません。
非同期再生の様に、再生が始めれば制御が戻り、しかも終了がわかるようにするために、新規スレッドで同期再生を行い、Play Sound APIの終了時に呼び出したダイアログボックスへ終了メッセージを送付するようにしました。
本プログラムのThreadPlaySoundを呼び出すと新規スレッドを作成し、そのスレッドの中でPlaySoundで同期再生を行います。再生が終了するとSendMessage APIで呼び出し側にIDC_EXITメッセージを通知します。
再生中は、タイマーにより秒数をカウントし表示します。

ソースコード

playsound_ctl.cpp


//      PlaySound API サンプル3
//      wavファイルを再生 ループ再生・終了検知などサポート

#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include "resource.h"

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

TCHAR* wavFile=_TEXT("test.wav");     //      再生するwavファイル名を指定

//      スレッドに渡すパラメータ
struct PlaySoundPara{
        TCHAR* name;    //      PlaySound APIに渡すwavファイル名        
        DWORD flags;    //      PlaySound APIに渡すフラグ
        HWND hWnd;              //      PlaySound API終了時にメッセージを渡すウィンドウを指定
};

// ダイアログボックスのプロシージャー
LRESULT CALLBACK DlgProc1(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);

int WINAPI _tWinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, TCHAR* CmdLine, int nCmdShow){
        DialogBox(hCurInst, TEXT("PLAY_CTL"), 0, (DLGPROC)DlgProc1);
        return 0;
}

//      CreateThreadにより実行されるスレッド

DWORD WINAPI proc(LPVOID lp){
        DWORD r;
        PlaySoundPara* p=(PlaySoundPara*)lp;
        r = PlaySound(p->name,NULL,p->flags)==FALSE ? FALSE : TRUE;
        SendMessage(p->hWnd,WM_COMMAND,IDC_EXIT,r);  //      PlaySoundの終了をダイアログに知らせる
        return 0;
}


HANDLE handle=0;        //      スレッドのハンドル

//      スレッドに渡すパラメータ(ThreadPlaySoundの自動変数として確保すると関数が終了するときに揮発するので注意)
PlaySoundPara para;


//      新規スレッドを作成する

void ThreadPlaySound(HWND hDlg,TCHAR* name,DWORD flags){
        DWORD id;
        para.name=name;
        para.flags=flags;
        para.hWnd=hDlg;
        handle=CreateThread(NULL,0,proc,(void*)&para,0,&id);
}


// ダイアログボックスのプロシージャー

LRESULT CALLBACK DlgProc1(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam){
        DWORD flags=0;
        static int count=0;
        TCHAR buf[16];
        static UINT_PTR timer_id=0;
        switch (msg) {
                case WM_INITDIALOG:
                        return TRUE;
                case WM_TIMER:
                        _stprintf_s(buf,sizeof(buf)/sizeof(TCHAR),_TEXT("再生中 %d.%d秒"),count/10,count % 10);
                        ++count;
                        SetWindowText(GetDlgItem(hDlg,IDC_LABEL1),buf);
                        return FALSE;
                case WM_COMMAND:
                        switch (LOWORD(wParam)) {
                                case IDC_EXIT:  //      新規スレッドで起動したPlaySoundが終了した場合に呼び出される
                                        KillTimer(hDlg,timer_id);       //      タイマーを終了
                                        EnableWindow(GetDlgItem(hDlg,IDC_STOP_BUTTON),TRUE);
                                        EnableWindow(GetDlgItem(hDlg,IDC_PLAY_BUTTON),TRUE);
                                        EnableWindow(GetDlgItem(hDlg,IDC_PLAY2_BUTTON),TRUE);
                                        EnableWindow(GetDlgItem(hDlg,IDC_REP_BUTTON),TRUE);
                                        CloseHandle(handle);
                                        handle=0;
                                        if((DWORD)lParam==0)
                                                SetWindowText(GetDlgItem(hDlg,IDC_LABEL1),_TEXT("エラー"));
                                        else
                                                SetWindowText(GetDlgItem(hDlg,IDC_LABEL1),_TEXT("再生を終了しました"));
                                        return FALSE;
                                case IDC_STOP_BUTTON:   //      停止ボタンをクリックした場合
                                        PlaySound(NULL,NULL,NULL);
                                        SetWindowText(GetDlgItem(hDlg,IDC_LABEL1),_TEXT("停止しました"));
                                        return TRUE;
                                case IDC_PLAY_BUTTON:   //      再生ボタンをクリックした場合
                                        flags=SND_FILENAME | SND_NODEFAULT | SND_ASYNC;
                                        PlaySound(wavFile,NULL,flags);
                                        SetWindowText(GetDlgItem(hDlg,IDC_LABEL1),_TEXT("再生を開始しました"));
                                        return TRUE;
                                case IDC_PLAY2_BUTTON:  //      新規スレッドでPlaySoundを起動
                                        flags=SND_FILENAME | SND_NODEFAULT | SND_SYNC;
                                        EnableWindow(GetDlgItem(hDlg,IDC_STOP_BUTTON),FALSE);
                                        EnableWindow(GetDlgItem(hDlg,IDC_PLAY_BUTTON),FALSE);
                                        EnableWindow(GetDlgItem(hDlg,IDC_PLAY2_BUTTON),FALSE);
                                        EnableWindow(GetDlgItem(hDlg,IDC_REP_BUTTON),FALSE);
                                        count=0;
                                        timer_id=SetTimer(hDlg,100,100,0);      //      0.1秒のインターバルタイマー
                                        ThreadPlaySound(hDlg,wavFile,flags);
                                        return TRUE;
                                case IDC_REP_BUTTON:    //      繰返再生ボタンをクリックした場合
                                        flags=SND_FILENAME | SND_NODEFAULT | SND_ASYNC | SND_LOOP;
                                        PlaySound(wavFile,NULL,flags);
                                        SetWindowText(GetDlgItem(hDlg,IDC_LABEL1),_TEXT("繰返再生を開始しました"));
                                        return TRUE;
                                case IDCANCEL:  //      終了ボタンをクリックした場合
                                        EndDialog(hDlg, TRUE);
                                        return TRUE;
                                default:
                                        return FALSE;
                        }
                        default:
                                return FALSE;
        }
        return TRUE;
}

resource.h


#define IDC_STOP_BUTTON         100
#define IDC_PLAY_BUTTON         101
#define IDC_PLAY2_BUTTON        102
#define IDC_REP_BUTTON          103
#define IDC_LABEL1 104
#define IDC_EXIT 105

resourece.rc


#include <windows.h>
#include "resource.h"

PLAY_CTL DIALOG DISCARDABLE 0, 0, 312, 47
EXSTYLE WS_EX_DLGMODALFRAME
STYLE WS_POPUP  | WS_CAPTION | WS_SYSMENU  | DS_SETFONT
CAPTION "PlaySound API"
FONT 9, "MS 明朝"
{
 LTEXT  "",IDC_LABEL1,7,7,260,12
 CONTROL "停止", IDC_STOP_BUTTON, "BUTTON", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 7, 26, 54, 14
 CONTROL "再生", IDC_PLAY_BUTTON, "BUTTON", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 68, 26, 54, 14
 CONTROL "終了検出再生", IDC_PLAY2_BUTTON, "BUTTON", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 129, 26, 54, 14
 CONTROL "繰返再生", IDC_REP_BUTTON, "BUTTON", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 190, 26, 54, 14
 CONTROL "終了", IDCANCEL, "BUTTON", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 251, 26, 54, 14
}

ダウンロード playsound_ctl.zip(41.9kByte)
ZIPファイルに含まれるファイル

playsound_ctl.cpp
playsound_ctl.exe
resource.h
resource.rc