smflite

annotate src/smf_tempo.c @ 0:4264abea8b06

smf-lite initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Thu, 26 Jan 2012 11:25:11 +0200
parents
children
rev   line source
nuclear@0 1 /*-
nuclear@0 2 * Copyright (c) 2007, 2008 Edward Tomasz NapieraƂa <trasz@FreeBSD.org>
nuclear@0 3 * All rights reserved.
nuclear@0 4 *
nuclear@0 5 * Redistribution and use in source and binary forms, with or without
nuclear@0 6 * modification, are permitted provided that the following conditions
nuclear@0 7 * are met:
nuclear@0 8 * 1. Redistributions of source code must retain the above copyright
nuclear@0 9 * notice, this list of conditions and the following disclaimer.
nuclear@0 10 * 2. Redistributions in binary form must reproduce the above copyright
nuclear@0 11 * notice, this list of conditions and the following disclaimer in the
nuclear@0 12 * documentation and/or other materials provided with the distribution.
nuclear@0 13 *
nuclear@0 14 * ALTHOUGH THIS SOFTWARE IS MADE OF WIN AND SCIENCE, IT IS PROVIDED BY THE
nuclear@0 15 * AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
nuclear@0 16 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
nuclear@0 17 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
nuclear@0 18 * THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
nuclear@0 19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
nuclear@0 20 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
nuclear@0 21 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
nuclear@0 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
nuclear@0 23 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
nuclear@0 24 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
nuclear@0 25 *
nuclear@0 26 */
nuclear@0 27
nuclear@0 28 /**
nuclear@0 29 * \file
nuclear@0 30 *
nuclear@0 31 * Tempo map related part.
nuclear@0 32 *
nuclear@0 33 */
nuclear@0 34
nuclear@0 35 #include <stdlib.h>
nuclear@0 36 #include <assert.h>
nuclear@0 37 #include <math.h>
nuclear@0 38 #include <string.h>
nuclear@0 39 #include "smf.h"
nuclear@0 40 #include "smf_private.h"
nuclear@0 41
nuclear@0 42 static double seconds_from_pulses(const smf_t *smf, int pulses);
nuclear@0 43
nuclear@0 44 /**
nuclear@0 45 * If there is tempo starting at "pulses" already, return it. Otherwise,
nuclear@0 46 * allocate new one, fill it with values from previous one (or default ones,
nuclear@0 47 * if there is no previous one) and attach it to "smf".
nuclear@0 48 */
nuclear@0 49 static smf_tempo_t *
nuclear@0 50 new_tempo(smf_t *smf, int pulses)
nuclear@0 51 {
nuclear@0 52 smf_tempo_t *tempo, *previous_tempo = NULL;
nuclear@0 53
nuclear@0 54 if (smf->tempo_array->len > 0) {
nuclear@0 55 previous_tempo = smf_get_last_tempo(smf);
nuclear@0 56
nuclear@0 57 /* If previous tempo starts at the same time as new one, reuse it, updating in place. */
nuclear@0 58 if (previous_tempo->time_pulses == pulses)
nuclear@0 59 return (previous_tempo);
nuclear@0 60 }
nuclear@0 61
nuclear@0 62 tempo = malloc(sizeof(smf_tempo_t));
nuclear@0 63 if (tempo == NULL) {
nuclear@0 64 fg_critical("Cannot allocate smf_tempo_t.");
nuclear@0 65 return (NULL);
nuclear@0 66 }
nuclear@0 67
nuclear@0 68 tempo->time_pulses = pulses;
nuclear@0 69
nuclear@0 70 if (previous_tempo != NULL) {
nuclear@0 71 tempo->microseconds_per_quarter_note = previous_tempo->microseconds_per_quarter_note;
nuclear@0 72 tempo->numerator = previous_tempo->numerator;
nuclear@0 73 tempo->denominator = previous_tempo->denominator;
nuclear@0 74 tempo->clocks_per_click = previous_tempo->clocks_per_click;
nuclear@0 75 tempo->notes_per_note = previous_tempo->notes_per_note;
nuclear@0 76 } else {
nuclear@0 77 tempo->microseconds_per_quarter_note = 500000; /* Initial tempo is 120 BPM. */
nuclear@0 78 tempo->numerator = 4;
nuclear@0 79 tempo->denominator = 4;
nuclear@0 80 tempo->clocks_per_click = -1;
nuclear@0 81 tempo->notes_per_note = -1;
nuclear@0 82 }
nuclear@0 83
nuclear@0 84 fg_ptr_array_add(smf->tempo_array, tempo);
nuclear@0 85
nuclear@0 86 if (pulses == 0)
nuclear@0 87 tempo->time_seconds = 0.0;
nuclear@0 88 else
nuclear@0 89 tempo->time_seconds = seconds_from_pulses(smf, pulses);
nuclear@0 90
nuclear@0 91 return (tempo);
nuclear@0 92 }
nuclear@0 93
nuclear@0 94 static int
nuclear@0 95 add_tempo(smf_t *smf, int pulses, int tempo)
nuclear@0 96 {
nuclear@0 97 smf_tempo_t *smf_tempo = new_tempo(smf, pulses);
nuclear@0 98 if (smf_tempo == NULL)
nuclear@0 99 return (-1);
nuclear@0 100
nuclear@0 101 smf_tempo->microseconds_per_quarter_note = tempo;
nuclear@0 102
nuclear@0 103 return (0);
nuclear@0 104 }
nuclear@0 105
nuclear@0 106 static int
nuclear@0 107 add_time_signature(smf_t *smf, int pulses, int numerator, int denominator, int clocks_per_click, int notes_per_note)
nuclear@0 108 {
nuclear@0 109 smf_tempo_t *smf_tempo = new_tempo(smf, pulses);
nuclear@0 110 if (smf_tempo == NULL)
nuclear@0 111 return (-1);
nuclear@0 112
nuclear@0 113 smf_tempo->numerator = numerator;
nuclear@0 114 smf_tempo->denominator = denominator;
nuclear@0 115 smf_tempo->clocks_per_click = clocks_per_click;
nuclear@0 116 smf_tempo->notes_per_note = notes_per_note;
nuclear@0 117
nuclear@0 118 return (0);
nuclear@0 119 }
nuclear@0 120
nuclear@0 121 /**
nuclear@0 122 * \internal
nuclear@0 123 */
nuclear@0 124 void
nuclear@0 125 maybe_add_to_tempo_map(smf_event_t *event)
nuclear@0 126 {
nuclear@0 127 if (!smf_event_is_metadata(event))
nuclear@0 128 return;
nuclear@0 129
nuclear@0 130 assert(event->track != NULL);
nuclear@0 131 assert(event->track->smf != NULL);
nuclear@0 132 assert(event->midi_buffer_length >= 1);
nuclear@0 133
nuclear@0 134 /* Tempo Change? */
nuclear@0 135 if (event->midi_buffer[1] == 0x51) {
nuclear@0 136 int new_tempo = (event->midi_buffer[3] << 16) + (event->midi_buffer[4] << 8) + event->midi_buffer[5];
nuclear@0 137 if (new_tempo <= 0) {
nuclear@0 138 fg_critical("Ignoring invalid tempo change.");
nuclear@0 139 return;
nuclear@0 140 }
nuclear@0 141
nuclear@0 142 add_tempo(event->track->smf, event->time_pulses, new_tempo);
nuclear@0 143 }
nuclear@0 144
nuclear@0 145 /* Time Signature? */
nuclear@0 146 if (event->midi_buffer[1] == 0x58) {
nuclear@0 147 int numerator, denominator, clocks_per_click, notes_per_note;
nuclear@0 148
nuclear@0 149 if (event->midi_buffer_length < 7) {
nuclear@0 150 fg_critical("Time Signature event seems truncated.");
nuclear@0 151 return;
nuclear@0 152 }
nuclear@0 153
nuclear@0 154 numerator = event->midi_buffer[3];
nuclear@0 155 denominator = (int)pow(2, event->midi_buffer[4]);
nuclear@0 156 clocks_per_click = event->midi_buffer[5];
nuclear@0 157 notes_per_note = event->midi_buffer[6];
nuclear@0 158
nuclear@0 159 add_time_signature(event->track->smf, event->time_pulses, numerator, denominator, clocks_per_click, notes_per_note);
nuclear@0 160 }
nuclear@0 161
nuclear@0 162 return;
nuclear@0 163 }
nuclear@0 164
nuclear@0 165 /**
nuclear@0 166 * \internal
nuclear@0 167 *
nuclear@0 168 * This is an internal function, called from smf_track_remove_event when tempo-related
nuclear@0 169 * event being removed does not require recreation of tempo map, i.e. there are no events
nuclear@0 170 * after that one.
nuclear@0 171 */
nuclear@0 172 void
nuclear@0 173 remove_last_tempo_with_pulses(smf_t *smf, int pulses)
nuclear@0 174 {
nuclear@0 175 smf_tempo_t *tempo;
nuclear@0 176
nuclear@0 177 /* XXX: This is a partial workaround for the following problem: we have two tempo-related
nuclear@0 178 events, A and B, that occur at the same time. We remove B, then try to remove
nuclear@0 179 A. However, both tempo changes got coalesced in new_tempo(), so it is impossible
nuclear@0 180 to remove B. */
nuclear@0 181 if (smf->tempo_array->len == 0)
nuclear@0 182 return;
nuclear@0 183
nuclear@0 184 tempo = smf_get_last_tempo(smf);
nuclear@0 185
nuclear@0 186 /* Workaround part two. */
nuclear@0 187 if (tempo->time_pulses != pulses)
nuclear@0 188 return;
nuclear@0 189
nuclear@0 190 memset(tempo, 0, sizeof(smf_tempo_t));
nuclear@0 191 free(tempo);
nuclear@0 192
nuclear@0 193 fg_ptr_array_remove_index(smf->tempo_array, smf->tempo_array->len - 1);
nuclear@0 194 }
nuclear@0 195
nuclear@0 196 static double
nuclear@0 197 seconds_from_pulses(const smf_t *smf, int pulses)
nuclear@0 198 {
nuclear@0 199 double seconds;
nuclear@0 200 smf_tempo_t *tempo;
nuclear@0 201
nuclear@0 202 tempo = smf_get_tempo_by_pulses(smf, pulses);
nuclear@0 203 assert(tempo);
nuclear@0 204 assert(tempo->time_pulses <= pulses);
nuclear@0 205
nuclear@0 206 seconds = tempo->time_seconds + (double)(pulses - tempo->time_pulses) *
nuclear@0 207 (tempo->microseconds_per_quarter_note / ((double)smf->ppqn * 1000000.0));
nuclear@0 208
nuclear@0 209 return (seconds);
nuclear@0 210 }
nuclear@0 211
nuclear@0 212 static int
nuclear@0 213 pulses_from_seconds(const smf_t *smf, double seconds)
nuclear@0 214 {
nuclear@0 215 int pulses = 0;
nuclear@0 216 smf_tempo_t *tempo;
nuclear@0 217
nuclear@0 218 tempo = smf_get_tempo_by_seconds(smf, seconds);
nuclear@0 219 assert(tempo);
nuclear@0 220 assert(tempo->time_seconds <= seconds);
nuclear@0 221
nuclear@0 222 pulses = tempo->time_pulses + (seconds - tempo->time_seconds) *
nuclear@0 223 ((double)smf->ppqn * 1000000.0 / tempo->microseconds_per_quarter_note);
nuclear@0 224
nuclear@0 225 return (pulses);
nuclear@0 226 }
nuclear@0 227
nuclear@0 228 /**
nuclear@0 229 * \internal
nuclear@0 230 *
nuclear@0 231 * Computes value of event->time_seconds for all events in smf.
nuclear@0 232 * Warning: rewinds the smf.
nuclear@0 233 */
nuclear@0 234 void
nuclear@0 235 smf_create_tempo_map_and_compute_seconds(smf_t *smf)
nuclear@0 236 {
nuclear@0 237 smf_event_t *event;
nuclear@0 238
nuclear@0 239 smf_rewind(smf);
nuclear@0 240 smf_init_tempo(smf);
nuclear@0 241
nuclear@0 242 for (;;) {
nuclear@0 243 event = smf_get_next_event(smf);
nuclear@0 244
nuclear@0 245 if (event == NULL)
nuclear@0 246 return;
nuclear@0 247
nuclear@0 248 maybe_add_to_tempo_map(event);
nuclear@0 249
nuclear@0 250 event->time_seconds = seconds_from_pulses(smf, event->time_pulses);
nuclear@0 251 }
nuclear@0 252
nuclear@0 253 /* Not reached. */
nuclear@0 254 }
nuclear@0 255
nuclear@0 256 smf_tempo_t *
nuclear@0 257 smf_get_tempo_by_number(const smf_t *smf, int number)
nuclear@0 258 {
nuclear@0 259 assert(number >= 0);
nuclear@0 260
nuclear@0 261 if (number >= smf->tempo_array->len)
nuclear@0 262 return (NULL);
nuclear@0 263
nuclear@0 264 return (fg_ptr_array_index(smf->tempo_array, number));
nuclear@0 265 }
nuclear@0 266
nuclear@0 267 /**
nuclear@0 268 * Return last tempo (i.e. tempo with greatest time_pulses) that happens before "pulses".
nuclear@0 269 */
nuclear@0 270 smf_tempo_t *
nuclear@0 271 smf_get_tempo_by_pulses(const smf_t *smf, int pulses)
nuclear@0 272 {
nuclear@0 273 int i;
nuclear@0 274 smf_tempo_t *tempo;
nuclear@0 275
nuclear@0 276 assert(pulses >= 0);
nuclear@0 277
nuclear@0 278 if (pulses == 0)
nuclear@0 279 return (smf_get_tempo_by_number(smf, 0));
nuclear@0 280
nuclear@0 281 assert(smf->tempo_array != NULL);
nuclear@0 282
nuclear@0 283 for (i = smf->tempo_array->len - 1; i >= 0; i--) {
nuclear@0 284 tempo = smf_get_tempo_by_number(smf, i);
nuclear@0 285
nuclear@0 286 assert(tempo);
nuclear@0 287 if (tempo->time_pulses < pulses)
nuclear@0 288 return (tempo);
nuclear@0 289 }
nuclear@0 290
nuclear@0 291 return (NULL);
nuclear@0 292 }
nuclear@0 293
nuclear@0 294 /**
nuclear@0 295 * Return last tempo (i.e. tempo with greatest time_seconds) that happens before "seconds".
nuclear@0 296 */
nuclear@0 297 smf_tempo_t *
nuclear@0 298 smf_get_tempo_by_seconds(const smf_t *smf, double seconds)
nuclear@0 299 {
nuclear@0 300 int i;
nuclear@0 301 smf_tempo_t *tempo;
nuclear@0 302
nuclear@0 303 assert(seconds >= 0.0);
nuclear@0 304
nuclear@0 305 if (seconds == 0.0)
nuclear@0 306 return (smf_get_tempo_by_number(smf, 0));
nuclear@0 307
nuclear@0 308 assert(smf->tempo_array != NULL);
nuclear@0 309
nuclear@0 310 for (i = smf->tempo_array->len - 1; i >= 0; i--) {
nuclear@0 311 tempo = smf_get_tempo_by_number(smf, i);
nuclear@0 312
nuclear@0 313 assert(tempo);
nuclear@0 314 if (tempo->time_seconds < seconds)
nuclear@0 315 return (tempo);
nuclear@0 316 }
nuclear@0 317
nuclear@0 318 return (NULL);
nuclear@0 319 }
nuclear@0 320
nuclear@0 321
nuclear@0 322 /**
nuclear@0 323 * Return last tempo.
nuclear@0 324 */
nuclear@0 325 smf_tempo_t *
nuclear@0 326 smf_get_last_tempo(const smf_t *smf)
nuclear@0 327 {
nuclear@0 328 smf_tempo_t *tempo;
nuclear@0 329
nuclear@0 330 tempo = smf_get_tempo_by_number(smf, smf->tempo_array->len - 1);
nuclear@0 331 assert(tempo);
nuclear@0 332
nuclear@0 333 return (tempo);
nuclear@0 334 }
nuclear@0 335
nuclear@0 336 /**
nuclear@0 337 * \internal
nuclear@0 338 *
nuclear@0 339 * Remove all smf_tempo_t structures from SMF.
nuclear@0 340 */
nuclear@0 341 void
nuclear@0 342 smf_fini_tempo(smf_t *smf)
nuclear@0 343 {
nuclear@0 344 smf_tempo_t *tempo;
nuclear@0 345
nuclear@0 346 while (smf->tempo_array->len > 0) {
nuclear@0 347 tempo = fg_ptr_array_index(smf->tempo_array, smf->tempo_array->len - 1);
nuclear@0 348 assert(tempo);
nuclear@0 349
nuclear@0 350 memset(tempo, 0, sizeof(smf_tempo_t));
nuclear@0 351 free(tempo);
nuclear@0 352
nuclear@0 353 fg_ptr_array_remove_index(smf->tempo_array, smf->tempo_array->len - 1);
nuclear@0 354 }
nuclear@0 355
nuclear@0 356 assert(smf->tempo_array->len == 0);
nuclear@0 357 }
nuclear@0 358
nuclear@0 359 /**
nuclear@0 360 * \internal
nuclear@0 361 *
nuclear@0 362 * Remove any existing tempos and add default one.
nuclear@0 363 *
nuclear@0 364 * \bug This will abort (by calling fg_error) if new_tempo() (memory allocation there) fails.
nuclear@0 365 */
nuclear@0 366 void
nuclear@0 367 smf_init_tempo(smf_t *smf)
nuclear@0 368 {
nuclear@0 369 smf_tempo_t *tempo;
nuclear@0 370
nuclear@0 371 smf_fini_tempo(smf);
nuclear@0 372
nuclear@0 373 tempo = new_tempo(smf, 0);
nuclear@0 374 if (tempo == NULL)
nuclear@0 375 fg_error("tempo_init failed, sorry.");
nuclear@0 376 }
nuclear@0 377
nuclear@0 378 /**
nuclear@0 379 * Returns ->time_pulses of last event on the given track, or 0, if track is empty.
nuclear@0 380 */
nuclear@0 381 static int
nuclear@0 382 last_event_pulses(const smf_track_t *track)
nuclear@0 383 {
nuclear@0 384 /* Get time of last event on this track. */
nuclear@0 385 if (track->number_of_events > 0) {
nuclear@0 386 smf_event_t *previous_event = smf_track_get_last_event(track);
nuclear@0 387 assert(previous_event);
nuclear@0 388 assert(previous_event->time_pulses >= 0);
nuclear@0 389
nuclear@0 390 return (previous_event->time_pulses);
nuclear@0 391 }
nuclear@0 392
nuclear@0 393 return (0);
nuclear@0 394 }
nuclear@0 395
nuclear@0 396 /**
nuclear@0 397 * Adds event to the track at the time "pulses" clocks from the previous event in this track.
nuclear@0 398 * The remaining two time fields will be computed automatically based on the third argument
nuclear@0 399 * and current tempo map. Note that ->delta_pulses is computed by smf.c:smf_track_add_event,
nuclear@0 400 * not here.
nuclear@0 401 */
nuclear@0 402 void
nuclear@0 403 smf_track_add_event_delta_pulses(smf_track_t *track, smf_event_t *event, int delta)
nuclear@0 404 {
nuclear@0 405 assert(delta >= 0);
nuclear@0 406 assert(event->time_pulses == -1);
nuclear@0 407 assert(event->time_seconds == -1.0);
nuclear@0 408 assert(track->smf != NULL);
nuclear@0 409
nuclear@0 410 smf_track_add_event_pulses(track, event, last_event_pulses(track) + delta);
nuclear@0 411 }
nuclear@0 412
nuclear@0 413 /**
nuclear@0 414 * Adds event to the track at the time "pulses" clocks from the start of song.
nuclear@0 415 * The remaining two time fields will be computed automatically based on the third argument
nuclear@0 416 * and current tempo map.
nuclear@0 417 */
nuclear@0 418 void
nuclear@0 419 smf_track_add_event_pulses(smf_track_t *track, smf_event_t *event, int pulses)
nuclear@0 420 {
nuclear@0 421 assert(pulses >= 0);
nuclear@0 422 assert(event->time_pulses == -1);
nuclear@0 423 assert(event->time_seconds == -1.0);
nuclear@0 424 assert(track->smf != NULL);
nuclear@0 425
nuclear@0 426 event->time_pulses = pulses;
nuclear@0 427 event->time_seconds = seconds_from_pulses(track->smf, pulses);
nuclear@0 428 smf_track_add_event(track, event);
nuclear@0 429 }
nuclear@0 430
nuclear@0 431 /**
nuclear@0 432 * Adds event to the track at the time "seconds" seconds from the start of song.
nuclear@0 433 * The remaining two time fields will be computed automatically based on the third argument
nuclear@0 434 * and current tempo map.
nuclear@0 435 */
nuclear@0 436 void
nuclear@0 437 smf_track_add_event_seconds(smf_track_t *track, smf_event_t *event, double seconds)
nuclear@0 438 {
nuclear@0 439 assert(seconds >= 0.0);
nuclear@0 440 assert(event->time_pulses == -1);
nuclear@0 441 assert(event->time_seconds == -1.0);
nuclear@0 442 assert(track->smf != NULL);
nuclear@0 443
nuclear@0 444 event->time_seconds = seconds;
nuclear@0 445 event->time_pulses = pulses_from_seconds(track->smf, seconds);
nuclear@0 446 smf_track_add_event(track, event);
nuclear@0 447 }
nuclear@0 448