ememzyvisuals commited on
Commit
c8be987
Β·
verified Β·
1 Parent(s): 0d00ef0

NaijaVox unified demo: V1+V2 model switcher, green Space Grotesk UI

Browse files
Files changed (2) hide show
  1. README.md +10 -8
  2. app.py +384 -62
README.md CHANGED
@@ -1,21 +1,23 @@
1
  ---
2
- title: NaijaVox V1 Demo
3
  emoji: πŸŽ™
4
  colorFrom: green
5
- colorTo: yellow
6
  sdk: gradio
7
- python_version: '3.11'
8
  app_file: app.py
9
  pinned: false
10
  license: apache-2.0
11
  ---
12
 
13
- # NaijaVox-V1 Demo
14
 
15
- Try Nigeria's open-weight speech recognition model -- Yoruba, Hausa, Igbo, Nigerian Pidgin, and Nigerian English.
16
 
17
- Record or upload audio, pick the language, and see it transcribed live.
18
 
19
- [Model card ->](https://huggingface.co/Axiveri/NaijaVox-V1)
 
 
20
 
21
- Built by [Emmanuel Ariyo (Ememzyvisuals)](https://huggingface.co/ememzyvisuals) - Axiveri
 
1
  ---
2
+ title: NaijaVox Demo
3
  emoji: πŸŽ™
4
  colorFrom: green
5
+ colorTo: green
6
  sdk: gradio
7
+ python_version: "3.11"
8
  app_file: app.py
9
  pinned: false
10
  license: apache-2.0
11
  ---
12
 
13
+ # NaijaVox Demo β€” Nigerian Speech Recognition
14
 
15
+ Unified demo for **NaijaVox-V1** and **NaijaVox-2.0** β€” open-weight ASR for Yoruba, Hausa, Igbo, Nigerian Pidgin, and Nigerian English.
16
 
17
+ Switch between models, pick your language, record or upload audio, and transcribe.
18
 
19
+ **Models:**
20
+ - [NaijaVox-V1](https://huggingface.co/Axiveri/NaijaVox-V1) β€” Avg WER 27.9%
21
+ - [NaijaVox-2.0](https://huggingface.co/Axiveri/NaijaVox-2.0) β€” Avg WER 22.58% (+19.1% better)
22
 
23
+ Built by [Emmanuel Ariyo (Ememzyvisuals)](https://huggingface.co/ememzyvisuals) Β· Axiveri
app.py CHANGED
@@ -2,99 +2,421 @@ import gradio as gr
2
  import torch
3
  import numpy as np
4
  import librosa
5
- from transformers import WhisperForConditionalGeneration, WhisperFeatureExtractor, PreTrainedTokenizerFast, WhisperProcessor
 
 
 
6
  from huggingface_hub import hf_hub_download
7
 
8
- MODEL_ID = "Axiveri/NaijaVox-V1"
9
- TARGET_SR = 16000 # Whisper always expects 16 kHz
10
 
11
- print("Loading NaijaVox-V1... (this takes a minute on first load)")
12
- model = WhisperForConditionalGeneration.from_pretrained(MODEL_ID, torch_dtype=torch.float32)
13
- fe = WhisperFeatureExtractor.from_pretrained(MODEL_ID)
14
- tok_path = hf_hub_download(repo_id=MODEL_ID, filename="tokenizer.json")
15
- tokenizer = PreTrainedTokenizerFast(tokenizer_file=tok_path)
16
- tokenizer.add_special_tokens({"additional_special_tokens": [t for t in ["<|startoftranscript|>","<|endoftext|>","<|transcribe|>","<|notimestamps|>","<|en|>","<|yo|>","<|ha|>","<|ig|>","<|pcm|>"] if t not in tokenizer.get_vocab()]})
17
- processor = WhisperProcessor(feature_extractor=fe, tokenizer=tokenizer)
18
- model.eval()
19
-
20
- VOCAB = processor.tokenizer.get_vocab()
21
- START_OF_TRANSCRIPT = VOCAB["<|startoftranscript|>"]
22
- TRANSCRIBE = VOCAB["<|transcribe|>"]
23
- NOTIMESTAMPS = VOCAB["<|notimestamps|>"]
24
 
25
  LANGUAGES = {
26
- "Nigerian English": "<|en|>",
27
- "Nigerian Pidgin": "<|pcm|>",
28
- "Yoruba": "<|yo|>",
29
- "Hausa": "<|ha|>",
30
- "Igbo": "<|ig|>",
31
  }
32
 
33
- print("Model loaded. Ready.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
 
 
 
 
35
 
36
- def transcribe(audio, language):
 
37
  if audio is None:
38
- return "Please record or upload audio first."
 
 
 
 
 
39
 
40
  sr, arr = audio
41
  arr = np.array(arr, dtype=np.float32)
42
-
43
- # Convert stereo to mono
44
  if arr.ndim > 1:
45
  arr = arr.mean(axis=1)
46
-
47
- # Normalise int16 PCM to float32 [-1, 1]
48
  if np.abs(arr).max() > 1.0:
49
- arr = arr / 32768.0
50
-
51
- # Resample to 16 kHz β€” browser mic records at 44100 Hz; Whisper needs 16000 Hz
52
  if sr != TARGET_SR:
53
  arr = librosa.resample(arr, orig_sr=sr, target_sr=TARGET_SR)
54
- sr = TARGET_SR
55
 
56
- lang_token = LANGUAGES[language]
57
- lang_id = VOCAB[lang_token]
58
- decoder_input_ids = torch.tensor([[START_OF_TRANSCRIPT, lang_id, TRANSCRIBE, NOTIMESTAMPS]])
 
59
 
60
- inputs = processor.feature_extractor(
61
- arr, sampling_rate=sr, return_tensors="pt"
 
62
  ).input_features
63
 
64
  with torch.no_grad():
65
- generated = model.generate(
66
  input_features=inputs,
67
- decoder_input_ids=decoder_input_ids,
68
  max_new_tokens=200,
69
  )
70
 
71
- return processor.tokenizer.decode(generated[0], skip_special_tokens=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
74
- with gr.Blocks(title="NaijaVox-V1 Demo") as demo:
75
- gr.Markdown(
76
- """
77
- # πŸ‡³πŸ‡¬ NaijaVox-V1 β€” Nigerian Speech Recognition
78
- Open-weight speech-to-text for **Yoruba, Hausa, Igbo, Nigerian Pidgin, and Nigerian English**.
79
- Record or upload audio, pick the language, and transcribe.
80
 
81
- [Model card](https://huggingface.co/Axiveri/NaijaVox-V1) Β· Built by Emmanuel Ariyo (Ememzyvisuals) Β· Axiveri
82
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  )
84
- with gr.Row():
85
- with gr.Column():
86
- audio_input = gr.Audio(sources=["microphone", "upload"], type="numpy", label="Audio")
87
- lang_input = gr.Dropdown(
88
- choices=list(LANGUAGES.keys()), value="Nigerian English", label="Language"
89
- )
90
- btn = gr.Button("Transcribe", variant="primary")
91
- with gr.Column():
92
- output = gr.Textbox(label="Transcription", lines=8)
93
-
94
- btn.click(fn=transcribe, inputs=[audio_input, lang_input], outputs=output)
95
-
96
- gr.Markdown(
97
- "_Running on free CPU hardware β€” transcription may take 20-60 seconds per clip._"
98
  )
99
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  demo.launch()
 
2
  import torch
3
  import numpy as np
4
  import librosa
5
+ from transformers import (
6
+ WhisperForConditionalGeneration, WhisperFeatureExtractor,
7
+ WhisperProcessor, PreTrainedTokenizerFast,
8
+ )
9
  from huggingface_hub import hf_hub_download
10
 
11
+ TARGET_SR = 16000
 
12
 
13
+ MODEL_IDS = {
14
+ "NaijaVox-V1": "Axiveri/NaijaVox-V1",
15
+ "NaijaVox-2.0": "Axiveri/NaijaVox-2.0",
16
+ }
 
 
 
 
 
 
 
 
 
17
 
18
  LANGUAGES = {
19
+ "πŸ‡³πŸ‡¬ Nigerian English": "<|en|>",
20
+ "πŸ‡³πŸ‡¬ Nigerian Pidgin": "<|pcm|>",
21
+ "πŸ‡³πŸ‡¬ Yoruba": "<|yo|>",
22
+ "πŸ‡³πŸ‡¬ Hausa": "<|ha|>",
23
+ "πŸ‡³πŸ‡¬ Igbo": "<|ig|>",
24
  }
25
 
26
+ MODEL_CACHE = {}
27
+
28
+
29
+ def load_model(model_key):
30
+ if model_key in MODEL_CACHE:
31
+ return MODEL_CACHE[model_key]
32
+
33
+ model_id = MODEL_IDS[model_key]
34
+ print(f"Loading {model_key} from {model_id}...")
35
+ model = WhisperForConditionalGeneration.from_pretrained(
36
+ model_id, torch_dtype=torch.float32
37
+ )
38
+
39
+ # Try standard load first; fall back to manual tokenizer (same approach as V1 deploy)
40
+ try:
41
+ processor = WhisperProcessor.from_pretrained(model_id)
42
+ vocab = processor.tokenizer.get_vocab()
43
+ assert "<|pcm|>" in vocab and "<|ig|>" in vocab
44
+ except Exception as e:
45
+ print(f" Standard load failed ({e}), using manual tokenizer...")
46
+ fe = WhisperFeatureExtractor.from_pretrained(model_id)
47
+ tok = hf_hub_download(repo_id=model_id, filename="tokenizer.json")
48
+ tokenizer = PreTrainedTokenizerFast(tokenizer_file=tok)
49
+ tokenizer.add_special_tokens({
50
+ "additional_special_tokens": [
51
+ t for t in [
52
+ "<|startoftranscript|>", "<|endoftext|>", "<|transcribe|>",
53
+ "<|notimestamps|>", "<|en|>", "<|yo|>", "<|ha|>", "<|ig|>", "<|pcm|>",
54
+ ]
55
+ if t not in tokenizer.get_vocab()
56
+ ]
57
+ })
58
+ processor = WhisperProcessor(feature_extractor=fe, tokenizer=tokenizer)
59
+ vocab = processor.tokenizer.get_vocab()
60
+
61
+ model.eval()
62
+ MODEL_CACHE[model_key] = (model, processor, vocab)
63
+ print(f" {model_key} ready.")
64
+ return model, processor, vocab
65
+
66
 
67
+ # Pre-load V1 on startup so the demo is immediately responsive
68
+ print("Pre-loading NaijaVox-V1...")
69
+ load_model("NaijaVox-V1")
70
+ print("Startup complete.")
71
 
72
+
73
+ def transcribe(audio, language, model_key):
74
  if audio is None:
75
+ return "⚠️ Record or upload audio first, then tap Transcribe."
76
+
77
+ try:
78
+ model, processor, vocab = load_model(model_key)
79
+ except Exception as e:
80
+ return f"❌ Error loading {model_key}: {e}"
81
 
82
  sr, arr = audio
83
  arr = np.array(arr, dtype=np.float32)
 
 
84
  if arr.ndim > 1:
85
  arr = arr.mean(axis=1)
 
 
86
  if np.abs(arr).max() > 1.0:
87
+ arr /= 32768.0
 
 
88
  if sr != TARGET_SR:
89
  arr = librosa.resample(arr, orig_sr=sr, target_sr=TARGET_SR)
 
90
 
91
+ lang_id = vocab[LANGUAGES[language]]
92
+ start = vocab["<|startoftranscript|>"]
93
+ trans = vocab["<|transcribe|>"]
94
+ nots = vocab["<|notimestamps|>"]
95
 
96
+ dec_ids = torch.tensor([[start, lang_id, trans, nots]])
97
+ inputs = processor.feature_extractor(
98
+ arr, sampling_rate=TARGET_SR, return_tensors="pt"
99
  ).input_features
100
 
101
  with torch.no_grad():
102
+ gen = model.generate(
103
  input_features=inputs,
104
+ decoder_input_ids=dec_ids,
105
  max_new_tokens=200,
106
  )
107
 
108
+ return processor.tokenizer.decode(gen[0], skip_special_tokens=True).strip()
109
+
110
+
111
+ # ── Theme ──────────────────────────────────────────────────────────────────────
112
+ GREEN = gr.themes.Color(
113
+ c50="#f0fdf4", c100="#dcfce7", c200="#bbf7d0",
114
+ c300="#86efac", c400="#4ade80", c500="#22c55e",
115
+ c600="#16a34a", c700="#15803d", c800="#166534",
116
+ c900="#14532d", c950="#052e16",
117
+ )
118
+
119
+ theme = gr.themes.Base(
120
+ primary_hue=GREEN,
121
+ secondary_hue=GREEN,
122
+ neutral_hue=GREEN,
123
+ font=gr.themes.GoogleFont("Space Grotesk"),
124
+ font_mono=gr.themes.GoogleFont("JetBrains Mono"),
125
+ ).set(
126
+ body_background_fill="#060d07",
127
+ body_background_fill_dark="#060d07",
128
+ block_background_fill="#0d1a0f",
129
+ block_background_fill_dark="#0d1a0f",
130
+ block_border_color="#1c3824",
131
+ block_border_color_dark="#1c3824",
132
+ block_border_width="1px",
133
+ block_radius="14px",
134
+ button_primary_background_fill="linear-gradient(135deg,#16a34a 0%,#22c55e 100%)",
135
+ button_primary_background_fill_hover="linear-gradient(135deg,#22c55e 0%,#4ade80 100%)",
136
+ button_primary_text_color="#ffffff",
137
+ button_primary_border_color="transparent",
138
+ button_primary_border_color_hover="transparent",
139
+ input_background_fill="#112015",
140
+ input_background_fill_dark="#112015",
141
+ input_border_color="#1c3824",
142
+ input_border_color_focus="#22c55e",
143
+ body_text_color="#dcfce7",
144
+ body_text_color_dark="#dcfce7",
145
+ body_text_color_subdued="#6ee7a0",
146
+ block_label_text_color="#4ade80",
147
+ block_label_text_color_dark="#4ade80",
148
+ block_label_text_weight="600",
149
+ block_title_text_color="#4ade80",
150
+ block_title_text_color_dark="#4ade80",
151
+ checkbox_label_background_fill="#112015",
152
+ checkbox_label_background_fill_hover="#1a3020",
153
+ checkbox_label_background_fill_selected="#0d2e1a",
154
+ checkbox_label_border_color="#1c3824",
155
+ checkbox_label_border_color_hover="#22c55e",
156
+ checkbox_label_text_color="#dcfce7",
157
+ table_even_background_fill="#0d1a0f",
158
+ table_odd_background_fill="#112015",
159
+ slider_color="#22c55e",
160
+ )
161
+
162
+ CSS = """
163
+ @import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;600&display=swap');
164
 
165
+ *, *::before, *::after {
166
+ font-family: 'Space Grotesk', system-ui, sans-serif !important;
167
+ box-sizing: border-box;
168
+ }
169
+
170
+ body, .gradio-container { background: #060d07 !important; }
171
+
172
+ .gradio-container {
173
+ max-width: 820px !important;
174
+ margin: 0 auto !important;
175
+ padding: 0 1rem !important;
176
+ }
177
+
178
+ /* ── Header ── */
179
+ .nv-header { text-align: center; padding: 2rem 0 0.5rem; }
180
+
181
+ .nv-logo {
182
+ display: block;
183
+ margin: 0 auto 1.2rem;
184
+ max-width: 100%;
185
+ border-radius: 12px;
186
+ }
187
+
188
+ .nv-title {
189
+ font-size: clamp(2.4rem, 6vw, 3.6rem);
190
+ font-weight: 700;
191
+ letter-spacing: -0.03em;
192
+ background: linear-gradient(135deg, #22c55e 0%, #4ade80 55%, #86efac 100%);
193
+ -webkit-background-clip: text;
194
+ -webkit-text-fill-color: transparent;
195
+ background-clip: text;
196
+ line-height: 1.1;
197
+ margin: 0 0 0.5rem;
198
+ }
199
+
200
+ .nv-sub {
201
+ color: #6ee7a0;
202
+ font-size: 0.95rem;
203
+ margin: 0 0 1.8rem;
204
+ letter-spacing: 0.01em;
205
+ }
206
+
207
+ /* ── Model info cards ── */
208
+ .model-info-row {
209
+ display: flex;
210
+ gap: 0.75rem;
211
+ margin-bottom: 0.75rem;
212
+ }
213
+
214
+ .model-info-card {
215
+ flex: 1;
216
+ background: #0d1a0f;
217
+ border: 1px solid #1c3824;
218
+ border-radius: 12px;
219
+ padding: 0.9rem 1.1rem;
220
+ position: relative;
221
+ }
222
+
223
+ .model-info-card.featured { border-color: #22c55e; background: #0d2315; }
224
+
225
+ .mic-badge {
226
+ position: absolute;
227
+ top: -10px; right: 12px;
228
+ background: #22c55e;
229
+ color: #052e16;
230
+ font-size: 0.65rem;
231
+ font-weight: 700;
232
+ padding: 2px 10px;
233
+ border-radius: 20px;
234
+ letter-spacing: 0.06em;
235
+ }
236
 
237
+ .card-name { color: #4ade80; font-weight: 700; font-size: 0.95rem; margin: 0 0 0.25rem; }
238
+ .card-stats { color: #6ee7a0; font-size: 0.78rem; }
239
+ .card-stats strong { color: #22c55e; }
 
 
 
240
 
241
+ /* ── Section labels ── */
242
+ .sl {
243
+ color: #4ade80;
244
+ font-size: 0.75rem;
245
+ font-weight: 700;
246
+ letter-spacing: 0.09em;
247
+ text-transform: uppercase;
248
+ margin: 1rem 0 0.35rem;
249
+ }
250
+
251
+ /* ── Model radio styled as pill tabs ── */
252
+ #model-radio .wrap { gap: 0.6rem !important; }
253
+
254
+ #model-radio label {
255
+ background: #0d1a0f !important;
256
+ border: 1.5px solid #1c3824 !important;
257
+ border-radius: 10px !important;
258
+ padding: 0.65rem 1rem !important;
259
+ color: #6ee7a0 !important;
260
+ font-weight: 500 !important;
261
+ cursor: pointer !important;
262
+ transition: all 0.16s !important;
263
+ flex: 1 !important;
264
+ text-align: center !important;
265
+ }
266
+
267
+ #model-radio label:hover {
268
+ border-color: #22c55e !important;
269
+ color: #dcfce7 !important;
270
+ background: #112015 !important;
271
+ }
272
+
273
+ #model-radio label:has(input:checked) {
274
+ border-color: #22c55e !important;
275
+ background: linear-gradient(135deg,#0d2e1a,#112015) !important;
276
+ color: #4ade80 !important;
277
+ box-shadow: 0 0 0 1px #22c55e, 0 4px 16px rgba(34,197,94,0.18) !important;
278
+ }
279
+
280
+ /* ── Transcribe button ── */
281
+ #transcribe-btn {
282
+ margin-top: 0.5rem !important;
283
+ }
284
+
285
+ #transcribe-btn button {
286
+ font-size: 1.05rem !important;
287
+ font-weight: 700 !important;
288
+ letter-spacing: 0.04em !important;
289
+ padding: 0.9rem !important;
290
+ border-radius: 14px !important;
291
+ box-shadow: 0 4px 20px rgba(34,197,94,0.28) !important;
292
+ transition: all 0.18s ease !important;
293
+ }
294
+
295
+ #transcribe-btn button:hover {
296
+ transform: translateY(-2px) !important;
297
+ box-shadow: 0 8px 28px rgba(34,197,94,0.4) !important;
298
+ }
299
+
300
+ #transcribe-btn button:active { transform: translateY(0) !important; }
301
+
302
+ /* ── Output ── */
303
+ #output-box textarea {
304
+ font-size: 1.1rem !important;
305
+ line-height: 1.75 !important;
306
+ min-height: 96px !important;
307
+ color: #dcfce7 !important;
308
+ }
309
+
310
+ /* ── Footer ── */
311
+ .nv-footer {
312
+ text-align: center;
313
+ padding: 1.5rem 0 2rem;
314
+ color: #6ee7a0;
315
+ font-size: 0.82rem;
316
+ border-top: 1px solid #1c3824;
317
+ margin-top: 1.5rem;
318
+ }
319
+
320
+ .nv-footer a { color: #4ade80; text-decoration: none; }
321
+ .nv-footer a:hover { text-decoration: underline; }
322
+
323
+ /* ── Scrollbar ── */
324
+ ::-webkit-scrollbar { width: 5px; height: 5px; }
325
+ ::-webkit-scrollbar-track { background: #060d07; }
326
+ ::-webkit-scrollbar-thumb { background: #1c3824; border-radius: 3px; }
327
+ ::-webkit-scrollbar-thumb:hover { background: #22c55e; }
328
+ """
329
+
330
+ # ── UI ─────────────────────────────────────────────────────────────────────────
331
+ with gr.Blocks(theme=theme, css=CSS, title="NaijaVox Demo β€” Nigerian ASR") as demo:
332
+
333
+ # Header
334
+ gr.HTML("""
335
+ <div class="nv-header">
336
+ <h1 class="nv-title">NaijaVox</h1>
337
+ <p class="nv-sub">
338
+ Open-weight Nigerian speech recognition &nbsp;Β·&nbsp;
339
+ Yoruba &nbsp;Β·&nbsp; Hausa &nbsp;Β·&nbsp; Igbo &nbsp;Β·&nbsp; Pidgin &nbsp;Β·&nbsp; Nigerian English
340
+ </p>
341
+ </div>
342
+ """)
343
+
344
+ # Model info cards (visual only)
345
+ gr.HTML("""
346
+ <div class="model-info-row">
347
+ <div class="model-info-card">
348
+ <p class="card-name">NaijaVox-V1</p>
349
+ <p class="card-stats">Avg WER <strong>27.9%</strong> &nbsp;Β·&nbsp; LoRA r=32 &nbsp;Β·&nbsp; 13,866 samples</p>
350
+ </div>
351
+ <div class="model-info-card featured">
352
+ <span class="mic-badge">IMPROVED</span>
353
+ <p class="card-name">NaijaVox-2.0</p>
354
+ <p class="card-stats">
355
+ Avg WER <strong>22.58%</strong> &nbsp;Β·&nbsp; LoRA r=64 + fc1/fc2 &nbsp;Β·&nbsp; 25,866 samples<br/>
356
+ <span style="color:#86efac;font-size:0.74rem;">SpecAugment Β· Noise augmentation Β· +19.1% better avg</span>
357
+ </p>
358
+ </div>
359
+ </div>
360
+ """)
361
+
362
+ # Model selector
363
+ gr.HTML('<p class="sl">Model</p>')
364
+ model_radio = gr.Radio(
365
+ choices=["NaijaVox-V1", "NaijaVox-2.0"],
366
+ value="NaijaVox-2.0",
367
+ label="",
368
+ elem_id="model-radio",
369
  )
370
+
371
+ # Language + Audio
372
+ gr.HTML('<p class="sl">Language</p>')
373
+ lang_dropdown = gr.Dropdown(
374
+ choices=list(LANGUAGES.keys()),
375
+ value="πŸ‡³πŸ‡¬ Nigerian English",
376
+ label="",
 
 
 
 
 
 
 
377
  )
378
 
379
+ gr.HTML('<p class="sl">Audio</p>')
380
+ audio_input = gr.Audio(
381
+ sources=["microphone", "upload"],
382
+ type="numpy",
383
+ label="",
384
+ )
385
+
386
+ # Transcribe button
387
+ transcribe_btn = gr.Button(
388
+ "Transcribe",
389
+ variant="primary",
390
+ elem_id="transcribe-btn",
391
+ )
392
+
393
+ # Output
394
+ gr.HTML('<p class="sl">Transcription</p>')
395
+ output_box = gr.Textbox(
396
+ label="",
397
+ placeholder="Transcription will appear here...",
398
+ lines=4,
399
+ elem_id="output-box",
400
+ )
401
+
402
+ # Wire up
403
+ transcribe_btn.click(
404
+ fn=transcribe,
405
+ inputs=[audio_input, lang_dropdown, model_radio],
406
+ outputs=output_box,
407
+ )
408
+
409
+ # Footer
410
+ gr.HTML("""
411
+ <div class="nv-footer">
412
+ <a href="https://huggingface.co/Axiveri/NaijaVox-V1">NaijaVox-V1</a>
413
+ &nbsp;Β·&nbsp;
414
+ <a href="https://huggingface.co/Axiveri/NaijaVox-2.0">NaijaVox-2.0</a>
415
+ &nbsp;Β·&nbsp;
416
+ <a href="https://huggingface.co/collections/Axiveri/naijavox-nigerian-speech-recognition">NaijaVox Collection</a>
417
+ &nbsp;Β·&nbsp;
418
+ Built by <a href="https://huggingface.co/ememzyvisuals">Emmanuel Ariyo</a> Β· Axiveri
419
+ </div>
420
+ """)
421
+
422
  demo.launch()