HRESULT MyClock::GetAudioClockTime(REFERENCE_TIME* pAudioTime, REFERENCE_TIME* pCounterTime) { CheckPointer(pAudioTime, E_POINTER); CAutoLock lock(this); if (m_audioClock) { uint64_t audioFrequency, audioPosition, audioTime; if (SUCCEEDED(m_audioClock->GetFrequency(&audioFrequency)) && SUCCEEDED(m_audioClock->GetPosition(&audioPosition, &audioTime))) { int64_t counterTime = llMulDiv(GetPerformanceCounter(), OneSecond, m_performanceFrequency, 0); int64_t clockTime = llMulDiv(audioPosition, OneSecond, audioFrequency, 0) + m_audioStart + (audioPosition > 0 ? m_audioOffset + counterTime - audioTime : 0); *pAudioTime = clockTime; if (pCounterTime) *pCounterTime = counterTime; return S_OK; } } return E_FAIL; }
int64_t AudioDevicePush::GetPosition() { UINT64 deviceClockFrequency, deviceClockPosition; ThrowIfFailed(m_backend->audioClock->GetFrequency(&deviceClockFrequency)); ThrowIfFailed(m_backend->audioClock->GetPosition(&deviceClockPosition, nullptr)); return llMulDiv(deviceClockPosition, OneSecond, deviceClockFrequency, 0); }
void DspRate::Process(DspChunk& chunk) { soxr_t soxr = GetBackend(); if (!soxr || chunk.IsEmpty()) return; if (m_state == State::Variable && !m_inStateTransition && m_variableDelay > 0) { uint64_t inputPosition = llMulDiv(m_variableOutputFrames, m_inputRate, m_outputRate, 0); int64_t adjustedFrames = inputPosition + m_variableDelay - m_variableInputFrames; REFERENCE_TIME adjustTime = m_adjustTime - FramesToTimeLong(adjustedFrames, m_inputRate); double ratio = (double)m_inputRate * 4 / (m_outputRate * (4 + (double)adjustTime / OneSecond)); // TODO: decrease jitter soxr_set_io_ratio(m_soxrv, ratio, m_outputRate / 1000); } DspChunk output = ProcessChunk(soxr, chunk); if (m_state == State::Variable) { m_variableInputFrames += chunk.GetFrameCount(); m_variableOutputFrames += output.GetFrameCount(); // soxr_delay() method is not implemented for variable rate conversion yet, // but the delay stays more or less constant and we can calculate it in a roundabout way. if (m_variableDelay == 0 && m_variableOutputFrames > 0) { uint64_t inputPosition = llMulDiv(m_variableOutputFrames, m_inputRate, m_outputRate, 0); m_variableDelay = m_variableInputFrames - inputPosition; } } FinishStateTransition(output, chunk, false); chunk = std::move(output); }
REFERENCE_TIME MyClock::GetPrivateTime() { CAutoLock lock(this); REFERENCE_TIME audioClockTime, audioClockCounterTime; if (SUCCEEDED(GetAudioClockTime(&audioClockTime, &audioClockCounterTime))) { m_counterOffset = audioClockTime - audioClockCounterTime; return audioClockTime; } return m_counterOffset + llMulDiv(GetPerformanceCounter(), OneSecond, m_performanceFrequency, 0); }
LONGLONG CRenderersData::GetPerfCounter() const { LARGE_INTEGER i64Ticks100ns; LARGE_INTEGER llPerfFrequency; QueryPerformanceFrequency(&llPerfFrequency); if (llPerfFrequency.QuadPart != 0) { QueryPerformanceCounter(&i64Ticks100ns); return llMulDiv(i64Ticks100ns.QuadPart, 10000000, llPerfFrequency.QuadPart, 0); } else { // ms to 100ns units return LONGLONG(timeGetTime()) * 10000; } }
REFERENCE_TIME MyTestClock::GetPrivateTime() { return llMulDiv(GetPerformanceCounter(), OneSecond, m_performanceFrequency, 0); }
void AudioRenderer::ApplyRateCorrection(DspChunk& chunk) { CAutoLock objectLock(this); assert(m_device); assert(!m_device->IsBitstream()); assert(m_state == State_Running); if (chunk.IsEmpty()) return; const REFERENCE_TIME latency = llMulDiv(chunk.GetFrameCount(), OneSecond, chunk.GetRate(), 0) + m_device->GetStreamLatency() + OneMillisecond * 10; const REFERENCE_TIME remaining = m_device->GetEnd() - m_device->GetPosition(); REFERENCE_TIME deltaTime = 0; if (m_live) { // Rate matching. if (remaining > latency) { size_t dropFrames = (size_t)llMulDiv(m_device->GetWaveFormat()->nSamplesPerSec, remaining - latency, OneSecond, 0); dropFrames = std::min(dropFrames, chunk.GetFrameCount()); chunk.ShrinkHead(chunk.GetFrameCount() - dropFrames); DebugOut("AudioRenderer drop", dropFrames, "frames for rate matching"); } } else { // Clock matching. assert(m_externalClock); REFERENCE_TIME graphTime, myTime, myStartTime; if (SUCCEEDED(m_myClock.GetAudioClockStartTime(&myStartTime)) && SUCCEEDED(m_myClock.GetAudioClockTime(&myTime, nullptr)) && SUCCEEDED(m_graphClock->GetTime(&graphTime)) && myTime > myStartTime) { myTime -= m_device->GetSilence(); if (myTime > graphTime) { // Pad and adjust backwards. REFERENCE_TIME padTime = myTime - graphTime; assert(padTime >= 0); size_t padFrames = (size_t)llMulDiv(m_device->GetWaveFormat()->nSamplesPerSec, padTime, OneSecond, 0); if (padFrames > m_device->GetWaveFormat()->nSamplesPerSec / 33) // ~30ms threshold { DspChunk tempChunk(chunk.GetFormat(), chunk.GetChannelCount(), chunk.GetFrameCount() + padFrames, chunk.GetRate()); size_t padBytes = tempChunk.GetFrameSize() * padFrames; ZeroMemory(tempChunk.GetData(), padBytes); memcpy(tempChunk.GetData() + padBytes, chunk.GetData(), chunk.GetSize()); chunk = std::move(tempChunk); REFERENCE_TIME paddedTime = llMulDiv(padFrames, OneSecond, m_device->GetWaveFormat()->nSamplesPerSec, 0); m_myClock.OffsetSlavedClock(-paddedTime); padTime -= paddedTime; assert(padTime >= 0); DebugOut("AudioRenderer pad", paddedTime / 10000., "ms for clock matching at", m_sampleCorrection.GetLastFrameEnd() / 10000., "frame position"); } // Correct the rest with variable rate. m_dspRealtimeRate.Adjust(padTime); m_myClock.OffsetSlavedClock(-padTime); } else if (remaining > latency) { // Crop and adjust forwards. assert(myTime <= graphTime); REFERENCE_TIME dropTime = std::min(graphTime - myTime, remaining - latency); assert(dropTime >= 0); size_t dropFrames = (size_t)llMulDiv(m_device->GetWaveFormat()->nSamplesPerSec, dropTime, OneSecond, 0); dropFrames = std::min(dropFrames, chunk.GetFrameCount()); if (dropFrames > m_device->GetWaveFormat()->nSamplesPerSec / 33) // ~30ms threshold { chunk.ShrinkHead(chunk.GetFrameCount() - dropFrames); REFERENCE_TIME droppedTime = llMulDiv(dropFrames, OneSecond, m_device->GetWaveFormat()->nSamplesPerSec, 0); m_myClock.OffsetSlavedClock(droppedTime); dropTime -= droppedTime; assert(dropTime >= 0); DebugOut("AudioRenderer drop", droppedTime / 10000., "ms for clock matching at", m_sampleCorrection.GetLastFrameEnd() / 10000., "frame position"); } // Correct the rest with variable rate. m_dspRealtimeRate.Adjust(-dropTime); m_myClock.OffsetSlavedClock(dropTime); } } } }