#include "../project/project_internal.h" #include #include /** * @brief Finds the dense index of a project track by stable ID. * * @param p Project to search; must not be NULL. * @param id Stable track identifier. * @return Dense track index, or SIZE_MAX when no track has the ID. */ static size_t find_track(const xtc_project *p, uint64_t id) { size_t i; for (i = 0; i < p->track_count; ++i) if (p->tracks[i].id == id) return i; return SIZE_MAX; } /** * @brief Sets a track's gain, pan, mute, and solo controls after validation. * * @param p Project model; borrowed for this call. * @param id Stable identifier of the object to find or edit. * @param gain Linear gain, 0 to 4 for track controls. * @param pan Pan position from -1 (left) to 1 (right). * @param mute Track mute flag, 0 or 1. * @param solo Track solo flag, 0 or 1. * @return XTC_OK on success, or an error code describing the failure. */ xtc_result xtc_project_set_mix(xtc_project *p, uint64_t id, float gain, float pan, int mute, int solo) { size_t i; if (!p || !isfinite(gain) || gain < 0 || gain > 4 || !isfinite(pan) || pan < -1 || pan > 1 || (mute != 0 && mute != 1) || (solo != 0 && solo != 1)) return XTC_ERR_INVALID_ARGUMENT; i = find_track(p, id); if (i == SIZE_MAX) return XTC_ERR_NOT_FOUND; p->tracks[i].gain = gain; p->tracks[i].pan = pan; p->tracks[i].mute = mute; p->tracks[i].solo = solo; return XTC_OK; } /** * @brief Inserts an automation point, replacing any point at the same track, parameter, and tick. * * @param p Project model; borrowed for this call. * @param a Automation point to read or update. * @return XTC_OK on success, or an error code describing the failure. */ xtc_result xtc_project_set_automation(xtc_project *p, const xtc_automation_point *a) { size_t i; if (!p || !a || !isfinite(a->value) || a->tick > XTC_PROJECT_MAX_TICK || (a->parameter != XTC_AUTOMATION_GAIN && a->parameter != XTC_AUTOMATION_PAN) || a->value < (a->parameter == XTC_AUTOMATION_GAIN ? 0 : -1) || a->value > (a->parameter == XTC_AUTOMATION_GAIN ? 4 : 1)) return XTC_ERR_INVALID_ARGUMENT; if (find_track(p, a->track_id) == SIZE_MAX) return XTC_ERR_NOT_FOUND; for (i = 0; i < p->automation_count; ++i) if (p->automation[i].track_id == a->track_id && p->automation[i].parameter == a->parameter && p->automation[i].tick == a->tick) break; if (i == XTC_MAX_AUTOMATION_POINTS) return XTC_ERR_CAPACITY; if (i == p->automation_count) ++p->automation_count; p->automation[i] = *a; return XTC_OK; } /** * @brief Removes the automation point at the specified track, parameter, and tick. * * @param p Project model; borrowed for this call. * @param id Stable identifier of the object to find or edit. * @param param Automation lane selector. * @param tick Musical position in ticks. * @return XTC_OK on success, or an error code describing the failure. */ xtc_result xtc_project_remove_automation(xtc_project *p, uint64_t id, xtc_automation_parameter param, uint64_t tick) { size_t i; if (!p || (param != XTC_AUTOMATION_GAIN && param != XTC_AUTOMATION_PAN) || tick > XTC_PROJECT_MAX_TICK) return XTC_ERR_INVALID_ARGUMENT; for (i = 0; i < p->automation_count; ++i) if (p->automation[i].track_id == id && p->automation[i].parameter == param && p->automation[i].tick == tick) break; if (i == p->automation_count) return XTC_ERR_NOT_FOUND; memmove(p->automation + i, p->automation + i + 1, (--p->automation_count - i) * sizeof(*p->automation)); return XTC_OK; } /** * @brief Copies the number of automation points in the project. * * @param p Project model; borrowed for this call. * @param n Receives the item count. * @return XTC_OK on success, or an error code describing the failure. */ xtc_result xtc_project_automation_count(const xtc_project *p, size_t *n) { if (!p || !n) return XTC_ERR_INVALID_ARGUMENT; *n = p->automation_count; return XTC_OK; } /** * @brief Copies an automation point selected by dense list index. * * @param p Project model; borrowed for this call. * @param i Zero-based index in the dense list. * @param a Automation point to read or update. * @return XTC_OK on success, or an error code describing the failure. */ xtc_result xtc_project_get_automation(const xtc_project *p, size_t i, xtc_automation_point *a) { if (!p || !a) return XTC_ERR_INVALID_ARGUMENT; if (i >= p->automation_count) return XTC_ERR_NOT_FOUND; *a = p->automation[i]; return XTC_OK; }