Skip to content

Serialization

IVLS handles parameter serialization through two mechanisms: the Engine Box's built-in PCCB and snapshot integration, and explicit NKA file save/load for data that lives outside the parameter system.

Why This Matters

KSP's raw persistence system is error-prone — arrays must be manually declared persistent, order matters, and there is no versioning. The Engine Box wraps all of this behind a schema-aware system with length checking, version callbacks, and Data IDs. NKA files handle the rest: pre-built lookup tables, articulation databases, and analysis results generated offline by Python tools.

Key Concepts

Engine Box Serialization

Every Engine Box participates in snapshot save/restore automatically once you call Data.PCCB(box_name) in cb PCCB. The box embeds four identifiers in the snapshot payload:

Identifier Purpose
Data Length Total integer slots in the box. Mismatch rejects the load.
Data ID Unique string per box. Collisions cause silent corruption.
Version Integer from get_current_version(). Mismatch triggers migration.
Product ID Refuses snapshots from a different product.

Implement version functions in the data file:

function plc.layers.check_version.response(version)
    // version is what was stored in the snapshot
    // migrate data here if schema changed incompatibly
end function

function plc.layers.get_current_version() -> out
    out := 0    // increment only on breaking schema changes
end function

As long as you only append parameters to the end of MEMBERS and keep SIZES large enough, version can stay at 0 indefinitely. Increment only when you change the meaning or position of existing parameters.

instpers vs. Box Parameters

declare instpers variables survive preset changes. Use them for view state — where the user is in the UI — not parameter data:

declare instpers section_id   := plc.ui.section.POLYCORE
declare instpers page_id      := plc.ui.page.MAIN
declare instpers layer_tab_id := plc.ui.layer_tabs.SOUND

Instpers variables are stored in the NKI, not in the snapshot. Snapshot/PCCB data controls what the instrument sounds like. Instpers data controls where the user is looking.

NKA Save/Load API

For data that does not fit into the box parameter system — lookup tables, pre-built articulation databases, non-integer values — use direct NKA file access:

At the lowest level, Kontakt provides save_array() and load_array() for NKA file access. The IVLS SDK wraps these in nka.ksp (see extended-library/components/nka.ksp) with convenience functions for common patterns. Product-specific systems (like TACT and the serialization framework) build higher-level save/load APIs on top.

NKAs built by Python tools (tact-config-importer.py, CreateReleaseArray.py) are typically placed in a Data/ subfolder inside the instrument's resource container and loaded at cb Init time via load_array().

PCCB vs. PostReload

// In the data file: registers with the snapshot system
cb PCCB:
    Data.PCCB(plc.layers)

// In the UI binds file: pushes restored values back to widgets
cb PostReload:
    call plc.layers.push_data_id_to_ctrls()
    call plc.layers.update_ctrls()

cb PostReload is the correct place to call push_data_id_to_ctrls() and uir.update(). Do not call uir.update() from cb PCCB; the UI may not be ready at that point.

Snapshot Type 3 Gotcha

set_snapshot_type(3) opts the instrument into Kontakt's full-state snapshot mode, which is required for Engine Box serialization to work correctly across DAW sessions.

However, it has a critical side effect: cb Init no longer initializes variables to zero. When Kontakt loads a session that already has saved state, the snapshot bleeds directly into variable space before cb Init runs. Any logic that tests "has this been initialized?" by checking for a zero sentinel will fail silently.

The correct guard is to explicitly initialize critical state in cb ICB (before snapshot restore), not in cb Init:

node AlteraKeys.Engine:
    cb ICB:
        recall.system_enabled := FALSE
        set_snapshot_type(3)

Practical rules with snapshot type 3: - Explicitly initialize all critical state in cb ICB rather than relying on KSP zero-fill. - To test a clean state, cold-load the NKI from disk (bypassing any existing session snapshot) or briefly switch to set_snapshot_type(2), save the session, then switch back. - Always use globally unique Data IDs per box. A snapshot saved under one schema can inject values into a different schema if IDs collide.

Oversizing for Schema Growth

Always declare SIZES larger than the current parameter count (see engine-box). When a snapshot is loaded with an older, smaller parameter list, the trailing indices receive their .default values — the load succeeds because the declared size is at least as large as the stored size.

Connections to Other Parts of IVLS

Serialization is wired into the engine-box through Data.PCCB. The cb PostReload pattern in the engine box UI file pushes restored values to widgets. NKA files loaded at init time are the data layer for tact (articulation metadata) and adaptive-release (volume envelope tables).

Patterns and Caveats

  • Data IDs must be globally unique per product — not just unique within a single box, but across all boxes in the entire instrument.
  • Do not store parameter values in instpers variables. They belong in Engine Box pars.
  • The dev-mode ResetParams button pattern (hidden in production, calls load_defaults() to reset pars to declared defaults) is a useful development tool. Hide it from end users.
  • engine-box — box declaration, callbacks, and the three-file pattern