summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSimon Howard2015-05-30 13:04:56 -0400
committerSimon Howard2015-05-30 13:04:56 -0400
commit0e90c19ca717298f4f3b83dfd075cdf0c458de32 (patch)
tree09fae977fd34c03759d5c90e7daf2a087e6e7756 /src
parent5082f14944442344030d66f6fbdf86a75a1c1c70 (diff)
parent1e516e34911d3a95479c303fe26b59f20e618252 (diff)
downloadchocolate-doom-0e90c19ca717298f4f3b83dfd075cdf0c458de32.tar.gz
chocolate-doom-0e90c19ca717298f4f3b83dfd075cdf0c458de32.tar.bz2
chocolate-doom-0e90c19ca717298f4f3b83dfd075cdf0c458de32.zip
Merge pull request #545 from khokh2001/opl3_mode
opl: Add OPL3 mode. The DMX library had limited support for the features of the OPL3 chip, enabled by setting the DMXOPTIONS variable. This reproduces the OPL3 support in Chocolate Doom's OPL playback and emulation layer. Huge thanks to Alexey Khokholov for researching and developing this. This fixes #470.
Diffstat (limited to 'src')
-rw-r--r--src/i_oplmusic.c131
-rw-r--r--src/i_sound.c2
-rw-r--r--src/m_config.c5
-rw-r--r--src/setup/sound.c53
4 files changed, 150 insertions, 41 deletions
diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c
index 7715465e..cbdbc329 100644
--- a/src/i_oplmusic.c
+++ b/src/i_oplmusic.c
@@ -86,6 +86,10 @@ typedef struct
int volume;
+ // Pan
+
+ int pan;
+
// Pitch bend value:
int bend;
@@ -115,6 +119,9 @@ struct opl_voice_s
// The operators used by this voice:
int op1, op2;
+ // Array used by voice:
+ int array;
+
// Currently-loaded instrument data
genmidi_instr_t *current_instr;
@@ -143,6 +150,9 @@ struct opl_voice_s
// The current volume (register value) that has been set for this channel.
unsigned int reg_volume;
+ // Pan.
+ unsigned int reg_pan;
+
// Priority.
unsigned int priority;
@@ -312,10 +322,12 @@ static char (*percussion_names)[32];
// Voices:
-static opl_voice_t voices[OPL_NUM_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 int voice_alloced_num;
+static int opl_opl3mode;
+static int num_opl_voices;
// Track data for playing tracks:
@@ -338,6 +350,7 @@ static unsigned int last_perc_count;
// adlib chip.
int opl_io_port = 0x388;
+int opl_type = 0;
// Load instrument table from GENMIDI lump:
@@ -519,15 +532,15 @@ static void SetVoiceInstrument(opl_voice_t *voice,
// is set in SetVoiceVolume (below). If we are not using
// modulating mode, we must set both to minimum volume.
- LoadOperatorData(voice->op2, &data->carrier, true);
- LoadOperatorData(voice->op1, &data->modulator, !modulating);
+ LoadOperatorData(voice->op2 | voice->array, &data->carrier, true);
+ LoadOperatorData(voice->op1 | voice->array, &data->modulator, !modulating);
// Set feedback register that control the connection between the
// two operators. Turn on bits in the upper nybble; I think this
// is for OPL3, where it turns on channel A/B.
- OPL_WriteRegister(OPL_REGS_FEEDBACK + voice->index,
- data->feedback | 0x30);
+ OPL_WriteRegister((OPL_REGS_FEEDBACK + voice->index) | voice->array,
+ data->feedback | voice->reg_pan);
// Hack to force a volume update.
@@ -568,7 +581,8 @@ static void SetVoiceVolume(opl_voice_t *voice, unsigned int volume)
{
voice->reg_volume = car_volume | (opl_voice->carrier.scale & 0xc0);
- OPL_WriteRegister(OPL_REGS_LEVEL + voice->op2, voice->reg_volume);
+ OPL_WriteRegister((OPL_REGS_LEVEL + voice->op2) | voice->array,
+ voice->reg_volume);
// If we are using non-modulated feedback mode, we must set the
// volume for both voices.
@@ -581,13 +595,24 @@ static void SetVoiceVolume(opl_voice_t *voice, unsigned int volume)
{
mod_volume = car_volume;
}
- OPL_WriteRegister(OPL_REGS_LEVEL + voice->op1,
+ OPL_WriteRegister((OPL_REGS_LEVEL + voice->op1) | voice->array,
mod_volume |
(opl_voice->modulator.scale & 0xc0));
}
}
}
+static void SetVoicePan(opl_voice_t *voice, unsigned int pan)
+{
+ genmidi_voice_t *opl_voice;
+
+ voice->reg_pan = pan;
+ opl_voice = &voice->current_instr->voices[voice->current_instr_voice];;
+
+ OPL_WriteRegister((OPL_REGS_FEEDBACK + voice->index) | voice->array,
+ opl_voice->feedback | pan);
+}
+
// Initialize the voice table and freelist
static void InitVoices(void)
@@ -600,11 +625,12 @@ static void InitVoices(void)
// Initialize each voice.
- for (i = 0; i < OPL_NUM_VOICES; ++i)
+ for (i = 0; i < num_opl_voices; ++i)
{
- voices[i].index = i;
- voices[i].op1 = voice_operators[0][i];
- voices[i].op2 = voice_operators[1][i];
+ voices[i].index = i % OPL_NUM_VOICES;
+ voices[i].op1 = voice_operators[0][i % OPL_NUM_VOICES];
+ voices[i].op2 = voice_operators[1][i % OPL_NUM_VOICES];
+ voices[i].array = (i / OPL_NUM_VOICES) << 8;
voices[i].current_instr = NULL;
// Add this voice to the freelist.
@@ -625,7 +651,7 @@ static void I_OPL_SetMusicVolume(int volume)
// Update the volume of all voices.
- for (i = 0; i < OPL_NUM_VOICES; ++i)
+ for (i = 0; i < num_opl_voices; ++i)
{
if (voices[i].channel != NULL)
{
@@ -636,7 +662,8 @@ static void I_OPL_SetMusicVolume(int volume)
static void VoiceKeyOff(opl_voice_t *voice)
{
- OPL_WriteRegister(OPL_REGS_FREQ_2 + voice->index, voice->freq >> 8);
+ OPL_WriteRegister((OPL_REGS_FREQ_2 + voice->index) | voice->array,
+ voice->freq >> 8);
}
static opl_channel_data_t *TrackChannelForEvent(opl_track_data_t *track,
@@ -869,8 +896,10 @@ static void UpdateVoiceFrequency(opl_voice_t *voice)
if (voice->freq != freq)
{
- OPL_WriteRegister(OPL_REGS_FREQ_1 + voice->index, freq & 0xff);
- OPL_WriteRegister(OPL_REGS_FREQ_2 + voice->index, (freq >> 8) | 0x20);
+ OPL_WriteRegister((OPL_REGS_FREQ_1 + voice->index) | voice->array,
+ freq & 0xff);
+ OPL_WriteRegister((OPL_REGS_FREQ_2 + voice->index) | voice->array,
+ (freq >> 8) | 0x20);
voice->freq = freq;
}
@@ -913,6 +942,8 @@ static void VoiceKeyOn(opl_channel_data_t *channel,
voice->note = note;
}
+ voice->reg_pan = channel->pan;
+
// Program the voice with the instrument data:
SetVoiceInstrument(voice, instrument, instrument_voice);
@@ -980,11 +1011,11 @@ static void KeyOnEvent(opl_track_data_t *track, midi_event_t *event)
if (opl_drv_ver == opl_v_old)
{
- if (voice_alloced_num == OPL_NUM_VOICES)
+ if (voice_alloced_num == num_opl_voices)
{
ReplaceExistingVoiceOld(channel);
}
- if (voice_alloced_num == OPL_NUM_VOICES - 1 && double_voice)
+ if (voice_alloced_num == num_opl_voices - 1 && double_voice)
{
ReplaceExistingVoiceOld(channel);
}
@@ -1041,7 +1072,7 @@ static void SetChannelVolume(opl_channel_data_t *channel, unsigned int volume)
// Update all voices that this channel is using.
- for (i = 0; i < OPL_NUM_VOICES; ++i)
+ for (i = 0; i < num_opl_voices; ++i)
{
if (voices[i].channel == channel)
{
@@ -1050,6 +1081,39 @@ static void SetChannelVolume(opl_channel_data_t *channel, unsigned int volume)
}
}
+static void SetChannelPan(opl_channel_data_t *channel, unsigned int pan)
+{
+ unsigned int reg_pan;
+ unsigned int i;
+
+ if (opl_opl3mode)
+ {
+ if (pan >= 96)
+ {
+ reg_pan = 0x10;
+ }
+ else if (pan <= 48)
+ {
+ reg_pan = 0x20;
+ }
+ else
+ {
+ reg_pan = 0x30;
+ }
+ if (channel->pan != reg_pan)
+ {
+ channel->pan = reg_pan;
+ for (i = 0; i < num_opl_voices; i++)
+ {
+ if (voices[i].channel == channel)
+ {
+ SetVoicePan(&voices[i], reg_pan);
+ }
+ }
+ }
+ }
+}
+
// Handler for the MIDI_CONTROLLER_ALL_NOTES_OFF channel event.
static void AllNotesOff(opl_channel_data_t *channel, unsigned int param)
{
@@ -1108,6 +1172,10 @@ static void ControllerEvent(opl_track_data_t *track, midi_event_t *event)
SetChannelVolume(channel, param);
break;
+ case MIDI_CONTROLLER_PAN:
+ SetChannelPan(channel, param);
+ break;
+
case MIDI_CONTROLLER_ALL_NOTES_OFF:
AllNotesOff(channel, param);
break;
@@ -1135,7 +1203,7 @@ static void PitchBendEvent(opl_track_data_t *track, midi_event_t *event)
// Update all voices for this channel.
- for (i = 0; i < OPL_NUM_VOICES; ++i)
+ for (i = 0; i < num_opl_voices; ++i)
{
if (voices[i].channel == channel)
{
@@ -1323,6 +1391,7 @@ static void InitChannel(opl_track_data_t *track, opl_channel_data_t *channel)
channel->instrument = &main_instrs[0];
channel->volume = 127;
+ channel->pan = 0x30;
channel->bend = 0;
}
@@ -1397,7 +1466,7 @@ static void I_OPL_PauseSong(void)
// Turn off all main instrument voices (not percussion).
// This is what Vanilla does.
- for (i = 0; i < OPL_NUM_VOICES; ++i)
+ for (i = 0; i < num_opl_voices; ++i)
{
if (voices[i].channel != NULL
&& voices[i].current_instr < percussion_instrs)
@@ -1434,7 +1503,7 @@ static void I_OPL_StopSong(void)
// Free all voices.
- for (i = 0; i < OPL_NUM_VOICES; ++i)
+ for (i = 0; i < num_opl_voices; ++i)
{
if (voices[i].channel != NULL)
{
@@ -1581,14 +1650,32 @@ static void I_OPL_ShutdownMusic(void)
static boolean I_OPL_InitMusic(void)
{
+ int opl_chip_type;
+
OPL_SetSampleRate(snd_samplerate);
- if (!OPL_Init(opl_io_port))
+ opl_chip_type = OPL_Init(opl_io_port);
+ if (!opl_chip_type)
{
printf("Dude. The Adlib isn't responding.\n");
return false;
}
+ if (opl_chip_type == 2 && opl_type)
+ {
+ opl_opl3mode = 1;
+ num_opl_voices = OPL_NUM_VOICES * 2;
+ }
+ else
+ {
+ opl_opl3mode = 0;
+ num_opl_voices = OPL_NUM_VOICES;
+ }
+
+ // Initialize all registers.
+
+ OPL_InitRegisters(opl_opl3mode);
+
// Load instruments from GENMIDI lump:
if (!LoadInstrumentTable())
diff --git a/src/i_sound.c b/src/i_sound.c
index 73442cbd..03a9facc 100644
--- a/src/i_sound.c
+++ b/src/i_sound.c
@@ -68,6 +68,7 @@ extern music_module_t music_opl_module;
extern opl_driver_ver_t opl_drv_ver;
extern int opl_io_port;
+extern int opl_type;
// For native music module:
@@ -446,6 +447,7 @@ void I_BindSoundVariables(void)
M_BindIntVariable("snd_samplerate", &snd_samplerate);
M_BindIntVariable("snd_cachesize", &snd_cachesize);
M_BindIntVariable("opl_io_port", &opl_io_port);
+ M_BindIntVariable("opl_type", &opl_type);
M_BindStringVariable("timidity_cfg_path", &timidity_cfg_path);
M_BindStringVariable("gus_patch_path", &gus_patch_path);
diff --git a/src/m_config.c b/src/m_config.c
index ad539a7f..3ec0b633 100644
--- a/src/m_config.c
+++ b/src/m_config.c
@@ -824,6 +824,11 @@ static default_t extra_defaults_list[] =
CONFIG_VARIABLE_INT_HEX(opl_io_port),
//!
+ // OPL chip type.
+ //
+ CONFIG_VARIABLE_INT(opl_type),
+
+ //!
// @game doom heretic strife
//
// If non-zero, the ENDOOM text screen is displayed when exiting the
diff --git a/src/setup/sound.c b/src/setup/sound.c
index 280a6bc2..092a5dd8 100644
--- a/src/setup/sound.c
+++ b/src/setup/sound.c
@@ -61,6 +61,12 @@ static char *musicmode_strings[] =
"CD audio"
};
+static char *opltype_strings[] =
+{
+ "OPL2",
+ "OPL3"
+};
+
static char *cfg_extension[] = { "cfg", NULL };
// Config file variables:
@@ -84,6 +90,7 @@ static float libsamplerate_scale = 0.65;
static char *timidity_cfg_path = NULL;
static char *gus_patch_path = NULL;
static int gus_ram_kb = 1024;
+static int opl_type = 0;
// DOS specific variables: these are unused but should be maintained
// so that the config file can be shared between chocolate
@@ -139,29 +146,36 @@ static void UpdateExtraTable(TXT_UNCAST_ARG(widget),
{
TXT_CAST_ARG(txt_table_t, extra_table);
- // Rebuild the GUS table. Start by emptying it, then only add the
- // GUS control widget if we are in GUS music mode.
-
- TXT_ClearTable(extra_table);
-
- if (snd_musicmode == MUSICMODE_GUS)
+ switch (snd_musicmode)
{
+ case MUSICMODE_OPL:
+ TXT_InitTable(extra_table, 2);
+ TXT_SetColumnWidths(extra_table, 19, 4);
TXT_AddWidgets(extra_table,
- TXT_NewLabel("GUS patch path:"),
- TXT_NewFileSelector(&gus_patch_path, 30,
- "Select path to GUS patches",
- TXT_DIRECTORY),
- NULL);
- }
+ TXT_NewLabel("OPL type"),
+ TXT_NewDropdownList(&opl_type, opltype_strings, 2),
+ NULL);
+ break;
- if (snd_musicmode == MUSICMODE_NATIVE)
- {
+ case MUSICMODE_GUS:
+ TXT_InitTable(extra_table, 1);
TXT_AddWidgets(extra_table,
- TXT_NewLabel("Timidity configuration file:"),
- TXT_NewFileSelector(&timidity_cfg_path, 30,
- "Select Timidity config file",
- cfg_extension),
- NULL);
+ TXT_NewLabel("GUS patch path:"),
+ TXT_NewFileSelector(&gus_patch_path, 30,
+ "Select path to GUS patches",
+ TXT_DIRECTORY),
+ NULL);
+ break;
+
+ case MUSICMODE_NATIVE:
+ TXT_InitTable(extra_table, 1);
+ TXT_AddWidgets(extra_table,
+ TXT_NewLabel("Timidity configuration file:"),
+ TXT_NewFileSelector(&timidity_cfg_path, 30,
+ "Select Timidity config file",
+ cfg_extension),
+ NULL);
+ break;
}
}
@@ -324,6 +338,7 @@ void BindSoundVariables(void)
M_BindIntVariable("snd_cachesize", &snd_cachesize);
M_BindIntVariable("opl_io_port", &opl_io_port);
+ M_BindIntVariable("opl_type", &opl_type);
if (gamemission == strife)
{