#include "xtc/xtc_dsp.h" #include "xtc/xtc_native.h" #include #include struct xtc_native { xtc_native_descriptor descriptor; void *state; size_t max_frames; uint64_t next_position; }; /** * @brief Checks that a native descriptor is compatible and complete. * * Validates the ABI version and structure size, the kind, the presence of all * callbacks, and that the identifier is a nonempty printable ASCII string. * * @param d Descriptor to validate; must not be NULL. * @return XTC_OK if the descriptor is usable, XTC_ERR_INVALID_ARGUMENT for a * missing descriptor or invalid fields, or XTC_ERR_UNSUPPORTED for an * ABI version or structure-size mismatch. */ xtc_result xtc_native_validate(const xtc_native_descriptor *d) { size_t i; if (!d) return XTC_ERR_INVALID_ARGUMENT; if (d->abi_version != XTC_NATIVE_ABI_VERSION || d->struct_size != sizeof(*d)) return XTC_ERR_UNSUPPORTED; if (d->kind < XTC_NATIVE_EFFECT || d->kind > XTC_NATIVE_INSTRUMENT || !d->prepare || !d->reset || !d->process || !d->destroy || !d->identifier[0]) return XTC_ERR_INVALID_ARGUMENT; for (i = 0; i < sizeof(d->identifier); ++i) { unsigned char c = (unsigned char)d->identifier[i]; if (!c) return XTC_OK; if (c < 33 || c > 126) return XTC_ERR_INVALID_ARGUMENT; } return XTC_ERR_INVALID_ARGUMENT; } /** * @brief Copies a native descriptor and prepares a processor instance on the control thread. * * @param d Native processor descriptor. * @param rate Sample rate in hertz. * @param frames Number of audio frames. * @param value Value to encode, write, or apply. * @param out Receives the newly allocated object; caller owns it on success. * @return XTC_OK on success, or an error code describing the failure. */ xtc_result xtc_native_create(const xtc_native_descriptor *d, double rate, size_t frames, float value, xtc_native **out) { xtc_native *p; xtc_result r; if (!out) return XTC_ERR_INVALID_ARGUMENT; *out = NULL; r = xtc_native_validate(d); if (r != XTC_OK) return r; if (!isfinite(rate) || rate <= 0 || rate > 768000 || !frames || frames > 65536 || !isfinite(value)) return XTC_ERR_INVALID_ARGUMENT; p = calloc(1, sizeof(*p)); if (!p) return XTC_ERR_OUT_OF_MEMORY; p->descriptor = *d; p->max_frames = frames; p->next_position = UINT64_MAX; r = d->prepare(rate, frames, value, &p->state); if (r != XTC_OK) { xtc_native_destroy(p); return r; } p->descriptor.reset(p->state); *out = p; return XTC_OK; } /** * @brief Destroys native state and releases the host wrapper; accepts NULL. * * @param p Native processor instance. */ void xtc_native_destroy(xtc_native *p) { if (p) { p->descriptor.destroy(p->state); free(p); } } /** * @brief Resets native state and transport continuity; accepts NULL. * * @param p Native processor instance. */ void xtc_native_reset(xtc_native *p) { if (p) { p->descriptor.reset(p->state); p->next_position = UINT64_MAX; } } /** * @brief Processes a bounded native block, resetting on discontinuity and silencing callback failures. * * @param p Native processor instance. * @param b Borrowed processing buffers and block metadata. * @return XTC_OK on success, or an error code describing the failure. */ xtc_result xtc_native_process(xtc_native *p, const xtc_native_block *b) { size_t i; xtc_result r; if (!p || !b || !b->output || b->position > UINT64_MAX - b->frames || b->event_count > XTC_MIDI_MAX_EVENTS || (b->event_count && !b->events) || (b->event_count && p->descriptor.kind != XTC_NATIVE_INSTRUMENT)) return XTC_ERR_INVALID_ARGUMENT; if (b->frames > p->max_frames) return XTC_ERR_CAPACITY; for (i = 0; i < b->event_count; ++i) { const xtc_midi_event *e = b->events + i; if (e->offset >= b->frames || (i && e->offset < b->events[i - 1].offset) || e->type < XTC_MIDI_NOTE_ON || e->type > XTC_MIDI_CONTROL || e->channel > 15 || e->pitch > 127 || e->velocity > 127 || (e->type == XTC_MIDI_NOTE_ON && !e->velocity)) return XTC_ERR_INVALID_ARGUMENT; } if (!b->frames) return XTC_OK; if (p->next_position != b->position) p->descriptor.reset(p->state); r = p->descriptor.process(p->state, b); if (r != XTC_OK) { for (i = 0; i < b->frames; ++i) b->output[i] = 0; xtc_native_reset(p); return r; } for (i = 0; i < b->frames; ++i) b->output[i] = xtc_dsp_finite(b->output[i]); p->next_position = b->position + b->frames; return XTC_OK; }