openfree commited on
Commit
4c79f75
·
verified ·
1 Parent(s): df21e91

Delete app-backup.py

Browse files
Files changed (1) hide show
  1. app-backup.py +0 -324
app-backup.py DELETED
@@ -1,324 +0,0 @@
1
- import random
2
- import numpy as np
3
- import torch
4
- from chatterbox.src.chatterbox.tts import ChatterboxTTS
5
- import gradio as gr
6
- import spaces
7
- import re
8
-
9
- DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
10
- print(f"🚀 Running on device: {DEVICE}")
11
-
12
- # --- Global Model Initialization ---
13
- MODEL = None
14
-
15
- def get_or_load_model():
16
- """Loads the ChatterboxTTS model if it hasn't been loaded already,
17
- and ensures it's on the correct device."""
18
- global MODEL
19
- if MODEL is None:
20
- print("Model not loaded, initializing...")
21
- try:
22
- MODEL = ChatterboxTTS.from_pretrained(DEVICE)
23
- if hasattr(MODEL, 'to') and str(MODEL.device) != DEVICE:
24
- MODEL.to(DEVICE)
25
- print(f"Model loaded successfully. Internal device: {getattr(MODEL, 'device', 'N/A')}")
26
- except Exception as e:
27
- print(f"Error loading model: {e}")
28
- raise
29
- return MODEL
30
-
31
- # Attempt to load the model at startup.
32
- try:
33
- get_or_load_model()
34
- except Exception as e:
35
- print(f"CRITICAL: Failed to load model on startup. Application may not function. Error: {e}")
36
-
37
- def set_seed(seed: int):
38
- """Sets the random seed for reproducibility across torch, numpy, and random."""
39
- torch.manual_seed(seed)
40
- if DEVICE == "cuda":
41
- torch.cuda.manual_seed(seed)
42
- torch.cuda.manual_seed_all(seed)
43
- random.seed(seed)
44
- np.random.seed(seed)
45
-
46
- def split_text_into_chunks(text: str, max_chars: int = 250) -> list[str]:
47
- """
48
- 텍스트를 문장 단위로 나누되, 각 청크가 max_chars를 넘지 않도록 합니다.
49
- """
50
- # 문장 단위로 분리 (기본적인 문장 분리)
51
- sentences = re.split(r'(?<=[.!?])\s+', text.strip())
52
-
53
- chunks = []
54
- current_chunk = ""
55
-
56
- for sentence in sentences:
57
- # 현재 청크에 문장을 추가해도 max_chars를 넘지 않으면 추가
58
- if len(current_chunk) + len(sentence) + 1 <= max_chars:
59
- if current_chunk:
60
- current_chunk += " " + sentence
61
- else:
62
- current_chunk = sentence
63
- else:
64
- # 현재 청크를 저장하고 새 청크 시작
65
- if current_chunk:
66
- chunks.append(current_chunk)
67
-
68
- # 문장 자체가 max_chars보다 긴 경우 강제로 분할
69
- if len(sentence) > max_chars:
70
- words = sentence.split()
71
- temp_chunk = ""
72
- for word in words:
73
- if len(temp_chunk) + len(word) + 1 <= max_chars:
74
- if temp_chunk:
75
- temp_chunk += " " + word
76
- else:
77
- temp_chunk = word
78
- else:
79
- if temp_chunk:
80
- chunks.append(temp_chunk)
81
- temp_chunk = word
82
- if temp_chunk:
83
- current_chunk = temp_chunk
84
- else:
85
- current_chunk = sentence
86
-
87
- # 마지막 청크 추가
88
- if current_chunk:
89
- chunks.append(current_chunk)
90
-
91
- return chunks
92
-
93
- @spaces.GPU
94
- def generate_tts_audio(
95
- text_input: str,
96
- audio_prompt_path_input: str,
97
- exaggeration_input: float,
98
- temperature_input: float,
99
- seed_num_input: int,
100
- cfgw_input: float,
101
- chunk_size_input: int,
102
- progress=gr.Progress()
103
- ) -> tuple[int, np.ndarray]:
104
- """
105
- 긴 텍스트를 청크로 나누어 TTS 오디오를 생성하고 연결합니다.
106
- 모든 처리를 단일 GPU 컨텍스트 내에서 수행합니다.
107
- """
108
- current_model = get_or_load_model()
109
-
110
- if current_model is None:
111
- raise RuntimeError("TTS model is not loaded.")
112
-
113
- if seed_num_input != 0:
114
- set_seed(int(seed_num_input))
115
-
116
- # 텍스트를 청크로 분할
117
- chunks = split_text_into_chunks(text_input, max_chars=chunk_size_input)
118
- total_chunks = len(chunks)
119
-
120
- print(f"텍스트를 {total_chunks}개의 청크로 분할했습니다.")
121
-
122
- # 각 청크에 대해 오디오 생성
123
- audio_segments = []
124
-
125
- for i, chunk in enumerate(chunks):
126
- progress((i + 1) / total_chunks, f"청크 {i + 1}/{total_chunks} 생성 중...")
127
- print(f"청크 {i + 1}/{total_chunks} 생성 중: '{chunk[:50]}...'")
128
-
129
- try:
130
- # 직접 generate 메서드 호출 (별도 함수 없이)
131
- wav = current_model.generate(
132
- chunk,
133
- audio_prompt_path=audio_prompt_path_input,
134
- exaggeration=exaggeration_input,
135
- temperature=temperature_input,
136
- cfg_weight=cfgw_input,
137
- )
138
- wav_chunk = wav.squeeze(0).numpy()
139
- audio_segments.append(wav_chunk)
140
-
141
- except Exception as e:
142
- print(f"청크 {i + 1} 생성 중 오류 발생: {e}")
143
- # 오류 발생 시 계속 진행
144
- continue
145
-
146
- # 모든 오디오 세그먼트 연결
147
- if audio_segments:
148
- # 각 청크 사이에 짧은 무음 추가 (선택사항)
149
- silence_duration = int(0.2 * current_model.sr) # 0.2초 무음
150
- silence = np.zeros(silence_duration)
151
-
152
- final_audio = []
153
- for i, segment in enumerate(audio_segments):
154
- final_audio.append(segment)
155
- if i < len(audio_segments) - 1: # 마지막 세그먼트가 아니면 무음 추가
156
- final_audio.append(silence)
157
-
158
- concatenated_audio = np.concatenate(final_audio)
159
-
160
- print(f"오디오 생성 완료. 총 길이: {len(concatenated_audio) / current_model.sr:.2f}초")
161
- return (current_model.sr, concatenated_audio)
162
- else:
163
- raise RuntimeError("오디오 생성에 실패했습니다.")
164
-
165
- # 단일 청크 생성을 위한 간단한 wrapper 함수 (GPU 데코레이터 포함)
166
- @spaces.GPU
167
- def generate_single_audio(
168
- text_input: str,
169
- audio_prompt_path_input: str,
170
- exaggeration_input: float,
171
- temperature_input: float,
172
- seed_num_input: int,
173
- cfgw_input: float
174
- ) -> tuple[int, np.ndarray]:
175
- """
176
- 단일 텍스트에 대한 TTS 오디오 생성 (300자 이하)
177
- """
178
- current_model = get_or_load_model()
179
-
180
- if current_model is None:
181
- raise RuntimeError("TTS model is not loaded.")
182
-
183
- if seed_num_input != 0:
184
- set_seed(int(seed_num_input))
185
-
186
- print(f"Generating audio for text: '{text_input[:50]}...'")
187
- wav = current_model.generate(
188
- text_input[:300], # 안전을 위해 300자로 제한
189
- audio_prompt_path=audio_prompt_path_input,
190
- exaggeration=exaggeration_input,
191
- temperature=temperature_input,
192
- cfg_weight=cfgw_input,
193
- )
194
- print("Audio generation complete.")
195
- return (current_model.sr, wav.squeeze(0).numpy())
196
-
197
- with gr.Blocks() as demo:
198
- gr.Markdown(
199
- """
200
- # Chatterbox TTS Demo - 무제한 길이 버전
201
- 긴 텍스트도 청크로 나누어 처리하여 제한 없이 음성을 생성합니다.
202
- """
203
- )
204
- with gr.Row():
205
- with gr.Column():
206
- text = gr.Textbox(
207
- value="Now let's make my mum's favourite. So three mars bars into the pan. Then we add the tuna and just stir for a bit, just let the chocolate and fish infuse. A sprinkle of olive oil and some tomato ketchup. Now smell that. Oh boy this is going to be incredible.",
208
- label="텍스트 입력 (길이 제한 없음)",
209
- lines=10,
210
- max_lines=30
211
- )
212
- ref_wav = gr.Audio(
213
- sources=["upload", "microphone"],
214
- type="filepath",
215
- label="Reference Audio File (Optional)",
216
- value="https://storage.googleapis.com/chatterbox-demo-samples/prompts/female_shadowheart4.flac"
217
- )
218
-
219
- with gr.Row():
220
- exaggeration = gr.Slider(
221
- 0.25, 2, step=.05,
222
- label="Exaggeration (Neutral = 0.5)",
223
- value=.5
224
- )
225
- cfg_weight = gr.Slider(
226
- 0.2, 1, step=.05,
227
- label="CFG/Pace",
228
- value=0.5
229
- )
230
-
231
- with gr.Row():
232
- chunk_size = gr.Slider(
233
- 100, 300, step=50,
234
- label="청크 크기 (문자 수)",
235
- value=250,
236
- info="텍스트를 나눌 청크의 최대 크기입니다. 작을수록 더 자연스럽지만 처리 시간이 길어집니다."
237
- )
238
- mode = gr.Radio(
239
- choices=["단일 생성 (300자 이하)", "청크 분할 (무제한)"],
240
- value="청크 분할 (무제한)",
241
- label="생성 모드"
242
- )
243
-
244
- with gr.Accordion("고급 옵션", open=False):
245
- seed_num = gr.Number(value=0, label="Random seed (0 for random)")
246
- temp = gr.Slider(0.05, 5, step=.05, label="Temperature", value=.8)
247
-
248
- run_btn = gr.Button("음성 생성", variant="primary")
249
-
250
- with gr.Column():
251
- audio_output = gr.Audio(label="생성된 음성")
252
-
253
- # 텍스트 길이 표시
254
- char_count = gr.Textbox(
255
- label="텍스트 정보",
256
- value="0 문자, 약 0개 청크",
257
- interactive=False
258
- )
259
-
260
- # 텍스트 입력 시 문자 수와 예상 청크 수 업데이트
261
- def update_char_count(text, chunk_size, mode):
262
- char_len = len(text)
263
- if mode == "단일 생성 (300자 이하)":
264
- if char_len > 300:
265
- return f"{char_len} 문자 (⚠️ 300자 초과 - 잘릴 수 있음)"
266
- else:
267
- return f"{char_len} 문자"
268
- else:
269
- chunks = split_text_into_chunks(text, max_chars=chunk_size)
270
- chunk_count = len(chunks)
271
- return f"{char_len} 문자, 약 {chunk_count}개 청크로 분할됨"
272
-
273
- text.change(
274
- fn=update_char_count,
275
- inputs=[text, chunk_size, mode],
276
- outputs=[char_count]
277
- )
278
-
279
- chunk_size.change(
280
- fn=update_char_count,
281
- inputs=[text, chunk_size, mode],
282
- outputs=[char_count]
283
- )
284
-
285
- mode.change(
286
- fn=update_char_count,
287
- inputs=[text, chunk_size, mode],
288
- outputs=[char_count]
289
- )
290
-
291
- # 모드에 따라 다른 함수 호출
292
- def process_audio(text, ref_wav, exaggeration, temp, seed_num, cfg_weight, chunk_size, mode):
293
- if mode == "단일 생성 (300자 이하)":
294
- return generate_single_audio(text, ref_wav, exaggeration, temp, seed_num, cfg_weight)
295
- else:
296
- return generate_tts_audio(text, ref_wav, exaggeration, temp, seed_num, cfg_weight, chunk_size)
297
-
298
- run_btn.click(
299
- fn=process_audio,
300
- inputs=[
301
- text,
302
- ref_wav,
303
- exaggeration,
304
- temp,
305
- seed_num,
306
- cfg_weight,
307
- chunk_size,
308
- mode
309
- ],
310
- outputs=[audio_output],
311
- )
312
-
313
- gr.Markdown(
314
- """
315
- ### 사용 팁:
316
- - **단일 생성 모드**: 300자 이하의 짧은 텍스트에 적합하며 빠르게 생성됩니다
317
- - **청크 분할 모드**: 긴 텍스트를 자동으로 여러 부분으로 나누어 처리합니다
318
- - 청크 크기를 조절하여 품질과 속도의 균형을 맞출 수 있습니다
319
- - 각 청크 사이에는 자연스러운 전환을 위해 짧은 무음이 추가됩니다
320
- - 매우 긴 텍스트의 경우 처리 시간이 오래 걸릴 수 있습니다
321
- """
322
- )
323
-
324
- demo.launch()