オーディオキャプチャ

 

1. 目的

パソコンのサウンドカードから、オーディオデータをキャプチャする。また、ストリーミングアプリケーションへの応用を念頭に、一定周期毎にキャプチャデータをバッファに書き出すようにする。

 

2. オーディオキャプチャの使用例

オーディオ圧縮、インターネットストリーミング。

 

3. オーディオキャプチャ関数

オペレーテイングシステムに依存し、専用の API 群が用意されている。

Windows
MCI Media Control Interface
DirectSound DirectX ファミリ
Linux
OSS Open Sound System
ほか  

 

4. 簡単なオーディオキャプチャプログラム

以下は、一定周期毎にサウンドカードからオーディオ信号をキャプチャし、ファイルに書き出すプログラムである。一連の手順として、

サンプリング周波数、サンプル当たりのビット数、チャネル数の指定
サウンドデバイスの初期化
サウンドキャプチャの開始
サウンドデバイスの終了処理

を基本とする。ただし、エラー処理は行っておらず、また GUI も貧弱な、あまりよくないプログラムである。

 

Windows MCI 版: waveInXXX 関数群の使用、リンクオプションに winmm.lib を加える。

main.c (GUIプログラム 参照)

#include <windows.h>

// サウンドキャプチャ関数 (sound.c)
BOOL initSound(int hertz, int bits, int channels, int interval, char *name);
BOOL startSound();
BOOL closeSound();

//
// イベント処理
//
LRESULT APIENTRY 
WndProc (HWND hWnd, UINT msg, UINT wParam, LONG lParam)
{
	// イベントに応じて処理を実行
	switch (msg) {
	    case WM_CREATE:
			/* サウンドキャプチャ開始 */
			/* サンプリング周波数、ビット数、チャネル数、キャプチャ周期、出力ファイル名、を指定 */
			/* 下の例では、8KHzサンプリング、8ビットサンプル、ステレオ、50msec毎のキャプチャ */
			initSound(8000, 8, 2, 50, "sample.pcm");
			startSound();
			return 0;

	    case WM_CLOSE:
			/* サウンドキャプチャ終了 */
			closeSound();
			DestroyWindow(hWnd);
			return 0;

		case WM_DESTROY:
			PostQuitMessage (0);
			break;
	}
	return DefWindowProc (hWnd, msg, wParam, lParam);
}

//
// 制御部 (main 関数)
//
int PASCAL
WinMain(HINSTANCE hInst, HINSTANCE hDummy, LPSTR lpStr, int nShowCmd)
{
	WNDCLASS 	wc;
	MSG			msg;
	HWND		hWnd;

	// ウィンドウクラスの定義
	wc.lpfnWndProc = WndProc;
	wc.hInstance = hInst;
	wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
	wc.hCursor = LoadCursor (NULL, IDC_ARROW);
	wc.lpszClassName = "Player";
	wc.hbrBackground = GetStockObject(WHITE_BRUSH);
	wc.style = 0;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.lpszMenuName = 0;
	RegisterClass (&wc);

    	// ウィンドウの生成
    	hWnd = CreateWindow(
       		wc.lpszClassName, 
			"Sound Capture Sample", 
			WS_OVERLAPPEDWINDOW,
			CW_USEDEFAULT, 
			CW_USEDEFAULT,
			256,
			256,
            	0, 
			0, 
			hInst, 
			0
    	);
	if (hWnd == NULL) ExitThread (1);
	ShowWindow (hWnd, SW_SHOWNOACTIVATE);
	UpdateWindow (hWnd);

	// 無限ループ (イベントが発生するたびに WndProc を呼び出す)
	while (GetMessage (&(msg), NULL, 0, 0)) {
		TranslateMessage(&msg);
		DispatchMessage (&msg);
	}

	return msg.wParam;
}

sound.c

#include <windows.h>
#include <stdio.h>

/* バッファサイズ */
#define BUFFER_NUMBER 100
#define BUFFER_SIZE   4800

/* サウンドパラメータ */
WAVEFORMATEX waveForm;
HWAVEIN hwi;
WAVEHDR wvhdr_in[BUFFER_NUMBER];
char	sound_in[BUFFER_NUMBER][BUFFER_SIZE];

