You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
31 lines
1.6 KiB
31 lines
1.6 KiB
#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); |
|
} |
|
};
|
|
|