#!/usr/bin/env python3 """Generate mpk-mini-sim.pd — MPK Mini Mk II simulator. Architecture: each key/pad has bng→t b b→msg for note-on, del→msg for note-off. All msgs connect to a single [netsend -u -b] via send/receive. Knobs: hsl→msg→[s $0-cc], central [r $0-cc]→netsend. """ from pathlib import Path SCRIPT_DIR = Path(__file__).resolve().parent OUTPUT = SCRIPT_DIR.parent / "pd" / "mpk-mini-sim.pd" def connect(c, src, sout, dst, din): c.append((src, sout, dst, din)) def Obj(o, x, y, *args): idx = len(o) o.append((x, y, *args)) return idx def gen(): o, c = [], [] # ── UDP sender (hardcoded IP — edit the msg box to change) ── Obj(o, 10, 10, "loadbang") Obj(o, 10, 30, "msg", ";") msg_conn = Obj(o, 10, 50, "msg", "connect 192.168.9.167 8081;") ns = Obj(o, 10, 80, "netsend", "-u", "-b") connect(c, msg_conn, 0, ns, 0) Obj(o, 200, 30, "text", "← edit this line to change target IP:port") instr = Obj(o, 10, 440, "text", "Click keys to play. Knobs send CC on change. IPS (192.168.9.167:8081)") instr2 = Obj(o, 10, 460, "text", "MIDI ch: 144=note-on ch1, 128=note-off ch1, 176=CC ch1") # ── Keyboard: C3(48) to C5(72) ── keys = [ ("C3",48),("C#3",49),("D3",50),("D#3",51),("E3",52), ("F3",53),("F#3",54),("G3",55),("G#3",56),("A3",57), ("A#3",58),("B3",59), ("C4",60),("C#4",61),("D4",62),("D#4",63),("E4",64), ("F4",65),("F#4",66),("G4",67),("G#4",68),("A4",69), ("A#4",70),("B4",71),("C5",72), ] Obj(o, 20, 120, "text", "-- KEYBOARD --") for i, (name, note) in enumerate(keys): x = 30 + (i % 13) * 46 y = 140 + (i // 13) * 60 Obj(o, x, y, "text", name) b = Obj(o, x, y+14, "bng", 15, 25, 0, 1, 0, 0, "empty", f"k{note}", -2,-8,0,10,-262144,-1,-1) trig = Obj(o, x, y+44, "t", "b b") msg_on = Obj(o, x, y+64, "msg", f"midi 144 {note} 100;") msg_off = Obj(o, x, y+82, "msg", f"midi 128 {note} 0;") del300 = Obj(o, x, y+100, "del", 300) connect(c, b, 0, trig, 0) connect(c, trig, 0, msg_on, 0) connect(c, msg_on, 0, ns, 0) connect(c, trig, 1, del300, 0) connect(c, del300, 0, msg_off, 0) connect(c, msg_off, 0, ns, 0) # ── Pads (C2-G2, notes 36-43) ── pads = [("C2",36),("C#2",37),("D2",38),("D#2",39),("E2",40),("F2",41),("F#2",42),("G2",43)] Obj(o, 680, 120, "text", "-- PADS --") for i, (name, note) in enumerate(pads): x = 680 + (i % 4) * 55 y = 140 + (i // 4) * 55 Obj(o, x, y, "text", name) b = Obj(o, x, y+14, "bng", 15, 15, 0, 1, 0, 0, "empty", f"p{note}", -2,-8,0,10,-262144,-1,-1) trig = Obj(o, x, y+32, "t", "b b") msg_on = Obj(o, x, y+52, "msg", f"midi 144 {note} 100;") msg_off = Obj(o, x, y+70, "msg", f"midi 128 {note} 0;") del200 = Obj(o, x, y+88, "del", 200) connect(c, b, 0, trig, 0) connect(c, trig, 0, msg_on, 0) connect(c, msg_on, 0, ns, 0) connect(c, trig, 1, del200, 0) connect(c, del200, 0, msg_off, 0) connect(c, msg_off, 0, ns, 0) # ── Knobs (CC 1, 71-77) ── knobs = [(1,"Mod",64),(71,"Reson",64),(72,"Release",64),(73,"Attack",64), (74,"Cutoff",100),(75,"Decay",64),(76,"LFO Rate",64),(77,"LFO Depth",64)] Obj(o, 20, 280, "text", "-- KNOBS --") for i, (cc, name, init) in enumerate(knobs): x = 30 + i * 85 y = 300 Obj(o, x, y, "text", f"CC{cc} {name}") hsl = Obj(o, x, y+15, "hsl", 50, 10, 0, 127, init, 0, 0, "empty", f"cc{cc}", -2,-8,0,10,-262144,-1,-1,7200,1) msg = Obj(o, x, y+30, "msg", f"midi 176 {cc} $1;") connect(c, hsl, 0, msg, 0) connect(c, msg, 0, ns, 0) # ── Write (Pd format: #X obj for objects, #X msg/text/floatatom/symbol for others) ── lines = [ "#N canvas 0 0 950 490 10;", "# DO NOT EDIT — generated by scripts/gen_mpk_sim.py;", ] obj_types = {"bng","t","del","netsend","loadbang","hsl","comment"} for x, y, *args in o: etype = args[0] rest = args[1:] if etype in obj_types: # Pd stores GUI objects as: #X obj x y name = etype if name == "comment": name = "text" # Pd uses text for comments joined = " ".join(str(a) for a in rest) lines.append(f"#X obj {x} {y} {name} {joined};") else: joined = " ".join(str(a) for a in rest) lines.append(f"#X {etype} {x} {y} {joined};") for src, sout, dst, din in c: lines.append(f"#X connect {src} {sout} {dst} {din};") lines.append("") return "\n".join(lines) if __name__ == "__main__": content = gen() OUTPUT.parent.mkdir(parents=True, exist_ok=True) OUTPUT.write_text(content) print(f"Wrote {OUTPUT}") print(f" {len(content.splitlines())} lines, {content.count('#X obj')} objects, {content.count('#X connect')} connections")