32 lines
1.6 KiB
C++
32 lines
1.6 KiB
C++
#pragma once
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <cstdint>
|
|
|
|
// Позиция 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);
|
|
}
|
|
};
|