DirectSound麦克风捕获采样率异常问题求助
问题:DirectSound麦克风捕获采样率异常(仅为设置值的2/3)
使用DirectSound从麦克风捕获音频时,其他功能正常,但捕获的实际采样率仅为设置值的2/3:设置44100Hz时实际约30320Hz,设置22050Hz时实际约14690Hz。以下是相关代码片段,请求排查原因。
初始化代码片段
long audio_rec::init_capture_buffer( const long dwNotifyArraySize, const long dwNotifyTickInterval, const GUID& guidDevice, const WAVEFORMATEX& wfxInput) { HRESULT hr = S_OK; SAFE_RELEASE(m_pDSCapture); // LPDIRECTSOUNDCAPTURE SAFE_RELEASE(m_pDSNotify); // LPDIRECTSOUNDNOTIFY SAFE_RELEASE(m_pDSCaptureBuffer); // LPDIRECTSOUNDCAPTUREBUFFER SAFE_DELETE_ARRAY(m_pNotifyArray);// LPDSBPOSITIONNOTIFY // Save Notify Array Size m_dwNotifyArraySize = dwNotifyArraySize; // Init Notification Event if (nullptr == m_hNotificationEvent) { m_hNotificationEvent = ::CreateEventW(NULL, FALSE, FALSE, NULL); } if (m_hNotificationEvent) { ::ResetEvent(m_hNotificationEvent); } // Init Direct Sound // Create IDirectSoundCapture using the preferred capture device if (FAILED(hr = ::DirectSoundCaptureCreate(&guidDevice, &m_pDSCapture, NULL))) { return hr; } // Allocate Buffer for Recording // Create Capture Buffer // Initialize Notification long dwNotifyByteInterval = (long)(((dwNotifyTickInterval) / 1000.0) * mic_wfx.nAvgBytesPerSec); dwNotifyByteInterval -= dwNotifyByteInterval % mic_wfx.nBlockAlign; m_dwNotifyByteInterval = dwNotifyByteInterval; // Set the buffer sizes m_dwBufferSize = dwNotifyByteInterval * dwNotifyArraySize; // Set the format during creatation DSCBUFFERDESC dscbd = { 0 }; dscbd.dwSize = sizeof(dscbd); dscbd.dwBufferBytes = m_dwBufferSize; dscbd.lpwfxFormat = (LPWAVEFORMATEX)&wfxInput; // Create the capture buffer if (FAILED(hr = m_pDSCapture->CreateCaptureBuffer(&dscbd, &m_pDSCaptureBuffer, nullptr))) { SAFE_RELEASE(m_pDSCapture); return hr; } // Set Notification if (FAILED(hr = m_pDSCaptureBuffer->QueryInterface(IID_IDirectSoundNotify, (VOID**)&m_pDSNotify))) { SAFE_RELEASE(m_pDSCapture); SAFE_RELEASE(m_pDSCaptureBuffer); return hr; } // Allocate Notification Array if (m_pNotifyArray) { SAFE_DELETE_ARRAY(m_pNotifyArray); } m_pNotifyArray = new DSBPOSITIONNOTIFY[dwNotifyArraySize + 1]; ::memset(m_pNotifyArray, 0, sizeof(DSBPOSITIONNOTIFY) * (dwNotifyArraySize + 1)); // Setup the notification positions for (int i = 0; i < (int)m_dwNotifyArraySize; i++) { m_pNotifyArray[i].dwOffset = m_dwNotifyByteInterval * (i + 1) - 1; m_pNotifyArray[i].hEventNotify = m_hNotificationEvent; } // Tell DirectSound when to notify us. the notification will come in the from // of signaled events that are handled in WinMain() if (FAILED(hr = m_pDSNotify->SetNotificationPositions(dwNotifyArraySize, m_pNotifyArray))) { SAFE_RELEASE(m_pDSCapture); SAFE_RELEASE(m_pDSNotify); SAFE_RELEASE(m_pDSCaptureBuffer); SAFE_DELETE_ARRAY(m_pNotifyArray); return hr; } return hr; }
主线程代码片段
void audio_rec::record_loop() { HANDLE& hNotiEvent = m_hNotificationEvent; long hr = S_OK; // Prepare buffer to record std::vector<unsigned char> buffer1; std::vector<unsigned char> buffer2; long dwNextOffset = 0; WAVEFORMATEX wfx = this->mic_wfx; // Reset Notify Event ::ResetEvent(hNotiEvent); // Start Recording if (FAILED(hr = this->m_pDSCaptureBuffer->Start(DSCBSTART_LOOPING))) { return; } // Loop Until Stop sign is signaled while (this->flag_stop_capture == false) { ::WaitForSingleObject(hNotiEvent, INFINITE); // read buffer // ReadCapturedData()함수에서 Buffer를 할당 hr = this->read_captured_data( this->m_pDSCaptureBuffer, buffer1, buffer2, dwNextOffset); if (FAILED(hr)) { break; } ... ::ResetEvent(hNotiEvent); } }
音频读取代码片段
long audio_rec::read_captured_data( __in LPDIRECTSOUNDCAPTUREBUFFER pCaptureBuffer, __out std::vector<unsigned char>& buffer1, __out std::vector<unsigned char>& buffer2, __inout long& dwNextOffset) { HRESULT hr = S_OK; PVOID pbCaptureData1 = nullptr; unsigned long dwCapturedLength1 = 0; PVOID pbCaptureData2 = nullptr; unsigned long dwCapturedLength2 = 0; unsigned long dwReadPos = 0; unsigned long dwCapturePos = 0; long lLockSize = 0; buffer1.clear(); buffer2.clear(); if (nullptr == pCaptureBuffer) { return S_FALSE; } if (FAILED(hr = pCaptureBuffer->GetCurrentPosition(&dwCapturePos, &dwReadPos))) { return (long)hr; } //assert(dwCapturePos - dwReadPos == dwNotiByteInterval); //dwLockSize = dwNotiByteInterval; lLockSize = dwReadPos - dwNextOffset; if (lLockSize < 0) { lLockSize += m_dwBufferSize; } // Block align lock size lLockSize -= (lLockSize % m_dwNotifyByteInterval); if (lLockSize == 0) { return S_FALSE; } // Lock the capture buffer down if (FAILED(hr = m_pDSCaptureBuffer->Lock( dwNextOffset, lLockSize, &pbCaptureData1, &dwCapturedLength1, &pbCaptureData2, &dwCapturedLength2, 0L))) { return hr; } // copy captured buffer if (pbCaptureData1 != nullptr) { if (dwCapturedLength1 > 0) { //buffer1.resize(dwCapturedLength1); //::memcpy(buffer1.data(), (unsigned char*)pbCaptureData1, dwCapturedLength1); buffer1.assign((unsigned char*)pbCaptureData1, ((unsigned char*)pbCaptureData1) + dwCapturedLength1); } // Move the capture offset along dwNextOffset += dwCapturedLength1; dwNextOffset %= m_dwBufferSize; // Circular buffer } if (pbCaptureData2 != nullptr) { if (dwCapturedLength2 > 0) { buffer2.assign( (unsigned char*)pbCaptureData2, ((unsigned char*)pbCaptureData2) + dwCapturedLength2); } dwNextOffset += dwCapturedLength2; dwNextOffset %= m_dwBufferSize; // Circular buffer } // Unlock the capture buffer hr = pCaptureBuffer->Unlock( pbCaptureData1, dwCapturedLength1, pbCaptureData2, dwCapturedLength2); return S_OK; }
问题排查分析
格式参数不一致导致通知间隔计算错误
初始化函数中,计算dwNotifyByteInterval时使用了类成员mic_wfx,但创建捕获缓冲区时传入的是参数wfxInput。如果mic_wfx的采样率并非目标设置值(比如实际是设置值的2/3),会导致通知字节间隔计算偏小,后续每次读取的数据量不足,最终表现为采样率降低。
修复方式:将计算逻辑替换为使用传入的wfxInput:long dwNotifyByteInterval = (long)(((dwNotifyTickInterval) / 1000.0) * wfxInput.nAvgBytesPerSec); dwNotifyByteInterval -= dwNotifyByteInterval % wfxInput.nBlockAlign;同时需确认
mic_wfx是否在初始化时被正确赋值为wfxInput,避免其他逻辑使用错误格式。读取逻辑的字节截断问题
在read_captured_data中,lLockSize被强制对齐到m_dwNotifyByteInterval,如果待读取数据量不是其整数倍,多余字节会被丢弃,长期积累会导致总数据量不足。需确保通知事件触发时,待读取数据量正好是m_dwNotifyByteInterval的整数倍(符合DirectSound通知机制的设计预期)。设备实际支持格式验证
部分音频设备可能不支持目标采样率,DirectSound会自动降级到兼容格式。可在创建缓冲区后调用GetFormat获取实际格式,验证是否与设置一致:WAVEFORMATEX actualFormat; DWORD formatSize = sizeof(actualFormat); if (SUCCEEDED(m_pDSCaptureBuffer->GetFormat(&actualFormat, &formatSize, nullptr))) { // 对比actualFormat.wSamplesPerSec与设置值 }
内容的提问来源于stack exchange,提问作者Jay Kim
相关产品推荐
相关产品推荐

