audio: remove opus codec, pass raw pcm to pymumble

pymumble handles opus encode/decode internally. The pipeline
is now pure PCM on both capture and playback paths.
This commit is contained in:
Username
2026-02-24 12:23:34 +01:00
parent 657ea6c255
commit c4f60df224
3 changed files with 12 additions and 19 deletions

View File

@@ -342,7 +342,7 @@ class TuimbleApp(App):
def _audio_send_loop(self) -> None:
"""Poll capture queue and send encoded frames to server."""
while self._client.connected:
frame = self._audio.get_encoded_frame()
frame = self._audio.get_capture_frame()
if frame is not None:
self._client.send_audio(frame)
else:

View File

@@ -1,8 +1,8 @@
"""Audio capture and playback pipeline.
Handles microphone input (PCM -> Opus) and speaker output (PCM direct).
pymumble already decodes incoming audio, so the playback path accepts raw
PCM — no decoder needed. The encoder is used only for the capture path.
Pure PCM — pymumble handles Opus encode/decode internally.
Capture path: mic -> raw PCM -> queue -> pymumble (encodes + sends).
Playback path: pymumble (decodes) -> raw PCM -> queue -> speakers.
"""
from __future__ import annotations
@@ -36,20 +36,14 @@ class AudioPipeline:
self._capture_queue: queue.Queue[bytes] = queue.Queue(maxsize=50)
self._playback_queue: queue.Queue[bytes] = queue.Queue(maxsize=50)
self._encoder = None
self._input_stream = None
self._output_stream = None
self._capturing = False
def start(self):
"""Initialize codec and open audio streams."""
import opuslib
"""Open audio streams."""
import sounddevice as sd
self._encoder = opuslib.Encoder(
self._sample_rate, CHANNELS, opuslib.APPLICATION_VOIP
)
self._output_stream = sd.RawOutputStream(
samplerate=self._sample_rate,
channels=CHANNELS,
@@ -94,10 +88,9 @@ class AudioPipeline:
"""Called by sounddevice when input data is available."""
if status:
log.warning("capture status: %s", status)
if self._capturing and self._encoder:
if self._capturing:
try:
encoded = self._encoder.encode(bytes(indata), self._frame_size)
self._capture_queue.put_nowait(encoded)
self._capture_queue.put_nowait(bytes(indata))
except queue.Full:
pass
@@ -114,8 +107,8 @@ class AudioPipeline:
except queue.Empty:
outdata[:] = b"\x00" * len(outdata)
def get_encoded_frame(self) -> bytes | None:
"""Retrieve next encoded frame for transmission."""
def get_capture_frame(self) -> bytes | None:
"""Retrieve next captured PCM frame for transmission."""
try:
return self._capture_queue.get_nowait()
except queue.Empty:

View File

@@ -170,10 +170,10 @@ class MumbleClient:
ch = self._mumble.channels[self._mumble.users.myself["channel_id"]]
ch.send_text_message(message)
def send_audio(self, opus_data: bytes):
"""Send encoded audio to the server."""
def send_audio(self, pcm_data: bytes):
"""Send PCM audio to the server (pymumble encodes to Opus)."""
if self._mumble and self._connected:
self._mumble.sound_output.add_sound(opus_data)
self._mumble.sound_output.add_sound(pcm_data)
def join_channel(self, channel_id: int):
"""Move to a different channel."""