ref: 7c377fc88a3fbb7f69b91a15dc414c8965624a89
parent: 5082f14944442344030d66f6fbdf86a75a1c1c70
author: Nuke.YKT <alexeytf2@gmail.com>
date: Thu May 28 22:00:45 EDT 2015
Added OPL3 mode support.
--- a/opl/opl.c
+++ b/opl/opl.c
@@ -67,6 +67,8 @@
static int InitDriver(opl_driver_t *_driver, unsigned int port_base)
{
+ int result1, result2;
+
// Initialize the driver.
if (!_driver->init_func(port_base))
@@ -82,7 +84,9 @@
driver = _driver;
init_stage_reg_writes = 1;
- if (!OPL_Detect() || !OPL_Detect())
+ result1 = OPL_Detect();
+ result2 = OPL_Detect();
+ if (!result1 || !result2)
{
printf("OPL_Init: No OPL detected using '%s' driver.\n", _driver->name);
_driver->shutdown_func();
@@ -90,15 +94,11 @@
return 0;
}
- // Initialize all registers.
-
- OPL_InitRegisters();
-
init_stage_reg_writes = 0;
printf("OPL_Init: Using driver '%s'.\n", driver->name);
- return 1;
+ return result2;
}
// Find a driver automatically by trying each in the list.
@@ -106,12 +106,14 @@
static int AutoSelectDriver(unsigned int port_base)
{
int i;
+ int result;
for (i=0; drivers[i] != NULL; ++i)
{
- if (InitDriver(drivers[i], port_base))
+ result = InitDriver(drivers[i], port_base);
+ if (result)
{
- return 1;
+ return result;
}
}
@@ -127,6 +129,7 @@
{
char *driver_name;
int i;
+ int result;
driver_name = getenv("OPL_DRIVER");
@@ -138,9 +141,10 @@
{
if (!strcmp(driver_name, drivers[i]->name))
{
- if (InitDriver(drivers[i], port_base))
+ result = InitDriver(drivers[i], port_base);
+ if (result)
{
- return 1;
+ return result;
}
else
{
@@ -233,7 +237,14 @@
{
int i;
- OPL_WritePort(OPL_REGISTER_PORT, reg);
+ if (reg & 0x100)
+ {
+ OPL_WritePort(OPL_REGISTER_PORT_OPL3, reg);
+ }
+ else
+ {
+ OPL_WritePort(OPL_REGISTER_PORT, reg);
+ }
// For timing, read the register port six times after writing the
// register number to cause the appropriate delay
@@ -306,13 +317,22 @@
// Enable interrupts:
OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x80);
- return (result1 & 0xe0) == 0x00
- && (result2 & 0xe0) == 0xc0;
+ if ((result1 & 0xe0) == 0x00 && (result2 & 0xe0) == 0xc0)
+ {
+ result1 = OPL_ReadPort(OPL_REGISTER_PORT);
+ result2 = OPL_ReadPort(OPL_REGISTER_PORT_OPL3);
+ if (result1 == 0x00 && result2 == 0xff)
+ {
+ return 2;
+ }
+ return 1;
+ }
+ return 0;
}
// Initialize registers on startup
-void OPL_InitRegisters(void)
+void OPL_InitRegisters(int opl3)
{
int r;
@@ -349,8 +369,42 @@
// "Allow FM chips to control the waveform of each operator":
OPL_WriteRegister(OPL_REG_WAVEFORM_ENABLE, 0x20);
+ if (opl3)
+ {
+ OPL_WriteRegister(OPL_REG_NEW, 0x01);
+
+ // Initialize level registers
+
+ for (r=OPL_REGS_LEVEL; r <= OPL_REGS_LEVEL + OPL_NUM_OPERATORS; ++r)
+ {
+ OPL_WriteRegister(r | 0x100, 0x3f);
+ }
+
+ // Initialize other registers
+ // These two loops write to registers that actually don't exist,
+ // but this is what Doom does ...
+ // Similarly, the <= is also intenational.
+
+ for (r=OPL_REGS_ATTACK; r <= OPL_REGS_WAVEFORM + OPL_NUM_OPERATORS; ++r)
+ {
+ OPL_WriteRegister(r | 0x100, 0x00);
+ }
+
+ // More registers ...
+
+ for (r=1; r < OPL_REGS_LEVEL; ++r)
+ {
+ OPL_WriteRegister(r | 0x100, 0x00);
+ }
+ }
+
// Keyboard split point on (?)
OPL_WriteRegister(OPL_REG_FM_MODE, 0x40);
+
+ if (opl3)
+ {
+ OPL_WriteRegister(OPL_REG_NEW, 0x01);
+ }
}
//
--- a/opl/opl.h
+++ b/opl/opl.h
@@ -26,7 +26,8 @@
typedef enum
{
OPL_REGISTER_PORT = 0,
- OPL_DATA_PORT = 1
+ OPL_DATA_PORT = 1,
+ OPL_REGISTER_PORT_OPL3 = 2
} opl_port_t;
#define OPL_NUM_OPERATORS 21
@@ -37,6 +38,7 @@
#define OPL_REG_TIMER2 0x03
#define OPL_REG_TIMER_CTRL 0x04
#define OPL_REG_FM_MODE 0x08
+#define OPL_REG_NEW 0x105
// Operator registers (21 of each):
@@ -101,7 +103,7 @@
// Initialize all registers, performed on startup.
-void OPL_InitRegisters(void);
+void OPL_InitRegisters(int opl3);
//
// Timer callback functions.
--- a/opl/opl_sdl.c
+++ b/opl/opl_sdl.c
@@ -71,6 +71,7 @@
// OPL software emulator structure.
static Chip opl_chip;
+static int opl_new;
// Temporary mixing buffer used by the mixing callback.
@@ -164,15 +165,30 @@
assert(nsamples < mixing_freq);
- Chip__GenerateBlock2(&opl_chip, nsamples, mix_buffer);
+ if (opl_new)
+ {
+ Chip__GenerateBlock3(&opl_chip, nsamples, mix_buffer);
- // Mix into the destination buffer, doubling up into stereo.
+ // Mix into the destination buffer, doubling up into stereo.
- for (i=0; i<nsamples; ++i)
- {
- buffer[i * 2] = (int16_t) mix_buffer[i];
- buffer[i * 2 + 1] = (int16_t) mix_buffer[i];
+ for (i=0; i<nsamples; ++i)
+ {
+ buffer[i * 2] = (int16_t) mix_buffer[i * 2];
+ buffer[i * 2 + 1] = (int16_t) mix_buffer[i * 2 + 1];
+ }
}
+ else
+ {
+ Chip__GenerateBlock2(&opl_chip, nsamples, mix_buffer);
+
+ // Mix into the destination buffer, doubling up into stereo.
+
+ for (i=0; i<nsamples; ++i)
+ {
+ buffer[i * 2] = (int16_t) mix_buffer[i];
+ buffer[i * 2 + 1] = (int16_t) mix_buffer[i];
+ }
+ }
}
// Callback function to fill a new sound buffer:
@@ -351,7 +367,7 @@
// Mix buffer:
- mix_buffer = malloc(mixing_freq * sizeof(uint32_t));
+ mix_buffer = malloc(mixing_freq * sizeof(uint32_t) * 2);
// Create the emulator structure:
@@ -358,6 +374,7 @@
DBOPL_InitTables();
Chip__Chip(&opl_chip);
Chip__Setup(&opl_chip, mixing_freq);
+ opl_new = 0;
callback_mutex = SDL_CreateMutex();
callback_queue_mutex = SDL_CreateMutex();
@@ -372,6 +389,11 @@
{
unsigned int result = 0;
+ if (port == OPL_REGISTER_PORT_OPL3)
+ {
+ return 0xff;
+ }
+
if (timer1.enabled && current_time > timer1.expire_time)
{
result |= 0x80; // Either have expired
@@ -439,6 +461,9 @@
break;
+ case OPL_REG_NEW:
+ opl_new = value & 0x01;
+
default:
Chip__WriteReg(&opl_chip, reg_num, value);
break;
@@ -450,6 +475,10 @@
if (port == OPL_REGISTER_PORT)
{
register_num = value;
+ }
+ else if (port == OPL_REGISTER_PORT_OPL3)
+ {
+ register_num = value | 0x100;
}
else if (port == OPL_DATA_PORT)
{
--- a/src/i_oplmusic.c
+++ b/src/i_oplmusic.c
@@ -86,6 +86,10 @@
int volume;
+ // Pan
+
+ int pan;
+
// Pitch bend value:
int bend;
@@ -115,6 +119,9 @@
// 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 @@
// 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 @@
// 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_new;
+static int opl_voice_num;
// Track data for playing tracks:
@@ -338,6 +350,7 @@
// adlib chip.
int opl_io_port = 0x388;
+int opl_type = 0;
// Load instrument table from GENMIDI lump:
@@ -519,15 +532,15 @@
// 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,7 @@
{
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,7 +594,7 @@
{
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));
}
@@ -588,6 +601,17 @@
}
}
+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 +624,12 @@
// Initialize each voice.
- for (i = 0; i < OPL_NUM_VOICES; ++i)
+ for (i = 0; i < opl_voice_num; ++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 +650,7 @@
// Update the volume of all voices.
- for (i = 0; i < OPL_NUM_VOICES; ++i)
+ for (i = 0; i < opl_voice_num; ++i)
{
if (voices[i].channel != NULL)
{
@@ -636,7 +661,7 @@
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 +894,8 @@
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 +938,8 @@
voice->note = note;
}
+ voice->reg_pan = channel->pan;
+
// Program the voice with the instrument data:
SetVoiceInstrument(voice, instrument, instrument_voice);
@@ -1041,7 +1068,7 @@
// Update all voices that this channel is using.
- for (i = 0; i < OPL_NUM_VOICES; ++i)
+ for (i = 0; i < opl_voice_num; ++i)
{
if (voices[i].channel == channel)
{
@@ -1050,6 +1077,39 @@
}
}
+static void SetChannelPan(opl_channel_data_t *channel, unsigned int pan)
+{
+ unsigned int reg_pan;
+ unsigned int i;
+
+ if (opl_new)
+ {
+ 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 < opl_voice_num; 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 +1168,10 @@
SetChannelVolume(channel, param);
break;
+ case MIDI_CONTROLLER_PAN:
+ SetChannelPan(channel, param);
+ break;
+
case MIDI_CONTROLLER_ALL_NOTES_OFF:
AllNotesOff(channel, param);
break;
@@ -1135,7 +1199,7 @@
// Update all voices for this channel.
- for (i = 0; i < OPL_NUM_VOICES; ++i)
+ for (i = 0; i < opl_voice_num; ++i)
{
if (voices[i].channel == channel)
{
@@ -1323,6 +1387,7 @@
channel->instrument = &main_instrs[0];
channel->volume = 127;
+ channel->pan = 0x30;
channel->bend = 0;
}
@@ -1397,7 +1462,7 @@
// 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 < opl_voice_num; ++i)
{
if (voices[i].channel != NULL
&& voices[i].current_instr < percussion_instrs)
@@ -1434,7 +1499,7 @@
// Free all voices.
- for (i = 0; i < OPL_NUM_VOICES; ++i)
+ for (i = 0; i < opl_voice_num; ++i)
{
if (voices[i].channel != NULL)
{
@@ -1581,13 +1646,31 @@
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_new = 1;
+ opl_voice_num = OPL_NUM_VOICES * 2;
+ }
+ else
+ {
+ opl_new = 0;
+ opl_voice_num = OPL_NUM_VOICES;
+ }
+
+ // Initialize all registers.
+
+ OPL_InitRegisters(opl_new);
// Load instruments from GENMIDI lump:
--- a/src/i_sound.c
+++ b/src/i_sound.c
@@ -68,6 +68,7 @@
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 @@
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);
--- a/src/m_config.c
+++ b/src/m_config.c
@@ -824,6 +824,11 @@
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
--- a/src/setup/sound.c
+++ b/src/setup/sound.c
@@ -61,6 +61,12 @@
"CD audio"
};
+static char *opltype_strings[] =
+{
+ "OPL2",
+ "OPL3"
+};
+
static char *cfg_extension[] = { "cfg", NULL };
// Config file variables:
@@ -84,6 +90,7 @@
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
@@ -142,26 +149,37 @@
// 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)
+ if (snd_musicmode == 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);
+ }
+ else
+ {
+ TXT_InitTable(extra_table, 1);
+ if (snd_musicmode == MUSICMODE_GUS)
+ {
+ TXT_AddWidgets(extra_table,
+ TXT_NewLabel("GUS patch path:"),
+ TXT_NewFileSelector(&gus_patch_path, 30,
+ "Select path to GUS patches",
+ TXT_DIRECTORY),
+ NULL);
+ }
- if (snd_musicmode == MUSICMODE_NATIVE)
- {
- TXT_AddWidgets(extra_table,
- TXT_NewLabel("Timidity configuration file:"),
- TXT_NewFileSelector(&timidity_cfg_path, 30,
- "Select Timidity config file",
- cfg_extension),
- NULL);
+ if (snd_musicmode == MUSICMODE_NATIVE)
+ {
+ TXT_AddWidgets(extra_table,
+ TXT_NewLabel("Timidity configuration file:"),
+ TXT_NewFileSelector(&timidity_cfg_path, 30,
+ "Select Timidity config file",
+ cfg_extension),
+ NULL);
+ }
}
}
@@ -324,6 +342,7 @@
M_BindIntVariable("snd_cachesize", &snd_cachesize);
M_BindIntVariable("opl_io_port", &opl_io_port);
+ M_BindIntVariable("opl_type", &opl_type);
if (gamemission == strife)
{