midifile ()
int (*Mf_getc) (); int (*Mf_error) (char *msg); int (*Mf_header) (int format, int ntrks, int division); int (*Mf_trackstart) (); int (*Mf_trackend) (); int (*Mf_noteon) (int chan, int pitch, int vol); int (*Mf_noteoff) (int chan, int pitch, int vol); int (*Mf_pressure) (int chan, int pitch, int pressure); int (*Mf_parameter) (int chan, int control, int value); int (*Mf_pitchbend) (int chan, int msb, int lsb); int (*Mf_program) (int chan, int program); int (*Mf_chanpressure) (int chan, int pressure); int (*Mf_sysex) (int leng, char *msg); int (*Mf_metamisc) (int type, int leng, int msg); int (*Mf_seqspecific) (int type, int leng, int msg); int (*Mf_seqnum) (int num); int (*Mf_text) (int type, int leng, int msg); int (*Mf_eot) (); int (*Mf_timesig) (int numer, int denom, int clocks, int qnotes); int (*Mf_smpte) (int hour, int min, int sec, int frame, int fract); int (*Mf_tempo) (int microsecs); int (*Mf_keysig) (int sharpflat, int minor); int (*Mf_arbitrary) (int leng, int msg); int Mf_nomerge; long Mf_currtime;
A single call to midifile will read an entire MIDI file. The interface to midifile is a set of external variables named Mf_*, most of which are function pointers to be called from within midifile during the process of parsing the MIDI file. Before calling midifile, the only requirement is that you assign a value to Mf_getc - a pointer to a function that will return characters from the MIDI file, using -1 to indicate EOF. All the rest of the function pointers are initialized to NULL, and the default action for each is to do nothing. The following is a complete program using midifile that could serve as a 'syntax checker' for MIDI files:
#include <stdio.h> #include "midifile.h" mygetc() { /* use standard input */ return(getchar()); } main() { Mf_getc = mygetc; midifile(); exit(0); }
This takes advantage of the default action when an error is detected, which is to exit silently with a return code of 1. An error function of your own can be used by giving a value to Mf_error; the function will be called with the error message as an argument. The other Mf_* variables can similarly be used to call arbitrary functions while parsing the MIDI file. The descriptions below of the information passed to these functions is sparse; refer to the MIDI file standard for the complete descriptions.
Mf_header is the first function to be called, and its arguments contain information from the MIDI file's header; the format (0,1, or 2), the number of tracks, and the division of a quarter-note that defines the times units. Mf_trackstart and Mf_trackend are called at the beginning and end of each track.
Once inside a track, each separate message causes a function to be called. For example, each note-on message causes Mf_noteon to be called with the channel, pitch, and volume as arguments. The time at which the message occurred is stored in Mf_currtime - one of the few external variables that isn't a function pointer. The other channel messages are handled in a similar and obvious fashion - Mf_noteoff, Mf_pressure, Mf_parameter, Mf_pitchbend, Mf_program, and Mf_chanpressure. See the declarations above for the arguments that are passed to each.
System exclusive messages are handled by calling Mf_sysex, passing as arguments the message length and a pointer to a static buffer containing the entire message. The buffer is expanded when necessary; memory availability is the only limit to its size. Normally, 'continued' system exclusives are automatically merged, and Mf_sysex is only called once. It you want to disable this you can set Mf_nomerge to 1, causing Mf_sysex to be called once for each part of the message.
Mf_seqnum is called by the meta message that provides a sequence number, which if present must appear at the beginning of a track. The tempo meta message causes Mf_tempo to be called; its argument is the number of microseconds per MIDI quarter-note (24 MIDI clocks). The end-of-track meta message causes Mf_eot to be called. The key signature meta message causes Mf_keysig to be called; the first argument conveys the number of sharps or flats, the second argument is 1 if the key is minor.
The Mf_timesig and Mf_smpte functions are called when the corresponding meta messages are seen. See the MIDI file standard for a description of their arguments.
The text messages in the MIDI file standard are of the following types:
0x01 Text Event 0x02 Copyright 0x03 Sequence/Track Name 0x04 Instrument 0x05 Lyric 0x06 Marker 0x07 Cue Point 0x08-0x0F Reserverd but Undefined
Mf_text is called for each of these; the arguments are the type number, the message length, and a pointer to the message buffer.
Misceallaneous meta messages are handled by Mf_metamisc, sequencer-specific messages are handled by Mf_seqspecific, and arbitrary "escape" messages (started with 0xF7) are handled by Mf_arbitrary.
#include <stdio.h> #include <ctype.h> #include "midifile.h" FILE *F; mygetc() { return(getc(F)); } mytext(type,leng,msg) char *msg; { char *p; char *ep = msg + leng; for ( p=msg; p<ep ; p++ ) putchar( isprint(*p) ? *p : '?' ); putchar('); } main(argc,argv) char **argv; { if ( argc > 1 ) F = fopen(argv[1],"r"); else F = stdin; Mf_getc = mygetc; Mf_text = mytext; midifile(); exit(0); }