46 #include <arpa/inet.h>
66 g_critical(
"SMF warning: no more chunks left.");
74 if (!isalpha(chunk->
id[0]) || !isalpha(chunk->
id[1]) || !isalpha(chunk->
id[2]) || !isalpha(chunk->
id[3])) {
75 g_critical(
"SMF error: chunk signature contains at least one non-alphanumeric byte.");
86 g_critical(
"SMF warning: malformed chunk; truncated file?");
99 if (!memcmp(chunk->
id, signature, 4))
109 parse_mthd_header(
smf_t *smf)
122 g_critical(
"SMF error: file is too short, it cannot be a MIDI file.");
129 if (!chunk_signature_matches(tmp_mthd,
"MThd")) {
130 g_critical(
"SMF error: MThd signature not found, is that a MIDI file?");
136 mthd = next_chunk(smf);
140 assert(mthd == tmp_mthd);
142 len = ntohl(mthd->
length);
144 g_critical(
"SMF error: MThd chunk length %d, must be 6.", len);
156 parse_mthd_chunk(
smf_t *smf)
158 signed char first_byte_of_division, second_byte_of_division;
164 if (parse_mthd_header(smf))
171 g_critical(
"SMF error: bad MThd format field value: %d, valid values are 0-2, inclusive.", smf->
format);
176 g_critical(
"SMF file uses format #2, no support for that yet.");
187 first_byte_of_division = *((
signed char *)&(mthd->
division));
188 second_byte_of_division = *((
signed char *)&(mthd->
division) + 1);
190 if (first_byte_of_division >= 0) {
200 if (smf->
ppqn == 0) {
201 g_critical(
"SMF file uses FPS timing instead of PPQN, no support for that yet.");
215 extract_vlq(
const unsigned char *buf,
const int buffer_length,
int *value,
int *len)
218 const unsigned char *c = buf;
220 assert(buffer_length > 0);
223 if (c >= buf + buffer_length) {
224 g_critical(
"End of buffer in extract_vlq().");
228 val = (val << 7) + (*c & 0x7F);
240 g_critical(
"SMF error: Variable Length Quantities longer than four bytes are not supported yet.");
253 return (status & 0x80);
257 is_sysex_byte(
const unsigned char status)
266 is_escape_byte(
const unsigned char status)
282 expected_sysex_length(
const unsigned char status,
const unsigned char *second_byte,
const int buffer_length,
int *consumed_bytes)
284 int sysex_length, len;
286 assert(status == 0xF0);
288 if (buffer_length < 3) {
289 g_critical(
"SMF error: end of buffer in expected_sysex_length().");
293 if (extract_vlq(second_byte, buffer_length, &sysex_length, &len))
296 if (consumed_bytes != NULL)
297 *consumed_bytes = len;
300 return (sysex_length + 1);
304 expected_escaped_length(
const unsigned char status,
const unsigned char *second_byte,
const int buffer_length,
int *consumed_bytes)
307 return (expected_sysex_length(status, second_byte, buffer_length, consumed_bytes) - 1);
316 expected_message_length(
unsigned char status,
const unsigned char *second_byte,
const int buffer_length)
322 assert(!is_sysex_byte(status));
325 assert(!is_escape_byte(status));
328 assert(buffer_length >= 0);
331 if (status == 0xFF) {
332 if (buffer_length < 2) {
333 g_critical(
"SMF error: end of buffer in expected_message_length().");
341 return (*(second_byte + 1) + 3);
344 if ((status & 0xF0) == 0xF0) {
363 g_critical(
"SMF error: unknown 0xFx-type status byte '0x%x'.", status);
384 g_critical(
"SMF error: unknown status byte '0x%x'.", status);
390 extract_sysex_event(
const unsigned char *buf,
const int buffer_length,
smf_event_t *event,
int *len,
int last_status)
392 int status, message_length, vlq_length;
393 const unsigned char *c = buf;
397 assert(is_sysex_byte(status));
401 message_length = expected_sysex_length(status, c, buffer_length - 1, &vlq_length);
403 if (message_length < 0)
408 if (vlq_length + message_length >= buffer_length) {
409 g_critical(
"End of buffer in extract_sysex_event().");
413 event->midi_buffer_length = message_length;
416 g_critical(
"Cannot allocate memory in extract_sysex_event(): %s", strerror(errno));
420 event->midi_buffer[0] = status;
421 memcpy(event->
midi_buffer + 1, c, message_length - 1);
423 *len = vlq_length + message_length;
429 extract_escaped_event(
const unsigned char *buf,
const int buffer_length,
smf_event_t *event,
int *len,
int last_status)
431 int status, message_length, vlq_length;
432 const unsigned char *c = buf;
436 assert(is_escape_byte(status));
440 message_length = expected_escaped_length(status, c, buffer_length - 1, &vlq_length);
442 if (message_length < 0)
447 if (vlq_length + message_length >= buffer_length) {
448 g_critical(
"End of buffer in extract_escaped_event().");
452 event->midi_buffer_length = message_length;
455 g_critical(
"Cannot allocate memory in extract_escaped_event(): %s", strerror(errno));
462 g_critical(
"Escaped event is invalid.");
467 g_warning(
"Escaped event is not System Realtime nor System Common.");
470 *len = vlq_length + message_length;
482 extract_midi_event(
const unsigned char *buf,
const int buffer_length,
smf_event_t *event,
int *len,
int last_status)
484 int status, message_length;
485 const unsigned char *c = buf;
487 assert(buffer_length > 0);
496 status = last_status;
500 g_critical(
"SMF error: bad status byte (MSB is zero).");
504 if (is_sysex_byte(status))
505 return (extract_sysex_event(buf, buffer_length, event, len, last_status));
507 if (is_escape_byte(status))
508 return (extract_escaped_event(buf, buffer_length, event, len, last_status));
511 message_length = expected_message_length(status, c, buffer_length - (c - buf));
513 if (message_length < 0)
516 if (message_length - 1 > buffer_length - (c - buf)) {
517 g_critical(
"End of buffer in extract_midi_event().");
521 event->midi_buffer_length = message_length;
524 g_critical(
"Cannot allocate memory in extract_midi_event(): %s", strerror(errno));
528 event->midi_buffer[0] = status;
529 memcpy(event->
midi_buffer + 1, c, message_length - 1);
531 *len = c + message_length - 1 - buf;
545 int time = 0, len, buffer_length;
546 unsigned char *c, *start;
559 assert(buffer_length > 0);
562 if (extract_vlq(c, buffer_length, &time, &len))
566 buffer_length -= len;
568 if (buffer_length <= 0)
572 if (extract_midi_event(c, buffer_length, event, &len, track->
last_status))
576 buffer_length -= len;
596 make_string(
const unsigned char *buf,
const int buffer_length,
int len)
600 assert(buffer_length > 0);
603 if (len > buffer_length) {
604 g_critical(
"End of buffer in make_string().");
609 str = malloc(len + 1);
611 g_critical(
"Cannot allocate memory in make_string().");
615 memcpy(str, buf, len);
649 int string_length = -1, length_length = -1;
655 g_critical(
"smf_event_extract_text: truncated MIDI message.");
661 if (string_length <= 0) {
662 g_critical(
"smf_event_extract_text: truncated MIDI message.");
680 assert(track->
smf != NULL);
682 mtrk = next_chunk(track->
smf);
687 if (!chunk_signature_matches(mtrk,
"MTrk")) {
688 g_warning(
"SMF warning: Expected MTrk signature, got %c%c%c%c instead; ignoring this chunk.",
689 mtrk->
id[0], mtrk->
id[1], mtrk->
id[2], mtrk->
id[3]);
752 g_critical(
"First byte of MIDI message is not a valid status byte.");
771 if (parse_mtrk_header(track))
775 event = parse_next_event(track);
779 g_critical(
"Unable to parse MIDI event; truncating track.");
781 g_critical(
"smf_track_add_eot_delta_pulses failed.");
789 if (event_is_end_of_track(event))
804 load_file_into_buffer(
void **file_buffer,
int *file_buffer_length,
const char *file_name)
806 FILE *stream = fopen(file_name,
"rb");
808 if (stream == NULL) {
809 g_critical(
"Cannot open input file: %s", strerror(errno));
814 if (fseek(stream, 0, SEEK_END)) {
815 g_critical(
"fseek(3) failed: %s", strerror(errno));
820 *file_buffer_length = ftell(stream);
821 if (*file_buffer_length == -1) {
822 g_critical(
"ftell(3) failed: %s", strerror(errno));
827 if (fseek(stream, 0, SEEK_SET)) {
828 g_critical(
"fseek(3) failed: %s", strerror(errno));
833 *file_buffer = malloc(*file_buffer_length);
834 if (*file_buffer == NULL) {
835 g_critical(
"malloc(3) failed: %s", strerror(errno));
840 if (fread(*file_buffer, 1, *file_buffer_length, stream) != *file_buffer_length) {
841 g_critical(
"fread(3) failed: %s", strerror(errno));
846 if (fclose(stream)) {
847 g_critical(
"fclose(3) failed: %s", strerror(errno));
870 if (parse_mthd_chunk(smf))
881 if (parse_mtrk_chunk(track)) {
882 g_warning(
"SMF warning: Cannot load track.");
892 g_warning(
"SMF warning: MThd header declared %d tracks, but only %d found; continuing anyway.",
914 int file_buffer_length;
918 if (load_file_into_buffer(&file_buffer, &file_buffer_length, file_name))
923 memset(file_buffer, 0, file_buffer_length);
void smf_rewind(smf_t *smf)
Rewinds the SMF.
SMF chunk, used only by smf_load.c and smf_save.c.
int smf_event_is_valid(const smf_event_t *event) WARN_UNUSED_RESULT
void smf_track_add_event_delta_pulses(smf_track_t *track, smf_event_t *event, int pulses)
Adds event to the track at the time "pulses" clocks from the previous event in this track...
smf_t * smf_load_from_memory(const void *buffer, const int buffer_length) WARN_UNUSED_RESULT
Creates new SMF and fills it with data loaded from the given buffer.
unsigned char * midi_buffer
Pointer to the buffer containing MIDI message.
int smf_track_add_eot_delta_pulses(smf_track_t *track, int delta)
Add End Of Track metaevent.
Represents a "song", that is, collection of one or more tracks.
smf_t * smf_load(const char *file_name) WARN_UNUSED_RESULT
Loads SMF file.
Represents a single track.
void smf_add_track(smf_t *smf, smf_track_t *track)
Appends smf_track_t to smf.
int ppqn
These fields are extracted from "division" field of MThd header.
int smf_event_is_metadata(const smf_event_t *event) WARN_UNUSED_RESULT
void smf_event_delete(smf_event_t *event)
Detaches event from its track and frees it.
int smf_event_is_system_common(const smf_event_t *event) WARN_UNUSED_RESULT
int expected_number_of_tracks
int smf_event_length_is_valid(const smf_event_t *event)
int smf_event_is_sysex(const smf_event_t *event) WARN_UNUSED_RESULT
smf_track_t * smf_track_new(void)
Allocates new smf_track_t structure.
int midi_buffer_length
Length of the MIDI message in the buffer, in bytes.
int next_event_offset
Private, used by smf.c.
smf_event_t * smf_event_new(void)
Allocates new smf_event_t structure.
int smf_event_is_system_realtime(const smf_event_t *event) WARN_UNUSED_RESULT
uint16_t number_of_tracks
Public interface declaration for libsmf, Standard MIDI File format library.
int is_status_byte(const unsigned char status)
Returns 1 if the given byte is a valid status byte, 0 otherwise.
char * smf_event_extract_text(const smf_event_t *event) WARN_UNUSED_RESULT
Extracts text from "textual metaevents", such as Text or Lyric.
Represents a single MIDI event or metaevent.
void * file_buffer
These are private fields using only by loading and saving routines.
void smf_track_delete(smf_track_t *track)
Detaches track from its smf and frees it.
int smf_event_is_textual(const smf_event_t *event) WARN_UNUSED_RESULT
smf_t * smf_new(void)
Allocates new smf_t structure.