From e577335bd97d2ca99e070e24ac871ad9d8c1ae48 Mon Sep 17 00:00:00 2001
From: hendricks266 <hendricks266@1a8010ca-5511-0410-912e-c29ae57300e0>
Date: Mon, 7 Mar 2016 11:22:07 +0000
Subject: [PATCH] Revert r5601 in its entirety. This fixes MIDI playback on
 Windows.

git-svn-id: https://svn.eduke32.com/eduke32@5650 1a8010ca-5511-0410-912e-c29ae57300e0
---
 polymer/eduke32/source/_midi.h                |  243 +++-
 .../eduke32/source/jaudiolib/include/music.h  |   87 +-
 polymer/eduke32/source/midi.c                 | 1277 +++++++++++++++--
 polymer/eduke32/source/midi.h                 |  106 +-
 polymer/eduke32/source/mpu401.c               |  325 ++++-
 polymer/eduke32/source/mpu401.h               |   56 +-
 polymer/eduke32/source/music.c                |  512 ++++++-
 polymer/eduke32/source/sdlmusic.c             |  202 ++-
 polymer/eduke32/source/sounds.c               |    2 +-
 9 files changed, 2463 insertions(+), 347 deletions(-)

diff --git a/polymer/eduke32/source/_midi.h b/polymer/eduke32/source/_midi.h
index d7ead6b36..164d1be21 100644
--- a/polymer/eduke32/source/_midi.h
+++ b/polymer/eduke32/source/_midi.h
@@ -1,12 +1,10 @@
-//-------------------------------------------------------------------------
 /*
-Copyright (C) 2016 EDuke32 developers and contributors
+Copyright (C) 1994-1995 Apogee Software, Ltd.
 
-This file is part of EDuke32.
-
-EDuke32 is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License version 2
-as published by the Free Software Foundation.
+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
@@ -17,14 +15,27 @@ 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au)
+
 */
-//-------------------------------------------------------------------------
+/**********************************************************************
+   module: _MIDI.H
+
+   author: James R. Dose
+   date:   May 25, 1994
+
+   Private header for MIDI.C.  Midi song file playback routines.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
 
 #ifndef ___MIDI_H
 #define ___MIDI_H
 #include "compat.h"
 
-#define RELATIVE_BEAT(measure, beat, tick) ((tick) + ((beat) << 9) + ((measure) << 16))
+#define RELATIVE_BEAT( measure, beat, tick ) \
+   ( ( tick ) + ( ( beat ) << 9 ) + ( ( measure ) << 16 ) )
 
 //Bobby Prince thinks this may be 100
 //#define GENMIDI_DefaultVolume 100
@@ -69,12 +80,12 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 #define MIDI_MONO_MODE_ON          0x7E
 #define MIDI_SYSTEM_RESET          0xFF
 
-#define GET_NEXT_EVENT(track, data)                                                                                    \
-    (data) = *(track)->pos;                                                                                            \
-    (track)->pos += 1
+#define GET_NEXT_EVENT( track, data ) \
+   ( data ) = *( track )->pos; \
+   ( track )->pos += 1
 
-#define GET_MIDI_CHANNEL(event) ((event)&0xf)
-#define GET_MIDI_COMMAND(event) ((event) >> 4)
+#define GET_MIDI_CHANNEL( event )       ( ( event ) & 0xf )
+#define GET_MIDI_COMMAND( event )       ( ( event ) >> 4 )
 
 #define EMIDI_INFINITE          -1
 #define EMIDI_END_LOOP_VALUE    127
@@ -92,54 +103,180 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 #define EMIDI_GeneralMIDI       0
 
-#define EMIDI_AffectsCurrentCard(c, type) (((c) == EMIDI_ALL_CARDS) || ((c) == (type)))
+#define EMIDI_AffectsCurrentCard( c, type ) \
+   ( ( ( c ) == EMIDI_ALL_CARDS ) || ( ( c ) == ( type ) ) )
+
 
 #define EMIDI_NUM_CONTEXTS      7
 typedef struct
-{
-    char *pos;
-    char *loopstart;
-    int16_t loopcount;
-    int16_t RunningStatus;
-    unsigned time;
-    int32_t FPSecondsPerTick;
-    int16_t tick;
-    int16_t beat;
-    int16_t measure;
-    int16_t BeatsPerMeasure;
-    int16_t TicksPerBeat;
-    int16_t TimeBase;
-    int32_t delay;
-    int16_t active;
-} songcontext;
+   {
+   char *pos;
+   char *loopstart;
+   int16_t          loopcount;
+   int16_t          RunningStatus;
+   unsigned       time;
+   int32_t           FPSecondsPerTick;
+   int16_t          tick;
+   int16_t          beat;
+   int16_t          measure;
+   int16_t          BeatsPerMeasure;
+   int16_t          TicksPerBeat;
+   int16_t          TimeBase;
+   int32_t           delay;
+   int16_t          active;
+   } songcontext;
 
 typedef struct
-{
-    char *start;
-    char *pos;
+   {
+   char *start;
+   char *pos;
 
-    int32_t delay;
-    int16_t active;
-    int16_t RunningStatus;
+   int32_t           delay;
+   int16_t          active;
+   int16_t          RunningStatus;
 
-    int16_t currentcontext;
-    songcontext context[EMIDI_NUM_CONTEXTS];
+   int16_t          currentcontext;
+   songcontext    context[ EMIDI_NUM_CONTEXTS ];
 
-    char EMIDI_IncludeTrack;
-    char EMIDI_ProgramChange;
-    char EMIDI_VolumeChange;
-} track;
+   char           EMIDI_IncludeTrack;
+   char           EMIDI_ProgramChange;
+   char           EMIDI_VolumeChange;
+   } track;
 
-static int32_t  _MIDI_ReadNumber( void *from, size_t size );
-static int32_t  _MIDI_ReadDelta( track *ptr );
-static void     _MIDI_ResetTracks( void );
-static void     _MIDI_AdvanceTick( void );
-static void     _MIDI_MetaEvent( track *Track );
-static void     _MIDI_SysEx( track *Track );
-static int32_t  _MIDI_InterpretControllerInfo(track *Track, int32_t TimeSet, int32_t channel, int32_t c1, int32_t c2);
+static int32_t _MIDI_ReadNumber( void *from, size_t size );
+static int32_t _MIDI_ReadDelta( track *ptr );
+static void _MIDI_ResetTracks( void );
+static void _MIDI_AdvanceTick( void );
+static void _MIDI_MetaEvent( track *Track );
+static void _MIDI_SysEx( track *Track );
+static int32_t  _MIDI_InterpretControllerInfo( track *Track, int32_t TimeSet,
+   int32_t channel, int32_t c1, int32_t c2 );
 static int32_t  _MIDI_SendControlChange( int32_t channel, int32_t c1, int32_t c2 );
-static void     _MIDI_SetChannelVolume( int32_t channel, int32_t volume );
-static void     _MIDI_SendChannelVolumes( void );
-static void     _MIDI_InitEMIDI( void );
+static void _MIDI_SetChannelVolume( int32_t channel, int32_t volume );
+static void _MIDI_SendChannelVolumes( void );
+static int32_t  _MIDI_ProcessNextTick( void );
+static void _MIDI_InitEMIDI( void );
+
+/*
+               if ( c1 == EMIDI_LOOP_START )
+                  {
+                  if ( c2 == 0 )
+                     {
+                     Track->context[ 0 ].loopcount = EMIDI_INFINITE;
+                     }
+                  else
+                     {
+                     Track->context[ 0 ].loopcount = c2;
+                     }
+
+                  Track->context[ 0 ].pos              = Track->pos;
+                  Track->context[ 0 ].loopstart        = Track->pos;
+                  Track->context[ 0 ].RunningStatus    = Track->RunningStatus;
+                  Track->context[ 0 ].time             = _MIDI_Time;
+                  Track->context[ 0 ].FPSecondsPerTick = _MIDI_FPSecondsPerTick;
+                  Track->context[ 0 ].tick             = _MIDI_Tick;
+                  Track->context[ 0 ].beat             = _MIDI_Beat;
+                  Track->context[ 0 ].measure          = _MIDI_Measure;
+                  Track->context[ 0 ].BeatsPerMeasure  = _MIDI_BeatsPerMeasure;
+                  Track->context[ 0 ].TicksPerBeat     = _MIDI_TicksPerBeat;
+                  Track->context[ 0 ].TimeBase         = _MIDI_TimeBase;
+                  break;
+                  }
+
+               if ( ( c1 == EMIDI_LOOP_END ) &&
+                  ( c2 == EMIDI_END_LOOP_VALUE ) )
+                  {
+                  if ( ( Track->context[ 0 ].loopstart != NULL ) &&
+                     ( Track->context[ 0 ].loopcount != 0 ) )
+                     {
+                     if ( Track->context[ 0 ].loopcount != EMIDI_INFINITE )
+                        {
+                        Track->context[ 0 ].loopcount--;
+                        }
+
+                     Track->pos           = Track->context[ 0 ].loopstart;
+                     Track->RunningStatus = Track->context[ 0 ].RunningStatus;
+
+                     if ( !TimeSet )
+                        {
+                        _MIDI_Time             = Track->context[ 0 ].time;
+                        _MIDI_FPSecondsPerTick = Track->context[ 0 ].FPSecondsPerTick;
+                        _MIDI_Tick             = Track->context[ 0 ].tick;
+                        _MIDI_Beat             = Track->context[ 0 ].beat;
+                        _MIDI_Measure          = Track->context[ 0 ].measure;
+                        _MIDI_BeatsPerMeasure  = Track->context[ 0 ].BeatsPerMeasure;
+                        _MIDI_TicksPerBeat     = Track->context[ 0 ].TicksPerBeat;
+                        _MIDI_TimeBase         = Track->context[ 0 ].TimeBase;
+                        TimeSet = TRUE;
+                        }
+                     }
+                  break;
+                  }
+
+               if ( c1 == MIDI_MONO_MODE_ON )
+                  {
+                  Track->pos++;
+                  }
+
+               if ( ( c1 == MIDI_VOLUME ) && ( !Track->EMIDI_VolumeChange ) )
+                  {
+                  _MIDI_SetChannelVolume( channel, c2 );
+                  break;
+                  }
+               else if ( ( c1 == EMIDI_VOLUME_CHANGE ) &&
+                  ( Track->EMIDI_VolumeChange ) )
+                  {
+                  _MIDI_SetChannelVolume( channel, c2 );
+                  break;
+                  }
+
+               if ( ( c1 == EMIDI_PROGRAM_CHANGE ) &&
+                  ( Track->EMIDI_ProgramChange ) )
+                  {
+                  _MIDI_Funcs->ProgramChange( channel, MIDI_PatchMap[ c2 & 0x7f ] );
+                  break;
+                  }
+
+               if ( c1 == EMIDI_CONTEXT_START )
+                  {
+                  break;
+                  }
+
+               if ( c1 == EMIDI_CONTEXT_END )
+                  {
+                  if ( ( Track->currentcontext != _MIDI_Context ) ||
+                     ( Track->context[ _MIDI_Context ].pos == NULL )
+                     {
+                     break;
+                     }
+
+                  Track->currentcontext = _MIDI_Context;
+                  Track->context[ 0 ].loopstart = Track->context[ _MIDI_Context ].loopstart;
+                  Track->context[ 0 ].loopcount = Track->context[ _MIDI_Context ].loopcount;
+                  Track->pos           = Track->context[ _MIDI_Context ].pos;
+                  Track->RunningStatus = Track->context[ _MIDI_Context ].RunningStatus;
+
+                  if ( TimeSet )
+                     {
+                     break;
+                     }
+
+                  _MIDI_Time             = Track->context[ _MIDI_Context ].time;
+                  _MIDI_FPSecondsPerTick = Track->context[ _MIDI_Context ].FPSecondsPerTick;
+                  _MIDI_Tick             = Track->context[ _MIDI_Context ].tick;
+                  _MIDI_Beat             = Track->context[ _MIDI_Context ].beat;
+                  _MIDI_Measure          = Track->context[ _MIDI_Context ].measure;
+                  _MIDI_BeatsPerMeasure  = Track->context[ _MIDI_Context ].BeatsPerMeasure;
+                  _MIDI_TicksPerBeat     = Track->context[ _MIDI_Context ].TicksPerBeat;
+                  _MIDI_TimeBase         = Track->context[ _MIDI_Context ].TimeBase;
+                  TimeSet = TRUE;
+                  break;
+                  }
+
+               if ( _MIDI_Funcs->ControlChange )
+                  {
+                  _MIDI_Funcs->ControlChange( channel, c1, c2 );
+                  }
+ */
 
 #endif
diff --git a/polymer/eduke32/source/jaudiolib/include/music.h b/polymer/eduke32/source/jaudiolib/include/music.h
index 219b7ee54..8671185c1 100644
--- a/polymer/eduke32/source/jaudiolib/include/music.h
+++ b/polymer/eduke32/source/jaudiolib/include/music.h
@@ -1,12 +1,10 @@
-//-------------------------------------------------------------------------
 /*
-Copyright (C) 2016 EDuke32 developers and contributors
+Copyright (C) 1994-1995 Apogee Software, Ltd.
 
-This file is part of EDuke32.
-
-EDuke32 is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License version 2
-as published by the Free Software Foundation.
+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
@@ -17,8 +15,19 @@ 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au)
 */
