diff options
author | Simon Howard | 2006-07-22 16:43:12 +0000 |
---|---|---|
committer | Simon Howard | 2006-07-22 16:43:12 +0000 |
commit | 87c062c1d4f7788620183f36b7239cdf28c4d2d1 (patch) | |
tree | 8c0981396399bc26b4ec8da010edb825af04f5db | |
parent | 6b9f3748cf561254f085fa83ada80cb5ba9b3946 (diff) | |
download | chocolate-doom-87c062c1d4f7788620183f36b7239cdf28c4d2d1.tar.gz chocolate-doom-87c062c1d4f7788620183f36b7239cdf28c4d2d1.tar.bz2 chocolate-doom-87c062c1d4f7788620183f36b7239cdf28c4d2d1.zip |
New mus -> mid conversion code thanks to Ben Ryves <benryves@benryves.com>
This plays back a lot of music closer to Vanilla Doom - eg. tnt.wad map02
Subversion-branch: /trunk/chocolate-doom
Subversion-revision: 566
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/i_sound.c | 47 | ||||
-rw-r--r-- | src/memio.c | 206 | ||||
-rw-r--r-- | src/memio.h | 47 | ||||
-rw-r--r-- | src/mmus2mid.c | 878 | ||||
-rw-r--r-- | src/mmus2mid.h | 80 | ||||
-rw-r--r-- | src/mus2mid.c | 599 | ||||
-rw-r--r-- | src/mus2mid.h | 37 |
8 files changed, 909 insertions, 989 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 01e2d77a..d5efe60b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -23,6 +23,7 @@ chocolate_server_SOURCES=$(DEDSERV_FILES) chocolate_server_LDADD = @LDFLAGS@ @SDL_LIBS@ @SDLNET_LIBS@ SOURCE_FILES=\ +sdlserver.c\ am_map.c am_map.h \ deh_ammo.c \ deh_cheat.c \ @@ -71,7 +72,8 @@ m_cheat.c m_cheat.h \ m_fixed.c m_fixed.h \ m_menu.c m_menu.h \ m_misc.c m_misc.h \ -mmus2mid.c mmus2mid.h \ +memio.c memio.h \ +mus2mid.c mus2mid.h \ m_random.c m_random.h \ m_swap.c m_swap.h \ net_client.c net_client.h \ diff --git a/src/i_sound.c b/src/i_sound.c index 0ef92b22..a2008862 100644 --- a/src/i_sound.c +++ b/src/i_sound.c @@ -1,7 +1,7 @@ // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // -// $Id: i_sound.c 473 2006-05-03 18:54:08Z fraggle $ +// $Id: i_sound.c 566 2006-07-22 16:43:12Z fraggle $ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005 Simon Howard @@ -128,7 +128,7 @@ //----------------------------------------------------------------------------- static const char -rcsid[] = "$Id: i_sound.c 473 2006-05-03 18:54:08Z fraggle $"; +rcsid[] = "$Id: i_sound.c 566 2006-07-22 16:43:12Z fraggle $"; #include <stdio.h> #include <stdlib.h> @@ -139,7 +139,8 @@ rcsid[] = "$Id: i_sound.c 473 2006-05-03 18:54:08Z fraggle $"; #include <unistd.h> #endif -#include "mmus2mid.h" +#include "memio.h" +#include "mus2mid.h" #include "z_zone.h" #include "i_system.h" @@ -687,40 +688,26 @@ static boolean IsMus(byte *mem, int len) static boolean ConvertMus(byte *musdata, int len, char *filename) { - MIDI *mididata; - UBYTE *mid; - int midlen; - boolean result; + MEMFILE *instream; + MEMFILE *outstream; + void *outbuf; + size_t outbuf_len; + int result; - // Convert from mus to midi - // Bits here came from PrBoom - - mididata = Z_Malloc(sizeof(MIDI), PU_STATIC, 0); - mmus2mid(musdata, mididata, 89, 0); - - if (MIDIToMidi(mididata, &mid, &midlen)) - { - // Error occurred + instream = mem_fopen_read(musdata, len); + outstream = mem_fopen_write(); - fprintf(stderr, "Error converting MUS lump.\n"); + result = mus2mid(instream, outstream); - result = false; - } - else + if (result == 0) { - // Write midi data to disk - - M_WriteFile(filename, mid, midlen); - - // Clean up - - free(mid); - free_mididata(mididata); + mem_get_buf(outstream, &outbuf, &outbuf_len); - result = true; + M_WriteFile(filename, outbuf, outbuf_len); } - Z_Free(mididata); + mem_fclose(instream); + mem_fclose(outstream); return result; } diff --git a/src/memio.c b/src/memio.c new file mode 100644 index 00000000..69d7b5fc --- /dev/null +++ b/src/memio.c @@ -0,0 +1,206 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id: w_wad.c 558 2006-06-16 17:06:05Z fraggle $ +// +// Copyright(C) 1993-1996 Id Software, Inc. +// Copyright(C) 2005 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// Emulates the IO functions in C stdio.h reading and writing to +// memory. +// + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "memio.h" + +#include "z_zone.h" + +typedef enum { + MODE_READ, + MODE_WRITE, +} memfile_mode_t; + +struct _MEMFILE { + unsigned char *buf; + size_t buflen; + size_t alloced; + int position; + memfile_mode_t mode; +}; + +// Open a memory area for reading + +MEMFILE *mem_fopen_read(void *buf, size_t buflen) +{ + MEMFILE *file; + + file = Z_Malloc(sizeof(MEMFILE), PU_STATIC, 0); + + file->buf = (unsigned char *) buf; + file->buflen = buflen; + file->position = 0; + file->mode = MODE_READ; + + return file; +} + +// Read bytes + +size_t mem_fread(void *buf, size_t size, size_t nmemb, MEMFILE *stream) +{ + size_t items; + + if (stream->mode != MODE_READ) + { + printf("not a read stream\n"); + return -1; + } + + // Trying to read more bytes than we have left? + + items = nmemb; + + if (items * size > stream->buflen - stream->position) + { + items = (stream->buflen - stream->position) / size; + } + + // Copy bytes to buffer + + memcpy(buf, stream->buf + stream->position, items * size); + + // Update position + + stream->position += items * size; + + return items; +} + +// Open a memory area for writing + +MEMFILE *mem_fopen_write(void) +{ + MEMFILE *file; + + file = Z_Malloc(sizeof(MEMFILE), PU_STATIC, 0); + + file->alloced = 1024; + file->buf = Z_Malloc(file->alloced, PU_STATIC, 0); + file->buflen = 0; + file->position = 0; + file->mode = MODE_WRITE; + + return file; +} + +// Write bytes to stream + +size_t mem_fwrite(const void *ptr, size_t size, size_t nmemb, MEMFILE *stream) +{ + size_t bytes; + + if (stream->mode != MODE_WRITE) + { + return -1; + } + + // More bytes than can fit in the buffer? + // If so, reallocate bigger. + + bytes = size * nmemb; + + while (bytes > stream->alloced - stream->position) + { + unsigned char *newbuf; + + newbuf = Z_Malloc(stream->alloced * 2, PU_STATIC, 0); + memcpy(newbuf, stream->buf, stream->alloced); + Z_Free(stream->buf); + stream->buf = newbuf; + stream->alloced *= 2; + } + + // Copy into buffer + + memcpy(stream->buf + stream->position, ptr, bytes); + stream->position += bytes; + + if (stream->position > stream->buflen) + stream->buflen = stream->position; + + return nmemb; +} + +void mem_get_buf(MEMFILE *stream, void **buf, size_t *buflen) +{ + *buf = stream->buf; + *buflen = stream->buflen; +} + +void mem_fclose(MEMFILE *stream) +{ + if (stream->mode == MODE_WRITE) + { + Z_Free(stream->buf); + } + + Z_Free(stream); +} + +long mem_ftell(MEMFILE *stream) +{ + return stream->position; +} + +int mem_fseek(MEMFILE *stream, signed long position, mem_rel_t whence) +{ + int newpos; + + switch (whence) + { + case MEM_SEEK_SET: + newpos = (int) position; + break; + + case MEM_SEEK_CUR: + newpos = (int) (stream->position + position); + break; + + case MEM_SEEK_END: + newpos = (int) (stream->buflen + position); + break; + default: + return -1; + } + + if (newpos >= 0 && newpos < stream->buflen) + { + stream->position = newpos; + return 0; + } + else + { + printf("Error seeking to %i\n", newpos); + return -1; + } +} + + diff --git a/src/memio.h b/src/memio.h new file mode 100644 index 00000000..552b7db7 --- /dev/null +++ b/src/memio.h @@ -0,0 +1,47 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id: w_wad.c 558 2006-06-16 17:06:05Z fraggle $ +// +// Copyright(C) 1993-1996 Id Software, Inc. +// Copyright(C) 2005 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// + +#ifndef MEMIO_H +#define MEMIO_H + +typedef struct _MEMFILE MEMFILE; + +typedef enum +{ + MEM_SEEK_SET, + MEM_SEEK_CUR, + MEM_SEEK_END, +} mem_rel_t; + +MEMFILE *mem_fopen_read(void *buf, size_t buflen); +size_t mem_fread(void *buf, size_t size, size_t nmemb, MEMFILE *stream); +MEMFILE *mem_fopen_write(void); +size_t mem_fwrite(const void *ptr, size_t size, size_t nmemb, MEMFILE *stream); +void mem_get_buf(MEMFILE *stream, void **buf, size_t *buflen); +void mem_fclose(MEMFILE *stream); +long mem_ftell(MEMFILE *stream); +int mem_fseek(MEMFILE *stream, signed long offset, mem_rel_t whence); + +#endif /* #ifndef MEMIO_H */ + diff --git a/src/mmus2mid.c b/src/mmus2mid.c deleted file mode 100644 index 2166984c..00000000 --- a/src/mmus2mid.c +++ /dev/null @@ -1,878 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id: mmus2mid.c 280 2006-01-10 22:14:13Z fraggle $ -// -// Copyright(C) 1993-1996 Id Software, Inc. -// Copyright (C) 1999 by -// id Software, Chi Hoang, Lee Killough, Jim Flynn, -// Rand Phares, Ty Halderman -// Copyright (C) 1999-2000 by -// Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze -// Copyright(C) 2005 Simon Howard -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA -// 02111-1307, USA. -// -// $Log$ -// Revision 1.4 2006/01/10 22:14:13 fraggle -// Shut up compiler warnings -// -// Revision 1.3 2006/01/05 02:48:03 fraggle -// Fixes for big endian machines (thanks locust) -// -// Revision 1.2 2005/09/06 21:06:45 fraggle -// Newer versions of mmus2mid.c,h from prboom -// -// Revision 1.1 2005/09/05 22:50:56 fraggle -// Add mmus2mid code from prboom. Use 'void *' for music handles. Pass -// length of data when registering music. -// -// -// DESCRIPTION: -// MUS to MID conversion utility from PrBoom. -// -// 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 -// - -#include <ctype.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <stdlib.h> -#ifdef MSDOS /* proff: I don't use allegro in windows */ -#include <allegro.h> -#endif /* !MSDOS */ -#include "mmus2mid.h" - -//#define STANDALONE /* uncomment this to make MMUS2MID.EXE */ -#ifndef STANDALONE -#include "m_swap.h" -#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(const 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 >= (ULONG)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, (UBYTE)(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(const 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(const UBYTE *mus, MIDI *mididata, UWORD division, int nocomp) -{ - UWORD TrackCnt = 0; - UBYTE evt, MUSchannel, MIDIchannel, MIDItrack=0, NewEvent; - int i, event, data; - const 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)); - MUSh.ScoreLength = SHORT(MUSh.ScoreLength); - MUSh.ScoreStart = SHORT(MUSh.ScoreStart); - MUSh.channels = SHORT(MUSh.channels); - MUSh.SecChannels = SHORT(MUSh.SecChannels); - MUSh.InstrCnt = SHORT(MUSh.InstrCnt); - - // 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] = (UBYTE)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, (UBYTE)(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, (UBYTE)(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, (UBYTE)((data & 1) << 6))) - return MEMALLOC; - if (TWriteByte(mididata, MIDItrack, (UBYTE)(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, (UBYTE)(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, (UBYTE)(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) && ((size_t)(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; -} - -void free_mididata(MIDI *mid) -{ - int i; - - for (i = 0; i < MIDI_TRACKS; i++) - if (mid->track[i].data) - free(mid->track[i].data); -} - -// -// 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*/ -// proff: I moved this down, because I need MIDItoMidi - -static void TWriteLength(UBYTE **midiptr,ULONG length); - -// -// 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) -{ -// proff: Added typecast to avoid warning - *(*midiptr)++ = (unsigned char)((length>>24)&0xff); - *(*midiptr)++ = (unsigned char)((length>>16)&0xff); - *(*midiptr)++ = (unsigned char)((length>>8)&0xff); - *(*midiptr)++ = (unsigned char)((length)&0xff); -} - -// -// MIDIToMidi() -// -// This routine converts an Allegro MIDI structure to a midi 1 format file -// in memory. It is used to support memory MUS -> MIDI conversion -// -// 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; -} - -#ifdef STANDALONE /* this code unused by BOOM provided for future portability */ - /* it also provides a MUS to MID file converter*/ -// proff: I moved this down, because I need MIDItoMidi - -// -// 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; - } -} - -// -// 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) - { - //jff 8/3/98 use logical output routine - lprintf(LO_INFO,"Usage: MMUS2MID musfile[.MUS]\n"); - lprintf(LO_INFO,"writes musfile.MID as output\n"); - lprintf(LO_INFO,"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)) - { - //jff 8/3/98 use logical output routine - lprintf(LO_FATAL,"Error reading MUS file\n"); - free(mus); - exit(1); - } - fclose(musst); - } - else - { - //jff 8/3/98 use logical output routine - lprintf(LO_FATAL,"Out of memory\n"); - free(mus); - exit(1); - } - - err = mmus2mid(mus,&mididata,89,1); - if (err) - { - //jff 8/3/98 use logical output routine - lprintf(LO_FATAL,"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)) - { - //jff 8/3/98 use logical output routine - lprintf(LO_FATAL,"Error writing MIDI file\n"); - FreeTracks(&mididata); - free(mid); - exit(1); - } - fclose(midst); - } - else - { - //jff 8/3/98 use logical output routine - lprintf(LO_FATAL,"Can't open MIDI file for output: %s\n", midfile); - FreeTracks(&mididata); - free(mid); - exit(1); - } - } - else - { - //jff 8/3/98 use logical output routine - lprintf(LO_FATAL,"Can't open MUS file for input: %s\n", midfile); - exit(1); - } - - //jff 8/3/98 use logical output routine - lprintf(LO_CONFIRM,"MUS file %s converted to MIDI file %s\n",musfile,midfile); - FreeTracks(&mididata); - free(mid); - } - exit(0); -} - -#endif diff --git a/src/mmus2mid.h b/src/mmus2mid.h deleted file mode 100644 index f57b25b4..00000000 --- a/src/mmus2mid.h +++ /dev/null @@ -1,80 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//-------------------------------------------------------------------------- -// -// $Id: mmus2mid.h 76 2005-09-06 21:06:45Z fraggle $ -// -// Copyright(C) 1993-1996 Id Software, Inc. -// Copyright (C) 1999 by -// id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman -// Copyright (C) 1999-2000 by -// Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze -// -// Copyright(C) 2005 Simon Howard -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA -// 02111-1307, USA. -// -// $Log$ -// Revision 1.2 2005/09/06 21:06:45 fraggle -// Newer versions of mmus2mid.c,h from prboom -// -// Revision 1.1 2005/09/05 22:50:56 fraggle -// Add mmus2mid code from prboom. Use 'void *' for music handles. Pass -// length of data when registering music. -// - -#if !defined( MMUS2MID_H ) -#define MMUS2MID_H - -// error codes - -typedef enum -{ - MUSDATACOR, // MUS data corrupt - TOOMCHAN, // Too many channels - MEMALLOC, // Memory allocation error - MUSDATAMT, // MUS file empty - BADMUSCTL, // MUS event 5 or 7 found - BADSYSEVT, // MUS system event not in 10-14 range - BADCTLCHG, // MUS control change larger than 9 - TRACKOVF, // MIDI track exceeds allocation - BADMIDHDR, // bad midi header detected -} error_code_t; - -// some names for integers of various sizes, all unsigned -typedef unsigned char UBYTE; // a one-byte int -typedef unsigned short UWORD; // a two-byte int -// proff: changed from unsigned int to unsigned long to avoid warning -typedef unsigned long ULONG; // a four-byte int (assumes int 4 bytes) - -#ifndef MSDOS /* proff: This is from allegro.h */ -#define MIDI_TRACKS 32 - -typedef struct MIDI /* a midi file */ -{ - int divisions; /* number of ticks per quarter note */ - struct { - unsigned char *data; /* MIDI message stream */ - int len; /* length of the track data */ - } track[MIDI_TRACKS]; -} MIDI; -#endif /* !MSDOS */ - -extern int mmus2mid(const UBYTE *mus,MIDI *mid, UWORD division, int nocomp); -extern void free_mididata(MIDI *mid); -extern int MIDIToMidi(MIDI *mididata,UBYTE **mid,int *midlen); -extern int MidiToMIDI(UBYTE *mid,MIDI *mididata); - -#endif diff --git a/src/mus2mid.c b/src/mus2mid.c new file mode 100644 index 00000000..ba05c4cf --- /dev/null +++ b/src/mus2mid.c @@ -0,0 +1,599 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id: z_zone.c 434 2006-03-24 19:55:04Z fraggle $ +// +// Copyright(C) 1993-1996 Id Software, Inc. +// Copyright(C) 2005 Simon Howard +// Copyright(C) 2006 Ben Ryves 2006 +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// mus2mid.c - Ben Ryves 2006 - http://benryves.com - benryves@benryves.com +// Use to convert a MUS file into a single track, type 0 MIDI file. + +#include <stdio.h> + +#include "memio.h" +#include "mus2mid.h" + +// MUS event codes +typedef enum +{ + mus_releasekey = 0x00, + mus_presskey = 0x10, + mus_pitchwheel = 0x20, + mus_systemevent = 0x30, + mus_changecontroller = 0x40, + mus_scoreend = 0x60 +} musevent; + +// MIDI event codes +typedef enum +{ + midi_releasekey = 0x80, + midi_presskey = 0x90, + midi_aftertouchkey = 0xA0, + midi_changecontroller = 0xB0, + midi_changepatch = 0xC0, + midi_aftertouchchannel = 0xD0, + midi_pitchwheel = 0xE0 +} midievent; + + +// Structure to hold MUS file header +typedef struct +{ + unsigned char id[4]; + unsigned short scorelength; + unsigned short scorestart; + unsigned short primarychannels; + unsigned short secondarychannels; + unsigned short instrumentcount; +} musheader; + + +// Standard MIDI type 0 header + track header +static unsigned char midiheader[] = +{ + 'M', 'T', 'h', 'd', // Main header + 0x00, 0x00, 0x00, 0x06, // Header size + 0x00, 0x00, // MIDI type (0) + 0x00, 0x01, // Number of tracks + 0x00, 0x46, // Resolution + 'M', 'T', 'r', 'k', // Start of track + 0x00, 0x00, 0x00, 0x00 // Placeholder for track length +}; + +// Cached channel velocities +static unsigned char channelvelocities[] = +{ + 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127 +}; + +// Timestamps between sequences of MUS events + +static unsigned int queuedtime = 0; + +// Counter for the length of the track + +static unsigned int tracksize = 0; + +static unsigned char mus2midi_translation[] = +{ + 0x00, 0x20, 0x01, 0x07, 0x0A, 0x0B, 0x5B, 0x5D, + 0x40, 0x43, 0x78, 0x7B, 0x7E, 0x7F, 0x79 +}; + +// Write timestamp to a MIDI file. + +static int midi_writetime(unsigned int time, MEMFILE *midioutput) +{ + unsigned int buffer = time & 0x7F; + unsigned char writeval; + + while ((time >>= 7) != 0) + { + buffer <<= 8; + buffer |= ((time & 0x7F) | 0x80); + } + + for (;;) + { + writeval = (unsigned char)(buffer & 0xFF); + + if (mem_fwrite(&writeval, 1, 1, midioutput) != 1) + { + return 1; + } + + ++tracksize; + + if ((buffer & 0x80) != 0) + { + buffer >>= 8; + } + else + { + queuedtime = 0; + return 0; + } + } +} + + +// Write the end of track marker +static int midi_writeendtrack(MEMFILE *midioutput) +{ + unsigned char endtrack[] = {0xFF, 0x2F, 0x00}; + + if (midi_writetime(queuedtime, midioutput)) + { + return 1; + } + + if (mem_fwrite(endtrack, 1, 3, midioutput) != 3) + { + return 1; + } + + tracksize += 3; + return 0; +} + +// Write a key press event +static int midi_writepresskey(unsigned char channel, unsigned char key, + unsigned char velocity, MEMFILE *midioutput) +{ + unsigned char working = midi_presskey | channel; + + if (midi_writetime(queuedtime, midioutput)) + { + return 1; + } + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return 1; + } + + working = key & 0x7F; + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return 1; + } + + working = velocity & 0x7F; + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return 1; + } + + tracksize += 3; + + return 0; +} + +// Write a key release event +static int midi_writereleasekey(unsigned char channel, unsigned char key, + MEMFILE *midioutput) +{ + unsigned char working = midi_releasekey | channel; + + if (midi_writetime(queuedtime, midioutput)) + { + return 1; + } + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return 1; + } + + working = key & 0x7F; + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return 1; + } + + working = 0; + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return 1; + } + + tracksize += 3; + + return 0; +} + +// Write a pitch wheel/bend event +static int midi_writepitchwheel(unsigned char channel, short wheel, + MEMFILE *midioutput) +{ + unsigned char working = midi_pitchwheel | channel; + + if (midi_writetime(queuedtime, midioutput)) + { + return 1; + } + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return 1; + } + + working = wheel & 0x7F; + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return 1; + } + + working = (wheel >> 7) & 0x7F; + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return 1; + } + + tracksize += 3; + return 0; +} + +// Write a patch change event +static int midi_writechangepatch(unsigned char channel, unsigned char patch, + MEMFILE *midioutput) +{ + unsigned char working = midi_changepatch | channel; + + if (midi_writetime(queuedtime, midioutput)) + { + return 1; + } + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return 1; + } + + working = patch & 0x7F; + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return 1; + } + + tracksize += 2; + + return 0; +} + + + +// Write a valued controller change event +static int midi_writechangecontroller_valued(unsigned char channel, + unsigned char control, + unsigned char value, + MEMFILE *midioutput) +{ + unsigned char working = midi_changecontroller | channel; + + if (midi_writetime(queuedtime, midioutput)) + { + return 1; + } + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return 1; + } + + working = control & 0x7F; + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return 1; + } + // Quirk in vanilla DOOM? MUS controller values should be + // 7-bit, not 8-bit. + + working = value;// & 0x7F; + + // Fix on said quirk to stop MIDI players from complaining that + // the value is out of range: + + if (working & 0x80) + { + working = 0x7F; + } + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return 1; + } + + tracksize += 3; + + return 0; +} + +// Write a valueless controller change event +static int midi_writechangecontroller_valueless(unsigned char channel, + unsigned char control, + MEMFILE *midioutput) +{ + return midi_writechangecontroller_valued(channel, control, 0, + midioutput); +} + +// Read a MUS file from a stream (musinput) and output a MIDI file to +// a stream (midioutput). +// +// Returns 0 on success or 1 on failure. + +int mus2mid(MEMFILE *musinput, MEMFILE *midioutput) +{ + // Header for the MUS file + musheader musfileheader; + + // Descriptor for the current MUS event + unsigned char eventdescriptor; + int channel; // Channel number + musevent event; + + + // Bunch of vars read from MUS lump + unsigned char key; + unsigned char controllernumber; + unsigned char controllervalue; + + // Buffer used for MIDI track size record + unsigned char tracksizebuffer[4]; + + // Flag for when the score end marker is hit. + int hitscoreend = 0; + + // Temp working byte + unsigned char working; + // Used in building up time delays + unsigned int timedelay; + + int i; + + // Grab the header + if (mem_fread(&musfileheader, sizeof(musheader), 1, musinput) != 1) + { + return 1; + } + + // Check MUS header + if (musfileheader.id[0] != 'M' + || musfileheader.id[1] != 'U' + || musfileheader.id[2] != 'S' + || musfileheader.id[3] != 0x1A) + { + return 1; + } + + // Seek to where the data is held + if (mem_fseek(musinput, (long)musfileheader.scorestart, SEEK_SET) != 0) + { + return 1; + } + + // So, we can assume the MUS file is faintly legit. Let's start + // writing MIDI data... + + mem_fwrite(midiheader, 1, 22, midioutput); + + // Now, process the MUS file: + while (!hitscoreend) + { + // Handle a block of events: + + while (!hitscoreend) + { + + // Fetch channel number and event code: + if (mem_fread(&eventdescriptor, 1, 1, musinput) != 1) + { + return 1; + } + + channel = eventdescriptor & 0x0F; + event = eventdescriptor & 0x70; + + // Swap channels 15 and 9. + // MIDI channel 9 = percussion. + // MUS channel 15 = percussion. + + if (channel == 15) + { + channel = 9; + } + else if (channel == 9) + { + channel = 15; + } + + switch (event) + { + + case mus_releasekey: + if (mem_fread(&key, 1, 1, musinput) != 1) + { + return 1; + } + + if (midi_writereleasekey(channel, key, midioutput)) + { + return 1; + } + + break; + + case mus_presskey: + if (mem_fread(&key, 1, 1, musinput) != 1) + { + return 1; + } + + if (key & 0x80) + { + if (mem_fread(&channelvelocities[channel], 1, 1, musinput) != 1) + { + return 1; + } + + channelvelocities[channel] &= 0x7F; + } + + if (midi_writepresskey(channel, key, channelvelocities[channel], midioutput)) + { + return 1; + } + + break; + + case mus_pitchwheel: + if (mem_fread(&key, 1, 1, musinput) != 1) + { + break; + } + if (midi_writepitchwheel(channel, (short)(key * 64), midioutput)) + { + return 1; + } + + break; + + case mus_systemevent: + if (mem_fread(&controllernumber, 1, 1, musinput) != 1) + { + return 1; + } + if (controllernumber < 10 || controllernumber > 14) + { + return 1; + } + + if (midi_writechangecontroller_valueless(channel, mus2midi_translation[controllernumber], midioutput)) + { + return 1; + } + + break; + + case mus_changecontroller: + if (mem_fread(&controllernumber, 1, 1, musinput) != 1) + { + return 1; + } + + if (mem_fread(&controllervalue, 1, 1, musinput) != 1) + { + return 1; + } + + if (controllernumber == 0) + { + if (midi_writechangepatch(channel, controllervalue, midioutput)) + { + return 1; + } + } + else + { + if (controllernumber < 1 || controllernumber > 9) + { + return 1; + } + + if (midi_writechangecontroller_valued(channel, mus2midi_translation[controllernumber], controllervalue, midioutput)) + { + return 1; + } + } + + break; + + case mus_scoreend: + hitscoreend = 1; + break; + + default: + return 1; + break; + } + + if (eventdescriptor & 0x80) + { + break; + } + } + // Now we need to read the time code: + if (!hitscoreend) + { + timedelay = 0; + for (;;) + { + if (mem_fread(&working, 1, 1, musinput) != 1) + { + return 1; + } + + timedelay = timedelay * 128 + (working & 0x7F); + if ((working & 0x80) == 0) + { + break; + } + } + queuedtime += timedelay; + } + } + + // End of track + if (midi_writeendtrack(midioutput)) + { + return 1; + } + + // Write the track size into the stream + if (mem_fseek(midioutput, 18, SEEK_SET)) + { + return 1; + } + + for (i = 0; i < 4; ++i) + { + tracksizebuffer[i] = (unsigned char)(tracksize >> 24); + tracksize <<= 8; + } + + if (mem_fwrite(tracksizebuffer, 1, 4, midioutput) != 4) + { + return 1; + } + + return 0; +} + diff --git a/src/mus2mid.h b/src/mus2mid.h new file mode 100644 index 00000000..cc0b7c80 --- /dev/null +++ b/src/mus2mid.h @@ -0,0 +1,37 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id: z_zone.c 434 2006-03-24 19:55:04Z fraggle $ +// +// Copyright(C) 1993-1996 Id Software, Inc. +// Copyright(C) 2005 Simon Howard +// Copyright(C) 2006 Ben Ryves 2006 +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// +// mus2mid.h - Ben Ryves 2006 - http://benryves.com - benryves@benryves.com +// Use to convert a MUS file into a single track, type 0 MIDI file. + +#ifndef MUS2MID_H +#define MUS2MID_H + +#include "memio.h" + +int mus2mid(MEMFILE *musinput, MEMFILE *midioutput); + +#endif /* #ifndef MUS2MID_H */ + |