Merge pull request #1 from atsampson/master
[calfbox.git] / track.c
blob3d785bdbcaef184b52d72528268dd78301729bf7
1 /*
2 Calf Box, an open source musical instrument.
3 Copyright (C) 2010-2011 Krzysztof Foltman
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "errors.h"
20 #include "master.h"
21 #include "pattern.h"
22 #include "rt.h"
23 #include "seq.h"
24 #include "track.h"
25 #include "song.h"
26 #include <assert.h>
27 #include <malloc.h>
29 CBOX_CLASS_DEFINITION_ROOT(cbox_track)
30 CBOX_CLASS_DEFINITION_ROOT(cbox_track_item)
32 static gboolean cbox_track_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error);
33 static gboolean cbox_track_item_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error);
35 void cbox_track_item_destroyfunc(struct cbox_objhdr *hdr)
37 struct cbox_track_item *item = CBOX_H2O(hdr);
38 item->owner->items = g_list_remove(item->owner->items, item);
39 free(item);
42 struct cbox_track *cbox_track_new(struct cbox_document *document)
44 struct cbox_track *p = malloc(sizeof(struct cbox_track));
45 CBOX_OBJECT_HEADER_INIT(p, cbox_track, document);
47 p->name = g_strdup("Unnamed");
48 p->items = NULL;
49 p->pb = NULL;
50 p->owner = NULL;
51 p->external_output_set = FALSE;
53 cbox_command_target_init(&p->cmd_target, cbox_track_process_cmd, p);
54 CBOX_OBJECT_REGISTER(p);
55 return p;
58 #define CBTI(it) ((struct cbox_track_item *)(it)->data)
60 struct cbox_track_item *cbox_track_add_item(struct cbox_track *track, uint32_t time, struct cbox_midi_pattern *pattern, uint32_t offset, uint32_t length)
62 struct cbox_track_item *item = malloc(sizeof(struct cbox_track_item));
63 CBOX_OBJECT_HEADER_INIT(item, cbox_track_item, CBOX_GET_DOCUMENT(track));
64 item->owner = track;
65 item->time = time;
66 item->pattern = pattern;
67 item->offset = offset;
68 item->length = length;
69 cbox_command_target_init(&item->cmd_target, cbox_track_item_process_cmd, item);
71 GList *it = track->items;
72 while(it != NULL && CBTI(it)->time < item->time)
73 it = g_list_next(it);
74 // all items earlier than the new one -> append
75 if (it == NULL)
77 track->items = g_list_append(track->items, item);
78 CBOX_OBJECT_REGISTER(item);
79 return item;
81 // Here, I don't really care about overlaps - it's more important to preserve
82 // all clips as sent by the caller.
83 track->items = g_list_insert_before(track->items, it, item);
84 CBOX_OBJECT_REGISTER(item);
85 return item;
88 void cbox_track_destroyfunc(struct cbox_objhdr *objhdr)
90 struct cbox_track *track = CBOX_H2O(objhdr);
91 if (track->owner)
92 cbox_song_remove_track(track->owner, track);
93 // XXXKF I'm not sure if I want the lifecycle of track playback objects to be managed by the track itself
94 if (track->pb)
95 cbox_track_playback_destroy(track->pb);
96 // The items will unlink themselves from the list in destructor
97 while(track->items)
98 cbox_object_destroy(track->items->data);
99 g_free((gchar *)track->name);
100 free(track);
103 gboolean cbox_track_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
105 struct cbox_track *track = ct->user_data;
106 if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
108 if (!cbox_check_fb_channel(fb, cmd->command, error))
109 return FALSE;
111 GList *it = track->items;
112 while(it != NULL)
114 struct cbox_track_item *trki = it->data;
115 if (!cbox_execute_on(fb, NULL, "/clip", "iiioo", error, trki->time, trki->offset, trki->length, trki->pattern, trki))
116 return FALSE;
117 it = g_list_next(it);
120 return cbox_execute_on(fb, NULL, "/name", "s", error, track->name) &&
121 (track->external_output_set ? cbox_uuid_report_as(&track->external_output, "/external_output", fb, error) : TRUE) &&
122 CBOX_OBJECT_DEFAULT_STATUS(track, fb, error);
124 else if (!strcmp(cmd->command, "/add_clip") && !strcmp(cmd->arg_types, "iiis"))
126 int pos = CBOX_ARG_I(cmd, 0);
127 int offset = CBOX_ARG_I(cmd, 1);
128 int length = CBOX_ARG_I(cmd, 2);
129 if (pos < 0)
131 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid pattern position %d (cannot be negative)", pos);
132 return FALSE;
134 if (offset < 0)
136 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid pattern offset %d (cannot be negative)", offset);
137 return FALSE;
139 if (length <= 0)
141 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid pattern length %d (must be positive)", length);
142 return FALSE;
144 struct cbox_objhdr *pattern = CBOX_ARG_O(cmd, 3, track, cbox_midi_pattern, error);
145 if (!pattern)
146 return FALSE;
147 struct cbox_midi_pattern *mp = CBOX_H2O(pattern);
148 struct cbox_track_item *trki = cbox_track_add_item(track, pos, mp, offset, length);
149 if (fb)
150 return cbox_execute_on(fb, NULL, "/uuid", "o", error, trki);
151 return TRUE;
153 else if (!strcmp(cmd->command, "/name") && !strcmp(cmd->arg_types, "s"))
155 char *old_name = track->name;
156 track->name = g_strdup(CBOX_ARG_S(cmd, 0));
157 g_free(old_name);
158 return TRUE;
160 else if (!strcmp(cmd->command, "/external_output") && !strcmp(cmd->arg_types, "s"))
162 if (*CBOX_ARG_S(cmd, 0))
164 if (cbox_uuid_fromstring(&track->external_output, CBOX_ARG_S(cmd, 0), error))
165 track->external_output_set = TRUE;
167 else
168 track->external_output_set = FALSE;
169 return TRUE;
171 else
172 return cbox_object_default_process_cmd(ct, fb, cmd, error);
175 gboolean cbox_track_item_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
177 struct cbox_track_item *trki = ct->user_data;
178 if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
180 if (!cbox_check_fb_channel(fb, cmd->command, error))
181 return FALSE;
183 return cbox_execute_on(fb, NULL, "/pos", "i", error, trki->time) &&
184 cbox_execute_on(fb, NULL, "/offset", "i", error, trki->offset) &&
185 cbox_execute_on(fb, NULL, "/length", "i", error, trki->length) &&
186 cbox_execute_on(fb, NULL, "/pattern", "o", error, trki->pattern) &&
187 CBOX_OBJECT_DEFAULT_STATUS(trki, fb, error);
189 if (!strcmp(cmd->command, "/delete") && !strcmp(cmd->arg_types, ""))
191 cbox_object_destroy(CBOX_O2H(trki));
192 return TRUE;
194 return cbox_object_default_process_cmd(ct, fb, cmd, error);