Windowsで録音 再生

メモメモ。 音声を録音して再生するだけのプログラムです

MMSystem.h だけだと 動かないけど、Windows.hで、 うごく。。。不思議
Windowsマルチメディア系のAPIはここに書いてありました msdn WIN32API

参考にしたサイト一覧
WAVEデータの作成と再生
WAVE
WAVEファイルにリアルタイムで加工を施し再生する

リアルタイムに音声を録音し続けたいなら、コールバック関数を利用して、複数のバファを切り替えながら、waveInAddBuffer でおk。
録音と再生のレイテンシを小さくしたいなら、バッファの大きさを小さくすればok?
どちらにせよ、再生中のポインタと録音中のポインタの距離が近いほどレイテンシは抑えられそう。

ポインタが重なったりしたら、えらい雑音が入りそうだな@@

#include <Windows.h>
//#include <MMSystem.h>
//#pragma comment (lib, "winmm.lib")

#include <iostream>
using namespace std;

// waveInOpenが利用するコールバック関数
void CALLBACK waveInProc(
	HWAVEIN hwi,      
	UINT uMsg,        
	DWORD dwInstance, 
	DWORD dwParam1,   
	DWORD dwParam2    
	)
{
	switch(uMsg)
	{
	case WIM_OPEN:
		cout << "waveInOpen" << endl;
		break;
	case WIM_CLOSE:
		cout << "waveInClose" << endl;
		break;
	case WIM_DATA:
		cout << "バッファがいっぱいになりました" << endl;
		break;
	}
}

// waveOutOpenが利用するコールバック関数
void CALLBACK waveOutProc(
	HWAVEOUT hwo,
	UINT uMsg,        
	DWORD dwInstance, 
	DWORD dwParam1,   
	DWORD dwParam2    
	)
{
	switch(uMsg)
	{
	case WOM_OPEN:
		cout << "waveOutOpen" << endl;
		break;
	case WOM_CLOSE:
		cout << "waveOutClose" << endl;
		break;
	case WOM_DONE:
		cout << "バッファをすべて再生しました" << endl;
		break;
	}
}

int main()
{
	/*******************************************************************************
		録音
	********************************************************************************/
	HWAVEIN      hwi = NULL;				// WAVEハンドル
	LPBYTE       lpWaveData = NULL;			// バッファ本体のポインタ[ヒープ領域]
	WAVEFORMATEX wf = {0};					// デバイスオープン時に使う、設定構造体
	WAVEHDR      wh = {0};					// デバイスに与えるバッファの設定構造体

	// デバイスオープン時の設定
	wf.wFormatTag      = WAVE_FORMAT_PCM;	// PCM形式
	wf.nChannels       = 1;					// ステレオかモノラルか
	wf.nSamplesPerSec  = 22050;				// サンプリングレート 22.05KHz
	wf.wBitsPerSample  = 8;					// 量子化レベル
	wf.nBlockAlign     = wf.wBitsPerSample * wf.nChannels / 8;	// バイトあたりのビット数[PCMの仕様]
	wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;	// 1 秒あたりのバイト数
	// モノラル+サンプル22050+量子化8bitなので 一秒間に22050byteのデータが発生する
	// サンプル x nBlockAlign = 一秒間のデータ量なので nBlockAlignは1

	// デバイスオープン
	if(waveInOpen(
		&hwi,				// ハンドル
		WAVE_MAPPER,		// デバイスID。 WAVE_MAPPERデフォルトのデバイスを指定
		&wf,				// オープン時の設定
		(DWORD)waveInProc,	// コールバック関数
		0,					// しらん
		CALLBACK_FUNCTION	// コールバック関数の属性
		) != MMSYSERR_NOERROR)
		cout << "デバイスのオープンに失敗しました" << endl;

	DWORD dwRecordSecond = 10;		// 10秒
	DWORD dwDataSize;

	dwDataSize = wf.nAvgBytesPerSec * dwRecordSecond;	// 一秒間のデータ量 x 秒数
	lpWaveData = (LPBYTE)new BYTE[dwDataSize];

	// バッファ情報セット
	wh.lpData         = (LPSTR)lpWaveData;
	wh.dwBufferLength = dwDataSize;
	wh.dwFlags        = 0;

	// バッファの初期化
	waveInPrepareHeader(hwi, &wh, sizeof(WAVEHDR));

	// バッファの関連付け
	waveInAddBuffer(hwi, &wh, sizeof(WAVEHDR));

	// 録音開始
	waveInStart(hwi);

	rewind(stdin);
	getchar(); // 10秒待機すると、 "バッファがいっぱいになった" が表示されるはず

	// 録音終了
	waveInReset(hwi);
	waveInUnprepareHeader(hwi, &wh, sizeof(WAVEHDR));
	waveInClose(hwi);


	/*******************************************************************************
		再生
	********************************************************************************/
	
	HWAVEOUT hwo;	// ハンドル
	WAVEHDR whdr;		// 

	// デバイスを開く
	if(waveOutOpen(
		&hwo,			// ハンドル
		WAVE_MAPPER,		// デフォルトのデバイスを指定
		&wf,				// waveInOpenのとおなじでおk
		(DWORD)waveOutProc,	// コールバック関数
		0,					// しらん
		CALLBACK_FUNCTION	// コールバック関数の属性
		)
		!= MMSYSERR_NOERROR )
		cout << "Erroe" << endl;


	// 音声ファイルのパラメータを設定
	whdr.lpData =			(LPSTR)lpWaveData;			// 音声データの入ってるポインタ
	whdr.dwBufferLength =	dwDataSize;						// 音声データのサイズ
	whdr.dwFlags =			WHDR_BEGINLOOP | WHDR_ENDLOOP;	// 再生オプション
	whdr.dwLoops =			1;								// ループ回数

	// 初期化
	waveOutPrepareHeader(hwo,&whdr,sizeof(WAVEHDR));

	// 音声データブロックの書き込み
	waveOutWrite(hwo,&whdr,whdr.dwBufferLength);

	rewind(stdin);
	getchar();	// 再生中待機

	waveOutReset(hwo);
	waveOutUnprepareHeader(hwo, &whdr, sizeof(whdr));
	waveOutClose(hwo);
}