C:\ADLIBITUM> SHOWCASE.EXE   v0.9.104 PANEL CLOSE

AdLibitum

Mental rock band for the Sound Blaster. You conduct. Chip answers at 388h.

SET BLASTER=A220 I7 D1 H5 P330 T6

01 // Quick jam hear notes in 60 seconds

C:\ADLIBITUM> AdLibitum.exe [engine] wait for green LED [ui] Enabled ON [fm] out = Emulate (Nuked-OPL3) [play] pick Role / Key / Style — hit keys or Free-run C:\ADLIBITUM> jam\force_dev_engine.bat [studio] http://127.0.0.1:8770/studio Need Python 3.9+ on PATH. Installer can pip numpy + ijson.
Band rules One key. One progression. One tempo. HOLD locks the groove. Mutation = chaos slider / CC1. Smart = learned harmony. Feed your own MOD/XM/XRNS/SID/SPC later — starter model jams now.

Stuck on "engine loading library…"? Run python -m pip install -r jam\requirements.txt. Missing numpy = no engine. As of 0.9.43 it says so instead of waiting forever.

VST / Standalone

Live jam client. Models, blend, FM emulate or serial, Studio panel.

Studio chip-DAW

Sections, clips, Journey/Dice, export XRNS / MIDI / A2M / stems.

FM31 silicon

Same register stream to DOSBox, Win3.1, Win98, or real ESS/OPL card.

02 // Watch serial demo + live surfaces

AdLibitum standalone jam UI
DOSBox OPL serial route
DOSBox OPL serial route The same register stream the VST emits, landing in DOSBox at 388h — proof the path is real before you wire up silicon. Full routing in SERIAL_OPL3.md.

03 // Serial FM SB16 lineage · 38400 8N1 · port 388h

SIGNAL PATH AdLibitum Serial/Both -> 127.0.0.1:8890 (FM31 monitor listen) -> optional COM forward -> FM31_DOS / Win16 / Win98 -> OPL3 / ESS ESFM @ 388h PROVE LAYERS 1. Emulate audible on modern PC 2. FM31_MONITOR.bat — channels move (monitor only) 3. FM31_DOS 1 /DEMO — six local notes on vintage box 4. Null-modem + receiver first, then sender demo Do not debug three layers at once.
Wire settings Baud 38400 · 8N1 · no flow control
Frame: AA bank reg val
Health: F0 -> F1

Pack: AdLibitum-FM31-Hardware-*.zip
Full kit: dos_testing\ARMCARE

04 // How we make music six modes · one brain · no random notes

C:\ADLIBITUM> TYPE PIPELINE.TXT ⛏ HARVEST ──▶ 🗄 BANKS ──▶ 🧠 MODELS ──▶ 🎼 COMPOSE ──▶ 📤 OUT ROL·BNK·CMF 1.3 GB of role-balanced one key, a2m·xrns·xm RAD·HSC·XRNS raw truth genre-carved one progression mid·wav·lsdsng XM·MOD·IT every phrase 12,615 OPL per section │ SID·NES·SNES knows its patches tagged a voice per ▼ YM·SPC·LSDJ source file by SOUND lane OPL3 · mGB · SID

Retrieve LIB

A mined phrase, verbatim. The real thing, played by the dead.

Blend BLND

Two phrases crossfaded. Drift controls the mix.

Latent LTNT

A VAE walk off-manifold, when nothing in the library will do.

Generate GEN

Motif development from the grammar. Not a quote — a composition.

Journey 🚂

"Start ambient, build to breakcore, end on a hymn." Compiled to form.

Dice 🎲

Constrained chaos. For when you are stuck and want a shove.

Modes are per CLIP, not per song Drums can be LIB while the lead is GEN and one pad section is LTNT. That is the point.

04a // Why it sounds coherent four rules, in order

