|
|
@@ -151,63 +151,54 @@ _playback_status: dict = {
|
|
|
async def _stream_file(filepath: Path, speed: float) -> None:
|
|
|
global _playback_status
|
|
|
try:
|
|
|
- import miniaudio # type: ignore
|
|
|
- except ImportError:
|
|
|
- _playback_status.update({
|
|
|
- "state": "error",
|
|
|
- "error": "miniaudio not installed — run: pip install miniaudio",
|
|
|
- })
|
|
|
+ import miniaudio
|
|
|
+ import bridge # import the running bridge module
|
|
|
+ except ImportError as e:
|
|
|
+ _playback_status.update({"state": "error", "error": str(e)})
|
|
|
return
|
|
|
|
|
|
try:
|
|
|
_playback_status["state"] = "loading"
|
|
|
-
|
|
|
- duration = 0.0
|
|
|
- try:
|
|
|
- info = miniaudio.get_file_info(str(filepath))
|
|
|
- duration = info.duration
|
|
|
- except Exception:
|
|
|
- pass
|
|
|
-
|
|
|
+ info = miniaudio.get_file_info(str(filepath))
|
|
|
+ duration = info.duration
|
|
|
_playback_status.update({
|
|
|
- "state": "playing",
|
|
|
- "duration": round(duration, 1),
|
|
|
- "elapsed": 0.0,
|
|
|
- "progress": 0,
|
|
|
+ "state": "playing", "duration": round(duration, 1),
|
|
|
+ "elapsed": 0.0, "progress": 0,
|
|
|
})
|
|
|
|
|
|
chunk_frames = 4096
|
|
|
chunk_secs = chunk_frames / 16000
|
|
|
elapsed = 0.0
|
|
|
|
|
|
- async with websockets.connect(WS_URL, max_size=2**23) as ws:
|
|
|
- stream = miniaudio.stream_file(
|
|
|
- str(filepath),
|
|
|
- output_format=miniaudio.SampleFormat.SIGNED16,
|
|
|
- nchannels=1,
|
|
|
- sample_rate=16000,
|
|
|
- frames_to_read=chunk_frames,
|
|
|
+ stream = miniaudio.stream_file(
|
|
|
+ str(filepath),
|
|
|
+ output_format=miniaudio.SampleFormat.FLOAT32, # match bridge dtype
|
|
|
+ nchannels=1,
|
|
|
+ sample_rate=16000,
|
|
|
+ frames_to_read=chunk_frames,
|
|
|
+ )
|
|
|
+
|
|
|
+ for chunk in stream:
|
|
|
+ # Wait until bridge has initialised its injection queue
|
|
|
+ while bridge.test_audio_queue is None:
|
|
|
+ await asyncio.sleep(0.1)
|
|
|
+
|
|
|
+ chunk_bytes = bytes(chunk)
|
|
|
+ await bridge.test_audio_queue.put(chunk_bytes)
|
|
|
+ elapsed += chunk_secs
|
|
|
+ _playback_status["elapsed"] = round(elapsed, 1)
|
|
|
+ _playback_status["progress"] = (
|
|
|
+ min(99, round(elapsed / duration * 100)) if duration else 0
|
|
|
)
|
|
|
- for chunk in stream:
|
|
|
- await ws.send(bytes(chunk))
|
|
|
- elapsed += chunk_secs
|
|
|
- _playback_status["elapsed"] = round(elapsed, 1)
|
|
|
- _playback_status["progress"] = (
|
|
|
- min(99, round(elapsed / duration * 100)) if duration else 0
|
|
|
- )
|
|
|
- await asyncio.sleep(chunk_secs / speed)
|
|
|
+ await asyncio.sleep(chunk_secs / speed)
|
|
|
|
|
|
_playback_status.update({
|
|
|
- "state": "done",
|
|
|
- "progress": 100,
|
|
|
- "elapsed": round(duration, 1),
|
|
|
+ "state": "done", "progress": 100, "elapsed": round(duration, 1),
|
|
|
})
|
|
|
-
|
|
|
except asyncio.CancelledError:
|
|
|
_playback_status.update({
|
|
|
"state": "idle", "file": None, "progress": 0, "elapsed": 0.0,
|
|
|
})
|
|
|
-
|
|
|
except Exception as exc:
|
|
|
_playback_status.update({"state": "error", "error": str(exc), "progress": 0})
|
|
|
print(f"[Playback] {exc}")
|