File size: 1,261 Bytes
14c7fcf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
from __future__ import annotations

import uuid
from functools import lru_cache
from pathlib import Path

import numpy as np

from .config import SETTINGS


@lru_cache(maxsize=1)
def _kokoro_pipeline():
    if not SETTINGS.enable_kokoro:
        raise RuntimeError("Kokoro is disabled. Set ENABLE_KOKORO=1 to enable it.")
    from kokoro import KPipeline

    return KPipeline(lang_code="a")


def generate_audio(
    text: str,
    voice: str = "af_heart",
    speed: float = 1.0,
) -> Path:
    """Generate a WAV narration using the open-weight Kokoro TTS model."""

    import soundfile as sf

    normalized = text.strip()
    if not normalized:
        raise ValueError("Narration text is empty.")
    normalized = normalized[: SETTINGS.max_audio_chars]

    pipeline = _kokoro_pipeline()
    audio_segments: list[np.ndarray] = []
    for _, _, audio in pipeline(normalized, voice=voice, speed=speed):
        audio_segments.append(np.asarray(audio, dtype=np.float32))

    if not audio_segments:
        raise RuntimeError("Kokoro returned no audio segments.")

    combined = np.concatenate(audio_segments)
    output_path = SETTINGS.output_dir / f"papercast-{uuid.uuid4().hex[:10]}.wav"
    sf.write(output_path, combined, 24000)
    return output_path