106 lines
2.7 KiB
Plaintext
106 lines
2.7 KiB
Plaintext
import asyncio
|
|
import os
|
|
import subprocess
|
|
import tempfile
|
|
import time
|
|
import wave
|
|
|
|
from wyoming.audio import AudioChunk, AudioStart, AudioStop
|
|
from wyoming.client import AsyncTcpClient
|
|
from wyoming.tts import Synthesize
|
|
|
|
|
|
PIPER_HOST = os.getenv("PIPER_HOST", "127.0.0.1")
|
|
PIPER_PORT = int(os.getenv("PIPER_PORT", "10200"))
|
|
AUDIO_DEVICE = os.getenv("AUDIO_OUTPUT_DEVICE", "plughw:1,0")
|
|
|
|
|
|
async def synthesize(text, wav_path):
|
|
client = AsyncTcpClient(PIPER_HOST, PIPER_PORT)
|
|
|
|
await client.connect()
|
|
|
|
try:
|
|
await client.write_event(
|
|
Synthesize(text=text).event()
|
|
)
|
|
|
|
wav_file = None
|
|
|
|
try:
|
|
while True:
|
|
event = await asyncio.wait_for(
|
|
client.read_event(),
|
|
timeout=30
|
|
)
|
|
|
|
if event is None:
|
|
raise RuntimeError("Connexion Piper interrompue")
|
|
|
|
if AudioStart.is_type(event.type):
|
|
start = AudioStart.from_event(event)
|
|
|
|
wav_file = wave.open(wav_path, "wb")
|
|
wav_file.setnchannels(start.channels)
|
|
wav_file.setsampwidth(start.width)
|
|
wav_file.setframerate(start.rate)
|
|
|
|
elif AudioChunk.is_type(event.type):
|
|
chunk = AudioChunk.from_event(event)
|
|
|
|
if wav_file is None:
|
|
wav_file = wave.open(wav_path, "wb")
|
|
wav_file.setnchannels(chunk.channels)
|
|
wav_file.setsampwidth(chunk.width)
|
|
wav_file.setframerate(chunk.rate)
|
|
|
|
wav_file.writeframes(chunk.audio)
|
|
|
|
elif AudioStop.is_type(event.type):
|
|
break
|
|
|
|
finally:
|
|
if wav_file is not None:
|
|
wav_file.close()
|
|
|
|
finally:
|
|
await client.disconnect()
|
|
|
|
|
|
def speak(text):
|
|
if not text or not text.strip():
|
|
return
|
|
|
|
fd, wav_path = tempfile.mkstemp(
|
|
prefix="alfred_piper_",
|
|
suffix=".wav"
|
|
)
|
|
os.close(fd)
|
|
|
|
start = time.monotonic()
|
|
|
|
try:
|
|
print("[ALFRED PIPER] Synthèse...", flush=True)
|
|
|
|
asyncio.run(synthesize(text, wav_path))
|
|
|
|
print("[ALFRED PIPER] Lecture...", flush=True)
|
|
|
|
subprocess.run(
|
|
["aplay", "-q", "-D", AUDIO_DEVICE, wav_path],
|
|
check=True,
|
|
timeout=120
|
|
)
|
|
|
|
print(
|
|
f"[ALFRED PIPER] Terminé en {time.monotonic() - start:.2f}s",
|
|
flush=True
|
|
)
|
|
|
|
except Exception as e:
|
|
print(f"[ALFRED PIPER] Erreur : {e}", flush=True)
|
|
raise
|
|
finally:
|
|
if os.path.exists(wav_path):
|
|
os.remove(wav_path)
|