Sound: added channel recycling.

Added sound channel count (aka voices) user option.
Added 3DPB font to sprite viewer.
Added version number to about dialog.
This commit is contained in:
Muzychenko Andrey 2021-10-09 17:28:30 +03:00
parent d80074b9b6
commit 43593b168d
8 changed files with 137 additions and 87 deletions

View File

@ -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);
}

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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<std::string, std::string> 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:

View File

@ -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();

View File

@ -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();
}

View File

@ -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<Menu1>(static_cast<int>(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<double>(ups) / fps;
TargetFrameTime = DurationMs(1000.0 / ups);
}

View File

@ -72,6 +72,7 @@ private:
static bool ShowSpriteViewer;
static double UpdateToFrameRatio;
static DurationMs TargetFrameTime;
static struct optionsStruct& Options;
static void RenderUi();
};