#include "../project/project_internal.h" #include "../graph/graph_internal.h" #include xtc_result xtc_project_add_midi_clip(xtc_project *p, uint64_t track, uint64_t start, uint64_t length, uint64_t *out) { xtc_project *c = NULL; xtc_result r; size_t i, source = SIZE_MAX, output = SIZE_MAX; uint64_t id; if (!p || !out) return XTC_ERR_INVALID_ARGUMENT; r = xtc_project_clone(p, &c); if (r == XTC_OK) r = xtc_project_add_clip(c, track, "MIDI", start, length, &id); if (r == XTC_OK) { c->clips[c->clip_count - 1].midi = 1; for (i = 0; i < c->graph->node_count; ++i) { xtc_node *n = c->graph->nodes + i; if (n->type == XTC_NODE_INSTRUMENT) source = n->id; if (n->type == XTC_NODE_OUTPUT) output = n->id; } if (source == SIZE_MAX) { r = xtc_graph_add_instrument(c->graph, &source); if (r == XTC_OK && output == SIZE_MAX) r = xtc_graph_add_output(c->graph, &output); if (r == XTC_OK) r = xtc_graph_connect(c->graph, source, output); } } if (r == XTC_OK) { xtc_graph *old = p->graph; *p = *c; c->graph = old; *out = id; } xtc_project_destroy(c); return r; } xtc_result xtc_project_note_count(const xtc_project *p, size_t *out) { if (!p || !out) return XTC_ERR_INVALID_ARGUMENT; *out = p->note_count; return XTC_OK; } xtc_result xtc_project_get_note(const xtc_project *p, size_t i, xtc_note_info *out) { if (!p || !out) return XTC_ERR_INVALID_ARGUMENT; if (i >= p->note_count) return XTC_ERR_NOT_FOUND; *out = p->notes[i]; return XTC_OK; } static xtc_result valid_note(const xtc_project *p, uint64_t clip, uint64_t start, uint64_t length, unsigned int pitch, unsigned int velocity, unsigned int channel) { xtc_clip_info c; if (pitch > 127 || !velocity || velocity > 127 || channel > 15 || !length) return XTC_ERR_INVALID_ARGUMENT; if (xtc_project_find_clip(p, clip, &c) != XTC_OK) return XTC_ERR_NOT_FOUND; if (!c.midi) return XTC_ERR_INVALID_STATE; if (start >= c.source_length_tick || length > c.source_length_tick - start) return XTC_ERR_INVALID_ARGUMENT; return XTC_OK; } xtc_result xtc_project_add_note(xtc_project *p, uint64_t clip, uint64_t start, uint64_t length, unsigned int pitch, unsigned int velocity, unsigned int channel, uint64_t *out) { xtc_result r; xtc_note_info n; if (!p || !out) return XTC_ERR_INVALID_ARGUMENT; r = valid_note(p, clip, start, length, pitch, velocity, channel); if (r != XTC_OK) return r; if (p->note_count == XTC_PROJECT_MAX_NOTES || p->next_note_id == UINT64_MAX) return XTC_ERR_CAPACITY; memset(&n, 0, sizeof(n)); n.id = p->next_note_id++; n.clip_id = clip; n.start_tick = start; n.length_tick = length; n.pitch = pitch; n.velocity = velocity; n.channel = channel; p->notes[p->note_count++] = n; *out = n.id; return XTC_OK; } xtc_result xtc_project_edit_note(xtc_project *p, uint64_t id, uint64_t start, uint64_t length, unsigned int pitch, unsigned int velocity, unsigned int channel) { size_t i; xtc_result r; if (!p) return XTC_ERR_INVALID_ARGUMENT; for (i = 0; i < p->note_count; ++i) if (p->notes[i].id == id) break; if (i == p->note_count) return XTC_ERR_NOT_FOUND; r = valid_note(p, p->notes[i].clip_id, start, length, pitch, velocity, channel); if (r != XTC_OK) return r; p->notes[i].start_tick = start; p->notes[i].length_tick = length; p->notes[i].pitch = pitch; p->notes[i].velocity = velocity; p->notes[i].channel = channel; return XTC_OK; } xtc_result xtc_project_remove_note(xtc_project *p, uint64_t id) { size_t i; if (!p) return XTC_ERR_INVALID_ARGUMENT; for (i = 0; i < p->note_count; ++i) if (p->notes[i].id == id) break; if (i == p->note_count) return XTC_ERR_NOT_FOUND; memmove(p->notes + i, p->notes + i + 1, (p->note_count - i - 1) * sizeof(*p->notes)); --p->note_count; return XTC_OK; }