LTTEAM commited on
Commit
b76f6b2
·
verified ·
1 Parent(s): 2390e5b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +114 -90
app.py CHANGED
@@ -5,16 +5,54 @@ import os
5
  import random
6
  import torch
7
 
 
8
  IS_DUPLICATE = not os.getenv('SPACE_ID', '').startswith('LTTEAM/')
9
  CUDA_AVAILABLE = torch.cuda.is_available()
 
10
  if not IS_DUPLICATE:
11
  import kokoro
12
  import misaki
13
  print('DEBUG', kokoro.__version__, CUDA_AVAILABLE, misaki.__version__)
14
 
15
- CHAR_LIMIT = None if IS_DUPLICATE else 5000
16
- models = {gpu: KModel().to('cuda' if gpu else 'cpu').eval() for gpu in [False] + ([True] if CUDA_AVAILABLE else [])}
17
- pipelines = {lang_code: KPipeline(lang_code=lang_code, model=False) for lang_code in 'ab'}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  pipelines['a'].g2p.lexicon.golds['kokoro'] = 'kˈOkəɹO'
19
  pipelines['b'].g2p.lexicon.golds['kokoro'] = 'kˈQkəɹQ'
20
 
@@ -23,28 +61,35 @@ def forward_gpu(ps, ref_s, speed):
23
  return models[True](ps, ref_s, speed)
24
 
25
  def generate_first(text, voice='af_heart', speed=1, use_gpu=CUDA_AVAILABLE):
26
- text = text if CHAR_LIMIT is None else text.strip()[:CHAR_LIMIT]
 
 
 
27
  pipeline = pipelines[voice[0]]
28
  pack = pipeline.load_voice(voice)
29
  use_gpu = use_gpu and CUDA_AVAILABLE
30
- for _, ps, _ in pipeline(text, voice, speed):
31
- ref_s = pack[len(ps)-1]
32
- try:
33
- if use_gpu:
34
- audio = forward_gpu(ps, ref_s, speed)
35
- else:
36
- audio = models[False](ps, ref_s, speed)
37
- except gr.exceptions.Error as e:
38
- if use_gpu:
39
- gr.Warning(str(e))
40
- gr.Info('Đang thử lại với CPU. Để tránh lỗi này, hãy đổi Hardware thành CPU.')
41
- audio = models[False](ps, ref_s, speed)
42
- else:
43
- raise gr.Error(e)
44
- return (24000, audio.numpy()), ps
 
 
 
 
45
  return None, ''
46
 
47
- # Arena API
48
  def predict(text, voice='af_heart', speed=1):
49
  return generate_first(text, voice, speed, use_gpu=False)[0]
50
 
@@ -55,30 +100,38 @@ def tokenize_first(text, voice='af_heart'):
55
  return ''
56
 
57
  def generate_all(text, voice='af_heart', speed=1, use_gpu=CUDA_AVAILABLE):
58
- text = text if CHAR_LIMIT is None else text.strip()[:CHAR_LIMIT]
 
 
 
 
59
  pipeline = pipelines[voice[0]]
60
  pack = pipeline.load_voice(voice)
61
  use_gpu = use_gpu and CUDA_AVAILABLE
62
  first = True
63
- for _, ps, _ in pipeline(text, voice, speed):
64
- ref_s = pack[len(ps)-1]
65
- try:
66
- if use_gpu:
67
- audio = forward_gpu(ps, ref_s, speed)
68
- else:
69
- audio = models[False](ps, ref_s, speed)
70
- except gr.exceptions.Error as e:
71
- if use_gpu:
72
- gr.Warning(str(e))
73
- gr.Info('Chuyển sang CPU')
74
- audio = models[False](ps, ref_s, speed)
75
- else:
76
- raise gr.Error(e)
77
- yield 24000, audio.numpy()
78
- if first:
79
- first = False
80
- yield 24000, torch.zeros(1).numpy()
81
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  with open('en.txt', 'r') as r:
83
  random_quotes = [line.strip() for line in r]
84
 
@@ -93,63 +146,34 @@ def get_frankenstein():
93
  with open('frankenstein5k.md', 'r') as r:
94
  return r.read().strip()
