Yongkang ZOU commited on
Commit
7e4b79e
·
1 Parent(s): f8a57e8

add music score utility

Browse files
Files changed (1) hide show
  1. app.py +92 -24
app.py CHANGED
@@ -2,49 +2,117 @@ import gradio as gr
2
  import requests
3
  import os
4
  from dotenv import load_dotenv
 
 
 
 
 
 
5
 
6
  # 加载环境变量
7
  load_dotenv()
8
  MUSICGEN_API_URL = os.getenv("MUSICGEN_API_URL")
9
 
10
- def generate_music(melody_file, prompt):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  """
12
- 使用上传的哼唱音频和文字描述生成新的音乐。
13
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  if not MUSICGEN_API_URL:
15
- return "❌ 未配置 MUSICGEN_API_URL,请检查 .env 文件"
16
 
 
 
 
 
17
  try:
18
- with open(melody_file, "rb") as f: # melody_file 是字符串路径
19
- files = {
20
- "melody": ("hum.wav", f, "audio/wav") # 用一个合理的名字传给接口
21
- }
22
- data = {
23
- "text": prompt
24
- }
25
  response = requests.post(MUSICGEN_API_URL, files=files, data=data)
26
  except Exception as e:
27
- return f"❌ 文件读取失败: {e}"
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
- if response.status_code == 200:
30
- output_path = "generated_music.wav"
31
- with open(output_path, "wb") as out:
32
- out.write(response.content)
33
- return output_path
34
- else:
35
- return f"❌ 错误 {response.status_code}: {response.text}"
36
 
 
37
  demo = gr.Interface(
38
- fn=generate_music,
39
  inputs=[
40
- gr.Audio(type="filepath", label="上传你的哼唱音频 (.wav)"),
41
- gr.Textbox(label="请输入你想生成的音乐风格或内容")
42
  ],
43
  outputs=[
44
- gr.Audio(type="filepath", label="生成的音乐")
 
45
  ],
46
- title="🎵 MusicGen 创作助手",
47
- description="上传哼唱并输入描述,自动生成带风格的音乐。"
48
  )
49
 
50
  if __name__ == "__main__":
 
2
  import requests
3
  import os
4
  from dotenv import load_dotenv
5
+ from datetime import datetime
6
+ from pathlib import Path
7
+ from basic_pitch.inference import predict_and_save
8
+ from basic_pitch import ICASSP_2022_MODEL_PATH
9
+ from music21 import converter
10
+ import shutil
11
 
12
  # 加载环境变量
13
  load_dotenv()
14
  MUSICGEN_API_URL = os.getenv("MUSICGEN_API_URL")
15
 
16
+ # 确保输出目录存在
17
+ Path("output").mkdir(exist_ok=True)
18
+ Path("static").mkdir(exist_ok=True)
19
+
20
+ def wav_to_midi_and_musicxml(wav_path: str, timestamp: str) -> Path:
21
+ """
22
+ 将 wav 文件转换为 MIDI 和 MusicXML
23
+ """
24
+ output_dir = Path("output")
25
+
26
+ # 清理旧 MIDI 文件
27
+ for f in output_dir.glob("*_basic_pitch.mid"):
28
+ f.unlink()
29
+
30
+ # 生成 MIDI
31
+ predict_and_save(
32
+ audio_path_list=[wav_path],
33
+ output_directory=str(output_dir),
34
+ save_midi=True,
35
+ sonify_midi=False,
36
+ save_model_outputs=False,
37
+ save_notes=False,
38
+ model_or_model_path=ICASSP_2022_MODEL_PATH
39
+ )
40
+
41
+ midi_files = list(output_dir.glob("*.mid"))
42
+ if not midi_files:
43
+ raise FileNotFoundError("❌ MIDI 文件生成失败")
44
+
45
+ # MIDI → MusicXML
46
+ midi_path = midi_files[0]
47
+ score = converter.parse(midi_path)
48
+ musicxml_path = output_dir / f"generated_{timestamp}.musicxml"
49
+ score.write("musicxml", fp=musicxml_path)
50
+
51
+ return musicxml_path
52
+
53
+ def build_verovio_iframe(musicxml_path: Path) -> str:
54
  """
55
+ 将 MusicXML 文件复制到 static 目录,并生成 Verovio 的 iframe HTML
56
  """
57
+ static_dir = Path("static")
58
+ target = static_dir / musicxml_path.name
59
+ shutil.copy(musicxml_path, target)
60
+
61
+ return f"""
62
+ <iframe
63
+ src="https://www.verovio.org/editor.html?load=static/{target.name}"
64
+ width="100%"
65
+ height="500px"
66
+ style="border: none;"
67
+ ></iframe>
68
+ """
69
+
70
+ def generate_music_and_score(melody_file, prompt):
71
  if not MUSICGEN_API_URL:
72
+ return None, "❌ 未配置 API URL,请检查 .env 文件"
73
 
74
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
75
+ wav_out_path = f"output/generated_{timestamp}.wav"
76
+
77
+ # 1. 调用 MusicGen 接口
78
  try:
79
+ with open(melody_file, "rb") as f:
80
+ files = {"melody": ("hum.wav", f, "audio/wav")}
81
+ data = {"text": prompt}
 
 
 
 
82
  response = requests.post(MUSICGEN_API_URL, files=files, data=data)
83
  except Exception as e:
84
+ return None, f"❌ 请求失败: {e}"
85
+
86
+ if response.status_code != 200:
87
+ return None, f"❌ API 错误 {response.status_code}: {response.text}"
88
+
89
+ # 保存生成的 wav
90
+ with open(wav_out_path, "wb") as out:
91
+ out.write(response.content)
92
+
93
+ # 2. wav → midi → musicxml
94
+ try:
95
+ musicxml_path = wav_to_midi_and_musicxml(wav_out_path, timestamp)
96
+ except Exception as e:
97
+ return wav_out_path, f"⚠️ 音乐生成成功,但乐谱转换失败:{e}"
98
 
99
+ # 3. 构造 iframe
100
+ iframe_html = build_verovio_iframe(musicxml_path)
101
+ return wav_out_path, iframe_html
 
 
 
 
102
 
103
+ # Gradio 接口
104
  demo = gr.Interface(
105
+ fn=generate_music_and_score,
106
  inputs=[
107
+ gr.Audio(type="filepath", label="上传哼唱音频 (.wav)"),
108
+ gr.Textbox(label="描述你想要的音乐风格(prompt)")
109
  ],
110
  outputs=[
111
+ gr.Audio(type="filepath", label="🎵 生成的音乐"),
112
+ gr.HTML(label="🎼 乐谱预览 (Verovio)")
113
  ],
114
+ title="🎵 MusicGen + 乐谱生成器",
115
+ description="上传一段哼唱 (.wav) 和文字描述,自动生成音乐,并以 Verovio 可视化乐谱。"
116
  )
117
 
118
  if __name__ == "__main__":