You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

问题排查分析

  1. 格式参数不一致导致通知间隔计算错误
    初始化函数中,计算dwNotifyByteInterval时使用了类成员mic_wfx,但创建捕获缓冲区时传入的是参数wfxInput。如果mic_wfx的采样率并非目标设置值(比如实际是设置值的2/3),会导致通知字节间隔计算偏小,后续每次读取的数据量不足,最终表现为采样率降低。
    修复方式:将计算逻辑替换为使用传入的wfxInput:

    long dwNotifyByteInterval
        = (long)(((dwNotifyTickInterval) / 1000.0) * wfxInput.nAvgBytesPerSec);
    dwNotifyByteInterval -= dwNotifyByteInterval % wfxInput.nBlockAlign;
    

    同时需确认mic_wfx是否在初始化时被正确赋值为wfxInput,避免其他逻辑使用错误格式。

  2. 读取逻辑的字节截断问题
    在read_captured_data中,lLockSize被强制对齐到m_dwNotifyByteInterval,如果待读取数据量不是其整数倍,多余字节会被丢弃,长期积累会导致总数据量不足。需确保通知事件触发时,待读取数据量正好是m_dwNotifyByteInterval的整数倍(符合DirectSound通知机制的设计预期)。

  3. 设备实际支持格式验证
    部分音频设备可能不支持目标采样率,DirectSound会自动降级到兼容格式。可在创建缓冲区后调用GetFormat获取实际格式,验证是否与设置一致:

    WAVEFORMATEX actualFormat;
    DWORD formatSize = sizeof(actualFormat);
    if (SUCCEEDED(m_pDSCaptureBuffer->GetFormat(&actualFormat, &formatSize, nullptr))) {
        // 对比actualFormat.wSamplesPerSec与设置值
    }
    

内容的提问来源于stack exchange,提问作者Jay Kim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 01:20:55