File size: 8,133 Bytes
3677478
 
 
8554d8e
474fe7b
8554d8e
3677478
 
474fe7b
3677478
8554d8e
 
474fe7b
 
8554d8e
 
 
474fe7b
8554d8e
 
 
f61c36a
 
 
474fe7b
f61c36a
474fe7b
 
 
 
 
 
 
 
 
 
 
9010af9
 
474fe7b
 
 
 
 
 
 
 
 
 
 
 
 
8554d8e
8ea2d3b
474fe7b
8ea2d3b
 
474fe7b
 
 
 
 
 
f2d5535
 
39067e3
474fe7b
 
fd6e2dc
39067e3
8ea2d3b
 
474fe7b
f87c373
f2d5535
8ea2d3b
474fe7b
3677478
8ea2d3b
 
fd6e2dc
5ad8b18
6e35245
d764ebc
9010af9
8ea2d3b
 
39067e3
0eedd37
 
 
 
 
 
 
84dd98b
0eedd37
4e07510
0eedd37
84dd98b
 
0eedd37
474fe7b
 
39067e3
c3ee8d5
0dc8871
5f07ca6
96a7770
 
 
f2d5535
474fe7b
cc41409
0f13d71
 
474fe7b
 
0f13d71
474fe7b
 
 
 
 
 
f2d5535
cc41409
474fe7b
 
8ea2d3b
 
 
 
474fe7b
de917d4
474fe7b
 
 
 
4e79f85
de917d4
f308f11
474fe7b
 
c3ee8d5
f2d5535
cc41409
c3ee8d5
8554d8e
474fe7b
a13ee7c
 
474fe7b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f2d5535
a13ee7c
474fe7b
 
 
 
 
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
"""
To run locally, do python app.py
"""
import os
os.environ["NUMBA_DISABLE_CUDA"] = "1"
os.environ['OMP_NUM_THREADS'] = '2'
import gradio as gr
import spaces
from huggingface_hub import hf_hub_download, login
from nemo.collections.tts.modules.magpietts_inference.utils import ModelLoadConfig, load_magpie_model

login(token=os.environ.get("HF_TOKEN"))
print("login successful")

server_port = os.getenv("SERVER_PORT")
if isinstance(server_port, str):
    server_port = int(server_port)
print(f"server_port {server_port}")

CODEC_MODEL_PATH = "nvidia/nemo-nano-codec-22khz-1.89kbps-21.5fps"

CHECKPOINT_PATH = hf_hub_download(
    repo_id="nvidia/magpie_tts_multilingual_357m",
    filename="magpie_tts_multilingual_357m.nemo",
    token=os.environ.get("HF_TOKEN"),
)
print(f"checkpoint downloaded to {CHECKPOINT_PATH}")

SPEAKER_MAP = {
    "Aria": 0,
    "Jason": 1,
    "John": 2,
    "Leo": 3,
    "Sofia": 4,
}

LANGUAGE_MAP = {
    # "Arabic-UAE": "ar-AE",
    # "Arabic-Saudi Arabia": "ar-SA",
    "Arabic-MSA": "ar-MSA",
    "Chinese": "zh",
    "English": "en",
    "French": "fr",
    "German": "de",
    "Hindi": "hi",
    "Italian": "it",
    "Japanese": "ja",
    "Korean": "ko",
    "Brazilian Portuguese": "pt-BR",
    "Spanish": "es",
    "Vietnamese": "vi",
}

