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
|