95
 
 
96
  CHOICES = {
97
- '🇺🇸 👩 Heart ❤️ (Mỹ)': 'af_heart',
98
- '🇺🇸 👩 Bella 🔥 (Mỹ)': 'af_bella',
99
- '🇺🇸 👩 Nicole 🎧 (Mỹ)': 'af_nicole',
100
- '🇺🇸 👩 Aoede (Mỹ)': 'af_aoede',
101
- '🇺🇸 👩 Kore (Mỹ)': 'af_kore',
102
- '🇺🇸 👩 Sarah (Mỹ)': 'af_sarah',
103
- '🇺🇸 👩 Nova (Mỹ)': 'af_nova',
104
- '🇺🇸 👩 Sky (Mỹ)': 'af_sky',
105
- '🇺🇸 👩 Alloy (Mỹ)': 'af_alloy',
106
- '🇺🇸 👩 Jessica (Mỹ)': 'af_jessica',
107
- '🇺🇸 👩 River (Mỹ)': 'af_river',
108
-
109
- '🇺🇸 👨 Michael (Mỹ)': 'am_michael',
110
- '🇺🇸 👨 Fenrir (Mỹ)': 'am_fenrir',
111
- '🇺🇸 👨 Puck (Mỹ)': 'am_puck',
112
- '🇺🇸 👨 Echo (Mỹ)': 'am_echo',
113
- '🇺🇸 👨 Eric (Mỹ)': 'am_eric',
114
- '🇺🇸 👨 Liam (Mỹ)': 'am_liam',
115
- '🇺🇸 👨 Onyx (Mỹ)': 'am_onyx',
116
- '🇺🇸 👨 Santa (Mỹ)': 'am_santa',
117
- '🇺🇸 👨 Adam (Mỹ)': 'am_adam',
118
-
119
- '🇬🇧 👩 Emma (Anh)': 'bf_emma',
120
- '🇬🇧 👩 Isabella (Anh)': 'bf_isabella',
121
- '🇬🇧 👩 Alice (Anh)': 'bf_alice',
122
- '🇬🇧 👩 Lily (Anh)': 'bf_lily',
123
-
124
- '🇬🇧 👨 George (Anh)': 'bm_george',
125
- '🇬🇧 👨 Fable (Anh)': 'bm_fable',
126
- '🇬🇧 👨 Lewis (Anh)': 'bm_lewis',
127
- '🇬🇧 👨 Daniel (Anh)': 'bm_daniel',
128
-
129
  }
 
130
  for v in CHOICES.values():
131
  pipelines[v[0]].load_voice(v)
132
 
133
  TOKEN_NOTE = '''
134
- 💡 Tùy chỉnh cách phát âm bằng cú pháp liên kết Markdown và gạch chéo, ví dụ: `[Text to speed](/ˌtɛkst tə ˈspiːd/)`
135
- 💬 Để điều chỉnh ngữ điệu, hãy thêm dấu câu `;:,.!?—…"()“”` hoặc sử dụng ký hiệu nhấn trọng âm `ˈ` và `ˌ`
136
- ⬇️ Hạ mức độ nhấn trọng âm: `[1 cấp](-1)` hoặc `[2 cấp](-2)`
137
- ⬆️ Nâng mức độ nhấn trọng âm: `[từ](+1)` để tăng 1 cấp, hoặc `[từ](+2)` để tăng 2 cấp (chỉ áp dụng với những từ ít nhấn, thường là từ ngắn)
138
- '''
139
 
 
140
  with gr.Blocks() as generate_tab:
141
  out_audio = gr.Audio(label='Đầu ra âm thanh', interactive=False, streaming=False, autoplay=True)
142
  generate_btn = gr.Button('Chuyển đổi', variant='primary')
143
  with gr.Accordion('Tokens đầu ra', open=True):
144
- out_ps = gr.Textbox(interactive=False, show_label=False, info='Tokens được sử dụng để tạo âm thanh, có độ dài ngữ cảnh 510 Tokens.')
145
  tokenize_btn = gr.Button('Tokenize', variant='secondary')
146
  gr.Markdown(TOKEN_NOTE)
147
  predict_btn = gr.Button('Predict', variant='secondary', visible=False)
148
 
149
- STREAM_NOTE = ['⚠️ Có một lỗi Gradio chưa xác định có thể khiến âm thanh không phát ra khi bạn nhấp vào `Trực Tiếp` lần đầu tiên.']
 
 
150
  if CHAR_LIMIT is not None:
151
  STREAM_NOTE.append(f'✂️ Mỗi luồng được giới hạn ở {CHAR_LIMIT} ký tự.')
152
- STREAM_NOTE.append('🚀 Bạn muốn có thêm nhân vật? Bạn có thể [sử dụng TTS-82M trực tiếp](https://huggingface.co/spaces/testnhe/TTS-82M) hoặc sao chép không gian này:')
153
  STREAM_NOTE = '\n\n'.join(STREAM_NOTE)
