milicka's picture
Upload 10 files
e5bbd16 verified
Raw
History Blame Contribute Delete
1.21 kB
from __future__ import annotations
import io
import wave
import numpy as np
from piper import PiperVoice
def load_voice(voice_path: str = "models/piper/cs_CZ-jirka-medium.onnx") -> PiperVoice:
return PiperVoice.load(voice_path)
def synthesize(voice: PiperVoice, text: str) -> tuple[np.ndarray, int]:
"""Synthesize `text` and return (mono float32 waveform in [-1,1], sample_rate)."""
buf = io.BytesIO()
with wave.open(buf, "wb") as wf:
voice.synthesize_wav(text, wf)
buf.seek(0)
with wave.open(buf, "rb") as wf:
sr = wf.getframerate()
n_channels = wf.getnchannels()
sampwidth = wf.getsampwidth()
raw = wf.readframes(wf.getnframes())
if sampwidth != 2:
raise RuntimeError(f"Unexpected Piper sample width: {sampwidth} bytes")
pcm = np.frombuffer(raw, dtype=np.int16).astype(np.float32) / 32768.0
if n_channels > 1:
pcm = pcm.reshape(-1, n_channels).mean(axis=1)
return pcm, sr
def synthesize_duration(voice: PiperVoice, text: str) -> tuple[float, np.ndarray, int]:
"""Convenience: returns (duration_seconds, waveform, sample_rate)."""
wav, sr = synthesize(voice, text)
return len(wav) / sr, wav, sr