app: wire audio pipeline and ptt
Start AudioPipeline on server connect, send loop polls capture queue, PTT toggles mic encoding, incoming sound queued for playback. Audio failure logs to chatlog without crashing.
This commit is contained in:
@@ -4,6 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import html
|
||||
import logging
|
||||
import time
|
||||
|
||||
from textual import events, on, work
|
||||
from textual.app import App, ComposeResult
|
||||
@@ -12,6 +13,7 @@ from textual.message import Message
|
||||
from textual.reactive import reactive
|
||||
from textual.widgets import Footer, Header, Input, RichLog, Static
|
||||
|
||||
from tuimble.audio import AudioPipeline
|
||||
from tuimble.client import Channel, MumbleClient
|
||||
from tuimble.config import Config, load_config
|
||||
from tuimble.ptt import KittyPtt, TogglePtt, detect_backend
|
||||
@@ -210,6 +212,13 @@ class TuimbleApp(App):
|
||||
username=srv.username,
|
||||
password=srv.password,
|
||||
)
|
||||
acfg = self._config.audio
|
||||
self._audio = AudioPipeline(
|
||||
sample_rate=acfg.sample_rate,
|
||||
frame_size=acfg.frame_size,
|
||||
input_device=acfg.input_device,
|
||||
output_device=acfg.output_device,
|
||||
)
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Header()
|
||||
@@ -238,6 +247,7 @@ class TuimbleApp(App):
|
||||
self._client.on_text_message = self._cb_text_message
|
||||
self._client.on_user_update = self._cb_state_changed
|
||||
self._client.on_channel_update = self._cb_state_changed
|
||||
self._client.on_sound_received = self._cb_sound_received
|
||||
|
||||
try:
|
||||
self._client.connect()
|
||||
@@ -256,6 +266,9 @@ class TuimbleApp(App):
|
||||
def _cb_state_changed(self) -> None:
|
||||
self.post_message(ServerStateChanged())
|
||||
|
||||
def _cb_sound_received(self, _user, pcm_data: bytes) -> None:
|
||||
self._audio.queue_playback(pcm_data)
|
||||
|
||||
def _show_error(self, text: str) -> None:
|
||||
chatlog = self.query_one("#chatlog", ChatLog)
|
||||
chatlog.write(f"[#f7768e]\u2717 {text}[/]")
|
||||
@@ -273,6 +286,7 @@ class TuimbleApp(App):
|
||||
f"[#9ece6a]\u2713 connected as {self._config.server.username}[/]"
|
||||
)
|
||||
self._refresh_channel_tree()
|
||||
self._start_audio()
|
||||
|
||||
def on_server_disconnected(self, _msg: ServerDisconnected) -> None:
|
||||
status = self.query_one("#status", StatusBar)
|
||||
@@ -311,6 +325,29 @@ class TuimbleApp(App):
|
||||
f"[#e0af68]{self._config.server.username}[/] {text}"
|
||||
)
|
||||
|
||||
# -- audio ---------------------------------------------------------------
|
||||
|
||||
def _start_audio(self) -> None:
|
||||
"""Start audio pipeline; log error if hardware unavailable."""
|
||||
chatlog = self.query_one("#chatlog", ChatLog)
|
||||
try:
|
||||
self._audio.start()
|
||||
chatlog.write("[dim]audio pipeline started[/dim]")
|
||||
self._audio_send_loop()
|
||||
except Exception as exc:
|
||||
chatlog.write(f"[#f7768e]\u2717 audio: {exc}[/]")
|
||||
log.warning("audio start failed: %s", exc)
|
||||
|
||||
@work(thread=True)
|
||||
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()
|
||||
if frame is not None:
|
||||
self._client.send_audio(frame)
|
||||
else:
|
||||
time.sleep(0.005)
|
||||
|
||||
# -- channel tree --------------------------------------------------------
|
||||
|
||||
def _refresh_channel_tree(self) -> None:
|
||||
@@ -344,12 +381,14 @@ class TuimbleApp(App):
|
||||
self._ptt.toggle()
|
||||
|
||||
def _on_ptt_change(self, transmitting: bool) -> None:
|
||||
self._audio.capturing = transmitting
|
||||
status = self.query_one("#status", StatusBar)
|
||||
status.ptt_active = transmitting
|
||||
|
||||
# -- lifecycle -----------------------------------------------------------
|
||||
|
||||
def action_quit(self) -> None:
|
||||
self._audio.stop()
|
||||
self._client.set_dispatcher(None)
|
||||
self._client.disconnect()
|
||||
self.exit()
|
||||
|
||||
Reference in New Issue
Block a user