1 /************************************************************************
2    readmidi.c -- last change: 1 Jan 96
3 
4    Creates a linked list of each chunk in a midi file.
5    ENTIRE MIDI FILE IS RETAINED IN MEMORY so that no additional malloc
6    calls need be made to store the data of the events in the midi file.
7 
8    Copyright (C) 1995-1996 Nathan I. Laredo
9 
10    This program is modifiable/redistributable under the terms
11    of the GNU General Public Licence.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16    Send your comments and all your spare pocket change to
17    laredo@gnu.ai.mit.edu (Nathan Laredo) or to PSC 1, BOX 709, 2401
18    Kelly Drive, Lackland AFB, TX 78236-5128, USA.
19  *************************************************************************/
20 #include "playmidi.h"
21 #include <unistd.h>
22 
23 int format, ntrks, division;
24 unsigned char *midifilebuf;
25 
26 /* the following few lines are needed for dealing with CMF files */
27 int reloadfm = 0;
28 extern void loadfm();
29 extern int seqfd, sb_dev, wantopl3, play_fm, fmloaded[256];
30 SEQ_USE_EXTBUF();
31 
32 extern struct miditrack seq[MAXTRKS];
33 extern int find_header;
34 extern unsigned long int default_tempo;
35 
36 unsigned short Read16()
37 {
38     register unsigned short x;
39 
40     x = (*(midifilebuf) << 8) | midifilebuf[1];
41     midifilebuf += 2;
42     return x;
43 }
44 
45 unsigned long Read32()
46 {
47     register unsigned long x;
48 
49     x = (*(midifilebuf) << 24) | (midifilebuf[1] << 16) |
50 	(midifilebuf[2] << 8) | midifilebuf[3];
51     midifilebuf += 4;
52     return x;
53 }
54 
55 int readmidi(filebuf, filelength)
56 unsigned char *filebuf;
57 off_t filelength;
58 {
59     unsigned long int i = 0, track, tracklen;
60 
61     midifilebuf = filebuf;
62     /* allow user to specify header number in from large archive */
63     while (i != find_header && midifilebuf < (filebuf + filelength - 32)) {
64 	if (strncmp(midifilebuf, "MThd", 4) == 0) {
65 	    i++;
66 	    midifilebuf += 4;
67 	} else
68 	    midifilebuf++;
69     }
70     if (i != find_header) {	/* specified header was not found */
71 	midifilebuf = filebuf;
72 	return find_header = 0;
73     }
74     if (midifilebuf != filebuf)
75 	midifilebuf -= 4;
76     i = Read32();
77     if (i == RIFF) {
78 	midifilebuf += 16;
79 	i = Read32();
80     }
81     if (i == MThd) {
82 	tracklen = Read32();
83 	format = Read16();
84 	ntrks = Read16();
85 	division = Read16();
86     } else if (i == CTMF) {
87 	/* load a creative labs CMF file, with instruments for fm */
88 	tracklen = midifilebuf[4] | (midifilebuf[5] << 8);
89 	format = 0;
90 	ntrks = 1;
91 	division = midifilebuf[6] | (midifilebuf[7] << 8);
92 	default_tempo = 1000000 * division /
93 		(midifilebuf[8] | (midifilebuf[9] << 8));
94 	seq[0].data = filebuf + tracklen;
95 	seq[0].length = filelength - tracklen;
96 	i = (unsigned long int) (*(short *) &midifilebuf[2]) - 4;
97 	/* if fm playback is enabled, load all fm patches from file */
98 	if (play_fm) {
99 	    struct sbi_instrument instr;
100 	    int j, k;
101 	    reloadfm = midifilebuf[32]; /* number of custom patches */
102             instr.device = sb_dev;
103 	    for (j = 0; j < 32; j++)
104 		instr.operators[j] = 0x3f;
105 	    instr.key = FM_PATCH;
106 	    for (j = 0; j < reloadfm && j < 255; j++) {
107                 instr.channel = j;
108 		fmloaded[j] = instr.key;
109                 for (k = 0; k < 16; k++)
110 		    instr.operators[k] = midifilebuf[i + (16 * j) + k];
111 		SEQ_WRPATCH(&instr, sizeof(instr));
112 	    }
113 	}
114 	return ntrks;
115     } else {
116 	int found = 0;
117 	while (!found && midifilebuf < (filebuf + filelength - 8))
118 	    if (strncmp(midifilebuf, "MThd", 4) == 0)
119 		found++;
120 	    else
121 		midifilebuf++;
122 	if (found) {
123 	    midifilebuf += 4;
124 	    tracklen = Read32();
125 	    format = Read16();
126 	    ntrks = Read16();
127 	    division = Read16();
128 	} else {
129 #ifndef DISABLE_RAW_MIDI_FILES
130 	    /* this allows playing ANY file, so watch out */
131 	    midifilebuf -= 4;
132 	    format = 0;		/* assume it's .mus file ? */
133 	    ntrks = 1;
134 	    division = 40;
135 #else
136 	    return -1;
137 #endif
138 	}
139     }
140     if (ntrks > MAXTRKS) {
141 	fprintf(stderr, "\nWARNING: %d TRACKS IGNORED!\n", ntrks - MAXTRKS);
142 	ntrks = MAXTRKS;
143     }
144     if (play_fm && reloadfm) {
145 	loadfm();	/* if custom CMF patches loaded, replace */
146 	reloadfm = 0;
147     }
148     for (track = 0; track < ntrks; track++) {
149 	if (Read32() != MTrk) {
150 	    /* MTrk isn't where it's supposed to be, search rest of file */
151 	    int fuzz, found = 0;
152 	    midifilebuf -= 4;
153 	    if (strncmp(midifilebuf, "MThd", 4) == 0)
154 		continue;
155 	    else {
156 		if (!track) {
157 		    seq[0].length = filebuf + filelength - midifilebuf;
158 		    seq[0].data = midifilebuf;
159 		    continue;	/* assume raw midi data file */
160 		}
161 		midifilebuf -= seq[track - 1].length;
162 		for (fuzz = 0; (fuzz + midifilebuf) <
163 		     (filebuf + filelength - 8) && !found; fuzz++)
164 		    if (strncmp(&midifilebuf[fuzz], "MTrk", 4) == 0)
165 			found++;
166 		seq[track - 1].length = fuzz;
167 		midifilebuf += fuzz;
168 		if (!found)
169 		    continue;
170 	    }
171 	}
172 	tracklen = Read32();
173 	if (midifilebuf + tracklen > filebuf + filelength)
174 	    tracklen = filebuf + filelength - midifilebuf;
175 	seq[track].length = tracklen;
176 	seq[track].data = midifilebuf;
177 	midifilebuf += tracklen;
178     }
179     ntrks = track;
180     return ntrks;
181 }
182