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