deva-tts 0.1.0__py3-none-any.whl
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.
- deva_tts/__init__.py +8 -0
- deva_tts/engine.py +155 -0
- deva_tts/utils.py +10 -0
- deva_tts-0.1.0.dist-info/METADATA +10 -0
- deva_tts-0.1.0.dist-info/RECORD +7 -0
- deva_tts-0.1.0.dist-info/WHEEL +5 -0
- deva_tts-0.1.0.dist-info/top_level.txt +1 -0
deva_tts/__init__.py
ADDED
deva_tts/engine.py
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import re
|
|
3
|
+
import edge_tts
|
|
4
|
+
import pyaudio
|
|
5
|
+
import miniaudio
|
|
6
|
+
from .utils import clean_text
|
|
7
|
+
import aiohttp
|
|
8
|
+
|
|
9
|
+
_background_tasks = []
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
_VOICE = "en-US-EmmaMultilingualNeural"
|
|
13
|
+
OUTPUT_SAMPLE_RATE = 24000
|
|
14
|
+
CHANNELS = 1
|
|
15
|
+
|
|
16
|
+
# Initialize PyAudio
|
|
17
|
+
p = pyaudio.PyAudio()
|
|
18
|
+
audio_stream = p.open(
|
|
19
|
+
format=pyaudio.paInt16,
|
|
20
|
+
channels=CHANNELS,
|
|
21
|
+
rate=OUTPUT_SAMPLE_RATE,
|
|
22
|
+
output=True
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
_chunk_queue = asyncio.Queue()
|
|
26
|
+
_sentence_queue = asyncio.Queue()
|
|
27
|
+
_pcm_queue = asyncio.Queue()
|
|
28
|
+
|
|
29
|
+
def set_voice(voice : str = "en-US-EmmaMultilingualNeural"):
|
|
30
|
+
"""allows users to change the TTS voice model"""
|
|
31
|
+
global _VOICE
|
|
32
|
+
try :
|
|
33
|
+
test = edge_tts.Communicate("hello", voice)
|
|
34
|
+
_VOICE = voice
|
|
35
|
+
except ValueError as e:
|
|
36
|
+
print(f"[INVALID VOICE NAME] : {voice}")
|
|
37
|
+
print("falling back to default voice")
|
|
38
|
+
|
|
39
|
+
async def feed_chunk(chunk):
|
|
40
|
+
if chunk != None or chunk != "":
|
|
41
|
+
await _chunk_queue.put(chunk)
|
|
42
|
+
|
|
43
|
+
async def text_parser():
|
|
44
|
+
sentence_end = re.compile(r'[^.!?]+[.!?](?=\s|$)')
|
|
45
|
+
buffer = ""
|
|
46
|
+
buffer_list = []
|
|
47
|
+
target = 1
|
|
48
|
+
|
|
49
|
+
while True:
|
|
50
|
+
chunk = await _chunk_queue.get()
|
|
51
|
+
if not chunk:
|
|
52
|
+
_chunk_queue.task_done()
|
|
53
|
+
await _sentence_queue.put(" ".join(buffer_list).strip())
|
|
54
|
+
break
|
|
55
|
+
buffer += chunk
|
|
56
|
+
|
|
57
|
+
while True:
|
|
58
|
+
match = sentence_end.match(buffer)
|
|
59
|
+
if not match:
|
|
60
|
+
break
|
|
61
|
+
sentence = match.group().strip()
|
|
62
|
+
sentence = clean_text(sentence)
|
|
63
|
+
buffer = buffer[match.end():].strip()
|
|
64
|
+
|
|
65
|
+
if sentence:
|
|
66
|
+
buffer_list.append(sentence)
|
|
67
|
+
if len(buffer_list) == target or target == 0:
|
|
68
|
+
print(f"[parser] sentence found: {sentence}")
|
|
69
|
+
await _sentence_queue.put(" ".join(buffer_list[:target]).strip())
|
|
70
|
+
buffer_list = buffer_list[target:]
|
|
71
|
+
if target < 5:
|
|
72
|
+
target += 1
|
|
73
|
+
await _sentence_queue.put(None)
|
|
74
|
+
|
|
75
|
+
async def tts_worker():
|
|
76
|
+
|
|
77
|
+
while True:
|
|
78
|
+
sentence = await _sentence_queue.get()
|
|
79
|
+
if sentence == None:
|
|
80
|
+
await _pcm_queue.put(None)
|
|
81
|
+
_sentence_queue.task_done()
|
|
82
|
+
break
|
|
83
|
+
print(f"[TTS] generating audio for: {sentence}")
|
|
84
|
+
communicate = edge_tts.Communicate(sentence, _VOICE)
|
|
85
|
+
mp3_buffer = b""
|
|
86
|
+
try :
|
|
87
|
+
async for chunk in communicate.stream():
|
|
88
|
+
if chunk["type"] == "audio":
|
|
89
|
+
mp3_buffer += chunk["data"]
|
|
90
|
+
if mp3_buffer:
|
|
91
|
+
decoded = miniaudio.decode(
|
|
92
|
+
mp3_buffer,
|
|
93
|
+
output_format=miniaudio.SampleFormat.SIGNED16,
|
|
94
|
+
nchannels=CHANNELS,
|
|
95
|
+
sample_rate=OUTPUT_SAMPLE_RATE
|
|
96
|
+
)
|
|
97
|
+
pcm_bytes = bytes(decoded.samples)
|
|
98
|
+
await _pcm_queue.put(pcm_bytes)
|
|
99
|
+
|
|
100
|
+
except aiohttp.client_exceptions.WSServerHandshakeError as e:
|
|
101
|
+
print(f"{e.status} : ")
|
|
102
|
+
if e.status == 403:
|
|
103
|
+
print("="*60 + "\n")
|
|
104
|
+
print(f"status : {e.status}")
|
|
105
|
+
print("="*60)
|
|
106
|
+
print("[DEVA_TTS_ERROR] CONNECTION BLOCKED")
|
|
107
|
+
print("="*60)
|
|
108
|
+
print("To fix this instantly, run this command in your terminal:")
|
|
109
|
+
print("\n python -m pip install --upgrade edge-tts\n")
|
|
110
|
+
print("="*60 + "\n")
|
|
111
|
+
else:
|
|
112
|
+
print("\n[DEVA_TTS] network error")
|
|
113
|
+
_sentence_queue.task_done()
|
|
114
|
+
continue
|
|
115
|
+
except asyncio.CancelledError:
|
|
116
|
+
print("\n[TTS] Download violently interrupted. Shutting down worker.")
|
|
117
|
+
break
|
|
118
|
+
_sentence_queue.task_done()
|
|
119
|
+
await _pcm_queue.put(None)
|
|
120
|
+
|
|
121
|
+
async def audio_playback():
|
|
122
|
+
|
|
123
|
+
loop = asyncio.get_running_loop()
|
|
124
|
+
chunk_size = 8192
|
|
125
|
+
try:
|
|
126
|
+
while True:
|
|
127
|
+
pcm_data = await _pcm_queue.get()
|
|
128
|
+
if not pcm_data:
|
|
129
|
+
_pcm_queue.task_done()
|
|
130
|
+
break
|
|
131
|
+
|
|
132
|
+
print(f"[Player] Playing back sentence audio segment ({len(pcm_data)} bytes)...")
|
|
133
|
+
for i in range(0,len(pcm_data),chunk_size):
|
|
134
|
+
sliced_data = pcm_data[i: i+chunk_size]
|
|
135
|
+
await loop.run_in_executor(None,audio_stream.write,sliced_data)
|
|
136
|
+
_pcm_queue.task_done()
|
|
137
|
+
except asyncio.CancelledError as e:
|
|
138
|
+
print("stoping the process")
|
|
139
|
+
finally:
|
|
140
|
+
print("\n[System] Shutting down audio streams...")
|
|
141
|
+
audio_stream.stop_stream()
|
|
142
|
+
audio_stream.close()
|
|
143
|
+
p.terminate()
|
|
144
|
+
print("[System] Done.")
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
async def start_engine():
|
|
148
|
+
print("the engine is starting....")
|
|
149
|
+
_background_tasks.append(asyncio.create_task(text_parser()))
|
|
150
|
+
_background_tasks.append(asyncio.create_task(tts_worker()))
|
|
151
|
+
_background_tasks.append(asyncio.create_task(audio_playback()))
|
|
152
|
+
|
|
153
|
+
async def stop_engine():
|
|
154
|
+
await _chunk_queue.put(None)
|
|
155
|
+
await asyncio.gather(*_background_tasks)
|
deva_tts/utils.py
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
deva_tts/__init__.py,sha256=6L9qJJvIJYaRupkD7QCKnGm74c9PHfl8JmyCRUQmsFU,160
|
|
2
|
+
deva_tts/engine.py,sha256=8FnSUI9i4y4fsCFeO3mhRUmWK4kLn_e2MI8xmA8f3j0,5223
|
|
3
|
+
deva_tts/utils.py,sha256=rkhUIYd25NAP33hXXYFL9kNHTUfzWDm4TUeCtLDOwmg,219
|
|
4
|
+
deva_tts-0.1.0.dist-info/METADATA,sha256=SaBI7i6r9wjaOno_NDhlTXrSWEZWB7OWPg_yGH4traI,250
|
|
5
|
+
deva_tts-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
6
|
+
deva_tts-0.1.0.dist-info/top_level.txt,sha256=Lq8h-sBoyHMN0UhjFspiP7T_p5FjmOv0LaVC89RC-ZU,9
|
|
7
|
+
deva_tts-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
deva_tts
|