File size: 5,952 Bytes
5f6e625
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import os
import sys
import json
import pickle
import random
import tempfile
from pathlib import Path

import torch
import gradio as gr
from pyharp import ModelCard, build_endpoint, load_midi, save_midi

sys.path.insert(0, str(Path(__file__).parent / "improvnet"))
sys.path.insert(0, str(Path(__file__).parent))

os.environ["TOKENIZERS_PARALLELISM"] = "true"

ARTIFACT_FOLDER = Path(__file__).parent / "artifacts"
model = None
tokenizer = None
decode_tokenizer = None
configs = None

def download_artifacts():
    if not (ARTIFACT_FOLDER / "style_transfer" / "fine_tuned_model").exists():
        print("Downloading artifacts from Google Drive...", flush=True)
        import gdown
        import zipfile
        zip_path = Path(__file__).parent / "artifacts.zip"
        gdown.download(
            "https://drive.google.com/uc?id=11H3y2sFUFldf6nS5pSpk8B-bIDHtFH4K",
            str(zip_path),
            quiet=False
        )
        print("Extracting artifacts...", flush=True)
        with zipfile.ZipFile(zip_path, "r") as z:
            z.extractall(str(Path(__file__).parent))
        zip_path.unlink()
        print("Artifacts ready.", flush=True)

download_artifacts()

def get_model():
    global model, tokenizer, decode_tokenizer, configs
    if model is None:
        import yaml
        from transformers import EncoderDecoderModel

        config_path = Path(__file__).parent / "configs" / "config_style_transfer.yaml"
        with open(config_path, "r") as f:
            configs = yaml.safe_load(f)

        tokenizer_path = ARTIFACT_FOLDER / "style_transfer" / "vocab_corrupted.pkl"
        with open(tokenizer_path, "rb") as f:
            tokenizer = pickle.load(f)
        decode_tokenizer = {v: k for k, v in tokenizer.items()}

        print("Loading ImprovNet model...", flush=True)
        model = EncoderDecoderModel.from_pretrained(
            str(ARTIFACT_FOLDER / "style_transfer" / "fine_tuned_model")
        )
        model.eval()
        device = "cuda" if torch.cuda.is_available() else "cpu"
        model.to(device)
        print("Model loaded.", flush=True)

    return model, tokenizer, decode_tokenizer, configs


model_card = ModelCard(
    name="ImprovNet",
    description="Generate expressive musical improvisations from piano MIDI. Supports Classical→Jazz and Classical→Classical style transfer.",
    author="Keshav Bhandari, Sungkyun Chang, Tongyu Lu, Fareza R. Enus, Louis B. Bradshaw, Dorien Herremans, Simon Colton",
    tags=["midi", "improvisation", "style-transfer", "jazz", "classical", "piano"],
)


def process_fn(input_midi_path: str, convert_to: str, num_passes: int, corruption_rate: float, t_segment_start: int) -> str:
    print(f"Processing: convert_to={convert_to}, passes={num_passes}, rate={corruption_rate}", flush=True)

    fusion_model, tok, decode_tok, cfg = get_model()

    from generation import generate

    passes = {}
    corruption_types = ["skyline", "skyline", "pitch_velocity_mask", "incorrect_transposition",
                        "permute_pitches", "note_modification", "onset_duration_mask", "fragmentation",
                        "whole_mask", "random"]
    for i in range(num_passes):
        passes[f"pass_{i+1}"] = {
            "corruption_rate": corruption_rate,
            "corruption_type": corruption_types[i % len(corruption_types)]
        }

    cfg["generation"]["passes"] = passes
    cfg["generation"]["convert_to"] = convert_to
    cfg["generation"]["t_segment_start"] = t_segment_start
    cfg["generation"]["novel_peaks_pct"] = 0.0
    cfg["generation"]["write_intermediate_passes"] = False
    cfg["generation"]["context_before"] = 5
    cfg["generation"]["context_after"] = 5
    cfg["generation"]["temperature"] = 1.0
    cfg["generation"]["end_original"] = True
    cfg["generation"]["t_segment_stop"] = -1

    output_dir = Path(tempfile.mkdtemp())

    generate(
        midi_file_path=input_midi_path,
        audio_file_path=None,
        fusion_model=fusion_model,
        configs=cfg,
        novel_peaks_pct=0.0,
        t_segment_start=t_segment_start,
        convert_to=convert_to,
        context_before=5,
        context_after=5,
        corruption_passes=passes,
        tokenizer=tok,
        decode_tokenizer=decode_tok,
        output_folder=str(output_dir),
        save_original=False,
        quiet=False,
        write_intermediate_passes=False,
        temperature=1.0,
        end_original=True,
        t_segment_stop=-1,
    )

    # Find output MIDI
    output_files = list(output_dir.glob("*.mid")) + list(output_dir.glob("*.midi"))
    if not output_files:
        raise ValueError("No output MIDI generated.")

    output_path = tempfile.mktemp(suffix=".mid")
    import shutil
    shutil.copy(output_files[0], output_path)
    print("Done.", flush=True)
    return output_path


with gr.Blocks() as demo:
    input_components = [
        gr.File(
            type="filepath",
            label="Input Piano MIDI",
            file_types=[".mid", ".midi"],
        ).harp_required(True),
        gr.Dropdown(
            choices=["jazz", "classical"],
            value="jazz",
            label="Convert To",
        ),
        gr.Slider(minimum=1, maximum=10, step=1, value=5, label="Number of Passes"),
        gr.Slider(minimum=0.1, maximum=1.0, step=0.1, value=0.5, label="Corruption Rate"),
        gr.Slider(minimum=0, maximum=10, step=1, value=2, label="Start Segment (5s each)"),
    ]
    output_components = [
        gr.File(
            type="filepath",
            label="Output MIDI",
            file_types=[".mid", ".midi"],
        ).set_info("Improvised MIDI output."),
    ]
    app = build_endpoint(
        model_card=model_card,
        input_components=input_components,
        output_components=output_components,
        process_fn=process_fn,
    )

print("Launching Gradio...", flush=True)
demo.queue().launch(server_name="0.0.0.0", server_port=7860, show_error=True, pwa=True)