void DspLimiter::Process(DspChunk& chunk) { if (chunk.IsEmpty()) return; if (!m_exclusive || (chunk.GetFormat() != DspFormat::Float && chunk.GetFormat() != DspFormat::Double)) { m_active = false; return; } m_active = true; // Analyze samples float peak; if (chunk.GetFormat() == DspFormat::Double) { double largePeak = GetPeak((double*)chunk.GetData(), chunk.GetSampleCount()); peak = std::nexttoward((float)largePeak, largePeak); } else { assert(chunk.GetFormat() == DspFormat::Float); peak = GetPeak((float*)chunk.GetData(), chunk.GetSampleCount()); } // Configure limiter if (peak > 1.0f) { if (m_holdWindow <= 0) { NewTreshold(std::max(peak, 1.4f)); } else if (peak > m_peak) { NewTreshold(peak); } m_holdWindow = (int64_t)m_rate * m_channels * 10; // 10 seconds } // Apply limiter if (m_holdWindow > 0) { if (chunk.GetFormat() == DspFormat::Double) { ApplyLimiter<double>((double*)chunk.GetData(), chunk.GetSampleCount(), m_threshold); } else { assert(chunk.GetFormat() == DspFormat::Float); ApplyLimiter((float*)chunk.GetData(), chunk.GetSampleCount(), m_threshold); } m_holdWindow -= chunk.GetSampleCount(); } }
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); } } } }