Spaces:
Running on Zero
Running on Zero
| #!/usr/bin/env python3 | |
| """CPU-only Whisper ASR for auto-transcribing voice reference clips. | |
| Runs on CPU and is never wrapped in @spaces.GPU — keeps it off the ZeroGPU | |
| quota entirely and lets it run any time, independent of whichever TTS | |
| backend currently holds the GPU. Powers the auto-fill of "Reference | |
| transcript" when a voice is picked from the gallery, which improves Higgs | |
| Audio v3's zero-shot cloning quality. | |
| Multilingual `whisper-base` (not the `.en` variant) since the voice gallery | |
| spans many languages. | |
| """ | |
| import logging | |
| import torch | |
| ASR_REPO = "openai/whisper-base" | |
| ASR_SAMPLE_RATE = 16000 | |
| _processor = None | |
| _model = None | |
| def load(): | |
| """Load the Whisper processor + model onto CPU. Idempotent.""" | |
| global _processor, _model | |
| if _model is not None: | |
| return | |
| from transformers import AutoProcessor, WhisperForConditionalGeneration | |
| logging.info(f"Loading Whisper ASR ({ASR_REPO}) on CPU…") | |
| _processor = AutoProcessor.from_pretrained(ASR_REPO) | |
| _model = WhisperForConditionalGeneration.from_pretrained(ASR_REPO).eval() | |
| logging.info("Whisper ASR ready.") | |
| def transcribe(audio_path): | |
| """Best-effort CPU transcription of a reference clip. | |
| Returns the stripped transcript, or "" if there's no clip or | |
| transcription fails — callers treat "" as "leave the field as-is / | |
| let the user fill it in manually". | |
| """ | |
| if not audio_path or _model is None: | |
| return "" | |
| import soundfile as sf | |
| import torchaudio | |
| try: | |
| data, sr = sf.read(audio_path, dtype="float32", always_2d=True) # [L, C] | |
| wav = torch.from_numpy(data).mean(dim=1) # mono [L] | |
| if sr != ASR_SAMPLE_RATE: | |
| wav = torchaudio.functional.resample(wav, orig_freq=sr, new_freq=ASR_SAMPLE_RATE) | |
| inputs = _processor(wav.numpy(), sampling_rate=ASR_SAMPLE_RATE, return_tensors="pt") | |
| with torch.no_grad(): | |
| tokens = _model.generate(**inputs) | |
| return _processor.batch_decode(tokens, skip_special_tokens=True)[0].strip() | |
| except Exception as e: | |
| logging.warning(f"Reference transcription failed: {e}") | |
| return "" | |