Spaces:
Running on Zero
Running on Zero
Auto-transcribe reference audio with CPU Whisper into the editable transcript field
Browse files
app.py
CHANGED
|
@@ -9,6 +9,7 @@ os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF", "expandable_segments:True")
|
|
| 9 |
|
| 10 |
import spaces # MUST come before any torch/CUDA import
|
| 11 |
import tempfile
|
|
|
|
| 12 |
import time
|
| 13 |
from pathlib import Path
|
| 14 |
|
|
@@ -27,6 +28,7 @@ from breeze_infer.templates import get_template, prepare_inputs
|
|
| 27 |
from models.fast_streaming import FastBreezeStreamingRuntime, FastStreamingConfig
|
| 28 |
|
| 29 |
MODEL_ID = "BreezeBlue/Breeze-TTS-2"
|
|
|
|
| 30 |
|
| 31 |
MAX_NEW_TOKENS = 1500
|
| 32 |
MAX_SEQ_LEN = 2048
|
|
@@ -74,6 +76,67 @@ print("Breeze TTS 2 loaded successfully.")
|
|
| 74 |
print(f"Sample rate: {_runtime.sample_rate}")
|
| 75 |
|
| 76 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
@spaces.GPU(duration=120)
|
| 78 |
def generate_voice_design(
|
| 79 |
text: str,
|
|
@@ -295,9 +358,16 @@ with gr.Blocks(theme=gr.themes.Citrus(), css=CSS) as demo:
|
|
| 295 |
)
|
| 296 |
clone_ref_text = gr.Textbox(
|
| 297 |
label="Reference transcript (exact)",
|
|
|
|
| 298 |
placeholder="This is the exact transcript of the reference audio.",
|
| 299 |
lines=2,
|
| 300 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 301 |
with gr.Accordion("Advanced settings", open=False):
|
| 302 |
clone_seed = gr.Number(label="Seed", value=42, precision=0)
|
| 303 |
clone_btn = gr.Button("Generate", variant="primary")
|
|
@@ -338,9 +408,16 @@ with gr.Blocks(theme=gr.themes.Citrus(), css=CSS) as demo:
|
|
| 338 |
)
|
| 339 |
dir_ref_text = gr.Textbox(
|
| 340 |
label="Reference transcript (exact)",
|
|
|
|
| 341 |
placeholder="This is the exact transcript of the reference audio.",
|
| 342 |
lines=2,
|
| 343 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 344 |
with gr.Accordion("Advanced settings", open=False):
|
| 345 |
dir_cfg = gr.Slider(
|
| 346 |
label="CFG scale",
|
|
@@ -378,4 +455,10 @@ with gr.Blocks(theme=gr.themes.Citrus(), css=CSS) as demo:
|
|
| 378 |
"Weights/outputs are for research and non-commercial use only."
|
| 379 |
)
|
| 380 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 381 |
demo.launch(mcp_server=True)
|
|
|
|
| 9 |
|
| 10 |
import spaces # MUST come before any torch/CUDA import
|
| 11 |
import tempfile
|
| 12 |
+
import threading
|
| 13 |
import time
|
| 14 |
from pathlib import Path
|
| 15 |
|
|
|
|
| 28 |
from models.fast_streaming import FastBreezeStreamingRuntime, FastStreamingConfig
|
| 29 |
|
| 30 |
MODEL_ID = "BreezeBlue/Breeze-TTS-2"
|
| 31 |
+
ASR_MODEL_ID = "openai/whisper-small" # multilingual (en/zh), runs on CPU
|
| 32 |
|
| 33 |
MAX_NEW_TOKENS = 1500
|
| 34 |
MAX_SEQ_LEN = 2048
|
|
|
|
| 76 |
print(f"Sample rate: {_runtime.sample_rate}")
|
| 77 |
|
| 78 |
|
| 79 |
+
_asr_pipe = None
|
| 80 |
+
_asr_lock = threading.Lock()
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
def _get_asr():
|
| 84 |
+
"""Lazily build the CPU Whisper pipeline used to auto-transcribe reference audio."""
|
| 85 |
+
global _asr_pipe
|
| 86 |
+
with _asr_lock:
|
| 87 |
+
if _asr_pipe is None:
|
| 88 |
+
from transformers import pipeline
|
| 89 |
+
|
| 90 |
+
_asr_pipe = pipeline(
|
| 91 |
+
"automatic-speech-recognition",
|
| 92 |
+
model=ASR_MODEL_ID,
|
| 93 |
+
device="cpu",
|
| 94 |
+
chunk_length_s=30,
|
| 95 |
+
)
|
| 96 |
+
return _asr_pipe
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def transcribe_reference(
|
| 100 |
+
ref_audio_path: str | None,
|
| 101 |
+
progress=gr.Progress(track_tqdm=True),
|
| 102 |
+
):
|
| 103 |
+
"""Auto-transcribe uploaded/recorded reference audio with Whisper on CPU.
|
| 104 |
+
|
| 105 |
+
The result populates the (still editable) reference transcript field.
|
| 106 |
+
|
| 107 |
+
Args:
|
| 108 |
+
ref_audio_path: Path to the reference audio file, or None when cleared.
|
| 109 |
+
"""
|
| 110 |
+
if not ref_audio_path:
|
| 111 |
+
return gr.update()
|
| 112 |
+
|
| 113 |
+
try:
|
| 114 |
+
asr = _get_asr()
|
| 115 |
+
except Exception as exc: # pragma: no cover - model download/load failure
|
| 116 |
+
raise gr.Error(f"Could not load the transcription model: {exc}") from exc
|
| 117 |
+
|
| 118 |
+
try:
|
| 119 |
+
import librosa
|
| 120 |
+
|
| 121 |
+
audio, _ = librosa.load(ref_audio_path, sr=16000, mono=True)
|
| 122 |
+
asr_input = {"raw": audio, "sampling_rate": 16000}
|
| 123 |
+
except Exception:
|
| 124 |
+
# Fall back to letting the pipeline decode the file itself (ffmpeg).
|
| 125 |
+
asr_input = str(ref_audio_path)
|
| 126 |
+
|
| 127 |
+
try:
|
| 128 |
+
result = asr(asr_input, generate_kwargs={"task": "transcribe"})
|
| 129 |
+
except Exception as exc:
|
| 130 |
+
raise gr.Error(f"Automatic transcription failed: {exc}") from exc
|
| 131 |
+
|
| 132 |
+
text = (result.get("text") or "").strip()
|
| 133 |
+
if not text:
|
| 134 |
+
raise gr.Error(
|
| 135 |
+
"Automatic transcription produced no text — please type the transcript manually."
|
| 136 |
+
)
|
| 137 |
+
return text
|
| 138 |
+
|
| 139 |
+
|
| 140 |
@spaces.GPU(duration=120)
|
| 141 |
def generate_voice_design(
|
| 142 |
text: str,
|
|
|
|
| 358 |
)
|
| 359 |
clone_ref_text = gr.Textbox(
|
| 360 |
label="Reference transcript (exact)",
|
| 361 |
+
info="Auto-transcribed with Whisper when you add reference audio — please review and edit if needed.",
|
| 362 |
placeholder="This is the exact transcript of the reference audio.",
|
| 363 |
lines=2,
|
| 364 |
)
|
| 365 |
+
clone_ref_audio.change(
|
| 366 |
+
fn=transcribe_reference,
|
| 367 |
+
inputs=[clone_ref_audio],
|
| 368 |
+
outputs=[clone_ref_text],
|
| 369 |
+
api_name="transcribe_reference",
|
| 370 |
+
)
|
| 371 |
with gr.Accordion("Advanced settings", open=False):
|
| 372 |
clone_seed = gr.Number(label="Seed", value=42, precision=0)
|
| 373 |
clone_btn = gr.Button("Generate", variant="primary")
|
|
|
|
| 408 |
)
|
| 409 |
dir_ref_text = gr.Textbox(
|
| 410 |
label="Reference transcript (exact)",
|
| 411 |
+
info="Auto-transcribed with Whisper when you add reference audio — please review and edit if needed.",
|
| 412 |
placeholder="This is the exact transcript of the reference audio.",
|
| 413 |
lines=2,
|
| 414 |
)
|
| 415 |
+
dir_ref_audio.change(
|
| 416 |
+
fn=transcribe_reference,
|
| 417 |
+
inputs=[dir_ref_audio],
|
| 418 |
+
outputs=[dir_ref_text],
|
| 419 |
+
api_name="transcribe_reference_direction",
|
| 420 |
+
)
|
| 421 |
with gr.Accordion("Advanced settings", open=False):
|
| 422 |
dir_cfg = gr.Slider(
|
| 423 |
label="CFG scale",
|
|
|
|
| 455 |
"Weights/outputs are for research and non-commercial use only."
|
| 456 |
)
|
| 457 |
|
| 458 |
+
try:
|
| 459 |
+
_get_asr()
|
| 460 |
+
print(f"Whisper ASR ({ASR_MODEL_ID}) loaded on CPU for reference transcription.")
|
| 461 |
+
except Exception as _exc: # pragma: no cover
|
| 462 |
+
print(f"Warning: could not preload Whisper ASR ({_exc}); will retry on first use.")
|
| 463 |
+
|
| 464 |
demo.launch(mcp_server=True)
|