BODY = """
**MagpieTTS** is NVIDIA's state-of-the-art multilingual text-to-speech system built on the [NeMo Speech](https://github.com/NVIDIA-NeMo/Speech) framework. \
It employs a two-stage pipeline architecture: a language model generates discrete acoustic tokens from text, which are then decoded into \
high-fidelity audio using a neural audio codec: [NanoCodec](https://huggingface.co/nvidia/nemo-nano-codec-22khz-1.89kbps-21.5fps). \
The model synthesizes speech in 5 different English speakers - Sofia, Aria, Jason, Leo, \
[John Van Stan](https://librivox.org/reader/9017?primary_key=9017&search_category=reader&search_page=1&search_form=get_results&search_order=alpha) \
across 12 languages: Arabic, Chinese, English, French, German, Hindi, Italian, Japanese, Korean, Portuguese, Spanish, and Vietnamese. \
The model predicts discrete audio codec tokens autoregressively using a transformer encoder-decoder architecture. It employs multi-codebook \
prediction (typically 8 codebooks) with frame stacking (factor = 2) and a local transformer for fine-grained refinement of \
high-fidelity audio generation, and leverages techniques like attention priors, classifier-free guidance (CFG), and Group Relative Policy \
Optimization (GRPO) for improved alignment.

### Key Features of the model
- **Multilingual Support** — Synthesizes natural speech in 12 languages: Arabic, Chinese, English, French, German, Hindi, Italian, Japanese, Korean, Portuguese, Spanish, and Vietnamese.
- **Expressive Voices** — Multiple voice options with emotional tones and gender variations including 4 proprietary voices and 1 public voice.
- **Text Normalization** — Built-in text normalization for handling numbers, abbreviations, and special characters for all languages.
### Resources
- 🤗 **MagpieTTS Weights**: [nvidia/magpie_tts_multilingual_357m](https://huggingface.co/nvidia/magpie_tts_multilingual_357m)
- 🤗 **NanoCodec Weights**: [nvidia/nemo-nano-codec-22khz-1.89kbps-21.5fps](https://huggingface.co/nvidia/nemo-nano-codec-22khz-1.89kbps-21.5fps)
- 💻 **NeMo Framework**: [github.com/NVIDIA-NeMo/NeMo](https://github.com/NVIDIA-NeMo/NeMo)

Note about the demo:
- Text normalization takes time to load, so if you want faster generation select "Do not apply TN."
- Text normalization works for all supported languages.
- Text normalization is required for numbers to be processed and spoken.
- When the model runs on ZeroGPU Hardware, expect slower generation because the model checkpoint is loaded everytime.
- As the model's speakers are all native English speakers, expect accented speech in the other languages.
- The current model can generate long-form speech (more than 20 seconds) in English. However, longer generations can cause timeout due to Huggingface timeout limit.
- Loan words are not supported at this time. For example, English characters in Mandarin will lead to unexpected results.
- To add custom phone pronunciations for supported languages, replace the word with it's IPA characters surrounded by '|' characters and add a space inbetween each IPA phone. For example, "Hello world from NeMo Text to Speech." could be written as "Hello world from | ˈ n ɛ m o ʊ | Text to Speech." replacing "NeMo" with "| ˈ n ɛ m o ʊ |".
  - Text normalization does not work with custom pronunciations. To mix the two, normalize the input first and then replace the word with the custom pronunciation.
- While the checkpoint was trained with 4 Arabic dialects, only ar-MSA is recommended for use.
- For the enterprise offering, see the [MagpieTTS NIM](https://build.nvidia.com/nvidia/magpie-tts-multilingual) which includes additional native voices in the supported languages, emotional speech capabilities, and optimized batch and latency inference pipeline.
"""

model_config = ModelLoadConfig(
    nemo_file=CHECKPOINT_PATH,
    codecmodel_path=CODEC_MODEL_PATH,
    legacy_codebooks=False,
    legacy_text_conditioning=False,
    hparams_from_wandb=None,
)
model = None
try:
    print(f"model_config {model_config}")
    model, _ = load_magpie_model(model_config)
    model.eval().cuda()
    print("Model loaded and ready for inference.")
except Exception as e:
    print(f"Exception in model loading: {e}")


@spaces.GPU
def demo_tts(input_text, language, speaker, apply_TN):
    global model

    if not input_text or input_text.strip() == "":
        raise gr.Error("Please enter some text to synthesize.")

    # Reload model inside GPU context if it failed at startup (ZeroGPU fallback)
    if model is None:
        try:
            model, _ = load_magpie_model(model_config)
            model.eval().cuda()
            print("Model loaded and ready for inference.")
        except Exception as e:
            raise gr.Error(f"Model failed to load: {e}")

    if speaker not in SPEAKER_MAP:
        raise gr.Error(f"Unknown speaker: {speaker!r}. Choose from {list(SPEAKER_MAP)}.")
    if language not in LANGUAGE_MAP:
        raise gr.Error(f"Unknown language: {language!r}. Choose from {list(LANGUAGE_MAP)}.")

    apply_tn_flag = apply_TN == "Apply TN"
    speaker_idx = SPEAKER_MAP[speaker]
    language_code = LANGUAGE_MAP[language]
    input_text = input_text.strip()

    if not (input_text.endswith(".") or input_text.endswith("?") or input_text.endswith("!")):
        input_text = f"{input_text}."

    try:
        import traceback
        audio, audio_len = model.do_tts(
            input_text, language=language_code, apply_TN=apply_tn_flag, speaker_index=speaker_idx
        )
        print(f"audio {audio.shape} audio_len {audio_len}")
    except Exception as e:
        traceback.print_exc()
        raise gr.Error(f"Speech synthesis failed: {e}")

    audio_np = audio[0, :audio_len[0]].cpu().numpy()

    # Return only picklable objects (int and numpy array)
    return model.sample_rate, audio_np


demo = gr.Interface(
    fn=demo_tts,
    inputs=[
        gr.Textbox(label="Text to synthesize"),
        gr.Dropdown(
            choices=LANGUAGE_MAP.keys(),
            value="English",
            label="Target Language",
            info="Select the target language for the speech to be synthesized in",
        ),
        gr.Dropdown(
            choices=["Aria", "Jason", "John", "Leo", "Sofia"],
            value="Aria",
            label="Target Speaker",
            info="Select the target speaker whose voice you would like the the speech to be synthesized in",
        ),
        gr.Radio(
            ["Apply TN", "Do not apply TN"],
            value="Do not apply TN",
            label="Apply Text Normalization",
            info="Select if you want to apply text normalization to the input text",
        ),
    ],
    outputs="audio",
    description=BODY,
    title="Text to Speech MagpieTTS Multilingual Demo",
)

if __name__ == "__main__":
    demo.launch(server_port=server_port)