smflite

annotate src/smf_save.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 * Standard MIDI File writer.
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 #define MAX_VLQ_LENGTH 128
nuclear@0 51
nuclear@0 52 /**
nuclear@0 53 * Extends (reallocates) smf->file_buffer and returns pointer to the newly added space,
nuclear@0 54 * that is, pointer to the first byte after the previous buffer end. Returns NULL in case
nuclear@0 55 * of error.
nuclear@0 56 */
nuclear@0 57 static void *
nuclear@0 58 smf_extend(smf_t *smf, const int length)
nuclear@0 59 {
nuclear@0 60 int i, previous_file_buffer_length = smf->file_buffer_length;
nuclear@0 61 char *previous_file_buffer = smf->file_buffer;
nuclear@0 62
nuclear@0 63 /* XXX: Not terribly efficient. */
nuclear@0 64 smf->file_buffer_length += length;
nuclear@0 65 smf->file_buffer = realloc(smf->file_buffer, smf->file_buffer_length);
nuclear@0 66 if (smf->file_buffer == NULL) {
nuclear@0 67 fg_critical("realloc(3) failed: %s", strerror(errno));
nuclear@0 68 smf->file_buffer_length = 0;
nuclear@0 69 return (NULL);
nuclear@0 70 }
nuclear@0 71
nuclear@0 72 /* Fix up pointers. XXX: omgwtf. */
nuclear@0 73 for (i = 1; i <= smf->number_of_tracks; i++) {
nuclear@0 74 smf_track_t *track;
nuclear@0 75 track = smf_get_track_by_number(smf, i);
nuclear@0 76 if (track->file_buffer != NULL)
nuclear@0 77 track->file_buffer = (char *)track->file_buffer + ((char *)smf->file_buffer - previous_file_buffer);
nuclear@0 78 }
nuclear@0 79
nuclear@0 80 return ((char *)smf->file_buffer + previous_file_buffer_length);
nuclear@0 81 }
nuclear@0 82
nuclear@0 83 /**
nuclear@0 84 * Appends "buffer_length" bytes pointed to by "buffer" to the smf, reallocating storage as needed. Returns 0
nuclear@0 85 * if everything went ok, different value if there was any problem.
nuclear@0 86 */
nuclear@0 87 static int
nuclear@0 88 smf_append(smf_t *smf, const void *buffer, const int buffer_length)
nuclear@0 89 {
nuclear@0 90 void *dest;
nuclear@0 91
nuclear@0 92 dest = smf_extend(smf, buffer_length);
nuclear@0 93 if (dest == NULL) {
nuclear@0 94 fg_critical("Cannot extend track buffer.");
nuclear@0 95 return (-1);
nuclear@0 96 }
nuclear@0 97
nuclear@0 98 memcpy(dest, buffer, buffer_length);
nuclear@0 99
nuclear@0 100 return (0);
nuclear@0 101 }
nuclear@0 102
nuclear@0 103 /**
nuclear@0 104 * Appends MThd header to the track. Returns 0 if everything went ok, different value if not.
nuclear@0 105 */
nuclear@0 106 static int
nuclear@0 107 write_mthd_header(smf_t *smf)
nuclear@0 108 {
nuclear@0 109 struct mthd_chunk_struct mthd_chunk;
nuclear@0 110
nuclear@0 111 memcpy(mthd_chunk.mthd_header.id, "MThd", 4);
nuclear@0 112 mthd_chunk.mthd_header.length = htonl(6);
nuclear@0 113 mthd_chunk.format = htons(smf->format);
nuclear@0 114 mthd_chunk.number_of_tracks = htons(smf->number_of_tracks);
nuclear@0 115 mthd_chunk.division = htons(smf->ppqn);
nuclear@0 116
nuclear@0 117 return (smf_append(smf, &mthd_chunk, sizeof(mthd_chunk)));
nuclear@0 118 }
nuclear@0 119
nuclear@0 120 /**
nuclear@0 121 * Extends (reallocates) track->file_buffer and returns pointer to the newly added space,
nuclear@0 122 * that is, pointer to the first byte after the previous buffer end. Returns NULL in case
nuclear@0 123 * of error.
nuclear@0 124 */
nuclear@0 125 static void *
nuclear@0 126 track_extend(smf_track_t *track, const int length)
nuclear@0 127 {
nuclear@0 128 void *buf;
nuclear@0 129
nuclear@0 130 assert(track->smf);
nuclear@0 131
nuclear@0 132 buf = smf_extend(track->smf, length);
nuclear@0 133 if (buf == NULL)
nuclear@0 134 return (NULL);
nuclear@0 135
nuclear@0 136 track->file_buffer_length += length;
nuclear@0 137 if (track->file_buffer == NULL)
nuclear@0 138 track->file_buffer = buf;
nuclear@0 139
nuclear@0 140 return (buf);
nuclear@0 141 }
nuclear@0 142
nuclear@0 143 /**
nuclear@0 144 * Appends "buffer_length" bytes pointed to by "buffer" to the track, reallocating storage as needed. Returns 0
nuclear@0 145 * if everything went ok, different value if there was any problem.
nuclear@0 146 */
nuclear@0 147 static int
nuclear@0 148 track_append(smf_track_t *track, const void *buffer, const int buffer_length)
nuclear@0 149 {
nuclear@0 150 void *dest;
nuclear@0 151
nuclear@0 152 dest = track_extend(track, buffer_length);
nuclear@0 153 if (dest == NULL) {
nuclear@0 154 fg_critical("Cannot extend track buffer.");
nuclear@0 155 return (-1);
nuclear@0 156 }
nuclear@0 157
nuclear@0 158 memcpy(dest, buffer, buffer_length);
nuclear@0 159
nuclear@0 160 return (0);
nuclear@0 161 }
nuclear@0 162
nuclear@0 163 static int
nuclear@0 164 format_vlq(unsigned char *buf, int length, unsigned long value)
nuclear@0 165 {
nuclear@0 166 int i;
nuclear@0 167 unsigned long buffer;
nuclear@0 168
nuclear@0 169 /* Taken from http://www.borg.com/~jglatt/tech/midifile/vari.htm */
nuclear@0 170 buffer = value & 0x7F;
nuclear@0 171
nuclear@0 172 while ((value >>= 7)) {
nuclear@0 173 buffer <<= 8;
nuclear@0 174 buffer |= ((value & 0x7F) | 0x80);
nuclear@0 175 }
nuclear@0 176
nuclear@0 177 for (i = 0;; i++) {
nuclear@0 178 buf[i] = buffer;
nuclear@0 179
nuclear@0 180 if (buffer & 0x80)
nuclear@0 181 buffer >>= 8;
nuclear@0 182 else
nuclear@0 183 break;
nuclear@0 184 }
nuclear@0 185
nuclear@0 186 assert(i <= length);
nuclear@0 187
nuclear@0 188 /* + 1, because "i" is an offset, not a count. */
nuclear@0 189 return (i + 1);
nuclear@0 190 }
nuclear@0 191
nuclear@0 192 smf_event_t *
nuclear@0 193 smf_event_new_textual(int type, const char *text)
nuclear@0 194 {
nuclear@0 195 int vlq_length, text_length, copied_length;
nuclear@0 196 smf_event_t *event;
nuclear@0 197
nuclear@0 198 assert(type >= 1 && type <= 9);
nuclear@0 199
nuclear@0 200 text_length = strlen(text);
nuclear@0 201
nuclear@0 202 event = smf_event_new();
nuclear@0 203 if (event == NULL)
nuclear@0 204 return (NULL);
nuclear@0 205
nuclear@0 206 /* "2 +" is for leading 0xFF 0xtype. */
nuclear@0 207 event->midi_buffer_length = 2 + text_length + MAX_VLQ_LENGTH;
nuclear@0 208 event->midi_buffer = malloc(event->midi_buffer_length);
nuclear@0 209 if (event->midi_buffer == NULL) {
nuclear@0 210 fg_critical("Cannot allocate MIDI buffer structure: %s", strerror(errno));
nuclear@0 211 smf_event_delete(event);
nuclear@0 212
nuclear@0 213 return (NULL);
nuclear@0 214 }
nuclear@0 215
nuclear@0 216 event->midi_buffer[0] = 0xFF;
nuclear@0 217 event->midi_buffer[1] = type;
nuclear@0 218
nuclear@0 219 vlq_length = format_vlq(event->midi_buffer + 2, MAX_VLQ_LENGTH - 2, text_length);
nuclear@0 220 copied_length = snprintf((char *)event->midi_buffer + vlq_length + 2, event->midi_buffer_length - vlq_length - 2, "%s", text);
nuclear@0 221
nuclear@0 222 assert(copied_length == text_length);
nuclear@0 223
nuclear@0 224 event->midi_buffer_length = 2 + vlq_length + text_length;
nuclear@0 225
nuclear@0 226 return event;
nuclear@0 227 }
nuclear@0 228
nuclear@0 229 /**
nuclear@0 230 * Appends value, expressed as Variable Length Quantity, to event->track.
nuclear@0 231 */
nuclear@0 232 static int
nuclear@0 233 write_vlq(smf_event_t *event, unsigned long value)
nuclear@0 234 {
nuclear@0 235 unsigned char buf[MAX_VLQ_LENGTH];
nuclear@0 236 int vlq_length;
nuclear@0 237
nuclear@0 238 vlq_length = format_vlq(buf, MAX_VLQ_LENGTH, value);
nuclear@0 239
nuclear@0 240 return (track_append(event->track, buf, vlq_length));
nuclear@0 241 }
nuclear@0 242
nuclear@0 243 /**
nuclear@0 244 * Appends event time as Variable Length Quantity. Returns 0 if everything went ok,
nuclear@0 245 * different value in case of error.
nuclear@0 246 */
nuclear@0 247 static int
nuclear@0 248 write_event_time(smf_event_t *event)
nuclear@0 249 {
nuclear@0 250 assert(event->delta_time_pulses >= 0);
nuclear@0 251
nuclear@0 252 return (write_vlq(event, event->delta_time_pulses));
nuclear@0 253 }
nuclear@0 254
nuclear@0 255 static int
nuclear@0 256 write_sysex_contents(smf_event_t *event)
nuclear@0 257 {
nuclear@0 258 int ret;
nuclear@0 259 unsigned char sysex_status = 0xF0;
nuclear@0 260
nuclear@0 261 assert(smf_event_is_sysex(event));
nuclear@0 262
nuclear@0 263 ret = track_append(event->track, &sysex_status, 1);
nuclear@0 264 if (ret)
nuclear@0 265 return (ret);
nuclear@0 266
nuclear@0 267 /* -1, because length does not include status byte. */
nuclear@0 268 ret = write_vlq(event, event->midi_buffer_length - 1);
nuclear@0 269 if (ret)
nuclear@0 270 return (ret);
nuclear@0 271
nuclear@0 272 ret = track_append(event->track, event->midi_buffer + 1, event->midi_buffer_length - 1);
nuclear@0 273 if (ret)
nuclear@0 274 return (ret);
nuclear@0 275
nuclear@0 276 return (0);
nuclear@0 277 }
nuclear@0 278
nuclear@0 279 /**
nuclear@0 280 * Appends contents of event->midi_buffer wrapped into 0xF7 MIDI event.
nuclear@0 281 */
nuclear@0 282 static int
nuclear@0 283 write_escaped_event_contents(smf_event_t *event)
nuclear@0 284 {
nuclear@0 285 int ret;
nuclear@0 286 unsigned char escape_status = 0xF7;
nuclear@0 287
nuclear@0 288 if (smf_event_is_sysex(event))
nuclear@0 289 return (write_sysex_contents(event));
nuclear@0 290
nuclear@0 291 ret = track_append(event->track, &escape_status, 1);
nuclear@0 292 if (ret)
nuclear@0 293 return (ret);
nuclear@0 294
nuclear@0 295 ret = write_vlq(event, event->midi_buffer_length);
nuclear@0 296 if (ret)
nuclear@0 297 return (ret);
nuclear@0 298
nuclear@0 299 ret = track_append(event->track, event->midi_buffer, event->midi_buffer_length);
nuclear@0 300 if (ret)
nuclear@0 301 return (ret);
nuclear@0 302
nuclear@0 303 return (0);
nuclear@0 304 }
nuclear@0 305
nuclear@0 306 /**
nuclear@0 307 * Appends contents of event->midi_buffer. Returns 0 if everything went 0,
nuclear@0 308 * different value in case of error.
nuclear@0 309 */
nuclear@0 310 static int
nuclear@0 311 write_event_contents(smf_event_t *event)
nuclear@0 312 {
nuclear@0 313 if (smf_event_is_system_realtime(event) || smf_event_is_system_common(event))
nuclear@0 314 return (write_escaped_event_contents(event));
nuclear@0 315
nuclear@0 316 return (track_append(event->track, event->midi_buffer, event->midi_buffer_length));
nuclear@0 317 }
nuclear@0 318
nuclear@0 319 /**
nuclear@0 320 * Writes out an event.
nuclear@0 321 */
nuclear@0 322 static int
nuclear@0 323 write_event(smf_event_t *event)
nuclear@0 324 {
nuclear@0 325 int ret;
nuclear@0 326
nuclear@0 327 ret = write_event_time(event);
nuclear@0 328 if (ret)
nuclear@0 329 return (ret);
nuclear@0 330
nuclear@0 331 ret = write_event_contents(event);
nuclear@0 332 if (ret)
nuclear@0 333 return (ret);
nuclear@0 334
nuclear@0 335 return (0);
nuclear@0 336 }
nuclear@0 337
nuclear@0 338 /**
nuclear@0 339 * Writes out MTrk header, except of MTrk chunk length, which is written by write_mtrk_length().
nuclear@0 340 */
nuclear@0 341 static int
nuclear@0 342 write_mtrk_header(smf_track_t *track)
nuclear@0 343 {
nuclear@0 344 struct chunk_header_struct mtrk_header;
nuclear@0 345
nuclear@0 346 memcpy(mtrk_header.id, "MTrk", 4);
nuclear@0 347
nuclear@0 348 return (track_append(track, &mtrk_header, sizeof(mtrk_header)));
nuclear@0 349 }
nuclear@0 350
nuclear@0 351 /**
nuclear@0 352 * Updates MTrk chunk length of a given track.
nuclear@0 353 */
nuclear@0 354 static int
nuclear@0 355 write_mtrk_length(smf_track_t *track)
nuclear@0 356 {
nuclear@0 357 struct chunk_header_struct *mtrk_header;
nuclear@0 358
nuclear@0 359 assert(track->file_buffer != NULL);
nuclear@0 360 assert(track->file_buffer_length >= 6);
nuclear@0 361
nuclear@0 362 mtrk_header = (struct chunk_header_struct *)track->file_buffer;
nuclear@0 363 mtrk_header->length = htonl(track->file_buffer_length - sizeof(struct chunk_header_struct));
nuclear@0 364
nuclear@0 365 return (0);
nuclear@0 366 }
nuclear@0 367
nuclear@0 368 /**
nuclear@0 369 * Writes out the track.
nuclear@0 370 */
nuclear@0 371 static int
nuclear@0 372 write_track(smf_track_t *track)
nuclear@0 373 {
nuclear@0 374 int ret;
nuclear@0 375 smf_event_t *event;
nuclear@0 376
nuclear@0 377 ret = write_mtrk_header(track);
nuclear@0 378 if (ret)
nuclear@0 379 return (ret);
nuclear@0 380
nuclear@0 381 while ((event = smf_track_get_next_event(track)) != NULL) {
nuclear@0 382 ret = write_event(event);
nuclear@0 383 if (ret)
nuclear@0 384 return (ret);
nuclear@0 385 }
nuclear@0 386
nuclear@0 387 ret = write_mtrk_length(track);
nuclear@0 388 if (ret)
nuclear@0 389 return (ret);
nuclear@0 390
nuclear@0 391 return (0);
nuclear@0 392 }
nuclear@0 393
nuclear@0 394 /**
nuclear@0 395 * Takes smf->file_buffer and saves it to the file.
nuclear@0 396 */
nuclear@0 397 static int
nuclear@0 398 write_file(smf_t *smf, const char *file_name)
nuclear@0 399 {
nuclear@0 400 FILE *stream;
nuclear@0 401
nuclear@0 402 stream = fopen(file_name, "wb+");
nuclear@0 403 if (stream == NULL) {
nuclear@0 404 fg_critical("Cannot open input file: %s", strerror(errno));
nuclear@0 405
nuclear@0 406 return (-1);
nuclear@0 407 }
nuclear@0 408
nuclear@0 409 if (fwrite(smf->file_buffer, 1, smf->file_buffer_length, stream) != smf->file_buffer_length) {
nuclear@0 410 fg_critical("fwrite(3) failed: %s", strerror(errno));
nuclear@0 411
nuclear@0 412 return (-2);
nuclear@0 413 }
nuclear@0 414
nuclear@0 415 if (fclose(stream)) {
nuclear@0 416 fg_critical("fclose(3) failed: %s", strerror(errno));
nuclear@0 417
nuclear@0 418 return (-3);
nuclear@0 419 }
nuclear@0 420
nuclear@0 421 return (0);
nuclear@0 422 }
nuclear@0 423
nuclear@0 424 static void
nuclear@0 425 free_buffer(smf_t *smf)
nuclear@0 426 {
nuclear@0 427 int i;
nuclear@0 428 smf_track_t *track;
nuclear@0 429
nuclear@0 430 /* Clear the pointers. */
nuclear@0 431 memset(smf->file_buffer, 0, smf->file_buffer_length);
nuclear@0 432 free(smf->file_buffer);
nuclear@0 433 smf->file_buffer = NULL;
nuclear@0 434 smf->file_buffer_length = 0;
nuclear@0 435
nuclear@0 436 for (i = 1; i <= smf->number_of_tracks; i++) {
nuclear@0 437 track = smf_get_track_by_number(smf, i);
nuclear@0 438 assert(track);
nuclear@0 439 track->file_buffer = NULL;
nuclear@0 440 track->file_buffer_length = 0;
nuclear@0 441 }
nuclear@0 442 }
nuclear@0 443
nuclear@0 444 #ifndef NDEBUG
nuclear@0 445
nuclear@0 446 /**
nuclear@0 447 * \return Nonzero, if all pointers supposed to be NULL are NULL. Triggers assertion if not.
nuclear@0 448 */
nuclear@0 449 static int
nuclear@0 450 pointers_are_clear(smf_t *smf)
nuclear@0 451 {
nuclear@0 452 int i;
nuclear@0 453
nuclear@0 454 smf_track_t *track;
nuclear@0 455 assert(smf->file_buffer == NULL);
nuclear@0 456 assert(smf->file_buffer_length == 0);
nuclear@0 457
nuclear@0 458 for (i = 1; i <= smf->number_of_tracks; i++) {
nuclear@0 459 track = smf_get_track_by_number(smf, i);
nuclear@0 460
nuclear@0 461 assert(track != NULL);
nuclear@0 462 assert(track->file_buffer == NULL);
nuclear@0 463 assert(track->file_buffer_length == 0);
nuclear@0 464 }
nuclear@0 465
nuclear@0 466 return (1);
nuclear@0 467 }
nuclear@0 468
nuclear@0 469 #endif /* !NDEBUG */
nuclear@0 470
nuclear@0 471 /**
nuclear@0 472 * \return Nonzero, if event is End Of Track metaevent.
nuclear@0 473 */
nuclear@0 474 int
nuclear@0 475 smf_event_is_eot(const smf_event_t *event)
nuclear@0 476 {
nuclear@0 477 if (event->midi_buffer_length != 3)
nuclear@0 478 return (0);
nuclear@0 479
nuclear@0 480 if (event->midi_buffer[0] != 0xFF || event->midi_buffer[1] != 0x2F || event->midi_buffer[2] != 0x00)
nuclear@0 481 return (0);
nuclear@0 482
nuclear@0 483 return (1);
nuclear@0 484 }
nuclear@0 485
nuclear@0 486 /**
nuclear@0 487 * Check if SMF is valid and add missing EOT events.
nuclear@0 488 *
nuclear@0 489 * \return 0, if SMF is valid.
nuclear@0 490 */
nuclear@0 491 static int
nuclear@0 492 smf_validate(smf_t *smf)
nuclear@0 493 {
nuclear@0 494 int trackno, eventno, eot_found;
nuclear@0 495 smf_track_t *track;
nuclear@0 496 smf_event_t *event;
nuclear@0 497
nuclear@0 498 if (smf->format < 0 || smf->format > 2) {
nuclear@0 499 fg_critical("SMF error: smf->format is less than zero of greater than two.");
nuclear@0 500 return (-1);
nuclear@0 501 }
nuclear@0 502
nuclear@0 503 if (smf->number_of_tracks < 1) {
nuclear@0 504 fg_critical("SMF error: number of tracks is less than one.");
nuclear@0 505 return (-2);
nuclear@0 506 }
nuclear@0 507
nuclear@0 508 if (smf->format == 0 && smf->number_of_tracks > 1) {
nuclear@0 509 fg_critical("SMF error: format is 0, but number of tracks is more than one.");
nuclear@0 510 return (-3);
nuclear@0 511 }
nuclear@0 512
nuclear@0 513 if (smf->ppqn <= 0) {
nuclear@0 514 fg_critical("SMF error: PPQN has to be > 0.");
nuclear@0 515 return (-4);
nuclear@0 516 }
nuclear@0 517
nuclear@0 518 for (trackno = 1; trackno <= smf->number_of_tracks; trackno++) {
nuclear@0 519 track = smf_get_track_by_number(smf, trackno);
nuclear@0 520 assert(track);
nuclear@0 521
nuclear@0 522 eot_found = 0;
nuclear@0 523
nuclear@0 524 for (eventno = 1; eventno <= track->number_of_events; eventno++) {
nuclear@0 525 event = smf_track_get_event_by_number(track, eventno);
nuclear@0 526 assert(event);
nuclear@0 527
nuclear@0 528 if (!smf_event_is_valid(event)) {
nuclear@0 529 fg_critical("Event #%d on track #%d is invalid.", eventno, trackno);
nuclear@0 530 return (-5);
nuclear@0 531 }
nuclear@0 532
nuclear@0 533 if (smf_event_is_eot(event)) {
nuclear@0 534 if (eot_found) {
nuclear@0 535 fg_critical("Duplicate End Of Track event on track #%d.", trackno);
nuclear@0 536 return (-6);
nuclear@0 537 }
nuclear@0 538
nuclear@0 539 eot_found = 1;
nuclear@0 540 }
nuclear@0 541 }
nuclear@0 542
nuclear@0 543 if (!eot_found) {
nuclear@0 544 if (smf_track_add_eot_delta_pulses(track, 0)) {
nuclear@0 545 fg_critical("smf_track_add_eot_delta_pulses failed.");
nuclear@0 546 return (-6);
nuclear@0 547 }
nuclear@0 548 }
nuclear@0 549
nuclear@0 550 }
nuclear@0 551
nuclear@0 552 return (0);
nuclear@0 553 }
nuclear@0 554
nuclear@0 555 #ifndef NDEBUG
nuclear@0 556
nuclear@0 557 static void
nuclear@0 558 assert_smf_event_is_identical(const smf_event_t *a, const smf_event_t *b)
nuclear@0 559 {
nuclear@0 560 assert(a->event_number == b->event_number);
nuclear@0 561 assert(a->delta_time_pulses == b->delta_time_pulses);
nuclear@0 562 assert(abs(a->time_pulses - b->time_pulses) <= 2);
nuclear@0 563 assert(fabs(a->time_seconds - b->time_seconds) <= 0.01);
nuclear@0 564 assert(a->track_number == b->track_number);
nuclear@0 565 assert(a->midi_buffer_length == b->midi_buffer_length);
nuclear@0 566 assert(memcmp(a->midi_buffer, b->midi_buffer, a->midi_buffer_length) == 0);
nuclear@0 567 }
nuclear@0 568
nuclear@0 569 static void
nuclear@0 570 assert_smf_track_is_identical(const smf_track_t *a, const smf_track_t *b)
nuclear@0 571 {
nuclear@0 572 int i;
nuclear@0 573
nuclear@0 574 assert(a->track_number == b->track_number);
nuclear@0 575 assert(a->number_of_events == b->number_of_events);
nuclear@0 576
nuclear@0 577 for (i = 1; i <= a->number_of_events; i++)
nuclear@0 578 assert_smf_event_is_identical(smf_track_get_event_by_number(a, i), smf_track_get_event_by_number(b, i));
nuclear@0 579 }
nuclear@0 580
nuclear@0 581 static void
nuclear@0 582 assert_smf_is_identical(const smf_t *a, const smf_t *b)
nuclear@0 583 {
nuclear@0 584 int i;
nuclear@0 585
nuclear@0 586 assert(a->format == b->format);
nuclear@0 587 assert(a->ppqn == b->ppqn);
nuclear@0 588 assert(a->frames_per_second == b->frames_per_second);
nuclear@0 589 assert(a->resolution == b->resolution);
nuclear@0 590 assert(a->number_of_tracks == b->number_of_tracks);
nuclear@0 591
nuclear@0 592 for (i = 1; i <= a->number_of_tracks; i++)
nuclear@0 593 assert_smf_track_is_identical(smf_get_track_by_number(a, i), smf_get_track_by_number(b, i));
nuclear@0 594
nuclear@0 595 /* We do not need to compare tempos explicitly, as tempo is always computed from track contents. */
nuclear@0 596 }
nuclear@0 597
nuclear@0 598 static void
nuclear@0 599 assert_smf_saved_correctly(const smf_t *smf, const char *file_name)
nuclear@0 600 {
nuclear@0 601 smf_t *saved;
nuclear@0 602
nuclear@0 603 saved = smf_load(file_name);
nuclear@0 604 assert(saved != NULL);
nuclear@0 605
nuclear@0 606 assert_smf_is_identical(smf, saved);
nuclear@0 607
nuclear@0 608 smf_delete(saved);
nuclear@0 609 }
nuclear@0 610
nuclear@0 611 #endif /* !NDEBUG */
nuclear@0 612
nuclear@0 613 /**
nuclear@0 614 * Writes the contents of SMF to the file given.
nuclear@0 615 * \param smf SMF.
nuclear@0 616 * \param file_name Path to the file.
nuclear@0 617 * \return 0, if saving was successfull.
nuclear@0 618 */
nuclear@0 619 int
nuclear@0 620 smf_save(smf_t *smf, const char *file_name)
nuclear@0 621 {
nuclear@0 622 int i, error;
nuclear@0 623 smf_track_t *track;
nuclear@0 624
nuclear@0 625 smf_rewind(smf);
nuclear@0 626
nuclear@0 627 assert(pointers_are_clear(smf));
nuclear@0 628
nuclear@0 629 if (smf_validate(smf))
nuclear@0 630 return (-1);
nuclear@0 631
nuclear@0 632 if (write_mthd_header(smf))
nuclear@0 633 return (-2);
nuclear@0 634
nuclear@0 635 for (i = 1; i <= smf->number_of_tracks; i++) {
nuclear@0 636 track = smf_get_track_by_number(smf, i);
nuclear@0 637
nuclear@0 638 assert(track != NULL);
nuclear@0 639
nuclear@0 640 error = write_track(track);
nuclear@0 641 if (error) {
nuclear@0 642 free_buffer(smf);
nuclear@0 643 return (error);
nuclear@0 644 }
nuclear@0 645 }
nuclear@0 646
nuclear@0 647 error = write_file(smf, file_name);
nuclear@0 648
nuclear@0 649 free_buffer(smf);
nuclear@0 650
nuclear@0 651 if (error)
nuclear@0 652 return (error);
nuclear@0 653
nuclear@0 654 #ifndef NDEBUG
nuclear@0 655 assert_smf_saved_correctly(smf, file_name);
nuclear@0 656 #endif
nuclear@0 657
nuclear@0 658 return (0);
nuclear@0 659 }
nuclear@0 660