保存は読み込みと違い、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);
★☆ ダウンロード ☆★