Merge pull request #1 from atsampson/master
[calfbox.git] / seq.c
blob2a336b0835424da0c25b3c6762aed7bf579fc838
1 /*
2 Calf Box, an open source musical instrument.
3 Copyright (C) 2010-2013 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 "engine.h"
20 #include "pattern.h"
21 #include "rt.h"
22 #include "seq.h"
23 #include "song.h"
24 #include "track.h"
25 #include <assert.h>
27 static inline void accumulate_event(struct cbox_midi_playback_active_notes *notes, const struct cbox_midi_event *event)
29 if (event->size != 3)
30 return;
31 // this ignores poly aftertouch - which, I supposed, is OK for now
32 if (event->data_inline[0] < 0x80 || event->data_inline[0] > 0x9F)
33 return;
34 int ch = event->data_inline[0] & 0x0F;
35 if (event->data_inline[0] >= 0x90 && event->data_inline[2] > 0)
37 int note = event->data_inline[1] & 0x7F;
38 if (!(notes->channels_active & (1 << ch)))
40 for (int i = 0; i < 4; i++)
41 notes->notes[ch][i] = 0;
42 notes->channels_active |= 1 << ch;
44 notes->notes[ch][note >> 5] |= 1 << (note & 0x1F);
48 struct cbox_track_playback *cbox_track_playback_new_from_track(struct cbox_track *track, struct cbox_master *master, struct cbox_song_playback *spb, struct cbox_track_playback *old_state)
50 struct cbox_track_playback *pb = malloc(sizeof(struct cbox_track_playback));
51 pb->track = track;
52 pb->old_state = old_state;
53 pb->master = master;
54 int len = g_list_length(track->items);
55 pb->items = calloc(len, sizeof(struct cbox_track_playback_item));
56 pb->external_merger = NULL;
57 pb->spb = spb;
58 pb->state_copied = FALSE;
60 GList *it = track->items;
61 struct cbox_track_playback_item *p = pb->items;
62 uint32_t safe = 0;
63 while(it != NULL)
65 struct cbox_track_item *item = it->data;
66 struct cbox_midi_pattern_playback *mppb = cbox_song_playback_get_pattern(spb, item->pattern);
68 // if items overlap, the first one takes precedence
69 if (item->time < safe)
71 // fully contained in previous item? skip all of it
72 // not fully contained - insert the fragment
73 if (item->time + item->length >= safe)
75 int cut = safe - item->time;
76 p->time = safe;
77 p->pattern = mppb;
78 p->offset = item->offset + cut;
79 p->length = item->length - cut;
80 p++;
83 else
85 p->time = item->time;
86 p->pattern = mppb;
87 p->offset = item->offset;
88 p->length = item->length;
89 safe = item->time + item->length;
90 p++;
93 it = g_list_next(it);
95 // in case of full overlap, some items might have been skipped
96 pb->items_count = p - pb->items;
97 pb->pos = 0;
98 cbox_midi_clip_playback_init(&pb->playback, &pb->active_notes, master);
99 cbox_midi_playback_active_notes_init(&pb->active_notes);
100 cbox_midi_buffer_init(&pb->output_buffer);
101 cbox_track_playback_start_item(pb, 0, FALSE, 0);
103 if (track->external_output_set)
105 struct cbox_midi_merger *merger = cbox_rt_get_midi_output(spb->engine->rt, &track->external_output);
106 if (merger)
108 cbox_midi_merger_connect(merger, &pb->output_buffer, spb->engine->rt);
109 pb->external_merger = merger;
113 return pb;
116 void cbox_track_playback_seek_ppqn(struct cbox_track_playback *pb, int time_ppqn, int min_time_ppqn)
118 pb->pos = 0;
119 while(pb->pos < pb->items_count && pb->items[pb->pos].time + pb->items[pb->pos].length < time_ppqn)
120 pb->pos++;
121 cbox_track_playback_start_item(pb, time_ppqn, TRUE, min_time_ppqn);
124 void cbox_track_playback_seek_samples(struct cbox_track_playback *pb, int time_samples)
126 pb->pos = 0;
127 while(pb->pos < pb->items_count && cbox_master_ppqn_to_samples(pb->master, pb->items[pb->pos].time + pb->items[pb->pos].length) < time_samples)
128 pb->pos++;
129 cbox_track_playback_start_item(pb, time_samples, FALSE, 0);
132 void cbox_track_playback_start_item(struct cbox_track_playback *pb, int time, int is_ppqn, int min_time_ppqn)
134 if (pb->pos >= pb->items_count)
136 return;
138 struct cbox_track_playback_item *cur = &pb->items[pb->pos];
139 int time_samples, time_ppqn;
141 if (is_ppqn)
143 time_ppqn = time;
144 time_samples = cbox_master_ppqn_to_samples(pb->master, time_ppqn);
146 else
148 time_samples = time;
149 time_ppqn = cbox_master_samples_to_ppqn(pb->master, time_samples);
151 int start_time_ppqn = cur->time, end_time_ppqn = cur->time + cur->length;
152 int start_time_samples = cbox_master_ppqn_to_samples(pb->master, start_time_ppqn);
153 int end_time_samples = cbox_master_ppqn_to_samples(pb->master, end_time_ppqn);
154 cbox_midi_clip_playback_set_pattern(&pb->playback, cur->pattern, start_time_samples, end_time_samples, cur->time, cur->offset);
156 if (is_ppqn)
158 if (time_ppqn < start_time_ppqn)
159 cbox_midi_clip_playback_seek_ppqn(&pb->playback, 0, min_time_ppqn);
160 else
161 cbox_midi_clip_playback_seek_ppqn(&pb->playback, time_ppqn - start_time_ppqn, min_time_ppqn);
163 else
165 if (time_ppqn < start_time_ppqn)
166 cbox_midi_clip_playback_seek_samples(&pb->playback, 0);
167 else
168 cbox_midi_clip_playback_seek_samples(&pb->playback, time_samples - start_time_samples);
172 void cbox_track_playback_render(struct cbox_track_playback *pb, int offset, int nsamples)
174 struct cbox_song_playback *spb = pb->master->spb;
175 int rpos = 0;
176 while(rpos < nsamples && pb->pos < pb->items_count)
178 int rend = nsamples;
179 struct cbox_track_playback_item *cur = &pb->items[pb->pos];
180 // a gap before the current item
181 if (spb->song_pos_samples + rpos < pb->playback.start_time_samples)
183 int space_samples = pb->playback.start_time_samples - (spb->song_pos_samples + rpos);
184 if (space_samples >= rend - rpos)
185 return;
186 rpos += space_samples;
187 offset += space_samples;
189 // check if item finished
190 int cur_segment_end_samples = cbox_master_ppqn_to_samples(pb->master, cur->time + cur->length);
191 int render_end_samples = spb->song_pos_samples + rend;
192 if (render_end_samples > cur_segment_end_samples)
194 rend = cur_segment_end_samples - spb->song_pos_samples;
195 cbox_midi_clip_playback_render(&pb->playback, &pb->output_buffer, offset, rend - rpos);
196 pb->pos++;
197 cbox_track_playback_start_item(pb, cur_segment_end_samples, FALSE, FALSE);
199 else
200 cbox_midi_clip_playback_render(&pb->playback, &pb->output_buffer, offset, rend - rpos);
201 offset += rend - rpos;
202 rpos = rend;
206 void cbox_track_playback_destroy(struct cbox_track_playback *pb)
208 if (pb->external_merger)
209 cbox_midi_merger_disconnect(pb->external_merger, &pb->output_buffer, pb->spb->engine->rt);
211 free(pb->items);
212 free(pb);
215 /////////////////////////////////////////////////////////////////////////////////////////////////////
217 void cbox_midi_pattern_playback_destroy(struct cbox_midi_pattern_playback *mppb)
219 free(mppb->events);
220 free(mppb);
223 /////////////////////////////////////////////////////////////////////////////////////////////////////
225 void cbox_midi_clip_playback_init(struct cbox_midi_clip_playback *pb, struct cbox_midi_playback_active_notes *active_notes, struct cbox_master *master)
227 pb->pattern = NULL;
228 pb->master = master;
229 pb->pos = 0;
230 pb->rel_time_samples = 0;
231 pb->start_time_samples = 0;
232 pb->end_time_samples = 0;
233 pb->active_notes = active_notes;
234 pb->min_time_ppqn = 0;
235 // cbox_midi_playback_active_notes_init(active_notes);
238 void cbox_midi_clip_playback_set_pattern(struct cbox_midi_clip_playback *pb, struct cbox_midi_pattern_playback *pattern, int start_time_samples, int end_time_samples, int item_start_ppqn, int offset_ppqn)
240 pb->pattern = pattern;
241 pb->pos = 0;
242 pb->rel_time_samples = 0;
243 pb->start_time_samples = start_time_samples;
244 pb->end_time_samples = end_time_samples;
245 pb->item_start_ppqn = item_start_ppqn;
246 pb->offset_ppqn = offset_ppqn;
247 pb->min_time_ppqn = offset_ppqn;
250 void cbox_midi_clip_playback_render(struct cbox_midi_clip_playback *pb, struct cbox_midi_buffer *buf, int offset, int nsamples)
252 uint32_t end_time_samples = pb->end_time_samples;
253 uint32_t cur_time_samples = pb->start_time_samples + pb->rel_time_samples;
255 if (end_time_samples > cur_time_samples + nsamples)
256 end_time_samples = cur_time_samples + nsamples;
258 while(pb->pos < pb->pattern->event_count)
260 const struct cbox_midi_event *src = &pb->pattern->events[pb->pos];
262 if (src->time - pb->offset_ppqn >= pb->min_time_ppqn)
264 int event_time_samples = cbox_master_ppqn_to_samples(pb->master, src->time - pb->offset_ppqn) + pb->start_time_samples;
266 if (event_time_samples >= end_time_samples)
267 break;
268 int32_t time = 0;
269 if (event_time_samples >= cur_time_samples) // convert negative relative time to 0 time
270 time = event_time_samples - cur_time_samples;
272 cbox_midi_buffer_copy_event(buf, src, offset + time);
273 if (pb->active_notes)
274 accumulate_event(pb->active_notes, src);
276 pb->pos++;
278 pb->rel_time_samples += nsamples;
281 void cbox_midi_clip_playback_seek_ppqn(struct cbox_midi_clip_playback *pb, int time_ppqn, int min_time_ppqn)
283 int pos = 0;
284 int patrel_time_ppqn = time_ppqn + pb->offset_ppqn;
285 while (pos < pb->pattern->event_count && patrel_time_ppqn > pb->pattern->events[pos].time)
286 pos++;
287 pb->rel_time_samples = cbox_master_ppqn_to_samples(pb->master, pb->item_start_ppqn + time_ppqn) - pb->start_time_samples;
288 pb->min_time_ppqn = min_time_ppqn;
289 pb->pos = pos;
292 void cbox_midi_clip_playback_seek_samples(struct cbox_midi_clip_playback *pb, int time_samples)
294 int pos = 0;
295 while (pos < pb->pattern->event_count && time_samples > cbox_master_ppqn_to_samples(pb->master, pb->item_start_ppqn + pb->pattern->events[pos].time - pb->offset_ppqn))
296 pos++;
297 pb->rel_time_samples = time_samples;
298 pb->min_time_ppqn = 0;
299 pb->pos = pos;
302 /////////////////////////////////////////////////////////////////////////////////////////////////////
304 void cbox_midi_playback_active_notes_init(struct cbox_midi_playback_active_notes *notes)
306 notes->channels_active = 0;
309 void cbox_midi_playback_active_notes_copy(struct cbox_midi_playback_active_notes *dest, const struct cbox_midi_playback_active_notes *src)
311 dest->channels_active = src->channels_active;
312 memcpy(dest->notes, src->notes, sizeof(dest->notes));
315 int cbox_midi_playback_active_notes_release(struct cbox_midi_playback_active_notes *notes, struct cbox_midi_buffer *buf)
317 if (!notes->channels_active)
318 return 0;
319 int note_offs = 0;
320 for (int c = 0; c < 16; c++)
322 if (!(notes->channels_active & (1 << c)))
323 continue;
325 for (int g = 0; g < 4; g++)
327 uint32_t group = notes->notes[c][g];
328 if (!group)
329 continue;
330 for (int i = 0; i < 32; i++)
332 int n = i + g * 32;
333 if (!(group & (1 << i)))
334 continue;
335 if (!cbox_midi_buffer_can_store_msg(buf, 3))
336 return -1;
337 cbox_midi_buffer_write_inline(buf, cbox_midi_buffer_get_last_event_time(buf), 0x80 + c, n, 0);
338 group &= ~(1 << i);
339 notes->notes[c][g] = group;
340 note_offs++;
343 // all Note Offs emitted without buffer overflow - channel is no longer active
344 notes->channels_active &= ~(1 << c);
346 return note_offs;
349 /////////////////////////////////////////////////////////////////////////////////////////////////////
351 struct cbox_song_playback *cbox_song_playback_new(struct cbox_song *song, struct cbox_master *master, struct cbox_engine *engine, struct cbox_song_playback *old_state)
353 struct cbox_song_playback *spb = calloc(1, sizeof(struct cbox_song_playback));
354 if (old_state && old_state->song != song)
355 old_state = NULL;
356 spb->song = song;
357 spb->engine = engine;
358 spb->pattern_map = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify)cbox_midi_pattern_playback_destroy);
359 spb->master = master;
360 spb->track_count = g_list_length(song->tracks);
361 spb->tracks = malloc(spb->track_count * sizeof(struct cbox_track_playback *));
362 spb->song_pos_samples = 0;
363 spb->song_pos_ppqn = 0;
364 spb->min_time_ppqn = 0;
365 spb->loop_start_ppqn = song->loop_start_ppqn;
366 spb->loop_end_ppqn = song->loop_end_ppqn;
367 cbox_midi_merger_init(&spb->track_merger, NULL);
368 int pos = 0;
369 for (GList *p = song->tracks; p != NULL; p = g_list_next(p))
371 struct cbox_track *trk = p->data;
372 struct cbox_track_playback *old_trk = NULL;
373 if (old_state && old_state->track_count)
375 for (int i = 0; i < old_state->track_count; i++)
377 if (old_state->tracks[i]->track == trk)
379 old_trk = old_state->tracks[i];
380 break;
384 spb->tracks[pos++] = cbox_track_playback_new_from_track(trk, spb->master, spb, old_trk);
385 if (!trk->external_output_set)
386 cbox_midi_merger_connect(&spb->track_merger, &spb->tracks[pos - 1]->output_buffer, NULL);
389 spb->tempo_map_item_count = g_list_length(song->master_track_items);
390 spb->tempo_map_items = malloc(spb->tempo_map_item_count * sizeof(struct cbox_tempo_map_item));
391 pos = 0;
392 int pos_ppqn = 0;
393 int pos_samples = 0;
394 double tempo = master->tempo;
395 int timesig_nom = master->timesig_nom;
396 int timesig_denom = master->timesig_denom;
397 for (GList *p = song->master_track_items; p != NULL; p = g_list_next(p))
399 struct cbox_master_track_item *mti = p->data;
400 if (mti->tempo > 0)
401 tempo = mti->tempo;
402 if (mti->timesig_nom > 0)
403 timesig_nom = mti->timesig_nom;
404 if (mti->timesig_denom > 0)
405 timesig_denom = mti->timesig_denom;
406 struct cbox_tempo_map_item *tmi = &spb->tempo_map_items[pos];
407 tmi->time_ppqn = pos_ppqn;
408 tmi->time_samples = pos_samples;
409 tmi->tempo = tempo;
410 tmi->timesig_nom = timesig_nom;
411 tmi->timesig_denom = timesig_denom;
413 pos_ppqn += mti->duration_ppqn;
414 pos_samples += spb->master->srate * 60.0 * mti->duration_ppqn / (tempo * master->ppqn_factor);
415 pos++;
417 return spb;
420 void cbox_song_playback_apply_old_state(struct cbox_song_playback *spb)
422 for (int i = 0; i < spb->track_count; i++)
424 struct cbox_track_playback *tpb = spb->tracks[i];
425 if (tpb->old_state)
427 cbox_midi_playback_active_notes_copy(&tpb->active_notes, &tpb->old_state->active_notes);
428 tpb->old_state->state_copied = TRUE;
429 tpb->old_state = NULL;
435 static void cbox_song_playback_set_tempo(struct cbox_song_playback *spb, double tempo)
437 int ppos = spb->song_pos_ppqn;
438 int pos1 = cbox_master_ppqn_to_samples(spb->master, ppos);
439 int pos2 = cbox_master_ppqn_to_samples(spb->master, ppos + 1);
440 double relpos = 0.0;
441 if (pos1 != pos2)
442 relpos = (spb->song_pos_samples - pos1) * 1.0 / (pos2 - pos1);
443 spb->master->tempo = tempo;
445 // This seek loses the fractional value of the PPQN song position.
446 // This needs to be compensated for by shifting the playback
447 // position by the fractional part.
448 cbox_song_playback_seek_ppqn(spb, ppos, spb->min_time_ppqn);
449 if (relpos > 0)
451 pos2 = cbox_master_ppqn_to_samples(spb->master, ppos + 1);
452 cbox_song_playback_seek_samples(spb, spb->song_pos_samples + (pos2 - spb->song_pos_samples) * relpos + 0.5);
456 int cbox_song_playback_get_next_tempo_change(struct cbox_song_playback *spb)
458 double new_tempo = 0;
459 // Skip items at or already past the playback pointer
460 while (spb->tempo_map_pos + 1 < spb->tempo_map_item_count &&
461 spb->song_pos_samples >= spb->tempo_map_items[spb->tempo_map_pos + 1].time_samples)
463 new_tempo = spb->tempo_map_items[spb->tempo_map_pos + 1].tempo;
464 spb->tempo_map_pos++;
466 if (new_tempo != 0.0 && new_tempo != spb->master->tempo)
467 cbox_song_playback_set_tempo(spb, new_tempo);
469 // No more items?
470 if (spb->tempo_map_pos + 1 >= spb->tempo_map_item_count)
471 return -1;
473 return spb->tempo_map_items[spb->tempo_map_pos + 1].time_samples;
476 void cbox_song_playback_render(struct cbox_song_playback *spb, struct cbox_midi_buffer *output, int nsamples)
478 cbox_midi_buffer_clear(output);
480 if (spb->master->new_tempo != 0 && spb->master->new_tempo != spb->master->tempo)
482 cbox_song_playback_set_tempo(spb, spb->master->new_tempo);
483 spb->master->new_tempo = 0;
485 for(int i = 0; i < spb->track_count; i++)
487 cbox_midi_buffer_clear(&spb->tracks[i]->output_buffer);
489 if (spb->master->state == CMTS_STOPPING)
491 if (cbox_song_playback_active_notes_release(spb, output) > 0)
492 spb->master->state = CMTS_STOP;
494 else
495 if (spb->master->state == CMTS_ROLLING)
497 int end_samples = cbox_master_ppqn_to_samples(spb->master, spb->loop_end_ppqn);
499 int rpos = 0;
500 while (rpos < nsamples)
502 int rend = nsamples;
504 // 1. Shorten the period so that it doesn't go past a tempo change
505 int tmpos = cbox_song_playback_get_next_tempo_change(spb);
506 if (tmpos != -1)
508 // Number of samples until the next tempo change
509 int stntc = tmpos - spb->song_pos_samples;
510 if (rend - rpos > stntc)
511 rend = rpos + stntc;
514 // 2. Shorten the period so that it doesn't go past the song length
515 int end_pos = spb->song_pos_samples + (rend - rpos);
516 if (end_pos >= end_samples)
518 rend = end_samples - spb->song_pos_samples;
519 end_pos = end_samples;
522 if (rend > rpos)
524 for (int i = 0; i < spb->track_count; i++)
525 cbox_track_playback_render(spb->tracks[i], rpos, rend - rpos);
528 if (end_pos < end_samples)
530 spb->song_pos_samples += rend - rpos;
531 // XXXKF optimize
532 spb->min_time_ppqn = cbox_master_samples_to_ppqn(spb->master, spb->song_pos_samples - 1) + 1;
533 spb->song_pos_ppqn = cbox_master_samples_to_ppqn(spb->master, spb->song_pos_samples);
535 else
537 if (spb->loop_start_ppqn >= spb->loop_end_ppqn)
539 spb->song_pos_samples = end_samples;
540 spb->song_pos_ppqn = spb->loop_end_ppqn;
541 spb->master->state = CMTS_STOPPING;
542 break;
545 cbox_song_playback_seek_ppqn(spb, spb->loop_start_ppqn, spb->loop_start_ppqn);
547 rpos = rend;
549 cbox_midi_merger_render_to(&spb->track_merger, output);
553 int cbox_song_playback_active_notes_release(struct cbox_song_playback *spb, struct cbox_midi_buffer *buf)
555 for(int i = 0; i < spb->track_count; i++)
557 struct cbox_track_playback *trk = spb->tracks[i];
558 if (trk->state_copied)
559 continue;
560 struct cbox_midi_buffer *output = trk->external_merger ? &trk->output_buffer : buf;
561 if (cbox_midi_playback_active_notes_release(&trk->active_notes, output) < 0)
562 return 0;
564 return 1;
567 void cbox_song_playback_seek_ppqn(struct cbox_song_playback *spb, int time_ppqn, int min_time_ppqn)
569 for(int i = 0; i < spb->track_count; i++)
571 struct cbox_track_playback *trk = spb->tracks[i];
572 cbox_track_playback_seek_ppqn(trk, time_ppqn, min_time_ppqn);
574 spb->song_pos_samples = cbox_master_ppqn_to_samples(spb->master, time_ppqn);
575 spb->song_pos_ppqn = time_ppqn;
576 spb->min_time_ppqn = min_time_ppqn;
577 spb->tempo_map_pos = cbox_song_playback_tmi_from_ppqn(spb, time_ppqn);
580 void cbox_song_playback_seek_samples(struct cbox_song_playback *spb, int time_samples)
582 for(int i = 0; i < spb->track_count; i++)
584 struct cbox_track_playback *trk = spb->tracks[i];
585 cbox_track_playback_seek_samples(trk, time_samples);
587 spb->song_pos_samples = time_samples;
588 spb->song_pos_ppqn = cbox_master_samples_to_ppqn(spb->master, time_samples);
589 spb->min_time_ppqn = spb->song_pos_ppqn;
590 spb->tempo_map_pos = cbox_song_playback_tmi_from_samples(spb, time_samples);
593 int cbox_song_playback_tmi_from_ppqn(struct cbox_song_playback *spb, int time_ppqn)
595 if (!spb->tempo_map_item_count)
596 return -1;
597 assert(spb->tempo_map_items[0].time_samples == 0);
598 assert(spb->tempo_map_items[0].time_ppqn == 0);
599 // XXXKF should use binary search here really
600 for (int i = 1; i < spb->tempo_map_item_count; i++)
602 if (time_ppqn < spb->tempo_map_items[i].time_ppqn)
603 return i - 1;
605 return spb->tempo_map_item_count - 1;
608 int cbox_song_playback_tmi_from_samples(struct cbox_song_playback *spb, int time_samples)
610 if (!spb->tempo_map_item_count)
611 return -1;
612 assert(spb->tempo_map_items[0].time_samples == 0);
613 assert(spb->tempo_map_items[0].time_ppqn == 0);
614 // XXXKF should use binary search here really
615 for (int i = 1; i < spb->tempo_map_item_count; i++)
617 if (time_samples < spb->tempo_map_items[i].time_samples)
618 return i - 1;
620 return spb->tempo_map_item_count - 1;
623 struct cbox_midi_pattern_playback *cbox_midi_pattern_playback_new(struct cbox_midi_pattern *pattern)
625 struct cbox_midi_pattern_playback *mppb = calloc(1, sizeof(struct cbox_midi_pattern_playback));
626 mppb->events = malloc(sizeof(struct cbox_midi_event) * pattern->event_count);
627 memcpy(mppb->events, pattern->events, sizeof(struct cbox_midi_event) * pattern->event_count);
628 mppb->event_count = pattern->event_count;
629 return mppb;
632 struct cbox_midi_pattern_playback *cbox_song_playback_get_pattern(struct cbox_song_playback *spb, struct cbox_midi_pattern *pattern)
634 struct cbox_midi_pattern_playback *mppb = g_hash_table_lookup(spb->pattern_map, pattern);
635 if (mppb)
636 return mppb;
638 mppb = cbox_midi_pattern_playback_new(pattern);
639 g_hash_table_insert(spb->pattern_map, pattern, mppb);
641 return mppb;
644 void cbox_song_playback_destroy(struct cbox_song_playback *spb)
646 for (int i = 0; i < spb->track_count; i++)
648 cbox_track_playback_destroy(spb->tracks[i]);
650 free(spb->tempo_map_items);
651 free(spb->tracks);
652 g_hash_table_destroy(spb->pattern_map);
653 cbox_midi_merger_close(&spb->track_merger);
654 free(spb);