// Processing DWORD CTimeStretchFilter::ThreadProc() { Log("CTimeStretchFilter::timestretch thread - starting up - thread ID: %d", m_ThreadId); SetThreadName(0, "TimeStretchFilter"); AudioSinkCommand command; CComPtr<IMediaSample> sample; while (true) { m_csResources.Unlock(); HRESULT hr = GetNextSampleOrCommand(&command, &sample.p, INFINITE, &m_hSampleEvents, &m_dwSampleWaitObjects); m_csResources.Lock(); if (hr == MPAR_S_THREAD_STOPPING) { Log("CTimeStretchFilter::timestretch thread - closing down - thread ID: %d", m_ThreadId); SetEvent(m_hCurrentSampleReleased); CloseThread(); m_csResources.Unlock(); return 0; } else { if (command == ASC_Flush) { Log("CTimeStretchFilter::timestretch thread - flushing"); m_rtInSampleTime = m_rtNextIncomingSampleTime = 0; if (m_pNextOutSample) m_pNextOutSample.Release(); flush(); sample.Release(); SetEvent(m_hCurrentSampleReleased); } else if (command == ASC_Pause || command == ASC_Resume) continue; else if (sample) { BYTE *pMediaBuffer = NULL; long size = sample->GetActualDataLength(); if (sample->IsDiscontinuity() == S_OK) { sample->SetDiscontinuity(false); m_bDiscontinuity = true; } if (CheckSample(sample) == S_FALSE) { DeleteMediaType(m_pMediaType); sample->GetMediaType(&m_pMediaType); } CheckStreamContinuity(sample); m_nSampleNum++; hr = sample->GetPointer(&pMediaBuffer); if ((hr == S_OK) && m_pMemAllocator) { uint unprocessedSamplesBefore = numUnprocessedSamples(); uint unprocessedSamplesAfter = 0; UINT32 nFrames = size / m_pOutputFormat->Format.nBlockAlign; REFERENCE_TIME estimatedSampleDuration = nFrames * UNITS / m_pOutputFormat->Format.nSamplesPerSec; double bias = m_pClock->GetBias(); double adjustment = m_pClock->Adjustment(); double AVMult = m_pClock->SuggestedAudioMultiplier(estimatedSampleDuration, bias, adjustment); setTempoInternal(AVMult, 1.0); // this should be the same as previous line, but in future we want to get rid of the 2nd parameter // Process the sample putSamplesInternal((const short*)pMediaBuffer, size / m_pOutputFormat->Format.nBlockAlign); unprocessedSamplesAfter = numUnprocessedSamples(); UINT32 nInFrames = (size / m_pOutputFormat->Format.nBlockAlign) - unprocessedSamplesAfter + unprocessedSamplesBefore; UINT32 nOutFrames = numSamples(); CreateOutput(nInFrames, nOutFrames, bias, adjustment, AVMult, false); } } } } }
// Processing DWORD CTimeStretchFilter::ThreadProc() { Log("CTimeStretchFilter::timestretch thread - starting up - thread ID: %d", m_ThreadId); SetThreadName(0, "TimeStretchFilter"); AudioSinkCommand command; CComPtr<IMediaSample> sample; while (true) { m_csResources.Unlock(); HRESULT hr = GetNextSampleOrCommand(&command, &sample.p, INFINITE, &m_hSampleEvents, &m_dwSampleWaitObjects); m_csResources.Lock(); if (hr == MPAR_S_THREAD_STOPPING) { Log("CTimeStretchFilter::timestretch thread - closing down - thread ID: %d", m_ThreadId); SetEvent(m_hCurrentSampleReleased); CloseThread(); m_csResources.Unlock(); return 0; } else { if (command == ASC_Flush) { Log("CTimeStretchFilter::timestretch thread - flushing"); m_rtInSampleTime = m_rtNextIncomingSampleTime = 0; m_rtLastOuputStart = m_rtLastOuputEnd = -1; if (m_pNextOutSample) m_pNextOutSample.Release(); flush(); m_pClock->Flush(); sample.Release(); SetEvent(m_hCurrentSampleReleased); } else if (command == ASC_Pause || command == ASC_Resume) continue; else if (sample) { BYTE *pMediaBuffer = NULL; long size = sample->GetActualDataLength(); if (sample->IsDiscontinuity() == S_OK) { sample->SetDiscontinuity(false); m_bDiscontinuity = true; } REFERENCE_TIME rtDrained = 0; if (CheckSample(sample, &rtDrained) == S_FALSE) { DeleteMediaType(m_pMediaType); sample->GetMediaType(&m_pMediaType); } CheckStreamContinuity(sample, rtDrained); m_nSampleNum++; hr = sample->GetPointer(&pMediaBuffer); if ((hr == S_OK) && m_pMemAllocator) { REFERENCE_TIME rtStart = 0; REFERENCE_TIME rtAdjustedStart = 0; REFERENCE_TIME rtEnd = 0; REFERENCE_TIME rtAdjustedEnd = 0; REFERENCE_TIME rtAHwTime = 0; REFERENCE_TIME rtRCTime = 0; m_pClock->GetHWTime(&rtRCTime, &rtAHwTime); sample->GetTime(&rtStart, &rtEnd); REFERENCE_TIME sampleDuration = rtEnd - rtStart; uint unprocessedSamplesBefore = numUnprocessedSamples(); uint unprocessedSamplesAfter = 0; UINT32 nFrames = size / m_pOutputFormat->Format.nBlockAlign; double bias = m_pClock->GetBias(); double adjustment = m_pClock->Adjustment(); double AVMult = m_pClock->SuggestedAudioMultiplier(rtAHwTime, rtRCTime, bias, adjustment); setTempoInternal(AVMult, 1.0); if (m_rtLastOuputEnd == -1) m_rtLastOuputEnd = rtStart / AVMult - 1; m_rtLastOuputStart = m_rtLastOuputEnd + 1; // Process the sample putSamplesInternal((const short*)pMediaBuffer, size / m_pOutputFormat->Format.nBlockAlign); unprocessedSamplesAfter = numUnprocessedSamples(); UINT32 nInFrames = (size / m_pOutputFormat->Format.nBlockAlign) - unprocessedSamplesAfter + unprocessedSamplesBefore; UINT32 nOutFrames = numSamples(); // TODO: Soundtouch can provide less samples than asked (but never more) so a cummulative error is possible. This will not happen over the course of a long TV stint, but could be solved for correctness // m_rtLastOuputEnd += (nOutFrames + unprocessedSamplesAfter - unprocessedSamplesBefore) * UNITS / m_pOutputFormat->Format.nSamplesPerSec; //rtStart = m_rtInSampleTime; rtEnd = rtStart + sampleDuration; rtAdjustedStart = m_rtLastOuputEnd +1; rtAdjustedEnd = rtAdjustedStart + sampleDuration / AVMult; m_rtLastOuputEnd += sampleDuration / AVMult; CreateOutput(nInFrames, nOutFrames, bias, adjustment, AVMult, false); m_pClock->AddSample(rtStart, rtAdjustedStart, rtEnd, rtAdjustedEnd); } } } } }