1 · ONE KEY PER SONG Krumhansl-Schmuckler over the WHOLE song. Four notes cannot name a key. 2 · ONE PROGRESSION / SECTION Functional grammar + learned chord loops + a real cadence. Every lane shares it. 3 · SECTIONS MEAN SOMETHING ~24 archetypes. 'drop' != 'intro'. energy · density · register · role hints 4 · EVERY LANE HAS A VOICE An FM patch chosen for the part, held across the whole lane. ( COHERENT ) WAS: random-walk melodies · per-bar phrase shuffle · velocity-thinned sections · five hardcoded voices

04b // Known vs guessed we do not dress a guess as a fact

✅ KNOWN — read from the registers envelope pluck / hit / tone / swell EG-type=0 cannot sustain. Not a pad. Ever. Whatever it is named. timbre bright · dark · metallic hollow · punchy · evolving registers the 13 bytes that make the chip actually speak real names 146 BNK patches that say what they are
⚠ GUESSED — quarantined family_guess "probably strings?" kNN over FM param space 35% accurate (10% random, 20% majority-class) confidence barely tracks truth — ~54% even at the top decile SO: shown for browsing, never in searchable tags, never in role scoring. Only a real name is trusted.

04c // Anatomy of a song metal_s99 · F minor · 158 BPM · 76 bars

FORM ─────────────────────────────────────────────────────── intro verse chorus solo breakdown chorus outro ▁▁▁ ▃▃▃▃ ███████ ▆▆▆▆▆ ▂▂▂ ███████ ▁▁ e=0.2 e=0.5 e=0.9 e=0.7 e=0.3 e=1.0 e=0.1 └─ energy drives DENSITY, not volume ──┘ ↑ +2 semitone lift HARMONY ──────────────────────────────────────────────────── i → VI → III → VII → i learned loop + cadence, ALL lanes LANES ────────────────────────────────────────────────────── drums ██ ██ ██ ██ "Exploding TomDrum of Death" [punchy] bass ▄▄▄▄▄▄▄▄▄▄▄ "BOUNCE MAC BASS" [dark] lead ~~~~~~~~~~~ chord-tones on strong beats [bright] arp ····::::··· locked to the chord, every step pad ▁▁▁▁▁▁▁▁▁▁▁ "Sawtooth" + 7th/9th colour [warm] counter ~-~-~-~-~-~ 3rd/6th BELOW the lead, in-key └─ only where the lead is actually playing ─┘ PROVENANCE ───────────────────────────────────────────────── every clip knows: source song · path · format · mode · seed every voice knows: which patch · from which .BNK/.ROL · how

04d // Train your own brain feed it your library

C:\ADLIBITUM> REM point it at anything — artist, OST, demo pack C:\ADLIBITUM> jam\train_model.bat --dir "Z:\music\Necros" --name necros [mine] xrns · xm · mod · it · s3m · mid [tag] every phrase tagged "necros" + provenance [out] jam\models\necros.json (2-10 MB, loads ~0.2s) C:\ADLIBITUM> REM or rebuild from banks you already mined C:\ADLIBITUM> python -m jam.backfill_keys <- keys FIRST C:\ADLIBITUM> python -m jam.build_models C:\ADLIBITUM> python jam\build_public_starter.py ORDER MATTERS. A model is a SNAPSHOT of the banks. Build before backfilling and you bake keyless phrases in. (that is exactly how the public model shipped 0% keyed)
Not a neural training run "Training" = mine real music into phrases, then reservoir-sample them into a small fast model. Deterministic. Checkpointable. Every phrase keeps a path back to the file it came from.

The VST does it too "Train model…" button, or drag a folder onto the plugin.

Your ratings are in the loop Learn/Forget weights retrieval, and banned phrases never even enter a model.

04e // Recall how a phrase gets chosen

jam(role, seed, bank, tags) pool = roles[role] bank given? ──▶ pool = banks[role][bank] sid/lsdj/snes… tags given? ──▶ tagged = pool ∩ tags ├─ match ──▶ narrow to tagged └─ none ──▶ keep wider pool (NEVER starve) filter_pool ──▶ drop anything you banned weighted_choice(seed) your ratings weight it phrase + apply_overrides()
Four properties that carry the sound Tags narrow, never starve. Ask for "dark" material the library lacks and you still get a part, not silence.

