From pa.dec.com!decwrl!uunet!sparky!kent Sun Aug 11 15:55:10 PDT 1991 Article: 2607 of comp.sources.misc Newsgroups: comp.sources.misc Path: pa.dec.com!decwrl!uunet!sparky!kent From: Oishii Ichigo Subject: v21i097: midilib - Read and write MIDI files in MMA standard format, Part02/02 Message-ID: <1991Aug8.015122.3142@sparky.IMD.Sterling.COM> X-Md4-Signature: 0f598fbf2f0016a12e269ce43b03037b Keywords: MIDI, SMF, music, standard file format Sender: kent@sparky.IMD.Sterling.COM (Kent Landfield) Organization: Bay Area Fruit Growers References: Date: Thu, 8 Aug 1991 01:51:22 GMT Approved: kent@sparky.imd.sterling.com Lines: 1295 Submitted-by: Oishii Ichigo Posting-number: Volume 21, Issue 97 Archive-name: midilib/part02 Environment: UNIX, MIDI, Sun, Mac, PC #! /bin/sh # This is a shell archive. Remove anything before this line, then feed it # into a shell via "sh file" or similar. To overwrite existing files, # type "sh file -c". # The tool that generated this appeared in the comp.sources.unix newsgroup; # send mail to comp-sources-unix@uunet.uu.net if you want that tool. # Contents: midifile/Makefile midifile/README midifile/crack.c # midifile/example1.mid.uu midifile/example2.mid.uu # midifile/example3.mid.uu midifile/example4.mid.uu # midifile/mfcheck.c midifile/mfstrings.c midifile/mftext.c # midifile/mfwrite_ex.c midifile/midifile.3 midifile/midifile.h # midifile/noteon.c # Wrapped by kent@sparky on Wed Aug 7 09:29:28 1991 PATH=/bin:/usr/bin:/usr/ucb ; export PATH echo If this archive is complete, you will see the following message: echo ' "shar: End of archive 2 (of 2)."' if test -f 'midifile/Makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'midifile/Makefile'\" else echo shar: Extracting \"'midifile/Makefile'\" \(1076 characters\) sed "s/^X//" >'midifile/Makefile' <<'END_OF_FILE' XCFLAGS = -g X Xmftext : midifile.o mftext.o crack.o X cc midifile.o mftext.o crack.o -o mftext X Xmfcheck : midifile.o mfcheck.o X cc midifile.o mfcheck.o -o mfcheck X Xmfstrings : midifile.o mfstrings.o X cc midifile.o mfstrings.o -o mfstrings X Xmf1to0 : midifile.o mf1to0.o X ${CC} ${CFLAGS} $@.c midifile.o -o $@ X Xmfwrite_ex : midifile.o mfwrite_ex.o X cc midifile.o mfwrite_ex.o -o mfwrite_ex X Xall : mftext mfcheck mfstrings mf1to0 midifile.man X Xmidifile.man : midifile.3 X nroff -man -Tlp midifile.3 | col -b > midifile.man X Xclean : X rm -f mftext mfcheck mfstrings mf1to0 mfwrite_ex *.o midifile.man X Xlint : X lint midifile.c mf1to0.c X Xmidtouu : X for i in example*.mid ; \ X do \ X echo $$i ; \ X uuencode $$i $$i > `basename $$i .mid`.uu ; \ X if [ $$? -eq 0 ] ; then rm -f $$i ; fi ; \ X done X Xuutomid : X for i in example*.uu ; \ X do \ X echo $$i ; \ X uudecode $$i ; \ X if [ $$? -eq 0 ] ; then rm -f $$i ; fi ; \ X done X Xtest : mftext X for i in example*.mid ; \ X do \ X echo $$i ; \ X mftext $$i > /dev/null ; \ X if [ $$? -ne 0 ] ; then echo "Mftext of $$i failed!" ; fi ; \ X done END_OF_FILE if test 1076 -ne `wc -c <'midifile/Makefile'`; then echo shar: \"'midifile/Makefile'\" unpacked with wrong size! fi # end of 'midifile/Makefile' fi if test -f 'midifile/README' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'midifile/README'\" else echo shar: Extracting \"'midifile/README'\" \(1559 characters\) sed "s/^X//" >'midifile/README' <<'END_OF_FILE' XThis directory contains a library of code to read and write standard XMIDI files, a standard as defined by the MMA, the MIDI Manufacturer's XAssocation. The format spec is available from the IMA: X X International MIDI Association X 5316 West 57th Street X Los Angeles, CA 90056 X X X"make mftext" will compile a program that gives a verbose textual Xlisting of a MIDI file, and is an example of how to read a file. "make Xmf1to0" will compile a program that demonstrates how to write midi Xfiles, and converts format 1 multitrack files to format 0. "make Xmidifile.man" will produce a formatted manual page. Example MIDI files Xare named "example*.uu". They're uuencoded; "make uutomid" will Xuudecode them all. The first two examples (example1 and example2) are Xthe examples given in the 0.06 version of the standard MIDI file Xspecification. X X ...Tim Thompson...att!twitch!glimmer!tjt... X XJune, 1989 - Added code to write midi files and the mf1to0 program. X Michael Czeiszperger czei@pan.com X XNotes on Porting: XThis library was written on UNIX, and tested on Sun's, PC's and Mac's. XIt is distributed in a raw vanilla state, which means you may have to do a Xlittle editing depending on the requirements of your particular development Xsystem. For instance, with THINK C on the Mac, you have to be careful Xand include all the include files needed by their implementation of XUNIX calls such as malloc(). I've left in the proper calls ifdef'd on XTHINK so you'll have a clue what to do. X Xczei@pan.com END_OF_FILE if test 1559 -ne `wc -c <'midifile/README'`; then echo shar: \"'midifile/README'\" unpacked with wrong size! fi # end of 'midifile/README' fi if test -f 'midifile/crack.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'midifile/crack.c'\" else echo shar: Extracting \"'midifile/crack.c'\" \(4123 characters\) sed "s/^X//" >'midifile/crack.c' <<'END_OF_FILE' X/* X * crack - put the command line to the vice. X * X * This routine takes four arguments, the variables argc and argv from X * the program's main procedure, a list of flags to recognize, and an X * indication whether to ignore unknown flags or not. X * X * The action of crack is to read a command line laid out in the format: X * X * % command [flags]... [files]... X * X * where the flags are of the form -foption, that is, a minus, a character X * and an optional argument, butted up to the character. No space may X * appear between any flag and its option. The first command line X * argument not preceeded by '-' is taken as the end of the flags and the X * beginning of file names. X * X * The flags argument to crack looks like this: "a|b|cd" for flags a b c X * and d. In this example, a and b take (optional!) options, as specified X * by the trailing colon, c and d do not. When crack scans a flag, it X * returns the flag character after setting the external character pointer X * arg_option to the option part. It also sets arg_index to the index of X * the argv variable scanned. Crack returns NULL when it has scanned the X * last flag. The value of arg_index then points at the first X * argument after the last flag, which should be the first filename, if X * any. If there are no arguments, or no more arguments after reading X * the flags, arg_index == argc; X * X * Flags may be concatenated, for instance, using the flags argument X * given above: -cd will treat c and d as X * flags. -cdaoption also works. However, tacking what you think is X * a flag after another flag that takes an option will cause the flag to X * be lost. I.e., -ac will treat c as an option, not a flag. X * X * When the ignore flag is zero, flags scanned that are not in the flags X * variable generate an error message and crack returns EOF. If ignore is X * nonzero, unknown flags are suppressed, that is, no flag is returned. X * The purpose of ignoring flags is so that more than one part of a X * program can scan the command line without having to know about the X * flags of all the other parts. For instance, where a program calculates X * a sampling rate by one flag and a value in seconds in another, it must X * search for the sampling rate first, then the time value. Two calls to X * crack() would be required, one to scan just for the flag setting sampling X * rate, another to ignore the rate flag, but to set the time value based X * on the sampling rate. X * NOTE: WHEN MAKING MORE THAN ONE CALL TO crack() IN A PROGRAM, IT X * IS NECESSARY TO RESET arg_index TO 0 FIRST. X * X * When ignoring unknown flags, if an unknown flag has an option X * associated with it, the option is also ignored. Care should be excercised X * here because it may be possible that the associated "option" is really X * more concatenated flags. These, if any, are lost. The rule is that, X * when ignoring unknown flags, the first instance of an unknown flag X * causes that flag and the rest of that argument to be discarded. For X * instance, if flags is set to "a:b:cd", and a command line: X * "-zcdaoption" is supplied, c d and a would be ignored because they come X * after z in the same argument. The point is there is no way to disambiguate X * flags from unknown options when ignoring flags, so concatenating options, X * while nice, is problematical. X */ X X#include X Xint arg_index = 0; Xchar *arg_option; Xchar *pvcon = NULL; X Xchar crack(argc, argv, flags, ign) X int argc; char **argv; char *flags; int ign; X{ X char *pv, *flgp, *strchr(); X while ((arg_index) < argc) X { X if (pvcon != NULL) X pv = pvcon; X else X { X if (++arg_index >= argc) return(NULL); X pv = argv[arg_index]; X if (*pv != '-') X return(NULL); X } X pv++; /* skip '-' or prev. flag */ X if (*pv != NULL) X { X if ((flgp=strchr(flags,*pv)) != NULL) X { X pvcon = pv; X if (*(flgp+1) == '|') { arg_option = pv+1; pvcon = NULL; } X return(*pv); X } X else X if (!ign) X { X fprintf(stderr, "%s: no such flag: %s\n", argv[0], pv); X return(EOF); X } X else X pvcon = NULL; X } X pvcon = NULL; X } X return(NULL); X } X END_OF_FILE if test 4123 -ne `wc -c <'midifile/crack.c'`; then echo shar: \"'midifile/crack.c'\" unpacked with wrong size! fi # end of 'midifile/crack.c' fi if test -f 'midifile/example1.mid.uu' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'midifile/example1.mid.uu'\" else echo shar: Extracting \"'midifile/example1.mid.uu'\" \(141 characters\) sed "s/^X//" >'midifile/example1.mid.uu' <<'END_OF_FILE' Xbegin 660 example1.mid XM351H9 8 ! &!-5')K .P#_6 0$ A@( /]1 P>A( # !0#!+@#" XD1@"2,& /&!@D4- 8)!,((% @C! #Q (%#0 " 3$ _R\ X Xend END_OF_FILE if test 141 -ne `wc -c <'midifile/example1.mid.uu'`; then echo shar: \"'midifile/example1.mid.uu'\" unpacked with wrong size! fi # end of 'midifile/example1.mid.uu' fi if test -f 'midifile/example2.mid.uu' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'midifile/example2.mid.uu'\" else echo shar: Extracting \"'midifile/example2.mid.uu'\" \(195 characters\) sed "s/^X//" >'midifile/example2.mid.uu' <<'END_OF_FILE' Xbegin 660 example2.mid XM351H9 8 0 $ &!-5')K % #_6 0$ A@( /]1 P>A((, _R\ 351R XM:P ! P 6!0)!,((% 3 _R\ 351R:P \ P2Y@D4- @B!# #_+P!- X<5')K %0#"1@"2,& /&"# # #P /\O \ X Xend END_OF_FILE if test 195 -ne `wc -c <'midifile/example2.mid.uu'`; then echo shar: \"'midifile/example2.mid.uu'\" unpacked with wrong size! fi # end of 'midifile/example2.mid.uu' fi if test -f 'midifile/example3.mid.uu' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'midifile/example3.mid.uu'\" else echo shar: Extracting \"'midifile/example3.mid.uu'\" \(191 characters\) sed "s/^X//" >'midifile/example3.mid.uu' <<'END_OF_FILE' Xbegin 660 example3.mid XM351H9 8 ! !A-5')K 7P#_ 29$('-C86QE(" @(" @(" @(" @ XM(" @(" @(" @(" @(" @(" @( "0/D ,/@ ,0$ ,0 ,0D ,0@ ,0T ,0P , X;14 ,10 ,1T ,1P ,24 ,20 ,2D ,2@ ,_R\ X Xend END_OF_FILE if test 191 -ne `wc -c <'midifile/example3.mid.uu'`; then echo shar: \"'midifile/example3.mid.uu'\" unpacked with wrong size! fi # end of 'midifile/example3.mid.uu' fi if test -f 'midifile/example4.mid.uu' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'midifile/example4.mid.uu'\" else echo shar: Extracting \"'midifile/example4.mid.uu'\" \(2519 characters\) sed "s/^X//" >'midifile/example4.mid.uu' <<'END_OF_FILE' Xbegin 660 example4.mid XM351H9 8 ! #!-5')K &]P#_ 28Y-G!P<3$L,B @(" @(" @(" @ XM(" @(" @(" @(" @(" @(" @( "0*U /E@ .U 05 $D4]T%) ^ [ !! XM 613F $3P /D"L 9%/7 !. !I19 !/ !!1 %/:A5/ $^0*T0 .E@ /5 XM0&@"D4]T%I Z ] ! 613EP"3P /3V@ 3@ "D"L &)%19 !/ !-/: 51 XM !)/ $U37 &0*T0 /E .TX 05 0D5, E10!I ^ [ !! 614UL#5 , XM4F0"4P "D"L #9%2 "%15 *0*T0 .E0 /5 0$T.D5)4 E$ ") Z ] ! XM 214@ "45H/4%0#D"L 9%1 E0 ")/6 20*T@ /EH .U 05H#D4\ %9 ^ XM [ !! :10U0*0P #148%D"L 9%#9 %% A"= 1# !5" %#7!%$6P!# XM !E$ )%9!%&4 )% !!& M'9!!' *0)$0"D4AH+I \1@ W0 ! 3 D *1 XM2 6D#P #< $ !9%(6P=( M(9 &0*T8*D4@ )4A< 9 \2 W.P! 4 K XM N12 -D#P #< $ &"1( Y%(7"V0/$T -T0 0$\ ) !D4@ %Y \ W XM ! !@K1@&11UP51P :D#Q0 #=( $!0 "L I%(7!:0/ -P 0 $D4@ XM DE;#4D DIH Y F2# W2 [3@ U2 ^*P F !@W [ U ^ !@K2":1 XM2@ *D#Y. #=. #5& #M( "L &#X #< #4 #L &"9 &I%#7 Q%: 1# :0 XM/DT -T@ -3X .T< )@ !D44 D-D"T)H!$, !I ^ W U [ Z10@ " XM0V8(D"M,#9%-9 !# !E*9 %- F0/DX -T8 .TT -3T *P (D4=< TH #9 ^ XM W [ U 611P #0UP0D"], )%# )*="Z0/E -4@ -TP .T\ +P # XMD4H %9 ^ U W [ 612F@)2@ (2F0"D"M$#Y%* "!*:@&0/E -T@ XM-44 .T@ *P 1D4H !Y ^ W U [ !@F4 &12F@OD#Y, #=( #M- #5 XM "8 I%)8 )* !20/@ -P .P -0 %D4D 4IH$I K2 .12UP"2@ 72P " XM4V@2D#Y0 #5& #=/ #M0 "L #I%27 )3 B0/@ -0 -P .P %D5%8 %( XM#5!;!%$ I D1PF13V0!4 FD#Q, #=0 $!4 "0 &#P #< $ %Y%/ &0 XM*TP?D4AD#4QF 4@ Y \3 W0 ! 4 K B13 $1V0+2UL!D#P #< $ XM Y%' M+ -*7 -&9 20)$TP/%@ -UH 0%@ ) 5D48 Y \ W ! B1 XM2@ 0D"9$&"8 &"A/ #Q8 #=0 $!:"I%+= Z0* / -P 0 D4L "4QJ XM#Y I3 "13 "370ND#E0 #Q( $%- "D Y%- !60.0 / 00 "D4QD#DP XM!DUH I D3"2130 ,D#E/ #Q( $%. "0 &)%/7 "0.0 / 00 1D4\ !$U; XM Y I4# Y3 \3@!!3 I !@Y \ !! :130 2D"1',#E( #Q0 $%4 "0 XM Y%/:!)/ .0.0 / 00 %D4U<$$T Y P3 &13%POD#=, #Q4 $!4 # XM!Y%, !&0-P / 0 $D4M;#4L !TQH ) K1!"13 +2UP.2P "2F0%D#Q0 XM #= $!0 "L #I%* J0/ -P 0 #D4E4#4D !DAD I P4# \4 W0 ! XM2 P :12 2D#P #< $ &"](&"\ &)%'6 "0+4@ /%@ -T@ 0$@0D4A4 XM $< ") M \ W ! 2125P%2 ,2F@#D"M$ )%) !%* !^0/D@ -40 XM-T< .T *P !D4-'$454 $, !I ^ U W [ *11EL&10 )1VH&1@ ! XMD"90")%' "8^9 *0/DX -40 -T0 .T0 )@ +D3]< CX "Y ^ U W [ XM 210'0 /P -0 '070 D"M(&I%! )%9!)% )(9 "0/D\ -U -4T .TX XM*P 8/@ -P -0 .P !D4@ TQJ$$P !) F3 .12G062@ ,3UL+D#L[ #X^ XM #5 #= "8 "I%37 1/ J0.P /@ -P 'D5, !E9T!Y U 0D3P \7@61 XM5%P 5@ 3D#P $9%4 >0) 8+4P .5@8*U@ -UP +0 4.0 $*P -P 8*%X XM-%@8* - 8)%\ ,&@8, 8) >D5M3!UU8!%L !%]'midifile/mfcheck.c' <<'END_OF_FILE' X#include X#include "midifile.h" X Xmygetc() { return(getchar()); } X Xmain() X{ X Mf_getc = mygetc; X midifile (); X exit(0); X} END_OF_FILE if test 129 -ne `wc -c <'midifile/mfcheck.c'`; then echo shar: \"'midifile/mfcheck.c'\" unpacked with wrong size! fi # end of 'midifile/mfcheck.c' fi if test -f 'midifile/mfstrings.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'midifile/mfstrings.c'\" else echo shar: Extracting \"'midifile/mfstrings.c'\" \(414 characters\) sed "s/^X//" >'midifile/mfstrings.c' <<'END_OF_FILE' X#include X#include X#include "midifile.h" X XFILE *F; X Xmygetc() { return(getc(F)); } X Xmytext(type,leng,msg) Xchar *msg; X{ X char *p; X char *ep = msg + leng; X X for ( p=msg; p 1 ) X F = fopen(argv[1],"r"); X else X F = stdin; X X Mf_getc = mygetc; X Mf_text = mytext; X X midifile(); X X exit(0); X} END_OF_FILE if test 414 -ne `wc -c <'midifile/mfstrings.c'`; then echo shar: \"'midifile/mfstrings.c'\" unpacked with wrong size! fi # end of 'midifile/mfstrings.c' fi if test -f 'midifile/mftext.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'midifile/mftext.c'\" else echo shar: Extracting \"'midifile/mftext.c'\" \(4885 characters\) sed "s/^X//" >'midifile/mftext.c' <<'END_OF_FILE' X/* X * mftext X * X * Convert a MIDI file to verbose text. X */ X X#include X#include X#include "midifile.h" X Xstatic FILE *F; Xint SECONDS; /* global that tells whether to display seconds or ticks */ Xint division; /* from the file header */ Xlong tempo = 500000; /* the default tempo is 120 beats/minute */ X Xfilegetc() X{ X return(getc(F)); X} X X/* for crack */ Xextern int arg_index; X Xmain(argc,argv) Xchar **argv; X{ X FILE *efopen(); X char ch; X X SECONDS = 0; X X while((ch = crack(argc,argv,"s",0)) != NULL){ X switch(ch){ X case 's' : SECONDS = 1; break; X } X } X X if ( argc < 2 && !SECONDS || argc < 3 && SECONDS ) X F = stdin; X else X F = efopen(argv[arg_index],"r"); X X initfuncs(); X Mf_getc = filegetc; X midifile(); X fclose(F); X exit(0); X} X XFILE * Xefopen(name,mode) Xchar *name; Xchar *mode; X{ X FILE *f; X extern int errno; X extern char *sys_errlist[]; X extern int sys_nerr; X char *errmess; X X if ( (f=fopen(name,mode)) == NULL ) { X (void) fprintf(stderr,"*** ERROR *** Cannot open '%s'!\n",name); X if ( errno <= sys_nerr ) X errmess = sys_errlist[errno]; X else X errmess = "Unknown error!"; X (void) fprintf(stderr,"************* Reason: %s\n",errmess); X exit(1); X } X return(f); X} X Xerror(s) Xchar *s; X{ X fprintf(stderr,"Error: %s\n",s); X} X Xtxt_header(format,ntrks,ldivision) X{ X division = ldivision; X printf("Header format=%d ntrks=%d division=%d\n",format,ntrks,division); X} X Xtxt_trackstart() X{ X printf("Track start\n"); X} X Xtxt_trackend() X{ X printf("Track end\n"); X} X Xtxt_noteon(chan,pitch,vol) X{ X prtime(); X printf("Note on, chan=%d pitch=%d vol=%d\n",chan+1,pitch,vol); X} X Xtxt_noteoff(chan,pitch,vol) X{ X prtime(); X printf("Note off, chan=%d pitch=%d vol=%d\n",chan+1,pitch,vol); X} X Xtxt_pressure(chan,pitch,press) X{ X prtime(); X printf("Pressure, chan=%d pitch=%d press=%d\n",chan+1,pitch,press); X} X Xtxt_parameter(chan,control,value) X{ X prtime(); X printf("Parameter, chan=%d c1=%d c2=%d\n",chan+1,control,value); X} X Xtxt_pitchbend(chan,msb,lsb) X{ X prtime(); X printf("Pitchbend, chan=%d msb=%d lsb=%d\n",chan+1,msb,lsb); X} X Xtxt_program(chan,program) X{ X prtime(); X printf("Program, chan=%d program=%d\n",chan+1,program); X} X Xtxt_chanpressure(chan,press) X{ X prtime(); X printf("Channel pressure, chan=%d pressure=%d\n",chan+1,press); X} X Xtxt_sysex(leng,mess) Xchar *mess; X{ X prtime(); X printf("Sysex, leng=%d\n",leng); X} X Xtxt_metamisc(type,leng,mess) Xchar *mess; X{ X prtime(); X printf("Meta event, unrecognized, type=0x%02x leng=%d\n",type,leng); X} X Xtxt_metaspecial(type,leng,mess) Xchar *mess; X{ X prtime(); X printf("Meta event, sequencer-specific, type=0x%02x leng=%d\n",type,leng); X} X Xtxt_metatext(type,leng,mess) Xchar *mess; X{ X static char *ttype[] = { X NULL, X "Text Event", /* type=0x01 */ X "Copyright Notice", /* type=0x02 */ X "Sequence/Track Name", X "Instrument Name", /* ... */ X "Lyric", X "Marker", X "Cue Point", /* type=0x07 */ X "Unrecognized" X }; X int unrecognized = (sizeof(ttype)/sizeof(char *)) - 1; X register int n, c; X register char *p = mess; X X if ( type < 1 || type > unrecognized ) X type = unrecognized; X prtime(); X printf("Meta Text, type=0x%02x (%s) leng=%d\n",type,ttype[type],leng); X printf(" Text = <"); X for ( n=0; n\n"); X} X Xtxt_metaseq(num) X{ X prtime(); X printf("Meta event, sequence number = %d\n",num); X} X Xtxt_metaeot() X{ X prtime(); X printf("Meta event, end of track\n"); X} X Xtxt_keysig(sf,mi) X{ X prtime(); X printf("Key signature, sharp/flats=%d minor=%d\n",sf,mi); X} X Xtxt_tempo(ltempo) Xlong ltempo; X{ X tempo = ltempo; X prtime(); X printf("Tempo, microseconds-per-MIDI-quarter-note=%d\n",tempo); X} X Xtxt_timesig(nn,dd,cc,bb) X{ X int denom = 1; X while ( dd-- > 0 ) X denom *= 2; X prtime(); X printf("Time signature=%d/%d MIDI-clocks/click=%d 32nd-notes/24-MIDI-clocks=%d\n", X nn,denom,cc,bb); X} X Xtxt_smpte(hr,mn,se,fr,ff) X{ X prtime(); X printf("SMPTE, hour=%d minute=%d second=%d frame=%d fract-frame=%d\n", X hr,mn,se,fr,ff); X} X Xtxt_arbitrary(leng,mess) Xchar *mess; X{ X prtime(); X printf("Arbitrary bytes, leng=%d\n",leng); X} X Xprtime() X{ X if(SECONDS) X printf("Time=%f ",mf_ticks2sec(Mf_currtime,division,tempo)); X else X printf("Time=%ld ",Mf_currtime); X} X Xinitfuncs() X{ X Mf_error = error; X Mf_header = txt_header; X Mf_trackstart = txt_trackstart; X Mf_trackend = txt_trackend; X Mf_noteon = txt_noteon; X Mf_noteoff = txt_noteoff; X Mf_pressure = txt_pressure; X Mf_parameter = txt_parameter; X Mf_pitchbend = txt_pitchbend; X Mf_program = txt_program; X Mf_chanpressure = txt_chanpressure; X Mf_sysex = txt_sysex; X Mf_metamisc = txt_metamisc; X Mf_seqnum = txt_metaseq; X Mf_eot = txt_metaeot; X Mf_timesig = txt_timesig; X Mf_smpte = txt_smpte; X Mf_tempo = txt_tempo; X Mf_keysig = txt_keysig; X Mf_seqspecific = txt_metaspecial; X Mf_text = txt_metatext; X Mf_arbitrary = txt_arbitrary; X} END_OF_FILE if test 4885 -ne `wc -c <'midifile/mftext.c'`; then echo shar: \"'midifile/mftext.c'\" unpacked with wrong size! fi # end of 'midifile/mftext.c' fi if test -f 'midifile/mfwrite_ex.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'midifile/mfwrite_ex.c'\" else echo shar: Extracting \"'midifile/mfwrite_ex.c'\" \(1158 characters\) sed "s/^X//" >'midifile/mfwrite_ex.c' <<'END_OF_FILE' X/* X * writing_example.c 4/30/89 X * X */ X#include X#include X#include "midifile.h" X X/* These lines are needed to use the library */ XFILE *fp; Xmyputc(c) { return(putc(c,fp));} X X X/* X * mywritetrack() X * X * Sample showing how to use the library routines to write out a track. X * Returns 1 if successful, and -1 if not. The track consists of X * a series of quarter notes from lowest to highest in pitch at X * constant velocity, each separted by a quarter-note rest. X * X */ Xint mywritetrack(track) Xint track; X{ X int i; X char data[2]; X X mf_write_tempo((long)500000); /* 120 beats/per/second */ X X for(i = 1 ; i < 128; i++){ X data[0] = i; /* note number */ X data[1] = 64; /* velocity */ X if(!mf_write_midi_event(480,note_on,1,data,2)) return(-1); X if(!mf_write_midi_event(480,note_off,1,data,2)) return(-1); X } X return(1); X} /* end of write_track() */ X Xmain(argc,argv) Xchar **argv; X{ X if((fp = fopen(argv[1],"w")) == 0L) X printf("f1to0: unable to open file %s for writing.\n",argv[1]); X X Mf_putc = myputc; X Mf_writetrack = mywritetrack; X X /* write a single track */ X mfwrite(0,1,480,fp); X} END_OF_FILE if test 1158 -ne `wc -c <'midifile/mfwrite_ex.c'`; then echo shar: \"'midifile/mfwrite_ex.c'\" unpacked with wrong size! fi # end of 'midifile/mfwrite_ex.c' fi if test -f 'midifile/midifile.3' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'midifile/midifile.3'\" else echo shar: Extracting \"'midifile/midifile.3'\" \(10710 characters\) sed "s/^X//" >'midifile/midifile.3' <<'END_OF_FILE' X.TH MIDIFILE 3 X.SH NAME Xmfread,mfwrite - read and write a standard MIDI file X.SH SYNOPSIS X\fC#include "mfread.h" X Xmfread () X X.nf Xint (*Mf_getc) (); Xint (*Mf_putc) (); Xint (*Mf_error) (char *msg); Xint (*Mf_header) (int format, int ntrks, int division); Xint (*Mf_trackstart) (); Xint (*Mf_trackend) (); Xint (*Mf_noteon) (int chan, int pitch, int vol); Xint (*Mf_noteoff) (int chan, int pitch, int vol); Xint (*Mf_pressure) (int chan, int pitch, int pressure); Xint (*Mf_parameter) (int chan, int control, int value); Xint (*Mf_pitchbend) (int chan, int msb, int lsb); Xint (*Mf_program) (int chan, int program); Xint (*Mf_chanpressure) (int chan, int pressure); Xint (*Mf_sysex) (int leng, char *msg); Xint (*Mf_metamisc) (int type, int leng, int msg); Xint (*Mf_seqspecific) (int type, int leng, int msg); Xint (*Mf_seqnum) (int num); Xint (*Mf_text) (int type, int leng, int msg); Xint (*Mf_eot) (); Xint (*Mf_timesig) (int numer, int denom, int clocks, int qnotes); Xint (*Mf_smpte) (int hour, int min, int sec, int frame, int fract); Xint (*Mf_tempo) (int microsecs); Xint (*Mf_keysig) (int sharpflat, int minor); Xint (*Mf_arbitrary) (int leng, int msg); Xint Mf_nomerge; Xlong Mf_currtime; X.fi X.sp 1 Xmfwrite(int format, int ntracks, int division, FILE *fp) X.sp 1 X.nf Xint (*Mf_writetrack)(int track); Xint (*Mf_writetempotrack)(); X Xvoid mf_write_midi_event(delta, type, chan, data, size) Xunsigned long delta; Xunsigned int type,chan,size; Xchar *data; X Xvoid mf_write_meta_event(delta, type, data, size) Xunsigned long delta; Xunsigned int type,chan,size; Xchar *data; X Xvoid mf_write_tempo(tempo) Xunsigned long tempo; X Xunsigned long mf_sec2ticks(float seconds, int division, int tempo) Xfloat seconds; Xint division; Xunsigned int tempo; X Xfloat mf_ticks2sec(ticks, division, tempo) Xunsigned long ticks; Xint division; Xunsigned int tempo; X.fi X X.SH DESCRIPTION XThe \fCmfread\fR function reads and inteprets a standard MIDI file. XTo use it you need to understand the general form of a XMIDI file and the type of information it contains, but you don't Xneed to know much, if anything, about the detailed format of the file Xand the mechanics of reading it reliably and portably. X XThe \fCmfwrite\fR function writes a standard MIDI file making Xuse of user-defined functions that access the program's Xdata structure. To use it you need to define your own Mf_writetrack Xroutine and then make use of the write_* family of routines to Xwrite out the MIDI data. The \fCmfwrite\fR routine takes Xcare of the file format and writing the file and track chunk headers. X X.SH READING STANDARD MIDI FILES XA single call to \fCmfread\fR will read an entire MIDI file. XThe interface to \fCmfread\fR is a set of external variables Xnamed \fCMf_*\fR, most of which are function pointers to be called Xfrom within \fCmfread\fR during the process of parsing the MIDI file. XBefore calling \fCmfread\fR, the only Xrequirement is that you assign a value Xto \fCMf_getc\fR - a pointer to a function that will return Xcharacters from the MIDI file, using -1 to indicate EOF. XAll the rest of the function Xpointers are initialized to NULL, and the default action for each Xis to do nothing. The following is a complete program using \fCmfread\fR Xthat could serve as a 'syntax checker' for MIDI files: X X.in +1i X.ft C X.nf X#include X#include "midifile.h" X Xmygetc() X{ X /* use standard input */ X return(getchar()); X} X Xmain() X{ X Mf_getc = mygetc; X mfread(); X exit(0); X} X.fi X.ft R X.in -1i X XThis takes advantage of the default action when an error is detected, which Xis to exit silently with a return code of 1. An error function of your Xown can be used by giving a value to \fCMf_error\fR; the function will be Xcalled with the error message as an argument. XThe other \fCMf_* variables can similarly be used to call arbitrary Xfunctions while parsing the MIDI file. The descriptions below Xof the information passed to these functions is sparse; refer to Xthe MIDI file standard for the complete descriptions. X X\fCMf_header\fR is the first function to be called, and its arguments Xcontain information from the MIDI file's header; the format (0,1, or 2), Xthe number of tracks, and the division of a quarter-note that defines Xthe times units. X\fCMf_trackstart\fR and X\fCMf_trackend\fR are called at the beginning and end of each track. X XOnce inside a track, each separate message causes a function to be called. XFor example, each note-on message causes \fCMf_noteon\fR to be called Xwith the channel, pitch, and volume as arguments. The time at which Xthe message occurred is stored in \fCMf_currtime\fR - one of the few Xexternal variables that isn't a function pointer. The other channel messages Xare handled in a similar and obvious fashion - X\fCMf_noteoff\fR, X\fCMf_pressure\fR, X\fCMf_parameter\fR, X\fCMf_pitchbend\fR, X\fCMf_program\fR, Xand \fCMf_chanpressure\fR. See the declarations above for the arguments Xthat are passed to each. X XSystem exclusive messages are handled by calling \fCMf_sysex\fR, passing Xas arguments the message length and a pointer to a static buffer containing Xthe entire message. XThe buffer is expanded when necessary; memory availability is the only limit Xto its size. Normally, 'continued' system exclusives are automatically Xmerged, and \fCMf_sysex\fR is only called once. It you want to disable this Xyou can set \fCMf_nomerge\fR to 1, causing \fCMf_sysex\fR to be called Xonce for each part of the message. X X\fCMf_seqnum\fR is called by the \fImeta\fR message that provides Xa sequence number, Xwhich if present must appear at the beginning of a track. XThe tempo \fImeta\fR message causes \fCMf_tempo\fR to be called; its Xargument is the number of microseconds per MIDI quarter-note (24 MIDI clocks). XThe end-of-track \fImeta\fR message causes \fCMf_eot\fR to be called. XThe key signature \fImeta\fR message causes \fCMf_keysig\fR to be called; Xthe first argument conveys the number of sharps or flats, the second Xargument is 1 if the key is minor. X XThe \fCMf_timesig\fR and \fCMf_smpte\fR functions are called when the Xcorresponding \fImeta\fR messages are seen. See the MIDI file standard Xfor a description of their arguments. X XThe \fItext\fR messages in the MIDI file standard are of the following Xtypes: X X.in +1i X.nf X0x01 Text Event X0x02 Copyright X0x03 Sequence/Track Name X0x04 Instrument X0x05 Lyric X0x06 Marker X0x07 Cue Point X0x08-0x0F Reserverd but Undefined X.fi X.in -1i X X\fCMf_text\fR is called for each of these; the arguments are Xthe type number, the message length, and a pointer to the message buffer. X XMisceallaneous \fImeta\fR messages are handled by \fCMf_metamisc\fR, Xsequencer-specific messages are handled by \fCMf_seqspecific\fR, and Xarbitrary "escape" messages (started with 0xF7) are handled by X\fCMf_arbitrary\fR. X.SH READING EXAMPLE XThe following is a \fCstrings\fR-like program for MIDI files: X X.in +1i X.ft C X.nf X#include X#include X#include "midifile.h" X XFILE *F; X Xmygetc() { return(getc(F)); } X Xmytext(type,leng,msg) Xchar *msg; X{ X char *p; X char *ep = msg + leng; X X for ( p=msg; p 1 ) X F = fopen(argv[1],"r"); X else X F = stdin; X X Mf_getc = mygetc; X Mf_text = mytext; X X mfread(); X X exit(0); X} X.fi X.ft R X.in -1i X.sp X.SH WRITING STANDARD MIDI FILES XA single call to \fCmfwrite\fR will write an entire MIDI file. Before Xcalling \fCmfwrite\fR, you must assign values to function pointers X\fCMf_writetrack\fR and \fCMf_putc\fR. The first is a routine to Xaccess your MIDI data structure, which can make use of other library Xroutines to write the actual MIDI data. The routine X\fCMf_writetrack\fR will be passed a single parameter which is the Xnumber of the track to be written. The pointer \fCMf_putc\fR should be Xset to point to a routine that accepts a charcter as input, writes that Xcharacter to a file, and returns the value that was written. In the Xcase of a format 1 file, a routine has to be written to write a tempo Xmap, and assigned to the function pointer \fCMf_writetempotrack\fR. XThis is because format 1 files assume the first track written is a Xtempo track. X X\fCmf_write_midi_event\fR and \fCmf_write_meta_event\fR are routines Xthat should be called from your \fCMf_writetrack\fR routine to write Xout MIDI events. The delta time param is the number of ticks since the Xlast event. The int "type" is the type of MIDI message. The int "chan" Xis the MIDI channel, which can be between 1 and 16. The char pointer X"data" points to an array containing the data bytes, if any exist. The Xint "size" is the number of data bytes. X X\fCmf_sec2ticks\fR and \fCmf_ticks2sec\fR are utility routines Xto help you convert between the MIDI file parameter of ticks Xand the more standard seconds. The int "division" is the same Xdivision parameter from the file header, and tempo is expressed Xin microseconds per MIDI quarter-note, or "24ths of a microsecond Xper MIDI clock". The division has two meanings, depending on Xwhether bit 15 is set or not. If bit 15 of division is zero, Xbits 14 through 0 represent the number of delta-time "ticks" Xwhich make up a quarter note. If bit 15 of division is a one, Xdelta-times in a file correspond to subdivisions of a second Xsimiliar to SMPTE and MIDI time code. In this format bits X14 through 8 contain one of four values - 24, -25, -29, or -30, Xcorresponding to the four standard SMPTE and MIDI time code Xframe per second formats, where -29 represents 30 drop frame. XThe second byte consisting of bits 7 through 0 corresponds Xthe the resolution within a frame. Refer the Standard MIDI Files X1.0 spec for more details. X X.SH WRITING EXAMPLE XThe following is a simple program to demonstrate writing MIDI files. XThe track would consist of a series of quarter notes from lowest to Xhighest in pitch at constant velocity, each separted by a quarter-note Xrest. X.sp X.in +1i X.ft C X.nf X#include X#include X#include "midifile.h" X XFILE *fp; Xmyputc(c) { return(putc(c,fp));} X Xint mywritetrack(track) Xint track; X{ X int i; X char data[2]; X X /* 120 beats/per/second */ X mf_write_tempo((long)500000); X X for(i = 1 ; i < 128; i++){ X data[0] = i; /* note number */ X data[1] = 64; /* velocity */ X if(!mf_write_midi_event(480,note_on,1,data,2)) X return(-1); X if(!mf_write_midi_event(480,note_off,1,data,2)) X return(-1); X } X X return(1); X} /* end of write_track() */ X Xmain(argc,argv) Xchar **argv; X{ X if((fp = fopen(argv[1],"w")) == 0L) X exit(1); X X Mf_putc = myputc; X Mf_writetrack = mywritetrack; X X /* write a single track */ X mfwrite(0,1,480,fp); X} X.sp X.fi X.ft R X.in -1i X.sp X.SH AUTHOR XTim Thompson (att!twitch!glimmer!tjt) X.SH CONTRIBUTORS XMichael Czeiszperger (mike@pan.com) END_OF_FILE if test 10710 -ne `wc -c <'midifile/midifile.3'`; then echo shar: \"'midifile/midifile.3'\" unpacked with wrong size! fi # end of 'midifile/midifile.3' fi if test -f 'midifile/midifile.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'midifile/midifile.h'\" else echo shar: Extracting \"'midifile/midifile.h'\" \(3285 characters\) sed "s/^X//" >'midifile/midifile.h' <<'END_OF_FILE' X/* definitions for MIDI file parsing code */ Xextern int (*Mf_getc)(); Xextern int (*Mf_header)(); Xextern int (*Mf_trackstart)(); Xextern int (*Mf_trackend)(); Xextern int (*Mf_noteon)(); Xextern int (*Mf_noteoff)(); Xextern int (*Mf_pressure)(); Xextern int (*Mf_parameter)(); Xextern int (*Mf_pitchbend)(); Xextern int (*Mf_program)(); Xextern int (*Mf_chanpressure)(); Xextern int (*Mf_sysex)(); Xextern int (*Mf_metamisc)(); Xextern int (*Mf_seqspecific)(); Xextern int (*Mf_seqnum)(); Xextern int (*Mf_text)(); Xextern int (*Mf_eot)(); Xextern int (*Mf_timesig)(); Xextern int (*Mf_smpte)(); Xextern int (*Mf_tempo)(); Xextern int (*Mf_keysig)(); Xextern int (*Mf_arbitrary)(); Xextern int (*Mf_error)(); Xextern long Mf_currtime; Xextern int Mf_nomerge; X X/* definitions for MIDI file writing code */ Xextern int (*Mf_putc)(); Xextern int (*Mf_writetrack)(); Xextern int (*Mf_writetempotrack)(); Xfloat mf_ticks2sec(); Xunsigned long mf_sec2ticks(); Xvoid mfwrite(); X X/* MIDI status commands most significant bit is 1 */ X#define note_off 0x80 X#define note_on 0x90 X#define poly_aftertouch 0xa0 X#define control_change 0xb0 X#define program_chng 0xc0 X#define channel_aftertouch 0xd0 X#define pitch_wheel 0xe0 X#define system_exclusive 0xf0 X#define delay_packet (1111) X X/* 7 bit controllers */ X#define damper_pedal 0x40 X#define portamento 0x41 X#define sostenuto 0x42 X#define soft_pedal 0x43 X#define general_4 0x44 X#define hold_2 0x45 X#define general_5 0x50 X#define general_6 0x51 X#define general_7 0x52 X#define general_8 0x53 X#define tremolo_depth 0x5c X#define chorus_depth 0x5d X#define detune 0x5e X#define phaser_depth 0x5f X X/* parameter values */ X#define data_inc 0x60 X#define data_dec 0x61 X X/* parameter selection */ X#define non_reg_lsb 0x62 X#define non_reg_msb 0x63 X#define reg_lsb 0x64 X#define reg_msb 0x65 X X/* Standard MIDI Files meta event definitions */ X#define meta_event 0xFF X#define sequence_number 0x00 X#define text_event 0x01 X#define copyright_notice 0x02 X#define sequence_name 0x03 X#define instrument_name 0x04 X#define lyric 0x05 X#define marker 0x06 X#define cue_point 0x07 X#define channel_prefix 0x20 X#define end_of_track 0x2f X#define set_tempo 0x51 X#define smpte_offset 0x54 X#define time_signature 0x58 X#define key_signature 0x59 X#define sequencer_specific 0x74 X X/* Manufacturer's ID number */ X#define Seq_Circuits (0x01) /* Sequential Circuits Inc. */ X#define Big_Briar (0x02) /* Big Briar Inc. */ X#define Octave (0x03) /* Octave/Plateau */ X#define Moog (0x04) /* Moog Music */ X#define Passport (0x05) /* Passport Designs */ X#define Lexicon (0x06) /* Lexicon */ X#define Tempi (0x20) /* Bon Tempi */ X#define Siel (0x21) /* S.I.E.L. */ X#define Kawai (0x41) X#define Roland (0x42) X#define Korg (0x42) X#define Yamaha (0x43) X X/* miscellaneous definitions */ X#define MThd 0x4d546864 X#define MTrk 0x4d54726b X#define lowerbyte(x) ((unsigned char)(x & 0xff)) X#define upperbyte(x) ((unsigned char)((x & 0xff00)>>8)) END_OF_FILE if test 3285 -ne `wc -c <'midifile/midifile.h'`; then echo shar: \"'midifile/midifile.h'\" unpacked with wrong size! fi # end of 'midifile/midifile.h' fi if test -f 'midifile/noteon.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'midifile/noteon.c'\" else echo shar: Extracting \"'midifile/noteon.c'\" \(510 characters\) sed "s/^X//" >'midifile/noteon.c' <<'END_OF_FILE' X#include X#include X#include "midifile.h" X#include "console.h" X XFILE *F; Xextern long Mf_currtime; X Xmygetc() { return(getc(F)); } X Xmynoteon(chan,note,velocity) Xint chan; Xunsigned char note; Xunsigned char velocity; X{ X printf("%ld: chan=%d, note=%d, velocity=%d\n",Mf_currtime, chan, note, velocity); X} X Xmain(argc,argv) Xchar **argv; X{ X argc = ccommand(&argv); X X if ( argc > 1 ) X F = fopen(argv[1],"r"); X else X F = stdin; X X Mf_getc = mygetc; X Mf_noteon = mynoteon; X X midifile(); X X exit(0); X} X END_OF_FILE if test 510 -ne `wc -c <'midifile/noteon.c'`; then echo shar: \"'midifile/noteon.c'\" unpacked with wrong size! fi # end of 'midifile/noteon.c' fi echo shar: End of archive 2 \(of 2\). cp /dev/null ark2isdone MISSING="" for I in 1 2 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked both archives. rm -f ark[1-9]isdone else echo You still must unpack the following archives: echo " " ${MISSING} fi exit 0 exit 0 # Just in case... -- Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM Sterling Software, IMD UUCP: uunet!sparky!kent Phone: (402) 291-8300 FAX: (402) 291-4362 Please send comp.sources.misc-related mail to kent@uunet.uu.net.