diff options
Diffstat (limited to 'src/mmus2mid.c')
-rw-r--r-- | src/mmus2mid.c | 866 |
1 files changed, 866 insertions, 0 deletions
diff --git a/src/mmus2mid.c b/src/mmus2mid.c new file mode 100644 index 0000000..3669c36 --- /dev/null +++ b/src/mmus2mid.c | |||
@@ -0,0 +1,866 @@ | |||
1 | /* Emacs style mode select -*- C++ -*- | ||
2 | *----------------------------------------------------------------------------- | ||
3 | * | ||
4 | * | ||
5 | * PrBoom: a Doom port merged with LxDoom and LSDLDoom | ||
6 | * based on BOOM, a modified and improved DOOM engine | ||
7 | * Copyright (C) 1999 by | ||
8 | * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman | ||
9 | * Copyright (C) 1999-2000 by | ||
10 | * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze | ||
11 | * Copyright 2005, 2006 by | ||
12 | * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or | ||
15 | * modify it under the terms of the GNU General Public License | ||
16 | * as published by the Free Software Foundation; either version 2 | ||
17 | * of the License, or (at your option) any later version. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be useful, | ||
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | * GNU General Public License for more details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, write to the Free Software | ||
26 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | ||
27 | * 02111-1307, USA. | ||
28 | * | ||
29 | * DESCRIPTION: | ||
30 | * This file supports conversion of MUS format music in memory | ||
31 | * to MIDI format 1 music in memory. | ||
32 | * | ||
33 | * The primary routine, mmus2mid, converts a block of memory in MUS format | ||
34 | * to an Allegro MIDI structure. This supports playing MUS lumps in a wad | ||
35 | * file with BOOM. | ||
36 | * | ||
37 | * Another routine, Midi2MIDI, converts a block of memory in MIDI format 1 to | ||
38 | * an Allegro MIDI structure. This supports playing MIDI lumps in a wad | ||
39 | * file with BOOM. | ||
40 | * | ||
41 | * For testing purposes, and to make a utility if desired, if the symbol | ||
42 | * STANDALONE is defined by uncommenting the definition below, a main | ||
43 | * routine is compiled that will convert a possibly wildcarded set of MUS | ||
44 | * files to a similarly named set of MIDI files. | ||
45 | * | ||
46 | * Much of the code here is thanks to S. Bacquet's source for QMUS2MID.C | ||
47 | * | ||
48 | *----------------------------------------------------------------------------- | ||
49 | */ | ||
50 | |||
51 | |||
52 | #include <ctype.h> | ||
53 | #include <stdio.h> | ||
54 | #include <stdlib.h> | ||
55 | #include <string.h> | ||
56 | #include <sys/types.h> | ||
57 | #include <sys/stat.h> | ||
58 | #include <stdlib.h> | ||
59 | #ifdef MSDOS /* proff: I don't use allegro in windows */ | ||
60 | #include <allegro.h> | ||
61 | #endif /* !MSDOS */ | ||
62 | #include "mmus2mid.h" | ||
63 | #include "lprintf.h" // jff 08/03/98 - declaration of lprintf | ||
64 | |||
65 | //#define STANDALONE /* uncomment this to make MMUS2MID.EXE */ | ||
66 | #ifndef STANDALONE | ||
67 | #include "m_swap.h" | ||
68 | #include "z_zone.h" | ||
69 | #endif | ||
70 | |||
71 | // some macros to decode mus event bit fields | ||
72 | |||
73 | #define last(e) ((UBYTE)((e) & 0x80)) | ||
74 | #define event_type(e) ((UBYTE)(((e) & 0x7F) >> 4)) | ||
75 | #define channel(e) ((UBYTE)((e) & 0x0F)) | ||
76 | |||
77 | // event types | ||
78 | |||
79 | typedef enum | ||
80 | { | ||
81 | RELEASE_NOTE, | ||
82 | PLAY_NOTE, | ||
83 | BEND_NOTE, | ||
84 | SYS_EVENT, | ||
85 | CNTL_CHANGE, | ||
86 | UNKNOWN_EVENT1, | ||
87 | SCORE_END, | ||
88 | UNKNOWN_EVENT2, | ||
89 | } mus_event_t; | ||
90 | |||
91 | // MUS format header structure | ||
92 | |||
93 | typedef struct | ||
94 | { | ||
95 | char ID[4]; // identifier "MUS"0x1A | ||
96 | UWORD ScoreLength; // length of music portion | ||
97 | UWORD ScoreStart; // offset of music portion | ||
98 | UWORD channels; // count of primary channels | ||
99 | UWORD SecChannels; // count of secondary channels | ||
100 | UWORD InstrCnt; // number of instruments | ||
101 | } PACKEDATTR MUSheader; | ||
102 | |||
103 | // to keep track of information in a MIDI track | ||
104 | |||
105 | typedef struct Track | ||
106 | { | ||
107 | char velocity; | ||
108 | long deltaT; | ||
109 | UBYTE lastEvt; | ||
110 | long alloced; | ||
111 | } TrackInfo; | ||
112 | |||
113 | // array of info about tracks | ||
114 | |||
115 | static TrackInfo track[MIDI_TRACKS]; | ||
116 | |||
117 | // initial track size allocation | ||
118 | #define TRACKBUFFERSIZE 1024 | ||
119 | |||
120 | // lookup table MUS -> MID controls | ||
121 | static UBYTE MUS2MIDcontrol[15] = | ||
122 | { | ||
123 | 0, // Program change - not a MIDI control change | ||
124 | 0x00, // Bank select | ||
125 | 0x01, // Modulation pot | ||
126 | 0x07, // Volume | ||
127 | 0x0A, // Pan pot | ||
128 | 0x0B, // Expression pot | ||
129 | 0x5B, // Reverb depth | ||
130 | 0x5D, // Chorus depth | ||
131 | 0x40, // Sustain pedal | ||
132 | 0x43, // Soft pedal | ||
133 | 0x78, // All sounds off | ||
134 | 0x7B, // All notes off | ||
135 | 0x7E, // Mono | ||
136 | 0x7F, // Poly | ||
137 | 0x79 // Reset all controllers | ||
138 | }; | ||
139 | |||
140 | // some strings of bytes used in the midi format | ||
141 | |||
142 | static UBYTE midikey[] = | ||
143 | {0x00,0xff,0x59,0x02,0x00,0x00}; // C major | ||
144 | static UBYTE miditempo[] = | ||
145 | {0x00,0xff,0x51,0x03,0x09,0xa3,0x1a}; // uS/qnote | ||
146 | static UBYTE midihdr[] = | ||
147 | {'M','T','h','d',0,0,0,6,0,1,0,0,0,0}; // header (length 6, format 1) | ||
148 | static UBYTE trackhdr[] = | ||
149 | {'M','T','r','k'}; // track header | ||
150 | |||
151 | // static routine prototypes | ||
152 | |||
153 | static int TWriteByte(MIDI *mididata, int MIDItrack, UBYTE byte); | ||
154 | static int TWriteVarLen(MIDI *mididata, int MIDItrack, register ULONG value); | ||
155 | static ULONG ReadTime(const UBYTE **musptrp); | ||
156 | static int FirstChannelAvailable(int MUS2MIDchannel[]); | ||
157 | static UBYTE MidiEvent(MIDI *mididata,UBYTE midicode,UBYTE MIDIchannel, | ||
158 | UBYTE MIDItrack,int nocomp); | ||
159 | |||
160 | // | ||
161 | // TWriteByte() | ||
162 | // | ||
163 | // write one byte to the selected MIDItrack, update current position | ||
164 | // if track allocation exceeded, double it | ||
165 | // if track not allocated, initially allocate TRACKBUFFERSIZE bytes | ||
166 | // | ||
167 | // Passed pointer to Allegro MIDI structure, number of the MIDI track being | ||
168 | // written, and the byte to write. | ||
169 | // | ||
170 | // Returns 0 on success, MEMALLOC if a memory allocation error occurs | ||
171 | // | ||
172 | static int TWriteByte(MIDI *mididata, int MIDItrack, UBYTE byte) | ||
173 | { | ||
174 | ULONG pos ; | ||
175 | |||
176 | pos = mididata->track[MIDItrack].len; | ||
177 | if (pos >= (ULONG)track[MIDItrack].alloced) | ||
178 | { | ||
179 | track[MIDItrack].alloced = // double allocation | ||
180 | track[MIDItrack].alloced? // or set initial TRACKBUFFERSIZE | ||
181 | 2*track[MIDItrack].alloced : | ||
182 | TRACKBUFFERSIZE; | ||
183 | |||
184 | if (!(mididata->track[MIDItrack].data = // attempt to reallocate | ||
185 | realloc(mididata->track[MIDItrack].data, | ||
186 | track[MIDItrack].alloced))) | ||
187 | return MEMALLOC; | ||
188 | } | ||
189 | mididata->track[MIDItrack].data[pos] = byte; | ||
190 | mididata->track[MIDItrack].len++; | ||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | // | ||
195 | // TWriteVarLen() | ||
196 | // | ||
197 | // write the ULONG value to tracknum-th track, in midi format, which is | ||
198 | // big endian, 7 bits per byte, with all bytes but the last flagged by | ||
199 | // bit 8 being set, allowing the length to vary. | ||
200 | // | ||
201 | // Passed the Allegro MIDI structure, the track number to write, | ||
202 | // and the ULONG value to encode in midi format there | ||
203 | // | ||
204 | // Returns 0 if sucessful, MEMALLOC if a memory allocation error occurs | ||
205 | // | ||
206 | static int TWriteVarLen(MIDI *mididata, int tracknum, register ULONG value) | ||
207 | { | ||
208 | register ULONG buffer; | ||
209 | |||
210 | buffer = value & 0x7f; | ||
211 | while ((value >>= 7)) // terminates because value unsigned | ||
212 | { | ||
213 | buffer <<= 8; // note first value shifted in has bit 8 clear | ||
214 | buffer |= 0x80; // all succeeding values do not | ||
215 | buffer += (value & 0x7f); | ||
216 | } | ||
217 | while (1) // write bytes out in opposite order | ||
218 | { | ||
219 | if (TWriteByte(mididata, tracknum, (UBYTE)(buffer&0xff))) // insure buffer masked | ||
220 | return MEMALLOC; | ||
221 | |||
222 | if (buffer & 0x80) | ||
223 | buffer >>= 8; | ||
224 | else // terminate on the byte with bit 8 clear | ||
225 | break; | ||
226 | } | ||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | // | ||
231 | // ReadTime() | ||
232 | // | ||
233 | // Read a time value from the MUS buffer, advancing the position in it | ||
234 | // | ||
235 | // A time value is a variable length sequence of 8 bit bytes, with all | ||
236 | // but the last having bit 8 set. | ||
237 | // | ||
238 | // Passed a pointer to the pointer to the MUS buffer | ||
239 | // Returns the integer unsigned long time value there and advances the pointer | ||
240 | // | ||
241 | static ULONG ReadTime(const UBYTE **musptrp) | ||
242 | { | ||
243 | register ULONG timeval = 0; | ||
244 | int byte; | ||
245 | |||
246 | do // shift each byte read up in the result until a byte with bit 8 clear | ||
247 | { | ||
248 | byte = *(*musptrp)++; | ||
249 | timeval = (timeval << 7) + (byte & 0x7F); | ||
250 | } | ||
251 | while(byte & 0x80); | ||
252 | |||
253 | return timeval; | ||
254 | } | ||
255 | |||
256 | // | ||
257 | // FirstChannelAvailable() | ||
258 | // | ||
259 | // Return the next unassigned MIDI channel number | ||
260 | // | ||
261 | // The assignment for MUS channel 15 is not counted in the caculation, that | ||
262 | // being percussion and always assigned to MIDI channel 9 (base 0). | ||
263 | // | ||
264 | // Passed the array of MIDI channels assigned to MUS channels | ||
265 | // Returns the maximum channel number unassigned unless that is 9 in which | ||
266 | // case 10 is returned. | ||
267 | // | ||
268 | // killough 10/7/98: changed char parameter, return values to int | ||
269 | |||
270 | static int FirstChannelAvailable(int MUS2MIDchannel[]) | ||
271 | { | ||
272 | int i ; | ||
273 | int max = -1 ; | ||
274 | |||
275 | // find the largest MIDI channel assigned so far | ||
276 | for (i = 0; i < 15; i++) | ||
277 | if (MUS2MIDchannel[i] > max) | ||
278 | max = MUS2MIDchannel[i]; | ||
279 | |||
280 | return (max == 8 ? 10 : max+1); // skip MIDI channel 9 (percussion) | ||
281 | } | ||
282 | |||
283 | // | ||
284 | // MidiEvent() | ||
285 | // | ||
286 | // Constructs a MIDI event code, and writes it to the current MIDI track | ||
287 | // unless its the same as the last event code and compressio is enabled | ||
288 | // in which case nothing is written. | ||
289 | // | ||
290 | // Passed the Allegro MIDI structure, the midi event code, the current | ||
291 | // MIDI channel number, the current MIDI track number, and whether compression | ||
292 | // (running status) is enabled. | ||
293 | // | ||
294 | // Returns the new event code if successful, 0 if a memory allocation error | ||
295 | // | ||
296 | static UBYTE MidiEvent(MIDI *mididata,UBYTE midicode,UBYTE MIDIchannel, | ||
297 | UBYTE MIDItrack,int nocomp) | ||
298 | { | ||
299 | UBYTE newevent; | ||
300 | |||
301 | newevent = midicode | MIDIchannel; | ||
302 | if ((newevent != track[MIDItrack].lastEvt) || nocomp) | ||
303 | { | ||
304 | if (TWriteByte(mididata,MIDItrack, newevent)) | ||
305 | return 0; // indicates MEMALLOC error | ||
306 | track[MIDItrack].lastEvt = newevent; | ||
307 | } | ||
308 | return newevent; | ||
309 | } | ||
310 | |||
311 | // | ||
312 | // mmus2mid() | ||
313 | // | ||
314 | // Convert a memory buffer contain MUS data to an Allegro MIDI structure | ||
315 | // with specified time division and compression. | ||
316 | // | ||
317 | // Passed a pointer to the buffer containing MUS data, a pointer to the | ||
318 | // Allegro MIDI structure, the divisions, and a flag whether to compress. | ||
319 | // | ||
320 | // Returns 0 if successful, otherwise an error code (see mmus2mid.h). | ||
321 | // | ||
322 | int mmus2mid(const UBYTE *mus, MIDI *mididata, UWORD division, int nocomp) | ||
323 | { | ||
324 | UWORD TrackCnt = 0; | ||
325 | UBYTE evt, MUSchannel, MIDIchannel, MIDItrack=0, NewEvent; | ||
326 | int i, event, data; | ||
327 | const UBYTE *musptr; | ||
328 | size_t muslen; | ||
329 | static MUSheader MUSh; | ||
330 | UBYTE MIDIchan2track[MIDI_TRACKS]; // killough 10/7/98: fix too small array | ||
331 | int MUS2MIDchannel[MIDI_TRACKS]; // killough 10/7/98: fix too small array | ||
332 | |||
333 | // copy the MUS header from the MUS buffer to the MUSh header structure | ||
334 | |||
335 | memcpy(&MUSh,mus,sizeof(MUSheader)); | ||
336 | MUSh.ScoreLength = doom_wtohs(MUSh.ScoreLength); | ||
337 | MUSh.ScoreStart = doom_wtohs(MUSh.ScoreStart); | ||
338 | MUSh.channels = doom_wtohs(MUSh.channels); | ||
339 | MUSh.SecChannels = doom_wtohs(MUSh.SecChannels); | ||
340 | MUSh.InstrCnt = doom_wtohs(MUSh.InstrCnt); | ||
341 | |||
342 | // check some things and set length of MUS buffer from internal data | ||
343 | |||
344 | if (!(muslen = MUSh.ScoreLength + MUSh.ScoreStart)) | ||
345 | return MUSDATAMT; // MUS file empty | ||
346 | |||
347 | if (MUSh.channels > 15) // MUSchannels + drum channel > 16 | ||
348 | return TOOMCHAN ; | ||
349 | |||
350 | musptr = mus+MUSh.ScoreStart; // init musptr to start of score | ||
351 | |||
352 | for (i = 0; i < MIDI_TRACKS; i++) // init the track structure's tracks | ||
353 | { | ||
354 | MUS2MIDchannel[i] = -1; // flag for channel not used yet | ||
355 | track[i].velocity = 64; | ||
356 | track[i].deltaT = 0; | ||
357 | track[i].lastEvt = 0; | ||
358 | //free(mididata->track[i].data);//jff 3/5/98 remove old allocations | ||
359 | mididata->track[i].data=NULL; | ||
360 | track[i].alloced = 0; | ||
361 | mididata->track[i].len = 0; | ||
362 | } | ||
363 | |||
364 | if (!division) | ||
365 | division = 70; | ||
366 | |||
367 | // allocate the first track which is a special tempo/key track | ||
368 | // note multiple tracks means midi format 1 | ||
369 | |||
370 | // set the divisions (ticks per quarter note) | ||
371 | mididata->divisions = division; | ||
372 | |||
373 | // allocate for midi tempo/key track, allow for end of track | ||
374 | if (!(mididata->track[0].data = | ||
375 | realloc(mididata->track[0].data,sizeof(midikey)+sizeof(miditempo)+4))) | ||
376 | return MEMALLOC; | ||
377 | |||
378 | // key C major | ||
379 | memcpy(mididata->track[0].data,midikey,sizeof(midikey)); | ||
380 | // tempo uS/qnote | ||
381 | memcpy(mididata->track[0].data+sizeof(midikey),miditempo,sizeof(miditempo)); | ||
382 | mididata->track[0].len = sizeof(midikey)+sizeof(miditempo); | ||
383 | |||
384 | TrackCnt++; // music tracks start at 1 | ||
385 | |||
386 | // process the MUS events in the MUS buffer | ||
387 | |||
388 | do | ||
389 | { | ||
390 | // get a mus event, decode its type and channel fields | ||
391 | |||
392 | event = *musptr++; | ||
393 | if ((evt = event_type(event)) == SCORE_END) //jff 1/23/98 use symbol | ||
394 | break; // if end of score event, leave | ||
395 | MUSchannel = channel(event); | ||
396 | |||
397 | // if this channel not initialized, do so | ||
398 | |||
399 | if (MUS2MIDchannel[MUSchannel] == -1) | ||
400 | { | ||
401 | // set MIDIchannel and MIDItrack | ||
402 | |||
403 | MIDIchannel = MUS2MIDchannel[MUSchannel] = | ||
404 | (MUSchannel == 15 ? 9 : FirstChannelAvailable(MUS2MIDchannel)); | ||
405 | MIDItrack = MIDIchan2track[MIDIchannel] = (UBYTE)TrackCnt++; | ||
406 | } | ||
407 | else // channel already allocated as a track, use those values | ||
408 | { | ||
409 | MIDIchannel = MUS2MIDchannel[MUSchannel]; | ||
410 | MIDItrack = MIDIchan2track[MIDIchannel]; | ||
411 | } | ||
412 | |||
413 | if (TWriteVarLen(mididata, MIDItrack, track[MIDItrack].deltaT)) | ||
414 | return MEMALLOC; | ||
415 | track[MIDItrack].deltaT = 0; | ||
416 | |||
417 | switch(evt) | ||
418 | { | ||
419 | case RELEASE_NOTE: | ||
420 | // killough 10/7/98: Fix noise problems by not allowing compression | ||
421 | if (!(NewEvent=MidiEvent(mididata,0x90,MIDIchannel,MIDItrack,1))) | ||
422 | return MEMALLOC; | ||
423 | |||
424 | data = *musptr++; | ||
425 | if (TWriteByte(mididata, MIDItrack, (UBYTE)(data & 0x7F))) | ||
426 | return MEMALLOC; | ||
427 | if (TWriteByte(mididata, MIDItrack, 0)) | ||
428 | return MEMALLOC; | ||
429 | break; | ||
430 | |||
431 | case PLAY_NOTE: | ||
432 | if (!(NewEvent=MidiEvent(mididata,0x90,MIDIchannel,MIDItrack,nocomp))) | ||
433 | return MEMALLOC; | ||
434 | |||
435 | data = *musptr++; | ||
436 | if (TWriteByte(mididata, MIDItrack, (UBYTE)(data & 0x7F))) | ||
437 | return MEMALLOC; | ||
438 | if( data & 0x80 ) | ||
439 | track[MIDItrack].velocity = (*musptr++) & 0x7f; | ||
440 | if (TWriteByte(mididata, MIDItrack, track[MIDItrack].velocity)) | ||
441 | return MEMALLOC; | ||
442 | break; | ||
443 | |||
444 | case BEND_NOTE: | ||
445 | if (!(NewEvent=MidiEvent(mididata,0xE0,MIDIchannel,MIDItrack,nocomp))) | ||
446 | return MEMALLOC; | ||
447 | |||
448 | data = *musptr++; | ||
449 | if (TWriteByte(mididata, MIDItrack, (UBYTE)((data & 1) << 6))) | ||
450 | return MEMALLOC; | ||
451 | if (TWriteByte(mididata, MIDItrack, (UBYTE)(data >> 1))) | ||
452 | return MEMALLOC; | ||
453 | break; | ||
454 | |||
455 | case SYS_EVENT: | ||
456 | if (!(NewEvent=MidiEvent(mididata,0xB0,MIDIchannel,MIDItrack,nocomp))) | ||
457 | return MEMALLOC; | ||
458 | |||
459 | data = *musptr++; | ||
460 | if (data<10 || data>14) | ||
461 | return BADSYSEVT; | ||
462 | |||
463 | if (TWriteByte(mididata, MIDItrack, MUS2MIDcontrol[data])) | ||
464 | return MEMALLOC; | ||
465 | if (data == 12) | ||
466 | { | ||
467 | if (TWriteByte(mididata, MIDItrack, (UBYTE)(MUSh.channels+1))) | ||
468 | return MEMALLOC; | ||
469 | } | ||
470 | else | ||
471 | if (TWriteByte(mididata, MIDItrack, 0)) | ||
472 | return MEMALLOC; | ||
473 | break; | ||
474 | |||
475 | case CNTL_CHANGE: | ||
476 | data = *musptr++; | ||
477 | if (data>9) | ||
478 | return BADCTLCHG; | ||
479 | |||
480 | if (data) | ||
481 | { | ||
482 | if (!(NewEvent=MidiEvent(mididata,0xB0,MIDIchannel,MIDItrack,nocomp))) | ||
483 | return MEMALLOC; | ||
484 | |||
485 | if (TWriteByte(mididata, MIDItrack, MUS2MIDcontrol[data])) | ||
486 | return MEMALLOC; | ||
487 | } | ||
488 | else | ||
489 | { | ||
490 | if (!(NewEvent=MidiEvent(mididata,0xC0,MIDIchannel,MIDItrack,nocomp))) | ||
491 | return MEMALLOC; | ||
492 | } | ||
493 | data = *musptr++; | ||
494 | if (TWriteByte(mididata, MIDItrack, (UBYTE)(data & 0x7F))) | ||
495 | return MEMALLOC; | ||
496 | break; | ||
497 | |||
498 | case UNKNOWN_EVENT1: // mus events 5 and 7 | ||
499 | case UNKNOWN_EVENT2: // meaning not known | ||
500 | return BADMUSCTL; | ||
501 | |||
502 | case SCORE_END: | ||
503 | break; | ||
504 | |||
505 | default: | ||
506 | return BADMUSCTL; // exit with error | ||
507 | } | ||
508 | if (last(event)) | ||
509 | { | ||
510 | ULONG DeltaTime = ReadTime(&musptr); // killough 10/7/98: make local | ||
511 | for (i = 0;i < MIDI_TRACKS; i++) //jff 3/13/98 update all tracks | ||
512 | track[i].deltaT += DeltaTime; //whether allocated yet or not | ||
513 | } | ||
514 | |||
515 | } | ||
516 | while ((evt != SCORE_END) && ((size_t)(musptr-mus) < muslen)); | ||
517 | |||
518 | if (evt!=SCORE_END) | ||
519 | return MUSDATACOR; | ||
520 | |||
521 | // Now add an end of track to each mididata track, correct allocation | ||
522 | |||
523 | for (i = 0; i < MIDI_TRACKS; i++) | ||
524 | if (mididata->track[i].len) | ||
525 | { // killough 10/7/98: simplify code | ||
526 | if (TWriteByte(mididata, i, 0x00) || // midi end of track code | ||
527 | TWriteByte(mididata, i, 0xFF) || | ||
528 | TWriteByte(mididata, i, 0x2F) || | ||
529 | TWriteByte(mididata, i, 0x00)) | ||
530 | return MEMALLOC; | ||
531 | |||
532 | // jff 1/23/98 fix failure to set data NULL, len 0 for unused tracks | ||
533 | // shorten allocation to proper length (important for Allegro) | ||
534 | if (!(mididata->track[i].data = | ||
535 | realloc(mididata->track[i].data,mididata->track[i].len))) | ||
536 | return MEMALLOC; | ||
537 | } | ||
538 | else | ||
539 | { | ||
540 | free(mididata->track[i].data); | ||
541 | mididata->track[i].data = NULL; | ||
542 | } | ||
543 | |||
544 | return 0; | ||
545 | } | ||
546 | |||
547 | void free_mididata(MIDI *mid) | ||
548 | { | ||
549 | int i; | ||
550 | |||
551 | for (i = 0; i < MIDI_TRACKS; i++) | ||
552 | if (mid->track[i].data) | ||
553 | free(mid->track[i].data); | ||
554 | } | ||
555 | |||
556 | // | ||
557 | // ReadLength() | ||
558 | // | ||
559 | // Reads the length of a chunk in a midi buffer, advancing the pointer | ||
560 | // 4 bytes, bigendian | ||
561 | // | ||
562 | // Passed a pointer to the pointer to a MIDI buffer | ||
563 | // Returns the chunk length at the pointer position | ||
564 | // | ||
565 | static size_t ReadLength(UBYTE **mid) | ||
566 | { | ||
567 | UBYTE *midptr = *mid; | ||
568 | |||
569 | size_t length = (*midptr++)<<24; | ||
570 | length += (*midptr++)<<16; | ||
571 | length += (*midptr++)<<8; | ||
572 | length += *midptr++; | ||
573 | *mid = midptr; | ||
574 | return length; | ||
575 | } | ||
576 | |||
577 | // | ||
578 | // MidiToMIDI() | ||
579 | // | ||
580 | // Convert an in-memory copy of a MIDI format 0 or 1 file to | ||
581 | // an Allegro MIDI structure, that is valid or has been zeroed | ||
582 | // | ||
583 | // Passed a pointer to a memory buffer with MIDI format music in it and a | ||
584 | // pointer to an Allegro MIDI structure. | ||
585 | // | ||
586 | // Returns 0 if successful, BADMIDHDR if the buffer is not MIDI format | ||
587 | // | ||
588 | int MidiToMIDI(UBYTE *mid,MIDI *mididata) | ||
589 | { | ||
590 | int i; | ||
591 | int ntracks; | ||
592 | |||
593 | // read the midi header | ||
594 | |||
595 | if (memcmp(mid,midihdr,4)) | ||
596 | return BADMIDHDR; | ||
597 | |||
598 | mididata->divisions = (mid[12]<<8)+mid[13]; | ||
599 | ntracks = (mid[10]<<8)+mid[11]; | ||
600 | |||
601 | if (ntracks>=MIDI_TRACKS) | ||
602 | return BADMIDHDR; | ||
603 | |||
604 | mid += 4; | ||
605 | { // killough 10/7/98: fix mid from being modified twice before sequence pt. | ||
606 | size_t t = ReadLength(&mid); // seek past header | ||
607 | mid += t; | ||
608 | } | ||
609 | |||
610 | // now read each track | ||
611 | |||
612 | for (i=0;i<ntracks;i++) | ||
613 | { | ||
614 | while (memcmp(mid,trackhdr,4)) // simply skip non-track data | ||
615 | { | ||
616 | mid += 4; | ||
617 | { | ||
618 | size_t t = ReadLength(&mid); // seek past header | ||
619 | mid += t; // killough 10/7/98: prevent mid undefined behavior | ||
620 | } | ||
621 | } | ||
622 | mid += 4; | ||
623 | mididata->track[i].len = ReadLength(&mid); // get length, move mid past it | ||
624 | |||
625 | // read a track | ||
626 | mididata->track[i].data = realloc(mididata->track[i].data,mididata->track[i].len); | ||
627 | memcpy(mididata->track[i].data,mid,mididata->track[i].len); | ||
628 | mid += mididata->track[i].len; | ||
629 | } | ||
630 | for (;i<MIDI_TRACKS;i++) | ||
631 | if (mididata->track[i].len) | ||
632 | { | ||
633 | free(mididata->track[i].data); | ||
634 | mididata->track[i].data = NULL; | ||
635 | mididata->track[i].len = 0; | ||
636 | } | ||
637 | return 0; | ||
638 | } | ||
639 | |||
640 | //#ifdef STANDALONE /* this code unused by BOOM provided for future portability */ | ||
641 | // /* it also provides a MUS to MID file converter*/ | ||
642 | // proff: I moved this down, because I need MIDItoMidi | ||
643 | |||
644 | static void FreeTracks(MIDI *mididata); | ||
645 | static void TWriteLength(UBYTE **midiptr,ULONG length); | ||
646 | |||
647 | // | ||
648 | // FreeTracks() | ||
649 | // | ||
650 | // Free all track allocations in the MIDI structure | ||
651 | // | ||
652 | // Passed a pointer to an Allegro MIDI structure | ||
653 | // Returns nothing | ||
654 | // | ||
655 | static void FreeTracks(MIDI *mididata) | ||
656 | { | ||
657 | int i; | ||
658 | |||
659 | for (i=0; i<MIDI_TRACKS; i++) | ||
660 | { | ||
661 | free(mididata->track[i].data); | ||
662 | mididata->track[i].data = NULL; | ||
663 | mididata->track[i].len = 0; | ||
664 | } | ||
665 | } | ||
666 | |||
667 | // | ||
668 | // TWriteLength() | ||
669 | // | ||
670 | // Write the length of a MIDI chunk to a midi buffer. The length is four | ||
671 | // bytes and is written byte-reversed for bigendian. The pointer to the | ||
672 | // midi buffer is advanced. | ||
673 | // | ||
674 | // Passed a pointer to the pointer to a midi buffer, and the length to write | ||
675 | // Returns nothing | ||
676 | // | ||
677 | static void TWriteLength(UBYTE **midiptr,ULONG length) | ||
678 | { | ||
679 | // proff: Added typecast to avoid warning | ||
680 | *(*midiptr)++ = (unsigned char)((length>>24)&0xff); | ||
681 | *(*midiptr)++ = (unsigned char)((length>>16)&0xff); | ||
682 | *(*midiptr)++ = (unsigned char)((length>>8)&0xff); | ||
683 | *(*midiptr)++ = (unsigned char)((length)&0xff); | ||
684 | } | ||
685 | |||
686 | // | ||
687 | // MIDIToMidi() | ||
688 | // | ||
689 | // This routine converts an Allegro MIDI structure to a midi 1 format file | ||
690 | // in memory. It is used to support memory MUS -> MIDI conversion | ||
691 | // | ||
692 | // Passed a pointer to an Allegro MIDI structure, a pointer to a pointer to | ||
693 | // a buffer containing midi data, and a pointer to a length return. | ||
694 | // Returns 0 if successful, MEMALLOC if a memory allocation error occurs | ||
695 | // | ||
696 | int MIDIToMidi(MIDI *mididata,UBYTE **mid,int *midlen) | ||
697 | { | ||
698 | size_t total; | ||
699 | int i,ntrks; | ||
700 | UBYTE *midiptr; | ||
701 | |||
702 | // calculate how long the mid buffer must be, and allocate | ||
703 | |||
704 | total = sizeof(midihdr); | ||
705 | for (i=0,ntrks=0;i<MIDI_TRACKS;i++) | ||
706 | if (mididata->track[i].len) | ||
707 | { | ||
708 | total += 8 + mididata->track[i].len; // Track hdr + track length | ||
709 | ntrks++; | ||
710 | } | ||
711 | if ((*mid = malloc(total))==NULL) | ||
712 | return MEMALLOC; | ||
713 | |||
714 | |||
715 | // fill in number of tracks and bigendian divisions (ticks/qnote) | ||
716 | |||
717 | midihdr[10] = 0; | ||
718 | midihdr[11] = (UBYTE)ntrks; // set number of tracks in header | ||
719 | midihdr[12] = (mididata->divisions>>8) & 0x7f; | ||
720 | midihdr[13] = (mididata->divisions) & 0xff; | ||
721 | |||
722 | // write the midi header | ||
723 | |||
724 | midiptr = *mid; | ||
725 | memcpy(midiptr,midihdr,sizeof(midihdr)); | ||
726 | midiptr += sizeof(midihdr); | ||
727 | |||
728 | // write the tracks | ||
729 | |||
730 | for (i=0;i<MIDI_TRACKS;i++) | ||
731 | { | ||
732 | if (mididata->track[i].len) | ||
733 | { | ||
734 | memcpy(midiptr,trackhdr,sizeof(trackhdr)); // header | ||
735 | midiptr += sizeof(trackhdr); | ||
736 | TWriteLength(&midiptr,mididata->track[i].len); // track length | ||
737 | // data | ||
738 | memcpy(midiptr,mididata->track[i].data,mididata->track[i].len); | ||
739 | midiptr += mididata->track[i].len; | ||
740 | } | ||
741 | } | ||
742 | |||
743 | // return length information | ||
744 | |||
745 | *midlen = midiptr - *mid; | ||
746 | |||
747 | return 0; | ||
748 | } | ||
749 | |||
750 | #ifdef STANDALONE /* this code unused by BOOM provided for future portability */ | ||
751 | /* it also provides a MUS to MID file converter*/ | ||
752 | // proff: I moved this down, because I need MIDItoMidi | ||
753 | |||
754 | // | ||
755 | // main() | ||
756 | // | ||
757 | // Main routine that will convert a globbed set of MUS files to the | ||
758 | // correspondingly named MID files using mmus2mid(). Only compiled | ||
759 | // if the STANDALONE symbol is defined. | ||
760 | // | ||
761 | // Passed the command line arguments, returns 0 if successful | ||
762 | // | ||
763 | int main(int argc,char **argv) | ||
764 | { | ||
765 | FILE *musst,*midst; | ||
766 | char musfile[FILENAME_MAX],midfile[FILENAME_MAX]; | ||
767 | MUSheader MUSh; | ||
768 | UBYTE *mus,*mid; | ||
769 | static MIDI mididata; | ||
770 | int err,midlen; | ||
771 | char *p,*q; | ||
772 | int i; | ||
773 | |||
774 | if (argc<2) | ||
775 | { | ||
776 | //jff 8/3/98 use logical output routine | ||
777 | lprintf(LO_INFO,"Usage: MMUS2MID musfile[.MUS]\n"); | ||
778 | lprintf(LO_INFO,"writes musfile.MID as output\n"); | ||
779 | lprintf(LO_INFO,"musfile may contain wildcards\n"); | ||
780 | exit(1); | ||
781 | } | ||
782 | |||
783 | for (i=1;i<argc;i++) | ||
784 | { | ||
785 | strcpy(musfile,argv[i]); | ||
786 | p = strrchr(musfile,'.'); | ||
787 | q = strrchr(musfile,'\\'); | ||
788 | if (p && (!q || q<p)) *p='\0'; | ||
789 | strcpy(midfile,musfile); | ||
790 | strcat(musfile,".MUS"); | ||
791 | strcat(midfile,".MID"); | ||
792 | |||
793 | musst = fopen(musfile,"rb"); | ||
794 | if (musst) | ||
795 | { | ||
796 | fread(&MUSh,sizeof(MUSheader),1,musst); | ||
797 | mus = malloc(MUSh.ScoreLength+MUSh.ScoreStart); | ||
798 | if (mus) | ||
799 | { | ||
800 | fseek(musst,0,SEEK_SET); | ||
801 | if (!fread(mus,MUSh.ScoreLength+MUSh.ScoreStart,1,musst)) | ||
802 | { | ||
803 | //jff 8/3/98 use logical output routine | ||
804 | lprintf(LO_FATAL,"Error reading MUS file\n"); | ||
805 | free(mus); | ||
806 | exit(1); | ||
807 | } | ||
808 | fclose(musst); | ||
809 | } | ||
810 | else | ||
811 | { | ||
812 | //jff 8/3/98 use logical output routine | ||
813 | lprintf(LO_FATAL,"Out of memory\n"); | ||
814 | free(mus); | ||
815 | exit(1); | ||
816 | } | ||
817 | |||
818 | err = mmus2mid(mus,&mididata,89,1); | ||
819 | if (err) | ||
820 | { | ||
821 | //jff 8/3/98 use logical output routine | ||
822 | lprintf(LO_FATAL,"Error converting MUS file to MIDI: %d\n",err); | ||
823 | exit(1); | ||
824 | } | ||
825 | free(mus); | ||
826 | |||
827 | MIDIToMidi(&mididata,&mid,&midlen); | ||
828 | |||
829 | midst = fopen(midfile,"wb"); | ||
830 | if (midst) | ||
831 | { | ||
832 | if (!fwrite(mid,midlen,1,midst)) | ||
833 | { | ||
834 | //jff 8/3/98 use logical output routine | ||
835 | lprintf(LO_FATAL,"Error writing MIDI file\n"); | ||
836 | FreeTracks(&mididata); | ||
837 | free(mid); | ||
838 | exit(1); | ||
839 | } | ||
840 | fclose(midst); | ||
841 | } | ||
842 | else | ||
843 | { | ||
844 | //jff 8/3/98 use logical output routine | ||
845 | lprintf(LO_FATAL,"Can't open MIDI file for output: %s\n", midfile); | ||
846 | FreeTracks(&mididata); | ||
847 | free(mid); | ||
848 | exit(1); | ||
849 | } | ||
850 | } | ||
851 | else | ||
852 | { | ||
853 | //jff 8/3/98 use logical output routine | ||
854 | lprintf(LO_FATAL,"Can't open MUS file for input: %s\n", midfile); | ||
855 | exit(1); | ||
856 | } | ||
857 | |||
858 | //jff 8/3/98 use logical output routine | ||
859 | lprintf(LO_CONFIRM,"MUS file %s converted to MIDI file %s\n",musfile,midfile); | ||
860 | FreeTracks(&mididata); | ||
861 | free(mid); | ||
862 | } | ||
863 | exit(0); | ||
864 | } | ||
865 | |||
866 | #endif | ||