audio: fix playback path to accept raw pcm

pymumble already decodes incoming audio. Remove the decoder from
the playback callback — write PCM directly to the output buffer.
This commit is contained in:
Username
2026-02-24 12:10:44 +01:00
parent 6efa9591dc
commit 0aa7b81439

View File

@@ -1,7 +1,8 @@
"""Audio capture and playback pipeline. """Audio capture and playback pipeline.
Handles microphone input, speaker output, and Opus encoding/decoding. Handles microphone input (PCM -> Opus) and speaker output (PCM direct).
Designed to run alongside the async event loop via callback-based I/O. pymumble already decodes incoming audio, so the playback path accepts raw
PCM — no decoder needed. The encoder is used only for the capture path.
""" """
from __future__ import annotations from __future__ import annotations
@@ -36,7 +37,6 @@ class AudioPipeline:
self._playback_queue: queue.Queue[bytes] = queue.Queue(maxsize=50) self._playback_queue: queue.Queue[bytes] = queue.Queue(maxsize=50)
self._encoder = None self._encoder = None
self._decoder = None
self._input_stream = None self._input_stream = None
self._output_stream = None self._output_stream = None
self._capturing = False self._capturing = False
@@ -49,7 +49,6 @@ class AudioPipeline:
self._encoder = opuslib.Encoder( self._encoder = opuslib.Encoder(
self._sample_rate, CHANNELS, opuslib.APPLICATION_VOIP self._sample_rate, CHANNELS, opuslib.APPLICATION_VOIP
) )
self._decoder = opuslib.Decoder(self._sample_rate, CHANNELS)
self._output_stream = sd.RawOutputStream( self._output_stream = sd.RawOutputStream(
samplerate=self._sample_rate, samplerate=self._sample_rate,
@@ -107,12 +106,11 @@ class AudioPipeline:
if status: if status:
log.warning("playback status: %s", status) log.warning("playback status: %s", status)
try: try:
data = self._playback_queue.get_nowait() pcm = self._playback_queue.get_nowait()
if self._decoder: n = min(len(pcm), len(outdata))
pcm = self._decoder.decode(data, self._frame_size) outdata[:n] = pcm[:n]
outdata[:] = pcm[: len(outdata)] if n < len(outdata):
else: outdata[n:] = b"\x00" * (len(outdata) - n)
outdata[:] = b"\x00" * len(outdata)
except queue.Empty: except queue.Empty:
outdata[:] = b"\x00" * len(outdata) outdata[:] = b"\x00" * len(outdata)
@@ -123,9 +121,9 @@ class AudioPipeline:
except queue.Empty: except queue.Empty:
return None return None
def queue_playback(self, opus_data: bytes): def queue_playback(self, pcm_data: bytes):
"""Queue encoded audio data for playback.""" """Queue raw PCM data for playback (16-bit, mono, 48kHz)."""
try: try:
self._playback_queue.put_nowait(opus_data) self._playback_queue.put_nowait(pcm_data)
except queue.Full: except queue.Full:
pass pass