xtc/src/native/native.c

146 lines
4.7 KiB
C
Raw Normal View History

#include "xtc/xtc_dsp.h"
#include "xtc/xtc_native.h"
#include <math.h>
#include <stdlib.h>
2026-10-27 01:37:21 +01:00
struct xtc_native {
xtc_native_descriptor descriptor;
void *state;
size_t max_frames;
uint64_t next_position;
};
2026-10-27 01:37:21 +01:00
/**
* @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;
2026-10-27 01:37:21 +01:00
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;
}
2026-10-27 01:37:21 +01:00
/**
* @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.
*/
2026-10-27 01:37:21 +01:00
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;
2026-10-27 01:37:21 +01:00
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;
}
2026-10-27 01:37:21 +01:00
/**
* @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);
}
}
2026-10-27 01:37:21 +01:00
/**
* @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;
}
}
2026-10-27 01:37:21 +01:00
/**
* @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;
2026-10-27 01:37:21 +01:00
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;
2026-10-27 01:37:21 +01:00
if (e->offset >= b->frames || (i && e->offset < b->events[i - 1].offset) || e->type < XTC_MIDI_NOTE_ON ||
e->type > XTC_MIDI_ALL_OFF || 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;
}