Spaces:
Running on Zero
Running on Zero
File size: 6,830 Bytes
7f96f26 ba4c0e4 7f96f26 | 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 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | import os
import time
os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF", "expandable_segments:True")
# ZeroGPU must be imported before torch or any module that imports torch.
import spaces
import gradio as gr
import numpy as np
import soundfile as sf
import torch
from scipy.signal import resample_poly
from src.modeling_moss_music import MossMusicModel
from src.processing_moss_music import MossMusicProcessor
MODEL_ID = "OpenMOSS-Team/MOSS-Music-8B-Instruct"
TITLE = "MOSS-Music-8B-Instruct"
DEFAULT_PROMPT = (
"请从风格与速度、调性与和声、乐器编配、结构安排以及整体情绪几个方面描述这段音乐。"
)
# ZeroGPU emulates CUDA while the app starts, packs the model weights, and
# materializes them on the allocated GPU when analyze_audio is called.
model = MossMusicModel.from_pretrained(
MODEL_ID,
trust_remote_code=True,
torch_dtype=torch.bfloat16,
low_cpu_mem_usage=True,
).to("cuda")
model.eval()
processor = MossMusicProcessor.from_pretrained(
MODEL_ID,
trust_remote_code=True,
enable_time_marker=True,
)
def load_audio(path: str, sample_rate: int) -> np.ndarray:
"""Load an audio file as mono float32 and resample it when necessary."""
waveform, original_sample_rate = sf.read(path, dtype="float32", always_2d=False)
if waveform.ndim > 1:
waveform = waveform.mean(axis=1)
if original_sample_rate != sample_rate:
waveform = resample_poly(waveform, sample_rate, original_sample_rate)
return np.asarray(waveform, dtype=np.float32)
@spaces.GPU(size="large", duration=300)
def analyze_audio(
audio_path: str | None,
prompt: str,
max_new_tokens: int,
temperature: float,
top_p: float,
top_k: int,
) -> tuple[str, str]:
question = (prompt or "").strip() or DEFAULT_PROMPT
started_at = time.perf_counter()
if audio_path:
raw_audio = load_audio(audio_path, sample_rate=processor.config.mel_sr)
inputs = processor(text=question, audios=[raw_audio], return_tensors="pt")
else:
inputs = processor(text=question, return_tensors="pt")
inputs = inputs.to(model.device)
if inputs.get("audio_data") is not None:
inputs["audio_data"] = inputs["audio_data"].to(model.dtype)
inputs["audio_input_mask"] = inputs["input_ids"] == processor.audio_token_id
generation_kwargs = {
"max_new_tokens": int(max_new_tokens),
"num_beams": 1,
"use_cache": True,
}
if temperature > 0:
generation_kwargs.update(
do_sample=True,
temperature=float(temperature),
top_p=float(top_p),
top_k=int(top_k),
)
else:
generation_kwargs["do_sample"] = False
with torch.inference_mode():
generated_ids = model.generate(**inputs, **generation_kwargs)
input_length = inputs["input_ids"].shape[1]
answer = processor.decode(
generated_ids[0, input_length:],
skip_special_tokens=True,
).strip()
elapsed = time.perf_counter() - started_at
generated_tokens = generated_ids.shape[1] - input_length
status = (
f"Model: `{MODEL_ID}` \n"
f"Elapsed: `{elapsed:.2f}s` \n"
f"Generated tokens: `{generated_tokens}`"
)
return answer, status
CSS = """
#app-container { max-width: 1120px; margin: 0 auto; }
"""
with gr.Blocks(title=TITLE, css=CSS) as demo:
with gr.Column(elem_id="app-container"):
gr.Markdown(f"# {TITLE}")
gr.Markdown(
"上传整曲音频音乐后,可以进行音乐描述、歌词识别、和弦/调性/速度分析、"
"段落结构分析和开放式音乐问答。"
)
with gr.Row():
with gr.Column(scale=5):
audio_input = gr.Audio(
label="音乐文件",
sources=["upload", "microphone"],
type="filepath",
)
prompt_input = gr.Textbox(
label="问题 / Prompt",
value=DEFAULT_PROMPT,
lines=4,
)
with gr.Accordion("高级参数", open=False):
max_new_tokens_input = gr.Slider(
minimum=64,
maximum=2048,
value=512,
step=32,
label="Max new tokens",
)
temperature_input = gr.Slider(
minimum=0,
maximum=1.5,
value=1.0,
step=0.1,
label="Temperature(0 为贪心解码)",
)
top_p_input = gr.Slider(
minimum=0.1,
maximum=1.0,
value=0.8,
step=0.05,
label="Top-p",
)
top_k_input = gr.Slider(
minimum=1,
maximum=100,
value=50,
step=1,
label="Top-k",
)
with gr.Row():
submit_button = gr.Button("开始分析", variant="primary")
gr.ClearButton(
[audio_input, prompt_input],
value="清空",
)
with gr.Column(scale=5):
output_text = gr.Textbox(
label="分析结果",
lines=20,
show_copy_button=True,
)
status_text = gr.Markdown("等待输入。")
gr.Examples(
examples=[
[None, "请详细描述这段音乐的风格、情绪、速度与主要乐器。"],
[None, "请转录这首歌的歌词,并尽量给出时间戳。"],
[None, "请分析这首歌的调性、速度和和弦进行。"],
[None, "请将这首歌划分为 intro、verse、chorus、bridge 和 outro。"],
],
inputs=[audio_input, prompt_input],
label="示例问题",
)
submit_button.click(
fn=analyze_audio,
inputs=[
audio_input,
prompt_input,
max_new_tokens_input,
temperature_input,
top_p_input,
top_k_input,
],
outputs=[output_text, status_text],
api_name="analyze",
)
if __name__ == "__main__":
demo.queue(default_concurrency_limit=1, max_size=8).launch(
server_name="0.0.0.0",
server_port=7860,
ssr_mode=False,
)
|