CoolFace
Apppublic

Kowsya/Polyphonic_Music_Generation_using_MuseGAN

sourceHugging Faceupdated 5mo agoView on Hugging Face
0likes
app.py228 linesDownload Raw Back to root
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>&#127925; MuseGAN &mdash; 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 &middot; Roll 25P0620007 &middot; Deep Learning &amp; Generative AI Lab &middot; April 2026</p>174    </div>""")175 176    with gr.Accordion("&#128218; 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        &nbsp;&bull; <strong>JSB Chorales</strong> &mdash; 382 Bach harmonies (classical, voice-led)<br>180        &nbsp;&bull; <strong>POP909</strong> &mdash; 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 &rarr; piano roll (2 tracks &times; 32 steps &times; 48 pitches)<br>184        3. Values above Threshold become note ON events<br>185        4. Synthesise with sine waves &rarr; 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("### &#127917; 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("&#127925;  Generate Music!", variant="primary", size="lg")200            gr.Markdown("---\n### &#127922; 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("### &#127908; Output")209            img_o = gr.Image(label="Piano Roll (Blue=Classical | Orange=Pop)",210                             type="filepath")211            wav_o = gr.Audio(label="&#128266; 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> &mdash; JSB Chorales (classical harmony) + POP909 (pop melody).222      WAV plays in browser. For richer sound, open .mid in GarageBand or FL Studio. &nbsp;|&nbsp;223      <strong>Kowsya Mutkundu &middot; 25P0620007 &middot; 2026</strong>224    </div>""")225 226if __name__ == "__main__":227    demo.launch()228