ref: 5d3c0e0a824abd076bacffcfd01a6df5765435fe
parent: a70231731a2a9af0cf1791ee57613b3771414d4f
parent: 8c8016300eb6e2af740b89da77e6a9c06a979786
author: Simon Howard <[email protected]>
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);