-//-------------------------------------------------------------------------
+/**********************************************************************
+   module: MUSIC.H
+
+   author: James R. Dose
+   date:   March 25, 1994
+
+   Public header for MUSIC.C
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
 
 #ifndef __MUSIC_H
 #define __MUSIC_H
@@ -28,24 +37,56 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 extern int32_t MUSIC_ErrorCode;
 
 enum MUSIC_ERRORS
-{
-    MUSIC_Warning = -2,
-    MUSIC_Error = -1,
-    MUSIC_Ok = 0,
-    MUSIC_MidiError,
-};
+   {
+   MUSIC_Warning = -2,
+   MUSIC_Error   = -1,
+   MUSIC_Ok      = 0,
+   MUSIC_ASSVersion,
+   MUSIC_SoundCardError,
+   MUSIC_MPU401Error,
+   MUSIC_InvalidCard,
+   MUSIC_MidiError,
+   MUSIC_TaskManError,
+   MUSIC_DPMI_Error
+   };
+
+typedef struct
+   {
+   uint32_t tickposition;
+   uint32_t milliseconds;
+   uint32_t  measure;
+   uint32_t  beat;
+   uint32_t  tick;
+   } songposition;
 
 #define MUSIC_LoopSong ( 1 == 1 )
 #define MUSIC_PlayOnce ( !MUSIC_LoopSong )
 
-const char *MUSIC_ErrorString(int32_t ErrorNumber);
-int32_t     MUSIC_Init(int32_t SoundCard);
-int32_t     MUSIC_Shutdown(void);
-void        MUSIC_SetVolume(int32_t volume);
-void        MUSIC_Continue(void);
-void        MUSIC_Pause(void);
-int32_t     MUSIC_StopSong(void);
-int32_t     MUSIC_PlaySong(char *song, int32_t loopflag);
-void        MUSIC_Update(void);
+const char *MUSIC_ErrorString( int32_t ErrorNumber );
+int32_t   MUSIC_Init( int32_t SoundCard, int32_t Address );
+int32_t   MUSIC_Shutdown( void );
+void  MUSIC_SetVolume( int32_t volume );
+void  MUSIC_SetMidiChannelVolume( int32_t channel, int32_t volume );
+void  MUSIC_ResetMidiChannelVolumes( void );
+int32_t   MUSIC_GetVolume( void );
+void  MUSIC_SetLoopFlag( int32_t loopflag );
+int32_t   MUSIC_SongPlaying( void );
+void  MUSIC_Continue( void );
+void  MUSIC_Pause( void );
+int32_t   MUSIC_StopSong( void );
+int32_t   MUSIC_PlaySong( char *song, int32_t loopflag );
+void  MUSIC_SetContext( int32_t context );
+int32_t   MUSIC_GetContext( void );
+void  MUSIC_SetSongTick( uint32_t PositionInTicks );
+void  MUSIC_SetSongTime( uint32_t milliseconds );
+void  MUSIC_SetSongPosition( int32_t measure, int32_t beat, int32_t tick );
+void  MUSIC_GetSongPosition( songposition *pos );
+void  MUSIC_GetSongLength( songposition *pos );
+int32_t   MUSIC_FadeVolume( int32_t tovolume, int32_t milliseconds );
+int32_t   MUSIC_FadeActive( void );
+void  MUSIC_StopFade( void );
+void  MUSIC_RerouteMidiChannel( int32_t channel, int32_t ( *function )( int32_t, int32_t, int32_t ) );
+void  MUSIC_RegisterTimbreBank( char *timbres );
+void  MUSIC_Update(void);
 
 #endif
diff --git a/polymer/eduke32/source/midi.c b/polymer/eduke32/source/midi.c
index afaaea966..06a5f00fa 100644
--- a/polymer/eduke32/source/midi.c
+++ b/polymer/eduke32/source/midi.c
@@ -1,6 +1,6 @@
 //-------------------------------------------------------------------------
 /*
-Copyright (C) 2016 EDuke32 developers and contributors
+Copyright (C) 2010 EDuke32 developers and contributors
 
 This file is part of EDuke32.
 
@@ -20,6 +20,17 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
 //-------------------------------------------------------------------------
 
+/**********************************************************************
+   module: MIDI.C
+
+   author: James R. Dose
+   date:   May 25, 1994
+
+   Midi song file playback routines.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
 #include <stdlib.h>
 #include <string.h>
 #include "standard.h"
@@ -40,6 +51,8 @@ static const int32_t _MIDI_CommandLengths[ NUM_MIDI_CHANNELS ] =
     0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 2, 0
 };
 
+static int32_t (*_MIDI_RerouteFunctions[ NUM_MIDI_CHANNELS ])(int32_t, int32_t, int32_t);
+
 static track *_MIDI_TrackPtr = NULL;
 static int32_t    _MIDI_TrackMemSize;
 static int32_t    _MIDI_NumTracks;
@@ -71,6 +84,11 @@ static int32_t _MIDI_ActiveTracks;
 static int32_t _MIDI_TotalVolume = MIDI_MaxVolume;
 
 static int32_t _MIDI_ChannelVolume[ NUM_MIDI_CHANNELS ];
+static int32_t _MIDI_UserChannelVolume[ NUM_MIDI_CHANNELS ] =
+{
+    256, 256, 256, 256, 256, 256, 256, 256,
+    256, 256, 256, 256, 256, 256, 256, 256
+};
 
 static midifuncs *_MIDI_Funcs = NULL;
 
@@ -94,22 +112,31 @@ char MIDI_PatchMap[ 128 ];
    Reads a variable length number from a MIDI track.
 ---------------------------------------------------------------------*/
 
-static int32_t _MIDI_ReadNumber(void *from, size_t size)
+static int32_t _MIDI_ReadNumber
+(
+    void *from,
+    size_t size
+)
+
 {
+    char *FromPtr;
+    int32_t          value;
+
     if (size > 4)
+    {
         size = 4;
+    }
 
-    char *FromPtr = (char *)from;
+    FromPtr = (char *)from;
 
-    int32_t value = 0;
-
-    do
+    value = 0;
+    while (size--)
     {
         value <<= 8;
         value += *FromPtr++;
-    } while (size--);
+    }
 
-    return value;
+    return(value);
 }
 
 
@@ -119,17 +146,20 @@ static int32_t _MIDI_ReadNumber(void *from, size_t size)
    Reads a variable length encoded delta delay time from the MIDI data.
 ---------------------------------------------------------------------*/
 
