diff --git a/.gitignore b/.gitignore index 8951a548..d1ad137c 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,5 @@ libs/ *.so *.dylib jni/*.bat +.vscode/pro-deployer.json +.vscode/settings.json diff --git a/core_options.h b/core_options.h index 1eb41c6b..a280a7ad 100644 --- a/core_options.h +++ b/core_options.h @@ -124,6 +124,14 @@ namespace DBP_Option gus, tandysound, swapstereo, + mixer_master, + mixer_disney, + mixer_spkr, + mixer_sb, + mixer_fm, + mixer_midi, + mixer_tandy, + mixer_gus, _OPTIONS_NULL_TERMINATOR, _OPTIONS_TOTAL, }; @@ -134,6 +142,10 @@ namespace DBP_Option bool GetHidden(const retro_core_option_v2_definition& d); }; +#define DBP_MIXER_VOLUME_VALUES \ + { "100", "100% (default)" }, { "90", "90%" }, { "80", "80%" }, { "70", "70%" }, { "60", "60%" }, { "50", "50%" }, \ + { "40", "40%" }, { "30", "30%" }, { "20", "20%" }, { "10", "10%" }, { "0", "0%" } + static retro_core_option_v2_definition option_defs[DBP_Option::_OPTIONS_TOTAL] = { // General @@ -274,7 +286,7 @@ static retro_core_option_v2_definition option_defs[DBP_Option::_OPTIONS_TOTAL] = #endif { "dosbox_pure_strict_mode", - "Advanced > Use Strict Mode", NULL, + "Use Strict Mode", NULL, "Disable the command line, running installed operating systems and using .BAT/.COM/.EXE/DOS.YML files from the save game.", NULL, DBP_OptionCat::General, { @@ -285,7 +297,7 @@ static retro_core_option_v2_definition option_defs[DBP_Option::_OPTIONS_TOTAL] = }, { "dosbox_pure_conf", - "Advanced > Loading of dosbox.conf", NULL, + "Loading of dosbox.conf", NULL, "DOSBox Pure is meant to be configured via core options but optionally supports loading of legacy .conf files.", NULL, DBP_OptionCat::General, { @@ -297,7 +309,7 @@ static retro_core_option_v2_definition option_defs[DBP_Option::_OPTIONS_TOTAL] = }, { "dosbox_pure_menu_time", - "Advanced > Start Menu", NULL, + "Start Menu", NULL, "Set the behavior of the start menu before and after launching a game." "\n" "You can also force it to open by holding shift or L2/R2 when selecting 'Restart'.", NULL, DBP_OptionCat::General, @@ -314,7 +326,7 @@ static retro_core_option_v2_definition option_defs[DBP_Option::_OPTIONS_TOTAL] = }, { "dosbox_pure_menu_transparency", - "Advanced > Menu Transparency", NULL, + "Menu Transparency", NULL, "Set the transparency level of the Menu and the On-Screen Keyboard.", NULL, DBP_OptionCat::General, { @@ -398,7 +410,7 @@ static retro_core_option_v2_definition option_defs[DBP_Option::_OPTIONS_TOTAL] = }, { "dosbox_pure_mouse_speed_factor_x", - "Advanced > Horizontal Mouse Sensitivity", NULL, + "Horizontal Mouse Sensitivity", NULL, "Experiment with this value if the mouse is too fast/slow when moving left/right.", NULL, DBP_OptionCat::Input, { @@ -414,7 +426,7 @@ static retro_core_option_v2_definition option_defs[DBP_Option::_OPTIONS_TOTAL] = }, { "dosbox_pure_actionwheel_inputs", - "Advanced > Action Wheel Inputs", NULL, + "Action Wheel Inputs", NULL, "Sets which inputs control the action wheel.", NULL, DBP_OptionCat::Input, { @@ -427,7 +439,7 @@ static retro_core_option_v2_definition option_defs[DBP_Option::_OPTIONS_TOTAL] = }, { "dosbox_pure_auto_mapping", - "Advanced > Automatic Game Pad Mappings", NULL, + "Automatic Game Pad Mappings", NULL, "DOSBox Pure can automatically apply a gamepad control mapping scheme when it detects a game." "\n" "These button mappings are provided by the Keyb2Joypad Project (by Jemy Murphy and bigjim).", NULL, DBP_OptionCat::Input, @@ -436,7 +448,7 @@ static retro_core_option_v2_definition option_defs[DBP_Option::_OPTIONS_TOTAL] = }, { "dosbox_pure_keyboard_layout", - "Advanced > Keyboard Layout", NULL, + "Keyboard Layout", NULL, "Select the keyboard layout (will not change the On-Screen Keyboard).", NULL, DBP_OptionCat::Input, { @@ -471,7 +483,7 @@ static retro_core_option_v2_definition option_defs[DBP_Option::_OPTIONS_TOTAL] = }, { "dosbox_pure_joystick_analog_deadzone", - "Advanced > Joystick Analog Deadzone", NULL, + "Joystick Analog Deadzone", NULL, "Set the deadzone of the joystick analog sticks. May be used to eliminate drift caused by poorly calibrated joystick hardware.", NULL, DBP_OptionCat::Input, { @@ -481,7 +493,7 @@ static retro_core_option_v2_definition option_defs[DBP_Option::_OPTIONS_TOTAL] = }, { "dosbox_pure_joystick_timed", - "Advanced > Enable Joystick Timed Intervals", NULL, + "Enable Joystick Timed Intervals", NULL, "Enable timed intervals for joystick axes. Experiment with this option if your joystick drifts." "\n\n", NULL, //end of Input > Advanced section DBP_OptionCat::Input, { { "true", "On (default)" }, { "false", "Off" } }, @@ -513,7 +525,7 @@ static retro_core_option_v2_definition option_defs[DBP_Option::_OPTIONS_TOTAL] = }, { "dosbox_pure_cycles_max", - "Detailed > Maximum Emulated Performance", NULL, + "Maximum Emulated Performance", NULL, "With dynamic CPU speed (AUTO or MAX above), the maximum emulated performance level.", NULL, DBP_OptionCat::Performance, { @@ -534,7 +546,7 @@ static retro_core_option_v2_definition option_defs[DBP_Option::_OPTIONS_TOTAL] = }, { "dosbox_pure_cycles_scale", - "Detailed > Performance Scale", NULL, + "Performance Scale", NULL, "Fine tune the emulated performance for specific needs.", NULL, DBP_OptionCat::Performance, { @@ -550,7 +562,7 @@ static retro_core_option_v2_definition option_defs[DBP_Option::_OPTIONS_TOTAL] = }, { "dosbox_pure_cycle_limit", - "Detailed > Limit CPU Usage", NULL, + "Limit CPU Usage", NULL, "When emulating DOS as fast as possible, how much time per frame should be used by the emulation." "\n" "Lower this if your device becomes hot while using this core." "\n\n", NULL, //end of Performance > Detailed section DBP_OptionCat::Performance, @@ -570,7 +582,7 @@ static retro_core_option_v2_definition option_defs[DBP_Option::_OPTIONS_TOTAL] = }, { "dosbox_pure_perfstats", - "Advanced > Show Performance Statistics", NULL, + "Show Performance Statistics", NULL, "Enable this to show statistics about performance and framerate and check if emulation runs at full speed.", NULL, DBP_OptionCat::Performance, { @@ -907,7 +919,7 @@ static retro_core_option_v2_definition option_defs[DBP_Option::_OPTIONS_TOTAL] = }, { "dosbox_pure_cpu_core", - "Advanced > CPU Core", NULL, + "CPU Core", NULL, "Emulation method (DOSBox CPU core) used.", NULL, DBP_OptionCat::System, { @@ -929,7 +941,7 @@ static retro_core_option_v2_definition option_defs[DBP_Option::_OPTIONS_TOTAL] = }, { "dosbox_pure_bootos_ramdisk", - "Advanced > OS Disk Modifications (restart required)", NULL, + "OS Disk Modifications (restart required)", NULL, "When running an installed operating system, modifications to the C: drive will be made on the disk image by default." "\n" "Setting it to 'Discard' allows the content to be closed any time without worry of file system or registry corruption." "\n" "When using 'Save Difference Per Content' the disk image must never be modified again, otherwise existing differences become unusable.", NULL, @@ -943,7 +955,7 @@ static retro_core_option_v2_definition option_defs[DBP_Option::_OPTIONS_TOTAL] = }, { "dosbox_pure_bootos_dfreespace", - "Advanced > Free Space on D: in OS (restart required)", NULL, + "Free Space on D: in OS (restart required)", NULL, "Controls the amount of free space available on the D: drive when running an installed operating system." "\n" "If the total size of the D: drive (data + free space) exceeds 2 GB, it can't be used in earlier versions of Windows 95." "\n" "WARNING: Created save files are tied to this setting, so changing this will hide all existing D: drive changes.", NULL, @@ -953,7 +965,7 @@ static retro_core_option_v2_definition option_defs[DBP_Option::_OPTIONS_TOTAL] = }, { "dosbox_pure_bootos_forcenormal", - "Advanced > Force Normal Core in OS", NULL, + "Force Normal Core in OS", NULL, "The normal core can be more stable when running an installed operating system." "\n" "This can be toggled on and off to navigate around crashes." "\n\n", NULL, //end of System > Advanced section DBP_OptionCat::System, @@ -1035,7 +1047,7 @@ static retro_core_option_v2_definition option_defs[DBP_Option::_OPTIONS_TOTAL] = }, { "dosbox_pure_sblaster_type", - "Advanced > SoundBlaster Type", NULL, + "SoundBlaster Type", NULL, "Type of emulated SoundBlaster card.", NULL, DBP_OptionCat::Audio, { @@ -1051,7 +1063,7 @@ static retro_core_option_v2_definition option_defs[DBP_Option::_OPTIONS_TOTAL] = }, { "dosbox_pure_sblaster_adlib_mode", - "Advanced > SoundBlaster Adlib/FM Mode", NULL, + "SoundBlaster Adlib/FM Mode", NULL, "The SoundBlaster emulated FM synth mode. All modes are Adlib compatible except CMS.", NULL, DBP_OptionCat::Audio, { @@ -1067,7 +1079,7 @@ static retro_core_option_v2_definition option_defs[DBP_Option::_OPTIONS_TOTAL] = }, { "dosbox_pure_sblaster_adlib_emu", - "Advanced > SoundBlaster Adlib Provider", NULL, + "SoundBlaster Adlib Provider", NULL, "Provider for the Adlib emulation. Default has good quality and low performance requirements.", NULL, DBP_OptionCat::Audio, { @@ -1078,7 +1090,7 @@ static retro_core_option_v2_definition option_defs[DBP_Option::_OPTIONS_TOTAL] = }, { "dosbox_pure_gus", - "Advanced > Enable Gravis Ultrasound (restart required)", NULL, + "Enable Gravis Ultrasound (restart required)", NULL, "Enable Gravis Ultrasound emulation. Settings are fixed at port 0x240, IRQ 5, DMA 3." "\n" "If the ULTRADIR variable needs to be different than the default 'C:\\ULTRASND' you need to issue 'SET ULTRADIR=...' in the command line or in a batch file.", NULL, DBP_OptionCat::Audio, @@ -1087,7 +1099,7 @@ static retro_core_option_v2_definition option_defs[DBP_Option::_OPTIONS_TOTAL] = }, { "dosbox_pure_tandysound", - "Advanced > Enable Tandy Sound Device (restart required)", NULL, + "Enable Tandy Sound Device (restart required)", NULL, "Enable Tandy Sound Device emulation even when running without Tandy Graphics Adapter emulation.", NULL, DBP_OptionCat::Audio, { { "auto", "Off (default)" }, { "on", "On" } }, @@ -1095,12 +1107,85 @@ static retro_core_option_v2_definition option_defs[DBP_Option::_OPTIONS_TOTAL] = }, { "dosbox_pure_swapstereo", - "Advanced > Swap Stereo Channels", NULL, - "Swap the left and the right audio channel." "\n\n", NULL, //end of Audio > Advanced section + "Swap Stereo Channels", NULL, + "Swap the left and the right audio channel.", NULL, DBP_OptionCat::Audio, { { "false", "Off (default)" }, { "true", "On" } }, "false" }, + { + "dosbox_pure_mixer_master", + "Master Volume", NULL, + "Set the volume for the global Master mixer channel.", + NULL, + DBP_OptionCat::Audio, + { DBP_MIXER_VOLUME_VALUES }, + "100" + }, + { + "dosbox_pure_mixer_fm", + "Adlib Volume", NULL, + "Set the volume for the Adlib mixer channel.", + NULL, + DBP_OptionCat::Audio, + { DBP_MIXER_VOLUME_VALUES }, + "100" + }, + { + "dosbox_pure_mixer_sb", + "Sound Blaster Volume", NULL, + "Set the volume for Sound Blaster and Creative Music System mixer channels.", + NULL, + DBP_OptionCat::Audio, + { DBP_MIXER_VOLUME_VALUES }, + "100" + }, + { + "dosbox_pure_mixer_midi", + "MIDI/MT-32 Volume", NULL, + "Set the volume for internal General MIDI and Roland MT-32 mixer channels." "\n" + "This does not affect Frontend MIDI driver output.", + NULL, + DBP_OptionCat::Audio, + { DBP_MIXER_VOLUME_VALUES }, + "100" + }, + { + "dosbox_pure_mixer_gus", + "Gravis Volume", NULL, + "Set the volume for the Gravis Ultrasound mixer channel.", + NULL, + DBP_OptionCat::Audio, + { DBP_MIXER_VOLUME_VALUES }, + "100" + }, + { + "dosbox_pure_mixer_tandy", + "Tandy Volume", NULL, + "Set the volume for Tandy 1000 mixer channel.", + NULL, + DBP_OptionCat::Audio, + { DBP_MIXER_VOLUME_VALUES }, + "100" + }, + { + "dosbox_pure_mixer_disney", + "Disney Volume", NULL, + "Set the volume for the Disney Sound Source mixer channel.", + NULL, + DBP_OptionCat::Audio, + { DBP_MIXER_VOLUME_VALUES }, + "100" + }, + { + "dosbox_pure_mixer_spkr", + "PC-Speaker Volume", NULL, + "Set the volume for the PC-Speaker mixer channel.", + NULL, + DBP_OptionCat::Audio, + { DBP_MIXER_VOLUME_VALUES }, + "100" + }, { NULL, NULL, NULL, NULL, NULL, NULL, {{0}}, NULL } }; diff --git a/dosbox_pure_libretro.cpp b/dosbox_pure_libretro.cpp index e4bea514..1dfc1d43 100644 --- a/dosbox_pure_libretro.cpp +++ b/dosbox_pure_libretro.cpp @@ -303,6 +303,16 @@ static const char* retro_get_variable(const char* key, const char* default_value return (environ_cb && environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value ? var.value : default_value); } +static float dbp_mixer_option_to_gain(DBP_Option::Index idx) +{ + // Perceptual (logarithmic) volume: 0% is mute, 100% is 0 dB + int percent = atoi(DBP_Option::Get(idx)); + if (percent <= 0) return 0.0f; + if (percent >= 100) return 1.0f; + const float t = ((float)percent * 0.01f), db = (1.0f - t) * -30.0f; + return powf(10.0f, db * 0.05f); +} + // ------------------------------------------------------------------------------ void DBP_DOSBOX_ForceShutdown(const Bitu = 0); @@ -314,6 +324,8 @@ void DBP_SetMountSwappingRequested(); Bit32u DBP_MIXER_GetFrequency(); Bit32u DBP_MIXER_DoneSamplesCount(); void DBP_MIXER_ScrapAudio(); +void DBP_MIXER_SetMasterVolume(float volume); +void DBP_MIXER_SetChannelVolume(const char* channel_name, float volume); void MIXER_CallBack(void *userdata, uint8_t *stream, int len); bool MSCDEX_HasDrive(char driveLetter); int MSCDEX_AddDrive(char driveLetter, const char* physicalPath, Bit8u& subUnit); @@ -2325,6 +2337,17 @@ static bool check_variables() DBP_Option::GetAndApply(sec_mixer, "swapstereo", DBP_Option::swapstereo); extern bool dbp_swapstereo; dbp_swapstereo = (bool)control->GetProp("mixer", "swapstereo")->GetValue(); // to also get dosbox.conf override + DBP_MIXER_SetMasterVolume(dbp_mixer_option_to_gain(DBP_Option::mixer_master)); + DBP_MIXER_SetChannelVolume("DISNEY", dbp_mixer_option_to_gain(DBP_Option::mixer_disney)); + DBP_MIXER_SetChannelVolume("SPKR", dbp_mixer_option_to_gain(DBP_Option::mixer_spkr)); + DBP_MIXER_SetChannelVolume("SB", dbp_mixer_option_to_gain(DBP_Option::mixer_sb)); + DBP_MIXER_SetChannelVolume("CMS", dbp_mixer_option_to_gain(DBP_Option::mixer_sb)); + DBP_MIXER_SetChannelVolume("FM", dbp_mixer_option_to_gain(DBP_Option::mixer_fm)); + DBP_MIXER_SetChannelVolume("TSF", dbp_mixer_option_to_gain(DBP_Option::mixer_midi)); + DBP_MIXER_SetChannelVolume("MT32", dbp_mixer_option_to_gain(DBP_Option::mixer_midi)); + DBP_MIXER_SetChannelVolume("TANDY", dbp_mixer_option_to_gain(DBP_Option::mixer_tandy)); + DBP_MIXER_SetChannelVolume("TANDYDAC", dbp_mixer_option_to_gain(DBP_Option::mixer_tandy)); + DBP_MIXER_SetChannelVolume("GUS", dbp_mixer_option_to_gain(DBP_Option::mixer_gus)); if (dbp_state == DBPSTATE_BOOT) { @@ -3420,7 +3443,9 @@ void retro_run(void) } bool variable_update = false; - if (!dbp_optionsupdatecallback && control && environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &variable_update) && variable_update) + // Always poll variable updates in retro_run: some frontends report callback support + // but do not invoke update-display callbacks consistently. + if (control && environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &variable_update) && variable_update) check_variables(); // can't do this while DOS has crashed (control is NULL) // start input update diff --git a/include/mixer.h b/include/mixer.h index 876ed254..a49e9c17 100644 --- a/include/mixer.h +++ b/include/mixer.h @@ -48,6 +48,7 @@ class MixerChannel { public: void SetVolume(float _left,float _right); void SetScale( float f ); + void SetUserScale(float f); void UpdateVolume(void); void SetFreq(Bitu _freq); void Mix(Bitu _needed); @@ -80,6 +81,7 @@ class MixerChannel { MIXER_Handler handler; float volmain[2]; float scale; + float user_scale; Bit32s volmul[2]; //This gets added the frequency counter each mixer step diff --git a/src/hardware/mixer.cpp b/src/hardware/mixer.cpp index a7c3cfbd..c1a4b23e 100644 --- a/src/hardware/mixer.cpp +++ b/src/hardware/mixer.cpp @@ -89,6 +89,54 @@ static Mutex DBP_AudioMutex; #ifdef C_DBP_LIBRETRO bool dbp_swapstereo; + +struct DBP_MixerChannelVolumeOverride { + const char* channel_name; + float volume; +}; + +static float dbp_master_volume = 1.0f; +static DBP_MixerChannelVolumeOverride dbp_channel_volumes[] = { + { "DISNEY", 1.0f }, + { "SPKR", 1.0f }, + { "SB", 1.0f }, + { "FM", 1.0f }, + { "TSF", 1.0f }, + { "MT32", 1.0f }, + { "TANDY", 1.0f }, + { "TANDYDAC", 1.0f }, + { "CMS", 1.0f }, + { "GUS", 1.0f }, +}; + +static float DBP_MIXER_ClampVolume(float volume) +{ + if (volume < 0.0f) return 0.0f; + if (volume > 1.0f) return 1.0f; + return volume; +} + +static float DBP_MIXER_GetChannelVolume(const char* channel_name) +{ + for (size_t i = 0; i != (sizeof(dbp_channel_volumes) / sizeof(dbp_channel_volumes[0])); i++) + { + if (!strcasecmp(dbp_channel_volumes[i].channel_name, channel_name)) + return dbp_channel_volumes[i].volume; + } + return 1.0f; +} + +static void DBP_MIXER_SetChannelVolumeStored(const char* channel_name, float volume) +{ + for (size_t i = 0; i != (sizeof(dbp_channel_volumes) / sizeof(dbp_channel_volumes[0])); i++) + { + if (!strcasecmp(dbp_channel_volumes[i].channel_name, channel_name)) + { + dbp_channel_volumes[i].volume = volume; + return; + } + } +} #endif @@ -120,6 +168,11 @@ Bit8u MixTemp[MIXER_BUFSIZE]; MixerChannel * MIXER_AddChannel(MIXER_Handler handler,Bitu freq,const char * name) { MixerChannel * chan=new MixerChannel(); chan->scale = 1.0; +#ifdef C_DBP_LIBRETRO + chan->user_scale = DBP_MIXER_GetChannelVolume(name); +#else + chan->user_scale = 1.0f; +#endif chan->handler=handler; chan->name=name; chan->next=mixer.channels; @@ -160,8 +213,8 @@ void MIXER_DelChannel(MixerChannel* delchan) { } void MixerChannel::UpdateVolume(void) { - volmul[0]=(Bits)((1 << MIXER_VOLSHIFT)*scale*volmain[0]*mixer.mastervol[0]); - volmul[1]=(Bits)((1 << MIXER_VOLSHIFT)*scale*volmain[1]*mixer.mastervol[1]); + volmul[0]=(Bits)((1 << MIXER_VOLSHIFT)*scale*user_scale*volmain[0]*mixer.mastervol[0]); + volmul[1]=(Bits)((1 << MIXER_VOLSHIFT)*scale*user_scale*volmain[1]*mixer.mastervol[1]); } void MixerChannel::SetVolume(float _left,float _right) { @@ -175,6 +228,11 @@ void MixerChannel::SetScale( float f ) { UpdateVolume(); } +void MixerChannel::SetUserScale(float f) { + user_scale = f; + UpdateVolume(); +} + void MixerChannel::Enable(bool _yesno) { if (_yesno==enabled) return; enabled=_yesno; @@ -800,8 +858,13 @@ void MIXER_Init(Section* sec) { mixer.pos=0; mixer.done=0; memset(mixer.work,0,sizeof(mixer.work)); +#ifdef C_DBP_LIBRETRO + mixer.mastervol[0]=dbp_master_volume; + mixer.mastervol[1]=dbp_master_volume; +#else mixer.mastervol[0]=1.0f; mixer.mastervol[1]=1.0f; +#endif #ifdef C_DBP_USE_SDL /* Start the Mixer using SDL Sound at 22 khz */ @@ -855,6 +918,31 @@ Bit32u DBP_MIXER_GetFrequency() return mixer.freq; } +#ifdef C_DBP_LIBRETRO +void DBP_MIXER_SetMasterVolume(float volume) +{ + dbp_master_volume = DBP_MIXER_ClampVolume(volume); + SDL_LockAudio(); + mixer.mastervol[0] = dbp_master_volume; + mixer.mastervol[1] = dbp_master_volume; + for (MixerChannel * chan = mixer.channels; chan; chan = chan->next) + chan->UpdateVolume(); + SDL_UnlockAudio(); +} + +void DBP_MIXER_SetChannelVolume(const char* channel_name, float volume) +{ + if (!channel_name || !*channel_name) return; + volume = DBP_MIXER_ClampVolume(volume); + DBP_MIXER_SetChannelVolumeStored(channel_name, volume); + SDL_LockAudio(); + for (MixerChannel * chan = mixer.channels; chan; chan = chan->next) + if (!strcasecmp(chan->name, channel_name)) + chan->SetUserScale(volume); + SDL_UnlockAudio(); +} +#endif + Bit32u DBP_MIXER_DoneSamplesCount() { return mixer.done;