Spaces:
Running on Zero
Running on Zero
| import sys | |
| sys.stdout.reconfigure(line_buffering=True) | |
| try: | |
| import spaces | |
| except ImportError: | |
| # keep @spaces.GPU usable as a no-op; ZeroGPU requires this exact name. | |
| class spaces: | |
| class GPU: | |
| def __init__(self, func=None, duration=60): | |
| self.func = func | |
| def __call__(self, *args, **kwargs): | |
| if self.func is not None: | |
| return self.func(*args, **kwargs) | |
| func = args[0] | |
| return func | |
| import gradio as gr | |
| import torch | |
| import librosa | |
| import numpy as np | |
| import soundfile as sf | |
| # adapted from AI-Music-Detection-HFSpace/app.py: torchaudio 2.9+ routes | |
| # torchaudio.load through load_with_torchcodec, which isn't installed here. | |
| # Loading via librosa/soundfile instead sidesteps that dependency entirely. | |
| import torchaudio | |
| def _patched_load(filepath, *args, **kwargs): | |
| frame_offset = kwargs.pop("frame_offset", 0) | |
| num_frames = kwargs.pop("num_frames", -1) | |
| channels_first = kwargs.pop("channels_first", True) | |
| if len(args) >= 1: | |
| frame_offset = args[0] | |
| if len(args) >= 2: | |
| num_frames = args[1] | |
| data, sample_rate = librosa.load(str(filepath), sr=None, mono=False) | |
| if data.ndim == 1: | |
| data = data[np.newaxis, :] | |
| if frame_offset and int(frame_offset) > 0: | |
| data = data[:, int(frame_offset):] | |
| if num_frames and int(num_frames) > 0: | |
| data = data[:, : int(num_frames)] | |
| duration_s = data.shape[-1] / max(sample_rate, 1) | |
| if duration_s < 1.5: | |
| raise RuntimeError( | |
| f"Audio is too short ({duration_s:.2f} s). " | |
| "Please upload a clip of at least 2 seconds (a few seconds of music works best)." | |
| ) | |
| waveform = torch.from_numpy(np.ascontiguousarray(data)).float() | |
| if not channels_first: | |
| waveform = waveform.transpose(0, 1).contiguous() | |
| return waveform, sample_rate | |
| torchaudio.load = _patched_load | |
| class _AudioInfo: | |
| __slots__ = ("sample_rate", "num_frames", "num_channels", "bits_per_sample", "encoding") | |
| def _patched_info(filepath, *args, **kwargs): | |
| info = sf.info(str(filepath)) | |
| out = _AudioInfo() | |
| out.sample_rate = info.samplerate | |
| out.num_frames = info.frames | |
| out.num_channels = info.channels | |
| out.bits_per_sample = 0 | |
| out.encoding = info.format | |
| return out | |
| torchaudio.info = _patched_info | |
| import tempfile | |
| import threading | |
| from pathlib import Path | |
| from huggingface_hub import hf_hub_download | |
| from pyharp import ModelCard, build_endpoint | |
| from inference import inference | |
| CHECKPOINT_REPO = "teamup-tech/FST-AI-Music-Detection-checkpoints" | |
| CHECKPOINT_DIR = Path("checkpoints") | |
| checkpoints_loading = True | |
| checkpoint_error = None | |
| def download_checkpoints(): | |
| """Fetch the Stage-1 and Stage-2 checkpoints so inference.py finds them | |
| already on disk instead of downloading ~1.3 GB on the first request.""" | |
| global checkpoints_loading, checkpoint_error | |
| try: | |
| CHECKPOINT_DIR.mkdir(exist_ok=True) | |
| for filename in ("Stage-1.ckpt", "Stage-2.ckpt"): | |
| hf_hub_download(repo_id=CHECKPOINT_REPO, filename=filename, local_dir=str(CHECKPOINT_DIR)) | |
| print("Checkpoints ready.") | |
| except Exception as e: | |
| checkpoint_error = str(e) | |
| print(f"Checkpoint download error: {e}") | |
| finally: | |
| checkpoints_loading = False | |
| threading.Thread(target=download_checkpoints, daemon=True).start() | |
| model_card = ModelCard( | |
| name="Fusion Segment Transformer", | |
| description=( | |
| "Detects AI-generated music by fusing MERT audio embeddings with a " | |
| "structural, downbeat-segmented view of the track. Submitted to ICASSP 2026." | |
| ), | |
| author="Yumin Kim, Seonghyeon Go", | |
| tags=["music", "classification", "ai-detection"], | |
| ) | |
| def process_fn(audio_path: str) -> str: | |
| """Runs the two-stage detector on an uploaded track and writes the verdict to a text file.""" | |
| if checkpoints_loading: | |
| raise gr.Error("Model checkpoints are still downloading, please wait a moment and try again.") | |
| if checkpoint_error: | |
| raise gr.Error(f"Failed to download checkpoints: {checkpoint_error}") | |
| result = inference(audio_path) | |
| prediction = result.get("prediction", "Unknown") | |
| confidence = result.get("confidence", "0.00") | |
| input_name = Path(audio_path).name | |
| with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as f: | |
| f.write(f"{input_name}\n\nAI-Detection Results\nPrediction: {prediction} ({confidence}% confidence)\n") | |
| out_path = f.name | |
| return out_path | |
| with gr.Blocks() as demo: | |
| input_components = [ | |
| gr.Audio(type="filepath", label="Input Audio").harp_required(True), | |
| ] | |
| output_components = [ | |
| gr.File(type="filepath", label="Detection Result", file_types=[".txt"]).set_info( | |
| "Prediction (Real/Fake) and confidence, as a text file." | |
| ), | |
| ] | |
| build_endpoint( | |
| model_card=model_card, | |
| input_components=input_components, | |
| output_components=output_components, | |
| process_fn=process_fn, | |
| ) | |
| if __name__ == "__main__": | |
| demo.queue().launch(pwa=True) | |