From 04609fa2c4fe8711f6312caf12861d1f53466e3a Mon Sep 17 00:00:00 2001 From: Alex Marsev Date: Thu, 18 Jun 2015 07:12:53 +0300 Subject: Redo AudioRenderer::NewSegment() And related stuff. --- src/AudioRenderer.cpp | 35 +++++++----- src/SampleCorrection.cpp | 144 +++++++++++++++++------------------------------ src/SampleCorrection.h | 10 ++-- 3 files changed, 76 insertions(+), 113 deletions(-) (limited to 'src') diff --git a/src/AudioRenderer.cpp b/src/AudioRenderer.cpp index 6705ad3..0c1a8da 100644 --- a/src/AudioRenderer.cpp +++ b/src/AudioRenderer.cpp @@ -93,10 +93,10 @@ namespace SaneAudioRenderer if (!m_device) CreateDevice(); - // Apply sample corrections (pad, crop, guess timings). + // Establish time/frame relation. chunk = m_sampleCorrection.ProcessSample(pSample, sampleProps); - // Apply clock corrections (what we couldn't correct with sample correction). + // Apply clock corrections. if (!m_live && m_device && m_state == State_Running) ApplyClockCorrection(); @@ -113,11 +113,11 @@ namespace SaneAudioRenderer DspChunk::ToFormat(m_device->GetDspFormat(), chunk); } - // Apply rate corrections (rate matching or external clock). + // Apply rate corrections (rate matching and clock slaving). if (m_device && !m_device->IsBitstream() && m_device->IsRealtime() && m_state == State_Running) ApplyRateCorrection(chunk); - // Don't deny the allocator to reuse IMediaSample while the chunk is hanging in the buffer. + // Don't deny the allocator its right to reuse IMediaSample while the chunk is hanging in the buffer. if (m_device && m_device->IsRealtime()) chunk.FreeMediaSample(); } @@ -227,7 +227,7 @@ namespace SaneAudioRenderer m_device->Reset(); m_sampleCorrection.NewDeviceBuffer(); InitializeProcessors(); - m_startClockOffset = m_sampleCorrection.GetLastSampleEnd(); + m_startClockOffset = m_sampleCorrection.GetLastFrameEnd(); StartDevice(); } else @@ -285,14 +285,19 @@ namespace SaneAudioRenderer { CAutoLock objectLock(this); + if (m_rate != rate) + { + m_rate = rate; + + if (m_device) + (m_device->GetEnd() > 0) ? ClearDevice() : InitializeProcessors(); + } + m_startClockOffset = 0; - m_rate = rate; - m_sampleCorrection.NewSegment(m_rate); + m_clockCorrection += m_sampleCorrection.GetLastFrameEnd(); - assert(m_inputFormat); - if (m_device) - InitializeProcessors(); + m_sampleCorrection.NewSegment(m_rate); } void AudioRenderer::Play(REFERENCE_TIME startTime) @@ -418,7 +423,7 @@ namespace SaneAudioRenderer InitializeProcessors(); - m_startClockOffset = m_sampleCorrection.GetLastSampleEnd(); + m_startClockOffset = m_sampleCorrection.GetLastFrameEnd(); if (m_state == State_Running) StartDevice(); @@ -445,7 +450,7 @@ namespace SaneAudioRenderer // Apply corrections to internal clock. { - REFERENCE_TIME offset = m_sampleCorrection.GetTimingsError() - m_clockCorrection; + REFERENCE_TIME offset = m_sampleCorrection.GetTimeDivergence() - m_clockCorrection; if (std::abs(offset) > 100) { m_myClock->OffsetSlavedClock(offset); @@ -520,7 +525,7 @@ namespace SaneAudioRenderer m_myClock->OffsetSlavedClock(-llMulDiv(padFrames, OneSecond, m_device->GetWaveFormat()->nSamplesPerSec, 0)); - DebugOut("AudioRenderer pad", padFrames, "frames for clock matching", m_sampleCorrection.GetLastSampleEnd() / 10000., (myTime - graphTime) / 10000.); + DebugOut("AudioRenderer pad", padFrames, "frames for clock matching", m_sampleCorrection.GetLastFrameEnd() / 10000., (myTime - graphTime) / 10000.); } } else if (remaining > latency) @@ -541,7 +546,7 @@ namespace SaneAudioRenderer m_myClock->OffsetSlavedClock(llMulDiv(dropFrames, OneSecond, m_device->GetWaveFormat()->nSamplesPerSec, 0)); - DebugOut("AudioRenderer drop", dropFrames, "frames for clock matching", m_sampleCorrection.GetLastSampleEnd() / 10000., (myTime - graphTime) / 10000.); + DebugOut("AudioRenderer drop", dropFrames, "frames for clock matching", m_sampleCorrection.GetLastFrameEnd() / 10000., (myTime - graphTime) / 10000.); } } @@ -617,7 +622,7 @@ namespace SaneAudioRenderer REFERENCE_TIME graphTime; if (m_state == State_Running && SUCCEEDED(m_graphClock->GetTime(&graphTime)) && - graphTime > m_startTime + m_sampleCorrection.GetLastSampleEnd() + m_sampleCorrection.GetTimingsError()) + graphTime > m_startTime + m_sampleCorrection.GetLastFrameEnd() + m_sampleCorrection.GetTimeDivergence()) { break; } diff --git a/src/SampleCorrection.cpp b/src/SampleCorrection.cpp index 9ca3387..30df66b 100644 --- a/src/SampleCorrection.cpp +++ b/src/SampleCorrection.cpp @@ -24,14 +24,12 @@ namespace SaneAudioRenderer m_rate = rate; - m_freshSegment = true; - m_segmentStartTimestamp = 0; m_segmentTimeInPreviousFormats = 0; m_segmentFramesInCurrentFormat = 0; - m_lastSampleEnd = 0; + m_lastFrameEnd = 0; - m_timingsError = 0; + m_timeDivergence = 0; } void SampleCorrection::NewDeviceBuffer() @@ -43,102 +41,71 @@ namespace SaneAudioRenderer { assert(m_format); - FillMissingTimings(sampleProps); + DspChunk chunk(pSample, sampleProps, *m_format); - DspChunk chunk; - - const bool drop = (m_bitstream && m_freshBuffer && !(sampleProps.dwSampleFlags & AM_SAMPLE_SPLICEPOINT)) || - (!m_bitstream && m_freshSegment && sampleProps.tStop <= 0); - - if (drop) + if (m_bitstream) { - // Drop the sample. - assert(chunk.IsEmpty()); - DebugOut("SampleCorrection drop [", sampleProps.tStart, sampleProps.tStop, "]"); - - if (m_bitstream && !m_freshSegment) + if (m_freshBuffer && !(sampleProps.dwSampleFlags & AM_SAMPLE_SPLICEPOINT)) { - assert(m_freshBuffer); - m_segmentFramesInCurrentFormat += sampleProps.lActual * 8 / m_format->wBitsPerSample / m_format->nChannels; + // Drop the sample. + DebugOut("SampleCorrection drop [", sampleProps.tStart, sampleProps.tStop, "]"); + chunk = DspChunk(); + assert(chunk.IsEmpty()); } } - else if (!m_bitstream && m_freshSegment && sampleProps.tStart < 0) + else if (m_lastFrameEnd == 0) { - // Crop the sample. - size_t cropFrames = (size_t)TimeToFrames(m_lastSampleEnd - sampleProps.tStart); - DebugOut("SampleCorrection crop", cropFrames, "frames from [", sampleProps.tStart, sampleProps.tStop, "]"); - - chunk = DspChunk(pSample, sampleProps, *m_format); - - if (cropFrames > 0) + if ((sampleProps.dwSampleFlags & AM_SAMPLE_STOPVALID) && sampleProps.tStop <= 0) { - assert(chunk.GetFrameCount() > cropFrames); - chunk.ShrinkHead(chunk.GetFrameCount() - cropFrames); + // Drop the sample. + DebugOut("SampleCorrection drop [", sampleProps.tStart, sampleProps.tStop, "]"); + chunk = DspChunk(); + assert(chunk.IsEmpty()); } - - AccumulateTimings(sampleProps, chunk.GetFrameCount()); - } - else if (!m_bitstream && m_freshSegment && sampleProps.tStart > 0) - { - // Zero-pad the sample. - size_t padFrames = (size_t)TimeToFrames(sampleProps.tStart - m_lastSampleEnd); - DebugOut("SampleCorrection pad", padFrames, "frames into [", sampleProps.tStart, sampleProps.tStop, "]"); - - if (padFrames > 0) + else if ((sampleProps.dwSampleFlags & AM_SAMPLE_TIMEVALID) && sampleProps.tStart < 0) { - DspChunk tempChunk(pSample, sampleProps, *m_format); - - size_t padBytes = padFrames * tempChunk.GetFrameSize(); - sampleProps.pbBuffer = nullptr; - sampleProps.lActual += (int32_t)padBytes; - sampleProps.tStart -= FramesToTime(padFrames); + // Crop the sample. + const size_t cropFrames = (size_t)TimeToFrames(0 - sampleProps.tStart); - AccumulateTimings(sampleProps, tempChunk.GetFrameCount() + padFrames); + if (cropFrames > 0) + { + DebugOut("SampleCorrection crop", cropFrames, "frames from [", + sampleProps.tStart, sampleProps.tStop, "]"); - chunk = DspChunk(tempChunk.GetFormat(), tempChunk.GetChannelCount(), - tempChunk.GetFrameCount() + padFrames, tempChunk.GetRate()); - - assert(chunk.GetSize() == tempChunk.GetSize() + padBytes); - ZeroMemory(chunk.GetData(), padBytes); - memcpy(chunk.GetData() + padBytes, tempChunk.GetData(), tempChunk.GetSize()); + chunk.ShrinkHead(chunk.GetFrameCount() > cropFrames ? chunk.GetFrameCount() - cropFrames : 0); + } } - else + else if ((sampleProps.dwSampleFlags & AM_SAMPLE_TIMEVALID) && sampleProps.tStart > 0) { - chunk = DspChunk(pSample, sampleProps, *m_format); - AccumulateTimings(sampleProps, chunk.GetFrameCount()); - } - } - else - { - // Leave the sample untouched. - chunk = DspChunk(pSample, sampleProps, *m_format); - AccumulateTimings(sampleProps, chunk.GetFrameCount()); - } + // Zero-pad the sample. + const size_t padFrames = (size_t)TimeToFrames(sampleProps.tStart - m_lastFrameEnd); - return chunk; - } + if (padFrames > 0 && + FramesToTime(padFrames) < 50 * OneMillisecond) + { + DebugOut("SampleCorrection pad", padFrames, "frames before [", + sampleProps.tStart, sampleProps.tStop, "]"); - void SampleCorrection::FillMissingTimings(AM_SAMPLE2_PROPERTIES& sampleProps) - { - assert(m_format); - assert(m_rate > 0.0); + DspChunk tempChunk(chunk.GetFormat(), chunk.GetChannelCount(), + chunk.GetFrameCount() + padFrames, chunk.GetRate()); - if (!(sampleProps.dwSampleFlags & AM_SAMPLE_TIMEVALID)) - { - REFERENCE_TIME time = m_segmentTimeInPreviousFormats + FramesToTime(m_segmentFramesInCurrentFormat); + const size_t padBytes = padFrames * chunk.GetFrameSize(); + sampleProps.pbBuffer = nullptr; + sampleProps.lActual += (int32_t)padBytes; + sampleProps.tStart -= FramesToTime(padFrames); + + assert(tempChunk.GetSize() == chunk.GetSize() + padBytes); + ZeroMemory(tempChunk.GetData(), padBytes); + memcpy(tempChunk.GetData() + padBytes, chunk.GetData(), chunk.GetSize()); - sampleProps.tStart = m_segmentStartTimestamp + time; - sampleProps.dwSampleFlags |= AM_SAMPLE_TIMEVALID; + chunk = std::move(tempChunk); + } + } } - if (!(sampleProps.dwSampleFlags & AM_SAMPLE_STOPVALID)) - { - REFERENCE_TIME time = sampleProps.lActual * 8 / m_format->wBitsPerSample / - m_format->nChannels * OneSecond / m_format->nSamplesPerSec; + AccumulateTimings(sampleProps, chunk.GetFrameCount()); - sampleProps.tStop = sampleProps.tStart + (REFERENCE_TIME)(time / m_rate); - sampleProps.dwSampleFlags |= AM_SAMPLE_STOPVALID; - } + return chunk; } uint64_t SampleCorrection::TimeToFrames(REFERENCE_TIME time) @@ -163,20 +130,13 @@ namespace SaneAudioRenderer if (frames == 0) return; - if (m_freshSegment) - { - assert(m_segmentStartTimestamp == 0); - m_segmentStartTimestamp = sampleProps.tStart; - m_freshSegment = false; - } - - m_lastSampleEnd = m_segmentTimeInPreviousFormats + FramesToTime(m_segmentFramesInCurrentFormat); - - m_timingsError = sampleProps.tStart - m_lastSampleEnd; + if (sampleProps.dwSampleFlags & AM_SAMPLE_TIMEVALID) + m_timeDivergence = sampleProps.tStart - m_lastFrameEnd; m_segmentFramesInCurrentFormat += frames; - if (m_freshBuffer) - m_freshBuffer = false; + m_lastFrameEnd = m_segmentTimeInPreviousFormats + FramesToTime(m_segmentFramesInCurrentFormat); + + m_freshBuffer = false; } } diff --git a/src/SampleCorrection.h b/src/SampleCorrection.h index 6fecf37..42f9432 100644 --- a/src/SampleCorrection.h +++ b/src/SampleCorrection.h @@ -16,8 +16,8 @@ namespace SaneAudioRenderer DspChunk ProcessSample(IMediaSample* pSample, AM_SAMPLE2_PROPERTIES& sampleProps); - REFERENCE_TIME GetLastSampleEnd() { return m_lastSampleEnd; } - REFERENCE_TIME GetTimingsError() { return m_timingsError; } + REFERENCE_TIME GetLastFrameEnd() const { return m_lastFrameEnd; } + REFERENCE_TIME GetTimeDivergence() const { return m_timeDivergence; } private: @@ -32,14 +32,12 @@ namespace SaneAudioRenderer double m_rate = 1.0; - bool m_freshSegment = true; - REFERENCE_TIME m_segmentStartTimestamp = 0; REFERENCE_TIME m_segmentTimeInPreviousFormats = 0; uint64_t m_segmentFramesInCurrentFormat = 0; - REFERENCE_TIME m_lastSampleEnd = 0; + REFERENCE_TIME m_lastFrameEnd = 0; - REFERENCE_TIME m_timingsError = 0; + REFERENCE_TIME m_timeDivergence = 0; bool m_freshBuffer = true; }; -- cgit v1.2.3