WAVEデータをファイルに保存する

概要:PCM形式のWAVEデータをファイルに保存する関数を作成します

保存は読み込みと違い、WAVEファイルの構造を理解していれば非常に簡単です。
保存しようとしているWAVEデータは正常であるという仮定が成り立つのでチェックの必要も無く、
基本的にはガンガン書き込んでいくだけです。

■WAVEファイルの構造(PCM形式)

factチャンクは必要ないので、factチャンク無しで保存します。

アドレス データ
0 "RIFF"
+4 (ファイルのバイト数)-8
+8 "WAVE"
+12 "fmt "
+16 フォーマット情報(PCMWAVEFORMAT構造体)のバイト数
+20 フォーマット情報(PCMWAVEFORMAT構造体)
+36 "data"
+40 波形データのバイト数
+44 〜 波形データ

■WAVEデータをファイルに保存する関数

PCM形式のWAVEデータをファイルに保存する関数を作ります。

関数の名前:SaveWaveFile
char *lpFileName:ファイルの名前を指すポインタ
WAVEFORMATEX *lpwfe:データが格納されているWAVEFORMATEX構造体を指すポインタ
WAVEHDR *lpwhdr:データが格納されているWAVEHDR構造体を指すポインタ
戻り値:0(成功) or 負値(失敗)

int SaveWaveFile(char *lpFileName,WAVEFORMATEX *lpwfe,WAVEHDR *lpwhdr)
{
    ……
}

■ファイルオープン

新しいファイルを書き込み属性で作成します。
上書きはしません。

    HANDLE fh=CreateFile(lpFileName,GENERIC_WRITE,0,NULL,
        CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
    if(fh==INVALID_HANDLE_VALUE){
        MessageBox(NULL,"同名のファイルが既に存在します",lpFileName,MB_OK);
        return -1;
    }

■WAVEデータの書き込み

ファイルの先頭から4〜7バイトに書き込む (ファイルのバイト数)-8 ですが、
ファイルのバイト数は 波形データのバイト数+44 で計算できます。
44 は波形データ以外にファイルに書き込むバイト数です(ファイルの構造参照)。

フォーマット情報はPCMWAVEFORMAT構造体で保存しなければいけませんが、
WAVEFORMATEX構造体の最後のメンバを書き込まないように
PCMWAVEFORMAT構造体のサイズである 16 バイトだけ書き込めば同じ事です。

波形データのバイト数は WAVEFORMATEX構造体のdwBufferLengthメンバ が、
波形データの先頭を指すポインタは WAVEFORMATEX構造体のlpDataメンバ が保持しています。

    DWORD dwWriteSize;
    WriteFile(fh,"RIFF",4,&dwWriteSize,NULL);
    DWORD size=(lpwhdr->dwBufferLength + 44)-8;
    WriteFile(fh,&size,4,&dwWriteSize,NULL);
    WriteFile(fh,"WAVE",4,&dwWriteSize,NULL);
    WriteFile(fh,"fmt ",4,&dwWriteSize,NULL);
    size=16;
    WriteFile(fh,&size,4,&dwWriteSize,NULL);
    WriteFile(fh,lpwfe,size,&dwWriteSize,NULL);
    WriteFile(fh,"data",4,&dwWriteSize,NULL);
    WriteFile(fh,&lpwhdr->dwBufferLength,4,&dwWriteSize,NULL);
    WriteFile(fh,lpwhdr->lpData,lpwhdr->dwBufferLength,&dwWriteSize,NULL);

■ファイルクローズ

書き込みが終了したのでファイルをクローズして関数を終了します。

    CloseHandle(fh);
    return 0;

関数の処理はこれだけです。
非常に簡潔だったでしょ?

■WAVEデータを作成して保存する

SaveWaveFile関数を使ってWAVEデータをファイルに保存してみましょう。
WAVEデータは作成するだけで再生したりしないので、waveOut○○関数の実行は必要ありません。

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

// winmm.lib をリンクする
#pragma comment(lib,"winmm")

#define SRATE    8000    //標本化周波数(1秒間のサンプル数)
#define F        400     //周波数(1秒間の波形数)

LRESULT CALLBACK WindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
    WAVEFORMATEX wfe;
    static WAVEHDR whdr;
    static LPBYTE lpWave;
    int i,len;

    switch(uMsg){
        case WM_CREATE:
            wfe.wFormatTag=WAVE_FORMAT_PCM;
            wfe.nChannels=1;    //モノラル
            wfe.wBitsPerSample=8;    //量子化ビット数
            wfe.nBlockAlign=wfe.nChannels * wfe.wBitsPerSample/8;
            wfe.nSamplesPerSec=SRATE;    //標本化周波数
            wfe.nAvgBytesPerSec=wfe.nSamplesPerSec * wfe.nBlockAlign;

            lpWave=(LPBYTE)calloc(wfe.nAvgBytesPerSec,2);  //2秒分

            len=SRATE/F;  //波長
            for(i=0;i<SRATE*2;i++){  //波形データ作成
                if(i%len < len/2)    lpWave[i]=128+64;
                else                 lpWave[i]=128-64;
            }

            whdr.lpData=(LPSTR)lpWave;
            whdr.dwBufferLength=wfe.nAvgBytesPerSec * 2;
            whdr.dwFlags=WHDR_BEGINLOOP | WHDR_ENDLOOP;
            whdr.dwLoops=1;

            SaveWaveFile("myWaveFile.wav",&wfe,&whdr);
            return 0;
        case WM_DESTROY:
            free(lpWave);
            PostQuitMessage(0);
            return 0;
    }
    return DefWindowProc(hWnd,uMsg,wParam,lParam);
}

■WaveFileIO.h

WaveFileIO.h はWAVEファイルを読み書きする関数を集めたヘッダーです。
ソースファイルが適切な場所に存在すれば、WaveFileIO.h をインクルードするだけで
WAVEファイルの読み込み、WAVEデータのファイル保存、の両方が行えるようになります。

// LoadWaveFile.cpp

int LoadWaveFile(char *lpFileName,WAVEFORMATEX *lpwfe,WAVEHDR *lpwhdr);
void CloseWaveFile(HWAVEOUT hWaveOut,WAVEHDR *lpwhdr);

// SaveWaveFile.cpp

int SaveWaveFile(char *lpFileName,WAVEFORMATEX *lpwfe,WAVEHDR *lpwhdr);

★☆ ダウンロード ☆★


戻る / ホーム