#include "xtc/xtc_processor.h" #include "xtc/xtc_dsp.h" #include #include #include #include struct xtc_processor { xtc_processor_descriptor descriptor; void *state; float values[XTC_PROCESSOR_PARAMETERS]; size_t frames; uint64_t next; }; static int text_valid(const char *s, size_t size, int empty) { size_t i; if (!empty && !s[0]) return 0; for (i = 0; i < size; ++i) { if (!s[i]) return 1; if ((unsigned char)s[i] < 32 || (unsigned char)s[i] > 126) return 0; } return 0; } xtc_result xtc_processor_validate(const xtc_processor_descriptor *d) { size_t i, j; if (!d) return XTC_ERR_INVALID_ARGUMENT; if (d->abi_version != XTC_PROCESSOR_ABI || d->struct_size != sizeof(*d)) return XTC_ERR_UNSUPPORTED; if (!text_valid(d->identifier, 64, 0) || d->kind < XTC_NATIVE_EFFECT || d->kind > XTC_NATIVE_INSTRUMENT || d->parameter_count > XTC_PROCESSOR_PARAMETERS || !d->prepare || !d->reset || !d->process || !d->destroy || (d->parameter_count && !d->parameter)) return XTC_ERR_INVALID_ARGUMENT; for (i = 0; i < d->parameter_count; ++i) { const xtc_parameter_info *p = d->parameters + i; if (!text_valid(p->name, sizeof(p->name), 0) || !text_valid(p->unit, sizeof(p->unit), 1) || !isfinite(p->minimum) || !isfinite(p->maximum) || !isfinite(p->default_value) || p->minimum > p->maximum || p->default_value < p->minimum || p->default_value > p->maximum) return XTC_ERR_INVALID_ARGUMENT; for (j = 0; j < i; ++j) if (d->parameters[j].id == p->id) return XTC_ERR_INVALID_ARGUMENT; } return XTC_OK; } static xtc_result create(const xtc_processor_descriptor *d, double rate, size_t frames, const float *values, xtc_processor **out) { xtc_processor *p; xtc_result result; if (!out) return XTC_ERR_INVALID_ARGUMENT; *out = NULL; result = xtc_processor_validate(d); if (result != XTC_OK) return result; if (!isfinite(rate) || rate <= 0 || rate > 768000 || !frames || frames > 65536) return XTC_ERR_INVALID_ARGUMENT; p = calloc(1, sizeof(*p)); if (!p) return XTC_ERR_OUT_OF_MEMORY; p->descriptor = *d; p->frames = frames; p->next = UINT64_MAX; if (values) memcpy(p->values, values, d->parameter_count * sizeof(float)); else { size_t i; for (i = 0; i < d->parameter_count; ++i) p->values[i] = d->parameters[i].default_value; } result = d->prepare(rate, frames, p->values, d->parameter_count, &p->state); if (result != XTC_OK) { xtc_processor_destroy(p); return result; } d->reset(p->state); *out = p; return XTC_OK; } xtc_result xtc_processor_create(const xtc_processor_descriptor *d, double rate, size_t frames, xtc_processor **out) { return create(d, rate, frames, NULL, out); } void xtc_processor_destroy(xtc_processor *p) { if (p) { p->descriptor.destroy(p->state); free(p); } } void xtc_processor_reset(xtc_processor *p) { if (p) { p->descriptor.reset(p->state); p->next = UINT64_MAX; } } static size_t parameter_index(const xtc_processor_descriptor *d, unsigned int id) { size_t i; for (i = 0; i < d->parameter_count; ++i) if (d->parameters[i].id == id) return i; return SIZE_MAX; } xtc_result xtc_processor_process(xtc_processor *p, const xtc_native_block *b, const xtc_parameter_event *events, size_t count) { size_t i, cursor = 0, param = 0, midi = 0; xtc_result result = XTC_OK; if (!p || !b || !b->output || b->position > UINT64_MAX - b->frames || b->frames > p->frames || count > 256 || (count && !events) || b->event_count > 256 || (b->event_count && !b->events) || (b->event_count && p->descriptor.kind != XTC_NATIVE_INSTRUMENT)) return XTC_ERR_INVALID_ARGUMENT; for (i = 0; i < count; ++i) { size_t index = parameter_index(&p->descriptor, events[i].id); if (index == SIZE_MAX || events[i].offset >= b->frames || (i && events[i].offset < events[i-1].offset) || !isfinite(events[i].value) || events[i].value < p->descriptor.parameters[index].minimum || events[i].value > p->descriptor.parameters[index].maximum) return XTC_ERR_INVALID_ARGUMENT; } 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_BEND || e->pitch > 127 || e->velocity > 127 || e->channel > 15 || (e->type == XTC_MIDI_NOTE_ON && !e->velocity)) return XTC_ERR_INVALID_ARGUMENT; } if (!b->frames) return XTC_OK; if (p->next != b->position) p->descriptor.reset(p->state); while (cursor < b->frames) { xtc_midi_event local[256]; size_t end, n = 0; xtc_native_block segment; while (param < count && events[param].offset == cursor) { size_t index = parameter_index(&p->descriptor, events[param].id); p->values[index] = events[param].value; p->descriptor.parameter(p->state, events[param].id, events[param].value); ++param; } end = param < count ? events[param].offset : b->frames; while (midi < b->event_count && b->events[midi].offset < end) { local[n] = b->events[midi++]; local[n++].offset -= (unsigned int)cursor; } segment = (xtc_native_block){b->input ? b->input + cursor : NULL, b->output + cursor, end - cursor, b->position + cursor, local, n}; result = p->descriptor.process(p->state, &segment); if (result != XTC_OK) { memset(b->output, 0, b->frames * sizeof(float)); xtc_processor_reset(p); return result; } cursor = end; } for (i = 0; i < b->frames; ++i) b->output[i] = xtc_dsp_finite(b->output[i]); p->next = b->position + b->frames; return result; } static void put(unsigned char *p, uint32_t value) { size_t i; for (i = 0; i < 4; ++i) p[i] = (unsigned char)(value >> (i * 8)); } static uint32_t get(const unsigned char *p) { return (uint32_t)p[0] | (uint32_t)p[1]<<8 | (uint32_t)p[2]<<16 | (uint32_t)p[3]<<24; } xtc_result xtc_processor_save(const xtc_processor *p, void *data, size_t capacity, size_t *written) { unsigned char *bytes = data; size_t i, size; if (!p || !written) return XTC_ERR_INVALID_ARGUMENT; size = 80 + p->descriptor.parameter_count * 8; *written = size; if (!data || capacity < size) return XTC_ERR_CAPACITY; if (sizeof(float) != 4 || FLT_RADIX != 2 || FLT_MANT_DIG != 24 || FLT_MAX_EXP != 128) return XTC_ERR_UNSUPPORTED; memset(bytes, 0, size); memcpy(bytes, "XTCN", 4); put(bytes+4, 2); memcpy(bytes+8, p->descriptor.identifier, strlen(p->descriptor.identifier)); put(bytes+72, (uint32_t)p->descriptor.parameter_count); for (i = 0; i < p->descriptor.parameter_count; ++i) { uint32_t bits; memcpy(&bits, p->values+i, 4); put(bytes+80+i*8, p->descriptor.parameters[i].id); put(bytes+84+i*8, bits); } return XTC_OK; } xtc_result xtc_processor_restore(const xtc_processor_descriptor *d, double rate, size_t frames, const void *data, size_t size, xtc_processor **out) { const unsigned char *bytes = data; float values[XTC_PROCESSOR_PARAMETERS] = {0}; size_t i, j; xtc_result result; if (!out) return XTC_ERR_INVALID_ARGUMENT; *out = NULL; result = xtc_processor_validate(d); if (result != XTC_OK) return result; if (sizeof(float) != 4 || FLT_RADIX != 2 || FLT_MANT_DIG != 24 || FLT_MAX_EXP != 128) return XTC_ERR_UNSUPPORTED; if (!bytes || size != 80 + 8*d->parameter_count || memcmp(bytes, "XTCN", 4) || get(bytes+4) != 2 || !memchr(bytes+8, 0, 64) || strcmp((const char *)bytes+8, d->identifier) || get(bytes+72) != d->parameter_count || get(bytes+76)) return XTC_ERR_FORMAT; for (i = 0; i < d->parameter_count; ++i) { uint32_t bits = get(bytes+84+i*8); if (get(bytes+80+i*8) != d->parameters[i].id) return XTC_ERR_UNSUPPORTED; memcpy(values+i, &bits, 4); if (!isfinite(values[i]) || values[i] < d->parameters[i].minimum || values[i] > d->parameters[i].maximum) return XTC_ERR_FORMAT; } for (j = 8 + strlen((const char *)bytes+8); j < 72; ++j) if (bytes[j]) return XTC_ERR_FORMAT; return create(d, rate, frames, values, out); }