Views
No views yet
1# install libraries
2!sudo apt-get update -y
3!apt-get install build-essential -y
4!pip install torch tensorboard transformers accelerate SoundFile torchaudio librosa phonemizer
5!pip install einops einops-exts tqdm typing typing-extensions munch pydub pyyaml nltk matplotlib
6!pip install git+https://github.com/resemble-ai/monotonic_align.git
7!pip install hf_transfer -qU
8!sudo apt-get install -y espeak-ng
9
10
11# ____________Download model_____________
12model_repo = 'benjaminogbonna/tts_demo_models'
13model_path = 'Models'
14target_files = ['config.yml', 'model_v1.pth']
15local_dir = '.'
16
17import os
18os.makedirs(local_dir, exist_ok=True)
19downloaded_files = []
20for file_name in target_files:
21 file_path = hf_hub_download(repo_id=model_repo, filename=f'{model_path}/{file_name}', local_dir=local_dir)
22 downloaded_files.append(file_path)
23
24print('Downloaded files', downloaded_files)
25
26
27#________________________
28import nltk
29nltk.download('punkt')
30nltk.download('punkt_tab')
31
32
33
34model_folder = 'Models/'
35
36# I do this to always pick the last trained epoch
37files = [f for f in os.listdir(model_folder) if f.endswith('.pth')]
38sorted_files = sorted(files, key=lambda x: int(x.split('_')[-1].split('.')[0]))
39print(sorted_files[-1])
40
41
42
43#________________________
44import torch
45torch.manual_seed(0)
46torch.backends.cudnn.benchmark = False
47torch.backends.cudnn.deterministic = True
48
49import random
50random.seed(0)
51
52import numpy as np
53np.random.seed(0)
54
55# load packages
56import time
57import random
58import yaml
59from munch import Munch
60import numpy as np
61import torch
62from torch import nn
63import torch.nn.functional as F
64import torchaudio
65import librosa
66from nltk.tokenize import word_tokenize
67
68from models import *
69from utils import *
70from text_utils import TextCleaner
71textclenaer = TextCleaner()
72
73%matplotlib inline
74
75
76#________________________
77to_mel = torchaudio.transforms.MelSpectrogram(
78 n_mels=80, n_fft=2048, win_length=1200, hop_length=300)
79mean, std = -4, 4
80
81def length_to_mask(lengths):
82 mask = torch.arange(lengths.max()).unsqueeze(0).expand(lengths.shape[0], -1).type_as(lengths)
83 mask = torch.gt(mask+1, lengths.unsqueeze(1))
84 return mask
85
86def preprocess(wave):
87 wave_tensor = torch.from_numpy(wave).float()
88 mel_tensor = to_mel(wave_tensor)
89 mel_tensor = (torch.log(1e-5 + mel_tensor.unsqueeze(0)) - mean) / std
90 return mel_tensor
91
92def compute_style(path):
93 wave, sr = librosa.load(path, sr=24000)
94 audio, index = librosa.effects.trim(wave, top_db=30)
95 if sr != 24000:
96 audio = librosa.resample(audio, sr, 24000)
97 mel_tensor = preprocess(audio).to(device)
98
99 with torch.no_grad():
100 ref_s = model.style_encoder(mel_tensor.unsqueeze(1))
101 ref_p = model.predictor_encoder(mel_tensor.unsqueeze(1))
102
103 return torch.cat([ref_s, ref_p], dim=1)
104
105device = 'cuda' if torch.cuda.is_available() else 'cpu'
106
107# load phonemizer
108import phonemizer
109global_phonemizer = phonemizer.backend.EspeakBackend(language='en-us', preserve_punctuation=True, with_stress=True)
110
111config = yaml.safe_load(open(f"{model_folder}config.yml"))
112
113# load pretrained ASR model
114ASR_config = config.get('ASR_config', False)
115ASR_path = config.get('ASR_path', False)
116text_aligner = load_ASR_models(ASR_path, ASR_config)
117
118# load pretrained F0 model
119F0_path = config.get('F0_path', False)
120pitch_extractor = load_F0_models(F0_path)
121
122# load BERT model
123from Utils.PLBERT.util import load_plbert
124BERT_path = config.get('PLBERT_dir', False)
125plbert = load_plbert(BERT_path)
126
127model_params = recursive_munch(config['model_params'])
128model = build_model(model_params, text_aligner, pitch_extractor, plbert)
129_ = [model[key].eval() for key in model]
130_ = [model[key].to(device) for key in model]
131
132
133#________________________
134params_whole = torch.load(f"{model_folder}" + sorted_files[-1], map_location='cpu')
135params = params_whole['net']
136
137
138#________________________
139for key in model:
140 if key in params:
141 print('%s loaded' % key)
142 try:
143 model[key].load_state_dict(params[key])
144 except:
145 from collections import OrderedDict
146 state_dict = params[key]
147 new_state_dict = OrderedDict()
148 for k, v in state_dict.items():
149 name = k[7:] # remove `module.`
150 new_state_dict[name] = v
151 # load params
152 model[key].load_state_dict(new_state_dict, strict=False)
153# except:
154# _load(params[key], model[key])
155_ = [model[key].eval() for key in model]
156
157
158#________________________
159from Modules.diffusion.sampler import DiffusionSampler, ADPM2Sampler, KarrasSchedule
160
161sampler = DiffusionSampler(
162 model.diffusion.diffusion,
163 sampler=ADPM2Sampler(),
164 sigma_schedule=KarrasSchedule(sigma_min=0.0001, sigma_max=3.0, rho=9.0), # empirical parameters
165 clamp=False
166)
167
168
169#________________________
170def inference(text, ref_s, alpha = 0.3, beta = 0.7, diffusion_steps=5, embedding_scale=1):
171 text = text.strip()
172 ps = global_phonemizer.phonemize([text])
173 ps = word_tokenize(ps[0])
174 ps = ' '.join(ps)
175 tokens = textclenaer(ps)
176 tokens.insert(0, 0)
177 tokens = torch.LongTensor(tokens).to(device).unsqueeze(0)
178
179 with torch.no_grad():
180 input_lengths = torch.LongTensor([tokens.shape[-1]]).to(device)
181 text_mask = length_to_mask(input_lengths).to(device)
182
183 t_en = model.text_encoder(tokens, input_lengths, text_mask)
184 bert_dur = model.bert(tokens, attention_mask=(~text_mask).int())
185 d_en = model.bert_encoder(bert_dur).transpose(-1, -2)
186
187 s_pred = sampler(noise = torch.randn((1, 256)).unsqueeze(1).to(device),
188 embedding=bert_dur,
189 embedding_scale=embedding_scale,
190 features=ref_s, # reference from the same speaker as the embedding
191 num_steps=diffusion_steps).squeeze(1)
192
193
194 s = s_pred[:, 128:]
195 ref = s_pred[:, :128]
196
197 ref = alpha * ref + (1 - alpha) * ref_s[:, :128]
198 s = beta * s + (1 - beta) * ref_s[:, 128:]
199
200 d = model.predictor.text_encoder(d_en,
201 s, input_lengths, text_mask)
202
203 x, _ = model.predictor.lstm(d)
204 duration = model.predictor.duration_proj(x)
205
206 duration = torch.sigmoid(duration).sum(axis=-1)
207 pred_dur = torch.round(duration.squeeze()).clamp(min=1)
208
209
210 pred_aln_trg = torch.zeros(input_lengths, int(pred_dur.sum().data))
211 c_frame = 0
212 for i in range(pred_aln_trg.size(0)):
213 pred_aln_trg[i, c_frame:c_frame + int(pred_dur[i].data)] = 1
214 c_frame += int(pred_dur[i].data)
215
216 # encode prosody
217 en = (d.transpose(-1, -2) @ pred_aln_trg.unsqueeze(0).to(device))
218 if model_params.decoder.type == "hifigan":
219 asr_new = torch.zeros_like(en)
220 asr_new[:, :, 0] = en[:, :, 0]
221 asr_new[:, :, 1:] = en[:, :, 0:-1]
222 en = asr_new
223
224 F0_pred, N_pred = model.predictor.F0Ntrain(en, s)
225
226 asr = (t_en @ pred_aln_trg.unsqueeze(0).to(device))
227 if model_params.decoder.type == "hifigan":
228 asr_new = torch.zeros_like(asr)
229 asr_new[:, :, 0] = asr[:, :, 0]
230 asr_new[:, :, 1:] = asr[:, :, 0:-1]
231 asr = asr_new
232
233 out = model.decoder(asr,
234 F0_pred, N_pred, ref.squeeze().unsqueeze(0))
235
236
237 return out.squeeze().cpu().numpy()[..., :-50] # weird pulse at the end of the model, need to be fixed later
238
239
240#________________________
241# Synthesize speech
242text = "We are happy to invite you to join us on a journey to the future."
243
244#________________________
245reference_dicts = {}
246reference_dicts['oge'] = "ref_audios/things_fall_apart_1.wav" # or use your own audio samples
247reference_dicts['ben'] = "ref_audios/feels_good_to_be_odd_1.wav"
248
249
250#________________________
251start = time.time()
252noise = torch.randn(1,1,256).to(device)
253for k, path in reference_dicts.items():
254 ref_s = compute_style(path)
255
256 wav = inference(text, ref_s, alpha=0.3, beta=0.9, diffusion_steps=10, embedding_scale=2)
257 rtf = (time.time() - start) / (len(wav) / 24000)
258 print(f"RTF = {rtf:5f}")
259 import IPython.display as ipd
260 print(k + ' Synthesized:')
261 display(ipd.Audio(wav, rate=24000, normalize=False))
262 print('Reference:')
263 display(ipd.Audio(path, rate=24000, normalize=False))
264| Text Input | Audio Output | Notes |
|---|---|---|
| We are happy to invite you to join us on a journey to the future. | (alpha=0.3, beta=0.9, diffusion_steps=10, embedding_scale=2), Speaker: Ben | |
| We are happy to invite you to join us on a journey to the future. | (alpha=0.3, beta=0.9, diffusion_steps=10, embedding_scale=2), Speaker: Oge | |
| If the supply of fruit is greater than the family needs, it may be made a source of income by sending the fresh fruit to the market if there is one near enough, or by preserving, canning, and making jelly for sale. To make such an enterprise a success the fruit and work must be first class. There is magic in the word "Homemade," when the product appeals to the eye and the palate; but many careless and incompetent people have found to their sorrow that this word has not magic enough to float inferior goods on the market. As a rule large canning and preserving establishments are clean and have the best appliances, and they employ chemists and skilled labor. The home product must be very good to compete with the attractive goods that are sent out from such establishments. Yet for first class home made products there is a market in all large cities. All first-class grocers have customers who purchase such goods. | (alpha = 0.3, beta = 0.9, t = 0.7, diffusion_steps=10, embedding_scale=1.5), Long narration: Oge |
1@misc{
2 authors = {Benjamin O, Oge N, Mathias E, Daniel A.},
3 title = {Nigerian-Accented Text-to-Speech Model},
4 year = {2025},
5 publisher = {Hugging Face},
6 url = {https://huggingface.co/benjaminogbonna/tts_demo_models}
7}Benjamin O, Oge N, Mathias E, Daniel A. (2025). Nigerian-Accented Text-to-Speech Model. Hugging Face. Available at: https://huggingface.co/benjaminogbonna/tts_demo_models