1 /*
2    Quick MUS to Midi converter.
3    (C) 1995,96 Sebastien Bacquet  ( bacquet@iie.cnam.fr )
4 
5    Ported to unix by Hans Peter Verne ( hpv@kjemi.uio.no )
6 
7    This is free software, distributed under the terms of the
8    GNU General Public License. For details see the file COPYING.
9 
10    Use gcc to compile, if possible.  Please look in  "qmus2mid.h"
11    for system dependencies, in particular the int2 and int4 typedef's.
12 
13    To compile for MS-DOS, #define MSDOG or use -DMSDOG parameter
14 
15    Otherwise, vanilla unix is assumed, but it still compiles under dos.
16 
17    For the time being, this only works for little-endian machines,
18    such as i86, dec-mips, alpha;  but not rs6000, sparc....
19 
20    Ripped for the lsdldoom port by Sam Lantinga - Thanks! :)
21 */
22 
23 #include <ctype.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include "qmus2mid.h"
30 
31 int4 TRACKBUFFERSIZE = 65536L ;  /* 64 Ko */
32 
33 typedef struct {
34 	unsigned char *buf;
35 	int len;
36 } buflen_t;
37 
38 /* Read a 16-bit little-endian value */
read1(buflen_t * buflen)39 int read1(buflen_t *buflen)
40 {
41   int value;
42 
43   if ( buflen->len < 1 ) {
44     return(-1);
45   }
46   value = buflen->buf[0];
47   buflen->buf++;
48   buflen->len--;
49   return(value);
50 }
read2(int2 * intp,buflen_t * buflen)51 int read2(int2 *intp, buflen_t *buflen)
52 {
53   int i;
54 
55   if ( buflen->len < 2 ) {
56     return(0);
57   }
58   for ( i=0; i<2; ++i ) {
59     *intp <<= 8;
60     *intp |= buflen->buf[1-i];
61   }
62   buflen->buf += 2;
63   buflen->len -= 2;
64   return(1);
65 }
66 /* Read a 32-bit little-endian value */
read4(int4 * intp,buflen_t * buflen)67 int read4(int4 *intp, buflen_t *buflen)
68 {
69   int i;
70 
71   if ( buflen->len < 4 ) {
72     return(0);
73   }
74   for ( i=0; i<4; ++i ) {
75     *intp <<= 8;
76     *intp |= buflen->buf[3-i];
77   }
78   buflen->buf += 4;
79   buflen->len -= 4;
80   return(1);
81 }
82 
fwrite2(const int2 * ptr,size_t size,FILE * file)83 size_t fwrite2(const int2 *ptr, size_t size, FILE *file)
84 {
85   int4 rev = 0;
86   int i;
87 
88   for( i = 0 ; i < size ; i++ )
89     rev = (rev << 8) + (((*ptr) >> (i*8)) & 0xFF) ;
90 
91   return fwrite( &rev, size, 1, file ) ;
92 }
93 
94 
FreeTracks(struct Track track[])95 void FreeTracks( struct Track track[] )
96 {
97   int i ;
98 
99   for( i = 0 ; i < 16 ; i++ )
100     if( track[i].data )
101       free( track[i].data ) ;
102 }
103 
104 
TWriteByte(unsigned char MIDItrack,char byte,struct Track track[])105 void TWriteByte( unsigned char MIDItrack, char byte, struct Track track[] )
106 {
107   int4 pos ;
108 
109   pos = track[MIDItrack].current ;
110   if( pos < TRACKBUFFERSIZE )
111     track[MIDItrack].data[pos] = byte ;
112   else
113     {
114       printf("ERROR : Track buffer full.\n"
115              "Increase the track buffer size (option -size).\n" ) ;
116       FreeTracks( track ) ;
117       exit( EXIT_FAILURE ) ;
118     }
119   track[MIDItrack].current++ ;
120 }
121 
122 
TWriteVarLen(int tracknum,register int4 value,struct Track track[])123 void TWriteVarLen( int tracknum, register int4 value,
124                   struct Track track[] )
125 {
126   register int4 buffer ;
127 
128   buffer = value & 0x7f ;
129   while( (value >>= 7) )
130     {
131       buffer <<= 8 ;
132       buffer |= 0x80 ;
133       buffer += (value & 0x7f) ;
134     }
135   while( 1 )
136     {
137       TWriteByte( tracknum, buffer, track ) ;
138       if( buffer & 0x80 )
139         buffer >>= 8 ;
140       else
141         break;
142     }
143 }
144 
145 
ReadMUSheader(MUSheader * MUSh,buflen_t * buflen)146 int ReadMUSheader( MUSheader *MUSh, buflen_t *buflen )
147 {
148   if ( buflen->len < 4 ) {
149     return NOTMUSFILE ;
150   }
151   memcpy(MUSh->ID, buflen->buf, 4);
152   buflen->buf += 4;
153   buflen->len -= 4;
154   if( strncmp( MUSh->ID, MUSMAGIC, 4 ) )
155     return NOTMUSFILE ;
156   if( read2( &(MUSh->ScoreLength),  buflen ) != 1 ) return COMUSFILE ;
157   if( read2( &(MUSh->ScoreStart),  buflen ) != 1 ) return COMUSFILE ;
158   if( read2( &(MUSh->channels),  buflen ) != 1 ) return COMUSFILE ;
159   if( read2( &(MUSh->SecChannels),  buflen ) != 1 ) return COMUSFILE ;
160   if( read2( &(MUSh->InstrCnt),  buflen ) != 1 ) return COMUSFILE ;
161   if( read2( &(MUSh->dummy),  buflen ) != 1 ) return COMUSFILE ;
162 #if 0
163   MUSh->instruments = (int2 *) calloc(MUSh->InstrCnt, sizeof(int2)) ;
164   if( fread( MUSh->instruments, 2, MUSh->InstrCnt, file ) != MUSh->InstrCnt )
165     {
166       free( MUSh->instruments ) ;
167       return COMUSFILE ;
168     }
169   free( MUSh->instruments ) ; /* suppress this line if you want to display
170 				 instruments later */
171 #else
172   buflen->buf += 2*MUSh->InstrCnt;
173   buflen->len += 2*MUSh->InstrCnt;
174 #endif
175   return 0 ;
176 }
177 
178 
WriteMIDheader(int2 ntrks,int2 division,FILE * file)179 int WriteMIDheader( int2 ntrks, int2 division, FILE *file )
180 {
181   fwrite( MIDIMAGIC , 10, 1, file ) ;
182   fwrite2( &ntrks, 2, file) ;
183   fwrite2( &division, 2, file ) ;
184   return 0 ;
185 }
186 
187         /* maybe for ms-dog too ? */ /* Yes, why not ?... */
188 #define last(e)         ((unsigned char)(e & 0x80))
189 #define event_type(e)   ((unsigned char)((e & 0x7F) >> 4))
190 #define channel(e)      ((unsigned char)(e & 0x0F))
191 
TWriteString(char tracknum,const char * string,int length,struct Track track[])192 void TWriteString( char tracknum, const char *string, int length,
193                    struct Track track[] )
194 {
195   register int i ;
196 
197   for( i = 0 ; i < length ; i++ )
198     TWriteByte( tracknum, string[i], track ) ;
199 }
200 
201 
WriteTrack(int tracknum,FILE * file,struct Track track[])202 void WriteTrack( int tracknum, FILE *file, struct Track track[] )
203 {
204   int2 size ;
205   size_t quot, rem ;
206 
207   /* Do we risk overflow here ? */
208   size = track[tracknum].current+4 ;
209   fwrite( "MTrk", 4, 1, file ) ;
210   if( !tracknum ) size += 33 ;
211 
212   fwrite2( &size, 4, file ) ;
213   if( !tracknum)
214     fwrite( TRACKMAGIC1 "Quick MUS->MID ! by S.Bacquet", 33, 1, file ) ;
215   quot = (size_t) (track[tracknum].current / 4096) ;
216   rem = (size_t) (track[tracknum].current - quot*4096) ;
217   fwrite( track[tracknum].data, 4096, quot, file ) ;
218   fwrite( ((const unsigned char *) track[tracknum].data)+4096*quot, rem,
219                          1, file ) ;
220   fwrite( TRACKMAGIC2, 4, 1, file ) ;
221 }
222 
223 
WriteFirstTrack(FILE * file)224 void WriteFirstTrack( FILE *file )
225 {
226   int2 size ;
227 
228   size = 43 ;
229   fwrite( "MTrk", 4, 1, file ) ;
230   fwrite2( &size, 4, file ) ;
231   fwrite( TRACKMAGIC3 , 4, 1, file ) ;
232   fwrite( "QMUS2MID (C) S.Bacquet", 22, 1, file ) ;
233   fwrite( TRACKMAGIC4, 6, 1, file ) ;
234   fwrite( TRACKMAGIC5, 7, 1, file ) ;
235   fwrite( TRACKMAGIC6, 4, 1, file ) ;
236 }
237 
ReadTime(buflen_t * buflen)238 int4 ReadTime( buflen_t *buflen )
239 {
240   register int4 time = 0 ;
241   int byte ;
242 
243   do
244     {
245       byte = read1( buflen ) ;
246       if( byte != EOF ) time = (time << 7) + (byte & 0x7F) ;
247     } while( (byte != EOF) && (byte & 0x80) ) ;
248 
249   return time ;
250 }
251 
FirstChannelAvailable(signed char MUS2MIDchannel[])252 char FirstChannelAvailable( signed char MUS2MIDchannel[] )
253 {
254   int i ;
255   signed char old15 = MUS2MIDchannel[15], max = -1 ;
256 
257   MUS2MIDchannel[15] = -1 ;
258   for( i = 0 ; i < 16 ; i++ )
259     if( MUS2MIDchannel[i] > max ) max = MUS2MIDchannel[i] ;
260   MUS2MIDchannel[15] = old15 ;
261 
262   return (max == 8 ? 10 : max+1) ;
263 }
264 
265 
qmus2mid(void * mus,size_t len,FILE * file_mid,int nodisplay,int2 division,int BufferSize,int nocomp)266 int qmus2mid( void *mus, size_t len, FILE *file_mid,
267               int nodisplay, int2 division, int BufferSize, int nocomp )
268 {
269   struct Track track[16] ;
270   int2 TrackCnt = 0 ;
271   unsigned char et, MUSchannel, MIDIchannel, MIDItrack, NewEvent ;
272   int i, event, data, r ;
273   buflen_t buflen;
274   static MUSheader MUSh ;
275   int4 DeltaTime, TotalTime = 0, time, min, n = 0 ;
276   unsigned char MUS2MIDcontrol[15] = {
277     0,                          /* Program change - not a MIDI control change */
278     0x00,                       /* Bank select */
279     0x01,                       /* Modulation pot */
280     0x07,                       /* Volume */
281     0x0A,                       /* Pan pot */
282     0x0B,                       /* Expression pot */
283     0x5B,                       /* Reverb depth */
284     0x5D,                       /* Chorus depth */
285     0x40,                       /* Sustain pedal */
286     0x43,                       /* Soft pedal */
287     0x78,                       /* All sounds off */
288     0x7B,                       /* All notes off */
289     0x7E,                       /* Mono */
290     0x7F,                       /* Poly */
291     0x79                        /* Reset all controllers */
292   }, MIDIchan2track[16] ;
293   signed char MUS2MIDchannel[16] ;
294 #ifdef MSDOG
295   char drive[MAXDRIVE], dir[MAXDIR], name[MAXFILE], ext[MAXEXT] ;
296 #endif
297   char ouch = 0, sec ;
298 
299   /* Set up the MUS data source */
300   buflen.buf = (unsigned char *)mus;
301   buflen.len = len;
302 
303   r = ReadMUSheader( &MUSh, &buflen ) ;
304   if( r )
305     {
306       return r ;
307     }
308   buflen.buf = (unsigned char *)mus + MUSh.ScoreStart;
309   buflen.len = len - MUSh.ScoreStart;
310   if ( buflen.len <= 0 )
311     {
312       return MUSFILECOR ;
313     }
314   if( !nodisplay )
315     printf( "MUS chunk (%lu bytes) contains %d melodic channel%s.\n",
316            (unsigned long) len, MUSh.channels,
317            MUSh.channels >= 2 ? "s" : "" );
318   if( MUSh.channels > 15 )      /* <=> MUSchannels+drums > 16 */
319     {
320       return TOOMCHAN ;
321     }
322 
323   for( i = 0 ; i < 16 ; i++ )
324     {
325       MUS2MIDchannel[i] = -1 ;
326       track[i].current = 0 ;
327       track[i].vel = 64 ;
328       track[i].DeltaTime = 0 ;
329       track[i].LastEvent = 0 ;
330       track[i].data = NULL ;
331     }
332   if( BufferSize )
333     {
334       TRACKBUFFERSIZE = ((int4) BufferSize) << 10 ;
335       if( !nodisplay )
336         printf( "Track buffer size set to %d KB.\n", BufferSize ) ;
337     }
338 
339   if( !nodisplay )
340     {
341       printf( "Converting..." ) ;
342       fflush( stdout ) ;
343     }
344   event = read1( &buflen ) ;
345   et = event_type( event ) ;
346   MUSchannel = channel( event ) ;
347   while( (et != 6) && (buflen.len > 0) )
348     {
349       if( MUS2MIDchannel[MUSchannel] == -1 )
350         {
351           MIDIchannel = MUS2MIDchannel[MUSchannel ] =
352             (MUSchannel == 15 ? 9 : FirstChannelAvailable( MUS2MIDchannel)) ;
353           MIDItrack   = MIDIchan2track[MIDIchannel] = TrackCnt++ ;
354           if( !(track[MIDItrack].data = (char *) malloc( TRACKBUFFERSIZE )) )
355             {
356               FreeTracks( track ) ;
357               return MEMALLOC ;
358             }
359         }
360       else
361         {
362           MIDIchannel = MUS2MIDchannel[MUSchannel] ;
363           MIDItrack   = MIDIchan2track [MIDIchannel] ;
364         }
365       TWriteVarLen( MIDItrack, track[MIDItrack].DeltaTime, track ) ;
366       track[MIDItrack].DeltaTime = 0 ;
367       switch( et )
368         {
369         case 0 :                /* release note */
370           NewEvent = 0x90 | MIDIchannel ;
371           if( (NewEvent != track[MIDItrack].LastEvent) || nocomp )
372             {
373               TWriteByte( MIDItrack, NewEvent, track ) ;
374               track[MIDItrack].LastEvent = NewEvent ;
375             }
376           else
377             n++ ;
378           data = read1( &buflen ) ;
379           TWriteByte( MIDItrack, data, track ) ;
380           TWriteByte( MIDItrack, 0, track ) ;
381           break ;
382         case 1 :
383           NewEvent = 0x90 | MIDIchannel ;
384           if( (NewEvent != track[MIDItrack].LastEvent) || nocomp )
385             {
386               TWriteByte( MIDItrack, NewEvent, track ) ;
387               track[MIDItrack].LastEvent = NewEvent ;
388             }
389           else
390             n++ ;
391           data = read1( &buflen ) ;
392           TWriteByte( MIDItrack, data & 0x7F, track ) ;
393           if( data & 0x80 )
394             track[MIDItrack].vel = read1( &buflen ) ;
395           TWriteByte( MIDItrack, track[MIDItrack].vel, track ) ;
396           break ;
397         case 2 :
398           NewEvent = 0xE0 | MIDIchannel ;
399           if( (NewEvent != track[MIDItrack].LastEvent) || nocomp )
400             {
401               TWriteByte( MIDItrack, NewEvent, track ) ;
402               track[MIDItrack].LastEvent = NewEvent ;
403             }
404           else
405             n++ ;
406           data = read1( &buflen ) ;
407           TWriteByte( MIDItrack, (data & 1) << 6, track ) ;
408           TWriteByte( MIDItrack, data >> 1, track ) ;
409           break ;
410         case 3 :
411           NewEvent = 0xB0 | MIDIchannel ;
412           if( (NewEvent != track[MIDItrack].LastEvent) || nocomp )
413             {
414               TWriteByte( MIDItrack, NewEvent, track ) ;
415               track[MIDItrack].LastEvent = NewEvent ;
416             }
417           else
418             n++ ;
419           data = read1( &buflen ) ;
420           TWriteByte( MIDItrack, MUS2MIDcontrol[data], track ) ;
421           if( data == 12 )
422             TWriteByte( MIDItrack, MUSh.channels+1, track ) ;
423           else
424             TWriteByte( MIDItrack, 0, track ) ;
425           break ;
426         case 4 :
427           data = read1( &buflen ) ;
428           if( data )
429             {
430               NewEvent = 0xB0 | MIDIchannel ;
431               if( (NewEvent != track[MIDItrack].LastEvent) || nocomp )
432                 {
433                   TWriteByte( MIDItrack, NewEvent, track ) ;
434                   track[MIDItrack].LastEvent = NewEvent ;
435                 }
436               else
437                 n++ ;
438               TWriteByte( MIDItrack, MUS2MIDcontrol[data], track ) ;
439             }
440           else
441             {
442               NewEvent = 0xC0 | MIDIchannel ;
443               if( (NewEvent != track[MIDItrack].LastEvent) || nocomp )
444                 {
445                   TWriteByte( MIDItrack, NewEvent, track ) ;
446                   track[MIDItrack].LastEvent = NewEvent ;
447                 }
448               else
449                 n++ ;
450             }
451           data = read1( &buflen ) ;
452           TWriteByte( MIDItrack, data, track ) ;
453           break ;
454         case 5 :
455         case 7 :
456           FreeTracks( track ) ;
457           return MUSFILECOR ;
458         default : break ;
459         }
460       if( last( event ) )
461 	{
462           DeltaTime = ReadTime( &buflen ) ;
463           TotalTime += DeltaTime ;
464 	  for( i = 0 ; i < (int) TrackCnt ; i++ )
465 	    track[i].DeltaTime += DeltaTime ;
466         }
467       event = read1( &buflen ) ;
468       if( event != EOF )
469                   {
470           et = event_type( event ) ;
471           MUSchannel = channel( event ) ;
472         }
473       else
474         ouch = 1 ;
475     }
476   if( !nodisplay ) printf( "done !\n" ) ;
477   if( ouch )
478     printf( "WARNING : There are bytes missing at the end of MUS.\n          "
479            "The end of the MIDI file might not fit the original one.\n") ;
480   if( !division )
481     division = 89 ;
482   else
483     if( !nodisplay ) printf( "Ticks per quarter note set to %d.\n", division ) ;
484   if( !nodisplay )
485     {
486       if( division != 89 )
487         {
488           time = TotalTime / 140 ;
489           min = time / 60 ;
490           sec = (char) (time - min*60) ;
491           printf( "Playing time of the MUS file : %u'%.2u''.\n", min, sec ) ;
492         }
493       time = (TotalTime * 89) / (140 * division) ;
494       min = time / 60 ;
495       sec = (char) (time - min*60) ;
496       if( division != 89 )
497         printf( "                    MID file" ) ;
498       else
499         printf( "Playing time" ) ;
500       printf( " : %u'%.2u''.\n", min, sec ) ;
501     }
502   if( !nodisplay )
503     {
504       printf( "Writing..." ) ;
505       fflush( stdout ) ;
506     }
507   WriteMIDheader( TrackCnt+1, division, file_mid ) ;
508   WriteFirstTrack( file_mid ) ;
509   for( i = 0 ; i < (int) TrackCnt ; i++ )
510     WriteTrack( i, file_mid, track ) ;
511   if( !nodisplay )
512     printf( "done !\n" ) ;
513   if( !nodisplay && !nocomp )
514     printf( "Compression : %u%%.\n",
515            (100 * n) / (n+ (int4) ftell( file_mid )) ) ;
516 
517   FreeTracks( track ) ;
518 
519   return 0 ;
520 }
521 
522 
523