urwebsiteaz-ux commited on
Commit
4c9ee07
Β·
1 Parent(s): d3752b1

chnaged app for the audio

Browse files
Files changed (1) hide show
  1. app.py +106 -7
app.py CHANGED
@@ -1,6 +1,11 @@
1
  """AUTOLYRICS β€” side-by-side baseline vs fine-tuned Gradio demo."""
2
  import os
3
  import time
 
 
 
 
 
4
  import torch
5
  import torchaudio
6
  import gradio as gr
@@ -36,12 +41,93 @@ print("Models ready.")
36
 
37
 
38
  def load_audio(path: str) -> torch.Tensor:
39
- wav, sr = torchaudio.load(path)
40
- if wav.shape[0] > 1:
41
- wav = wav.mean(0, keepdim=True)
42
- if sr != 16000:
43
- wav = torchaudio.functional.resample(wav, sr, 16000)
44
- return wav.squeeze(0)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
 
47
  @torch.inference_mode()
@@ -64,7 +150,20 @@ def transcribe_with(model, audio_tensor, num_beams: int):
64
  def run(audio_path: str, num_beams: int, model_choice: str):
65
  if audio_path is None:
66
  return "β€”", "β€”", "β€”", "β€”", "Please upload audio."
67
- audio = load_audio(audio_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  duration = audio.shape[-1] / 16000
69
 
70
  if model_choice == "Baseline only":
 
1
  """AUTOLYRICS β€” side-by-side baseline vs fine-tuned Gradio demo."""
2
  import os
3
  import time
4
+ import subprocess
5
+ import tempfile
6
+ import wave
7
+ from pathlib import Path
8
+ import numpy as np
9
  import torch
10
  import torchaudio
11
  import gradio as gr
 
41
 
42
 
43
  def load_audio(path: str) -> torch.Tensor:
44
+ """Load any browser-uploaded audio format β†’ 16 kHz mono float32 tensor.
45
+
46
+ Strategy (two-stage, zero libsndfile dependency):
47
+ 1. ffmpeg transcodes ANY browser format (webm/opus, ogg, mp3, m4a, wav)
48
+ into a clean 16-bit PCM WAV at 16 kHz mono. ffmpeg handles every
49
+ container/codec that browsers produce, including Gradio mic recordings.
50
+ 2. Python's built-in `wave` module reads the raw PCM bytes directly.
51
+ This **completely bypasses soundfile / libsndfile**, which cannot decode
52
+ webm, ogg/opus, or partially-encoded containers and raises
53
+ ``soundfile.LibsndfileError: Format not recognised`` on HF Spaces.
54
+
55
+ ffmpeg is pre-installed on HF Spaces via packages.txt β€” no extra Python
56
+ package is needed. `wave` and `numpy` are always available.
57
+
58
+ Returns
59
+ -------
60
+ torch.Tensor
61
+ 1-D float32 waveform on CPU, normalised to [-1, 1], at 16 000 Hz.
62
+ """
63
+ src = Path(path)
64
+ if not src.exists() or src.stat().st_size == 0:
65
+ raise ValueError(f"Audio file missing or empty: {path}")
66
+
67
+ with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmp:
68
+ tmp_path = tmp.name
69
+
70
+ try:
71
+ # ── Step 1: transcode to clean PCM WAV via ffmpeg ──────────────────
72
+ result = subprocess.run(
73
+ [
74
+ "ffmpeg", "-y", # overwrite without prompting
75
+ "-i", str(src), # any browser-upload format
76
+ "-ac", "1", # force mono
77
+ "-ar", "16000", # resample to 16 kHz
78
+ "-sample_fmt", "s16", # 16-bit signed PCM
79
+ "-f", "wav", # output container: wav
80
+ tmp_path,
81
+ ],
82
+ stdout=subprocess.DEVNULL,
83
+ stderr=subprocess.PIPE,
84
+ timeout=60,
85
+ )
86
+ if result.returncode != 0:
87
+ err = result.stderr.decode(errors="replace").strip().splitlines()
88
+ raise RuntimeError(
89
+ f"ffmpeg failed (code {result.returncode}): "
90
+ f"{err[-1] if err else 'unknown error'}"
91
+ )
92
+
93
+ # ── Step 2: read PCM bytes with stdlib `wave` β€” no soundfile ───────
94
+ with wave.open(tmp_path, "rb") as wf:
95
+ n_channels = wf.getnchannels()
96
+ sampwidth = wf.getsampwidth() # bytes per sample: 2 for s16
97
+ framerate = wf.getframerate()
98
+ n_frames = wf.getnframes()
99
+ if n_frames == 0:
100
+ raise ValueError("ffmpeg produced an empty audio file.")
101
+ raw = wf.readframes(n_frames)
102
+
103
+ # Parse raw bytes β†’ float32 in [-1, 1]
104
+ # ffmpeg guarantees s16, but use sampwidth defensively.
105
+ if sampwidth == 2:
106
+ arr = np.frombuffer(raw, dtype=np.int16).astype(np.float32) / 32768.0
107
+ elif sampwidth == 4:
108
+ arr = np.frombuffer(raw, dtype=np.int32).astype(np.float32) / 2147483648.0
109
+ else:
110
+ arr = np.frombuffer(raw, dtype=np.uint8).astype(np.float32) / 128.0 - 1.0
111
+
112
+ # Mix down multi-channel (guard: -ac 1 already handles this)
113
+ if n_channels > 1:
114
+ arr = arr.reshape(-1, n_channels).mean(axis=1)
115
+
116
+ wav = torch.from_numpy(arr.copy()) # copy() avoids non-writable buffer warning
117
+
118
+ # Resample if framerate drifted (guard: -ar 16000 already handles this)
119
+ if framerate != 16000:
120
+ wav = torchaudio.functional.resample(
121
+ wav.unsqueeze(0), framerate, 16000
122
+ ).squeeze(0)
123
+
124
+ return wav # 1-D float32 CPU tensor
125
+
126
+ finally:
127
+ try:
128
+ os.unlink(tmp_path)
129
+ except OSError:
130
+ pass
131
 
132
 
133
  @torch.inference_mode()
 
150
  def run(audio_path: str, num_beams: int, model_choice: str):
151
  if audio_path is None:
152
  return "β€”", "β€”", "β€”", "β€”", "Please upload audio."
153
+
154
+ # Load and decode audio β€” raises ValueError/RuntimeError on bad input.
155
+ try:
156
+ audio = load_audio(audio_path)
157
+ except (ValueError, RuntimeError, subprocess.TimeoutExpired) as exc:
158
+ err_msg = f"⚠️ Audio error: {exc}"
159
+ return err_msg, err_msg, "β€”", "β€”", "Audio could not be decoded β€” try a different file."
160
+ except Exception as exc: # noqa: BLE001
161
+ err_msg = f"⚠️ Unexpected error loading audio: {exc}"
162
+ return err_msg, err_msg, "β€”", "β€”", "Audio could not be decoded β€” try a different file."
163
+
164
+ if audio.numel() == 0:
165
+ return "β€”", "β€”", "β€”", "β€”", "⚠️ Audio file appears to be empty or silent."
166
+
167
  duration = audio.shape[-1] / 16000
168
 
169
  if model_choice == "Baseline only":