MusiConGen / app.py
Vansh Chugh
fidelity: match reference script's loudness normalization on output
0c534f3
Raw
History Blame Contribute Delete
4.7 kB
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 threading
import audiotools
import gradio as gr
import torch
from pyharp import ModelCard, build_endpoint, save_audio
from audiocraft.data.audio_utils import normalize_audio
from audiocraft.models import MusicGen
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
CHECKPOINT_REPO = "Cyan0731/MusiConGen"
model = None
model_ready = False # has model been moved onto the GPU yet?
model_loading = True
model_error = None
def load_model():
"""Download + construct on CPU only. ZeroGPU only intercepts CUDA calls
made inside @spaces.GPU decorated call."""
global model, model_loading, model_error
try:
model = MusicGen.get_pretrained(CHECKPOINT_REPO, device="cpu")
print("Model loaded (CPU).")
except Exception as e:
model_error = str(e)
print(f"Load error: {e}")
finally:
model_loading = False
threading.Thread(target=load_model, daemon=True).start()
model_card = ModelCard(
name="MusiConGen",
description=(
"Text-to-music generation with rhythm and chord control. Generates a "
"music clip from a text description, a chord progression, and a "
"tempo/time signature."
),
author="Yun-Han Lan, Wen-Yi Hsiao, Hao-Chung Cheng, Yi-Hsuan Yang",
tags=["music generation", "text-to-music"],
)
@spaces.GPU(duration=120)
@torch.inference_mode()
def process_fn(description, chords, bpm, meter, duration, conditioning_strength):
"""Generate a music clip conditioned on text, chords, and rhythm."""
global model, model_ready
if model_loading:
raise gr.Error("Model is still loading, please wait a moment and try again.")
if model is None:
raise gr.Error(f"Model failed to load: {model_error}")
if not model_ready:
model = MusicGen.get_pretrained(CHECKPOINT_REPO, device=DEVICE)
model_ready = True
model.set_generation_params(
duration=duration,
extend_stride=duration // 2,
cfg_coef=conditioning_strength,
)
wav = model.generate_with_chords_and_beats([description], [chords], [bpm], [meter])
wav = normalize_audio(wav[0].cpu(), strategy="loudness", loudness_compressor=True, sample_rate=model.sample_rate)
signal = audiotools.AudioSignal(wav, sample_rate=model.sample_rate)
return save_audio(signal)
with gr.Blocks() as demo:
input_components = [
gr.Textbox(
label="Description",
value="A laid-back blues shuffle with a relaxed tempo, warm guitar tones, and a comfortable groove. Instruments: electric guitar, bass, drums.",
info="Text description of the music to generate.",
),
gr.Textbox(
label="Chord Progression",
value="C G A:min F",
info="Space-separated chord symbols, one per bar, repeating to fill the duration (e.g. 'C G A:min F'). Syntax: root note plus optional ':quality', e.g. C, A:min, D:min7.",
),
gr.Number(
label="Tempo (BPM)", value=120, minimum=40, maximum=240,
info="Tempo in beats per minute (default: 120, per repo demo script).",
),
gr.Number(
label="Time Signature (beats per bar)", value=4, minimum=2, maximum=12,
info="Numerator of the time signature (default: 4, per repo demo script).",
),
gr.Slider(
minimum=5, maximum=30, step=1, value=30, label="Duration (seconds)",
info="Length of the generated clip (default/max: 30s, the model's trained segment length, per repo config segment_duration=30).",
),
gr.Slider(
minimum=0.0, maximum=10.0, step=0.5, value=3.0, label="Conditioning Strength",
info="How strongly generation follows the description/chords/rhythm vs. sounding more free (default: 3.0, per paper guidance scale γ).",
),
]
output_components = [
gr.Audio(type="filepath", label="Generated Music").set_info("Generated music, 32kHz."),
]
build_endpoint(
model_card=model_card,
input_components=input_components,
output_components=output_components,
process_fn=process_fn,
)
demo.queue().launch(pwa=True)