From 43593b168d24ecffbdfbc6e84ab535a49738f635 Mon Sep 17 00:00:00 2001 From: Muzychenko Andrey <33288308+k4zmu2a@users.noreply.github.com> Date: Sat, 9 Oct 2021 17:28:30 +0300 Subject: [PATCH] Sound: added channel recycling. Added sound channel count (aka voices) user option. Added 3DPB font to sprite viewer. Added version number to about dialog. --- SpaceCadetPinball/Sound.cpp | 49 ++++++++++++++----- SpaceCadetPinball/Sound.h | 12 +++-- SpaceCadetPinball/loader.cpp | 2 +- SpaceCadetPinball/options.cpp | 37 ++++++-------- SpaceCadetPinball/options.h | 9 ++-- SpaceCadetPinball/render.cpp | 22 +++++++++ SpaceCadetPinball/winmain.cpp | 92 +++++++++++++++++++---------------- SpaceCadetPinball/winmain.h | 1 + 8 files changed, 137 insertions(+), 87 deletions(-) diff --git a/SpaceCadetPinball/Sound.cpp b/SpaceCadetPinball/Sound.cpp index 4e053b1..bad0a95 100644 --- a/SpaceCadetPinball/Sound.cpp +++ b/SpaceCadetPinball/Sound.cpp @@ -3,22 +3,23 @@ int Sound::num_channels; -unsigned int Sound::enabled_flag = -1; +bool Sound::enabled_flag = false; +int* Sound::TimeStamps = nullptr; -int Sound::Init(int voices) +bool Sound::Init(int channels, bool enableFlag) { - int channelCount = voices; - if (voices > 8) - channelCount = 8; - num_channels = channelCount; - Mix_Init(MIX_INIT_MID); - return Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, 2, 1024); + auto result = Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, 2, 1024); + SetChannels(channels); + Enable(enableFlag); + return !result; } -void Sound::Enable(int channelFrom, int channelTo, int enableFlag) +void Sound::Enable(bool enableFlag) { - enabled_flag = enableFlag ? -1 : 0; + enabled_flag = enableFlag; + if (!enableFlag) + Mix_HaltChannel(-1); } void Sound::Activate() @@ -33,17 +34,28 @@ void Sound::Deactivate() void Sound::Close() { + delete[] TimeStamps; Mix_CloseAudio(); Mix_Quit(); } -void Sound::PlaySound(Mix_Chunk* wavePtr, int minChannel, int maxChannel, unsigned int dwFlags, int16_t loops) +void Sound::PlaySound(Mix_Chunk* wavePtr, int time) { if (wavePtr && enabled_flag) - Mix_PlayChannel(-1, wavePtr, loops); + { + if (Mix_Playing(-1) == num_channels) + { + auto oldestChannel = std::min_element(TimeStamps, TimeStamps + num_channels) - TimeStamps; + Mix_HaltChannel(oldestChannel); + } + + auto channel = Mix_PlayChannel(-1, wavePtr, 0); + if (channel != -1) + TimeStamps[channel] = time; + } } -Mix_Chunk* Sound::LoadWaveFile(std::string lpName) +Mix_Chunk* Sound::LoadWaveFile(const std::string& lpName) { auto wavFile = fopen(lpName.c_str(), "r"); if (!wavFile) @@ -58,3 +70,14 @@ void Sound::FreeSound(Mix_Chunk* wave) if (wave) Mix_FreeChunk(wave); } + +void Sound::SetChannels(int channels) +{ + if (channels <= 0) + channels = 8; + + num_channels = channels; + delete[] TimeStamps; + TimeStamps = new int[num_channels](); + Mix_AllocateChannels(num_channels); +} diff --git a/SpaceCadetPinball/Sound.h b/SpaceCadetPinball/Sound.h index 37854cd..0665b78 100644 --- a/SpaceCadetPinball/Sound.h +++ b/SpaceCadetPinball/Sound.h @@ -4,15 +4,17 @@ class Sound { public: - static int Init(int voices); - static void Enable(int channelFrom, int channelTo, int enableFlag); + static bool Init(int channels, bool enableFlag); + static void Enable(bool enableFlag); static void Activate(); static void Deactivate(); static void Close(); - static void PlaySound(Mix_Chunk* wavePtr, int minChannel, int maxChannel, unsigned int dwFlags, int16_t loops); - static Mix_Chunk* LoadWaveFile(std::string lpName); + static void PlaySound(Mix_Chunk* wavePtr, int time); + static Mix_Chunk* LoadWaveFile(const std::string& lpName); static void FreeSound(Mix_Chunk* wave); + static void SetChannels(int channels); private: static int num_channels; - static unsigned int enabled_flag; + static bool enabled_flag; + static int* TimeStamps; }; diff --git a/SpaceCadetPinball/loader.cpp b/SpaceCadetPinball/loader.cpp index a9a0e2a..4b68880 100644 --- a/SpaceCadetPinball/loader.cpp +++ b/SpaceCadetPinball/loader.cpp @@ -331,7 +331,7 @@ float loader::play_sound(int soundIndex) { if (soundIndex <= 0) return 0.0; - Sound::PlaySound(sound_list[soundIndex].WavePtr, 0, 7, 0, 0); + Sound::PlaySound(sound_list[soundIndex].WavePtr, pb::time_ticks); return sound_list[soundIndex].Duration; } diff --git a/SpaceCadetPinball/options.cpp b/SpaceCadetPinball/options.cpp index bf884a6..4e9f809 100644 --- a/SpaceCadetPinball/options.cpp +++ b/SpaceCadetPinball/options.cpp @@ -7,6 +7,7 @@ #include "winmain.h" constexpr int options::MaxUps, options::MaxFps, options::MinUps, options::MinFps, options::DefUps, options::DefFps; +constexpr int options::MaxSoundChannels, options::MinSoundChannels, options::DefSoundChannels; optionsStruct options::Options{}; std::map options::settings{}; @@ -42,9 +43,6 @@ void options::init() ImGui::EndFrame(); } - Options.Sounds = 1; - Options.Music = 0; - Options.FullScreen = 0; Options.KeyDft.LeftFlipper = SDLK_z; Options.KeyDft.RightFlipper = SDLK_SLASH; Options.KeyDft.Plunger = SDLK_SPACE; @@ -52,20 +50,18 @@ void options::init() Options.KeyDft.RightTableBump = SDLK_PERIOD; Options.KeyDft.BottomTableBump = SDLK_UP; Options.Key = Options.KeyDft; - Options.Players = 1; - Options.UniformScaling = true; - Options.Sounds = get_int("Sounds", Options.Sounds); - Options.Music = get_int("Music", Options.Music); - Options.FullScreen = get_int("FullScreen", Options.FullScreen); - Options.Players = get_int("Players", Options.Players); + Options.Sounds = get_int("Sounds", true); + Options.Music = get_int("Music", false); + Options.FullScreen = get_int("FullScreen", false); + Options.Players = get_int("Players", 1); Options.Key.LeftFlipper = get_int("Left Flipper key", Options.Key.LeftFlipper); Options.Key.RightFlipper = get_int("Right Flipper key", Options.Key.RightFlipper); Options.Key.Plunger = get_int("Plunger key", Options.Key.Plunger); Options.Key.LeftTableBump = get_int("Left Table Bump key", Options.Key.LeftTableBump); Options.Key.RightTableBump = get_int("Right Table Bump key", Options.Key.RightTableBump); Options.Key.BottomTableBump = get_int("Bottom Table Bump key", Options.Key.BottomTableBump); - Options.UniformScaling = get_int("Uniform scaling", Options.UniformScaling); + Options.UniformScaling = get_int("Uniform scaling", true); ImGui::GetIO().FontGlobalScale = get_float("UI Scale", 1.0f); Options.Resolution = get_int("Screen Resolution", -1); Options.LinearFiltering = get_int("Linear Filtering", true); @@ -74,11 +70,11 @@ void options::init() Options.UpdatesPerSecond = std::max(Options.UpdatesPerSecond, Options.FramesPerSecond); Options.ShowMenu = get_int("ShowMenu", true); Options.UncappedUpdatesPerSecond = get_int("Uncapped Updates Per Second", false); + Options.SoundChannels = get_int("Sound Channels", DefSoundChannels); + Options.SoundChannels = std::min(MaxSoundChannels, std::max(MinSoundChannels, Options.SoundChannels)); winmain::UpdateFrameRate(); - Sound::Enable(0, 7, Options.Sounds); - auto maxRes = fullscrn::GetMaxResolution(); if (Options.Resolution >= 0 && Options.Resolution > maxRes) Options.Resolution = maxRes; @@ -105,6 +101,7 @@ void options::uninit() set_int("Updates Per Second", Options.UpdatesPerSecond); set_int("ShowMenu", Options.ShowMenu); set_int("Uncapped Updates Per Second", Options.UncappedUpdatesPerSecond); + set_int("Sound Channels", Options.SoundChannels); } @@ -143,18 +140,15 @@ void options::set_float(LPCSTR lpValueName, float data) void options::toggle(Menu1 uIDCheckItem) { - int newValue; switch (uIDCheckItem) { case Menu1::Sounds: - newValue = Options.Sounds == 0; - Options.Sounds = Options.Sounds == 0; - Sound::Enable(0, 7, newValue); + Options.Sounds ^= true; + Sound::Enable(Options.Sounds); return; case Menu1::Music: - newValue = Options.Music == 0; - Options.Music = Options.Music == 0; - if (!newValue) + Options.Music ^= true; + if (!Options.Music) midi::music_stop(); else midi::play_pb_theme(); @@ -163,9 +157,8 @@ void options::toggle(Menu1 uIDCheckItem) Options.ShowMenu = Options.ShowMenu == 0; return; case Menu1::Full_Screen: - newValue = Options.FullScreen == 0; - Options.FullScreen = Options.FullScreen == 0; - fullscrn::set_screen_mode(newValue); + Options.FullScreen ^= true; + fullscrn::set_screen_mode(Options.FullScreen); return; case Menu1::OnePlayer: case Menu1::TwoPlayers: diff --git a/SpaceCadetPinball/options.h b/SpaceCadetPinball/options.h index 16f2a06..734100c 100644 --- a/SpaceCadetPinball/options.h +++ b/SpaceCadetPinball/options.h @@ -42,9 +42,9 @@ struct optionsStruct { ControlsStruct Key; ControlsStruct KeyDft; - int Sounds; - int Music; - int FullScreen; + bool Sounds; + bool Music; + bool FullScreen; int Players; int Resolution; bool UniformScaling; @@ -53,6 +53,7 @@ struct optionsStruct int UpdatesPerSecond; bool ShowMenu; bool UncappedUpdatesPerSecond; + int SoundChannels; }; struct ControlRef @@ -68,6 +69,8 @@ public: // Original does ~120 updates per second. static constexpr int MaxUps = 360, MaxFps = MaxUps, MinUps = 60, MinFps = MinUps, DefUps = 120, DefFps = 60; + // Original uses 8 sound channels + static constexpr int MaxSoundChannels = 32, MinSoundChannels = 1, DefSoundChannels = 8; static optionsStruct Options; static void init(); diff --git a/SpaceCadetPinball/render.cpp b/SpaceCadetPinball/render.cpp index cb3dda7..bfe9936 100644 --- a/SpaceCadetPinball/render.cpp +++ b/SpaceCadetPinball/render.cpp @@ -4,6 +4,7 @@ #include "GroupData.h" #include "options.h" #include "pb.h" +#include "score.h" #include "TPinballTable.h" #include "winmain.h" @@ -520,6 +521,27 @@ void render::SpriteViewer(bool* show) } } } + + // 3DPB font is not in dat file. + if (!pb::FullTiltMode) + { + int index = -1; + for (auto bmp : score::msg_fontp->Chars) + { + index++; + if (!bmp) + continue; + + ImGui::Text("Char: %d, symbol:'%c'", index, index); + + gdrv::CreatePreview(*bmp); + if (bmp->Texture) + { + ImGui::Image(bmp->Texture, ImVec2(bmp->Width * scale, bmp->Height * scale), + uv_min, uv_max, tint_col, border_col); + } + } + } } ImGui::End(); } diff --git a/SpaceCadetPinball/winmain.cpp b/SpaceCadetPinball/winmain.cpp index 2f88544..eaa9073 100644 --- a/SpaceCadetPinball/winmain.cpp +++ b/SpaceCadetPinball/winmain.cpp @@ -40,6 +40,7 @@ char* winmain::BasePath; std::string winmain::FpsDetails; double winmain::UpdateToFrameRatio; winmain::DurationMs winmain::TargetFrameTime; +optionsStruct& winmain::Options = options::Options; int winmain::WinMain(LPCSTR lpCmdLine) { @@ -118,13 +119,11 @@ int winmain::WinMain(LPCSTR lpCmdLine) // PB init from message handler { options::init(); - auto voiceCount = options::get_int("Voices", 8); - if (Sound::Init(voiceCount)) - options::Options.Sounds = 0; - Sound::Activate(); + if (!Sound::Init(Options.SoundChannels, Options.Sounds)) + Options.Sounds = false; if (!pinball::quickFlag && !midi::music_init()) - options::Options.Music = 0; + Options.Music = false; if (pb::init()) { @@ -141,11 +140,11 @@ int winmain::WinMain(LPCSTR lpCmdLine) if (strstr(lpCmdLine, "-fullscreen")) { - options::Options.FullScreen = 1; + Options.FullScreen = true; } SDL_ShowWindow(window); - fullscrn::set_screen_mode(options::Options.FullScreen); + fullscrn::set_screen_mode(Options.FullScreen); if (strstr(lpCmdLine, "-demo")) pb::toggle_demo(); @@ -265,7 +264,7 @@ int winmain::WinMain(LPCSTR lpCmdLine) auto targetTimeDelta = TargetFrameTime - DurationMs(updateEnd - frameStart) - sleepRemainder; TimePoint frameEnd; - if (targetTimeDelta > DurationMs::zero() && !options::Options.UncappedUpdatesPerSecond) + if (targetTimeDelta > DurationMs::zero() && !Options.UncappedUpdatesPerSecond) { std::this_thread::sleep_for(targetTimeDelta); frameEnd = Clock::now(); @@ -302,7 +301,7 @@ int winmain::WinMain(LPCSTR lpCmdLine) void winmain::RenderUi() { // A minimal window with a button to prevent menu lockout. - if (!options::Options.ShowMenu) + if (!Options.ShowMenu) { ImGui::SetNextWindowPos(ImVec2{}); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{10, 0}); @@ -371,32 +370,32 @@ void winmain::RenderUi() if (ImGui::BeginMenu("Options")) { - if (ImGui::MenuItem("Show Menu", "F9", options::Options.ShowMenu)) + if (ImGui::MenuItem("Show Menu", "F9", Options.ShowMenu)) { options::toggle(Menu1::Show_Menu); } - if (ImGui::MenuItem("Full Screen", "F4", options::Options.FullScreen)) + if (ImGui::MenuItem("Full Screen", "F4", Options.FullScreen)) { options::toggle(Menu1::Full_Screen); } if (ImGui::BeginMenu("Select Players")) { - if (ImGui::MenuItem("1 Player", nullptr, options::Options.Players == 1)) + if (ImGui::MenuItem("1 Player", nullptr, Options.Players == 1)) { options::toggle(Menu1::OnePlayer); new_game(); } - if (ImGui::MenuItem("2 Players", nullptr, options::Options.Players == 2)) + if (ImGui::MenuItem("2 Players", nullptr, Options.Players == 2)) { options::toggle(Menu1::TwoPlayers); new_game(); } - if (ImGui::MenuItem("3 Players", nullptr, options::Options.Players == 3)) + if (ImGui::MenuItem("3 Players", nullptr, Options.Players == 3)) { options::toggle(Menu1::ThreePlayers); new_game(); } - if (ImGui::MenuItem("4 Players", nullptr, options::Options.Players == 4)) + if (ImGui::MenuItem("4 Players", nullptr, Options.Players == 4)) { options::toggle(Menu1::FourPlayers); new_game(); @@ -405,14 +404,22 @@ void winmain::RenderUi() } ImGui::Separator(); - if (ImGui::MenuItem("Sound", nullptr, options::Options.Sounds)) + if (ImGui::MenuItem("Sound", "F5", Options.Sounds)) { options::toggle(Menu1::Sounds); } - if (ImGui::MenuItem("Music", nullptr, options::Options.Music)) + if (ImGui::MenuItem("Music", "F6", Options.Music)) { options::toggle(Menu1::Music); } + ImGui::TextUnformatted("Sound Channels"); + if (ImGui::SliderInt("##Sound Channels", &Options.SoundChannels, options::MinSoundChannels, + options::MaxSoundChannels, "%d", ImGuiSliderFlags_AlwaysClamp)) + { + Options.SoundChannels = std::min(options::MaxSoundChannels, + std::max(options::MinSoundChannels, Options.SoundChannels)); + Sound::SetChannels(Options.SoundChannels); + } ImGui::Separator(); if (ImGui::MenuItem("Player Controls...", "F8")) @@ -425,7 +432,7 @@ void winmain::RenderUi() { char buffer[20]{}; auto maxResText = pinball::get_rc_string(fullscrn::GetMaxResolution() + 2030, 0); - if (ImGui::MenuItem(maxResText, nullptr, options::Options.Resolution == -1)) + if (ImGui::MenuItem(maxResText, nullptr, Options.Resolution == -1)) { options::toggle(Menu1::MaximumResolution); } @@ -433,7 +440,7 @@ void winmain::RenderUi() { auto& res = fullscrn::resolution_array[i]; snprintf(buffer, sizeof buffer - 1, "%d x %d", res.ScreenWidth, res.ScreenHeight); - if (ImGui::MenuItem(buffer, nullptr, options::Options.Resolution == i)) + if (ImGui::MenuItem(buffer, nullptr, Options.Resolution == i)) { options::toggle(static_cast(static_cast(Menu1::R640x480) + i)); } @@ -442,11 +449,11 @@ void winmain::RenderUi() } if (ImGui::BeginMenu("Graphics")) { - if (ImGui::MenuItem("Uniform Scaling", nullptr, options::Options.UniformScaling)) + if (ImGui::MenuItem("Uniform Scaling", nullptr, Options.UniformScaling)) { options::toggle(Menu1::WindowUniformScale); } - if (ImGui::MenuItem("Linear Filtering", nullptr, options::Options.LinearFiltering)) + if (ImGui::MenuItem("Linear Filtering", nullptr, Options.LinearFiltering)) { options::toggle(Menu1::WindowLinearFilter); } @@ -459,27 +466,25 @@ void winmain::RenderUi() if (ImGui::MenuItem("Set Default UPS/FPS")) { changed = true; - options::Options.UpdatesPerSecond = options::DefUps; - options::Options.FramesPerSecond = options::DefFps; + Options.UpdatesPerSecond = options::DefUps; + Options.FramesPerSecond = options::DefFps; } - if (ImGui::DragInt("UPS", &options::Options.UpdatesPerSecond, 1, options::MinUps, options::MaxUps, - "%d", ImGuiSliderFlags_AlwaysClamp)) + if (ImGui::SliderInt("UPS", &Options.UpdatesPerSecond, options::MinUps, options::MaxUps, "%d", + ImGuiSliderFlags_AlwaysClamp)) { changed = true; - options::Options.FramesPerSecond = std::min(options::Options.UpdatesPerSecond, - options::Options.FramesPerSecond); + Options.FramesPerSecond = std::min(Options.UpdatesPerSecond, Options.FramesPerSecond); } - if (ImGui::DragInt("FPS", &options::Options.FramesPerSecond, 1, options::MinFps, options::MaxFps, - "%d", ImGuiSliderFlags_AlwaysClamp)) + if (ImGui::SliderInt("FPS", &Options.FramesPerSecond, options::MinFps, options::MaxFps, "%d", + ImGuiSliderFlags_AlwaysClamp)) { changed = true; - options::Options.UpdatesPerSecond = std::max(options::Options.UpdatesPerSecond, - options::Options.FramesPerSecond); + Options.UpdatesPerSecond = std::max(Options.UpdatesPerSecond, Options.FramesPerSecond); } snprintf(buffer, sizeof buffer - 1, "Uncapped UPS (FPS ratio %02.02f)", UpdateToFrameRatio); - if (ImGui::MenuItem(buffer, nullptr, options::Options.UncappedUpdatesPerSecond)) + if (ImGui::MenuItem(buffer, nullptr, Options.UncappedUpdatesPerSecond)) { - options::Options.UncappedUpdatesPerSecond ^= true; + Options.UncappedUpdatesPerSecond ^= true; } if (changed) @@ -596,7 +601,7 @@ int winmain::event_handler(const SDL_Event* event) switch (event->key.keysym.sym) { case SDLK_ESCAPE: - if (options::Options.FullScreen) + if (Options.FullScreen) options::toggle(Menu1::Full_Screen); SDL_MinimizeWindow(MainWindow); break; @@ -664,14 +669,14 @@ int winmain::event_handler(const SDL_Event* event) SDL_SetWindowGrab(MainWindow, SDL_TRUE); } else - pb::keydown(options::Options.Key.LeftFlipper); + pb::keydown(Options.Key.LeftFlipper); break; case SDL_BUTTON_RIGHT: if (!pb::cheat_mode) - pb::keydown(options::Options.Key.RightFlipper); + pb::keydown(Options.Key.RightFlipper); break; case SDL_BUTTON_MIDDLE: - pb::keydown(options::Options.Key.Plunger); + pb::keydown(Options.Key.Plunger); break; default: break; @@ -688,14 +693,14 @@ int winmain::event_handler(const SDL_Event* event) SDL_SetWindowGrab(MainWindow, SDL_FALSE); } if (!pb::cheat_mode) - pb::keyup(options::Options.Key.LeftFlipper); + pb::keyup(Options.Key.LeftFlipper); break; case SDL_BUTTON_RIGHT: if (!pb::cheat_mode) - pb::keyup(options::Options.Key.RightFlipper); + pb::keyup(Options.Key.RightFlipper); break; case SDL_BUTTON_MIDDLE: - pb::keyup(options::Options.Key.Plunger); + pb::keyup(Options.Key.Plunger); break; default: break; @@ -709,7 +714,7 @@ int winmain::event_handler(const SDL_Event* event) case SDL_WINDOWEVENT_SHOWN: activated = 1; Sound::Activate(); - if (options::Options.Music && !single_step) + if (Options.Music && !single_step) midi::play_pb_theme(); no_time_loss = 1; has_focus = 1; @@ -718,7 +723,7 @@ int winmain::event_handler(const SDL_Event* event) case SDL_WINDOWEVENT_HIDDEN: activated = 0; fullscrn::activate(0); - options::Options.FullScreen = 0; + Options.FullScreen = false; Sound::Deactivate(); midi::music_stop(); has_focus = 0; @@ -789,6 +794,7 @@ void winmain::a_dialog() ImGui::Separator(); ImGui::TextUnformatted("Decompiled -> Ported to SDL"); + ImGui::TextUnformatted("Version 2.0"); if (ImGui::SmallButton("Project home: https://github.com/k4zmu2a/SpaceCadetPinball")) { #if SDL_VERSION_ATLEAST(2, 0, 14) @@ -837,7 +843,7 @@ void winmain::Restart() void winmain::UpdateFrameRate() { // UPS >= FPS - auto fps = options::Options.FramesPerSecond, ups = options::Options.UpdatesPerSecond; + auto fps = Options.FramesPerSecond, ups = Options.UpdatesPerSecond; UpdateToFrameRatio = static_cast(ups) / fps; TargetFrameTime = DurationMs(1000.0 / ups); } diff --git a/SpaceCadetPinball/winmain.h b/SpaceCadetPinball/winmain.h index d99a98c..fc75257 100644 --- a/SpaceCadetPinball/winmain.h +++ b/SpaceCadetPinball/winmain.h @@ -72,6 +72,7 @@ private: static bool ShowSpriteViewer; static double UpdateToFrameRatio; static DurationMs TargetFrameTime; + static struct optionsStruct& Options; static void RenderUi(); };