/* 出力ファイル、制御フラグ */
FILE	*fp;
BOOL	bSound = FALSE;

// -------------------------------------------
//	キャプチャ・コールバック
// -------------------------------------------
void CALLBACK 
waveInProc(HWAVEIN hwi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
	LPWAVEHDR	lpwvhdr;
	char		sound_data[BUFFER_SIZE];
	int			length;

	/* フラグ制御 */
	if(bSound == FALSE) return;

	/* イベント処理 */
	switch(uMsg){
    	case WIM_DATA:
		/* キャプチャデータをバッファにコピー */
		lpwvhdr = (LPWAVEHDR) dwParam1;
		length = lpwvhdr->dwBufferLength;
		memcpy(sound_data, lpwvhdr->lpData, length);

		/* キャプチャバッファ更新 */
		waveInUnprepareHeader(hwi, lpwvhdr, sizeof(WAVEHDR));
		waveInPrepareHeader(hwi, lpwvhdr, sizeof(WAVEHDR));
		waveInAddBuffer(hwi, lpwvhdr, sizeof(WAVEHDR));

		/* サウンドデータのファイル書き出し */
		fwrite(sound_data, 1, length, fp);

		break;
	}
}

// -------------------------------------------
//	初期化
// -------------------------------------------
BOOL
initSound(int hertz, int bits, int channels, int interval, char *name) {
	int i;
	int res;

	/* ファイルオープン */
	fp = fopen(name, "wb");
	if(fp == NULL) return FALSE;

	/* バッファ初期化 */
	for(i=0; i<BUFFER_NUMBER; i++) memset(sound_in[i], 0, BUFFER_SIZE);

	/* WAVEFORMATEX 設定 (サンプリング周波数、ビット数、チャネル数) */
	waveForm.wFormatTag = WAVE_FORMAT_PCM;
	waveForm.nChannels = channels;
	waveForm.nSamplesPerSec = hertz;
	waveForm.wBitsPerSample = bits;
	waveForm.nBlockAlign = waveForm.nChannels * waveForm.wBitsPerSample / 8;
	waveForm.nAvgBytesPerSec = waveForm.nSamplesPerSec * waveForm.nBlockAlign;

	/* waveIn オープン */
	res = waveInOpen(&hwi, WAVE_MAPPER, &waveForm, (DWORD)waveInProc, 0,
                    CALLBACK_FUNCTION);
	if(res != MMSYSERR_NOERROR) return FALSE;

	/* キャプチャバッファ確保 */
	for(i=0; i<BUFFER_NUMBER; i++) {
		wvhdr_in[i].lpData = sound_in[i];
		wvhdr_in[i].dwBufferLength = waveForm.nAvgBytesPerSec * interval / 1000;
		wvhdr_in[i].dwFlags = 0;
		wvhdr_in[i].reserved = 0;

		waveInPrepareHeader(hwi, &wvhdr_in[i], sizeof(WAVEHDR));
		waveInAddBuffer(hwi, &wvhdr_in[i], sizeof(WAVEHDR));
	}

	return TRUE;
}

// -------------------------------------------
//	キャプチャ開始
// -------------------------------------------
BOOL
startSound() {
	int res;

	/* フラグ制御 */
	bSound = TRUE;

	/* waveIn 開始 */
	res = waveInStart(hwi);
	if(res != MMSYSERR_NOERROR) return FALSE;

	return TRUE;
}

// -------------------------------------------
//	終了処理
// -------------------------------------------
BOOL
closeSound() {
	int i;

	/* フラグ制御 */
	bSound = FALSE;

	/* waveIn 終了 */
	waveInStop(hwi);
	for(i=0; i<BUFFER_NUMBER; i++) {
		wvhdr_in[i].lpData = NULL;
		waveInUnprepareHeader(hwi, &wvhdr_in[i], sizeof(WAVEHDR));
	}
	waveInReset(hwi);
	waveInClose(hwi);

	/* ファイルクローズ */
	fclose(fp);

	return TRUE;
}

 

Windows DirectSound 版: 準備中

Linux OSS 版: 準備中