Seeded = reproducible. Same seed, same phrase. That is what makes HOLD hold, a reroll change one thing, and a track replayable years later.

One source per request, not per bar. Pick ONE phrase, then develop it every bar. Per-bar picking is song-hopping — a real cause of the old randomness.

Retrieval is one of six modes. Blend/latent/generate leave the library behind by degrees; journey and dice drive the rest.

04f // Move the brain GitHub ships code, not your library

jam\models\ 122 MB <- THIS is the brain. Copy it, done. jam\banks\ 1.7 GB <- only needed to REBUILD models. Optional. The engine loads MODELS, never banks. So a fresh PC needs: C:\> git clone https://github.com/aday1/AdLibitum.git (code) C:\> robocopy old:\jam\models new:\jam\models /MIR (brain) C:\> python -m jam.server --model full [brain] expect green FULL — not amber SMALL BRAIN Both folders are gitignored; the publish fails a leak gate if one ever sneaks in. Your library never touches GitHub.

04g // Measured a number without a baseline is decoration

ClaimMeasuredBaseline
Chip key detection, in-scale fit92% median58% random
Chip songs with >75% fit84%
Lane voices per song6/6, zero defaultswas 0
LSDJ model, real Game Boy material93%was 3.4%
Chip phrases given a key, no re-harvest767,185was 0
sid / vgm median key confidence0.58 / 0.46was 0.00
Patch family kNN guess35%10% random
Where it is still thin — stated plainly synthwave is 7% own-tag material — mostly not synthwave, backfilled from its cluster. hiphop / ambient / prog are thin and backfill until more genre folders are mined. LSDJ dynamics are still flattened at the bridge (bars=1, no vel/dur) — known, not fixed. family_guess is 35% — do not build on it.
C:\ADLIBITUM> TYPE HOW_WE_MAKE_MUSIC.MD Full method, every claim with its baseline, and the rules for the next agent: HOW_WE_MAKE_MUSIC.md Made with mined ghosts and Krumhansl-Schmuckler. \m/

04h // DAW Horsemen AI rides REAPER / Bitwig / Renoise beside the chip band

MCP Survival Pack Shared Bitwig SSE so Cursor / Claude / Desktop do not fistfight over UDP 9001. CARE heals OSC + Mackie. v2.6.0 = MSI + tray GUI (ports + update checks). AdLibitum stays the OPL jam; Horsemen drives the big DAWs around it.
C:\> showcase (GitHub Pages) C:\> github.com/aday1/...Horsemen... C:\> DAW-Horsemen-2.6.0.msi [run] launch_daw_mcp.bat or CARE.bat

05 // What ships 0.9.104 Panel Close

PieceNotes
One launcherAdLibitum.bat → the hub: engine control + Studio + generate + a2m import + FM send + harvest + hardware, one window, tooltips, system tray
Compose → DAWStudio song → REAPER .rpp / Renoise .xrns + .opl.json sidecar; a ReaScript loads the AdLibitum VST per lane at the right FM patch
Renoise / REAPER toolsOne-click .xrnx Renoise tool + REAPER ReaScripts, auto-copied into your DAWs by Setup (optional task) or python -m jam.install_daw_tools; a live Launch Driver GUI — routing fan to Nuked-OPL emu and/or the Armada box (toggle either live), per-channel mute/solo, instrument names, live notes + VU. Import any scope (lanes × patterns) and Preview♪ it on the chip before committing; capture auto-tempo + one-pass (no loop)
Setup.exeVST3 + Standalone + jam engine + starter model + FM31 tools. One click.
Hardware ZIPDOS / Win31 Safe·Stage·Full / Win98 receivers + C source + build scripts
Brainstarter.json ships; FULL brain on machines with genre models
SF2 liveStudio live button (FluidSynth) + VST SoundFont backend (TinySoundFont) — real-time SF2, not offline render only
FM DeckStudio Deck panel: drag A2M tracks/patterns onto lanes (WebView2-safe drag)
WorkbenchRaised phrase/table caps (30k/8k) + Generator panel Close (no stuck Text title)
Fresh Boot fixNo more forever-hang probing E:\ before install path
Voices12,615 OPL patches tagged by sound — every lane gets a real FM voice, not a default
Chip keysSID/NES/SNES/YM/LSDJ finally in-key — 767,185 phrases keyed, no re-harvest
NeedsPython 3.9+ · optional WebView2 for in-plugin Studio
C:\ADLIBITUM> TYPE README Ad libitum — improvise at pleasure. Not a WAV folder. A band mate with provenance. Code: beerware / CC BY 4.0. Your module rights stay yours. \m/