-static int32_t _MIDI_ReadDelta(track *ptr)
+static int32_t _MIDI_ReadDelta
+(
+    track *ptr
+)
+
 {
     int32_t          value;
+    char c;
 
     GET_NEXT_EVENT(ptr, value);
 
     if (value & 0x80)
     {
         value &= 0x7f;
-
-        char c;
         do
         {
             GET_NEXT_EVENT(ptr, c);
@@ -138,7 +168,7 @@ static int32_t _MIDI_ReadDelta(track *ptr)
         while (c & 0x80);
     }
 
-    return value;
+    return(value);
 }
 
 
@@ -148,8 +178,15 @@ static int32_t _MIDI_ReadDelta(track *ptr)
    Sets the track pointers to the beginning of the song.
 ---------------------------------------------------------------------*/
 
-static void _MIDI_ResetTracks(void)
+static void _MIDI_ResetTracks
+(
+    void
+)
+
 {
+    int32_t    i;
+    track *ptr;
+
     _MIDI_Tick = 0;
     _MIDI_Beat = 1;
     _MIDI_Measure = 1;
@@ -163,9 +200,8 @@ static void _MIDI_ResetTracks(void)
     _MIDI_ActiveTracks    = 0;
     _MIDI_Context         = 0;
 
-    track *ptr = _MIDI_TrackPtr;
-
-    for (int i = 0; i < _MIDI_NumTracks; i++)
+    ptr = _MIDI_TrackPtr;
+    for (i = 0; i < _MIDI_NumTracks; i++)
     {
         ptr->pos                    = ptr->start;
         ptr->delay                  = _MIDI_ReadDelta(ptr);
@@ -176,7 +212,9 @@ static void _MIDI_ResetTracks(void)
         ptr->context[ 0 ].loopcount = 0;
 
         if (ptr->active)
+        {
             _MIDI_ActiveTracks++;
+        }
 
         ptr++;
     }
@@ -189,17 +227,21 @@ static void _MIDI_ResetTracks(void)
    Increment tick counters.
 ---------------------------------------------------------------------*/
 
-static void _MIDI_AdvanceTick(void)
+static void _MIDI_AdvanceTick
+(
+    void
+)
+
 {
     _MIDI_PositionInTicks++;
     _MIDI_Time += _MIDI_FPSecondsPerTick;
 
-    while (++_MIDI_Tick > _MIDI_TicksPerBeat)
+    _MIDI_Tick++;
+    while (_MIDI_Tick > _MIDI_TicksPerBeat)
     {
         _MIDI_Tick -= _MIDI_TicksPerBeat;
         _MIDI_Beat++;
     }
-
     while (_MIDI_Beat > _MIDI_BeatsPerMeasure)
     {
         _MIDI_Beat -= _MIDI_BeatsPerMeasure;
@@ -214,9 +256,15 @@ static void _MIDI_AdvanceTick(void)
    Interpret SysEx Event.
 ---------------------------------------------------------------------*/
 
-static void _MIDI_SysEx(track *Track)
+static void _MIDI_SysEx
+(
+    track *Track
+)
+
 {
-    int32_t const length = _MIDI_ReadDelta(Track);
+    int32_t length;
+
+    length = _MIDI_ReadDelta(Track);
     Track->pos += length;
 }
 
@@ -227,10 +275,16 @@ static void _MIDI_SysEx(track *Track)
    Interpret Meta Event.
 ---------------------------------------------------------------------*/
 
-static void _MIDI_MetaEvent(track *Track)
+static void _MIDI_MetaEvent
+(
+    track *Track
+)
+
 {
     int32_t   command;
     int32_t   length;
+    int32_t   denominator;
+    int32_t  tempo;
 
     GET_NEXT_EVENT(Track, command);
     GET_NEXT_EVENT(Track, length);
@@ -244,22 +298,22 @@ static void _MIDI_MetaEvent(track *Track)
         break;
 
     case MIDI_TEMPO_CHANGE :
-        MIDI_SetTempo(
-            tabledivide32_noinline(60000000L, _MIDI_ReadNumber(Track->pos, 3)));
+        tempo = tabledivide32_noinline(60000000L, _MIDI_ReadNumber(Track->pos, 3));
+        MIDI_SetTempo(tempo);
         break;
 
     case MIDI_TIME_SIGNATURE :
-    {
         if ((_MIDI_Tick > 0) || (_MIDI_Beat > 1))
+        {
             _MIDI_Measure++;
+        }
 
         _MIDI_Tick = 0;
         _MIDI_Beat = 1;
 
-        _MIDI_BeatsPerMeasure = (int32_t) *Track->pos;
-        int32_t denominator = (int32_t) *(Track->pos + 1);
+        _MIDI_BeatsPerMeasure = (int32_t)*Track->pos;
+        denominator = (int32_t)*(Track->pos + 1);
         _MIDI_TimeBase = 1;
-
         while (denominator > 0)
         {
             _MIDI_TimeBase += _MIDI_TimeBase;
@@ -268,7 +322,6 @@ static void _MIDI_MetaEvent(track *Track)
         _MIDI_TicksPerBeat = tabledivide32_noinline(_MIDI_Division * 4, _MIDI_TimeBase);
         break;
     }
-    }
 
     Track->pos += length;
 }
@@ -280,7 +333,15 @@ static void _MIDI_MetaEvent(track *Track)
    Interprets the MIDI controller info.
 ---------------------------------------------------------------------*/
 
-static int32_t _MIDI_InterpretControllerInfo(track *Track, int32_t TimeSet, int32_t channel, int32_t c1, int32_t c2)
+static int32_t _MIDI_InterpretControllerInfo
+(
+    track *Track,
+    int32_t   TimeSet,
+    int32_t   channel,
+    int32_t   c1,
+    int32_t   c2
+)
+
 {
     track *trackptr;
     int32_t tracknum;
@@ -294,7 +355,9 @@ static int32_t _MIDI_InterpretControllerInfo(track *Track, int32_t TimeSet, int3
 
     case MIDI_VOLUME :
         if (!Track->EMIDI_VolumeChange)
+        {
             _MIDI_SetChannelVolume(channel, c2);
+        }
         break;
 
     case EMIDI_INCLUDE_TRACK :
@@ -303,21 +366,28 @@ static int32_t _MIDI_InterpretControllerInfo(track *Track, int32_t TimeSet, int3
 
     case EMIDI_PROGRAM_CHANGE :
         if (Track->EMIDI_ProgramChange)
+        {
             _MIDI_Funcs->ProgramChange(channel, MIDI_PatchMap[ c2 & 0x7f ]);
+        }
         break;
 
     case EMIDI_VOLUME_CHANGE :
         if (Track->EMIDI_VolumeChange)
+        {
             _MIDI_SetChannelVolume(channel, c2);
+        }
         break;
 
     case EMIDI_CONTEXT_START :
         break;
 
     case EMIDI_CONTEXT_END :
-        if ((Track->currentcontext == _MIDI_Context) || (_MIDI_Context < 0) ||
-            (Track->context[_MIDI_Context].pos == NULL))
+        if ((Track->currentcontext == _MIDI_Context) ||
+                (_MIDI_Context < 0) ||
+                (Track->context[ _MIDI_Context ].pos == NULL))
+        {
             break;
+        }
 
         Track->currentcontext = _MIDI_Context;
         Track->context[ 0 ].loopstart = Track->context[ _MIDI_Context ].loopstart;
@@ -326,7 +396,9 @@ static int32_t _MIDI_InterpretControllerInfo(track *Track, int32_t TimeSet, int3
         Track->RunningStatus = Track->context[ _MIDI_Context ].RunningStatus;
 
         if (TimeSet)
+        {
             break;
+        }
 
         _MIDI_Time             = Track->context[ _MIDI_Context ].time;
         _MIDI_FPSecondsPerTick = Track->context[ _MIDI_Context ].FPSecondsPerTick;
@@ -341,7 +413,14 @@ static int32_t _MIDI_InterpretControllerInfo(track *Track, int32_t TimeSet, int3
 
     case EMIDI_LOOP_START :
     case EMIDI_SONG_LOOP_START :
-        loopcount = c2 ? c2 : EMIDI_INFINITE;
+        if (c2 == 0)
+        {
+            loopcount = EMIDI_INFINITE;
+        }
+        else
+        {
+            loopcount = c2;
+        }
 
         if (c1 == EMIDI_SONG_LOOP_START)
         {
@@ -377,8 +456,12 @@ static int32_t _MIDI_InterpretControllerInfo(track *Track, int32_t TimeSet, int3
 
     case EMIDI_LOOP_END :
     case EMIDI_SONG_LOOP_END :
-        if ((c2 != EMIDI_END_LOOP_VALUE) || (Track->context[0].loopstart == NULL) || (Track->context[0].loopcount == 0))
+        if ((c2 != EMIDI_END_LOOP_VALUE) ||
+                (Track->context[ 0 ].loopstart == NULL) ||
+                (Track->context[ 0 ].loopcount == 0))
+        {
             break;
+        }
 
         if (c1 == EMIDI_SONG_LOOP_END)
         {
@@ -396,15 +479,18 @@ static int32_t _MIDI_InterpretControllerInfo(track *Track, int32_t TimeSet, int3
         while (tracknum > 0)
         {
             if (trackptr->context[ 0 ].loopcount != EMIDI_INFINITE)
+            {
                 trackptr->context[ 0 ].loopcount--;
+            }
 
             trackptr->pos           = trackptr->context[ 0 ].loopstart;
             trackptr->RunningStatus = trackptr->context[ 0 ].RunningStatus;
             trackptr->delay         = trackptr->context[ 0 ].delay;
             trackptr->active        = trackptr->context[ 0 ].active;
-
             if (trackptr->active)
+            {
                 _MIDI_ActiveTracks++;
+            }
 
             if (!TimeSet)
             {
@@ -426,7 +512,9 @@ static int32_t _MIDI_InterpretControllerInfo(track *Track, int32_t TimeSet, int3
 
     default :
         if (_MIDI_Funcs->ControlChange)
+        {
             _MIDI_Funcs->ControlChange(channel, c1, c2);
+        }
     }
 
     return TimeSet;
@@ -441,18 +529,24 @@ static int32_t _MIDI_InterpretControllerInfo(track *Track, int32_t TimeSet, int3
 
 static int32_t _MIDI_ServiceRoutine(void)
 {
+    int32_t   event;
+    int32_t   channel;
+    int32_t   command;
+    track *Track;
+    int32_t   tracknum;
+    int32_t   status;
+    int32_t   c1 = 0;
+    int32_t   c2 = 0;
+    int32_t   TimeSet = FALSE;
+
     if (_MIDI_SongActive)
     {
-        int32_t TimeSet = FALSE;
-        track   *Track = _MIDI_TrackPtr;
-        int32_t tracknum = 0;
-
+        Track = _MIDI_TrackPtr;
+        tracknum = 0;
         while (tracknum < _MIDI_NumTracks)
         {
             while ((Track->active) && (Track->delay == 0))
             {
-                int32_t event;
-
                 GET_NEXT_EVENT(Track, event);
 
                 if (GET_MIDI_COMMAND(event) == MIDI_SPECIAL)
@@ -470,8 +564,9 @@ static int32_t _MIDI_ServiceRoutine(void)
                     }
 
                     if (Track->active)
+                    {
                         Track->delay = _MIDI_ReadDelta(Track);
-
+                    }
                     continue;
                 }
 
@@ -485,35 +580,50 @@ static int32_t _MIDI_ServiceRoutine(void)
                     Track->pos--;
                 }
 
-                int32_t channel = GET_MIDI_CHANNEL(event);
-                int32_t command = GET_MIDI_COMMAND(event);
-
-                int32_t c1 = 0;
-                int32_t c2 = 0;
+                channel = GET_MIDI_CHANNEL(event);
+                command = GET_MIDI_COMMAND(event);
 
                 if (_MIDI_CommandLengths[ command ] > 0)
                 {
                     GET_NEXT_EVENT(Track, c1);
-
                     if (_MIDI_CommandLengths[ command ] > 1)
+                    {
                         GET_NEXT_EVENT(Track, c2);
+                    }
+                }
+
+                if (_MIDI_RerouteFunctions[ channel ] != NULL)
+                {
+                    status = _MIDI_RerouteFunctions[ channel ](event, c1, c2);
+
+                    if (status == MIDI_DONT_PLAY)
+                    {
+                        Track->delay = _MIDI_ReadDelta(Track);
+                        continue;
+                    }
                 }
 
                 switch (command)
                 {
                 case MIDI_NOTE_OFF :
                     if (_MIDI_Funcs->NoteOff)
+                    {
                         _MIDI_Funcs->NoteOff(channel, c1, c2);
+                    }
                     break;
 
                 case MIDI_NOTE_ON :
                     if (_MIDI_Funcs->NoteOn)
+                    {
                         _MIDI_Funcs->NoteOn(channel, c1, c2);
+                    }
                     break;
 
                 case MIDI_POLY_AFTER_TCH :
                     if (_MIDI_Funcs->PolyAftertouch)
+                    {
                         _MIDI_Funcs->PolyAftertouch(channel, c1, c2);
+                    }
                     break;
 
                 case MIDI_CONTROL_CHANGE :
@@ -522,17 +632,23 @@ static int32_t _MIDI_ServiceRoutine(void)
 
                 case MIDI_PROGRAM_CHANGE :
                     if ((_MIDI_Funcs->ProgramChange) && (!Track->EMIDI_ProgramChange))
+                    {
                         _MIDI_Funcs->ProgramChange(channel, MIDI_PatchMap[ c1 & 0x7f ]);
+                    }
                     break;
 
                 case MIDI_AFTER_TOUCH :
                     if (_MIDI_Funcs->ChannelAftertouch)
+                    {
                         _MIDI_Funcs->ChannelAftertouch(channel, c1);
+                    }
                     break;
 
                 case MIDI_PITCH_BEND :
                     if (_MIDI_Funcs->PitchBend)
+                    {
                         _MIDI_Funcs->PitchBend(channel, c1, c2);
+                    }
                     break;
 
                 default :
@@ -568,49 +684,242 @@ static int32_t _MIDI_ServiceRoutine(void)
     return 0;
 }
 
-static int32_t _MIDI_SendControlChange(int32_t channel, int32_t c1, int32_t c2)
+
+/*---------------------------------------------------------------------
+   Function: _MIDI_SendControlChange
+
+   Sends a control change to the proper device
+---------------------------------------------------------------------*/
+
+static int32_t _MIDI_SendControlChange
+(
+    int32_t channel,
+    int32_t c1,
+    int32_t c2
+)
+
 {
-    if (_MIDI_Funcs == NULL || _MIDI_Funcs->ControlChange == NULL)
-        return MIDI_Error;
+    int32_t status;
+
+    if (_MIDI_RerouteFunctions[ channel ] != NULL)
+    {
+        status = _MIDI_RerouteFunctions[ channel ](0xB0 + channel,
+                 c1, c2);
+        if (status == MIDI_DONT_PLAY)
+        {
+            return(MIDI_Ok);
+        }
+    }
+
+    if (_MIDI_Funcs == NULL)
+    {
+        return(MIDI_Error);
+    }
+
+    if (_MIDI_Funcs->ControlChange == NULL)
+    {
+        return(MIDI_Error);
+    }
 
     _MIDI_Funcs->ControlChange(channel, c1, c2);
 
-    return MIDI_Ok;
+    return(MIDI_Ok);
 }
 
-int32_t MIDI_AllNotesOff(void)
+
+/*---------------------------------------------------------------------
+   Function: MIDI_RerouteMidiChannel
+
+   Sets callback function to reroute MIDI commands from specified
+   function.
+---------------------------------------------------------------------*/
+
+void MIDI_RerouteMidiChannel
+(
+    int32_t channel,
+    int32_t(*function)(int32_t, int32_t, int32_t)
+)
+
 {
-    for (int32_t channel = 0; channel < NUM_MIDI_CHANNELS; channel++)
+    if ((channel >= 1) && (channel <= 16))
+    {
+        _MIDI_RerouteFunctions[ channel - 1 ] = function;
+    }
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_AllNotesOff
+
+   Sends all notes off commands on all midi channels.
+---------------------------------------------------------------------*/
+
+int32_t MIDI_AllNotesOff
+(
+    void
+)
+
+{
+    int32_t channel;
+
+    for (channel = 0; channel < NUM_MIDI_CHANNELS; channel++)
     {
         _MIDI_SendControlChange(channel, 0x40, 0);
         _MIDI_SendControlChange(channel, MIDI_ALL_NOTES_OFF, 0);
         _MIDI_SendControlChange(channel, 0x78, 0);
     }
 
-    return MIDI_Ok;
+    return(MIDI_Ok);
 }
 
-static void _MIDI_SetChannelVolume(int32_t channel, int32_t volume)
+
+/*---------------------------------------------------------------------
+   Function: _MIDI_SetChannelVolume
+
+   Sets the volume of the specified midi channel.
+---------------------------------------------------------------------*/
+
+static void _MIDI_SetChannelVolume
+(
+    int32_t channel,
+    int32_t volume
+)
+
 {
+    int32_t status;
+    int32_t remotevolume;
+
     _MIDI_ChannelVolume[ channel ] = volume;
 
-    if (_MIDI_Funcs == NULL || _MIDI_Funcs->ControlChange == NULL)
+    if (_MIDI_RerouteFunctions[ channel ] != NULL)
+    {
+        remotevolume = volume * _MIDI_TotalVolume;
+        remotevolume *= _MIDI_UserChannelVolume[ channel ];
+        remotevolume = tabledivide32_noinline(remotevolume, MIDI_MaxVolume);
+        remotevolume >>= 8;
+
+        status = _MIDI_RerouteFunctions[ channel ](0xB0 + channel,
+                 MIDI_VOLUME, remotevolume);
+        if (status == MIDI_DONT_PLAY)
+        {
+            return;
+        }
+    }
+
+    if (_MIDI_Funcs == NULL)
+    {
         return;
+    }
+
+    if (_MIDI_Funcs->ControlChange == NULL)
+    {
+        return;
+    }
+
+    // For user volume
+    volume *= _MIDI_UserChannelVolume[ channel ];
+
+    if (_MIDI_Funcs->SetVolume == NULL)
+    {
+        volume *= _MIDI_TotalVolume;
+        volume = tabledivide32_noinline(volume, MIDI_MaxVolume);
+    }
+
+    // For user volume
+    volume >>= 8;
 
     _MIDI_Funcs->ControlChange(channel, MIDI_VOLUME, volume);
 }
 
-static void _MIDI_SendChannelVolumes(void)
+
+/*---------------------------------------------------------------------
+   Function: MIDI_SetUserChannelVolume
+
+   Sets the volume of the specified midi channel.
+---------------------------------------------------------------------*/
+
+void MIDI_SetUserChannelVolume
+(
+    int32_t channel,
+    int32_t volume
+)
+
 {
-    for (int32_t channel = 0; channel < NUM_MIDI_CHANNELS; channel++)
+    // Convert channel from 1-16 to 0-15
+    channel--;
+
+    volume = max(0, volume);
+    volume = min(volume, 256);
+
+    if ((channel >= 0) && (channel < NUM_MIDI_CHANNELS))
+    {
+        _MIDI_UserChannelVolume[ channel ] = volume;
         _MIDI_SetChannelVolume(channel, _MIDI_ChannelVolume[ channel ]);
+    }
 }
 
-int32_t MIDI_Reset(void)
+
+/*---------------------------------------------------------------------
+   Function: MIDI_ResetUserChannelVolume
+
+   Sets the volume of the specified midi channel.
+---------------------------------------------------------------------*/
+
+void MIDI_ResetUserChannelVolume
+(
+    void
+)
+
 {
+    int32_t channel;
+
+    for (channel = 0; channel < NUM_MIDI_CHANNELS; channel++)
+    {
+        _MIDI_UserChannelVolume[ channel ] = 256;
+    }
+
+    _MIDI_SendChannelVolumes();
+}
+
+
+/*---------------------------------------------------------------------
+   Function: _MIDI_SendChannelVolumes
+
+   Sets the volume on all the midi channels.
+---------------------------------------------------------------------*/
+
+static void _MIDI_SendChannelVolumes
+(
+    void
+)
+
+{
+    int32_t channel;
+
+    for (channel = 0; channel < NUM_MIDI_CHANNELS; channel++)
+    {
+        _MIDI_SetChannelVolume(channel, _MIDI_ChannelVolume[ channel ]);
+    }
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_Reset
+
+   Resets the MIDI device to General Midi defaults.
+---------------------------------------------------------------------*/
+
+int32_t MIDI_Reset
+(
+    void
+)
+
+{
+    int32_t channel;
+
     MIDI_AllNotesOff();
 
-    for (int32_t channel = 0; channel < NUM_MIDI_CHANNELS; channel++)
+    for (channel = 0; channel < NUM_MIDI_CHANNELS; channel++)
     {
         _MIDI_SendControlChange(channel, MIDI_RESET_ALL_CONTROLLERS, 0);
         _MIDI_SendControlChange(channel, MIDI_RPN_MSB, MIDI_PITCHBEND_MSB);
@@ -624,7 +933,7 @@ int32_t MIDI_Reset(void)
 
     Reset = TRUE;
 
-    return MIDI_Ok;
+    return(MIDI_Ok);
 }
 
 
@@ -634,21 +943,139 @@ int32_t MIDI_Reset(void)
    Sets the total volume of the music.
 ---------------------------------------------------------------------*/
 
-int32_t MIDI_SetVolume(int32_t volume)
-{
-    if (_MIDI_Funcs == NULL)
-        return MIDI_NullMidiModule;
+int32_t MIDI_SetVolume
+(
+    int32_t volume
+)
 
-    volume = clamp(volume, 0, MIDI_MaxVolume);
+{
+    int32_t i;
+
+    if (_MIDI_Funcs == NULL)
+    {
+        return(MIDI_NullMidiModule);
+    }
+
+    volume = min(MIDI_MaxVolume, volume);
+    volume = max(0, volume);
 
     _MIDI_TotalVolume = volume;
 
-    _MIDI_SendChannelVolumes();
+    if (_MIDI_Funcs->SetVolume)
+    {
+        _MIDI_Funcs->SetVolume(volume);
 
-    return MIDI_Ok;
+        for (i = 0; i < NUM_MIDI_CHANNELS; i++)
+        {
+            if (_MIDI_RerouteFunctions[ i ] != NULL)
+            {
+                _MIDI_SetChannelVolume(i, _MIDI_ChannelVolume[ i ]);
+            }
+        }
+    }
+    else
+    {
+        _MIDI_SendChannelVolumes();
+    }
+
+    return(MIDI_Ok);
 }
 
-void MIDI_ContinueSong(void)
+
+/*---------------------------------------------------------------------
+   Function: MIDI_GetVolume
+
+   Returns the total volume of the music.
+---------------------------------------------------------------------*/
+
+int32_t MIDI_GetVolume
+(
+    void
+)
+
+{
+    int32_t volume;
+
+    if (_MIDI_Funcs == NULL)
+    {
+        return(MIDI_NullMidiModule);
+    }
+
+    if (_MIDI_Funcs->GetVolume)
+    {
+        volume = _MIDI_Funcs->GetVolume();
+    }
+    else
+    {
+        volume = _MIDI_TotalVolume;
+    }
+
+    return(volume);
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_SetContext
+
+   Sets the song context.
+---------------------------------------------------------------------*/
+
+void MIDI_SetContext
+(
+    int32_t context
+)
+
+{
+    if ((context > 0) && (context < EMIDI_NUM_CONTEXTS))
+    {
+        _MIDI_Context = context;
+    }
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_GetContext
+
+   Returns the current song context.
+---------------------------------------------------------------------*/
+
+int32_t MIDI_GetContext
+(
+    void
+)
+
+{
+    return _MIDI_Context;
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_SetLoopFlag
+
+   Sets whether the song should loop when finished or not.
+---------------------------------------------------------------------*/
+
+void MIDI_SetLoopFlag
+(
+    int32_t loopflag
+)
+
+{
+    _MIDI_Loop = loopflag;
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_ContinueSong
+
+   Continues playback of a paused song.
+---------------------------------------------------------------------*/
+
+void MIDI_ContinueSong
+(
+    void
+)
+
 {
     if (_MIDI_SongLoaded)
     {
@@ -657,7 +1084,18 @@ void MIDI_ContinueSong(void)
     }
 }
 
-void MIDI_PauseSong(void)
+
+/*---------------------------------------------------------------------
+   Function: MIDI_PauseSong
+
+   Pauses playback of the current song.
+---------------------------------------------------------------------*/
+
+void MIDI_PauseSong
+(
+    void
+)
+
 {
     if (_MIDI_SongLoaded)
     {
@@ -667,15 +1105,50 @@ void MIDI_PauseSong(void)
     }
 }
 
+
+/*---------------------------------------------------------------------
+   Function: MIDI_SongPlaying
+
+   Returns whether a song is playing or not.
+---------------------------------------------------------------------*/
+
+int32_t MIDI_SongPlaying
+(
+    void
+)
+
+{
+    return(_MIDI_SongActive);
+}
+
+
 /*---------------------------------------------------------------------
    Function: MIDI_SetMidiFuncs
 
    Selects the routines that send the MIDI data to the music device.
 ---------------------------------------------------------------------*/
 
-void MIDI_SetMidiFuncs(midifuncs *funcs) { _MIDI_Funcs = funcs; }
+void MIDI_SetMidiFuncs
+(
+    midifuncs *funcs
+)
+
+{
+    _MIDI_Funcs = funcs;
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_StopSong
+
+   Stops playback of the currently playing song.
+---------------------------------------------------------------------*/
+
+void MIDI_StopSong
+(
+    void
+)
 
-void MIDI_StopSong(void)
 {
     if (_MIDI_SongLoaded)
     {
@@ -685,6 +1158,11 @@ void MIDI_StopSong(void)
         MIDI_Reset();
         _MIDI_ResetTracks();
 
+        if (_MIDI_Funcs->ReleasePatches)
+        {
+            _MIDI_Funcs->ReleasePatches();
+        }
+
         DO_FREE_AND_NULL(_MIDI_TrackPtr);
 
         _MIDI_NumTracks    = 0;
@@ -699,89 +1177,145 @@ void MIDI_StopSong(void)
     }
 }
 
-int32_t MIDI_PlaySong(char *song, int32_t loopflag)
+
+/*---------------------------------------------------------------------
+   Function: MIDI_PlaySong
+
+   Begins playback of a MIDI song.
+---------------------------------------------------------------------*/
+
+int32_t MIDI_PlaySong
+(
+    char *song,
+    int32_t loopflag
+)
+
 {
+    int32_t    numtracks;
+    int32_t    format;
+    int32_t   headersize;
+    int32_t   tracklength;
+    track *CurrentTrack;
+    char *ptr;
+
     if (_MIDI_SongLoaded)
+    {
         MIDI_StopSong();
+    }
 
     MPU_Init(MUSIC_SoundDevice);
 
     _MIDI_Loop = loopflag;
 
     if (_MIDI_Funcs == NULL)
-        return MIDI_NullMidiModule;
+    {
+        return(MIDI_NullMidiModule);
+    }
 
     if (B_UNBUF32(song) != MIDI_HEADER_SIGNATURE)
-        return MIDI_InvalidMidiFile;
+    {
+        return(MIDI_InvalidMidiFile);
+    }
 
-    song += sizeof(int32_t);
+    song += 4;
 
-    int headersize      = _MIDI_ReadNumber(song, sizeof(int32_t));
-    song += sizeof(int32_t);
-    int format          = _MIDI_ReadNumber(song, sizeof(int16_t));
-    _MIDI_NumTracks = _MIDI_ReadNumber(song + sizeof(int16_t), sizeof(int16_t));
-    _MIDI_Division  = _MIDI_ReadNumber(song + sizeof(int16_t) + sizeof(int16_t), sizeof(int16_t));
-
-    // If a SMPTE time division is given, just set to 96 so no errors occur
+    headersize      = _MIDI_ReadNumber(song, 4);
+    song += 4;
+    format          = _MIDI_ReadNumber(song, 2);
+    _MIDI_NumTracks = _MIDI_ReadNumber(song + 2, 2);
+    _MIDI_Division  = _MIDI_ReadNumber(song + 4, 2);
     if (_MIDI_Division < 0)
+    {
+        // If a SMPTE time division is given, just set to 96 so no errors occur
         _MIDI_Division = 96;
+    }
 
     if (format > MAX_FORMAT)
-        return MIDI_UnknownMidiFormat;
+    {
+        return(MIDI_UnknownMidiFormat);
+    }
 
-    char *ptr = song + headersize;
+    ptr = song + headersize;
 
     if (_MIDI_NumTracks == 0)
-        return MIDI_NoTracks;
+    {
+        return(MIDI_NoTracks);
+    }
 
     _MIDI_TrackMemSize = _MIDI_NumTracks  * sizeof(track);
     _MIDI_TrackPtr = (track *)Xmalloc(_MIDI_TrackMemSize);
 
-    track *CurrentTrack = _MIDI_TrackPtr;
-    int numtracks    = _MIDI_NumTracks-1;
-
-    do
+    CurrentTrack = _MIDI_TrackPtr;
+    numtracks    = _MIDI_NumTracks;
+    while (numtracks--)
     {
         if (B_UNBUF32(ptr) != MIDI_TRACK_SIGNATURE)
         {
             DO_FREE_AND_NULL(_MIDI_TrackPtr);
+
             _MIDI_TrackMemSize = 0;
 
-            return MIDI_InvalidTrack;
+            return(MIDI_InvalidTrack);
         }
 
-        int tracklength = _MIDI_ReadNumber(ptr + sizeof(int32_t), sizeof(int32_t));
-        ptr += sizeof(int32_t) + sizeof(int32_t);
+        tracklength = _MIDI_ReadNumber(ptr + 4, 4);
+        ptr += 8;
         CurrentTrack->start = ptr;
         ptr += tracklength;
         CurrentTrack++;
     }
-    while (numtracks--);
+
+    if (_MIDI_Funcs->GetVolume != NULL)
+    {
+        _MIDI_TotalVolume = _MIDI_Funcs->GetVolume();
+    }
 
     _MIDI_InitEMIDI();
+
+    if (_MIDI_Funcs->LoadPatch)
+    {
+        MIDI_LoadTimbres();
+    }
+
     _MIDI_ResetTracks();
 
     if (!Reset)
+    {
         MIDI_Reset();
+    }
 
     Reset = FALSE;
 
     MIDI_SetDivision(_MIDI_Division);
+    //MIDI_SetTempo( 120 );
 
     _MIDI_SongLoaded = TRUE;
     _MIDI_SongActive = TRUE;
 
     while (_MPU_BuffersWaiting < 4) _MIDI_ServiceRoutine();
-
     MPU_BeginPlayback();
 
-    return MIDI_Ok;
+    return(MIDI_Ok);
 }
 
-void MIDI_SetTempo(int32_t tempo)
+
+/*---------------------------------------------------------------------
+   Function: MIDI_SetTempo
+
+   Sets the song tempo.
+---------------------------------------------------------------------*/
+
+void MIDI_SetTempo
+(
+    int32_t tempo
+)
+
 {
+    int32_t tickspersecond;
+
     MIDI_Tempo = tempo;
-    _MIDI_FPSecondsPerTick = tabledivide32_noinline(1 << TIME_PRECISION, ((tempo * _MIDI_Division) / 60));
+    tickspersecond = ((tempo) * _MIDI_Division)/60;
+    _MIDI_FPSecondsPerTick = tabledivide32_noinline(1 << TIME_PRECISION, tickspersecond);
     MPU_SetTempo(tempo);
 }
 
@@ -790,9 +1324,403 @@ void MIDI_SetDivision(int32_t division)
     MPU_SetDivision(division);
 }
 
-static void _MIDI_InitEMIDI(void)
+
+/*---------------------------------------------------------------------
+   Function: MIDI_GetTempo
+
+   Returns the song tempo.
+---------------------------------------------------------------------*/
+
+int32_t MIDI_GetTempo
+(
+    void
+)
+
 {
-    int32_t const type = EMIDI_GeneralMIDI;
+    return(MIDI_Tempo);
+}
+
+
+/*---------------------------------------------------------------------
+   Function: _MIDI_ProcessNextTick
+
+   Sets the position of the song pointer.
+---------------------------------------------------------------------*/
+
+static int32_t _MIDI_ProcessNextTick
+(
+    void
+)
+
+{
+    int32_t   event;
+    int32_t   channel;
+    int32_t   command;
+    track *Track;
+    int32_t   tracknum;
+    int32_t   status;
+    int32_t   c1 = 0;
+    int32_t   c2 = 0;
+    int32_t   TimeSet = FALSE;
+
+    Track = _MIDI_TrackPtr;
+    tracknum = 0;
+    while ((tracknum < _MIDI_NumTracks) && (Track != NULL))
+    {
+        while ((Track->active) && (Track->delay == 0))
+        {
+            GET_NEXT_EVENT(Track, event);
+
+            if (GET_MIDI_COMMAND(event) == MIDI_SPECIAL)
+            {
+                switch (event)
+                {
+                case MIDI_SYSEX :
+                case MIDI_SYSEX_CONTINUE :
+                    _MIDI_SysEx(Track);
+                    break;
+
+                case MIDI_META_EVENT :
+                    _MIDI_MetaEvent(Track);
+                    break;
+                }
+
+                if (Track->active)
+                {
+                    Track->delay = _MIDI_ReadDelta(Track);
+                }
+
+                continue;
+            }
+
+            if (event & MIDI_RUNNING_STATUS)
+            {
+                Track->RunningStatus = event;
+            }
+            else
+            {
+                event = Track->RunningStatus;
+                Track->pos--;
+            }
+
+            channel = GET_MIDI_CHANNEL(event);
+            command = GET_MIDI_COMMAND(event);
+
+            if (_MIDI_CommandLengths[ command ] > 0)
+            {
+                GET_NEXT_EVENT(Track, c1);
+                if (_MIDI_CommandLengths[ command ] > 1)
+                {
+                    GET_NEXT_EVENT(Track, c2);
+                }
+            }
+
+            if (_MIDI_RerouteFunctions[ channel ] != NULL)
+            {
+                status = _MIDI_RerouteFunctions[ channel ](event, c1, c2);
+
+                if (status == MIDI_DONT_PLAY)
+                {
+                    Track->delay = _MIDI_ReadDelta(Track);
+                    continue;
+                }
+            }
+
+            switch (command)
+            {
+            case MIDI_NOTE_OFF :
+                break;
+
+            case MIDI_NOTE_ON :
+                break;
+
+            case MIDI_POLY_AFTER_TCH :
+                if (_MIDI_Funcs->PolyAftertouch)
+                {
+                    _MIDI_Funcs->PolyAftertouch(channel, c1, c2);
+                }
+                break;
+
+            case MIDI_CONTROL_CHANGE :
+                TimeSet = _MIDI_InterpretControllerInfo(Track, TimeSet,
+                                                        channel, c1, c2);
+                break;
+
+            case MIDI_PROGRAM_CHANGE :
+                if ((_MIDI_Funcs->ProgramChange) &&
+                        (!Track->EMIDI_ProgramChange))
+                {
+                    _MIDI_Funcs->ProgramChange(channel, c1);
+                }
+                break;
+
+            case MIDI_AFTER_TOUCH :
+                if (_MIDI_Funcs->ChannelAftertouch)
+                {
+                    _MIDI_Funcs->ChannelAftertouch(channel, c1);
+                }
+                break;
+
+            case MIDI_PITCH_BEND :
+                if (_MIDI_Funcs->PitchBend)
+                {
+                    _MIDI_Funcs->PitchBend(channel, c1, c2);
+                }
+                break;
+
+            default :
+                break;
+            }
+
+            Track->delay = _MIDI_ReadDelta(Track);
+        }
+
+        Track->delay--;
+        Track++;
+        tracknum++;
+
+        if (_MIDI_ActiveTracks == 0)
+        {
+            break;
+        }
+    }
+
+    _MIDI_AdvanceTick();
+
+    return(TimeSet);
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_SetSongTick
+
+   Sets the position of the song pointer.
+---------------------------------------------------------------------*/
+
+void MIDI_SetSongTick
+(
+    uint32_t PositionInTicks
+)
+
+{
+    if (!_MIDI_SongLoaded)
+    {
+        return;
+    }
+
+    MIDI_PauseSong();
+
+    if (PositionInTicks < _MIDI_PositionInTicks)
+    {
+        _MIDI_ResetTracks();
+        MIDI_Reset();
+    }
+
+    while (_MIDI_PositionInTicks < PositionInTicks)
+    {
+        if (_MIDI_ProcessNextTick())
+        {
+            break;
+        }
+        if (_MIDI_ActiveTracks == 0)
+        {
+            _MIDI_ResetTracks();
+            if (!_MIDI_Loop)
+            {
+                return;
+            }
+            break;
+        }
+    }
+
+    MIDI_SetVolume(_MIDI_TotalVolume);
+    MIDI_ContinueSong();
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_SetSongTime
+
+   Sets the position of the song pointer.
+---------------------------------------------------------------------*/
+
+void MIDI_SetSongTime
+(
+    uint32_t milliseconds
+)
+
+{
+    uint32_t mil;
+    uint32_t sec;
+    uint32_t newtime;
+
+    if (!_MIDI_SongLoaded)
+    {
+        return;
+    }
+
+    MIDI_PauseSong();
+
+    mil = tabledivide32_noinline((milliseconds % 1000) << TIME_PRECISION, 1000);
+    sec = tabledivide32_noinline(milliseconds, 1000) << TIME_PRECISION;
+    newtime = sec + mil;
+
+    if (newtime < _MIDI_Time)
+    {
+        _MIDI_ResetTracks();
+        MIDI_Reset();
+    }
+
+    while (_MIDI_Time < newtime)
+    {
+        if (_MIDI_ProcessNextTick())
+        {
+            break;
+        }
+        if (_MIDI_ActiveTracks == 0)
+        {
+            _MIDI_ResetTracks();
+            if (!_MIDI_Loop)
+            {
+                return;
+            }
+            break;
+        }
+    }
+
+    MIDI_SetVolume(_MIDI_TotalVolume);
+    MIDI_ContinueSong();
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_SetSongPosition
+
+   Sets the position of the song pointer.
+---------------------------------------------------------------------*/
+
+void MIDI_SetSongPosition
+(
+    int32_t measure,
+    int32_t beat,
+    int32_t tick
+)
+
+{
+    uint32_t pos;
+
+    if (!_MIDI_SongLoaded)
+    {
+        return;
+    }
+
+    MIDI_PauseSong();
+
+    pos = RELATIVE_BEAT(measure, beat, tick);
+
+    if (pos < (uint32_t)RELATIVE_BEAT(_MIDI_Measure, _MIDI_Beat, _MIDI_Tick))
+    {
+        _MIDI_ResetTracks();
+        MIDI_Reset();
+    }
+
+    while ((uint32_t)RELATIVE_BEAT(_MIDI_Measure, _MIDI_Beat, _MIDI_Tick) < pos)
+    {
+        if (_MIDI_ProcessNextTick())
+        {
+            break;
+        }
+        if (_MIDI_ActiveTracks == 0)
+        {
+            _MIDI_ResetTracks();
+            if (!_MIDI_Loop)
+            {
+                return;
+            }
+            break;
+        }
+    }
+
+    MIDI_SetVolume(_MIDI_TotalVolume);
+    MIDI_ContinueSong();
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_GetSongPosition
+
+   Returns the position of the song pointer in Measures, beats, ticks.
+---------------------------------------------------------------------*/
+
+void MIDI_GetSongPosition
+(
+    songposition *pos
+)
+
+{
+    uint32_t mil;
+    uint32_t sec;
+
+    mil = (_MIDI_Time & ((1 << TIME_PRECISION) - 1)) * 1000;
+    sec = _MIDI_Time >> TIME_PRECISION;
+    pos->milliseconds = (mil >> TIME_PRECISION) + (sec * 1000);
+    pos->tickposition = _MIDI_PositionInTicks;
+    pos->measure      = _MIDI_Measure;
+    pos->beat         = _MIDI_Beat;
+    pos->tick         = _MIDI_Tick;
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_GetSongLength
+
+   Returns the length of the song.
+---------------------------------------------------------------------*/
+
+void MIDI_GetSongLength
+(
+    songposition *pos
+)
+
+{
+    uint32_t mil;
+    uint32_t sec;
+
+    mil = (_MIDI_TotalTime & ((1 << TIME_PRECISION) - 1)) * 1000;
+    sec = _MIDI_TotalTime >> TIME_PRECISION;
+
+    pos->milliseconds = (mil >> TIME_PRECISION) + (sec * 1000);
+    pos->measure      = _MIDI_TotalMeasures;
+    pos->beat         = _MIDI_TotalBeats;
+    pos->tick         = _MIDI_TotalTicks;
+    pos->tickposition = 0;
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_InitEMIDI
+
+   Sets up the EMIDI
+---------------------------------------------------------------------*/
+
+static void _MIDI_InitEMIDI
+(
+    void
+)
+
+{
+    int32_t    event;
+    int32_t    command;
+//    int32_t    channel;
+    int32_t    length;
+    int32_t    IncludeFound;
+    track *Track;
+    int32_t    tracknum;
+    int32_t    type;
+    int32_t    c1;
+    int32_t    c2;
+
+    type = EMIDI_GeneralMIDI;
 
     _MIDI_ResetTracks();
 
@@ -801,8 +1729,8 @@ static void _MIDI_InitEMIDI(void)
     _MIDI_TotalBeats    = 0;
     _MIDI_TotalMeasures = 0;
 
-    track *Track = _MIDI_TrackPtr;
-    int32_t tracknum = 0;
+    Track = _MIDI_TrackPtr;
+    tracknum = 0;
     while ((tracknum < _MIDI_NumTracks) && (Track != NULL))
     {
         _MIDI_Tick = 0;
@@ -832,11 +1760,9 @@ static void _MIDI_InitEMIDI(void)
             Track->delay--;
         }
 
-        int IncludeFound = FALSE;
+        IncludeFound = FALSE;
         while (Track->active)
         {
-            int32_t event;
-
             GET_NEXT_EVENT(Track, event);
 
             if (GET_MIDI_COMMAND(event) == MIDI_SPECIAL)
@@ -867,33 +1793,42 @@ static void _MIDI_InitEMIDI(void)
             }
 
             if (event & MIDI_RUNNING_STATUS)
+            {
                 Track->RunningStatus = event;
+            }
             else
             {
                 event = Track->RunningStatus;
                 Track->pos--;
             }
 
-            int32_t const command = GET_MIDI_COMMAND(event);
-            int32_t length = _MIDI_CommandLengths[ command ];
+//            channel = GET_MIDI_CHANNEL(event);
+            command = GET_MIDI_COMMAND(event);
+            length = _MIDI_CommandLengths[ command ];
 
             if (command == MIDI_CONTROL_CHANGE)
             {
                 if (*Track->pos == MIDI_MONO_MODE_ON)
+                {
                     length++;
-
-                int32_t    c1, c2;
-
+                }
                 GET_NEXT_EVENT(Track, c1);
                 GET_NEXT_EVENT(Track, c2);
-
                 length -= 2;
 
                 switch (c1)
                 {
                 case EMIDI_LOOP_START :
                 case EMIDI_SONG_LOOP_START :
-                    Track->context[ 0 ].loopcount        = c2 ? c2 : EMIDI_INFINITE;
+                    if (c2 == 0)
+                    {
+                        Track->context[ 0 ].loopcount = EMIDI_INFINITE;
+                    }
+                    else
+                    {
+                        Track->context[ 0 ].loopcount = c2;
+                    }
+
                     Track->context[ 0 ].pos              = Track->pos;
                     Track->context[ 0 ].loopstart        = Track->pos;
                     Track->context[ 0 ].RunningStatus    = Track->RunningStatus;
@@ -985,9 +1920,9 @@ static void _MIDI_InitEMIDI(void)
         }
 
         _MIDI_TotalTime = max(_MIDI_TotalTime, _MIDI_Time);
-
         if (RELATIVE_BEAT(_MIDI_Measure, _MIDI_Beat, _MIDI_Tick) >
-            RELATIVE_BEAT(_MIDI_TotalMeasures, _MIDI_TotalBeats, _MIDI_TotalTicks))
+                RELATIVE_BEAT(_MIDI_TotalMeasures, _MIDI_TotalBeats,
+                              _MIDI_TotalTicks))
         {
             _MIDI_TotalTicks    = _MIDI_Tick;
             _MIDI_TotalBeats    = _MIDI_Beat;
@@ -1002,6 +1937,118 @@ static void _MIDI_InitEMIDI(void)
 }
 
 
+/*---------------------------------------------------------------------
+   Function: MIDI_LoadTimbres
+
+   Preloads the timbres on cards that use patch-caching.
+---------------------------------------------------------------------*/
+
+void MIDI_LoadTimbres
+(
+    void
+)
+
+{
+    int32_t    event;
+    int32_t    command;
+    int32_t    channel;
+    int32_t    length;
+    int32_t    Finished;
+    track *Track;
+    int32_t    tracknum;
+
+    Track = _MIDI_TrackPtr;
+    tracknum = 0;
+    while ((tracknum < _MIDI_NumTracks) && (Track != NULL))
+    {
+        Finished = FALSE;
+        while (!Finished)
+        {
+            GET_NEXT_EVENT(Track, event);
+
+            if (GET_MIDI_COMMAND(event) == MIDI_SPECIAL)
+            {
+                switch (event)
+                {
+                case MIDI_SYSEX :
+                case MIDI_SYSEX_CONTINUE :
+                    length = _MIDI_ReadDelta(Track);
+                    Track->pos += length;
+                    break;
+
+                case MIDI_META_EVENT :
+                    GET_NEXT_EVENT(Track, command);
+                    GET_NEXT_EVENT(Track, length);
+
+                    if (command == MIDI_END_OF_TRACK)
+                    {
+                        Finished = TRUE;
+                    }
+
+                    Track->pos += length;
+                    break;
+                }
+
+                if (!Finished)
+                {
+                    _MIDI_ReadDelta(Track);
+                }
+
+                continue;
+            }
+
+            if (event & MIDI_RUNNING_STATUS)
+            {
+                Track->RunningStatus = event;
+            }
+            else
+            {
+                event = Track->RunningStatus;
+                Track->pos--;
+            }
+
+            channel = GET_MIDI_CHANNEL(event);
+            command = GET_MIDI_COMMAND(event);
+            length = _MIDI_CommandLengths[ command ];
+
+            if (command == MIDI_CONTROL_CHANGE)
+            {
+                if (*Track->pos == MIDI_MONO_MODE_ON)
+                {
+                    length++;
+                }
+
+                if (*Track->pos == EMIDI_PROGRAM_CHANGE)
+                {
+                    _MIDI_Funcs->LoadPatch(*(Track->pos + 1));
+                }
+            }
+
+            if (channel == MIDI_RHYTHM_CHANNEL)
+            {
+                if (command == MIDI_NOTE_ON)
+                {
+                    _MIDI_Funcs->LoadPatch(128 + *Track->pos);
+                }
+            }
+            else
+            {
+                if (command == MIDI_PROGRAM_CHANGE)
+                {
+                    _MIDI_Funcs->LoadPatch(*Track->pos);
+                }
+            }
+            Track->pos += length;
+            _MIDI_ReadDelta(Track);
+        }
+        Track++;
+        tracknum++;
+    }
+
+    _MIDI_ResetTracks();
+}
+
+
 void MIDI_UpdateMusic(void)
 {
     if (!_MIDI_SongLoaded || !_MIDI_SongActive) return;
diff --git a/polymer/eduke32/source/midi.h b/polymer/eduke32/source/midi.h
index 0f7a83430..68002098b 100644
--- a/polymer/eduke32/source/midi.h
+++ b/polymer/eduke32/source/midi.h
@@ -1,12 +1,10 @@
-//-------------------------------------------------------------------------
 /*
-Copyright (C) 2016 EDuke32 developers and contributors
+Copyright (C) 1994-1995 Apogee Software, Ltd.
 
-This file is part of EDuke32.
-
-EDuke32 is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License version 2
-as published by the Free Software Foundation.
+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
@@ -17,22 +15,36 @@ 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au)
 */
-//-------------------------------------------------------------------------
+/**********************************************************************
+   module: MIDI.H
+
+   author: James R. Dose
+   date:   May 25, 1994
+
+   Public header for MIDI.C.  Midi song file playback routines.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
 
 #ifndef __MIDI_H
 #define __MIDI_H
 
 enum MIDI_Errors
-{
-    MIDI_Error = -1,
-    MIDI_Ok = 0,
-    MIDI_NullMidiModule,
-    MIDI_InvalidMidiFile,
-    MIDI_UnknownMidiFormat,
-    MIDI_NoTracks,
-    MIDI_InvalidTrack,
-};
+   {
+   MIDI_Warning = -2,
+   MIDI_Error   = -1,
+   MIDI_Ok      = 0,
+   MIDI_NullMidiModule,
+   MIDI_InvalidMidiFile,
+   MIDI_UnknownMidiFormat,
+   MIDI_NoTracks,
+   MIDI_InvalidTrack,
+   MIDI_NoMemory,
+   MIDI_DPMI_Error
+   };
 
 
 #define MIDI_PASS_THROUGH 1
@@ -43,26 +55,46 @@ enum MIDI_Errors
 extern char MIDI_PatchMap[ 128 ];
 
 typedef struct
-{
-    void(*NoteOff)(char channel, char key, char velocity);
-    void(*NoteOn)(char channel, char key, char velocity);
-    void(*PolyAftertouch)(char channel, char key, char pressure);
-    void(*ControlChange)(char channel, char number, char value);
-    void(*ProgramChange)(char channel, char program);
-    void(*ChannelAftertouch)(char channel, char pressure);
-    void(*PitchBend)(char channel, char lsb, char msb);
-} midifuncs;
+   {
+   void ( *NoteOff )( int32_t channel, int32_t key, int32_t velocity );
+   void ( *NoteOn )( int32_t channel, int32_t key, int32_t velocity );
+   void ( *PolyAftertouch )( int32_t channel, int32_t key, int32_t pressure );
+   void ( *ControlChange )( int32_t channel, int32_t number, int32_t value );
+   void ( *ProgramChange )( int32_t channel, int32_t program );
+   void ( *ChannelAftertouch )( int32_t channel, int32_t pressure );
+   void ( *PitchBend )( int32_t channel, int32_t lsb, int32_t msb );
+   void ( *ReleasePatches )( void );
+   void ( *LoadPatch )( int32_t number );
+   void ( *SetVolume )( int32_t volume );
+   int32_t  ( *GetVolume )( void );
+   void ( *FinishBuffer )( void );
+   } midifuncs;
 
-int32_t     MIDI_AllNotesOff(void);
-int32_t     MIDI_Reset(void);
-int32_t     MIDI_SetVolume(int32_t volume);
-void        MIDI_SetMidiFuncs(midifuncs *funcs);
-void        MIDI_ContinueSong(void);
-void        MIDI_PauseSong(void);
-void        MIDI_StopSong(void);
-int32_t     MIDI_PlaySong(char *song, int32_t loopflag);
-void        MIDI_SetTempo(int32_t tempo);
-void        MIDI_UpdateMusic(void);
-void        MIDI_SetDivision(int32_t division);
+void MIDI_RerouteMidiChannel( int32_t channel, int32_t ( *function )( int32_t event, int32_t c1, int32_t c2 ) );
+int32_t  MIDI_AllNotesOff( void );
+void MIDI_SetUserChannelVolume( int32_t channel, int32_t volume );
+void MIDI_ResetUserChannelVolume( void );
+int32_t  MIDI_Reset( void );
+int32_t  MIDI_SetVolume( int32_t volume );
+int32_t  MIDI_GetVolume( void );
+void MIDI_SetMidiFuncs( midifuncs *funcs );
+void MIDI_SetContext( int32_t context );
+int32_t  MIDI_GetContext( void );
+void MIDI_SetLoopFlag( int32_t loopflag );
+void MIDI_ContinueSong( void );
+void MIDI_PauseSong( void );
+int32_t  MIDI_SongPlaying( void );
+void MIDI_StopSong( void );
+int32_t  MIDI_PlaySong( char *song, int32_t loopflag );
+void MIDI_SetTempo( int32_t tempo );
+int32_t  MIDI_GetTempo( void );
+void MIDI_SetSongTick( uint32_t PositionInTicks );
+void MIDI_SetSongTime( uint32_t milliseconds );
+void MIDI_SetSongPosition( int32_t measure, int32_t beat, int32_t tick );
+void MIDI_GetSongPosition( songposition *pos );
+void MIDI_GetSongLength( songposition *pos );
+void MIDI_LoadTimbres( void );
+void MIDI_UpdateMusic(void);
+void MIDI_SetDivision( int32_t division );
 
 #endif
diff --git a/polymer/eduke32/source/mpu401.c b/polymer/eduke32/source/mpu401.c
index e3584a204..d62e1978f 100644
--- a/polymer/eduke32/source/mpu401.c
+++ b/polymer/eduke32/source/mpu401.c
@@ -1,6 +1,6 @@
 //-------------------------------------------------------------------------
 /*
-Copyright (C) 2016 EDuke32 developers and contributors
+Copyright (C) 2010 EDuke32 developers and contributors
 
 This file is part of EDuke32.
 
@@ -20,6 +20,18 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
 //-------------------------------------------------------------------------
 
+/**********************************************************************
+   module: MPU401.C
+
+   author: James R. Dose
+   date:   January 1, 1994
+
+   Low level routines to support sending of MIDI data to MPU401
+   compatible MIDI interfaces.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
 #include "mpu401.h"
 #include "compat.h"
 #include "pragmas.h"
@@ -32,6 +44,13 @@ static HMIDISTRM hmido = (HMIDISTRM)-1;
 static MIDIOUTCAPS midicaps;
 static DWORD mididevice = -1;
 
+typedef struct
+{
+    int32_t time;
+    int32_t stream;
+    int32_t event;
+}
+MIDIEVENTHEAD;
 #define PAD(x) ((((x)+3)&(~3)))
 
 #define BUFFERLEN (32*4*4)
@@ -39,11 +58,11 @@ static DWORD mididevice = -1;
 static char eventbuf[NUMBUFFERS][BUFFERLEN];
 static int32_t  eventcnt[NUMBUFFERS];
 static MIDIHDR bufferheaders[NUMBUFFERS];
-static int32_t  _MPU_CurrentBuffer = 0;
+int32_t  _MPU_CurrentBuffer = 0;
 int32_t  _MPU_BuffersWaiting = 0;
 
 extern uint32_t _MIDI_GlobalPositionInTicks;
-static uint32_t _MPU_LastEvent=0;
+uint32_t _MPU_LastEvent=0;
 
 #define MIDI_NOTE_OFF         0x80
 #define MIDI_NOTE_ON          0x90
@@ -58,6 +77,14 @@ static uint32_t _MPU_LastEvent=0;
 #define MIDI_MONO_MODE_ON     0x7E
 #define MIDI_ALL_NOTES_OFF    0x7B
 
+
+/**********************************************************************
+
+   Memory locked functions:
+
+**********************************************************************/
+
+
 void MPU_FinishBuffer(int32_t buffer)
 {
     if (!eventcnt[buffer]) return;
@@ -90,6 +117,8 @@ void MPU_Unpause(void)
 
 void CALLBACK MPU_MIDICallback(HMIDIOUT handle, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
 {
+    int32_t i;
+
     UNREFERENCED_PARAMETER(dwInstance);
     UNREFERENCED_PARAMETER(dwParam2);
 
@@ -97,7 +126,7 @@ void CALLBACK MPU_MIDICallback(HMIDIOUT handle, UINT uMsg, DWORD_PTR dwInstance,
     {
     case MOM_DONE:
         midiOutUnprepareHeader((HMIDIOUT)handle, (MIDIHDR *)dwParam1, sizeof(MIDIHDR));
-        for (int i=0; i<NUMBUFFERS; i++)
+        for (i=0; i<NUMBUFFERS; i++)
         {
             if ((MIDIHDR *)dwParam1 == &bufferheaders[i])
             {
@@ -113,27 +142,36 @@ void CALLBACK MPU_MIDICallback(HMIDIOUT handle, UINT uMsg, DWORD_PTR dwInstance,
     }
 }
 
+
+/*---------------------------------------------------------------------
+   Function: MPU_SendMidi
+
+   Queues a MIDI message to the music device.
+---------------------------------------------------------------------*/
+
 int32_t MPU_GetNextBuffer(void)
 {
-    for (int i = 0; i < NUMBUFFERS; i++)
-        if (eventcnt[i] == 0)
-            return i;
-
+    int32_t i;
+    for (i=0; i<NUMBUFFERS; i++)
+    {
+        if (eventcnt[i] == 0) return i;
+    }
     return -1;
 }
 
-static int32_t const masks[3] ={ 0x00ffffffl, 0x0000ffffl, 0x000000ffl };
-
 void MPU_SendMidi(char *data, int32_t count)
 {
-    if (count <= 0) return;
+    char *p;
+    int32_t padded, nextbuffer;
+    static int32_t masks[3] = { 0x000000ffl, 0x0000ffffl, 0x00ffffffl };
 
+    if (count <= 0) return;
     if (count <= 3)
     {
         if (eventcnt[_MPU_CurrentBuffer] + 12 > BUFFERLEN)
         {
             // buffer over-full
-            int32_t nextbuffer = MPU_GetNextBuffer();
+            nextbuffer = MPU_GetNextBuffer();
             if (nextbuffer < 0)
             {
 //				printf("All buffers full!\n");
@@ -143,7 +181,7 @@ void MPU_SendMidi(char *data, int32_t count)
             _MPU_CurrentBuffer = nextbuffer;
         }
 
-        char *p = eventbuf[_MPU_CurrentBuffer] + eventcnt[_MPU_CurrentBuffer];
+        p = eventbuf[_MPU_CurrentBuffer] + eventcnt[_MPU_CurrentBuffer];
         ((int32_t *)p)[0] = _MIDI_GlobalPositionInTicks - _MPU_LastEvent;
         ((int32_t *)p)[1] = 0;
         ((int32_t *)p)[2] = (MEVT_SHORTMSG << 24) | ((*((int32_t *)data)) & masks[count-1]);
@@ -151,11 +189,11 @@ void MPU_SendMidi(char *data, int32_t count)
     }
     else
     {
-        int32_t padded = PAD(count);
+        padded = PAD(count);
         if (eventcnt[_MPU_CurrentBuffer] + 12 + padded > BUFFERLEN)
         {
             // buffer over-full
-            int32_t nextbuffer = MPU_GetNextBuffer();
+            nextbuffer = MPU_GetNextBuffer();
             if (nextbuffer < 0)
             {
 //				printf("All buffers full!\n");
@@ -165,7 +203,7 @@ void MPU_SendMidi(char *data, int32_t count)
             _MPU_CurrentBuffer = nextbuffer;
         }
 
-        char *p = eventbuf[_MPU_CurrentBuffer] + eventcnt[_MPU_CurrentBuffer];
+        p = eventbuf[_MPU_CurrentBuffer] + eventcnt[_MPU_CurrentBuffer];
         ((int32_t *)p)[0] = _MIDI_GlobalPositionInTicks - _MPU_LastEvent;
         ((int32_t *)p)[1] = 0;
         ((int32_t *)p)[2] = (MEVT_LONGMSG<<24) | (count & 0xffffffl);
@@ -178,69 +216,228 @@ void MPU_SendMidi(char *data, int32_t count)
     _MPU_LastEvent = _MIDI_GlobalPositionInTicks;
 }
 
-int32_t MPU_Reset(void)
+
+/*---------------------------------------------------------------------
+   Function: MPU_SendMidiImmediate
+
+   Sends a MIDI message immediately to the the music device.
+---------------------------------------------------------------------*/
+void MPU_SendMidiImmediate(char *data, int32_t count)
+{
+    MIDIHDR mhdr;
+    static int32_t masks[3] = { 0x00ffffffl, 0x0000ffffl, 0x000000ffl };
+
+    if (!count) return;
+    if (count<=3) midiOutShortMsg((HMIDIOUT)hmido, (*((int32_t *)data)) & masks[count-1]);
+    else
+    {
+        ZeroMemory(&mhdr, sizeof(mhdr));
+        mhdr.lpData = data;
+        mhdr.dwBufferLength = count;
+        midiOutPrepareHeader((HMIDIOUT)hmido, &mhdr, sizeof(MIDIHDR));
+        midiOutLongMsg((HMIDIOUT)hmido, &mhdr, sizeof(MIDIHDR));
+        while (!(mhdr.dwFlags & MHDR_DONE)) ;
+        midiOutUnprepareHeader((HMIDIOUT)hmido, &mhdr, sizeof(MIDIHDR));
+    }
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MPU_Reset
+
+   Resets the MPU401 card.
+---------------------------------------------------------------------*/
+
+int32_t MPU_Reset
+(
+    void
+)
+
 {
     midiStreamStop(hmido);
     midiStreamClose(hmido);
 
-    return MPU_Ok;
+    return(MPU_Ok);
 }
 
-int32_t MPU_Init(int32_t addr)
+
+/*---------------------------------------------------------------------
+   Function: MPU_Init
+
+   Detects and initializes the MPU401 card.
+---------------------------------------------------------------------*/
+
+int32_t MPU_Init
+(
+    int32_t addr
+)
+
 {
-    for (int i=0; i<NUMBUFFERS; i++) eventcnt[i]=0;
+    int32_t i;
+
+    for (i=0; i<NUMBUFFERS; i++) eventcnt[i]=0;
 
     mididevice = addr;
 
     if (midiOutGetDevCaps(mididevice, &midicaps, sizeof(MIDIOUTCAPS)) != MMSYSERR_NOERROR) return MPU_Error;
 
-    if (midiStreamOpen(&hmido,(LPUINT)&mididevice,1,(DWORD_PTR)MPU_MIDICallback,0L,CALLBACK_FUNCTION) != MMSYSERR_NOERROR) return MPU_Error;
+    if (midiStreamOpen(&hmido,(LPUINT)&mididevice,1,(DWORD_PTR)MPU_MIDICallback,0L,CALLBACK_FUNCTION) != MMSYSERR_NOERROR) return(MPU_Error);
 
-    return MPU_Ok;
+    return(MPU_Ok);
 }
 
-void MPU_NoteOff(char channel, char key, char velocity)
+
+/*---------------------------------------------------------------------
+   Function: MPU_NoteOff
+
+   Sends a full MIDI note off event out to the music device.
+---------------------------------------------------------------------*/
+
+void MPU_NoteOff
+(
+    int32_t channel,
+    int32_t key,
+    int32_t velocity
+)
+
 {
-    char msg[] = { (char)(MIDI_NOTE_OFF | channel), key, velocity };
-    MPU_SendMidi(msg, sizeof(msg));
+    char msg[3];
+    msg[0] = (MIDI_NOTE_OFF | channel);
+    msg[1] = (key);
+    msg[2] = (velocity);
+    MPU_SendMidi(msg, 3);
 }
 
-void MPU_NoteOn(char channel, char key, char velocity)
+
+/*---------------------------------------------------------------------
+   Function: MPU_NoteOn
+
+   Sends a full MIDI note on event out to the music device.
+---------------------------------------------------------------------*/
+
+void MPU_NoteOn
+(
+    int32_t channel,
+    int32_t key,
+    int32_t velocity
+)
+
 {
-    char msg[] = { (char)(MIDI_NOTE_ON | channel), key, velocity };
-    MPU_SendMidi(msg, sizeof(msg));
+    char msg[3];
+    msg[0] = (MIDI_NOTE_ON | channel);
+    msg[1] = (key);
+    msg[2] = (velocity);
+    MPU_SendMidi(msg, 3);
 }
 
-void MPU_PolyAftertouch(char channel, char key, char pressure)
+
+/*---------------------------------------------------------------------
+   Function: MPU_PolyAftertouch
+
+   Sends a full MIDI polyphonic aftertouch event out to the music device.
+---------------------------------------------------------------------*/
+
+void MPU_PolyAftertouch
+(
+    int32_t channel,
+    int32_t key,
+    int32_t pressure
+)
+
 {
-    char msg[] = { (char) (MIDI_POLY_AFTER_TCH | channel), key, pressure };
-    MPU_SendMidi(msg, sizeof(msg));
+    char msg[3];
+    msg[0] = (MIDI_POLY_AFTER_TCH | channel);
+    msg[1] = (key);
+    msg[2] = (pressure);
+    MPU_SendMidi(msg, 3);
 }
 
-void MPU_ControlChange(char channel, char number, char value)
+
+/*---------------------------------------------------------------------
+   Function: MPU_ControlChange
+
+   Sends a full MIDI control change event out to the music device.
+---------------------------------------------------------------------*/
+
+void MPU_ControlChange
+(
+    int32_t channel,
+    int32_t number,
+    int32_t value
+)
+
 {
-    char msg[] = { (char) (MIDI_CONTROL_CHANGE | channel), number, value };
-    MPU_SendMidi(msg, sizeof(msg));
+    char msg[3];
+    msg[0] = (MIDI_CONTROL_CHANGE | channel);
+    msg[1] = (number);
+    msg[2] = (value);
+    MPU_SendMidi(msg, 3);
 }
 
-void MPU_ProgramChange(char channel, char program)
+
+/*---------------------------------------------------------------------
+   Function: MPU_ProgramChange
+
+   Sends a full MIDI program change event out to the music device.
+---------------------------------------------------------------------*/
+
+void MPU_ProgramChange
+(
+    int32_t channel,
+    int32_t program
+)
+
 {
-    char msg[] = { (char)(MIDI_PROGRAM_CHANGE | channel), program };
-    MPU_SendMidi(msg, sizeof(msg));
+    char msg[2];
+    msg[0] = (MIDI_PROGRAM_CHANGE | channel);
+    msg[1] = (program);
+    MPU_SendMidi(msg, 2);
 }
 
-void MPU_ChannelAftertouch(char channel, char pressure)
+
+/*---------------------------------------------------------------------
+   Function: MPU_ChannelAftertouch
+
+   Sends a full MIDI channel aftertouch event out to the music device.
+---------------------------------------------------------------------*/
+
+void MPU_ChannelAftertouch
+(
+    int32_t channel,
+    int32_t pressure
+)
+
 {
-    char msg[] = { (char)(MIDI_AFTER_TOUCH | channel), pressure };
-    MPU_SendMidi(msg, sizeof(msg));
+    char msg[2];
+    msg[0] = (MIDI_AFTER_TOUCH | channel);
+    msg[1] = (pressure);
+    MPU_SendMidi(msg, 2);
 }
 
-void MPU_PitchBend(char channel, char lsb, char msb)
+
+/*---------------------------------------------------------------------
+   Function: MPU_PitchBend
+
+   Sends a full MIDI pitch bend event out to the music device.
+---------------------------------------------------------------------*/
+
+void MPU_PitchBend
+(
+    int32_t channel,
+    int32_t lsb,
+    int32_t msb
+)
+
 {
-    char msg[] = { (char)(MIDI_PITCH_BEND | channel), lsb, msb };
-    MPU_SendMidi(msg, sizeof(msg));
+    char msg[3];
+    msg[0] = (MIDI_PITCH_BEND | channel);
+    msg[1] = (lsb);
+    msg[2] = (msb);
+    MPU_SendMidi(msg, 3);
 }
 
+
+
 void MPU_SetTempo(int32_t tempo)
 {
     MIDIPROPTEMPO prop;
@@ -257,3 +454,45 @@ void MPU_SetDivision(int32_t division)
     midiStreamProperty(hmido, (LPBYTE)&prop, MIDIPROP_SET|MIDIPROP_TIMEDIV);
 }
 
+void MPU_SetVolume(int32_t volume)
+{
+    /*
+    HMIXER hmixer;
+    int32_t mixerid;
+    MIXERCONTROLDETAILS mxcd;
+    MIXERCONTROLDETAILS_UNSIGNED mxcdu;
+    MMRESULT mme;
+
+    if (mididevice < 0) return;
+
+    mme = mixerOpen(&hmixer, mididevice, 0,0, MIXER_OBJECTF_MIDIOUT);
+    if (mme) {
+    	puts("Failed opening mixer");
+    	return;
+    }
+
+    mixerGetID(hmixer, &mixerid, MIXER_OBJECTF_HMIXER);
+    printf("mixerid=%d\n",mixerid);
+
+    ZeroMemory(&mxcd,sizeof(mxcd));
+    mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
+    mxcd.dwControlID = MIXERCONTROL_CONTROLTYPE_VOLUME;
+    mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
+    mxcd.paDetails = (LPVOID)&mxcdu;
+    mxcdu.dwValue = (volume << 8) & 0xffff;
+
+    printf("set %d\n",mixerSetControlDetails((HMIXEROBJ)mididevice, &mxcd,
+    	MIXER_OBJECTF_MIDIOUT|MIXER_SETCONTROLDETAILSF_VALUE));
+
+    mixerClose(hmixer);
+    */
+    UNREFERENCED_PARAMETER(volume);
+}
+
+int32_t MPU_GetVolume(void)
+{
+//    if (mididevice < 0) return 0;
+
+    return 0;
+}
+
diff --git a/polymer/eduke32/source/mpu401.h b/polymer/eduke32/source/mpu401.h
index c4f94ce2b..419ce59af 100644
--- a/polymer/eduke32/source/mpu401.h
+++ b/polymer/eduke32/source/mpu401.h
@@ -1,12 +1,10 @@
-//-------------------------------------------------------------------------
 /*
-Copyright (C) 2016 EDuke32 developers and contributors
+Copyright (C) 1994-1995 Apogee Software, Ltd.
 
-This file is part of EDuke32.
-
-EDuke32 is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License version 2
-as published by the Free Software Foundation.
+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
@@ -17,35 +15,51 @@ 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-*/
-//-------------------------------------------------------------------------
 
+Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au)
+*/
 #ifndef __MPU401_H
 #define __MPU401_H
 
 #include "compat.h"
 
-enum MPU_ERRORS
-{
-    MPU_Error = -1,
-    MPU_Ok = 0
-};
+#define MPU_DefaultAddress 0x330
 
+enum MPU_ERRORS
+   {
+   MPU_Warning = -2,
+   MPU_Error = -1,
+   MPU_Ok = 0
+   };
+
+#define MPU_NotFound       -1
+#define MPU_UARTFailed     -2
+
+#define MPU_ReadyToWrite   0x40
+#define MPU_ReadyToRead    0x80
+#define MPU_CmdEnterUART   0x3f
+#define MPU_CmdReset       0xff
+#define MPU_CmdAcknowledge 0xfe
+
+extern int32_t _MPU_CurrentBuffer;
 extern int32_t _MPU_BuffersWaiting;
 
 void MPU_SendMidi( char *data, int32_t count );
+void MPU_SendMidiImmediate( char *data, int32_t count );
 int32_t  MPU_Reset( void );
 int32_t  MPU_Init( int32_t addr );
-void MPU_NoteOff( char channel, char key, char velocity );
-void MPU_NoteOn( char channel, char key, char velocity );
-void MPU_PolyAftertouch( char channel, char key, char pressure );
-void MPU_ControlChange( char channel, char number, char value );
-void MPU_ProgramChange( char channel, char program );
-void MPU_ChannelAftertouch( char channel, char pressure );
-void MPU_PitchBend( char channel, char lsb, char msb );
+void MPU_NoteOff( int32_t channel, int32_t key, int32_t velocity );
+void MPU_NoteOn( int32_t channel, int32_t key, int32_t velocity );
+void MPU_PolyAftertouch( int32_t channel, int32_t key, int32_t pressure );
+void MPU_ControlChange( int32_t channel, int32_t number, int32_t value );
+void MPU_ProgramChange( int32_t channel, int32_t program );
+void MPU_ChannelAftertouch( int32_t channel, int32_t pressure );
+void MPU_PitchBend( int32_t channel, int32_t lsb, int32_t msb );
 
 void MPU_SetTempo(int32_t tempo);
 void MPU_SetDivision(int32_t division);
+void MPU_SetVolume(int32_t volume);
+int32_t  MPU_GetVolume(void);
 
 void MPU_BeginPlayback( void );
 void MPU_Pause(void);
diff --git a/polymer/eduke32/source/music.c b/polymer/eduke32/source/music.c
index 82bb71dee..57a305561 100644
--- a/polymer/eduke32/source/music.c
+++ b/polymer/eduke32/source/music.c
@@ -1,6 +1,6 @@
 //-------------------------------------------------------------------------
 /*
-Copyright (C) 2016 EDuke32 developers and contributors
+Copyright (C) 2010 EDuke32 developers and contributors
 
 This file is part of EDuke32.
 
@@ -20,6 +20,17 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
 //-------------------------------------------------------------------------
 
+/**********************************************************************
+   module: MUSIC.C
+
+   author: James R. Dose
+   date:   March 25, 1994
+
+   Device independant music playback routines.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
 #include <stdio.h>
 #include <stdlib.h>
 #include "music.h"
@@ -27,16 +38,41 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 #include "mpu401.h"
 #include "compat.h"
 
+#ifndef TRUE
+#define TRUE  ( 1 == 1 )
+#define FALSE ( !TRUE )
+#endif
+
+#ifndef min
+#define min(a,b) (((a)<(b))?(a):(b))
+#endif
+
+#ifndef max
+# define max(a,b) ( ((a) > (b)) ? (a) : (b) )
+#endif
+
 int32_t MUSIC_SoundDevice = -1;
 int32_t MUSIC_ErrorCode = MUSIC_Ok;
 
 static midifuncs MUSIC_MidiFunctions;
 
-int32_t MUSIC_InitMidi(midifuncs *Funcs);
+int32_t MUSIC_InitMidi(int32_t card, midifuncs *Funcs, int32_t Address);
 
-#define MUSIC_SetErrorCode(status) MUSIC_ErrorCode = (status);
+#define MUSIC_SetErrorCode( status ) \
+   MUSIC_ErrorCode = ( status );
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_ErrorString
+
+   Returns a pointer to the error message associated with an error
+   number.  A -1 returns a pointer the current error.
+---------------------------------------------------------------------*/
+
+const char *MUSIC_ErrorString
+(
+    int32_t ErrorNumber
+)
 
-const char *MUSIC_ErrorString(int32_t ErrorNumber)
 {
     const char *ErrorString;
 
@@ -51,10 +87,33 @@ const char *MUSIC_ErrorString(int32_t ErrorNumber)
         ErrorString = "Music ok.";
         break;
 
+    case MUSIC_ASSVersion :
+        ErrorString = "Apogee Sound System Version WinMM  "
+                      "Programmed by Jim Dose, Ported by Jonathon Fowler\n"
+                      "(c) Copyright 1996 James R. Dose.  All Rights Reserved.\n";
+        break;
+
+    case MUSIC_SoundCardError :
+    case MUSIC_MPU401Error :
+        ErrorString = "Could not detect MPU-401.";
+        break;
+
+    case MUSIC_InvalidCard :
+        ErrorString = "Invalid Music device.";
+        break;
+
     case MUSIC_MidiError :
         ErrorString = "Error playing MIDI file.";
         break;
 
+    case MUSIC_TaskManError :
+        ErrorString = "TaskMan error.";
+        break;
+
+    case MUSIC_DPMI_Error :
+        ErrorString = "DPMI Error in MUSIC.";
+        break;
+
     default :
         ErrorString = "Unknown Music error code.";
         break;
@@ -63,57 +122,371 @@ const char *MUSIC_ErrorString(int32_t ErrorNumber)
     return(ErrorString);
 }
 
-int32_t MUSIC_Init(int32_t SoundCard)
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_Init
+
+   Selects which sound device to use.
+---------------------------------------------------------------------*/
+
+int32_t MUSIC_Init
+(
+    int32_t SoundCard,
+    int32_t Address
+)
+
 {
-    for (int i = 0; i < 128; i++)
+    int32_t i;
+    int32_t status;
+
+    for (i = 0; i < 128; i++)
+    {
         MIDI_PatchMap[ i ] = i;
+    }
 
     MUSIC_SoundDevice = SoundCard;
 
-    return MUSIC_InitMidi(&MUSIC_MidiFunctions);
+    status = MUSIC_InitMidi(SoundCard, &MUSIC_MidiFunctions, Address);
+
+    return(status);
 }
 
-int32_t MUSIC_Shutdown(void)
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_Shutdown
+
+   Terminates use of sound device.
+---------------------------------------------------------------------*/
+
+int32_t MUSIC_Shutdown
+(
+    void
+)
+
 {
+    int32_t status;
+
+    status = MUSIC_Ok;
+
     MIDI_StopSong();
 
-    return MUSIC_Ok;
+    //MPU_Reset();
+
+    return(status);
 }
 
-void MUSIC_SetVolume(int32_t volume)
-{
-    volume = clamp(volume, 0, 255);
 
+/*---------------------------------------------------------------------
+   Function: MUSIC_SetVolume
+
+   Sets the volume of music playback.
+---------------------------------------------------------------------*/
+
+void MUSIC_SetVolume
+(
+    int32_t volume
+)
+
+{
+    volume = max(0, volume);
+    volume = min(volume, 255);
     if (MUSIC_SoundDevice != -1)
+    {
         MIDI_SetVolume(volume);
+    }
 }
 
-void MUSIC_Continue(void) { MIDI_ContinueSong(); }
 
-void MUSIC_Pause(void) { MIDI_PauseSong(); }
+/*---------------------------------------------------------------------
+   Function: MUSIC_SetMidiChannelVolume
+
+   Sets the volume of music playback on the specified MIDI channel.
+---------------------------------------------------------------------*/
+
+void MUSIC_SetMidiChannelVolume
+(
+    int32_t channel,
+    int32_t volume
+)
 
-int32_t MUSIC_StopSong(void)
 {
+    MIDI_SetUserChannelVolume(channel, volume);
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_ResetMidiChannelVolumes
+
+   Sets the volume of music playback on all MIDI channels to full volume.
+---------------------------------------------------------------------*/
+
+void MUSIC_ResetMidiChannelVolumes
+(
+    void
+)
+
+{
+    MIDI_ResetUserChannelVolume();
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_GetVolume
+
+   Returns the volume of music playback.
+---------------------------------------------------------------------*/
+
+int32_t MUSIC_GetVolume
+(
+    void
+)
+
+{
+    if (MUSIC_SoundDevice == -1)
+    {
+        return(0);
+    }
+    return(MIDI_GetVolume());
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_SetLoopFlag
+
+   Set whether the music will loop or end when it reaches the end of
+   the song.
+---------------------------------------------------------------------*/
+
+void MUSIC_SetLoopFlag
+(
+    int32_t loopflag
+)
+
+{
+    MIDI_SetLoopFlag(loopflag);
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_SongPlaying
+
+   Returns whether there is a song playing.
+---------------------------------------------------------------------*/
+
+int32_t MUSIC_SongPlaying
+(
+    void
+)
+
+{
+    return(MIDI_SongPlaying());
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_Continue
+
+   Continues playback of a paused song.
+---------------------------------------------------------------------*/
+
+void MUSIC_Continue
+(
+    void
+)
+
+{
+    MIDI_ContinueSong();
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_Pause
+
+   Pauses playback of a song.
+---------------------------------------------------------------------*/
+
+void MUSIC_Pause
+(
+    void
+)
+
+{
+    MIDI_PauseSong();
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_StopSong
+
+   Stops playback of current song.
+---------------------------------------------------------------------*/
+
+int32_t MUSIC_StopSong
+(
+    void
+)
+
+{
+    MUSIC_StopFade();
     MIDI_StopSong();
     MUSIC_SetErrorCode(MUSIC_Ok);
-    return MUSIC_Ok;
+    return(MUSIC_Ok);
 }
 
-int32_t MUSIC_PlaySong(char *song, int32_t loopflag)
-{
-    MUSIC_StopSong();
 
-    if (MIDI_PlaySong(song, loopflag) != MIDI_Ok)
+/*---------------------------------------------------------------------
+   Function: MUSIC_PlaySong
+
+   Begins playback of MIDI song.
+---------------------------------------------------------------------*/
+
+int32_t MUSIC_PlaySong
+(
+    char *song,
+    int32_t loopflag
+)
+
+{
+    int32_t status;
+
     {
-        MUSIC_SetErrorCode(MUSIC_MidiError);
-        return MUSIC_Warning;
+        MUSIC_StopSong();
+        status = MIDI_PlaySong(song, loopflag);
+        if (status != MIDI_Ok)
+        {
+            MUSIC_SetErrorCode(MUSIC_MidiError);
+            return(MUSIC_Warning);
+        }
     }
 
-    return MUSIC_Ok;
+    return(MUSIC_Ok);
 }
 
-int32_t MUSIC_InitMidi(midifuncs *Funcs)
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_SetContext
+
+   Sets the song context.
+---------------------------------------------------------------------*/
+
+void MUSIC_SetContext
+(
+    int32_t context
+)
+
 {
+    MIDI_SetContext(context);
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_GetContext
+
+   Returns the current song context.
+---------------------------------------------------------------------*/
+
+int32_t MUSIC_GetContext
+(
+    void
+)
+
+{
+    return MIDI_GetContext();
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_SetSongTick
+
+   Sets the position of the song pointer.
+---------------------------------------------------------------------*/
+
+void MUSIC_SetSongTick
+(
+    uint32_t PositionInTicks
+)
+
+{
+    MIDI_SetSongTick(PositionInTicks);
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_SetSongTime
+
+   Sets the position of the song pointer.
+---------------------------------------------------------------------*/
+
+void MUSIC_SetSongTime
+(
+    uint32_t milliseconds
+)
+
+{
+    MIDI_SetSongTime(milliseconds);
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_SetSongPosition
+
+   Sets the position of the song pointer.
+---------------------------------------------------------------------*/
+
+void MUSIC_SetSongPosition
+(
+    int32_t measure,
+    int32_t beat,
+    int32_t tick
+)
+
+{
+    MIDI_SetSongPosition(measure, beat, tick);
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_GetSongPosition
+
+   Returns the position of the song pointer.
+---------------------------------------------------------------------*/
+
+void MUSIC_GetSongPosition
+(
+    songposition *pos
+)
+
+{
+    MIDI_GetSongPosition(pos);
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_GetSongLength
+
+   Returns the length of the song.
+---------------------------------------------------------------------*/
+
+void MUSIC_GetSongLength
+(
+    songposition *pos
+)
+
+{
+    MIDI_GetSongLength(pos);
+}
+
+
+int32_t MUSIC_InitMidi
+(
+    int32_t        card,
+    midifuncs *Funcs,
+    int32_t        Address
+)
+
+{
+    UNREFERENCED_PARAMETER(card);
+    UNREFERENCED_PARAMETER(Address);
     Funcs->NoteOff           = MPU_NoteOff;
     Funcs->NoteOn            = MPU_NoteOn;
     Funcs->PolyAftertouch    = MPU_PolyAftertouch;
@@ -121,12 +494,101 @@ int32_t MUSIC_InitMidi(midifuncs *Funcs)
     Funcs->ProgramChange     = MPU_ProgramChange;
     Funcs->ChannelAftertouch = MPU_ChannelAftertouch;
     Funcs->PitchBend         = MPU_PitchBend;
+    Funcs->ReleasePatches    = NULL;
+    Funcs->LoadPatch         = NULL;
+    Funcs->SetVolume         = NULL /*MPU_SetVolume*/;
+    Funcs->GetVolume         = NULL /*MPU_GetVolume*/;
 
     MIDI_SetMidiFuncs(Funcs);
 
-    return MIDI_Ok;
+    return(MIDI_Ok);
 }
 
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_FadeVolume
+
+   Fades music volume from current level to another over a specified
+   period of time.
+---------------------------------------------------------------------*/
+
+int32_t MUSIC_FadeVolume
+(
+    int32_t tovolume,
+    int32_t milliseconds
+)
+
+{
+    UNREFERENCED_PARAMETER(milliseconds);
+    MIDI_SetVolume(tovolume);
+    return(MUSIC_Ok);
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_FadeActive
+
+   Returns whether the fade routine is active.
+---------------------------------------------------------------------*/
+
+int32_t MUSIC_FadeActive
+(
+    void
+)
+
+{
+    return(0);
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_StopFade
+
+   Stops fading the music.
+---------------------------------------------------------------------*/
+
+void MUSIC_StopFade
+(
+    void
+)
+
+{}
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_RerouteMidiChannel
+
+   Sets callback function to reroute MIDI commands from specified
+   function.
+---------------------------------------------------------------------*/
+
+void MUSIC_RerouteMidiChannel
+(
+    int32_t channel,
+    int32_t(*function)(int32_t, int32_t, int32_t)
+)
+
+{
+    MIDI_RerouteMidiChannel(channel, function);
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_RegisterTimbreBank
+
+   Halts playback of all sounds.
+---------------------------------------------------------------------*/
+
+void MUSIC_RegisterTimbreBank
+(
+    char *timbres
+)
+
+{
+    UNREFERENCED_PARAMETER(timbres);
+}
+
+
 void MUSIC_Update(void)
 {
     MIDI_UpdateMusic();
diff --git a/polymer/eduke32/source/sdlmusic.c b/polymer/eduke32/source/sdlmusic.c
index a9431be58..faa66fc30 100644
--- a/polymer/eduke32/source/sdlmusic.c
+++ b/polymer/eduke32/source/sdlmusic.c
@@ -64,10 +64,12 @@ static int32_t external_midi = 0;
 
 int32_t MUSIC_ErrorCode = MUSIC_Ok;
 
+static char warningMessage[80];
 static char errorMessage[80];
 
 static int32_t music_initialized = 0;
 static int32_t music_context = 0;
+static int32_t music_loopflag = MUSIC_PlayOnce;
 static Mix_Music *music_musicchunk = NULL;
 
 static void setErrorMessage(const char *msg)
@@ -81,45 +83,70 @@ const char *MUSIC_ErrorString(int32_t ErrorNumber)
 {
     switch (ErrorNumber)
     {
+    case MUSIC_Warning:
+        return(warningMessage);
+
     case MUSIC_Error:
-        return errorMessage;
+        return(errorMessage);
 
     case MUSIC_Ok:
-        return "OK; no error.";
+        return("OK; no error.");
+
+    case MUSIC_ASSVersion:
+        return("Incorrect sound library version.");
+
+    case MUSIC_SoundCardError:
+        return("General sound card error.");
+
+    case MUSIC_InvalidCard:
+        return("Invalid sound card.");
 
     case MUSIC_MidiError:
-        return "MIDI error.";
+        return("MIDI error.");
+
+    case MUSIC_MPU401Error:
+        return("MPU401 error.");
+
+    case MUSIC_TaskManError:
+        return("Task Manager error.");
+
+        //case MUSIC_FMNotDetected:
+        //    return("FM not detected error.");
+
+    case MUSIC_DPMI_Error:
+        return("DPMI error.");
 
     default:
-        return "Unknown error.";
+        return("Unknown error.");
     } // switch
 
-    return NULL;
+    return(NULL);
 } // MUSIC_ErrorString
 
-int32_t MUSIC_Init(int32_t SoundCard)
+int32_t MUSIC_Init(int32_t SoundCard, int32_t Address)
 {
 #ifdef __ANDROID__
 	music_initialized = 1;
-	return MUSIC_Ok;
+	return(MUSIC_Ok);
 #endif
     // Use an external MIDI player if the user has specified to do so
     char *command = getenv("EDUKE32_MUSIC_CMD");
     const SDL_version *linked = Mix_Linked_Version();
 
     UNREFERENCED_PARAMETER(SoundCard);
+    UNREFERENCED_PARAMETER(Address);
 
     if (music_initialized)
     {
         setErrorMessage("Music system is already initialized.");
-        return MUSIC_Error;
+        return(MUSIC_Error);
     } // if
 
     if (SDL_VERSIONNUM(linked->major,linked->minor,linked->patch) < MIX_REQUIREDVERSION)
     {
         // reject running with SDL_Mixer versions older than what is stated in sdl_inc.h
         initprintf("You need at least v%d.%d.%d of SDL_mixer for music\n",SDL_MIXER_MIN_X,SDL_MIXER_MIN_Y,SDL_MIXER_MIN_Z);
-        return MUSIC_Error;
+        return(MUSIC_Error);
     }
 
     external_midi = (command != NULL && command[0] != 0);
@@ -205,35 +232,38 @@ int32_t MUSIC_Init(int32_t SoundCard)
 # endif
 #endif
         music_initialized = 1;
-        return MUSIC_Ok;
+        return(MUSIC_Ok);
 
 fallback:
         initprintf("Error setting music command, falling back to timidity.\n");
     }
 
-    static const char *s[] = { "/etc/timidity.cfg", "/etc/timidity/timidity.cfg", "/etc/timidity/freepats.cfg" };
-    FILE *fp;
-
-    for (int i = ARRAY_SIZE(s)-1; i>=0; i--)
     {
-        fp = Bfopen(s[i], "r");
-        if (fp == NULL)
+        static const char *s[] = { "/etc/timidity.cfg", "/etc/timidity/timidity.cfg", "/etc/timidity/freepats.cfg" };
+        FILE *fp;
+        int32_t i;
+
+        for (i = ARRAY_SIZE(s)-1; i>=0; i--)
         {
-            if (i == 0)
+            fp = Bfopen(s[i], "r");
+            if (fp == NULL)
             {
-                initprintf("Error: couldn't open any of the following files:\n");
-                for (i = ARRAY_SIZE(s)-1; i>=0; i--)
-                    initprintf("%s\n",s[i]);
-                return MUSIC_Error;
+                if (i == 0)
+                {
+                    initprintf("Error: couldn't open any of the following files:\n");
+                    for (i = ARRAY_SIZE(s)-1; i>=0; i--)
+                        initprintf("%s\n",s[i]);
+                    return(MUSIC_Error);
+                }
+                continue;
             }
-            continue;
+            else break;
         }
-        else break;
+        Bfclose(fp);
     }
-    Bfclose(fp);
 
     music_initialized = 1;
-    return MUSIC_Ok;
+    return(MUSIC_Ok);
 } // MUSIC_Init
 
 
@@ -248,11 +278,18 @@ int32_t MUSIC_Shutdown(void)
     MUSIC_StopSong();
     music_context = 0;
     music_initialized = 0;
+    music_loopflag = MUSIC_PlayOnce;
 
-    return MUSIC_Ok;
+    return(MUSIC_Ok);
 } // MUSIC_Shutdown
 
 
+void MUSIC_SetMaxFMMidiChannel(int32_t channel)
+{
+    UNREFERENCED_PARAMETER(channel);
+} // MUSIC_SetMaxFMMidiChannel
+
+
 void MUSIC_SetVolume(int32_t volume)
 {
     volume = max(0, volume);
@@ -262,6 +299,36 @@ void MUSIC_SetVolume(int32_t volume)
 } // MUSIC_SetVolume
 
 
+void MUSIC_SetMidiChannelVolume(int32_t channel, int32_t volume)
+{
+    UNREFERENCED_PARAMETER(channel);
+    UNREFERENCED_PARAMETER(volume);
+} // MUSIC_SetMidiChannelVolume
+
+
+void MUSIC_ResetMidiChannelVolumes(void)
+{
+} // MUSIC_ResetMidiChannelVolumes
+
+
+int32_t MUSIC_GetVolume(void)
+{
+    return(Mix_VolumeMusic(-1) << 1);  // convert 0-128 to 0-255.
+} // MUSIC_GetVolume
+
+
+void MUSIC_SetLoopFlag(int32_t loopflag)
+{
+    music_loopflag = loopflag;
+} // MUSIC_SetLoopFlag
+
+
+int32_t MUSIC_SongPlaying(void)
+{
+    return((Mix_PlayingMusic()) ? TRUE : FALSE);
+} // MUSIC_SongPlaying
+
+
 void MUSIC_Continue(void)
 {
     if (Mix_PausedMusic())
@@ -311,7 +378,7 @@ int32_t MUSIC_StopSong(void)
             external_midi_pid = -1;
         }
 
-        return MUSIC_Ok;
+        return(MUSIC_Ok);
     }
 #endif
 
@@ -319,7 +386,7 @@ int32_t MUSIC_StopSong(void)
     if (!Mix_QuerySpec(NULL, NULL, NULL))
     {
         setErrorMessage("Need FX system initialized, too. Sorry.");
-        return MUSIC_Error;
+        return(MUSIC_Error);
     } // if
 
     if ((Mix_PlayingMusic()) || (Mix_PausedMusic()))
@@ -330,7 +397,7 @@ int32_t MUSIC_StopSong(void)
 
     music_musicchunk = NULL;
 
-    return MUSIC_Ok;
+    return(MUSIC_Ok);
 } // MUSIC_StopSong
 
 #if defined FORK_EXEC_MIDI
@@ -437,5 +504,82 @@ int32_t MUSIC_PlaySong(char *song, int32_t loopflag)
     return MUSIC_Ok;
 }
 
+
+void MUSIC_SetContext(int32_t context)
+{
+    music_context = context;
+} // MUSIC_SetContext
+
+
+int32_t MUSIC_GetContext(void)
+{
+    return(music_context);
+} // MUSIC_GetContext
+
+
+void MUSIC_SetSongTick(uint32_t PositionInTicks)
+{
+    UNREFERENCED_PARAMETER(PositionInTicks);
+} // MUSIC_SetSongTick
+
+
+void MUSIC_SetSongTime(uint32_t milliseconds)
+{
+    UNREFERENCED_PARAMETER(milliseconds);
+}// MUSIC_SetSongTime
+
+
+void MUSIC_SetSongPosition(int32_t measure, int32_t beat, int32_t tick)
+{
+    UNREFERENCED_PARAMETER(measure);
+    UNREFERENCED_PARAMETER(beat);
+    UNREFERENCED_PARAMETER(tick);
+} // MUSIC_SetSongPosition
+
+
+void MUSIC_GetSongPosition(songposition *pos)
+{
+    UNREFERENCED_PARAMETER(pos);
+} // MUSIC_GetSongPosition
+
+
+void MUSIC_GetSongLength(songposition *pos)
+{
+    UNREFERENCED_PARAMETER(pos);
+} // MUSIC_GetSongLength
+
+
+int32_t MUSIC_FadeVolume(int32_t tovolume, int32_t milliseconds)
+{
+    UNREFERENCED_PARAMETER(tovolume);
+    Mix_FadeOutMusic(milliseconds);
+    return(MUSIC_Ok);
+} // MUSIC_FadeVolume
+
+
+int32_t MUSIC_FadeActive(void)
+{
+    return((Mix_FadingMusic() == MIX_FADING_OUT) ? TRUE : FALSE);
+} // MUSIC_FadeActive
+
+
+void MUSIC_StopFade(void)
+{
+} // MUSIC_StopFade
+
+
+void MUSIC_RerouteMidiChannel(int32_t channel, int32_t (*function)(int32_t, int32_t, int32_t))
+{
+    UNREFERENCED_PARAMETER(channel);
+    UNREFERENCED_PARAMETER(function);
+} // MUSIC_RerouteMidiChannel
+
+
+void MUSIC_RegisterTimbreBank(char *timbres)
+{
+    UNREFERENCED_PARAMETER(timbres);
+} // MUSIC_RegisterTimbreBank
+
+
 void MUSIC_Update(void)
 {}
diff --git a/polymer/eduke32/source/sounds.c b/polymer/eduke32/source/sounds.c
index 7ebc5f7b5..45b27c6de 100644
--- a/polymer/eduke32/source/sounds.c
+++ b/polymer/eduke32/source/sounds.c
@@ -101,7 +101,7 @@ void S_MusicStartup(void)
 {
     initprintf("Initializing music...\n");
 
-    if (MUSIC_Init(0) == MUSIC_Ok || MUSIC_Init(1) == MUSIC_Ok)
+    if (MUSIC_Init(0, 0) == MUSIC_Ok || MUSIC_Init(1, 0) == MUSIC_Ok)
     {
         MUSIC_SetVolume(MASTER_VOLUME(ud.config.MusicVolume));
         return;