libsmf
smf_decode.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
34
35#include <stdlib.h>
36#include <string.h>
37#include <assert.h>
38#include <math.h>
39#include <errno.h>
40#ifdef __MINGW32__
41#include <windows.h>
42#else /* ! __MINGW32__ */
43#include <arpa/inet.h>
44#endif /* ! __MINGW32__ */
45#include <stdint.h>
46#include "smf.h"
47#include "smf_private.h"
48
49#define BUFFER_SIZE 1024
50
56int
58{
59 assert(event->midi_buffer);
60 assert(event->midi_buffer_length > 0);
61
62 if (event->midi_buffer[0] == 0xFF)
63 return (1);
64
65 return (0);
66}
67
71int
73{
74 assert(event->midi_buffer);
75 assert(event->midi_buffer_length > 0);
76
77 if (smf_event_is_metadata(event))
78 return (0);
79
80 if (event->midi_buffer[0] >= 0xF8)
81 return (1);
82
83 return (0);
84}
85
89int
91{
92 assert(event->midi_buffer);
93 assert(event->midi_buffer_length > 0);
94
95 if (event->midi_buffer[0] >= 0xF0 && event->midi_buffer[0] <= 0xF7)
96 return (1);
97
98 return (0);
99}
100
103int
105{
106 assert(event->midi_buffer);
107 assert(event->midi_buffer_length > 0);
108
109 if (event->midi_buffer[0] == 0xF0)
110 return (1);
111
112 return (0);
113}
114
115static char *
116smf_event_decode_textual(const smf_event_t *event, const char *name)
117{
118 int off = 0;
119 char *buf, *extracted;
120
121 buf = malloc(BUFFER_SIZE);
122 if (buf == NULL) {
123 g_critical("smf_event_decode_textual: malloc failed.");
124 return (NULL);
125 }
126
127 extracted = smf_event_extract_text(event);
128 if (extracted == NULL) {
129 free(buf);
130 return (NULL);
131 }
132
133 snprintf(buf + off, BUFFER_SIZE - off, "%s: %s", name, extracted);
134
135 return (buf);
136}
137
138static char *
139smf_event_decode_metadata(const smf_event_t *event)
140{
141 int off = 0, mspqn, flats, isminor;
142 char *buf;
143
144 static const char *const major_keys[] = {"Fb", "Cb", "Gb", "Db", "Ab",
145 "Eb", "Bb", "F", "C", "G", "D", "A", "E", "B", "F#", "C#", "G#"};
146
147 static const char *const minor_keys[] = {"Dbm", "Abm", "Ebm", "Bbm", "Fm",
148 "Cm", "Gm", "Dm", "Am", "Em", "Bm", "F#m", "C#m", "G#m", "D#m", "A#m", "E#m"};
149
150 assert(smf_event_is_metadata(event));
151
152 switch (event->midi_buffer[1]) {
153 case 0x01:
154 return (smf_event_decode_textual(event, "Text"));
155
156 case 0x02:
157 return (smf_event_decode_textual(event, "Copyright"));
158
159 case 0x03:
160 return (smf_event_decode_textual(event, "Sequence/Track Name"));
161
162 case 0x04:
163 return (smf_event_decode_textual(event, "Instrument"));
164
165 case 0x05:
166 return (smf_event_decode_textual(event, "Lyric"));
167
168 case 0x06:
169 return (smf_event_decode_textual(event, "Marker"));
170
171 case 0x07:
172 return (smf_event_decode_textual(event, "Cue Point"));
173
174 case 0x08:
175 return (smf_event_decode_textual(event, "Program Name"));
176
177 case 0x09:
178 return (smf_event_decode_textual(event, "Device (Port) Name"));
179
180 default:
181 break;
182 }
183
184 buf = malloc(BUFFER_SIZE);
185 if (buf == NULL) {
186 g_critical("smf_event_decode_metadata: malloc failed.");
187 return (NULL);
188 }
189
190 switch (event->midi_buffer[1]) {
191 case 0x00:
192 off += snprintf(buf + off, BUFFER_SIZE - off, "Sequence number");
193 break;
194
195 /* http://music.columbia.edu/pipermail/music-dsp/2004-August/061196.html */
196 case 0x20:
197 if (event->midi_buffer_length < 4) {
198 g_critical("smf_event_decode_metadata: truncated MIDI message.");
199 goto error;
200 }
201
202 off += snprintf(buf + off, BUFFER_SIZE - off, "Channel Prefix: %d", event->midi_buffer[3]);
203 break;
204
205 case 0x21:
206 if (event->midi_buffer_length < 4) {
207 g_critical("smf_event_decode_metadata: truncated MIDI message.");
208 goto error;
209 }
210
211 off += snprintf(buf + off, BUFFER_SIZE - off, "MIDI Port: %d", event->midi_buffer[3]);
212 break;
213
214 case 0x2F:
215 off += snprintf(buf + off, BUFFER_SIZE - off, "End Of Track");
216 break;
217
218 case 0x51:
219 if (event->midi_buffer_length < 6) {
220 g_critical("smf_event_decode_metadata: truncated MIDI message.");
221 goto error;
222 }
223
224 mspqn = (event->midi_buffer[3] << 16) + (event->midi_buffer[4] << 8) + event->midi_buffer[5];
225
226 off += snprintf(buf + off, BUFFER_SIZE - off, "Tempo: %d microseconds per quarter note, %.2f BPM",
227 mspqn, 60000000.0 / (double)mspqn);
228 break;
229
230 case 0x54:
231 off += snprintf(buf + off, BUFFER_SIZE - off, "SMPTE Offset");
232 break;
233
234 case 0x58:
235 if (event->midi_buffer_length < 7) {
236 g_critical("smf_event_decode_metadata: truncated MIDI message.");
237 goto error;
238 }
239
240 off += snprintf(buf + off, BUFFER_SIZE - off,
241 "Time Signature: %d/%d, %d clocks per click, %d notated 32nd notes per quarter note",
242 event->midi_buffer[3], (int)pow(2, event->midi_buffer[4]), event->midi_buffer[5],
243 event->midi_buffer[6]);
244 break;
245
246 case 0x59:
247 if (event->midi_buffer_length < 5) {
248 g_critical("smf_event_decode_metadata: truncated MIDI message.");
249 goto error;
250 }
251
252 flats = event->midi_buffer[3];
253 isminor = event->midi_buffer[4];
254
255 if (isminor != 0 && isminor != 1) {
256 g_critical("smf_event_decode_metadata: last byte of the Key Signature event has invalid value %d.", isminor);
257 goto error;
258 }
259
260 off += snprintf(buf + off, BUFFER_SIZE - off, "Key Signature: ");
261
262 if (flats > 8 && flats < 248) {
263 off += snprintf(buf + off, BUFFER_SIZE - off, "%d %s, %s key", abs((int8_t)flats),
264 flats > 127 ? "flats" : "sharps", isminor ? "minor" : "major");
265 } else {
266 int i = (flats - 248) & 255;
267
268 assert(i >= 0 && i < sizeof(minor_keys) / sizeof(*minor_keys));
269 assert(i >= 0 && i < sizeof(major_keys) / sizeof(*major_keys));
270
271 if (isminor)
272 off += snprintf(buf + off, BUFFER_SIZE - off, "%s", minor_keys[i]);
273 else
274 off += snprintf(buf + off, BUFFER_SIZE - off, "%s", major_keys[i]);
275 }
276
277 break;
278
279 case 0x7F:
280 off += snprintf(buf + off, BUFFER_SIZE - off, "Proprietary (aka Sequencer) Event, length %d",
281 event->midi_buffer_length);
282 break;
283
284 default:
285 goto error;
286 }
287
288 return (buf);
289
290error:
291 free(buf);
292
293 return (NULL);
294}
295
296static char *
297smf_event_decode_system_realtime(const smf_event_t *event)
298{
299 int off = 0;
300 char *buf;
301
302 assert(smf_event_is_system_realtime(event));
303
304 if (event->midi_buffer_length != 1) {
305 g_critical("smf_event_decode_system_realtime: event length is not 1.");
306 return (NULL);
307 }
308
309 buf = malloc(BUFFER_SIZE);
310 if (buf == NULL) {
311 g_critical("smf_event_decode_system_realtime: malloc failed.");
312 return (NULL);
313 }
314
315 switch (event->midi_buffer[0]) {
316 case 0xF8:
317 off += snprintf(buf + off, BUFFER_SIZE - off, "MIDI Clock (realtime)");
318 break;
319
320 case 0xF9:
321 off += snprintf(buf + off, BUFFER_SIZE - off, "Tick (realtime)");
322 break;
323
324 case 0xFA:
325 off += snprintf(buf + off, BUFFER_SIZE - off, "MIDI Start (realtime)");
326 break;
327
328 case 0xFB:
329 off += snprintf(buf + off, BUFFER_SIZE - off, "MIDI Continue (realtime)");
330 break;
331
332 case 0xFC:
333 off += snprintf(buf + off, BUFFER_SIZE - off, "MIDI Stop (realtime)");
334 break;
335
336 case 0xFE:
337 off += snprintf(buf + off, BUFFER_SIZE - off, "Active Sense (realtime)");
338 break;
339
340 default:
341 free(buf);
342 return (NULL);
343 }
344
345 return (buf);
346}
347
348static char *
349smf_event_decode_sysex(const smf_event_t *event)
350{
351 int off = 0;
352 char *buf, manufacturer, subid, subid2;
353
354 assert(smf_event_is_sysex(event));
355
356 if (event->midi_buffer_length < 5) {
357 g_critical("smf_event_decode_sysex: truncated MIDI message.");
358 return (NULL);
359 }
360
361 buf = malloc(BUFFER_SIZE);
362 if (buf == NULL) {
363 g_critical("smf_event_decode_sysex: malloc failed.");
364 return (NULL);
365 }
366
367 manufacturer = event->midi_buffer[1];
368
369 if (manufacturer == 0x7F) {
370 off += snprintf(buf + off, BUFFER_SIZE - off, "SysEx, realtime, channel %d", event->midi_buffer[2]);
371 } else if (manufacturer == 0x7E) {
372 off += snprintf(buf + off, BUFFER_SIZE - off, "SysEx, non-realtime, channel %d", event->midi_buffer[2]);
373 } else {
374 off += snprintf(buf + off, BUFFER_SIZE - off, "SysEx, manufacturer 0x%x", manufacturer);
375
376 return (buf);
377 }
378
379 subid = event->midi_buffer[3];
380 subid2 = event->midi_buffer[4];
381
382 if (subid == 0x01)
383 off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump Header");
384
385 else if (subid == 0x02)
386 off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump Data Packet");
387
388 else if (subid == 0x03)
389 off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump Request");
390
391 else if (subid == 0x04 && subid2 == 0x01)
392 off += snprintf(buf + off, BUFFER_SIZE - off, ", Master Volume");
393
394 else if (subid == 0x05 && subid2 == 0x01)
395 off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump Loop Point Retransmit");
396
397 else if (subid == 0x05 && subid2 == 0x02)
398 off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump Loop Point Request");
399
400 else if (subid == 0x06 && subid2 == 0x01)
401 off += snprintf(buf + off, BUFFER_SIZE - off, ", Identity Request");
402
403 else if (subid == 0x06 && subid2 == 0x02)
404 off += snprintf(buf + off, BUFFER_SIZE - off, ", Identity Reply");
405
406 else if (subid == 0x08 && subid2 == 0x00)
407 off += snprintf(buf + off, BUFFER_SIZE - off, ", Bulk Tuning Dump Request");
408
409 else if (subid == 0x08 && subid2 == 0x01)
410 off += snprintf(buf + off, BUFFER_SIZE - off, ", Bulk Tuning Dump");
411
412 else if (subid == 0x08 && subid2 == 0x02)
413 off += snprintf(buf + off, BUFFER_SIZE - off, ", Single Note Tuning Change");
414
415 else if (subid == 0x08 && subid2 == 0x03)
416 off += snprintf(buf + off, BUFFER_SIZE - off, ", Bulk Tuning Dump Request (Bank)");
417
418 else if (subid == 0x08 && subid2 == 0x04)
419 off += snprintf(buf + off, BUFFER_SIZE - off, ", Key Based Tuning Dump");
420
421 else if (subid == 0x08 && subid2 == 0x05)
422 off += snprintf(buf + off, BUFFER_SIZE - off, ", Scale/Octave Tuning Dump, 1 byte format");
423
424 else if (subid == 0x08 && subid2 == 0x06)
425 off += snprintf(buf + off, BUFFER_SIZE - off, ", Scale/Octave Tuning Dump, 2 byte format");
426
427 else if (subid == 0x08 && subid2 == 0x07)
428 off += snprintf(buf + off, BUFFER_SIZE - off, ", Single Note Tuning Change (Bank)");
429
430 else if (subid == 0x09)
431 off += snprintf(buf + off, BUFFER_SIZE - off, ", General MIDI %s", subid2 == 0 ? "disable" : "enable");
432
433 else if (subid == 0x7C)
434 off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump Wait");
435
436 else if (subid == 0x7D)
437 off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump Cancel");
438
439 else if (subid == 0x7E)
440 off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump NAK");
441
442 else if (subid == 0x7F)
443 off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump ACK");
444
445 else
446 off += snprintf(buf + off, BUFFER_SIZE - off, ", Unknown");
447
448 return (buf);
449}
450
451static char *
452smf_event_decode_system_common(const smf_event_t *event)
453{
454 int off = 0;
455 char *buf;
456
457 assert(smf_event_is_system_common(event));
458
459 if (smf_event_is_sysex(event))
460 return (smf_event_decode_sysex(event));
461
462 buf = malloc(BUFFER_SIZE);
463 if (buf == NULL) {
464 g_critical("smf_event_decode_system_realtime: malloc failed.");
465 return (NULL);
466 }
467
468 switch (event->midi_buffer[0]) {
469 case 0xF1:
470 off += snprintf(buf + off, BUFFER_SIZE - off, "MTC Quarter Frame");
471 break;
472
473 case 0xF2:
474 off += snprintf(buf + off, BUFFER_SIZE - off, "Song Position Pointer");
475 break;
476
477 case 0xF3:
478 off += snprintf(buf + off, BUFFER_SIZE - off, "Song Select");
479 break;
480
481 case 0xF6:
482 off += snprintf(buf + off, BUFFER_SIZE - off, "Tune Request");
483 break;
484
485 default:
486 free(buf);
487 return (NULL);
488 }
489
490 return (buf);
491}
492
493static void
494note_from_int(char *buf, int note_number)
495{
496 int note, octave;
497 char *names[] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"};
498
499 octave = note_number / 12 - 1;
500 note = note_number % 12;
501
502 sprintf(buf, "%s%d", names[note], octave);
503}
504
513char *
515{
516 int off = 0, channel;
517 char *buf, note[5];
518
519 if (smf_event_is_metadata(event))
520 return (smf_event_decode_metadata(event));
521
523 return (smf_event_decode_system_realtime(event));
524
526 return (smf_event_decode_system_common(event));
527
528 if (!smf_event_length_is_valid(event)) {
529 g_critical("smf_event_decode: incorrect MIDI message length.");
530 return (NULL);
531 }
532
533 buf = malloc(BUFFER_SIZE);
534 if (buf == NULL) {
535 g_critical("smf_event_decode: malloc failed.");
536 return (NULL);
537 }
538
539 /* + 1, because user-visible channels used to be in range <1-16>. */
540 channel = (event->midi_buffer[0] & 0x0F) + 1;
541
542 switch (event->midi_buffer[0] & 0xF0) {
543 case 0x80:
544 note_from_int(note, event->midi_buffer[1]);
545 off += snprintf(buf + off, BUFFER_SIZE - off, "Note Off, channel %d, note %s, velocity %d",
546 channel, note, event->midi_buffer[2]);
547 break;
548
549 case 0x90:
550 note_from_int(note, event->midi_buffer[1]);
551 off += snprintf(buf + off, BUFFER_SIZE - off, "Note On, channel %d, note %s, velocity %d",
552 channel, note, event->midi_buffer[2]);
553 break;
554
555 case 0xA0:
556 note_from_int(note, event->midi_buffer[1]);
557 off += snprintf(buf + off, BUFFER_SIZE - off, "Aftertouch, channel %d, note %s, pressure %d",
558 channel, note, event->midi_buffer[2]);
559 break;
560
561 case 0xB0:
562 off += snprintf(buf + off, BUFFER_SIZE - off, "Controller, channel %d, controller %d, value %d",
563 channel, event->midi_buffer[1], event->midi_buffer[2]);
564 break;
565
566 case 0xC0:
567 off += snprintf(buf + off, BUFFER_SIZE - off, "Program Change, channel %d, controller %d",
568 channel, event->midi_buffer[1]);
569 break;
570
571 case 0xD0:
572 off += snprintf(buf + off, BUFFER_SIZE - off, "Channel Pressure, channel %d, pressure %d",
573 channel, event->midi_buffer[1]);
574 break;
575
576 case 0xE0:
577 off += snprintf(buf + off, BUFFER_SIZE - off, "Pitch Wheel, channel %d, value %d",
578 channel, ((int)event->midi_buffer[2] << 7) | (int)event->midi_buffer[2]);
579 break;
580
581 default:
582 free(buf);
583 return (NULL);
584 }
585
586 return (buf);
587}
588
597char *
599{
600 int off = 0;
601 char *buf;
602
603 buf = malloc(BUFFER_SIZE);
604 if (buf == NULL) {
605 g_critical("smf_event_decode: malloc failed.");
606 return (NULL);
607 }
608
609 off += snprintf(buf + off, BUFFER_SIZE - off, "format: %d ", smf->format);
610
611 switch (smf->format) {
612 case 0:
613 off += snprintf(buf + off, BUFFER_SIZE - off, "(single track)");
614 break;
615
616 case 1:
617 off += snprintf(buf + off, BUFFER_SIZE - off, "(several simultaneous tracks)");
618 break;
619
620 case 2:
621 off += snprintf(buf + off, BUFFER_SIZE - off, "(several independent tracks)");
622 break;
623
624 default:
625 off += snprintf(buf + off, BUFFER_SIZE - off, "(INVALID FORMAT)");
626 break;
627 }
628
629 off += snprintf(buf + off, BUFFER_SIZE - off, "; number of tracks: %d", smf->number_of_tracks);
630
631 if (smf->ppqn != 0)
632 off += snprintf(buf + off, BUFFER_SIZE - off, "; division: %d PPQN", smf->ppqn);
633 else
634 off += snprintf(buf + off, BUFFER_SIZE - off, "; division: %d FPS, %d resolution", smf->frames_per_second, smf->resolution);
635
636 return (buf);
637}
638
Public interface declaration for libsmf, Standard MIDI File format library.
struct smf_event_struct smf_event_t
Definition smf.h:334
char * smf_event_extract_text(const smf_event_t *event) WARN_UNUSED_RESULT
Extracts text from "textual metaevents", such as Text or Lyric.
Definition smf_load.c:657
struct smf_struct smf_t
Definition smf.h:255
int smf_event_is_system_realtime(const smf_event_t *event)
Definition smf_decode.c:72
char * smf_decode(const smf_t *smf)
Definition smf_decode.c:598
int smf_event_is_sysex(const smf_event_t *event)
Definition smf_decode.c:104
char * smf_event_decode(const smf_event_t *event)
Definition smf_decode.c:514
int smf_event_is_metadata(const smf_event_t *event)
Definition smf_decode.c:57
int smf_event_is_system_common(const smf_event_t *event)
Definition smf_decode.c:90
int smf_event_length_is_valid(const smf_event_t *event)
Definition smf_load.c:728
Private header.
smf_t * smf
Definition smfsh.c:56
unsigned char * midi_buffer
Pointer to the buffer containing MIDI message.
Definition smf.h:322
int midi_buffer_length
Length of the MIDI message in the buffer, in bytes.
Definition smf.h:325