admin commited on
Commit
c73c29a
·
1 Parent(s): 215960c
Files changed (2) hide show
  1. app.py +126 -77
  2. requirements.txt +2 -1
app.py CHANGED
@@ -1,20 +1,51 @@
1
  import os
2
  import re
3
  import json
4
- import uuid
5
  import torch
6
  import shutil
7
  import requests
 
 
8
  import gradio as gr
9
  from piano_transcription_inference import PianoTranscription, load_audio, sample_rate
10
- from huggingface_hub import snapshot_download
11
  from urllib.parse import urlparse
12
  from convert import midi2xml, xml2abc, xml2mxl, xml2jpg
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  WEIGHTS_PATH = (
15
- snapshot_download("Genius-Society/piano_trans", cache_dir="./__pycache__")
16
- + "/CRNN_note_F1=0.9677_pedal_F1=0.9186.pth"
17
- )
 
 
 
 
 
 
 
 
 
 
 
18
 
19
 
20
  def clean_cache(cache_dir):
@@ -34,15 +65,18 @@ def download_audio(url: str, save_path: str):
34
 
35
  def is_url(s: str):
36
  try:
 
37
  result = urlparse(s)
 
38
  return all([result.scheme, result.netloc])
39
 
40
  except:
 
41
  return False
42
 
43
 
44
  def audio2midi(audio_path: str, cache_dir: str):
