Windows Vista に搭載されている Windows Core Audio を使って、サウンドデバイスからキャプチャ(録音)をする。Windows Core Audioの特徴を生かして、排他モードで行い、低レイテンシを目指す。
はじめにデバイスの「エンドポイント」を取得する手順を説明する。通常、サウンドデバイスには入出力端子が複数搭載されている。入力系ではMicrophone や Line In 、出力系では前面と背面それぞれの Line Out などである。エンドポイントはデバイス上にあるこれらの端子1つを表す。録音するには、どのエンドポイントから録音するのか決める。
あるデバイス上のエンドポイントから録音するクラスをCoreAudioCaptor
とする。
1 2 3 4 5 6 7 | class CoreAudioCaptor {
CComPtr<IMMDeviceEnumerator> pEnum;
CComPtr<IMMDeviceCollection> pEndCollect;
CComPtr<IMMDevice> pCapDevice;
CComPtr<IAudioClient> pAudClient;
CComPtr<IAudioCaptureClient> pCapClient;
CComPtr<IAudioClock> pClock;
|
MMDeviceEnumerator
のインスタンスを作成し、IMMDeviceEnumerator::EnumAudioEndpoints
を呼び出すと、エンドポイントのコレクションIMMDeviceCollection
を取得できる。このとき引数にDEVICE_STATE_ACTIVE
を渡すと、アクティブなエンドポイントのみ取得する。アクティブなエンドポイントとは、エンドポイントに対応する端子にケーブルが差し込まれて使用可能な状態のことを言います。Windows Vistaではケーブルが端子に差し込まれたことを検出し、その段階でエンドポイントが使用可能になる機能がある。検出が不可能なデバイスもある。
IMMDeviceCollection::GetCount
で数を取得できる。
1 2 3 4 5 6 7 8 9 10 11 12 | HRESULT SetEndPoint() {
HRESULT hr;
// アクティブなキャプチャ用のエンドポイントを列挙
if(FAILED(pEnum.CreateInstance<MMDeviceEnumerator>
(__uuidof(IMMDeviceEnumerator))))
{
return E_FAIL;
}
pEnum->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE, &pEndCollect);
UINT count;
pEndCollect->GetCount(&count);
DebugMsg(_T("%S count=%d"), __FUNCTION__, count);
|
エンドポイントのコレクションから、任意にエンドポイントを取得する。取得したエンドポイントはIMMDevice
を持つ。IMMDevice::Activate
を呼び出してエンドポイントをアクティベートする。アクティベートするとIAudioClient
を取得できる。録音・再生の制御はIAudioClient
で行う。
一般的なアプリケーションであればここでエンドポイントをすべて取得し、ユーザに対してどのエンドポイントから録音するか選択させれば良いだろう。その際、ユーザフレンドリな名前を表示できると良い。そこで IMMDevice::OpenPropertyStore
を使って取得 したIPropertyStore
の IPropertyStore::GetValue
を使って名前を取得 する。取得した名前のバッファはPROPVARIANT
型である。バッファは ComTaskMemFree
で解放する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | for(UINT i=0;i<count;++i) {
CComPtr<IMMDevice> pItem;
LPWSTR id_str;
CComPtr<IPropertyStore> pPropStore;
PROPVARIANT varName;
DWORD state;
pEndCollect->Item(i, &pItem);
pItem->GetId(&id_str);
pItem->OpenPropertyStore(STGM_READ, &pPropStore);
pItem->GetState(&state);
PropVariantInit(&varName);
pPropStore->GetValue(PKEY_Device_FriendlyName, &varName);
DebugMsg(_T("%S #%d %s %s %X"), __FUNCTION__, i, id_str, varName.pwszVal, state);
PropVariantClear(&varName);
CoTaskMemFree(id_str);
//フローは必ずキャプチャであるはず
CComPtr<IMMEndpoint> pEndPoint;
pItem.QueryInterface<IMMEndpoint>(&pEndPoint);
EDataFlow flow;
pEndPoint->GetDataFlow(&flow);
assert(flow==eCapture);
// 2番目のエンドポイントを選択
if(i == 1) {
pCapDevice=pItem;
if(FAILED(pCapDevice->Activate(
__uuidof(IAudioClient), CLSCTX_ALL,NULL, (void**)&pAudClient)))
{
return E_FAIL;
}
}
}
|
Comments
comments powered by Disqus