Kowsya/Polyphonic_Music_Generation_using_MuseGAN
0
1# MuseGAN - Hugging Face Gradio App2# Kowsya Mutkundu | Roll 25P06200073# Upload: app.py + requirements.txt + generator.keras4 5import os, io, time, tempfile, warnings6warnings.filterwarnings('ignore')7import numpy as np8import matplotlib; matplotlib.use('Agg')9import matplotlib.pyplot as plt10from scipy.io import wavfile11import pretty_midi12import gradio as gr13import tensorflow as tf14 15PITCH_LOW, PITCH_HIGH = 36, 8416PITCH_RANGE = PITCH_HIGH - PITCH_LOW17BEAT_RES, N_BARS = 4, 218N_STEPS = N_BARS * 4 * BEAT_RES19N_TRACKS = 220LATENT_DIM = 12821SR = 2205022 23print("Loading MuseGAN Generator...")24try:25 generator = tf.keras.models.load_model("generator.keras")26 _ = generator(tf.random.normal([1, LATENT_DIM]), training=False)27 OK = True28 print("Model loaded OK")29except Exception as e:30 print(f"Load failed: {e}")31 generator, OK = None, False32 33def to_midi(roll, bpm=120, thr=0.35):34 pm = pretty_midi.PrettyMIDI(initial_tempo=float(bpm))35 sps = (60.0 / bpm) / BEAT_RES36 insts = [pretty_midi.Instrument(program=0, name="Classical"),37 pretty_midi.Instrument(program=4, name="Pop")]38 vels = [80, 88]39 for ti in range(N_TRACKS):40 for pi in range(PITCH_RANGE):41 mp, on = pi + PITCH_LOW, None42 for st in range(N_STEPS + 1):43 act = (st < N_STEPS) and (roll[ti, st, pi] > thr)44 if act and on is None: on = st45 elif not act and on is not None:46 s, e = on * sps, st * sps47 if e > s:48 insts[ti].notes.append(pretty_midi.Note(vels[ti], mp, s, e))49 on = None50 pm.instruments.append(insts[ti])51 return pm52 53def synth(pm):54 dur = pm.get_end_time() + 0.555 audio = np.zeros(int(dur * SR), dtype=np.float64)56 for inst in pm.instruments:57 for note in inst.notes:58 freq = 440.0 * (2.0 ** ((note.pitch - 69) / 12.0))59 n_s = int((note.end - note.start) * SR)60 if n_s <= 0: continue61 t = np.linspace(0, note.end - note.start, n_s, endpoint=False)62 wave = (0.60*np.sin(2*np.pi*freq*t) + 0.25*np.sin(4*np.pi*freq*t) +63 0.10*np.sin(6*np.pi*freq*t) + 0.05*np.sin(8*np.pi*freq*t))64 env = np.ones(n_s) * 0.765 atk = min(int(0.012*SR), n_s)66 dec = min(int(0.060*SR), max(0, n_s-atk))67 rel = min(int(0.060*SR), n_s)68 if atk > 0: env[:atk] = np.linspace(0, 1, atk)69 if dec > 0: env[atk:atk+dec] = np.linspace(1, 0.7, dec)70 env[-rel:] = np.linspace(env[-rel], 0, rel)71 wave *= env * (note.velocity/127.0) * 0.2872 s0 = int(note.start * SR)73 e0 = min(s0 + n_s, len(audio))74 audio[s0:e0] += wave[:e0-s0]75 pk = np.abs(audio).max()76 if pk > 0: audio /= pk77 return (audio * 32767).astype(np.int16)78 79def roll_fig(roll, bpm, temp, thr):80 fig, axes = plt.subplots(2, 1, figsize=(12, 5.5), sharex=True, facecolor='#0F1117')81 fig.patch.set_facecolor('#0F1117')82 for i, (cmap, color, label) in enumerate([83 ('Blues', '#3A7FC1', f'Classical Melody (Grand Piano) BPM={bpm}'),84 ('Oranges', '#E07B39', f'Pop Melody (Electric Piano) Temp={temp}')85 ]):86 axes[i].imshow(roll[i].T, aspect='auto', origin='lower', cmap=cmap,87 vmin=0, vmax=1, interpolation='nearest')88 axes[i].set_facecolor('#1A1A2E')89 axes[i].set_title(label, color=color, fontsize=11, pad=6)90 axes[i].set_ylabel('Pitch (C2-B5)', color='#AAA', fontsize=9)91 axes[i].tick_params(colors='#888', labelsize=8)92 for sp in axes[i].spines.values(): sp.set_edgecolor('#333355')93 n = int(np.sum(roll[i] > thr))94 d = float(np.mean(roll[i] > thr) * 100)95 axes[i].text(0.99, 0.05, f'{n} notes | {d:.1f}% density',96 transform=axes[i].transAxes, ha='right', va='bottom',97 color='white', fontsize=8.5, alpha=0.85,98 bbox=dict(facecolor='#00000066', edgecolor='none', pad=2))99 axes[1].set_xlabel('Time step (16th notes) - 2 bars = 32 steps', color='#AAA', fontsize=9)100 fig.suptitle('MuseGAN - Generated Hybrid Music Piano Roll',101 color='white', fontsize=13, fontweight='bold', y=1.01)102 plt.tight_layout(pad=1.5)103 return fig104 105def generate(bpm, temperature, threshold, repeats, seed):106 if not OK:107 return None, None, "Model not loaded. Upload generator.keras to the Space."108 if seed > 0:109 tf.random.set_seed(int(seed)); np.random.seed(int(seed))110 t0 = time.time()111 z = tf.random.normal([1, LATENT_DIM]) * float(temperature)112 out = generator(z, training=False).numpy()[0]113 pm = to_midi(out, bpm=int(bpm), thr=float(threshold))114 if int(repeats) > 1:115 dur = pm.get_end_time()116 pm2 = pretty_midi.PrettyMIDI(initial_tempo=float(bpm))117 for oi in pm.instruments:118 ni = pretty_midi.Instrument(program=oi.program, name=oi.name)119 for r in range(int(repeats)):120 for n in oi.notes:121 ni.notes.append(pretty_midi.Note(n.velocity, n.pitch,122 n.start+r*dur, n.end+r*dur))123 pm2.instruments.append(ni)124 pm = pm2125 audio = synth(pm)126 wf = tempfile.NamedTemporaryFile(suffix='.wav', delete=False)127 wavfile.write(wf.name, SR, audio)128 fig = roll_fig(out, bpm, temperature, threshold)129 imgf = tempfile.NamedTemporaryFile(suffix='.png', delete=False)130 fig.savefig(imgf.name, dpi=130, bbox_inches='tight', facecolor='#0F1117')131 plt.close(fig)132 nc = int(np.sum(out[0] > threshold))133 np_ = int(np.sum(out[1] > threshold))134 dur_s = pm.get_end_time()135 s = (f"DONE ({time.time()-t0:.2f}s)\n{'─'*36}\n"136 f"Tempo: {bpm} BPM | Temp: {temperature} | Thr: {threshold}\n"137 f"Repeats: {repeats}x | Seed: {seed if seed>0 else 'random'}\n"138 f"{'─'*36}\n"139 f"Classical notes : {nc}\nPop notes : {np_}\n"140 f"Total notes : {nc+np_}\n"141 f"Density : {(nc+np_)/(2*N_STEPS*PITCH_RANGE)*100:.2f}%\n"142 f"Duration : {dur_s:.1f} sec\n{'─'*36}\n"143 f"Right-click audio -> Save as WAV\n"144 f"Open .mid in GarageBand/FL Studio\nfor richer instrument sounds")145 return imgf.name, wf.name, s146 147CSS = """148.hdr{background:linear-gradient(135deg,#1B3A6B,#2E6DB4,#1B3A6B);149 padding:26px 30px;border-radius:14px;margin-bottom:18px;text-align:center;150 box-shadow:0 4px 20px rgba(46,109,180,.4)}151.hdr h1{color:#fff;font-size:2rem;margin:0 0 6px}152.hdr p{color:#B8D4F0;margin:3px 0;font-size:.93rem}153.ib{background:#F0F6FF;border-left:4px solid #2E6DB4;padding:13px 17px;154 border-radius:8px;margin:8px 0;font-size:.89rem;line-height:1.6}155footer{display:none!important}156"""157 158with gr.Blocks(159 title="MuseGAN Hybrid Music Generator",160 theme=gr.themes.Soft(primary_hue="blue", secondary_hue="orange",161 font=[gr.themes.GoogleFont("Inter"), "sans-serif"]),162 css=CSS163) as demo:164 165 gr.HTML("""166 <div class="hdr">167 <h1>🎵 MuseGAN — Hybrid Music Generator</h1>168 <p>Generates original <strong>polyphonic piano music</strong> fusing169 <span style="color:#7FC8F8"><strong>Classical harmony</strong></span> (JSB Bach Chorales)170 with <span style="color:#F8A96B"><strong>Pop rhythm</strong></span> (POP909)171 using a Generative Adversarial Network.</p>172 <p style="color:#8BAED0;font-size:.83rem">173 Kowsya Mutkundu · Roll 25P0620007 · Deep Learning & Generative AI Lab · April 2026</p>174 </div>""")175 176 with gr.Accordion("📚 How it works (click to read)", open=False):177 gr.HTML("""<div class="ib">178 <strong>MuseGAN</strong> is a GAN trained on two datasets:<br>179 • <strong>JSB Chorales</strong> — 382 Bach harmonies (classical, voice-led)<br>180 • <strong>POP909</strong> — 909 modern pop piano songs (rhythmic, chord-based)<br><br>181 <strong>How it generates music:</strong><br>182 1. Sample 128 random numbers (noise), scale by Temperature<br>183 2. Generator transforms noise → piano roll (2 tracks × 32 steps × 48 pitches)<br>184 3. Values above Threshold become note ON events<br>185 4. Synthesise with sine waves → WAV audio playable in browser<br><br>186 <strong>Controls:</strong> Tempo=speed | Temperature=creativity |187 Threshold=note density | Repeats=longer audio | Seed=reproducibility</div>""")188 189 gr.Markdown("---")190 191 with gr.Row(equal_height=False):192 with gr.Column(scale=1, min_width=280):193 gr.Markdown("### 🎭 Controls")194 bpm_s = gr.Slider(60, 200, 120, step=5, label="Tempo (BPM)")195 tmp_s = gr.Slider(0.1, 2.5, 1.0, step=0.1, label="Creativity (Temperature)")196 thr_s = gr.Slider(0.1, 0.7, 0.35, step=0.05, label="Note Threshold")197 rep_s = gr.Slider(1, 8, 2, step=1, label="Phrase Repeats")198 sed_n = gr.Number(0, label="Seed (0=random)", precision=0)199 btn = gr.Button("🎵 Generate Music!", variant="primary", size="lg")200 gr.Markdown("---\n### 🎲 Quick Presets")201 gr.Examples(202 examples=[[90,0.8,0.35,2,42],[120,1.0,0.35,4,0],203 [150,1.5,0.30,2,7],[75,0.5,0.40,4,100],204 [130,2.0,0.25,2,0],[110,1.2,0.35,3,555]],205 inputs=[bpm_s, tmp_s, thr_s, rep_s, sed_n], label=None)206 207 with gr.Column(scale=2):208 gr.Markdown("### 🎤 Output")209 img_o = gr.Image(label="Piano Roll (Blue=Classical | Orange=Pop)",210 type="filepath")211 wav_o = gr.Audio(label="🔊 Audio WAV (play in browser or right-click to save)",212 type="filepath")213 txt_o = gr.Textbox(label="Statistics", lines=13, max_lines=15)214 215 btn.click(fn=generate, inputs=[bpm_s,tmp_s,thr_s,rep_s,sed_n],216 outputs=[img_o, wav_o, txt_o])217 218 gr.HTML("""219 <div style="margin-top:18px;padding:13px 17px;background:#F0F6FF;border-radius:10px;220 font-size:.86rem;color:#444;text-align:center">221 <strong>About</strong> — JSB Chorales (classical harmony) + POP909 (pop melody).222 WAV plays in browser. For richer sound, open .mid in GarageBand or FL Studio. | 223 <strong>Kowsya Mutkundu · 25P0620007 · 2026</strong>224 </div>""")225 226if __name__ == "__main__":227 demo.launch()228 