refactor: simplify clock drift compensation with hardcoded parameters and improved frame drop logic
This commit is contained in:
@@ -42,7 +42,6 @@ class AuracastGlobalConfig(BaseModel):
|
|||||||
assisted_listening_stream: bool = False
|
assisted_listening_stream: bool = False
|
||||||
# Clock drift compensation: discard excess samples when buffer exceeds threshold
|
# Clock drift compensation: discard excess samples when buffer exceeds threshold
|
||||||
enable_drift_compensation: bool = False
|
enable_drift_compensation: bool = False
|
||||||
drift_threshold_ms: float = 2.0 # Discard threshold in milliseconds
|
|
||||||
|
|
||||||
# "Audio input. "
|
# "Audio input. "
|
||||||
# "'device' -> use the host's default sound input device, "
|
# "'device' -> use the host's default sound input device, "
|
||||||
|
|||||||
@@ -651,19 +651,22 @@ class Streamer():
|
|||||||
bigs = self.bigs
|
bigs = self.bigs
|
||||||
self.is_streaming = True
|
self.is_streaming = True
|
||||||
|
|
||||||
# Sample discard stats (clock drift compensation)
|
# frame drop algo parameters
|
||||||
sample_rate = big['audio_input']._pcm_format.sample_rate
|
sample_rate = big['audio_input']._pcm_format.sample_rate
|
||||||
samples_discarded_total = 0 # Total samples discarded
|
samples_discarded_total = 0 # Total samples discarded
|
||||||
discard_events = 0 # Number of times we discarded samples
|
discard_events = 0 # Number of times we discarded samples
|
||||||
frames_since_last_discard = 999 # Guard: frames since last discard (start high to allow first drop)
|
frames_since_last_discard = 999 # Guard: frames since last discard (start high to allow first drop)
|
||||||
enable_drift_compensation = global_config.enable_drift_compensation
|
enable_drift_compensation = global_config.enable_drift_compensation
|
||||||
discard_guard_frames = sample_rate // 100 # Don't allow discard within this many frames of previous discard
|
# Hardcoded parameters (unit: milliseconds)
|
||||||
# Calculate threshold based on config (default 2ms)
|
drift_threshold_ms = 2.0 if enable_drift_compensation else 0.0
|
||||||
drift_threshold_ms = global_config.drift_threshold_ms if global_config.enable_drift_compensation else 0
|
static_drop_ms = 0.5 if enable_drift_compensation else 0.0
|
||||||
|
# Guard interval measured in LC3 frames (10 ms each); 50 => 500 ms cooldown
|
||||||
|
discard_guard_frames = int(2*sample_rate / 1000) if enable_drift_compensation else 0
|
||||||
|
# Derived sample counts
|
||||||
drop_threshold_samples = int(sample_rate * drift_threshold_ms / 1000.0)
|
drop_threshold_samples = int(sample_rate * drift_threshold_ms / 1000.0)
|
||||||
static_drop_samples = int(sample_rate * 0.0005) # Always drop a static amount of samples
|
static_drop_samples = int(sample_rate * static_drop_ms / 1000.0)
|
||||||
|
|
||||||
if global_config.enable_drift_compensation:
|
if enable_drift_compensation:
|
||||||
logging.info(f"Clock drift compensation ENABLED: threshold={drift_threshold_ms}ms, guard={discard_guard_frames} frames")
|
logging.info(f"Clock drift compensation ENABLED: threshold={drift_threshold_ms}ms, guard={discard_guard_frames} frames")
|
||||||
else:
|
else:
|
||||||
logging.info("Clock drift compensation DISABLED")
|
logging.info("Clock drift compensation DISABLED")
|
||||||
@@ -699,12 +702,6 @@ class Streamer():
|
|||||||
stream_finished[i] = True
|
stream_finished[i] = True
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Calculate threshold samples based on sample rate (only once per BIG)
|
|
||||||
if enable_drift_compensation and drop_threshold_samples == 0:
|
|
||||||
drop_threshold_samples = int(sample_rate * drift_threshold_ms / 1000.0)
|
|
||||||
logging.info(f"Drift compensation threshold: {drop_threshold_samples} samples ({drift_threshold_ms}ms @ {sample_rate}Hz)")
|
|
||||||
logging.info(f"Static drop amount: {static_drop_samples} samples (3.0ms @ {sample_rate}Hz)")
|
|
||||||
|
|
||||||
# Discard excess samples in buffer if above threshold (clock drift compensation)
|
# Discard excess samples in buffer if above threshold (clock drift compensation)
|
||||||
if enable_drift_compensation and hasattr(big['audio_input'], '_stream') and big['audio_input']._stream:
|
if enable_drift_compensation and hasattr(big['audio_input'], '_stream') and big['audio_input']._stream:
|
||||||
sd_buffer_samples = big['audio_input']._stream.read_available
|
sd_buffer_samples = big['audio_input']._stream.read_available
|
||||||
@@ -713,7 +710,7 @@ class Streamer():
|
|||||||
if sd_buffer_samples > drop_threshold_samples and frames_since_last_discard >= discard_guard_frames:
|
if sd_buffer_samples > drop_threshold_samples and frames_since_last_discard >= discard_guard_frames:
|
||||||
# Always drop a static amount (3ms) for predictable behavior
|
# Always drop a static amount (3ms) for predictable behavior
|
||||||
# This matches the crossfade duration better for smoother transitions
|
# This matches the crossfade duration better for smoother transitions
|
||||||
samples_to_drop = static_drop_samples
|
samples_to_drop = min(static_drop_samples, max(1, big['lc3_frame_samples'] - 1))
|
||||||
try:
|
try:
|
||||||
discarded_data = await anext(big['audio_input'].frames(samples_to_drop))
|
discarded_data = await anext(big['audio_input'].frames(samples_to_drop))
|
||||||
samples_discarded_total += samples_to_drop
|
samples_discarded_total += samples_to_drop
|
||||||
|
|||||||
@@ -111,7 +111,6 @@ if __name__ == "__main__":
|
|||||||
octets_per_frame = OCTETS_PER_FRAME,
|
octets_per_frame = OCTETS_PER_FRAME,
|
||||||
transport=TRANSPORT1,
|
transport=TRANSPORT1,
|
||||||
enable_drift_compensation=True,
|
enable_drift_compensation=True,
|
||||||
drift_threshold_ms=2.0
|
|
||||||
)
|
)
|
||||||
config.debug = False
|
config.debug = False
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user