Spaces:
Running on Zero
Running on Zero
File size: 5,177 Bytes
a84b146 c2aac78 a84b146 c2aac78 a84b146 44cbb43 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | 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"],
)
@spaces.GPU
@torch.inference_mode()
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)
|