45
- audio, _ = load_audio(audio_path, sr=sample_rate)
46
  transcriptor = PianoTranscription(
47
  device="cuda" if torch.cuda.is_available() else "cpu",
48
  checkpoint_path=WEIGHTS_PATH,
@@ -52,25 +86,11 @@ def audio2midi(audio_path: str, cache_dir: str):
52
  return midi_path, os.path.basename(audio_path).split(".")[-2].capitalize()
53
 
54
 
55
- def upl_infer(audio_path: str, cache_dir="./__pycache__/mode1"):
56
- clean_cache(cache_dir)
57
- try:
58
- print(audio_path)
59
- midi, title = audio2midi(audio_path, cache_dir)
60
- xml = midi2xml(midi, title)
61
- abc = xml2abc(xml)
62
- mxl = xml2mxl(xml)
63
- pdf, jpg = xml2jpg(xml)
64
- return midi, pdf, xml, mxl, abc, jpg
65
-
66
- except Exception as e:
67
- return None, None, None, None, f"{e}", None
68
-
69
-
70
- def get_1st_int(input_string: str):
71
  match = re.search(r"\d+", input_string)
72
  if match:
73
  return str(int(match.group()))
 
74
  else:
75
  return ""
76
 
@@ -79,9 +99,11 @@ def music163_song_info(id: str):
79
  detail_api = "https://music.163.com/api/v3/song/detail"
80
  parm_dict = {"id": id, "c": str([{"id": id}]), "csrf_token": ""}
81
  free = False
82
- song_name = "Failed to get the song"
83
  response = requests.get(detail_api, params=parm_dict)
 
84
  if response.status_code == 200:
 
85
  data = json.loads(response.text)
86
  if data and "songs" in data and data["songs"]:
87
  fee = int(data["songs"][0]["fee"])
@@ -89,31 +111,48 @@ def music163_song_info(id: str):
89
  song_name = str(data["songs"][0]["name"])
90
 
91
  else:
92
- song_name = "The song does not exist"
93
 
94
  else:
95
- raise ConnectionError(f"Error: {response.status_code}, {response.text}")
96
 
97
  return song_name, free
98
 
99
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  def url_infer(song: str, cache_dir="./__pycache__/mode2"):
101
  song_name = ""
102
- clean_cache(cache_dir)
103
- audio_path = f"/tmp/gradio/{uuid.uuid4().hex}/"
104
- os.makedirs(audio_path, exist_ok=True)
105
  try:
 
106
  if (is_url(song) and "163" in song and "?id=" in song) or song.isdigit():
107
- song_id = get_1st_int(song.split("?id=")[-1])
108
- song_url = f"https://music.163.com/song/media/outer/url?id={song_id}.mp3"
109
  song_name, free = music163_song_info(song_id)
110
  if not free:
111
- raise AttributeError("Unable to parse VIP songs")
112
 
113
- audio_path += f"{song_id}.mp3"
114
- download_audio(song_url, audio_path)
115
 
116
- midi, title = audio2midi(audio_path, cache_dir)
117
  if song_name:
118
  title = song_name
119
 
@@ -121,54 +160,64 @@ def url_infer(song: str, cache_dir="./__pycache__/mode2"):
121
  abc = xml2abc(xml)
122
  mxl = xml2mxl(xml)
123
  pdf, jpg = xml2jpg(xml)
124
- return audio_path, midi, pdf, xml, mxl, abc, jpg
 
125
 
126
  except Exception as e:
127
- return None, None, None, None, None, f"{e}", None
 
 
128
 
129
 
130
  if __name__ == "__main__":
131
  with gr.Blocks() as iface:
132
- gr.Markdown("# Piano Transcription Tool")
133
- # with gr.Tab("Uploading Mode"):
134
- gr.Interface(
135
- fn=upl_infer,
136
- inputs=gr.Audio(
137
- label="Upload an audio",
138
- type="filepath",
139
- ),
140
- outputs=[
141
- gr.File(label="Download MIDI"),
142
- gr.File(label="Download PDF score"),
143
- gr.File(label="Download MusicXML"),
144
- gr.File(label="Download MXL"),
145
- gr.Textbox(label="ABC notation", show_copy_button=True),
146
- gr.Image(label="Staff", type="filepath", show_share_button=False),
147
- ],
148
- description="Please make sure the audio is completely uploaded before clicking Submit",
149
- flagging_mode="never",
150
- )
151
-
152
- # with gr.Tab("Direct Link Mode"):
153
- # gr.Interface(
154
- # fn=url_infer,
155
- # inputs=gr.Textbox(
156
- # label="Input audio direct link",
157
- # placeholder="https://music.163.com/#/song?id=",
158
- # ),
159
- # outputs=[
160
- # gr.Audio(label="Download audio", type="filepath"),
161
- # gr.File(label="Download MIDI"),
162
- # gr.File(label="Download PDF score"),
163
- # gr.File(label="Download MusicXML"),
164
- # gr.File(label="Download MXL"),
165
- # gr.Textbox(label="ABC notation", show_copy_button=True),
166
- # gr.Image(label="Staff", type="filepath"),
167
- # ],
168
- # description="For Netease Cloud music, you can directly input the non-VIP song page link",
169
- # examples=["1945798894", "1945798973", "1946098771"],
170
- # flagging_mode="never",
171
- # cache_examples=False,
172
- # )
 
 
 
 
 
 
 
173
 
174
  iface.launch()
 
1
  import os
2
  import re
3
  import json
 
4
  import torch
5
  import shutil
6
  import requests
7
+ import modelscope
8
+ import huggingface_hub
9
  import gradio as gr
10
  from piano_transcription_inference import PianoTranscription, load_audio, sample_rate
 
11
  from urllib.parse import urlparse
12
  from convert import midi2xml, xml2abc, xml2mxl, xml2jpg
13
 
14
+ EN_US = os.getenv("LANG") != "zh_CN.UTF-8"
15
+
16
+ ZH2EN = {
17
+ "上传模式": "Uploading Mode",
18
+ "上传音频": "Upload an audio",
19
+ "下载 MIDI": "Download MIDI",
20
+ "下载 PDF 乐谱": "Download PDF score",
21
+ "下载 MusicXML": "Download MusicXML",
22
+ "下载 MXL": "Download MXL",
23
+ "ABC 记谱": "ABC notation",
24
+ "五线谱": "Staff",
25
+ "状态栏": "Status",
26
+ "请上传音频 100% 后再点提交": "Please make sure the audio is completely uploaded before clicking Submit",
27
+ "直链模式": "Direct Link Mode",
28
+ "输入音频 URL 直链": "Input audio direct link",
29
+ "下载音频": "Download audio",
30
+ "网易云音乐可直接输入非 VIP 歌曲页面链接自动解析": "For Netease Cloud music, you can directly input the non-VIP song page link",
31
+ "# 钢琴转谱工具": "Piano Transcription Tool",
32
+ }
33
+
34
  WEIGHTS_PATH = (
35
+ huggingface_hub.snapshot_download(
36
+ "Genius-Society/piano_trans",
37
+ cache_dir="./__pycache__",
38
+ )
39
+ if EN_US
40
+ else modelscope.snapshot_download(
41
+ "Genius-Society/piano_trans",
42
+ cache_dir="./__pycache__",
43
+ )
44
+ ) + "/CRNN_note_F1=0.9677_pedal_F1=0.9186.pth"
45
+
46
+
47
+ def _L(zh_txt: str):
48
+ return ZH2EN[zh_txt] if EN_US else zh_txt
49
 
50
 
51
  def clean_cache(cache_dir):
 
65
 
66
  def is_url(s: str):
67
  try:
68
+ # 解析字符串
69
  result = urlparse(s)
70
+ # 检查scheme(如http, https)和netloc(域名)
71
  return all([result.scheme, result.netloc])
72
 
73
  except:
74
+ # 如果解析过程中发生异常,则返回False
75
  return False
76
 
77
 
78
  def audio2midi(audio_path: str, cache_dir: str):
79
+ audio, _ = load_audio(audio_path, sr=sample_rate, mono=True)
80
  transcriptor = PianoTranscription(
81
  device="cuda" if torch.cuda.is_available() else "cpu",
82
  checkpoint_path=WEIGHTS_PATH,
 
86
  return midi_path, os.path.basename(audio_path).split(".")[-2].capitalize()
87
 
88
 
89
+ def get_first_integer(input_string: str):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  match = re.search(r"\d+", input_string)
91
  if match:
92
  return str(int(match.group()))
93
+
94
  else:
95
  return ""
96
 
 
99
  detail_api = "https://music.163.com/api/v3/song/detail"
100
  parm_dict = {"id": id, "c": str([{"id": id}]), "csrf_token": ""}
101
  free = False
102
+ song_name = "获取歌曲失败"
103
  response = requests.get(detail_api, params=parm_dict)
104
+ # 检查请求是否成功
105
  if response.status_code == 200:
106
+ # 处理成功响应
107
  data = json.loads(response.text)
108
  if data and "songs" in data and data["songs"]:
109
  fee = int(data["songs"][0]["fee"])
 
111
  song_name = str(data["songs"][0]["name"])
112
 
113
  else:
114
+ song_name = "歌曲不存在"
115
 
116
  else:
117
+ raise ConnectionError(f"错误: {response.status_code}, {response.text}")
118
 
119
  return song_name, free
120
 
121
 
122
+ def upl_infer(audio_path: str, cache_dir="./__pycache__/mode1"):
123
+ status = "Success"
124
+ midi = pdf = xml = mxl = abc = jpg = None
125
+ try:
126
+ clean_cache(cache_dir)
127
+ midi, title = audio2midi(audio_path, cache_dir)
128
+ xml = midi2xml(midi, title)
129
+ abc = xml2abc(xml)
130
+ mxl = xml2mxl(xml)
131
+ pdf, jpg = xml2jpg(xml)
132
+
133
+ except Exception as e:
134
+ status = f"{e}"
135
+
136
+ return status, midi, pdf, xml, mxl, abc, jpg
137
+
138
+
139
  def url_infer(song: str, cache_dir="./__pycache__/mode2"):
140
  song_name = ""
141
+ status = "Success"
142
+ download_path = f"{cache_dir}/output.mp3"
143
+ midi = pdf = xml = mxl = abc = jpg = None
144
  try:
145
+ clean_cache(cache_dir)
146
  if (is_url(song) and "163" in song and "?id=" in song) or song.isdigit():
147
+ song_id = get_first_integer(song.split("?id=")[-1])
148
+ song = f"https://music.163.com/song/media/outer/url?id={song_id}.mp3"
149
  song_name, free = music163_song_info(song_id)
150
  if not free:
151
+ raise AttributeError("付费歌曲无法解析")
152
 
153
+ download_audio(song, download_path)
 
154
 
155
+ midi, title = audio2midi(download_path, cache_dir)
156
  if song_name:
157
  title = song_name
158
 
 
160
  abc = xml2abc(xml)
161
  mxl = xml2mxl(xml)
162
  pdf, jpg = xml2jpg(xml)
163
+ if not os.path.exists(download_path):
164
+ raise FileExistsError(f"{download_path} not exist")
165
 
166
  except Exception as e:
167
+ status = f"{e}"
168
+
169
+ return status, download_path, midi, pdf, xml, mxl, abc, jpg
170
 
171
 
172
  if __name__ == "__main__":
173
  with gr.Blocks() as iface:
174
+ gr.Markdown(_L("# 钢琴转谱工具"))
175
+ with gr.Tab(_L("上传模式")):
176
+ gr.Interface(
177
+ fn=upl_infer,
178
+ inputs=gr.Audio(label=_L("上传音频"), type="filepath"),
179
+ outputs=[
180
+ gr.Textbox(label=_L("状态栏"), show_copy_button=True),
181
+ gr.File(label=_L("下载 MIDI")),
182
+ gr.File(label=_L("下载 PDF 乐谱")),
183
+ gr.File(label=_L("下载 MusicXML")),
184
+ gr.File(label=_L("下载 MXL")),
185
+ gr.Textbox(label=_L("ABC 记谱"), show_copy_button=True),
186
+ gr.Image(
187
+ label=_L("五线谱"),
188
+ type="filepath",
189
+ show_share_button=False,
190
+ ),
191
+ ],
192
+ title=_L("请上传音频 100% 后再点提交"),
193
+ flagging_mode="never",
194
+ )
195
+
196
+ with gr.Tab(_L("直链模式")):
197
+ gr.Interface(
198
+ fn=url_infer,
199
+ inputs=gr.Textbox(
200
+ label=_L("输入音频 URL 直链"),
201
+ placeholder="https://music.163.com/#/song?id=",
202
+ ),
203
+ outputs=[
204
+ gr.Textbox(label=_L("状态栏"), show_copy_button=True),
205
+ gr.Audio(label=_L("下载音频"), type="filepath"),
206
+ gr.File(label=_L("下载 MIDI")),
207
+ gr.File(label=_L("下载 PDF 乐谱")),
208
+ gr.File(label=_L("下载 MusicXML")),
209
+ gr.File(label=_L("���载 MXL")),
210
+ gr.Textbox(label=_L("ABC 记谱"), show_copy_button=True),
211
+ gr.Image(
212
+ label=_L("五线谱"),
213
+ type="filepath",
214
+ show_share_button=False,
215
+ ),
216
+ ],
217
+ title=_L("网易云音乐可直接输入非 VIP 歌曲页面链接自动解析"),
218
+ examples=["1945798894", "1945798973", "1946098771"],
219
+ flagging_mode="never",
220
+ cache_examples=False,
221
+ )
222
 
223
  iface.launch()
requirements.txt CHANGED
@@ -1,4 +1,5 @@
1
- torch
 
2
  pymupdf
3
  music21
4
  piano_transcription_inference
 
1
+ torch==2.6.0+cu118
2
+ -f https://download.pytorch.org/whl/torch
3
  pymupdf
4
  music21
5
  piano_transcription_inference