使用 WASAPI 捕获蓝牙音频数据
capturing bluetooth audio data with WASAPI
我正在做一个小项目,我需要混合来自内部麦克风和蓝牙耳机 (Bose) 的传入数据。我以为我会使用 WASAPI 来实现它。然而,虽然我可以很容易地读出我的内置麦克风,但我的蓝牙耳机却不是这样。
我几乎完全按照文档给出的示例进行操作,但是我做了一个小改动,以便能够使用提供的 ID 选择我自己的“输入设备”(内部麦克风或耳机)。
方法如下:
HRESULT RecordAudioStreamBLE(MyAudioSink *pMySink, LPWSTR pwszID)
{
HRESULT hr;
REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;
REFERENCE_TIME hnsActualDuration;
UINT32 bufferFrameCount;
UINT32 numFramesAvailable;
IMMDeviceEnumerator *pEnumerator = NULL;
IMMDevice *pDevice = NULL;
IAudioClient *pAudioClient = NULL;
IAudioCaptureClient *pCaptureClient = NULL;
WAVEFORMATEX *pwfx = NULL;
UINT32 packetLength = 0;
BOOL bDone = FALSE;
BYTE *pData;
DWORD flags;
hr = CoInitialize(0);
hr = CoCreateInstance(
CLSID_MMDeviceEnumerator, NULL,
CLSCTX_ALL, IID_IMMDeviceEnumerator,
(void**)&pEnumerator);
EXIT_ON_ERROR(hr)
hr = pEnumerator->GetDevice(pwszID, &pDevice);
EXIT_ON_ERROR(hr)
hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL,
NULL, (void**)&pAudioClient);
EXIT_ON_ERROR(hr)
hr = pAudioClient->GetMixFormat(&pwfx);
EXIT_ON_ERROR(hr)
hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED,
0, hnsRequestedDuration,
0, pwfx, NULL);
EXIT_ON_ERROR(hr)
// Get the size of the allocated buffer.
hr = pAudioClient->GetBufferSize(&bufferFrameCount);
EXIT_ON_ERROR(hr)
hr = pAudioClient->GetService(IID_IAudioCaptureClient,
(void**)&pCaptureClient);
EXIT_ON_ERROR(hr)
// Notify the audio sink which format to use.
hr = pMySink->SetFormat(pwfx);
EXIT_ON_ERROR(hr)
// Calculate the actual duration of the allocated buffer.
hnsActualDuration = (double)REFTIMES_PER_SEC * bufferFrameCount / pwfx->nSamplesPerSec;
hr = pAudioClient->Start(); // Start recording.
EXIT_ON_ERROR(hr)
// Each loop fills about half of the shared buffer.
while (bDone == FALSE)
{
// Sleep for half the buffer duration.
Sleep(hnsActualDuration/REFTIMES_PER_MILLISEC/2);
hr = pCaptureClient->GetNextPacketSize(&packetLength);
EXIT_ON_ERROR(hr)
printf("packet size = %d\n", packetLength);
while (packetLength != 0)
{
// Get the available data in the shared buffer.
hr = pCaptureClient->GetBuffer( &pData,
&numFramesAvailable,
&flags, NULL, NULL);
EXIT_ON_ERROR(hr)
if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
{
pData = NULL; // Tell CopyData to write silence.
}
// Copy the available capture data to the audio sink.
hr = pMySink->CopyData(pData, numFramesAvailable, &bDone);
EXIT_ON_ERROR(hr)
hr = pCaptureClient->ReleaseBuffer(numFramesAvailable);
EXIT_ON_ERROR(hr)
hr = pCaptureClient->GetNextPacketSize(&packetLength);
EXIT_ON_ERROR(hr)
}
}
hr = pAudioClient->Stop(); // Stop recording.
EXIT_ON_ERROR(hr)
Exit:
printf("%s\n", hr);
CoTaskMemFree(pwfx);
SAFE_RELEASE(pEnumerator)
SAFE_RELEASE(pDevice)
SAFE_RELEASE(pAudioClient)
SAFE_RELEASE(pCaptureClient)
return hr;
}
使用内置麦克风的 ID 执行此操作时,一切正常。但是,如果使用蓝牙设备的 ID,我会得到以下输出:(我首先列出所有活动设备并在终端中选择 ID:
Endpoint 0: "S24D330 (Intel(R) Display Audio)" ({0.0.0.00000000}.{0f483f83-6e29-482e-94b5-fb9cc257a03d})
Endpoint 1: "Hoofdtelefoon (LE-My headphone Hands-Free AG Audio)" ({0.0.0.00000000}.{75c8e0c3-2e44-4538-940d-f8c2ae6424ca})
Endpoint 2: "Hoofdtelefoon (My headphone Stereo)" ({0.0.0.00000000}.{849b4cc2-ed72-4d40-9725-d1ce6b4abfa0})
Endpoint 3: "Luidsprekers (2- High Definition Audio Device)" ({0.0.0.00000000}.{cb8d7625-257e-4fd0-84b8-26de6aeb1e1b})
Endpoint 4: "Microfoon (2- High Definition Audio Device)" ({0.0.1.00000000}.{5edec961-7a46-4554-bdcd-43fb7d9a9d9a})
Endpoint 5: "Hoofdtelefoon (LE-My headphone Hands-Free AG Audio)" ({0.0.1.00000000}.{f937a0fa-1475-495b-81be-7aec0c1c7ea5})
Which input would you like?5
samples per second 16000
packet size = 0
packet size = 0
packet size = 0
packet size = 0
packet size = 0
packet size = 0
packet size = 0
packet size = 0
packet size = 0
^C
数据包大小在上面显示的方法中循环打印。显示每次的数据包大小刚好为0。
有谁知道如何解决这个问题,并从中获取“常规”数据?
我可能需要使用不同的 API 吗?然而,速度是关键。
谨致问候
许多蓝牙耳机同时支持用于立体声播放的 A2DP 配置文件和用于双向单声道通信的免提协议,但不是同时支持。
如果其中一个协议处于活动状态,则另一个将停止。
我怀疑您遇到的问题是 A2DP 端点“Hoofdtelefoon(我的耳机立体声)”正在播放某些内容,这导致两个 HF 端点上的所有 activity 停止。
我正在做一个小项目,我需要混合来自内部麦克风和蓝牙耳机 (Bose) 的传入数据。我以为我会使用 WASAPI 来实现它。然而,虽然我可以很容易地读出我的内置麦克风,但我的蓝牙耳机却不是这样。 我几乎完全按照文档给出的示例进行操作,但是我做了一个小改动,以便能够使用提供的 ID 选择我自己的“输入设备”(内部麦克风或耳机)。
方法如下:
HRESULT RecordAudioStreamBLE(MyAudioSink *pMySink, LPWSTR pwszID)
{
HRESULT hr;
REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;
REFERENCE_TIME hnsActualDuration;
UINT32 bufferFrameCount;
UINT32 numFramesAvailable;
IMMDeviceEnumerator *pEnumerator = NULL;
IMMDevice *pDevice = NULL;
IAudioClient *pAudioClient = NULL;
IAudioCaptureClient *pCaptureClient = NULL;
WAVEFORMATEX *pwfx = NULL;
UINT32 packetLength = 0;
BOOL bDone = FALSE;
BYTE *pData;
DWORD flags;
hr = CoInitialize(0);
hr = CoCreateInstance(
CLSID_MMDeviceEnumerator, NULL,
CLSCTX_ALL, IID_IMMDeviceEnumerator,
(void**)&pEnumerator);
EXIT_ON_ERROR(hr)
hr = pEnumerator->GetDevice(pwszID, &pDevice);
EXIT_ON_ERROR(hr)
hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL,
NULL, (void**)&pAudioClient);
EXIT_ON_ERROR(hr)
hr = pAudioClient->GetMixFormat(&pwfx);
EXIT_ON_ERROR(hr)
hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED,
0, hnsRequestedDuration,
0, pwfx, NULL);
EXIT_ON_ERROR(hr)
// Get the size of the allocated buffer.
hr = pAudioClient->GetBufferSize(&bufferFrameCount);
EXIT_ON_ERROR(hr)
hr = pAudioClient->GetService(IID_IAudioCaptureClient,
(void**)&pCaptureClient);
EXIT_ON_ERROR(hr)
// Notify the audio sink which format to use.
hr = pMySink->SetFormat(pwfx);
EXIT_ON_ERROR(hr)
// Calculate the actual duration of the allocated buffer.
hnsActualDuration = (double)REFTIMES_PER_SEC * bufferFrameCount / pwfx->nSamplesPerSec;
hr = pAudioClient->Start(); // Start recording.
EXIT_ON_ERROR(hr)
// Each loop fills about half of the shared buffer.
while (bDone == FALSE)
{
// Sleep for half the buffer duration.
Sleep(hnsActualDuration/REFTIMES_PER_MILLISEC/2);
hr = pCaptureClient->GetNextPacketSize(&packetLength);
EXIT_ON_ERROR(hr)
printf("packet size = %d\n", packetLength);
while (packetLength != 0)
{
// Get the available data in the shared buffer.
hr = pCaptureClient->GetBuffer( &pData,
&numFramesAvailable,
&flags, NULL, NULL);
EXIT_ON_ERROR(hr)
if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
{
pData = NULL; // Tell CopyData to write silence.
}
// Copy the available capture data to the audio sink.
hr = pMySink->CopyData(pData, numFramesAvailable, &bDone);
EXIT_ON_ERROR(hr)
hr = pCaptureClient->ReleaseBuffer(numFramesAvailable);
EXIT_ON_ERROR(hr)
hr = pCaptureClient->GetNextPacketSize(&packetLength);
EXIT_ON_ERROR(hr)
}
}
hr = pAudioClient->Stop(); // Stop recording.
EXIT_ON_ERROR(hr)
Exit:
printf("%s\n", hr);
CoTaskMemFree(pwfx);
SAFE_RELEASE(pEnumerator)
SAFE_RELEASE(pDevice)
SAFE_RELEASE(pAudioClient)
SAFE_RELEASE(pCaptureClient)
return hr;
}
使用内置麦克风的 ID 执行此操作时,一切正常。但是,如果使用蓝牙设备的 ID,我会得到以下输出:(我首先列出所有活动设备并在终端中选择 ID:
Endpoint 0: "S24D330 (Intel(R) Display Audio)" ({0.0.0.00000000}.{0f483f83-6e29-482e-94b5-fb9cc257a03d})
Endpoint 1: "Hoofdtelefoon (LE-My headphone Hands-Free AG Audio)" ({0.0.0.00000000}.{75c8e0c3-2e44-4538-940d-f8c2ae6424ca})
Endpoint 2: "Hoofdtelefoon (My headphone Stereo)" ({0.0.0.00000000}.{849b4cc2-ed72-4d40-9725-d1ce6b4abfa0})
Endpoint 3: "Luidsprekers (2- High Definition Audio Device)" ({0.0.0.00000000}.{cb8d7625-257e-4fd0-84b8-26de6aeb1e1b})
Endpoint 4: "Microfoon (2- High Definition Audio Device)" ({0.0.1.00000000}.{5edec961-7a46-4554-bdcd-43fb7d9a9d9a})
Endpoint 5: "Hoofdtelefoon (LE-My headphone Hands-Free AG Audio)" ({0.0.1.00000000}.{f937a0fa-1475-495b-81be-7aec0c1c7ea5})
Which input would you like?5
samples per second 16000
packet size = 0
packet size = 0
packet size = 0
packet size = 0
packet size = 0
packet size = 0
packet size = 0
packet size = 0
packet size = 0
^C
数据包大小在上面显示的方法中循环打印。显示每次的数据包大小刚好为0。 有谁知道如何解决这个问题,并从中获取“常规”数据? 我可能需要使用不同的 API 吗?然而,速度是关键。
谨致问候
许多蓝牙耳机同时支持用于立体声播放的 A2DP 配置文件和用于双向单声道通信的免提协议,但不是同时支持。
如果其中一个协议处于活动状态,则另一个将停止。
我怀疑您遇到的问题是 A2DP 端点“Hoofdtelefoon(我的耳机立体声)”正在播放某些内容,这导致两个 HF 端点上的所有 activity 停止。