voidash commited on
Commit
145c9e7
·
verified ·
1 Parent(s): 4d51cdd

fix mic input: always downmix to mono 16 kHz (stereo recordings crashed NeMo)

Browse files
Files changed (1) hide show
  1. app.py +33 -15
app.py CHANGED
@@ -20,30 +20,48 @@ def get_model():
20
  return MODEL
21
 
22
 
23
- def _ensure_wav(path):
24
- """Mic recordings arrive as webm/mp4; libsndfile can't read those convert."""
 
 
 
 
 
 
 
 
25
  import soundfile as sf
 
26
  try:
27
- sf.info(path)
28
- return path
 
29
  except Exception:
30
- import subprocess
31
- import tempfile
32
- out = tempfile.NamedTemporaryFile(suffix=".wav", delete=False).name
33
- subprocess.run(
34
- ["ffmpeg", "-y", "-loglevel", "error", "-i", path, "-ar", "16000", "-ac", "1", out],
35
- check=True,
 
 
 
 
 
 
 
 
 
 
36
  )
37
- return out
38
 
39
 
40
  def transcribe(audio_path):
41
  if not audio_path:
42
  return "(no audio)"
43
- import soundfile as sf
44
- audio_path = _ensure_wav(audio_path)
45
- info = sf.info(audio_path)
46
- if info.duration > 60:
47
  return "Please keep clips under 60 seconds for this CPU demo."
48
  model = get_model()
49
  out = model.transcribe([audio_path], batch_size=1, verbose=False)[0]
 
20
  return MODEL
21
 
22
 
23
+ def _prepare(path):
24
+ """Normalize any input to mono 16 kHz PCM and return (path, duration_seconds).
25
+
26
+ Browser microphone recordings arrive as stereo (and sometimes as webm/mp4 that
27
+ libsndfile cannot open at all). The model takes a mono signal only: feeding it a
28
+ two-channel file raises "Output shape expected = (batch, time)" inside NeMo.
29
+ """
30
+ import subprocess
31
+ import tempfile
32
+
33
  import soundfile as sf
34
+
35
  try:
36
+ info = sf.info(path)
37
+ needs_convert = info.channels != 1 or info.samplerate != 16000
38
+ duration = info.duration
39
  except Exception:
40
+ needs_convert = True
41
+ duration = None
42
+
43
+ if not needs_convert:
44
+ return path, duration
45
+
46
+ out = tempfile.NamedTemporaryFile(suffix=".wav", delete=False).name
47
+ proc = subprocess.run(
48
+ ["ffmpeg", "-y", "-loglevel", "error", "-i", path,
49
+ "-ar", "16000", "-ac", "1", "-c:a", "pcm_s16le", out],
50
+ capture_output=True, text=True,
51
+ )
52
+ if proc.returncode != 0:
53
+ raise gr.Error(
54
+ "Could not read that audio (ffmpeg: %s). Try a WAV or MP3 file."
55
+ % (proc.stderr.strip().splitlines() or ["unknown error"])[-1]
56
  )
57
+ return out, sf.info(out).duration
58
 
59
 
60
  def transcribe(audio_path):
61
  if not audio_path:
62
  return "(no audio)"
63
+ audio_path, duration = _prepare(audio_path)
64
+ if duration is not None and duration > 60:
 
 
65
  return "Please keep clips under 60 seconds for this CPU demo."
66
  model = get_model()
67
  out = model.transcribe([audio_path], batch_size=1, verbose=False)[0]