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 //
TWriteByte(MIDI * mididata,int MIDItrack,UBYTE byte)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 //
TWriteVarLen(MIDI * mididata,int tracknum,register ULONG value)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 //
ReadTime(const UBYTE ** musptrp)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 
FirstChannelAvailable(int MUS2MIDchannel[])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 //
MidiEvent(MIDI * mididata,UBYTE midicode,UBYTE MIDIchannel,UBYTE MIDItrack,int nocomp)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 //
mmus2mid(const UBYTE * mus,MIDI * mididata,UWORD division,int nocomp)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 
free_mididata(MIDI * mid)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 //
ReadLength(UBYTE ** mid)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 //
MidiToMIDI(UBYTE * mid,MIDI * mididata)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 //
FreeTracks(MIDI * mididata)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 //
TWriteLength(UBYTE ** midiptr,ULONG length)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 //
MIDIToMidi(MIDI * mididata,UBYTE ** mid,int * midlen)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 //
main(int argc,char ** argv)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
867