07 // Architecture how the pieces fit — one diagram per subsystem

Each box maps to a Claude skill in .claude/skills/ — ask Claude to load one (e.g. "use the adlib-studio skill") to work on that part naturally.

musicbrain — the composition brain

musicbrain is AdLibitum's music-theory + generation core: it mines a module archive into per-genre models, then turns a seed into a coherent SongPlan (key, cadenced progression, developed motif, form) that every chip/DAW backend renders.

 LIBRARY (.xrns .xm .mod .it .s3m)
        |
        v
  parsers/  --> ParsedModule (TrackLane: row,midi,vol)
        |
        v
  mine.analyze  --> SongAnalysis (Krumhansl key, per-bar romans,
        |                          interval/onset/drum histograms)
        v
  mine.build_model  --> models/style_<genre>.json  (+ style_all)
        |                        ^                          |
        |                blend.py (hybrid: anthem)          | export
        |                                                   v
        |                            musicbrain.ts <-- data_musicbrain.js
        v                                (LSDJ workbench, browser)
 ===================  build_song_plan(seed, style, model)  ==============
   structural RNG(seed): key -> form -> progression (unconditional draws)
   theory.generate_progression   form.get_form/parse_form_spec
   melody.render_phrase (motif dev, AABA, chord-tones-on-strong-beats)
   rhythm (grooves/fills; density thins EVENTS not velocity)
   per-lane RNG(seed,lane,var): each lane re-rollable in isolation
        |
        v
     SongPlan  = [ PNote(bar, step, midi, vel, dur, lane) ]  + critique()
        |
   +----+---------------+-----------------+----------------+-----------+
   v                    v                 v                v           v
 generate_xrns     export_stems      reaper_write     at2kit       lsdj-
  (.xrns)          (.mid/.wav)         (.rpp)         (.a2m)      toolkit

AdLibitum Studio — the web chip-DAW

Studio is a single-page browser chip-DAW that turns a GENERATE press into a full arranged, harmonically-coherent song (sections x lanes) saved as an .adlib.json project you can preview in WebAudio and export or stream to a real OPL3 chip.

 BROWSER  jam/studio.html SPA         server.py (HTTP :8770, ThreadingHTTP)
 +--------------------------+  POST   +-------------------------------------+
 | toolbar: preset chip key |  JSON   | /songplan /journey /dice /genlane   |
 | scale bpm seed  GENERATE +-------->| /clip /section /newsong /export     |
 | Dice Journey Fill +Track |         | /fmstream /fanout /launch /health   |
 +------------+-------------+         +------------------+------------------+
              |                                          | ENGINE = JamEngine
              |                                          v  .jam() retrieve/
              |                     studio_song.build_song()  blend/latent +
              |                     +-----------------------+  _smart_progression
              |                     | musicbrain.form ->    |
              |                     |   sections x LANES    |
              |                     | section_archetypes    |
              |                     |   .resolve (energy,   |
              |                     |   roles, tag hints)   |
              |                     | CHIP_PROFILES ->      |
              |                     |   bank/target/voice   |
              |                     | lane_patch/voice_for  |
              |                     |   -> OPL 13-byte reg  |
              |                     | _drum_floor _humanize |
              |                     +-----------+-----------+
              |                                 v
              |                       *.adlib.json  PROJECT
              |        render()   generated/projects | generated/studio/<t>/
              v  <------------------------+-----------------+
 +--------------------------+            |                 |
 | PREVIEW (WebAudio)       |     studio_export.py    fm_stream.py  (FM >)
 | synth(): per-lane TARGET |     xrns mid rpp        at2net.exe -> TCP
 | oplRawVoice(13 regs)     |     xm a2m stems        :3819 -> real OPL3
 | laneInstr>laneAutoVoice  |                         or Nuked-OPL3 emu
 +--------------------------+

