1 //
2 // Copyright(C) 1993-1996 Id Software, Inc.
3 // Copyright(C) 2005-2014 Simon Howard
4 // Copyright(C) 2006 Ben Ryves 2006
5 //
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //
16 // mus2mid.c - Ben Ryves 2006 - http://benryves.com - benryves@benryves.com
17 // Use to convert a MUS file into a single track, type 0 MIDI file.
18 
19 #include <stdio.h>
20 
21 #include "doomtype.h"
22 #include "i_swap.h"
23 
24 #include "memio.h"
25 #include "mus2mid.h"
26 
27 #define NUM_CHANNELS 16
28 
29 #define MIDI_PERCUSSION_CHAN 9
30 #define MUS_PERCUSSION_CHAN 15
31 
32 // MUS event codes
33 typedef enum
34 {
35     mus_releasekey = 0x00,
36     mus_presskey = 0x10,
37     mus_pitchwheel = 0x20,
38     mus_systemevent = 0x30,
39     mus_changecontroller = 0x40,
40     mus_scoreend = 0x60
41 } musevent;
42 
43 // MIDI event codes
44 typedef enum
45 {
46     midi_releasekey = 0x80,
47     midi_presskey = 0x90,
48     midi_aftertouchkey = 0xA0,
49     midi_changecontroller = 0xB0,
50     midi_changepatch = 0xC0,
51     midi_aftertouchchannel = 0xD0,
52     midi_pitchwheel = 0xE0
53 } midievent;
54 
55 // Structure to hold MUS file header
56 typedef PACKED_STRUCT (
57 {
58     byte id[4];
59     unsigned short scorelength;
60     unsigned short scorestart;
61     unsigned short primarychannels;
62     unsigned short secondarychannels;
63     unsigned short instrumentcount;
64 }) musheader;
65 
66 // Standard MIDI type 0 header + track header
67 static const byte midiheader[] =
68 {
69     'M', 'T', 'h', 'd',     // Main header
70     0x00, 0x00, 0x00, 0x06, // Header size
71     0x00, 0x00,             // MIDI type (0)
72     0x00, 0x01,             // Number of tracks
73     0x00, 0x46,             // Resolution
74     'M', 'T', 'r', 'k',        // Start of track
75     0x00, 0x00, 0x00, 0x00  // Placeholder for track length
76 };
77 
78 // Cached channel velocities
79 static byte channelvelocities[] =
80 {
81     127, 127, 127, 127, 127, 127, 127, 127,
82     127, 127, 127, 127, 127, 127, 127, 127
83 };
84 
85 // Timestamps between sequences of MUS events
86 
87 static unsigned int queuedtime = 0;
88 
89 // Counter for the length of the track
90 
91 static unsigned int tracksize;
92 
93 static const byte controller_map[] =
94 {
95     0x00, 0x20, 0x01, 0x07, 0x0A, 0x0B, 0x5B, 0x5D,
96     0x40, 0x43, 0x78, 0x7B, 0x7E, 0x7F, 0x79
97 };
98 
99 static int channel_map[NUM_CHANNELS];
100 
101 // Write timestamp to a MIDI file.
102 
WriteTime(unsigned int time,MEMFILE * midioutput)103 static boolean WriteTime(unsigned int time, MEMFILE *midioutput)
104 {
105     unsigned int buffer = time & 0x7F;
106     byte writeval;
107 
108     while ((time >>= 7) != 0)
109     {
110         buffer <<= 8;
111         buffer |= ((time & 0x7F) | 0x80);
112     }
113 
114     for (;;)
115     {
116         writeval = (byte)(buffer & 0xFF);
117 
118         if (mem_fwrite(&writeval, 1, 1, midioutput) != 1)
119         {
120             return true;
121         }
122 
123         ++tracksize;
124 
125         if ((buffer & 0x80) != 0)
126         {
127             buffer >>= 8;
128         }
129         else
130         {
131             queuedtime = 0;
132             return false;
133         }
134     }
135 }
136 
137 
138 // Write the end of track marker
WriteEndTrack(MEMFILE * midioutput)139 static boolean WriteEndTrack(MEMFILE *midioutput)
140 {
141     byte endtrack[] = {0xFF, 0x2F, 0x00};
142 
143     if (WriteTime(queuedtime, midioutput))
144     {
145         return true;
146     }
147 
148     if (mem_fwrite(endtrack, 1, 3, midioutput) != 3)
149     {
150         return true;
151     }
152 
153     tracksize += 3;
154     return false;
155 }
156 
157 // Write a key press event
WritePressKey(byte channel,byte key,byte velocity,MEMFILE * midioutput)158 static boolean WritePressKey(byte channel, byte key,
159                              byte velocity, MEMFILE *midioutput)
160 {
161     byte working = midi_presskey | channel;
162 
163     if (WriteTime(queuedtime, midioutput))
164     {
165         return true;
166     }
167 
168     if (mem_fwrite(&working, 1, 1, midioutput) != 1)
169     {
170         return true;
171     }
172 
173     working = key & 0x7F;
174 
175     if (mem_fwrite(&working, 1, 1, midioutput) != 1)
176     {
177         return true;
178     }
179 
180     working = velocity & 0x7F;
181 
182     if (mem_fwrite(&working, 1, 1, midioutput) != 1)
183     {
184         return true;
185     }
186 
187     tracksize += 3;
188 
189     return false;
190 }
191 
192 // Write a key release event
WriteReleaseKey(byte channel,byte key,MEMFILE * midioutput)193 static boolean WriteReleaseKey(byte channel, byte key,
194                                MEMFILE *midioutput)
195 {
196     byte working = midi_releasekey | channel;
197 
198     if (WriteTime(queuedtime, midioutput))
199     {
200         return true;
201     }
202 
203     if (mem_fwrite(&working, 1, 1, midioutput) != 1)
204     {
205         return true;
206     }
207 
208     working = key & 0x7F;
209 
210     if (mem_fwrite(&working, 1, 1, midioutput) != 1)
211     {
212         return true;
213     }
214 
215     working = 0;
216 
217     if (mem_fwrite(&working, 1, 1, midioutput) != 1)
218     {
219         return true;
220     }
221 
222     tracksize += 3;
223 
224     return false;
225 }
226 
227 // Write a pitch wheel/bend event
WritePitchWheel(byte channel,short wheel,MEMFILE * midioutput)228 static boolean WritePitchWheel(byte channel, short wheel,
229                                MEMFILE *midioutput)
230 {
231     byte working = midi_pitchwheel | channel;
232 
233     if (WriteTime(queuedtime, midioutput))
234     {
235         return true;
236     }
237 
238     if (mem_fwrite(&working, 1, 1, midioutput) != 1)
239     {
240         return true;
241     }
242 
243     working = wheel & 0x7F;
244 
245     if (mem_fwrite(&working, 1, 1, midioutput) != 1)
246     {
247         return true;
248     }
249 
250     working = (wheel >> 7) & 0x7F;
251 
252     if (mem_fwrite(&working, 1, 1, midioutput) != 1)
253     {
254         return true;
255     }
256 
257     tracksize += 3;
258     return false;
259 }
260 
261 // Write a patch change event
WriteChangePatch(byte channel,byte patch,MEMFILE * midioutput)262 static boolean WriteChangePatch(byte channel, byte patch,
263                                 MEMFILE *midioutput)
264 {
265     byte working = midi_changepatch | channel;
266 
267     if (WriteTime(queuedtime, midioutput))
268     {
269         return true;
270     }
271 
272     if (mem_fwrite(&working, 1, 1, midioutput) != 1)
273     {
274         return true;
275     }
276 
277     working = patch & 0x7F;
278 
279     if (mem_fwrite(&working, 1, 1, midioutput) != 1)
280     {
281         return true;
282     }
283 
284     tracksize += 2;
285 
286     return false;
287 }
288 
289 // Write a valued controller change event
290 
WriteChangeController_Valued(byte channel,byte control,byte value,MEMFILE * midioutput)291 static boolean WriteChangeController_Valued(byte channel,
292                                             byte control,
293                                             byte value,
294                                             MEMFILE *midioutput)
295 {
296     byte working = midi_changecontroller | channel;
297 
298     if (WriteTime(queuedtime, midioutput))
299     {
300         return true;
301     }
302 
303     if (mem_fwrite(&working, 1, 1, midioutput) != 1)
304     {
305         return true;
306     }
307 
308     working = control & 0x7F;
309 
310     if (mem_fwrite(&working, 1, 1, midioutput) != 1)
311     {
312         return true;
313     }
314 
315     // Quirk in vanilla DOOM? MUS controller values should be
316     // 7-bit, not 8-bit.
317 
318     working = value;// & 0x7F;
319 
320     // Fix on said quirk to stop MIDI players from complaining that
321     // the value is out of range:
322 
323     if (working & 0x80)
324     {
325         working = 0x7F;
326     }
327 
328     if (mem_fwrite(&working, 1, 1, midioutput) != 1)
329     {
330         return true;
331     }
332 
333     tracksize += 3;
334 
335     return false;
336 }
337 
338 // Write a valueless controller change event
WriteChangeController_Valueless(byte channel,byte control,MEMFILE * midioutput)339 static boolean WriteChangeController_Valueless(byte channel,
340                                                byte control,
341                                                MEMFILE *midioutput)
342 {
343     return WriteChangeController_Valued(channel, control, 0,
344                                              midioutput);
345 }
346 
347 // Allocate a free MIDI channel.
348 
AllocateMIDIChannel(void)349 static int AllocateMIDIChannel(void)
350 {
351     int result;
352     int max;
353     int i;
354 
355     // Find the current highest-allocated channel.
356 
357     max = -1;
358 
359     for (i=0; i<NUM_CHANNELS; ++i)
360     {
361         if (channel_map[i] > max)
362         {
363             max = channel_map[i];
364         }
365     }
366 
367     // max is now equal to the highest-allocated MIDI channel.  We can
368     // now allocate the next available channel.  This also works if
369     // no channels are currently allocated (max=-1)
370 
371     result = max + 1;
372 
373     // Don't allocate the MIDI percussion channel!
374 
375     if (result == MIDI_PERCUSSION_CHAN)
376     {
377         ++result;
378     }
379 
380     return result;
381 }
382 
383 // Given a MUS channel number, get the MIDI channel number to use
384 // in the outputted file.
385 
GetMIDIChannel(int mus_channel,MEMFILE * midioutput)386 static int GetMIDIChannel(int mus_channel, MEMFILE *midioutput)
387 {
388     // Find the MIDI channel to use for this MUS channel.
389     // MUS channel 15 is the percusssion channel.
390 
391     if (mus_channel == MUS_PERCUSSION_CHAN)
392     {
393         return MIDI_PERCUSSION_CHAN;
394     }
395     else
396     {
397         // If a MIDI channel hasn't been allocated for this MUS channel
398         // yet, allocate the next free MIDI channel.
399 
400         if (channel_map[mus_channel] == -1)
401         {
402             channel_map[mus_channel] = AllocateMIDIChannel();
403 
404             // First time using the channel, send an "all notes off"
405             // event. This fixes "The D_DDTBLU disease" described here:
406             // https://www.doomworld.com/vb/source-ports/66802-the
407             WriteChangeController_Valueless(channel_map[mus_channel], 0x7b,
408                                             midioutput);
409         }
410 
411         return channel_map[mus_channel];
412     }
413 }
414 
ReadMusHeader(MEMFILE * file,musheader * header)415 static boolean ReadMusHeader(MEMFILE *file, musheader *header)
416 {
417     boolean result;
418 
419     result = mem_fread(&header->id, sizeof(byte), 4, file) == 4
420           && mem_fread(&header->scorelength, sizeof(short), 1, file) == 1
421           && mem_fread(&header->scorestart, sizeof(short), 1, file) == 1
422           && mem_fread(&header->primarychannels, sizeof(short), 1, file) == 1
423           && mem_fread(&header->secondarychannels, sizeof(short), 1, file) == 1
424           && mem_fread(&header->instrumentcount, sizeof(short), 1, file) == 1;
425 
426     if (result)
427     {
428         header->scorelength = SHORT(header->scorelength);
429         header->scorestart = SHORT(header->scorestart);
430         header->primarychannels = SHORT(header->primarychannels);
431         header->secondarychannels = SHORT(header->secondarychannels);
432         header->instrumentcount = SHORT(header->instrumentcount);
433     }
434 
435     return result;
436 }
437 
438 
439 // Read a MUS file from a stream (musinput) and output a MIDI file to
440 // a stream (midioutput).
441 //
442 // Returns 0 on success or 1 on failure.
443 
mus2mid(MEMFILE * musinput,MEMFILE * midioutput)444 boolean mus2mid(MEMFILE *musinput, MEMFILE *midioutput)
445 {
446     // Header for the MUS file
447     musheader musfileheader;
448 
449     // Descriptor for the current MUS event
450     byte eventdescriptor;
451     int channel; // Channel number
452     musevent event;
453 
454 
455     // Bunch of vars read from MUS lump
456     byte key;
457     byte controllernumber;
458     byte controllervalue;
459 
460     // Buffer used for MIDI track size record
461     byte tracksizebuffer[4];
462 
463     // Flag for when the score end marker is hit.
464     int hitscoreend = 0;
465 
466     // Temp working byte
467     byte working;
468     // Used in building up time delays
469     unsigned int timedelay;
470 
471     // Initialise channel map to mark all channels as unused.
472 
473     for (channel=0; channel<NUM_CHANNELS; ++channel)
474     {
475         channel_map[channel] = -1;
476     }
477 
478     // Grab the header
479 
480     if (!ReadMusHeader(musinput, &musfileheader))
481     {
482         return true;
483     }
484 
485 // [crispy] enable MUS format header check
486 #define CHECK_MUS_HEADER
487 #ifdef CHECK_MUS_HEADER
488     // Check MUS header
489     if (musfileheader.id[0] != 'M'
490      || musfileheader.id[1] != 'U'
491      || musfileheader.id[2] != 'S'
492      || musfileheader.id[3] != 0x1A)
493     {
494         return true;
495     }
496 #endif
497 
498     // Seek to where the data is held
499     if (mem_fseek(musinput, (long)musfileheader.scorestart,
500                   MEM_SEEK_SET) != 0)
501     {
502         return true;
503     }
504 
505     // So, we can assume the MUS file is faintly legit. Let's start
506     // writing MIDI data...
507 
508     mem_fwrite(midiheader, 1, sizeof(midiheader), midioutput);
509     tracksize = 0;
510 
511     // Now, process the MUS file:
512     while (!hitscoreend)
513     {
514         // Handle a block of events:
515 
516         while (!hitscoreend)
517         {
518             // Fetch channel number and event code:
519 
520             if (mem_fread(&eventdescriptor, 1, 1, musinput) != 1)
521             {
522                 return true;
523             }
524 
525             channel = GetMIDIChannel(eventdescriptor & 0x0F, midioutput);
526             event = eventdescriptor & 0x70;
527 
528             switch (event)
529             {
530                 case mus_releasekey:
531                     if (mem_fread(&key, 1, 1, musinput) != 1)
532                     {
533                         return true;
534                     }
535 
536                     if (WriteReleaseKey(channel, key, midioutput))
537                     {
538                         return true;
539                     }
540 
541                     break;
542 
543                 case mus_presskey:
544                     if (mem_fread(&key, 1, 1, musinput) != 1)
545                     {
546                         return true;
547                     }
548 
549                     if (key & 0x80)
550                     {
551                         if (mem_fread(&channelvelocities[channel], 1, 1, musinput) != 1)
552                         {
553                             return true;
554                         }
555 
556                         channelvelocities[channel] &= 0x7F;
557                     }
558 
559                     if (WritePressKey(channel, key,
560                                       channelvelocities[channel], midioutput))
561                     {
562                         return true;
563                     }
564 
565                     break;
566 
567                 case mus_pitchwheel:
568                     if (mem_fread(&key, 1, 1, musinput) != 1)
569                     {
570                         break;
571                     }
572                     if (WritePitchWheel(channel, (short)(key * 64), midioutput))
573                     {
574                         return true;
575                     }
576 
577                     break;
578 
579                 case mus_systemevent:
580                     if (mem_fread(&controllernumber, 1, 1, musinput) != 1)
581                     {
582                         return true;
583                     }
584                     if (controllernumber < 10 || controllernumber > 14)
585                     {
586                         return true;
587                     }
588 
589                     if (WriteChangeController_Valueless(channel,
590                                                         controller_map[controllernumber],
591                                                         midioutput))
592                     {
593                         return true;
594                     }
595 
596                     break;
597 
598                 case mus_changecontroller:
599                     if (mem_fread(&controllernumber, 1, 1, musinput) != 1)
600                     {
601                         return true;
602                     }
603 
604                     if (mem_fread(&controllervalue, 1, 1, musinput) != 1)
605                     {
606                         return true;
607                     }
608 
609                     if (controllernumber == 0)
610                     {
611                         if (WriteChangePatch(channel, controllervalue,
612                                              midioutput))
613                         {
614                             return true;
615                         }
616                     }
617                     else
618                     {
619                         if (controllernumber < 1 || controllernumber > 9)
620                         {
621                             return true;
622                         }
623 
624                         if (WriteChangeController_Valued(channel,
625                                                          controller_map[controllernumber],
626                                                          controllervalue,
627                                                          midioutput))
628                         {
629                             return true;
630                         }
631                     }
632 
633                     break;
634 
635                 case mus_scoreend:
636                     hitscoreend = 1;
637                     break;
638 
639                 default:
640                     return true;
641                     break;
642             }
643 
644             if (eventdescriptor & 0x80)
645             {
646                 break;
647             }
648         }
649         // Now we need to read the time code:
650         if (!hitscoreend)
651         {
652             timedelay = 0;
653             for (;;)
654             {
655                 if (mem_fread(&working, 1, 1, musinput) != 1)
656                 {
657                     return true;
658                 }
659 
660                 timedelay = timedelay * 128 + (working & 0x7F);
661                 if ((working & 0x80) == 0)
662                 {
663                     break;
664                 }
665             }
666             queuedtime += timedelay;
667         }
668     }
669 
670     // End of track
671     if (WriteEndTrack(midioutput))
672     {
673         return true;
674     }
675 
676     // Write the track size into the stream
677     if (mem_fseek(midioutput, 18, MEM_SEEK_SET))
678     {
679         return true;
680     }
681 
682     tracksizebuffer[0] = (tracksize >> 24) & 0xff;
683     tracksizebuffer[1] = (tracksize >> 16) & 0xff;
684     tracksizebuffer[2] = (tracksize >> 8) & 0xff;
685     tracksizebuffer[3] = tracksize & 0xff;
686 
687     if (mem_fwrite(tracksizebuffer, 1, 4, midioutput) != 4)
688     {
689         return true;
690     }
691 
692     return false;
693 }
694 
695 #ifdef STANDALONE
696 
697 #include "m_misc.h"
698 #include "z_zone.h"
699 
main(int argc,char * argv[])700 int main(int argc, char *argv[])
701 {
702     MEMFILE *src, *dst;
703     byte *infile;
704     long infile_len;
705     void *outfile;
706     size_t outfile_len;
707 
708     if (argc != 3)
709     {
710         printf("Usage: %s <musfile> <midfile>\n", argv[0]);
711         exit(-1);
712     }
713 
714     Z_Init();
715 
716     infile_len = M_ReadFile(argv[1], &infile);
717 
718     src = mem_fopen_read(infile, infile_len);
719     dst = mem_fopen_write();
720 
721     if (mus2mid(src, dst))
722     {
723         fprintf(stderr, "mus2mid() failed\n");
724         exit(-1);
725     }
726 
727     // Write result to output file:
728 
729     mem_get_buf(dst, &outfile, &outfile_len);
730 
731     M_WriteFile(argv[2], outfile, outfile_len);
732 
733     return 0;
734 }
735 
736 #endif
737 
738