音声の入力
入力デバイスから録音する
低レベル API を用いることで、入力デバイスから、直接バッファに音声を入力できます
開発者から見れば、「録音」というよりも、「入力」という言葉が適切でしょう
音声の入力も、出力とやり方は基本的に同じです
入力デバイスは、割り当てられたバッファ分だけ音声を記録し
バッファが一杯になれば、メッセージを使ってアプリケーションにバッファを返します
音声入力デバイスの代表は、マイク入力でしょう
この他でも、音声入力デバイスであれば、この場は何でもかまいません
音声出力同様に、最初は waveInOpen() 関数を使います
この関数は、ウェーブフォームオーディオ入力デバイスのハンドルを取得します
MMRESULT waveInOpen(
LPHWAVEIN phwi , UINT uDeviceID ,
LPWAVEFORMATEX pwfx ,
DWORD dwCallback , DWORD dwCallbackInstance ,
DWORD fdwOpen
);
phwi は HWAVEIN 型の、デバイス識別用ハンドルへのポインタを指定します
これは、ウェーブフォームオーディオ入力デバイスのハンドルです
以後、音声入力関数に対しては、このハンドルを用いて出力デバイスを指定します
uDeviceID には、対象の入力デバイスの識別子を指定します
WAVE_MAPPER を指定すれば、ユーザーが選択している優先デバイスが適応されます
pwfx には、WAVEFORMATEX 構造体へのポインタを指定します
dwCallback は、関連メッセージを受け取る対象を指定します
dwCallbackInstance には、コールバック関数に渡すデータを指定できます
fdwOpen は、デバイスを開くためのフラグを指定します
この引数には、以下の値のいずれか、または組み合わせを指定できます
定数 | 解説
|
---|
CALLBACK_EVENT | dwCallback はイベントハンドルである
|
CALLBACK_FUNCTION | dwCallback はコールバック関数のポインタである
|
CALLBACK_NULL | コールバックは使用しない
|
CALLBACK_THREAD | dwCallback はスレッドハンドルである
|
CALLBACK_WINDOW | dwCallback はウィンドウハンドルである
|
WAVE_FORMAT_DIRECT | このフラグが指定されていれば ACM ドライバは音声データを転送しない
|
WAVE_FORMAT_QUERY | このフラグが指定されていれば 関数はデバイスに、指定フォーマットをサポートしているか求める ただし、実際にデバイスはオープンされない
|
WAVE_MAPPED | このフラグが指定されていれば uDeviceID は、ウェーブマっパによってマップされる ウェーブフォームオーディオデバイスを指定する
|
関数が成功すれば MMSYSERR_NOERROR が、そうでなければエラー値が返ります
エラーが発生した場合、次のいずれかの値がかえります
定数 | 解説
|
---|
MMSYSERR_ALLOCATED | リソースは、すでに割り当てられている
|
MMSYSERR_BADDEVICEID | デバイス識別子が無効である
|
MMSYSERR_NODRIVER | デバイスドライバがない
|
MMSYSERR_NOMEM | メモリを割り当てられない、またはロックできない
|
WAVERR_BADFORMAT | デバイスドライバは、指定されたフォーマットをサポートしていない
|
この関数の引数は、waveOutOpen() 関数とまったく同じです
単純に、対象が入力デバイスか出力デバイスかの違いしかありません
次に、入力バッファを準備する必要があります
これには waveInPrepareHeader() 関数を使用します
MMRESULT waveInPrepareHeader(
HWAVEIN hwi , LPWAVEHDR pwh , UINT cbwh
);
hwi にはウェーブフォームオーディオ入力デバイスのハンドルを指定します
pwh は、WAVEHDR 構造体へのポインタです
cbwh には、WAVEHDR 構造体のサイズを指定します
関数が成功すれば MMSYSERR_NOERROR が、そうでなければエラー値が返ります
この関数が返すエラー値は、以下のいずれかの定数になります
定数 | 解説
|
---|
MMSYSERR_HANDLEBUSY | 別のスレッドがハンドルを使用中である
|
MMSYSERR_INVALHANDLE | デバイスハンドルが無効である
|
MMSYSERR_NODRIVER | デバイスドライバがない
|
MMSYSERR_NOMEM | メモリを割り当てられない、またはロックできない
|
次に、アプリケーションは入力デバイスに対して準備済みバッファを提供します
バッファを設定するには waveInAddBuffer() 関数を用います
この関数は、デバイスが管理するキューにバッファを追加します
MMRESULT waveInAddBuffer(
HWAVEIN hwi , LPWAVEHDR pwh , UINT cbwh
);
hwi にはウェーブフォームオーディオ入力デバイスのハンドルを指定します
pwh は、準備済みバッファを表す WAVEHDR 構造体へのポインタを指定します
cbwh には、WAVEHDR 構造体のサイズを指定します
関数が成功すれば MMSYSERR_NOERROR が、そうでなければエラー値が返ります
この関数が返すエラー値は、以下のいずれかの定数になります
定数 | 解説
|
---|
MMSYSERR_HANDLEBUSY | 別のスレッドがハンドルを使用中である
|
MMSYSERR_INVALHANDLE | デバイスハンドルが無効である
|
MMSYSERR_NODRIVER | デバイスドライバがない
|
MMSYSERR_NOMEM | メモリを割り当てられない、またはロックできない
|
WAVERR_UNPREPARED | バッファが準備されていない
|
入力デバイスは、キューに指定されたバッファを割り当てます
音声の入力は、このキューのバッファに対し FIFO 順で行われます
これらの関数を使って、デバイスとバッファの準備が整えば
あとは、waveInStart() 関数でバッファへの保存を開始します
MMRESULT waveInStart(HWAVEIN hwi);
hwi には、 ウェーブフォームオーディオ入力デバイスのハンドルを指定します
関数が成功すれば MMSYSERR_NOERROR が、そうでなければエラー値が返ります
この関数が返すエラー値は、以下のいずれかの定数になります
定数 | 解説
|
---|
MMSYSERR_HANDLEBUSY | 別のスレッドがハンドルを使用中である
|
MMSYSERR_INVALHANDLE | デバイスハンドルが無効である
|
MMSYSERR_NODRIVER | デバイスドライバがない
|
MMSYSERR_NOMEM | メモリを割り当てられない、またはロックできない
|
waveInStart() 関数が呼び出されると、キューのバッファに対して音声が入力され
バッファが満杯になれば、メッセージでアプリケーションに返されます
そして、次のバッファがデキューされ、音声が入力されるという仕組みになっています
バッファへの入力を中断する場合は waveInStop() 関数を使います
これを呼び出せば、デバイスは空のバッファをキューを残して入力を停止します
MMRESULT waveInStop(HWAVEIN hwi);
hwi には、 ウェーブフォームオーディオ入力デバイスのハンドルを指定します
関数が成功すれば MMSYSERR_NOERROR が、そうでなければエラー値が返ります
この関数が返すエラー値は、以下のいずれかの定数になります
定数 | 解説
|
---|
MMSYSERR_HANDLEBUSY | 別のスレッドがハンドルを使用中である
|
MMSYSERR_INVALHANDLE | デバイスハンドルが無効である
|
MMSYSERR_NODRIVER | デバイスドライバがない
|
MMSYSERR_NOMEM | メモリを割り当てられない、またはロックできない
|
ウェーブフォームオーディオ入力デバイスが開けば MM_WIM_OPEN が、
閉じられれば MM_WIM_CLOSE メッセージがコールバック先に送信されます
WPARAM には入力デバイスのハンドルが、LPARAM には 0 が格納されています
入力に対してバッファが一杯になるか、入力が停止されれば
MM_WIM_DATA メッセージが送信されます
WPARAM には保存を行った入力デバイスのハンドルが
LPARAM には、返されたバッファを表す WAVEHDR 構造体へのポインタが格納されています
それぞれのメッセージで、戻り値は定義されていません
MM_WIM_DATA を処理すれば、満杯になったバッファごとの処理を行えます
複数のバッファに分けて入力を行う時に、重要なメッセージになるでしょう
入力処理が終了すれば、waveInUnprepareHeader() 関数を用い
入力用に準備済みのバッファを解放できるように、準備を終了させます
MMRESULT waveInUnprepareHeader(
HWAVEIN hwi , LPWAVEHDR pwh , UINT cbwh
);
hwi にはウェーブフォームオーディオ入力デバイスのハンドルを指定します
pwh は、WAVEHDR 構造体へのポインタです
cbwh には、WAVEHDR 構造体のサイズを指定します
関数が成功すれば MMSYSERR_NOERROR が、そうでなければエラー値が返ります
この関数が返すエラー値は、以下のいずれかの定数になります
定数 | 解説
|
---|
MMSYSERR_HANDLEBUSY | 別のスレッドがハンドルを使用中である
|
MMSYSERR_INVALHANDLE | デバイスハンドルが無効である
|
MMSYSERR_NODRIVER | デバイスドライバがない
|
MMSYSERR_NOMEM | メモリを割り当てられない、またはロックできない
|
WAVERR_STILLPLAYING | キュー内にバッファがまだ残っている
|
ウェーブフォームオーディオ入力デバイスのハンドルが不用になれば
waveInClose() 関数でデバイスを解放しなければなりません
MMRESULT waveInClose(HWAVEIN hwi);
hwi にはウェーブフォームオーディオ入力デバイスのハンドルを指定します
関数が成功すれば MMSYSERR_NOERROR が、そうでなければエラー値が返ります
この関数が返すエラー値は、以下のいずれかの定数になります
定数 | 解説
|
---|
MMSYSERR_INVALHANDLE | デバイスハンドルが無効である
|
MMSYSERR_NODRIVER | デバイスドライバがない
|
MMSYSERR_NOMEM | メモリを割り当てられない、またはロックできない
|
WAVERR_STILLPLAYING | キュー内にバッファがまだ残っている
|
これらの関数を使えば、入力デバイスから音声を録音するレコーダーを作れます
それも、入力された音声を最大で 1/44100 秒という単位で制御できるのですから
このような細かい音声の制御は、低レベル API のすばらしいメリットです
//resource.h
#define IDB_REC 0x100
#define IDB_PLAY 0x101
//リソーススクリプト
#include "resource.h"
KITTY DIALOG 10 , 10 , 100 , 20
FONT 12 , "MS Serif"
STYLE WS_VISIBLE | WS_POPUPWINDOW
CLASS "KITTY"
CAPTION "Kitty on your lap" {
PUSHBUTTON "Recourd" , IDB_REC , 5 , 5 , 40 , 10
PUSHBUTTON "Play" , IDB_PLAY, 50 , 5 , 40 , 10
}
#include <windows.h>
#include "resource.h"
#define TITLE TEXT("Kitty on your lap")
#define SRATE 11025
LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
static WAVEFORMATEX wfe;
static HWAVEOUT hWaveOut;
static HWAVEIN hWaveIn;
static BYTE *bWave;
static WAVEHDR whdr;
switch (msg) {
case WM_DESTROY:
free(bWave);
PostQuitMessage(0);
return 0;
case WM_CREATE:
bWave = malloc(SRATE * 5);
wfe.wFormatTag = WAVE_FORMAT_PCM;
wfe.nChannels = 1;
wfe.nSamplesPerSec = SRATE;
wfe.nAvgBytesPerSec = SRATE;
wfe.wBitsPerSample = 8;
wfe.nBlockAlign = wfe.nChannels * wfe.wBitsPerSample / 8;
whdr.lpData = bWave;
whdr.dwBufferLength = SRATE * 5;
whdr.dwLoops = 1;
return 0;
case WM_COMMAND:
switch(LOWORD(wp)) {
case IDB_REC:
waveInOpen(&hWaveIn , WAVE_MAPPER , &wfe ,
(DWORD)hWnd , 0 , CALLBACK_WINDOW);
waveInPrepareHeader(hWaveIn , &whdr , sizeof(WAVEHDR));
break;
case IDB_PLAY:
waveOutOpen(&hWaveOut , WAVE_MAPPER , &wfe ,
(DWORD)hWnd , 0 , CALLBACK_WINDOW);
waveOutPrepareHeader(hWaveOut , &whdr , sizeof(WAVEHDR));
break;
}
return 0;
case MM_WIM_OPEN:
EnableWindow(GetDlgItem(hWnd , IDB_PLAY) , FALSE);
EnableWindow(GetDlgItem(hWnd , IDB_REC) , FALSE);
waveInAddBuffer(hWaveIn , &whdr , sizeof(WAVEHDR));
waveInStart(hWaveIn);
return 0;
case MM_WIM_DATA:
waveInStop(hWaveIn);
waveInClose(hWaveIn);
EnableWindow(GetDlgItem(hWnd , IDB_PLAY) , TRUE);
EnableWindow(GetDlgItem(hWnd , IDB_REC) , TRUE);
return 0;
case MM_WIM_CLOSE:
waveInUnprepareHeader(hWaveIn , &whdr , sizeof(WAVEHDR));
return 0;
case MM_WOM_OPEN:
EnableWindow(GetDlgItem(hWnd , IDB_PLAY) , FALSE);
EnableWindow(GetDlgItem(hWnd , IDB_REC) , FALSE);
waveOutWrite(hWaveOut , &whdr , sizeof(WAVEHDR));
return 0;
case MM_WOM_DONE:
waveOutClose(hWaveOut);
EnableWindow(GetDlgItem(hWnd , IDB_PLAY) , TRUE);
EnableWindow(GetDlgItem(hWnd , IDB_REC) , TRUE);
return 0;
case MM_WOM_CLOSE:
waveOutUnprepareHeader(hWaveOut , &whdr , sizeof(WAVEHDR));
return 0;
}
return DefWindowProc(hWnd , msg , wp , lp);
}
int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
PSTR lpCmdLine , int nCmdShow ) {
HWND hwnd;
MSG msg;
WNDCLASS winc;
winc.style = CS_HREDRAW | CS_VREDRAW;
winc.lpfnWndProc = WndProc;
winc.cbClsExtra = 0;
winc.cbWndExtra = DLGWINDOWEXTRA;
winc.hInstance = hInstance;
winc.hIcon = LoadIcon(NULL , IDI_APPLICATION);
winc.hCursor = LoadCursor(NULL , IDC_ARROW);
winc.hbrBackground = (HBRUSH)CreateSolidBrush(GetSysColor(COLOR_MENU));
winc.lpszMenuName = NULL;
winc.lpszClassName = TEXT("KITTY");
if (!RegisterClass(&winc)) return -1;
hwnd = CreateDialog(hInstance , TEXT("KITTY") , 0 , NULL);
if (hwnd == NULL) return -1;
while(GetMessage(&msg , NULL , 0 , 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
このプログラムを実行すると、上のようなダイアログウィンドウが出現します
Record ボタンを押せば録音、Play ボタンを押せば再生が始まります
バッファは、サンプリングレート × 5 というサイズに設定してあります
これは、すなわちモノラル音声で 5 秒間分のバッファサイズということを意味しています
Record ボタンを押せば、入力デバイスから5秒間だけ、バッファに音が録音されます
バッファが一杯になれば、デバイスは MM_WIM_DATA を送信してくるので
これを処理して録音を終了し、バッファの準備を解いて再生などに備えます
ただし、このプログラムは簡単に表現するために、一切のエラー処理を省略しています
再生、録音中にアプリケーションを終了させたりしないで下さい
デバイス、バッファなどの解除過程を無視して強制的に終了させることになります
上のプログラムの場合、録音時間が常に一定で限られていますが
実践アプリケーションの場合は、もちろんこのような尾粗末なプログラムではいけません
ユーザーが、コンピュータの容量が許す限り、好きな時間だけ録音できるべきでしょう
動的に録音時間を変化させる場合、MM_WIM_DATA メッセージを処理し
定期的に入力デバイスが返したバッファを、他のバッファにコピーすることで実現します
コピー先のバッファを動的に確保すれば、自由な時間だけ録音することができます
保存バッファにデータをコピーし、再び入力用のバッファをデバイスに渡します
ただし、MM_WIM_DATA を処理する時間は、バッファに録音が行えないため
単一のバッファでこれを行うと正しく録音できません
そのため、キューに2つ以上のバッファをインキューし、交互に音声を入力させ
デキューされたバッファを、保存用バッファにコピーするという方式を取ります
コピーが終了すれば、再びバッファをインキューすることで使いまわすことができます
録音を明示的に終了させるには waveInReset() 関数を使います
この関数が呼び出されると、MM_WIM_DATA メッセージが発行されます
これと、静的な判定用変数を用いれば、イベントによって録音を中断できます
MMRESULT waveInReset(HWAVEIN hwi);
hwi には、 ウェーブフォームオーディオ入力デバイスのハンドルを指定します
関数が成功すれば MMSYSERR_NOERROR が、そうでなければエラー値が返ります
この関数が返すエラー値は、以下のいずれかの定数になります
定数 | 解説
|
---|
MMSYSERR_HANDLEBUSY | 別のスレッドがハンドルを使用中である
|
MMSYSERR_INVALHANDLE | デバイスハンドルが無効である
|
MMSYSERR_NODRIVER | デバイスドライバがない
|
MMSYSERR_NOMEM | メモリを割り当てられない、またはロックできない
|
ただし、PCM は非圧縮形式のため、短い時間で大量の情報量を消費します
長時間の録音に対応するには、マシンに負荷をかけないように心がけ
定期的にメモリバッファをディスクに退避させるなどの工夫が必要になります
//resource.h
#define IDB_REC 0x100
#define IDB_PLAY 0x101
#define IDB_REC_END 0x102
#define IDB_PLY_END 0x103
//リソーススクリプト
#include "resource.h"
KITTY DIALOG 10 , 10 , 100 , 40
FONT 12 , "MS Serif"
STYLE WS_VISIBLE | WS_POPUPWINDOW
CLASS "KITTY"
CAPTION "Kitty on your lap" {
PUSHBUTTON "Record" , IDB_REC , 5 , 5 , 40 , 10
PUSHBUTTON "End" , IDB_REC_END , 50 , 5 , 40 , 10 , WS_DISABLED
PUSHBUTTON "Play" , IDB_PLAY, 5 , 20 , 40 , 10
PUSHBUTTON "End" , IDB_PLY_END , 50 , 20 , 40 , 10 , WS_DISABLED
}
#include <windows.h>
#include "resource.h"
#define TITLE TEXT("Kitty on your lap")
#define SRATE 11025
LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
static WAVEFORMATEX wfe;
static HWAVEOUT hWaveOut;
static HWAVEIN hWaveIn;
static BYTE *bWave1 , *bWave2 , *bSave , *bTmp;
static WAVEHDR whdr1 , whdr2;
static dwLength = 0 , dwCount;
static BOOL blReset = FALSE;
switch (msg) {
case WM_DESTROY:
if (bSave) free(bSave);
PostQuitMessage(0);
return 0;
case WM_CREATE:
wfe.wFormatTag = WAVE_FORMAT_PCM;
wfe.nChannels = 1;
wfe.nSamplesPerSec = SRATE;
wfe.nAvgBytesPerSec = SRATE;
wfe.wBitsPerSample = 8;
wfe.nBlockAlign = 1;
wfe.cbSize = 0;
return 0;
case WM_COMMAND:
switch(LOWORD(wp)) {
case IDB_REC:
bWave1 = malloc(SRATE);
bWave2 = malloc(SRATE);
whdr1.lpData = bWave1;
whdr1.dwBufferLength = SRATE;
whdr1.dwBytesRecorded = 0;
whdr1.dwFlags = 0;
whdr1.dwLoops = 1;
whdr1.lpNext = NULL;
whdr1.dwUser = 0;
whdr1.reserved = 0;
whdr2.lpData = bWave2;
whdr2.dwBufferLength = SRATE;
whdr2.dwBytesRecorded = 0;
whdr2.dwFlags = 0;
whdr2.dwLoops = 1;
whdr2.lpNext = NULL;
whdr2.dwUser = 0;
whdr2.reserved = 0;
waveInOpen(&hWaveIn , WAVE_MAPPER , &wfe ,
(DWORD)hWnd , 0 , CALLBACK_WINDOW);
waveInPrepareHeader(hWaveIn , &whdr1 , sizeof(WAVEHDR));
waveInPrepareHeader(hWaveIn , &whdr2 , sizeof(WAVEHDR));
break;
case IDB_PLAY:
whdr1.lpData = bSave;
whdr1.dwBufferLength = dwLength;
whdr1.dwBytesRecorded = 0;
whdr1.dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP;
whdr1.dwLoops = 1;
whdr1.lpNext = NULL;
whdr1.dwUser = 0;
whdr1.reserved = 0;
waveOutOpen(&hWaveOut , WAVE_MAPPER , &wfe ,
(DWORD)hWnd , 0 , CALLBACK_WINDOW);
waveOutPrepareHeader(
hWaveOut , &whdr1 , sizeof(WAVEHDR));
waveOutWrite(hWaveOut , &whdr1 , sizeof(WAVEHDR));
break;
case IDB_REC_END:
blReset = TRUE;
waveInReset(hWaveIn);
break;
case IDB_PLY_END:
waveOutReset(hWaveOut);
break;
}
return 0;
case MM_WIM_OPEN:
dwLength = 0;
bSave = realloc(bSave , 1);
EnableWindow(GetDlgItem(hWnd , IDB_PLAY) , FALSE);
EnableWindow(GetDlgItem(hWnd , IDB_REC) , FALSE);
EnableWindow(GetDlgItem(hWnd , IDB_REC_END) , TRUE);
waveInAddBuffer(hWaveIn , &whdr1 , sizeof(WAVEHDR));
waveInAddBuffer(hWaveIn , &whdr2 , sizeof(WAVEHDR));
waveInStart(hWaveIn);
return 0;
case MM_WIM_DATA:
bTmp = realloc(bSave ,
dwLength + ((PWAVEHDR)lp)->dwBytesRecorded);
if (blReset || !bTmp) {
waveInClose(hWaveIn);
blReset = FALSE;
return 0;
}
bSave = bTmp;
for (dwCount = 0 ; dwCount < ((PWAVEHDR)lp)->dwBytesRecorded ; dwCount++)
*(bSave + dwLength + dwCount) = *(((PWAVEHDR)lp)->lpData + dwCount);
dwLength += ((PWAVEHDR)lp)->dwBytesRecorded;
waveInAddBuffer(hWaveIn , (PWAVEHDR)lp , sizeof(WAVEHDR));
return 0;
case MM_WIM_CLOSE:
waveInUnprepareHeader(hWaveIn , &whdr1 , sizeof(WAVEHDR));
waveInUnprepareHeader(hWaveIn , &whdr2 , sizeof(WAVEHDR));
free(bWave1);
free(bWave2);
EnableWindow(GetDlgItem(hWnd , IDB_PLAY) , TRUE);
EnableWindow(GetDlgItem(hWnd , IDB_REC) , TRUE);
EnableWindow(GetDlgItem(hWnd , IDB_REC_END) , FALSE);
return 0;
case MM_WOM_OPEN:
EnableWindow(GetDlgItem(hWnd , IDB_PLAY) , FALSE);
EnableWindow(GetDlgItem(hWnd , IDB_REC) , FALSE);
EnableWindow(GetDlgItem(hWnd , IDB_PLY_END) , TRUE);
return 0;
case MM_WOM_DONE:
waveOutClose(hWaveOut);
return 0;
case MM_WOM_CLOSE:
waveOutUnprepareHeader(hWaveOut , &whdr1 , sizeof(WAVEHDR));
EnableWindow(GetDlgItem(hWnd , IDB_PLAY) , TRUE);
EnableWindow(GetDlgItem(hWnd , IDB_REC) , TRUE);
EnableWindow(GetDlgItem(hWnd , IDB_PLY_END) , FALSE);
return 0;
}
return DefWindowProc(hWnd , msg , wp , lp);
}
int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
PSTR lpCmdLine , int nCmdShow ) {
HWND hwnd;
MSG msg;
WNDCLASS winc;
winc.style = CS_HREDRAW | CS_VREDRAW;
winc.lpfnWndProc = WndProc;
winc.cbClsExtra = 0;
winc.cbWndExtra = DLGWINDOWEXTRA;
winc.hInstance = hInstance;
winc.hIcon = LoadIcon(NULL , IDI_APPLICATION);
winc.hCursor = LoadCursor(NULL , IDC_ARROW);
winc.hbrBackground = CreateSolidBrush(GetSysColor(COLOR_MENU));
winc.lpszMenuName = NULL;
winc.lpszClassName = TEXT("KITTY");
if (!RegisterClass(&winc)) return -1;
hwnd = CreateDialog(hInstance , TEXT("KITTY") , 0 , NULL);
if (hwnd == NULL) return -1;
while(GetMessage(&msg , NULL , 0 , 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
プログラムを実行し、Record ボタンを押せば録音が開始されます
録音中は Record と Play ボタンが無効となり、横の End が有効になります
End ボタンを押せば、録音がその時点で終了します
録音した音声は、Play ボタンを押すことで再生することができます
Play ボタンの横の End ボタンを押せば、再生を中断できます
エラーチェックをしていないため、録音していない状態で Play ボタンを押さないで下さい
本来ならば、入出力デバイスのオープンのエラーチャックなども行うべきです
MM_WIM_DATA が送られてくると、キューのカレントバッファが満杯になったということなので
入力デバイスは待機中のもうひとつのバッファに切り替えて録音を続きます
その間に、MM_WIM_DATA 内で保存用バッファを拡張し
拡張した部分に録音したバッファをコピーし、再びキューに入力用バッファを入れます
こうすることによって、最小のメモリ領域で長時間の録音にも対応することができます
waveInOpen()
MMRESULT waveInOpen(
LPHWAVEIN phwi , UINT uDeviceID ,
LPWAVEFORMATEX pwfx ,
DWORD dwCallback , DWORD dwCallbackInstance ,
DWORD fdwOpen
);
ウェーブフォームオーディオ入力デバイスを開きます
phwi - HWAVEIN 型の、デバイス識別用ハンドルへのポインタを指定します
uDeviceID - 対象の入力デバイスの識別子を指定します
pwfx - WAVEFORMATEX 構造体へのポインタを指定します
dwCallback - 関連メッセージを受け取る対象を指定します
dwCallbackInstance - コールバック関数に渡すデータを指定できます
fdwOpen - デバイスを開くためのフラグを指定します
戻り値 - 成功すれば MMSYSERR_NOERROR、そうでなければエラー値
fdwOpen には以下のいずれかの値、または組み合わせを指定します
WAVE_ に組みあわせることができるのは 1 つの CALLBACK_ だけです
定数 | 解説
|
---|
CALLBACK_EVENT | dwCallback はイベントハンドルである
|
CALLBACK_FUNCTION | dwCallback はコールバック関数のポインタである
|
CALLBACK_NULL | コールバックは使用しない
|
CALLBACK_THREAD | dwCallback はスレッドハンドルである
|
CALLBACK_WINDOW | dwCallback はウィンドウハンドルである
|
WAVE_ALLOWSYNC | このフラグが指定されていれば 同期したウェーブオーディオを開くことができる 指定されていなければ、失敗する
|
WAVE_FORMAT_DIRECT | このフラグが指定されていれば ACM ドライバは音声データを転送しない
|
WAVE_FORMAT_QUERY | このフラグが指定されていれば 関数はデバイスに、指定フォーマットをサポートしているか求める ただし、実際にデバイスはオープンされない
|
WAVE_MAPPED | このフラグが指定されていれば uDeviceID は、ウェーブマっパによってマップされる ウェーブフォームオーディオデバイスを指定する
|
この関数が返すエラー値は、以下の定数のいずれかになります
定数 | 解説
|
---|
MMSYSERR_ALLOCATED | リソースは、すでに割り当てられている
|
MMSYSERR_BADDEVICEID | デバイス識別子が無効である
|
MMSYSERR_NODRIVER | デバイスドライバがない
|
MMSYSERR_NOMEM | メモリを割り当てられない、またはロックできない
|
WAVERR_BADFORMAT | デバイスドライバは、指定されたフォーマットをサポートしていない
|
WAVERR_SYNC | 同期デバイスが指定されたが WAVE_ALLOWSYNC フラグが指定されていない
|
waveInPrepareHeader()
MMRESULT waveInPrepareHeader(
HWAVEIN hwi , LPWAVEHDR pwh , UINT cbwh
);
入力バッファブロックを準備します
hwi - ウェーブフォームオーディオ入力デバイスのハンドルを指定します
pwh - WAVEHDR 構造体へのポインタです
cbwh - WAVEHDR 構造体のサイズを指定します
戻り値 - 成功すれば MMSYSERR_NOERROR、そうでなければエラー値
この関数が返すエラー値は、以下の定数のいずれかになります
定数 | 解説
|
---|
MMSYSERR_HANDLEBUSY | 別のスレッドがハンドルを使用中である
|
MMSYSERR_INVALHANDLE | デバイスハンドルが無効である
|
MMSYSERR_NODRIVER | デバイスドライバがない
|
MMSYSERR_NOMEM | メモリを割り当てられない、またはロックできない
|
waveInAddBuffer()
MMRESULT waveInAddBuffer(
HWAVEIN hwi , LPWAVEHDR pwh , UINT cbwh
);
入力デバイスのバッファキューに準備済みバッファブロックを追加します
hwi - ウェーブフォームオーディオ入力デバイスのハンドルを指定します
pwh - WAVEHDR 構造体へのポインタです
cbwh - WAVEHDR 構造体のサイズを指定します
戻り値 - 成功すれば MMSYSERR_NOERROR、そうでなければエラー値
この関数が返すエラー値は、以下の定数のいずれかになります
定数 | 解説
|
---|
MMSYSERR_HANDLEBUSY | 別のスレッドがハンドルを使用中である
|
MMSYSERR_INVALHANDLE | デバイスハンドルが無効である
|
MMSYSERR_NODRIVER | デバイスドライバがない
|
MMSYSERR_NOMEM | メモリを割り当てられない、またはロックできない
|
WAVERR_UNPREPARED | バッファが準備されていない
|
waveInStart()
MMRESULT waveInStart(HWAVEIN hwi);
デバイスのキューのバッファに音声の入力を開始します
hwi - ウェーブフォームオーディオ入力デバイスのハンドルを指定します
戻り値 - 成功すれば MMSYSERR_NOERROR、そうでなければエラー値
この関数が返すエラー値は、以下の定数のいずれかになります
定数 | 解説
|
---|
MMSYSERR_HANDLEBUSY | 別のスレッドがハンドルを使用中である
|
MMSYSERR_INVALHANDLE | デバイスハンドルが無効である
|
MMSYSERR_NODRIVER | デバイスドライバがない
|
MMSYSERR_NOMEM | メモリを割り当てられない、またはロックできない
|
waveInStop()
MMRESULT waveInStop(HWAVEIN hwi);
デバイスのキューのバッファに音声の入力を開始します
hwi - ウェーブフォームオーディオ入力デバイスのハンドルを指定します
戻り値 - 成功すれば MMSYSERR_NOERROR、そうでなければエラー値
この関数が返すエラー値は、以下の定数のいずれかになります
定数 | 解説
|
---|
MMSYSERR_HANDLEBUSY | 別のスレッドがハンドルを使用中である
|
MMSYSERR_INVALHANDLE | デバイスハンドルが無効である
|
MMSYSERR_NODRIVER | デバイスドライバがない
|
MMSYSERR_NOMEM | メモリを割り当てられない、またはロックできない
|
waveInUnprepareHeader()
MMRESULT waveInUnprepareHeader(
HWAVEIN hwi , LPWAVEHDR pwh , UINT cbwh
);
入力用に準備されたバッファブロックの準備を終了します
hwi - ウェーブフォームオーディオ入力デバイスのハンドルを指定します
pwh - WAVEHDR 構造体へのポインタを指定します
cbwh - WAVEHDR 構造体のサイズを指定します
戻り値 - 成功すれば MMSYSERR_NOERROR、そうでなければエラー値
この関数が返すエラー値は、以下の定数のいずれかになります
定数 | 解説
|
---|
MMSYSERR_HANDLEBUSY | 別のスレッドがハンドルを使用中である
|
MMSYSERR_INVALHANDLE | デバイスハンドルが無効である
|
MMSYSERR_NODRIVER | デバイスドライバがない
|
MMSYSERR_NOMEM | メモリを割り当てられない、またはロックできない
|
WAVERR_STILLPLAYING | キュー内にバッファがまだ残っている
|
waveInClose()
MMRESULT waveInClose(HWAVEIN hwi);
ウェーブフォームオーディオ入力バッファを解放します
hwi - ウェーブフォームオーディオ入力デバイスのハンドルを指定します
戻り値 - 成功すれば MMSYSERR_NOERROR、そうでなければエラー値
この関数が返すエラー値は、以下の定数のいずれかになります
定数 | 解説
|
---|
MMSYSERR_INVALHANDLE | デバイスハンドルが無効である
|
MMSYSERR_NODRIVER | デバイスドライバがない
|
MMSYSERR_NOMEM | メモリを割り当てられない、またはロックできない
|
WAVERR_STILLPLAYING | キュー内にバッファがまだ残っている
|
waveInReset()
MMRESULT waveInReset(HWAVEIN hwi);
ウェーブフォームオーディオ入力デバイスからの入力を停止します
hwi - ウェーブフォームオーディオ入力デバイスのハンドルを指定します
戻り値 - 成功すれば MMSYSERR_NOERROR、そうでなければエラー値
この関数が返すエラー値は、以下の定数のいずれかになります
定数 | 解説
|
---|
MMSYSERR_HANDLEBUSY | 別のスレッドがハンドルを使用中である
|
MMSYSERR_INVALHANDLE | デバイスハンドルが無効である
|
MMSYSERR_NODRIVER | デバイスドライバがない
|
MMSYSERR_NOMEM | メモリを割り当てられない、またはロックできない
|