FM drivers — MIDI/notes to OPL registers

It translates musical intent — MIDI notes+CC, verbatim register SysEx, or a compiled .a2m module — into exact OPL3 [bank,reg,val] writes and streams them over the FM31NET TCP protocol to a real, emulated, or fanned-out FM chip.

 Studio / DAW / MIDI keys      verbatim regs        .a2m module
   note + CC (rtmidi)          F0 7D..F7 SysEx           file
        |                           |                     |
        v                           v                     v
  +--------------------------------------+        at2net.exe
  |            OplDriver (pure)          |     (fm_stream.py mgr;
  |  18-voice alloc, LRU steal, patch    |      mix desk :3820)
  |  cache; note -> A0/B0/40/C0/60/80/E0 |            |
  |  sysex() -> verbatim register writes |   emits [0xAA b r v]
  +------------------+-------------------+            |
             emit(bank, reg, val)                     |
                     |                                |
          +----------+-----------+                    |
          v                      v                    v
   +--------------+     +--------------------------------+
   | Fm31NetSink  |     |  MultiSink / Fanout proxy      |
   | F0->F1 probe |     |  frame_split, F0->F1 ACK,      |
   | TCP_NODELAY  |     |  mirror to N live targets      |
   +------+-------+     +----------------+---------------+
          |     [0xAA bank reg val] over TCP :3819       |
          v                    v              v          v
   fm31emu.exe (Nuked    Armada FM31NET   backup box  test_sink.py
   OPL3->waveOut,:3819)  real OPL3 chip   (redundant) (verify, no HW)

at2net — AT2's replay engine over TCP

