Attempt to fix the clip offset bug.
[calfbox.git] / pattern.c
blob26f06608883e11353e745f7ad4ad5ac22ad42ab0
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 "blob.h"
20 #include "config.h"
21 #include "config-api.h"
22 #include "pattern.h"
23 #include "pattern-maker.h"
24 #include "song.h"
26 #include <glib.h>
28 extern void cbox_song_remove_pattern(struct cbox_song *song, struct cbox_midi_pattern *pattern);
30 CBOX_CLASS_DEFINITION_ROOT(cbox_midi_pattern)
32 struct cbox_midi_pattern *cbox_midi_pattern_new_metronome(struct cbox_song *song, int ts, uint64_t ppqn_factor)
34 struct cbox_midi_pattern_maker *m = cbox_midi_pattern_maker_new(ppqn_factor);
36 int length = (int)ppqn_factor;
37 int channel = cbox_config_get_int("metronome", "channel", 10);
38 int accnote = cbox_config_get_note("metronome", "note_accent", 37);
39 int note = cbox_config_get_note("metronome", "note", 37);
41 for (int i = 0; i < ts; i++)
43 int accent = !i && ts != 1;
44 cbox_midi_pattern_maker_add(m, length * i, 0x90 + channel - 1, accent ? accnote : note, accent ? 127 : 100);
45 cbox_midi_pattern_maker_add(m, length * i + 1, 0x80 + channel - 1, accent ? accnote : note, 0);
48 struct cbox_midi_pattern *p = cbox_midi_pattern_maker_create_pattern(m, song, g_strdup_printf("click-%d", ts));
49 p->loop_end = length * ts;
51 cbox_midi_pattern_maker_destroy(m);
53 return p;
56 void cbox_midi_pattern_destroyfunc(struct cbox_objhdr *objhdr)
58 struct cbox_midi_pattern *pattern = CBOX_H2O(objhdr);
59 if (pattern->owner)
60 cbox_song_remove_pattern(pattern->owner, pattern);
61 g_free(pattern->name);
62 if (pattern->events != NULL)
63 free(pattern->events);
64 free(pattern);
67 #if USE_LIBSMF
68 static int cbox_midi_pattern_load_smf_into(struct cbox_midi_pattern_maker *m, const char *smf)
70 int length = 0;
71 if (!cbox_midi_pattern_maker_load_smf(m, smf, &length, NULL))
73 g_error("Cannot load SMF file %s", smf);
74 return -1;
76 return length;
78 #endif
80 static int cbox_midi_pattern_load_melodic_into(struct cbox_midi_pattern_maker *m, const char *name, int start_pos, int transpose, int transpose_to_note, uint64_t ppqn_factor)
82 gchar *cfg_section = g_strdup_printf("pattern:%s", name);
84 if (!cbox_config_has_section(cfg_section))
86 g_error("Melodic pattern '%s' not found", name);
87 g_free(cfg_section);
88 return -1;
91 gchar *smf = cbox_config_get_string(cfg_section, "smf");
92 #if USE_LIBSMF
93 if (smf)
94 return cbox_midi_pattern_load_smf_into(m, smf);
95 #else
96 if (smf)
97 g_warning("libsmf disabled at build time, MIDI import functionality not available.");
98 #endif
100 int length = ppqn_factor * cbox_config_get_int(cfg_section, "beats", 4);
101 int gchannel = cbox_config_get_int(cfg_section, "channel", 1);
102 int gswing = cbox_config_get_int(cfg_section, "swing", 0);
103 int gres = cbox_config_get_int(cfg_section, "resolution", 4);
104 int orignote = cbox_config_get_note(cfg_section, "base_note", 24);
105 if (transpose_to_note != -1)
106 transpose += transpose_to_note - orignote;
108 for (int t = 1; ; t++)
110 gchar *tname = g_strdup_printf("track%d", t);
111 char *trkname = cbox_config_get_string(cfg_section, tname);
112 g_free(tname);
113 if (trkname)
115 tname = g_strdup_printf("%s_vel", trkname);
116 int vel = cbox_config_get_note(cfg_section, tname, 100);
117 g_free(tname);
118 tname = g_strdup_printf("%s_res", trkname);
119 int res = cbox_config_get_note(cfg_section, tname, gres);
120 g_free(tname);
121 tname = g_strdup_printf("%s_channel", trkname);
122 int channel = cbox_config_get_note(cfg_section, tname, gchannel);
123 g_free(tname);
124 tname = g_strdup_printf("%s_swing", trkname);
125 int swing = cbox_config_get_int(cfg_section, tname, gswing);
126 g_free(tname);
127 tname = g_strdup_printf("%s_notes", trkname);
128 const char *notes = cbox_config_get_string(cfg_section, tname);
129 g_free(tname);
130 if (!notes)
132 g_error("Invalid track %s", trkname);
134 const char *s = notes;
135 int t = 0;
136 while(1)
138 if (!*s)
139 break;
141 gchar *note;
142 const char *comma = strchr(s, ',');
143 if (comma)
145 note = g_strndup(s, comma - s);
146 s = comma + 1;
148 else
150 note = g_strdup(s);
151 s += strlen(s);
154 if (*note)
156 int pitch = note_from_string(note);
158 int pos = t * ppqn_factor / res + start_pos;
159 if (t & 1)
160 pos += ppqn_factor * swing / (res * 24);
162 int pos2 = (t + 1) * ppqn_factor / res + start_pos;
163 if (t & 1)
164 pos2 += ppqn_factor * swing / (res * 24);
166 pitch += transpose;
168 cbox_midi_pattern_maker_add(m, pos, 0x90 + channel - 1, pitch, vel);
169 cbox_midi_pattern_maker_add(m, pos2 - 1, 0x80 + channel - 1, pitch, 0);
171 t++;
174 else
175 break;
178 g_free(cfg_section);
180 return length;
183 static int cbox_midi_pattern_load_drum_into(struct cbox_midi_pattern_maker *m, const char *name, int start_pos, uint64_t ppqn_factor)
185 gchar *cfg_section = g_strdup_printf("drumpattern:%s", name);
187 if (!cbox_config_has_section(cfg_section))
189 g_error("Drum pattern '%s' not found", name);
190 g_free(cfg_section);
191 return -1;
194 gchar *smf = cbox_config_get_string(cfg_section, "smf");
195 #if USE_LIBSMF
196 if (smf)
197 return cbox_midi_pattern_load_smf_into(m, smf);
198 #else
199 if (smf)
200 g_warning("libsmf disabled at build time, MIDI import functionality not available.");
201 #endif
203 int length = ppqn_factor * cbox_config_get_int(cfg_section, "beats", 4);
204 int channel = cbox_config_get_int(cfg_section, "channel", 10);
205 int gswing = cbox_config_get_int(cfg_section, "swing", 0);
206 int gres = cbox_config_get_int(cfg_section, "resolution", 4);
208 for (int t = 1; ; t++)
210 gchar *tname = g_strdup_printf("track%d", t);
211 char *trkname = cbox_config_get_string(cfg_section, tname);
212 g_free(tname);
213 if (trkname)
215 tname = g_strdup_printf("%s_note", trkname);
216 int note = cbox_config_get_note(cfg_section, tname, -1);
217 g_free(tname);
218 tname = g_strdup_printf("%s_res", trkname);
219 int res = cbox_config_get_note(cfg_section, tname, gres);
220 g_free(tname);
221 tname = g_strdup_printf("%s_swing", trkname);
222 int swing = cbox_config_get_int(cfg_section, tname, gswing);
223 g_free(tname);
224 tname = g_strdup_printf("%s_trigger", trkname);
225 const char *trigger = cbox_config_get_string(cfg_section, tname);
226 g_free(tname);
227 if (!trigger || note == -1)
229 g_error("Invalid track %s", trkname);
231 int t = 0;
232 for (int i = 0; trigger[i]; i++)
234 int pos = t * ppqn_factor / res + start_pos;
235 if (t & 1)
236 pos += ppqn_factor * swing / (res * 24);
237 if (trigger[i] >= '1' && trigger[i] <= '9')
239 int amt = (trigger[i] - '0') * 127 / 9;
240 cbox_midi_pattern_maker_add(m, pos, 0x90 + channel - 1, note, amt);
241 cbox_midi_pattern_maker_add(m, pos + 1, 0x80 + channel - 1, note, 0);
242 t++;
244 if (trigger[i] == 'F') // flam
246 int dflam = ppqn_factor / 4;
247 int rnd = rand() & 7;
248 dflam += rnd / 2;
249 cbox_midi_pattern_maker_add(m, pos - dflam, 0x90 + channel - 1, note, 90+rnd);
250 cbox_midi_pattern_maker_add(m, pos - dflam + 1, 0x80 + channel - 1, note, 0);
251 cbox_midi_pattern_maker_add(m, pos , 0x90 + channel - 1, note, 120 + rnd);
252 cbox_midi_pattern_maker_add(m, pos + 1, 0x80 + channel - 1, note, 0);
253 t++;
255 if (trigger[i] == 'D') // drag
257 pos = (t + 1) * ppqn_factor / res + start_pos;
258 //if (!(t & 1))
259 // pos += ppqn_factor * swing / (res * 24);
260 float dflam = ppqn_factor/8.0;
261 int rnd = rand() & 7;
262 cbox_midi_pattern_maker_add(m, pos - dflam*2, 0x90 + channel - 1, note, 70+rnd);
263 cbox_midi_pattern_maker_add(m, pos - dflam*2 + 1, 0x80 + channel - 1, note, 0);
264 cbox_midi_pattern_maker_add(m, pos - dflam, 0x90 + channel - 1, note, 60+rnd);
265 cbox_midi_pattern_maker_add(m, pos - dflam + 1, 0x80 + channel - 1, note, 0);
266 t++;
268 else if (trigger[i] == '.')
269 t++;
272 else
273 break;
276 g_free(cfg_section);
278 return length;
281 struct cbox_midi_pattern *cbox_midi_pattern_load(struct cbox_song *song, const char *name, int is_drum, uint64_t ppqn_factor)
283 struct cbox_midi_pattern_maker *m = cbox_midi_pattern_maker_new(ppqn_factor);
285 int length = 0;
286 if (is_drum)
287 length = cbox_midi_pattern_load_drum_into(m, name, 0, ppqn_factor);
288 else
289 length = cbox_midi_pattern_load_melodic_into(m, name, 0, 0, -1, ppqn_factor);
290 struct cbox_midi_pattern *p = cbox_midi_pattern_maker_create_pattern(m, song, g_strdup(name));
291 p->loop_end = length;
293 cbox_midi_pattern_maker_destroy(m);
295 return p;
298 struct cbox_midi_pattern *cbox_midi_pattern_load_track(struct cbox_song *song, const char *name, int is_drum, uint64_t ppqn_factor)
300 int length = 0;
301 struct cbox_midi_pattern_maker *m = cbox_midi_pattern_maker_new(ppqn_factor);
303 gchar *cfg_section = g_strdup_printf(is_drum ? "drumtrack:%s" : "track:%s", name);
305 if (!cbox_config_has_section(cfg_section))
307 g_error("Drum track '%s' not found", name);
308 g_free(cfg_section);
309 return NULL;
312 for (int p = 1; ; p++)
314 gchar *pname = g_strdup_printf("pos%d", p);
315 char *patname = cbox_config_get_string(cfg_section, pname);
316 g_free(pname);
317 if (patname)
319 int tplen = 0;
320 char *comma = strchr(patname, ',');
321 while(*patname)
323 char *v = comma ? g_strndup(patname, comma - patname) : g_strdup(patname);
324 patname = comma ? comma + 1 : patname + strlen(patname);
326 int xpval = 0, xpnote = -1;
327 if (!is_drum)
329 char *xp = strchr(v, '+');
330 if (xp)
332 *xp = '\0';
333 xpval = atoi(xp + 1);
335 else
337 xp = strchr(v, '=');
338 if (xp)
340 *xp = '\0';
341 xpnote = note_from_string(xp + 1);
345 int plen = 0;
346 int is_drum_pat = is_drum;
347 int nofs = 0;
348 if (*v == '@')
350 nofs = 1;
351 is_drum_pat = !is_drum_pat;
353 if (is_drum_pat)
354 plen = cbox_midi_pattern_load_drum_into(m, v + nofs, length, ppqn_factor);
355 else
356 plen = cbox_midi_pattern_load_melodic_into(m, v + nofs, length, xpval, xpnote, ppqn_factor);
357 g_free(v);
358 if (plen < 0)
360 cbox_midi_pattern_maker_destroy(m);
361 return NULL;
363 if (plen > tplen)
364 tplen = plen;
365 if (*patname)
366 comma = strchr(patname, ',');
368 length += tplen;
370 else
371 break;
374 g_free(cfg_section);
376 struct cbox_midi_pattern *p = cbox_midi_pattern_maker_create_pattern(m, song, g_strdup(name));
377 p->loop_end = length;
379 cbox_midi_pattern_maker_destroy(m);
381 return p;
384 struct cbox_midi_pattern *cbox_midi_pattern_new_from_blob(struct cbox_song *song, const struct cbox_blob *blob, int length, uint64_t ppqn_factor)
386 struct cbox_midi_pattern_maker *m = cbox_midi_pattern_maker_new(ppqn_factor);
388 struct cbox_blob_serialized_event event;
389 for (size_t i = 0; i < blob->size; i += sizeof(event))
391 // not sure about alignment guarantees of Python buffers
392 memcpy(&event, ((uint8_t *)blob->data) + i, sizeof(event));
393 cbox_midi_pattern_maker_add(m, event.time, event.cmd, event.byte1, event.byte2);
396 struct cbox_midi_pattern *p = cbox_midi_pattern_maker_create_pattern(m, song, g_strdup("unnamed-blob"));
397 p->loop_end = length;
399 cbox_midi_pattern_maker_destroy(m);
401 return p;
404 struct cbox_blob *cbox_midi_pattern_to_blob(struct cbox_midi_pattern *pat, int *length)
406 if (length)
407 *length = pat->loop_end;
409 struct cbox_blob_serialized_event event;
410 int size = 0;
411 for (int i = 0; i < pat->event_count; i++)
413 // currently sysex events and the like are not supported
414 if (pat->events[i].size < 4)
415 size += sizeof(event);
418 struct cbox_blob *blob = cbox_blob_new(size);
420 size = 0;
421 uint8_t *data = blob->data;
422 for (int i = 0; i < pat->event_count; i++)
424 // currently sysex events and the like are not supported
425 const struct cbox_midi_event *src = &pat->events[i];
426 if (src->size < 4)
428 event.time = src->time;
429 event.len = src->size;
430 memcpy(&event.cmd, &src->data_inline[0], event.len);
431 memcpy(data + size, &event, sizeof(event));
432 size += sizeof(event);
435 return blob;
438 gboolean cbox_midi_pattern_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
440 struct cbox_midi_pattern *p = ct->user_data;
442 if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
444 if (!cbox_check_fb_channel(fb, cmd->command, error))
445 return FALSE;
447 return cbox_execute_on(fb, NULL, "/event_count", "i", error, (int)p->event_count) &&
448 cbox_execute_on(fb, NULL, "/loop_end", "i", error, (int)p->loop_end) &&
449 cbox_execute_on(fb, NULL, "/name", "s", error, p->name) &&
450 CBOX_OBJECT_DEFAULT_STATUS(p, fb, error)
453 else if (!strcmp(cmd->command, "/name") && !strcmp(cmd->arg_types, "s"))
455 char *old_name = p->name;
456 p->name = g_strdup(CBOX_ARG_S(cmd, 0));
457 g_free(old_name);
458 return TRUE;
460 return cbox_object_default_process_cmd(ct, fb, cmd, error);