From 7c377fc88a3fbb7f69b91a15dc414c8965624a89 Mon Sep 17 00:00:00 2001 From: Nuke.YKT Date: Fri, 29 May 2015 02:00:45 +0900 Subject: Added OPL3 mode support. --- opl/opl.c | 82 +++++++++++++++++++++++++++++------- opl/opl.h | 6 ++- opl/opl_sdl.c | 43 +++++++++++++++---- src/i_oplmusic.c | 123 +++++++++++++++++++++++++++++++++++++++++++++--------- src/i_sound.c | 2 + src/m_config.c | 5 +++ src/setup/sound.c | 53 +++++++++++++++-------- 7 files changed, 254 insertions(+), 60 deletions(-) diff --git a/opl/opl.c b/opl/opl.c index 2800f434..92ed3b5b 100644 --- a/opl/opl.c +++ b/opl/opl.c @@ -67,6 +67,8 @@ unsigned int opl_sample_rate = 22050; 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 @@ static int InitDriver(opl_driver_t *_driver, unsigned int port_base) 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 @@ static int InitDriver(opl_driver_t *_driver, unsigned int port_base) 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 InitDriver(opl_driver_t *_driver, unsigned int port_base) 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 @@ int OPL_Init(unsigned int port_base) { char *driver_name; int i; + int result; driver_name = getenv("OPL_DRIVER"); @@ -138,9 +141,10 @@ int OPL_Init(unsigned int port_base) { 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 @@ void OPL_WriteRegister(int reg, int value) { 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 @@ int OPL_Detect(void) // 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 @@ void OPL_InitRegisters(void) // "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); + } } // diff --git a/opl/opl.h b/opl/opl.h index c4b91b25..c4bc2c80 100644 --- a/opl/opl.h +++ b/opl/opl.h @@ -26,7 +26,8 @@ typedef void (*opl_callback_t)(void *data); 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 @@ typedef enum #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 @@ int OPL_Detect(void); // Initialize all registers, performed on startup. -void OPL_InitRegisters(void); +void OPL_InitRegisters(int opl3); // // Timer callback functions. diff --git a/opl/opl_sdl.c b/opl/opl_sdl.c index 8834ee07..0621995f 100644 --- a/opl/opl_sdl.c +++ b/opl/opl_sdl.c @@ -71,6 +71,7 @@ static uint64_t pause_offset; // OPL software emulator structure. static Chip opl_chip; +static int opl_new; // Temporary mixing buffer used by the mixing callback. @@ -164,15 +165,30 @@ static void FillBuffer(int16_t *buffer, unsigned int nsamples) 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 timer1.expire_time) { result |= 0x80; // Either have expired @@ -439,6 +461,9 @@ static void WriteRegister(unsigned int reg_num, unsigned int value) break; + case OPL_REG_NEW: + opl_new = value & 0x01; + default: Chip__WriteReg(&opl_chip, reg_num, value); break; @@ -451,6 +476,10 @@ static void OPL_SDL_PortWrite(opl_port_t port, unsigned int value) { register_num = value; } + else if (port == OPL_REGISTER_PORT_OPL3) + { + register_num = value | 0x100; + } else if (port == OPL_DATA_PORT) { WriteRegister(register_num, value); diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c index 7715465e..dff9480c 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_new; +static int opl_voice_num; // 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,7 @@ 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 +594,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 +624,12 @@ static void InitVoices(void) // 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 @@ 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 < opl_voice_num; ++i) { if (voices[i].channel != NULL) { @@ -636,7 +661,7 @@ 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 +894,8 @@ 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 +938,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); @@ -1041,7 +1068,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 < opl_voice_num; ++i) { if (voices[i].channel == channel) { @@ -1050,6 +1077,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_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 @@ 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 +1199,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 < opl_voice_num; ++i) { if (voices[i].channel == channel) { @@ -1323,6 +1387,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 +1462,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 < opl_voice_num; ++i) { if (voices[i].channel != NULL && voices[i].current_instr < percussion_instrs) @@ -1434,7 +1499,7 @@ static void I_OPL_StopSong(void) // 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,14 +1646,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_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: 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..3d898705 100644 --- a/src/m_config.c +++ b/src/m_config.c @@ -823,6 +823,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 // diff --git a/src/setup/sound.c b/src/setup/sound.c index 280a6bc2..bc2bdf33 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 @@ -142,26 +149,37 @@ static void UpdateExtraTable(TXT_UNCAST_ARG(widget), // 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 @@ 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) { -- cgit v1.2.3