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/>.
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
);
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");
51 p
->external_output_set
= FALSE
;
53 cbox_command_target_init(&p
->cmd_target
, cbox_track_process_cmd
, p
);
54 CBOX_OBJECT_REGISTER(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
));
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
)
74 // all items earlier than the new one -> append
77 track
->items
= g_list_append(track
->items
, item
);
78 CBOX_OBJECT_REGISTER(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
);
88 void cbox_track_destroyfunc(struct cbox_objhdr
*objhdr
)
90 struct cbox_track
*track
= CBOX_H2O(objhdr
);
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
95 cbox_track_playback_destroy(track
->pb
);
96 // The items will unlink themselves from the list in destructor
98 cbox_object_destroy(track
->items
->data
);
99 g_free((gchar
*)track
->name
);
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
))
111 GList
*it
= track
->items
;
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
))
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);
131 g_set_error(error
, CBOX_MODULE_ERROR
, CBOX_MODULE_ERROR_FAILED
, "Invalid pattern position %d (cannot be negative)", pos
);
136 g_set_error(error
, CBOX_MODULE_ERROR
, CBOX_MODULE_ERROR_FAILED
, "Invalid pattern offset %d (cannot be negative)", offset
);
141 g_set_error(error
, CBOX_MODULE_ERROR
, CBOX_MODULE_ERROR_FAILED
, "Invalid pattern length %d (must be positive)", length
);
144 struct cbox_objhdr
*pattern
= CBOX_ARG_O(cmd
, 3, track
, cbox_midi_pattern
, error
);
147 struct cbox_midi_pattern
*mp
= CBOX_H2O(pattern
);
148 struct cbox_track_item
*trki
= cbox_track_add_item(track
, pos
, mp
, offset
, length
);
150 return cbox_execute_on(fb
, NULL
, "/uuid", "o", error
, trki
);
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));
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
;
168 track
->external_output_set
= FALSE
;
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
))
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
));
194 return cbox_object_default_process_cmd(ct
, fb
, cmd
, error
);