- Python main entry with CLI (--room-id, --voice, --speed) - blivedm integration for real-time danmaku receiving - Chinese-to-Japanese katakana conversion (pypinyin + mapping table) - Node.js persistent bridge for aquestalk.js TTS synthesis - Audio playback via sounddevice - Message queue for sequential TTS playback
47 lines
1.1 KiB
Python
47 lines
1.1 KiB
Python
"""Audio playback for WAV files."""
|
|
|
|
import asyncio
|
|
import io
|
|
import logging
|
|
|
|
import sounddevice as sd
|
|
import soundfile as sf
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def play_wav(wav_data: bytes) -> None:
|
|
"""Play WAV audio data synchronously (blocking).
|
|
|
|
Args:
|
|
wav_data: Raw WAV file bytes.
|
|
"""
|
|
data, samplerate = sf.read(io.BytesIO(wav_data), dtype="float32")
|
|
sd.play(data, samplerate)
|
|
sd.wait()
|
|
|
|
|
|
async def play_wav_async(wav_data: bytes) -> None:
|
|
"""Play WAV audio data asynchronously.
|
|
|
|
Args:
|
|
wav_data: Raw WAV file bytes.
|
|
"""
|
|
loop = asyncio.get_running_loop()
|
|
await loop.run_in_executor(None, play_wav, wav_data)
|
|
|
|
|
|
def get_output_devices() -> list[dict]:
|
|
"""List available audio output devices."""
|
|
devices = sd.query_devices()
|
|
result = []
|
|
for i, dev in enumerate(devices):
|
|
if dev["max_output_channels"] > 0:
|
|
result.append({
|
|
"index": i,
|
|
"name": dev["name"],
|
|
"channels": dev["max_output_channels"],
|
|
"default_samplerate": dev["default_samplerate"],
|
|
})
|
|
return result
|