003-02  前へ←  ホームへ  →次へ  003-04

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丸

 003-02  前へ←  ホームへ  →次へ  003-04