-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathtts_engine.py
More file actions
92 lines (71 loc) · 2.64 KB
/
Copy pathtts_engine.py
File metadata and controls
92 lines (71 loc) · 2.64 KB
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
"""
tts_engine.py -- Edge-TTS narration (free, local, no cloud cost)
==================================================================
Generates MP3 narration audio from scene text using the ``edge-tts``
library (Microsoft Edge TTS engine — runs fully locally).
"""
from __future__ import annotations
import asyncio
import logging
from pathlib import Path
log = logging.getLogger("stickman_studio.tts")
_DEFAULT_VOICE = "en-US-JennyNeural"
class TTSEngine:
"""Generates narration audio using Edge-TTS (local, free).
Uses Microsoft Edge's neural TTS voices. No cloud API calls needed.
"""
def __init__(
self,
voice: str = _DEFAULT_VOICE,
) -> None:
self._voice = voice
def synthesize_speech(self, text: str, output_path: str | Path) -> Path:
"""Synthesise text into an MP3 file via edge-tts.
Args:
text: The text to speak.
output_path: Where to write the MP3 file.
Returns:
The output Path.
"""
import edge_tts
output = Path(output_path)
output.parent.mkdir(parents=True, exist_ok=True)
async def _do() -> None:
communicate = edge_tts.Communicate(text, self._voice)
await communicate.save(str(output))
asyncio.run(_do())
size = output.stat().st_size
log.info("TTS: %d bytes -> %s (voice=%s)", size, output, self._voice)
return output
def generate_per_scene_audio(
self,
scenes: list,
output_dir: str | Path,
) -> list[Path]:
"""Generate one MP3 per scene from its narration text.
Args:
scenes: Iterable of objects with a ``narration`` attribute.
output_dir: Directory to write MP3 files into.
Returns:
List of Paths to the generated audio files, one per scene.
"""
out_dir = Path(output_dir)
out_dir.mkdir(parents=True, exist_ok=True)
paths: list[Path] = []
for i, scene in enumerate(scenes):
text = (scene.narration or "").strip()
if not text:
log.warning("Scene %d has no narration; skipping audio.", i)
continue
path = out_dir / f"scene_{i:02d}.mp3"
self.synthesize_speech(text, path)
paths.append(path)
log.info("TTS: generated %d audio file(s)", len(paths))
return paths
def generate_script_audio(
self,
script_text: str,
output_path: str | Path,
) -> Path:
"""Generate a single audio file from the full script text."""
return self.synthesize_speech(script_text, output_path)