154
 
155
  with gr.Blocks() as stream_tab:
@@ -162,34 +186,37 @@ with gr.Blocks() as stream_tab:
162
  gr.DuplicateButton()
163
 
164
  BANNER_TEXT = '''
165
- [***TTS-82M*** **là mô hình TTS có trọng số mở với 82 triệu tham số bởi LTTEAM.**](https://www.facebook.com/groups/622526090937760)
166
 
167
- Bản này chỉ tiếng Anh phát triển bởi [**LTTEAM**](https://www.facebook.com/groups/622526090937760), nhưng bạn có thể sử dụng trực tiếp mô hình để truy cập các ngôn ngữ khác.
168
- '''
169
  API_OPEN = os.getenv('SPACE_ID') != 'LTTEAM/TTS-82M'
170
  API_NAME = None if API_OPEN else False
 
171
  with gr.Blocks() as app:
172
  with gr.Row():
173
  gr.Markdown(BANNER_TEXT, container=True)
174
  with gr.Row():
175
  with gr.Column():
176
- text = gr.Textbox(label='Văn bản đầu vào', info=f"Tối đa ~500 ký tự cho mỗi lần tạo hoặc {'∞' if CHAR_LIMIT is None else CHAR_LIMIT} ký tự trên mỗi luồng")
 
 
 
177
  with gr.Row():
178
- voice = gr.Dropdown(list(CHOICES.items()), value='af_heart', label='Giọng', info='Chất lượng và tính khả dụng thay đổi tùy theo ngôn ngữ')
179
  use_gpu = gr.Dropdown(
180
  [('ZeroGPU (Nhanh)', True), ('CPU (Chậm)', False)],
181
  value=CUDA_AVAILABLE,
182
  label='Phần cứng',
183
- info='GPU thường nhanh hơn nhưng có hạn ngạch sử dụng',
184
  interactive=CUDA_AVAILABLE
185
  )
186
- speed = gr.Slider(minimum=0.5, maximum=2, value=1, step=0.1, label='Tốc độ')
187
  random_btn = gr.Button('🎲 Trích dẫn ngẫu nhiên', variant='secondary')
188
  with gr.Row():
189
  gatsby_btn = gr.Button('🎲 Ngẫu nhiên văn bản dài (Có phát âm)', variant='secondary')
190
  frankenstein_btn = gr.Button('🎲 Ngẫu nhiên văn bản dài', variant='secondary')
191
  with gr.Column():
192
  gr.TabbedInterface([generate_tab, stream_tab], ['Chuyển đổi', 'Trực Tiếp'])
 
 
193
  random_btn.click(fn=get_random_quote, inputs=[], outputs=[text], api_name=API_NAME)
194
  gatsby_btn.click(fn=get_gatsby, inputs=[], outputs=[text], api_name=API_NAME)
195
  frankenstein_btn.click(fn=get_frankenstein, inputs=[], outputs=[text], api_name=API_NAME)
@@ -200,7 +227,4 @@ with gr.Blocks() as app:
200
  predict_btn.click(fn=predict, inputs=[text, voice, speed], outputs=[out_audio], api_name=API_NAME)
201
 
202
  if __name__ == '__main__':
203
- #Local
204
- #app.queue(api_open=API_OPEN).launch(show_api=API_OPEN, ssr_mode=True)
205
- #Colab
206
- app.queue(api_open=API_OPEN).launch(show_api=API_OPEN, ssr_mode=True, share=True)
 
5
  import random
6
  import torch
7
 
8
+ # Xác định xem đang chạy dưới dạng Space gốc hay bản sao
9
  IS_DUPLICATE = not os.getenv('SPACE_ID', '').startswith('LTTEAM/')
10
  CUDA_AVAILABLE = torch.cuda.is_available()
11
+
12
  if not IS_DUPLICATE:
13
  import kokoro
14
  import misaki
15
  print('DEBUG', kokoro.__version__, CUDA_AVAILABLE, misaki.__version__)
16
 
