shithub: choc

Download patch

ref: 5d3c0e0a824abd076bacffcfd01a6df5765435fe
parent: a70231731a2a9af0cf1791ee57613b3771414d4f
parent: 8c8016300eb6e2af740b89da77e6a9c06a979786
author: Simon Howard <fraggle+github@gmail.com>
date: Wed Dec 14 04:33:15 EST 2016

Merge pull request #820 from nukeykt/opl-miscfix

OPL driver bug fixes

--- 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);