File size: 5,727 Bytes
7b1592f
8b93af5
e9222b3
9f2c3ca
8b13692
 
e9222b3
 
8b13692
 
 
 
 
 
78aa5a4
8b13692
 
 
6f1f7b9
486d10a
8b13692
 
7d11fa9
8b13692
8b93af5
 
8b13692
436c835
 
 
668edc0
436c835
e9222b3
 
8b13692
 
 
 
 
 
 
 
 
486d10a
9b709d7
8b13692
 
 
 
486d10a
8b13692
 
e9222b3
 
486d10a
 
e9222b3
9d68b29
486d10a
8b13692
 
 
 
 
e9222b3
8b13692
 
e9222b3
 
668edc0
8b13692
 
 
 
 
e9222b3
 
8b13692
 
 
 
c7d8a86
8b13692
 
 
 
 
 
 
7d11fa9
301e30f
c7d8a86
e9222b3
8b13692
c7d8a86
e9222b3
8b13692
e9222b3
 
486d10a
8b13692
486d10a
9f2c3ca
 
8b13692
bb1c682
668edc0
98c2473
78aa5a4
 
c7d8a86
78aa5a4
9f2c3ca
288d286
78aa5a4
98c2473
8b13692
 
7b1592f
8b13692
 
 
 
98c2473
8b13692
 
 
e9222b3
 
8b13692
 
e1d735f
c7d8a86
8b13692
 
 
c7d8a86
8b13692
 
 
c7d8a86
 
 
 
 
 
8b13692
d5dda18
3b26d67
8b13692
 
c7d8a86
8b13692
 
78aa5a4
c7d8a86
78aa5a4
 
8b13692
c7d8a86
 
 
 
 
 
 
8b13692
d5dda18
a9a4b3c
3b26d67
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
import os
import re
import json
import uuid
import torch
import shutil
import requests
import gradio as gr
from piano_transcription_inference import PianoTranscription, load_audio, sample_rate
from modelscope import snapshot_download
from urllib.parse import urlparse
from convert import midi2xml, xml2abc, xml2mxl, xml2jpg

WEIGHTS_PATH = (
    snapshot_download("Genius-Society/piano_trans", cache_dir="./__pycache__")
    + "/CRNN_note_F1=0.9677_pedal_F1=0.9186.pth"
)


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()
    with open(save_path, "wb") as file:
        for chunk in response.iter_content(chunk_size=8192):
            file.write(chunk)


def is_url(s: str):
    try:
        result = urlparse(s)
        return all([result.scheme, result.netloc])

    except:
        return False


def audio2midi(audio_path: str, cache_dir: str):
    audio, _ = load_audio(audio_path, sr=sample_rate)
    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 upl_infer(audio_path: str, cache_dir="./__pycache__/mode1"):
    clean_cache(cache_dir)
    try:
        print(audio_path)
        midi, title = audio2midi(audio_path, cache_dir)
        xml = midi2xml(midi, title)
        abc = xml2abc(xml)
        mxl = xml2mxl(xml)
        pdf, jpg = xml2jpg(xml)
        return midi, pdf, xml, mxl, abc, jpg

    except Exception as e:
        return None, None, None, None, f"{e}", None


def get_1st_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 = "Failed to get the song"
    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 = "The song does not exist"

    else:
        raise ConnectionError(f"Error: {response.status_code}, {response.text}")

    return song_name, free


def url_infer(song: str, cache_dir="./__pycache__/mode2"):
    song_name = ""
    clean_cache(cache_dir)
    audio_path = f"/tmp/gradio/{uuid.uuid4().hex}/"
    os.makedirs(audio_path, exist_ok=True)
    try:
        if (is_url(song) and "163" in song and "?id=" in song) or song.isdigit():
            song_id = get_1st_int(song.split("?id=")[-1])
            song_url = 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("Unable to parse VIP songs")

            audio_path += f"{song_id}.mp3"
            download_audio(song_url, audio_path)

        midi, title = audio2midi(audio_path, cache_dir)
        if song_name:
            title = song_name

        xml = midi2xml(midi, title)
        abc = xml2abc(xml)
        mxl = xml2mxl(xml)
        pdf, jpg = xml2jpg(xml)
        return audio_path, midi, pdf, xml, mxl, abc, jpg

    except Exception as e:
        return None, None, None, None, None, f"{e}", None


if __name__ == "__main__":
    with gr.Blocks() as iface:
        gr.Markdown("# Piano Transcription Tool")
        with gr.Tab("Uploading Mode"):
            gr.Interface(
                fn=upl_infer,
                inputs=gr.Audio(
                    label="Upload an audio",
                    type="filepath",
                ),
                outputs=[
                    gr.File(label="Download MIDI"),
                    gr.File(label="Download PDF score"),
                    gr.File(label="Download MusicXML"),
                    gr.File(label="Download MXL"),
                    gr.Textbox(label="ABC notation", show_copy_button=True),
                    gr.Image(label="Staff", type="filepath"),
                ],
                description="Please make sure the audio is completely uploaded before clicking Submit",
                flagging_mode="never",
            )

        with gr.Tab("Direct Link Mode"):
            gr.Interface(
                fn=url_infer,
                inputs=gr.Textbox(
                    label="Input audio direct link",
                    placeholder="https://music.163.com/#/song?id=",
                ),
                outputs=[
                    gr.Audio(label="Download audio", type="filepath"),
                    gr.File(label="Download MIDI"),
                    gr.File(label="Download PDF score"),
                    gr.File(label="Download MusicXML"),
                    gr.File(label="Download MXL"),
                    gr.Textbox(label="ABC notation", show_copy_button=True),
                    gr.Image(label="Staff", type="filepath"),
                ],
                description="For Netease Cloud music, you can directly input the non-VIP song page link",
                examples=["1945798894", "1945798973", "1946098771"],
                flagging_mode="never",
                cache_examples=False,
            )

    iface.launch()