Spaces:
M4xjunior
/
Running on Zero

File size: 10,025 Bytes
a679a35
 
175a497
a679a35
175a497
2ebd303
dd217c7
d24a68b
e35df77
2ebd303
d24a68b
dd217c7
 
d24a68b
 
dd217c7
2ebd303
a679a35
dd217c7
 
a679a35
dd217c7
 
 
 
a679a35
dd217c7
 
 
 
 
 
7804f9c
a679a35
 
 
 
 
 
 
 
 
 
 
 
 
15e444a
6249865
9bf0190
a679a35
15e444a
6249865
 
 
15e444a
 
 
a679a35
 
 
6249865
a679a35
 
 
7804f9c
a679a35
 
9bf0190
a679a35
9bf0190
a674527
a679a35
 
9bf0190
a679a35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
889c030
a679a35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
889c030
a679a35
 
 
 
 
 
 
 
 
889c030
338cecc
a679a35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d986efa
 
 
9bf0190
e09b9e9
d986efa
 
a679a35
d986efa
e09b9e9
 
 
d986efa
 
 
 
0559e57
d986efa
 
 
 
4dab15f
d986efa
 
 
 
 
 
 
0cc615c
a679a35
d986efa
 
 
 
 
 
 
 
26a9ffe
 
1384004
 
 
 
26a9ffe
1384004
ebbe300
 
 
 
 
 
 
 
e09b9e9
d986efa
889c030
d986efa
26a9ffe
d986efa
 
 
4dab15f
d986efa
 
d4320d1
26a9ffe
a679a35
d986efa
26a9ffe
d986efa
9bf0190
26a9ffe
 
 
 
 
9bf0190
26a9ffe
d986efa
26a9ffe
a679a35
 
 
 
 
 
 
 
f68e5ad
d986efa
a679a35
 
9bf0190
26a9ffe
d986efa
 
dce6ff7
a679a35
 
 
 
 
 
dce6ff7
d986efa
 
a679a35
d986efa
83c804a
 
 
a679a35
 
83c804a
 
 
 
 
 
 
 
 
 
ebbe300
83c804a
a679a35
 
83c804a
 
a679a35
 
f68e5ad
83c804a
 
a679a35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9bf0190
a679a35
a674527
 
 
 
 
7341fd9
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
import random
import sys
import nltk

nltk.download('punkt_tab')
from sentence_analyzer import SentenceAnalyzer
import re
import tempfile
from collections import OrderedDict
from importlib.resources import files
import click
import gradio as gr
import numpy as np
import soundfile as sf
import torchaudio
from cached_path import cached_path
from transformers import AutoModelForCausalLM, AutoTokenizer

try:
    import spaces

    USING_SPACES = True
except ImportError:
    USING_SPACES = False


def gpu_decorator(func):
    if USING_SPACES:
        return spaces.GPU(func)
    else:
        return func


from f5_tts.model import DiT, UNetT
from f5_tts.model.utils import seed_everything
from f5_tts.infer.utils_infer import (
    load_vocoder,
    load_model,
    preprocess_ref_audio_text,
    infer_process,
    remove_silence_for_generated_wav,
    save_spectrogram,
)

# Carregar vocoder
vocoder = load_vocoder()
import os
from huggingface_hub import hf_hub_download


def load_f5tts():
    # Carrega o caminho do repositório e o nome do arquivo das variáveis de ambiente
    repo_id = os.getenv("MODEL_REPO_ID", "SWivid/F5-TTS/F5TTS_Base")
    filename = os.getenv("MODEL_FILENAME", "model_1200000.safetensors")
    token = os.getenv("HUGGINGFACE_TOKEN")
    # Valida se o token está presente
    if not token:
        raise ValueError(
            "A variável de ambiente 'HUGGINGFACE_TOKEN' não foi definida."
        )
    # Faz o download do modelo do repositório privado
    ckpt_path = hf_hub_download(
        repo_id=repo_id, filename=filename, use_auth_token=token
    )

    F5TTS_model_cfg = dict(
        dim=1024, depth=22, heads=16, ff_mult=2, text_dim=512, conv_layers=4
    )
    return load_model(DiT, F5TTS_model_cfg, ckpt_path, use_ema=True)


# Carregar modelo F5TTS
F5TTS_ema_model = load_f5tts()

@gpu_decorator
def infer(
    ref_audio_orig,
    ref_text,
    gen_text,
    remove_silence,
    cross_fade_duration=0.15,
    speed=1,
    nfe=32,
    show_info=gr.Info,
    seed=-1,
):
    if seed == -1:
        seed = random.randint(0, sys.maxsize)
    seed_everything(seed)

    print(f"Usando seed: {seed}")
    ref_audio, ref_text = preprocess_ref_audio_text(
        ref_audio_orig, ref_text, show_info=show_info
    )
    ema_model = F5TTS_ema_model
    final_wave, final_sample_rate, combined_spectrogram = infer_process(
        ref_audio,
        ref_text.lower().strip(),
        gen_text.lower().strip(),
        ema_model,
        vocoder,
        cross_fade_duration=cross_fade_duration,
        nfe_step=nfe,
        speed=speed,
        show_info=show_info,
        progress=gr.Progress(),
    )
    # Remover silêncios
    if remove_silence:
        with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as f:
            sf.write(f.name, final_wave, final_sample_rate)
            remove_silence_for_generated_wav(f.name)
            final_wave, _ = torchaudio.load(f.name)
        final_wave = final_wave.squeeze().cpu().numpy()
    # Salvar espectrograma
    with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_spectrogram:
        spectrogram_path = tmp_spectrogram.name
        save_spectrogram(combined_spectrogram, spectrogram_path)
    return (final_sample_rate, final_wave), spectrogram_path, ref_text, seed


