This repository has been archived on 2026-08-19. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
ROCm-Research-Archive/_TestScripts/Scripts and Tests/_speaker_patched.py
T
2026-08-20 00:45:43 +02:00

115 lines
3.7 KiB
Python

import time
import queue
import numpy as np
try:
import sounddevice as sd
except (ImportError, OSError):
sd = None
from ..schema.protocol import SpeakerRequest, SpeakerResponse
def handle_command(cmd: SpeakerRequest, state: dict):
if cmd is None or cmd.msg_type == "EXIT":
state["stop"] = True
return
if cmd.msg_type == "STOP":
state["current_data"] = np.zeros((0, 1), dtype=np.float32)
state["started"] = False
return
if cmd.msg_type == "PAUSE":
state["paused"] = True
return
if cmd.msg_type == "CONTINUE":
state["paused"] = False
return
if cmd.msg_type == "AUDIO":
if cmd.audio is not None and len(cmd.audio) > 0:
state["current_data"] = np.concatenate(
[state["current_data"], cmd.audio.reshape(-1, 1).astype(np.float32)],
axis=0
)
def sync_playback_status(state: dict, result_queue):
if result_queue is None: return
if state.get("paused", False):
target = "PAUSED"
elif state.get("started", False):
target = "PLAYING"
else:
target = "IDLE"
if target == state["playback_state"]:
return
msg_map = {"PAUSED": "PAUSED", "PLAYING": "STARTED", "IDLE": "FINISHED"}
result_queue.put(SpeakerResponse(msg_type=msg_map[target]))
state["playback_state"] = target
def fill_audio(outdata, frames, state: dict):
if state.get("paused", False):
outdata.fill(0)
return
if not state["started"]:
if len(state["current_data"]) >= state["threshold"]:
state["started"] = True
else:
outdata.fill(0)
return
avail = len(state["current_data"])
to_copy = min(avail, frames)
if to_copy > 0:
outdata[:to_copy] = state["current_data"][:to_copy]
state["current_data"] = state["current_data"][to_copy:]
if to_copy < frames:
outdata[to_copy:].fill(0)
state["started"] = False
def speaker_worker_proc(play_queue, result_queue=None, sample_rate=24000):
state = {
"current_data": np.zeros((0, 1), dtype=np.float32),
"started": False,
"threshold": 1200,
"stop": False,
"paused": False,
"playback_state": "IDLE"
}
def audio_callback(outdata, frames, time_info, status):
while True:
try:
command = play_queue.get_nowait()
handle_command(command, state)
except queue.Empty:
break
fill_audio(outdata, frames, state)
sync_playback_status(state, result_queue)
if sd is None:
# Headless mode: no audio device available
if result_queue:
result_queue.put(SpeakerResponse(msg_type="READY"))
while not state.get("stop"):
try:
command = play_queue.get(timeout=0.5)
handle_command(command, state)
except queue.Empty:
pass
return
try:
with sd.OutputStream(samplerate=sample_rate, channels=1, callback=audio_callback, blocksize=2048):
if result_queue:
result_queue.put(SpeakerResponse(msg_type="READY"))
while True:
time.sleep(0.2)
if state.get("stop"): break
except KeyboardInterrupt:
pass
except Exception as e:
print(f"⚠️ [SpeakerWorker] No audio device, running headless: {e}")
if result_queue:
result_queue.put(SpeakerResponse(msg_type="READY"))
while not state.get("stop"):
try:
command = play_queue.get(timeout=0.5)
handle_command(command, state)
except Exception:
pass