オーディオ再生

 

1. 目的

パソコンから、オーディオデータをサウンドカードに再生させる。ストリーミングアプリケーションへの応用を念頭に、一定周期毎にオーディオデータをサウンドカードに書き出すようにする。

 

2. オーディオ再生の使用例

オーディオ再生プレーヤ、インターネットストリーミング。

 

3. オーディオ再生関数

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

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

 

4. 簡単なオーディオ再生プログラム

以下は、一定周期毎にファイルからオーディオデータを読出し、同じく一定周期毎にサウンドカードに書き出して再生させるプログラムである。一連の手順として、

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

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

 

Windows MCI 版: waveOutXXX 関数群の使用、リンクオプションに 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

/* サウンドバッファ */
char	sound_out[BUFFER_NUMBER][BUFFER_SIZE];

/* サウンドパラメータ */
WAVEFORMATEX waveForm;
HWAVEOUT hwo;
WAVEHDR wvhdr_out[BUFFER_NUMBER];

/* 出力ファイル、制御フラグ、各種制御パラメータ */
FILE	*fp;
BOOL	bSound = FALSE;
int	nInterval;
int	nLength;
int	nWritePosition = 0;
int	nReadPosition = 0;
MMRESULT hEvRead;
MMRESULT hEvPlay;

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

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

	/* バッファ初期化 */
	for(i=0; i<BUFFER_NUMBER; i++) memset(sound_out[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;

	/* waveOut オープン */
	res = waveOutOpen(&hwo, WAVE_MAPPER, &waveForm, 0, 0, WAVE_ALLOWSYNC);
	if(res != MMSYSERR_NOERROR) return FALSE;

	/* 再生バッファ確保 */
	nInterval = interval;
	nLength = waveForm.nAvgBytesPerSec * nInterval / 1000;
	for(i=0; i<BUFFER_NUMBER; i++) {
		wvhdr_out[i].lpData = sound_out[i];
		wvhdr_out[i].dwBufferLength = waveForm.nAvgBytesPerSec * nInterval / 1000;
		wvhdr_out[i].dwBytesRecorded = 0;
		wvhdr_out[i].dwUser = 0;
		wvhdr_out[i].dwFlags = 0;
		wvhdr_out[i].dwLoops = 0;

		waveOutPrepareHeader(hwo, &wvhdr_out[i], sizeof(WAVEHDR));
	}

	return TRUE;
}

// -------------------------------------------
//	再生開始
// -------------------------------------------
// ファイルからデータ読出し
static void CALLBACK 
readEvent(UINT timerID, UINT msg, DWORD usrParam, DWORD dw1, DWORD dw2) {
	char sound_data[BUFFER_SIZE];

	/* サウンドデータの読みこみ */
	if(feof(fp)) fseek(fp, 0, SEEK_SET);
	fread(sound_data, 1, nLength, fp);
	memcpy(sound_out[nWritePosition % BUFFER_NUMBER], sound_data, nLength);
	nWritePosition++;
}

// サウンドの再生実行
static void CALLBACK 
playEvent(UINT timerID, UINT msg, DWORD usrParam, DWORD dw1, DWORD dw2) {
	int index;
	
	/* フラグ制御 */
	if(bSound == FALSE) return;

	/* サウンドデータの書きこみ */
	if(nReadPosition < nWritePosition) {
		index = nReadPosition % BUFFER_NUMBER;
		waveOutWrite(hwo, &wvhdr_out[index], sizeof(WAVEHDR));
		waveOutUnprepareHeader(hwo, &wvhdr_out[index], sizeof(WAVEHDR));
		waveOutPrepareHeader(hwo, &wvhdr_out[index], sizeof(WAVEHDR));
		nReadPosition++;
	}
}

BOOL
startSound() {
	/* フラグ制御 */
	bSound = TRUE;

	/* タイムイベント起動 */
	// readEvent
	hEvRead = timeSetEvent(nInterval, 1, (LPTIMECALLBACK) readEvent, (DWORD) NULL, TIME_PERIODIC);
	// playEvent
	hEvPlay = timeSetEvent(nInterval, 1, (LPTIMECALLBACK) playEvent, (DWORD) NULL, TIME_PERIODIC);

	return TRUE;
}

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

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

	/* waveOut 終了 */
	for(i=0; i<BUFFER_NUMBER; i++) {
		wvhdr_out[i].lpData = NULL;
		waveOutUnprepareHeader(hwo, &wvhdr_out[i], sizeof(WAVEHDR));
	}
	waveOutPause(hwo);
	waveOutReset(hwo);
	waveOutClose(hwo);

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

	return TRUE;
}
 

 

Windows DirectSound 版: 準備中

Linux OSS 版: 準備中