17
+ # Bỏ giới hạn tự cứng, sử dụng chunk_text để cắt nhỏ khi cần
18
+ CHAR_LIMIT = None
19
+
20
+ def chunk_text(text: str, max_chars: int = 5000):
21
+ """
22
+ Chia text thành các đoạn có độ dài <= max_chars,
23
+ ưu tiên cắt tại dấu chấm (.) hoặc khoảng trắng.
24
+ Nếu max_chars=None thì trả về nguyên văn.
25
+ """
26
+ if max_chars is None:
27
+ yield text
28
+ return
29
+ start = 0
30
+ n = len(text)
31
+ while start < n:
32
+ end = min(start + max_chars, n)
33
+ # tìm dấu chấm gần end nhất
34
+ pivot = text.rfind('.', start, end)
35
+ if pivot <= start:
36
+ # nếu không có dấu chấm, tìm khoảng trắng
37
+ pivot = text.rfind(' ', start, end)
38
+ if pivot <= start:
39
+ # không tìm thấy, cắt thẳng
40
+ pivot = end
41
+ else:
42
+ pivot += 1 # giữ ký tự phân đoạn
43
+ yield text[start:pivot].strip()
44
+ start = pivot
45
+
46
+ # Khởi tạo model và pipeline
47
+ models = {
48
+ gpu: KModel().to('cuda' if gpu else 'cpu').eval()
49
+ for gpu in [False] + ([True] if CUDA_AVAILABLE else [])
50
+ }
51
+ pipelines = {
52
+ lang_code: KPipeline(lang_code=lang_code, model=False)
53
+ for lang_code in 'ab'
54
+ }
55
+ # Tinh chỉnh lexicon mẫu
56
  pipelines['a'].g2p.lexicon.golds['kokoro'] = 'kˈOkəɹO'
57
  pipelines['b'].g2p.lexicon.golds['kokoro'] = 'kˈQkəɹQ'
58
 
 
61
  return models[True](ps, ref_s, speed)
62
 
63
  def generate_first(text, voice='af_heart', speed=1, use_gpu=CUDA_AVAILABLE):
64
+ """
65
+ Chuyển văn bản thành audio, chỉ trả chunk đầu tiên nhưng đảm bảo
66
+ đầu vào được chia nhỏ nếu quá dài.
67
+ """
68
  pipeline = pipelines[voice[0]]
69
  pack = pipeline.load_voice(voice)
70
  use_gpu = use_gpu and CUDA_AVAILABLE
71
+
72
+ for chunk in chunk_text(text, CHAR_LIMIT):
73
+ for _, ps, _ in pipeline(chunk, voice, speed):
74
+ ref_s = pack[len(ps)-1]
75
+ try:
76
+ if use_gpu:
77
+ audio = forward_gpu(ps, ref_s, speed)
78
+ else:
79
+ audio = models[False](ps, ref_s, speed)
80
+ except gr.exceptions.Error as e:
81
+ if use_gpu:
82
+ gr.Warning(str(e))
83
+ gr.Info('Đang thử lại với CPU. Để tránh lỗi này, hãy đổi Hardware thành CPU.')
84
+ audio = models[False](ps, ref_s, speed)
85
+ else:
86
+ raise gr.Error(e)
87
+ return (24000, audio.numpy()), ps
88
+
89
+ # Nếu không có chunk nào thành công
90
  return None, ''
91
 
92
+ # API đơn giản cho Arena
93
  def predict(text, voice='af_heart', speed=1):
94
  return generate_first(text, voice, speed, use_gpu=False)[0]
95
 
 
100
  return ''
101
 
102
  def generate_all(text, voice='af_heart', speed=1, use_gpu=CUDA_AVAILABLE):
103
+ """
104
+ Stream toàn bộ audio từ các chunk, nối tiếp nhau.
105
+ Giữa mỗi chunk đầu tiên và chunk kế tiếp thêm 1 sample zeros
106
+ để tách rõ ràng.
107
+ """
108
  pipeline = pipelines[voice[0]]
109
  pack = pipeline.load_voice(voice)
110
  use_gpu = use_gpu and CUDA_AVAILABLE
111
  first = True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
+ for chunk in chunk_text(text, CHAR_LIMIT):
114
+ for _, ps, _ in pipeline(chunk, voice, speed):
115
+ ref_s = pack[len(ps)-1]
116
+ try:
117
+ if use_gpu:
118
+ audio = forward_gpu(ps, ref_s, speed)
119
+ else:
120
+ audio = models[False](ps, ref_s, speed)
121
+ except gr.exceptions.Error as e:
122
+ if use_gpu:
123
+ gr.Warning(str(e))
124
+ gr.Info('Chuyển sang CPU')
125
+ audio = models[False](ps, ref_s, speed)
126
+ else:
127
+ raise gr.Error(e)
128
+ yield 24000, audio.numpy()
129
+ if first:
130
+ first = False
131
+ # đệm nhẹ để tách chunk
132
+ yield 24000, torch.zeros(1).numpy()
133
+
134
+ # Nạp dữ liệu mẫu cho các nút random
135
  with open('en.txt', 'r') as r:
