shithub: choc

Download patch

ref: 98f798dd4b926ff8e97ed0b058c750ba2d5e2b06
parent: b36170d140fa89f2977e6a14c1042dcaa524b949
parent: 939cbfeb115338cf7868e516019a4ae832b1ebde
author: Simon Howard <fraggle+github@gmail.com>
date: Mon Jan 2 10:02:20 EST 2017

Merge branch 'master' into commandline-savedir

--- a/NEWS.md
+++ b/NEWS.md
@@ -1,6 +1,12 @@
 ## HEAD
 
 ### General
+  * A new parameter -savedir allows users to specify a directory from
+    which to load and save games. (thanks CapnClever)
+
+## 2.3.0 (2016-12-29)
+
+### General
   * Bash completion scripts are included (thanks Fabian)
   * The OS X launcher now supports the .lmp file format (thanks Jon)
   * Pitch-shifting from early versions of Doom, Heretic, and Hexen.
@@ -21,8 +27,9 @@
   * Multiple capitalizations are now tried when searching for WAD files,
     for convenience when running on case sensitive filesystems (thanks
     Fabian).
-  * New parameter -savedir allows users to specify a directory from
-    which to load and save games. (thanks CapnClever)
+  * A new command line argument, `-strictdemos`, was added, to allow
+    more careful control over demo format extensions. Such extensions
+    are now forbidden in WAD files and warning messages are shown.
 
 ### Build systems
   * There is better compatibility with BSD Make (thanks R.Rebello)
--- a/codeblocks/config.h
+++ b/codeblocks/config.h
@@ -9,13 +9,13 @@
 #define PACKAGE_NAME "Chocolate Doom"
 
 /* Define to the full name and version of this package. */
-#define PACKAGE_STRING "Chocolate Doom 2.2.1"
+#define PACKAGE_STRING "Chocolate Doom 2.3.0"
 
 /* Define to the one symbol short name of this package. */
 #define PACKAGE_TARNAME "chocolate-doom"
 
 /* Define to the version of this package. */
-#define PACKAGE_VERSION "2.2.1"
+#define PACKAGE_VERSION "2.3.0"
 
 /* Change this when you create your awesome forked version */
 #define PROGRAM_PREFIX "chocolate-"
@@ -24,7 +24,7 @@
 #define STDC_HEADERS 1
 
 /* Version number of package */
-#define VERSION "2.2.1"
+#define VERSION "2.3.0"
 
 /* Define to 1 if your processor stores words with the most significant byte
    first (like Motorola and SPARC, unlike Intel and VAX). */
--- a/codeblocks/game-res.rc
+++ b/codeblocks/game-res.rc
@@ -1,8 +1,8 @@
 1 ICON "../data/doom.ico"
 
 1 VERSIONINFO
