No.003 - 03 WAVEファイル読込再生 |

WAVEファイルを読込再生するプログラムを紹介します。
Borland C++ Compiler でコンパイルして下さい
| wave_player.c |
#include <windows.h>
#include <stdio.h>
#include <math.h>
#define IDC_STATIC -1
#define IDC_TEXT 1001
#define IDC_ONOFF 1002
#define IDC_FILE_OPEN 1003
#define SAMPLE_RATE 11025
TCHAR app_name [] = TEXT("PlayWave");
// WAVEファイル読込
read_file(HWND hwnd, char *file_name, WAVEFORMATEX *p_wfe, PBYTE *data_buf, int *data_buf_len){
FILE *fp;
char rb[5];
int ri, byte_len, fmt_len;
short rs;
OPENFILENAME ofn;
memset(&ofn, 0, sizeof(OPENFILENAME));
ofn.lStructSize = sizeof (OPENFILENAME);
ofn.hwndOwner = hwnd;
ofn.lpstrFilter = TEXT("wave ファイル\0*.wav\0")
TEXT("すべて表示 \0*.*\0\0");
ofn.lpstrCustomFilter = NULL;
ofn.nMaxCustFilter = 0;
ofn.nFilterIndex = 0;
ofn.lpstrFile = file_name;
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_FILEMUSTEXIST;
if (!GetOpenFileName(&ofn)) return 0;
rb[4]='\0';
fp=fopen(file_name, "rb");
if (!fp) return -1;
fread( rb, 1, 4, fp); if (strcmp(rb, "RIFF")) return -1;
fread(&ri, 1, 4, fp); byte_len=ri;
fread( rb, 1, 4, fp); if (strcmp(rb, "WAVE")) return -2; // WAVEヘッダ
// == 波形フォーマット
fread( rb, 1, 4, fp); if (strncmp(rb, "fmt", 3)) return -3; // fmtチャンク
fread(&ri, 1, 4, fp); fmt_len=ri; // fmtチャンクバイト数
fread(&rs, 1, 2, fp); p_wfe->wFormatTag=rs; // fmtフォーマットID
fread(&rs, 1, 2, fp); p_wfe->nChannels=rs; // チャンネル数 ==1 or ==2
fread(&ri, 1, 4, fp); p_wfe->nSamplesPerSec=ri; // サンプリング・レート
fread(&ri, 1, 4, fp); p_wfe->nAvgBytesPerSec=ri; // データ速度 (Byte/sec)
fread(&rs, 1, 2, fp); p_wfe->nBlockAlign=rs; // ブロックサイズ (sample×チャンネル数/Byte)
fread(&rs, 1, 2, fp); p_wfe->wBitsPerSample=rs; // サンプル当たりのビット数
p_wfe->cbSize = 0;
fread(rb, 1, 4, fp); if (strcmp(rb, "data")) return -4;
fread(&ri, 1, 4, fp); *data_buf_len=ri;
//サンプリングデータ部 (サンプルビット数)×(チャンネル数)×(サンプル数)
(*data_buf) = malloc(*data_buf_len);
fread(*data_buf, 1, *data_buf_len, fp);
fclose(fp);
return 0;
}
// データをバッファに書きこむ
VOID set_buf(PWAVEHDR p_whdr, char *data_buf, int *index, int data_buf_len){
int i, len=min(p_whdr->dwBufferLength, data_buf_len- *index);
PBYTE p_buffer=p_whdr->lpData;
int i_index= *index, rem_len;
// 音データを出力バッファに入れる
for(i=0; i<len; i++) {
p_buffer [i] = data_buf[i_index];
i_index++;
}
// 音データ末尾のとき出力バッファ残りを0で埋める
rem_len=p_whdr->dwBufferLength-len;
for(i=len; i<rem_len; i++) p_buffer [i] = 0;
*index=i_index;
}
// プロシージャ
BOOL CALLBACK DlgProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam){
static HWAVEOUT hwave; // WAVEデバイス・ハンドル
static PBYTE p_buffer1, p_buffer2; // 音データ格納バッファ(ポインタ)
static PWAVEHDR p_whdr1, p_whdr2; // 音データ・ヘッダ(ポインタ)
static WAVEFORMATEX waveformat; // WAVEデバイス初期化構造体(WAVEデバイスを開くため専用)
static BOOL shut_off_flag, app_closing_flag; // 音の再生停止フラグ、プログラム終了フラグ
static char file_name[MAX_PATH];
static char *data_buf=NULL;
static int data_buf_len=0, index=0;
switch(msg) {
case WM_CREATE: // プログラム起動時
file_name[0]='\0';
CreateWindow( TEXT("STATIC"), TEXT("ファイル選択して下さい"), WS_CHILD | WS_VISIBLE | SS_LEFT, 10,10,320,18,
hwnd, (HMENU)IDC_TEXT, (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE), NULL
);
CreateWindow(TEXT("BUTTON"), TEXT("再生"), WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 80,36,50,28,
hwnd, (HMENU)IDC_ONOFF, (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE), NULL
);
CreateWindow(TEXT("BUTTON"), TEXT("参照"), WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 20,36,50,28,
hwnd, (HMENU)IDC_FILE_OPEN, (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE), NULL
);
return TRUE;
case WM_COMMAND: // コマンド・メッセージ(このプログラムでは IDC_FILE_OPEN, IDC_ONOFF のみ)
switch(LOWORD(wparam)) {
case IDC_FILE_OPEN:
if (read_file(hwnd, file_name, &waveformat, &data_buf, &data_buf_len)<0)
MessageBox(hwnd, TEXT("ファイルを開けませんでした!"), app_name, MB_ICONEXCLAMATION | MB_OK);
if (file_name[0]==0) break;
SetDlgItemText(hwnd, IDC_TEXT, file_name); // 表示ファイル名の更新
InvalidateRect(hwnd, NULL, 1);
break;
case IDC_ONOFF:
if(data_buf == NULL) {
MessageBox(hwnd, TEXT("先にファイルを選んで下さい"), app_name, MB_ICONEXCLAMATION | MB_OK);
break;
}
// hwave が NULLなら(まだWAVEデバイスを開いてまいなら) WAVEデバイスを開く
if(hwave == NULL) { // 2 ヘッダ(WAVEHDR) と 2 バッファの メモリ確保
// 音データ・バッファ確保
p_whdr1 = malloc(sizeof(WAVEHDR));
p_whdr2 = malloc(sizeof(WAVEHDR));
p_buffer1 = malloc(waveformat.nAvgBytesPerSec);
p_buffer2 = malloc(waveformat.nAvgBytesPerSec);
if(!p_whdr1 || !p_whdr2 || !p_buffer1 || !p_buffer2) { // メモリ確保に失敗した時
if(!p_whdr1) free(p_whdr1);
if(!p_whdr2) free(p_whdr2);
if(!p_buffer1) free(p_buffer1);
if(!p_buffer2) free(p_buffer2);
MessageBeep(MB_ICONEXCLAMATION);
MessageBox(hwnd, TEXT("メモリ確保に失敗しました!"), app_name, MB_ICONEXCLAMATION | MB_OK);
return TRUE;
}
shut_off_flag = FALSE; // 音を出力してるかどうかのフラグ
index = 0; // 再生位置リセット
// WAVEデバイスを開く
if(waveOutOpen(&hwave, WAVE_MAPPER, &waveformat,(DWORD) hwnd, 0, CALLBACK_WINDOW)
!= MMSYSERR_NOERROR){ // WAVEデバイスを開くのに失敗したとき
free(p_whdr1);
free(p_whdr2);
free(p_buffer1);
free(p_buffer2);
hwave = NULL;
MessageBeep(MB_ICONEXCLAMATION);
MessageBox(hwnd, TEXT("WAVEデバイスのオープンに失敗しました!"),
app_name, MB_ICONEXCLAMATION | MB_OK);
return TRUE;
}
// ヘッダの準備とセットアップ
p_whdr1->lpData = p_buffer1;
p_whdr1->dwBufferLength = waveformat.nAvgBytesPerSec;
p_whdr1->dwBytesRecorded = 0;
p_whdr1->dwUser = 0;
p_whdr1->dwFlags = 0;
p_whdr1->dwLoops = 1;
p_whdr1->lpNext = NULL;
p_whdr1->reserved = 0;
waveOutPrepareHeader(hwave, p_whdr1, sizeof(WAVEHDR)); // スワップアウト不可にする
p_whdr2->lpData = p_buffer2;
p_whdr2->dwBufferLength = waveformat.nAvgBytesPerSec;
p_whdr2->dwBytesRecorded = 0;
p_whdr2->dwUser = 0;
p_whdr2->dwFlags = 0;
p_whdr2->dwLoops = 1;
p_whdr2->lpNext = NULL;
p_whdr2->reserved = 0;
waveOutPrepareHeader(hwave, p_whdr2, sizeof(WAVEHDR)); // スワップアウト不可にする
}
else { // 音を止める
shut_off_flag = TRUE;
waveOutReset(hwave);
}
return TRUE;
}
break;
case MM_WOM_OPEN: // WAVEデバイス開く
SetDlgItemText(hwnd, IDC_ONOFF, TEXT("停止"));
// WAVEデバイスに2バッファ 送信
set_buf(p_whdr1, data_buf, &index, data_buf_len); waveOutWrite(hwave, p_whdr1, sizeof(WAVEHDR));
set_buf(p_whdr2, data_buf, &index, data_buf_len); waveOutWrite(hwave, p_whdr2, sizeof(WAVEHDR));
return TRUE;
case MM_WOM_DONE: // WAVEデバイス出力分の再生終了
if(shut_off_flag || index>=data_buf_len) {
waveOutClose(hwave); // MM_WOM_CLOSEメッセージ送信
return TRUE;
}
set_buf((PWAVEHDR)lparam, data_buf, &index, data_buf_len);
waveOutWrite(hwave,(PWAVEHDR)lparam, sizeof(WAVEHDR)); // 再生後、MM_WOM_DONEメッセージ送信
return TRUE;
case MM_WOM_CLOSE: // WAVEデバイス閉じる
// p_whdr1、p_whdr2 のスワップアウト禁止状態を解除
waveOutUnprepareHeader(hwave, p_whdr1, sizeof(WAVEHDR));
waveOutUnprepareHeader(hwave, p_whdr2, sizeof(WAVEHDR));
free(p_whdr1);
free(p_whdr2);
free(p_buffer1);
free(p_buffer2);
hwave = NULL;
SetDlgItemText(hwnd, IDC_ONOFF, TEXT("再生"));
if(app_closing_flag) SendMessage(hwnd, WM_CLOSE, 0, 0L); // 1度閉じるボタンが押されている
break;
case WM_CLOSE:
if(hwave != NULL) { // WAVEデバイスを開いてるなら
shut_off_flag = TRUE;
app_closing_flag = TRUE;
waveOutReset(hwave); // 再生停止後、MM_WOM_DONEメッセージ送信( → MM_WOM_CLOSE → 終了)
}
else DestroyWindow(hwnd);
break;
case WM_DESTROY:
free(data_buf);
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wparam, lparam);
return FALSE;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow) {
HWND hwnd;
MSG msg;
WNDCLASS winc;
winc.style = CS_HREDRAW | CS_VREDRAW;
winc.lpfnWndProc = DlgProc;
winc.cbClsExtra = winc.cbWndExtra = 0;
winc.hInstance = hInstance;
winc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
winc.hCursor = LoadCursor(NULL, IDC_ARROW);
winc.hbrBackground = (HBRUSH)GetSysColorBrush(COLOR_BTNFACE );
winc.lpszMenuName = NULL;
winc.lpszClassName = TEXT("play_wave_winc");
if (!RegisterClass(&winc)) return 1;
hwnd = CreateWindow(
TEXT("play_wave_winc"), app_name, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 350, 100, NULL, NULL, hInstance, NULL
);
if (hwnd == NULL) return 1;
while (GetMessage(&msg, NULL, 0, 0 )) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
|
WAVEファイルフォーマットについては以下のサイトを参考にさせていただきました。
参考: WAVEファイルフォーマット (近藤正芳のウェブページ)
上記ソースは全て自由に改編して使ってかまいませんが必ず自己責任でね。
| 2004.3.30 G丸 |