# Native C Processor API, Version 1 `xtc_native.h` defines a small compile-time extension boundary for trusted C99 sources, effects and instruments. Build `cmake --build build --target native-gain` and run it for a complete graph-effect example in `examples/native_gain.c`. This is a source API for this release, not a promise of binary compatibility across builds/platforms. There is no module scanner, dynamic loader, SDK dependency or sandbox. --- ## Descriptor and Ownership Set `abi_version = XTC_NATIVE_ABI_VERSION`, `struct_size = sizeof(descriptor)`, a terminated printable non-space ASCII identifier (1..63 bytes), kind and all four callbacks. Unknown ABI/size returns UNSUPPORTED; invalid fields return INVALID_ARGUMENT. The host copies the entire descriptor, including its name. Callback code must remain loaded until all graphs, plans and instances are gone. No plugin/global registry is consulted or mutated by rendering. `xtc_native_create` validates rate (positive, <=768000), frames (1..65536) and a finite scalar parameter. It runs prepare on the control thread with a NULL initial state pointer. Prepare may allocate/preload; on failure the host calls destroy even for NULL state. Destroy must accept NULL. The host then resets the new instance. On failure the output handle is NULL; successful instances belong to the caller. Destroy only after the processing owner is stopped/joined. Reset and process have one audio owner (or run while stopped). They must never allocate/free, perform I/O/logging, block, call GUI functions or mutate shared control data. Reset must be idempotent. Prepare/destroy run only on the control thread, including failed compilation and retired-plan collection. Code executed inside callbacks is trusted: xtc cannot enforce its realtime behavior or protect against memory corruption. C++ processors must use C linkage and never throw across this boundary; domain/DSP code in this repository remains C. --- ## Block Contract A block contains mono input/output, frames, absolute sample position and optional MIDI events. Input NULL means silence; exact input/output aliasing is allowed and processors must support it. Output is writable for every frame, including silent frames; do not retain any borrowed block pointers. Other overlapping buffers are not supported. Only instruments receive events; event count <=256, offsets are sorted and strictly less than frames, with equal offsets retaining caller order. Pitch/velocity/channel/type bounds follow xtc_midi.h. Zero frames is a no-op. The host rejects invalid blocks before changing state/output, and reports CAPACITY above the prepared block limit. Position arithmetic cannot overflow. A discontinuous position resets held voices/state before processing. Call reset for Stop/panic when hosting instruments yourself; send channel ALL_OFF for channel-specific panic. The native host does not implement sustain or routing. On successful processing, nonfinite samples become silence. Callback failure silences the block, resets that processor and returns its error. Already advanced processors in a graph are not rolled back. Never rely on failure to transactionally rewind DSP; prepare fallible resources before publication. There is no latency compensation, tail protocol, parameter ramp or dynamic port negotiation in ABI 1. --- ## Graph and Instrument Integration `xtc_graph_add_native` adds effects (input 0/output 1) or sources (output 1), with a scalar immutable parameter. Compilation creates two independent mono instances for stereo; no mutable state is shared between sides. Graph-only edits retain the existing pending/retired publication contract. Reset clears native state and its graph-local frame clock; graph positions count successfully rendered frames since compilation/reset, independently of backend seek/transport. Sources needing transport time should be hosted explicitly with a block position. For an instrument, use create/process directly with sample-offset events, then feed its output to an existing XTC_NODE_INSTRUMENT through process_sources or process_stereo. The host owns event routing and panic/reset delivery. Graph-native instrument addition returns UNSUPPORTED because current graph ports carry audio, not MIDI. This separation avoids silently dropping instrument events. The built-in JACK MIDI sine instrument remains the GUI's instrument; custom instrument selection and event graph ports are future work. Descriptors survive graph/project cloning and transactional remove/connect edits; custom ADD_NODE commands use the dedicated constructor rather than a GUI menu. Native nodes have no project-file representation: Save returns UNSUPPORTED and preserves the destination. Format v4 still accepts only builtin types 1..6. A durable identifier/registry, versioned processor state and missing-module policy must precede serialized native extensions or external plugin hosting. Tests cover ABI/size/field rejection, descriptor copying, aliased buffers, prepare/process failures, stereo compile cleanup, retirement/destruction outside processing, event offsets, seek note reset, source ports, instrument-buffer graph routing and unsupported-save preservation. Run `cmake --build build --target check`. --- ## Structured Parameter Processors `xtc_processor.h` is the public C99 parameter-only extension interface (ABI 2). It complements the ABI 1 graph adapter without changing its layout or behavior. `examples/processors.c` implements a mono gain effect and a monophonic MIDI sine instrument using only public headers; `cmake --build build --target test-processor` builds and exercises both. The instrument has immediate note-off and no voice stealing/release tail. Descriptors have an exact ABI version and structure size, a persistent identifier, and up to 16 parameters with unique unsigned IDs, names, units, finite ranges and defaults. Unsupported ABI versions/sizes are rejected before initialization. Changing identifier, parameter order, IDs or ranges requires an explicit migration by the extension author. There is no promise of binary compatibility across compiler architectures, and no C++/FLTK types in either API. Create prepares a copied descriptor on the control thread. Failed preparation calls destroy, including with NULL: callbacks must implement partial cleanup. Reset/process/parameter have one audio owner and must never allocate, free, block, perform I/O or call GUI functions. Destroy is control-thread only after callback quiescence. Reconfiguration prepares a new independent instance; publish it with an appropriate owner handoff and retire the old instance after quiescence. The callback code must remain loaded throughout that lifetime. Each instance has one mono output and either one optional input (effect) or no input (source/instrument). NULL input means silence. Stereo effects use two independent instances; multichannel interdependent effects have no adapter. Instruments receive at most 256 sorted sample-offset MIDI events per block. The API intentionally has no channel-count argument: unsupported layouts cannot be represented. Graph ABI 1 rejects instrument descriptors; explicitly render instrument output and feed the existing instrument source buffers. Up to 256 sorted parameter events split the process block at exact sample positions. Equal-offset parameters execute in array order, then MIDI at that sample. Values outside the descriptor range, bad IDs and malformed events reject the entire call before touching state/output. Values are stepped; extension callbacks own any smoothing. Transport discontinuities reset transient state; reset retains current parameters. Callback errors silence the whole output and reset voices/history; already applied parameters persist. Nonfinite/subnormal successful samples become zero. Input/output may alias exactly. `xtc_processor_save`/`restore` run only while quiescent and serialize configuration, not DSP history. Format: `XTCN`, little-endian uint32 version 2, zero-padded 64-byte identifier, uint32 parameter count, zero uint32 reserved, then ordered pairs of uint32 ID and IEEE float32 bits. Header is 80 bytes, pairs 8 bytes. Restore validates exact length, identity, IDs, ranges and defaults/descriptor compatibility before preparing a new instance; failure leaves existing handles intact. Opaque assets and hidden state are outside this ABI. ABI 1 graph nodes remain session-only and fail project saves explicitly; ABI 2 configuration blobs are a separate public facility, not automatically embedded in project files. GUI graph access continues through public C APIs only.