-PRODUCTVERSION 2,2,1,0
-FILEVERSION 2,2,1,0
+PRODUCTVERSION 2,3,0,0
+FILEVERSION 2,3,0,0
 FILETYPE 1
 {
  BLOCK "StringFileInfo"
@@ -9,13 +9,13 @@
  {
   BLOCK "040904E4"
   {
-   VALUE "FileVersion", "2.2.1"
-   VALUE "FileDescription", "2.2.1"
+   VALUE "FileVersion", "2.3.0"
+   VALUE "FileDescription", "2.3.0"
    VALUE "InternalName", "Chocolate Doom"
    VALUE "CompanyName", "Chocolate Doom"
    VALUE "LegalCopyright", "GNU General Public License"
    VALUE "ProductName", "Chocolate Doom"
-   VALUE "ProductVersion", "2.2.1"
+   VALUE "ProductVersion", "2.3.0"
   }
  }
  BLOCK "VarFileInfo"
--- a/codeblocks/setup-res.rc
+++ b/codeblocks/setup-res.rc
@@ -1,8 +1,8 @@
 1 ICON "../data/setup.ico"
 
 1 VERSIONINFO
-PRODUCTVERSION 2,2,1,0
-FILEVERSION 2,2,1,0
+PRODUCTVERSION 2,3,0,0
+FILEVERSION 2,3,0,0
 FILETYPE 1
 {
  BLOCK "StringFileInfo"
@@ -9,13 +9,13 @@
  {
   BLOCK "040904E4"
   {
-   VALUE "FileVersion", "2.2.1"
+   VALUE "FileVersion", "2.3.0"
    VALUE "FileDescription", "Chocolate Doom Setup"
    VALUE "InternalName", "chocolate-setup"
    VALUE "CompanyName", "Chocolate Doom"
    VALUE "LegalCopyright", "GNU General Public License"
    VALUE "ProductName", "Chocolate Doom Setup"
-   VALUE "ProductVersion", "2.2.1"
+   VALUE "ProductVersion", "2.3.0"
   }
  }
  BLOCK "VarFileInfo"
--- a/configure.ac
+++ b/configure.ac
@@ -1,4 +1,4 @@
-AC_INIT(Chocolate Doom, 2.2.1, chocolate-doom-dev-list@chocolate-doom.org,
+AC_INIT(Chocolate Doom, 2.3.0, chocolate-doom-dev-list@chocolate-doom.org,
         chocolate-doom)
 
 PACKAGE_SHORTNAME=${PACKAGE_NAME% Doom}
--- a/msvc/config.h
+++ b/msvc/config.h
@@ -11,19 +11,19 @@
 #define PACKAGE_NAME "Chocolate Doom"
 
 /* Define to the full name and version of this package. */
-#define PACKAGE_STRING "Chocolate Doom 2.2.1"
+#define PACKAGE_STRING "Chocolate Doom 2.3.0"
 
 /* Define to the one symbol short name of this package. */
 #define PACKAGE_TARNAME "chocolate-doom"
 
 /* Define to the version of this package. */
-#define PACKAGE_VERSION "2.2.1"
+#define PACKAGE_VERSION "2.3.0"
 
 /* Change this when you create your awesome forked version */
 #define PROGRAM_PREFIX "chocolate-"
 
 /* Version number of package */
-#define VERSION "2.2.1"
+#define VERSION "2.3.0"
 
 /* Define to 1 if your processor stores words with the most significant byte
    first (like Motorola and SPARC, unlike Intel and VAX). */
--- a/msvc/win32.rc
+++ b/msvc/win32.rc
@@ -25,8 +25,8 @@
 #endif
 
 1 VERSIONINFO
-PRODUCTVERSION 2,2,1,0
-FILEVERSION 2,2,1,0
+PRODUCTVERSION 2,3,0,0
+FILEVERSION 2,3,0,0
 FILETYPE 1
 BEGIN
 	BLOCK "StringFileInfo"
@@ -34,12 +34,12 @@
 		BLOCK "040904E4"
 		BEGIN
 			VALUE "FileVersion", "1.0.0"
-			VALUE "FileDescription", "Chocolate Doom 2.2.1"
+			VALUE "FileDescription", "Chocolate Doom 2.3.0"
 			VALUE "InternalName", "chocolate-doom"
 			VALUE "CompanyName", "fraggle@gmail.com"
 			VALUE "LegalCopyright", "GNU General Public License"
 			VALUE "ProductName", "Chocolate Doom"
-			VALUE "ProductVersion", "2.2.1"
+			VALUE "ProductVersion", "2.3.0"
 		END
 	END
 	BLOCK "VarFileInfo"
--- a/src/heretic/p_enemy.c
+++ b/src/heretic/p_enemy.c
@@ -845,8 +845,8 @@
     r1 = P_SubRandom();
     r2 = P_SubRandom();
 
-    mo = P_SpawnMobj(actor->x + (r1 << 11),
-                     actor->y + (r2 << 11), actor->z,
+    mo = P_SpawnMobj(actor->x + (r2 << 11),
+                     actor->y + (r1 << 11), actor->z,
                      MT_BLOOD);
     mo->momx = P_SubRandom() << 10;
     mo->momy = P_SubRandom() << 10;
@@ -920,9 +920,9 @@
         r1 = P_SubRandom();
         r2 = P_SubRandom();
         r3 = P_SubRandom();
-        P_SpawnMobj(actor->x + (r1 << 10),
+        P_SpawnMobj(actor->x + (r3 << 10),
                     actor->y + (r2 << 10),
-                    actor->z + (r3 << 10), MT_PUFFY);
+                    actor->z + (r1 << 10), MT_PUFFY);
     }
 }
 
@@ -1769,8 +1769,8 @@
     r2 = P_SubRandom();
 
     actor->z = actor->floorz;
-    mo = P_SpawnMobj(actor->x + (r1 << 10),
-                     actor->y + (r2 << 10), ONFLOORZ,
+    mo = P_SpawnMobj(actor->x + (r2 << 10),
+                     actor->y + (r1 << 10), ONFLOORZ,
                      MT_MNTRFX3);
     mo->target = actor->target;
     mo->momx = 1;               // Force block checking
@@ -2416,11 +2416,12 @@
 void A_SpawnTeleGlitter(mobj_t * actor)
 {
     mobj_t *mo;
-    int r;
+    int r1, r2;
 
-    r = P_Random();
-    mo = P_SpawnMobj(actor->x + ((r & 31) - 16) * FRACUNIT,
-                     actor->y + ((P_Random() & 31) - 16) * FRACUNIT,
+    r1 = P_Random();
+    r2 = P_Random();
+    mo = P_SpawnMobj(actor->x + ((r2 & 31) - 16) * FRACUNIT,
+                     actor->y + ((r1 & 31) - 16) * FRACUNIT,
                      actor->subsector->sector->floorheight, MT_TELEGLITTER);
     mo->momz = FRACUNIT / 4;
 }
@@ -2434,11 +2435,12 @@
 void A_SpawnTeleGlitter2(mobj_t * actor)
 {
     mobj_t *mo;
-    int r;
+    int r1, r2;
 
-    r = P_Random();
-    mo = P_SpawnMobj(actor->x + ((r & 31) - 16) * FRACUNIT,
-                     actor->y + ((P_Random() & 31) - 16) * FRACUNIT,
+    r1 = P_Random();
+    r2 = P_Random();
+    mo = P_SpawnMobj(actor->x + ((r2 & 31) - 16) * FRACUNIT,
+                     actor->y + ((r1 & 31) - 16) * FRACUNIT,
                      actor->subsector->sector->floorheight, MT_TELEGLITTER2);
     mo->momz = FRACUNIT / 4;
 }
--- a/src/hexen/a_action.c
+++ b/src/hexen/a_action.c
@@ -1175,9 +1175,9 @@
         r1 = P_Random();
         r2 = P_Random();
         r3 = P_Random();
-        mo = P_SpawnMobj(actor->x + ((r1 - 128) << 12),
+        mo = P_SpawnMobj(actor->x + ((r3 - 128) << 12),
                          actor->y + ((r2 - 128) << 12),
-                         actor->z + (r3 * actor->height / 256),
+                         actor->z + (r1 * actor->height / 256),
                          MT_ZARMORCHUNK);
         P_SetMobjState(mo, mo->info->spawnstate + i);
         if (mo)
--- a/src/hexen/p_enemy.c
+++ b/src/hexen/p_enemy.c
@@ -1481,13 +1481,14 @@
 void A_MntrFloorFire(mobj_t * actor)
 {
     mobj_t *mo;
-    int r;
+    int r1, r2;
 
-    r = P_SubRandom();
+    r1 = P_SubRandom();
+    r2 = P_SubRandom();
 
     actor->z = actor->floorz;
-    mo = P_SpawnMobj(actor->x + (r << 10),
-                     actor->y + (P_SubRandom() << 10), ONFLOORZ,
+    mo = P_SpawnMobj(actor->x + (r2 << 10),
+                     actor->y + (r1 << 10), ONFLOORZ,
                      MT_MNTRFX3);
     mo->target = actor->target;
     mo->momx = 1;               // Force block checking
@@ -2411,10 +2412,12 @@
 void A_SerpentSpawnGibs(mobj_t * actor)
 {
     mobj_t *mo;
-    int r = P_Random();
+    int r1, r2;
 
-    mo = P_SpawnMobj(actor->x + ((r - 128) << 12),
-                     actor->y + ((P_Random() - 128) << 12),
+    r1 = P_Random();
+    r2 = P_Random();
+    mo = P_SpawnMobj(actor->x + ((r2 - 128) << 12),
+                     actor->y + ((r1 - 128) << 12),
                      actor->floorz + FRACUNIT, MT_SERPENT_GIB1);
     if (mo)
     {
@@ -2422,9 +2425,10 @@
         mo->momy = (P_Random() - 128) << 6;
         mo->floorclip = 6 * FRACUNIT;
     }
-    r = P_Random();
-    mo = P_SpawnMobj(actor->x + ((r - 128) << 12),
-                     actor->y + ((P_Random() - 128) << 12),
+    r1 = P_Random();
+    r2 = P_Random();
+    mo = P_SpawnMobj(actor->x + ((r2 - 128) << 12),
+                     actor->y + ((r1 - 128) << 12),
                      actor->floorz + FRACUNIT, MT_SERPENT_GIB2);
     if (mo)
     {
@@ -2432,9 +2436,10 @@
         mo->momy = (P_Random() - 128) << 6;
         mo->floorclip = 6 * FRACUNIT;
     }
-    r = P_Random();
-    mo = P_SpawnMobj(actor->x + ((r - 128) << 12),
-                     actor->y + ((P_Random() - 128) << 12),
+    r1 = P_Random();
+    r2 = P_Random();
+    mo = P_SpawnMobj(actor->x + ((r2 - 128) << 12),
+                     actor->y + ((r1 - 128) << 12),
                      actor->floorz + FRACUNIT, MT_SERPENT_GIB3);
     if (mo)
     {
@@ -2797,9 +2802,9 @@
     r2 = P_SubRandom();
     r3 = P_SubRandom();
 
-    mo = P_SpawnMobj(actor->x + (r1 << 12), actor->y
+    mo = P_SpawnMobj(actor->x + (r3 << 12), actor->y
                      + (r2 << 12),
-                     actor->z + (r3 << 11),
+                     actor->z + (r1 << 11),
                      MT_BISHOPPAINBLUR);
     if (mo)
     {
@@ -3048,9 +3053,9 @@
         r1 = P_Random();
         r2 = P_Random();
         r3 = P_Random();
-        mo = P_SpawnMobj(actor->x + ((r1 - 128) << 14),
+        mo = P_SpawnMobj(actor->x + ((r3 - 128) << 14),
                          actor->y + ((r2 - 128) << 14),
-                         actor->z + ((r3 - 128) << 12),
+                         actor->z + ((r1 - 128) << 12),
                          MT_DRAGON_FX2);
         if (mo)
         {
@@ -4865,10 +4870,10 @@
         r2 = P_Random();
         r3 = P_Random();
         mo = P_SpawnMobj(actor->x +
-                         (((r1 - 128) * actor->radius) >> 7),
+                         (((r3 - 128) * actor->radius) >> 7),
                          actor->y +
                          (((r2 - 128) * actor->radius) >> 7),
-                         actor->z + (r3 * actor->height / 255),
+                         actor->z + (r1 * actor->height / 255),
                          MT_ICECHUNK);
         P_SetMobjState(mo, mo->info->spawnstate + (P_Random() % 3));
         if (mo)
@@ -4885,10 +4890,10 @@
         r2 = P_Random();
         r3 = P_Random();
         mo = P_SpawnMobj(actor->x +
-                         (((r1 - 128) * actor->radius) >> 7),
+                         (((r3 - 128) * actor->radius) >> 7),
                          actor->y +
                          (((r2 - 128) * actor->radius) >> 7),
-                         actor->z + (r3 * actor->height / 255),
+                         actor->z + (r1 * actor->height / 255),
                          MT_ICECHUNK);
         P_SetMobjState(mo, mo->info->spawnstate + (P_Random() % 3));
         if (mo)
--- a/src/hexen/p_mobj.c
+++ b/src/hexen/p_mobj.c
@@ -1830,9 +1830,12 @@
 void P_BloodSplatter2(fixed_t x, fixed_t y, fixed_t z, mobj_t * originator)
 {
     mobj_t *mo;
+    int r1, r2;
 
-    mo = P_SpawnMobj(x + ((P_Random() - 128) << 11),
-                     y + ((P_Random() - 128) << 11), z, MT_AXEBLOOD);
+    r1 = P_Random();
+    r2 = P_Random();
+    mo = P_SpawnMobj(x + ((r2 - 128) << 11),
+                     y + ((r1 - 128) << 11), z, MT_AXEBLOOD);
     mo->target = originator;
 }
 
--- a/src/hexen/p_pspr.c
+++ b/src/hexen/p_pspr.c
@@ -868,9 +868,9 @@
         r1 = P_Random();
         r2 = P_Random();
         r3 = P_Random();
-        P_SpawnMobj(actor->x + ((r1 - 128) << 12), actor->y
+        P_SpawnMobj(actor->x + ((r3 - 128) << 12), actor->y
                     + ((r2 - 128) << 12),
-                    actor->z + ((r3 - 128) << 11), MT_FSWORD_FLAME);
+                    actor->z + ((r1 - 128) << 11), MT_FSWORD_FLAME);
     }
 }
 
@@ -1003,8 +1003,8 @@
     }
     r1 = P_Random();
     r2 = P_Random();
-    mo = P_SpawnMobj(actor->x + ((r1 - 128) * actor->radius / 256),
-                     actor->y + ((r2 - 128) * actor->radius / 256),
+    mo = P_SpawnMobj(actor->x + ((r2 - 128) * actor->radius / 256),
+                     actor->y + ((r1 - 128) * actor->radius / 256),
                      actor->z + deltaZ, MT_LIGHTNING_ZAP);
     if (mo)
     {
--- a/src/i_oplmusic.c
+++ b/src/i_oplmusic.c
@@ -101,10 +101,6 @@
 
 typedef struct
 {
-    // Data for each channel.
-
-    opl_channel_data_t channels[MIDI_CHANNELS_PER_TRACK];
-
     // Track iterator used to read new events.
 
     midi_track_iter_t *iter;
@@ -149,7 +145,8 @@
     unsigned int note_volume;
 
     // The current volume (register value) that has been set for this channel.
-    unsigned int reg_volume;
+    unsigned int car_volume;
+    unsigned int mod_volume;
 
     // Pan.
     unsigned int reg_pan;
@@ -156,10 +153,6 @@
 
     // Priority.
     unsigned int priority;
-
-    // Next in linked list; a voice is always either in the
-    // free list or the allocated list.
-    opl_voice_t *next;
 };
 
 // Operators used by the different voices.
@@ -325,12 +318,17 @@
 // Voices:
 
 static opl_voice_t voices[OPL_NUM_VOICES * 2];
-static opl_voice_t *voice_free_list;
-static opl_voice_t *voice_alloced_list;
+static opl_voice_t *voice_free_list[OPL_NUM_VOICES * 2];
+static opl_voice_t *voice_alloced_list[OPL_NUM_VOICES * 2];
+static int voice_free_num;
 static int voice_alloced_num;
 static int opl_opl3mode;
 static int num_opl_voices;
 
+// Data for each channel.
+
+static opl_channel_data_t channels[MIDI_CHANNELS_PER_TRACK];
+
 // Track data for playing tracks:
 
 static opl_track_data_t *tracks;
@@ -366,17 +364,10 @@
 {
     byte *lump;
 
-    lump = W_CacheLumpName("GENMIDI", PU_STATIC);
+    lump = W_CacheLumpName(DEH_String("genmidi"), PU_STATIC);
 
-    // Check header
+    // DMX does not check header
 
-    if (strncmp((char *) lump, GENMIDI_HEADER, strlen(GENMIDI_HEADER)) != 0)
-    {
-        W_ReleaseLumpName("GENMIDI");
-
-        return false;
-    }
-
     main_instrs = (genmidi_instr_t *) (lump + strlen(GENMIDI_HEADER));
     percussion_instrs = main_instrs + GENMIDI_NUM_INSTRS;
     main_instr_names =
@@ -391,11 +382,11 @@
 static opl_voice_t *GetFreeVoice(void)
 {
     opl_voice_t *result;
-    opl_voice_t **rover;
+    int i;
 
     // None available?
 
-    if (voice_free_list == NULL)
+    if (voice_free_num == 0)
     {
         return NULL;
     }
@@ -402,86 +393,66 @@
 
     // Remove from free list
 
-    result = voice_free_list;
-    voice_free_list = voice_free_list->next;
+    result = voice_free_list[0];
 
-    // Add to allocated list
+    voice_free_num--;
 
-    rover = &voice_alloced_list;
-
-    while (*rover != NULL)
+    for (i = 0; i < voice_free_num; i++)
     {
-        rover = &(*rover)->next;
+        voice_free_list[i] = voice_free_list[i + 1];
     }
 
-    *rover = result;
-    result->next = NULL;
+    // Add to allocated list
 
-    voice_alloced_num++;
+    voice_alloced_list[voice_alloced_num++] = result;
 
     return result;
 }
 
-// Remove a voice from the allocated voices list.
+// Release a voice back to the freelist.
 
-static void RemoveVoiceFromAllocedList(opl_voice_t *voice)
+static void VoiceKeyOff(opl_voice_t *voice);
+
+static void ReleaseVoice(int index)
 {
-    opl_voice_t **rover;
+    opl_voice_t *voice;
+    boolean double_voice;
+    int i;
 
-    rover = &voice_alloced_list;
-
-    // Search the list until we find the voice, then remove it.
-
-    while (*rover != NULL)
+    // Doom 2 1.666 OPL crash emulation.
+    if (index >= voice_alloced_num)
     {
-        if (*rover == voice)
-        {
-            *rover = voice->next;
-            voice->next = NULL;
-            voice_alloced_num--;
-            break;
-        }
 
-        rover = &(*rover)->next;
+        voice_alloced_num = 0;
+        voice_free_num = 0;
+        return;
     }
-}
 
-// Release a voice back to the freelist.
+    voice = voice_alloced_list[index];
 
-static void VoiceKeyOff(opl_voice_t *voice);
+    VoiceKeyOff(voice);
 
-static void ReleaseVoice(opl_voice_t *voice)
-{
-    opl_voice_t **rover;
-    opl_voice_t *next;
-    boolean double_voice;
-
     voice->channel = NULL;
     voice->note = 0;
 
     double_voice = voice->current_instr_voice != 0;
-    next = voice->next;
-
+    
     // Remove from alloced list.
 
-    RemoveVoiceFromAllocedList(voice);
+    voice_alloced_num--;
 
-    // Search to the end of the freelist (This is how Doom behaves!)
-
-    rover = &voice_free_list;
-
-    while (*rover != NULL)
+    for (i = index; i < voice_alloced_num; i++)
     {
-        rover = &(*rover)->next;
+        voice_alloced_list[i] = voice_alloced_list[i + 1];
     }
 
-    *rover = voice;
-    voice->next = NULL;
+    // Search to the end of the freelist (This is how Doom behaves!)
 
-    if (next != NULL && double_voice && opl_drv_ver != opl_doom_1_9)
+    voice_free_list[voice_free_num++] = voice;
+
+    if (double_voice && opl_drv_ver < opl_doom_1_9)
     {
-        VoiceKeyOff(next);
-        ReleaseVoice(next);
+        ReleaseVoice(index);
     }
 }
 
@@ -488,7 +459,7 @@
 // Load data to the specified operator
 
 static void LoadOperatorData(int operator, genmidi_op_t *data,
-                             boolean max_level)
+                             boolean max_level, unsigned int *volume)
 {
     int level;
 
@@ -495,13 +466,19 @@
     // The scale and level fields must be combined for the level register.
     // For the carrier wave we always set the maximum level.
 
-    level = (data->scale & 0xc0) | (data->level & 0x3f);
+    level = data->scale;
 
     if (max_level)
     {
         level |= 0x3f;
     }
+    else
+    {
+        level |= data->level;
+    }
 
+    *volume = level;
+
     OPL_WriteRegister(OPL_REGS_LEVEL + operator, level);
     OPL_WriteRegister(OPL_REGS_TREMOLO + operator, data->tremolo);
     OPL_WriteRegister(OPL_REGS_ATTACK + operator, data->attack);
@@ -540,8 +517,10 @@
     // is set in SetVoiceVolume (below).  If we are not using
     // modulating mode, we must set both to minimum volume.
 
-    LoadOperatorData(voice->op2 | voice->array, &data->carrier, true);
-    LoadOperatorData(voice->op1 | voice->array, &data->modulator, !modulating);
+    LoadOperatorData(voice->op2 | voice->array, &data->carrier, true,
+                     &voice->car_volume);
+    LoadOperatorData(voice->op1 | voice->array, &data->modulator, !modulating,
+                     &voice->mod_volume);
 
     // Set feedback register that control the connection between the
     // two operators.  Turn on bits in the upper nybble; I think this
@@ -550,10 +529,6 @@
     OPL_WriteRegister((OPL_REGS_FEEDBACK + voice->index) | voice->array,
                       data->feedback | voice->reg_pan);
 
-    // Hack to force a volume update.
-
-    voice->reg_volume = 999;
-
     // Calculate voice priority.
 
     voice->priority = 0x0f - (data->carrier.attack >> 4)
@@ -584,12 +559,12 @@
 
     // Update the volume register(s) if necessary.
 
-    if (car_volume != voice->reg_volume)
+    if (car_volume != (voice->car_volume & 0x3f))
     {
-        voice->reg_volume = car_volume | (opl_voice->carrier.scale & 0xc0);
+        voice->car_volume = car_volume | (voice->car_volume & 0xc0);
 
         OPL_WriteRegister((OPL_REGS_LEVEL + voice->op2) | voice->array,
-                          voice->reg_volume);
+                          voice->car_volume);
 
         // If we are using non-modulated feedback mode, we must set the
         // volume for both voices.
@@ -597,14 +572,21 @@
         if ((opl_voice->feedback & 0x01) != 0
          && opl_voice->modulator.level != 0x3f)
         {
-            mod_volume = 0x3f - opl_voice->modulator.level;
-            if (mod_volume >= car_volume)
+            mod_volume = opl_voice->modulator.level;
+            if (mod_volume < car_volume)
             {
                 mod_volume = car_volume;
             }
-            OPL_WriteRegister((OPL_REGS_LEVEL + voice->op1) | voice->array,
-                              mod_volume |
-                              (opl_voice->modulator.scale & 0xc0));
+
+            mod_volume |= voice->mod_volume & 0xc0;
+
+            if(mod_volume != voice->mod_volume)
+            {
+                voice->mod_volume = mod_volume;
+                OPL_WriteRegister((OPL_REGS_LEVEL + voice->op1) | voice->array,
+                                  mod_volume |
+                                  (opl_voice->modulator.scale & 0xc0));
+            }
         }
     }
 }
@@ -627,9 +609,10 @@
     int i;
 
     // Start with an empty free list.
+    
+    voice_free_num = num_opl_voices;
+    voice_alloced_num = 0;
 
-    voice_free_list = NULL;
-
     // Initialize each voice.
 
     for (i = 0; i < num_opl_voices; ++i)
@@ -642,7 +625,7 @@
 
         // Add this voice to the freelist.
 
-        ReleaseVoice(&voices[i]);
+        voice_free_list[i] = &voices[i];
     }
 }
 
@@ -653,7 +636,7 @@
 
 static void I_OPL_SetMusicVolume(int volume)
 {
-    unsigned int i, j;
+    unsigned int i;
 
     if (current_music_volume == volume)
     {
@@ -666,20 +649,16 @@
 
     // Update the volume of all voices.
 
-    for (i = 0; i < num_tracks; ++i)
+    for (i = 0; i < MIDI_CHANNELS_PER_TRACK; ++i)
     {
-        for (j = 0; j < MIDI_CHANNELS_PER_TRACK; ++j)
+        if (i == 15)
         {
-            if (j == 15)
-            {
-                SetChannelVolume(&tracks[i].channels[j], volume, false);
-            }
-            else
-            {
-                SetChannelVolume(&tracks[i].channels[j],
-                                 tracks[i].channels[j].volume_base, false);
-            }
+            SetChannelVolume(&channels[i], volume, false);
         }
+        else
+        {
+            SetChannelVolume(&channels[i], channels[i].volume_base, false);
+        }
     }
 }
 
@@ -706,7 +685,7 @@
         channel_num = 9;
     }
 
-    return &track->channels[channel_num];
+    return &channels[channel_num];
 }
 
 // Get the frequency that we should be using for a voice.
@@ -714,8 +693,7 @@
 static void KeyOffEvent(opl_track_data_t *track, midi_event_t *event)
 {
     opl_channel_data_t *channel;
-    opl_voice_t *rover;
-    opl_voice_t *prev;
+    int i;
     unsigned int key;
 
 /*
@@ -731,32 +709,17 @@
     // Turn off voices being used to play this key.
     // If it is a double voice instrument there will be two.
 
-    rover = voice_alloced_list;
-    prev = NULL;
-
-    while (rover != NULL)
+    for (i = 0; i < voice_alloced_num; i++)
     {
-        if (rover->channel == channel && rover->key == key)
+        if (voice_alloced_list[i]->channel == channel
+         && voice_alloced_list[i]->key == key)
         {
-            VoiceKeyOff(rover);
-
             // Finished with this voice now.
 
-            ReleaseVoice(rover);
-            if (prev == NULL)
-            {
-                rover = voice_alloced_list;
-            }
-            else
-            {
-                rover = prev->next;
-            }
+            ReleaseVoice(i);
+
+            i--;
         }
-        else
-        {
-            prev = rover;
-            rover = rover->next;
-        }
     }
 }
 
@@ -767,8 +730,8 @@
 
 static void ReplaceExistingVoice(void)
 {
-    opl_voice_t *rover;
-    opl_voice_t *result;
+    int i;
+    int result;
 
     // Check the allocated voices, if we find an instrument that is
     // of a lower priority to the new instrument, discard it.
@@ -778,18 +741,18 @@
     // than higher-numbered channels, eg. MIDI channel 1 is never
     // discarded for MIDI channel 2.
 
-    result = voice_alloced_list;
+    result = 0;
 
-    for (rover = voice_alloced_list; rover != NULL; rover = rover->next)
+    for (i = 0; i < voice_alloced_num; i++)
     {
-        if (rover->current_instr_voice != 0
-         || rover->channel >= result->channel)
+        if (voice_alloced_list[i]->current_instr_voice != 0
+         || voice_alloced_list[i]->channel
+         >= voice_alloced_list[result]->channel)
         {
-            result = rover;
+            result = i;
         }
     }
 
-    VoiceKeyOff(result);
     ReleaseVoice(result);
 }
 
@@ -798,53 +761,43 @@
 
 static void ReplaceExistingVoiceDoom1(void)
 {
-    opl_voice_t *rover;
-    opl_voice_t *result;
+    int i;
+    int result;
 
-    result = voice_alloced_list;
+    result = 0;
 
-    for (rover = voice_alloced_list; rover != NULL; rover = rover->next)
+    for (i = 0; i < voice_alloced_num; i++)
     {
-        if (rover->channel > result->channel)
+        if (voice_alloced_list[i]->channel
+          > voice_alloced_list[result]->channel)
         {
-            result = rover;
+            result = i;
         }
     }
 
-    VoiceKeyOff(result);
     ReleaseVoice(result);
 }
 
 static void ReplaceExistingVoiceDoom2(opl_channel_data_t *channel)
 {
-    opl_voice_t *rover;
-    opl_voice_t *result;
-    opl_voice_t *roverend;
     int i;
+    int result;
     int priority;
 
-    result = voice_alloced_list;
+    result = 0;
 
-    roverend = voice_alloced_list;
+    priority = 0x8000;
 
     for (i = 0; i < voice_alloced_num - 3; i++)
     {
-        roverend = roverend->next;
-    }
-
-    priority = 0x8000;
-
-    for (rover = voice_alloced_list; rover != roverend; rover = rover->next)
-    {
-        if (rover->priority < priority
-         && rover->channel >= channel)
+        if (voice_alloced_list[i]->priority < priority
+         && voice_alloced_list[i]->channel >= channel)
         {
-            priority = rover->priority;
-            result = rover;
+            priority = voice_alloced_list[i]->priority;
+            result = i;
         }
     }
 
-    VoiceKeyOff(result);
     ReleaseVoice(result);
 }
 
@@ -1101,7 +1054,7 @@
             break;
         default:
         case opl_doom_1_9:
-            if (voice_free_list == NULL)
+            if (voice_free_num == 0)
             {
                 ReplaceExistingVoice();
             }
@@ -1210,35 +1163,18 @@
 // Handler for the MIDI_CONTROLLER_ALL_NOTES_OFF channel event.
 static void AllNotesOff(opl_channel_data_t *channel, unsigned int param)
 {
-    opl_voice_t *rover;
-    opl_voice_t *prev;
+    int i;
 
-    rover = voice_alloced_list;
-    prev = NULL;
-
-    while (rover!=NULL)
+    for (i = 0; i < voice_alloced_num; i++)
     {
-        if (rover->channel == channel)
+        if (voice_alloced_list[i]->channel == channel)
         {
-            VoiceKeyOff(rover);
-
             // Finished with this voice now.
 
-            ReleaseVoice(rover);
-            if (prev == NULL)
-            {
-                rover = voice_alloced_list;
-            }
-            else
-            {
-                rover = prev->next;
-            }
+            ReleaseVoice(i);
+            
+            i--;
         }
-        else
-        {
-            prev = rover;
-            rover = rover->next;
-        }
     }
 }
 
@@ -1286,7 +1222,11 @@
 static void PitchBendEvent(opl_track_data_t *track, midi_event_t *event)
 {
     opl_channel_data_t *channel;
-    unsigned int i;
+    int i;
+    opl_voice_t *voice_updated_list[OPL_NUM_VOICES * 2];
+    unsigned int voice_updated_num = 0;
+    opl_voice_t *voice_not_updated_list[OPL_NUM_VOICES * 2];
+    unsigned int voice_not_updated_num = 0;
 
     // Update the channel bend value.  Only the MSB of the pitch bend
     // value is considered: this is what Doom does.
@@ -1296,13 +1236,29 @@
 
     // Update all voices for this channel.
 
-    for (i = 0; i < num_opl_voices; ++i)
+	for (i = 0; i < voice_alloced_num; ++i)
     {
-        if (voices[i].channel == channel)
+        if (voice_alloced_list[i]->channel == channel)
         {
-            UpdateVoiceFrequency(&voices[i]);
+            UpdateVoiceFrequency(voice_alloced_list[i]);
+            voice_updated_list[voice_updated_num++] = voice_alloced_list[i];
         }
+        else
+        {
+            voice_not_updated_list[voice_not_updated_num++] =
+            voice_alloced_list[i];
+        }
     }
+
+    for (i = 0; i < voice_not_updated_num; i++)
+    {
+        voice_alloced_list[i] = voice_not_updated_list[i];
+    }
+
+    for (i = 0; i < voice_updated_num; i++)
+    {
+        voice_alloced_list[i + voice_not_updated_num] = voice_updated_list[i];
+    }
 }
 
 static void MetaSetTempo(unsigned int tempo)
@@ -1400,13 +1356,13 @@
 }
 
 static void ScheduleTrack(opl_track_data_t *track);
-static void InitChannel(opl_track_data_t *track, opl_channel_data_t *channel);
+static void InitChannel(opl_channel_data_t *channel);
 
 // Restart a song from the beginning.
 
 static void RestartSong(void *unused)
 {
-    unsigned int i, j;
+    unsigned int i;
 
     running_tracks = num_tracks;
 
@@ -1416,11 +1372,12 @@
     {
         MIDI_RestartIterator(tracks[i].iter);
         ScheduleTrack(&tracks[i]);
-        for (j = 0; j < MIDI_CHANNELS_PER_TRACK; ++j)
-        {
-            InitChannel(&tracks[i], &tracks[i].channels[j]);
-        }
     }
+
+    for (i = 0; i < MIDI_CHANNELS_PER_TRACK; ++i)
+    {
+        InitChannel(&channels[i]);
+    }
 }
 
 // Callback function invoked when another event needs to be read from
@@ -1485,7 +1442,7 @@
 
 // Initialize a channel.
 
-static void InitChannel(opl_track_data_t *track, opl_channel_data_t *channel)
+static void InitChannel(opl_channel_data_t *channel)
 {
     // TODO: Work out sensible defaults?
 
@@ -1505,16 +1462,10 @@
 static void StartTrack(midi_file_t *file, unsigned int track_num)
 {
     opl_track_data_t *track;
-    unsigned int i;
 
     track = &tracks[track_num];
     track->iter = MIDI_IterateTrack(file, track_num);
 
-    for (i = 0; i < MIDI_CHANNELS_PER_TRACK; ++i)
-    {
-        InitChannel(track, &track->channels[i]);
-    }
-
     // Schedule the first event.
 
     ScheduleTrack(track);
@@ -1555,6 +1506,11 @@
     {
         StartTrack(file, i);
     }
+
+    for (i = 0; i < MIDI_CHANNELS_PER_TRACK; ++i)
+    {
+        InitChannel(&channels[i]);
+    }
 }
 
 static void I_OPL_PauseSong(void)
@@ -1610,13 +1566,9 @@
 
     // Free all voices.
 
-    for (i = 0; i < num_opl_voices; ++i)
+    for (i = 0; i < MIDI_CHANNELS_PER_TRACK; ++i)
     {
-        if (voices[i].channel != NULL)
-        {
-            VoiceKeyOff(&voices[i]);
-            ReleaseVoice(&voices[i]);
-        }
+        AllNotesOff(&channels[i], 0);
     }
 
     // Free all track data.
@@ -1747,7 +1699,7 @@
 
         // Release GENMIDI lump
 
-        W_ReleaseLumpName("GENMIDI");
+        W_ReleaseLumpName(DEH_String("genmidi"));
 
         music_initialized = false;
     }
@@ -1854,7 +1806,7 @@
 
     for (i = MIDI_CHANNELS_PER_TRACK - 1; i >= 0; --i)
     {
-        if (tracks[0].channels[i].instrument != &main_instrs[0])
+        if (channels[i].instrument != &main_instrs[0])
         {
             return i + 1;
         }
@@ -1865,11 +1817,11 @@
 
 static int ChannelInUse(opl_channel_data_t *channel)
 {
-    opl_voice_t *voice;
+    int i;
 
-    for (voice = voice_alloced_list; voice != NULL; voice = voice->next)
+    for (i = 0; i < voice_alloced_num; i++)
     {
-        if (voice->channel == channel)
+        if (voice_alloced_list[i]->channel == channel)
         {
             return 1;
         }
@@ -1896,17 +1848,17 @@
 
     for (i = 0; i < NumActiveChannels(); ++i)
     {
-        if (tracks[0].channels[i].instrument == NULL)
+        if (channels[i].instrument == NULL)
         {
             continue;
         }
 
-        instr_num = tracks[0].channels[i].instrument - main_instrs;
+        instr_num = channels[i].instrument - main_instrs;
 
         M_snprintf(tmp, sizeof(tmp),
                    "chan %i: %c i#%i (%s)\n",
                    i,
-                   ChannelInUse(&tracks[0].channels[i]) ? '\'' : ' ',
+                   ChannelInUse(&channels[i]) ? '\'' : ' ',
                    instr_num + 1,
                    main_instr_names[instr_num]);
         M_StringConcat(result, tmp, result_len);