mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-29 18:46:59 +00:00
fix(voice): add WSL audio warmup to eliminate RDP crackling
WSLg RDP audio has two issues causing crackling: 1. systemd-timesyncd clock adjustments jitter PulseAudio timing (microsoft/wslg#1257) — user action: stop the service 2. Cold-start RDP connection drops first ~100ms of audio packets before the virtual channel stabilises Fix (automated, WSL-only): - Detect WSL via /proc/version 'microsoft' marker - Prepend 100ms silence + apply 100ms fade-in to audio - Append 50ms silence tail for clean stream teardown - Set blocksize=4096 (default auto ~1024 is too small for RDP) - All in a single continuous sd.play() buffer Non-WSL paths unchanged. Closes #38893
This commit is contained in:
parent
ef686c3878
commit
ec7a46a6fe
1 changed files with 31 additions and 1 deletions
|
|
@ -1178,6 +1178,16 @@ def stop_playback() -> None:
|
|||
pass
|
||||
|
||||
|
||||
def _is_wsl() -> bool:
|
||||
"""True when running inside Windows Subsystem for Linux."""
|
||||
try:
|
||||
with open("/proc/version", "r") as f:
|
||||
return "microsoft" in f.read().lower()
|
||||
except Exception:
|
||||
return False
|
||||
return False
|
||||
|
||||
|
||||
def play_audio_file(file_path: str) -> bool:
|
||||
"""Play an audio file through the default output device.
|
||||
|
||||
|
|
@ -1206,7 +1216,27 @@ def play_audio_file(file_path: str) -> bool:
|
|||
audio_data = np.frombuffer(frames, dtype=np.int16)
|
||||
sample_rate = wf.getframerate()
|
||||
|
||||
sd.play(audio_data, samplerate=sample_rate)
|
||||
# WSLg RDP audio needs a warmup to avoid crackling at the start.
|
||||
# The RDP virtual-channel connection takes ~100 ms to stabilise,
|
||||
# and small default blocksize exasperates timing jitter caused by
|
||||
# systemd-timesyncd clock adjustments (microsoft/wslg#1257).
|
||||
if _is_wsl():
|
||||
silence_samples = int(0.1 * sample_rate)
|
||||
fade_samples = int(0.1 * sample_rate)
|
||||
fade = np.linspace(0.0, 1.0, fade_samples, dtype=np.float64)
|
||||
audio_float = audio_data.astype(np.float64)
|
||||
audio_float[:fade_samples] *= fade
|
||||
tail = np.zeros(int(0.05 * sample_rate), dtype=np.int16)
|
||||
audio_data = np.concatenate([
|
||||
np.zeros(silence_samples, dtype=np.int16),
|
||||
audio_float.astype(np.int16),
|
||||
tail,
|
||||
])
|
||||
blocksize = 4096
|
||||
else:
|
||||
blocksize = 0 # default (auto)
|
||||
|
||||
sd.play(audio_data, samplerate=sample_rate, blocksize=blocksize)
|
||||
# sd.wait() calls Event.wait() without timeout — hangs forever if
|
||||
# the audio device stalls. Poll with a ceiling and force-stop.
|
||||
duration_secs = len(audio_data) / sample_rate
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue