Repository navigation
Expand file tree
/
Copy pathtts_client.py
More file actions
109 lines (92 loc) · 3.42 KB
/
Copy pathtts_client.py
File metadata and controls
109 lines (92 loc) · 3.42 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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
"""
PromptTTS Client — Combines Azure OpenAI (style analysis) + edge-tts (speech synthesis).
Architecture:
1. User provides: style prompt + content text
2. GPT-5.1 analyzes the style prompt → selects voice, style, prosody params
3. edge-tts renders the speech with the selected parameters
4. Returns audio file
This mirrors the PromptTTS paper architecture:
- GPT-5.1 acts as the "Style Encoder" (replacing BERT + P-Tuning v2)
- edge-tts acts as the "Decoder + Vocoder" (replacing Transformer decoder + HiFi-GAN)
"""
import os
import tempfile
from pathlib import Path
from style_analyzer import StyleAnalyzer, build_ssml, VOICES, VOICE_NAMES
from speech_engine import synthesize
class PromptTTSClient:
"""
PromptTTS: natural language style control for text-to-speech.
Uses Azure OpenAI GPT-5.1 as the style encoder and
Microsoft edge-tts as the speech synthesizer.
"""
def __init__(
self,
api_key: str | None = None,
endpoint: str | None = None,
deployment: str | None = None,
api_version: str | None = None,
):
self.analyzer = StyleAnalyzer(
api_key=api_key,
endpoint=endpoint,
deployment=deployment,
api_version=api_version,
)
def generate(
self,
style_prompt: str,
text: str,
voice_override: str | None = None,
output_path: str | None = None,
) -> dict:
"""
Full PromptTTS pipeline: style prompt + text → audio.
Args:
style_prompt: Natural language style description
e.g., "A cheerful young woman speaks quickly"
text: Content text to synthesize
voice_override: Force a specific voice (None = auto from GPT-5.1)
output_path: Save path (None = temp file)
Returns:
Dict with:
audio_bytes: Raw audio data
output_path: Path to saved audio file
style_params: The analyzed style parameters from GPT-5.1
ssml: The generated SSML markup
"""
# Step 1: Analyze style with GPT-5.1
style_params = self.analyzer.analyze(style_prompt)
# Apply voice override if specified
if voice_override and voice_override in VOICES:
style_params["voice"] = voice_override
# Step 2: Build SSML
ssml = build_ssml(text=text, **style_params)
# Step 3: Synthesize with edge-tts
if output_path is None:
output_path = tempfile.mktemp(suffix=".mp3")
audio_bytes, saved_path = synthesize(
text=text,
voice=style_params["voice"],
rate=style_params.get("rate", "+0%"),
pitch=style_params.get("pitch", "+0Hz"),
volume=self._map_volume(style_params.get("volume", "default")),
output_path=output_path,
)
return {
"audio_bytes": audio_bytes,
"output_path": Path(saved_path),
"style_params": style_params,
"ssml": ssml,
}
def _map_volume(self, volume_str: str) -> str:
"""Map descriptive volume to edge-tts percentage format."""
mapping = {
"x-soft": "-50%",
"soft": "-30%",
"medium": "+0%",
"default": "+0%",
"loud": "+30%",
"x-loud": "+50%",
}
return mapping.get(volume_str, "+0%")