aboutsummaryrefslogtreecommitdiff
path: root/src/mmus2mid.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mmus2mid.c')
-rw-r--r--src/mmus2mid.c866
1 files changed, 866 insertions, 0 deletions
diff --git a/src/mmus2mid.c b/src/mmus2mid.c
new file mode 100644
index 0000000..3669c36
--- /dev/null
+++ b/src/mmus2mid.c
@@ -0,0 +1,866 @@
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
79typedef 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
93typedef 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
105typedef 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
115static TrackInfo track[MIDI_TRACKS];
116
117// initial track size allocation
118#define TRACKBUFFERSIZE 1024
119
120// lookup table MUS -> MID controls
121static 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
142static UBYTE midikey[] =
143{0x00,0xff,0x59,0x02,0x00,0x00}; // C major
144static UBYTE miditempo[] =
145{0x00,0xff,0x51,0x03,0x09,0xa3,0x1a}; // uS/qnote
146static UBYTE midihdr[] =
147{'M','T','h','d',0,0,0,6,0,1,0,0,0,0}; // header (length 6, format 1)
148static UBYTE trackhdr[] =
149{'M','T','r','k'}; // track header
150
151// static routine prototypes
152
153static int TWriteByte(MIDI *mididata, int MIDItrack, UBYTE byte);
154static int TWriteVarLen(MIDI *mididata, int MIDItrack, register ULONG value);
155static ULONG ReadTime(const UBYTE **musptrp);
156static int FirstChannelAvailable(int MUS2MIDchannel[]);
157static 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//
172static 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//
206static 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//
241static 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
270static 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//
296static 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//
322int 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
547void 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//
565static 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//
588int 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
644static void FreeTracks(MIDI *mididata);
645static 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//
655static 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//
677static 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//
696int 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//
763int 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