Spaces:
Sleeping
Sleeping
Upload audio_utils.py with huggingface_hub
Browse files- audio_utils.py +57 -0
audio_utils.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Audio preparation helpers for DreamVoice reference clips."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import os
|
| 6 |
+
import tempfile
|
| 7 |
+
|
| 8 |
+
import librosa
|
| 9 |
+
import numpy as np
|
| 10 |
+
import soundfile as sf
|
| 11 |
+
|
| 12 |
+
REFERENCE_SAMPLE_RATE = 16_000
|
| 13 |
+
MIN_REFERENCE_SECONDS = 5.0
|
| 14 |
+
MAX_REFERENCE_SECONDS = 60.0
|
| 15 |
+
MIN_RMS = 0.005
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def prepare_reference(path: str) -> str:
|
| 19 |
+
"""Clean and validate a voice reference clip for VoxCPM2.
|
| 20 |
+
|
| 21 |
+
The returned file is a temporary mono 16 kHz WAV. The caller owns cleanup of
|
| 22 |
+
that returned path after synthesis finishes.
|
| 23 |
+
"""
|
| 24 |
+
if not path or not os.path.exists(path):
|
| 25 |
+
raise ValueError("Please record or upload a voice clip first.")
|
| 26 |
+
|
| 27 |
+
try:
|
| 28 |
+
audio, _ = librosa.load(path, sr=REFERENCE_SAMPLE_RATE, mono=True)
|
| 29 |
+
except Exception as exc: # noqa: BLE001 - present a friendly UI-safe error.
|
| 30 |
+
raise ValueError("I couldn't read that audio clip. Please try a WAV or MP3 recording.") from exc
|
| 31 |
+
|
| 32 |
+
if audio.size == 0 or not np.isfinite(audio).all():
|
| 33 |
+
raise ValueError("That voice clip looks empty. Please record 5-60 seconds of clear speech.")
|
| 34 |
+
|
| 35 |
+
audio = np.asarray(audio, dtype=np.float32)
|
| 36 |
+
audio, _ = librosa.effects.trim(audio, top_db=35)
|
| 37 |
+
|
| 38 |
+
if audio.size == 0:
|
| 39 |
+
raise ValueError("That voice clip is too quiet. Please record closer to the microphone.")
|
| 40 |
+
|
| 41 |
+
peak = float(np.max(np.abs(audio)))
|
| 42 |
+
rms = float(np.sqrt(np.mean(np.square(audio))))
|
| 43 |
+
if peak <= 0.0 or rms < MIN_RMS:
|
| 44 |
+
raise ValueError("That voice clip is too quiet. Please record in a quiet room, closer to the microphone.")
|
| 45 |
+
|
| 46 |
+
duration = audio.size / REFERENCE_SAMPLE_RATE
|
| 47 |
+
if duration < MIN_REFERENCE_SECONDS:
|
| 48 |
+
raise ValueError("Please record at least 5 seconds of clear speech.")
|
| 49 |
+
if duration > MAX_REFERENCE_SECONDS:
|
| 50 |
+
max_samples = int(MAX_REFERENCE_SECONDS * REFERENCE_SAMPLE_RATE)
|
| 51 |
+
audio = audio[:max_samples]
|
| 52 |
+
|
| 53 |
+
audio = np.clip(audio, -1.0, 1.0)
|
| 54 |
+
fd, cleaned_path = tempfile.mkstemp(prefix="dreamvoice_ref_", suffix=".wav")
|
| 55 |
+
os.close(fd)
|
| 56 |
+
sf.write(cleaned_path, audio, REFERENCE_SAMPLE_RATE, subtype="PCM_16")
|
| 57 |
+
return cleaned_path
|