uint32_t AudioSink::PlaySilence(uint32_t aFrames) { // Maximum number of bytes we'll allocate and write at once to the audio // hardware when the audio stream contains missing frames and we're // writing silence in order to fill the gap. We limit our silence-writes // to 32KB in order to avoid allocating an impossibly large chunk of // memory if we encounter a large chunk of silence. const uint32_t SILENCE_BYTES_CHUNK = 32 * 1024; AssertOnAudioThread(); NS_ASSERTION(!mAudioStream->IsPaused(), "Don't play when paused"); uint32_t maxFrames = SILENCE_BYTES_CHUNK / mInfo.mChannels / sizeof(AudioDataValue); uint32_t frames = std::min(aFrames, maxFrames); SINK_LOG_V("playing %u frames of silence", aFrames); WriteSilence(frames); return frames; }
//------------------------------------------------------------------------- // Description: // // Verifies that the APO is ready to process and locks its state if so. // // Parameters: // // u32NumInputConnections - [in] number of input connections attached to this APO // ppInputConnections - [in] connection descriptor of each input connection attached to this APO // u32NumOutputConnections - [in] number of output connections attached to this APO // ppOutputConnections - [in] connection descriptor of each output connection attached to this APO // // Return values: // // S_OK Object is locked and ready to process. // E_POINTER Invalid pointer passed to function. // APOERR_INVALID_CONNECTION_FORMAT Invalid connection format. // APOERR_NUM_CONNECTIONS_INVALID Number of input or output connections is not valid on // this APO. STDMETHODIMP CSwapAPOMFX::LockForProcess(UINT32 u32NumInputConnections, APO_CONNECTION_DESCRIPTOR** ppInputConnections, UINT32 u32NumOutputConnections, APO_CONNECTION_DESCRIPTOR** ppOutputConnections) { ASSERT_NONREALTIME(); HRESULT hr = S_OK; hr = CBaseAudioProcessingObject::LockForProcess(u32NumInputConnections, ppInputConnections, u32NumOutputConnections, ppOutputConnections); IF_FAILED_JUMP(hr, Exit); if (!IsEqualGUID(m_AudioProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW) && m_fEnableDelayMFX) { m_nDelayFrames = FRAMES_FROM_HNS(HNS_DELAY); m_iDelayIndex = 0; m_pf32DelayBuffer.Free(); // Allocate one second's worth of audio // // This allocation is being done using CoTaskMemAlloc because the delay is very large // This introduces a risk of glitches if the delay buffer gets paged out // // A more typical approach would be to allocate the memory using AERT_Allocate, which locks the memory // But for the purposes of this APO, CoTaskMemAlloc suffices, and the risk of glitches is not important m_pf32DelayBuffer.Allocate(GetSamplesPerFrame() * m_nDelayFrames); WriteSilence(m_pf32DelayBuffer, m_nDelayFrames, GetSamplesPerFrame()); if (nullptr == m_pf32DelayBuffer) { hr = E_OUTOFMEMORY; goto Exit; } } Exit: return hr; }
BOOL AudioStream::ServiceBuffer (void) { long vol; int fRtn = TRUE; if ( status != ASF_USED ) return FALSE; EnterCriticalSection(&write_lock); // status may have changed, so lets check once again if ( status != ASF_USED ){ LeaveCriticalSection(&write_lock); return FALSE; } // Check for reentrance if (InterlockedExchange (&m_lInService, TRUE) == FALSE) { if ( m_bFade == TRUE ) { if ( m_lCutoffVolume == -10000 ) { vol = Get_Volume(); // nprintf(("Alan","Volume is: %d\n",vol)); m_lCutoffVolume = max(vol - VOLUME_ATTENUATION_BEFORE_CUTOFF, -10000); } vol = Get_Volume(); vol = vol - FADE_VOLUME_INTERVAL; // decrease by 1db // nprintf(("Alan","Volume is now: %d\n",vol)); Set_Volume(vol); // nprintf(("Sound","SOUND => Volume for stream sound is %d\n",vol)); // nprintf(("Alan","Cuttoff Volume is: %d\n",m_lCutoffVolume)); if ( vol < m_lCutoffVolume ) { m_bFade = 0; m_lCutoffVolume = -10000; if ( m_bDestroy_when_faded == TRUE ) { LeaveCriticalSection(&write_lock); Destroy(); // Reset reentrancy semaphore InterlockedExchange (&m_lInService, FALSE); return FALSE; } else { Stop_and_Rewind(); // Reset reentrancy semaphore LeaveCriticalSection(&write_lock); InterlockedExchange (&m_lInService, FALSE); return TRUE; } } } // All of sound not played yet, send more data to buffer DWORD dwFreeSpace = GetMaxWriteSize (); // Determine free space in sound buffer if (dwFreeSpace) { // Some wave data remains, but not enough to fill free space // Send wave data to buffer, fill remainder of free space with silence uint num_bytes_written; if (WriteWaveData (dwFreeSpace, &num_bytes_written) == SUCCESS) { // nprintf(("Alan","Num bytes written: %d\n", num_bytes_written)); if ( m_pwavefile->m_total_uncompressed_bytes_read >= m_pwavefile->m_max_uncompressed_bytes_to_read ) { m_fade_timer_id = timer_get_milliseconds() + 1700; // start fading 1.7 seconds from now m_finished_id = timer_get_milliseconds() + 2000; // 2 seconds left to play out buffer m_pwavefile->m_max_uncompressed_bytes_to_read = AS_HIGHEST_MAX; } if ( (m_fade_timer_id>0) && ((uint)timer_get_milliseconds() > m_fade_timer_id) ) { m_fade_timer_id = 0; Fade_and_Stop(); } if ( (m_finished_id>0) && ((uint)timer_get_milliseconds() > m_finished_id) ) { m_finished_id = 0; m_bPastLimit = TRUE; } if ( (num_bytes_written < dwFreeSpace) && m_bReadingDone ) { int num_bytes_silence; num_bytes_silence = dwFreeSpace - num_bytes_written; if ( num_bytes_silence > 0 ) { m_silence_written += num_bytes_silence; if (WriteSilence (num_bytes_silence) == FAILURE) { fRtn = FALSE; Int3(); } if ( m_silence_written >= m_cbBufSize ) { m_silence_written = 0; if ( m_bDestroy_when_faded == TRUE ) { LeaveCriticalSection(&write_lock); Destroy(); // Reset reentrancy semaphore InterlockedExchange (&m_lInService, FALSE); return FALSE; } // All of sound has played, stop playback or loop again if ( m_bLooping && !m_bFade) { Play(m_lVolume, m_bLooping); } else { Stop_and_Rewind(); } } } } } else { // Error writing wave data fRtn = FALSE; Int3(); } } // Reset reentrancy semaphore InterlockedExchange (&m_lInService, FALSE); } else { // Service routine reentered. Do nothing, just return fRtn = FALSE; } LeaveCriticalSection(&write_lock); return (fRtn); }
//------------------------------------------------------------------------- // Description: // // Do the actual processing of data. // // Parameters: // // u32NumInputConnections - [in] number of input connections // ppInputConnections - [in] pointer to list of input APO_CONNECTION_PROPERTY pointers // u32NumOutputConnections - [in] number of output connections // ppOutputConnections - [in] pointer to list of output APO_CONNECTION_PROPERTY pointers // // Return values: // // void // // Remarks: // // This function processes data in a manner dependent on the implementing // object. This routine can not fail and can not block, or call any other // routine that blocks, or touch pagable memory. // STDMETHODIMP_(void) CSwapAPOMFX::APOProcess( UINT32 u32NumInputConnections, APO_CONNECTION_PROPERTY** ppInputConnections, UINT32 u32NumOutputConnections, APO_CONNECTION_PROPERTY** ppOutputConnections) { UNREFERENCED_PARAMETER(u32NumInputConnections); UNREFERENCED_PARAMETER(u32NumOutputConnections); FLOAT32 *pf32InputFrames, *pf32OutputFrames; ATLASSERT(m_bIsLocked); // assert that the number of input and output connectins fits our registration properties ATLASSERT(m_pRegProperties->u32MinInputConnections <= u32NumInputConnections); ATLASSERT(m_pRegProperties->u32MaxInputConnections >= u32NumInputConnections); ATLASSERT(m_pRegProperties->u32MinOutputConnections <= u32NumOutputConnections); ATLASSERT(m_pRegProperties->u32MaxOutputConnections >= u32NumOutputConnections); // check APO_BUFFER_FLAGS. switch( ppInputConnections[0]->u32BufferFlags ) { case BUFFER_INVALID: { ATLASSERT(false); // invalid flag - should never occur. don't do anything. break; } case BUFFER_VALID: case BUFFER_SILENT: { // get input pointer to connection buffer pf32InputFrames = reinterpret_cast<FLOAT32*>(ppInputConnections[0]->pBuffer); ATLASSERT( IS_VALID_TYPED_READ_POINTER(pf32InputFrames) ); // get output pointer to connection buffer pf32OutputFrames = reinterpret_cast<FLOAT32*>(ppOutputConnections[0]->pBuffer); ATLASSERT( IS_VALID_TYPED_READ_POINTER(pf32OutputFrames) ); if (BUFFER_SILENT == ppInputConnections[0]->u32BufferFlags) { WriteSilence( pf32InputFrames, ppInputConnections[0]->u32ValidFrameCount, GetSamplesPerFrame() ); } // swap and apply coefficients to the input buffer in-place if ( !IsEqualGUID(m_AudioProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW) && m_fEnableSwapMFX && (1 < m_u32SamplesPerFrame) ) { ProcessSwapScale(pf32InputFrames, pf32InputFrames, ppInputConnections[0]->u32ValidFrameCount, m_u32SamplesPerFrame, m_pf32Coefficients ); } // copy to the delay buffer if ( !IsEqualGUID(m_AudioProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW) && m_fEnableDelayMFX ) { ProcessDelay(pf32OutputFrames, pf32InputFrames, ppInputConnections[0]->u32ValidFrameCount, GetSamplesPerFrame(), m_pf32DelayBuffer, m_nDelayFrames, &m_iDelayIndex); // we don't try to remember silence ppOutputConnections[0]->u32BufferFlags = BUFFER_VALID; } else { // copy the memory only if there is an output connection, and input/output pointers are unequal if ( (0 != u32NumOutputConnections) && (ppOutputConnections[0]->pBuffer != ppInputConnections[0]->pBuffer) ) { CopyFrames( pf32OutputFrames, pf32InputFrames, ppInputConnections[0]->u32ValidFrameCount, GetSamplesPerFrame() ); } // pass along buffer flags ppOutputConnections[0]->u32BufferFlags = ppInputConnections[0]->u32BufferFlags; } // Set the valid frame count. ppOutputConnections[0]->u32ValidFrameCount = ppInputConnections[0]->u32ValidFrameCount; break; } default: { ATLASSERT(false); // invalid flag - should never occur break; } } // switch } // APOProcess