File size: 10,911 Bytes
175a497
 
fd6544f
2ebd303
dd217c7
d24a68b
e35df77
2ebd303
d24a68b
dd217c7
 
d24a68b
 
f8da648
dd217c7
2ebd303
 
dd217c7
 
 
 
 
 
 
 
 
 
 
 
9bf0190
 
c6b897d
7804f9c
15e444a
6249865
9bf0190
15e444a
6249865
 
 
15e444a
 
 
 
6249865
 
9bf0190
 
7804f9c
 
9bf0190
 
e82eef5
9bf0190
e6f3513
08eb64a
9bf0190
 
 
 
7804f9c
a674527
9bf0190
 
 
 
 
 
 
a674527
0978fba
c6b897d
0978fba
c6b897d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a674527
d986efa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4dab15f
0559e57
d986efa
 
 
 
9bf0190
e09b9e9
d986efa
 
 
 
e09b9e9
 
 
d986efa
 
 
 
0559e57
d986efa
 
 
 
4dab15f
d986efa
 
 
 
 
 
 
0cc615c
d986efa
 
 
 
 
 
 
 
26a9ffe
 
1384004
 
 
 
26a9ffe
1384004
ebbe300
 
 
 
 
 
 
 
9bf0190
e09b9e9
d986efa
4dab15f
d986efa
26a9ffe
d986efa
 
 
4dab15f
d986efa
 
d4320d1
26a9ffe
9bf0190
d986efa
26a9ffe
d986efa
9bf0190
26a9ffe
 
 
 
 
9bf0190
26a9ffe
d986efa
26a9ffe
c6b897d
 
 
 
 
 
d986efa
c6b897d
d4320d1
c6b897d
 
 
 
d986efa
c6b897d
 
9bf0190
26a9ffe
d986efa
 
dce6ff7
c6b897d
 
 
 
dce6ff7
d986efa
 
9bf0190
d986efa
83c804a
 
 
9bf0190
1384004
83c804a
 
 
 
 
 
 
 
 
 
ebbe300
83c804a
9bf0190
83c804a
 
 
c6b897d
83c804a
9bf0190
83c804a
 
9bf0190
c6b897d
9bf0190
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a674527
 
 
 
 
175a497
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
import nltk
nltk.download('punkt_tab')
#tr
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
import torch
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

# Importando a nova API F5TTS
from f5_tts.api import F5TTS
from f5_tts.infer.utils_infer import preprocess_ref_audio_text 

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)

    # Define as configurações do modelo (ajuste se necessário)
    F5TTS_model_cfg = dict(dim=1024, depth=22, heads=16, ff_mult=2, text_dim=512, conv_layers=4)

    # Retorna a instância da API F5TTS
    return F5TTS(
        model_type="F5-TTS",  # Ajuste o nome do modelo se necessário
        ckpt_file=ckpt_path,
        vocab_file="/home/user/app/data/Emilia_ZH_EN_pinyin/vocab.txt", # Caminho para o arquivo vocab.txt
        device="cuda" if torch.cuda.is_available() else "cpu", # Define o dispositivo
        use_ema=True
    )

# Carregar modelo F5TTS usando a nova API
F5TTS_ema_model = load_f5tts()

# Variáveis globais para o cache
last_checkpoint = None
last_device = None
last_ema = None
tts_api = None
training_process = None  # Adicione esta linha se necessário para o seu contexto

@gpu_decorator
def infer(
    project, file_checkpoint, exp_name, ref_text, ref_audio, gen_text, nfe_step, use_ema, speed, seed, remove_silence
):
    global last_checkpoint, last_device, tts_api, last_ema
    if not os.path.isfile(file_checkpoint):
        return None, "checkpoint not found!"
    if training_process is not None:
        device_test = "cpu"
    else:
        device_test = None
    if last_checkpoint != file_checkpoint or last_device != device_test or last_ema != use_ema or tts_api is None:
        if last_checkpoint != file_checkpoint:
            last_checkpoint = file_checkpoint
        if last_device != device_test:
            last_device = device_test
        if last_ema != use_ema:
            last_ema = use_ema
        vocab_file = "/home/user/app/data/Emilia_ZH_EN_pinyin/vocab.txt" 
        tts_api = F5TTS(
            model_type=exp_name, ckpt_file=file_checkpoint, vocab_file=vocab_file, device=device_test, use_ema=use_ema
        )
        print("update >> ", device_test, file_checkpoint, use_ema)
    with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as f:
        tts_api.infer(
            gen_text=gen_text.lower().strip(),
            ref_text=ref_text.lower().strip(),
            ref_file=ref_audio,
            nfe_step=nfe_step,
            file_wave=f.name,
            speed=speed,
            seed=seed,
            remove_silence=remove_silence,
        )
        return f.name, tts_api.device, str(tts_api.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)
            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.",
                )
                seed_input = gr.Number(label="Seed", value=-1, minimum=-1)  # Seed na seção avançada

            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,  # Passando o seed para process_chunks
            ):
                # 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:
                    # Usando a função infer correta aqui
                    audio_file, device_used, seed_used = infer(
                        "Emilia_ZH_EN_pinyin", # Substitua pelo nome do seu projeto
                        "/home/user/app/model_1200000.safetensors", # Substitua pelo caminho do seu checkpoint
                        "F5-TTS", # Ou "E2-TTS" dependendo do seu modelo
                        ref_text_input,
                        ref_audio_input,
                        chunk,
                        nfe_slider,
                        True, # use_ema - ajuste se necessário
                        speed_slider,
                        seed_input,
                        remove_silence,
                    )
                    audio_data, _ = torchaudio.load(audio_file)
                    audio_segments.append(audio_data.squeeze().cpu().numpy())

                # Concatenar os segmentos de áudio gerados
                if audio_segments:
                    final_audio_data = np.concatenate(audio_segments)
                    return (
                        (24000, final_audio_data),  # Áudio final - assumindo taxa de amostragem de 24000
                        None,  # Espectrograma - não estamos gerando um espectrograma aqui
                        gr.update(value=ref_text_input),  # Nenhuma mudança no Texto de Referência
                        seed_used  # Retornando o seed
                    )
                else:
                    gr.Warning("Nenhum áudio gerado.")
                    return None, None, gr.update(), None  # Retornando None para o seed

            # Saídas
            gr.Markdown("### Resultados")
            audio_output = gr.Audio(label="Áudio Sintetizado")
            seed_output = gr.Text(label="Seed usada:")  # Saída do Seed

            # 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,  # Passando o seed como entrada
                ],
                outputs=[
                    audio_output,
                    None,  # Não estamos usando a saída do espectrograma
                    ref_text_input,  # Atualiza o texto de referência, se necessário
                    seed_output,  # Saída do Seed
                ],
            )

# Código para iniciar a aplicação Gradio
    @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()