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/>.
27 CBOX_CLASS_DEFINITION_ROOT(cbox_song
)
29 /////////////////////////////////////////////////////////////////////////////////////////////////
31 void cbox_master_track_item_destroy(struct cbox_master_track_item
*item
)
36 /////////////////////////////////////////////////////////////////////////////////////////////////
38 gboolean
cbox_song_process_cmd(struct cbox_command_target
*ct
, struct cbox_command_target
*fb
, struct cbox_osc_command
*cmd
, GError
**error
)
40 struct cbox_song
*song
= ct
->user_data
;
41 if (!strcmp(cmd
->command
, "/status") && !strcmp(cmd
->arg_types
, ""))
43 if (!cbox_check_fb_channel(fb
, cmd
->command
, error
))
46 for(GList
*p
= song
->tracks
; p
; p
= g_list_next(p
))
48 struct cbox_track
*trk
= p
->data
;
49 if (!cbox_execute_on(fb
, NULL
, "/track", "sio", error
, trk
->name
, g_list_length(trk
->items
), trk
))
52 for(GList
*p
= song
->patterns
; p
; p
= g_list_next(p
))
54 struct cbox_midi_pattern
*pat
= p
->data
;
55 if (!cbox_execute_on(fb
, NULL
, "/pattern", "sio", error
, pat
->name
, pat
->loop_end
, pat
))
59 for(GList
*p
= song
->master_track_items
; p
; p
= g_list_next(p
))
61 struct cbox_master_track_item
*mti
= p
->data
;
62 if (!cbox_execute_on(fb
, NULL
, "/mti", "ifii", error
, pos
, mti
->tempo
, mti
->timesig_nom
, mti
->timesig_denom
))
64 pos
+= mti
->duration_ppqn
;
66 return cbox_execute_on(fb
, NULL
, "/loop_start", "i", error
, (int)song
->loop_start_ppqn
) &&
67 cbox_execute_on(fb
, NULL
, "/loop_end", "i", error
, (int)song
->loop_end_ppqn
) &&
68 CBOX_OBJECT_DEFAULT_STATUS(song
, fb
, error
);
71 if (!strcmp(cmd
->command
, "/set_loop") && !strcmp(cmd
->arg_types
, "ii"))
73 song
->loop_start_ppqn
= CBOX_ARG_I(cmd
, 0);
74 song
->loop_end_ppqn
= CBOX_ARG_I(cmd
, 1);
78 if (!strcmp(cmd
->command
, "/set_mti") && !strcmp(cmd
->arg_types
, "ifii"))
80 cbox_song_set_mti(song
, CBOX_ARG_I(cmd
, 0), CBOX_ARG_F(cmd
, 1), CBOX_ARG_I(cmd
, 2), CBOX_ARG_I(cmd
, 3));
84 if (!strcmp(cmd
->command
, "/clear") && !strcmp(cmd
->arg_types
, ""))
86 cbox_song_clear(song
);
90 if (!strcmp(cmd
->command
, "/add_track") && !strcmp(cmd
->arg_types
, ""))
92 if (!cbox_check_fb_channel(fb
, cmd
->command
, error
))
95 struct cbox_track
*track
= cbox_track_new(CBOX_GET_DOCUMENT(song
));
96 cbox_song_add_track(song
, track
);
97 if (!cbox_execute_on(fb
, NULL
, "/uuid", "o", error
, track
))
106 if (!strcmp(cmd
->command
, "/load_pattern") && !strcmp(cmd
->arg_types
, "si"))
108 if (!cbox_check_fb_channel(fb
, cmd
->command
, error
))
111 struct cbox_midi_pattern
*pattern
= cbox_midi_pattern_load(song
, CBOX_ARG_S(cmd
, 0), CBOX_ARG_I(cmd
, 1), app
.engine
->master
->ppqn_factor
);
112 if (!cbox_execute_on(fb
, NULL
, "/uuid", "o", error
, pattern
))
114 CBOX_DELETE(pattern
);
121 if (!strcmp(cmd
->command
, "/load_track") && !strcmp(cmd
->arg_types
, "si"))
123 if (!cbox_check_fb_channel(fb
, cmd
->command
, error
))
126 struct cbox_midi_pattern
*pattern
= cbox_midi_pattern_load_track(song
, CBOX_ARG_S(cmd
, 0), CBOX_ARG_I(cmd
, 1), app
.engine
->master
->ppqn_factor
);
127 if (!cbox_execute_on(fb
, NULL
, "/uuid", "o", error
, pattern
))
129 CBOX_DELETE(pattern
);
136 if (!strcmp(cmd
->command
, "/load_metronome") && !strcmp(cmd
->arg_types
, "i"))
138 if (!cbox_check_fb_channel(fb
, cmd
->command
, error
))
141 struct cbox_midi_pattern
*pattern
= cbox_midi_pattern_new_metronome(song
, CBOX_ARG_I(cmd
, 0), app
.engine
->master
->ppqn_factor
);
142 if (!cbox_execute_on(fb
, NULL
, "/uuid", "o", error
, pattern
))
144 CBOX_DELETE(pattern
);
151 if (!strcmp(cmd
->command
, "/load_blob") && !strcmp(cmd
->arg_types
, "bi"))
153 if (!cbox_check_fb_channel(fb
, cmd
->command
, error
))
156 struct cbox_midi_pattern
*pattern
= cbox_midi_pattern_new_from_blob(song
, CBOX_ARG_B(cmd
, 0), CBOX_ARG_I(cmd
, 1), app
.engine
->master
->ppqn_factor
);
157 if (!cbox_execute_on(fb
, NULL
, "/uuid", "o", error
, pattern
))
159 CBOX_DELETE(pattern
);
166 return cbox_object_default_process_cmd(ct
, fb
, cmd
, error
);
171 /////////////////////////////////////////////////////////////////////////////////////////////////
173 struct cbox_song
*cbox_song_new(struct cbox_document
*document
)
175 struct cbox_song
*p
= calloc(1, sizeof(struct cbox_song
));
176 CBOX_OBJECT_HEADER_INIT(p
, cbox_song
, document
);
177 p
->master_track_items
= NULL
;
180 p
->lyrics_sheet
= NULL
;
181 p
->chord_sheet
= NULL
;
182 p
->loop_start_ppqn
= 0;
183 p
->loop_end_ppqn
= 0;
184 cbox_command_target_init(&p
->cmd_target
, cbox_song_process_cmd
, p
);
185 CBOX_OBJECT_REGISTER(p
);
190 void cbox_song_set_mti(struct cbox_song
*song
, uint32_t pos
, double tempo
, int timesig_nom
, int timesig_denom
)
192 uint32_t tstart
= 0, tend
= 0;
195 if (tempo
< 0 && timesig_nom
< 0)
197 gboolean is_noop
= tempo
== 0 && timesig_nom
== 0;
199 struct cbox_master_track_item
*mti
= NULL
;
200 for(GList
*p
= song
->master_track_items
; p
; p
= g_list_next(p
))
203 tend
= tstart
+ mti
->duration_ppqn
;
204 // printf("range %d-%d %f %d\n", tstart, tend, mti->tempo, mti->timesig_nom);
207 double new_tempo
= tempo
>= 0 ? tempo
: mti
->tempo
;
208 int new_timesig_nom
= timesig_nom
>= 0 ? timesig_nom
: mti
->timesig_nom
;
209 // Is this operation going to become a no-op after the change?
210 gboolean is_noop_here
= new_tempo
<= 0 && new_timesig_nom
<= 0;
211 // If the new item is a no-op and not the first item, delete it
212 // and extend the previous item by deleted item's duration
213 if (is_noop_here
&& prev
)
215 uint32_t deleted_duration
= mti
->duration_ppqn
;
216 song
->master_track_items
= g_list_remove(song
->master_track_items
, mti
);
218 mti
->duration_ppqn
+= deleted_duration
;
223 if (pos
>= tstart
&& pos
< tend
)
225 if (is_noop
|| (tempo
<= 0 && timesig_nom
<= 0))
227 // Split old item's duration
228 mti
->duration_ppqn
= pos
- tstart
;
229 mti
= calloc(1, sizeof(struct cbox_master_track_item
));
230 mti
->duration_ppqn
= tend
- pos
;
232 song
->master_track_items
= g_list_insert_before(song
->master_track_items
, p
, mti
);
238 // The new item is a no-op and it's not deleting any of the current MTIs.
242 // The add position is past the end of the current MTIs.
245 // Either extend the previous item, if there's any
249 mti
->duration_ppqn
+= pos
- tend
;
253 // ... or add a dummy 'pad' item
254 mti
= calloc(1, sizeof(struct cbox_master_track_item
));
255 mti
->duration_ppqn
= pos
;
256 assert(!song
->master_track_items
);
257 song
->master_track_items
= g_list_append(song
->master_track_items
, mti
);
258 prev
= song
->master_track_items
;
261 // Add the new item at the end
262 mti
= calloc(1, sizeof(struct cbox_master_track_item
));
263 song
->master_track_items
= g_list_append(song
->master_track_items
, mti
);
268 if ((timesig_nom
> 0 && timesig_denom
> 0) ||
269 (timesig_nom
== 0 && timesig_denom
== 0))
271 mti
->timesig_nom
= timesig_nom
;
272 mti
->timesig_denom
= timesig_denom
;
276 void cbox_song_add_track(struct cbox_song
*song
, struct cbox_track
*track
)
279 song
->tracks
= g_list_append(song
->tracks
, track
);
282 void cbox_song_remove_track(struct cbox_song
*song
, struct cbox_track
*track
)
284 assert(track
->owner
== song
);
285 song
->tracks
= g_list_remove(song
->tracks
, track
);
289 void cbox_song_add_pattern(struct cbox_song
*song
, struct cbox_midi_pattern
*pattern
)
291 pattern
->owner
= song
;
292 song
->patterns
= g_list_append(song
->patterns
, pattern
);
295 void cbox_song_remove_pattern(struct cbox_song
*song
, struct cbox_midi_pattern
*pattern
)
297 assert(pattern
->owner
== song
);
298 pattern
->owner
= NULL
;
299 song
->patterns
= g_list_remove(song
->patterns
, pattern
);
302 void cbox_song_clear(struct cbox_song
*song
)
305 cbox_object_destroy(song
->tracks
->data
);
306 while(song
->patterns
)
307 cbox_object_destroy(song
->patterns
->data
);
308 while(song
->master_track_items
)
310 struct cbox_master_track_item
*mti
= song
->master_track_items
->data
;
311 song
->master_track_items
= g_list_remove(song
->master_track_items
, mti
);
312 cbox_master_track_item_destroy(mti
);
316 void cbox_song_use_looped_pattern(struct cbox_song
*song
, struct cbox_midi_pattern
*pattern
)
318 assert(pattern
->owner
== song
);
319 song
->patterns
= g_list_remove(song
->patterns
, pattern
);
320 pattern
->owner
= NULL
;
322 cbox_song_clear(song
);
323 struct cbox_track
*trk
= cbox_track_new(CBOX_GET_DOCUMENT(song
));
324 cbox_song_add_track(song
, trk
);
325 cbox_song_add_pattern(song
, pattern
);
326 song
->loop_start_ppqn
= 0;
327 song
->loop_end_ppqn
= pattern
->loop_end
;
328 cbox_track_add_item(trk
, 0, pattern
, 0, pattern
->loop_end
);
329 cbox_engine_update_song_playback(app
.engine
);
332 void cbox_song_destroyfunc(struct cbox_objhdr
*objhdr
)
334 struct cbox_song
*song
= CBOX_H2O(objhdr
);
335 cbox_song_clear(song
);