[Dialogs] Move SoundConfig dialog to its own class
This fixes a number of issues with the current implementation. Namely, some options were not properly saved and the audio driver was not properly reloaded when some options were changed. In addition, this moves most sound-related options to `g_owned_opts` and simplifies many call sites.
This commit is contained in:
parent
047ad27777
commit
c8106573d8
|
@ -2,8 +2,6 @@
|
||||||
Developer Information File
|
Developer Information File
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Known preprocessor switches:
|
Known preprocessor switches:
|
||||||
- SDL: Defined for the SDL version
|
- SDL: Defined for the SDL version
|
||||||
- GBA_LOGGING: Enables logging for the GBA core
|
- GBA_LOGGING: Enables logging for the GBA core
|
||||||
|
@ -13,46 +11,18 @@ Known preprocessor switches:
|
||||||
- RGB555: Use 16bit colors with 5bit green instead of 6bit green in hq3x/4x filters (C++ version)
|
- RGB555: Use 16bit colors with 5bit green instead of 6bit green in hq3x/4x filters (C++ version)
|
||||||
- NO_OGL: Exclude OpenGL code
|
- NO_OGL: Exclude OpenGL code
|
||||||
- NO_D3D: Exclude Direct3D code
|
- NO_D3D: Exclude Direct3D code
|
||||||
- NO_XAUDIO2: Exclude XAudio2 code (the XAudio2 interface is DirectSound's successor)
|
- VBAM_ENABLE_XAUDIO2: Enable XAudio2 code (the XAudio2 interface is DirectSound's successor)
|
||||||
|
- VBAM_ENABLE_FAUDIO: Enable FAudio code (the FAudio interface is an open source multiplatform re-implementation of XAudio2)
|
||||||
- NO_LINK: Exclude linking code (joybus, multilink, ...)
|
- NO_LINK: Exclude linking code (joybus, multilink, ...)
|
||||||
- WIN64: This macro is only defined for 64 bit builds
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Download locations:
|
|
||||||
NASM: http://nasm.us/
|
|
||||||
DirectX SDK: http://msdn.microsoft.com/en-us/xna/aa937788.aspx
|
|
||||||
OpenAL SDK: http://connect.creativelabs.com/openal/default.aspx
|
|
||||||
OpenGL files: http://www.opengl.org/registry/
|
|
||||||
zlib: http://zlib.net/
|
|
||||||
libpng: http://libpng.org/pub/png/libpng.html
|
|
||||||
|
|
||||||
You can find pre-built versions of zlib & libpng at:
|
|
||||||
http://spacy51.sp.funpic.de/VBA-M/libs/
|
|
||||||
Just extract them somewhere and point Visual C++ 2008 to the include & lib folders.
|
|
||||||
They are built with the static C runtime (this is what the release builds use).
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###########################
|
###########################
|
||||||
# --- Build Systems --- #
|
# --- Build Systems --- #
|
||||||
###########################
|
###########################
|
||||||
|
|
||||||
===Win32/MFC===
|
===src/sdl===
|
||||||
This is the full-featured Windows build using the MFC GUI.
|
|
||||||
The project files are located in /project/vc2008_mfc (VBA2008.sln) and /project/vs2010_mfc (VBA2010.sln).
|
|
||||||
Anyone distributing builds should be using MSVC 2010 SP1, the unpatched release has a bug where it applies SSE2 updates to mov and other instructions resulting in illegal instruction errors on cpu's only supporting SSE.
|
|
||||||
You also have to install Microsoft's DirectX SDK for Direct3D, DirectInput & XAudio2.
|
|
||||||
If you want to enable OpenAL sound output, install the OpenAL SDK. If you do not want it, add NO_OAL to the VBA-M project's preprocessor definitions.
|
|
||||||
SubWCRev.exe is used to append the svn versioning to the output executable, this as of TortoiseSVN 1.7, is only available by installing TortoiseSVN.
|
|
||||||
All other dependencies for MSVC builds may be found in the ../dependencies directory (above /trunk).
|
|
||||||
Normally, Windows users will want to checkout the root of the repository instead of just the trunk directory. Afterwards, simply opening the .sln of choice, setting preprocessor definitions, and hitting build is all that's required.
|
|
||||||
|
|
||||||
===*nix/GTK===
|
|
||||||
This is the standard build configuration on non-Windows.
|
This is the standard build configuration on non-Windows.
|
||||||
Running cmake will inform you of any packages you need to install.
|
Running cmake will inform you of any packages you need to install.
|
||||||
|
|
||||||
===*/wxw===
|
===src/wx===
|
||||||
The wxWidgets interface is an in-development frontend meant to be more cross-platform friendly than MFC and GTK.
|
The wxWidgets interface is an in-development frontend meant to be more cross-platform friendly than MFC and SDL.
|
||||||
Running cmake will inform you of any packages you need to install.
|
Running cmake will inform you of any packages you need to install.
|
||||||
NOTE: In addition to what cmake currently checks for, you will also need the wxrc tool and libgdiplus.
|
|
|
@ -39,6 +39,8 @@ set(VBAM_WX_COMMON
|
||||||
dialogs/gb-rom-info.h
|
dialogs/gb-rom-info.h
|
||||||
dialogs/joypad-config.cpp
|
dialogs/joypad-config.cpp
|
||||||
dialogs/joypad-config.h
|
dialogs/joypad-config.h
|
||||||
|
dialogs/sound-config.cpp
|
||||||
|
dialogs/sound-config.h
|
||||||
dialogs/validated-child.h
|
dialogs/validated-child.h
|
||||||
drawing.h
|
drawing.h
|
||||||
extra-translations.cpp
|
extra-translations.cpp
|
||||||
|
@ -87,11 +89,11 @@ set(VBAM_WX_COMMON
|
||||||
x11keymap.h
|
x11keymap.h
|
||||||
xrc/visualboyadvance-m.xpm
|
xrc/visualboyadvance-m.xpm
|
||||||
# Generated files.
|
# Generated files.
|
||||||
${VBAM_GENERATED_DIR}/wx//builtin-xrc.h
|
${VBAM_GENERATED_DIR}/wx/builtin-xrc.h
|
||||||
${VBAM_GENERATED_DIR}/wx//builtin-over.h
|
${VBAM_GENERATED_DIR}/wx/builtin-over.h
|
||||||
${VBAM_GENERATED_DIR}/wx//cmdhandlers.h
|
${VBAM_GENERATED_DIR}/wx/cmdhandlers.h
|
||||||
${VBAM_GENERATED_DIR}/wx//cmd-evtable.h
|
${VBAM_GENERATED_DIR}/wx/cmd-evtable.h
|
||||||
${VBAM_GENERATED_DIR}/wx//cmdtab.cpp
|
${VBAM_GENERATED_DIR}/wx/cmdtab.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if(NOT ZIP_PROGRAM)
|
if(NOT ZIP_PROGRAM)
|
||||||
|
@ -182,19 +184,19 @@ add_executable(
|
||||||
)
|
)
|
||||||
|
|
||||||
target_sources(visualboyadvance-m PRIVATE ${VBAM_WX_COMMON} ${VBAM_ICON_PATH})
|
target_sources(visualboyadvance-m PRIVATE ${VBAM_WX_COMMON} ${VBAM_ICON_PATH})
|
||||||
target_include_directories(visualboyadvance-m PRIVATE ${NONSTD_INCLUDE_DIR})
|
target_include_directories(visualboyadvance-m PRIVATE ${NONSTD_INCLUDE_DIR} ${SDL2_INCLUDE_DIRS})
|
||||||
|
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
visualboyadvance-m
|
visualboyadvance-m
|
||||||
nonstd-lib
|
nonstd-lib
|
||||||
vbam-core
|
vbam-core
|
||||||
vbam-components-audio-sdl
|
|
||||||
vbam-components-draw-text
|
vbam-components-draw-text
|
||||||
vbam-components-filters
|
vbam-components-filters
|
||||||
vbam-components-filters-agb
|
vbam-components-filters-agb
|
||||||
vbam-components-filters-interframe
|
vbam-components-filters-interframe
|
||||||
vbam-components-user-config
|
vbam-components-user-config
|
||||||
${OPENGL_LIBRARIES}
|
${OPENGL_LIBRARIES}
|
||||||
|
${VBAM_SDL2_LIBS}
|
||||||
)
|
)
|
||||||
|
|
||||||
# adjust link command when making a static binary for gcc
|
# adjust link command when making a static binary for gcc
|
||||||
|
@ -312,8 +314,7 @@ target_link_libraries(visualboyadvance-m ${OPENAL_LIBRARY})
|
||||||
# XAudio2.
|
# XAudio2.
|
||||||
if(ENABLE_XAUDIO2)
|
if(ENABLE_XAUDIO2)
|
||||||
target_sources(visualboyadvance-m PRIVATE xaudio2.cpp)
|
target_sources(visualboyadvance-m PRIVATE xaudio2.cpp)
|
||||||
else()
|
target_compile_definitions(visualboyadvance-m PRIVATE VBAM_ENABLE_XAUDIO2)
|
||||||
target_compile_definitions(visualboyadvance-m PRIVATE NO_XAUDIO2)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Direct3D.
|
# Direct3D.
|
||||||
|
@ -325,8 +326,7 @@ endif()
|
||||||
if(ENABLE_FAUDIO)
|
if(ENABLE_FAUDIO)
|
||||||
find_package(FAudio REQUIRED)
|
find_package(FAudio REQUIRED)
|
||||||
target_link_libraries(visualboyadvance-m FAudio::FAudio)
|
target_link_libraries(visualboyadvance-m FAudio::FAudio)
|
||||||
else()
|
target_compile_definitions(visualboyadvance-m PRIVATE VBAM_ENABLE_FAUDIO)
|
||||||
target_compile_definitions(visualboyadvance-m PRIVATE NO_FAUDIO)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# wxWidgets.
|
# wxWidgets.
|
||||||
|
@ -605,11 +605,6 @@ add_custom_command(
|
||||||
copy-events.cmake
|
copy-events.cmake
|
||||||
)
|
)
|
||||||
|
|
||||||
# # Win32 definitions common to all toolchains.
|
|
||||||
# if (WIN32)
|
|
||||||
# add_compile_definitions(wxUSE_GUI=1)
|
|
||||||
# endif()
|
|
||||||
|
|
||||||
set(VBAM_LOCALIZABLE_FILES ${VBAM_WX_COMMON})
|
set(VBAM_LOCALIZABLE_FILES ${VBAM_WX_COMMON})
|
||||||
list(APPEND VBAM_LOCALIZABLE_FILES
|
list(APPEND VBAM_LOCALIZABLE_FILES
|
||||||
autoupdater/autoupdater.h
|
autoupdater/autoupdater.h
|
||||||
|
|
|
@ -1686,30 +1686,13 @@ EVT_HANDLER(ToggleSound, "Enable/disable all sound channels")
|
||||||
|
|
||||||
EVT_HANDLER(IncreaseVolume, "Increase volume")
|
EVT_HANDLER(IncreaseVolume, "Increase volume")
|
||||||
{
|
{
|
||||||
gopts.sound_vol += 5;
|
OPTION(kSoundVolume) += 5;
|
||||||
|
|
||||||
if (gopts.sound_vol > 200)
|
|
||||||
gopts.sound_vol = 200;
|
|
||||||
|
|
||||||
update_opts();
|
|
||||||
soundSetVolume((float)gopts.sound_vol / 100.0);
|
|
||||||
wxString msg;
|
|
||||||
msg.Printf(_("Volume: %d %%"), gopts.sound_vol);
|
|
||||||
systemScreenMessage(msg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EVT_HANDLER(DecreaseVolume, "Decrease volume")
|
EVT_HANDLER(DecreaseVolume, "Decrease volume")
|
||||||
{
|
{
|
||||||
gopts.sound_vol -= 5;
|
OPTION(kSoundVolume) -= 5;
|
||||||
|
|
||||||
if (gopts.sound_vol < 0)
|
|
||||||
gopts.sound_vol = 0;
|
|
||||||
|
|
||||||
update_opts();
|
|
||||||
soundSetVolume((float)gopts.sound_vol / 100.0);
|
|
||||||
wxString msg;
|
|
||||||
msg.Printf(_("Volume: %d %%"), gopts.sound_vol);
|
|
||||||
systemScreenMessage(msg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EVT_HANDLER_MASK(NextFrame, "Next Frame", CMDEN_GB | CMDEN_GBA)
|
EVT_HANDLER_MASK(NextFrame, "Next Frame", CMDEN_GB | CMDEN_GBA)
|
||||||
|
@ -2174,45 +2157,13 @@ EVT_HANDLER_MASK(ChangeIFB, "Change Interframe Blending", CMDEN_NREC_ANY)
|
||||||
|
|
||||||
EVT_HANDLER_MASK(SoundConfigure, "Sound options...", CMDEN_NREC_ANY)
|
EVT_HANDLER_MASK(SoundConfigure, "Sound options...", CMDEN_NREC_ANY)
|
||||||
{
|
{
|
||||||
int oqual = gopts.sound_qual, oapi = gopts.audio_api;
|
if (ShowModal(GetXRCDialog("SoundConfig")) != wxID_OK)
|
||||||
bool oupmix = gopts.upmix, ohw = gopts.dsound_hw_accel;
|
|
||||||
wxString odev = gopts.audio_dev;
|
|
||||||
wxDialog* dlg = GetXRCDialog("SoundConfig");
|
|
||||||
|
|
||||||
if (ShowModal(dlg) != wxID_OK)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
switch (panel->game_type()) {
|
// No point in observing these since they can only be set in this dialog.
|
||||||
case IMAGE_UNKNOWN:
|
gb_effects_config.echo = (float)OPTION(kSoundGBEcho) / 100.0;
|
||||||
break;
|
gb_effects_config.stereo = (float)OPTION(kSoundGBStereo) / 100.0;
|
||||||
|
soundFiltering = (float)OPTION(kSoundGBAFiltering) / 100.0f;
|
||||||
case IMAGE_GB:
|
|
||||||
gb_effects_config.echo = (float)gopts.gb_echo / 100.0;
|
|
||||||
gb_effects_config.stereo = (float)gopts.gb_stereo / 100.0;
|
|
||||||
gbSoundSetSampleRate(!gopts.sound_qual ? 48000 : 44100 / (1 << (gopts.sound_qual - 1)));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IMAGE_GBA:
|
|
||||||
soundSetSampleRate(!gopts.sound_qual ? 48000 : 44100 / (1 << (gopts.sound_qual - 1)));
|
|
||||||
soundFiltering = (float)gopts.gba_sound_filter / 100.0f;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// changing sample rate causes driver reload, so no explicit reload needed
|
|
||||||
if (oqual == gopts.sound_qual &&
|
|
||||||
// otherwise reload if API changes
|
|
||||||
(oapi != gopts.audio_api || odev != gopts.audio_dev ||
|
|
||||||
// or init-only options
|
|
||||||
(oapi == AUD_XAUDIO2 && oupmix != gopts.upmix) || (oapi == AUD_FAUDIO && oupmix != gopts.upmix) || (oapi == AUD_DIRECTSOUND && ohw != gopts.dsound_hw_accel))) {
|
|
||||||
soundShutdown();
|
|
||||||
|
|
||||||
if (!soundInit()) {
|
|
||||||
wxLogError(_("Could not initialize the sound driver!"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
soundSetVolume((float)gopts.sound_vol / 100.0);
|
|
||||||
update_opts();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EVT_HANDLER(EmulatorDirectories, "Directories...")
|
EVT_HANDLER(EmulatorDirectories, "Directories...")
|
||||||
|
|
|
@ -77,47 +77,26 @@ static const std::array<wxString, kNbRenderMethods> kRenderMethodStrings = {
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
// This enum must be kept in sync with the one in wxvbam.h
|
|
||||||
// TODO: These 2 enums should be unified and a validator created for this enum.
|
|
||||||
// TODO: DirectSound and XAudio2 should only be used on Windows.
|
|
||||||
enum class AudioApi {
|
|
||||||
kSdl = 0,
|
|
||||||
kOpenAL,
|
|
||||||
kDirectSound,
|
|
||||||
kXAudio2,
|
|
||||||
kFaudio,
|
|
||||||
|
|
||||||
// Do not add anything under here.
|
|
||||||
kLast,
|
|
||||||
};
|
|
||||||
constexpr size_t kNbAudioApis = static_cast<size_t>(AudioApi::kLast);
|
|
||||||
|
|
||||||
// These MUST follow the same order as the definitions of the enum above.
|
// These MUST follow the same order as the definitions of the enum above.
|
||||||
// Adding an option without adding to this array will result in a compiler
|
// Adding an option without adding to this array will result in a compiler
|
||||||
// error since kNbAudioApis is automatically updated.
|
// error since kNbAudioApis is automatically updated.
|
||||||
static const std::array<wxString, kNbAudioApis> kAudioApiStrings = {
|
static const std::array<wxString, kNbAudioApis> kAudioApiStrings = {
|
||||||
"sdl",
|
|
||||||
"openal",
|
"openal",
|
||||||
|
#if defined(__WXMSW__)
|
||||||
"directsound",
|
"directsound",
|
||||||
|
#endif
|
||||||
|
#if defined(VBAM_ENABLE_XAUDIO2)
|
||||||
"xaudio2",
|
"xaudio2",
|
||||||
|
#endif
|
||||||
|
#if defined(VBAM_ENABLE_FAUDIO)
|
||||||
"faudio",
|
"faudio",
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class SoundQuality {
|
|
||||||
k48kHz = 0,
|
|
||||||
k44kHz,
|
|
||||||
k22kHz,
|
|
||||||
k11kHz,
|
|
||||||
|
|
||||||
// Do not add anything under here.
|
|
||||||
kLast,
|
|
||||||
};
|
|
||||||
constexpr size_t kNbSoundQualities = static_cast<size_t>(SoundQuality::kLast);
|
|
||||||
|
|
||||||
// These MUST follow the same order as the definitions of the enum above.
|
// These MUST follow the same order as the definitions of the enum above.
|
||||||
// Adding an option without adding to this array will result in a compiler
|
// Adding an option without adding to this array will result in a compiler
|
||||||
// error since kNbSoundQualities is automatically updated.
|
// error since kNbSoundQualities is automatically updated.
|
||||||
static const std::array<wxString, kNbSoundQualities> kSoundQualityStrings = {
|
static const std::array<wxString, kNbSoundRate> kAudioRateStrings = {
|
||||||
"48",
|
"48",
|
||||||
"44",
|
"44",
|
||||||
"22",
|
"22",
|
||||||
|
@ -176,9 +155,11 @@ std::array<Option, kNbOptions>& Option::All() {
|
||||||
|
|
||||||
/// GBA
|
/// GBA
|
||||||
bool gba_lcd_filter = false;
|
bool gba_lcd_filter = false;
|
||||||
|
#ifndef NO_LINK
|
||||||
bool link_auto = false;
|
bool link_auto = false;
|
||||||
bool link_hacks = true;
|
bool link_hacks = true;
|
||||||
bool link_proto = false;
|
bool link_proto = false;
|
||||||
|
#endif
|
||||||
wxString gba_rom_dir;
|
wxString gba_rom_dir;
|
||||||
|
|
||||||
/// Core
|
/// Core
|
||||||
|
@ -226,9 +207,25 @@ std::array<Option, kNbOptions>& Option::All() {
|
||||||
bool allow_joystick_background_input = true;
|
bool allow_joystick_background_input = true;
|
||||||
|
|
||||||
/// Sound
|
/// Sound
|
||||||
|
#if defined(VBAM_ENABLE_XAUDIO2)
|
||||||
|
AudioApi audio_api = AudioApi::kXAudio2;
|
||||||
|
#else
|
||||||
|
AudioApi audio_api = AudioApi::kOpenAL;
|
||||||
|
#endif
|
||||||
|
wxString audio_dev;
|
||||||
|
// 10 fixes stuttering on mac with openal, as opposed to 5
|
||||||
|
// also should be better for modern hardware in general
|
||||||
|
int32_t audio_buffers = 10;
|
||||||
|
int32_t gba_sound_filtering = 50;
|
||||||
bool gb_declicking = true;
|
bool gb_declicking = true;
|
||||||
|
int32_t gb_echo = 20;
|
||||||
bool gb_effects_config_enabled = false;
|
bool gb_effects_config_enabled = false;
|
||||||
|
int32_t gb_stereo = 15;
|
||||||
bool gb_effects_config_surround = false;
|
bool gb_effects_config_surround = false;
|
||||||
|
AudioRate sound_quality = AudioRate::k44kHz;
|
||||||
|
bool dsound_hw_accel = false;
|
||||||
|
bool upmix = false;
|
||||||
|
int32_t volume = 100;
|
||||||
};
|
};
|
||||||
static OwnedOptions g_owned_opts;
|
static OwnedOptions g_owned_opts;
|
||||||
|
|
||||||
|
@ -352,19 +349,21 @@ std::array<Option, kNbOptions>& Option::All() {
|
||||||
Option(OptionID::kUISuspendScreenSaver, &gopts.suspend_screensaver),
|
Option(OptionID::kUISuspendScreenSaver, &gopts.suspend_screensaver),
|
||||||
|
|
||||||
/// Sound
|
/// Sound
|
||||||
Option(OptionID::kSoundAudioAPI, &gopts.audio_api),
|
Option(OptionID::kSoundAudioAPI, &g_owned_opts.audio_api),
|
||||||
Option(OptionID::kSoundAudioDevice, &gopts.audio_dev),
|
Option(OptionID::kSoundAudioDevice, &g_owned_opts.audio_dev),
|
||||||
Option(OptionID::kSoundBuffers, &gopts.audio_buffers, 2, 10),
|
Option(OptionID::kSoundBuffers, &g_owned_opts.audio_buffers, 2, 10),
|
||||||
Option(OptionID::kSoundEnable, &gopts.sound_en, 0, 0x30f),
|
Option(OptionID::kSoundEnable, &gopts.sound_en, 0, 0x30f),
|
||||||
Option(OptionID::kSoundGBAFiltering, &gopts.gba_sound_filter, 0, 100),
|
Option(OptionID::kSoundGBAFiltering, &g_owned_opts.gba_sound_filtering, 0, 100),
|
||||||
Option(OptionID::kSoundGBAInterpolation, &g_gbaSoundInterpolation),
|
Option(OptionID::kSoundGBAInterpolation, &g_gbaSoundInterpolation),
|
||||||
Option(OptionID::kSoundGBDeclicking, &g_owned_opts.gb_declicking),
|
Option(OptionID::kSoundGBDeclicking, &g_owned_opts.gb_declicking),
|
||||||
Option(OptionID::kSoundGBEcho, &gopts.gb_echo, 0, 100),
|
Option(OptionID::kSoundGBEcho, &g_owned_opts.gb_echo, 0, 100),
|
||||||
Option(OptionID::kSoundGBEnableEffects, &g_owned_opts.gb_effects_config_enabled),
|
Option(OptionID::kSoundGBEnableEffects, &g_owned_opts.gb_effects_config_enabled),
|
||||||
Option(OptionID::kSoundGBStereo, &gopts.gb_stereo, 0, 100),
|
Option(OptionID::kSoundGBStereo, &g_owned_opts.gb_stereo, 0, 100),
|
||||||
Option(OptionID::kSoundGBSurround, &g_owned_opts.gb_effects_config_surround),
|
Option(OptionID::kSoundGBSurround, &g_owned_opts.gb_effects_config_surround),
|
||||||
Option(OptionID::kSoundQuality, &gopts.sound_qual),
|
Option(OptionID::kSoundAudioRate, &g_owned_opts.sound_quality),
|
||||||
Option(OptionID::kSoundVolume, &gopts.sound_vol, 0, 200),
|
Option(OptionID::kSoundDSoundHWAccel, &g_owned_opts.dsound_hw_accel),
|
||||||
|
Option(OptionID::kSoundUpmix, &g_owned_opts.upmix),
|
||||||
|
Option(OptionID::kSoundVolume, &g_owned_opts.volume, 0, 200),
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
return g_all_opts;
|
return g_all_opts;
|
||||||
|
@ -377,8 +376,7 @@ namespace internal {
|
||||||
// error since kNbOptions is automatically updated.
|
// error since kNbOptions is automatically updated.
|
||||||
const std::array<OptionData, kNbOptions + 1> kAllOptionsData = {
|
const std::array<OptionData, kNbOptions + 1> kAllOptionsData = {
|
||||||
/// Display
|
/// Display
|
||||||
OptionData{"Display/Bilinear", "Bilinear",
|
OptionData{"Display/Bilinear", "Bilinear", _("Use bilinear filter with 3d renderer")},
|
||||||
_("Use bilinear filter with 3d renderer")},
|
|
||||||
OptionData{"Display/Filter", "", _("Full-screen filter to apply")},
|
OptionData{"Display/Filter", "", _("Full-screen filter to apply")},
|
||||||
OptionData{"Display/FilterPlugin", "", _("Filter plugin library")},
|
OptionData{"Display/FilterPlugin", "", _("Filter plugin library")},
|
||||||
OptionData{"Display/IFB", "", _("Interframe blending function")},
|
OptionData{"Display/IFB", "", _("Interframe blending function")},
|
||||||
|
@ -388,20 +386,14 @@ const std::array<OptionData, kNbOptions + 1> kAllOptionsData = {
|
||||||
OptionData{"Display/RenderMethod", "",
|
OptionData{"Display/RenderMethod", "",
|
||||||
_("Render method; if unsupported, simple method will be used")},
|
_("Render method; if unsupported, simple method will be used")},
|
||||||
OptionData{"Display/Scale", "", _("Default scale factor")},
|
OptionData{"Display/Scale", "", _("Default scale factor")},
|
||||||
OptionData{"Display/Stretch", "RetainAspect",
|
OptionData{"Display/Stretch", "RetainAspect", _("Retain aspect ratio when resizing")},
|
||||||
_("Retain aspect ratio when resizing")},
|
|
||||||
|
|
||||||
/// GB
|
/// GB
|
||||||
OptionData{"GB/BiosFile", "",
|
OptionData{"GB/BiosFile", "", _("BIOS file to use for Game Boy, if enabled")},
|
||||||
_("BIOS file to use for Game Boy, if enabled")},
|
OptionData{"GB/ColorOption", "GBColorOption", _("Game Boy color enhancement, if enabled")},
|
||||||
OptionData{"GB/ColorOption", "GBColorOption",
|
OptionData{"GB/ColorizerHack", "ColorizerHack", _("Enable DX Colorization Hacks")},
|
||||||
_("Game Boy color enhancement, if enabled")},
|
OptionData{"GB/LCDFilter", "GBLcdFilter", _("Apply LCD filter, if enabled")},
|
||||||
OptionData{"GB/ColorizerHack", "ColorizerHack",
|
OptionData{"GB/GBCBiosFile", "", _("BIOS file to use for Game Boy Color, if enabled")},
|
||||||
_("Enable DX Colorization Hacks")},
|
|
||||||
OptionData{"GB/LCDFilter", "GBLcdFilter",
|
|
||||||
_("Apply LCD filter, if enabled")},
|
|
||||||
OptionData{"GB/GBCBiosFile", "",
|
|
||||||
_("BIOS file to use for Game Boy Color, if enabled")},
|
|
||||||
OptionData{"GB/Palette0", "",
|
OptionData{"GB/Palette0", "",
|
||||||
_("The default palette, as 8 comma-separated 4-digit hex "
|
_("The default palette, as 8 comma-separated 4-digit hex "
|
||||||
"integers (rgb555).")},
|
"integers (rgb555).")},
|
||||||
|
@ -417,8 +409,7 @@ const std::array<OptionData, kNbOptions + 1> kAllOptionsData = {
|
||||||
_("Automatically save printouts as screen captures with -print "
|
_("Automatically save printouts as screen captures with -print "
|
||||||
"suffix")},
|
"suffix")},
|
||||||
OptionData{"GB/ROMDir", "", _("Directory to look for ROM files")},
|
OptionData{"GB/ROMDir", "", _("Directory to look for ROM files")},
|
||||||
OptionData{"GB/GBCROMDir", "",
|
OptionData{"GB/GBCROMDir", "", _("Directory to look for Game Boy Color ROM files")},
|
||||||
_("Directory to look for Game Boy Color ROM files")},
|
|
||||||
|
|
||||||
/// GBA
|
/// GBA
|
||||||
OptionData{"GBA/BiosFile", "", _("BIOS file to use, if enabled")},
|
OptionData{"GBA/BiosFile", "", _("BIOS file to use, if enabled")},
|
||||||
|
@ -440,8 +431,7 @@ const std::array<OptionData, kNbOptions + 1> kAllOptionsData = {
|
||||||
},
|
},
|
||||||
OptionData{"GBA/LinkHost", "", _("Default network link client host")},
|
OptionData{"GBA/LinkHost", "", _("Default network link client host")},
|
||||||
OptionData{"GBA/ServerIP", "", _("Default network link server IP to bind")},
|
OptionData{"GBA/ServerIP", "", _("Default network link server IP to bind")},
|
||||||
OptionData{"GBA/LinkPort", "",
|
OptionData{"GBA/LinkPort", "", _("Default network link port (server and client)")},
|
||||||
_("Default network link port (server and client)")},
|
|
||||||
OptionData{"GBA/LinkProto", "LinkProto", _("Default network protocol")},
|
OptionData{"GBA/LinkProto", "LinkProto", _("Default network protocol")},
|
||||||
OptionData{"GBA/LinkTimeout", "LinkTimeout", _("Link timeout (ms)")},
|
OptionData{"GBA/LinkTimeout", "LinkTimeout", _("Link timeout (ms)")},
|
||||||
OptionData{"GBA/LinkType", "LinkType", _("Link cable type")},
|
OptionData{"GBA/LinkType", "LinkType", _("Link cable type")},
|
||||||
|
@ -449,8 +439,7 @@ const std::array<OptionData, kNbOptions + 1> kAllOptionsData = {
|
||||||
OptionData{"GBA/ROMDir", "", _("Directory to look for ROM files")},
|
OptionData{"GBA/ROMDir", "", _("Directory to look for ROM files")},
|
||||||
|
|
||||||
/// General
|
/// General
|
||||||
OptionData{"General/AutoLoadLastState", "",
|
OptionData{"General/AutoLoadLastState", "", _("Automatically load last saved state")},
|
||||||
_("Automatically load last saved state")},
|
|
||||||
OptionData{"General/BatteryDir", "",
|
OptionData{"General/BatteryDir", "",
|
||||||
_("Directory to store game save files (relative paths are "
|
_("Directory to store game save files (relative paths are "
|
||||||
"relative to ROM; blank is config dir)")},
|
"relative to ROM; blank is config dir)")},
|
||||||
|
@ -476,10 +465,8 @@ const std::array<OptionData, kNbOptions + 1> kAllOptionsData = {
|
||||||
"Button is one of Up, Down, Left, Right, A, B, L, R, Select, "
|
"Button is one of Up, Down, Left, Right, A, B, L, R, Select, "
|
||||||
"Start, MotionUp, MotionDown, MotionLeft, MotionRight, AutoA, "
|
"Start, MotionUp, MotionDown, MotionLeft, MotionRight, AutoA, "
|
||||||
"AutoB, Speed, Capture, GS")},
|
"AutoB, Speed, Capture, GS")},
|
||||||
OptionData{"Joypad/AutofireThrottle", "",
|
OptionData{"Joypad/AutofireThrottle", "", _("The autofire toggle period, in frames (1/60 s)")},
|
||||||
_("The autofire toggle period, in frames (1/60 s)")},
|
OptionData{"Joypad/Default", "", _("The number of the stick to use in single-player mode")},
|
||||||
OptionData{"Joypad/Default", "",
|
|
||||||
_("The number of the stick to use in single-player mode")},
|
|
||||||
OptionData{"Joypad/SDLGameControllerMode", "SDLGameControllerMode",
|
OptionData{"Joypad/SDLGameControllerMode", "SDLGameControllerMode",
|
||||||
_("Whether to enable SDL GameController mode")},
|
_("Whether to enable SDL GameController mode")},
|
||||||
|
|
||||||
|
@ -490,10 +477,8 @@ const std::array<OptionData, kNbOptions + 1> kAllOptionsData = {
|
||||||
"pressed, the command <cmd> is executed.")},
|
"pressed, the command <cmd> is executed.")},
|
||||||
|
|
||||||
/// Core
|
/// Core
|
||||||
OptionData{"preferences/agbPrint", "AGBPrinter",
|
OptionData{"preferences/agbPrint", "AGBPrinter", _("Enable AGB debug print")},
|
||||||
_("Enable AGB debug print")},
|
OptionData{"preferences/autoFrameSkip", "FrameSkipAuto", _("Auto skip frames")},
|
||||||
OptionData{"preferences/autoFrameSkip", "FrameSkipAuto",
|
|
||||||
_("Auto skip frames")},
|
|
||||||
OptionData{"preferences/autoPatch", "ApplyPatches",
|
OptionData{"preferences/autoPatch", "ApplyPatches",
|
||||||
_("Apply IPS / UPS / IPF patches if found")},
|
_("Apply IPS / UPS / IPF patches if found")},
|
||||||
OptionData{"preferences/autoSaveLoadCheatList", "",
|
OptionData{"preferences/autoSaveLoadCheatList", "",
|
||||||
|
@ -504,41 +489,32 @@ const std::array<OptionData, kNbOptions + 1> kAllOptionsData = {
|
||||||
_("Automatically enable border for Super Game Boy games"),
|
_("Automatically enable border for Super Game Boy games"),
|
||||||
},
|
},
|
||||||
OptionData{"preferences/borderOn", "", _("Always enable border")},
|
OptionData{"preferences/borderOn", "", _("Always enable border")},
|
||||||
OptionData{"preferences/captureFormat", "",
|
OptionData{"preferences/captureFormat", "", _("Screen capture file format")},
|
||||||
_("Screen capture file format")},
|
|
||||||
OptionData{"preferences/cheatsEnabled", "", _("Enable cheats")},
|
OptionData{"preferences/cheatsEnabled", "", _("Enable cheats")},
|
||||||
OptionData{"preferences/disableStatus", "NoStatusMsg",
|
OptionData{"preferences/disableStatus", "NoStatusMsg", _("Disable on-screen status messages")},
|
||||||
_("Disable on-screen status messages")},
|
|
||||||
OptionData{"preferences/emulatorType", "", _("Type of system to emulate")},
|
OptionData{"preferences/emulatorType", "", _("Type of system to emulate")},
|
||||||
OptionData{"preferences/flashSize", "",
|
OptionData{"preferences/flashSize", "", _("Flash size 0 = 64 KB 1 = 128 KB")},
|
||||||
_("Flash size 0 = 64 KB 1 = 128 KB")},
|
|
||||||
OptionData{"preferences/frameSkip", "FrameSkip",
|
OptionData{"preferences/frameSkip", "FrameSkip",
|
||||||
_("Skip frames. Values are 0-9 or -1 to skip automatically "
|
_("Skip frames. Values are 0-9 or -1 to skip automatically "
|
||||||
"based on time.")},
|
"based on time.")},
|
||||||
OptionData{"preferences/gbPaletteOption", "", _("The palette to use")},
|
OptionData{"preferences/gbPaletteOption", "", _("The palette to use")},
|
||||||
OptionData{"preferences/gbPrinter", "Printer",
|
OptionData{"preferences/gbPrinter", "Printer", _("Enable printer emulation")},
|
||||||
_("Enable printer emulation")},
|
|
||||||
OptionData{"preferences/gdbBreakOnLoad", "DebugGDBBreakOnLoad",
|
OptionData{"preferences/gdbBreakOnLoad", "DebugGDBBreakOnLoad",
|
||||||
_("Break into GDB after loading the game.")},
|
_("Break into GDB after loading the game.")},
|
||||||
OptionData{"preferences/gdbPort", "DebugGDBPort",
|
OptionData{"preferences/gdbPort", "DebugGDBPort", _("Port to connect GDB to")},
|
||||||
_("Port to connect GDB to")},
|
|
||||||
#ifndef NO_LINK
|
#ifndef NO_LINK
|
||||||
OptionData{"preferences/LinkNumPlayers", "",
|
OptionData{"preferences/LinkNumPlayers", "", _("Number of players in network")},
|
||||||
_("Number of players in network")},
|
|
||||||
#endif
|
#endif
|
||||||
OptionData{"preferences/maxScale", "",
|
OptionData{"preferences/maxScale", "", _("Maximum scale factor (0 = no limit)")},
|
||||||
_("Maximum scale factor (0 = no limit)")},
|
|
||||||
OptionData{"preferences/pauseWhenInactive", "PauseWhenInactive",
|
OptionData{"preferences/pauseWhenInactive", "PauseWhenInactive",
|
||||||
_("Pause game when main window loses focus")},
|
_("Pause game when main window loses focus")},
|
||||||
OptionData{"preferences/rtcEnabled", "RTC",
|
OptionData{"preferences/rtcEnabled", "RTC",
|
||||||
_("Enable RTC (vba-over.ini override is rtcEnabled")},
|
_("Enable RTC (vba-over.ini override is rtcEnabled")},
|
||||||
OptionData{"preferences/saveType", "",
|
OptionData{"preferences/saveType", "", _("Native save (\"battery\") hardware type")},
|
||||||
_("Native save (\"battery\") hardware type")},
|
|
||||||
OptionData{"preferences/showSpeed", "", _("Show speed indicator")},
|
OptionData{"preferences/showSpeed", "", _("Show speed indicator")},
|
||||||
OptionData{"preferences/showSpeedTransparent", "Transparent",
|
OptionData{"preferences/showSpeedTransparent", "Transparent",
|
||||||
_("Draw on-screen messages transparently")},
|
_("Draw on-screen messages transparently")},
|
||||||
OptionData{"preferences/skipBios", "SkipIntro",
|
OptionData{"preferences/skipBios", "SkipIntro", _("Skip BIOS initialization")},
|
||||||
_("Skip BIOS initialization")},
|
|
||||||
OptionData{"preferences/skipSaveGameCheats", "",
|
OptionData{"preferences/skipSaveGameCheats", "",
|
||||||
_("Do not overwrite cheat list when loading state")},
|
_("Do not overwrite cheat list when loading state")},
|
||||||
OptionData{"preferences/skipSaveGameBattery", "",
|
OptionData{"preferences/skipSaveGameBattery", "",
|
||||||
|
@ -553,54 +529,45 @@ const std::array<OptionData, kNbOptions + 1> kAllOptionsData = {
|
||||||
"throttle)")},
|
"throttle)")},
|
||||||
OptionData{"preferences/speedupThrottleFrameSkip", "",
|
OptionData{"preferences/speedupThrottleFrameSkip", "",
|
||||||
_("Use frame skip for speedup throttle")},
|
_("Use frame skip for speedup throttle")},
|
||||||
OptionData{"preferences/useBiosGB", "BootRomGB",
|
OptionData{"preferences/useBiosGB", "BootRomGB", _("Use the specified BIOS file for Game Boy")},
|
||||||
_("Use the specified BIOS file for Game Boy")},
|
OptionData{"preferences/useBiosGBA", "BootRomEn", _("Use the specified BIOS file")},
|
||||||
OptionData{"preferences/useBiosGBA", "BootRomEn",
|
|
||||||
_("Use the specified BIOS file")},
|
|
||||||
OptionData{"preferences/useBiosGBC", "BootRomGBC",
|
OptionData{"preferences/useBiosGBC", "BootRomGBC",
|
||||||
_("Use the specified BIOS file for Game Boy Color")},
|
_("Use the specified BIOS file for Game Boy Color")},
|
||||||
OptionData{"preferences/vsync", "VSync", _("Wait for vertical sync")},
|
OptionData{"preferences/vsync", "VSync", _("Wait for vertical sync")},
|
||||||
|
|
||||||
/// Geometry
|
/// Geometry
|
||||||
OptionData{"geometry/fullScreen", "Fullscreen",
|
OptionData{"geometry/fullScreen", "Fullscreen", _("Enter fullscreen mode at startup")},
|
||||||
_("Enter fullscreen mode at startup")},
|
|
||||||
OptionData{"geometry/isMaximized", "Maximized", _("Window maximized")},
|
OptionData{"geometry/isMaximized", "Maximized", _("Window maximized")},
|
||||||
OptionData{"geometry/windowHeight", "Height",
|
OptionData{"geometry/windowHeight", "Height", _("Window height at startup")},
|
||||||
_("Window height at startup")},
|
|
||||||
OptionData{"geometry/windowWidth", "Width", _("Window width at startup")},
|
OptionData{"geometry/windowWidth", "Width", _("Window width at startup")},
|
||||||
OptionData{"geometry/windowX", "X", _("Window axis X position at startup")},
|
OptionData{"geometry/windowX", "X", _("Window axis X position at startup")},
|
||||||
OptionData{"geometry/windowY", "Y", _("Window axis Y position at startup")},
|
OptionData{"geometry/windowY", "Y", _("Window axis Y position at startup")},
|
||||||
|
|
||||||
/// UI
|
/// UI
|
||||||
OptionData{"ui/allowKeyboardBackgroundInput",
|
OptionData{"ui/allowKeyboardBackgroundInput", "AllowKeyboardBackgroundInput",
|
||||||
"AllowKeyboardBackgroundInput",
|
|
||||||
_("Capture key events while on background")},
|
_("Capture key events while on background")},
|
||||||
OptionData{"ui/allowJoystickBackgroundInput",
|
OptionData{"ui/allowJoystickBackgroundInput", "AllowJoystickBackgroundInput",
|
||||||
"AllowJoystickBackgroundInput",
|
|
||||||
_("Capture joy events while on background")},
|
_("Capture joy events while on background")},
|
||||||
OptionData{"ui/hideMenuBar", "HideMenuBar", _("Hide menu bar when mouse is inactive")},
|
OptionData{"ui/hideMenuBar", "HideMenuBar", _("Hide menu bar when mouse is inactive")},
|
||||||
OptionData{"ui/suspendScreenSaver", "SuspendScreenSaver", _("Suspend screensaver when game is running")},
|
OptionData{"ui/suspendScreenSaver", "SuspendScreenSaver",
|
||||||
|
_("Suspend screensaver when game is running")},
|
||||||
|
|
||||||
/// Sound
|
/// Sound
|
||||||
OptionData{"Sound/AudioAPI", "",
|
OptionData{"Sound/AudioAPI", "", _("Sound API; if unsupported, default API will be used")},
|
||||||
_("Sound API; if unsupported, default API will be used")},
|
OptionData{"Sound/AudioDevice", "", _("Device ID of chosen audio device for chosen driver")},
|
||||||
OptionData{"Sound/AudioDevice", "",
|
|
||||||
_("Device ID of chosen audio device for chosen driver")},
|
|
||||||
OptionData{"Sound/Buffers", "", _("Number of sound buffers")},
|
OptionData{"Sound/Buffers", "", _("Number of sound buffers")},
|
||||||
OptionData{"Sound/Enable", "", _("Bit mask of sound channels to enable")},
|
OptionData{"Sound/Enable", "", _("Bit mask of sound channels to enable")},
|
||||||
OptionData{"Sound/GBAFiltering", "",
|
OptionData{"Sound/GBAFiltering", "", _("Game Boy Advance sound filtering (%)")},
|
||||||
_("Game Boy Advance sound filtering (%)")},
|
|
||||||
OptionData{"Sound/GBAInterpolation", "GBASoundInterpolation",
|
OptionData{"Sound/GBAInterpolation", "GBASoundInterpolation",
|
||||||
_("Game Boy Advance sound interpolation")},
|
_("Game Boy Advance sound interpolation")},
|
||||||
OptionData{"Sound/GBDeclicking", "GBDeclicking",
|
OptionData{"Sound/GBDeclicking", "GBDeclicking", _("Game Boy sound declicking")},
|
||||||
_("Game Boy sound declicking")},
|
|
||||||
OptionData{"Sound/GBEcho", "", _("Game Boy echo effect (%)")},
|
OptionData{"Sound/GBEcho", "", _("Game Boy echo effect (%)")},
|
||||||
OptionData{"Sound/GBEnableEffects", "GBEnhanceSound",
|
OptionData{"Sound/GBEnableEffects", "GBEnhanceSound", _("Enable Game Boy sound effects")},
|
||||||
_("Enable Game Boy sound effects")},
|
|
||||||
OptionData{"Sound/GBStereo", "", _("Game Boy stereo effect (%)")},
|
OptionData{"Sound/GBStereo", "", _("Game Boy stereo effect (%)")},
|
||||||
OptionData{"Sound/GBSurround", "GBSurround",
|
OptionData{"Sound/GBSurround", "GBSurround", _("Game Boy surround sound effect (%)")},
|
||||||
_("Game Boy surround sound effect (%)")},
|
|
||||||
OptionData{"Sound/Quality", "", _("Sound sample rate (kHz)")},
|
OptionData{"Sound/Quality", "", _("Sound sample rate (kHz)")},
|
||||||
|
OptionData{"Sound/DSoundHWAccel", "DSoundHWAccel", _("Use DirectSound hardware acceleration")},
|
||||||
|
OptionData{"Sound/Upmix", "Upmix", _("Upmix stereo to surround")},
|
||||||
OptionData{"Sound/Volume", "", _("Sound volume (%)")},
|
OptionData{"Sound/Volume", "", _("Sound volume (%)")},
|
||||||
|
|
||||||
// Last. This should never be used, it actually maps to OptionID::kLast.
|
// Last. This should never be used, it actually maps to OptionID::kLast.
|
||||||
|
@ -646,14 +613,16 @@ wxString RenderMethodToString(const RenderMethod& value) {
|
||||||
return kRenderMethodStrings[size_value];
|
return kRenderMethodStrings[size_value];
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString AudioApiToString(int value) {
|
wxString AudioApiToString(const AudioApi& value) {
|
||||||
assert(value >= 0 && static_cast<size_t>(value) < kNbAudioApis);
|
const size_t size_value = static_cast<size_t>(value);
|
||||||
return kAudioApiStrings[value];
|
assert(size_value < kNbAudioApis);
|
||||||
|
return kAudioApiStrings[size_value];
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString SoundQualityToString(int value) {
|
wxString AudioRateToString(const AudioRate& value) {
|
||||||
assert(value >= 0 && static_cast<size_t>(value) < kNbSoundQualities);
|
const size_t size_value = static_cast<size_t>(value);
|
||||||
return kSoundQualityStrings[value];
|
assert(size_value < kNbSoundRate);
|
||||||
|
return kAudioRateStrings[size_value];
|
||||||
}
|
}
|
||||||
|
|
||||||
Filter StringToFilter(const wxString& config_name, const wxString& input) {
|
Filter StringToFilter(const wxString& config_name, const wxString& input) {
|
||||||
|
@ -716,7 +685,7 @@ RenderMethod StringToRenderMethod(const wxString& config_name,
|
||||||
return iter->second;
|
return iter->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
int StringToAudioApi(const wxString& config_name, const wxString& input_) {
|
AudioApi StringToAudioApi(const wxString& config_name, const wxString& input) {
|
||||||
static std::map<wxString, AudioApi> kStringToAudioApi;
|
static std::map<wxString, AudioApi> kStringToAudioApi;
|
||||||
if (kStringToAudioApi.empty()) {
|
if (kStringToAudioApi.empty()) {
|
||||||
for (size_t i = 0; i < kNbAudioApis; i++) {
|
for (size_t i = 0; i < kNbAudioApis; i++) {
|
||||||
|
@ -726,45 +695,32 @@ int StringToAudioApi(const wxString& config_name, const wxString& input_) {
|
||||||
assert(kStringToAudioApi.size() == kNbAudioApis);
|
assert(kStringToAudioApi.size() == kNbAudioApis);
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString input = input_;
|
|
||||||
|
|
||||||
// sdl has been removed, rewrite to new default
|
|
||||||
if (input == "sdl") {
|
|
||||||
#ifdef __WXMSW__
|
|
||||||
input = "xaudio2";
|
|
||||||
#else
|
|
||||||
input = "openal";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto iter = kStringToAudioApi.find(input);
|
const auto iter = kStringToAudioApi.find(input);
|
||||||
if (iter == kStringToAudioApi.end()) {
|
if (iter == kStringToAudioApi.end()) {
|
||||||
wxLogWarning(_("Invalid value %s for option %s; valid values are %s"),
|
wxLogWarning(_("Invalid value %s for option %s; valid values are %s"),
|
||||||
input, config_name,
|
input, config_name,
|
||||||
AllEnumValuesForType(Option::Type::kAudioApi));
|
AllEnumValuesForType(Option::Type::kAudioApi));
|
||||||
return 0;
|
return AudioApi::kOpenAL;
|
||||||
}
|
}
|
||||||
return static_cast<int>(iter->second);
|
return iter->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
int StringToSoundQuality(const wxString& config_name, const wxString& input) {
|
AudioRate StringToSoundQuality(const wxString& config_name, const wxString& input) {
|
||||||
static std::map<wxString, SoundQuality> kStringToSoundQuality;
|
static std::map<wxString, AudioRate> kStringToSoundQuality;
|
||||||
if (kStringToSoundQuality.empty()) {
|
if (kStringToSoundQuality.empty()) {
|
||||||
for (size_t i = 0; i < kNbSoundQualities; i++) {
|
for (size_t i = 0; i < kNbSoundRate; i++) {
|
||||||
kStringToSoundQuality.emplace(kSoundQualityStrings[i],
|
kStringToSoundQuality.emplace(kAudioRateStrings[i], static_cast<AudioRate>(i));
|
||||||
static_cast<SoundQuality>(i));
|
|
||||||
}
|
}
|
||||||
assert(kStringToSoundQuality.size() == kNbSoundQualities);
|
assert(kStringToSoundQuality.size() == kNbSoundRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto iter = kStringToSoundQuality.find(input);
|
const auto iter = kStringToSoundQuality.find(input);
|
||||||
if (iter == kStringToSoundQuality.end()) {
|
if (iter == kStringToSoundQuality.end()) {
|
||||||
wxLogWarning(_("Invalid value %s for option %s; valid values are %s"),
|
wxLogWarning(_("Invalid value %s for option %s; valid values are %s"), input, config_name,
|
||||||
input, config_name,
|
AllEnumValuesForType(Option::Type::kAudioRate));
|
||||||
AllEnumValuesForType(Option::Type::kSoundQuality));
|
return AudioRate::k44kHz;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
return static_cast<int>(iter->second);
|
return iter->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString AllEnumValuesForType(Option::Type type) {
|
wxString AllEnumValuesForType(Option::Type type) {
|
||||||
|
@ -789,9 +745,9 @@ wxString AllEnumValuesForType(Option::Type type) {
|
||||||
AllEnumValuesForArray(kAudioApiStrings);
|
AllEnumValuesForArray(kAudioApiStrings);
|
||||||
return kAllAudioApiValues;
|
return kAllAudioApiValues;
|
||||||
}
|
}
|
||||||
case Option::Type::kSoundQuality: {
|
case Option::Type::kAudioRate: {
|
||||||
static const wxString kAllSoundQualityValues =
|
static const wxString kAllSoundQualityValues =
|
||||||
AllEnumValuesForArray(kSoundQualityStrings);
|
AllEnumValuesForArray(kAudioRateStrings);
|
||||||
return kAllSoundQualityValues;
|
return kAllSoundQualityValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -811,7 +767,7 @@ wxString AllEnumValuesForType(Option::Type type) {
|
||||||
return wxEmptyString;
|
return wxEmptyString;
|
||||||
}
|
}
|
||||||
|
|
||||||
int MaxForType(Option::Type type) {
|
size_t MaxForType(Option::Type type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Option::Type::kFilter:
|
case Option::Type::kFilter:
|
||||||
return kNbFilters;
|
return kNbFilters;
|
||||||
|
@ -821,8 +777,8 @@ int MaxForType(Option::Type type) {
|
||||||
return kNbRenderMethods;
|
return kNbRenderMethods;
|
||||||
case Option::Type::kAudioApi:
|
case Option::Type::kAudioApi:
|
||||||
return kNbAudioApis;
|
return kNbAudioApis;
|
||||||
case Option::Type::kSoundQuality:
|
case Option::Type::kAudioRate:
|
||||||
return kNbSoundQualities;
|
return kNbSoundRate;
|
||||||
|
|
||||||
// We don't use default here to explicitly trigger a compiler warning
|
// We don't use default here to explicitly trigger a compiler warning
|
||||||
// when adding a new value.
|
// when adding a new value.
|
||||||
|
|
|
@ -27,18 +27,18 @@ nonstd::optional<OptionID> StringToOptionId(const wxString& input);
|
||||||
wxString FilterToString(const Filter& value);
|
wxString FilterToString(const Filter& value);
|
||||||
wxString InterframeToString(const Interframe& value);
|
wxString InterframeToString(const Interframe& value);
|
||||||
wxString RenderMethodToString(const RenderMethod& value);
|
wxString RenderMethodToString(const RenderMethod& value);
|
||||||
wxString AudioApiToString(int value);
|
wxString AudioApiToString(const AudioApi& value);
|
||||||
wxString SoundQualityToString(int value);
|
wxString AudioRateToString(const AudioRate& value);
|
||||||
Filter StringToFilter(const wxString& config_name, const wxString& input);
|
Filter StringToFilter(const wxString& config_name, const wxString& input);
|
||||||
Interframe StringToInterframe(const wxString& config_name, const wxString& input);
|
Interframe StringToInterframe(const wxString& config_name, const wxString& input);
|
||||||
RenderMethod StringToRenderMethod(const wxString& config_name, const wxString& input);
|
RenderMethod StringToRenderMethod(const wxString& config_name, const wxString& input);
|
||||||
int StringToAudioApi(const wxString& config_name, const wxString& input);
|
AudioApi StringToAudioApi(const wxString& config_name, const wxString& input);
|
||||||
int StringToSoundQuality(const wxString& config_name, const wxString& input);
|
AudioRate StringToSoundQuality(const wxString& config_name, const wxString& input);
|
||||||
|
|
||||||
wxString AllEnumValuesForType(Option::Type type);
|
wxString AllEnumValuesForType(Option::Type type);
|
||||||
|
|
||||||
// Max value for enum types.
|
// Max value for enum types.
|
||||||
int MaxForType(Option::Type type);
|
size_t MaxForType(Option::Type type);
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace config
|
} // namespace config
|
||||||
|
|
|
@ -130,7 +130,9 @@ enum class OptionID {
|
||||||
kSoundGBEnableEffects,
|
kSoundGBEnableEffects,
|
||||||
kSoundGBStereo,
|
kSoundGBStereo,
|
||||||
kSoundGBSurround,
|
kSoundGBSurround,
|
||||||
kSoundQuality,
|
kSoundAudioRate,
|
||||||
|
kSoundDSoundHWAccel,
|
||||||
|
kSoundUpmix,
|
||||||
kSoundVolume,
|
kSoundVolume,
|
||||||
|
|
||||||
// Do not add anything under here.
|
// Do not add anything under here.
|
||||||
|
|
|
@ -134,7 +134,9 @@ static constexpr std::array<Option::Type, kNbOptions> kOptionsTypes = {
|
||||||
/*kSoundGBEnableEffects*/ Option::Type::kBool,
|
/*kSoundGBEnableEffects*/ Option::Type::kBool,
|
||||||
/*kSoundGBStereo*/ Option::Type::kInt,
|
/*kSoundGBStereo*/ Option::Type::kInt,
|
||||||
/*kSoundGBSurround*/ Option::Type::kBool,
|
/*kSoundGBSurround*/ Option::Type::kBool,
|
||||||
/*kSoundQuality*/ Option::Type::kSoundQuality,
|
/*kSoundAudioRate*/ Option::Type::kAudioRate,
|
||||||
|
/*kSoundDSoundHWAccel*/ Option::Type::kBool,
|
||||||
|
/*kSoundUpmix*/ Option::Type::kBool,
|
||||||
/*kSoundVolume*/ Option::Type::kInt,
|
/*kSoundVolume*/ Option::Type::kInt,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -208,6 +210,22 @@ public:
|
||||||
int32_t Max() const { return option_->GetIntMax(); }
|
int32_t Max() const { return option_->GetIntMax(); }
|
||||||
|
|
||||||
bool operator=(int32_t value) { return Set(value); }
|
bool operator=(int32_t value) { return Set(value); }
|
||||||
|
bool operator+=(int32_t value) {
|
||||||
|
const int new_value = Get() + value;
|
||||||
|
if (new_value > Max()) {
|
||||||
|
return Set(Max());
|
||||||
|
} else {
|
||||||
|
return Set(new_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool operator-=(int32_t value) {
|
||||||
|
const int new_value = Get() - value;
|
||||||
|
if (new_value < Min()) {
|
||||||
|
return Set(Min());
|
||||||
|
} else {
|
||||||
|
return Set(new_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
operator int32_t() const { return Get(); }
|
operator int32_t() const { return Get(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -313,6 +331,44 @@ private:
|
||||||
Option* option_;
|
Option* option_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <OptionID ID>
|
||||||
|
class OptionProxy<
|
||||||
|
ID,
|
||||||
|
typename std::enable_if<kOptionsTypes[static_cast<size_t>(ID)] ==
|
||||||
|
Option::Type::kAudioApi>::type> {
|
||||||
|
public:
|
||||||
|
OptionProxy() : option_(Option::ByID(ID)) {}
|
||||||
|
~OptionProxy() = default;
|
||||||
|
|
||||||
|
AudioApi Get() const { return option_->GetAudioApi(); }
|
||||||
|
bool Set(AudioApi value) { return option_->SetAudioApi(value); }
|
||||||
|
|
||||||
|
bool operator=(AudioApi value) { return Set(value); }
|
||||||
|
operator AudioApi() const { return Get(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Option* option_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <OptionID ID>
|
||||||
|
class OptionProxy<
|
||||||
|
ID,
|
||||||
|
typename std::enable_if<kOptionsTypes[static_cast<size_t>(ID)] ==
|
||||||
|
Option::Type::kAudioRate>::type> {
|
||||||
|
public:
|
||||||
|
OptionProxy() : option_(Option::ByID(ID)) {}
|
||||||
|
~OptionProxy() = default;
|
||||||
|
|
||||||
|
AudioRate Get() const { return option_->GetAudioRate(); }
|
||||||
|
bool Set(AudioRate value) { return option_->SetAudioRate(value); }
|
||||||
|
|
||||||
|
bool operator=(AudioRate value) { return Set(value); }
|
||||||
|
operator AudioRate() const { return Get(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Option* option_;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace config
|
} // namespace config
|
||||||
|
|
||||||
#endif // VBAM_WX_CONFIG_OPTION_PROXY_H_
|
#endif // VBAM_WX_CONFIG_OPTION_PROXY_H_
|
||||||
|
|
|
@ -17,8 +17,7 @@ namespace config {
|
||||||
|
|
||||||
// static
|
// static
|
||||||
Option* Option::ByName(const wxString& config_name) {
|
Option* Option::ByName(const wxString& config_name) {
|
||||||
nonstd::optional<OptionID> option_id =
|
nonstd::optional<OptionID> option_id = internal::StringToOptionId(config_name);
|
||||||
internal::StringToOptionId(config_name);
|
|
||||||
if (!option_id) {
|
if (!option_id) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -33,8 +32,7 @@ Option* Option::ByID(OptionID id) {
|
||||||
|
|
||||||
Option::~Option() = default;
|
Option::~Option() = default;
|
||||||
|
|
||||||
Option::Observer::Observer(OptionID option_id)
|
Option::Observer::Observer(OptionID option_id) : option_(Option::ByID(option_id)) {
|
||||||
: option_(Option::ByID(option_id)) {
|
|
||||||
assert(option_);
|
assert(option_);
|
||||||
option_->AddObserver(this);
|
option_->AddObserver(this);
|
||||||
}
|
}
|
||||||
|
@ -44,11 +42,9 @@ Option::Observer::~Observer() {
|
||||||
|
|
||||||
Option::Option(OptionID id)
|
Option::Option(OptionID id)
|
||||||
: id_(id),
|
: id_(id),
|
||||||
config_name_(
|
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||||
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
|
||||||
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
||||||
ux_helper_(wxGetTranslation(
|
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||||
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
|
||||||
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
||||||
value_(),
|
value_(),
|
||||||
min_(),
|
min_(),
|
||||||
|
@ -59,11 +55,9 @@ Option::Option(OptionID id)
|
||||||
|
|
||||||
Option::Option(OptionID id, bool* option)
|
Option::Option(OptionID id, bool* option)
|
||||||
: id_(id),
|
: id_(id),
|
||||||
config_name_(
|
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||||
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
|
||||||
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
||||||
ux_helper_(wxGetTranslation(
|
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||||
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
|
||||||
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
||||||
value_(option),
|
value_(option),
|
||||||
min_(),
|
min_(),
|
||||||
|
@ -74,11 +68,9 @@ Option::Option(OptionID id, bool* option)
|
||||||
|
|
||||||
Option::Option(OptionID id, double* option, double min, double max)
|
Option::Option(OptionID id, double* option, double min, double max)
|
||||||
: id_(id),
|
: id_(id),
|
||||||
config_name_(
|
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||||
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
|
||||||
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
||||||
ux_helper_(wxGetTranslation(
|
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||||
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
|
||||||
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
||||||
value_(option),
|
value_(option),
|
||||||
min_(min),
|
min_(min),
|
||||||
|
@ -92,11 +84,9 @@ Option::Option(OptionID id, double* option, double min, double max)
|
||||||
|
|
||||||
Option::Option(OptionID id, int32_t* option, int32_t min, int32_t max)
|
Option::Option(OptionID id, int32_t* option, int32_t min, int32_t max)
|
||||||
: id_(id),
|
: id_(id),
|
||||||
config_name_(
|
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||||
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
|
||||||
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
||||||
ux_helper_(wxGetTranslation(
|
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||||
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
|
||||||
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
||||||
value_(option),
|
value_(option),
|
||||||
min_(min),
|
min_(min),
|
||||||
|
@ -110,11 +100,9 @@ Option::Option(OptionID id, int32_t* option, int32_t min, int32_t max)
|
||||||
|
|
||||||
Option::Option(OptionID id, uint32_t* option, uint32_t min, uint32_t max)
|
Option::Option(OptionID id, uint32_t* option, uint32_t min, uint32_t max)
|
||||||
: id_(id),
|
: id_(id),
|
||||||
config_name_(
|
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||||
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
|
||||||
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
||||||
ux_helper_(wxGetTranslation(
|
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||||
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
|
||||||
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
||||||
value_(option),
|
value_(option),
|
||||||
min_(min),
|
min_(min),
|
||||||
|
@ -128,11 +116,9 @@ Option::Option(OptionID id, uint32_t* option, uint32_t min, uint32_t max)
|
||||||
|
|
||||||
Option::Option(OptionID id, wxString* option)
|
Option::Option(OptionID id, wxString* option)
|
||||||
: id_(id),
|
: id_(id),
|
||||||
config_name_(
|
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||||
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
|
||||||
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
||||||
ux_helper_(wxGetTranslation(
|
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||||
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
|
||||||
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
||||||
value_(option),
|
value_(option),
|
||||||
min_(),
|
min_(),
|
||||||
|
@ -143,74 +129,74 @@ Option::Option(OptionID id, wxString* option)
|
||||||
|
|
||||||
Option::Option(OptionID id, Filter* option)
|
Option::Option(OptionID id, Filter* option)
|
||||||
: id_(id),
|
: id_(id),
|
||||||
config_name_(
|
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||||
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
|
||||||
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
||||||
ux_helper_(wxGetTranslation(
|
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||||
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
|
||||||
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
||||||
value_(option),
|
value_(option),
|
||||||
min_(0),
|
min_(),
|
||||||
max_(internal::MaxForType(type_)) {
|
max_(nonstd::in_place_type<size_t>, internal::MaxForType(type_)) {
|
||||||
assert(id != OptionID::Last);
|
assert(id != OptionID::Last);
|
||||||
assert(is_filter());
|
assert(is_filter());
|
||||||
}
|
}
|
||||||
|
|
||||||
Option::Option(OptionID id, Interframe* option)
|
Option::Option(OptionID id, Interframe* option)
|
||||||
: id_(id),
|
: id_(id),
|
||||||
config_name_(
|
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||||
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
|
||||||
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
||||||
ux_helper_(wxGetTranslation(
|
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||||
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
|
||||||
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
||||||
value_(option),
|
value_(option),
|
||||||
min_(0),
|
min_(),
|
||||||
max_(internal::MaxForType(type_)) {
|
max_(nonstd::in_place_type<size_t>, internal::MaxForType(type_)) {
|
||||||
assert(id != OptionID::Last);
|
assert(id != OptionID::Last);
|
||||||
assert(is_interframe());
|
assert(is_interframe());
|
||||||
}
|
}
|
||||||
|
|
||||||
Option::Option(OptionID id, RenderMethod* option)
|
Option::Option(OptionID id, RenderMethod* option)
|
||||||
: id_(id),
|
: id_(id),
|
||||||
config_name_(
|
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||||
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
|
||||||
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
||||||
ux_helper_(wxGetTranslation(
|
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||||
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
|
||||||
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
||||||
value_(option),
|
value_(option),
|
||||||
min_(0),
|
min_(),
|
||||||
max_(internal::MaxForType(type_)) {
|
max_(nonstd::in_place_type<size_t>, internal::MaxForType(type_)) {
|
||||||
assert(id != OptionID::Last);
|
assert(id != OptionID::Last);
|
||||||
assert(is_render_method());
|
assert(is_render_method());
|
||||||
}
|
}
|
||||||
|
|
||||||
Option::Option(OptionID id, int* option)
|
Option::Option(OptionID id, AudioApi* option)
|
||||||
: id_(id),
|
: id_(id),
|
||||||
config_name_(
|
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||||
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
|
||||||
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
||||||
ux_helper_(wxGetTranslation(
|
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||||
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
|
||||||
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
||||||
value_(option),
|
value_(option),
|
||||||
min_(0),
|
min_(),
|
||||||
max_(internal::MaxForType(type_)) {
|
max_(nonstd::in_place_type<size_t>, internal::MaxForType(type_)) {
|
||||||
assert(id != OptionID::Last);
|
assert(id != OptionID::Last);
|
||||||
assert(is_audio_api() || is_sound_quality());
|
assert(is_audio_api());
|
||||||
|
}
|
||||||
|
|
||||||
// Validate the initial value.
|
Option::Option(OptionID id, AudioRate* option)
|
||||||
SetEnumInt(*option);
|
: id_(id),
|
||||||
|
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||||
|
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
||||||
|
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||||
|
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
||||||
|
value_(option),
|
||||||
|
min_(),
|
||||||
|
max_(nonstd::in_place_type<size_t>, internal::MaxForType(type_)) {
|
||||||
|
assert(id != OptionID::Last);
|
||||||
|
assert(is_audio_rate());
|
||||||
}
|
}
|
||||||
|
|
||||||
Option::Option(OptionID id, uint16_t* option)
|
Option::Option(OptionID id, uint16_t* option)
|
||||||
: id_(id),
|
: id_(id),
|
||||||
config_name_(
|
config_name_(internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
||||||
internal::kAllOptionsData[static_cast<size_t>(id)].config_name),
|
|
||||||
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
command_(internal::kAllOptionsData[static_cast<size_t>(id)].command),
|
||||||
ux_helper_(wxGetTranslation(
|
ux_helper_(wxGetTranslation(internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
||||||
internal::kAllOptionsData[static_cast<size_t>(id)].ux_helper)),
|
|
||||||
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
type_(kOptionsTypes[static_cast<size_t>(id)]),
|
||||||
value_(option),
|
value_(option),
|
||||||
min_(),
|
min_(),
|
||||||
|
@ -259,6 +245,16 @@ RenderMethod Option::GetRenderMethod() const {
|
||||||
return *(nonstd::get<RenderMethod*>(value_));
|
return *(nonstd::get<RenderMethod*>(value_));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AudioApi Option::GetAudioApi() const {
|
||||||
|
assert(is_audio_api());
|
||||||
|
return *(nonstd::get<AudioApi*>(value_));
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioRate Option::GetAudioRate() const {
|
||||||
|
assert(is_audio_rate());
|
||||||
|
return *(nonstd::get<AudioRate*>(value_));
|
||||||
|
}
|
||||||
|
|
||||||
wxString Option::GetEnumString() const {
|
wxString Option::GetEnumString() const {
|
||||||
switch (type_) {
|
switch (type_) {
|
||||||
case Option::Type::kFilter:
|
case Option::Type::kFilter:
|
||||||
|
@ -268,10 +264,9 @@ wxString Option::GetEnumString() const {
|
||||||
case Option::Type::kRenderMethod:
|
case Option::Type::kRenderMethod:
|
||||||
return internal::RenderMethodToString(GetRenderMethod());
|
return internal::RenderMethodToString(GetRenderMethod());
|
||||||
case Option::Type::kAudioApi:
|
case Option::Type::kAudioApi:
|
||||||
return internal::AudioApiToString(*(nonstd::get<int32_t*>(value_)));
|
return internal::AudioApiToString(GetAudioApi());
|
||||||
case Option::Type::kSoundQuality:
|
case Option::Type::kAudioRate:
|
||||||
return internal::SoundQualityToString(
|
return internal::AudioRateToString(GetAudioRate());
|
||||||
*(nonstd::get<int32_t*>(value_)));
|
|
||||||
|
|
||||||
// We don't use default here to explicitly trigger a compiler warning
|
// We don't use default here to explicitly trigger a compiler warning
|
||||||
// when adding a new value.
|
// when adding a new value.
|
||||||
|
@ -303,9 +298,8 @@ wxString Option::GetGbPaletteString() const {
|
||||||
|
|
||||||
wxString palette_string;
|
wxString palette_string;
|
||||||
uint16_t const* value = nonstd::get<uint16_t*>(value_);
|
uint16_t const* value = nonstd::get<uint16_t*>(value_);
|
||||||
palette_string.Printf("%04X,%04X,%04X,%04X,%04X,%04X,%04X,%04X", value[0],
|
palette_string.Printf("%04X,%04X,%04X,%04X,%04X,%04X,%04X,%04X", value[0], value[1], value[2],
|
||||||
value[1], value[2], value[3], value[4], value[5],
|
value[3], value[4], value[5], value[6], value[7]);
|
||||||
value[6], value[7]);
|
|
||||||
return palette_string;
|
return palette_string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,12 +316,9 @@ bool Option::SetBool(bool value) {
|
||||||
bool Option::SetDouble(double value) {
|
bool Option::SetDouble(double value) {
|
||||||
assert(is_double());
|
assert(is_double());
|
||||||
double old_value = GetDouble();
|
double old_value = GetDouble();
|
||||||
if (value < nonstd::get<double>(min_) ||
|
if (value < nonstd::get<double>(min_) || value > nonstd::get<double>(max_)) {
|
||||||
value > nonstd::get<double>(max_)) {
|
wxLogWarning(_("Invalid value %f for option %s; valid values are %f - %f"), value,
|
||||||
wxLogWarning(
|
config_name_, nonstd::get<double>(min_), nonstd::get<double>(max_));
|
||||||
_("Invalid value %f for option %s; valid values are %f - %f"),
|
|
||||||
value, config_name_, nonstd::get<double>(min_),
|
|
||||||
nonstd::get<double>(max_));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*nonstd::get<double*>(value_) = value;
|
*nonstd::get<double*>(value_) = value;
|
||||||
|
@ -340,12 +331,9 @@ bool Option::SetDouble(double value) {
|
||||||
bool Option::SetInt(int32_t value) {
|
bool Option::SetInt(int32_t value) {
|
||||||
assert(is_int());
|
assert(is_int());
|
||||||
int old_value = GetInt();
|
int old_value = GetInt();
|
||||||
if (value < nonstd::get<int32_t>(min_) ||
|
if (value < nonstd::get<int32_t>(min_) || value > nonstd::get<int32_t>(max_)) {
|
||||||
value > nonstd::get<int32_t>(max_)) {
|
wxLogWarning(_("Invalid value %d for option %s; valid values are %d - %d"), value,
|
||||||
wxLogWarning(
|
config_name_, nonstd::get<int32_t>(min_), nonstd::get<int32_t>(max_));
|
||||||
_("Invalid value %d for option %s; valid values are %d - %d"),
|
|
||||||
value, config_name_, nonstd::get<int32_t>(min_),
|
|
||||||
nonstd::get<int32_t>(max_));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*nonstd::get<int32_t*>(value_) = value;
|
*nonstd::get<int32_t*>(value_) = value;
|
||||||
|
@ -358,12 +346,9 @@ bool Option::SetInt(int32_t value) {
|
||||||
bool Option::SetUnsigned(uint32_t value) {
|
bool Option::SetUnsigned(uint32_t value) {
|
||||||
assert(is_unsigned());
|
assert(is_unsigned());
|
||||||
uint32_t old_value = GetUnsigned();
|
uint32_t old_value = GetUnsigned();
|
||||||
if (value < nonstd::get<uint32_t>(min_) ||
|
if (value < nonstd::get<uint32_t>(min_) || value > nonstd::get<uint32_t>(max_)) {
|
||||||
value > nonstd::get<uint32_t>(max_)) {
|
wxLogWarning(_("Invalid value %d for option %s; valid values are %d - %d"), value,
|
||||||
wxLogWarning(
|
config_name_, nonstd::get<uint32_t>(min_), nonstd::get<uint32_t>(max_));
|
||||||
_("Invalid value %d for option %s; valid values are %d - %d"),
|
|
||||||
value, config_name_, nonstd::get<uint32_t>(min_),
|
|
||||||
nonstd::get<uint32_t>(max_));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*nonstd::get<uint32_t*>(value_) = value;
|
*nonstd::get<uint32_t*>(value_) = value;
|
||||||
|
@ -385,7 +370,7 @@ bool Option::SetString(const wxString& value) {
|
||||||
|
|
||||||
bool Option::SetFilter(const Filter& value) {
|
bool Option::SetFilter(const Filter& value) {
|
||||||
assert(is_filter());
|
assert(is_filter());
|
||||||
assert(value != Filter::kLast);
|
assert(value < Filter::kLast);
|
||||||
const Filter old_value = GetFilter();
|
const Filter old_value = GetFilter();
|
||||||
*nonstd::get<Filter*>(value_) = value;
|
*nonstd::get<Filter*>(value_) = value;
|
||||||
if (old_value != value) {
|
if (old_value != value) {
|
||||||
|
@ -396,7 +381,7 @@ bool Option::SetFilter(const Filter& value) {
|
||||||
|
|
||||||
bool Option::SetInterframe(const Interframe& value) {
|
bool Option::SetInterframe(const Interframe& value) {
|
||||||
assert(is_interframe());
|
assert(is_interframe());
|
||||||
assert(value != Interframe::kLast);
|
assert(value < Interframe::kLast);
|
||||||
const Interframe old_value = GetInterframe();
|
const Interframe old_value = GetInterframe();
|
||||||
*nonstd::get<Interframe*>(value_) = value;
|
*nonstd::get<Interframe*>(value_) = value;
|
||||||
if (old_value != value) {
|
if (old_value != value) {
|
||||||
|
@ -407,7 +392,7 @@ bool Option::SetInterframe(const Interframe& value) {
|
||||||
|
|
||||||
bool Option::SetRenderMethod(const RenderMethod& value) {
|
bool Option::SetRenderMethod(const RenderMethod& value) {
|
||||||
assert(is_render_method());
|
assert(is_render_method());
|
||||||
assert(value != RenderMethod::kLast);
|
assert(value < RenderMethod::kLast);
|
||||||
const RenderMethod old_value = GetRenderMethod();
|
const RenderMethod old_value = GetRenderMethod();
|
||||||
*nonstd::get<RenderMethod*>(value_) = value;
|
*nonstd::get<RenderMethod*>(value_) = value;
|
||||||
if (old_value != value) {
|
if (old_value != value) {
|
||||||
|
@ -416,21 +401,40 @@ bool Option::SetRenderMethod(const RenderMethod& value) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Option::SetAudioApi(const AudioApi& value) {
|
||||||
|
assert(is_audio_api());
|
||||||
|
assert(value < AudioApi::kLast);
|
||||||
|
const AudioApi old_value = GetAudioApi();
|
||||||
|
*nonstd::get<AudioApi*>(value_) = value;
|
||||||
|
if (old_value != value) {
|
||||||
|
CallObservers();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Option::SetAudioRate(const AudioRate& value) {
|
||||||
|
assert(is_audio_rate());
|
||||||
|
assert(value < AudioRate::kLast);
|
||||||
|
const AudioRate old_value = GetAudioRate();
|
||||||
|
*nonstd::get<AudioRate*>(value_) = value;
|
||||||
|
if (old_value != value) {
|
||||||
|
CallObservers();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool Option::SetEnumString(const wxString& value) {
|
bool Option::SetEnumString(const wxString& value) {
|
||||||
switch (type_) {
|
switch (type_) {
|
||||||
case Option::Type::kFilter:
|
case Option::Type::kFilter:
|
||||||
return SetFilter(internal::StringToFilter(config_name_, value));
|
return SetFilter(internal::StringToFilter(config_name_, value));
|
||||||
case Option::Type::kInterframe:
|
case Option::Type::kInterframe:
|
||||||
return SetInterframe(
|
return SetInterframe(internal::StringToInterframe(config_name_, value));
|
||||||
internal::StringToInterframe(config_name_, value));
|
|
||||||
case Option::Type::kRenderMethod:
|
case Option::Type::kRenderMethod:
|
||||||
return SetRenderMethod(
|
return SetRenderMethod(internal::StringToRenderMethod(config_name_, value));
|
||||||
internal::StringToRenderMethod(config_name_, value));
|
|
||||||
case Option::Type::kAudioApi:
|
case Option::Type::kAudioApi:
|
||||||
return SetEnumInt(internal::StringToAudioApi(config_name_, value));
|
return SetAudioApi(internal::StringToAudioApi(config_name_, value));
|
||||||
case Option::Type::kSoundQuality:
|
case Option::Type::kAudioRate:
|
||||||
return SetEnumInt(
|
return SetAudioRate(internal::StringToSoundQuality(config_name_, value));
|
||||||
internal::StringToSoundQuality(config_name_, value));
|
|
||||||
|
|
||||||
// We don't use default here to explicitly trigger a compiler warning
|
// We don't use default here to explicitly trigger a compiler warning
|
||||||
// when adding a new value.
|
// when adding a new value.
|
||||||
|
@ -448,23 +452,6 @@ bool Option::SetEnumString(const wxString& value) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Option::SetEnumInt(int value) {
|
|
||||||
assert(is_audio_api() || is_sound_quality());
|
|
||||||
int32_t old_value = *nonstd::get<int32_t*>(value_);
|
|
||||||
if (value < nonstd::get<int32_t>(min_) ||
|
|
||||||
value > nonstd::get<int32_t>(max_)) {
|
|
||||||
wxLogWarning(_("Invalid value %d for option %s; valid values are %s"),
|
|
||||||
value, config_name_,
|
|
||||||
internal::AllEnumValuesForType(type_));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*nonstd::get<int32_t*>(value_) = value;
|
|
||||||
if (old_value != value) {
|
|
||||||
CallObservers();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Option::SetGbPalette(const std::array<uint16_t, 8>& value) {
|
bool Option::SetGbPalette(const std::array<uint16_t, 8>& value) {
|
||||||
assert(is_gb_palette());
|
assert(is_gb_palette());
|
||||||
|
|
||||||
|
@ -536,6 +523,12 @@ uint32_t Option::GetUnsignedMax() const {
|
||||||
return nonstd::get<uint32_t>(max_);
|
return nonstd::get<uint32_t>(max_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t Option::GetEnumMax() const {
|
||||||
|
assert(is_filter() || is_interframe() || is_render_method() || is_audio_api() ||
|
||||||
|
is_audio_rate());
|
||||||
|
return nonstd::get<size_t>(max_);
|
||||||
|
}
|
||||||
|
|
||||||
void Option::NextFilter() {
|
void Option::NextFilter() {
|
||||||
assert(is_filter());
|
assert(is_filter());
|
||||||
const int old_value = static_cast<int>(GetFilter());
|
const int old_value = static_cast<int>(GetFilter());
|
||||||
|
@ -575,7 +568,7 @@ wxString Option::ToHelperString() const {
|
||||||
case Option::Type::kInterframe:
|
case Option::Type::kInterframe:
|
||||||
case Option::Type::kRenderMethod:
|
case Option::Type::kRenderMethod:
|
||||||
case Option::Type::kAudioApi:
|
case Option::Type::kAudioApi:
|
||||||
case Option::Type::kSoundQuality:
|
case Option::Type::kAudioRate:
|
||||||
helper_string.Append(" (");
|
helper_string.Append(" (");
|
||||||
helper_string.Append(internal::AllEnumValuesForType(type_));
|
helper_string.Append(internal::AllEnumValuesForType(type_));
|
||||||
helper_string.Append(")");
|
helper_string.Append(")");
|
||||||
|
|
|
@ -75,8 +75,36 @@ enum class RenderMethod {
|
||||||
// Do not add anything under here.
|
// Do not add anything under here.
|
||||||
kLast,
|
kLast,
|
||||||
};
|
};
|
||||||
static constexpr size_t kNbRenderMethods =
|
static constexpr size_t kNbRenderMethods = static_cast<size_t>(RenderMethod::kLast);
|
||||||
static_cast<size_t>(RenderMethod::kLast);
|
|
||||||
|
// Values for kAudioApi.
|
||||||
|
enum class AudioApi {
|
||||||
|
kOpenAL,
|
||||||
|
#if defined(__WXMSW__)
|
||||||
|
kDirectSound,
|
||||||
|
#endif // __WXMSW__
|
||||||
|
#if defined(VBAM_ENABLE_XAUDIO2)
|
||||||
|
kXAudio2,
|
||||||
|
#endif // VBAM_ENABLE_XAUDIO2
|
||||||
|
#if defined(VBAM_ENABLE_FAUDIO)
|
||||||
|
kFAudio,
|
||||||
|
#endif // VBAM_ENABLE_FAUDIO
|
||||||
|
|
||||||
|
// Do not add anything under here.
|
||||||
|
kLast,
|
||||||
|
};
|
||||||
|
static constexpr size_t kNbAudioApis = static_cast<size_t>(AudioApi::kLast);
|
||||||
|
|
||||||
|
enum class AudioRate {
|
||||||
|
k48kHz = 0,
|
||||||
|
k44kHz,
|
||||||
|
k22kHz,
|
||||||
|
k11kHz,
|
||||||
|
|
||||||
|
// Do not add anything under here.
|
||||||
|
kLast,
|
||||||
|
};
|
||||||
|
static constexpr size_t kNbSoundRate = static_cast<size_t>(AudioRate::kLast);
|
||||||
|
|
||||||
// This is incremented whenever we want to change a default value between
|
// This is incremented whenever we want to change a default value between
|
||||||
// release versions. The option update code is in load_opts.
|
// release versions. The option update code is in load_opts.
|
||||||
|
@ -108,7 +136,7 @@ public:
|
||||||
kInterframe,
|
kInterframe,
|
||||||
kRenderMethod,
|
kRenderMethod,
|
||||||
kAudioApi,
|
kAudioApi,
|
||||||
kSoundQuality,
|
kAudioRate,
|
||||||
kGbPalette,
|
kGbPalette,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -165,11 +193,11 @@ public:
|
||||||
bool is_interframe() const { return type() == Type::kInterframe; }
|
bool is_interframe() const { return type() == Type::kInterframe; }
|
||||||
bool is_render_method() const { return type() == Type::kRenderMethod; }
|
bool is_render_method() const { return type() == Type::kRenderMethod; }
|
||||||
bool is_audio_api() const { return type() == Type::kAudioApi; }
|
bool is_audio_api() const { return type() == Type::kAudioApi; }
|
||||||
bool is_sound_quality() const { return type() == Type::kSoundQuality; }
|
bool is_audio_rate() const { return type() == Type::kAudioRate; }
|
||||||
bool is_gb_palette() const { return type() == Type::kGbPalette; }
|
bool is_gb_palette() const { return type() == Type::kGbPalette; }
|
||||||
|
|
||||||
// Returns a reference to the stored data. Will assert on type mismatch.
|
// Returns a reference to the stored data. Will assert on type mismatch.
|
||||||
// Only enum types can use through GetEnumString().
|
// Only enum types can use GetEnumString().
|
||||||
bool GetBool() const;
|
bool GetBool() const;
|
||||||
double GetDouble() const;
|
double GetDouble() const;
|
||||||
int32_t GetInt() const;
|
int32_t GetInt() const;
|
||||||
|
@ -178,6 +206,8 @@ public:
|
||||||
Filter GetFilter() const;
|
Filter GetFilter() const;
|
||||||
Interframe GetInterframe() const;
|
Interframe GetInterframe() const;
|
||||||
RenderMethod GetRenderMethod() const;
|
RenderMethod GetRenderMethod() const;
|
||||||
|
AudioApi GetAudioApi() const;
|
||||||
|
AudioRate GetAudioRate() const;
|
||||||
wxString GetEnumString() const;
|
wxString GetEnumString() const;
|
||||||
std::array<uint16_t, 8> GetGbPalette() const;
|
std::array<uint16_t, 8> GetGbPalette() const;
|
||||||
wxString GetGbPaletteString() const;
|
wxString GetGbPaletteString() const;
|
||||||
|
@ -193,6 +223,8 @@ public:
|
||||||
bool SetFilter(const Filter& value);
|
bool SetFilter(const Filter& value);
|
||||||
bool SetInterframe(const Interframe& value);
|
bool SetInterframe(const Interframe& value);
|
||||||
bool SetRenderMethod(const RenderMethod& value);
|
bool SetRenderMethod(const RenderMethod& value);
|
||||||
|
bool SetAudioApi(const AudioApi& value);
|
||||||
|
bool SetAudioRate(const AudioRate& value);
|
||||||
bool SetEnumString(const wxString& value);
|
bool SetEnumString(const wxString& value);
|
||||||
bool SetGbPalette(const std::array<uint16_t, 8>& value);
|
bool SetGbPalette(const std::array<uint16_t, 8>& value);
|
||||||
bool SetGbPaletteString(const wxString& value);
|
bool SetGbPaletteString(const wxString& value);
|
||||||
|
@ -204,6 +236,7 @@ public:
|
||||||
int32_t GetIntMax() const;
|
int32_t GetIntMax() const;
|
||||||
uint32_t GetUnsignedMin() const;
|
uint32_t GetUnsignedMin() const;
|
||||||
uint32_t GetUnsignedMax() const;
|
uint32_t GetUnsignedMax() const;
|
||||||
|
size_t GetEnumMax() const;
|
||||||
|
|
||||||
// Special convenience modifiers.
|
// Special convenience modifiers.
|
||||||
void NextFilter();
|
void NextFilter();
|
||||||
|
@ -226,12 +259,11 @@ private:
|
||||||
Option(OptionID id, Filter* option);
|
Option(OptionID id, Filter* option);
|
||||||
Option(OptionID id, Interframe* option);
|
Option(OptionID id, Interframe* option);
|
||||||
Option(OptionID id, RenderMethod* option);
|
Option(OptionID id, RenderMethod* option);
|
||||||
|
Option(OptionID id, AudioApi* option);
|
||||||
|
Option(OptionID id, AudioRate* option);
|
||||||
Option(OptionID id, int* option);
|
Option(OptionID id, int* option);
|
||||||
Option(OptionID id, uint16_t* option);
|
Option(OptionID id, uint16_t* option);
|
||||||
|
|
||||||
// Helper method for enums not fully converted yet.
|
|
||||||
bool SetEnumInt(int value);
|
|
||||||
|
|
||||||
// Observer.
|
// Observer.
|
||||||
void AddObserver(Observer* observer);
|
void AddObserver(Observer* observer);
|
||||||
void RemoveObserver(Observer* observer);
|
void RemoveObserver(Observer* observer);
|
||||||
|
@ -259,11 +291,16 @@ private:
|
||||||
Filter*,
|
Filter*,
|
||||||
Interframe*,
|
Interframe*,
|
||||||
RenderMethod*,
|
RenderMethod*,
|
||||||
|
AudioApi*,
|
||||||
|
AudioRate*,
|
||||||
uint16_t*>
|
uint16_t*>
|
||||||
value_;
|
value_;
|
||||||
|
|
||||||
|
// Technically, `uint64_t` is only needed for 64 bits targets, as `size_t`.
|
||||||
|
// However, `size_t` is the same as `uint32_t` on 32 bits targets, resulting
|
||||||
|
// in a compiler error if we use `size_t` here.
|
||||||
const nonstd::variant<nonstd::monostate, double, int32_t, uint32_t> min_;
|
const nonstd::variant<nonstd::monostate, double, int32_t, uint32_t> min_;
|
||||||
const nonstd::variant<nonstd::monostate, double, int32_t, uint32_t> max_;
|
const nonstd::variant<nonstd::monostate, double, int32_t, uint32_t, uint64_t> max_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace config
|
} // namespace config
|
||||||
|
|
|
@ -98,7 +98,7 @@ private:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (static_cast<size_t>(selection) > config::kNbFilters) {
|
if (static_cast<size_t>(selection) >= option()->GetEnumMax()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ private:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (static_cast<size_t>(selection) > config::kNbInterframes) {
|
if (static_cast<size_t>(selection) >= option()->GetEnumMax()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,7 +245,7 @@ DisplayConfig::DisplayConfig(wxWindow* parent)
|
||||||
// Speed
|
// Speed
|
||||||
GetValidatedChild(this, "FrameSkip")
|
GetValidatedChild(this, "FrameSkip")
|
||||||
->SetValidator(
|
->SetValidator(
|
||||||
widgets::OptionSpinCtrlValidator(config::OptionID::kPrefFrameSkip));
|
widgets::OptionIntValidator(config::OptionID::kPrefFrameSkip));
|
||||||
|
|
||||||
// On-Screen Display
|
// On-Screen Display
|
||||||
GetValidatedChild(this, "SpeedIndicator")
|
GetValidatedChild(this, "SpeedIndicator")
|
||||||
|
|
|
@ -0,0 +1,333 @@
|
||||||
|
#include "wx/dialogs/sound-config.h"
|
||||||
|
|
||||||
|
#include <wx/arrstr.h>
|
||||||
|
#include <wx/checkbox.h>
|
||||||
|
#include <wx/choice.h>
|
||||||
|
#include <wx/clntdata.h>
|
||||||
|
#include <wx/event.h>
|
||||||
|
#include <wx/radiobut.h>
|
||||||
|
#include <wx/slider.h>
|
||||||
|
|
||||||
|
#include <wx/xrc/xmlres.h>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include "wx/config/option-id.h"
|
||||||
|
#include "wx/config/option-proxy.h"
|
||||||
|
#include "wx/config/option.h"
|
||||||
|
#include "wx/dialogs/validated-child.h"
|
||||||
|
#include "wx/widgets/option-validator.h"
|
||||||
|
#include "wx/wxvbam.h"
|
||||||
|
|
||||||
|
namespace dialogs {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Validator for the sound rate choice.
|
||||||
|
class SoundRateValidator : public widgets::OptionValidator {
|
||||||
|
public:
|
||||||
|
SoundRateValidator() : widgets::OptionValidator(config::OptionID::kSoundAudioRate) {}
|
||||||
|
~SoundRateValidator() override = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// widgets::OptionValidator implementation.
|
||||||
|
wxObject* Clone() const override { return new SoundRateValidator(); }
|
||||||
|
|
||||||
|
bool IsWindowValueValid() override { return true; }
|
||||||
|
|
||||||
|
bool WriteToWindow() override {
|
||||||
|
wxChoice* choice = wxDynamicCast(GetWindow(), wxChoice);
|
||||||
|
assert(choice);
|
||||||
|
choice->SetSelection(static_cast<int>(option()->GetAudioRate()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WriteToOption() override {
|
||||||
|
const wxChoice* choice = wxDynamicCast(GetWindow(), wxChoice);
|
||||||
|
assert(choice);
|
||||||
|
const int selection = choice->GetSelection();
|
||||||
|
if (selection == wxNOT_FOUND) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (static_cast<size_t>(selection) >= option()->GetEnumMax()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return option()->SetAudioRate(static_cast<config::AudioRate>(choice->GetSelection()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Validator for a wxRadioButton with an AudioApi value.
|
||||||
|
class AudioApiValidator : public widgets::OptionValidator {
|
||||||
|
public:
|
||||||
|
explicit AudioApiValidator(config::AudioApi audio_api)
|
||||||
|
: OptionValidator(config::OptionID::kSoundAudioAPI), audio_api_(audio_api) {
|
||||||
|
assert(audio_api < config::AudioApi::kLast);
|
||||||
|
}
|
||||||
|
~AudioApiValidator() override = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// OptionValidator implementation.
|
||||||
|
wxObject* Clone() const override { return new AudioApiValidator(audio_api_); }
|
||||||
|
|
||||||
|
bool IsWindowValueValid() override { return true; }
|
||||||
|
|
||||||
|
bool WriteToWindow() override {
|
||||||
|
wxDynamicCast(GetWindow(), wxRadioButton)->SetValue(option()->GetAudioApi() == audio_api_);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WriteToOption() override {
|
||||||
|
if (wxDynamicCast(GetWindow(), wxRadioButton)->GetValue()) {
|
||||||
|
return option()->SetAudioApi(audio_api_);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const config::AudioApi audio_api_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Validator for the audio device wxChoice.
|
||||||
|
class AudioDeviceValidator : public widgets::OptionValidator {
|
||||||
|
public:
|
||||||
|
AudioDeviceValidator() : widgets::OptionValidator(config::OptionID::kSoundAudioDevice) {}
|
||||||
|
~AudioDeviceValidator() override = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// OptionValidator implementation.
|
||||||
|
wxObject* Clone() const override { return new AudioDeviceValidator(); }
|
||||||
|
|
||||||
|
bool IsWindowValueValid() override { return true; }
|
||||||
|
|
||||||
|
bool WriteToWindow() override {
|
||||||
|
wxChoice* choice = wxDynamicCast(GetWindow(), wxChoice);
|
||||||
|
assert(choice);
|
||||||
|
const wxString& device = option()->GetString();
|
||||||
|
const int selection = choice->FindString(device);
|
||||||
|
if (selection == wxNOT_FOUND) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
choice->SetSelection(selection);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WriteToOption() override {
|
||||||
|
const wxChoice* choice = wxDynamicCast(GetWindow(), wxChoice);
|
||||||
|
assert(choice);
|
||||||
|
const int selection = choice->GetSelection();
|
||||||
|
if (selection == wxNOT_FOUND) {
|
||||||
|
return option()->SetString(wxEmptyString);
|
||||||
|
}
|
||||||
|
|
||||||
|
return option()->SetString(choice->GetString(selection));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// static
|
||||||
|
SoundConfig* SoundConfig::NewInstance(wxWindow* parent) {
|
||||||
|
assert(parent);
|
||||||
|
return new SoundConfig(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
SoundConfig::SoundConfig(wxWindow* parent) : wxDialog(), keep_on_top_styler_(this) {
|
||||||
|
#if !wxCHECK_VERSION(3, 1, 0)
|
||||||
|
// This needs to be set before loading any element on the window. This also
|
||||||
|
// has no effect since wx 3.1.0, where it became the default.
|
||||||
|
this->SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY);
|
||||||
|
#endif
|
||||||
|
wxXmlResource::Get()->LoadDialog(this, parent, "SoundConfig");
|
||||||
|
|
||||||
|
// Volume slider configuration.
|
||||||
|
wxSlider* volume_slider = GetValidatedChild<wxSlider>(this, "Volume");
|
||||||
|
volume_slider->SetValidator(widgets::OptionIntValidator(config::OptionID::kSoundVolume));
|
||||||
|
GetValidatedChild(this, "Volume100")
|
||||||
|
->Bind(wxEVT_BUTTON, std::bind(&wxSlider::SetValue, volume_slider, 100));
|
||||||
|
|
||||||
|
// Sound quality.
|
||||||
|
GetValidatedChild(this, "Rate")->SetValidator(SoundRateValidator());
|
||||||
|
|
||||||
|
// Audio API selection.
|
||||||
|
wxWindow* audio_api_button = GetValidatedChild(this, "OpenAL");
|
||||||
|
audio_api_button->SetValidator(AudioApiValidator(config::AudioApi::kOpenAL));
|
||||||
|
audio_api_button->Bind(wxEVT_RADIOBUTTON,
|
||||||
|
std::bind(&SoundConfig::OnAudioApiChanged, this, std::placeholders::_1,
|
||||||
|
config::AudioApi::kOpenAL));
|
||||||
|
|
||||||
|
audio_api_button = GetValidatedChild(this, "DirectSound");
|
||||||
|
#if defined(__WXMSW__)
|
||||||
|
audio_api_button->SetValidator(AudioApiValidator(config::AudioApi::kDirectSound));
|
||||||
|
audio_api_button->Bind(wxEVT_RADIOBUTTON,
|
||||||
|
std::bind(&SoundConfig::OnAudioApiChanged, this, std::placeholders::_1,
|
||||||
|
config::AudioApi::kDirectSound));
|
||||||
|
#else
|
||||||
|
audio_api_button->Hide();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
audio_api_button = GetValidatedChild(this, "XAudio2");
|
||||||
|
#if defined(VBAM_ENABLE_XAUDIO2)
|
||||||
|
audio_api_button->SetValidator(AudioApiValidator(config::AudioApi::kXAudio2));
|
||||||
|
audio_api_button->Bind(wxEVT_RADIOBUTTON,
|
||||||
|
std::bind(&SoundConfig::OnAudioApiChanged, this, std::placeholders::_1,
|
||||||
|
config::AudioApi::kXAudio2));
|
||||||
|
#else
|
||||||
|
audio_api_button->Hide();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
audio_api_button = GetValidatedChild(this, "FAudio");
|
||||||
|
#if defined(VBAM_ENABLE_FAUDIO)
|
||||||
|
audio_api_button->SetValidator(AudioApiValidator(config::AudioApi::kFAudio));
|
||||||
|
audio_api_button->Bind(wxEVT_RADIOBUTTON,
|
||||||
|
std::bind(&SoundConfig::OnAudioApiChanged, this, std::placeholders::_1,
|
||||||
|
config::AudioApi::kFAudio));
|
||||||
|
#else
|
||||||
|
audio_api_button->Hide();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Upmix configuration.
|
||||||
|
upmix_checkbox_ = GetValidatedChild<wxCheckBox>(this, "Upmix");
|
||||||
|
#if defined(VBAM_ENABLE_XAUDIO2) || defined(VBAM_ENABLE_FAUDIO)
|
||||||
|
upmix_checkbox_->SetValidator(widgets::OptionBoolValidator(config::OptionID::kSoundUpmix));
|
||||||
|
#else
|
||||||
|
upmix_checkbox_->Hide();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// DSound HW acceleration.
|
||||||
|
hw_accel_checkbox_ = GetValidatedChild<wxCheckBox>(this, "HWAccel");
|
||||||
|
#if defined(__WXMSW__)
|
||||||
|
hw_accel_checkbox_->SetValidator(
|
||||||
|
widgets::OptionBoolValidator(config::OptionID::kSoundDSoundHWAccel));
|
||||||
|
#else
|
||||||
|
hw_accel_checkbox_->Hide();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Buffers configuration.
|
||||||
|
buffers_info_label_ = GetValidatedChild<wxControl>(this, "BuffersInfo");
|
||||||
|
buffers_slider_ = GetValidatedChild<wxSlider>(this, "Buffers");
|
||||||
|
buffers_slider_->SetValidator(widgets::OptionIntValidator(config::OptionID::kSoundBuffers));
|
||||||
|
buffers_slider_->Bind(wxEVT_SLIDER, &SoundConfig::OnBuffersChanged, this);
|
||||||
|
|
||||||
|
// Game Boy configuration.
|
||||||
|
GetValidatedChild(this, "GBEcho")
|
||||||
|
->SetValidator(widgets::OptionIntValidator(config::OptionID::kSoundGBEcho));
|
||||||
|
GetValidatedChild(this, "GBStereo")
|
||||||
|
->SetValidator(widgets::OptionIntValidator(config::OptionID::kSoundGBStereo));
|
||||||
|
|
||||||
|
// Game Boy Advance configuration.
|
||||||
|
GetValidatedChild(this, "GBASoundFiltering")
|
||||||
|
->SetValidator(widgets::OptionIntValidator(config::OptionID::kSoundGBAFiltering));
|
||||||
|
|
||||||
|
// Audio Device configuration.
|
||||||
|
audio_device_selector_ = GetValidatedChild<wxChoice>(this, "Device");
|
||||||
|
audio_device_selector_->SetValidator(AudioDeviceValidator());
|
||||||
|
|
||||||
|
this->Bind(wxEVT_SHOW, &SoundConfig::OnShow, this);
|
||||||
|
|
||||||
|
// Finally, fit everything nicely.
|
||||||
|
Fit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundConfig::OnShow(wxShowEvent& event) {
|
||||||
|
wxCommandEvent dummy_event;
|
||||||
|
|
||||||
|
// Referesh the buffers information.
|
||||||
|
OnBuffersChanged(dummy_event);
|
||||||
|
|
||||||
|
// Refresh the audio device selector.
|
||||||
|
OnAudioApiChanged(dummy_event, OPTION(kSoundAudioAPI));
|
||||||
|
|
||||||
|
// Let the event propagate.
|
||||||
|
event.Skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundConfig::OnBuffersChanged(wxCommandEvent& event) {
|
||||||
|
const int buffers_count = buffers_slider_->GetValue();
|
||||||
|
const double buffer_time = static_cast<double>(buffers_count) / 60.0 * 1000.0;
|
||||||
|
buffers_info_label_->SetLabel(
|
||||||
|
wxString::Format(_("%d frame = %.2f ms"), buffers_count, buffer_time));
|
||||||
|
|
||||||
|
// Let the event propagate.
|
||||||
|
event.Skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundConfig::OnAudioApiChanged(wxCommandEvent& event, config::AudioApi audio_api) {
|
||||||
|
audio_device_selector_->Clear();
|
||||||
|
audio_device_selector_->Append(_("Default device"), new wxStringClientData(wxEmptyString));
|
||||||
|
|
||||||
|
// Gather device names and IDs.
|
||||||
|
wxArrayString device_names;
|
||||||
|
wxArrayString device_ids;
|
||||||
|
switch (audio_api) {
|
||||||
|
case config::AudioApi::kOpenAL:
|
||||||
|
if (!GetOALDevices(device_names, device_ids)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
#if defined(__WXMSW__)
|
||||||
|
case config::AudioApi::kDirectSound:
|
||||||
|
if (!(GetDSDevices(device_names, device_ids))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(VBAM_ENABLE_XAUDIO2)
|
||||||
|
case config::AudioApi::kXAudio2:
|
||||||
|
if (!GetXA2Devices(device_names, device_ids)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(VBAM_ENABLE_FAUDIO)
|
||||||
|
case config::AudioApi::kFAudio:
|
||||||
|
if (!GetFADevices(device_names, device_ids)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
case config::AudioApi::kLast:
|
||||||
|
// This should never happen.
|
||||||
|
assert(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
audio_device_selector_->SetSelection(0);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < device_names.size(); i++) {
|
||||||
|
audio_device_selector_->Append(device_names[i], new wxStringClientData(device_ids[i]));
|
||||||
|
|
||||||
|
if (audio_api == OPTION(kSoundAudioAPI) && OPTION(kSoundAudioDevice) == device_ids[i]) {
|
||||||
|
audio_device_selector_->SetSelection(i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(VBAM_ENABLE_XAUDIO2) && defined(VBAM_ENABLE_FAUDIO)
|
||||||
|
upmix_checkbox_->Enable(audio_api == config::AudioApi::kXAudio2 ||
|
||||||
|
audio_api == config::AudioApi::kFAudio);
|
||||||
|
#elif defined(VBAM_ENABLE_XAUDIO2)
|
||||||
|
upmix_checkbox_->Enable(audio_api == config::AudioApi::kXAudio2);
|
||||||
|
#elif defined(VBAM_ENABLE_FAUDIO)
|
||||||
|
upmix_checkbox_->Enable(audio_api == config::AudioApi::kFAudio);
|
||||||
|
#else
|
||||||
|
upmix_checkbox_->Enable(false);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__WXMSW__)
|
||||||
|
hw_accel_checkbox_->Enable(audio_api == config::AudioApi::kDirectSound);
|
||||||
|
#else
|
||||||
|
hw_accel_checkbox_->Enable(false);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
current_audio_api_ = audio_api;
|
||||||
|
|
||||||
|
// Let the event propagate.
|
||||||
|
event.Skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dialogs
|
|
@ -0,0 +1,50 @@
|
||||||
|
#ifndef VBAM_WX_DIALOGS_SOUND_CONFIG_H_
|
||||||
|
#define VBAM_WX_DIALOGS_SOUND_CONFIG_H_
|
||||||
|
|
||||||
|
#include <wx/dialog.h>
|
||||||
|
#include <wx/event.h>
|
||||||
|
|
||||||
|
#include "wx/config/option.h"
|
||||||
|
#include "wx/widgets/keep-on-top-styler.h"
|
||||||
|
|
||||||
|
// Forward declarations.
|
||||||
|
class wxChoice;
|
||||||
|
class wxCheckBox;
|
||||||
|
class wxControl;
|
||||||
|
class wxSlider;
|
||||||
|
class wxWindow;
|
||||||
|
|
||||||
|
namespace dialogs {
|
||||||
|
|
||||||
|
// Manages the sound configuration dialog.
|
||||||
|
class SoundConfig : public wxDialog {
|
||||||
|
public:
|
||||||
|
static SoundConfig* NewInstance(wxWindow* parent);
|
||||||
|
~SoundConfig() override = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// The constructor is private so initialization has to be done via the
|
||||||
|
// static method. This is because this class is destroyed when its
|
||||||
|
// owner, `parent` is destroyed. This prevents accidental deletion.
|
||||||
|
SoundConfig(wxWindow* parent);
|
||||||
|
|
||||||
|
void OnShow(wxShowEvent& event);
|
||||||
|
|
||||||
|
// Populate `buffers_info_label_` with the current buffer information.
|
||||||
|
void OnBuffersChanged(wxCommandEvent& event);
|
||||||
|
|
||||||
|
// Refresh `audio_device_selector_`.
|
||||||
|
void OnAudioApiChanged(wxCommandEvent& event, config::AudioApi api);
|
||||||
|
|
||||||
|
wxSlider* buffers_slider_;
|
||||||
|
wxControl* buffers_info_label_;
|
||||||
|
wxChoice* audio_device_selector_;
|
||||||
|
wxCheckBox* upmix_checkbox_;
|
||||||
|
wxCheckBox* hw_accel_checkbox_;
|
||||||
|
config::AudioApi current_audio_api_;
|
||||||
|
const widgets::KeepOnTopStyler keep_on_top_styler_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dialogs
|
||||||
|
|
||||||
|
#endif // VBAM_WX_DIALOGS_SOUND_CONFIG_H_
|
|
@ -1,17 +1,26 @@
|
||||||
// Application
|
#if !defined(__WXMSW__)
|
||||||
#include "wx/wxvbam.h"
|
#error "This file should only be compiled on Windows"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// DirectSound8
|
||||||
|
#define DIRECTSOUND_VERSION 0x0800
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <mmeapi.h>
|
||||||
|
|
||||||
|
#include <dsound.h>
|
||||||
|
#include <uuids.h>
|
||||||
|
|
||||||
|
#include <wx/arrstr.h>
|
||||||
|
#include <wx/log.h>
|
||||||
|
#include <wx/translation.h>
|
||||||
|
|
||||||
// Internals
|
// Internals
|
||||||
#include "core/base/sound_driver.h"
|
#include "core/base/sound_driver.h"
|
||||||
#include "core/base/system.h"
|
#include "core/base/system.h"
|
||||||
#include "core/gba/gbaGlobals.h"
|
#include "core/gba/gbaGlobals.h"
|
||||||
#include "core/gba/gbaSound.h"
|
#include "core/gba/gbaSound.h"
|
||||||
|
#include "wx/config/option-proxy.h"
|
||||||
// DirectSound8
|
#include "wx/wxvbam.h"
|
||||||
#define DIRECTSOUND_VERSION 0x0800
|
|
||||||
#include <mmeapi.h>
|
|
||||||
#include <uuids.h>
|
|
||||||
#include <dsound.h>
|
|
||||||
|
|
||||||
extern bool soundBufferLow;
|
extern bool soundBufferLow;
|
||||||
|
|
||||||
|
@ -31,12 +40,13 @@ public:
|
||||||
DirectSound();
|
DirectSound();
|
||||||
virtual ~DirectSound();
|
virtual ~DirectSound();
|
||||||
|
|
||||||
bool init(long sampleRate); // initialize the primary and secondary sound buffer
|
bool init(long sampleRate) override; // initialize the primary and secondary sound buffer
|
||||||
void setThrottle(unsigned short throttle_); // set game speed
|
void setThrottle(unsigned short throttle_) override; // set game speed
|
||||||
void pause(); // pause the secondary sound buffer
|
void pause() override; // pause the secondary sound buffer
|
||||||
void reset(); // stop and reset the secondary sound buffer
|
void reset() override; // stop and reset the secondary sound buffer
|
||||||
void resume(); // resume the secondary sound buffer
|
void resume() override; // resume the secondary sound buffer
|
||||||
void write(uint16_t* finalWave, int length); // write the emulated sound to the secondary sound buffer
|
void write(uint16_t* finalWave,
|
||||||
|
int length) override; // write the emulated sound to the secondary sound buffer
|
||||||
};
|
};
|
||||||
|
|
||||||
DirectSound::DirectSound()
|
DirectSound::DirectSound()
|
||||||
|
@ -93,10 +103,11 @@ bool DirectSound::init(long sampleRate)
|
||||||
|
|
||||||
GUID dev;
|
GUID dev;
|
||||||
|
|
||||||
if (gopts.audio_dev.empty())
|
const wxString& audio_device = OPTION(kSoundAudioDevice);
|
||||||
|
if (audio_device.empty())
|
||||||
dev = DSDEVID_DefaultPlayback;
|
dev = DSDEVID_DefaultPlayback;
|
||||||
else
|
else
|
||||||
CLSIDFromString(gopts.audio_dev.wc_str(), &dev);
|
CLSIDFromString(audio_device.wc_str(), &dev);
|
||||||
|
|
||||||
pDirectSound->Initialize(&dev);
|
pDirectSound->Initialize(&dev);
|
||||||
|
|
||||||
|
@ -115,7 +126,8 @@ bool DirectSound::init(long sampleRate)
|
||||||
dsbdesc.dwSize = sizeof(DSBUFFERDESC);
|
dsbdesc.dwSize = sizeof(DSBUFFERDESC);
|
||||||
dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER;
|
dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER;
|
||||||
|
|
||||||
if (!gopts.dsound_hw_accel) {
|
const bool hw_accel = OPTION(kSoundDSoundHWAccel);
|
||||||
|
if (!hw_accel) {
|
||||||
dsbdesc.dwFlags |= DSBCAPS_LOCSOFTWARE;
|
dsbdesc.dwFlags |= DSBCAPS_LOCSOFTWARE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,7 +160,7 @@ bool DirectSound::init(long sampleRate)
|
||||||
dsbdesc.dwSize = sizeof(DSBUFFERDESC);
|
dsbdesc.dwSize = sizeof(DSBUFFERDESC);
|
||||||
dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLFREQUENCY;
|
dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLFREQUENCY;
|
||||||
|
|
||||||
if (!gopts.dsound_hw_accel) {
|
if (!hw_accel) {
|
||||||
dsbdesc.dwFlags |= DSBCAPS_LOCSOFTWARE;
|
dsbdesc.dwFlags |= DSBCAPS_LOCSOFTWARE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,23 +1,24 @@
|
||||||
#ifndef NO_FAUDIO
|
#if !defined(VBAM_ENABLE_FAUDIO)
|
||||||
|
#error "This file should only be compiled if FAudio is enabled"
|
||||||
|
#endif
|
||||||
|
|
||||||
// Application
|
#include <cstdio>
|
||||||
#include "wx/wxvbam.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
// Interface
|
#include <string>
|
||||||
#include "core/base/sound_driver.h"
|
#include <vector>
|
||||||
|
|
||||||
// FAudio
|
// FAudio
|
||||||
#include <faudio.h>
|
#include <faudio.h>
|
||||||
|
|
||||||
// MMDevice API
|
// MMDevice API
|
||||||
#include <mmdeviceapi.h>
|
#include <mmdeviceapi.h>
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
// Internals
|
#include <wx/arrstr.h>
|
||||||
#include "core/base/system.h" // for systemMessage()
|
|
||||||
|
#include "core/base/sound_driver.h"
|
||||||
|
#include "core/base/system.h"
|
||||||
#include "core/gba/gbaGlobals.h"
|
#include "core/gba/gbaGlobals.h"
|
||||||
|
#include "wx/config/option-proxy.h"
|
||||||
|
|
||||||
int GetFADevices(FAudio* fa, wxArrayString* names, wxArrayString* ids,
|
int GetFADevices(FAudio* fa, wxArrayString* names, wxArrayString* ids,
|
||||||
const wxString* match)
|
const wxString* match)
|
||||||
|
@ -75,10 +76,11 @@ bool GetFADevices(wxArrayString& names, wxArrayString& ids)
|
||||||
|
|
||||||
static int FAGetDev(FAudio* fa)
|
static int FAGetDev(FAudio* fa)
|
||||||
{
|
{
|
||||||
if (gopts.audio_dev.empty())
|
const wxString& audio_device = OPTION(kSoundAudioDevice);
|
||||||
|
if (audio_device.empty())
|
||||||
return 0;
|
return 0;
|
||||||
else {
|
else {
|
||||||
int ret = GetFADevices(fa, NULL, NULL, &gopts.audio_dev);
|
int ret = GetFADevices(fa, NULL, NULL, &audio_device);
|
||||||
return ret < 0 ? 0 : ret;
|
return ret < 0 ? 0 : ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -281,7 +283,7 @@ FAudio_Output::FAudio_Output()
|
||||||
initialized = false;
|
initialized = false;
|
||||||
playing = false;
|
playing = false;
|
||||||
freq = 0;
|
freq = 0;
|
||||||
bufferCount = gopts.audio_buffers;
|
bufferCount = OPTION(kSoundBuffers);
|
||||||
buffers = NULL;
|
buffers = NULL;
|
||||||
currentBuffer = 0;
|
currentBuffer = 0;
|
||||||
device_changed = false;
|
device_changed = false;
|
||||||
|
@ -396,7 +398,7 @@ bool FAudio_Output::init(long sampleRate)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gopts.upmix) {
|
if (OPTION(kSoundUpmix)) {
|
||||||
// set up stereo upmixing
|
// set up stereo upmixing
|
||||||
FAudioDeviceDetails dd;
|
FAudioDeviceDetails dd;
|
||||||
ZeroMemory(&dd, sizeof(dd));
|
ZeroMemory(&dd, sizeof(dd));
|
||||||
|
@ -626,5 +628,3 @@ SoundDriver* newFAudio_Output()
|
||||||
{
|
{
|
||||||
return new FAudio_Output();
|
return new FAudio_Output();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // #ifndef NO_FAUDIO
|
|
||||||
|
|
|
@ -45,9 +45,10 @@
|
||||||
#include "wx/dialogs/game-boy-config.h"
|
#include "wx/dialogs/game-boy-config.h"
|
||||||
#include "wx/dialogs/gb-rom-info.h"
|
#include "wx/dialogs/gb-rom-info.h"
|
||||||
#include "wx/dialogs/joypad-config.h"
|
#include "wx/dialogs/joypad-config.h"
|
||||||
|
#include "wx/dialogs/sound-config.h"
|
||||||
#include "wx/opts.h"
|
#include "wx/opts.h"
|
||||||
#include "wx/widgets/option-validator.h"
|
|
||||||
#include "wx/widgets/checkedlistctrl.h"
|
#include "wx/widgets/checkedlistctrl.h"
|
||||||
|
#include "wx/widgets/option-validator.h"
|
||||||
#include "wx/wxhead.h"
|
#include "wx/wxhead.h"
|
||||||
|
|
||||||
#if defined(__WXGTK__)
|
#if defined(__WXGTK__)
|
||||||
|
@ -1469,143 +1470,6 @@ public:
|
||||||
}
|
}
|
||||||
} BatConfigHandler;
|
} BatConfigHandler;
|
||||||
|
|
||||||
// manage the sound prefs dialog
|
|
||||||
static class SoundConfig_t : public wxEvtHandler {
|
|
||||||
public:
|
|
||||||
wxSlider *vol, *bufs;
|
|
||||||
wxControl* bufinfo;
|
|
||||||
int lastapi;
|
|
||||||
wxChoice* dev;
|
|
||||||
wxControl *umix, *hwacc;
|
|
||||||
wxArrayString dev_ids;
|
|
||||||
|
|
||||||
void FullVol(wxCommandEvent& ev)
|
|
||||||
{
|
|
||||||
(void)ev; // unused params
|
|
||||||
vol->SetValue(100);
|
|
||||||
}
|
|
||||||
void AdjustFrames(int count)
|
|
||||||
{
|
|
||||||
wxString s;
|
|
||||||
s.Printf(_("%d frames = %.2f ms"), count, (double)count / 60.0 * 1000.0);
|
|
||||||
bufinfo->SetLabel(s);
|
|
||||||
}
|
|
||||||
void AdjustFramesEv(wxCommandEvent& ev)
|
|
||||||
{
|
|
||||||
(void)ev; // unused params
|
|
||||||
AdjustFrames(bufs->GetValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FillDev(int api)
|
|
||||||
{
|
|
||||||
dev->Clear();
|
|
||||||
dev->Append(_("Default device"));
|
|
||||||
dev_ids.clear();
|
|
||||||
wxArrayString names;
|
|
||||||
|
|
||||||
switch (api) {
|
|
||||||
case AUD_SDL:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AUD_OPENAL:
|
|
||||||
if (!GetOALDevices(names, dev_ids))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
break;
|
|
||||||
#ifdef __WXMSW__
|
|
||||||
|
|
||||||
case AUD_DIRECTSOUND:
|
|
||||||
if (!(GetDSDevices(names, dev_ids)))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
break;
|
|
||||||
#ifndef NO_XAUDIO2
|
|
||||||
|
|
||||||
case AUD_XAUDIO2:
|
|
||||||
if (!GetXA2Devices(names, dev_ids))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
#ifndef NO_FAUDIO
|
|
||||||
|
|
||||||
case AUD_FAUDIO:
|
|
||||||
if (!GetFADevices(names, dev_ids))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
dev->SetSelection(0);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < names.size(); i++) {
|
|
||||||
dev->Append(names[i]);
|
|
||||||
|
|
||||||
if (api == gopts.audio_api && gopts.audio_dev == dev_ids[i])
|
|
||||||
dev->SetSelection(i + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
umix->Enable(api == AUD_XAUDIO2);
|
|
||||||
hwacc->Enable(api == AUD_DIRECTSOUND);
|
|
||||||
lastapi = api;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
void SetAPI(wxCommandEvent& ev)
|
|
||||||
{
|
|
||||||
int api = gopts.audio_api;
|
|
||||||
wxValidator* v = wxStaticCast(ev.GetEventObject(), wxWindow)->GetValidator();
|
|
||||||
v->TransferFromWindow();
|
|
||||||
int newapi = gopts.audio_api;
|
|
||||||
gopts.audio_api = api;
|
|
||||||
|
|
||||||
if (newapi == lastapi)
|
|
||||||
return;
|
|
||||||
|
|
||||||
gopts.audio_dev = wxT("");
|
|
||||||
FillDev(newapi);
|
|
||||||
}
|
|
||||||
} sound_config_handler;
|
|
||||||
|
|
||||||
// Validator/widget filler for sound device selector & time indicator
|
|
||||||
class SoundConfigLoad : public wxValidator {
|
|
||||||
public:
|
|
||||||
SoundConfigLoad()
|
|
||||||
: wxValidator()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
SoundConfigLoad(const SoundConfigLoad& e)
|
|
||||||
: wxValidator()
|
|
||||||
{
|
|
||||||
(void)e; // unused params
|
|
||||||
}
|
|
||||||
wxObject* Clone() const { return new SoundConfigLoad(*this); }
|
|
||||||
bool Validate(wxWindow* p) {
|
|
||||||
(void)p; // unused params
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool TransferToWindow()
|
|
||||||
{
|
|
||||||
SoundConfig_t& sch = sound_config_handler;
|
|
||||||
sch.FillDev(gopts.audio_api);
|
|
||||||
sch.AdjustFrames(gopts.audio_buffers);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool TransferFromWindow()
|
|
||||||
{
|
|
||||||
SoundConfig_t& sch = sound_config_handler;
|
|
||||||
int devs = sch.dev->GetSelection();
|
|
||||||
|
|
||||||
if (devs <= 0)
|
|
||||||
gopts.audio_dev = wxEmptyString;
|
|
||||||
else
|
|
||||||
gopts.audio_dev = sch.dev_ids[devs - 1];
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// manage throttle spinctrl/canned setting choice interaction
|
// manage throttle spinctrl/canned setting choice interaction
|
||||||
static class ThrottleCtrl_t : public wxEvtHandler {
|
static class ThrottleCtrl_t : public wxEvtHandler {
|
||||||
public:
|
public:
|
||||||
|
@ -2398,12 +2262,12 @@ bool MainFrame::BindControls()
|
||||||
cheat_list_handler.item1.SetFont(cl->GetFont());
|
cheat_list_handler.item1.SetFont(cl->GetFont());
|
||||||
cheat_list_handler.item1.SetColumn(1);
|
cheat_list_handler.item1.SetColumn(1);
|
||||||
#if 0
|
#if 0
|
||||||
// the ideal way to set col 0's width would be to use
|
// the ideal way to set col 0's width would be to use
|
||||||
// wxLIST_AUTOSIZE after setting value to a sample:
|
// wxLIST_AUTOSIZE after setting value to a sample:
|
||||||
cheat_list_handler.item0.SetText(wxT("00000000 00000000"));
|
cheat_list_handler.item0.SetText(wxT("00000000 00000000"));
|
||||||
cl->InsertItem(cheat_list_handler.item0);
|
cl->InsertItem(cheat_list_handler.item0);
|
||||||
cl->SetColumnWidth(0, wxLIST_AUTOSIZE);
|
cl->SetColumnWidth(0, wxLIST_AUTOSIZE);
|
||||||
cl->RemoveItem(0);
|
cl->RemoveItem(0);
|
||||||
#else
|
#else
|
||||||
// however, the generic listctrl implementation uses the wrong
|
// however, the generic listctrl implementation uses the wrong
|
||||||
// font to determine width (window vs. item), and does not
|
// font to determine width (window vs. item), and does not
|
||||||
|
@ -2544,12 +2408,6 @@ bool MainFrame::BindControls()
|
||||||
}
|
}
|
||||||
//// config menu
|
//// config menu
|
||||||
d = LoadXRCDialog("GeneralConfig");
|
d = LoadXRCDialog("GeneralConfig");
|
||||||
wxCheckBox* cb;
|
|
||||||
#define getcbb(n, o) \
|
|
||||||
do { \
|
|
||||||
cb = SafeXRCCTRL<wxCheckBox>(d, n); \
|
|
||||||
cb->SetValidator(wxGenericValidator(&o)); \
|
|
||||||
} while (0)
|
|
||||||
wxSpinCtrl* sc;
|
wxSpinCtrl* sc;
|
||||||
#define getsc(n, o) \
|
#define getsc(n, o) \
|
||||||
do { \
|
do { \
|
||||||
|
@ -2670,77 +2528,7 @@ bool MainFrame::BindControls()
|
||||||
}
|
}
|
||||||
|
|
||||||
dialogs::DisplayConfig::NewInstance(this);
|
dialogs::DisplayConfig::NewInstance(this);
|
||||||
|
dialogs::SoundConfig::NewInstance(this);
|
||||||
d = LoadXRCropertySheetDialog("SoundConfig");
|
|
||||||
wxSlider* sl;
|
|
||||||
#define getsl(n, o) \
|
|
||||||
do { \
|
|
||||||
sl = SafeXRCCTRL<wxSlider>(d, n); \
|
|
||||||
sl->SetValidator(wxGenericValidator(&o)); \
|
|
||||||
} while (0)
|
|
||||||
{
|
|
||||||
/// Basic
|
|
||||||
getsl("Volume", gopts.sound_vol);
|
|
||||||
sound_config_handler.vol = sl;
|
|
||||||
d->Connect(XRCID("Volume100"), wxEVT_COMMAND_BUTTON_CLICKED,
|
|
||||||
wxCommandEventHandler(SoundConfig_t::FullVol),
|
|
||||||
NULL, &sound_config_handler);
|
|
||||||
ch = GetValidatedChild<wxChoice, wxGenericValidator>(d, "Rate", wxGenericValidator(&gopts.sound_qual));
|
|
||||||
/// Advanced
|
|
||||||
#define audapi_rb(n, v) \
|
|
||||||
do { \
|
|
||||||
getrbi(n, gopts.audio_api, v); \
|
|
||||||
rb->Connect(wxEVT_COMMAND_RADIOBUTTON_SELECTED, \
|
|
||||||
wxCommandEventHandler(SoundConfig_t::SetAPI), \
|
|
||||||
NULL, &sound_config_handler); \
|
|
||||||
} while (0)
|
|
||||||
audapi_rb("SDL", AUD_SDL);
|
|
||||||
rb->Hide(); // currently disabled
|
|
||||||
|
|
||||||
audapi_rb("OpenAL", AUD_OPENAL);
|
|
||||||
audapi_rb("DirectSound", AUD_DIRECTSOUND);
|
|
||||||
#ifndef __WXMSW__
|
|
||||||
rb->Hide();
|
|
||||||
#endif
|
|
||||||
audapi_rb("XAudio2", AUD_XAUDIO2);
|
|
||||||
#if !defined(__WXMSW__) || defined(NO_XAUDIO2)
|
|
||||||
rb->Hide();
|
|
||||||
#endif
|
|
||||||
audapi_rb("FAudio", AUD_FAUDIO);
|
|
||||||
#ifdef NO_FAUDIO
|
|
||||||
rb->Hide();
|
|
||||||
#endif
|
|
||||||
sound_config_handler.dev = SafeXRCCTRL<wxChoice>(d, "Device");
|
|
||||||
sound_config_handler.dev->SetValidator(SoundConfigLoad());
|
|
||||||
getcbb("Upmix", gopts.upmix);
|
|
||||||
sound_config_handler.umix = cb;
|
|
||||||
#if !defined(__WXMSW__) || defined(NO_XAUDIO2)
|
|
||||||
cb->Hide();
|
|
||||||
#endif
|
|
||||||
getcbb("HWAccel", gopts.dsound_hw_accel);
|
|
||||||
sound_config_handler.hwacc = cb;
|
|
||||||
#ifndef __WXMSW__
|
|
||||||
cb->Hide();
|
|
||||||
#endif
|
|
||||||
getsl("Buffers", gopts.audio_buffers);
|
|
||||||
sound_config_handler.bufs = sl;
|
|
||||||
getlab("BuffersInfo");
|
|
||||||
sound_config_handler.bufinfo = lab;
|
|
||||||
sl->Connect(wxEVT_SCROLL_CHANGED,
|
|
||||||
wxCommandEventHandler(SoundConfig_t::AdjustFramesEv),
|
|
||||||
NULL, &sound_config_handler);
|
|
||||||
sl->Connect(wxEVT_SCROLL_THUMBTRACK,
|
|
||||||
wxCommandEventHandler(SoundConfig_t::AdjustFramesEv),
|
|
||||||
NULL, &sound_config_handler);
|
|
||||||
sound_config_handler.AdjustFrames(10);
|
|
||||||
/// Game Boy
|
|
||||||
SafeXRCCTRL<wxPanel>(d, "GBEnhanceSoundDep");
|
|
||||||
getsl("GBEcho", gopts.gb_echo);
|
|
||||||
getsl("GBStereo", gopts.gb_stereo);
|
|
||||||
/// Game Boy Advance
|
|
||||||
getsl("GBASoundFiltering", gopts.gba_sound_filter);
|
|
||||||
d->Fit();
|
|
||||||
}
|
|
||||||
dialogs::DirectoriesConfig::NewInstance(this);
|
dialogs::DirectoriesConfig::NewInstance(this);
|
||||||
dialogs::JoypadConfig::NewInstance(this);
|
dialogs::JoypadConfig::NewInstance(this);
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,19 @@
|
||||||
// === LOGALL writes very detailed informations to vba-trace.log ===
|
// === LOGALL writes very detailed informations to vba-trace.log ===
|
||||||
//#define LOGALL
|
//#define LOGALL
|
||||||
|
|
||||||
// for gopts
|
#include "wx/openal.h"
|
||||||
// also, wx-related
|
|
||||||
#include "wx/wxvbam.h"
|
#include <cassert>
|
||||||
|
|
||||||
|
#include <wx/arrstr.h>
|
||||||
|
#include <wx/utils.h>
|
||||||
|
|
||||||
// Interface
|
|
||||||
#include "core/base/sound_driver.h"
|
#include "core/base/sound_driver.h"
|
||||||
|
#include "core/gba/gbaGlobals.h"
|
||||||
// OpenAL
|
|
||||||
#include "openal.h"
|
|
||||||
|
|
||||||
// Internals
|
|
||||||
#include "core/gba/gbaGlobals.h" // for 'speedup' and 'synchronize'
|
|
||||||
#include "core/gba/gbaSound.h"
|
#include "core/gba/gbaSound.h"
|
||||||
|
#include "wx/config/option-proxy.h"
|
||||||
|
|
||||||
// Debug
|
// Debug
|
||||||
#include <assert.h>
|
|
||||||
#define ASSERT_SUCCESS assert(AL_NO_ERROR == alGetError())
|
#define ASSERT_SUCCESS assert(AL_NO_ERROR == alGetError())
|
||||||
|
|
||||||
#ifndef LOGALL
|
#ifndef LOGALL
|
||||||
|
@ -66,8 +63,8 @@ OpenAL::OpenAL()
|
||||||
buffersLoaded = false;
|
buffersLoaded = false;
|
||||||
device = NULL;
|
device = NULL;
|
||||||
context = NULL;
|
context = NULL;
|
||||||
buffer = (ALuint*)malloc(gopts.audio_buffers * sizeof(ALuint));
|
buffer = (ALuint*)malloc(OPTION(kSoundBuffers) * sizeof(ALuint));
|
||||||
memset(buffer, 0, gopts.audio_buffers * sizeof(ALuint));
|
memset(buffer, 0, OPTION(kSoundBuffers) * sizeof(ALuint));
|
||||||
tempBuffer = 0;
|
tempBuffer = 0;
|
||||||
source = 0;
|
source = 0;
|
||||||
}
|
}
|
||||||
|
@ -83,7 +80,7 @@ OpenAL::~OpenAL()
|
||||||
ASSERT_SUCCESS;
|
ASSERT_SUCCESS;
|
||||||
alDeleteSources(1, &source);
|
alDeleteSources(1, &source);
|
||||||
ASSERT_SUCCESS;
|
ASSERT_SUCCESS;
|
||||||
alDeleteBuffers(gopts.audio_buffers, buffer);
|
alDeleteBuffers(OPTION(kSoundBuffers), buffer);
|
||||||
ASSERT_SUCCESS;
|
ASSERT_SUCCESS;
|
||||||
free(buffer);
|
free(buffer);
|
||||||
alcMakeContextCurrent(NULL);
|
alcMakeContextCurrent(NULL);
|
||||||
|
@ -146,8 +143,9 @@ bool OpenAL::init(long sampleRate)
|
||||||
winlog("OpenAL::init\n");
|
winlog("OpenAL::init\n");
|
||||||
assert(initialized == false);
|
assert(initialized == false);
|
||||||
|
|
||||||
if (!gopts.audio_dev.empty()) {
|
const wxString& audio_device = OPTION(kSoundAudioDevice);
|
||||||
device = alcOpenDevice(gopts.audio_dev.utf8_str());
|
if (!audio_device.empty()) {
|
||||||
|
device = alcOpenDevice(audio_device.utf8_str());
|
||||||
} else {
|
} else {
|
||||||
device = alcOpenDevice(NULL);
|
device = alcOpenDevice(NULL);
|
||||||
}
|
}
|
||||||
|
@ -157,7 +155,7 @@ bool OpenAL::init(long sampleRate)
|
||||||
assert(context != NULL);
|
assert(context != NULL);
|
||||||
ALCboolean retVal = alcMakeContextCurrent(context);
|
ALCboolean retVal = alcMakeContextCurrent(context);
|
||||||
assert(ALC_TRUE == retVal);
|
assert(ALC_TRUE == retVal);
|
||||||
alGenBuffers(gopts.audio_buffers, buffer);
|
alGenBuffers(OPTION(kSoundBuffers), buffer);
|
||||||
ASSERT_SUCCESS;
|
ASSERT_SUCCESS;
|
||||||
alGenSources(1, &source);
|
alGenSources(1, &source);
|
||||||
ASSERT_SUCCESS;
|
ASSERT_SUCCESS;
|
||||||
|
@ -264,14 +262,14 @@ void OpenAL::write(uint16_t* finalWave, int length)
|
||||||
// ==initial buffer filling==
|
// ==initial buffer filling==
|
||||||
winlog(" initial buffer filling\n");
|
winlog(" initial buffer filling\n");
|
||||||
|
|
||||||
for (int i = 0; i < gopts.audio_buffers; i++) {
|
for (int i = 0; i < OPTION(kSoundBuffers); i++) {
|
||||||
// Filling the buffers explicitly with silence would be cleaner,
|
// Filling the buffers explicitly with silence would be cleaner,
|
||||||
// but the very first sample is usually silence anyway.
|
// but the very first sample is usually silence anyway.
|
||||||
alBufferData(buffer[i], AL_FORMAT_STEREO16, finalWave, soundBufferLen, freq);
|
alBufferData(buffer[i], AL_FORMAT_STEREO16, finalWave, soundBufferLen, freq);
|
||||||
ASSERT_SUCCESS;
|
ASSERT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
alSourceQueueBuffers(source, gopts.audio_buffers, buffer);
|
alSourceQueueBuffers(source, OPTION(kSoundBuffers), buffer);
|
||||||
ASSERT_SUCCESS;
|
ASSERT_SUCCESS;
|
||||||
buffersLoaded = true;
|
buffersLoaded = true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -280,7 +278,7 @@ void OpenAL::write(uint16_t* finalWave, int length)
|
||||||
alGetSourcei(source, AL_BUFFERS_PROCESSED, &nBuffersProcessed);
|
alGetSourcei(source, AL_BUFFERS_PROCESSED, &nBuffersProcessed);
|
||||||
ASSERT_SUCCESS;
|
ASSERT_SUCCESS;
|
||||||
|
|
||||||
if (nBuffersProcessed == gopts.audio_buffers) {
|
if (nBuffersProcessed == OPTION(kSoundBuffers)) {
|
||||||
// we only want to know about it when we are emulating at full speed or faster:
|
// we only want to know about it when we are emulating at full speed or faster:
|
||||||
if ((coreOptions.throttle >= 100) || (coreOptions.throttle == 0)) {
|
if ((coreOptions.throttle >= 100) || (coreOptions.throttle == 0)) {
|
||||||
if (systemVerbose & VERBOSE_SOUNDOUTPUT) {
|
if (systemVerbose & VERBOSE_SOUNDOUTPUT) {
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
#include "wx/config/option.h"
|
#include "wx/config/option.h"
|
||||||
#include "wx/config/user-input.h"
|
#include "wx/config/user-input.h"
|
||||||
#include "wx/strutils.h"
|
#include "wx/strutils.h"
|
||||||
#include "wx/wxhead.h"
|
|
||||||
#include "wx/wxvbam.h"
|
#include "wx/wxvbam.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -55,7 +54,7 @@ void SaveOption(config::Option* option) {
|
||||||
case config::Option::Type::kInterframe:
|
case config::Option::Type::kInterframe:
|
||||||
case config::Option::Type::kRenderMethod:
|
case config::Option::Type::kRenderMethod:
|
||||||
case config::Option::Type::kAudioApi:
|
case config::Option::Type::kAudioApi:
|
||||||
case config::Option::Type::kSoundQuality:
|
case config::Option::Type::kAudioRate:
|
||||||
cfg->Write(option->config_name(), option->GetEnumString());
|
cfg->Write(option->config_name(), option->GetEnumString());
|
||||||
break;
|
break;
|
||||||
case config::Option::Type::kGbPalette:
|
case config::Option::Type::kGbPalette:
|
||||||
|
@ -387,6 +386,19 @@ void load_opts(bool first_time_launch) {
|
||||||
// config file will be updated with unset options
|
// config file will be updated with unset options
|
||||||
cfg->SetRecordDefaults();
|
cfg->SetRecordDefaults();
|
||||||
|
|
||||||
|
// Deprecated / moved options handling.
|
||||||
|
{
|
||||||
|
// The SDL audio API is no longer supported.
|
||||||
|
wxString temp;
|
||||||
|
if (cfg->Read("Sound/AudioAPI", &temp) && temp == "sdl") {
|
||||||
|
#if defined(VBAM_ENABLE_XAUDIO2)
|
||||||
|
cfg->Write("Sound/AudioAPI", "xaudio2");
|
||||||
|
#else
|
||||||
|
cfg->Write("Sound/AudioAPI", "openal");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// First access here will also initialize translations.
|
// First access here will also initialize translations.
|
||||||
for (config::Option& opt : config::Option::All()) {
|
for (config::Option& opt : config::Option::All()) {
|
||||||
switch (opt.type()) {
|
switch (opt.type()) {
|
||||||
|
@ -427,7 +439,7 @@ void load_opts(bool first_time_launch) {
|
||||||
case config::Option::Type::kInterframe:
|
case config::Option::Type::kInterframe:
|
||||||
case config::Option::Type::kRenderMethod:
|
case config::Option::Type::kRenderMethod:
|
||||||
case config::Option::Type::kAudioApi:
|
case config::Option::Type::kAudioApi:
|
||||||
case config::Option::Type::kSoundQuality: {
|
case config::Option::Type::kAudioRate: {
|
||||||
wxString temp;
|
wxString temp;
|
||||||
if (cfg->Read(opt.config_name(), &temp) && !temp.empty()) {
|
if (cfg->Read(opt.config_name(), &temp) && !temp.empty()) {
|
||||||
opt.SetEnumString(temp.MakeLower());
|
opt.SetEnumString(temp.MakeLower());
|
||||||
|
@ -638,7 +650,7 @@ void opt_set(const wxString& name, const wxString& val) {
|
||||||
case config::Option::Type::kInterframe:
|
case config::Option::Type::kInterframe:
|
||||||
case config::Option::Type::kRenderMethod:
|
case config::Option::Type::kRenderMethod:
|
||||||
case config::Option::Type::kAudioApi:
|
case config::Option::Type::kAudioApi:
|
||||||
case config::Option::Type::kSoundQuality:
|
case config::Option::Type::kAudioRate:
|
||||||
opt->SetEnumString(val);
|
opt->SetEnumString(val);
|
||||||
return;
|
return;
|
||||||
case config::Option::Type::kGbPalette:
|
case config::Option::Type::kGbPalette:
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#include "wx/config/game-control.h"
|
#include "wx/config/game-control.h"
|
||||||
#include "wx/config/shortcuts.h"
|
#include "wx/config/shortcuts.h"
|
||||||
#include "wx/config/user-input.h"
|
#include "wx/config/user-input.h"
|
||||||
#include "wx/wxhead.h"
|
|
||||||
|
|
||||||
// Forward declaration.
|
// Forward declaration.
|
||||||
class wxFileHistory;
|
class wxFileHistory;
|
||||||
|
@ -20,8 +19,6 @@ extern const std::map<config::GameControl, std::set<config::UserInput>>
|
||||||
|
|
||||||
extern struct opts_t {
|
extern struct opts_t {
|
||||||
opts_t();
|
opts_t();
|
||||||
// while I would normally put large objects in front to reduce gaps,
|
|
||||||
// I instead organized this by opts.cpp table order
|
|
||||||
|
|
||||||
/// Display
|
/// Display
|
||||||
wxVideoMode fs_mode;
|
wxVideoMode fs_mode;
|
||||||
|
@ -52,23 +49,7 @@ extern struct opts_t {
|
||||||
int max_scale = 0;
|
int max_scale = 0;
|
||||||
|
|
||||||
/// Sound
|
/// Sound
|
||||||
#ifdef __WXMSW__
|
|
||||||
int audio_api = AUD_XAUDIO2;
|
|
||||||
#else
|
|
||||||
int audio_api = AUD_OPENAL;
|
|
||||||
#endif
|
|
||||||
// 10 fixes stuttering on mac with openal, as opposed to 5
|
|
||||||
// also should be better for modern hardware in general
|
|
||||||
int audio_buffers = 10;
|
|
||||||
wxString audio_dev;
|
|
||||||
int sound_en = 0x30f; // soundSetEnable()
|
int sound_en = 0x30f; // soundSetEnable()
|
||||||
int gba_sound_filter = 50;
|
|
||||||
int gb_echo = 20;
|
|
||||||
bool dsound_hw_accel;
|
|
||||||
int gb_stereo = 15;
|
|
||||||
int sound_qual = 1; // soundSetSampleRate() / gbSoundSetSampleRate()
|
|
||||||
int sound_vol = 100; // soundSetVolume()
|
|
||||||
bool upmix = false; // xa2 only
|
|
||||||
|
|
||||||
/// Recent
|
/// Recent
|
||||||
wxFileHistory* recent = nullptr;
|
wxFileHistory* recent = nullptr;
|
||||||
|
|
131
src/wx/panel.cpp
131
src/wx/panel.cpp
|
@ -3,6 +3,8 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include "components/filters_agb/filters_agb.h"
|
#include "components/filters_agb/filters_agb.h"
|
||||||
#include "components/filters_interframe/interframe.h"
|
#include "components/filters_interframe/interframe.h"
|
||||||
|
#include "core/base/system.h"
|
||||||
|
#include "wx/config/option-id.h"
|
||||||
|
|
||||||
#ifdef __WXGTK__
|
#ifdef __WXGTK__
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
|
@ -94,6 +96,28 @@ double GetFilterScale() {
|
||||||
return 1.0;
|
return 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long GetSampleRate() {
|
||||||
|
switch (OPTION(kSoundAudioRate)) {
|
||||||
|
case config::AudioRate::k48kHz:
|
||||||
|
return 48000;
|
||||||
|
break;
|
||||||
|
case config::AudioRate::k44kHz:
|
||||||
|
return 44100;
|
||||||
|
break;
|
||||||
|
case config::AudioRate::k22kHz:
|
||||||
|
return 22050;
|
||||||
|
break;
|
||||||
|
case config::AudioRate::k11kHz:
|
||||||
|
return 11025;
|
||||||
|
break;
|
||||||
|
case config::AudioRate::kLast:
|
||||||
|
assert(false);
|
||||||
|
return 44100;
|
||||||
|
}
|
||||||
|
assert(false);
|
||||||
|
return 44100;
|
||||||
|
}
|
||||||
|
|
||||||
#define out_16 (systemColorDepth == 16)
|
#define out_16 (systemColorDepth == 16)
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -118,28 +142,29 @@ GameArea::GameArea()
|
||||||
paused(false),
|
paused(false),
|
||||||
pointer_blanked(false),
|
pointer_blanked(false),
|
||||||
mouse_active_time(0),
|
mouse_active_time(0),
|
||||||
render_observer_(
|
render_observer_({config::OptionID::kDispBilinear, config::OptionID::kDispFilter,
|
||||||
{config::OptionID::kDispBilinear, config::OptionID::kDispFilter,
|
config::OptionID::kDispRenderMethod, config::OptionID::kDispIFB,
|
||||||
config::OptionID::kDispRenderMethod, config::OptionID::kDispIFB,
|
config::OptionID::kDispStretch, config::OptionID::kPrefVsync},
|
||||||
config::OptionID::kDispStretch, config::OptionID::kPrefVsync},
|
std::bind(&GameArea::ResetPanel, this)),
|
||||||
std::bind(&GameArea::ResetPanel, this)),
|
scale_observer_(config::OptionID::kDispScale, std::bind(&GameArea::AdjustSize, this, true)),
|
||||||
scale_observer_(config::OptionID::kDispScale,
|
gb_border_observer_(config::OptionID::kPrefBorderOn,
|
||||||
std::bind(&GameArea::AdjustSize, this, true)),
|
std::bind(&GameArea::OnGBBorderChanged, this, std::placeholders::_1)),
|
||||||
gb_border_observer_(
|
gb_palette_observer_({config::OptionID::kGBPalette0, config::OptionID::kGBPalette1,
|
||||||
config::OptionID::kPrefBorderOn,
|
config::OptionID::kGBPalette2, config::OptionID::kPrefGBPaletteOption},
|
||||||
std::bind(&GameArea::OnGBBorderChanged, this, std::placeholders::_1)),
|
std::bind(&gbResetPalette)),
|
||||||
gb_palette_observer_(
|
gb_declick_observer_(
|
||||||
{config::OptionID::kGBPalette0, config::OptionID::kGBPalette1,
|
config::OptionID::kSoundGBDeclicking,
|
||||||
config::OptionID::kGBPalette2,
|
[&](config::Option* option) { gbSoundSetDeclicking(option->GetBool()); }),
|
||||||
config::OptionID::kPrefGBPaletteOption},
|
lcd_filters_observer_({config::OptionID::kGBLCDFilter, config::OptionID::kGBALCDFilter},
|
||||||
std::bind(&gbResetPalette)),
|
std::bind(&GameArea::UpdateLcdFilter, this)),
|
||||||
gb_declick_observer_(config::OptionID::kSoundGBDeclicking,
|
audio_rate_observer_(config::OptionID::kSoundAudioRate,
|
||||||
[&](config::Option* option) {
|
std::bind(&GameArea::OnAudioRateChanged, this)),
|
||||||
gbSoundSetDeclicking(option->GetBool());
|
audio_volume_observer_(config::OptionID::kSoundVolume,
|
||||||
}),
|
std::bind(&GameArea::OnVolumeChanged, this, std::placeholders::_1)),
|
||||||
lcd_filters_observer_(
|
audio_observer_({config::OptionID::kSoundAudioAPI, config::OptionID::kSoundAudioDevice,
|
||||||
{config::OptionID::kGBLCDFilter, config::OptionID::kGBALCDFilter},
|
config::OptionID::kSoundBuffers, config::OptionID::kSoundDSoundHWAccel,
|
||||||
std::bind(&GameArea::UpdateLcdFilter, this)) {
|
config::OptionID::kSoundUpmix},
|
||||||
|
[&](config::Option*) { schedule_audio_restart_ = true; }) {
|
||||||
SetSizer(new wxBoxSizer(wxVERTICAL));
|
SetSizer(new wxBoxSizer(wxVERTICAL));
|
||||||
// all renderers prefer 32-bit
|
// all renderers prefer 32-bit
|
||||||
// well, "simple" prefers 24-bit, but that's not available for filters
|
// well, "simple" prefers 24-bit, but that's not available for filters
|
||||||
|
@ -237,15 +262,15 @@ void GameArea::LoadGame(const wxString& name)
|
||||||
if (!pfn.IsFileReadable()) {
|
if (!pfn.IsFileReadable()) {
|
||||||
pfn.SetExt(wxT("ups"));
|
pfn.SetExt(wxT("ups"));
|
||||||
|
|
||||||
if (!pfn.IsFileReadable()) {
|
if (!pfn.IsFileReadable()) {
|
||||||
pfn.SetExt(wxT("bps"));
|
pfn.SetExt(wxT("bps"));
|
||||||
|
|
||||||
if (!pfn.IsFileReadable()) {
|
if (!pfn.IsFileReadable()) {
|
||||||
pfn.SetExt(wxT("ppf"));
|
pfn.SetExt(wxT("ppf"));
|
||||||
loadpatch = pfn.IsFileReadable();
|
loadpatch = pfn.IsFileReadable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (t == IMAGE_GB) {
|
if (t == IMAGE_GB) {
|
||||||
|
@ -264,14 +289,13 @@ void GameArea::LoadGame(const wxString& name)
|
||||||
// start sound; this must happen before CPU stuff
|
// start sound; this must happen before CPU stuff
|
||||||
gb_effects_config.enabled = OPTION(kSoundGBEnableEffects);
|
gb_effects_config.enabled = OPTION(kSoundGBEnableEffects);
|
||||||
gb_effects_config.surround = OPTION(kSoundGBSurround);
|
gb_effects_config.surround = OPTION(kSoundGBSurround);
|
||||||
gb_effects_config.echo = (float)gopts.gb_echo / 100.0;
|
gb_effects_config.echo = (float)OPTION(kSoundGBEcho) / 100.0;
|
||||||
gb_effects_config.stereo = (float)gopts.gb_stereo / 100.0;
|
gb_effects_config.stereo = (float)OPTION(kSoundGBStereo) / 100.0;
|
||||||
if (!soundInit()) {
|
if (!soundInit()) {
|
||||||
wxLogError(_("Could not initialize the sound driver!"));
|
wxLogError(_("Could not initialize the sound driver!"));
|
||||||
}
|
}
|
||||||
soundSetEnable(gopts.sound_en);
|
soundSetEnable(gopts.sound_en);
|
||||||
gbSoundSetSampleRate(!gopts.sound_qual ? 48000 : 44100 / (1 << (gopts.sound_qual - 1)));
|
gbSoundSetSampleRate(GetSampleRate());
|
||||||
soundSetVolume((float)gopts.sound_vol / 100.0);
|
|
||||||
// this **MUST** be called **AFTER** setting sample rate because the core calls soundInit()
|
// this **MUST** be called **AFTER** setting sample rate because the core calls soundInit()
|
||||||
soundSetThrottle(coreOptions.throttle);
|
soundSetThrottle(coreOptions.throttle);
|
||||||
gbGetHardwareType();
|
gbGetHardwareType();
|
||||||
|
@ -382,11 +406,10 @@ void GameArea::LoadGame(const wxString& name)
|
||||||
wxLogError(_("Could not initialize the sound driver!"));
|
wxLogError(_("Could not initialize the sound driver!"));
|
||||||
}
|
}
|
||||||
soundSetEnable(gopts.sound_en);
|
soundSetEnable(gopts.sound_en);
|
||||||
soundSetSampleRate(!gopts.sound_qual ? 48000 : 44100 / (1 << (gopts.sound_qual - 1)));
|
soundSetSampleRate(GetSampleRate());
|
||||||
soundSetVolume((float)gopts.sound_vol / 100.0);
|
|
||||||
// this **MUST** be called **AFTER** setting sample rate because the core calls soundInit()
|
// this **MUST** be called **AFTER** setting sample rate because the core calls soundInit()
|
||||||
soundSetThrottle(coreOptions.throttle);
|
soundSetThrottle(coreOptions.throttle);
|
||||||
soundFiltering = (float)gopts.gba_sound_filter / 100.0f;
|
soundFiltering = (float)OPTION(kSoundGBAFiltering) / 100.0f;
|
||||||
|
|
||||||
rtcEnableRumble(true);
|
rtcEnableRumble(true);
|
||||||
|
|
||||||
|
@ -415,6 +438,7 @@ void GameArea::LoadGame(const wxString& name)
|
||||||
AdjustSize(false);
|
AdjustSize(false);
|
||||||
emulating = true;
|
emulating = true;
|
||||||
was_paused = true;
|
was_paused = true;
|
||||||
|
schedule_audio_restart_ = false;
|
||||||
MainFrame* mf = wxGetApp().frame;
|
MainFrame* mf = wxGetApp().frame;
|
||||||
mf->StopJoyPollTimer();
|
mf->StopJoyPollTimer();
|
||||||
mf->SetJoystick();
|
mf->SetJoystick();
|
||||||
|
@ -1123,6 +1147,14 @@ void GameArea::OnIdle(wxIdleEvent& event)
|
||||||
if (!emusys)
|
if (!emusys)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (schedule_audio_restart_) {
|
||||||
|
soundShutdown();
|
||||||
|
if (!soundInit()) {
|
||||||
|
wxLogError(_("Could not initialize the sound driver!"));
|
||||||
|
}
|
||||||
|
schedule_audio_restart_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!panel) {
|
if (!panel) {
|
||||||
switch (OPTION(kDispRenderMethod)) {
|
switch (OPTION(kDispRenderMethod)) {
|
||||||
case config::RenderMethod::kSimple:
|
case config::RenderMethod::kSimple:
|
||||||
|
@ -2710,4 +2742,29 @@ void GameArea::UnsuspendScreenSaver() {
|
||||||
xscreensaver_suspended = false;
|
xscreensaver_suspended = false;
|
||||||
}
|
}
|
||||||
#endif // HAVE_XSS
|
#endif // HAVE_XSS
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameArea::OnAudioRateChanged() {
|
||||||
|
if (loaded == IMAGE_UNKNOWN) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (game_type()) {
|
||||||
|
case IMAGE_UNKNOWN:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IMAGE_GB:
|
||||||
|
gbSoundSetSampleRate(GetSampleRate());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IMAGE_GBA:
|
||||||
|
soundSetSampleRate(GetSampleRate());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameArea::OnVolumeChanged(config::Option* option) {
|
||||||
|
const int volume = option->GetInt();
|
||||||
|
soundSetVolume((float)volume / 100.0);
|
||||||
|
systemScreenMessage(wxString::Format(_("Volume: %d %%"), volume));
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
#include <wx/ffile.h>
|
#include <wx/ffile.h>
|
||||||
#include <wx/generic/prntdlgg.h>
|
#include <wx/generic/prntdlgg.h>
|
||||||
|
@ -6,13 +7,13 @@
|
||||||
#include <wx/printdlg.h>
|
#include <wx/printdlg.h>
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
|
||||||
#include "components/audio_sdl/audio_sdl.h"
|
|
||||||
#include "core/base/image_util.h"
|
#include "core/base/image_util.h"
|
||||||
#include "core/gb/gbGlobals.h"
|
#include "core/gb/gbGlobals.h"
|
||||||
#include "core/gba/gbaGlobals.h"
|
#include "core/gba/gbaGlobals.h"
|
||||||
#include "core/gba/gbaSound.h"
|
#include "core/gba/gbaSound.h"
|
||||||
#include "wx/config/game-control.h"
|
#include "wx/config/game-control.h"
|
||||||
#include "wx/config/option-proxy.h"
|
#include "wx/config/option-proxy.h"
|
||||||
|
#include "wx/config/option.h"
|
||||||
#include "wx/wxvbam.h"
|
#include "wx/wxvbam.h"
|
||||||
|
|
||||||
// These should probably be in vbamcore
|
// These should probably be in vbamcore
|
||||||
|
@ -1230,32 +1231,33 @@ SoundDriver* systemSoundInit()
|
||||||
{
|
{
|
||||||
soundShutdown();
|
soundShutdown();
|
||||||
|
|
||||||
switch (gopts.audio_api) {
|
switch (OPTION(kSoundAudioAPI)) {
|
||||||
case AUD_SDL:
|
case config::AudioApi::kOpenAL:
|
||||||
return new SoundSDL();
|
return newOpenAL();
|
||||||
|
|
||||||
case AUD_OPENAL:
|
#if defined(__WXMSW__)
|
||||||
return newOpenAL();
|
case config::AudioApi::kDirectSound:
|
||||||
#ifdef __WXMSW__
|
return newDirectSound();
|
||||||
|
|
||||||
case AUD_DIRECTSOUND:
|
|
||||||
return newDirectSound();
|
|
||||||
#ifndef NO_XAUDIO2
|
|
||||||
|
|
||||||
case AUD_XAUDIO2:
|
|
||||||
return newXAudio2_Output();
|
|
||||||
#endif
|
|
||||||
#ifndef NO_FAUDIO
|
|
||||||
case AUD_FAUDIO:
|
|
||||||
return newFAudio_Output();
|
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
default:
|
#if defined(VBAM_ENABLE_XAUDIO2)
|
||||||
gopts.audio_api = 0;
|
case config::AudioApi::kXAudio2:
|
||||||
|
return newXAudio2_Output();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(VBAM_ENABLE_FAUDIO)
|
||||||
|
case config::AudioApi::kFAudio:
|
||||||
|
return newFAudio_Output();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
case config::AudioApi::kLast:
|
||||||
|
// This should never happen.
|
||||||
|
assert(false);
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
assert(false);
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void systemOnWriteDataToSoundBuffer(const uint16_t* finalWave, int length)
|
void systemOnWriteDataToSoundBuffer(const uint16_t* finalWave, int length)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <wx/checkbox.h>
|
#include <wx/checkbox.h>
|
||||||
#include <wx/choice.h>
|
#include <wx/choice.h>
|
||||||
#include <wx/radiobut.h>
|
#include <wx/radiobut.h>
|
||||||
|
#include <wx/slider.h>
|
||||||
#include <wx/spinctrl.h>
|
#include <wx/spinctrl.h>
|
||||||
|
|
||||||
namespace widgets {
|
namespace widgets {
|
||||||
|
@ -90,30 +91,49 @@ bool OptionSelectedValidator::WriteToOption() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
OptionSpinCtrlValidator::OptionSpinCtrlValidator(config::OptionID option_id)
|
OptionIntValidator::OptionIntValidator(config::OptionID option_id)
|
||||||
: OptionValidator(option_id) {
|
: OptionValidator(option_id) {
|
||||||
assert(option()->is_int());
|
assert(option()->is_int());
|
||||||
}
|
}
|
||||||
|
|
||||||
wxObject* OptionSpinCtrlValidator::Clone() const {
|
wxObject* OptionIntValidator::Clone() const {
|
||||||
return new OptionSpinCtrlValidator(option()->id());
|
return new OptionIntValidator(option()->id());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OptionSpinCtrlValidator::IsWindowValueValid() {
|
bool OptionIntValidator::IsWindowValueValid() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OptionSpinCtrlValidator::WriteToWindow() {
|
bool OptionIntValidator::WriteToWindow() {
|
||||||
wxSpinCtrl* spin_ctrl = wxDynamicCast(GetWindow(), wxSpinCtrl);
|
wxSpinCtrl* spin_ctrl = wxDynamicCast(GetWindow(), wxSpinCtrl);
|
||||||
assert(spin_ctrl);
|
if (spin_ctrl) {
|
||||||
spin_ctrl->SetValue(option()->GetInt());
|
spin_ctrl->SetValue(option()->GetInt());
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxSlider* slider = wxDynamicCast(GetWindow(), wxSlider);
|
||||||
|
if (slider) {
|
||||||
|
slider->SetValue(option()->GetInt());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(false);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OptionSpinCtrlValidator::WriteToOption() {
|
bool OptionIntValidator::WriteToOption() {
|
||||||
const wxSpinCtrl* spin_ctrl = wxDynamicCast(GetWindow(), wxSpinCtrl);
|
const wxSpinCtrl* spin_ctrl = wxDynamicCast(GetWindow(), wxSpinCtrl);
|
||||||
assert(spin_ctrl);
|
if (spin_ctrl) {
|
||||||
return option()->SetInt(spin_ctrl->GetValue());
|
return option()->SetInt(spin_ctrl->GetValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
const wxSlider* slider = wxDynamicCast(GetWindow(), wxSlider);
|
||||||
|
if (slider) {
|
||||||
|
return option()->SetInt(slider->GetValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(false);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
OptionChoiceValidator::OptionChoiceValidator(config::OptionID option_id)
|
OptionChoiceValidator::OptionChoiceValidator(config::OptionID option_id)
|
||||||
|
@ -142,4 +162,30 @@ bool OptionChoiceValidator::WriteToOption() {
|
||||||
return option()->SetUnsigned(choice->GetSelection());
|
return option()->SetUnsigned(choice->GetSelection());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OptionBoolValidator::OptionBoolValidator(config::OptionID option_id)
|
||||||
|
: OptionValidator(option_id) {
|
||||||
|
assert(option()->is_bool());
|
||||||
|
}
|
||||||
|
|
||||||
|
wxObject* OptionBoolValidator::Clone() const {
|
||||||
|
return new OptionBoolValidator(option()->id());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OptionBoolValidator::IsWindowValueValid() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OptionBoolValidator::WriteToWindow() {
|
||||||
|
wxCheckBox* checkbox = wxDynamicCast(GetWindow(), wxCheckBox);
|
||||||
|
assert(checkbox);
|
||||||
|
checkbox->SetValue(option()->GetBool());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OptionBoolValidator::WriteToOption() {
|
||||||
|
const wxCheckBox* checkbox = wxDynamicCast(GetWindow(), wxCheckBox);
|
||||||
|
assert(checkbox);
|
||||||
|
return option()->SetBool(checkbox->GetValue());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace widgets
|
} // namespace widgets
|
||||||
|
|
|
@ -106,12 +106,12 @@ private:
|
||||||
const uint32_t value_;
|
const uint32_t value_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Validator for a wxSpinCtrl widget with a kInt Option. This will keep the
|
// Validator for a wxSpinCtrl or wxSlider widget with a kInt Option. This will
|
||||||
// kInt Option and the wxSpinCtrl selection in sync.
|
// keep the kInt Option and the wxSpinCtrl or wxSlider selection in sync.
|
||||||
class OptionSpinCtrlValidator : public OptionValidator {
|
class OptionIntValidator : public OptionValidator {
|
||||||
public:
|
public:
|
||||||
explicit OptionSpinCtrlValidator(config::OptionID option_id);
|
explicit OptionIntValidator(config::OptionID option_id);
|
||||||
~OptionSpinCtrlValidator() override = default;
|
~OptionIntValidator() override = default;
|
||||||
|
|
||||||
// Returns a copy of the object.
|
// Returns a copy of the object.
|
||||||
wxObject* Clone() const override;
|
wxObject* Clone() const override;
|
||||||
|
@ -123,7 +123,7 @@ private:
|
||||||
bool WriteToOption() override;
|
bool WriteToOption() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Validator for a wxChoice widget with a kUnsigned Option. This will keep the
|
// Validator for a wxChoice widget with a kUnsigned Option. This will keep the
|
||||||
// kUnsigned Option and the wxChoice selection in sync.
|
// kUnsigned Option and the wxChoice selection in sync.
|
||||||
class OptionChoiceValidator : public OptionValidator {
|
class OptionChoiceValidator : public OptionValidator {
|
||||||
public:
|
public:
|
||||||
|
@ -140,6 +140,23 @@ private:
|
||||||
bool WriteToOption() override;
|
bool WriteToOption() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Validator for a wxCheckBox widgets with a kBool Option. This will keep the
|
||||||
|
// kBool Option and the wxCheckBox selection in sync.
|
||||||
|
class OptionBoolValidator : public OptionValidator {
|
||||||
|
public:
|
||||||
|
explicit OptionBoolValidator(config::OptionID option_id);
|
||||||
|
~OptionBoolValidator() override = default;
|
||||||
|
|
||||||
|
// Returns a copy of the object.
|
||||||
|
wxObject* Clone() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// OptionValidator implementation.
|
||||||
|
bool IsWindowValueValid() override;
|
||||||
|
bool WriteToWindow() override;
|
||||||
|
bool WriteToOption() override;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace widgets
|
} // namespace widgets
|
||||||
|
|
||||||
#endif // VBAM_WX_WIDGETS_OPTION_VALIDATOR_H_
|
#endif // VBAM_WX_WIDGETS_OPTION_VALIDATOR_H_
|
||||||
|
|
|
@ -59,16 +59,6 @@ using std::int32_t;
|
||||||
|
|
||||||
#include "wx/wxutil.h"
|
#include "wx/wxutil.h"
|
||||||
|
|
||||||
// This enum must be kept in sync with the one in vbam-options-static.cpp.
|
|
||||||
// TODO: These 2 enums should be unified and a validator created for this enum.
|
|
||||||
enum audioapi {
|
|
||||||
AUD_SDL,
|
|
||||||
AUD_OPENAL,
|
|
||||||
AUD_DIRECTSOUND,
|
|
||||||
AUD_XAUDIO2,
|
|
||||||
AUD_FAUDIO
|
|
||||||
};
|
|
||||||
|
|
||||||
// wxrc helpers (for dynamic strings instead of constant)
|
// wxrc helpers (for dynamic strings instead of constant)
|
||||||
#define XRCID_D(str) wxXmlResource::GetXRCID(str)
|
#define XRCID_D(str) wxXmlResource::GetXRCID(str)
|
||||||
//#define XRCCTRL_D(win, id, type) (wxStaticCast((win).FindWindow(XRCID_D(id)), type))
|
//#define XRCCTRL_D(win, id, type) (wxStaticCast((win).FindWindow(XRCID_D(id)), type))
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
#include "wx/wxutil.h"
|
#include "wx/wxutil.h"
|
||||||
|
|
||||||
int getKeyboardKeyCode(const wxKeyEvent& event) {
|
int getKeyboardKeyCode(const wxKeyEvent& event) {
|
||||||
|
const int key_code = event.GetKeyCode();
|
||||||
|
if (key_code > WXK_START) {
|
||||||
|
return key_code;
|
||||||
|
}
|
||||||
int uc = event.GetUnicodeKey();
|
int uc = event.GetUnicodeKey();
|
||||||
if (uc != WXK_NONE) {
|
if (uc != WXK_NONE) {
|
||||||
if (uc < 32) { // not all control chars
|
if (uc < 32) { // not all control chars
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
#include "core/base/system.h"
|
#include "core/base/system.h"
|
||||||
#include "wx/config/option-observer.h"
|
#include "wx/config/option-observer.h"
|
||||||
|
#include "wx/config/option.h"
|
||||||
#include "wx/widgets/dpi-support.h"
|
#include "wx/widgets/dpi-support.h"
|
||||||
#include "wx/widgets/keep-on-top-styler.h"
|
#include "wx/widgets/keep-on-top-styler.h"
|
||||||
#include "wx/widgets/sdljoy.h"
|
#include "wx/widgets/sdljoy.h"
|
||||||
|
@ -616,12 +617,20 @@ protected:
|
||||||
DECLARE_EVENT_TABLE()
|
DECLARE_EVENT_TABLE()
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void OnAudioRateChanged();
|
||||||
|
void OnVolumeChanged(config::Option* option);
|
||||||
|
|
||||||
|
bool schedule_audio_restart_ = false;
|
||||||
|
|
||||||
const config::OptionsObserver render_observer_;
|
const config::OptionsObserver render_observer_;
|
||||||
const config::OptionsObserver scale_observer_;
|
const config::OptionsObserver scale_observer_;
|
||||||
const config::OptionsObserver gb_border_observer_;
|
const config::OptionsObserver gb_border_observer_;
|
||||||
const config::OptionsObserver gb_palette_observer_;
|
const config::OptionsObserver gb_palette_observer_;
|
||||||
const config::OptionsObserver gb_declick_observer_;
|
const config::OptionsObserver gb_declick_observer_;
|
||||||
const config::OptionsObserver lcd_filters_observer_;
|
const config::OptionsObserver lcd_filters_observer_;
|
||||||
|
const config::OptionsObserver audio_rate_observer_;
|
||||||
|
const config::OptionsObserver audio_volume_observer_;
|
||||||
|
const config::OptionsObserver audio_observer_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// wxString version of OSD message
|
// wxString version of OSD message
|
||||||
|
@ -704,25 +713,24 @@ private:
|
||||||
|
|
||||||
// I should add this to SoundDriver, but wxArrayString is wx-specific
|
// I should add this to SoundDriver, but wxArrayString is wx-specific
|
||||||
// I suppose I could make subclass wxSoundDriver. maybe later.
|
// I suppose I could make subclass wxSoundDriver. maybe later.
|
||||||
|
|
||||||
class SoundDriver;
|
class SoundDriver;
|
||||||
extern SoundDriver* newOpenAL();
|
extern SoundDriver* newOpenAL();
|
||||||
extern bool GetOALDevices(wxArrayString& names, wxArrayString& ids);
|
extern bool GetOALDevices(wxArrayString& names, wxArrayString& ids);
|
||||||
|
|
||||||
#ifdef __WXMSW__
|
#if defined(__WXMSW__)
|
||||||
extern SoundDriver* newDirectSound();
|
extern SoundDriver* newDirectSound();
|
||||||
extern bool GetDSDevices(wxArrayString& names, wxArrayString& ids);
|
extern bool GetDSDevices(wxArrayString& names, wxArrayString& ids);
|
||||||
|
#endif // defined(__WXMSW__)
|
||||||
|
|
||||||
#ifndef NO_XAUDIO2
|
#if defined(VBAM_ENABLE_XAUDIO2)
|
||||||
extern SoundDriver* newXAudio2_Output();
|
extern SoundDriver* newXAudio2_Output();
|
||||||
extern bool GetXA2Devices(wxArrayString& names, wxArrayString& ids);
|
extern bool GetXA2Devices(wxArrayString& names, wxArrayString& ids);
|
||||||
#endif
|
#endif // defined(VBAM_ENABLE_XAUDIO2)
|
||||||
|
|
||||||
#ifndef NO_FAUDIO
|
#if defined(VBAM_ENABLE_FAUDIO)
|
||||||
extern SoundDriver* newFAudio_Output();
|
extern SoundDriver* newFAudio_Output();
|
||||||
extern bool GetFADevices(wxArrayString& names, wxArrayString& ids);
|
extern bool GetFADevices(wxArrayString& names, wxArrayString& ids);
|
||||||
#endif
|
#endif // defined(VBAM_ENABLE_FAUDIO)
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(VBAM_ENABLE_DEBUGGER)
|
#if defined(VBAM_ENABLE_DEBUGGER)
|
||||||
extern bool debugger;
|
extern bool debugger;
|
||||||
|
|
|
@ -1,27 +1,29 @@
|
||||||
#ifndef NO_XAUDIO2
|
#if !defined(VBAM_ENABLE_XAUDIO2)
|
||||||
|
#error "This file should only be compiled if XAudio2 is enabled"
|
||||||
|
#endif
|
||||||
|
|
||||||
// Application
|
#include <cstdio>
|
||||||
#include "wx/wxvbam.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
// Interface
|
#include <string>
|
||||||
#include "core/base/sound_driver.h"
|
#include <vector>
|
||||||
|
|
||||||
|
// MMDevice API
|
||||||
|
#include <mmdeviceapi.h>
|
||||||
|
|
||||||
// XAudio2
|
|
||||||
#if _MSC_VER
|
#if _MSC_VER
|
||||||
#include <xaudio2.legacy.h>
|
#include <xaudio2.legacy.h>
|
||||||
#else
|
#else
|
||||||
#include <XAudio2.h>
|
#include <XAudio2.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// MMDevice API
|
#include <wx/arrstr.h>
|
||||||
#include <mmdeviceapi.h>
|
#include <wx/log.h>
|
||||||
#include <string>
|
#include <wx/translation.h>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
// Internals
|
#include "core/base/sound_driver.h"
|
||||||
#include "core/base/system.h" // for systemMessage()
|
#include "core/base/system.h" // for systemMessage()
|
||||||
#include "core/gba/gbaGlobals.h"
|
#include "core/gba/gbaGlobals.h"
|
||||||
|
#include "wx/config/option-proxy.h"
|
||||||
|
|
||||||
int GetXA2Devices(IXAudio2* xa, wxArrayString* names, wxArrayString* ids,
|
int GetXA2Devices(IXAudio2* xa, wxArrayString* names, wxArrayString* ids,
|
||||||
const wxString* match)
|
const wxString* match)
|
||||||
|
@ -72,10 +74,11 @@ bool GetXA2Devices(wxArrayString& names, wxArrayString& ids)
|
||||||
|
|
||||||
static int XA2GetDev(IXAudio2* xa)
|
static int XA2GetDev(IXAudio2* xa)
|
||||||
{
|
{
|
||||||
if (gopts.audio_dev.empty())
|
const wxString& audio_device = OPTION(kSoundAudioDevice);
|
||||||
|
if (audio_device.empty())
|
||||||
return 0;
|
return 0;
|
||||||
else {
|
else {
|
||||||
int ret = GetXA2Devices(xa, NULL, NULL, &gopts.audio_dev);
|
int ret = GetXA2Devices(xa, NULL, NULL, &audio_device);
|
||||||
return ret < 0 ? 0 : ret;
|
return ret < 0 ? 0 : ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -278,7 +281,7 @@ XAudio2_Output::XAudio2_Output()
|
||||||
initialized = false;
|
initialized = false;
|
||||||
playing = false;
|
playing = false;
|
||||||
freq = 0;
|
freq = 0;
|
||||||
bufferCount = gopts.audio_buffers;
|
bufferCount = OPTION(kSoundBuffers);
|
||||||
buffers = NULL;
|
buffers = NULL;
|
||||||
currentBuffer = 0;
|
currentBuffer = 0;
|
||||||
device_changed = false;
|
device_changed = false;
|
||||||
|
@ -386,7 +389,7 @@ bool XAudio2_Output::init(long sampleRate)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gopts.upmix) {
|
if (OPTION(kSoundUpmix)) {
|
||||||
// set up stereo upmixing
|
// set up stereo upmixing
|
||||||
XAUDIO2_DEVICE_DETAILS dd;
|
XAUDIO2_DEVICE_DETAILS dd;
|
||||||
ZeroMemory(&dd, sizeof(dd));
|
ZeroMemory(&dd, sizeof(dd));
|
||||||
|
@ -615,5 +618,3 @@ SoundDriver* newXAudio2_Output()
|
||||||
{
|
{
|
||||||
return new XAudio2_Output();
|
return new XAudio2_Output();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // #ifndef NO_XAUDIO2
|
|
||||||
|
|
|
@ -384,11 +384,9 @@
|
||||||
</object>
|
</object>
|
||||||
<object class="wxMenuItem" name="IncreaseVolume">
|
<object class="wxMenuItem" name="IncreaseVolume">
|
||||||
<label>_Increase volume</label>
|
<label>_Increase volume</label>
|
||||||
<enabled>0</enabled>
|
|
||||||
</object>
|
</object>
|
||||||
<object class="wxMenuItem" name="DecreaseVolume">
|
<object class="wxMenuItem" name="DecreaseVolume">
|
||||||
<label>_Decrease volume</label>
|
<label>_Decrease volume</label>
|
||||||
<enabled>0</enabled>
|
|
||||||
</object>
|
</object>
|
||||||
<object class="wxMenuItem" name="ToggleSound">
|
<object class="wxMenuItem" name="ToggleSound">
|
||||||
<label>_Toggle sound</label>
|
<label>_Toggle sound</label>
|
||||||
|
|
|
@ -100,14 +100,6 @@
|
||||||
<object class="wxStaticBoxSizer">
|
<object class="wxStaticBoxSizer">
|
||||||
<object class="sizeritem">
|
<object class="sizeritem">
|
||||||
<object class="wxBoxSizer">
|
<object class="wxBoxSizer">
|
||||||
<object class="sizeritem">
|
|
||||||
<object class="wxRadioButton" name="SDL">
|
|
||||||
<label translate="0">SDL</label>
|
|
||||||
<style>wxRB_GROUP</style>
|
|
||||||
</object>
|
|
||||||
<flag>wxALL|wxEXPAND</flag>
|
|
||||||
<border>5</border>
|
|
||||||
</object>
|
|
||||||
<object class="sizeritem">
|
<object class="sizeritem">
|
||||||
<object class="wxRadioButton" name="OpenAL">
|
<object class="wxRadioButton" name="OpenAL">
|
||||||
<label translate="0">OpenAL</label>
|
<label translate="0">OpenAL</label>
|
||||||
|
|
Loading…
Reference in New Issue