#pragma once #include #include #include // Позиция PCM задаёт непрерывное время; backend уточняет привязку без скачков на каждом обновлении latency. // Единственный владелец — capture worker. Настоящий разрыв sequence/route/time вызывает reset у владельца. struct VoiceCaptureClock { bool initialized = false; uint64_t originSequence = 0, previousSequence = 0; int64_t originUs = 0, errorUs = 0, adjustmentUs = 0; double phaseUs = 0; void reset() { *this = {}; } int64_t map(uint64_t sequence, int64_t observedUs) { if (!initialized) { initialized = true; originSequence = previousSequence = sequence; originUs = observedUs; return observedUs; } const double nominal = (sequence - originSequence) * (1000000.0 / 48000.0); const double elapsed = (sequence - previousSequence) * (1000000.0 / 48000.0); const double error = double(observedUs - originUs) - nominal - phaseUs; // Постоянная времени 2с; предел 0.5% (не более 100мкс коррекции на 20мс PCM). const double adjustment = std::clamp(error * (1.0 - std::exp(-elapsed / 2000000.0)), -elapsed / 200, elapsed / 200); phaseUs += adjustment; previousSequence = sequence; errorUs = std::llround(error); adjustmentUs = std::llround(adjustment); return originUs + std::llround(nominal + phaseUs); } };