From 05572a0921b13d8f554113463d47815da66be937 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Sun, 24 Dec 2023 01:53:49 +0100 Subject: [PATCH 001/222] FreeType: Calculate font size correctly depending on whether it is a pixel or a true type font. Fix small rendering issues. Fix #2953 --- src/font.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/font.cpp b/src/font.cpp index 415e53ad5..e52c43366 100644 --- a/src/font.cpp +++ b/src/font.cpp @@ -535,7 +535,7 @@ void FTFont::SetSize(int height, bool create) { hb_ft_font_set_funcs(hb_font); #endif - baseline_offset = FT_MulFix(face->ascender, face->size->metrics.y_scale) / 64; + baseline_offset = static_cast(FT_MulFix(face->ascender, face->size->metrics.y_scale) / 64); if (baseline_offset == 0) { // FIXME: Becomes 0 for FON files. How is the baseline calculated for them? baseline_offset = static_cast(height * (10.0 / 12.0)); From 8f3d3c3c172b995e095b98ec9a08bcf4ab49a09c Mon Sep 17 00:00:00 2001 From: Ghabry Date: Fri, 1 Dec 2023 16:24:22 +0100 Subject: [PATCH 002/222] MIDI: Report the device status --- src/audio_generic.cpp | 3 ++- src/audio_generic_midiout.cpp | 22 ++++++++++++---- src/audio_generic_midiout.h | 4 +-- src/audio_midi.cpp | 26 ++++++++++++++----- src/audio_midi.h | 16 ++++++++++++ src/decoder_fluidsynth.cpp | 12 ++++----- src/decoder_fluidsynth.h | 2 +- src/decoder_wildmidi.cpp | 9 ++++--- src/decoder_wildmidi.h | 2 +- src/platform/libretro/midiout_device.cpp | 6 ++--- src/platform/libretro/midiout_device.h | 2 +- src/platform/linux/midiout_device_alsa.cpp | 14 +++++----- src/platform/linux/midiout_device_alsa.h | 2 +- .../macos/midiout_device_coreaudio.cpp | 6 ++--- src/platform/macos/midiout_device_coreaudio.h | 2 +- src/platform/windows/midiout_device_win32.cpp | 4 +-- src/platform/windows/midiout_device_win32.h | 2 +- 17 files changed, 89 insertions(+), 45 deletions(-) diff --git a/src/audio_generic.cpp b/src/audio_generic.cpp index 1135064b9..48511672a 100644 --- a/src/audio_generic.cpp +++ b/src/audio_generic.cpp @@ -223,7 +223,8 @@ bool GenericAudio::PlayOnChannel(BgmChannel& chan, Filesystem_Stream::InputStrea if (!MidiDecoder::CreateFluidsynth(true) && !MidiDecoder::CreateWildMidi(true)) { if (!midi_thread) { midi_thread = std::make_unique(); - if (midi_thread->IsInitialized()) { + std::string status_message; + if (midi_thread->IsInitialized(status_message)) { midi_thread->StartThread(); } else { midi_thread.reset(); diff --git a/src/audio_generic_midiout.cpp b/src/audio_generic_midiout.cpp index 31911e414..7f75e22c2 100644 --- a/src/audio_generic_midiout.cpp +++ b/src/audio_generic_midiout.cpp @@ -23,6 +23,7 @@ #include #include "filesystem_stream.h" #include "game_clock.h" +#include "output.h" #ifdef USE_LIBRETRO #include "platform/libretro/midiout_device.h" @@ -43,18 +44,21 @@ static struct { bool alsa = true; bool win32 = true; bool coreaudio = true; + std::string status; } works; GenericAudioMidiOut::GenericAudioMidiOut() { stop_thread.store(false); #ifdef USE_LIBRETRO + std::string libretro_status; if (works.libretro) { - auto dec = std::make_unique(); + auto dec = std::make_unique(libretro_status); if (dec->IsInitialized()) { midi_out = std::make_unique(std::move(dec)); } else { works.libretro = false; + Output::Debug(libretro_status); } } @@ -65,32 +69,39 @@ GenericAudioMidiOut::GenericAudioMidiOut() { #ifdef HAVE_ALSA if (works.alsa) { - auto dec = std::make_unique(); + auto dec = std::make_unique(works.status); if (dec->IsInitialized()) { midi_out = std::make_unique(std::move(dec)); } else { works.alsa = false; + Output::Debug(works.status); } } #elif _WIN32 if (works.win32) { - auto dec = std::make_unique(); + auto dec = std::make_unique(works.status); if (dec->IsInitialized()) { midi_out = std::make_unique(std::move(dec)); } else { works.win32 = false; + Output::Debug(works.status); } } #elif __APPLE__ if (works.coreaudio) { - auto dec = std::make_unique(); + auto dec = std::make_unique(works.status); if (dec->IsInitialized()) { midi_out = std::make_unique(std::move(dec)); } else { works.coreaudio = false; + Output::Debug(works.status); } } #endif + +#ifdef USE_LIBRETRO + works.status = libretro_status + ". " + works.status; +#endif } GenericAudioMidiOut::~GenericAudioMidiOut() { @@ -145,7 +156,8 @@ void GenericAudioMidiOut::ThreadFunction() { } } -bool GenericAudioMidiOut::IsInitialized() const { +bool GenericAudioMidiOut::IsInitialized(std::string& status_message) const { + status_message = works.status; return midi_out != nullptr; } diff --git a/src/audio_generic_midiout.h b/src/audio_generic_midiout.h index f9538a3bf..1f252bde5 100644 --- a/src/audio_generic_midiout.h +++ b/src/audio_generic_midiout.h @@ -52,7 +52,7 @@ class GenericAudioMidiOut final { void StartThread(); void StopThread(); void ThreadFunction(); - bool IsInitialized() const; + bool IsInitialized(std::string& status_message) const; static bool IsSupported(Filesystem_Stream::InputStream& stream); private: @@ -79,7 +79,7 @@ class GenericAudioMidiOut final { void StartThread() {}; void StopThread() {}; - bool IsInitialized() const { + bool IsInitialized(std::string&) const { return false; } diff --git a/src/audio_midi.cpp b/src/audio_midi.cpp index 011fb480b..c03dd2948 100644 --- a/src/audio_midi.cpp +++ b/src/audio_midi.cpp @@ -43,6 +43,8 @@ bool MidiDecoder::SetFormat(int frequency, AudioDecoderBase::Format format, int static struct { bool fluidsynth = true; bool wildmidi = true; + std::string fluidsynth_status; + std::string wildmidi_status; } works; std::unique_ptr MidiDecoder::Create(bool resample) { @@ -63,13 +65,12 @@ std::unique_ptr MidiDecoder::CreateFluidsynth(bool resample) { std::unique_ptr mididec; #if defined(HAVE_FLUIDSYNTH) || defined(HAVE_FLUIDLITE) - std::string error_message; - if (works.fluidsynth && FluidSynthDecoder::Initialize(error_message)) { + if (works.fluidsynth && FluidSynthDecoder::Initialize(works.fluidsynth_status)) { auto dec = std::make_unique(); mididec = std::make_unique(std::move(dec)); } else if (!mididec && works.fluidsynth) { - Output::Debug("{}", error_message); + Output::Debug("Fluidsynth: {}", works.fluidsynth_status); works.fluidsynth = false; } #endif @@ -87,13 +88,12 @@ std::unique_ptr MidiDecoder::CreateWildMidi(bool resample) { std::unique_ptr mididec; #ifdef HAVE_LIBWILDMIDI - std::string error_message; - if (!mididec && works.wildmidi && WildMidiDecoder::Initialize(error_message)) { + if (!mididec && works.wildmidi && WildMidiDecoder::Initialize(works.wildmidi_status)) { auto dec = std::make_unique(); mididec = std::make_unique(std::move(dec)); } else if (!mididec && works.wildmidi) { - Output::Debug("{}", error_message); + Output::Debug("WildMidi: {}", works.wildmidi_status); works.wildmidi = false; } #endif @@ -126,6 +126,20 @@ std::unique_ptr MidiDecoder::CreateFmMidi(bool resample) { return mididec; } +bool MidiDecoder::CheckFluidsynth(std::string& status_message) { + CreateFluidsynth(true); + + status_message = works.fluidsynth_status; + return works.fluidsynth; +} + +bool MidiDecoder::CheckWildMidi(std::string &status_message) { + CreateWildMidi(true); + + status_message = works.wildmidi_status; + return works.wildmidi; +} + void MidiDecoder::Reset() { works.fluidsynth = true; works.wildmidi = true; diff --git a/src/audio_midi.h b/src/audio_midi.h index 82a70635e..39ca9fa2c 100644 --- a/src/audio_midi.h +++ b/src/audio_midi.h @@ -204,6 +204,22 @@ class MidiDecoder { static std::unique_ptr CreateFmMidi(bool resample); + /** + * Checks if Fluidsynth works. + * + * @param status_message Current Fluidsynth status + * @return true: Works, false: Not working + */ + static bool CheckFluidsynth(std::string& status_message); + + /** + * Checks if WildMidi works. + * + * @param status_message Current Fluidsynth status + * @return true: Works, false: Not working + */ + static bool CheckWildMidi(std::string& status_message); + /** * Resets the global state of the midi libraries. */ diff --git a/src/decoder_fluidsynth.cpp b/src/decoder_fluidsynth.cpp index 2c6506d02..3f363d110 100644 --- a/src/decoder_fluidsynth.cpp +++ b/src/decoder_fluidsynth.cpp @@ -119,10 +119,10 @@ namespace { int instances = 0; } -static fluid_synth_t* create_synth(std::string& error_message) { +static fluid_synth_t* create_synth(std::string& status_message) { fluid_synth_t* syn = new_fluid_synth(global_settings.get()); if (!syn) { - error_message = "new_fluid_synth failed"; + status_message = "new_fluid_synth failed"; return nullptr; } @@ -168,13 +168,13 @@ static fluid_synth_t* create_synth(std::string& error_message) { for (const auto& sf_name: sf_paths) { if (fluid_synth_sfload(syn, sf_name.c_str(), 1) != FLUID_FAILED) { sf_load_success = true; - Output::Debug("Fluidsynth: Using soundfont {}", sf_name); + status_message = fmt::format("Using soundfont {}", sf_name); break; } } if (!sf_load_success) { - error_message = "Fluidsynth: Could not load soundfont."; + status_message = "Could not load soundfont."; return nullptr; } @@ -210,7 +210,7 @@ FluidSynthDecoder::~FluidSynthDecoder() { } } -bool FluidSynthDecoder::Initialize(std::string& error_message) { +bool FluidSynthDecoder::Initialize(std::string& status_message) { // only initialize once until a new game starts if (once) return init; @@ -247,7 +247,7 @@ bool FluidSynthDecoder::Initialize(std::string& error_message) { vio_open, vio_read, vio_seek, vio_tell, vio_close); #endif - global_synth.reset(create_synth(error_message)); + global_synth.reset(create_synth(status_message)); if (!global_synth) { return false; } diff --git a/src/decoder_fluidsynth.h b/src/decoder_fluidsynth.h index f0799413e..1eb6fe27b 100644 --- a/src/decoder_fluidsynth.h +++ b/src/decoder_fluidsynth.h @@ -44,7 +44,7 @@ class FluidSynthDecoder : public MidiDecoder { FluidSynthDecoder(); ~FluidSynthDecoder() override; - static bool Initialize(std::string& error_message); + static bool Initialize(std::string& status_message); static void ResetState(); /** diff --git a/src/decoder_wildmidi.cpp b/src/decoder_wildmidi.cpp index d4738e961..a4d00cd7c 100644 --- a/src/decoder_wildmidi.cpp +++ b/src/decoder_wildmidi.cpp @@ -84,7 +84,7 @@ WildMidiDecoder::~WildMidiDecoder() { WildMidi_Close(handle); } -bool WildMidiDecoder::Initialize(std::string& error_message) { +bool WildMidiDecoder::Initialize(std::string& status_message) { std::string config_file; bool found = false; @@ -282,10 +282,11 @@ bool WildMidiDecoder::Initialize(std::string& error_message) { // bail, if nothing found if (!found) { - error_message = "WildMidi: Could not find configuration file."; + status_message = "Could not find configuration file."; return false; } - Output::Debug("WildMidi: Using {} as configuration file...", config_file); + + status_message = fmt::format("Using {} as configuration file...", config_file); #if LIBWILDMIDI_VERSION >= 1027 // at least 0.4.3 init = (WildMidi_InitVIO(&vio, config_file.c_str(), EP_MIDI_FREQ, WILDMIDI_OPTS) == 0); @@ -294,7 +295,7 @@ bool WildMidiDecoder::Initialize(std::string& error_message) { #endif if (!init) { - error_message = std::string("WildMidi_Init() failed : ") + WildMidi_GetError(); + status_message = std::string("WildMidi_Init() failed: ") + WildMidi_GetError(); return false; } diff --git a/src/decoder_wildmidi.h b/src/decoder_wildmidi.h index 4142b3e09..8c32e60f5 100644 --- a/src/decoder_wildmidi.h +++ b/src/decoder_wildmidi.h @@ -33,7 +33,7 @@ class WildMidiDecoder : public MidiDecoder { public: ~WildMidiDecoder(); - static bool Initialize(std::string& error_message); + static bool Initialize(std::string& status_message); static void ResetState(); // Audio Decoder interface diff --git a/src/platform/libretro/midiout_device.cpp b/src/platform/libretro/midiout_device.cpp index 13960a360..48a800696 100644 --- a/src/platform/libretro/midiout_device.cpp +++ b/src/platform/libretro/midiout_device.cpp @@ -19,14 +19,14 @@ #include "ui.h" #include "output.h" -LibretroMidiOutDevice::LibretroMidiOutDevice() { +LibretroMidiOutDevice::LibretroMidiOutDevice(std::string& status_message) { if (!LibretroUi::environ_cb(RETRO_ENVIRONMENT_GET_MIDI_INTERFACE, &midi_out)) { - Output::Debug("libretro: GET_MIDI_INTERFACE unsupported"); + status_message = "libretro: GET_MIDI_INTERFACE unsupported"; return; } if (!midi_out.output_enabled()) { - Output::Debug("libretro: MIDI output not enabled"); + status_message = "libretro: MIDI output not enabled"; return; } diff --git a/src/platform/libretro/midiout_device.h b/src/platform/libretro/midiout_device.h index fa3c7fae4..62a87b9f1 100644 --- a/src/platform/libretro/midiout_device.h +++ b/src/platform/libretro/midiout_device.h @@ -27,7 +27,7 @@ */ class LibretroMidiOutDevice : public MidiDecoder { public: - LibretroMidiOutDevice(); + LibretroMidiOutDevice(std::string& status_message); void SendMidiMessage(uint32_t message) override; void SendSysExMessage(const uint8_t* data, size_t size) override; diff --git a/src/platform/linux/midiout_device_alsa.cpp b/src/platform/linux/midiout_device_alsa.cpp index e444a0825..9ed75b590 100644 --- a/src/platform/linux/midiout_device_alsa.cpp +++ b/src/platform/linux/midiout_device_alsa.cpp @@ -19,10 +19,10 @@ #include "output.h" #include "system.h" -AlsaMidiOutDevice::AlsaMidiOutDevice() { +AlsaMidiOutDevice::AlsaMidiOutDevice(std::string& status_message) { int status = snd_seq_open(&midi_out, "default", SND_SEQ_OPEN_DUPLEX, 0); if (status < 0) { - Output::Debug("ALSA MIDI: snd_seq_open failed: {}", snd_strerror(status)); + status_message = fmt::format("ALSA MIDI: snd_seq_open failed: {}", snd_strerror(status)); return; } @@ -75,7 +75,7 @@ AlsaMidiOutDevice::AlsaMidiOutDevice() { done:; if (!candidate_found) { - Output::Debug("ALSA MIDI: No suitable client found"); + status_message = "ALSA MIDI: No suitable client found"; return; } @@ -84,7 +84,7 @@ AlsaMidiOutDevice::AlsaMidiOutDevice() { status = snd_seq_create_simple_port(midi_out, "Harmony", SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE, SND_SEQ_PORT_TYPE_APPLICATION); if (status < 0) { - Output::Debug("ALSA MIDI: snd_seq_create_simple_port failed: {}", snd_strerror(status)); + status_message = fmt::format("ALSA MIDI: snd_seq_create_simple_port failed: {}", snd_strerror(status)); return; } @@ -92,19 +92,19 @@ AlsaMidiOutDevice::AlsaMidiOutDevice() { status = snd_seq_connect_to(midi_out, 0, dst_client, dst_port); if (status < 0) { - Output::Debug("ALSA MIDI: snd_seq_connect_to failed: {}", snd_strerror(status)); + status_message = fmt::format("ALSA MIDI: snd_seq_connect_to failed: {}", snd_strerror(status)); return; } queue = snd_seq_alloc_named_queue(midi_out, GAME_TITLE); if (queue < 0) { - Output::Debug("ALSA MIDI: snd_seq_connect_to failed: {}", snd_strerror(queue)); + status_message = fmt::format("ALSA MIDI: snd_seq_connect_to failed: {}", snd_strerror(queue)); return; } status = snd_seq_start_queue(midi_out, queue, nullptr); if (status < 0) { - Output::Debug("ALSA MIDI: snd_seq_connect_to failed: {}", snd_strerror(status)); + status_message = fmt::format("ALSA MIDI: snd_seq_connect_to failed: {}", snd_strerror(status)); return; } diff --git a/src/platform/linux/midiout_device_alsa.h b/src/platform/linux/midiout_device_alsa.h index 7a6dffbc2..6a88d32c6 100644 --- a/src/platform/linux/midiout_device_alsa.h +++ b/src/platform/linux/midiout_device_alsa.h @@ -27,7 +27,7 @@ */ class AlsaMidiOutDevice : public MidiDecoder { public: - AlsaMidiOutDevice(); + AlsaMidiOutDevice(std::string& status_message); ~AlsaMidiOutDevice(); void SendMidiMessage(uint32_t message) override; diff --git a/src/platform/macos/midiout_device_coreaudio.cpp b/src/platform/macos/midiout_device_coreaudio.cpp index 98ee57b8d..4bb86fc98 100644 --- a/src/platform/macos/midiout_device_coreaudio.cpp +++ b/src/platform/macos/midiout_device_coreaudio.cpp @@ -19,10 +19,10 @@ #include "midiout_device_coreaudio.h" #include "output.h" -CoreAudioMidiOutDevice::CoreAudioMidiOutDevice() { +CoreAudioMidiOutDevice::CoreAudioMidiOutDevice(std::string& status_message) { OSStatus status = NewAUGraph(&graph); if (status != noErr) { - Output::Debug("macOS Midi: NewAUGraph failed: {}", status); + status_message = fmt::format("macOS Midi: NewAUGraph failed: {}", status); return; } AudioComponentDescription synthDesc = { @@ -70,7 +70,7 @@ CoreAudioMidiOutDevice::CoreAudioMidiOutDevice() { status = AUGraphStart(graph); if (status != noErr) { - Output::Debug("macOS Midi: AUGraphStart failed: {}", status); + status_message = fmt::format("macOS Midi: AUGraphStart failed: {}", status); return; } diff --git a/src/platform/macos/midiout_device_coreaudio.h b/src/platform/macos/midiout_device_coreaudio.h index dbcf40f5d..6880cfba0 100644 --- a/src/platform/macos/midiout_device_coreaudio.h +++ b/src/platform/macos/midiout_device_coreaudio.h @@ -26,7 +26,7 @@ */ class CoreAudioMidiOutDevice : public MidiDecoder { public: - CoreAudioMidiOutDevice(); + CoreAudioMidiOutDevice(std::string& status_message); ~CoreAudioMidiOutDevice(); void SendMidiMessage(uint32_t message) override; diff --git a/src/platform/windows/midiout_device_win32.cpp b/src/platform/windows/midiout_device_win32.cpp index 9a231b16e..ec005c239 100644 --- a/src/platform/windows/midiout_device_win32.cpp +++ b/src/platform/windows/midiout_device_win32.cpp @@ -25,7 +25,7 @@ static std::string get_error_str(MMRESULT res) { return std::string(errMsg); } -Win32MidiOutDevice::Win32MidiOutDevice() { +Win32MidiOutDevice::Win32MidiOutDevice(std::string& status_message) { // TODO: Windows MIDI Mapper was removed in Windows 8. // This means it's impossible to change the default ("0") MIDI device // without third party software. We should allow specifying the MIDI device @@ -34,7 +34,7 @@ Win32MidiOutDevice::Win32MidiOutDevice() { MMRESULT err = midiOutOpen(&midi_out, device_id, 0, 0, CALLBACK_NULL); if (err != MMSYSERR_NOERROR) { - Output::Debug("Win32 midiOutOpen {} failed: ({}) {}", 0, err, get_error_str(err)); + status_message = fmt::format("Win32 midiOutOpen {} failed: ({}) {}", 0, err, get_error_str(err)); midi_out = nullptr; return; } diff --git a/src/platform/windows/midiout_device_win32.h b/src/platform/windows/midiout_device_win32.h index 2b96fd265..d581c481a 100644 --- a/src/platform/windows/midiout_device_win32.h +++ b/src/platform/windows/midiout_device_win32.h @@ -31,7 +31,7 @@ */ class Win32MidiOutDevice : public MidiDecoder { public: - Win32MidiOutDevice(); + Win32MidiOutDevice(std::string& status_message); ~Win32MidiOutDevice(); void SendMidiMessage(uint32_t message) override; From bddc3c5cfa9bf767912e49baf9aba545ecf9345d Mon Sep 17 00:00:00 2001 From: Ghabry Date: Sat, 2 Dec 2023 00:53:40 +0100 Subject: [PATCH 003/222] Settings: Add configuring MIDI devices --- src/audio.cpp | 40 ++++++++++++++++++++ src/audio.h | 15 ++++++++ src/audio_generic.cpp | 9 +++-- src/audio_midi.cpp | 24 ++++++++---- src/config_param.h | 3 +- src/decoder_fluidsynth.cpp | 8 +--- src/decoder_fluidsynth.h | 8 ---- src/game_config.cpp | 17 +++++++++ src/game_config.h | 5 +++ src/player.cpp | 16 ++------ src/scene_settings.cpp | 7 +++- src/scene_settings.h | 1 + src/window_settings.cpp | 75 ++++++++++++++++++++++++++++++++++---- src/window_settings.h | 5 +++ 14 files changed, 186 insertions(+), 47 deletions(-) diff --git a/src/audio.cpp b/src/audio.cpp index 0ce51b1f0..1448736ce 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -89,3 +89,43 @@ int AudioInterface::SE_GetGlobalVolume() const { void AudioInterface::SE_SetGlobalVolume(int volume) { cfg.sound_volume.Set(volume); } + +bool AudioInterface::GetFluidsynthEnabled() const { + return cfg.fluidsynth_midi.Get(); +} + +void AudioInterface::SetFluidsynthEnabled(bool enable) { + cfg.fluidsynth_midi.Set(enable); +} + +bool AudioInterface::GetWildMidiEnabled() const { + return cfg.wildmidi_midi.Get(); +} + +void AudioInterface::SetWildMidiEnabled(bool enable) { + cfg.wildmidi_midi.Set(enable); +} + +bool AudioInterface::GetNativeMidiEnabled() const { + return cfg.native_midi.Get(); +} + +void AudioInterface::SetNativeMidiEnabled(bool enable) { + cfg.native_midi.Set(enable); +} + +bool AudioInterface::GetFmMidiEnabled() const { + return cfg.fmmidi_midi.Get(); +} + +void AudioInterface::SetFmMidiEnabled(bool enable) { + cfg.fmmidi_midi.Set(enable); +} + +std::string AudioInterface::GetFluidsynthSoundfont() const { + return cfg.soundfont.Get(); +} + +void AudioInterface::SetFluidsynthSoundfont(StringView sf) { + cfg.soundfont.Set(ToString(sf)); +} diff --git a/src/audio.h b/src/audio.h index bb0e6b7ab..43ca569d4 100644 --- a/src/audio.h +++ b/src/audio.h @@ -133,6 +133,21 @@ struct AudioInterface { int SE_GetGlobalVolume() const; void SE_SetGlobalVolume(int volume); + bool GetFluidsynthEnabled() const; + void SetFluidsynthEnabled(bool enable); + + bool GetWildMidiEnabled() const; + void SetWildMidiEnabled(bool enable); + + bool GetNativeMidiEnabled() const; + void SetNativeMidiEnabled(bool enable); + + bool GetFmMidiEnabled() const; + void SetFmMidiEnabled(bool enable); + + std::string GetFluidsynthSoundfont() const; + void SetFluidsynthSoundfont(StringView sf); + protected: Game_ConfigAudio cfg; }; diff --git a/src/audio_generic.cpp b/src/audio_generic.cpp index 48511672a..cd45e13db 100644 --- a/src/audio_generic.cpp +++ b/src/audio_generic.cpp @@ -217,10 +217,11 @@ bool GenericAudio::PlayOnChannel(BgmChannel& chan, Filesystem_Stream::InputStrea if (chan.id == 0 && GenericAudioMidiOut::IsSupported(filestream)) { chan.decoder.reset(); - // FIXME: Try Fluidsynth and WildMidi first - // If they work fallback to the normal AudioDecoder handler below - // There should be a way to configure the order - if (!MidiDecoder::CreateFluidsynth(true) && !MidiDecoder::CreateWildMidi(true)) { + // Order is Fluidsynth, WildMidi, Native, FmMidi + bool fluidsynth = Audio().GetFluidsynthEnabled() && MidiDecoder::CreateFluidsynth(true); + bool wildmidi = Audio().GetWildMidiEnabled() && MidiDecoder::CreateWildMidi(true); + + if (!fluidsynth && !wildmidi && Audio().GetNativeMidiEnabled()) { if (!midi_thread) { midi_thread = std::make_unique(); std::string status_message; diff --git a/src/audio_midi.cpp b/src/audio_midi.cpp index c03dd2948..a21b4603a 100644 --- a/src/audio_midi.cpp +++ b/src/audio_midi.cpp @@ -25,6 +25,8 @@ #ifdef USE_AUDIO_RESAMPLER #include "audio_resampler.h" +#include "audio.h" + #endif void MidiDecoder::GetFormat(int& freq, AudioDecoderBase::Format& format, int& channels) const { @@ -50,12 +52,16 @@ static struct { std::unique_ptr MidiDecoder::Create(bool resample) { std::unique_ptr mididec; - mididec = CreateFluidsynth(resample); - if (!mididec) { + if (Audio().GetFluidsynthEnabled()) { + mididec = CreateFluidsynth(resample); + } + + if (!mididec && Audio().GetWildMidiEnabled()) { mididec = CreateWildMidi(resample); - if (!mididec) { - mididec = CreateFmMidi(resample); - } + } + + if (!mididec && Audio().GetFmMidiEnabled()) { + mididec = CreateFmMidi(resample); } return mididec; @@ -127,14 +133,18 @@ std::unique_ptr MidiDecoder::CreateFmMidi(bool resample) { } bool MidiDecoder::CheckFluidsynth(std::string& status_message) { - CreateFluidsynth(true); + if (works.fluidsynth && works.fluidsynth_status.empty()) { + CreateFluidsynth(true); + } status_message = works.fluidsynth_status; return works.fluidsynth; } bool MidiDecoder::CheckWildMidi(std::string &status_message) { - CreateWildMidi(true); + if (works.wildmidi && works.wildmidi_status.empty()) { + CreateWildMidi(true); + } status_message = works.wildmidi_status; return works.wildmidi; diff --git a/src/config_param.h b/src/config_param.h index 036503eb1..777b2144c 100644 --- a/src/config_param.h +++ b/src/config_param.h @@ -337,12 +337,13 @@ class BoolConfigParam : public ConfigParamBase { return true; } - void Toggle() { + bool Toggle() { if (Get()) { Set(false); } else { Set(true); } + return Get(); } std::string ValueToString() const override { diff --git a/src/decoder_fluidsynth.cpp b/src/decoder_fluidsynth.cpp index 3f363d110..9fb8444a4 100644 --- a/src/decoder_fluidsynth.cpp +++ b/src/decoder_fluidsynth.cpp @@ -16,6 +16,7 @@ */ #include "system.h" +#include "audio.h" #include "decoder_fluidsynth.h" #if defined(HAVE_FLUIDSYNTH) || defined(HAVE_FLUIDLITE) @@ -94,8 +95,6 @@ static fluid_fileapi_t fluidlite_vio = { #endif namespace { - std::string preferred_soundfont; - bool once = false; bool init = false; } @@ -132,6 +131,7 @@ static fluid_synth_t* create_synth(std::string& status_message) { // Attempt loading a soundfont std::vector sf_paths; + std::string preferred_soundfont = Audio().GetFluidsynthSoundfont(); if (!preferred_soundfont.empty()) { sf_paths.emplace_back(preferred_soundfont); } @@ -265,10 +265,6 @@ void FluidSynthDecoder::ResetState() { global_settings.reset(); } -void FluidSynthDecoder::SetSoundfont(StringView sf) { - preferred_soundfont = ToString(sf); -} - int FluidSynthDecoder::FillBuffer(uint8_t* buffer, int length) { auto* instance_synth = GetSynthInstance(); diff --git a/src/decoder_fluidsynth.h b/src/decoder_fluidsynth.h index 1eb6fe27b..3c90276cc 100644 --- a/src/decoder_fluidsynth.h +++ b/src/decoder_fluidsynth.h @@ -47,14 +47,6 @@ class FluidSynthDecoder : public MidiDecoder { static bool Initialize(std::string& status_message); static void ResetState(); - /** - * Sets the name of the preferred soundfont. - * Must be called before the first MIDI is played. - * - * @param sf soundfont to check for first - */ - static void SetSoundfont(StringView sf); - int FillBuffer(uint8_t* buffer, int length) override; void SendMidiMessage(uint32_t message) override; diff --git a/src/game_config.cpp b/src/game_config.cpp index 48ba6a3be..bcaa72b4d 100644 --- a/src/game_config.cpp +++ b/src/game_config.cpp @@ -321,6 +321,12 @@ void Game_Config::LoadFromArgs(CmdlineParser& cp) { } continue; } + if (cp.ParseNext(arg, 1, "--soundfont")) { + if (arg.NumValues() > 0) { + audio.soundfont.Set(arg.Value(0)); + } + continue; + } cp.SkipNext(); } @@ -356,6 +362,11 @@ void Game_Config::LoadFromStream(Filesystem_Stream::InputStream& is) { /** AUDIO SECTION */ audio.music_volume.FromIni(ini); audio.sound_volume.FromIni(ini); + audio.fluidsynth_midi.FromIni(ini); + audio.wildmidi_midi.FromIni(ini); + audio.native_midi.FromIni(ini); + audio.fmmidi_midi.FromIni(ini); + audio.soundfont.FromIni(ini); /** INPUT SECTION */ input.buttons = Input::GetDefaultButtonMappings(); @@ -442,6 +453,12 @@ void Game_Config::WriteToStream(Filesystem_Stream::OutputStream& os) const { audio.music_volume.ToIni(os); audio.sound_volume.ToIni(os); + audio.fluidsynth_midi.ToIni(os); + audio.wildmidi_midi.ToIni(os); + audio.native_midi.ToIni(os); + audio.fmmidi_midi.ToIni(os); + audio.soundfont.ToIni(os); + os << "\n"; /** INPUT SECTION */ diff --git a/src/game_config.h b/src/game_config.h index 95905c981..0462a1a45 100644 --- a/src/game_config.h +++ b/src/game_config.h @@ -103,6 +103,11 @@ struct Game_ConfigVideo { struct Game_ConfigAudio { RangeConfigParam music_volume{ "BGM Volume", "Volume of the background music", "Audio", "MusicVolume", 100, 0, 100 }; RangeConfigParam sound_volume{ "SFX Volume", "Volume of the sound effects", "Audio", "SoundVolume", 100, 0, 100 }; + BoolConfigParam fluidsynth_midi { "Fluidsynth (SF2)", "Play MIDI using SF2 soundfonts", "Audio", "Fluidsynth", true }; + BoolConfigParam wildmidi_midi { "WildMidi (GUS)", "Play MIDI using GUS patches", "Audio", "WildMidi", true }; + BoolConfigParam native_midi { "Native MIDI", "Play MIDI through the operating system ", "Audio", "NativeMidi", true }; + BoolConfigParam fmmidi_midi { "FmMidi", "Play MIDI using the built-in MIDI synthesizer", "Audio", "FmMidi", true }; + StringConfigParam soundfont { "Soundfont", "Soundfont to use for Fluidsynth", "Audio", "Soundfont", "" }; void Hide(); }; diff --git a/src/player.cpp b/src/player.cpp index 46e20d9de..88c2c9b56 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -83,15 +83,12 @@ #include "baseui.h" #include "game_clock.h" #include "message_overlay.h" +#include "audio_midi.h" #ifdef __ANDROID__ #include "platform/android/android.h" #endif -#if defined(HAVE_FLUIDSYNTH) || defined(HAVE_FLUIDLITE) -#include "decoder_fluidsynth.h" -#endif - #ifndef EMSCRIPTEN // This is not used on Emscripten. #include "exe_reader.h" @@ -639,14 +636,6 @@ Game_Config Player::ParseCommandLine() { } continue; } -#if defined(HAVE_FLUIDSYNTH) || defined(HAVE_FLUIDLITE) - if (cp.ParseNext(arg, 1, "--soundfont")) { - if (arg.NumValues() > 0) { - FluidSynthDecoder::SetSoundfont(arg.Value(0)); - } - continue; - } -#endif if (cp.ParseNext(arg, 0, "--version", 'v')) { std::cout << GetFullVersionString() << std::endl; exit(0); @@ -676,6 +665,9 @@ void Player::CreateGameObjects() { CmdlineParser cp(arguments); game_config = Game_ConfigGame::Create(cp); + // Reinit MIDI + MidiDecoder::Reset(); + // Load the meta information file. // Note: This should eventually be split across multiple folders as described in Issue #1210 std::string meta_file = FileFinder::Game().FindFile(META_NAME); diff --git a/src/scene_settings.cpp b/src/scene_settings.cpp index bf564d071..4b7bc7cc3 100644 --- a/src/scene_settings.cpp +++ b/src/scene_settings.cpp @@ -87,10 +87,13 @@ void Scene_Settings::CreateMainWindow() { } void Scene_Settings::CreateOptionsWindow() { - help_window.reset(new Window_Help(Player::menu_offset_x, 0, MENU_WIDTH, 32)); + help_window = std::make_unique(Player::menu_offset_x, 0, MENU_WIDTH, 32); options_window = std::make_unique(Player::menu_offset_x + 32, 32, MENU_WIDTH - 64, Player::screen_height - 32 * 2); options_window->SetHelpWindow(help_window.get()); + help_window2 = std::make_unique(Player::menu_offset_x, Player::screen_height - 32, MENU_WIDTH, 32); + options_window->help_window2 = help_window2.get(); + input_window = std::make_unique(Player::menu_offset_x, 32, MENU_WIDTH, Player::screen_height - 32 * 3); input_window->SetHelpWindow(help_window.get()); @@ -140,6 +143,7 @@ void Scene_Settings::SetMode(Window_Settings::UiMode new_mode) { input_mode_window->SetVisible(false); input_help_window->SetVisible(false); help_window->SetVisible(false); + help_window2->SetVisible(false); about_window->SetVisible(false); picker_window.reset(); @@ -244,6 +248,7 @@ void Scene_Settings::vUpdate() { case Window_Settings::eInput: case Window_Settings::eVideo: case Window_Settings::eAudio: + case Window_Settings::eAudioMidi: case Window_Settings::eLicense: case Window_Settings::eEngine: case Window_Settings::eInputButtonCategory: diff --git a/src/scene_settings.h b/src/scene_settings.h index cf21a8b91..0bd902ec0 100644 --- a/src/scene_settings.h +++ b/src/scene_settings.h @@ -72,6 +72,7 @@ class Scene_Settings : public Scene { std::unique_ptr main_window; std::unique_ptr help_window; + std::unique_ptr help_window2; std::unique_ptr about_window; std::unique_ptr options_window; std::unique_ptr input_window; diff --git a/src/window_settings.cpp b/src/window_settings.cpp index cba597ced..0cb1003d4 100644 --- a/src/window_settings.cpp +++ b/src/window_settings.cpp @@ -31,6 +31,8 @@ #include "player.h" #include "system.h" #include "audio.h" +#include "audio_midi.h" +#include "audio_generic_midiout.h" class MenuItem final : public ConfigParam { public: @@ -51,7 +53,7 @@ void Window_Settings::DrawOption(int index) { auto& option = options[index]; bool enabled = bool(option.action); - Font::SystemColor color = enabled ? Font::ColorDefault : Font::ColorDisabled; + Font::SystemColor color = enabled ? option.color : Font::ColorDisabled; contents->TextDraw(rect, color, option.text); contents->TextDraw(rect, color, option.value_text, Text::AlignRight); @@ -127,6 +129,9 @@ void Window_Settings::Refresh() { case eAudio: RefreshAudio(); break; + case eAudioMidi: + RefreshAudioMidi(); + break; case eEngine: RefreshEngine(); break; @@ -163,8 +168,15 @@ void Window_Settings::Refresh() { void Window_Settings::UpdateHelp() { if (index >= 0 && index < static_cast(options.size())) { help_window->SetText(options[index].help); + if (help_window2) { + help_window2->SetText(options[index].help2); + help_window2->SetVisible(!options[index].help2.empty()); + } } else { help_window->SetText(""); + if (help_window2) { + help_window2->SetVisible(false); + } } } @@ -265,12 +277,59 @@ void Window_Settings::RefreshAudio() { AddOption(cfg.music_volume, [this](){ Audio().BGM_SetGlobalVolume(GetCurrentOption().current_value); }); AddOption(cfg.sound_volume, [this](){ Audio().SE_SetGlobalVolume(GetCurrentOption().current_value); }); - /*AddOption("Midi Backend", LockedConfigParam("Unknown"), "", - [](){}, - "Which MIDI backend to use"); - AddOption("Midi Soundfont", LockedConfigParam("Default"), "", - [](){}, - "Which MIDI soundfont to use");*/ + AddOption(MenuItem("MIDI drivers", "Configure MIDI playback", ""), [this](){ Push(eAudioMidi); }); + AddOption(cfg.soundfont, [this](){ }); +} + +void Window_Settings::RefreshAudioMidi() { + auto cfg = Audio().GetConfig(); + + bool used = false; + + AddOption(MenuItem("> Information <", "The first active and working option is used for MIDI", ""), [](){}); + options.back().help2 = "Changes take effect when a new MIDI file is played"; + +#if defined(HAVE_FLUIDSYNTH) || defined(HAVE_FLUIDLITE) + AddOption(cfg.fluidsynth_midi, []() { Audio().SetFluidsynthEnabled(Audio().GetConfig().fluidsynth_midi.Toggle()); }); + if (!MidiDecoder::CheckFluidsynth(options.back().help2)) { + options.back().text += " [Not working]"; + options.back().color = Font::ColorKnockout; + } else if (cfg.fluidsynth_midi.Get()) { + options.back().text += " [In use]"; + used = true; + } +#endif +#ifdef HAVE_LIBWILDMIDI + AddOption(cfg.wildmidi_midi, [](){ Audio().SetWildMidiEnabled(Audio().GetConfig().wildmidi_midi.Toggle()); }); + if (!MidiDecoder::CheckWildMidi(options.back().help2)) { + options.back().text += " [Not working]"; + options.back().color = Font::ColorKnockout; + } else if (cfg.wildmidi_midi.Get() && !used) { + options.back().text += " [In use]"; + used = true; + } +#endif +#ifdef HAVE_NATIVE_MIDI + AddOption(cfg.native_midi, [](){ Audio().SetNativeMidiEnabled(Audio().GetConfig().native_midi.Toggle()); }); + if (!GenericAudioMidiOut().IsInitialized(options.back().help2)) { + options.back().text += " [Not working]"; + options.back().color = Font::ColorKnockout; + } else if (cfg.native_midi.Get() && !used) { + options.back().text += " [In use]"; + used = true; + } +#endif +#ifdef WANT_FMMIDI + AddOption(cfg.fmmidi_midi, [](){ Audio().SetFmMidiEnabled(Audio().GetConfig().fmmidi_midi.Toggle()); }); + if (cfg.fmmidi_midi.Get() && !used) { + options.back().text += " [In use]"; + } +#endif + + if (options.size() == 1) { + options.pop_back(); + AddOption(MenuItem("MIDI is unavailable", "EasyRPG Player was not compiled with MIDI support", ""), [](){}); + } } void Window_Settings::RefreshEngine() { @@ -333,7 +392,7 @@ void Window_Settings::RefreshLicense() { #ifdef HAVE_OPUS AddOption(MenuItem("opus", "Decodes the free OPUS audio codec", "BSD"), [](){}); #endif -#ifdef HAVE_WILDMIDI +#ifdef HAVE_LIBWILDMIDI AddOption(MenuItem("WildMidi", "MIDI synthesizer", "LGPLv3+"), [](){}); #endif #ifdef HAVE_FLUIDSYNTH diff --git a/src/window_settings.h b/src/window_settings.h index 9effde03c..80d1d34ef 100644 --- a/src/window_settings.h +++ b/src/window_settings.h @@ -42,6 +42,7 @@ class Window_Settings : public Window_Selectable { eInputButtonRemove, eVideo, eAudio, + eAudioMidi, eLicense, eEngine, eSave, @@ -60,8 +61,10 @@ class Window_Settings : public Window_Selectable { std::string text; std::string value_text; std::string help; + std::string help2; std::function action; OptionMode mode; + Font::SystemColor color = Font::ColorDefault; int current_value; int original_value; int min_value; @@ -106,6 +109,7 @@ class Window_Settings : public Window_Selectable { */ void Refresh(); + Window_Help* help_window2 = nullptr; private: /** @@ -134,6 +138,7 @@ class Window_Settings : public Window_Selectable { void RefreshButtonList(); void RefreshVideo(); void RefreshAudio(); + void RefreshAudioMidi(); void RefreshEngine(); void RefreshLicense(); From 8a00b7cc4353a8d9d0db4a9301c058cc0a5fd4fa Mon Sep 17 00:00:00 2001 From: Ghabry Date: Sat, 2 Dec 2023 00:55:28 +0100 Subject: [PATCH 004/222] Config Param: Simplify code via constexpr if --- src/config_param.h | 38 +++++++++++++------------------------- 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/src/config_param.h b/src/config_param.h index 777b2144c..5cc7158eb 100644 --- a/src/config_param.h +++ b/src/config_param.h @@ -76,7 +76,7 @@ class ConfigParamBase { } if (IsValid(value)) { - _value = std::move(value); + _value = value; return true; } return false; @@ -181,30 +181,19 @@ class ConfigParamBase { /** @return human readable representation of the value for the settings scene */ virtual std::string ValueToString() const = 0; - template ::value, int>::type = 0> - bool FromIni(const lcf::INIReader& ini) { + virtual bool FromIni(const lcf::INIReader& ini) { // FIXME: Migrate IniReader to StringView (or std::string_view with C++17) if (ini.HasValue(ToString(_config_section), ToString(_config_key))) { - Set(ini.GetString(ToString(_config_section), ToString(_config_key), T())); - return true; - } - return false; - } - - template ::value, int>::type = 0> - bool FromIni(const lcf::INIReader& ini) { - if (ini.HasValue(ToString(_config_section), ToString(_config_key))) { - Set(ini.GetInteger(ToString(_config_section), ToString(_config_key), T())); - return true; - } - return false; - } - - template ::value, int>::type = 0> - bool FromIni(const lcf::INIReader& ini) { - if (ini.HasValue(ToString(_config_section), ToString(_config_key))) { - Set(ini.GetBoolean(ToString(_config_section), ToString(_config_key), T())); - return true; + if constexpr (std::is_same_v) { + Set(ini.GetString(ToString(_config_section), ToString(_config_key), T())); + return true; + } else if constexpr (std::is_same_v) { + Set(ini.GetInteger(ToString(_config_section), ToString(_config_key), T())); + return true; + } else if constexpr (std::is_same_v) { + Set(ini.GetBoolean(ToString(_config_section), ToString(_config_key), T())); + return true; + } } return false; } @@ -405,8 +394,7 @@ class EnumConfigParam : public ConfigParamBase { return false; } - template ::value, int>::type = 0> - bool FromIni(const lcf::INIReader& ini) { + bool FromIni(const lcf::INIReader& ini) override { if (ini.HasValue(ToString(this->_config_section), ToString(this->_config_key))) { std::string s = ini.GetString(ToString(this->_config_section), ToString(this->_config_key), std::string()); for (size_t i = 0; i < _tags.size(); ++i) { From d3577f56f95e3f1a76609855fc99817e8eb1640c Mon Sep 17 00:00:00 2001 From: Ghabry Date: Mon, 4 Dec 2023 01:53:00 +0100 Subject: [PATCH 005/222] Ui: Add OpenURL to SDL2 This function can open folders and websites, useful for the settings --- src/baseui.h | 9 +++++++++ src/platform/sdl/sdl2_ui.cpp | 19 ++++++++++++++++++- src/platform/sdl/sdl2_ui.h | 1 + 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/baseui.h b/src/baseui.h index 266c3ee4a..05cb71f10 100644 --- a/src/baseui.h +++ b/src/baseui.h @@ -220,6 +220,15 @@ class BaseUi { */ void SetGameResolution(GameResolution resolution); + /** + * Opens the specified URL through the operating system. + * Opens a file browser when file:// is provided. + * + * @param url URL to open + * @return true when successful + */ + virtual bool OpenURL(StringView path) { (void)path; return false; } + /** Toggles "stretch to screen width" on or off */ virtual void ToggleStretch() {}; diff --git a/src/platform/sdl/sdl2_ui.cpp b/src/platform/sdl/sdl2_ui.cpp index 6e5255166..3d4311caa 100644 --- a/src/platform/sdl/sdl2_ui.cpp +++ b/src/platform/sdl/sdl2_ui.cpp @@ -749,7 +749,6 @@ void Sdl2Ui::ProcessWindowEvent(SDL_Event &evnt) { break; } } -#endif ShowCursor(last); @@ -1262,3 +1261,21 @@ Rect Sdl2Ui::GetWindowMetrics() const { return window_mode_metrics; } } + +bool Sdl2Ui::OpenURL(StringView url) { +#if SDL_VERSION_ATLEAST(2, 0, 14) + if (IsFullscreen()) { + ToggleFullscreen(); + } + + if (SDL_OpenURL(ToString(url).c_str()) < 0) { + Output::Warning("Open URL {} failed: {}", url, SDL_GetError()); + return false; + } + + return true; +#else + Output::Warning("Cannot Open URL: SDL2 version too old (must be 2.0.14)"); + return false; +#endif +} diff --git a/src/platform/sdl/sdl2_ui.h b/src/platform/sdl/sdl2_ui.h index c9543f2aa..cfa28769c 100644 --- a/src/platform/sdl/sdl2_ui.h +++ b/src/platform/sdl/sdl2_ui.h @@ -70,6 +70,7 @@ class Sdl2Ui final : public BaseUi { void ToggleStretch() override; void ToggleVsync() override; void vGetConfig(Game_ConfigVideo& cfg) const override; + bool OpenURL(StringView url) override; Rect GetWindowMetrics() const override; #ifdef SUPPORT_AUDIO From 1cbf5e77c320b4d014e0eecd8f558fda9e1fb531 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Mon, 4 Dec 2023 01:55:45 +0100 Subject: [PATCH 006/222] Settings: Make "Pause when focus lost" a setting instead of a compile time flag Fix #2925 --- src/baseui.h | 12 +++++++++++- src/game_config.cpp | 2 ++ src/game_config.h | 1 + src/options.h | 12 ------------ src/platform/sdl/sdl2_ui.cpp | 12 ++++++++---- src/platform/sdl/sdl_ui.cpp | 8 ++++++-- src/window_settings.cpp | 1 + 7 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/baseui.h b/src/baseui.h index 05cb71f10..3ebc01be9 100644 --- a/src/baseui.h +++ b/src/baseui.h @@ -191,9 +191,15 @@ class BaseUi { /** Toggle whether we should show fps */ void ToggleShowFps(); - /** Toggle wheter we should show fps on the titlebar */ + /** Toggle whether we should show fps on the titlebar */ void ToggleShowFpsOnTitle(); + /** + * Set whether the program pauses the execution when the program focus is lost. + * @param value + */ + void SetPauseWhenFocusLost(bool value); + /** * @return the minimum amount of time each physical frame should take. * If the UI manages time (i.e.) vsync, will return a 0 duration. @@ -386,6 +392,10 @@ inline void BaseUi::ToggleShowFpsOnTitle() { vcfg.fps_render_window.Toggle(); } +inline void BaseUi::SetPauseWhenFocusLost(bool value) { + vcfg.pause_when_focus_lost.Set(value); +} + inline Game_Clock::duration BaseUi::GetFrameLimit() const { return IsFrameRateSynchronized() ? Game_Clock::duration(0) : frame_limit; } diff --git a/src/game_config.cpp b/src/game_config.cpp index bcaa72b4d..24f1d9c4c 100644 --- a/src/game_config.cpp +++ b/src/game_config.cpp @@ -350,6 +350,7 @@ void Game_Config::LoadFromStream(Filesystem_Stream::InputStream& is) { video.scaling_mode.FromIni(ini); video.stretch.FromIni(ini); video.touch_ui.FromIni(ini); + video.pause_when_focus_lost.FromIni(ini); video.game_resolution.FromIni(ini); if (ini.HasValue("Video", "WindowX") && ini.HasValue("Video", "WindowY") && ini.HasValue("Video", "WindowWidth") && ini.HasValue("Video", "WindowHeight")) { @@ -437,6 +438,7 @@ void Game_Config::WriteToStream(Filesystem_Stream::OutputStream& os) const { video.scaling_mode.ToIni(os); video.stretch.ToIni(os); video.touch_ui.ToIni(os); + video.pause_when_focus_lost.ToIni(os); video.game_resolution.ToIni(os); // only preserve when toggling between window and fullscreen is supported diff --git a/src/game_config.h b/src/game_config.h index 0462a1a45..b0bb22aef 100644 --- a/src/game_config.h +++ b/src/game_config.h @@ -85,6 +85,7 @@ struct Game_ConfigVideo { Utils::MakeSvArray("nearest", "integer", "bilinear"), Utils::MakeSvArray("Scale to screen size (Causes scaling artifacts)", "Scale to multiple of the game resolution", "Like Nearest, but output is blurred to avoid artifacts")}; BoolConfigParam stretch{ "Stretch", "Stretch to the width of the window/screen", "Video", "Stretch", false }; + BoolConfigParam pause_when_focus_lost{ "Pause when focus lost", "Pause the execution when the program is in the background", "Video", "PauseWhenFocusLost", true }; BoolConfigParam touch_ui{ "Touch Ui", "Display the touch ui", "Video", "TouchUi", true }; EnumConfigParam game_resolution{ "Resolution", "Game resolution. Changes require a restart.", "Video", "GameResolution", GameResolution::Original, Utils::MakeSvArray("Original (Recommended)", "Widescreen (Experimental)", "Ultrawide (Experimental)"), diff --git a/src/options.h b/src/options.h index 5f673d83a..fc05038da 100644 --- a/src/options.h +++ b/src/options.h @@ -52,18 +52,6 @@ /** Targeted screen bits per pixel. */ #define SCREEN_TARGET_BPP 32 -/** - * Pause the game process when the player window - * looses its focus. - */ -#define PAUSE_GAME_WHEN_FOCUS_LOST 1 - -/** - * Pause the audio process when the player window - * looses its focus. - */ -#define PAUSE_AUDIO_WHEN_FOCUS_LOST 1 - /** INI configuration filename. */ #define INI_NAME "RPG_RT.ini" #define EASYRPG_INI_NAME "EasyRPG.ini" diff --git a/src/platform/sdl/sdl2_ui.cpp b/src/platform/sdl/sdl2_ui.cpp index 3d4311caa..1b062505e 100644 --- a/src/platform/sdl/sdl2_ui.cpp +++ b/src/platform/sdl/sdl2_ui.cpp @@ -732,16 +732,17 @@ void Sdl2Ui::ProcessEvent(SDL_Event &evnt) { void Sdl2Ui::ProcessWindowEvent(SDL_Event &evnt) { int state = evnt.window.event; -#if PAUSE_GAME_WHEN_FOCUS_LOST + if (state == SDL_WINDOWEVENT_FOCUS_LOST) { + if (!vcfg.pause_when_focus_lost.Get()) { + return; + } Player::Pause(); bool last = ShowCursor(true); -#ifndef EMSCRIPTEN // Filter SDL events until focus is regained - SDL_Event wait_event; while (SDL_WaitEvent(&wait_event)) { @@ -757,7 +758,7 @@ void Sdl2Ui::ProcessWindowEvent(SDL_Event &evnt) { return; } -#endif + #if defined(USE_MOUSE_OR_TOUCH) && defined(SUPPORT_MOUSE_OR_TOUCH) if (state == SDL_WINDOWEVENT_ENTER) { mouse_focus = true; @@ -1230,6 +1231,7 @@ void Sdl2Ui::vGetConfig(Game_ConfigVideo& cfg) const { cfg.scaling_mode.SetOptionVisible(true); cfg.stretch.SetOptionVisible(true); cfg.game_resolution.SetOptionVisible(true); + cfg.pause_when_focus_lost.SetOptionVisible(true); cfg.vsync.Set(current_display_mode.vsync); cfg.window_zoom.Set(current_display_mode.zoom); @@ -1243,6 +1245,8 @@ void Sdl2Ui::vGetConfig(Game_ConfigVideo& cfg) const { cfg.window_zoom.SetOptionVisible(false); // Toggling this freezes the web player cfg.vsync.SetOptionVisible(false); + cfg.pause_when_focus_lost.Lock(false); + cfg.pause_when_focus_lost.SetOptionVisible(false); #elif defined(__WIIU__) // FIXME: Some options below may crash, better disable for now cfg.fullscreen.SetOptionVisible(false); diff --git a/src/platform/sdl/sdl_ui.cpp b/src/platform/sdl/sdl_ui.cpp index 63ed428c2..04961a612 100644 --- a/src/platform/sdl/sdl_ui.cpp +++ b/src/platform/sdl/sdl_ui.cpp @@ -478,8 +478,11 @@ void SdlUi::ProcessActiveEvent(SDL_Event &evnt) { int state; state = evnt.active.state; -#if PAUSE_GAME_WHEN_FOCUS_LOST if (state == SDL_APPINPUTFOCUS && !evnt.active.gain) { + if (!vcfg.pause_when_focus_lost.Get()) { + return; + } + Player::Pause(); bool last = ShowCursor(true); @@ -500,7 +503,6 @@ void SdlUi::ProcessActiveEvent(SDL_Event &evnt) { return; } -#endif } void SdlUi::ProcessKeyDownEvent(SDL_Event &evnt) { @@ -747,6 +749,8 @@ void SdlUi::vGetConfig(Game_ConfigVideo& cfg) const { #endif cfg.fullscreen.SetOptionVisible(toggle_fs_available); + cfg.pause_when_focus_lost.SetOptionVisible(toggle_fs_available); + #ifdef SUPPORT_ZOOM cfg.window_zoom.SetOptionVisible(true); cfg.window_zoom.Set(current_display_mode.zoom); diff --git a/src/window_settings.cpp b/src/window_settings.cpp index 0cb1003d4..3a901b58e 100644 --- a/src/window_settings.cpp +++ b/src/window_settings.cpp @@ -268,6 +268,7 @@ void Window_Settings::RefreshVideo() { AddOption(cfg.fps_render_window, [](){ DisplayUi->ToggleShowFpsOnTitle(); }); AddOption(cfg.stretch, []() { DisplayUi->ToggleStretch(); }); AddOption(cfg.scaling_mode, [this](){ DisplayUi->SetScalingMode(static_cast(GetCurrentOption().current_value)); }); + AddOption(cfg.pause_when_focus_lost, [cfg]() mutable { DisplayUi->SetPauseWhenFocusLost(cfg.pause_when_focus_lost.Toggle()); }); AddOption(cfg.touch_ui, [](){ DisplayUi->ToggleTouchUi(); }); AddOption(cfg.game_resolution, [this]() { DisplayUi->SetGameResolution(static_cast(GetCurrentOption().current_value)); }); } From ff6d8cd284e0fbf932a84f4406d8dc35cc4d0127 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Mon, 4 Dec 2023 01:58:02 +0100 Subject: [PATCH 007/222] Settings: Code improvements for MIDI This already contains a few lines of font I couldn't properly split. Wrote both at the same time... --- src/audio.cpp | 15 ++++++ src/config_param.h | 11 ++++ src/game_config.cpp | 52 +++++++++++++++++++ src/game_config.h | 19 ++++++- src/scene_settings.cpp | 3 +- src/window_settings.cpp | 109 ++++++++++++++++++++++++++-------------- src/window_settings.h | 6 +++ 7 files changed, 176 insertions(+), 39 deletions(-) diff --git a/src/audio.cpp b/src/audio.cpp index 1448736ce..fd6e720ec 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -70,6 +70,21 @@ AudioInterface::AudioInterface(const Game_ConfigAudio& cfg) : cfg(cfg) { Game_ConfigAudio AudioInterface::GetConfig() const { auto acfg = cfg; acfg.Hide(); + +#if !defined(HAVE_FLUIDSYNTH) && !defined(HAVE_FLUIDLITE) + acfg.fluidsynth_midi.SetOptionVisible(false); + acfg.soundfont.SetOptionVisible(false); +#endif +#ifndef HAVE_LIBWILDMIDI + acfg.wildmidi_midi.SetOptionVisible(false); +#endif +#ifndef HAVE_NATIVE_MIDI + acfg.native_midi.SetOptionVisible(false); +#endif +#ifndef WANT_FMMIDI + acfg.fmmidi_midi.SetOptionVisible(false); +#endif + vGetConfig(acfg); return acfg; } diff --git a/src/config_param.h b/src/config_param.h index 5cc7158eb..cdbe2ecd5 100644 --- a/src/config_param.h +++ b/src/config_param.h @@ -19,6 +19,7 @@ #define EP_CONFIG_PARAM_H #include "string_view.h" +#include "filefinder.h" #include #include #include @@ -431,4 +432,14 @@ class EnumConfigParam : public ConfigParamBase { } }; +class PathConfigParam : public StringConfigParam { +public: + PathConfigParam(StringView name, StringView description, StringView config_section, StringView config_key, std::string value) : + StringConfigParam(name, description, config_section, config_key, value) {} + + std::string ValueToString() const override { + return std::get<1>(FileFinder::GetPathAndFilename(Get())); + } +}; + #endif diff --git a/src/game_config.cpp b/src/game_config.cpp index 24f1d9c4c..883c82591 100644 --- a/src/game_config.cpp +++ b/src/game_config.cpp @@ -35,6 +35,8 @@ namespace { std::string config_path; + std::string soundfont_path; + std::string font_path; StringView config_name = "config.ini"; } @@ -58,6 +60,7 @@ void Game_ConfigVideo::Hide() { scaling_mode.SetOptionVisible(false); stretch.SetOptionVisible(false); touch_ui.SetOptionVisible(false); + pause_when_focus_lost.SetOptionVisible(false); game_resolution.SetOptionVisible(false); } @@ -201,6 +204,35 @@ Filesystem_Stream::InputStream Game_Config::GetGlobalConfigFileInput() { return Filesystem_Stream::InputStream(); } +FilesystemView Game_Config::GetSoundfontFilesystem() { + std::string path = soundfont_path; + if (path.empty()) { + path = FileFinder::MakePath(GetGlobalConfigFilesystem().GetFullPath(), "Soundfont"); + } + + if (!FileFinder::Root().MakeDirectory(path, true)) { + Output::Warning("Could not create soundfont path {}", path); + return {}; + } + + return FileFinder::Root().Create(path); +} + + +FilesystemView Game_Config::GetFontFilesystem() { + std::string path = font_path; + if (path.empty()) { + path = FileFinder::MakePath(GetGlobalConfigFilesystem().GetFullPath(), "Font"); + } + + if (!FileFinder::Root().MakeDirectory(path, true)) { + Output::Warning("Could not create fount path {}", path); + return {}; + } + + return FileFinder::Root().Create(path); +} + Filesystem_Stream::OutputStream Game_Config::GetGlobalConfigFileOutput() { auto fs = GetGlobalConfigFilesystem(); @@ -327,6 +359,18 @@ void Game_Config::LoadFromArgs(CmdlineParser& cp) { } continue; } + if (cp.ParseNext(arg, 1, "--soundfont-path")) { + if (arg.NumValues() > 0) { + soundfont_path = FileFinder::MakeCanonical(arg.Value(0), 0); + } + continue; + } + if (cp.ParseNext(arg, 1, "--font-path")) { + if (arg.NumValues() > 0) { + font_path = FileFinder::MakeCanonical(arg.Value(0), 0); + } + continue; + } cp.SkipNext(); } @@ -423,6 +467,10 @@ void Game_Config::LoadFromStream(Filesystem_Stream::InputStream& is) { player.settings_in_title.FromIni(ini); player.settings_in_menu.FromIni(ini); player.show_startup_logos.FromIni(ini); + player.font1.FromIni(ini); + player.font1_size.FromIni(ini); + player.font2.FromIni(ini); + player.font2_size.FromIni(ini); } void Game_Config::WriteToStream(Filesystem_Stream::OutputStream& os) const { @@ -506,6 +554,10 @@ void Game_Config::WriteToStream(Filesystem_Stream::OutputStream& os) const { player.settings_in_title.ToIni(os); player.settings_in_menu.ToIni(os); player.show_startup_logos.ToIni(os); + player.font1.ToIni(os); + player.font1_size.ToIni(os); + player.font2.ToIni(os); + player.font2_size.ToIni(os); os << "\n"; } diff --git a/src/game_config.h b/src/game_config.h index b0bb22aef..c22151448 100644 --- a/src/game_config.h +++ b/src/game_config.h @@ -68,6 +68,10 @@ struct Game_ConfigPlayer { Utils::MakeSvArray("None", "Custom", "All"), Utils::MakeSvArray("none", "custom", "all"), Utils::MakeSvArray("Do not show any additional logos", "Show custom logos bundled with the game", "Show all logos, including the original from RPG Maker")}; + PathConfigParam font1 { "Font 1", "The game chooses whether it wants font 1 or 2", "Player", "Font1", "" }; + RangeConfigParam font1_size { "Font 1 Size", "", "Player", "Font1Size", 12, 6, 16}; + PathConfigParam font2 { "Font 2", "The game chooses whether it wants font 1 or 2", "Player", "Font2", "" }; + RangeConfigParam font2_size { "Font 2 Size", "", "Player", "Font2Size", 12, 6, 16}; void Hide(); }; @@ -108,7 +112,7 @@ struct Game_ConfigAudio { BoolConfigParam wildmidi_midi { "WildMidi (GUS)", "Play MIDI using GUS patches", "Audio", "WildMidi", true }; BoolConfigParam native_midi { "Native MIDI", "Play MIDI through the operating system ", "Audio", "NativeMidi", true }; BoolConfigParam fmmidi_midi { "FmMidi", "Play MIDI using the built-in MIDI synthesizer", "Audio", "FmMidi", true }; - StringConfigParam soundfont { "Soundfont", "Soundfont to use for Fluidsynth", "Audio", "Soundfont", "" }; + PathConfigParam soundfont { "Soundfont", "Soundfont to use for Fluidsynth", "Audio", "Soundfont", "" }; void Hide(); }; @@ -151,6 +155,19 @@ struct Game_Config { */ static FilesystemView GetGlobalConfigFilesystem(); + /** + * Returns the filesystem view to the soundfont directory + * By default this is config/Soundfont + */ + static FilesystemView GetSoundfontFilesystem(); + + /** + * Returns the filesystem view to the font directory + * By default this is config/Font + */ + static FilesystemView GetFontFilesystem(); + + /** * Returns a handle to the global config file for reading. * The file is created if it does not exist. diff --git a/src/scene_settings.cpp b/src/scene_settings.cpp index 4b7bc7cc3..afbcd91f0 100644 --- a/src/scene_settings.cpp +++ b/src/scene_settings.cpp @@ -91,7 +91,7 @@ void Scene_Settings::CreateOptionsWindow() { options_window = std::make_unique(Player::menu_offset_x + 32, 32, MENU_WIDTH - 64, Player::screen_height - 32 * 2); options_window->SetHelpWindow(help_window.get()); - help_window2 = std::make_unique(Player::menu_offset_x, Player::screen_height - 32, MENU_WIDTH, 32); + help_window2 = std::make_unique(Player::menu_offset_x, options_window->GetBottomY(), MENU_WIDTH, 32); options_window->help_window2 = help_window2.get(); input_window = std::make_unique(Player::menu_offset_x, 32, MENU_WIDTH, Player::screen_height - 32 * 3); @@ -249,6 +249,7 @@ void Scene_Settings::vUpdate() { case Window_Settings::eVideo: case Window_Settings::eAudio: case Window_Settings::eAudioMidi: + case Window_Settings::eAudioSoundfont: case Window_Settings::eLicense: case Window_Settings::eEngine: case Window_Settings::eInputButtonCategory: diff --git a/src/window_settings.cpp b/src/window_settings.cpp index 3a901b58e..4b748ea40 100644 --- a/src/window_settings.cpp +++ b/src/window_settings.cpp @@ -23,6 +23,7 @@ #include "text.h" #include "window_settings.h" #include "game_config.h" +#include "game_system.h" #include "input_buttons.h" #include "keys.h" #include "output.h" @@ -132,6 +133,9 @@ void Window_Settings::Refresh() { case eAudioMidi: RefreshAudioMidi(); break; + case eAudioSoundfont: + RefreshAudioSoundfont(); + break; case eEngine: RefreshEngine(); break; @@ -278,8 +282,10 @@ void Window_Settings::RefreshAudio() { AddOption(cfg.music_volume, [this](){ Audio().BGM_SetGlobalVolume(GetCurrentOption().current_value); }); AddOption(cfg.sound_volume, [this](){ Audio().SE_SetGlobalVolume(GetCurrentOption().current_value); }); - AddOption(MenuItem("MIDI drivers", "Configure MIDI playback", ""), [this](){ Push(eAudioMidi); }); - AddOption(cfg.soundfont, [this](){ }); + if (cfg.fluidsynth_midi.IsOptionVisible() || cfg.wildmidi_midi.IsOptionVisible() || cfg.native_midi.IsOptionVisible() || cfg.fmmidi_midi.IsOptionVisible()) { + AddOption(MenuItem("MIDI drivers", "Configure MIDI playback", ""), [this]() { Push(eAudioMidi); }); + } + AddOption(cfg.soundfont, [this](){ Push(eAudioSoundfont); }); } void Window_Settings::RefreshAudioMidi() { @@ -290,47 +296,76 @@ void Window_Settings::RefreshAudioMidi() { AddOption(MenuItem("> Information <", "The first active and working option is used for MIDI", ""), [](){}); options.back().help2 = "Changes take effect when a new MIDI file is played"; -#if defined(HAVE_FLUIDSYNTH) || defined(HAVE_FLUIDLITE) - AddOption(cfg.fluidsynth_midi, []() { Audio().SetFluidsynthEnabled(Audio().GetConfig().fluidsynth_midi.Toggle()); }); - if (!MidiDecoder::CheckFluidsynth(options.back().help2)) { - options.back().text += " [Not working]"; - options.back().color = Font::ColorKnockout; - } else if (cfg.fluidsynth_midi.Get()) { - options.back().text += " [In use]"; - used = true; + if (cfg.fluidsynth_midi.IsOptionVisible()) { + AddOption(cfg.fluidsynth_midi, []() { Audio().SetFluidsynthEnabled(Audio().GetConfig().fluidsynth_midi.Toggle()); }); + if (!MidiDecoder::CheckFluidsynth(options.back().help2)) { + options.back().text += " [Not working]"; + options.back().color = Font::ColorKnockout; + } else if (cfg.fluidsynth_midi.Get()) { + options.back().text += " [In use]"; + used = true; + } } -#endif -#ifdef HAVE_LIBWILDMIDI - AddOption(cfg.wildmidi_midi, [](){ Audio().SetWildMidiEnabled(Audio().GetConfig().wildmidi_midi.Toggle()); }); - if (!MidiDecoder::CheckWildMidi(options.back().help2)) { - options.back().text += " [Not working]"; - options.back().color = Font::ColorKnockout; - } else if (cfg.wildmidi_midi.Get() && !used) { - options.back().text += " [In use]"; - used = true; + + if (cfg.wildmidi_midi.IsOptionVisible()) { + AddOption(cfg.wildmidi_midi, []() { Audio().SetWildMidiEnabled(Audio().GetConfig().wildmidi_midi.Toggle()); }); + if (!MidiDecoder::CheckWildMidi(options.back().help2)) { + options.back().text += " [Not working]"; + options.back().color = Font::ColorKnockout; + } else if (cfg.wildmidi_midi.Get() && !used) { + options.back().text += " [In use]"; + used = true; + } } -#endif -#ifdef HAVE_NATIVE_MIDI - AddOption(cfg.native_midi, [](){ Audio().SetNativeMidiEnabled(Audio().GetConfig().native_midi.Toggle()); }); - if (!GenericAudioMidiOut().IsInitialized(options.back().help2)) { - options.back().text += " [Not working]"; - options.back().color = Font::ColorKnockout; - } else if (cfg.native_midi.Get() && !used) { - options.back().text += " [In use]"; - used = true; + + if (cfg.native_midi.IsOptionVisible()) { + AddOption(cfg.native_midi, []() { Audio().SetNativeMidiEnabled(Audio().GetConfig().native_midi.Toggle()); }); + if (!GenericAudioMidiOut().IsInitialized(options.back().help2)) { + options.back().text += " [Not working]"; + options.back().color = Font::ColorKnockout; + } else if (cfg.native_midi.Get() && !used) { + options.back().text += " [In use]"; + used = true; + } } -#endif -#ifdef WANT_FMMIDI - AddOption(cfg.fmmidi_midi, [](){ Audio().SetFmMidiEnabled(Audio().GetConfig().fmmidi_midi.Toggle()); }); - if (cfg.fmmidi_midi.Get() && !used) { - options.back().text += " [In use]"; + + if (cfg.fmmidi_midi.IsOptionVisible()) { + AddOption(cfg.fmmidi_midi, []() { Audio().SetFmMidiEnabled(Audio().GetConfig().fmmidi_midi.Toggle()); }); + if (cfg.fmmidi_midi.Get() && !used) { + options.back().text += " [In use]"; + } } -#endif +} + +void Window_Settings::RefreshAudioSoundfont() { + auto fs = Game_Config::GetSoundfontFilesystem(); + + if (!fs) { + Pop(); + } + + fs.ClearCache(); - if (options.size() == 1) { - options.pop_back(); - AddOption(MenuItem("MIDI is unavailable", "EasyRPG Player was not compiled with MIDI support", ""), [](){}); + auto acfg = Audio().GetConfig(); + AddOption(MenuItem("", "Attempt to find a suitable soundfont automatically", acfg.soundfont.Get().empty() ? "[x]" : ""), [this]() { + Audio().SetFluidsynthSoundfont({}); + Pop(); + }); + + auto list = fs.ListDirectory(); + assert(list); + + std::string sf_lower = Utils::LowerCase(Audio().GetFluidsynthSoundfont()); + for (const auto& item: *list) { + if (item.second.type == DirectoryTree::FileType::Regular && StringView(item.first).ends_with(".sf2")) { + AddOption(MenuItem(item.second.name, "Use this custom soundfont", StringView(sf_lower).ends_with(item.first) ? "[x]" : ""), [this, fs, item]() { + Audio().SetFluidsynthSoundfont(FileFinder::MakePath(fs.GetFullPath(), item.second.name)); + Pop(); + }); + } } + + AddOption(MenuItem("", "Open the soundfont directory in a file browser", ""), [fs]() { DisplayUi->OpenURL(fs.GetFullPath()); }); } void Window_Settings::RefreshEngine() { diff --git a/src/window_settings.h b/src/window_settings.h index 80d1d34ef..7270ea2fe 100644 --- a/src/window_settings.h +++ b/src/window_settings.h @@ -43,8 +43,11 @@ class Window_Settings : public Window_Selectable { eVideo, eAudio, eAudioMidi, + eAudioSoundfont, eLicense, eEngine, + eEngineFont1, + eEngineFont2, eSave, eEnd, eAbout, @@ -110,6 +113,7 @@ class Window_Settings : public Window_Selectable { void Refresh(); Window_Help* help_window2 = nullptr; + RangeConfigParam font_size { "", "Font size to use. Not supported for the built-in font.", "", "", 12, 6, 16}; private: /** @@ -139,7 +143,9 @@ class Window_Settings : public Window_Selectable { void RefreshVideo(); void RefreshAudio(); void RefreshAudioMidi(); + void RefreshAudioSoundfont(); void RefreshEngine(); + void RefreshEngineFont(bool mincho); void RefreshLicense(); void UpdateHelp() override; From 3e5d648a5532ed741f7c44ed5ac58aab2eae46d4 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Mon, 4 Dec 2023 01:58:16 +0100 Subject: [PATCH 008/222] Settings: Allow configuring the default fonts The Window class was extended to allow setting a font. --- src/bitmap.cpp | 28 ++++++------- src/bitmap.h | 20 ++++++++-- src/filefinder.cpp | 9 ----- src/filefinder.h | 8 ++++ src/font.cpp | 16 +++++++- src/game_config.cpp | 7 +++- src/scene_logo.cpp | 2 + src/scene_settings.cpp | 31 +++++++++++++++ src/scene_settings.h | 1 + src/window.h | 27 +++++++++++++ src/window_help.cpp | 2 +- src/window_settings.cpp | 87 +++++++++++++++++++++++++++++++++++++---- 12 files changed, 200 insertions(+), 38 deletions(-) diff --git a/src/bitmap.cpp b/src/bitmap.cpp index d74a358e9..c7785efbd 100644 --- a/src/bitmap.cpp +++ b/src/bitmap.cpp @@ -333,20 +333,20 @@ void Bitmap::HueChangeBlit(int x, int y, Bitmap const& src, Rect const& src_rect } Point Bitmap::TextDraw(Rect const& rect, int color, StringView text, Text::Alignment align) { - FontRef font = Font::Default(); - switch (align) { case Text::AlignLeft: return TextDraw(rect.x, rect.y, color, text); break; case Text::AlignCenter: { - Rect text_rect = Text::GetSize(*font, text); + auto f = font ? font : Font::Default(); + Rect text_rect = Text::GetSize(*f, text); int dx = rect.x + (rect.width - text_rect.width) / 2; return TextDraw(dx, rect.y, color, text); break; } case Text::AlignRight: { - Rect text_rect = Text::GetSize(*font, text); + auto f = font ? font : Font::Default(); + Rect text_rect = Text::GetSize(*f, text); int dx = rect.x + rect.width - text_rect.width; return TextDraw(dx, rect.y, color, text); break; @@ -354,30 +354,30 @@ Point Bitmap::TextDraw(Rect const& rect, int color, StringView text, Text::Align default: assert(false); } - return Point(); + return {}; } Point Bitmap::TextDraw(int x, int y, int color, StringView text, Text::Alignment align) { - auto font = Font::Default(); + auto f = font ? font : Font::Default(); auto system = Cache::SystemOrBlack(); - return Text::Draw(*this, x, y, *font, *system, color, text, align); + return Text::Draw(*this, x, y, *f, *system, color, text, align); } Point Bitmap::TextDraw(Rect const& rect, Color color, StringView text, Text::Alignment align) { - FontRef font = Font::Default(); - switch (align) { case Text::AlignLeft: return TextDraw(rect.x, rect.y, color, text); break; case Text::AlignCenter: { - Rect text_rect = Text::GetSize(*font, text); + auto f = font ? font : Font::Default(); + Rect text_rect = Text::GetSize(*f, text); int dx = rect.x + (rect.width - text_rect.width) / 2; return TextDraw(dx, rect.y, color, text); break; } case Text::AlignRight: { - Rect text_rect = Text::GetSize(*font, text); + auto f = font ? font : Font::Default(); + Rect text_rect = Text::GetSize(*f, text); int dx = rect.x + rect.width - text_rect.width; return TextDraw(dx, rect.y, color, text); break; @@ -385,12 +385,12 @@ Point Bitmap::TextDraw(Rect const& rect, Color color, StringView text, Text::Ali default: assert(false); } - return Point(); + return {}; } Point Bitmap::TextDraw(int x, int y, Color color, StringView text) { - auto font = Font::Default(); - return Text::Draw(*this, x, y, *font, color, text); + auto f = font ? font : Font::Default(); + return Text::Draw(*this, x, y, *f, color, text); } Rect Bitmap::TransformRectangle(const Transform& xform, const Rect& rect) { diff --git a/src/bitmap.h b/src/bitmap.h index b7be3f1db..e10fb1426 100644 --- a/src/bitmap.h +++ b/src/bitmap.h @@ -241,7 +241,7 @@ class Bitmap { Color GetColorAt(int x, int y) const; /** - * Draws text to bitmap using the Font::Default() font. + * Draws text to bitmap using the configured Font or the Font::Default() font. * * @param x x coordinate where text rendering starts. * @param y y coordinate where text rendering starts. @@ -253,7 +253,7 @@ class Bitmap { Point TextDraw(int x, int y, int color, StringView text, Text::Alignment align = Text::AlignLeft); /** - * Draws text to bitmap using the Font::Default() font. + * Draws text to bitmap using the configured Font or the Font::Default() font. * * @param rect bounding rectangle. * @param color system color index. @@ -264,7 +264,7 @@ class Bitmap { Point TextDraw(Rect const& rect, int color, StringView text, Text::Alignment align = Text::AlignLeft); /** - * Draws text to bitmap using the Font::Default() font. + * Draws text to bitmap using the configured Font or the Font::Default() font. * * @param x x coordinate where text rendering starts. * @param y y coordinate where text rendering starts. @@ -275,7 +275,7 @@ class Bitmap { Point TextDraw(int x, int y, Color color, StringView text); /** - * Draws text to bitmap using the Font::Default() font. + * Draws text to bitmap using the configured Font or the Font::Default() font. * * @param rect bounding rectangle. * @param color text color. @@ -594,6 +594,9 @@ class Bitmap { int bpp() const; int pitch() const; + FontRef GetFont() const; + void SetFont(FontRef font); + ImageOpacity ComputeImageOpacity() const; ImageOpacity ComputeImageOpacity(Rect rect) const; @@ -603,6 +606,7 @@ class Bitmap { ImageOpacity image_opacity = ImageOpacity::Alpha_8Bit; TileOpacity tile_opacity; Color bg_color, sh_color; + FontRef font; std::string filename; @@ -689,6 +693,14 @@ inline StringView Bitmap::GetFilename() const { return filename; } +inline FontRef Bitmap::GetFont() const { + return font; +} + +inline void Bitmap::SetFont(FontRef font) { + this->font = font; +} + inline int Bitmap::GetOriginalBpp() const { return original_bpp; } diff --git a/src/filefinder.cpp b/src/filefinder.cpp index cc6b9004c..e78ae5805 100644 --- a/src/filefinder.cpp +++ b/src/filefinder.cpp @@ -57,18 +57,9 @@ namespace { auto MOVIE_TYPES = { ".avi", ".mpg" }; #endif - std::string fonts_path; std::shared_ptr root_fs; FilesystemView game_fs; FilesystemView save_fs; - - constexpr const auto IMG_TYPES = Utils::MakeSvArray(".bmp", ".png", ".xyz"); - constexpr const auto MUSIC_TYPES = Utils::MakeSvArray( - ".opus", ".oga", ".ogg", ".wav", ".mid", ".midi", ".mp3", ".wma"); - constexpr const auto SOUND_TYPES = Utils::MakeSvArray( - ".opus", ".oga", ".ogg", ".wav", ".mp3", ".wma"); - constexpr const auto FONTS_TYPES = Utils::MakeSvArray(".fon", ".fnt", ".bdf", ".ttf", ".ttc", ".otf", ".woff2", ".woff"); - constexpr const auto TEXT_TYPES = Utils::MakeSvArray(".txt", ".csv", ""); // "" = Complete Filename (incl. extension) provided by the user } FilesystemView FileFinder::Game() { diff --git a/src/filefinder.h b/src/filefinder.h index f7cfe6229..7c181808a 100644 --- a/src/filefinder.h +++ b/src/filefinder.h @@ -37,6 +37,14 @@ * insensitive files paths. */ namespace FileFinder { + constexpr const auto IMG_TYPES = Utils::MakeSvArray(".bmp", ".png", ".xyz"); + constexpr const auto MUSIC_TYPES = Utils::MakeSvArray( + ".opus", ".oga", ".ogg", ".wav", ".mid", ".midi", ".mp3", ".wma"); + constexpr const auto SOUND_TYPES = Utils::MakeSvArray( + ".opus", ".oga", ".ogg", ".wav", ".mp3", ".wma"); + constexpr const auto FONTS_TYPES = Utils::MakeSvArray(".fon", ".fnt", ".bdf", ".ttf", ".ttc", ".otf", ".woff2", ".woff"); + constexpr const auto TEXT_TYPES = Utils::MakeSvArray(".txt", ".csv", ""); // "" = Complete Filename (incl. extension) provided by the user + /** * Quits FileFinder. */ diff --git a/src/font.cpp b/src/font.cpp index e52c43366..f893116d5 100644 --- a/src/font.cpp +++ b/src/font.cpp @@ -619,10 +619,24 @@ FontRef Font::CreateFtFont(Filesystem_Stream::InputStream is, int size, bool bol void Font::ResetDefault() { SetDefault(nullptr, true); SetDefault(nullptr, false); + +#ifdef HAVE_FREETYPE + const auto& cfg = Player::player_config; + if (!cfg.font1.Get().empty()) { + auto is = FileFinder::Root().OpenInputStream(cfg.font1.Get()); + SetDefault(CreateFtFont(std::move(is), cfg.font1_size.Get(), false, false), false); + } + + if (!cfg.font2.Get().empty()) { + auto is = FileFinder::Root().OpenInputStream(cfg.font2.Get()); + SetDefault(CreateFtFont(std::move(is), cfg.font2_size.Get(), false, false), true); + } +#endif } void Font::Dispose() { - ResetDefault(); + SetDefault(nullptr, true); + SetDefault(nullptr, false); #ifdef HAVE_FREETYPE if (library) { diff --git a/src/game_config.cpp b/src/game_config.cpp index 883c82591..94df55604 100644 --- a/src/game_config.cpp +++ b/src/game_config.cpp @@ -41,7 +41,12 @@ namespace { } void Game_ConfigPlayer::Hide() { - // Game specific settings unsupported +#ifndef HAVE_FREETYPE + font1.SetOptionVisible(false); + font1_size.SetOptionVisible(false); + font2.SetOptionVisible(false); + font2_size.SetOptionVisible(false); +#endif } void Game_ConfigVideo::Hide() { diff --git a/src/scene_logo.cpp b/src/scene_logo.cpp index e81f1ce4e..dc6188ccf 100644 --- a/src/scene_logo.cpp +++ b/src/scene_logo.cpp @@ -63,6 +63,8 @@ void Scene_Logo::Start() { void Scene_Logo::vUpdate() { if (current_logo_index == 0 && frame_counter == 0) { + Font::ResetDefault(); + if (!DetectGame()) { // async delay for emscripten return; diff --git a/src/scene_settings.cpp b/src/scene_settings.cpp index afbcd91f0..ea8aa9415 100644 --- a/src/scene_settings.cpp +++ b/src/scene_settings.cpp @@ -258,6 +258,12 @@ void Scene_Settings::vUpdate() { case Window_Settings::eInputListButtonsDeveloper: UpdateOptions(); break; + case Window_Settings::eEngineFont1: + UpdateFont(false); + break; + case Window_Settings::eEngineFont2: + UpdateFont(true); + break; case Window_Settings::eInputButtonOption: UpdateButtonOption(); break; @@ -442,6 +448,31 @@ void Scene_Settings::UpdateOptions() { } } +void Scene_Settings::UpdateFont(bool mincho) { + auto fs = Game_Config::GetFontFilesystem(); + + help_window2->SetText("The quick brown fox jumps over the lazy dog 1234567890."); + + int index = options_window->GetIndex(); + if (index == 0) { + help_window2->SetFont(Font::DefaultBitmapFont(mincho)); + help_window2->SetVisible(true); + } else if (index >= options_window->GetRowMax() - 2) { + // Size or browse + } else { + auto is = fs.OpenInputStream(options_window->GetCurrentOption().text); + if (is) { + auto font = Font::CreateFtFont(std::move(is), options_window->font_size.Get(), false, false); + if (font) { + help_window2->SetFont(font); + help_window2->SetVisible(true); + } + } + } + + UpdateOptions(); +} + void Scene_Settings::UpdateButtonOption() { if (Input::IsTriggered(Input::DECISION)) { switch (input_mode_window->GetIndex()) { diff --git a/src/scene_settings.h b/src/scene_settings.h index 0bd902ec0..f62704ea1 100644 --- a/src/scene_settings.h +++ b/src/scene_settings.h @@ -63,6 +63,7 @@ class Scene_Settings : public Scene { void UpdateMain(); void UpdateOptions(); + void UpdateFont(bool mincho); void UpdateButtonOption(); void UpdateButtonAdd(); void UpdateButtonRemove(); diff --git a/src/window.h b/src/window.h index 0968b4931..abefad8f5 100644 --- a/src/window.h +++ b/src/window.h @@ -20,6 +20,7 @@ // Headers #include "system.h" +#include "bitmap.h" #include "drawable.h" #include "rect.h" @@ -61,6 +62,8 @@ class Window : public Drawable { void SetWidth(int nwidth); int GetHeight() const; void SetHeight(int nheight); + int GetRightX() const; + int GetBottomY() const; int GetOx() const; void SetOx(int nox); int GetOy() const; @@ -80,6 +83,9 @@ class Window : public Drawable { void SetOpenAnimation(int frames); void SetCloseAnimation(int frames); + FontRef GetFont() const; + void SetFont(FontRef font); + bool IsOpening() const; bool IsClosing() const; bool IsOpeningOrClosing() const; @@ -88,6 +94,7 @@ class Window : public Drawable { virtual bool IsSystemGraphicUpdateAllowed() const; unsigned long ID; + FontRef font; BitmapRef windowskin, contents; bool stretch = true; Rect cursor_rect; @@ -153,6 +160,7 @@ inline BitmapRef Window::GetContents() const { inline void Window::SetContents(BitmapRef const& ncontents) { contents = ncontents; + contents->SetFont(font); } inline bool Window::GetStretch() const { @@ -236,6 +244,14 @@ inline int Window::GetHeight() const { return height; } +inline int Window::GetRightX() const { + return x + width; +} + +inline int Window::GetBottomY() const { + return y + height; +} + inline int Window::GetOx() const { return ox; } @@ -304,4 +320,15 @@ inline bool Window::IsSystemGraphicUpdateAllowed() const { return !IsClosing(); } +inline FontRef Window::GetFont() const { + return font; +} + +inline void Window::SetFont(FontRef font) { + this->font = font; + if (contents) { + contents->SetFont(font); + } +} + #endif diff --git a/src/window_help.cpp b/src/window_help.cpp index 3641a7476..1417a6bc7 100644 --- a/src/window_help.cpp +++ b/src/window_help.cpp @@ -61,7 +61,7 @@ void Window_Help::AddText(std::string text, int color, Text::Alignment align, bo // Special handling for proportional fonts: If the "normal" space is already small do not half it again if (nextpos != decltype(text)::npos) { - int space_width = Text::GetSize(*Font::Default(), " ").width; + int space_width = Text::GetSize(*(font ? font : Font::Default()), " ").width; if (halfwidthspace && space_width >= 6) { text_x_offset += space_width / 2; diff --git a/src/window_settings.cpp b/src/window_settings.cpp index 4b748ea40..7313c8f6b 100644 --- a/src/window_settings.cpp +++ b/src/window_settings.cpp @@ -139,6 +139,12 @@ void Window_Settings::Refresh() { case eEngine: RefreshEngine(); break; + case eEngineFont1: + RefreshEngineFont(false); + break; + case eEngineFont2: + RefreshEngineFont(true); + break; case eLicense: RefreshLicense(); break; @@ -372,17 +378,82 @@ void Window_Settings::RefreshEngine() { auto& cfg = Player::player_config; // FIXME: Binding &cfg is not needed and generates a warning but MSVC requires it -#ifdef _MSC_VER + AddOption(cfg.font1, [this, &cfg]() { + font_size.Set(cfg.font1_size.Get()); + Push(eEngineFont1); + }); + if (Main_Data::game_system->GetFontId() == lcf::rpg::System::Font_gothic) { + options.back().text += " [In use]"; + } + + AddOption(cfg.font2, [this, &cfg]() { + font_size.Set(cfg.font2_size.Get()); + Push(eEngineFont2); + }); + if (Main_Data::game_system->GetFontId() == lcf::rpg::System::Font_mincho) { + options.back().text += " [In use]"; + } + + AddOption(cfg.show_startup_logos, [this, &cfg](){ cfg.show_startup_logos.Set(static_cast(GetCurrentOption().current_value)); }); AddOption(cfg.settings_autosave, [&cfg](){ cfg.settings_autosave.Toggle(); }); AddOption(cfg.settings_in_title, [&cfg](){ cfg.settings_in_title.Toggle(); }); AddOption(cfg.settings_in_menu, [&cfg](){ cfg.settings_in_menu.Toggle(); }); - AddOption(cfg.show_startup_logos, [this, &cfg](){ cfg.show_startup_logos.Set(static_cast(GetCurrentOption().current_value)); }); -#else - AddOption(cfg.settings_autosave, [](){ cfg.settings_autosave.Toggle(); }); - AddOption(cfg.settings_in_title, [](){ cfg.settings_in_title.Toggle(); }); - AddOption(cfg.settings_in_menu, [](){ cfg.settings_in_menu.Toggle(); }); - AddOption(cfg.show_startup_logos, [this](){ cfg.show_startup_logos.Set(static_cast(GetCurrentOption().current_value)); }); -#endif +} + +void Window_Settings::RefreshEngineFont(bool mincho) { + auto fs = Game_Config::GetFontFilesystem(); + + if (!fs) { + Pop(); + } + + fs.ClearCache(); + + auto& cfg = Player::player_config; + + auto& setting = mincho ? cfg.font2 : cfg.font1; + + AddOption(MenuItem("", "Use the built-in pixel font", setting.Get().empty() ? "[x]" : ""), [this, &setting, mincho]() { + Font::SetDefault(nullptr, mincho); + setting.Set(""); + Pop(); + }); + + //std::string font_lower = Utils::LowerCase(Audio().GetFluidsynthSoundfont()); + + auto list = fs.ListDirectory(); + assert(list); + for (const auto& item: *list) { + bool is_font = std::any_of(FileFinder::FONTS_TYPES.begin(), FileFinder::FONTS_TYPES.end(), [&item](const auto& ext) { + return StringView(item.first).ends_with(ext); + }); + + if (item.second.type == DirectoryTree::FileType::Regular && is_font) { + /*AddOption(MenuItem(item.second.name, "Use this custom soundfont", StringView(sf_lower).ends_with(item.first) ? "[x]" : ""), [fs, item]() { + Audio().SetFluidsynthSoundfont(FileFinder::MakePath(fs.GetFullPath(), item.second.name)); + });*/ + AddOption(MenuItem(item.second.name, "Use this font", ""), [=, &cfg, &setting]() mutable { + auto is = fs.OpenInputStream(item.second.name); + if (is) { + auto font = Font::CreateFtFont(std::move(is), font_size.Get(), false, false); + if (font) { + setting.Set(FileFinder::MakePath(fs.GetFullPath(), item.second.name)); + auto& setting_size = mincho ? cfg.font2_size : cfg.font1_size; + setting_size.Set(font->GetCurrentStyle().size); + Font::SetDefault(font, mincho); + Pop(); + } + } + }); + } + } + + AddOption(font_size, [this]() mutable { + font_size.Set(GetCurrentOption().current_value); + }); + + AddOption(MenuItem("", "Open the font directory in a file browser", ""), [fs]() { DisplayUi->OpenURL(fs.GetFullPath()); }); + } void Window_Settings::RefreshLicense() { From 988719eebc7b896c78be689873dc84247ff67030 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Wed, 6 Dec 2023 21:27:20 +0100 Subject: [PATCH 009/222] Add live reload of Fluidsynth soundfont Does a program reset, won't work for all MIDI files. Playing a new file will fix it. --- src/audio.cpp | 2 ++ src/audio_midi.cpp | 12 +++++++ src/audio_midi.h | 4 ++- src/decoder_fluidsynth.cpp | 66 ++++++++++++++++++++++++++++++-------- src/decoder_fluidsynth.h | 1 + 5 files changed, 70 insertions(+), 15 deletions(-) diff --git a/src/audio.cpp b/src/audio.cpp index fd6e720ec..e177a3dbe 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -17,6 +17,7 @@ // Headers #include "audio.h" +#include "audio_midi.h" #include "system.h" #include "baseui.h" #include "player.h" @@ -143,4 +144,5 @@ std::string AudioInterface::GetFluidsynthSoundfont() const { void AudioInterface::SetFluidsynthSoundfont(StringView sf) { cfg.soundfont.Set(ToString(sf)); + MidiDecoder::ChangeFluidsynthSoundfont(sf); } diff --git a/src/audio_midi.cpp b/src/audio_midi.cpp index a21b4603a..8ba50c593 100644 --- a/src/audio_midi.cpp +++ b/src/audio_midi.cpp @@ -141,6 +141,18 @@ bool MidiDecoder::CheckFluidsynth(std::string& status_message) { return works.fluidsynth; } +void MidiDecoder::ChangeFluidsynthSoundfont(StringView sf_path) { + if (!works.fluidsynth || works.fluidsynth_status.empty()) { + // Fluidsynth was not initialized yet or failed, will use the path from the config automatically + works.fluidsynth = true; + CreateFluidsynth(true); + return; + } + + // Was initialized before + works.fluidsynth = FluidSynthDecoder::ChangeGlobalSoundfont(sf_path, works.fluidsynth_status); +} + bool MidiDecoder::CheckWildMidi(std::string &status_message) { if (works.wildmidi && works.wildmidi_status.empty()) { CreateWildMidi(true); diff --git a/src/audio_midi.h b/src/audio_midi.h index 39ca9fa2c..8882cc35f 100644 --- a/src/audio_midi.h +++ b/src/audio_midi.h @@ -212,10 +212,12 @@ class MidiDecoder { */ static bool CheckFluidsynth(std::string& status_message); + static void ChangeFluidsynthSoundfont(StringView sf_path); + /** * Checks if WildMidi works. * - * @param status_message Current Fluidsynth status + * @param status_message Current WildMidi status * @return true: Works, false: Not working */ static bool CheckWildMidi(std::string& status_message); diff --git a/src/decoder_fluidsynth.cpp b/src/decoder_fluidsynth.cpp index 9fb8444a4..e4584d310 100644 --- a/src/decoder_fluidsynth.cpp +++ b/src/decoder_fluidsynth.cpp @@ -115,20 +115,11 @@ namespace { #if defined(HAVE_FLUIDSYNTH) && FLUIDSYNTH_VERSION_MAJOR > 1 fluid_sfloader_t* global_loader; // owned by global_settings #endif + int global_synth_id = -1; int instances = 0; } -static fluid_synth_t* create_synth(std::string& status_message) { - fluid_synth_t* syn = new_fluid_synth(global_settings.get()); - if (!syn) { - status_message = "new_fluid_synth failed"; - return nullptr; - } - -#if defined(HAVE_FLUIDSYNTH) && FLUIDSYNTH_VERSION_MAJOR > 1 - fluid_synth_add_sfloader(syn, global_loader); -#endif - +static bool load_default_sf(std::string& status_message, fluid_synth_t* syn, int& synth_id) { // Attempt loading a soundfont std::vector sf_paths; std::string preferred_soundfont = Audio().GetFluidsynthSoundfont(); @@ -165,8 +156,14 @@ static fluid_synth_t* create_synth(std::string& status_message) { } bool sf_load_success = false; + int old_synth_id = synth_id; for (const auto& sf_name: sf_paths) { - if (fluid_synth_sfload(syn, sf_name.c_str(), 1) != FLUID_FAILED) { + int new_synth_id = fluid_synth_sfload(syn, sf_name.c_str(), 1); + if (new_synth_id != FLUID_FAILED) { + synth_id = new_synth_id; + if (old_synth_id > 0) { + fluid_synth_sfunload(syn, old_synth_id, 1); + } sf_load_success = true; status_message = fmt::format("Using soundfont {}", sf_name); break; @@ -175,6 +172,24 @@ static fluid_synth_t* create_synth(std::string& status_message) { if (!sf_load_success) { status_message = "Could not load soundfont."; + return false; + } + + return true; +} + +static fluid_synth_t* create_synth(std::string& status_message, int& synth_id) { + fluid_synth_t* syn = new_fluid_synth(global_settings.get()); + if (!syn) { + status_message = "new_fluid_synth failed"; + return nullptr; + } + +#if defined(HAVE_FLUIDSYNTH) && FLUIDSYNTH_VERSION_MAJOR > 1 + fluid_synth_add_sfloader(syn, global_loader); +#endif + + if (!load_default_sf(status_message, syn, synth_id)) { return nullptr; } @@ -190,7 +205,8 @@ FluidSynthDecoder::FluidSynthDecoder() { // Sharing is only not possible when a Midi is played as a SE (unlikely) if (instances > 1) { std::string error_message; - local_synth = create_synth(error_message); + int unused = -1; + local_synth = create_synth(error_message, unused); if (!local_synth) { // unlikely, the SF was already allocated once Output::Debug("FluidSynth failed: {}", error_message); @@ -247,7 +263,8 @@ bool FluidSynthDecoder::Initialize(std::string& status_message) { vio_open, vio_read, vio_seek, vio_tell, vio_close); #endif - global_synth.reset(create_synth(status_message)); + global_synth_id = -1; + global_synth.reset(create_synth(status_message, global_synth_id)); if (!global_synth) { return false; } @@ -265,6 +282,27 @@ void FluidSynthDecoder::ResetState() { global_settings.reset(); } +bool FluidSynthDecoder::ChangeGlobalSoundfont(StringView sf_path, std::string& status_message) { + if (!global_synth) { + return false; + } + + if (sf_path.empty()) { + return load_default_sf(status_message, global_synth.get(), global_synth_id); + } + + int new_global_id = fluid_synth_sfload(global_synth.get(), ToString(sf_path).c_str(), 1); + if (new_global_id != FLUID_FAILED) { + assert(fluid_synth_sfunload(global_synth.get(), global_synth_id, 1) == FLUID_OK); + global_synth_id = new_global_id; + status_message = fmt::format("Using soundfont {}", sf_path); + return true; + } + + status_message = "Could not load soundfont."; + return false; +} + int FluidSynthDecoder::FillBuffer(uint8_t* buffer, int length) { auto* instance_synth = GetSynthInstance(); diff --git a/src/decoder_fluidsynth.h b/src/decoder_fluidsynth.h index 3c90276cc..486b7550f 100644 --- a/src/decoder_fluidsynth.h +++ b/src/decoder_fluidsynth.h @@ -46,6 +46,7 @@ class FluidSynthDecoder : public MidiDecoder { static bool Initialize(std::string& status_message); static void ResetState(); + static bool ChangeGlobalSoundfont(StringView sf_path, std::string& status_message); int FillBuffer(uint8_t* buffer, int length) override; From 62533fcb92456014a6561c3eb1d97ee73029e8ca Mon Sep 17 00:00:00 2001 From: Ghabry Date: Wed, 6 Dec 2023 22:12:47 +0100 Subject: [PATCH 010/222] Message Window: Only change the font when a new page starts Prevents glitches when switching the font in the settings while rendering --- src/window_message.cpp | 20 ++++++++++++-------- src/window_message.h | 2 ++ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/window_message.cpp b/src/window_message.cpp index 4c00c6e91..9c46aff0b 100644 --- a/src/window_message.cpp +++ b/src/window_message.cpp @@ -265,6 +265,12 @@ void Window_Message::InsertNewPage() { prev_char_printable = false; prev_char_waited = true; + if (GetFont()) { + page_font = GetFont(); + } else { + page_font = Font::Default(); + } + // Position the message box vertically // Game_Message::GetRealPosition() specify top/middle/bottom if (Game_Message::GetRealPosition() == 0) { @@ -318,7 +324,6 @@ void Window_Message::InsertNewPage() { ShowGoldWindow(); } } - } void Window_Message::InsertNewLine() { @@ -459,7 +464,6 @@ void Window_Message::UpdateMessage() { } auto system = Cache::SystemOrBlack(); - auto font = Font::Default(); while (true) { const auto* end = text.data() + text.size(); @@ -471,7 +475,7 @@ void Window_Message::UpdateMessage() { } if (!shape_ret.empty()) { - if (!DrawGlyph(*font, *system, shape_ret[0])) { + if (!DrawGlyph(*page_font, *system, shape_ret[0])) { continue; } @@ -498,7 +502,7 @@ void Window_Message::UpdateMessage() { const auto ch = tret.ch; if (tret.is_exfont) { - if (!DrawGlyph(*font, *system, ch, true)) { + if (!DrawGlyph(*page_font, *system, ch, true)) { text_index = text_prev; } continue; @@ -573,7 +577,7 @@ void Window_Message::UpdateMessage() { break; case '_': // Insert half size space - contents_x += Text::GetSize(*Font::Default(), " ").width / 2; + contents_x += Text::GetSize(*page_font, " ").width / 2; DebugLogText("{}: MSG HalfWait \\_"); SetWaitForCharacter(1); break; @@ -631,7 +635,7 @@ void Window_Message::UpdateMessage() { continue; } - if (font->CanShape()) { + if (page_font->CanShape()) { assert(shape_ret.empty()); auto text_index_shape = text_index; @@ -657,10 +661,10 @@ void Window_Message::UpdateMessage() { text32 += tret.ch; } - shape_ret = font->Shape(text32); + shape_ret = page_font->Shape(text32); continue; } else { - if (!DrawGlyph(*font, *system, ch, false)) { + if (!DrawGlyph(*page_font, *system, ch, false)) { text_index = text_prev; continue; } diff --git a/src/window_message.h b/src/window_message.h index 2bfb2b65a..8d9054696 100644 --- a/src/window_message.h +++ b/src/window_message.h @@ -179,6 +179,8 @@ class Window_Message: public Window_Selectable { bool prev_char_waited = true; /** Was the previous character printable? */ bool prev_char_printable = false; + /** Active font for the current page */ + FontRef page_font; /** Used by the number input event. */ std::unique_ptr number_input_window; From 59cbcb0a11e3d1f295992b9f4b22a5642cfc6474 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Wed, 6 Dec 2023 22:20:08 +0100 Subject: [PATCH 011/222] Window Help: Support optional scrolling when the text does not fit in the window --- src/filefinder.h | 2 +- src/game_config.h | 2 +- src/scene_settings.cpp | 3 +++ src/text.cpp | 1 - src/window_help.cpp | 51 ++++++++++++++++++++++++++++++++++++++++++ src/window_help.h | 30 +++++++++++++++++++++++++ 6 files changed, 86 insertions(+), 3 deletions(-) diff --git a/src/filefinder.h b/src/filefinder.h index 7c181808a..3bd774afc 100644 --- a/src/filefinder.h +++ b/src/filefinder.h @@ -37,7 +37,7 @@ * insensitive files paths. */ namespace FileFinder { - constexpr const auto IMG_TYPES = Utils::MakeSvArray(".bmp", ".png", ".xyz"); + constexpr const auto IMG_TYPES = Utils::MakeSvArray(".bmp", ".png", ".xyz"); constexpr const auto MUSIC_TYPES = Utils::MakeSvArray( ".opus", ".oga", ".ogg", ".wav", ".mid", ".midi", ".mp3", ".wma"); constexpr const auto SOUND_TYPES = Utils::MakeSvArray( diff --git a/src/game_config.h b/src/game_config.h index c22151448..0118960c0 100644 --- a/src/game_config.h +++ b/src/game_config.h @@ -89,7 +89,7 @@ struct Game_ConfigVideo { Utils::MakeSvArray("nearest", "integer", "bilinear"), Utils::MakeSvArray("Scale to screen size (Causes scaling artifacts)", "Scale to multiple of the game resolution", "Like Nearest, but output is blurred to avoid artifacts")}; BoolConfigParam stretch{ "Stretch", "Stretch to the width of the window/screen", "Video", "Stretch", false }; - BoolConfigParam pause_when_focus_lost{ "Pause when focus lost", "Pause the execution when the program is in the background", "Video", "PauseWhenFocusLost", true }; + BoolConfigParam pause_when_focus_lost{ "Pause when focus lost", "Pause the program when it is in the background", "Video", "PauseWhenFocusLost", true }; BoolConfigParam touch_ui{ "Touch Ui", "Display the touch ui", "Video", "TouchUi", true }; EnumConfigParam game_resolution{ "Resolution", "Game resolution. Changes require a restart.", "Video", "GameResolution", GameResolution::Original, Utils::MakeSvArray("Original (Recommended)", "Widescreen (Experimental)", "Ultrawide (Experimental)"), diff --git a/src/scene_settings.cpp b/src/scene_settings.cpp index ea8aa9415..f74c8472f 100644 --- a/src/scene_settings.cpp +++ b/src/scene_settings.cpp @@ -88,10 +88,12 @@ void Scene_Settings::CreateMainWindow() { void Scene_Settings::CreateOptionsWindow() { help_window = std::make_unique(Player::menu_offset_x, 0, MENU_WIDTH, 32); + help_window->SetAnimation(Window_Help::Animation::Loop); options_window = std::make_unique(Player::menu_offset_x + 32, 32, MENU_WIDTH - 64, Player::screen_height - 32 * 2); options_window->SetHelpWindow(help_window.get()); help_window2 = std::make_unique(Player::menu_offset_x, options_window->GetBottomY(), MENU_WIDTH, 32); + help_window2->SetAnimation(Window_Help::Animation::Loop); options_window->help_window2 = help_window2.get(); input_window = std::make_unique(Player::menu_offset_x, 32, MENU_WIDTH, Player::screen_height - 32 * 3); @@ -200,6 +202,7 @@ void Scene_Settings::vUpdate() { main_window->Update(); help_window->Update(); + help_window2->Update(); options_window->Update(); input_window->Update(); input_mode_window->Update(); diff --git a/src/text.cpp b/src/text.cpp index 3154b20e3..e8050f6de 100644 --- a/src/text.cpp +++ b/src/text.cpp @@ -75,7 +75,6 @@ Point Text::Draw(Bitmap& dest, const int x, const int y, const Font& font, const dst_rect.y = y; dst_rect.width += 1; dst_rect.height += 1; // Need place for shadow - if (dst_rect.IsOutOfBounds(dest.GetWidth(), dest.GetHeight())) return { 0, 0 }; const int iy = dst_rect.y; const int ix = dst_rect.x; diff --git a/src/window_help.cpp b/src/window_help.cpp index 1417a6bc7..b080537c1 100644 --- a/src/window_help.cpp +++ b/src/window_help.cpp @@ -39,6 +39,9 @@ void Window_Help::SetText(std::string text, int color, Text::Alignment align, bo this->text = std::move(text); this->align = align; this->color = color; + this->text_x_scroll = 0; + this->text_x_scroll_dir = false; + this->text_x_width = text_x_offset; } } @@ -72,3 +75,51 @@ void Window_Help::AddText(std::string text, int color, Text::Alignment align, bo } } } + +void Window_Help::SetAnimation(Window_Help::Animation animation) { + text_x_scroll = 0; + this->animation = animation; +} + +void Window_Help::UpdateScroll() { + if (animation == Animation::None) { + return; + } + + if (text_x_width <= contents->GetWidth()) { + // no need to scroll + return; + } + + if (animation == Animation::BackAndForth) { + text_x_scroll += text_x_scroll_dir ? 1 : -1; + + if ((!text_x_scroll_dir && (text_x_width + text_x_scroll) == contents->GetWidth()) || + (text_x_scroll_dir && text_x_scroll == 0)) { + text_x_scroll_dir = !text_x_scroll_dir; + } + + contents->Clear(); + text_x_offset = text_x_scroll; + AddText(text, color, align, true); + } else if (animation == Animation::Loop) { + --text_x_scroll; + + const int gap_size = 18; + + if (text_x_scroll == -text_x_width - gap_size) { + text_x_scroll = 0; + } + + contents->Clear(); + text_x_offset = text_x_scroll; + AddText(text, color, align, true); + text_x_offset += gap_size; + AddText(text, color, align, true); + } +} + +void Window_Help::Update() { + Window_Base::Update(); + UpdateScroll(); +} diff --git a/src/window_help.h b/src/window_help.h index 5965e3a38..4e290c439 100644 --- a/src/window_help.h +++ b/src/window_help.h @@ -30,6 +30,15 @@ class Window_Help : public Window_Base { public: + enum class Animation { + /** Never scroll */ + None, + /** Scroll left and when the end of the text is reached scroll right, then repeat */ + BackAndForth, + /** Scroll left and repeat the text in an endless loop (also known as marquee) */ + Loop + }; + /** * Constructor. */ @@ -44,6 +53,18 @@ class Window_Help : public Window_Base { */ void SetText(std::string text, int color = Font::ColorDefault, Text::Alignment align = Text::AlignLeft, bool halfwidthspace = true); + /** + * Sets the scrolling animation when the text is larger than the help window. + * + * @param animation + */ + void SetAnimation(Animation animation); + + /** + * Updates the scrolling animation if set. + */ + void Update() override; + /** * Clears the window */ @@ -60,6 +81,8 @@ class Window_Help : public Window_Base { void AddText(std::string text, int color = Font::ColorDefault, Text::Alignment align = Text::AlignLeft, bool halfwidthspace = true); private: + void UpdateScroll(); + /** Text to draw. */ std::string text; /** Color of Text to draw. */ @@ -68,6 +91,13 @@ class Window_Help : public Window_Base { Text::Alignment align; /** Current text position */ int text_x_offset = 0; + /** Width of the text */ + int text_x_width = 0; + /** Current scroll position of the animation */ + int text_x_scroll = 0; + bool text_x_scroll_dir = false; + + Animation animation = Animation::None; }; #endif From c43c5af728371df9dde14c8d12d881f10649fe42 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Wed, 6 Dec 2023 23:40:02 +0100 Subject: [PATCH 012/222] Settings: Add a font preview and mark the active font --- src/font.cpp | 4 ++++ src/font.h | 5 +++++ src/scene_settings.cpp | 17 ++++++++++++++--- src/window_settings.cpp | 21 +++++++++++++++------ src/window_settings.h | 28 ++++++++++++++++++++++++++++ 5 files changed, 66 insertions(+), 9 deletions(-) diff --git a/src/font.cpp b/src/font.cpp index f893116d5..d3c9dba1a 100644 --- a/src/font.cpp +++ b/src/font.cpp @@ -656,6 +656,10 @@ Font::Font(StringView name, int size, bool bold, bool italic) current_style = original_style; } +StringView Font::GetName() const { + return name; +} + Rect Font::GetSize(char32_t glyph) const { if (EP_UNLIKELY(Utils::IsControlCharacter(glyph))) { if (glyph == '\n') { diff --git a/src/font.h b/src/font.h index 8bdb2d89e..e563d62ef 100644 --- a/src/font.h +++ b/src/font.h @@ -98,6 +98,11 @@ class Font { virtual ~Font() = default; + /** + * @return Name of the font + */ + StringView GetName() const; + /** * Determines the size of a bitmap required to render a single character. * The dimensions of the Rect describe a bounding box to fit the text. diff --git a/src/scene_settings.cpp b/src/scene_settings.cpp index f74c8472f..d12c305ea 100644 --- a/src/scene_settings.cpp +++ b/src/scene_settings.cpp @@ -454,19 +454,30 @@ void Scene_Settings::UpdateOptions() { void Scene_Settings::UpdateFont(bool mincho) { auto fs = Game_Config::GetFontFilesystem(); - help_window2->SetText("The quick brown fox jumps over the lazy dog 1234567890."); + auto& last_index = options_window->GetFrame().scratch; int index = options_window->GetIndex(); + if (last_index == index) { + UpdateOptions(); + return; + } + last_index = index; + if (index == 0) { + help_window2->Clear(); help_window2->SetFont(Font::DefaultBitmapFont(mincho)); help_window2->SetVisible(true); - } else if (index >= options_window->GetRowMax() - 2) { - // Size or browse + } else if (index >= options_window->GetRowMax() - 3) { + // Size, sample or browse + help_window2->Clear(); + help_window2->SetFont(nullptr); + help_window2->SetVisible(true); } else { auto is = fs.OpenInputStream(options_window->GetCurrentOption().text); if (is) { auto font = Font::CreateFtFont(std::move(is), options_window->font_size.Get(), false, false); if (font) { + help_window2->Clear(); help_window2->SetFont(font); help_window2->SetVisible(true); } diff --git a/src/window_settings.cpp b/src/window_settings.cpp index 7313c8f6b..02a024487 100644 --- a/src/window_settings.cpp +++ b/src/window_settings.cpp @@ -381,6 +381,7 @@ void Window_Settings::RefreshEngine() { AddOption(cfg.font1, [this, &cfg]() { font_size.Set(cfg.font1_size.Get()); Push(eEngineFont1); + GetFrame().scratch = -1; }); if (Main_Data::game_system->GetFontId() == lcf::rpg::System::Font_gothic) { options.back().text += " [In use]"; @@ -389,6 +390,7 @@ void Window_Settings::RefreshEngine() { AddOption(cfg.font2, [this, &cfg]() { font_size.Set(cfg.font2_size.Get()); Push(eEngineFont2); + GetFrame().scratch = -1; }); if (Main_Data::game_system->GetFontId() == lcf::rpg::System::Font_mincho) { options.back().text += " [In use]"; @@ -413,13 +415,18 @@ void Window_Settings::RefreshEngineFont(bool mincho) { auto& setting = mincho ? cfg.font2 : cfg.font1; + auto set_help2 = [this]() { + options.back().help2 = ToString(sample_text.GetDescriptions()[static_cast(sample_text.Get())]); + }; + AddOption(MenuItem("", "Use the built-in pixel font", setting.Get().empty() ? "[x]" : ""), [this, &setting, mincho]() { Font::SetDefault(nullptr, mincho); setting.Set(""); Pop(); }); + set_help2(); - //std::string font_lower = Utils::LowerCase(Audio().GetFluidsynthSoundfont()); + std::string font_lower = Utils::LowerCase(Font::Default(mincho)->GetName()); auto list = fs.ListDirectory(); assert(list); @@ -429,10 +436,7 @@ void Window_Settings::RefreshEngineFont(bool mincho) { }); if (item.second.type == DirectoryTree::FileType::Regular && is_font) { - /*AddOption(MenuItem(item.second.name, "Use this custom soundfont", StringView(sf_lower).ends_with(item.first) ? "[x]" : ""), [fs, item]() { - Audio().SetFluidsynthSoundfont(FileFinder::MakePath(fs.GetFullPath(), item.second.name)); - });*/ - AddOption(MenuItem(item.second.name, "Use this font", ""), [=, &cfg, &setting]() mutable { + AddOption(MenuItem(item.second.name, "Use this font", StringView(font_lower).ends_with(item.first) ? "[x]" : ""), [=, &cfg, &setting]() mutable { auto is = fs.OpenInputStream(item.second.name); if (is) { auto font = Font::CreateFtFont(std::move(is), font_size.Get(), false, false); @@ -445,6 +449,7 @@ void Window_Settings::RefreshEngineFont(bool mincho) { } } }); + set_help2(); } } @@ -452,8 +457,12 @@ void Window_Settings::RefreshEngineFont(bool mincho) { font_size.Set(GetCurrentOption().current_value); }); - AddOption(MenuItem("", "Open the font directory in a file browser", ""), [fs]() { DisplayUi->OpenURL(fs.GetFullPath()); }); + AddOption(sample_text, [this]() { + sample_text.Set(static_cast(GetCurrentOption().current_value)); + }); + set_help2(); + AddOption(MenuItem("", "Open the font directory in a file browser", ""), [fs]() { DisplayUi->OpenURL(fs.GetFullPath()); }); } void Window_Settings::RefreshLicense() { diff --git a/src/window_settings.h b/src/window_settings.h index 7270ea2fe..4a2d03460 100644 --- a/src/window_settings.h +++ b/src/window_settings.h @@ -164,6 +164,34 @@ class Window_Settings : public Window_Selectable { void SavePosition(); void RestorePosition(); + + // The sample text for the font preview. This option is not saved. + enum class SampleText { + English, + French, + German, + Chinese, + Japanese_Hiragana, + Japanese_Kanji, + Korean, + Diacritics + }; + + EnumConfigParam sample_text{ + "", "Text to show in the preview", "", "", SampleText::English, + Utils::MakeSvArray("English", "French", "German", "Chinese", "Japanese (Hiragana)", "Japanese (Kanji)", "Korean", "Various Diacritics"), + Utils::MakeSvArray("", "", "", "", "", "", "", ""), + Utils::MakeSvArray( + "The quick brown fox jumps over the lazy dog 1234567890.?!", + "Voix ambiguë d’un cœur qui au zéphyr préfère les jattes de kiwis", + "Victor jagt zwölf Boxkämpfer quer über den großen Sylter Deich", + "天地玄黃宇宙洪荒。日月盈昃辰宿列張。寒來暑往秋收冬藏。閏餘成歲律呂調陽。雲騰致雨露結為霜。金生麗水玉出崑岡。劍號巨闕珠稱夜光。果珍李柰菜重芥薑。海鹹河淡鱗潛羽翔。", + "いろはにほへと ちりぬるを わかよたれそ つねならむ うゐのおくやま けふこえて あさきゆめみし ゑひもせす", + "色は匂えど散りぬるを我が世誰ぞ常ならん有為の奥山今日越えて浅き夢見じ酔いもせず", + "키스의 고유조건은 입술끼리 만나야 하고 특별한 기술은 필요치 않다", + "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ" + ) + }; }; #endif From 1069008a9a886ffe7a0d8f6923a988f15cd20b44 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Wed, 20 Dec 2023 20:16:23 +0100 Subject: [PATCH 013/222] Emscripten: Add Soundfont and Font upload Currently lacking sanity checks --- resources/emscripten/emscripten-post.js | 74 ++++++++++++++++++------- src/decoder_fluidsynth.cpp | 8 ++- src/platform/emscripten/interface.cpp | 74 +++++++++++++++++++++++++ src/platform/emscripten/interface.h | 8 ++- src/platform/sdl/sdl2_ui.cpp | 4 +- src/scene_settings.cpp | 4 ++ src/scene_settings.h | 1 + src/window_settings.cpp | 12 ++++ 8 files changed, 162 insertions(+), 23 deletions(-) diff --git a/resources/emscripten/emscripten-post.js b/resources/emscripten/emscripten-post.js index 7277533b8..1344420f7 100644 --- a/resources/emscripten/emscripten-post.js +++ b/resources/emscripten/emscripten-post.js @@ -14,7 +14,7 @@ if (Module.saveFs === undefined) { } Module.initApi = function() { - Module.api_private.download_js = function(buffer, size, filename) { + Module.api_private.download_js = function (buffer, size, filename) { const blob = new Blob([Module.HEAPU8.slice(buffer, buffer + size)]); const link = document.createElement('a'); link.href = window.URL.createObjectURL(blob); @@ -23,28 +23,62 @@ Module.initApi = function() { link.remove(); } - Module.api_private.uploadSavegame_js = function(slot) { - let saveFile = document.getElementById('easyrpg_saveFile'); - if (saveFile == null) { - saveFile = document.createElement('input'); - saveFile.type = 'file'; - saveFile.id = 'easyrpg_saveFile'; - saveFile.style.display = 'none'; - saveFile.addEventListener('change', function(evt) { - const save = evt.target.files[0]; + Module.api_private.createInputElement_js = function (id, event) { + let file = document.getElementById(id); + if (file == null) { + file = document.createElement('input'); + file.type = 'file'; + file.id = id; + file.style.display = 'none'; + file.addEventListener('change', function (evt) { + const selected_file = evt.target.files[0]; const reader = new FileReader(); - reader.onload = function (file) { - const result = new Uint8Array(file.currentTarget.result); - var buf = Module._malloc(result.length); - Module.HEAPU8.set(result, buf); - Module.api_private.uploadSavegameStep2(slot, buf, result.length); - Module._free(buf); - Module.api.refreshScene(); - }; - reader.readAsArrayBuffer(save); + reader.onload = function(file) { + event(file, selected_file.name); + } + reader.readAsArrayBuffer(selected_file); }); } - saveFile.click(); + file.click(); + } + + Module.api_private.uploadSavegame_js = function (slot) { + Module.api_private.createInputElement_js('easyrpg_saveFile', function (file) { + const result = new Uint8Array(file.currentTarget.result); + var buf = Module._malloc(result.length); + Module.HEAPU8.set(result, buf); + Module.api_private.uploadSavegameStep2(slot, buf, result.length); + Module._free(buf); + Module.api.refreshScene(); + }); + } + + Module.api_private.uploadSoundfont_js = function () { + Module.api_private.createInputElement_js('easyrpg_sfFile', function (file, name) { + const result = new Uint8Array(file.currentTarget.result); + //const name_buf = Module._malloc(name.length + 1); + //stringToUTF8(name, name_buf, name.length + 1); + const content_buf = Module._malloc(result.length); + Module.HEAPU8.set(result, content_buf); + Module.api_private.uploadSoundfontStep2(name, content_buf, result.length); + //Module._free(name_buf); + Module._free(content_buf); + Module.api.refreshScene(); + }); + } + + Module.api_private.uploadFont_js = function () { + Module.api_private.createInputElement_js('easyrpg_sfFile', function (file, name) { + const result = new Uint8Array(file.currentTarget.result); + //const name_buf = Module._malloc(name.length + 1); + //stringToUTF8(name, name_buf, name.length + 1); + const content_buf = Module._malloc(result.length); + Module.HEAPU8.set(result, content_buf); + Module.api_private.uploadFontStep2(name, content_buf, result.length); + //Module._free(name_buf); + Module._free(content_buf); + Module.api.refreshScene(); + }); } } diff --git a/src/decoder_fluidsynth.cpp b/src/decoder_fluidsynth.cpp index e4584d310..48cebea3e 100644 --- a/src/decoder_fluidsynth.cpp +++ b/src/decoder_fluidsynth.cpp @@ -228,8 +228,14 @@ FluidSynthDecoder::~FluidSynthDecoder() { bool FluidSynthDecoder::Initialize(std::string& status_message) { // only initialize once until a new game starts - if (once) + if (once) { + if (!init && global_settings && !global_synth) { + global_synth.reset(create_synth(status_message, global_synth_id)); + init = (global_synth != nullptr); + } return init; + } + once = true; #ifdef HAVE_FLUIDLITE diff --git a/src/platform/emscripten/interface.cpp b/src/platform/emscripten/interface.cpp index 10524d6d5..0e88efbf2 100644 --- a/src/platform/emscripten/interface.cpp +++ b/src/platform/emscripten/interface.cpp @@ -53,6 +53,18 @@ void Emscripten_Interface::UploadSavegame(int slot) { }, slot); } +void Emscripten_Interface::UploadSoundfont() { + EM_ASM_INT({ + Module.api_private.uploadSoundfont_js($0); + }); +} + +void Emscripten_Interface::UploadFont() { + EM_ASM_INT({ + Module.api_private.uploadFont_js($0); + }); +} + void Emscripten_Interface::RefreshScene() { Scene::instance->Refresh(); } @@ -91,17 +103,79 @@ bool Emscripten_Interface_Private::UploadSavegameStep2(int slot, int buffer_addr return true; } +bool Emscripten_Interface_Private::UploadSoundfontStep2(std::string filename, int buffer_addr, int size) { + auto fs = Game_Config::GetSoundfontFilesystem(); + if (!fs) { + Output::Warning("Cannot access Soundfont directory"); + return false; + } + + std::string name = std::get<1>(FileFinder::GetPathAndFilename(filename)); + + // TODO: Sanitize + /*std::istream is(new Filesystem_Stream::InputMemoryStreamBufView(lcf::Span(reinterpret_cast(buffer_addr), size))); + + if (!lcf::LSD_Reader::Load(is)) { + Output::Warning("Selected file is not a valid savegame"); + return false; + }*/ + + { + auto os = fs.OpenOutputStream(name); + if (!os) + return false; + os.write(reinterpret_cast(buffer_addr), size); + } + + AsyncHandler::SaveFilesystem(); + + return true; +} + +bool Emscripten_Interface_Private::UploadFontStep2(std::string filename, int buffer_addr, int size) { + auto fs = Game_Config::GetFontFilesystem(); + if (!fs) { + Output::Warning("Cannot access Font directory"); + return false; + } + + std::string name = std::get<1>(FileFinder::GetPathAndFilename(filename)); + + // TODO: Sanitize + /*std::istream is(new Filesystem_Stream::InputMemoryStreamBufView(lcf::Span(reinterpret_cast(buffer_addr), size))); + + if (!lcf::LSD_Reader::Load(is)) { + Output::Warning("Selected file is not a valid savegame"); + return false; + }*/ + + { + auto os = fs.OpenOutputStream(name); + if (!os) + return false; + os.write(reinterpret_cast(buffer_addr), size); + } + + AsyncHandler::SaveFilesystem(); + + return true; +} + // Binding code EMSCRIPTEN_BINDINGS(player_interface) { emscripten::class_("api") .class_function("requestReset", &Emscripten_Interface::Reset) .class_function("downloadSavegame", &Emscripten_Interface::DownloadSavegame) .class_function("uploadSavegame", &Emscripten_Interface::UploadSavegame) + .class_function("uploadSoundfont", &Emscripten_Interface::UploadSoundfont) + .class_function("uploadFont", &Emscripten_Interface::UploadFont) .class_function("refreshScene", &Emscripten_Interface::RefreshScene) .class_function("takeScreenshot", &Emscripten_Interface::TakeScreenshot) ; emscripten::class_("api_private") .class_function("uploadSavegameStep2", &Emscripten_Interface_Private::UploadSavegameStep2) + .class_function("uploadSoundfontStep2", &Emscripten_Interface_Private::UploadSoundfontStep2) + .class_function("uploadFontStep2", &Emscripten_Interface_Private::UploadFontStep2) ; } diff --git a/src/platform/emscripten/interface.h b/src/platform/emscripten/interface.h index b84c9b14f..795587cc8 100644 --- a/src/platform/emscripten/interface.h +++ b/src/platform/emscripten/interface.h @@ -18,11 +18,15 @@ #ifndef EP_EMSCRIPTEN_INTERFACE_H #define EP_EMSCRIPTEN_INTERFACE_H +#include + class Emscripten_Interface { public: static bool DownloadSavegame(int slot); static void UploadSavegame(int slot); - static void RefreshScene(); + static void UploadSoundfont(); + static void UploadFont(); + static void RefreshScene(); static void TakeScreenshot(); static void Reset(); }; @@ -30,6 +34,8 @@ class Emscripten_Interface { class Emscripten_Interface_Private { public: static bool UploadSavegameStep2(int slot, int buffer_addr, int size); + static bool UploadFontStep2(std::string name, int buffer_addr, int size); + static bool UploadSoundfontStep2(std::string name, int buffer_addr, int size); }; #endif diff --git a/src/platform/sdl/sdl2_ui.cpp b/src/platform/sdl/sdl2_ui.cpp index 1b062505e..fd529bb47 100644 --- a/src/platform/sdl/sdl2_ui.cpp +++ b/src/platform/sdl/sdl2_ui.cpp @@ -734,7 +734,9 @@ void Sdl2Ui::ProcessWindowEvent(SDL_Event &evnt) { int state = evnt.window.event; if (state == SDL_WINDOWEVENT_FOCUS_LOST) { - if (!vcfg.pause_when_focus_lost.Get()) { + auto cfg = vcfg; + vGetConfig(cfg); + if (!cfg.pause_when_focus_lost.Get()) { return; } diff --git a/src/scene_settings.cpp b/src/scene_settings.cpp index d12c305ea..14fb5fc4b 100644 --- a/src/scene_settings.cpp +++ b/src/scene_settings.cpp @@ -195,6 +195,10 @@ void Scene_Settings::SetMode(Window_Settings::UiMode new_mode) { } } +void Scene_Settings::Refresh() { + options_window->Refresh(); +} + void Scene_Settings::vUpdate() { if (RefreshInputEmergencyReset()) { return; diff --git a/src/scene_settings.h b/src/scene_settings.h index f62704ea1..ce2143bc2 100644 --- a/src/scene_settings.h +++ b/src/scene_settings.h @@ -43,6 +43,7 @@ class Scene_Settings : public Scene { Scene_Settings(); void Start() override; + void Refresh() override; void vUpdate() override; /** diff --git a/src/window_settings.cpp b/src/window_settings.cpp index 02a024487..39de34c7d 100644 --- a/src/window_settings.cpp +++ b/src/window_settings.cpp @@ -35,6 +35,10 @@ #include "audio_midi.h" #include "audio_generic_midiout.h" +#ifdef EMSCRIPTEN +# include "platform/emscripten/interface.h" +#endif + class MenuItem final : public ConfigParam { public: explicit MenuItem(StringView name, StringView description, StringView value) : @@ -371,7 +375,11 @@ void Window_Settings::RefreshAudioSoundfont() { } } +#ifdef EMSCRIPTEN + AddOption(MenuItem("", "Provide a soundfont from your system", ""), [fs]() { Emscripten_Interface::UploadSoundfont(); }); +#else AddOption(MenuItem("", "Open the soundfont directory in a file browser", ""), [fs]() { DisplayUi->OpenURL(fs.GetFullPath()); }); +#endif } void Window_Settings::RefreshEngine() { @@ -462,7 +470,11 @@ void Window_Settings::RefreshEngineFont(bool mincho) { }); set_help2(); +#ifdef EMSCRIPTEN + AddOption(MenuItem("", "Provide a font from your system", ""), [fs]() { Emscripten_Interface::UploadFont(); }); +#else AddOption(MenuItem("", "Open the font directory in a file browser", ""), [fs]() { DisplayUi->OpenURL(fs.GetFullPath()); }); +#endif } void Window_Settings::RefreshLicense() { From 62d7f35c851cac8455907d169a4f0ef913d025ef Mon Sep 17 00:00:00 2001 From: Ghabry Date: Wed, 20 Dec 2023 20:24:12 +0100 Subject: [PATCH 014/222] Settings: Do not allow disabling of FmMidi Is the last option, disabling it breaks MIDI entirely --- src/audio.cpp | 8 -------- src/audio.h | 3 --- src/audio_midi.cpp | 2 +- src/config_param.h | 10 +++++----- src/game_config.cpp | 2 -- src/game_config.h | 2 +- src/window_settings.cpp | 4 ++-- 7 files changed, 9 insertions(+), 22 deletions(-) diff --git a/src/audio.cpp b/src/audio.cpp index e177a3dbe..a892540a8 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -130,14 +130,6 @@ void AudioInterface::SetNativeMidiEnabled(bool enable) { cfg.native_midi.Set(enable); } -bool AudioInterface::GetFmMidiEnabled() const { - return cfg.fmmidi_midi.Get(); -} - -void AudioInterface::SetFmMidiEnabled(bool enable) { - cfg.fmmidi_midi.Set(enable); -} - std::string AudioInterface::GetFluidsynthSoundfont() const { return cfg.soundfont.Get(); } diff --git a/src/audio.h b/src/audio.h index 43ca569d4..7449034ab 100644 --- a/src/audio.h +++ b/src/audio.h @@ -142,9 +142,6 @@ struct AudioInterface { bool GetNativeMidiEnabled() const; void SetNativeMidiEnabled(bool enable); - bool GetFmMidiEnabled() const; - void SetFmMidiEnabled(bool enable); - std::string GetFluidsynthSoundfont() const; void SetFluidsynthSoundfont(StringView sf); diff --git a/src/audio_midi.cpp b/src/audio_midi.cpp index 8ba50c593..aee12a405 100644 --- a/src/audio_midi.cpp +++ b/src/audio_midi.cpp @@ -60,7 +60,7 @@ std::unique_ptr MidiDecoder::Create(bool resample) { mididec = CreateWildMidi(resample); } - if (!mididec && Audio().GetFmMidiEnabled()) { + if (!mididec) { mididec = CreateFmMidi(resample); } diff --git a/src/config_param.h b/src/config_param.h index cdbe2ecd5..4ed9ea3cf 100644 --- a/src/config_param.h +++ b/src/config_param.h @@ -31,10 +31,6 @@ #include namespace { - inline const std::string& ParamValueToString(const std::string& s) { - return s; - } - inline std::string ParamValueToString(StringView s) { return ToString(s); } @@ -42,6 +38,10 @@ namespace { inline std::string ParamValueToString(int i) { return std::to_string(i); } + + inline std::string ParamValueToString(bool b) { + return b ? "[ON]" : "[OFF]"; + } } /** @@ -337,7 +337,7 @@ class BoolConfigParam : public ConfigParamBase { } std::string ValueToString() const override { - return Get() ? "[ON]" : "[OFF]"; + return ParamValueToString(Get()); } }; diff --git a/src/game_config.cpp b/src/game_config.cpp index 94df55604..fa786b37b 100644 --- a/src/game_config.cpp +++ b/src/game_config.cpp @@ -415,7 +415,6 @@ void Game_Config::LoadFromStream(Filesystem_Stream::InputStream& is) { audio.fluidsynth_midi.FromIni(ini); audio.wildmidi_midi.FromIni(ini); audio.native_midi.FromIni(ini); - audio.fmmidi_midi.FromIni(ini); audio.soundfont.FromIni(ini); /** INPUT SECTION */ @@ -511,7 +510,6 @@ void Game_Config::WriteToStream(Filesystem_Stream::OutputStream& os) const { audio.fluidsynth_midi.ToIni(os); audio.wildmidi_midi.ToIni(os); audio.native_midi.ToIni(os); - audio.fmmidi_midi.ToIni(os); audio.soundfont.ToIni(os); os << "\n"; diff --git a/src/game_config.h b/src/game_config.h index 0118960c0..092116bca 100644 --- a/src/game_config.h +++ b/src/game_config.h @@ -111,7 +111,7 @@ struct Game_ConfigAudio { BoolConfigParam fluidsynth_midi { "Fluidsynth (SF2)", "Play MIDI using SF2 soundfonts", "Audio", "Fluidsynth", true }; BoolConfigParam wildmidi_midi { "WildMidi (GUS)", "Play MIDI using GUS patches", "Audio", "WildMidi", true }; BoolConfigParam native_midi { "Native MIDI", "Play MIDI through the operating system ", "Audio", "NativeMidi", true }; - BoolConfigParam fmmidi_midi { "FmMidi", "Play MIDI using the built-in MIDI synthesizer", "Audio", "FmMidi", true }; + LockedConfigParam fmmidi_midi { "FmMidi", "Play MIDI using the built-in MIDI synthesizer", "[Always ON]" }; PathConfigParam soundfont { "Soundfont", "Soundfont to use for Fluidsynth", "Audio", "Soundfont", "" }; void Hide(); diff --git a/src/window_settings.cpp b/src/window_settings.cpp index 39de34c7d..7250893e0 100644 --- a/src/window_settings.cpp +++ b/src/window_settings.cpp @@ -340,8 +340,8 @@ void Window_Settings::RefreshAudioMidi() { } if (cfg.fmmidi_midi.IsOptionVisible()) { - AddOption(cfg.fmmidi_midi, []() { Audio().SetFmMidiEnabled(Audio().GetConfig().fmmidi_midi.Toggle()); }); - if (cfg.fmmidi_midi.Get() && !used) { + AddOption(cfg.fmmidi_midi, []() {}); + if (!used) { options.back().text += " [In use]"; } } From 9ffb9b43806130215374470e072d9214f6499731 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Wed, 20 Dec 2023 20:48:10 +0100 Subject: [PATCH 015/222] Fix build when Fluidsynth is missing or Fluidlite is used --- src/audio_midi.cpp | 2 ++ src/config_param.h | 4 ++++ src/decoder_fluidsynth.cpp | 4 ++-- src/scene_title.cpp | 1 - 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/audio_midi.cpp b/src/audio_midi.cpp index aee12a405..9827be97a 100644 --- a/src/audio_midi.cpp +++ b/src/audio_midi.cpp @@ -149,8 +149,10 @@ void MidiDecoder::ChangeFluidsynthSoundfont(StringView sf_path) { return; } +#if defined(HAVE_FLUIDSYNTH) || defined(HAVE_FLUIDLITE) // Was initialized before works.fluidsynth = FluidSynthDecoder::ChangeGlobalSoundfont(sf_path, works.fluidsynth_status); +#endif } bool MidiDecoder::CheckWildMidi(std::string &status_message) { diff --git a/src/config_param.h b/src/config_param.h index 4ed9ea3cf..6b5e5e701 100644 --- a/src/config_param.h +++ b/src/config_param.h @@ -39,6 +39,10 @@ namespace { return std::to_string(i); } + inline std::string ParamValueToString(double d) { + return std::to_string(d); + } + inline std::string ParamValueToString(bool b) { return b ? "[ON]" : "[OFF]"; } diff --git a/src/decoder_fluidsynth.cpp b/src/decoder_fluidsynth.cpp index 48cebea3e..b2c541e2d 100644 --- a/src/decoder_fluidsynth.cpp +++ b/src/decoder_fluidsynth.cpp @@ -130,7 +130,7 @@ static bool load_default_sf(std::string& status_message, fluid_synth_t* syn, int #if FLUIDSYNTH_VERSION_MAJOR >= 2 char* default_sf = nullptr; - if (fluid_settings_dupstr(global_settings.get(), "synth.default-soundfont", &default_sf) == FLUID_OK) { + if (fluid_settings_dupstr(global_settings.get(), "synth.default-soundfont", &default_sf) == 0) { if (default_sf != nullptr && default_sf[0] != '\0') { sf_paths.emplace_back(default_sf); } @@ -299,7 +299,7 @@ bool FluidSynthDecoder::ChangeGlobalSoundfont(StringView sf_path, std::string& s int new_global_id = fluid_synth_sfload(global_synth.get(), ToString(sf_path).c_str(), 1); if (new_global_id != FLUID_FAILED) { - assert(fluid_synth_sfunload(global_synth.get(), global_synth_id, 1) == FLUID_OK); + assert(fluid_synth_sfunload(global_synth.get(), global_synth_id, 1) == 0); global_synth_id = new_global_id; status_message = fmt::format("Using soundfont {}", sf_path); return true; diff --git a/src/scene_title.cpp b/src/scene_title.cpp index 3d4c38b07..bd23305a0 100644 --- a/src/scene_title.cpp +++ b/src/scene_title.cpp @@ -92,7 +92,6 @@ void Scene_Title::CreateHelpWindow() { translate_window->SetHelpWindow(help_window.get()); } - void Scene_Title::Continue(SceneType prev_scene) { Main_Data::game_system->ResetSystemGraphic(); From e63b72b61eb88d542908c6d30f60027ecd4871b7 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Thu, 21 Dec 2023 01:35:51 +0100 Subject: [PATCH 016/222] Update help/manual --- resources/unix/easyrpg-player.6.adoc | 27 +++++++++++++++++++++ src/game_config.cpp | 35 ++++++++++++++++++++++++++++ src/player.cpp | 11 +++++++++ 3 files changed, 73 insertions(+) diff --git a/resources/unix/easyrpg-player.6.adoc b/resources/unix/easyrpg-player.6.adoc index 0a4081d08..a7166d61c 100644 --- a/resources/unix/easyrpg-player.6.adoc +++ b/resources/unix/easyrpg-player.6.adoc @@ -62,6 +62,25 @@ NOTE: For games that only use ASCII (English games) use '1252'. - 'rpg2k3v105' - RPG Maker 2003 (v1.05 - v1.09a) - 'rpg2k3e' - RPG Maker 2003 RPG Maker 2003 (English release, v1.12) +*--font1* _FILE_:: + Path to a font to use for the first font. The system graphic of the game + determines whether font 1 or 2 is used. When no font is selected or the + selected font lacks certain glyphs the built-in pixel font is used. + +*--font1-size* _PX_:: + Size of font 1 in pixel. The default value is 12. + +*--font2* _FILE_:: + Path to a font to use for the second font. See font 1 for further information. + +*--font2-size* _PX_:: + Size of font 2 in pixel. The default value is 12. + +*--font-path* _PATH_:: + Configures the path where the settings scene looks for fonts. The user can + choose from any font in the directory. This is more flexible than using + *--font1* or *--font2* directly. The default path is 'config-path/Font'. + *--language* _LANG_:: Loads the game translation in language/'LANG' folder. @@ -166,6 +185,10 @@ NOTE: When using the game browser all games will share the same save directory! - 'widescreen' - 416x240 (16:9) - 'ultrawide' - 560x240 (21:9) +*--pause-focus-lost*:: + Pause the game when the window has no focus. Can be disabled with + *--no-pause-focus-lost*. + *--scaling* _MODE_:: How the video output is scaled. Possible options: - 'nearest' - Scale to screen size using nearest neighbour algorithm. @@ -206,6 +229,10 @@ NOTE: When using the game browser all games will share the same save directory! Adds 'FILE' to the list of soundfonts used for playing MIDI files and use this one with highest precedence. The soundfont must be in SF2 format. +*--soundfont-path* _P_:: + Configures the path where the settings scene looks for soundfonts. The user + can choose from any soundfont in the directory. This is more flexible than + using *--soundfont* directly. The default path is 'config-path/Soundfont'. === Debug options diff --git a/src/game_config.cpp b/src/game_config.cpp index fa786b37b..043cc70d1 100644 --- a/src/game_config.cpp +++ b/src/game_config.cpp @@ -275,6 +275,9 @@ std::string Game_Config::GetConfigPath(CmdlineParser& cp) { } void Game_Config::LoadFromArgs(CmdlineParser& cp) { + font_path.clear(); + soundfont_path.clear(); + while (!cp.Done()) { CmdlineArg arg; long li_value = 0; @@ -302,6 +305,14 @@ void Game_Config::LoadFromArgs(CmdlineParser& cp) { video.fps_render_window.Set(arg.ArgIsOn()); continue; } + if (cp.ParseNext(arg, 0, "--pause-focus-lost")) { + video.pause_when_focus_lost.Set(true); + continue; + } + if (cp.ParseNext(arg, 0, "--no-pause-focus-lost")) { + video.pause_when_focus_lost.Set(false); + continue; + } if (cp.ParseNext(arg, 0, "--window")) { video.fullscreen.Set(false); continue; @@ -364,6 +375,30 @@ void Game_Config::LoadFromArgs(CmdlineParser& cp) { } continue; } + if (cp.ParseNext(arg, 1, "--font1")) { + if (arg.NumValues() > 0) { + player.font1.Set(FileFinder::MakeCanonical(arg.Value(0), 0)); + } + continue; + } + if (cp.ParseNext(arg, 1, "--font1-size")) { + if (arg.ParseValue(0, li_value)) { + player.font1_size.Set(li_value); + } + continue; + } + if (cp.ParseNext(arg, 1, "--font2")) { + if (arg.NumValues() > 0) { + player.font2.Set(FileFinder::MakeCanonical(arg.Value(0), 0)); + } + continue; + } + if (cp.ParseNext(arg, 1, "--font2-size")) { + if (arg.ParseValue(0, li_value)) { + player.font2_size.Set(li_value); + } + continue; + } if (cp.ParseNext(arg, 1, "--soundfont-path")) { if (arg.NumValues() > 0) { soundfont_path = FileFinder::MakeCanonical(arg.Value(0), 0); diff --git a/src/player.cpp b/src/player.cpp index 88c2c9b56..ff3be5789 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -1380,6 +1380,13 @@ Engine options: rpg2k3 - RPG Maker 2003 (v1.00 - v1.04) rpg2k3v105 - RPG Maker 2003 (v1.05 - v1.09a) rpg2k3e - RPG Maker 2003 (English release, v1.12) + --font1 FILE Font to use for the first font. The system graphic of the + game determines whether font 1 or 2 is used. + --font1-size PX Size of font 1 in pixel. The default is 12. + --font2 FILE Font to use for the second font. + --font2-size PX Size of font 2 in pixel. The default is 12. + --font-path PATH The path in which the settings scene looks for fonts. + The default is config-path/Font. --language LANG Load the game translation in language/LANG folder. --load-game-id N Skip the title scene and load SaveN.lsd (N is padded to two digits). @@ -1432,6 +1439,8 @@ Video options: original - 320x240 (4:3). Recommended widescreen - 416x240 (16:9) ultrawide - 560x240 (21:9) + --pause-focus-lost Pause the game when the window has no focus. + Disable with --no-pause-focus-lost. --scaling S How the video output is scaled. Options: nearest - Scale to screen size. Fast, but causes scaling @@ -1453,6 +1462,8 @@ Audio options: --music-volume V Set volume of background music to V (0-100). --sound-volume V Set volume of sound effects to V (0-100). --soundfont FILE Soundfont in sf2 format to use when playing MIDI files. + --soundfont-path P The path in which the settings scene looks for soundfonts. + The default is config-path/Soundfont. Debug options: --battle-test N... Start a battle test. From b6e397b6a6b8c1fbc2f51020f98dd881719dae1a Mon Sep 17 00:00:00 2001 From: Ghabry Date: Thu, 21 Dec 2023 15:14:49 +0100 Subject: [PATCH 017/222] Fluidsynth: Only try /usr/share on desktop Linux/BSD/macOS systems --- src/decoder_fluidsynth.cpp | 2 ++ src/system.h | 1 + 2 files changed, 3 insertions(+) diff --git a/src/decoder_fluidsynth.cpp b/src/decoder_fluidsynth.cpp index b2c541e2d..dd2d383ea 100644 --- a/src/decoder_fluidsynth.cpp +++ b/src/decoder_fluidsynth.cpp @@ -149,11 +149,13 @@ static bool load_default_sf(std::string& status_message, fluid_synth_t* syn, int sf_paths.insert(sf_paths.end(), sdl_sfs.begin(), sdl_sfs.end()); } +#ifdef SYSTEM_DESKTOP_LINUX_BSD_MACOS auto sf_files = {"FluidR3_GM.sf2"}; for (const auto& sf_file: sf_files) { sf_paths.emplace_back(FileFinder::MakePath("/usr/share/soundfonts", sf_file)); sf_paths.emplace_back(FileFinder::MakePath("/usr/share/sounds/sf2", sf_file)); } +#endif bool sf_load_success = false; int old_synth_id = synth_id; diff --git a/src/system.h b/src/system.h index 74b8d0b12..8e6b14970 100644 --- a/src/system.h +++ b/src/system.h @@ -93,6 +93,7 @@ # define SUPPORT_TOUCH # define SUPPORT_JOYSTICK # define SUPPORT_JOYSTICK_AXIS +# define SYSTEM_DESKTOP_LINUX_BSD_MACOS #endif #ifdef USE_SDL From fb71fe492cee89f34ca870eeb8787b299868c144 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Thu, 21 Dec 2023 16:28:46 +0100 Subject: [PATCH 018/222] Settings: Prevent changing the font when the game has a custom font --- src/player.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/player.cpp b/src/player.cpp index ff3be5789..f64122b78 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -1054,12 +1054,20 @@ void Player::LoadFonts() { // Look for bundled fonts auto gothic = FileFinder::OpenFont("Font"); if (gothic) { - Font::SetDefault(Font::CreateFtFont(std::move(gothic), 12, false, false), false); + auto ft = Font::CreateFtFont(std::move(gothic), 12, false, false); + player_config.font1.SetLocked(ft != nullptr); + if (ft) { + Font::SetDefault(ft, false); + } } auto mincho = FileFinder::OpenFont("Font2"); if (mincho) { - Font::SetDefault(Font::CreateFtFont(std::move(mincho), 12, false, false), true); + auto ft = Font::CreateFtFont(std::move(mincho), 12, false, false); + player_config.font2.SetLocked(ft != nullptr); + if (ft) { + Font::SetDefault(ft, true); + } } #endif } From 5a91e4239cd2a913393b24d9bfd2a183e8243b92 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Thu, 21 Dec 2023 16:29:15 +0100 Subject: [PATCH 019/222] Settings: Fix memory corruption after Push The option was deleted. This randomly crashed when opening the font list. --- src/window_settings.cpp | 62 ++++++++++++++++++++++------------------- src/window_settings.h | 9 +++--- 2 files changed, 38 insertions(+), 33 deletions(-) diff --git a/src/window_settings.cpp b/src/window_settings.cpp index 7250893e0..5a02ddb06 100644 --- a/src/window_settings.cpp +++ b/src/window_settings.cpp @@ -55,7 +55,7 @@ void Window_Settings::DrawOption(int index) { Rect rect = GetItemRect(index); contents->ClearRect(rect); - auto& option = options[index]; + auto& option = GetFrame().options[index]; bool enabled = bool(option.action); Font::SystemColor color = enabled ? option.color : Font::ColorDisabled; @@ -119,7 +119,7 @@ Window_Settings::UiMode Window_Settings::GetMode() const { } void Window_Settings::Refresh() { - options.clear(); + GetFrame().options.clear(); switch (GetFrame().uimode) { case eNone: @@ -164,9 +164,9 @@ void Window_Settings::Refresh() { break; } - SetItemMax(options.size()); + SetItemMax(GetFrame().options.size()); - if (GetFrame().uimode == eNone || options.empty()) { + if (GetFrame().uimode == eNone || GetFrame().options.empty()) { SetIndex(-1); } @@ -180,11 +180,11 @@ void Window_Settings::Refresh() { } void Window_Settings::UpdateHelp() { - if (index >= 0 && index < static_cast(options.size())) { - help_window->SetText(options[index].help); + if (index >= 0 && index < static_cast(GetFrame().options.size())) { + help_window->SetText(GetFrame().options[index].help); if (help_window2) { - help_window2->SetText(options[index].help2); - help_window2->SetVisible(!options[index].help2.empty()); + help_window2->SetText(GetFrame().options[index].help2); + help_window2->SetVisible(!GetFrame().options[index].help2.empty()); } } else { help_window->SetText(""); @@ -209,7 +209,7 @@ void Window_Settings::AddOption(const Param& param, if (!param.IsLocked()) { opt.action = std::forward(action); } - options.push_back(std::move(opt)); + GetFrame().options.push_back(std::move(opt)); } template @@ -231,7 +231,7 @@ void Window_Settings::AddOption(const RangeConfigParam& param, if (!param.IsLocked()) { opt.action = std::forward(action); } - options.push_back(std::move(opt)); + GetFrame().options.push_back(std::move(opt)); } template @@ -267,7 +267,7 @@ void Window_Settings::AddOption(const EnumConfigParam& param, if (!param.IsLocked()) { opt.action = std::forward(action); } - options.push_back(std::move(opt)); + GetFrame().options.push_back(std::move(opt)); } void Window_Settings::RefreshVideo() { @@ -304,37 +304,37 @@ void Window_Settings::RefreshAudioMidi() { bool used = false; AddOption(MenuItem("> Information <", "The first active and working option is used for MIDI", ""), [](){}); - options.back().help2 = "Changes take effect when a new MIDI file is played"; + GetFrame().options.back().help2 = "Changes take effect when a new MIDI file is played"; if (cfg.fluidsynth_midi.IsOptionVisible()) { AddOption(cfg.fluidsynth_midi, []() { Audio().SetFluidsynthEnabled(Audio().GetConfig().fluidsynth_midi.Toggle()); }); - if (!MidiDecoder::CheckFluidsynth(options.back().help2)) { - options.back().text += " [Not working]"; - options.back().color = Font::ColorKnockout; + if (!MidiDecoder::CheckFluidsynth(GetFrame().options.back().help2)) { + GetFrame().options.back().text += " [Not working]"; + GetFrame().options.back().color = Font::ColorKnockout; } else if (cfg.fluidsynth_midi.Get()) { - options.back().text += " [In use]"; + GetFrame().options.back().text += " [In use]"; used = true; } } if (cfg.wildmidi_midi.IsOptionVisible()) { AddOption(cfg.wildmidi_midi, []() { Audio().SetWildMidiEnabled(Audio().GetConfig().wildmidi_midi.Toggle()); }); - if (!MidiDecoder::CheckWildMidi(options.back().help2)) { - options.back().text += " [Not working]"; - options.back().color = Font::ColorKnockout; + if (!MidiDecoder::CheckWildMidi(GetFrame().options.back().help2)) { + GetFrame().options.back().text += " [Not working]"; + GetFrame().options.back().color = Font::ColorKnockout; } else if (cfg.wildmidi_midi.Get() && !used) { - options.back().text += " [In use]"; + GetFrame().options.back().text += " [In use]"; used = true; } } if (cfg.native_midi.IsOptionVisible()) { AddOption(cfg.native_midi, []() { Audio().SetNativeMidiEnabled(Audio().GetConfig().native_midi.Toggle()); }); - if (!GenericAudioMidiOut().IsInitialized(options.back().help2)) { - options.back().text += " [Not working]"; - options.back().color = Font::ColorKnockout; + if (!GenericAudioMidiOut().IsInitialized(GetFrame().options.back().help2)) { + GetFrame().options.back().text += " [Not working]"; + GetFrame().options.back().color = Font::ColorKnockout; } else if (cfg.native_midi.Get() && !used) { - options.back().text += " [In use]"; + GetFrame().options.back().text += " [In use]"; used = true; } } @@ -342,7 +342,7 @@ void Window_Settings::RefreshAudioMidi() { if (cfg.fmmidi_midi.IsOptionVisible()) { AddOption(cfg.fmmidi_midi, []() {}); if (!used) { - options.back().text += " [In use]"; + GetFrame().options.back().text += " [In use]"; } } } @@ -391,8 +391,11 @@ void Window_Settings::RefreshEngine() { Push(eEngineFont1); GetFrame().scratch = -1; }); + if (cfg.font1.IsLocked()) { + GetFrame().options.back().help = "This game uses a custom font"; + } if (Main_Data::game_system->GetFontId() == lcf::rpg::System::Font_gothic) { - options.back().text += " [In use]"; + GetFrame().options.back().text += " [In use]"; } AddOption(cfg.font2, [this, &cfg]() { @@ -400,8 +403,11 @@ void Window_Settings::RefreshEngine() { Push(eEngineFont2); GetFrame().scratch = -1; }); + if (cfg.font2.IsLocked()) { + GetFrame().options.back().help = "This game uses a custom font"; + } if (Main_Data::game_system->GetFontId() == lcf::rpg::System::Font_mincho) { - options.back().text += " [In use]"; + GetFrame().options.back().text += " [In use]"; } AddOption(cfg.show_startup_logos, [this, &cfg](){ cfg.show_startup_logos.Set(static_cast(GetCurrentOption().current_value)); }); @@ -424,7 +430,7 @@ void Window_Settings::RefreshEngineFont(bool mincho) { auto& setting = mincho ? cfg.font2 : cfg.font1; auto set_help2 = [this]() { - options.back().help2 = ToString(sample_text.GetDescriptions()[static_cast(sample_text.Get())]); + GetFrame().options.back().help2 = ToString(sample_text.GetDescriptions()[static_cast(sample_text.Get())]); }; AddOption(MenuItem("", "Use the built-in pixel font", setting.Get().empty() ? "[x]" : ""), [this, &setting, mincho]() { diff --git a/src/window_settings.h b/src/window_settings.h index 4a2d03460..73f72b9bc 100644 --- a/src/window_settings.h +++ b/src/window_settings.h @@ -82,6 +82,7 @@ class Window_Settings : public Window_Selectable { int arg = -1; int scratch = 0; int scratch2 = 0; + std::vector