smflite
diff 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 |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/smf_save.c Thu Jan 26 11:25:11 2012 +0200 1.3 @@ -0,0 +1,660 @@ 1.4 +/*- 1.5 + * Copyright (c) 2007, 2008 Edward Tomasz NapieraĆa <trasz@FreeBSD.org> 1.6 + * All rights reserved. 1.7 + * 1.8 + * Redistribution and use in source and binary forms, with or without 1.9 + * modification, are permitted provided that the following conditions 1.10 + * are met: 1.11 + * 1. Redistributions of source code must retain the above copyright 1.12 + * notice, this list of conditions and the following disclaimer. 1.13 + * 2. Redistributions in binary form must reproduce the above copyright 1.14 + * notice, this list of conditions and the following disclaimer in the 1.15 + * documentation and/or other materials provided with the distribution. 1.16 + * 1.17 + * ALTHOUGH THIS SOFTWARE IS MADE OF WIN AND SCIENCE, IT IS PROVIDED BY THE 1.18 + * AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 1.19 + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 1.20 + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 1.21 + * THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.22 + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 1.23 + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 1.24 + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 1.25 + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 1.26 + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 1.27 + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.28 + * 1.29 + */ 1.30 + 1.31 +/** 1.32 + * \file 1.33 + * 1.34 + * Standard MIDI File writer. 1.35 + * 1.36 + */ 1.37 + 1.38 +/* Reference: http://www.borg.com/~jglatt/tech/midifile.htm */ 1.39 + 1.40 +#include <stdlib.h> 1.41 +#include <string.h> 1.42 +#include <assert.h> 1.43 +#include <math.h> 1.44 +#include <errno.h> 1.45 +#ifdef __MINGW32__ 1.46 +#include <windows.h> 1.47 +#else /* ! __MINGW32__ */ 1.48 +#include <arpa/inet.h> 1.49 +#endif /* ! __MINGW32__ */ 1.50 +#include "smf.h" 1.51 +#include "smf_private.h" 1.52 + 1.53 +#define MAX_VLQ_LENGTH 128 1.54 + 1.55 +/** 1.56 + * Extends (reallocates) smf->file_buffer and returns pointer to the newly added space, 1.57 + * that is, pointer to the first byte after the previous buffer end. Returns NULL in case 1.58 + * of error. 1.59 + */ 1.60 +static void * 1.61 +smf_extend(smf_t *smf, const int length) 1.62 +{ 1.63 + int i, previous_file_buffer_length = smf->file_buffer_length; 1.64 + char *previous_file_buffer = smf->file_buffer; 1.65 + 1.66 + /* XXX: Not terribly efficient. */ 1.67 + smf->file_buffer_length += length; 1.68 + smf->file_buffer = realloc(smf->file_buffer, smf->file_buffer_length); 1.69 + if (smf->file_buffer == NULL) { 1.70 + fg_critical("realloc(3) failed: %s", strerror(errno)); 1.71 + smf->file_buffer_length = 0; 1.72 + return (NULL); 1.73 + } 1.74 + 1.75 + /* Fix up pointers. XXX: omgwtf. */ 1.76 + for (i = 1; i <= smf->number_of_tracks; i++) { 1.77 + smf_track_t *track; 1.78 + track = smf_get_track_by_number(smf, i); 1.79 + if (track->file_buffer != NULL) 1.80 + track->file_buffer = (char *)track->file_buffer + ((char *)smf->file_buffer - previous_file_buffer); 1.81 + } 1.82 + 1.83 + return ((char *)smf->file_buffer + previous_file_buffer_length); 1.84 +} 1.85 + 1.86 +/** 1.87 + * Appends "buffer_length" bytes pointed to by "buffer" to the smf, reallocating storage as needed. Returns 0 1.88 + * if everything went ok, different value if there was any problem. 1.89 + */ 1.90 +static int 1.91 +smf_append(smf_t *smf, const void *buffer, const int buffer_length) 1.92 +{ 1.93 + void *dest; 1.94 + 1.95 + dest = smf_extend(smf, buffer_length); 1.96 + if (dest == NULL) { 1.97 + fg_critical("Cannot extend track buffer."); 1.98 + return (-1); 1.99 + } 1.100 + 1.101 + memcpy(dest, buffer, buffer_length); 1.102 + 1.103 + return (0); 1.104 +} 1.105 + 1.106 +/** 1.107 + * Appends MThd header to the track. Returns 0 if everything went ok, different value if not. 1.108 + */ 1.109 +static int 1.110 +write_mthd_header(smf_t *smf) 1.111 +{ 1.112 + struct mthd_chunk_struct mthd_chunk; 1.113 + 1.114 + memcpy(mthd_chunk.mthd_header.id, "MThd", 4); 1.115 + mthd_chunk.mthd_header.length = htonl(6); 1.116 + mthd_chunk.format = htons(smf->format); 1.117 + mthd_chunk.number_of_tracks = htons(smf->number_of_tracks); 1.118 + mthd_chunk.division = htons(smf->ppqn); 1.119 + 1.120 + return (smf_append(smf, &mthd_chunk, sizeof(mthd_chunk))); 1.121 +} 1.122 + 1.123 +/** 1.124 + * Extends (reallocates) track->file_buffer and returns pointer to the newly added space, 1.125 + * that is, pointer to the first byte after the previous buffer end. Returns NULL in case 1.126 + * of error. 1.127 + */ 1.128 +static void * 1.129 +track_extend(smf_track_t *track, const int length) 1.130 +{ 1.131 + void *buf; 1.132 + 1.133 + assert(track->smf); 1.134 + 1.135 + buf = smf_extend(track->smf, length); 1.136 + if (buf == NULL) 1.137 + return (NULL); 1.138 + 1.139 + track->file_buffer_length += length; 1.140 + if (track->file_buffer == NULL) 1.141 + track->file_buffer = buf; 1.142 + 1.143 + return (buf); 1.144 +} 1.145 + 1.146 +/** 1.147 + * Appends "buffer_length" bytes pointed to by "buffer" to the track, reallocating storage as needed. Returns 0 1.148 + * if everything went ok, different value if there was any problem. 1.149 + */ 1.150 +static int 1.151 +track_append(smf_track_t *track, const void *buffer, const int buffer_length) 1.152 +{ 1.153 + void *dest; 1.154 + 1.155 + dest = track_extend(track, buffer_length); 1.156 + if (dest == NULL) { 1.157 + fg_critical("Cannot extend track buffer."); 1.158 + return (-1); 1.159 + } 1.160 + 1.161 + memcpy(dest, buffer, buffer_length); 1.162 + 1.163 + return (0); 1.164 +} 1.165 + 1.166 +static int 1.167 +format_vlq(unsigned char *buf, int length, unsigned long value) 1.168 +{ 1.169 + int i; 1.170 + unsigned long buffer; 1.171 + 1.172 + /* Taken from http://www.borg.com/~jglatt/tech/midifile/vari.htm */ 1.173 + buffer = value & 0x7F; 1.174 + 1.175 + while ((value >>= 7)) { 1.176 + buffer <<= 8; 1.177 + buffer |= ((value & 0x7F) | 0x80); 1.178 + } 1.179 + 1.180 + for (i = 0;; i++) { 1.181 + buf[i] = buffer; 1.182 + 1.183 + if (buffer & 0x80) 1.184 + buffer >>= 8; 1.185 + else 1.186 + break; 1.187 + } 1.188 + 1.189 + assert(i <= length); 1.190 + 1.191 + /* + 1, because "i" is an offset, not a count. */ 1.192 + return (i + 1); 1.193 +} 1.194 + 1.195 +smf_event_t * 1.196 +smf_event_new_textual(int type, const char *text) 1.197 +{ 1.198 + int vlq_length, text_length, copied_length; 1.199 + smf_event_t *event; 1.200 + 1.201 + assert(type >= 1 && type <= 9); 1.202 + 1.203 + text_length = strlen(text); 1.204 + 1.205 + event = smf_event_new(); 1.206 + if (event == NULL) 1.207 + return (NULL); 1.208 + 1.209 + /* "2 +" is for leading 0xFF 0xtype. */ 1.210 + event->midi_buffer_length = 2 + text_length + MAX_VLQ_LENGTH; 1.211 + event->midi_buffer = malloc(event->midi_buffer_length); 1.212 + if (event->midi_buffer == NULL) { 1.213 + fg_critical("Cannot allocate MIDI buffer structure: %s", strerror(errno)); 1.214 + smf_event_delete(event); 1.215 + 1.216 + return (NULL); 1.217 + } 1.218 + 1.219 + event->midi_buffer[0] = 0xFF; 1.220 + event->midi_buffer[1] = type; 1.221 + 1.222 + vlq_length = format_vlq(event->midi_buffer + 2, MAX_VLQ_LENGTH - 2, text_length); 1.223 + copied_length = snprintf((char *)event->midi_buffer + vlq_length + 2, event->midi_buffer_length - vlq_length - 2, "%s", text); 1.224 + 1.225 + assert(copied_length == text_length); 1.226 + 1.227 + event->midi_buffer_length = 2 + vlq_length + text_length; 1.228 + 1.229 + return event; 1.230 +} 1.231 + 1.232 +/** 1.233 + * Appends value, expressed as Variable Length Quantity, to event->track. 1.234 + */ 1.235 +static int 1.236 +write_vlq(smf_event_t *event, unsigned long value) 1.237 +{ 1.238 + unsigned char buf[MAX_VLQ_LENGTH]; 1.239 + int vlq_length; 1.240 + 1.241 + vlq_length = format_vlq(buf, MAX_VLQ_LENGTH, value); 1.242 + 1.243 + return (track_append(event->track, buf, vlq_length)); 1.244 +} 1.245 + 1.246 +/** 1.247 + * Appends event time as Variable Length Quantity. Returns 0 if everything went ok, 1.248 + * different value in case of error. 1.249 + */ 1.250 +static int 1.251 +write_event_time(smf_event_t *event) 1.252 +{ 1.253 + assert(event->delta_time_pulses >= 0); 1.254 + 1.255 + return (write_vlq(event, event->delta_time_pulses)); 1.256 +} 1.257 + 1.258 +static int 1.259 +write_sysex_contents(smf_event_t *event) 1.260 +{ 1.261 + int ret; 1.262 + unsigned char sysex_status = 0xF0; 1.263 + 1.264 + assert(smf_event_is_sysex(event)); 1.265 + 1.266 + ret = track_append(event->track, &sysex_status, 1); 1.267 + if (ret) 1.268 + return (ret); 1.269 + 1.270 + /* -1, because length does not include status byte. */ 1.271 + ret = write_vlq(event, event->midi_buffer_length - 1); 1.272 + if (ret) 1.273 + return (ret); 1.274 + 1.275 + ret = track_append(event->track, event->midi_buffer + 1, event->midi_buffer_length - 1); 1.276 + if (ret) 1.277 + return (ret); 1.278 + 1.279 + return (0); 1.280 +} 1.281 + 1.282 +/** 1.283 + * Appends contents of event->midi_buffer wrapped into 0xF7 MIDI event. 1.284 + */ 1.285 +static int 1.286 +write_escaped_event_contents(smf_event_t *event) 1.287 +{ 1.288 + int ret; 1.289 + unsigned char escape_status = 0xF7; 1.290 + 1.291 + if (smf_event_is_sysex(event)) 1.292 + return (write_sysex_contents(event)); 1.293 + 1.294 + ret = track_append(event->track, &escape_status, 1); 1.295 + if (ret) 1.296 + return (ret); 1.297 + 1.298 + ret = write_vlq(event, event->midi_buffer_length); 1.299 + if (ret) 1.300 + return (ret); 1.301 + 1.302 + ret = track_append(event->track, event->midi_buffer, event->midi_buffer_length); 1.303 + if (ret) 1.304 + return (ret); 1.305 + 1.306 + return (0); 1.307 +} 1.308 + 1.309 +/** 1.310 + * Appends contents of event->midi_buffer. Returns 0 if everything went 0, 1.311 + * different value in case of error. 1.312 + */ 1.313 +static int 1.314 +write_event_contents(smf_event_t *event) 1.315 +{ 1.316 + if (smf_event_is_system_realtime(event) || smf_event_is_system_common(event)) 1.317 + return (write_escaped_event_contents(event)); 1.318 + 1.319 + return (track_append(event->track, event->midi_buffer, event->midi_buffer_length)); 1.320 +} 1.321 + 1.322 +/** 1.323 + * Writes out an event. 1.324 + */ 1.325 +static int 1.326 +write_event(smf_event_t *event) 1.327 +{ 1.328 + int ret; 1.329 + 1.330 + ret = write_event_time(event); 1.331 + if (ret) 1.332 + return (ret); 1.333 + 1.334 + ret = write_event_contents(event); 1.335 + if (ret) 1.336 + return (ret); 1.337 + 1.338 + return (0); 1.339 +} 1.340 + 1.341 +/** 1.342 + * Writes out MTrk header, except of MTrk chunk length, which is written by write_mtrk_length(). 1.343 + */ 1.344 +static int 1.345 +write_mtrk_header(smf_track_t *track) 1.346 +{ 1.347 + struct chunk_header_struct mtrk_header; 1.348 + 1.349 + memcpy(mtrk_header.id, "MTrk", 4); 1.350 + 1.351 + return (track_append(track, &mtrk_header, sizeof(mtrk_header))); 1.352 +} 1.353 + 1.354 +/** 1.355 + * Updates MTrk chunk length of a given track. 1.356 + */ 1.357 +static int 1.358 +write_mtrk_length(smf_track_t *track) 1.359 +{ 1.360 + struct chunk_header_struct *mtrk_header; 1.361 + 1.362 + assert(track->file_buffer != NULL); 1.363 + assert(track->file_buffer_length >= 6); 1.364 + 1.365 + mtrk_header = (struct chunk_header_struct *)track->file_buffer; 1.366 + mtrk_header->length = htonl(track->file_buffer_length - sizeof(struct chunk_header_struct)); 1.367 + 1.368 + return (0); 1.369 +} 1.370 + 1.371 +/** 1.372 + * Writes out the track. 1.373 + */ 1.374 +static int 1.375 +write_track(smf_track_t *track) 1.376 +{ 1.377 + int ret; 1.378 + smf_event_t *event; 1.379 + 1.380 + ret = write_mtrk_header(track); 1.381 + if (ret) 1.382 + return (ret); 1.383 + 1.384 + while ((event = smf_track_get_next_event(track)) != NULL) { 1.385 + ret = write_event(event); 1.386 + if (ret) 1.387 + return (ret); 1.388 + } 1.389 + 1.390 + ret = write_mtrk_length(track); 1.391 + if (ret) 1.392 + return (ret); 1.393 + 1.394 + return (0); 1.395 +} 1.396 + 1.397 +/** 1.398 + * Takes smf->file_buffer and saves it to the file. 1.399 + */ 1.400 +static int 1.401 +write_file(smf_t *smf, const char *file_name) 1.402 +{ 1.403 + FILE *stream; 1.404 + 1.405 + stream = fopen(file_name, "wb+"); 1.406 + if (stream == NULL) { 1.407 + fg_critical("Cannot open input file: %s", strerror(errno)); 1.408 + 1.409 + return (-1); 1.410 + } 1.411 + 1.412 + if (fwrite(smf->file_buffer, 1, smf->file_buffer_length, stream) != smf->file_buffer_length) { 1.413 + fg_critical("fwrite(3) failed: %s", strerror(errno)); 1.414 + 1.415 + return (-2); 1.416 + } 1.417 + 1.418 + if (fclose(stream)) { 1.419 + fg_critical("fclose(3) failed: %s", strerror(errno)); 1.420 + 1.421 + return (-3); 1.422 + } 1.423 + 1.424 + return (0); 1.425 +} 1.426 + 1.427 +static void 1.428 +free_buffer(smf_t *smf) 1.429 +{ 1.430 + int i; 1.431 + smf_track_t *track; 1.432 + 1.433 + /* Clear the pointers. */ 1.434 + memset(smf->file_buffer, 0, smf->file_buffer_length); 1.435 + free(smf->file_buffer); 1.436 + smf->file_buffer = NULL; 1.437 + smf->file_buffer_length = 0; 1.438 + 1.439 + for (i = 1; i <= smf->number_of_tracks; i++) { 1.440 + track = smf_get_track_by_number(smf, i); 1.441 + assert(track); 1.442 + track->file_buffer = NULL; 1.443 + track->file_buffer_length = 0; 1.444 + } 1.445 +} 1.446 + 1.447 +#ifndef NDEBUG 1.448 + 1.449 +/** 1.450 + * \return Nonzero, if all pointers supposed to be NULL are NULL. Triggers assertion if not. 1.451 + */ 1.452 +static int 1.453 +pointers_are_clear(smf_t *smf) 1.454 +{ 1.455 + int i; 1.456 + 1.457 + smf_track_t *track; 1.458 + assert(smf->file_buffer == NULL); 1.459 + assert(smf->file_buffer_length == 0); 1.460 + 1.461 + for (i = 1; i <= smf->number_of_tracks; i++) { 1.462 + track = smf_get_track_by_number(smf, i); 1.463 + 1.464 + assert(track != NULL); 1.465 + assert(track->file_buffer == NULL); 1.466 + assert(track->file_buffer_length == 0); 1.467 + } 1.468 + 1.469 + return (1); 1.470 +} 1.471 + 1.472 +#endif /* !NDEBUG */ 1.473 + 1.474 +/** 1.475 + * \return Nonzero, if event is End Of Track metaevent. 1.476 + */ 1.477 +int 1.478 +smf_event_is_eot(const smf_event_t *event) 1.479 +{ 1.480 + if (event->midi_buffer_length != 3) 1.481 + return (0); 1.482 + 1.483 + if (event->midi_buffer[0] != 0xFF || event->midi_buffer[1] != 0x2F || event->midi_buffer[2] != 0x00) 1.484 + return (0); 1.485 + 1.486 + return (1); 1.487 +} 1.488 + 1.489 +/** 1.490 + * Check if SMF is valid and add missing EOT events. 1.491 + * 1.492 + * \return 0, if SMF is valid. 1.493 + */ 1.494 +static int 1.495 +smf_validate(smf_t *smf) 1.496 +{ 1.497 + int trackno, eventno, eot_found; 1.498 + smf_track_t *track; 1.499 + smf_event_t *event; 1.500 + 1.501 + if (smf->format < 0 || smf->format > 2) { 1.502 + fg_critical("SMF error: smf->format is less than zero of greater than two."); 1.503 + return (-1); 1.504 + } 1.505 + 1.506 + if (smf->number_of_tracks < 1) { 1.507 + fg_critical("SMF error: number of tracks is less than one."); 1.508 + return (-2); 1.509 + } 1.510 + 1.511 + if (smf->format == 0 && smf->number_of_tracks > 1) { 1.512 + fg_critical("SMF error: format is 0, but number of tracks is more than one."); 1.513 + return (-3); 1.514 + } 1.515 + 1.516 + if (smf->ppqn <= 0) { 1.517 + fg_critical("SMF error: PPQN has to be > 0."); 1.518 + return (-4); 1.519 + } 1.520 + 1.521 + for (trackno = 1; trackno <= smf->number_of_tracks; trackno++) { 1.522 + track = smf_get_track_by_number(smf, trackno); 1.523 + assert(track); 1.524 + 1.525 + eot_found = 0; 1.526 + 1.527 + for (eventno = 1; eventno <= track->number_of_events; eventno++) { 1.528 + event = smf_track_get_event_by_number(track, eventno); 1.529 + assert(event); 1.530 + 1.531 + if (!smf_event_is_valid(event)) { 1.532 + fg_critical("Event #%d on track #%d is invalid.", eventno, trackno); 1.533 + return (-5); 1.534 + } 1.535 + 1.536 + if (smf_event_is_eot(event)) { 1.537 + if (eot_found) { 1.538 + fg_critical("Duplicate End Of Track event on track #%d.", trackno); 1.539 + return (-6); 1.540 + } 1.541 + 1.542 + eot_found = 1; 1.543 + } 1.544 + } 1.545 + 1.546 + if (!eot_found) { 1.547 + if (smf_track_add_eot_delta_pulses(track, 0)) { 1.548 + fg_critical("smf_track_add_eot_delta_pulses failed."); 1.549 + return (-6); 1.550 + } 1.551 + } 1.552 + 1.553 + } 1.554 + 1.555 + return (0); 1.556 +} 1.557 + 1.558 +#ifndef NDEBUG 1.559 + 1.560 +static void 1.561 +assert_smf_event_is_identical(const smf_event_t *a, const smf_event_t *b) 1.562 +{ 1.563 + assert(a->event_number == b->event_number); 1.564 + assert(a->delta_time_pulses == b->delta_time_pulses); 1.565 + assert(abs(a->time_pulses - b->time_pulses) <= 2); 1.566 + assert(fabs(a->time_seconds - b->time_seconds) <= 0.01); 1.567 + assert(a->track_number == b->track_number); 1.568 + assert(a->midi_buffer_length == b->midi_buffer_length); 1.569 + assert(memcmp(a->midi_buffer, b->midi_buffer, a->midi_buffer_length) == 0); 1.570 +} 1.571 + 1.572 +static void 1.573 +assert_smf_track_is_identical(const smf_track_t *a, const smf_track_t *b) 1.574 +{ 1.575 + int i; 1.576 + 1.577 + assert(a->track_number == b->track_number); 1.578 + assert(a->number_of_events == b->number_of_events); 1.579 + 1.580 + for (i = 1; i <= a->number_of_events; i++) 1.581 + assert_smf_event_is_identical(smf_track_get_event_by_number(a, i), smf_track_get_event_by_number(b, i)); 1.582 +} 1.583 + 1.584 +static void 1.585 +assert_smf_is_identical(const smf_t *a, const smf_t *b) 1.586 +{ 1.587 + int i; 1.588 + 1.589 + assert(a->format == b->format); 1.590 + assert(a->ppqn == b->ppqn); 1.591 + assert(a->frames_per_second == b->frames_per_second); 1.592 + assert(a->resolution == b->resolution); 1.593 + assert(a->number_of_tracks == b->number_of_tracks); 1.594 + 1.595 + for (i = 1; i <= a->number_of_tracks; i++) 1.596 + assert_smf_track_is_identical(smf_get_track_by_number(a, i), smf_get_track_by_number(b, i)); 1.597 + 1.598 + /* We do not need to compare tempos explicitly, as tempo is always computed from track contents. */ 1.599 +} 1.600 + 1.601 +static void 1.602 +assert_smf_saved_correctly(const smf_t *smf, const char *file_name) 1.603 +{ 1.604 + smf_t *saved; 1.605 + 1.606 + saved = smf_load(file_name); 1.607 + assert(saved != NULL); 1.608 + 1.609 + assert_smf_is_identical(smf, saved); 1.610 + 1.611 + smf_delete(saved); 1.612 +} 1.613 + 1.614 +#endif /* !NDEBUG */ 1.615 + 1.616 +/** 1.617 + * Writes the contents of SMF to the file given. 1.618 + * \param smf SMF. 1.619 + * \param file_name Path to the file. 1.620 + * \return 0, if saving was successfull. 1.621 + */ 1.622 +int 1.623 +smf_save(smf_t *smf, const char *file_name) 1.624 +{ 1.625 + int i, error; 1.626 + smf_track_t *track; 1.627 + 1.628 + smf_rewind(smf); 1.629 + 1.630 + assert(pointers_are_clear(smf)); 1.631 + 1.632 + if (smf_validate(smf)) 1.633 + return (-1); 1.634 + 1.635 + if (write_mthd_header(smf)) 1.636 + return (-2); 1.637 + 1.638 + for (i = 1; i <= smf->number_of_tracks; i++) { 1.639 + track = smf_get_track_by_number(smf, i); 1.640 + 1.641 + assert(track != NULL); 1.642 + 1.643 + error = write_track(track); 1.644 + if (error) { 1.645 + free_buffer(smf); 1.646 + return (error); 1.647 + } 1.648 + } 1.649 + 1.650 + error = write_file(smf, file_name); 1.651 + 1.652 + free_buffer(smf); 1.653 + 1.654 + if (error) 1.655 + return (error); 1.656 + 1.657 +#ifndef NDEBUG 1.658 + assert_smf_saved_correctly(smf, file_name); 1.659 +#endif 1.660 + 1.661 + return (0); 1.662 +} 1.663 +