@David-Healey I have it set up like this. Works for me so far, but it's one of those things with a lot of use cases.
The only scripting I have is to load and save the MidiMappings.json file. The preprocessor handles the rest.
I had Claude write this (hence the em-dashes 😜), to catch all the states:
The three persistence layers
Plugin state (the DAW session) — per HISE default, every instance's current CC assignments are serialized into the plugin state chunk the host saves with the session. This is the day-to-day layer and it's completely automatic. User presets — new preprocessor HISE_MIDI_AUTOMATION_IN_USER_PRESETS=0 in project_info.xml ExtraDefinitions means presets neither store nor restore mappings. Switching or saving presets never touches CC assignments. Default map file — AppData/MidiMappings.json, handled by Scripts/MidiMappings.js via Engine.createMidiAutomationHandler(). Only ever touched by the two buttons in the MIDI Control panel ("Save As Default Map" / "Load Default Map"). Nothing reads or writes it automatically — there is no auto-load in onInit or anywhere else.Use cases
First launch after installation — No plugin state exists and MidiMappings.json doesn't exist yet. The instance starts with zero CC mappings. If the user clicks "Load Default Map" at this point, loadDefault() returns false (no file) and the button flashes "No map found"; nothing changes.
New instance in a fresh DAW session (plugin already in use on this machine) — Same as above from the plugin's perspective: a fresh instance has no state, so it starts with no mappings, even if a default map file exists. The default map is opt-in per instance — the user clicks "Load Default Map" to apply it. This is the intentional trade-off of the design: no surprise mappings, at the cost of one click per new instance.
Reopening a DAW session where mappings were set — The host hands back the saved plugin state, and the exact assignments from when the session was saved are restored per instance. The default map file is not consulted, so it can't overwrite or drift from what the session had.
Reopening a DAW session where no mappings were set — The restored state contains an empty assignment set and the instance stays empty. There's no fallback to the default map file; saved state is authoritative.
Loading / switching user presets (any time) — No effect on mappings in either direction, thanks to the HISE_MIDI_AUTOMATION_IN_USER_PRESETS =0 flag. A preset saved while mappings existed doesn't carry them, and loading one doesn't clear them.
Clicking "Save As Default Map" — Writes the current instance's entire assignment set to AppData/MidiMappings.json (temp-file + atomic rename, so a failed write can't corrupt an existing map). Flashes "Saved!" or "Error!". Attempting to save with zero assignments shows "No mappings to save" and doesn't save an empty default map.
Clicking "Load Default Map" — Replaces all current assignments in that instance with the file's set. If the file is missing or doesn't parse (hand-edited/corrupt JSON), current assignments are left untouched and the button flashes "No valid map found". A successful load gives no flash; the MIDI learn table updating is the feedback.
Multiple instances in one session — Each instance's mappings are fully independent (per-instance state). The only cross-instance channel is the default map file, and only via explicit save in one instance and explicit load in another. Changes in one instance never propagate on their own.
One subtlety worth remembering: since the default map is per-machine AppData, it's shared across all DAWs and sessions on that machine, but versioned by nothing — the last "Save As Default Map" click from any instance wins. There is no "Save over existing map?" kind of confirmation.
Also note that the HISE_MIDI_AUTOMATION_IN_USER_PRESETS preprocessor I added is unlikely to get merged, per Christoph's responses earlier in this post.