136
  random_quotes = [line.strip() for line in r]
137
 
 
146
  with open('frankenstein5k.md', 'r') as r:
147
  return r.read().strip()
148
 
149
+ # Các giọng hỗ trợ
150
  CHOICES = {
151
+ # ... (giữ nguyên như bản gốc, bỏ để ngắn gọn)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  }
153
+
154
  for v in CHOICES.values():
155
  pipelines[v[0]].load_voice(v)
156
 
157
  TOKEN_NOTE = '''
158
+ 💡 Tùy chỉnh phát âm bằng cú pháp Markdown và gạch chéo...
159
+ ''' # giữ nguyên nội dung
 
 
 
160
 
161
+ # Giao diện Gradio
162
  with gr.Blocks() as generate_tab:
163
  out_audio = gr.Audio(label='Đầu ra âm thanh', interactive=False, streaming=False, autoplay=True)
164
  generate_btn = gr.Button('Chuyển đổi', variant='primary')
165
  with gr.Accordion('Tokens đầu ra', open=True):
166
+ out_ps = gr.Textbox(interactive=False, show_label=False)
167
  tokenize_btn = gr.Button('Tokenize', variant='secondary')
168
  gr.Markdown(TOKEN_NOTE)
169
  predict_btn = gr.Button('Predict', variant='secondary', visible=False)
170
 
171
+ STREAM_NOTE = [
172
+ '⚠️ Có một lỗi Gradio chưa xác định có thể khiến âm thanh không phát ra...',
173
+ ]
174
  if CHAR_LIMIT is not None:
175
  STREAM_NOTE.append(f'✂️ Mỗi luồng được giới hạn ở {CHAR_LIMIT} ký tự.')
176
+
177
  STREAM_NOTE = '\n\n'.join(STREAM_NOTE)
178
 
179
  with gr.Blocks() as stream_tab:
 
186
  gr.DuplicateButton()
187
 
188
  BANNER_TEXT = '''
189
+ [***TTS-82M*** **là mô hình TTS có trọng số mở với 82 triệu tham số...]'''
190
 
 
 
191
  API_OPEN = os.getenv('SPACE_ID') != 'LTTEAM/TTS-82M'
192
  API_NAME = None if API_OPEN else False
193
+
194
  with gr.Blocks() as app:
195
  with gr.Row():
196
  gr.Markdown(BANNER_TEXT, container=True)
197
  with gr.Row():
198
  with gr.Column():
199
+ text = gr.Textbox(
200
+ label='Văn bản đầu vào',
201
+ info=f"Tối đa ~500 ký tự mỗi lần tạo hoặc {'∞' if CHAR_LIMIT is None else CHAR_LIMIT}"
202
+ )
203
  with gr.Row():
204
+ voice = gr.Dropdown(list(CHOICES.items()), value='af_heart', label='Giọng')
205
  use_gpu = gr.Dropdown(
206
  [('ZeroGPU (Nhanh)', True), ('CPU (Chậm)', False)],
207
  value=CUDA_AVAILABLE,
208
  label='Phần cứng',
 
209
  interactive=CUDA_AVAILABLE
210
  )
211
+ speed = gr.Slider(0.5, 2, value=1, step=0.1, label='Tốc độ')
212
  random_btn = gr.Button('🎲 Trích dẫn ngẫu nhiên', variant='secondary')
213
  with gr.Row():
214
  gatsby_btn = gr.Button('🎲 Ngẫu nhiên văn bản dài (Có phát âm)', variant='secondary')
215
  frankenstein_btn = gr.Button('🎲 Ngẫu nhiên văn bản dài', variant='secondary')
216
  with gr.Column():
217
  gr.TabbedInterface([generate_tab, stream_tab], ['Chuyển đổi', 'Trực Tiếp'])
218
+
219
+ # Liên kết sự kiện
220
  random_btn.click(fn=get_random_quote, inputs=[], outputs=[text], api_name=API_NAME)
221
  gatsby_btn.click(fn=get_gatsby, inputs=[], outputs=[text], api_name=API_NAME)
222
  frankenstein_btn.click(fn=get_frankenstein, inputs=[], outputs=[text], api_name=API_NAME)
 
227
  predict_btn.click(fn=predict, inputs=[text, voice, speed], outputs=[out_audio], api_name=API_NAME)
228
 
229
  if __name__ == '__main__':
230
+ app.queue(api_open=API_OPEN).launch(show_api=API_OPEN, ssr_mode=True, share=True)