shithub: choc

Download patch

ref: 87c062c1d4f7788620183f36b7239cdf28c4d2d1
parent: 6b9f3748cf561254f085fa83ada80cb5ba9b3946
author: Simon Howard <[email protected]>
date: Sat Jul 22 12:43:12 EDT 2006

New mus -> mid conversion code thanks to Ben Ryves <[email protected]>
This plays back a lot of music closer to Vanilla Doom - eg. tnt.wad map02

Subversion-branch: /trunk/chocolate-doom
Subversion-revision: 566

--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -23,6 +23,7 @@
 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_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          \
--- 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 @@
 #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 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);
+    instream = mem_fopen_read(musdata, len);
+    outstream = mem_fopen_write();
 
-    if (MIDIToMidi(mididata, &mid, &midlen))
-    {
-        // Error occurred
+    result = mus2mid(instream, outstream);
 
-        fprintf(stderr, "Error converting MUS lump.\n");
-
-        result = false;
-    }
-    else
+    if (result == 0)
     {
-        // Write midi data to disk
-       
-        M_WriteFile(filename, mid, midlen);
+        mem_get_buf(outstream, &outbuf, &outbuf_len);
 
-        // Clean up
-       
-        free(mid);
-        free_mididata(mididata);
-
-        result = true;
+        M_WriteFile(filename, outbuf, outbuf_len);
     }
 
-    Z_Free(mididata);
+    mem_fclose(instream);
+    mem_fclose(outstream);
 
     return result;
 }
--- /dev/null
+++ b/src/memio.c
@@ -1,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;
+	}
+}
+
+
--- /dev/null
+++ b/src/memio.h
@@ -1,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 */
+	  
--- a/src/mmus2mid.c
+++ /dev/null
@@ -1,878 +1,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
--- a/src/mmus2mid.h
+++ /dev/null
@@ -1,80 +1,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
--- /dev/null
+++ b/src/mus2mid.c
@@ -1,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 - [email protected]
+// 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;
+}
+
--- /dev/null
+++ b/src/mus2mid.h
@@ -1,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 - [email protected]
+// 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 */
+