Search code examples
windows-8.1c++-cxwasapi

WASAPI sample for Windows Store using pointer to released buffer


I'm working with the Audio Capture portion of the Windows Audio Session (WASAPI) sample for Windows 8.1 Store, last update 3/24/2015. I'm writing because I can see examples of code that appears to be wrong or overly complicated. I'm guessing that's because I don't fully understand the WASAPI interface and the Windows Runtime work queues, so I'm hoping the Stack Overflow community can tell me why this isn't a bug.

All of them are in WASAPICapture.cpp. For now, I'll just test the waters with the simplest (and most serious) example: usage of a pointer into a buffer that has been released.

What follows is some pseudocode to highlight the specific lines in question. Line numbers are relative to the more complete code extract further down.

P.S. - my access to the Internet is sporadic, and I'm a newbie - so please be patient with my responses.

Line 1:  hr = m_AudioCaptureClient->GetBuffer( &Data, &FramesAvailable, ...  
         ... returns in Data a pointer to the audio buffer  

Line 21: auto dataByte = ref new Platform::Array<BYTE, 1>( Data, cbBytesToCapture );  
         ... copies from the buffer into a new Platform Array for the GUI's 'scope.  

Line 24: m_AudioCaptureClient->ReleaseBuffer( FramesAvailable );  
         ... releases the buffer  

Line 27: ProcessScopeData( Data, cbBytesToCapture );  
         ... uses the pointer after the underlying buffer has been released!?  

... and here is the more complete code excerpt:

hr = m_AudioCaptureClient->GetBuffer( &Data, &FramesAvailable, &dwCaptureFlags, &u64DevicePosition, &u64QPCPosition );
if (FAILED( hr ))
{
    goto exit;
}

if (dwCaptureFlags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY)
{
    // Pass down a discontinuity flag in case the app is interested and reset back to capturing
    m_DeviceStateChanged->SetState( DeviceState::DeviceStateDiscontinuity, S_OK, true );
    m_DeviceStateChanged->SetState( DeviceState::DeviceStateCapturing, S_OK, false );
}

// Zero out sample if silence
if ( (dwCaptureFlags & AUDCLNT_BUFFERFLAGS_SILENT) || IsSilence )
{
    memset( Data, 0, FramesAvailable * m_MixFormat->nBlockAlign );
}

// Store data in array
auto dataByte = ref new Platform::Array<BYTE, 1>( Data, cbBytesToCapture );

// Release buffer back
m_AudioCaptureClient->ReleaseBuffer( FramesAvailable );

// Update plotter data
ProcessScopeData( Data, cbBytesToCapture );

// Write File and async store
m_WAVDataWriter->WriteBytes( dataByte );

Solution

  • You are correct.

    There are two problems here.

    1. Platform::Array(...) makes a copy, which is an allocation. It's a bad practice to allocate on the streaming thread, because allocation can block, which results in glitches.
    2. ProcessScopeData is being called with Data, which the application should not read from after calling ReleaseBuffer(). Calling it with dataBytes would have been OK.