Spaces:
Running
Running
File size: 8,249 Bytes
7b1592f 8b93af5 e9222b3 8b13692 e9222b3 c73c29a e9222b3 da9545e 8b13692 c73c29a da9545e c73c29a 8b13692 c73c29a 8b13692 6f1f7b9 486d10a 8b13692 7d11fa9 8b13692 8b93af5 8b13692 2d39109 436c835 2d39109 668edc0 2d39109 e9222b3 8b13692 c73c29a 8b13692 c73c29a 8b13692 c73c29a 8b13692 486d10a c73c29a 8b13692 486d10a 8b13692 e9222b3 2d39109 8b13692 e9222b3 8b13692 c73c29a 8b13692 c73c29a 8b13692 c73c29a 8b13692 7d11fa9 301e30f c73c29a e9222b3 8b13692 c73c29a e9222b3 8b13692 e9222b3 c73c29a 486d10a 8b13692 c73c29a aa904d9 8b13692 c73c29a aa904d9 bb1c682 2d39109 c73c29a 78aa5a4 c73c29a 78aa5a4 c73c29a 78aa5a4 2d39109 c73c29a 8b13692 7b1592f aa904d9 8b13692 c73c29a aa904d9 e9222b3 8b13692 c73c29a 489ecd1 c73c29a 489ecd1 8b13692 |
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 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
import os
import re
import json
import torch
import shutil
import requests
import modelscope
import huggingface_hub
import gradio as gr
from tqdm import tqdm
from piano_transcription_inference import PianoTranscription, load_audio, sample_rate
from urllib.parse import urlparse
from convert import midi2xml, xml2abc, xml2mxl, xml2jpg
EN_US = os.getenv("LANG") != "zh_CN.UTF-8"
ZH2EN = {
"上传模式": "Uploading Mode",
"上传音频": "Upload an audio",
"下载 MIDI": "Download MIDI",
"下载 PDF 乐谱": "Download PDF score",
"下载 MusicXML": "Download MusicXML",
"下载 MXL": "Download MXL",
"ABC 记谱": "ABC notation",
"五线谱": "Staff",
"状态栏": "Status",
"请上传音频 100% 后再点提交": "Please make sure the audio is completely uploaded before clicking Submit",
"直链模式": "Direct Link Mode",
"输入音频 URL 直链": "Input audio direct link",
"下载音频": "Download audio",
"网易云音乐可直接输入非 VIP 歌曲页面链接自动解析": "For Netease Cloud music, you can directly input the non-VIP song page link",
"# 钢琴转谱工具": "# Piano Transcription Tool",
}
WEIGHTS_PATH = (
huggingface_hub.snapshot_download(
"Genius-Society/piano_trans",
cache_dir="./__pycache__",
)
if EN_US
else modelscope.snapshot_download(
"Genius-Society/piano_trans",
cache_dir="./__pycache__",
)
) + "/CRNN_note_F1=0.9677_pedal_F1=0.9186.pth"
def _L(zh_txt: str):
return ZH2EN[zh_txt] if EN_US else zh_txt
def clean_cache(cache_dir):
if os.path.exists(cache_dir):
shutil.rmtree(cache_dir)
os.mkdir(cache_dir)
def download_audio(url: str, save_path: str):
# 发起流式请求
response = requests.get(url, stream=True)
response.raise_for_status()
# 获取文件总大小(字节),如果服务器未返回,则 total=0
total = int(response.headers.get("content-length", 0))
# 打开文件并创建 tqdm 进度条
with open(save_path, "wb") as file, tqdm(
desc=save_path, # 进度条前缀文字
total=total, # 总大小
unit="B", # 单位为字节
unit_scale=True, # 根据文件大小自动转换单位
unit_divisor=1024, # 1024 字节 = 1 KB
) as pbar:
# 以 8 KB 为块循环写入
for chunk in response.iter_content(chunk_size=8192):
if chunk: # 忽略 keep-alive 产生的空块
file.write(chunk)
pbar.update(len(chunk)) # 更新进度条
def is_url(s: str):
try:
# 解析字符串
result = urlparse(s)
# 检查scheme(如http, https)和netloc(域名)
return all([result.scheme, result.netloc])
except:
# 如果解析过程中发生异常,则返回False
return False
def audio2midi(audio_path: str, cache_dir: str):
audio, _ = load_audio(audio_path, sr=sample_rate, mono=True)
transcriptor = PianoTranscription(
device="cuda" if torch.cuda.is_available() else "cpu",
checkpoint_path=WEIGHTS_PATH,
)
midi_path = f"{cache_dir}/output.mid"
transcriptor.transcribe(audio, midi_path)
return midi_path, os.path.basename(audio_path).split(".")[-2].capitalize()
def extract_fst_int(input_string: str):
match = re.search(r"\d+", input_string)
if match:
return str(int(match.group()))
else:
return ""
def music163_song_info(id: str):
detail_api = "https://music.163.com/api/v3/song/detail"
parm_dict = {"id": id, "c": str([{"id": id}]), "csrf_token": ""}
free = False
song_name = "获取歌曲失败"
response = requests.get(detail_api, params=parm_dict)
# 检查请求是否成功
if response.status_code == 200:
# 处理成功响应
data = json.loads(response.text)
if data and "songs" in data and data["songs"]:
fee = int(data["songs"][0]["fee"])
free = fee == 0 or fee == 8
song_name = str(data["songs"][0]["name"])
else:
song_name = "歌曲不存在"
else:
raise ConnectionError(f"错误: {response.status_code}, {response.text}")
return song_name, free
def upl_infer(audio_path: str, cache_dir="./__pycache__/mode1"):
status = "Success"
midi = pdf = xml = mxl = abc = jpg = None
try:
clean_cache(cache_dir)
midi, title = audio2midi(audio_path, cache_dir)
xml = midi2xml(midi, title)
abc = xml2abc(xml)
mxl = xml2mxl(xml)
pdf, jpg = xml2jpg(xml)
except Exception as e:
status = f"{e}"
return status, midi, pdf, xml, mxl, abc, jpg
def url_infer(song: str, cache_dir="./__pycache__/mode2"):
song_name = ""
status = "Success"
audio = midi = pdf = xml = mxl = abc = jpg = None
try:
clean_cache(cache_dir)
download_path = f"{cache_dir}/output.mp3"
if (is_url(song) and "163" in song and "?id=" in song) or song.isdigit():
song_id = extract_fst_int(song.split("?id=")[-1])
song = f"https://music.163.com/song/media/outer/url?id={song_id}.mp3"
song_name, free = music163_song_info(song_id)
if not free:
raise AttributeError("付费歌曲无法解析")
download_audio(song, download_path)
if not os.path.exists(download_path):
raise FileExistsError(f"{download_path} not exist")
midi, title = audio2midi(download_path, cache_dir)
if song_name:
title = song_name
audio = download_path
xml = midi2xml(midi, title)
abc = xml2abc(xml)
mxl = xml2mxl(xml)
pdf, jpg = xml2jpg(xml)
except Exception as e:
status = f"{e}"
return status, audio, midi, pdf, xml, mxl, abc, jpg
if __name__ == "__main__":
with gr.Blocks() as iface:
gr.Markdown(_L("# 钢琴转谱工具"))
with gr.Tab(_L("上传模式")):
gr.Interface(
fn=upl_infer,
inputs=gr.Audio(label=_L("上传音频"), type="filepath"),
outputs=[
gr.Textbox(label=_L("状态栏"), show_copy_button=True),
gr.File(label=_L("下载 MIDI")),
gr.File(label=_L("下载 PDF 乐谱")),
gr.File(label=_L("下载 MusicXML")),
gr.File(label=_L("下载 MXL")),
gr.Textbox(label=_L("ABC 记谱"), show_copy_button=True),
gr.Image(
label=_L("五线谱"),
type="filepath",
show_share_button=False,
),
],
title=_L("请上传音频 100% 后再点提交"),
flagging_mode="never",
)
if not EN_US:
with gr.Tab(_L("直链模式")):
gr.Interface(
fn=url_infer,
inputs=gr.Textbox(
label=_L("输入音频 URL 直链"),
placeholder="https://music.163.com/#/song?id=",
),
outputs=[
gr.Textbox(label=_L("状态栏"), show_copy_button=True),
gr.Audio(label=_L("下载音频"), type="filepath"),
gr.File(label=_L("下载 MIDI")),
gr.File(label=_L("下载 PDF 乐谱")),
gr.File(label=_L("下载 MusicXML")),
gr.File(label=_L("下载 MXL")),
gr.Textbox(label=_L("ABC 记谱"), show_copy_button=True),
gr.Image(
label=_L("五线谱"),
type="filepath",
show_share_button=False,
),
],
title=_L("网易云音乐可直接输入非 VIP 歌曲页面链接自动解析"),
examples=["1945798894", "1945798973", "1946098771"],
flagging_mode="never",
cache_examples=False,
)
iface.launch()
|