C:\ADLIBITUM> SHOWCASE.EXE   v0.9.152 HANDS OFF THE DESK

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

04i // Click to Chip Explorer is a player now — right-click does the rest

.a2m double-click

Opens in native AT2 (SDL) with the song loaded and playing. Right-click: foobar2000 (AdPlug), real DOS AT2 in DOSBox (auto-loads via keystroke injection), or the NetPlay desk.

A2M NetPlay

a2m → at2net → real OPL3 over TCP. Transport, 18-channel mute/solo before the wire, per-instrument mute, 2s chip AUDITION, TAG/BAN instruments to a cleanup list, and a true tracker view parsed from the module — rests, effects, playhead.

.lsdsng double-click

LSDSNG NetPlay streams LSDJ songs to mGB (real Game Boy or RetroPlug hosting mGB.gb) as MIDI: baked instrument tables, slides/retrigs/chords, kits voiced on NOI, PU1/PU2/WAV/NOI mute/solo + phrase lookahead. Right-click: real LSDJ in RetroPlug playing the actual cart.

C:\> jam\a2m_assoc\INSTALL_A2M_ASSOC.bat ← one double-click, per-user, no admin C:\> jam\lsdj_assoc\INSTALL_LSDSNG_ASSOC.bat [ok] banned instruments feed the brain: python -m musicbrain.train <lib> --skip-banned

04j // Hands off the desk REAPER drives the X-Touch — the plug-in stopped fighting it

The FM Driver used to open the X-Touch itself. Windows grants a MIDI port to one process, so with REAPER's own Mackie surface enabled the two owners took turns: SELECT worked on some tracks and not others, and scrubbing died whenever focus moved. A plug-in is the wrong place for a surface anyway — one instance per lane means ten would-be owners in a ten-lane import. So the surface layer came out (parked and documented in jam\fmdriver\attic\) and the desk is REAPER's.

Reach the instrument

SELECT the lane → PLUG-IN → V-Pots 1-8. The 27 patch parameters moved to the front of the list so bank 1 is the modulator envelope, and every name is unique in its first 7 characters — one Mackie scribble cell. FLIP puts the page on the motor faders.

Write and read automation

Toolbar AUTOMATION WRITE / READ: arms LATCH plus the chosen patch envelopes, then flips back to READ with everything disarmed so the motors follow the envelope. It asks which group, because an FX envelope holds its parameter and arming all 27 would freeze an imported lane on one timbre.

MIDI learn, and unlearn

Right-click any knob and you get REAPER's parameter menu — Learn MIDI, Remove learn, envelope, parameter modulation. The host owns the binding, so it survives, shows in the FX window, and can be undone from either side.

FM MONITOR, docked

A ReaScript panel inside REAPER: all 18 OPL3 channels lit from the real register stream, live writes/s, frames, chip channels held, and an ICMP ping to the Armada with its latency. The plug-in publishes one snapshot at 4 Hz from a single hook that sees the whole chip — no individual instance can.

Names that fit the desk

Tracks are named content-first — kick 01 [FM v4096] — because the old FM 01  kick put "FM 01 " in all seven cells and every strip read the same. Lanes that still collide get the number moved to the front, which is unique by construction.

FM MINI on the Armada

The full-screen Win98 desk colour-cycles its title, so every frame was dirty and a Pentium II's fans never came down. FM MINI is 640×336, fixed, repaints only when something changes, and shows song / composer / order / line / tempo plus the instrument name sounding on each channel. Desktop shortcuts carry real 8-bpp icons — 32-bit alpha icons are an XP feature and Win98 draws them as noise.

C:\> python jam\reaper\setup_mcu_surface.py ← REAPER closed; writes csurf_0=MCU into [reaper] C:\> python jam\reaper\surface_healthcheck.py --fix C:\> powershell -File jam\opl3_player\win98\deploy_armada.ps1 ← archives the old generation, installs the new [ok] manual: docs\video\xtouch_map.html — TL;DR table first, measured vs stock marked

05 // What ships 0.9.152 HANDS OFF THE DESK

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.exeAdLibitum-0.9.152-Setup.exe — VST3 + Standalone + jam engine + starter + FM31 tools. Shows version in wizard and Add/Remove; re-run replaces prior install. Versioned Start Menu / Desktop icons (e.g. AdLibitum (Standalone) 0.9.152). Packages Retroplug Extreme when the local binary is present.
Retroplug ExtremeShortcut AdLibitum (Retroplug Extreme) 0.9.152 → packaged jam\retroplug\retroplugxtreme.exe (or local gameboy\RetroplugDFP\build\bin\). Refresh: python -m jam.make_shortcuts
FM Driver VSTSecond plugin "AdLibitum FM Driver": plain OPL3 note player for DAW tracks — MIDI in → harvested oplbank voice → Nuked-OPL3 monitor + FM31NET wire to the Armada. Stable enough to sit on 8 tracks; X-Touch pages; voice fav/flag/ban marks keyed to register bytes
Studio Bridge VSTThird plugin "AdLibitum Studio Bridge": the Studio as a DAW sidecar — COMPOSE in the plugin, DRAG any lane (or whole song / .rpp / .xrns / SysEx register-verbatim / mGB CC set) straight onto a DAW track. LSDJ LIBRARY tab pulls one channel of a real .lsdsng as mGB MIDI
Explorer players.a2m and .lsdsng are double-clickable (section 04i): native AT2, foobar2000, DOSBox AT2, A2M NetPlay chip mix desk, LSDSNG NetPlay mGB streamer, RetroPlug LSDJ — per-user installers, no admin
Ban the brainInstruments TAG/BAN'd in NetPlay feed train --skip-banned: banned lanes leave the corpus before mining
LSDJ cartsStudio LSDJ panel: rich kit browser, synth editor, arrangement/chain grid, phrase tracker view; export .lsdsng straight to Retroplug
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