PyThaiTTS 0.6.0__tar.gz → 0.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pythaitts-0.6.0 → pythaitts-0.7.0}/PKG-INFO +64 -1
- {pythaitts-0.6.0 → pythaitts-0.7.0}/PyThaiTTS.egg-info/PKG-INFO +64 -1
- {pythaitts-0.6.0 → pythaitts-0.7.0}/PyThaiTTS.egg-info/SOURCES.txt +2 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/PyThaiTTS.egg-info/requires.txt +3 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/README.md +61 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/pythaitts/__init__.py +78 -1
- {pythaitts-0.6.0 → pythaitts-0.7.0}/pythaitts/pretrained/fastthaig2p/__init__.py +21 -1
- {pythaitts-0.6.0 → pythaitts-0.7.0}/pythaitts/pretrained/fastthaig2p/tts.py +79 -1
- pythaitts-0.7.0/pythaitts/realtime.py +591 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/setup.py +2 -1
- pythaitts-0.7.0/tests/test_realtime.py +302 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/LICENSE +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/PyThaiTTS.egg-info/dependency_links.txt +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/PyThaiTTS.egg-info/not-zip-safe +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/PyThaiTTS.egg-info/top_level.txt +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/pythaitts/preprocess.py +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/pythaitts/pretrained/__init__.py +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/pythaitts/pretrained/fastthaig2p/_data.py +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/pythaitts/pretrained/fastthaig2p/_th2ipa.py +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/pythaitts/pretrained/fastthaig2p/data/dict.txt +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/pythaitts/pretrained/fastthaig2p/data/fallback/PhSTrigram.sts +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/pythaitts/pretrained/fastthaig2p/data/fallback/sylform_var.pkl +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/pythaitts/pretrained/fastthaig2p/data/fallback/sylrule.lts +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/pythaitts/pretrained/fastthaig2p/data/fallback/sylseg.3g +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/pythaitts/pretrained/fastthaig2p/data/fallback/thaisyl.dict +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/pythaitts/pretrained/fastthaig2p/data/fallback/thdict +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/pythaitts/pretrained/fastthaig2p/data/ipa.json +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/pythaitts/pretrained/fastthaig2p/fallback.py +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/pythaitts/pretrained/fastthaig2p/g2p.py +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/pythaitts/pretrained/fastthaig2p/kokoro.py +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/pythaitts/pretrained/fastthaig2p/normalizer.py +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/pythaitts/pretrained/fastthaig2p/tokenizer.py +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/pythaitts/pretrained/khanomtan_tts.py +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/pythaitts/pretrained/lunarlist_model.py +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/pythaitts/pretrained/lunarlist_onnx.py +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/pythaitts/pretrained/vachana_tts.py +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/setup.cfg +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/tests/__init__.py +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/tests/test_fastthaig2p.py +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/tests/test_preprocess.py +0 -0
- {pythaitts-0.6.0 → pythaitts-0.7.0}/tests/test_vachana.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyThaiTTS
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Open Source Thai Text-to-speech library in Python
|
|
5
5
|
Home-page: https://github.com/pythainlp/pythaitts
|
|
6
6
|
Author: Wannaphong
|
|
@@ -30,6 +30,8 @@ Provides-Extra: vachanatts
|
|
|
30
30
|
Requires-Dist: vachanatts; extra == "vachanatts"
|
|
31
31
|
Provides-Extra: vachana
|
|
32
32
|
Requires-Dist: vachanatts; extra == "vachana"
|
|
33
|
+
Provides-Extra: realtime
|
|
34
|
+
Requires-Dist: RealtimeTTS; extra == "realtime"
|
|
33
35
|
Dynamic: author
|
|
34
36
|
Dynamic: author-email
|
|
35
37
|
Dynamic: classifier
|
|
@@ -101,6 +103,67 @@ tts = TTS(pretrained="khanomtan")
|
|
|
101
103
|
file = tts.tts("ภาษาไทย", speaker_idx="Linda", filename="output.wav")
|
|
102
104
|
```
|
|
103
105
|
|
|
106
|
+
### Real-time / Streaming TTS (FastThaiG2P)
|
|
107
|
+
|
|
108
|
+
PyThaiTTS supports low-latency, real-time streaming speech synthesis with FastThaiG2P, making it ideal for conversational voice agents and LLM streaming:
|
|
109
|
+
|
|
110
|
+
#### 1. Streaming Audio from Text
|
|
111
|
+
|
|
112
|
+
Synthesize chunk-by-chunk in real time:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from pythaitts import TTS
|
|
116
|
+
|
|
117
|
+
tts = TTS(pretrained="fastthaig2p")
|
|
118
|
+
|
|
119
|
+
# Stream audio chunks as 24kHz float32 NumPy arrays
|
|
120
|
+
for audio_chunk in tts.stream("สวัสดีครับ ยินดีต้อนรับสู่ระบบเรียลไทม์ทีทีเอส"):
|
|
121
|
+
print(f"Audio chunk shape: {audio_chunk.shape}")
|
|
122
|
+
|
|
123
|
+
# Stream raw 16-bit PCM bytes (for WebSockets or PyAudio)
|
|
124
|
+
for pcm_bytes in tts.stream("สวัสดีครับ", return_type="bytes"):
|
|
125
|
+
# send over websocket or write to audio stream
|
|
126
|
+
pass
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
#### 2. Streaming from an LLM Token Stream
|
|
130
|
+
|
|
131
|
+
Feed tokens directly from an LLM or generator into `tts.stream()`:
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
from pythaitts import TTS
|
|
135
|
+
|
|
136
|
+
tts = TTS()
|
|
137
|
+
|
|
138
|
+
def token_stream():
|
|
139
|
+
tokens = ["สวัสดี", "ครับ", " ", "นี่", "คือ", "การ", "สตรีม", "มิ่ง"]
|
|
140
|
+
for tok in tokens:
|
|
141
|
+
yield tok
|
|
142
|
+
|
|
143
|
+
for audio_chunk in tts.stream(token_stream()):
|
|
144
|
+
# Process or play chunk with low latency
|
|
145
|
+
pass
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
#### 3. Integration with the RealtimeTTS Library
|
|
149
|
+
|
|
150
|
+
You can use FastThaiG2P as an engine with [KoljaB/RealtimeTTS](https://github.com/KoljaB/RealtimeTTS):
|
|
151
|
+
|
|
152
|
+
```sh
|
|
153
|
+
pip install pythaitts[realtime]
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
from RealtimeTTS import TextToAudioStream
|
|
158
|
+
from pythaitts.realtime import FastThaiG2PEngine
|
|
159
|
+
|
|
160
|
+
engine = FastThaiG2PEngine()
|
|
161
|
+
stream = TextToAudioStream(engine)
|
|
162
|
+
stream.feed("สวัสดีครับ วันนี้อากาศดีมาก")
|
|
163
|
+
stream.play()
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
|
|
104
167
|
### Text Preprocessing
|
|
105
168
|
|
|
106
169
|
PyThaiTTS includes automatic text preprocessing to improve TTS quality:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyThaiTTS
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Open Source Thai Text-to-speech library in Python
|
|
5
5
|
Home-page: https://github.com/pythainlp/pythaitts
|
|
6
6
|
Author: Wannaphong
|
|
@@ -30,6 +30,8 @@ Provides-Extra: vachanatts
|
|
|
30
30
|
Requires-Dist: vachanatts; extra == "vachanatts"
|
|
31
31
|
Provides-Extra: vachana
|
|
32
32
|
Requires-Dist: vachanatts; extra == "vachana"
|
|
33
|
+
Provides-Extra: realtime
|
|
34
|
+
Requires-Dist: RealtimeTTS; extra == "realtime"
|
|
33
35
|
Dynamic: author
|
|
34
36
|
Dynamic: author-email
|
|
35
37
|
Dynamic: classifier
|
|
@@ -101,6 +103,67 @@ tts = TTS(pretrained="khanomtan")
|
|
|
101
103
|
file = tts.tts("ภาษาไทย", speaker_idx="Linda", filename="output.wav")
|
|
102
104
|
```
|
|
103
105
|
|
|
106
|
+
### Real-time / Streaming TTS (FastThaiG2P)
|
|
107
|
+
|
|
108
|
+
PyThaiTTS supports low-latency, real-time streaming speech synthesis with FastThaiG2P, making it ideal for conversational voice agents and LLM streaming:
|
|
109
|
+
|
|
110
|
+
#### 1. Streaming Audio from Text
|
|
111
|
+
|
|
112
|
+
Synthesize chunk-by-chunk in real time:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from pythaitts import TTS
|
|
116
|
+
|
|
117
|
+
tts = TTS(pretrained="fastthaig2p")
|
|
118
|
+
|
|
119
|
+
# Stream audio chunks as 24kHz float32 NumPy arrays
|
|
120
|
+
for audio_chunk in tts.stream("สวัสดีครับ ยินดีต้อนรับสู่ระบบเรียลไทม์ทีทีเอส"):
|
|
121
|
+
print(f"Audio chunk shape: {audio_chunk.shape}")
|
|
122
|
+
|
|
123
|
+
# Stream raw 16-bit PCM bytes (for WebSockets or PyAudio)
|
|
124
|
+
for pcm_bytes in tts.stream("สวัสดีครับ", return_type="bytes"):
|
|
125
|
+
# send over websocket or write to audio stream
|
|
126
|
+
pass
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
#### 2. Streaming from an LLM Token Stream
|
|
130
|
+
|
|
131
|
+
Feed tokens directly from an LLM or generator into `tts.stream()`:
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
from pythaitts import TTS
|
|
135
|
+
|
|
136
|
+
tts = TTS()
|
|
137
|
+
|
|
138
|
+
def token_stream():
|
|
139
|
+
tokens = ["สวัสดี", "ครับ", " ", "นี่", "คือ", "การ", "สตรีม", "มิ่ง"]
|
|
140
|
+
for tok in tokens:
|
|
141
|
+
yield tok
|
|
142
|
+
|
|
143
|
+
for audio_chunk in tts.stream(token_stream()):
|
|
144
|
+
# Process or play chunk with low latency
|
|
145
|
+
pass
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
#### 3. Integration with the RealtimeTTS Library
|
|
149
|
+
|
|
150
|
+
You can use FastThaiG2P as an engine with [KoljaB/RealtimeTTS](https://github.com/KoljaB/RealtimeTTS):
|
|
151
|
+
|
|
152
|
+
```sh
|
|
153
|
+
pip install pythaitts[realtime]
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
from RealtimeTTS import TextToAudioStream
|
|
158
|
+
from pythaitts.realtime import FastThaiG2PEngine
|
|
159
|
+
|
|
160
|
+
engine = FastThaiG2PEngine()
|
|
161
|
+
stream = TextToAudioStream(engine)
|
|
162
|
+
stream.feed("สวัสดีครับ วันนี้อากาศดีมาก")
|
|
163
|
+
stream.play()
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
|
|
104
167
|
### Text Preprocessing
|
|
105
168
|
|
|
106
169
|
PyThaiTTS includes automatic text preprocessing to improve TTS quality:
|
|
@@ -9,6 +9,7 @@ PyThaiTTS.egg-info/requires.txt
|
|
|
9
9
|
PyThaiTTS.egg-info/top_level.txt
|
|
10
10
|
pythaitts/__init__.py
|
|
11
11
|
pythaitts/preprocess.py
|
|
12
|
+
pythaitts/realtime.py
|
|
12
13
|
pythaitts/pretrained/__init__.py
|
|
13
14
|
pythaitts/pretrained/khanomtan_tts.py
|
|
14
15
|
pythaitts/pretrained/lunarlist_model.py
|
|
@@ -34,4 +35,5 @@ pythaitts/pretrained/fastthaig2p/data/fallback/thdict
|
|
|
34
35
|
tests/__init__.py
|
|
35
36
|
tests/test_fastthaig2p.py
|
|
36
37
|
tests/test_preprocess.py
|
|
38
|
+
tests/test_realtime.py
|
|
37
39
|
tests/test_vachana.py
|
|
@@ -54,6 +54,67 @@ tts = TTS(pretrained="khanomtan")
|
|
|
54
54
|
file = tts.tts("ภาษาไทย", speaker_idx="Linda", filename="output.wav")
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
+
### Real-time / Streaming TTS (FastThaiG2P)
|
|
58
|
+
|
|
59
|
+
PyThaiTTS supports low-latency, real-time streaming speech synthesis with FastThaiG2P, making it ideal for conversational voice agents and LLM streaming:
|
|
60
|
+
|
|
61
|
+
#### 1. Streaming Audio from Text
|
|
62
|
+
|
|
63
|
+
Synthesize chunk-by-chunk in real time:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from pythaitts import TTS
|
|
67
|
+
|
|
68
|
+
tts = TTS(pretrained="fastthaig2p")
|
|
69
|
+
|
|
70
|
+
# Stream audio chunks as 24kHz float32 NumPy arrays
|
|
71
|
+
for audio_chunk in tts.stream("สวัสดีครับ ยินดีต้อนรับสู่ระบบเรียลไทม์ทีทีเอส"):
|
|
72
|
+
print(f"Audio chunk shape: {audio_chunk.shape}")
|
|
73
|
+
|
|
74
|
+
# Stream raw 16-bit PCM bytes (for WebSockets or PyAudio)
|
|
75
|
+
for pcm_bytes in tts.stream("สวัสดีครับ", return_type="bytes"):
|
|
76
|
+
# send over websocket or write to audio stream
|
|
77
|
+
pass
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
#### 2. Streaming from an LLM Token Stream
|
|
81
|
+
|
|
82
|
+
Feed tokens directly from an LLM or generator into `tts.stream()`:
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
from pythaitts import TTS
|
|
86
|
+
|
|
87
|
+
tts = TTS()
|
|
88
|
+
|
|
89
|
+
def token_stream():
|
|
90
|
+
tokens = ["สวัสดี", "ครับ", " ", "นี่", "คือ", "การ", "สตรีม", "มิ่ง"]
|
|
91
|
+
for tok in tokens:
|
|
92
|
+
yield tok
|
|
93
|
+
|
|
94
|
+
for audio_chunk in tts.stream(token_stream()):
|
|
95
|
+
# Process or play chunk with low latency
|
|
96
|
+
pass
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
#### 3. Integration with the RealtimeTTS Library
|
|
100
|
+
|
|
101
|
+
You can use FastThaiG2P as an engine with [KoljaB/RealtimeTTS](https://github.com/KoljaB/RealtimeTTS):
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
pip install pythaitts[realtime]
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from RealtimeTTS import TextToAudioStream
|
|
109
|
+
from pythaitts.realtime import FastThaiG2PEngine
|
|
110
|
+
|
|
111
|
+
engine = FastThaiG2PEngine()
|
|
112
|
+
stream = TextToAudioStream(engine)
|
|
113
|
+
stream.feed("สวัสดีครับ วันนี้อากาศดีมาก")
|
|
114
|
+
stream.play()
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
|
|
57
118
|
### Text Preprocessing
|
|
58
119
|
|
|
59
120
|
PyThaiTTS includes automatic text preprocessing to improve TTS quality:
|
|
@@ -2,9 +2,19 @@
|
|
|
2
2
|
"""
|
|
3
3
|
PyThaiTTS
|
|
4
4
|
"""
|
|
5
|
-
__version__ = "0.
|
|
5
|
+
__version__ = "0.7.0"
|
|
6
|
+
|
|
7
|
+
from typing import Union, Iterable, Iterator
|
|
6
8
|
|
|
7
9
|
from pythaitts.preprocess import preprocess_text, num_to_thai, expand_maiyamok
|
|
10
|
+
from pythaitts.realtime import (
|
|
11
|
+
RealtimeTTS,
|
|
12
|
+
FastThaiG2PEngine,
|
|
13
|
+
PyThaiTTSEngine,
|
|
14
|
+
RealtimeTTSEngine,
|
|
15
|
+
chunk_text,
|
|
16
|
+
stream_text_to_chunks,
|
|
17
|
+
)
|
|
8
18
|
|
|
9
19
|
|
|
10
20
|
class TTS:
|
|
@@ -100,3 +110,70 @@ class TTS:
|
|
|
100
110
|
return_type=return_type,
|
|
101
111
|
filename=filename
|
|
102
112
|
)
|
|
113
|
+
|
|
114
|
+
def stream(
|
|
115
|
+
self,
|
|
116
|
+
text: Union[str, Iterable[str]],
|
|
117
|
+
speaker_idx: str = "thai_som",
|
|
118
|
+
return_type: str = "waveform",
|
|
119
|
+
play: bool = False,
|
|
120
|
+
preprocess: bool = True,
|
|
121
|
+
max_phonemes: int = 400,
|
|
122
|
+
**kwargs,
|
|
123
|
+
):
|
|
124
|
+
"""
|
|
125
|
+
Stream speech synthesis in real-time.
|
|
126
|
+
|
|
127
|
+
:param Union[str, Iterable[str]] text: Input text or stream of text tokens (e.g. from LLM)
|
|
128
|
+
:param str speaker_idx: Voice to use (default: "thai_som" for fastthaig2p)
|
|
129
|
+
:param str return_type: Return format ("waveform", "bytes", "raw", "file")
|
|
130
|
+
:param bool play: Whether to play audio chunks in real-time to speakers
|
|
131
|
+
:param bool preprocess: Whether to preprocess text (numbers to words, ๆ)
|
|
132
|
+
:param int max_phonemes: Maximum phonemes per synthesized chunk
|
|
133
|
+
:param kwargs: Additional parameters passed to the model
|
|
134
|
+
:return: Generator yielding audio chunks
|
|
135
|
+
"""
|
|
136
|
+
if self.pretrained in ("fastthaig2p", "FastThaiG2P"):
|
|
137
|
+
if speaker_idx in ("Linda", None):
|
|
138
|
+
speaker_idx = "thai_som"
|
|
139
|
+
return self.model.stream(
|
|
140
|
+
text=text,
|
|
141
|
+
speaker_idx=speaker_idx,
|
|
142
|
+
return_type=return_type,
|
|
143
|
+
play=play,
|
|
144
|
+
preprocess=preprocess,
|
|
145
|
+
max_phonemes=max_phonemes,
|
|
146
|
+
**kwargs,
|
|
147
|
+
)
|
|
148
|
+
else:
|
|
149
|
+
from pythaitts.realtime import stream_text_to_chunks
|
|
150
|
+
|
|
151
|
+
def _gen():
|
|
152
|
+
for chunk in stream_text_to_chunks(
|
|
153
|
+
text, max_phonemes=max_phonemes, preprocess=preprocess
|
|
154
|
+
):
|
|
155
|
+
yield self.tts(
|
|
156
|
+
text=chunk,
|
|
157
|
+
speaker_idx=speaker_idx,
|
|
158
|
+
return_type=return_type,
|
|
159
|
+
preprocess=False,
|
|
160
|
+
**kwargs,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
return _gen()
|
|
164
|
+
|
|
165
|
+
tts_stream = stream
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
__all__ = [
|
|
169
|
+
"TTS",
|
|
170
|
+
"RealtimeTTS",
|
|
171
|
+
"FastThaiG2PEngine",
|
|
172
|
+
"PyThaiTTSEngine",
|
|
173
|
+
"RealtimeTTSEngine",
|
|
174
|
+
"preprocess_text",
|
|
175
|
+
"num_to_thai",
|
|
176
|
+
"expand_maiyamok",
|
|
177
|
+
"chunk_text",
|
|
178
|
+
"stream_text_to_chunks",
|
|
179
|
+
]
|
|
@@ -23,7 +23,18 @@ from .kokoro import ipa_to_kokoro
|
|
|
23
23
|
from .normalizer import normalize
|
|
24
24
|
from .tokenizer import Tokenizer
|
|
25
25
|
|
|
26
|
-
__all__ = [
|
|
26
|
+
__all__ = [
|
|
27
|
+
"G2P",
|
|
28
|
+
"Tokenizer",
|
|
29
|
+
"normalize",
|
|
30
|
+
"ipa_to_kokoro",
|
|
31
|
+
"TTS",
|
|
32
|
+
"FastThaiG2P",
|
|
33
|
+
"FastThaiG2PEngine",
|
|
34
|
+
"FastThaiG2PVoice",
|
|
35
|
+
"chunk_text",
|
|
36
|
+
"stream_text_to_chunks",
|
|
37
|
+
]
|
|
27
38
|
|
|
28
39
|
|
|
29
40
|
def __getattr__(name):
|
|
@@ -31,5 +42,14 @@ def __getattr__(name):
|
|
|
31
42
|
from .tts import TTS, FastThaiG2P
|
|
32
43
|
|
|
33
44
|
return FastThaiG2P if name == "FastThaiG2P" else TTS
|
|
45
|
+
if name in ("FastThaiG2PEngine", "FastThaiG2PVoice", "chunk_text", "stream_text_to_chunks"):
|
|
46
|
+
from pythaitts.realtime import (
|
|
47
|
+
FastThaiG2PEngine,
|
|
48
|
+
FastThaiG2PVoice,
|
|
49
|
+
chunk_text,
|
|
50
|
+
stream_text_to_chunks,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
return locals()[name]
|
|
34
54
|
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
35
55
|
|
|
@@ -32,7 +32,7 @@ import json
|
|
|
32
32
|
import os
|
|
33
33
|
import wave
|
|
34
34
|
from pathlib import Path
|
|
35
|
-
from typing import Optional
|
|
35
|
+
from typing import Optional, Union, Iterable, Iterator
|
|
36
36
|
|
|
37
37
|
import numpy as np
|
|
38
38
|
|
|
@@ -277,6 +277,84 @@ class TTS:
|
|
|
277
277
|
self.synthesize(text, filename)
|
|
278
278
|
return str(filename)
|
|
279
279
|
|
|
280
|
+
def stream(
|
|
281
|
+
self,
|
|
282
|
+
text: Union[str, Iterable[str]],
|
|
283
|
+
speaker_idx: str = "thai_som",
|
|
284
|
+
return_type: str = "waveform",
|
|
285
|
+
play: bool = False,
|
|
286
|
+
preprocess: bool = True,
|
|
287
|
+
max_phonemes: int = 400,
|
|
288
|
+
**kwargs,
|
|
289
|
+
):
|
|
290
|
+
"""Stream Thai speech generation chunk by chunk in real-time.
|
|
291
|
+
|
|
292
|
+
:param Union[str, Iterable[str]] text: Input text or stream of text tokens (e.g. from LLM)
|
|
293
|
+
:param str speaker_idx: Voice to use (default: "thai_som" or path to custom voicepack)
|
|
294
|
+
:param str return_type: Return format ("waveform", "bytes", "raw", "file")
|
|
295
|
+
:param bool play: Whether to play audio chunks in real-time to speakers
|
|
296
|
+
:param bool preprocess: Whether to preprocess text (numbers to words, ๆ)
|
|
297
|
+
:param int max_phonemes: Maximum phonemes per synthesized chunk (default: 400)
|
|
298
|
+
:param kwargs: Additional parameters (e.g., speed)
|
|
299
|
+
:yield: Audio chunk (np.ndarray float32 waveform, int16 bytes, or wav file path)
|
|
300
|
+
"""
|
|
301
|
+
if speaker_idx in ("Linda", None):
|
|
302
|
+
speaker_idx = "thai_som"
|
|
303
|
+
|
|
304
|
+
if speaker_idx not in self.SUPPORTED_VOICES and not os.path.exists(str(speaker_idx)):
|
|
305
|
+
raise ValueError(
|
|
306
|
+
f"Unsupported voice '{speaker_idx}'. Supported voices are: {', '.join(self.SUPPORTED_VOICES)}"
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
orig_speed = self.speed
|
|
310
|
+
if "speed" in kwargs:
|
|
311
|
+
self.speed = kwargs["speed"]
|
|
312
|
+
|
|
313
|
+
from pythaitts.realtime import _AudioPlayer, stream_text_to_chunks
|
|
314
|
+
|
|
315
|
+
player = _AudioPlayer(sample_rate=self.sample_rate) if play else None
|
|
316
|
+
|
|
317
|
+
try:
|
|
318
|
+
chunks = stream_text_to_chunks(
|
|
319
|
+
text,
|
|
320
|
+
max_phonemes=max_phonemes,
|
|
321
|
+
preprocess=preprocess,
|
|
322
|
+
g2p_converter=self._text_to_phonemes,
|
|
323
|
+
)
|
|
324
|
+
for chunk in chunks:
|
|
325
|
+
audio = self.generate(chunk)
|
|
326
|
+
if len(audio) == 0:
|
|
327
|
+
continue
|
|
328
|
+
|
|
329
|
+
if player is not None:
|
|
330
|
+
player.play(audio)
|
|
331
|
+
|
|
332
|
+
if return_type == "waveform":
|
|
333
|
+
yield audio
|
|
334
|
+
elif return_type in ("bytes", "raw"):
|
|
335
|
+
yield (np.clip(audio, -1.0, 1.0) * 32767).astype(np.int16).tobytes()
|
|
336
|
+
elif return_type == "file":
|
|
337
|
+
import tempfile
|
|
338
|
+
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as fp:
|
|
339
|
+
temp_name = fp.name
|
|
340
|
+
audio_int16 = (np.clip(audio, -1.0, 1.0) * 32767).astype(np.int16)
|
|
341
|
+
with wave.open(temp_name, "wb") as wf:
|
|
342
|
+
wf.setnchannels(1)
|
|
343
|
+
wf.setsampwidth(2)
|
|
344
|
+
wf.setframerate(self.sample_rate)
|
|
345
|
+
wf.writeframes(audio_int16.tobytes())
|
|
346
|
+
yield temp_name
|
|
347
|
+
else:
|
|
348
|
+
raise ValueError(
|
|
349
|
+
f"Unsupported return_type '{return_type}'. Choose 'waveform', 'bytes', or 'file'."
|
|
350
|
+
)
|
|
351
|
+
finally:
|
|
352
|
+
if player is not None:
|
|
353
|
+
player.close()
|
|
354
|
+
self.speed = orig_speed
|
|
355
|
+
|
|
356
|
+
tts_stream = stream
|
|
357
|
+
|
|
280
358
|
|
|
281
359
|
FastThaiG2P = TTS
|
|
282
360
|
|