| .TH MIDIFILE 3 | |
| .SH NAME | |
| mfread,mfwrite \- read and write a standard MIDI file | |
| .SH SYNOPSIS | |
| \fC#include "mfread.h" | |
| mfread () | |
| .nf | |
| int (*Mf_getc) (); | |
| int (*Mf_putc) (); | |
| 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; | |
| .fi | |
| .sp 1 | |
| mfwrite(int format, int ntracks, int division, FILE *fp) | |
| .sp 1 | |
| .nf | |
| int (*Mf_writetrack)(int track); | |
| int (*Mf_writetempotrack)(); | |
| void mf_write_midi_event(delta, type, chan, data, size) | |
| unsigned long delta; | |
| unsigned int type,chan,size; | |
| char *data; | |
| void mf_write_meta_event(delta, type, data, size) | |
| unsigned long delta; | |
| unsigned int type,chan,size; | |
| char *data; | |
| void mf_write_tempo(tempo) | |
| unsigned long tempo; | |
| unsigned long mf_sec2ticks(float seconds, int division, int tempo) | |
| float seconds; | |
| int division; | |
| unsigned int tempo; | |
| float mf_ticks2sec(ticks, division, tempo) | |
| unsigned long ticks; | |
| int division; | |
| unsigned int tempo; | |
| .fi | |
| .SH DESCRIPTION | |
| The \fCmfread\fR function reads and interprets a standard MIDI file. | |
| To use it you need to understand the general form of a | |
| MIDI file and the type of information it contains, but you don't | |
| need to know much, if anything, about the detailed format of the file | |
| and the mechanics of reading it reliably and portably. | |
| The \fCmfwrite\fR function writes a standard MIDI file making | |
| use of user-defined functions that access the program's | |
| data structure. To use it you need to define your own Mf_writetrack | |
| routine and then make use of the write_* family of routines to | |
| write out the MIDI data. The \fCmfwrite\fR routine takes | |
| care of the file format and writing the file and track chunk headers. | |
| .SH READING STANDARD MIDI FILES | |
| A single call to \fCmfread\fR will read an entire MIDI file. | |
| The interface to \fCmfread\fR is a set of external variables | |
| named \fCMf_*\fR, most of which are function pointers to be called | |
| from within \fCmfread\fR during the process of parsing the MIDI file. | |
| Before calling \fCmfread\fR, the only | |
| requirement is that you assign a value | |
| to \fCMf_getc\fR - 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 \fCmfread\fR | |
| that could serve as a 'syntax checker' for MIDI files: | |
| .in +1i | |
| .ft C | |
| .nf | |
| #include <stdio.h> | |
| #include "midifile.h" | |
| mygetc() | |
| { | |
| /* use standard input */ | |
| return(getchar()); | |
| } | |
| main() | |
| { | |
| Mf_getc = mygetc; | |
| mfread(); | |
| exit(0); | |
| } | |
| .fi | |
| .ft R | |
| .in -1i | |
| 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 \fCMf_error\fR; the function will be | |
| called with the error message as an argument. | |
| The other \fCMf_* 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. | |
| \fCMf_header\fR 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. | |
| \fCMf_trackstart\fR and | |
| \fCMf_trackend\fR 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 \fCMf_noteon\fR to be called | |
| with the channel, pitch, and volume as arguments. The time at which | |
| the message occurred is stored in \fCMf_currtime\fR - one of the few | |
| external variables that isn't a function pointer. The other channel messages | |
| are handled in a similar and obvious fashion - | |
| \fCMf_noteoff\fR, | |
| \fCMf_pressure\fR, | |
| \fCMf_parameter\fR, | |
| \fCMf_pitchbend\fR, | |
| \fCMf_program\fR, | |
| and \fCMf_chanpressure\fR. See the declarations above for the arguments | |
| that are passed to each. | |
| System exclusive messages are handled by calling \fCMf_sysex\fR, 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 \fCMf_sysex\fR is only called once. It you want to disable this | |
| you can set \fCMf_nomerge\fR to 1, causing \fCMf_sysex\fR to be called | |
| once for each part of the message. | |
| \fCMf_seqnum\fR is called by the \fImeta\fR message that provides | |
| a sequence number, | |
| which if present must appear at the beginning of a track. | |
| The tempo \fImeta\fR message causes \fCMf_tempo\fR to be called; its | |
| argument is the number of microseconds per MIDI quarter-note (24 MIDI clocks). | |
| The end-of-track \fImeta\fR message causes \fCMf_eot\fR to be called. | |
| The key signature \fImeta\fR message causes \fCMf_keysig\fR to be called; | |
| the first argument conveys the number of sharps or flats, the second | |
| argument is 1 if the key is minor. | |
| The \fCMf_timesig\fR and \fCMf_smpte\fR functions are called when the | |
| corresponding \fImeta\fR messages are seen. See the MIDI file standard | |
| for a description of their arguments. | |
| The \fItext\fR messages in the MIDI file standard are of the following | |
| types: | |
| .in +1i | |
| .nf | |
| 0x01 Text Event | |
| 0x02 Copyright | |
| 0x03 Sequence/Track Name | |
| 0x04 Instrument | |
| 0x05 Lyric | |
| 0x06 Marker | |
| 0x07 Cue Point | |
| 0x08-0x0F Reserved but Undefined | |
| .fi | |
| .in -1i | |
| \fCMf_text\fR is called for each of these; the arguments are | |
| the type number, the message length, and a pointer to the message buffer. | |
| Miscellaneous \fImeta\fR messages are handled by \fCMf_metamisc\fR, | |
| sequencer-specific messages are handled by \fCMf_seqspecific\fR, and | |
| arbitrary "escape" messages (started with 0xF7) are handled by | |
| \fCMf_arbitrary\fR. | |
| .SH READING EXAMPLE | |
| The following is a \fCstrings\fR-like program for MIDI files: | |
| .in +1i | |
| .ft C | |
| .nf | |
| #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('\n'); | |
| } | |
| main(argc,argv) | |
| char **argv; | |
| { | |
| if ( argc > 1 ) | |
| F = fopen(argv[1],"r"); | |
| else | |
| F = stdin; | |
| Mf_getc = mygetc; | |
| Mf_text = mytext; | |
| mfread(); | |
| exit(0); | |
| } | |
| .fi | |
| .ft R | |
| .in -1i | |
| .sp | |
| .SH WRITING STANDARD MIDI FILES | |
| A single call to \fCmfwrite\fR will write an entire MIDI file. Before | |
| calling \fCmfwrite\fR, you must assign values to function pointers | |
| \fCMf_writetrack\fR and \fCMf_putc\fR. The first is a routine to | |
| access your MIDI data structure, which can make use of other library | |
| routines to write the actual MIDI data. The routine | |
| \fCMf_writetrack\fR will be passed a single parameter which is the | |
| number of the track to be written. The pointer \fCMf_putc\fR should be | |
| set to point to a routine that accepts a character as input, writes that | |
| character to a file, and returns the value that was written. In the | |
| case of a format 1 file, a routine has to be written to write a tempo | |
| map, and assigned to the function pointer \fCMf_writetempotrack\fR. | |
| This is because format 1 files assume the first track written is a | |
| tempo track. | |
| \fCmf_write_midi_event\fR and \fCmf_write_meta_event\fR are routines | |
| that should be called from your \fCMf_writetrack\fR routine to write | |
| out MIDI events. The delta time param is the number of ticks since the | |
| last event. The int "type" is the type of MIDI message. The int "chan" | |
| is the MIDI channel, which can be between 1 and 16. The char pointer | |
| "data" points to an array containing the data bytes, if any exist. The | |
| int "size" is the number of data bytes. | |
| \fCmf_sec2ticks\fR and \fCmf_ticks2sec\fR are utility routines | |
| to help you convert between the MIDI file parameter of ticks | |
| and the more standard seconds. The int "division" is the same | |
| division parameter from the file header, and tempo is expressed | |
| in microseconds per MIDI quarter-note, or "24ths of a microsecond | |
| per MIDI clock". The division has two meanings, depending on | |
| whether bit 15 is set or not. If bit 15 of division is zero, | |
| bits 14 through 0 represent the number of delta-time "ticks" | |
| which make up a quarter note. If bit 15 of division is a one, | |
| delta-times in a file correspond to subdivisions of a second | |
| similar to SMPTE and MIDI time code. In this format bits | |
| 14 through 8 contain one of four values \-24, \-25, \-29, or \-30, | |
| corresponding to the four standard SMPTE and MIDI time code | |
| frame per second formats, where \-29 represents 30 drop frame. | |
| The second byte consisting of bits 7 through 0 corresponds | |
| the the resolution within a frame. Refer the Standard MIDI Files | |
| 1.0 spec for more details. | |
| .SH WRITING EXAMPLE | |
| The following is a simple program to demonstrate writing MIDI files. | |
| The track would consist of a series of quarter notes from lowest to | |
| highest in pitch at constant velocity, each separated by a quarter-note | |
| rest. | |
| .sp | |
| .in +1i | |
| .ft C | |
| .nf | |
| #include <stdio.h> | |
| #include <ctype.h> | |
| #include "midifile.h" | |
| FILE *fp; | |
| myputc(c) { return(putc(c,fp));} | |
| int mywritetrack(track) | |
| int track; | |
| { | |
| int i; | |
| char data[2]; | |
| /* 120 beats/per/second */ | |
| mf_write_tempo((long)500000); | |
| for(i = 1 ; i < 128; i++){ | |
| data[0] = i; /* note number */ | |
| data[1] = 64; /* velocity */ | |
| if(!mf_write_midi_event(480,note_on,1,data,2)) | |
| return(\-1); | |
| if(!mf_write_midi_event(480,note_off,1,data,2)) | |
| return(\-1); | |
| } | |
| return(1); | |
| } /* end of write_track() */ | |
| main(argc,argv) | |
| char **argv; | |
| { | |
| if((fp = fopen(argv[1],"w")) == 0L) | |
| exit(1); | |
| Mf_putc = myputc; | |
| Mf_writetrack = mywritetrack; | |
| /* write a single track */ | |
| mfwrite(0,1,480,fp); | |
| } | |
| .sp | |
| .fi | |
| .ft R | |
| .in -1i | |
| .sp | |
| .SH AUTHOR | |
| Tim Thompson (att!twitch!glimmer!tjt) | |
| .SH CONTRIBUTORS | |
| Michael Czeiszperger (mike@pan.com) |