audio: add DeviceMonitor for device list polling

This commit is contained in:
Username
2026-02-24 23:50:23 +01:00
parent df6f2ff354
commit 9e6c11e588

View File

@@ -10,6 +10,8 @@ from __future__ import annotations
import array import array
import logging import logging
import queue import queue
import threading
from typing import Callable
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@@ -30,6 +32,60 @@ def _apply_gain(pcm: bytes, gain: float) -> bytes:
return samples.tobytes() return samples.tobytes()
class DeviceMonitor:
"""Poll sounddevice for device list changes.
Runs a daemon thread that compares a string fingerprint of
``sounddevice.query_devices()`` against a cached snapshot every
*interval* seconds. Fires *callback* when the list changes.
Uses ``threading.Event`` for cancellation (same pattern as
``ReconnectManager``).
"""
def __init__(self, callback: Callable[[], None], interval: float = 2.0):
self._callback = callback
self._interval = interval
self._cancel = threading.Event()
self._snapshot = self._device_fingerprint()
self._thread: threading.Thread | None = None
@staticmethod
def _device_fingerprint() -> str:
"""Return a string snapshot of the current device list."""
try:
import sounddevice as sd
return str(sd.query_devices())
except Exception:
return ""
def start(self) -> None:
"""Start polling in a daemon thread."""
if self._thread is not None and self._thread.is_alive():
return
self._cancel.clear()
self._snapshot = self._device_fingerprint()
self._thread = threading.Thread(target=self._poll, daemon=True)
self._thread.start()
def stop(self) -> None:
"""Signal the poll loop to exit."""
self._cancel.set()
self._thread = None
def _poll(self) -> None:
while not self._cancel.wait(timeout=self._interval):
current = self._device_fingerprint()
if current != self._snapshot:
self._snapshot = current
log.info("audio device list changed")
try:
self._callback()
except Exception:
log.exception("device change callback failed")
class AudioPipeline: class AudioPipeline:
"""Manages audio input/output streams and Opus codec.""" """Manages audio input/output streams and Opus codec."""