# Estilos CSS
custom_css = """
#sentences-container {
    border: 1px solid #ddd;
    border-radius: 4px;
    padding: 10px;
    margin-bottom: 10px;
}
.sentence-box {
    border: 1px solid #eee;
    padding: 5px;
    margin-bottom: 5px;
    border-radius: 4px;
    background-color: #f9f9f9;
}
"""
with gr.Blocks(css=custom_css) as app:
    with gr.Tabs():
        with gr.Tab("TTS Básico"):
            gr.Markdown("# TTS Básico com F5-TTS")

            # Entradas básicas
            ref_audio_input = gr.Audio(label="Áudio de Referência", type="filepath")
            gen_text_input = gr.Textbox(label="Texto para Gerar", lines=10)
            seed_input = gr.Number(label="Seed (opcional)", value=-1)
            generate_btn = gr.Button("Sintetizar", variant="primary")
            # Configurações avançadas
            gr.Markdown("### Configurações Avançadas")
            with gr.Accordion("Expandir Configurações Avançadas", open=False):
                ref_text_input = gr.Textbox(
                    label="Texto de Referência",
                    info="Deixe em branco para transcrever automaticamente o áudio de referência. Se você inserir texto, ele substituirá a transcrição automática.",
                    lines=2,
                )
                remove_silence = gr.Checkbox(
                    label="Remover Silêncios",
                    info="O modelo tende a produzir silêncios, especialmente em áudios mais longos. Podemos remover manualmente os silêncios, se necessário. Isso também aumentará o tempo de geração.",
                    value=False,
                )
                speed_slider = gr.Slider(
                    label="Velocidade",
                    minimum=0.3,
                    maximum=2.0,
                    value=1.0,
                    step=0.1,
                    info="Ajuste a velocidade do áudio.",
                )

                cross_fade_duration_slider = gr.Slider(
                    label="Duração do Cross-fade (s)",
                    minimum=0.0,
                    maximum=1.0,
                    value=0.15,
                    step=0.01,
                    info="Defina a duração do cross-fade entre os clipes de áudio.",
                )
                chunk_size_slider = gr.Slider(
                    label="Número de Sentenças por Chunk",
                    minimum=1,
                    maximum=10,
                    value=1,
                    step=1,
                    info="Defina quantas sentenças serão processadas em cada chunk.",
                )
                nfe_slider = gr.Slider(
                    label="NFE",
                    minimum=16,
                    maximum=64,
                    value=32,
                    step=1,
                    info="Ajuste NFE Step.",
                )

            analyzer = SentenceAnalyzer()

            @gpu_decorator
            def process_chunks(
                ref_audio_input,
                ref_text_input,
                gen_text_input,
                remove_silence,
                cross_fade_duration_slider,
                speed_slider,
                nfe_slider,
                chunk_size_slider,
                seed_input,
            ):
                # Dividir o texto em sentenças
                sentences = analyzer.split_into_sentences(gen_text_input)

                # Agrupar sentenças em chunks
                chunks = [
                    " ".join(sentences[i : i + chunk_size_slider])
                    for i in range(0, len(sentences), chunk_size_slider)
                ]

                # Processar cada chunk
                audio_segments = []
                for chunk in chunks:
                    audio_out, spectrogram_path, ref_text_out, seed_used = infer(
                        ref_audio_input,
                        ref_text_input,  # Utiliza o Texto de Referência como está
                        chunk,  # Processa o chunk atual
                        remove_silence,
                        cross_fade_duration_slider,
                        speed_slider,
                        nfe_slider,
                        seed=seed_input,
                    )
                    sr, audio_data = audio_out
                    audio_segments.append(audio_data)

                # Concatenar os segmentos de áudio gerados
                if audio_segments:
                    final_audio_data = np.concatenate(audio_segments)
                    return (
                        (sr, final_audio_data),  # Áudio final
                        spectrogram_path,  # Espectrograma
                        gr.update(
                            value=ref_text_out
                        ),  # Nenhuma mudança no Texto de Referência
                        gr.update(value=seed_used),
                    )
                else:
                    gr.Warning("Nenhum áudio gerado.")
                    return None, None, gr.update(), gr.update()

            # Saídas
            gr.Markdown("### Resultados")
            audio_output = gr.Audio(label="Áudio Sintetizado")
            spectrogram_output = gr.Image(label="Espectrograma")
            seed_output = gr.Number(label="Seed Usada")
            # Associação do botão `generate_btn` à função `process_chunks`
            generate_btn.click(
                process_chunks,
                inputs=[
                    ref_audio_input,
                    ref_text_input,
                    gen_text_input,
                    remove_silence,
                    cross_fade_duration_slider,
                    speed_slider,
                    nfe_slider,
                    chunk_size_slider,
                    seed_input,
                ],
                outputs=[
                    audio_output,
                    spectrogram_output,
                    ref_text_input,  # Atualiza o texto de referência, se necessário
                    seed_output,
                ],
            )


@click.command()
@click.option("--port", "-p", default=None, type=int, help="Port to run the app on")
@click.option("--host", "-H", default=None, help="Host to run the app on")
@click.option(
    "--share",
    "-s",
    default=False,
    is_flag=True,
    help="Share the app via Gradio share link",
)
@click.option("--api", "-a", default=True, is_flag=True, help="Allow API access")
def main(port, host, share, api):
    global app
    print("Starting app...")
    app.queue(api_open=api).launch(
        server_name=host, server_port=port, share=share, show_api=api
    )


if __name__ == "__main__":
    if not USING_SPACES:
        main()
    else:
        app.queue().launch()