Start with empty scene as last resort option.
[calfbox.git] / track.c
blobda6c30a657debe71e2e097dd57980c781d5bd854
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;
52 cbox_command_target_init(&p->cmd_target, cbox_track_process_cmd, p);
53 CBOX_OBJECT_REGISTER(p);
54 return p;
57 #define CBTI(it) ((struct cbox_track_item *)(it)->data)
59 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)
61 struct cbox_track_item *item = malloc(sizeof(struct cbox_track_item));
62 CBOX_OBJECT_HEADER_INIT(item, cbox_track_item, CBOX_GET_DOCUMENT(track));
63 item->owner = track;
64 item->time = time;
65 item->pattern = pattern;
66 item->offset = offset;
67 item->length = length;
68 cbox_command_target_init(&item->cmd_target, cbox_track_item_process_cmd, item);
70 GList *it = track->items;
71 while(it != NULL && CBTI(it)->time < item->time)
72 it = g_list_next(it);
73 // all items earlier than the new one -> append
74 if (it == NULL)
76 track->items = g_list_append(track->items, item);
77 CBOX_OBJECT_REGISTER(item);
78 return item;
80 // Here, I don't really care about overlaps - it's more important to preserve
81 // all clips as sent by the caller.
82 track->items = g_list_insert_before(track->items, it, item);
83 CBOX_OBJECT_REGISTER(item);
84 return item;
87 void cbox_track_destroyfunc(struct cbox_objhdr *objhdr)
89 struct cbox_track *track = CBOX_H2O(objhdr);
90 if (track->owner)
91 cbox_song_remove_track(track->owner, track);
92 // XXXKF I'm not sure if I want the lifecycle of track playback objects to be managed by the track itself
93 if (track->pb)
94 cbox_track_playback_destroy(track->pb);
95 // The items will unlink themselves from the list in destructor
96 while(track->items)
97 cbox_object_destroy(track->items->data);
98 g_free((gchar *)track->name);
99 free(track);
102 gboolean cbox_track_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
104 struct cbox_track *track = ct->user_data;
105 if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
107 if (!cbox_check_fb_channel(fb, cmd->command, error))
108 return FALSE;
110 GList *it = track->items;
111 while(it != NULL)
113 struct cbox_track_item *trki = it->data;
114 if (!cbox_execute_on(fb, NULL, "/clip", "iiioo", error, trki->time, trki->offset, trki->length, trki->pattern, trki))
115 return FALSE;
116 it = g_list_next(it);
119 return cbox_execute_on(fb, NULL, "/name", "s", error, track->name) &&
120 CBOX_OBJECT_DEFAULT_STATUS(track, fb, error);
122 else if (!strcmp(cmd->command, "/add_clip") && !strcmp(cmd->arg_types, "iiis"))
124 int pos = CBOX_ARG_I(cmd, 0);
125 int offset = CBOX_ARG_I(cmd, 1);
126 int length = CBOX_ARG_I(cmd, 2);
127 if (pos < 0)
129 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid pattern position %d (cannot be negative)", pos);
130 return FALSE;
132 if (offset < 0)
134 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid pattern offset %d (cannot be negative)", offset);
135 return FALSE;
137 if (length <= 0)
139 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid pattern length %d (must be positive)", length);
140 return FALSE;
142 struct cbox_objhdr *pattern = CBOX_ARG_O(cmd, 3, track, cbox_midi_pattern, error);
143 if (!pattern)
144 return FALSE;
145 struct cbox_midi_pattern *mp = CBOX_H2O(pattern);
146 struct cbox_track_item *trki = cbox_track_add_item(track, pos, mp, offset, length);
147 if (fb)
148 return cbox_execute_on(fb, NULL, "/uuid", "o", error, trki);
149 return TRUE;
151 else if (!strcmp(cmd->command, "/name") && !strcmp(cmd->arg_types, "s"))
153 char *old_name = track->name;
154 track->name = g_strdup(CBOX_ARG_S(cmd, 0));
155 g_free(old_name);
156 return TRUE;
158 else
159 return cbox_object_default_process_cmd(ct, fb, cmd, error);
162 gboolean cbox_track_item_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
164 struct cbox_track_item *trki = ct->user_data;
165 if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
167 if (!cbox_check_fb_channel(fb, cmd->command, error))
168 return FALSE;
170 return cbox_execute_on(fb, NULL, "/pos", "i", error, trki->time) &&
171 cbox_execute_on(fb, NULL, "/offset", "i", error, trki->offset) &&
172 cbox_execute_on(fb, NULL, "/length", "i", error, trki->length) &&
173 cbox_execute_on(fb, NULL, "/pattern", "o", error, trki->pattern) &&
174 CBOX_OBJECT_DEFAULT_STATUS(trki, fb, error);
176 if (!strcmp(cmd->command, "/delete") && !strcmp(cmd->arg_types, ""))
178 cbox_object_destroy(CBOX_O2H(trki));
179 return TRUE;
181 return cbox_object_default_process_cmd(ct, fb, cmd, error);