CoolFace
Apppublic

ThreadAbort/E2-F5-TTS

sourceHugging Faceupdated 1y agoView on Hugging Face
26likes
app_local.py219 linesDownload Raw Back to root
1print("WARNING: You are running this unofficial E2/F5 TTS demo locally, it may not be as up-to-date as the hosted version (https://huggingface.co/spaces/mrfakename/E2-F5-TTS)")2 3import os4import re5import torch6import torchaudio7import gradio as gr8import numpy as np9import tempfile10from einops import rearrange11from ema_pytorch import EMA12from vocos import Vocos13from pydub import AudioSegment14from model import CFM, UNetT, DiT, MMDiT15from cached_path import cached_path16from model.utils import (17    get_tokenizer, 18    convert_char_to_pinyin, 19    save_spectrogram,20)21from transformers import pipeline22import librosa23from txtsplit import txtsplit24 25device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"26 27pipe = pipeline(28    "automatic-speech-recognition",29    model="openai/whisper-large-v3-turbo",30    torch_dtype=torch.float16,31    device=device,32)33 34# --------------------- Settings -------------------- #35 36target_sample_rate = 2400037n_mel_channels = 10038hop_length = 25639target_rms = 0.140nfe_step = 32  # 16, 3241cfg_strength = 2.042ode_method = 'euler'43sway_sampling_coef = -1.044speed = 1.045# fix_duration = 27  # None or float (duration in seconds)46fix_duration = None47 48def load_model(exp_name, model_cls, model_cfg, ckpt_step):49    checkpoint = torch.load(str(cached_path(f"hf://SWivid/F5-TTS/{exp_name}/model_{ckpt_step}.pt")), map_location=device)50    vocab_char_map, vocab_size = get_tokenizer("Emilia_ZH_EN", "pinyin")51    model = CFM(52        transformer=model_cls(53            **model_cfg,54            text_num_embeds=vocab_size,55            mel_dim=n_mel_channels56        ),57        mel_spec_kwargs=dict(58            target_sample_rate=target_sample_rate,59            n_mel_channels=n_mel_channels,60            hop_length=hop_length,61        ),62        odeint_kwargs=dict(63            method=ode_method,64        ),65        vocab_char_map=vocab_char_map,66    ).to(device)67 68    ema_model = EMA(model, include_online_model=False).to(device)69    ema_model.load_state_dict(checkpoint['ema_model_state_dict'])70    ema_model.copy_params_from_ema_to_model()71 72    return ema_model, model73 74# load models75F5TTS_model_cfg = dict(dim=1024, depth=22, heads=16, ff_mult=2, text_dim=512, conv_layers=4)76E2TTS_model_cfg = dict(dim=1024, depth=24, heads=16, ff_mult=4)77 78F5TTS_ema_model, F5TTS_base_model = load_model("F5TTS_Base", DiT, F5TTS_model_cfg, 1200000)79E2TTS_ema_model, E2TTS_base_model = load_model("E2TTS_Base", UNetT, E2TTS_model_cfg, 1200000)80 81def infer(ref_audio_orig, ref_text, gen_text, exp_name, remove_silence, progress = gr.Progress()):82    print(gen_text)83    gr.Info("Converting audio...")84    with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as f:85        aseg = AudioSegment.from_file(ref_audio_orig)86        # Convert to mono87        aseg = aseg.set_channels(1)88        audio_duration = len(aseg)89        if audio_duration > 15000:90            gr.Warning("Audio is over 15s, clipping to only first 15s.")91            aseg = aseg[:15000]92        aseg.export(f.name, format="wav")93        ref_audio = f.name94    if exp_name == "F5-TTS":95        ema_model = F5TTS_ema_model96        base_model = F5TTS_base_model97    elif exp_name == "E2-TTS":98        ema_model = E2TTS_ema_model99        base_model = E2TTS_base_model100    101    if not ref_text.strip():102        gr.Info("No reference text provided, transcribing reference audio...")103        ref_text = outputs = pipe(104            ref_audio,105            chunk_length_s=30,106            batch_size=128,107            generate_kwargs={"task": "transcribe"},108            return_timestamps=False,109        )['text'].strip()110        gr.Info("Finished transcription")111    else:112        gr.Info("Using custom reference text...")113    audio, sr = torchaudio.load(ref_audio)114    # Audio115    if audio.shape[0] > 1:116        audio = torch.mean(audio, dim=0, keepdim=True)117    rms = torch.sqrt(torch.mean(torch.square(audio)))118    if rms < target_rms:119        audio = audio * target_rms / rms120    if sr != target_sample_rate:121        resampler = torchaudio.transforms.Resample(sr, target_sample_rate)122        audio = resampler(audio)123    audio = audio.to(device)124    # Chunk125    chunks = txtsplit(gen_text, 100, 150) # 100 chars preferred, 150 max126    results = []127    generated_mel_specs = []128    for chunk in progress.tqdm(chunks):129        # Prepare the text130        text_list = [ref_text + chunk]131        final_text_list = convert_char_to_pinyin(text_list)132    133        # Calculate duration134        ref_audio_len = audio.shape[-1] // hop_length135        # if fix_duration is not None:136        #     duration = int(fix_duration * target_sample_rate / hop_length)137        # else:138        zh_pause_punc = r"。,、;:?!"139        ref_text_len = len(ref_text) + len(re.findall(zh_pause_punc, ref_text))140        gen_text_len = len(gen_text) + len(re.findall(zh_pause_punc, gen_text))141        duration = ref_audio_len + int(ref_audio_len / ref_text_len * gen_text_len / speed)142    143        # inference144        gr.Info(f"Generating audio using {exp_name}")145        with torch.inference_mode():146            generated, _ = base_model.sample(147                cond=audio,148                text=final_text_list,149                duration=duration,150                steps=nfe_step,151                cfg_strength=cfg_strength,152                sway_sampling_coef=sway_sampling_coef,153            )154    155        generated = generated[:, ref_audio_len:, :]156        generated_mel_spec = rearrange(generated, '1 n d -> 1 d n')157        gr.Info("Running vocoder")158        vocos = Vocos.from_pretrained("charactr/vocos-mel-24khz")159        generated_wave = vocos.decode(generated_mel_spec.cpu())160        if rms < target_rms:161            generated_wave = generated_wave * rms / target_rms162    163        # wav -> numpy164        generated_wave = generated_wave.squeeze().cpu().numpy()165        results.append(generated_wave)166    generated_wave = np.concatenate(results)167    if remove_silence:168        gr.Info("Removing audio silences... This may take a moment")169        non_silent_intervals = librosa.effects.split(generated_wave, top_db=30)170        non_silent_wave = np.array([])171        for interval in non_silent_intervals:172            start, end = interval173            non_silent_wave = np.concatenate([non_silent_wave, generated_wave[start:end]])174        generated_wave = non_silent_wave175 176 177    # spectogram178    # with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_spectrogram:179    #     spectrogram_path = tmp_spectrogram.name180    #     save_spectrogram(generated_mel_spec[0].cpu().numpy(), spectrogram_path)181 182    return (target_sample_rate, generated_wave)183 184with gr.Blocks() as app:185    gr.Markdown("""186# E2/F5 TTS187 188This is an unofficial E2/F5 TTS demo. This demo supports the following TTS models:189 190* [E2-TTS](https://arxiv.org/abs/2406.18009) (Embarrassingly Easy Fully Non-Autoregressive Zero-Shot TTS)191* [F5-TTS](https://arxiv.org/abs/2410.06885) (A Fairytaler that Fakes Fluent and Faithful Speech with Flow Matching)192 193This demo is based on the [F5-TTS](https://github.com/SWivid/F5-TTS) codebase, which is based on an [unofficial E2-TTS implementation](https://github.com/lucidrains/e2-tts-pytorch).194 195The checkpoints support English and Chinese.196 197If you're having issues, try converting your reference audio to WAV or MP3, clipping it to 15s, and shortening your prompt. If you're still running into issues, please open a [community Discussion](https://huggingface.co/spaces/mrfakename/E2-F5-TTS/discussions).198 199Long-form/batched inference + speech editing is coming soon!200 201**NOTE: Reference text will be automatically transcribed with Whisper if not provided. For best results, keep your reference clips short (<15s). Ensure the audio is fully uploaded before generating.**202""")203 204    ref_audio_input = gr.Audio(label="Reference Audio", type="filepath")205    gen_text_input = gr.Textbox(label="Text to Generate (longer text will use chunking)", lines=4)206    model_choice = gr.Radio(choices=["F5-TTS", "E2-TTS"], label="Choose TTS Model", value="F5-TTS")207    generate_btn = gr.Button("Synthesize", variant="primary")208    with gr.Accordion("Advanced Settings", open=False):209        ref_text_input = gr.Textbox(label="Reference Text", info="Leave blank to automatically transcribe the reference audio. If you enter text it will override automatic transcription.", lines=2)210        remove_silence = gr.Checkbox(label="Remove Silences", info="The model tends to produce silences, especially on longer audio. We can manually remove silences if needed. Note that this is an experimental feature and may produce strange results. This will also increase generation time.", value=True)211    212    audio_output = gr.Audio(label="Synthesized Audio")213    # spectrogram_output = gr.Image(label="Spectrogram")214 215    generate_btn.click(infer, inputs=[ref_audio_input, ref_text_input, gen_text_input, model_choice, remove_silence], outputs=[audio_output])216    gr.Markdown("Unofficial demo by [mrfakename](https://x.com/realmrfakename)")217 218 219app.queue().launch()