Misc fixes for smfsh.c, no significant functional changes.
[libsmf.git] / src / smf_decode.c
blob1c1b91e292029d72fcde1fc6f6884e80c5b9b16e
1 /*-
2 * Copyright (c) 2007, 2008 Edward Tomasz NapieraƂa <trasz@FreeBSD.org>
3 * All rights reserved.
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.
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.
28 /**
29 * \file
31 * Event decoding routines.
35 #include <stdlib.h>
36 #include <string.h>
37 #include <assert.h>
38 #include <math.h>
39 #include <errno.h>
40 #include <arpa/inet.h>
41 #include <stdint.h>
42 #include "smf.h"
43 #include "smf_private.h"
45 #define BUFFER_SIZE 1024
47 /**
48 * \return Nonzero if event is metaevent. You should never send metaevents;
49 * they are not really MIDI messages. They carry information like track title,
50 * time signature etc.
52 int
53 smf_event_is_metadata(const smf_event_t *event)
55 assert(event->midi_buffer);
56 assert(event->midi_buffer_length > 0);
58 if (event->midi_buffer[0] == 0xFF)
59 return (1);
61 return (0);
64 /**
65 * \return Nonzero if event is System Realtime.
67 int
68 smf_event_is_system_realtime(const smf_event_t *event)
70 assert(event->midi_buffer);
71 assert(event->midi_buffer_length > 0);
73 if (smf_event_is_metadata(event))
74 return (0);
76 if (event->midi_buffer[0] >= 0xF8)
77 return (1);
79 return (0);
82 /**
83 * \return Nonzero if event is System Common.
85 int
86 smf_event_is_system_common(const smf_event_t *event)
88 assert(event->midi_buffer);
89 assert(event->midi_buffer_length > 0);
91 if (event->midi_buffer[0] >= 0xF0 && event->midi_buffer[0] <= 0xF7)
92 return (1);
94 return (0);
96 /**
97 * \return Nonzero if event is SysEx message.
99 int
100 smf_event_is_sysex(const smf_event_t *event)
102 assert(event->midi_buffer);
103 assert(event->midi_buffer_length > 0);
105 if (event->midi_buffer[0] == 0xF0)
106 return (1);
108 return (0);
111 static char *
112 smf_event_decode_textual(const smf_event_t *event, const char *name)
114 int off = 0;
115 char *buf, *extracted;
117 buf = malloc(BUFFER_SIZE);
118 if (buf == NULL) {
119 g_critical("smf_event_decode_textual: malloc failed.");
120 return (NULL);
123 extracted = smf_event_extract_text(event);
124 if (extracted == NULL) {
125 free(buf);
126 return (NULL);
129 snprintf(buf + off, BUFFER_SIZE - off, "%s: %s", name, extracted);
131 return (buf);
134 static char *
135 smf_event_decode_metadata(const smf_event_t *event)
137 int off = 0, mspqn, flats, isminor;
138 char *buf;
140 static const char *const major_keys[] = {"Fb", "Cb", "Gb", "Db", "Ab",
141 "Eb", "Bb", "F", "C", "G", "D", "A", "E", "B", "F#", "C#", "G#"};
143 static const char *const minor_keys[] = {"Dbm", "Abm", "Ebm", "Bbm", "Fm",
144 "Cm", "Gm", "Dm", "Am", "Em", "Bm", "F#m", "C#m", "G#m", "D#m", "A#m", "E#m"};
146 assert(smf_event_is_metadata(event));
148 switch (event->midi_buffer[1]) {
149 case 0x01:
150 return (smf_event_decode_textual(event, "Text"));
152 case 0x02:
153 return (smf_event_decode_textual(event, "Copyright"));
155 case 0x03:
156 return (smf_event_decode_textual(event, "Sequence/Track Name"));
158 case 0x04:
159 return (smf_event_decode_textual(event, "Instrument"));
161 case 0x05:
162 return (smf_event_decode_textual(event, "Lyric"));
164 case 0x06:
165 return (smf_event_decode_textual(event, "Marker"));
167 case 0x07:
168 return (smf_event_decode_textual(event, "Cue Point"));
170 case 0x08:
171 return (smf_event_decode_textual(event, "Program Name"));
173 case 0x09:
174 return (smf_event_decode_textual(event, "Device (Port) Name"));
176 default:
177 break;
180 buf = malloc(BUFFER_SIZE);
181 if (buf == NULL) {
182 g_critical("smf_event_decode_metadata: malloc failed.");
183 return (NULL);
186 switch (event->midi_buffer[1]) {
187 case 0x00:
188 off += snprintf(buf + off, BUFFER_SIZE - off, "Sequence number");
189 break;
191 /* http://music.columbia.edu/pipermail/music-dsp/2004-August/061196.html */
192 case 0x20:
193 if (event->midi_buffer_length < 4) {
194 g_critical("smf_event_decode_metadata: truncated MIDI message.");
195 goto error;
198 off += snprintf(buf + off, BUFFER_SIZE - off, "Channel Prefix: %d", event->midi_buffer[3]);
199 break;
201 case 0x21:
202 if (event->midi_buffer_length < 4) {
203 g_critical("smf_event_decode_metadata: truncated MIDI message.");
204 goto error;
207 off += snprintf(buf + off, BUFFER_SIZE - off, "MIDI Port: %d", event->midi_buffer[3]);
208 break;
210 case 0x2F:
211 off += snprintf(buf + off, BUFFER_SIZE - off, "End Of Track");
212 break;
214 case 0x51:
215 if (event->midi_buffer_length < 6) {
216 g_critical("smf_event_decode_metadata: truncated MIDI message.");
217 goto error;
220 mspqn = (event->midi_buffer[3] << 16) + (event->midi_buffer[4] << 8) + event->midi_buffer[5];
222 off += snprintf(buf + off, BUFFER_SIZE - off, "Tempo: %d microseconds per quarter note, %.2f BPM",
223 mspqn, 60000000.0 / (double)mspqn);
224 break;
226 case 0x54:
227 off += snprintf(buf + off, BUFFER_SIZE - off, "SMPTE Offset");
228 break;
230 case 0x58:
231 if (event->midi_buffer_length < 7) {
232 g_critical("smf_event_decode_metadata: truncated MIDI message.");
233 goto error;
236 off += snprintf(buf + off, BUFFER_SIZE - off,
237 "Time Signature: %d/%d, %d clocks per click, %d notated 32nd notes per quarter note",
238 event->midi_buffer[3], (int)pow(2, event->midi_buffer[4]), event->midi_buffer[5],
239 event->midi_buffer[6]);
240 break;
242 case 0x59:
243 if (event->midi_buffer_length < 5) {
244 g_critical("smf_event_decode_metadata: truncated MIDI message.");
245 goto error;
248 flats = event->midi_buffer[3];
249 isminor = event->midi_buffer[4];
251 if (isminor != 0 && isminor != 1) {
252 g_critical("smf_event_decode_metadata: last byte of the Key Signature event has invalid value %d.", isminor);
253 goto error;
256 off += snprintf(buf + off, BUFFER_SIZE - off, "Key Signature: ");
258 if (flats > 8 && flats < 248) {
259 off += snprintf(buf + off, BUFFER_SIZE - off, "%d %s, %s key", abs((int8_t)flats),
260 flats > 127 ? "flats" : "sharps", isminor ? "minor" : "major");
261 } else {
262 int i = (flats - 248) & 255;
264 assert(i >= 0 && i < sizeof(minor_keys) / sizeof(*minor_keys));
265 assert(i >= 0 && i < sizeof(major_keys) / sizeof(*major_keys));
267 if (isminor)
268 off += snprintf(buf + off, BUFFER_SIZE - off, "%s", minor_keys[i]);
269 else
270 off += snprintf(buf + off, BUFFER_SIZE - off, "%s", major_keys[i]);
273 break;
275 case 0x7F:
276 off += snprintf(buf + off, BUFFER_SIZE - off, "Proprietary (aka Sequencer) Event, length %d",
277 event->midi_buffer_length);
278 break;
280 default:
281 goto error;
284 return (buf);
286 error:
287 free(buf);
289 return (NULL);
292 static char *
293 smf_event_decode_system_realtime(const smf_event_t *event)
295 int off = 0;
296 char *buf;
298 assert(smf_event_is_system_realtime(event));
300 if (event->midi_buffer_length != 1) {
301 g_critical("smf_event_decode_system_realtime: event length is not 1.");
302 return (NULL);
305 buf = malloc(BUFFER_SIZE);
306 if (buf == NULL) {
307 g_critical("smf_event_decode_system_realtime: malloc failed.");
308 return (NULL);
311 switch (event->midi_buffer[0]) {
312 case 0xF8:
313 off += snprintf(buf + off, BUFFER_SIZE - off, "MIDI Clock (realtime)");
314 break;
316 case 0xF9:
317 off += snprintf(buf + off, BUFFER_SIZE - off, "Tick (realtime)");
318 break;
320 case 0xFA:
321 off += snprintf(buf + off, BUFFER_SIZE - off, "MIDI Start (realtime)");
322 break;
324 case 0xFB:
325 off += snprintf(buf + off, BUFFER_SIZE - off, "MIDI Continue (realtime)");
326 break;
328 case 0xFC:
329 off += snprintf(buf + off, BUFFER_SIZE - off, "MIDI Stop (realtime)");
330 break;
332 case 0xFE:
333 off += snprintf(buf + off, BUFFER_SIZE - off, "Active Sense (realtime)");
334 break;
336 default:
337 free(buf);
338 return (NULL);
341 return (buf);
344 static char *
345 smf_event_decode_sysex(const smf_event_t *event)
347 int off = 0;
348 char *buf, manufacturer, subid, subid2;
350 assert(smf_event_is_sysex(event));
352 if (event->midi_buffer_length < 5) {
353 g_critical("smf_event_decode_sysex: truncated MIDI message.");
354 return (NULL);
357 buf = malloc(BUFFER_SIZE);
358 if (buf == NULL) {
359 g_critical("smf_event_decode_sysex: malloc failed.");
360 return (NULL);
363 manufacturer = event->midi_buffer[1];
365 if (manufacturer == 0x7F) {
366 off += snprintf(buf + off, BUFFER_SIZE - off, "SysEx, realtime, channel %d", event->midi_buffer[2]);
367 } else if (manufacturer == 0x7E) {
368 off += snprintf(buf + off, BUFFER_SIZE - off, "SysEx, non-realtime, channel %d", event->midi_buffer[2]);
369 } else {
370 off += snprintf(buf + off, BUFFER_SIZE - off, "SysEx, manufacturer 0x%x", manufacturer);
372 return (buf);
375 subid = event->midi_buffer[3];
376 subid2 = event->midi_buffer[4];
378 if (subid == 0x01)
379 off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump Header");
381 else if (subid == 0x02)
382 off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump Data Packet");
384 else if (subid == 0x03)
385 off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump Request");
387 else if (subid == 0x04 && subid2 == 0x01)
388 off += snprintf(buf + off, BUFFER_SIZE - off, ", Master Volume");
390 else if (subid == 0x05 && subid2 == 0x01)
391 off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump Loop Point Retransmit");
393 else if (subid == 0x05 && subid2 == 0x02)
394 off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump Loop Point Request");
396 else if (subid == 0x06 && subid2 == 0x01)
397 off += snprintf(buf + off, BUFFER_SIZE - off, ", Identity Request");
399 else if (subid == 0x06 && subid2 == 0x02)
400 off += snprintf(buf + off, BUFFER_SIZE - off, ", Identity Reply");
402 else if (subid == 0x08 && subid2 == 0x00)
403 off += snprintf(buf + off, BUFFER_SIZE - off, ", Bulk Tuning Dump Request");
405 else if (subid == 0x08 && subid2 == 0x01)
406 off += snprintf(buf + off, BUFFER_SIZE - off, ", Bulk Tuning Dump");
408 else if (subid == 0x08 && subid2 == 0x02)
409 off += snprintf(buf + off, BUFFER_SIZE - off, ", Single Note Tuning Change");
411 else if (subid == 0x08 && subid2 == 0x03)
412 off += snprintf(buf + off, BUFFER_SIZE - off, ", Bulk Tuning Dump Request (Bank)");
414 else if (subid == 0x08 && subid2 == 0x04)
415 off += snprintf(buf + off, BUFFER_SIZE - off, ", Key Based Tuning Dump");
417 else if (subid == 0x08 && subid2 == 0x05)
418 off += snprintf(buf + off, BUFFER_SIZE - off, ", Scale/Octave Tuning Dump, 1 byte format");
420 else if (subid == 0x08 && subid2 == 0x06)
421 off += snprintf(buf + off, BUFFER_SIZE - off, ", Scale/Octave Tuning Dump, 2 byte format");
423 else if (subid == 0x08 && subid2 == 0x07)
424 off += snprintf(buf + off, BUFFER_SIZE - off, ", Single Note Tuning Change (Bank)");
426 else if (subid == 0x09)
427 off += snprintf(buf + off, BUFFER_SIZE - off, ", General MIDI %s", subid2 == 0 ? "disable" : "enable");
429 else if (subid == 0x7C)
430 off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump Wait");
432 else if (subid == 0x7D)
433 off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump Cancel");
435 else if (subid == 0x7E)
436 off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump NAK");
438 else if (subid == 0x7F)
439 off += snprintf(buf + off, BUFFER_SIZE - off, ", Sample Dump ACK");
441 else
442 off += snprintf(buf + off, BUFFER_SIZE - off, ", Unknown");
444 return (buf);
447 static char *
448 smf_event_decode_system_common(const smf_event_t *event)
450 int off = 0;
451 char *buf;
453 assert(smf_event_is_system_common(event));
455 if (smf_event_is_sysex(event))
456 return (smf_event_decode_sysex(event));
458 buf = malloc(BUFFER_SIZE);
459 if (buf == NULL) {
460 g_critical("smf_event_decode_system_realtime: malloc failed.");
461 return (NULL);
464 switch (event->midi_buffer[0]) {
465 case 0xF1:
466 off += snprintf(buf + off, BUFFER_SIZE - off, "MTC Quarter Frame");
467 break;
469 case 0xF2:
470 off += snprintf(buf + off, BUFFER_SIZE - off, "Song Position Pointer");
471 break;
473 case 0xF3:
474 off += snprintf(buf + off, BUFFER_SIZE - off, "Song Select");
475 break;
477 case 0xF6:
478 off += snprintf(buf + off, BUFFER_SIZE - off, "Tune Request");
479 break;
481 default:
482 free(buf);
483 return (NULL);
486 return (buf);
489 static void
490 note_from_int(char *buf, int note_number)
492 int note, octave;
493 char *names[] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"};
495 octave = note_number / 12 - 1;
496 note = note_number % 12;
498 sprintf(buf, "%s%d", names[note], octave);
502 * \return Textual representation of the event given, or NULL, if event is unknown.
503 * Returned string looks like this:
505 * Note On, channel 1, note F#3, velocity 0
507 * You should free the returned string afterwards, using free(3).
509 char *
510 smf_event_decode(const smf_event_t *event)
512 int off = 0, channel;
513 char *buf, note[5];
515 if (smf_event_is_metadata(event))
516 return (smf_event_decode_metadata(event));
518 if (smf_event_is_system_realtime(event))
519 return (smf_event_decode_system_realtime(event));
521 if (smf_event_is_system_common(event))
522 return (smf_event_decode_system_common(event));
524 if (!smf_event_length_is_valid(event)) {
525 g_critical("smf_event_decode: incorrect MIDI message length.");
526 return (NULL);
529 buf = malloc(BUFFER_SIZE);
530 if (buf == NULL) {
531 g_critical("smf_event_decode: malloc failed.");
532 return (NULL);
535 /* + 1, because user-visible channels used to be in range <1-16>. */
536 channel = (event->midi_buffer[0] & 0x0F) + 1;
538 switch (event->midi_buffer[0] & 0xF0) {
539 case 0x80:
540 note_from_int(note, event->midi_buffer[1]);
541 off += snprintf(buf + off, BUFFER_SIZE - off, "Note Off, channel %d, note %s, velocity %d",
542 channel, note, event->midi_buffer[2]);
543 break;
545 case 0x90:
546 note_from_int(note, event->midi_buffer[1]);
547 off += snprintf(buf + off, BUFFER_SIZE - off, "Note On, channel %d, note %s, velocity %d",
548 channel, note, event->midi_buffer[2]);
549 break;
551 case 0xA0:
552 note_from_int(note, event->midi_buffer[1]);
553 off += snprintf(buf + off, BUFFER_SIZE - off, "Aftertouch, channel %d, note %s, pressure %d",
554 channel, note, event->midi_buffer[2]);
555 break;
557 case 0xB0:
558 off += snprintf(buf + off, BUFFER_SIZE - off, "Controller, channel %d, controller %d, value %d",
559 channel, event->midi_buffer[1], event->midi_buffer[2]);
560 break;
562 case 0xC0:
563 off += snprintf(buf + off, BUFFER_SIZE - off, "Program Change, channel %d, controller %d",
564 channel, event->midi_buffer[1]);
565 break;
567 case 0xD0:
568 off += snprintf(buf + off, BUFFER_SIZE - off, "Channel Pressure, channel %d, pressure %d",
569 channel, event->midi_buffer[1]);
570 break;
572 case 0xE0:
573 off += snprintf(buf + off, BUFFER_SIZE - off, "Pitch Wheel, channel %d, value %d",
574 channel, ((int)event->midi_buffer[2] << 7) | (int)event->midi_buffer[2]);
575 break;
577 default:
578 free(buf);
579 return (NULL);
582 return (buf);
586 * \return Textual representation of the data extracted from MThd header, or NULL, if something goes wrong.
587 * Returned string looks like this:
589 * format: 1 (several simultaneous tracks); number of tracks: 4; division: 192 PPQN.
591 * You should free the returned string afterwards, using free(3).
593 char *
594 smf_decode(const smf_t *smf)
596 int off = 0;
597 char *buf;
599 buf = malloc(BUFFER_SIZE);
600 if (buf == NULL) {
601 g_critical("smf_event_decode: malloc failed.");
602 return (NULL);
605 off += snprintf(buf + off, BUFFER_SIZE - off, "format: %d ", smf->format);
607 switch (smf->format) {
608 case 0:
609 off += snprintf(buf + off, BUFFER_SIZE - off, "(single track)");
610 break;
612 case 1:
613 off += snprintf(buf + off, BUFFER_SIZE - off, "(several simultaneous tracks)");
614 break;
616 case 2:
617 off += snprintf(buf + off, BUFFER_SIZE - off, "(several independent tracks)");
618 break;
620 default:
621 off += snprintf(buf + off, BUFFER_SIZE - off, "(INVALID FORMAT)");
622 break;
625 off += snprintf(buf + off, BUFFER_SIZE - off, "; number of tracks: %d", smf->number_of_tracks);
627 if (smf->ppqn != 0)
628 off += snprintf(buf + off, BUFFER_SIZE - off, "; division: %d PPQN", smf->ppqn);
629 else
630 off += snprintf(buf + off, BUFFER_SIZE - off, "; division: %d FPS, %d resolution", smf->frames_per_second, smf->resolution);
632 return (buf);