libsmf
smf_save.c
Go to the documentation of this file.
1/*-
2 * Copyright (c) 2007, 2008 Edward Tomasz NapieraƂa <trasz@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * ALTHOUGH THIS SOFTWARE IS MADE OF WIN AND SCIENCE, IT IS PROVIDED BY THE
15 * AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18 * THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
20 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
21 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 */
27
35/* Reference: http://www.borg.com/~jglatt/tech/midifile.htm */
36
37#include <stdlib.h>
38#include <string.h>
39#include <assert.h>
40#include <math.h>
41#include <errno.h>
42#ifdef __MINGW32__
43#include <windows.h>
44#else /* ! __MINGW32__ */
45#include <arpa/inet.h>
46#endif /* ! __MINGW32__ */
47#include "smf.h"
48#include "smf_private.h"
49
50#define MAX_VLQ_LENGTH 128
51
57static void *
58smf_extend(smf_t *smf, const int length)
59{
60 int i, previous_file_buffer_length = smf->file_buffer_length;
61 char *previous_file_buffer = smf->file_buffer;
62
63 /* XXX: Not terribly efficient. */
64 smf->file_buffer_length += length;
66 if (smf->file_buffer == NULL) {
67 g_critical("realloc(3) failed: %s", strerror(errno));
69 return (NULL);
70 }
71
72 /* Fix up pointers. XXX: omgwtf. */
73 for (i = 1; i <= smf->number_of_tracks; i++) {
74 smf_track_t *track;
75 track = smf_get_track_by_number(smf, i);
76 if (track->file_buffer != NULL)
77 track->file_buffer = (char *)track->file_buffer + ((char *)smf->file_buffer - previous_file_buffer);
78 }
79
80 return ((char *)smf->file_buffer + previous_file_buffer_length);
81}
82
87static int
88smf_append(smf_t *smf, const void *buffer, const int buffer_length)
89{
90 void *dest;
91
92 dest = smf_extend(smf, buffer_length);
93 if (dest == NULL) {
94 g_critical("Cannot extend track buffer.");
95 return (-1);
96 }
97
98 memcpy(dest, buffer, buffer_length);
99
100 return (0);
101}
102
106static int
107write_mthd_header(smf_t *smf)
108{
109 struct mthd_chunk_struct mthd_chunk;
110
111 memcpy(mthd_chunk.mthd_header.id, "MThd", 4);
112 mthd_chunk.mthd_header.length = htonl(6);
113 mthd_chunk.format = htons(smf->format);
114 mthd_chunk.number_of_tracks = htons(smf->number_of_tracks);
115 mthd_chunk.division = htons(smf->ppqn);
116
117 return (smf_append(smf, &mthd_chunk, sizeof(mthd_chunk)));
118}
119
125static void *
126track_extend(smf_track_t *track, const int length)
127{
128 void *buf;
129
130 assert(track->smf);
131
132 buf = smf_extend(track->smf, length);
133 if (buf == NULL)
134 return (NULL);
135
136 track->file_buffer_length += length;
137 if (track->file_buffer == NULL)
138 track->file_buffer = buf;
139
140 return (buf);
141}
142
147static int
148track_append(smf_track_t *track, const void *buffer, const int buffer_length)
149{
150 void *dest;
151
152 dest = track_extend(track, buffer_length);
153 if (dest == NULL) {
154 g_critical("Cannot extend track buffer.");
155 return (-1);
156 }
157
158 memcpy(dest, buffer, buffer_length);
159
160 return (0);
161}
162
163static int
164format_vlq(unsigned char *buf, int length, unsigned long value)
165{
166 int i;
167 unsigned long buffer;
168
169 /* Taken from http://www.borg.com/~jglatt/tech/midifile/vari.htm */
170 buffer = value & 0x7F;
171
172 while ((value >>= 7)) {
173 buffer <<= 8;
174 buffer |= ((value & 0x7F) | 0x80);
175 }
176
177 for (i = 0;; i++) {
178 buf[i] = buffer;
179
180 if (buffer & 0x80)
181 buffer >>= 8;
182 else
183 break;
184 }
185
186 assert(i <= length);
187
188 /* + 1, because "i" is an offset, not a count. */
189 return (i + 1);
190}
191
193smf_event_new_textual(int type, const char *text)
194{
195 int vlq_length, text_length, copied_length;
196 smf_event_t *event;
197
198 assert(type >= 1 && type <= 9);
199
200 text_length = strlen(text);
201
202 event = smf_event_new();
203 if (event == NULL)
204 return (NULL);
205
206 /* "2 +" is for leading 0xFF 0xtype. */
207 event->midi_buffer_length = 2 + text_length + MAX_VLQ_LENGTH;
208 event->midi_buffer = malloc(event->midi_buffer_length);
209 if (event->midi_buffer == NULL) {
210 g_critical("Cannot allocate MIDI buffer structure: %s", strerror(errno));
211 smf_event_delete(event);
212
213 return (NULL);
214 }
215
216 event->midi_buffer[0] = 0xFF;
217 event->midi_buffer[1] = type;
218
219 vlq_length = format_vlq(event->midi_buffer + 2, MAX_VLQ_LENGTH - 2, text_length);
220 copied_length = snprintf((char *)event->midi_buffer + vlq_length + 2, event->midi_buffer_length - vlq_length - 2, "%s", text);
221
222 assert(copied_length == text_length);
223
224 event->midi_buffer_length = 2 + vlq_length + text_length;
225
226 return event;
227}
228
232static int
233write_vlq(smf_event_t *event, unsigned long value)
234{
235 unsigned char buf[MAX_VLQ_LENGTH];
236 int vlq_length;
237
238 vlq_length = format_vlq(buf, MAX_VLQ_LENGTH, value);
239
240 return (track_append(event->track, buf, vlq_length));
241}
242
247static int
248write_event_time(smf_event_t *event)
249{
250 assert(event->delta_time_pulses >= 0);
251
252 return (write_vlq(event, event->delta_time_pulses));
253}
254
255static int
256write_sysex_contents(smf_event_t *event)
257{
258 int ret;
259 unsigned char sysex_status = 0xF0;
260
261 assert(smf_event_is_sysex(event));
262
263 ret = track_append(event->track, &sysex_status, 1);
264 if (ret)
265 return (ret);
266
267 /* -1, because length does not include status byte. */
268 ret = write_vlq(event, event->midi_buffer_length - 1);
269 if (ret)
270 return (ret);
271
272 ret = track_append(event->track, event->midi_buffer + 1, event->midi_buffer_length - 1);
273 if (ret)
274 return (ret);
275
276 return (0);
277}
278
282static int
283write_escaped_event_contents(smf_event_t *event)
284{
285 int ret;
286 unsigned char escape_status = 0xF7;
287
288 if (smf_event_is_sysex(event))
289 return (write_sysex_contents(event));
290
291 ret = track_append(event->track, &escape_status, 1);
292 if (ret)
293 return (ret);
294
295 ret = write_vlq(event, event->midi_buffer_length);
296 if (ret)
297 return (ret);
298
299 ret = track_append(event->track, event->midi_buffer, event->midi_buffer_length);
300 if (ret)
301 return (ret);
302
303 return (0);
304}
305
310static int
311write_event_contents(smf_event_t *event)
312{
314 return (write_escaped_event_contents(event));
315
316 return (track_append(event->track, event->midi_buffer, event->midi_buffer_length));
317}
318
322static int
323write_event(smf_event_t *event)
324{
325 int ret;
326
327 ret = write_event_time(event);
328 if (ret)
329 return (ret);
330
331 ret = write_event_contents(event);
332 if (ret)
333 return (ret);
334
335 return (0);
336}
337
341static int
342write_mtrk_header(smf_track_t *track)
343{
344 struct chunk_header_struct mtrk_header;
345
346 memcpy(mtrk_header.id, "MTrk", 4);
347
348 return (track_append(track, &mtrk_header, sizeof(mtrk_header)));
349}
350
354static int
355write_mtrk_length(smf_track_t *track)
356{
357 struct chunk_header_struct *mtrk_header;
358
359 assert(track->file_buffer != NULL);
360 assert(track->file_buffer_length >= 6);
361
362 mtrk_header = (struct chunk_header_struct *)track->file_buffer;
363 mtrk_header->length = htonl(track->file_buffer_length - sizeof(struct chunk_header_struct));
364
365 return (0);
366}
367
371static int
372write_track(smf_track_t *track)
373{
374 int ret;
375 smf_event_t *event;
376
377 ret = write_mtrk_header(track);
378 if (ret)
379 return (ret);
380
381 while ((event = smf_track_get_next_event(track)) != NULL) {
382 ret = write_event(event);
383 if (ret)
384 return (ret);
385 }
386
387 ret = write_mtrk_length(track);
388 if (ret)
389 return (ret);
390
391 return (0);
392}
393
397static int
398write_file(smf_t *smf, const char *file_name)
399{
400 FILE *stream;
401
402 stream = fopen(file_name, "wb+");
403 if (stream == NULL) {
404 g_critical("Cannot open input file: %s", strerror(errno));
405
406 return (-1);
407 }
408
409 if (fwrite(smf->file_buffer, 1, smf->file_buffer_length, stream) != smf->file_buffer_length) {
410 g_critical("fwrite(3) failed: %s", strerror(errno));
411
412 return (-2);
413 }
414
415 if (fclose(stream)) {
416 g_critical("fclose(3) failed: %s", strerror(errno));
417
418 return (-3);
419 }
420
421 return (0);
422}
423
424static void
425free_buffer(smf_t *smf)
426{
427 int i;
428 smf_track_t *track;
429
430 /* Clear the pointers. */
432 free(smf->file_buffer);
433 smf->file_buffer = NULL;
435
436 for (i = 1; i <= smf->number_of_tracks; i++) {
437 track = smf_get_track_by_number(smf, i);
438 assert(track);
439 track->file_buffer = NULL;
440 track->file_buffer_length = 0;
441 }
442}
443
444#ifndef NDEBUG
445
449static int
450pointers_are_clear(smf_t *smf)
451{
452 int i;
453
454 smf_track_t *track;
455 assert(smf->file_buffer == NULL);
456 assert(smf->file_buffer_length == 0);
457
458 for (i = 1; i <= smf->number_of_tracks; i++) {
459 track = smf_get_track_by_number(smf, i);
460
461 assert(track != NULL);
462 assert(track->file_buffer == NULL);
463 assert(track->file_buffer_length == 0);
464 }
465
466 return (1);
467}
468
469#endif /* !NDEBUG */
470
474int
476{
477 if (event->midi_buffer_length != 3)
478 return (0);
479
480 if (event->midi_buffer[0] != 0xFF || event->midi_buffer[1] != 0x2F || event->midi_buffer[2] != 0x00)
481 return (0);
482
483 return (1);
484}
485
491static int
492smf_validate(smf_t *smf)
493{
494 int trackno, eventno, eot_found;
495 smf_track_t *track;
496 smf_event_t *event;
497
498 if (smf->format < 0 || smf->format > 2) {
499 g_critical("SMF error: smf->format is less than zero of greater than two.");
500 return (-1);
501 }
502
503 if (smf->number_of_tracks < 1) {
504 g_critical("SMF error: number of tracks is less than one.");
505 return (-2);
506 }
507
508 if (smf->format == 0 && smf->number_of_tracks > 1) {
509 g_critical("SMF error: format is 0, but number of tracks is more than one.");
510 return (-3);
511 }
512
513 if (smf->ppqn <= 0) {
514 g_critical("SMF error: PPQN has to be > 0.");
515 return (-4);
516 }
517
518 for (trackno = 1; trackno <= smf->number_of_tracks; trackno++) {
519 track = smf_get_track_by_number(smf, trackno);
520 assert(track);
521
522 eot_found = 0;
523
524 for (eventno = 1; eventno <= track->number_of_events; eventno++) {
525 event = smf_track_get_event_by_number(track, eventno);
526 assert(event);
527
528 if (!smf_event_is_valid(event)) {
529 g_critical("Event #%d on track #%d is invalid.", eventno, trackno);
530 return (-5);
531 }
532
533 if (smf_event_is_eot(event)) {
534 if (eot_found) {
535 g_critical("Duplicate End Of Track event on track #%d.", trackno);
536 return (-6);
537 }
538
539 eot_found = 1;
540 }
541 }
542
543 if (!eot_found) {
544 if (smf_track_add_eot_delta_pulses(track, 0)) {
545 g_critical("smf_track_add_eot_delta_pulses failed.");
546 return (-6);
547 }
548 }
549
550 }
551
552 return (0);
553}
554
555#ifndef NDEBUG
556
557static void
558assert_smf_event_is_identical(const smf_event_t *a, const smf_event_t *b)
559{
560 assert(a->event_number == b->event_number);
561 assert(a->delta_time_pulses == b->delta_time_pulses);
562 assert(abs(a->time_pulses - b->time_pulses) <= 2);
563 assert(fabs(a->time_seconds - b->time_seconds) <= 0.01);
564 assert(a->track_number == b->track_number);
565 assert(a->midi_buffer_length == b->midi_buffer_length);
566 assert(memcmp(a->midi_buffer, b->midi_buffer, a->midi_buffer_length) == 0);
567}
568
569static void
570assert_smf_track_is_identical(const smf_track_t *a, const smf_track_t *b)
571{
572 int i;
573
574 assert(a->track_number == b->track_number);
575 assert(a->number_of_events == b->number_of_events);
576
577 for (i = 1; i <= a->number_of_events; i++)
578 assert_smf_event_is_identical(smf_track_get_event_by_number(a, i), smf_track_get_event_by_number(b, i));
579}
580
581static void
582assert_smf_is_identical(const smf_t *a, const smf_t *b)
583{
584 int i;
585
586 assert(a->format == b->format);
587 assert(a->ppqn == b->ppqn);
588 assert(a->frames_per_second == b->frames_per_second);
589 assert(a->resolution == b->resolution);
590 assert(a->number_of_tracks == b->number_of_tracks);
591
592 for (i = 1; i <= a->number_of_tracks; i++)
593 assert_smf_track_is_identical(smf_get_track_by_number(a, i), smf_get_track_by_number(b, i));
594
595 /* We do not need to compare tempos explicitly, as tempo is always computed from track contents. */
596}
597
598static void
599assert_smf_saved_correctly(const smf_t *smf, const char *file_name)
600{
601 smf_t *saved;
602
603 saved = smf_load(file_name);
604 assert(saved != NULL);
605
606 assert_smf_is_identical(smf, saved);
607
608 smf_delete(saved);
609}
610
611#endif /* !NDEBUG */
612
619int
620smf_save(smf_t *smf, const char *file_name)
621{
622 int i, error;
623 smf_track_t *track;
624
626
627 assert(pointers_are_clear(smf));
628
629 if (smf_validate(smf))
630 return (-1);
631
632 if (write_mthd_header(smf))
633 return (-2);
634
635 for (i = 1; i <= smf->number_of_tracks; i++) {
636 track = smf_get_track_by_number(smf, i);
637
638 assert(track != NULL);
639
640 error = write_track(track);
641 if (error) {
642 free_buffer(smf);
643 return (error);
644 }
645 }
646
647 error = write_file(smf, file_name);
648
649 free_buffer(smf);
650
651 if (error)
652 return (error);
653
654#ifndef NDEBUG
655 assert_smf_saved_correctly(smf, file_name);
656#endif
657
658 return (0);
659}
660
smf_event_t * smf_event_new(void)
Allocates new smf_event_t structure.
Definition: smf.c:217
smf_event_t * smf_track_get_event_by_number(const smf_track_t *track, int event_number)
Definition: smf.c:769
smf_track_t * smf_get_track_by_number(const smf_t *smf, int track_number)
Definition: smf.c:748
void smf_delete(smf_t *smf)
Frees smf and all it's descendant structures.
Definition: smf.c:88
int smf_track_add_eot_delta_pulses(smf_track_t *track, int delta)
Add End Of Track metaevent.
Definition: smf.c:524
smf_event_t * smf_track_get_next_event(smf_track_t *track)
Returns next event from the track given and advances next event counter.
Definition: smf.c:692
void smf_rewind(smf_t *smf)
Rewinds the SMF.
Definition: smf.c:899
void smf_event_delete(smf_event_t *event)
Detaches event from its track and frees it.
Definition: smf.c:363
Public interface declaration for libsmf, Standard MIDI File format library.
int smf_event_is_system_common(const smf_event_t *event) WARN_UNUSED_RESULT
Definition: smf_decode.c:90
int smf_event_is_sysex(const smf_event_t *event) WARN_UNUSED_RESULT
Definition: smf_decode.c:104
int smf_event_is_valid(const smf_event_t *event) WARN_UNUSED_RESULT
Definition: smf_load.c:755
int smf_event_is_system_realtime(const smf_event_t *event) WARN_UNUSED_RESULT
Definition: smf_decode.c:72
smf_t * smf_load(const char *file_name) WARN_UNUSED_RESULT
Loads SMF file.
Definition: smf_load.c:938
Private header.
int smf_save(smf_t *smf, const char *file_name)
Writes the contents of SMF to the file given.
Definition: smf_save.c:620
smf_event_t * smf_event_new_textual(int type, const char *text)
Definition: smf_save.c:193
int smf_event_is_eot(const smf_event_t *event)
Definition: smf_save.c:475
smf_t * smf
Definition: smfsh.c:56
SMF chunk header, used only by smf_load.c and smf_save.c.
Definition: smf_private.h:53
SMF chunk, used only by smf_load.c and smf_save.c.
Definition: smf_private.h:59
Represents a single MIDI event or metaevent.
Definition: smf.h:301
int time_pulses
Time, in pulses, since the start of the song.
Definition: smf.h:313
int event_number
Number of this event in the track.
Definition: smf.h:306
unsigned char * midi_buffer
Pointer to the buffer containing MIDI message.
Definition: smf.h:322
double time_seconds
Time, in seconds, since the start of the song.
Definition: smf.h:316
int track_number
Tracks are numbered consecutively, starting from 1.
Definition: smf.h:319
int midi_buffer_length
Length of the MIDI message in the buffer, in bytes.
Definition: smf.h:325
int delta_time_pulses
Note that the time fields are invalid, if event is not attached to a track.
Definition: smf.h:310
smf_track_t * track
Pointer to the track, or NULL if event is not attached.
Definition: smf.h:303
Represents a "song", that is, collection of one or more tracks.
Definition: smf.h:230
void * file_buffer
Definition: smf.h:241
int resolution
Definition: smf.h:236
int format
Definition: smf.h:231
int file_buffer_length
Definition: smf.h:242
int frames_per_second
Definition: smf.h:235
int number_of_tracks
Definition: smf.h:237
int ppqn
These fields are extracted from "division" field of MThd header.
Definition: smf.h:234
Represents a single track.
Definition: smf.h:271
int track_number
Definition: smf.h:274
smf_t * smf
Definition: smf.h:272
void * file_buffer
These are private fields using only by loading and saving routines.
Definition: smf.h:278
int file_buffer_length
Definition: smf.h:279
int number_of_events
Definition: smf.h:275