/** * Puts MIDI data extracted from from "buf" into "event" and number of consumed bytes into "len". * In case valid status is not found, it uses "last_status" (so called "running status"). * Returns 0 iff everything went OK, value < 0 in case of error. */ static int extract_midi_event(const unsigned char *buf, const size_t buffer_length, smf_event_t *event, uint32_t *len, int last_status) { int status; int32_t message_length; const unsigned char *c = buf; assert(buffer_length > 0); /* Is the first byte the status byte? */ if (is_status_byte(*c)) { status = *c; c++; } else { /* No, we use running status then. */ status = last_status; } if (!is_status_byte(status)) { g_critical("SMF error: bad status byte (MSB is zero)."); return (-1); } if (is_sysex_byte(status)) return (extract_sysex_event(buf, buffer_length, event, len, last_status)); if (is_escape_byte(status)) return (extract_escaped_event(buf, buffer_length, event, len, last_status)); /* At this point, "c" points to first byte following the status byte. */ message_length = expected_message_length(status, c, buffer_length - (c - buf)); if (message_length < 0) return (-3); if ((size_t)message_length > buffer_length - (c - buf) + 1) { g_critical("End of buffer in extract_midi_event()."); return (-5); } event->midi_buffer_length = message_length; event->midi_buffer = (uint8_t*)malloc(event->midi_buffer_length); if (event->midi_buffer == NULL) { g_critical("Cannot allocate memory in extract_midi_event(): %s", strerror(errno)); return (-4); } event->midi_buffer[0] = status; memcpy(event->midi_buffer + 1, c, message_length - 1); *len = c + message_length - 1 - buf; return (0); }
/* XXX: this routine requires some more work to detect more errors. */ int smf_event_is_valid(const smf_event_t *event) { assert(event); assert(event->midi_buffer); assert(event->midi_buffer_length >= 1); if (!is_status_byte(event->midi_buffer[0])) { g_critical("First byte of MIDI message is not a valid status byte."); return (0); } if (!smf_event_length_is_valid(event)) return (0); return (1); }
/** * Allocates an smf_event_t structure and fills it with at most three bytes of data. * For example, if you need to create Note On event, do something like this: * * smf_event_new_from_bytes(0x90, 0x3C, 0x7f); * * To create event for MIDI message that is shorter than three bytes, do something * like this: * * smf_event_new_from_bytes(0xC0, 0x42, -1); * * \param first_byte First byte of MIDI message. Must be valid status byte. * \param second_byte Second byte of MIDI message or -1, if message is one byte long. * \param third_byte Third byte of MIDI message or -1, if message is two bytes long. * \return Event containing MIDI data or NULL. */ smf_event_t * smf_event_new_from_bytes(int first_byte, int second_byte, int third_byte) { int len; smf_event_t *event; event = smf_event_new(); if (event == NULL) return (NULL); if (first_byte < 0) { g_critical("First byte of MIDI message cannot be < 0"); smf_event_delete(event); return (NULL); } if (first_byte > 255) { g_critical("smf_event_new_from_bytes: first byte is %d, which is larger than 255.", first_byte); return (NULL); } if (!is_status_byte(first_byte)) { g_critical("smf_event_new_from_bytes: first byte is not a valid status byte."); return (NULL); } if (second_byte < 0) len = 1; else if (third_byte < 0) len = 2; else len = 3; if (len > 1) { if (second_byte > 255) { g_critical("smf_event_new_from_bytes: second byte is %d, which is larger than 255.", second_byte); return (NULL); } if (is_status_byte(second_byte)) { g_critical("smf_event_new_from_bytes: second byte cannot be a status byte."); return (NULL); } } if (len > 2) { if (third_byte > 255) { g_critical("smf_event_new_from_bytes: third byte is %d, which is larger than 255.", third_byte); return (NULL); } if (is_status_byte(third_byte)) { g_critical("smf_event_new_from_bytes: third byte cannot be a status byte."); return (NULL); } } event->midi_buffer_length = len; event->midi_buffer = malloc(event->midi_buffer_length); if (event->midi_buffer == NULL) { g_critical("Cannot allocate MIDI buffer structure: %s", strerror(errno)); smf_event_delete(event); return (NULL); } event->midi_buffer[0] = first_byte; if (len > 1) event->midi_buffer[1] = second_byte; if (len > 2) event->midi_buffer[2] = third_byte; return (event); }
/** * Returns expected length of the midi message (including the status byte), in bytes, for the given status byte. * The "second_byte" points to the expected second byte of the MIDI message. "buffer_length" is the buffer * length limit, counting from "second_byte". Returns value < 0 iff there was an error. */ static int32_t expected_message_length(unsigned char status, const unsigned char *second_byte, const size_t buffer_length) { /* Make sure this really is a valid status byte. */ assert(is_status_byte(status)); /* We cannot use this routine for sysexes. */ assert(!is_sysex_byte(status)); /* We cannot use this routine for escaped events. */ assert(!is_escape_byte(status)); /* Is this a metamessage? */ if (status == 0xFF) { if (buffer_length < 2) { g_critical("SMF error: end of buffer in expected_message_length()."); return (-1); } /* * Format of this kind of messages is like this: 0xFF 0xwhatever 0xlength and then "length" bytes. * Second byte points to this: ^^^^^^^^^^ */ return (*(second_byte + 1) + 3); } if ((status & 0xF0) == 0xF0) { switch (status) { case 0xF2: /* Song Position Pointer. */ return (3); case 0xF1: /* MTC Quarter Frame. */ case 0xF3: /* Song Select. */ return (2); case 0xF6: /* Tune Request. */ case 0xF8: /* MIDI Clock. */ case 0xF9: /* Tick. */ case 0xFA: /* MIDI Start. */ case 0xFB: /* MIDI Continue. */ case 0xFC: /* MIDI Stop. */ case 0xFE: /* Active Sense. */ return (1); default: g_critical("SMF error: unknown 0xFx-type status byte '0x%x'.", status); return (-2); } } /* Filter out the channel. */ status &= 0xF0; switch (status) { case 0x80: /* Note Off. */ case 0x90: /* Note On. */ case 0xA0: /* AfterTouch. */ case 0xB0: /* Control Change. */ case 0xE0: /* Pitch Wheel. */ return (3); case 0xC0: /* Program Change. */ case 0xD0: /* Channel Pressure. */ return (2); default: g_critical("SMF error: unknown status byte '0x%x'.", status); return (-3); } }