Я пытаюсь визуализировать звуковую волну, захваченную обратной связью WASAPI, но обнаруживаю, что пакеты, которые я записываю, не образуют плавной волны при объединении.
Насколько я понимаю, как работает клиент захвата WASAPI, так это то, что когда я вызываю pCaptureClient->GetBuffer(&pData, &numFramesAvailable, &flags, NULL, NULL)
, буфер pData
заполняется спереди numFramesAvailable
точками данных. Каждая точка данных является плавающей, и они чередуются по каналам. Таким образом, чтобы получить все доступные точки данных, я должен привести pData
к указателю с плавающей запятой и взять первые значения channels * numFramesAvailable
. Как только я освобождаю буфер и снова вызываю GetBuffer
, он предоставляет следующий пакет. Я бы предположил, что эти пакеты будут следовать друг за другом, но, похоже, это не так.
Я предполагаю, что либо я делаю неверное предположение о формате аудиоданных в pData
, либо в клиенте захвата отсутствуют или перекрываются кадры. Но понятия не имею, как их проверить.
Чтобы сделать приведенный ниже код как можно короче, я удалил такие вещи, как проверка статуса ошибки и очистка.
Инициализация клиента захвата:
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
const IID IID_IAudioClient = __uuidof(IAudioClient);
const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient);
pAudioClient = NULL;
IMMDeviceEnumerator * pDeviceEnumerator = NULL;
IMMDevice * pDeviceEndpoint = NULL;
IAudioClient *pAudioClient = NULL;
IAudioCaptureClient *pCaptureClient = NULL;
int channels;
// Initialize audio device endpoint
CoInitialize(nullptr);
CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator );
pDeviceEnumerator ->GetDefaultAudioEndpoint(eRender, eConsole, &pDeviceEndpoint );
// init audio client
WAVEFORMATEX *pwfx = NULL;
REFERENCE_TIME hnsRequestedDuration = 10000000;
REFERENCE_TIME hnsActualDuration;
audio_device_endpoint->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);
pAudioClient->GetMixFormat(&pwfx);
pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK, hnsRequestedDuration, 0, pwfx, NULL);
channels = pwfx->nChannels;
pAudioClient->GetService(IID_IAudioCaptureClient, (void**)&pCaptureClient);
pAudioClient->Start(); // Start recording.
Захват пакетов (обратите внимание, что std::mutex packet_buffer_mutex
и vector<vector<float>> packet_buffer
уже определены и используются другим потоком для безопасного отображения данных):
UINT32 packetLength = 0;
BYTE *pData = NULL;
UINT32 numFramesAvailable;
DWORD flags;
int max_packets = 8;
std::unique_lock<std::mutex>write_guard(packet_buffer_mutex, std::defer_lock);
while (true) {
pCaptureClient->GetNextPacketSize(&packetLength);
while (packetLength != 0)
{
// Get the available data in the shared buffer.
pData = NULL;
pCaptureClient->GetBuffer(&pData, &numFramesAvailable, &flags, NULL, NULL);
if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
{
pData = NULL; // Tell CopyData to write silence.
}
write_guard.lock();
if (packet_buffer.size() == max_packets) {
packet_buffer.pop_back();
}
if (pData) {
float * pfData = (float*)pData;
packet_buffer.emplace(packet_buffer.begin(), pfData, pfData + channels * numFramesAvailable);
} else {
packet_buffer.emplace(packet_buffer.begin());
}
write_guard.unlock();
hpCaptureClient->ReleaseBuffer(numFramesAvailable);
pCaptureClient->GetNextPacketSize(&packetLength);
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
Я храню пакеты в vector<vector<float>>
(где каждый vector<float>
является пакетом), удаляя последний и вставляя самый новый в начале, чтобы я мог перебирать их по порядку. Ниже показан результат захваченной синусоиды с чередующимися значениями, поэтому он представляет только один канал. Понятно, где пакеты сшиваются.
vector<float>
? - person KyleL   schedule 01.10.2020AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY
возвращалиflags
- person Roman R.   schedule 19.10.2020