at2net reuses AdLib Tracker II's own DOS replay engine (compiled for Win32) but redirects every OPL register write out over TCP as [0xAA bank reg val] frames, so any .a2m version plays on a real networked OPL3 with authentic tracker effects.

 .a2m / .a2t file
      |  a2m_file_loader (v1-14; aPLib depack via patched a2depack.pas)
      v
 +----------------------------+     AT2 replay engine (engine/*.pas)
 | songdata + pattdata (RAM)  |     patched by patch_engine.py:
 +----------------------------+       ~10 asm helpers -> pure Pascal
      |                                parserio.pas CRC override (-Fu.)
      | timer_poll_proc  <-- driven at effective_hz by the main loop
      v                       (QueryPerformanceCounter, HIGH prio, 1ms)
 opl3out / opl2out / opl3exp   (engine procedure vars, REDIRECTED)
      |   DisableTimerIRQ/EnableTimerIRQ -> no_irq  (DOS PIC bypass)
      v
 tcp_out(reg,data): reg_cache dedup + sender mute/solo filter
      v
 txbuf --tx_flush (1 send/tick, TCP_NODELAY)--> [0xAA bank reg val]
      |   handshake 0xF0 -> 0xF1 0xF2            [0xB0/B2/B4 metadata]
      v
  TCP :3819 =========== network ===========>  FM31NET (Win98 box)
                                                     |
 control :3820 <--line cmds-- Studio / MCP           v
  mute solo jump loop pause status quit          real OPL3 chip

 test_sink.py = local :3819 stand-in (counts frames, no hardware)
 fm_stream.py = spawn at2net.exe + drive :3820  (Studio FM>, MCP)

FM31 Receivers — DOS / Win3.x / Win98 + FM31NET

A family of tiny native receivers that parse AdLibitum's [0xAA bank reg val] FM31 frames off a serial cable or TCP :3819 and write them straight to a real OPL3/ESS-ESFM chip's I/O ports on retro DOS/Windows hardware (or a Nuked-OPL3 emulator).

      MODERN BOX (sender)                  RETRO / EMU BOX (receiver)
 +--------------------------+           +----------------------------+
 | Studio / VST / at2net    |           |  FM31 4-state frame parser  |
 |  builds the wire stream  |           |  0 idle -> 1 bank -> 2 reg  |
 +------------+-------------+           |         -> 3 val -> apply   |
              |                         +-------------+--------------+
              |  [0xAA][bank][reg][val]  = OPL register write        |
              |  [0xF0] -------------->   reply [0xF1] (+[0xF2] v2)  |
              |  [0xB0/B2/B4 ..] = v2 metadata (title / pos / ins)   |
              v                                       |
 +-----------------------------+                      v
 |  TRANSPORT                  |            +----------------------+
 |   serial 38400 8N1          |            | direct OPL port I/O  |
 |    (null-modem, NO flow)    |            |  idx = base+(bank?2:0)|
 |   or  TCP :3819 (NODELAY)   |            |  dat = idx+1          |
 +-----------------------------+            |  settle 6 / 35 reads  |
                                            |  base 388h / bank1 38Ah|
 SERIAL rx: oplserial, fm31_dos (DOS)       +----------+-----------+
            fm31 / safe / stage   (Win16)               |
            fm31w98              (Win9x)                 v
 TCP rx:    fm31net (9x)  fm31w16 (3.x)         YMF262 OPL3 / ESS ESFM
            fmstage / fm31gui    (Win32)      NT-class: gNoOpl -> LINK TEST
            fm31emu (Nuked-OPL3 -> waveOut)   (fm31emu -> waveOut speakers)

AdLibitum VST — the JUCE plugin

A JUCE 8 VST3/Standalone synth that asks the local Python jam engine for MIDI each bar, then turns those notes into OPL3 register writes rendered as FM audio (built-in or Nuked-OPL3) or streamed as serial frames to real AdLib hardware.

        DAW host / Standalone
        (playhead ppq, transport, host MIDI)
                    |
                    v
 +=============== AUDIO THREAD (processBlock) ==================+
 | params->atomics (fmMode) | pick motif block from ppq        |
 | emit scheduled MIDI | drive OplVoice/SfPlayer | render out   |
 +===+================+==========================+=============+
     | requestBars     | noteOn/Off + fmPatch     | render()
     v                 v                          v
 +------------+  +-------------------+       audio out L/R
 |EngineClient|  | OplVoice          |<-- oplbank.bnk (4096 patches)
 |HTTP THREAD |  | note -> OPL3 regs |
 |POST /jam   |  | onWrite(bank,reg,val) fans to:
 |build body  |  +--------+----------------+
 |<-- events  |           | queueReg       | oplEmu.writeReg
 |ingestResult|           v                v
 | scheduled[]|  +----------------+  +------------------+
 +-----+------+  | SerialWriter   |  | OplEmu (Nuked)   |
       |         | SERIAL THREAD  |  | or compact FM    |
       v         | [AA bk rg val] |  +--------+---------+
 jam/server.py   | TCP :8890 /COM |           | audio out L/R
 :8770           +-------+--------+           v
 retrieve/blend/         |
 gen, harmony,           v
 provenance       FM31 monitor / at2net -> real OPL3 chip
       ^
       | launchEngine (python -m jam.server)
 +-----+---------- MESSAGE THREAD (Editor) ------------------+
 | NoteHeatmap 30fps <- monQueue/playStep | LatchKeyboard    |
 | polls /health /sources /models /log /patches | FM31 panel |
 | WebView2 Studio | APVTS params | MIDI-CC learn            |
 +----------------------------------------------------------+

a2m -> DAW importers & drivers

Captures an AdLib module's exact OPL register stream as at2net plays it, then rebuilds it two ways — a bit-true SysEx timeline for REAPER and an editable note/voice song for Renoise — and drives the result back onto the real OPL3 chip over MIDI. Import the whole song or a scope (any lanes × any patterns), Preview that scope on the chip before committing, and steer playback from the Launch Driver GUI: a routing fan to the Nuked-OPL emulator and/or the Armada box (toggle either live), with per-channel mute/solo and live notes + VU.

                     .a2m / .a2t  (any version)
                              |
      +--------------- CAPTURE (real-time) ----------------+
      |  at2net.exe --[0xAA bank reg val]--> capture_sink  |
      |   (plays it)      TCP :3996          (JSONL sink)   |
      |   "tempo N speed M" -> BPM auto ; one-pass loop cut |
      +---------------------------+------------------------+
                                  v   capture.jsonl {t,b,r,v}
            SCOPE (a2m_scope): lanes × patterns, or whole song  +  Preview♪
                 +----------------+-----------------+
                 v                                  v
        VERBATIM layer                      EDITABLE layer
        a2m_verbatim.py                     a2m_to_renoise.py
        reg_lane -> 18 ch + GLOBALS         decode_notes: fnum->pitch,
        19-trk SMF, each write =            carrierTL->vel, on/off->dur
        SysEx F0 7D..nibbles..F7            18-trk .xrns + .opl.json
        verify_smf (bit-true)               (oplbank voice per track)
                 |                                  |
                 v                                  v
        <name>_verbatim.mid                 <name>_renoise.xrns
                 |                                  |
        REAPER ReaScript                    Renoise tool main.lua
        InsertMedia = 19 trk                load_song + route_to_opl
                 +----------------+-----------------+
                                  v   MIDI out -> LoopBe1
              daw_fm_driver / fm_driver_gui  (Launch Driver GUI)
              notes/CC/prog -> OPL regs  (+ mute/solo/VU/live notes)
              sysex() = verbatim passthrough
                                  |
                                  v   fm_route RoutingSink  (proxy fan)
                    +-------------+--------------+
                    v                            v
             Nuked-OPL emu               Armada / Win98 box
             127.0.0.1:3819              192.168.1.60:3819
           (toggle either live; a dead box never blocks, rejoins) = real OPL3

  (a2m_to_daw.py: capture-free v8 parse -> .rpp + note MIDI, same driver)

LSDJ & Game Boy (mGB / Retroplug)

The Game Boy arm: it mines your LSDJ library, generates Game-Boy songs from the jam brain, edits them in-browser, and writes real .lsdsng/.sav carts that route to the Retroplug emulator VST, out as MIDI to mGB hardware, or through PyBoy to a WAV.

 YOUR LSDJ LIBRARY  (.sav carts / .lsdsng single songs)
        | lsdjtk.index + lsdjtk.harvest  (tag + dedup + provenance)
        v
 banks/phrases.json, instruments.json   (~25k phrases / 5.8k inst)
        | jam/lsdj_bridge.py  (GB channel -> role; NB bars=1, key=0)
        v
 jam/banks/lsdj.json  --build_models-->  jam/models/lsdj.json
        |                                        |
        |            Studio: chip="lsdj"         | jam engine
        |            CHIP_PROFILES                v (retrieve/blend)
        +------------------> studio_song.build_song
                              | bank=lsdj target=retroplug voice=gb
                              v
        project.adlib.json  (lead>PU1 arp>PU2 bass/pad>WAV drums>NOI)
                              | lsdj_store.generate_from_project
                              v  (dedup bars -> shared 255-phrase pool)
   /lsdj/* editor routes  SongBuilder
   (lsdj_store + Studio      |
    LSDJ panel)  <---------> |
                              v
 ===== 0x8000 SONG MEMORY  (song.py view) =========================
  arrangement(256x4 chains) -> chains(16 phrases) -> phrases(16 steps)
  + instruments(64) + synths(16) + waves(256) + kits(ROM) + tables
 ==================================================================
             | native.py  (liblsdj.dll  compress)
             v
        .lsdsng   /   .sav
         |            |                 |
         v            v                 v
   Retroplug     lsdjtk.midi        lsdjtk.render
   (GB VST:      -> mGB SMF          (PyBoy + real
    ROM+.sav)    CH1-4=PU1/PU2/       LSDJ ROM -> WAV)
                 WAV/NOI, CC map