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