Instructions to use NewGame/libritts_f5tts_training with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- ESPnet
How to use NewGame/libritts_f5tts_training with ESPnet:
unknown model type (must be text-to-speech or automatic-speech-recognition)
- Notebooks
- Google Colab
- Kaggle
Download src/app.py from NewGame/libritts_f5tts_training: direct link, hf CLI and curl.
- Browser
- Download file 9.29 kB
-
https://huggingface.co/NewGame/libritts_f5tts_training/resolve/main/src/app.py
- Command line
-
hf download hf://NewGame/libritts_f5tts_training/src/app.py
-
curl -L -o app.py https://huggingface.co/NewGame/libritts_f5tts_training/resolve/main/src/app.py
9.29 kB
| """Recipe-local Gradio launcher for ESPnet3 F5-TTS demos. | |
| The generic launcher (``egs3/TEMPLATE/esp2_asr/src/app.py``) binds the demo | |
| session's inference function straight to the Gradio components; the session | |
| already turns the model's ``Audio`` into the ``(sample_rate, samples)`` pair | |
| ``gr.Audio`` plays. This launcher adds what a text-to-speech UI needs on top: | |
| an untouched text box arrives as ``""``, which is passed on as "not given" so | |
| the contract refuses it by name instead of synthesizing from an empty | |
| transcript, and that refusal is shown in the UI rather than as a bare error. | |
| """ | |
| from __future__ import annotations | |
| # ZeroGPU Spaces kill an app that declares no `@spaces.GPU` function and need | |
| # `spaces` imported before any package that touches CUDA, so this import comes | |
| # first. The package exists only on Hugging Face Spaces. | |
| try: | |
| import spaces # isort: skip | |
| except ImportError: # local runs and plain CPU Spaces | |
| spaces = None | |
| import argparse # noqa: E402 | |
| import logging # noqa: E402 | |
| from pathlib import Path # noqa: E402 | |
| import gradio as gr # noqa: E402 | |
| from omegaconf import OmegaConf # noqa: E402 | |
| from espnet3.publication.demo.session import load_demo_session # noqa: E402 | |
| from espnet3.utils.logging_utils import configure_logging # noqa: E402 | |
| logger = logging.getLogger(__name__) | |
| # Example values with one of these suffixes are files inside the packed demo. | |
| _AUDIO_SUFFIXES = {".wav", ".flac", ".mp3", ".ogg"} | |
| def _read_example_rows(demo_cfg, demo_dir: Path) -> list[list]: | |
| """Return ``ui.examples`` as plain rows, audio paths made absolute. | |
| Args: | |
| demo_cfg: The packed demo config. | |
| demo_dir: The packed demo directory the paths are relative to. | |
| Returns: | |
| One list per example, in the order of ``ui.inputs``; empty when the | |
| config has no ``ui.examples``. | |
| Raises: | |
| FileNotFoundError: If an example names a file that is not in the | |
| packed demo, so a broken example is caught at launch, not on | |
| click. | |
| Example: | |
| .. code-block:: python | |
| _read_example_rows(cfg, Path("demo")) | |
| # -> [["Hello.", "/abs/demo/examples/prompt.wav", "the prompt"]] | |
| """ | |
| ui_cfg = getattr(demo_cfg, "ui", None) | |
| raw = ui_cfg.get("examples") if ui_cfg is not None else None | |
| if not raw: | |
| return [] | |
| rows = [] | |
| for row in OmegaConf.to_container(raw, resolve=True): | |
| values = [] | |
| for value in row: | |
| if isinstance(value, str) and Path(value).suffix.lower() in _AUDIO_SUFFIXES: | |
| path = demo_dir / value | |
| if not path.is_file(): | |
| raise FileNotFoundError( | |
| f"ui.examples names {value!r}, which is not in the packed " | |
| f"demo {demo_dir}; list it under pack.include" | |
| ) | |
| value = str(path) | |
| values.append(value) | |
| rows.append(values) | |
| return rows | |
| def build_demo( | |
| demo_dir: Path, | |
| demo_config_path: Path | None = None, | |
| ): | |
| """Build the Gradio Blocks app for one packed F5-TTS demo. | |
| Input and output components are read from the packed ``demo.yaml``. With | |
| the template's ``conf/demo.yaml`` that is: target text, reference speech | |
| and reference transcript in, synthesized speech out. | |
| Args: | |
| demo_dir: Packed demo directory, containing ``demo.yaml`` and the | |
| model reference it points at. | |
| demo_config_path: Optional demo config override. Defaults to | |
| ``demo_dir / "demo.yaml"``. | |
| Returns: | |
| gradio.Blocks: App with one component per input/output spec, bound | |
| positionally to the session's inference function. | |
| Raises: | |
| FileNotFoundError: If ``ui.examples`` names an audio file that is not | |
| in the packed demo, so a broken example fails at launch rather | |
| than on click. | |
| Example: | |
| .. code-block:: python | |
| app = build_demo(Path("exp/training/demo")) | |
| app.launch() | |
| Note: | |
| Reference speech is passed to the model as Gradio delivers it, a | |
| ``(rate, samples)`` pair; ``Inference`` resamples it to the model's | |
| rate. A packed config that names the bare ``F5TTSInference`` engine | |
| instead does no such conversion and is not supported by this app. | |
| """ | |
| # Resolve first: a relative config path is joined onto the demo directory | |
| # by `load_demo_session`, which would double a relative `demo_dir`. | |
| demo_dir = Path(demo_dir).resolve() | |
| if demo_config_path is None: | |
| demo_config_path = demo_dir / "demo.yaml" | |
| logger.info( | |
| "Building recipe demo UI | demo_dir=%s demo_config_path=%s", | |
| demo_dir, | |
| demo_config_path, | |
| ) | |
| session = load_demo_session(demo_dir, demo_config_path) | |
| logger.info( | |
| "Resolved demo specs | inputs=%s outputs=%s", | |
| session.input_specs, | |
| session.output_specs, | |
| ) | |
| inference_fn = session.create_inference_fn( | |
| session.input_specs, | |
| session.output_specs, | |
| ) | |
| def synthesize(*values): | |
| """Run the packed model on the UI values; the session shapes the outputs.""" | |
| # An untouched Gradio text box holds "", which the model would take | |
| # as an empty transcript; None means "not given", which the contract | |
| # refuses for a required field with a message naming it. | |
| values = [None if value == "" else value for value in values] | |
| try: | |
| return inference_fn(*values) | |
| except TypeError as error: | |
| # A missing or malformed input, e.g. no reference speech or no | |
| # transcript: show the reason in the UI instead of a bare "Error". | |
| raise gr.Error(str(error)) from error | |
| if spaces is not None: | |
| # On ZeroGPU the model runs on a GPU leased per call; the decorated | |
| # function is what the Space's scheduler sees. | |
| logger.info("ZeroGPU detected; registering the handler with spaces.GPU") | |
| synthesize = spaces.GPU(synthesize) | |
| with gr.Blocks(title=session.title) as app: | |
| if session.title: | |
| gr.Markdown(f"# {session.title}") | |
| # What the inputs are and what to expect, before the inputs themselves. | |
| if session.description: | |
| gr.Markdown(session.description) | |
| input_components = [] | |
| with gr.Column(): | |
| # Gradio click handlers bind positional values, so this list keeps | |
| # the order of session.input_specs. | |
| for spec in session.input_specs: | |
| logger.info("Building input component | spec=%s", spec) | |
| input_components.append(session.build_input_component(spec)) | |
| submit_button = gr.Button("Synthesize") | |
| output_components = [] | |
| with gr.Column(): | |
| # Outputs stay positional too: one value per output spec, in order. | |
| for spec in session.output_specs: | |
| logger.info("Building output component | spec=%s", spec) | |
| output_components.append(session.build_output_component(spec)) | |
| logger.info("Binding Synthesize button click handler") | |
| submit_button.click( | |
| fn=synthesize, | |
| inputs=input_components, | |
| outputs=output_components, | |
| ) | |
| # `ui.examples`: rows of input values in the order of `ui.inputs`; an | |
| # audio value is a path relative to the demo directory, which is where | |
| # the app runs from. A click fills the inputs; Synthesize runs them. | |
| examples = _read_example_rows(session.demo_cfg, demo_dir) | |
| if examples: | |
| logger.info("Adding %d example row(s)", len(examples)) | |
| gr.Examples(examples=examples, inputs=input_components, label="Examples") | |
| # The first example is filled in when the page opens, so a visitor | |
| # can press Synthesize right away; the rows below refill the inputs. | |
| first = tuple(examples[0]) if len(input_components) > 1 else examples[0][0] | |
| app.load(fn=lambda: first, inputs=None, outputs=input_components) | |
| logger.info("Recipe demo UI ready") | |
| return app | |
| def main() -> None: | |
| """Parse CLI arguments and launch the packed demo. | |
| Returns: | |
| None. Blocks while the Gradio server is running. | |
| Example: | |
| .. code-block:: bash | |
| python app.py --demo-dir exp/training/demo | |
| """ | |
| parser = argparse.ArgumentParser(description="Launch an ESPnet3 F5-TTS demo.") | |
| parser.add_argument( | |
| "--demo-dir", | |
| type=Path, | |
| default=Path(__file__).resolve().parent, | |
| help="Path to the demo directory. Defaults to this script's directory.", | |
| ) | |
| parser.add_argument( | |
| "--demo-config", | |
| type=Path, | |
| default=None, | |
| help="Optional packed demo config path. Relative paths use --demo-dir.", | |
| ) | |
| args = parser.parse_args() | |
| configure_logging(log_dir=args.demo_dir, filename="demo.log") | |
| logger.info("Starting recipe demo CLI | args=%s", args) | |
| demo_config_path = args.demo_config or (args.demo_dir / "demo.yaml") | |
| app = build_demo( | |
| args.demo_dir, | |
| demo_config_path=demo_config_path, | |
| ) | |
| logger.info("Launching Gradio app") | |
| app.launch() | |
| if __name__ == "__main__": | |
| main() | |