smflite

annotate src/smf.c @ 2:d9e0d0500a78

added COPYING and README
author John Tsiombikas <nuclear@member.fsf.org>
date Thu, 26 Jan 2012 15:51:53 +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 * Various functions.
nuclear@0 32 *
nuclear@0 33 */
nuclear@0 34
nuclear@0 35 /* Reference: http://www.borg.com/~jglatt/tech/midifile.htm */
nuclear@0 36
nuclear@0 37 #include <stdlib.h>
nuclear@0 38 #include <string.h>
nuclear@0 39 #include <assert.h>
nuclear@0 40 #include <math.h>
nuclear@0 41 #include <errno.h>
nuclear@0 42 #ifdef __MINGW32__
nuclear@0 43 #include <windows.h>
nuclear@0 44 #else /* ! __MINGW32__ */
nuclear@0 45 #include <arpa/inet.h>
nuclear@0 46 #endif /* ! __MINGW32__ */
nuclear@0 47 #include "smf.h"
nuclear@0 48 #include "smf_private.h"
nuclear@0 49
nuclear@0 50 /**
nuclear@0 51 * Allocates new smf_t structure.
nuclear@0 52 * \return pointer to smf_t or NULL.
nuclear@0 53 */
nuclear@0 54 smf_t *
nuclear@0 55 smf_new(void)
nuclear@0 56 {
nuclear@0 57 int cantfail;
nuclear@0 58
nuclear@0 59 smf_t *smf = malloc(sizeof(smf_t));
nuclear@0 60 if (smf == NULL) {
nuclear@0 61 fg_critical("Cannot allocate smf_t structure: %s", strerror(errno));
nuclear@0 62 return (NULL);
nuclear@0 63 }
nuclear@0 64
nuclear@0 65 memset(smf, 0, sizeof(smf_t));
nuclear@0 66
nuclear@0 67 smf->tracks_array = fg_ptr_array_new();
nuclear@0 68 assert(smf->tracks_array);
nuclear@0 69
nuclear@0 70 smf->tempo_array = fg_ptr_array_new();
nuclear@0 71 assert(smf->tempo_array);
nuclear@0 72
nuclear@0 73 cantfail = smf_set_ppqn(smf, 120);
nuclear@0 74 assert(!cantfail);
nuclear@0 75
nuclear@0 76 cantfail = smf_set_format(smf, 0);
nuclear@0 77 assert(!cantfail);
nuclear@0 78
nuclear@0 79 smf_init_tempo(smf);
nuclear@0 80
nuclear@0 81 return (smf);
nuclear@0 82 }
nuclear@0 83
nuclear@0 84 /**
nuclear@0 85 * Frees smf and all it's descendant structures.
nuclear@0 86 */
nuclear@0 87 void
nuclear@0 88 smf_delete(smf_t *smf)
nuclear@0 89 {
nuclear@0 90 /* Remove all the tracks, from last to first. */
nuclear@0 91 while (smf->tracks_array->len > 0)
nuclear@0 92 smf_track_delete(fg_ptr_array_index(smf->tracks_array, smf->tracks_array->len - 1));
nuclear@0 93
nuclear@0 94 smf_fini_tempo(smf);
nuclear@0 95
nuclear@0 96 assert(smf->tracks_array->len == 0);
nuclear@0 97 assert(smf->number_of_tracks == 0);
nuclear@0 98 fg_ptr_array_free(smf->tracks_array, TRUE);
nuclear@0 99 fg_ptr_array_free(smf->tempo_array, TRUE);
nuclear@0 100
nuclear@0 101 memset(smf, 0, sizeof(smf_t));
nuclear@0 102 free(smf);
nuclear@0 103 }
nuclear@0 104
nuclear@0 105 /**
nuclear@0 106 * Allocates new smf_track_t structure.
nuclear@0 107 * \return pointer to smf_track_t or NULL.
nuclear@0 108 */
nuclear@0 109 smf_track_t *
nuclear@0 110 smf_track_new(void)
nuclear@0 111 {
nuclear@0 112 smf_track_t *track = malloc(sizeof(smf_track_t));
nuclear@0 113 if (track == NULL) {
nuclear@0 114 fg_critical("Cannot allocate smf_track_t structure: %s", strerror(errno));
nuclear@0 115 return (NULL);
nuclear@0 116 }
nuclear@0 117
nuclear@0 118 memset(track, 0, sizeof(smf_track_t));
nuclear@0 119 track->next_event_number = -1;
nuclear@0 120
nuclear@0 121 track->events_array = fg_ptr_array_new();
nuclear@0 122 assert(track->events_array);
nuclear@0 123
nuclear@0 124 return (track);
nuclear@0 125 }
nuclear@0 126
nuclear@0 127 /**
nuclear@0 128 * Detaches track from its smf and frees it.
nuclear@0 129 */
nuclear@0 130 void
nuclear@0 131 smf_track_delete(smf_track_t *track)
nuclear@0 132 {
nuclear@0 133 assert(track);
nuclear@0 134 assert(track->events_array);
nuclear@0 135
nuclear@0 136 /* Remove all the events, from last to first. */
nuclear@0 137 while (track->events_array->len > 0)
nuclear@0 138 smf_event_delete(fg_ptr_array_index(track->events_array, track->events_array->len - 1));
nuclear@0 139
nuclear@0 140 if (track->smf)
nuclear@0 141 smf_track_remove_from_smf(track);
nuclear@0 142
nuclear@0 143 assert(track->events_array->len == 0);
nuclear@0 144 assert(track->number_of_events == 0);
nuclear@0 145 fg_ptr_array_free(track->events_array, TRUE);
nuclear@0 146
nuclear@0 147 memset(track, 0, sizeof(smf_track_t));
nuclear@0 148 free(track);
nuclear@0 149 }
nuclear@0 150
nuclear@0 151
nuclear@0 152 /**
nuclear@0 153 * Appends smf_track_t to smf.
nuclear@0 154 */
nuclear@0 155 void
nuclear@0 156 smf_add_track(smf_t *smf, smf_track_t *track)
nuclear@0 157 {
nuclear@0 158 int cantfail;
nuclear@0 159
nuclear@0 160 assert(track->smf == NULL);
nuclear@0 161
nuclear@0 162 track->smf = smf;
nuclear@0 163 fg_ptr_array_add(smf->tracks_array, track);
nuclear@0 164
nuclear@0 165 smf->number_of_tracks++;
nuclear@0 166 track->track_number = smf->number_of_tracks;
nuclear@0 167
nuclear@0 168 if (smf->number_of_tracks > 1) {
nuclear@0 169 cantfail = smf_set_format(smf, 1);
nuclear@0 170 assert(!cantfail);
nuclear@0 171 }
nuclear@0 172 }
nuclear@0 173
nuclear@0 174 /**
nuclear@0 175 * Detaches track from the smf.
nuclear@0 176 */
nuclear@0 177 void
nuclear@0 178 smf_track_remove_from_smf(smf_track_t *track)
nuclear@0 179 {
nuclear@0 180 int i, j;
nuclear@0 181 smf_track_t *tmp;
nuclear@0 182 smf_event_t *ev;
nuclear@0 183
nuclear@0 184 assert(track->smf != NULL);
nuclear@0 185
nuclear@0 186 track->smf->number_of_tracks--;
nuclear@0 187
nuclear@0 188 assert(track->smf->tracks_array);
nuclear@0 189 fg_ptr_array_remove(track->smf->tracks_array, track);
nuclear@0 190
nuclear@0 191 /* Renumber the rest of the tracks, so they are consecutively numbered. */
nuclear@0 192 for (i = track->track_number; i <= track->smf->number_of_tracks; i++) {
nuclear@0 193 tmp = smf_get_track_by_number(track->smf, i);
nuclear@0 194 tmp->track_number = i;
nuclear@0 195
nuclear@0 196 /*
nuclear@0 197 * Events have track numbers too. I guess this wasn't a wise
nuclear@0 198 * decision. ;-/
nuclear@0 199 */
nuclear@0 200 for (j = 1; j <= tmp->number_of_events; j++) {
nuclear@0 201 ev = smf_track_get_event_by_number(tmp, j);
nuclear@0 202 ev->track_number = i;
nuclear@0 203 }
nuclear@0 204 }
nuclear@0 205
nuclear@0 206 track->track_number = -1;
nuclear@0 207 track->smf = NULL;
nuclear@0 208 }
nuclear@0 209
nuclear@0 210 /**
nuclear@0 211 * Allocates new smf_event_t structure. The caller is responsible for allocating
nuclear@0 212 * event->midi_buffer, filling it with MIDI data and setting event->midi_buffer_length properly.
nuclear@0 213 * Note that event->midi_buffer will be freed by smf_event_delete.
nuclear@0 214 * \return pointer to smf_event_t or NULL.
nuclear@0 215 */
nuclear@0 216 smf_event_t *
nuclear@0 217 smf_event_new(void)
nuclear@0 218 {
nuclear@0 219 smf_event_t *event = malloc(sizeof(smf_event_t));
nuclear@0 220 if (event == NULL) {
nuclear@0 221 fg_critical("Cannot allocate smf_event_t structure: %s", strerror(errno));
nuclear@0 222 return (NULL);
nuclear@0 223 }
nuclear@0 224
nuclear@0 225 memset(event, 0, sizeof(smf_event_t));
nuclear@0 226
nuclear@0 227 event->delta_time_pulses = -1;
nuclear@0 228 event->time_pulses = -1;
nuclear@0 229 event->time_seconds = -1.0;
nuclear@0 230 event->track_number = -1;
nuclear@0 231
nuclear@0 232 return (event);
nuclear@0 233 }
nuclear@0 234
nuclear@0 235 /**
nuclear@0 236 * Allocates an smf_event_t structure and fills it with "len" bytes copied
nuclear@0 237 * from "midi_data".
nuclear@0 238 * \param midi_data Pointer to MIDI data. It sill be copied to the newly allocated event->midi_buffer.
nuclear@0 239 * \param len Length of the buffer. It must be proper MIDI event length, e.g. 3 for Note On event.
nuclear@0 240 * \return Event containing MIDI data or NULL.
nuclear@0 241 */
nuclear@0 242 smf_event_t *
nuclear@0 243 smf_event_new_from_pointer(void *midi_data, int len)
nuclear@0 244 {
nuclear@0 245 smf_event_t *event;
nuclear@0 246
nuclear@0 247 event = smf_event_new();
nuclear@0 248 if (event == NULL)
nuclear@0 249 return (NULL);
nuclear@0 250
nuclear@0 251 event->midi_buffer_length = len;
nuclear@0 252 event->midi_buffer = malloc(event->midi_buffer_length);
nuclear@0 253 if (event->midi_buffer == NULL) {
nuclear@0 254 fg_critical("Cannot allocate MIDI buffer structure: %s", strerror(errno));
nuclear@0 255 smf_event_delete(event);
nuclear@0 256
nuclear@0 257 return (NULL);
nuclear@0 258 }
nuclear@0 259
nuclear@0 260 memcpy(event->midi_buffer, midi_data, len);
nuclear@0 261
nuclear@0 262 return (event);
nuclear@0 263 }
nuclear@0 264
nuclear@0 265 /**
nuclear@0 266 * Allocates an smf_event_t structure and fills it with at most three bytes of data.
nuclear@0 267 * For example, if you need to create Note On event, do something like this:
nuclear@0 268 *
nuclear@0 269 * smf_event_new_from_bytes(0x90, 0x3C, 0x7f);
nuclear@0 270 *
nuclear@0 271 * To create event for MIDI message that is shorter than three bytes, do something
nuclear@0 272 * like this:
nuclear@0 273 *
nuclear@0 274 * smf_event_new_from_bytes(0xC0, 0x42, -1);
nuclear@0 275 *
nuclear@0 276 * \param first_byte First byte of MIDI message. Must be valid status byte.
nuclear@0 277 * \param second_byte Second byte of MIDI message or -1, if message is one byte long.
nuclear@0 278 * \param third_byte Third byte of MIDI message or -1, if message is two bytes long.
nuclear@0 279 * \return Event containing MIDI data or NULL.
nuclear@0 280 */
nuclear@0 281 smf_event_t *
nuclear@0 282 smf_event_new_from_bytes(int first_byte, int second_byte, int third_byte)
nuclear@0 283 {
nuclear@0 284 int len;
nuclear@0 285
nuclear@0 286 smf_event_t *event;
nuclear@0 287
nuclear@0 288 event = smf_event_new();
nuclear@0 289 if (event == NULL)
nuclear@0 290 return (NULL);
nuclear@0 291
nuclear@0 292 if (first_byte < 0) {
nuclear@0 293 fg_critical("First byte of MIDI message cannot be < 0");
nuclear@0 294 smf_event_delete(event);
nuclear@0 295
nuclear@0 296 return (NULL);
nuclear@0 297 }
nuclear@0 298
nuclear@0 299 if (first_byte > 255) {
nuclear@0 300 fg_critical("smf_event_new_from_bytes: first byte is %d, which is larger than 255.", first_byte);
nuclear@0 301 return (NULL);
nuclear@0 302 }
nuclear@0 303
nuclear@0 304 if (!is_status_byte(first_byte)) {
nuclear@0 305 fg_critical("smf_event_new_from_bytes: first byte is not a valid status byte.");
nuclear@0 306 return (NULL);
nuclear@0 307 }
nuclear@0 308
nuclear@0 309
nuclear@0 310 if (second_byte < 0)
nuclear@0 311 len = 1;
nuclear@0 312 else if (third_byte < 0)
nuclear@0 313 len = 2;
nuclear@0 314 else
nuclear@0 315 len = 3;
nuclear@0 316
nuclear@0 317 if (len > 1) {
nuclear@0 318 if (second_byte > 255) {
nuclear@0 319 fg_critical("smf_event_new_from_bytes: second byte is %d, which is larger than 255.", second_byte);
nuclear@0 320 return (NULL);
nuclear@0 321 }
nuclear@0 322
nuclear@0 323 if (is_status_byte(second_byte)) {
nuclear@0 324 fg_critical("smf_event_new_from_bytes: second byte cannot be a status byte.");
nuclear@0 325 return (NULL);
nuclear@0 326 }
nuclear@0 327 }
nuclear@0 328
nuclear@0 329 if (len > 2) {
nuclear@0 330 if (third_byte > 255) {
nuclear@0 331 fg_critical("smf_event_new_from_bytes: third byte is %d, which is larger than 255.", third_byte);
nuclear@0 332 return (NULL);
nuclear@0 333 }
nuclear@0 334
nuclear@0 335 if (is_status_byte(third_byte)) {
nuclear@0 336 fg_critical("smf_event_new_from_bytes: third byte cannot be a status byte.");
nuclear@0 337 return (NULL);
nuclear@0 338 }
nuclear@0 339 }
nuclear@0 340
nuclear@0 341 event->midi_buffer_length = len;
nuclear@0 342 event->midi_buffer = malloc(event->midi_buffer_length);
nuclear@0 343 if (event->midi_buffer == NULL) {
nuclear@0 344 fg_critical("Cannot allocate MIDI buffer structure: %s", strerror(errno));
nuclear@0 345 smf_event_delete(event);
nuclear@0 346
nuclear@0 347 return (NULL);
nuclear@0 348 }
nuclear@0 349
nuclear@0 350 event->midi_buffer[0] = first_byte;
nuclear@0 351 if (len > 1)
nuclear@0 352 event->midi_buffer[1] = second_byte;
nuclear@0 353 if (len > 2)
nuclear@0 354 event->midi_buffer[2] = third_byte;
nuclear@0 355
nuclear@0 356 return (event);
nuclear@0 357 }
nuclear@0 358
nuclear@0 359 /**
nuclear@0 360 * Detaches event from its track and frees it.
nuclear@0 361 */
nuclear@0 362 void
nuclear@0 363 smf_event_delete(smf_event_t *event)
nuclear@0 364 {
nuclear@0 365 if (event->track != NULL)
nuclear@0 366 smf_event_remove_from_track(event);
nuclear@0 367
nuclear@0 368 if (event->midi_buffer != NULL) {
nuclear@0 369 memset(event->midi_buffer, 0, event->midi_buffer_length);
nuclear@0 370 free(event->midi_buffer);
nuclear@0 371 }
nuclear@0 372
nuclear@0 373 memset(event, 0, sizeof(smf_event_t));
nuclear@0 374 free(event);
nuclear@0 375 }
nuclear@0 376
nuclear@0 377 /**
nuclear@0 378 * Used for sorting track->events_array.
nuclear@0 379 */
nuclear@0 380 static gint
nuclear@0 381 events_array_compare_function(gconstpointer aa, gconstpointer bb)
nuclear@0 382 {
nuclear@0 383 smf_event_t *a, *b;
nuclear@0 384
nuclear@0 385 /* "The comparison function for fg_ptr_array_sort() doesn't take the pointers
nuclear@0 386 from the array as arguments, it takes pointers to the pointers in the array." */
nuclear@0 387 a = (smf_event_t *)*(gpointer *)aa;
nuclear@0 388 b = (smf_event_t *)*(gpointer *)bb;
nuclear@0 389
nuclear@0 390 if (a->time_pulses < b->time_pulses)
nuclear@0 391 return (-1);
nuclear@0 392
nuclear@0 393 if (a->time_pulses > b->time_pulses)
nuclear@0 394 return (1);
nuclear@0 395
nuclear@0 396 /*
nuclear@0 397 * We need to preserve original order, otherwise things will break
nuclear@0 398 * when there are several events with the same ->time_pulses.
nuclear@0 399 * XXX: This is an ugly hack; we should remove sorting altogether.
nuclear@0 400 */
nuclear@0 401
nuclear@0 402 if (a->event_number < b->event_number)
nuclear@0 403 return (-1);
nuclear@0 404
nuclear@0 405 if (a->event_number > b->event_number)
nuclear@0 406 return (1);
nuclear@0 407
nuclear@0 408 return (0);
nuclear@0 409 }
nuclear@0 410
nuclear@0 411 /*
nuclear@0 412 * An assumption here is that if there is an EOT event, it will be at the end of the track.
nuclear@0 413 */
nuclear@0 414 static void
nuclear@0 415 remove_eot_if_before_pulses(smf_track_t *track, int pulses)
nuclear@0 416 {
nuclear@0 417 smf_event_t *event;
nuclear@0 418
nuclear@0 419 event = smf_track_get_last_event(track);
nuclear@0 420
nuclear@0 421 if (event == NULL)
nuclear@0 422 return;
nuclear@0 423
nuclear@0 424 if (!smf_event_is_eot(event))
nuclear@0 425 return;
nuclear@0 426
nuclear@0 427 if (event->time_pulses > pulses)
nuclear@0 428 return;
nuclear@0 429
nuclear@0 430 smf_event_remove_from_track(event);
nuclear@0 431 }
nuclear@0 432
nuclear@0 433 /**
nuclear@0 434 * Adds the event to the track and computes ->delta_pulses. Note that it is faster
nuclear@0 435 * to append events to the end of the track than to insert them in the middle.
nuclear@0 436 * Usually you want to use smf_track_add_event_seconds or smf_track_add_event_pulses
nuclear@0 437 * instead of this one. Event needs to have ->time_pulses and ->time_seconds already set.
nuclear@0 438 * If you try to add event after an EOT, EOT event will be automatically deleted.
nuclear@0 439 */
nuclear@0 440 void
nuclear@0 441 smf_track_add_event(smf_track_t *track, smf_event_t *event)
nuclear@0 442 {
nuclear@0 443 int i, last_pulses = 0;
nuclear@0 444
nuclear@0 445 assert(track->smf != NULL);
nuclear@0 446 assert(event->track == NULL);
nuclear@0 447 assert(event->delta_time_pulses == -1);
nuclear@0 448 assert(event->time_pulses >= 0);
nuclear@0 449 assert(event->time_seconds >= 0.0);
nuclear@0 450
nuclear@0 451 remove_eot_if_before_pulses(track, event->time_pulses);
nuclear@0 452
nuclear@0 453 event->track = track;
nuclear@0 454 event->track_number = track->track_number;
nuclear@0 455
nuclear@0 456 if (track->number_of_events == 0) {
nuclear@0 457 assert(track->next_event_number == -1);
nuclear@0 458 track->next_event_number = 1;
nuclear@0 459 }
nuclear@0 460
nuclear@0 461 if (track->number_of_events > 0)
nuclear@0 462 last_pulses = smf_track_get_last_event(track)->time_pulses;
nuclear@0 463
nuclear@0 464 track->number_of_events++;
nuclear@0 465
nuclear@0 466 /* Are we just appending element at the end of the track? */
nuclear@0 467 if (last_pulses <= event->time_pulses) {
nuclear@0 468 event->delta_time_pulses = event->time_pulses - last_pulses;
nuclear@0 469 assert(event->delta_time_pulses >= 0);
nuclear@0 470 fg_ptr_array_add(track->events_array, event);
nuclear@0 471 event->event_number = track->number_of_events;
nuclear@0 472
nuclear@0 473 /* We need to insert in the middle of the track. XXX: This is slow. */
nuclear@0 474 } else {
nuclear@0 475 /* Append, then sort according to ->time_pulses. */
nuclear@0 476 fg_ptr_array_add(track->events_array, event);
nuclear@0 477 fg_ptr_array_sort(track->events_array, events_array_compare_function);
nuclear@0 478
nuclear@0 479 /* Renumber entries and fix their ->delta_pulses. */
nuclear@0 480 for (i = 1; i <= track->number_of_events; i++) {
nuclear@0 481 smf_event_t *tmp = smf_track_get_event_by_number(track, i);
nuclear@0 482 tmp->event_number = i;
nuclear@0 483
nuclear@0 484 if (tmp->delta_time_pulses != -1)
nuclear@0 485 continue;
nuclear@0 486
nuclear@0 487 if (i == 1) {
nuclear@0 488 tmp->delta_time_pulses = tmp->time_pulses;
nuclear@0 489 } else {
nuclear@0 490 tmp->delta_time_pulses = tmp->time_pulses -
nuclear@0 491 smf_track_get_event_by_number(track, i - 1)->time_pulses;
nuclear@0 492 assert(tmp->delta_time_pulses >= 0);
nuclear@0 493 }
nuclear@0 494 }
nuclear@0 495
nuclear@0 496 /* Adjust ->delta_time_pulses of the next event. */
nuclear@0 497 if (event->event_number < track->number_of_events) {
nuclear@0 498 smf_event_t *next_event = smf_track_get_event_by_number(track, event->event_number + 1);
nuclear@0 499 assert(next_event);
nuclear@0 500 assert(next_event->time_pulses >= event->time_pulses);
nuclear@0 501 next_event->delta_time_pulses -= event->delta_time_pulses;
nuclear@0 502 assert(next_event->delta_time_pulses >= 0);
nuclear@0 503 }
nuclear@0 504 }
nuclear@0 505
nuclear@0 506 if (smf_event_is_tempo_change_or_time_signature(event)) {
nuclear@0 507 if (smf_event_is_last(event))
nuclear@0 508 maybe_add_to_tempo_map(event);
nuclear@0 509 else
nuclear@0 510 smf_create_tempo_map_and_compute_seconds(event->track->smf);
nuclear@0 511 }
nuclear@0 512 }
nuclear@0 513
nuclear@0 514 /**
nuclear@0 515 * Add End Of Track metaevent. Using it is optional, libsmf will automatically
nuclear@0 516 * add EOT to the tracks during smf_save, with delta_pulses 0. If you try to add EOT
nuclear@0 517 * in the middle of the track, it will fail and nonzero value will be returned.
nuclear@0 518 * If you try to add EOT after another EOT event, it will be added, but the existing
nuclear@0 519 * EOT event will be removed.
nuclear@0 520 *
nuclear@0 521 * \return 0 if everything went ok, nonzero otherwise.
nuclear@0 522 */
nuclear@0 523 int
nuclear@0 524 smf_track_add_eot_delta_pulses(smf_track_t *track, int delta)
nuclear@0 525 {
nuclear@0 526 smf_event_t *event;
nuclear@0 527
nuclear@0 528 event = smf_event_new_from_bytes(0xFF, 0x2F, 0x00);
nuclear@0 529 if (event == NULL)
nuclear@0 530 return (-1);
nuclear@0 531
nuclear@0 532 smf_track_add_event_delta_pulses(track, event, delta);
nuclear@0 533
nuclear@0 534 return (0);
nuclear@0 535 }
nuclear@0 536
nuclear@0 537 int
nuclear@0 538 smf_track_add_eot_pulses(smf_track_t *track, int pulses)
nuclear@0 539 {
nuclear@0 540 smf_event_t *event, *last_event;
nuclear@0 541
nuclear@0 542 last_event = smf_track_get_last_event(track);
nuclear@0 543 if (last_event != NULL) {
nuclear@0 544 if (last_event->time_pulses > pulses)
nuclear@0 545 return (-2);
nuclear@0 546 }
nuclear@0 547
nuclear@0 548 event = smf_event_new_from_bytes(0xFF, 0x2F, 0x00);
nuclear@0 549 if (event == NULL)
nuclear@0 550 return (-3);
nuclear@0 551
nuclear@0 552 smf_track_add_event_pulses(track, event, pulses);
nuclear@0 553
nuclear@0 554 return (0);
nuclear@0 555 }
nuclear@0 556
nuclear@0 557 int
nuclear@0 558 smf_track_add_eot_seconds(smf_track_t *track, double seconds)
nuclear@0 559 {
nuclear@0 560 smf_event_t *event, *last_event;
nuclear@0 561
nuclear@0 562 last_event = smf_track_get_last_event(track);
nuclear@0 563 if (last_event != NULL) {
nuclear@0 564 if (last_event->time_seconds > seconds)
nuclear@0 565 return (-2);
nuclear@0 566 }
nuclear@0 567
nuclear@0 568 event = smf_event_new_from_bytes(0xFF, 0x2F, 0x00);
nuclear@0 569 if (event == NULL)
nuclear@0 570 return (-1);
nuclear@0 571
nuclear@0 572 smf_track_add_event_seconds(track, event, seconds);
nuclear@0 573
nuclear@0 574 return (0);
nuclear@0 575 }
nuclear@0 576
nuclear@0 577 /**
nuclear@0 578 * Detaches event from its track.
nuclear@0 579 */
nuclear@0 580 void
nuclear@0 581 smf_event_remove_from_track(smf_event_t *event)
nuclear@0 582 {
nuclear@0 583 int i, was_last;
nuclear@0 584 smf_event_t *tmp;
nuclear@0 585 smf_track_t *track;
nuclear@0 586
nuclear@0 587 assert(event->track != NULL);
nuclear@0 588 assert(event->track->smf != NULL);
nuclear@0 589
nuclear@0 590 track = event->track;
nuclear@0 591 was_last = smf_event_is_last(event);
nuclear@0 592
nuclear@0 593 /* Adjust ->delta_time_pulses of the next event. */
nuclear@0 594 if (event->event_number < track->number_of_events) {
nuclear@0 595 tmp = smf_track_get_event_by_number(track, event->event_number + 1);
nuclear@0 596 assert(tmp);
nuclear@0 597 tmp->delta_time_pulses += event->delta_time_pulses;
nuclear@0 598 }
nuclear@0 599
nuclear@0 600 track->number_of_events--;
nuclear@0 601 fg_ptr_array_remove(track->events_array, event);
nuclear@0 602
nuclear@0 603 if (track->number_of_events == 0)
nuclear@0 604 track->next_event_number = -1;
nuclear@0 605
nuclear@0 606 /* Renumber the rest of the events, so they are consecutively numbered. */
nuclear@0 607 for (i = event->event_number; i <= track->number_of_events; i++) {
nuclear@0 608 tmp = smf_track_get_event_by_number(track, i);
nuclear@0 609 tmp->event_number = i;
nuclear@0 610 }
nuclear@0 611
nuclear@0 612 if (smf_event_is_tempo_change_or_time_signature(event)) {
nuclear@0 613 /* XXX: This will cause problems, when there is more than one Tempo Change event at a given time. */
nuclear@0 614 if (was_last)
nuclear@0 615 remove_last_tempo_with_pulses(event->track->smf, event->time_pulses);
nuclear@0 616 else
nuclear@0 617 smf_create_tempo_map_and_compute_seconds(track->smf);
nuclear@0 618 }
nuclear@0 619
nuclear@0 620 event->track = NULL;
nuclear@0 621 event->event_number = -1;
nuclear@0 622 event->delta_time_pulses = -1;
nuclear@0 623 event->time_pulses = -1;
nuclear@0 624 event->time_seconds = -1.0;
nuclear@0 625 }
nuclear@0 626
nuclear@0 627 /**
nuclear@0 628 * \return Nonzero if event is Tempo Change or Time Signature metaevent.
nuclear@0 629 */
nuclear@0 630 int
nuclear@0 631 smf_event_is_tempo_change_or_time_signature(const smf_event_t *event)
nuclear@0 632 {
nuclear@0 633 if (!smf_event_is_metadata(event))
nuclear@0 634 return (0);
nuclear@0 635
nuclear@0 636 assert(event->midi_buffer_length >= 2);
nuclear@0 637
nuclear@0 638 if (event->midi_buffer[1] == 0x51 || event->midi_buffer[1] == 0x58)
nuclear@0 639 return (1);
nuclear@0 640
nuclear@0 641 return (0);
nuclear@0 642 }
nuclear@0 643
nuclear@0 644 /**
nuclear@0 645 * Sets "Format" field of MThd header to the specified value. Note that you
nuclear@0 646 * don't really need to use this, as libsmf will automatically change format
nuclear@0 647 * from 0 to 1 when you add the second track.
nuclear@0 648 * \param smf SMF.
nuclear@0 649 * \param format 0 for one track per file, 1 for several tracks per file.
nuclear@0 650 */
nuclear@0 651 int
nuclear@0 652 smf_set_format(smf_t *smf, int format)
nuclear@0 653 {
nuclear@0 654 assert(format == 0 || format == 1);
nuclear@0 655
nuclear@0 656 if (smf->number_of_tracks > 1 && format == 0) {
nuclear@0 657 fg_critical("There is more than one track, cannot set format to 0.");
nuclear@0 658 return (-1);
nuclear@0 659 }
nuclear@0 660
nuclear@0 661 smf->format = format;
nuclear@0 662
nuclear@0 663 return (0);
nuclear@0 664 }
nuclear@0 665
nuclear@0 666 /**
nuclear@0 667 * Sets the PPQN ("Division") field of MThd header. This is mandatory, you
nuclear@0 668 * should call it right after smf_new. Note that changing PPQN will change time_seconds
nuclear@0 669 * of all the events.
nuclear@0 670 * \param smf SMF.
nuclear@0 671 * \param ppqn New PPQN.
nuclear@0 672 */
nuclear@0 673 int
nuclear@0 674 smf_set_ppqn(smf_t *smf, int ppqn)
nuclear@0 675 {
nuclear@0 676 assert(ppqn > 0);
nuclear@0 677
nuclear@0 678 smf->ppqn = ppqn;
nuclear@0 679
nuclear@0 680 return (0);
nuclear@0 681 }
nuclear@0 682
nuclear@0 683 /**
nuclear@0 684 * Returns next event from the track given and advances next event counter.
nuclear@0 685 * Do not depend on End Of Track event being the last event on the track - it
nuclear@0 686 * is possible that the track will not end with EOT if you haven't added it
nuclear@0 687 * yet. EOTs are added automatically during smf_save().
nuclear@0 688 *
nuclear@0 689 * \return Event or NULL, if there are no more events left in this track.
nuclear@0 690 */
nuclear@0 691 smf_event_t *
nuclear@0 692 smf_track_get_next_event(smf_track_t *track)
nuclear@0 693 {
nuclear@0 694 smf_event_t *event, *next_event;
nuclear@0 695
nuclear@0 696 /* End of track? */
nuclear@0 697 if (track->next_event_number == -1)
nuclear@0 698 return (NULL);
nuclear@0 699
nuclear@0 700 assert(track->next_event_number >= 1);
nuclear@0 701 assert(track->number_of_events > 0);
nuclear@0 702
nuclear@0 703 event = smf_track_get_event_by_number(track, track->next_event_number);
nuclear@0 704
nuclear@0 705 assert(event != NULL);
nuclear@0 706
nuclear@0 707 /* Is this the last event in the track? */
nuclear@0 708 if (track->next_event_number < track->number_of_events) {
nuclear@0 709 next_event = smf_track_get_event_by_number(track, track->next_event_number + 1);
nuclear@0 710 assert(next_event);
nuclear@0 711
nuclear@0 712 track->time_of_next_event = next_event->time_pulses;
nuclear@0 713 track->next_event_number++;
nuclear@0 714 } else {
nuclear@0 715 track->next_event_number = -1;
nuclear@0 716 }
nuclear@0 717
nuclear@0 718 return (event);
nuclear@0 719 }
nuclear@0 720
nuclear@0 721 /**
nuclear@0 722 * Returns next event from the track given. Does not change next event counter,
nuclear@0 723 * so repeatedly calling this routine will return the same event.
nuclear@0 724 * \return Event or NULL, if there are no more events left in this track.
nuclear@0 725 */
nuclear@0 726 static smf_event_t *
nuclear@0 727 smf_peek_next_event_from_track(smf_track_t *track)
nuclear@0 728 {
nuclear@0 729 smf_event_t *event;
nuclear@0 730
nuclear@0 731 /* End of track? */
nuclear@0 732 if (track->next_event_number == -1)
nuclear@0 733 return (NULL);
nuclear@0 734
nuclear@0 735 assert(track->next_event_number >= 1);
nuclear@0 736 assert(track->events_array->len != 0);
nuclear@0 737
nuclear@0 738 event = smf_track_get_event_by_number(track, track->next_event_number);
nuclear@0 739
nuclear@0 740 return (event);
nuclear@0 741 }
nuclear@0 742
nuclear@0 743 /**
nuclear@0 744 * \return Track with a given number or NULL, if there is no such track.
nuclear@0 745 * Tracks are numbered consecutively starting from one.
nuclear@0 746 */
nuclear@0 747 smf_track_t *
nuclear@0 748 smf_get_track_by_number(const smf_t *smf, int track_number)
nuclear@0 749 {
nuclear@0 750 smf_track_t *track;
nuclear@0 751
nuclear@0 752 assert(track_number >= 1);
nuclear@0 753
nuclear@0 754 if (track_number > smf->number_of_tracks)
nuclear@0 755 return (NULL);
nuclear@0 756
nuclear@0 757 track = (smf_track_t *)fg_ptr_array_index(smf->tracks_array, track_number - 1);
nuclear@0 758
nuclear@0 759 assert(track);
nuclear@0 760
nuclear@0 761 return (track);
nuclear@0 762 }
nuclear@0 763
nuclear@0 764 /**
nuclear@0 765 * \return Event with a given number or NULL, if there is no such event.
nuclear@0 766 * Events are numbered consecutively starting from one.
nuclear@0 767 */
nuclear@0 768 smf_event_t *
nuclear@0 769 smf_track_get_event_by_number(const smf_track_t *track, int event_number)
nuclear@0 770 {
nuclear@0 771 smf_event_t *event;
nuclear@0 772
nuclear@0 773 assert(event_number >= 1);
nuclear@0 774
nuclear@0 775 if (event_number > track->number_of_events)
nuclear@0 776 return (NULL);
nuclear@0 777
nuclear@0 778 event = fg_ptr_array_index(track->events_array, event_number - 1);
nuclear@0 779
nuclear@0 780 assert(event);
nuclear@0 781
nuclear@0 782 return (event);
nuclear@0 783 }
nuclear@0 784
nuclear@0 785 /**
nuclear@0 786 * \return Last event on the track or NULL, if track is empty.
nuclear@0 787 */
nuclear@0 788 smf_event_t *
nuclear@0 789 smf_track_get_last_event(const smf_track_t *track)
nuclear@0 790 {
nuclear@0 791 smf_event_t *event;
nuclear@0 792
nuclear@0 793 if (track->number_of_events == 0)
nuclear@0 794 return (NULL);
nuclear@0 795
nuclear@0 796 event = smf_track_get_event_by_number(track, track->number_of_events);
nuclear@0 797
nuclear@0 798 return (event);
nuclear@0 799 }
nuclear@0 800
nuclear@0 801 /**
nuclear@0 802 * Searches for track that contains next event, in time order. In other words,
nuclear@0 803 * returns the track that contains event that should be played next.
nuclear@0 804 * \return Track with next event or NULL, if there are no events left.
nuclear@0 805 */
nuclear@0 806 smf_track_t *
nuclear@0 807 smf_find_track_with_next_event(smf_t *smf)
nuclear@0 808 {
nuclear@0 809 int i, min_time = 0;
nuclear@0 810 smf_track_t *track = NULL, *min_time_track = NULL;
nuclear@0 811
nuclear@0 812 /* Find track with event that should be played next. */
nuclear@0 813 for (i = 1; i <= smf->number_of_tracks; i++) {
nuclear@0 814 track = smf_get_track_by_number(smf, i);
nuclear@0 815
nuclear@0 816 assert(track);
nuclear@0 817
nuclear@0 818 /* No more events in this track? */
nuclear@0 819 if (track->next_event_number == -1)
nuclear@0 820 continue;
nuclear@0 821
nuclear@0 822 if (track->time_of_next_event < min_time || min_time_track == NULL) {
nuclear@0 823 min_time = track->time_of_next_event;
nuclear@0 824 min_time_track = track;
nuclear@0 825 }
nuclear@0 826 }
nuclear@0 827
nuclear@0 828 return (min_time_track);
nuclear@0 829 }
nuclear@0 830
nuclear@0 831 /**
nuclear@0 832 * \return Next event, in time order, or NULL, if there are none left.
nuclear@0 833 */
nuclear@0 834 smf_event_t *
nuclear@0 835 smf_get_next_event(smf_t *smf)
nuclear@0 836 {
nuclear@0 837 smf_event_t *event;
nuclear@0 838 smf_track_t *track = smf_find_track_with_next_event(smf);
nuclear@0 839
nuclear@0 840 if (track == NULL) {
nuclear@0 841 #if 0
nuclear@0 842 g_debug("End of the song.");
nuclear@0 843 #endif
nuclear@0 844
nuclear@0 845 return (NULL);
nuclear@0 846 }
nuclear@0 847
nuclear@0 848 event = smf_track_get_next_event(track);
nuclear@0 849
nuclear@0 850 assert(event != NULL);
nuclear@0 851
nuclear@0 852 event->track->smf->last_seek_position = -1.0;
nuclear@0 853
nuclear@0 854 return (event);
nuclear@0 855 }
nuclear@0 856
nuclear@0 857 /**
nuclear@0 858 * Advance the "next event counter". This is functionally the same as calling
nuclear@0 859 * smf_get_next_event and ignoring the return value.
nuclear@0 860 */
nuclear@0 861 void
nuclear@0 862 smf_skip_next_event(smf_t *smf)
nuclear@0 863 {
nuclear@0 864 void *notused;
nuclear@0 865
nuclear@0 866 notused = smf_get_next_event(smf);
nuclear@0 867 }
nuclear@0 868
nuclear@0 869 /**
nuclear@0 870 * \return Next event, in time order, or NULL, if there are none left. Does
nuclear@0 871 * not advance position in song.
nuclear@0 872 */
nuclear@0 873 smf_event_t *
nuclear@0 874 smf_peek_next_event(smf_t *smf)
nuclear@0 875 {
nuclear@0 876 smf_event_t *event;
nuclear@0 877 smf_track_t *track = smf_find_track_with_next_event(smf);
nuclear@0 878
nuclear@0 879 if (track == NULL) {
nuclear@0 880 #if 0
nuclear@0 881 g_debug("End of the song.");
nuclear@0 882 #endif
nuclear@0 883
nuclear@0 884 return (NULL);
nuclear@0 885 }
nuclear@0 886
nuclear@0 887 event = smf_peek_next_event_from_track(track);
nuclear@0 888
nuclear@0 889 assert(event != NULL);
nuclear@0 890
nuclear@0 891 return (event);
nuclear@0 892 }
nuclear@0 893
nuclear@0 894 /**
nuclear@0 895 * Rewinds the SMF. What that means is, after calling this routine, smf_get_next_event
nuclear@0 896 * will return first event in the song.
nuclear@0 897 */
nuclear@0 898 void
nuclear@0 899 smf_rewind(smf_t *smf)
nuclear@0 900 {
nuclear@0 901 int i;
nuclear@0 902 smf_track_t *track = NULL;
nuclear@0 903 smf_event_t *event;
nuclear@0 904
nuclear@0 905 assert(smf);
nuclear@0 906
nuclear@0 907 smf->last_seek_position = 0.0;
nuclear@0 908
nuclear@0 909 for (i = 1; i <= smf->number_of_tracks; i++) {
nuclear@0 910 track = smf_get_track_by_number(smf, i);
nuclear@0 911
nuclear@0 912 assert(track != NULL);
nuclear@0 913
nuclear@0 914 if (track->number_of_events > 0) {
nuclear@0 915 track->next_event_number = 1;
nuclear@0 916 event = smf_peek_next_event_from_track(track);
nuclear@0 917 assert(event);
nuclear@0 918 track->time_of_next_event = event->time_pulses;
nuclear@0 919 } else {
nuclear@0 920 track->next_event_number = -1;
nuclear@0 921 track->time_of_next_event = 0;
nuclear@0 922 #if 0
nuclear@0 923 g_warning("Warning: empty track.");
nuclear@0 924 #endif
nuclear@0 925 }
nuclear@0 926 }
nuclear@0 927 }
nuclear@0 928
nuclear@0 929 /**
nuclear@0 930 * Seeks the SMF to the given event. After calling this routine, smf_get_next_event
nuclear@0 931 * will return the event that was the second argument of this call.
nuclear@0 932 */
nuclear@0 933 int
nuclear@0 934 smf_seek_to_event(smf_t *smf, const smf_event_t *target)
nuclear@0 935 {
nuclear@0 936 smf_event_t *event;
nuclear@0 937
nuclear@0 938 smf_rewind(smf);
nuclear@0 939
nuclear@0 940 #if 0
nuclear@0 941 g_debug("Seeking to event %d, track %d.", target->event_number, target->track->track_number);
nuclear@0 942 #endif
nuclear@0 943
nuclear@0 944 for (;;) {
nuclear@0 945 event = smf_peek_next_event(smf);
nuclear@0 946
nuclear@0 947 /* There can't be NULL here, unless "target" is not in this smf. */
nuclear@0 948 assert(event);
nuclear@0 949
nuclear@0 950 if (event != target)
nuclear@0 951 smf_skip_next_event(smf);
nuclear@0 952 else
nuclear@0 953 break;
nuclear@0 954 }
nuclear@0 955
nuclear@0 956 smf->last_seek_position = event->time_seconds;
nuclear@0 957
nuclear@0 958 return (0);
nuclear@0 959 }
nuclear@0 960
nuclear@0 961 /**
nuclear@0 962 * Seeks the SMF to the given position. For example, after seeking to 1.0 seconds,
nuclear@0 963 * smf_get_next_event will return first event that happens after the first second of song.
nuclear@0 964 */
nuclear@0 965 int
nuclear@0 966 smf_seek_to_seconds(smf_t *smf, double seconds)
nuclear@0 967 {
nuclear@0 968 smf_event_t *event;
nuclear@0 969
nuclear@0 970 assert(seconds >= 0.0);
nuclear@0 971
nuclear@0 972 if (seconds == smf->last_seek_position) {
nuclear@0 973 #if 0
nuclear@0 974 g_debug("Avoiding seek to %f seconds.", seconds);
nuclear@0 975 #endif
nuclear@0 976 return (0);
nuclear@0 977 }
nuclear@0 978
nuclear@0 979 smf_rewind(smf);
nuclear@0 980
nuclear@0 981 #if 0
nuclear@0 982 g_debug("Seeking to %f seconds.", seconds);
nuclear@0 983 #endif
nuclear@0 984
nuclear@0 985 for (;;) {
nuclear@0 986 event = smf_peek_next_event(smf);
nuclear@0 987
nuclear@0 988 if (event == NULL) {
nuclear@0 989 fg_critical("Trying to seek past the end of song.");
nuclear@0 990 return (-1);
nuclear@0 991 }
nuclear@0 992
nuclear@0 993 if (event->time_seconds < seconds)
nuclear@0 994 smf_skip_next_event(smf);
nuclear@0 995 else
nuclear@0 996 break;
nuclear@0 997 }
nuclear@0 998
nuclear@0 999 smf->last_seek_position = seconds;
nuclear@0 1000
nuclear@0 1001 return (0);
nuclear@0 1002 }
nuclear@0 1003
nuclear@0 1004 /**
nuclear@0 1005 * Seeks the SMF to the given position. For example, after seeking to 10 pulses,
nuclear@0 1006 * smf_get_next_event will return first event that happens after the first ten pulses.
nuclear@0 1007 */
nuclear@0 1008 int
nuclear@0 1009 smf_seek_to_pulses(smf_t *smf, int pulses)
nuclear@0 1010 {
nuclear@0 1011 smf_event_t *event;
nuclear@0 1012
nuclear@0 1013 assert(pulses >= 0);
nuclear@0 1014
nuclear@0 1015 smf_rewind(smf);
nuclear@0 1016
nuclear@0 1017 #if 0
nuclear@0 1018 g_debug("Seeking to %d pulses.", pulses);
nuclear@0 1019 #endif
nuclear@0 1020
nuclear@0 1021 for (;;) {
nuclear@0 1022 event = smf_peek_next_event(smf);
nuclear@0 1023
nuclear@0 1024 if (event == NULL) {
nuclear@0 1025 fg_critical("Trying to seek past the end of song.");
nuclear@0 1026 return (-1);
nuclear@0 1027 }
nuclear@0 1028
nuclear@0 1029 if (event->time_pulses < pulses)
nuclear@0 1030 smf_skip_next_event(smf);
nuclear@0 1031 else
nuclear@0 1032 break;
nuclear@0 1033 }
nuclear@0 1034
nuclear@0 1035 smf->last_seek_position = event->time_seconds;
nuclear@0 1036
nuclear@0 1037 return (0);
nuclear@0 1038 }
nuclear@0 1039
nuclear@0 1040 /**
nuclear@0 1041 * \return Length of SMF, in pulses.
nuclear@0 1042 */
nuclear@0 1043 int
nuclear@0 1044 smf_get_length_pulses(const smf_t *smf)
nuclear@0 1045 {
nuclear@0 1046 int pulses = 0, i;
nuclear@0 1047
nuclear@0 1048 for (i = 1; i <= smf->number_of_tracks; i++) {
nuclear@0 1049 smf_track_t *track;
nuclear@0 1050 smf_event_t *event;
nuclear@0 1051
nuclear@0 1052 track = smf_get_track_by_number(smf, i);
nuclear@0 1053 assert(track);
nuclear@0 1054
nuclear@0 1055 event = smf_track_get_last_event(track);
nuclear@0 1056 /* Empty track? */
nuclear@0 1057 if (event == NULL)
nuclear@0 1058 continue;
nuclear@0 1059
nuclear@0 1060 if (event->time_pulses > pulses)
nuclear@0 1061 pulses = event->time_pulses;
nuclear@0 1062 }
nuclear@0 1063
nuclear@0 1064 return (pulses);
nuclear@0 1065 }
nuclear@0 1066
nuclear@0 1067 /**
nuclear@0 1068 * \return Length of SMF, in seconds.
nuclear@0 1069 */
nuclear@0 1070 double
nuclear@0 1071 smf_get_length_seconds(const smf_t *smf)
nuclear@0 1072 {
nuclear@0 1073 int i;
nuclear@0 1074 double seconds = 0.0;
nuclear@0 1075
nuclear@0 1076 for (i = 1; i <= smf->number_of_tracks; i++) {
nuclear@0 1077 smf_track_t *track;
nuclear@0 1078 smf_event_t *event;
nuclear@0 1079
nuclear@0 1080 track = smf_get_track_by_number(smf, i);
nuclear@0 1081 assert(track);
nuclear@0 1082
nuclear@0 1083 event = smf_track_get_last_event(track);
nuclear@0 1084 /* Empty track? */
nuclear@0 1085 if (event == NULL)
nuclear@0 1086 continue;
nuclear@0 1087
nuclear@0 1088 if (event->time_seconds > seconds)
nuclear@0 1089 seconds = event->time_seconds;
nuclear@0 1090 }
nuclear@0 1091
nuclear@0 1092 return (seconds);
nuclear@0 1093 }
nuclear@0 1094
nuclear@0 1095 /**
nuclear@0 1096 * \return Nonzero, if there are no events in the SMF after this one.
nuclear@0 1097 * Note that may be more than one "last event", if they occur at the same time.
nuclear@0 1098 */
nuclear@0 1099 int
nuclear@0 1100 smf_event_is_last(const smf_event_t *event)
nuclear@0 1101 {
nuclear@0 1102 if (smf_get_length_pulses(event->track->smf) <= event->time_pulses)
nuclear@0 1103 return (1);
nuclear@0 1104
nuclear@0 1105 return (0);
nuclear@0 1106 }
nuclear@0 1107
nuclear@0 1108 /**
nuclear@0 1109 * \return Version of libsmf.
nuclear@0 1110 */
nuclear@0 1111 const char *
nuclear@0 1112 smf_get_version(void)
nuclear@0 1113 {
nuclear@0 1114 return (SMF_VERSION);
nuclear@0 1115 }
nuclear@0 1116