Added sound and music volume control.

Issue #120.
This commit is contained in:
Muzychenko Andrey 2022-01-12 17:17:38 +03:00
parent 43af97127b
commit 0a2d6847ba
8 changed files with 99 additions and 46 deletions

View File

@ -4,9 +4,11 @@
int Sound::num_channels;
bool Sound::enabled_flag = false;
int* Sound::TimeStamps = nullptr;
int Sound::Volume = MIX_MAX_VOLUME;
bool Sound::Init(int channels, bool enableFlag)
bool Sound::Init(int channels, bool enableFlag, int volume)
{
Volume = volume;
Mix_Init(MIX_INIT_MID_Proxy);
auto result = Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, 2, 1024);
SetChannels(channels);
@ -80,4 +82,11 @@ void Sound::SetChannels(int channels)
delete[] TimeStamps;
TimeStamps = new int[num_channels]();
Mix_AllocateChannels(num_channels);
SetVolume(Volume);
}
void Sound::SetVolume(int volume)
{
Volume = volume;
Mix_Volume(-1, volume);
}

View File

@ -4,7 +4,7 @@
class Sound
{
public:
static bool Init(int channels, bool enableFlag);
static bool Init(int channels, bool enableFlag, int volume);
static void Enable(bool enableFlag);
static void Activate();
static void Deactivate();
@ -13,8 +13,10 @@ public:
static Mix_Chunk* LoadWaveFile(const std::string& lpName);
static void FreeSound(Mix_Chunk* wave);
static void SetChannels(int channels);
static void SetVolume(int volume);
private:
static int num_channels;
static bool enabled_flag;
static int* TimeStamps;
static int Volume;
};

View File

@ -9,6 +9,7 @@
std::vector<Mix_Music*> midi::LoadedTracks{};
Mix_Music *midi::track1, *midi::track2, *midi::track3, *midi::active_track, *midi::NextTrack;
bool midi::SetNextTrackFlag;
int midi::Volume = MIX_MAX_VOLUME;
constexpr uint32_t FOURCC(uint8_t a, uint8_t b, uint8_t c, uint8_t d)
{
@ -47,8 +48,9 @@ int midi::music_stop()
return true;
}
int midi::music_init()
int midi::music_init(int volume)
{
SetVolume(volume);
active_track = nullptr;
if (pb::FullTiltMode)
@ -87,6 +89,12 @@ void midi::music_shutdown()
LoadedTracks.clear();
}
void midi::SetVolume(int volume)
{
Volume = volume;
Mix_VolumeMusic(volume);
}
Mix_Music* midi::load_track(std::string fileName)
{
Mix_Music* audio = nullptr;
@ -157,6 +165,9 @@ bool midi::play_track(Mix_Music* midi)
return false;
}
// On Windows, MIDI volume can only be set during playback.
// And it changes application master volume for some reason.
SetVolume(Volume);
active_track = midi;
return true;
}

View File

@ -88,12 +88,15 @@ class midi
public:
static int play_pb_theme();
static int music_stop();
static int music_init();
static int music_init(int volume);
static void music_shutdown();
static void SetVolume(int volume);
private:
static std::vector<Mix_Music*> LoadedTracks;
static Mix_Music *track1, *track2, *track3, *active_track, *NextTrack;
static bool SetNextTrackFlag;
static int Volume;
static Mix_Music* load_track(std::string fileName);
static bool play_track(Mix_Music* midi);
static std::vector<uint8_t>* MdsToMidi(std::string file);

View File

@ -9,6 +9,7 @@
constexpr int options::MaxUps, options::MaxFps, options::MinUps, options::MinFps, options::DefUps, options::DefFps;
constexpr int options::MaxSoundChannels, options::MinSoundChannels, options::DefSoundChannels;
constexpr int options::MaxVolume, options::MinVolume, options::DefVolume;
optionsStruct options::Options{};
std::map<std::string, std::string> options::settings{};
@ -92,16 +93,17 @@ void options::InitPrimary()
ImGui::GetIO().FontGlobalScale = get_float("UI Scale", 1.0f);
Options.Resolution = get_int("Screen Resolution", -1);
Options.LinearFiltering = get_int("Linear Filtering", true);
Options.FramesPerSecond = std::min(MaxFps, std::max(MinUps, get_int("Frames Per Second", DefFps)));
Options.UpdatesPerSecond = std::min(MaxUps, std::max(MinUps, get_int("Updates Per Second", DefUps)));
Options.FramesPerSecond = Clamp(get_int("Frames Per Second", DefFps), MinFps, MaxFps);
Options.UpdatesPerSecond = Clamp(get_int("Updates Per Second", DefUps), MinUps, MaxUps);
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));
Options.SoundChannels = Clamp(get_int("Sound Channels", DefSoundChannels), MinSoundChannels, MaxSoundChannels);
Options.HybridSleep = get_int("HybridSleep", false);
Options.Prefer3DPBGameData = get_int("Prefer 3DPB Game Data", false);
Options.IntegerScaling = get_int("Integer Scaling", false);
Options.SoundVolume = Clamp(get_int("Sound Volume", DefVolume), MinVolume, MaxVolume);
Options.MusicVolume = Clamp(get_int("Music Volume", DefVolume), MinVolume, MaxVolume);
}
void options::InitSecondary()
@ -139,6 +141,8 @@ void options::uninit()
set_int("HybridSleep", Options.HybridSleep);
set_int("Prefer 3DPB Game Data", Options.Prefer3DPBGameData);
set_int("Integer Scaling", Options.IntegerScaling);
set_int("Sound Volume", Options.SoundVolume);
set_int("Music Volume", Options.MusicVolume);
}

View File

@ -78,6 +78,8 @@ struct optionsStruct
bool HybridSleep;
bool Prefer3DPBGameData;
bool IntegerScaling;
int SoundVolume;
int MusicVolume;
};
struct ControlRef
@ -95,6 +97,7 @@ public:
DefUps = 120, DefFps = 60;
// Original uses 8 sound channels
static constexpr int MaxSoundChannels = 32, MinSoundChannels = 1, DefSoundChannels = 8;
static constexpr int MaxVolume = MIX_MAX_VOLUME, MinVolume = 0, DefVolume = MaxVolume;
static optionsStruct Options;
static void InitPrimary();

View File

@ -78,12 +78,18 @@ inline float RandFloat()
return static_cast<float>(std::rand() / static_cast<double>(RAND_MAX));
}
template <typename T> constexpr
int Sign(T val)
template <typename T>
constexpr int Sign(T val)
{
return (T(0) < val) - (val < T(0));
}
template <typename T>
const T& Clamp(const T& n, const T& lower, const T& upper)
{
return std::max(lower, std::min(n, upper));
}
// UTF-8 path adapter for fopen on Windows, implemented in SpaceCadetPinball.cpp
#ifdef _WIN32
extern FILE* fopenu(const char* path, const char* opt);

View File

@ -133,10 +133,10 @@ int winmain::WinMain(LPCSTR lpCmdLine)
// Second step: run updates depending on FullTiltMode
options::InitSecondary();
if (!Sound::Init(Options.SoundChannels, Options.Sounds))
if (!Sound::Init(Options.SoundChannels, Options.Sounds, Options.SoundVolume))
Options.Sounds = false;
if (!pinball::quickFlag && !midi::music_init())
if (!pinball::quickFlag && !midi::music_init(Options.MusicVolume))
Options.Music = false;
if (pb::init())
@ -291,8 +291,7 @@ int winmain::WinMain(LPCSTR lpCmdLine)
}
// Limit duration to 2 * target time
sleepRemainder = std::max(std::min(DurationMs(frameEnd - updateEnd) - targetTimeDelta, TargetFrameTime),
-TargetFrameTime);
sleepRemainder = Clamp(DurationMs(frameEnd - updateEnd) - targetTimeDelta, -TargetFrameTime, TargetFrameTime);
frameDuration = std::min<DurationMs>(DurationMs(frameEnd - frameStart), 2 * TargetFrameTime);
frameStart = frameEnd;
UpdateToFrameCounter++;
@ -431,50 +430,46 @@ void winmain::RenderUi()
}
ImGui::EndMenu();
}
ImGui::Separator();
if (ImGui::MenuItem("Sound", "F5", Options.Sounds))
{
options::toggle(Menu1::Sounds);
}
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"))
{
pause(false);
options::ShowControlDialog();
}
if (ImGui::BeginMenu("Table Resolution"))
ImGui::Separator();
if (ImGui::BeginMenu("Audio"))
{
char buffer[20]{};
auto maxResText = pinball::get_rc_string(fullscrn::GetMaxResolution() + 2030, 0);
if (ImGui::MenuItem(maxResText, nullptr, Options.Resolution == -1))
if (ImGui::MenuItem("Sound", "F5", Options.Sounds))
{
options::toggle(Menu1::MaximumResolution);
options::toggle(Menu1::Sounds);
}
for (auto i = 0; i <= fullscrn::GetMaxResolution(); i++)
ImGui::TextUnformatted("Sound Volume");
if (ImGui::SliderInt("##Sound Volume", &Options.SoundVolume, options::MinVolume, options::MaxVolume, "%d",
ImGuiSliderFlags_AlwaysClamp))
{
auto& res = fullscrn::resolution_array[i];
snprintf(buffer, sizeof buffer - 1, "%d x %d", res.ScreenWidth, res.ScreenHeight);
if (ImGui::MenuItem(buffer, nullptr, Options.Resolution == i))
{
options::toggle(static_cast<Menu1>(static_cast<int>(Menu1::R640x480) + i));
}
Sound::SetVolume(Options.SoundVolume);
}
ImGui::TextUnformatted("Sound Channels");
if (ImGui::SliderInt("##Sound Channels", &Options.SoundChannels, options::MinSoundChannels,
options::MaxSoundChannels, "%d", ImGuiSliderFlags_AlwaysClamp))
{
Sound::SetChannels(Options.SoundChannels);
}
ImGui::Separator();
if (ImGui::MenuItem("Music", "F6", Options.Music))
{
options::toggle(Menu1::Music);
}
ImGui::TextUnformatted("Music Volume");
if (ImGui::SliderInt("##Music Volume", &Options.MusicVolume, options::MinVolume, options::MaxVolume, "%d",
ImGuiSliderFlags_AlwaysClamp))
{
midi::SetVolume(Options.MusicVolume);
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Graphics"))
{
if (ImGui::MenuItem("Uniform Scaling", nullptr, Options.UniformScaling))
@ -533,6 +528,26 @@ void winmain::RenderUi()
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Table Resolution"))
{
char buffer[20]{};
auto maxResText = pinball::get_rc_string(fullscrn::GetMaxResolution() + 2030, 0);
if (ImGui::MenuItem(maxResText, nullptr, Options.Resolution == -1))
{
options::toggle(Menu1::MaximumResolution);
}
for (auto i = 0; i <= fullscrn::GetMaxResolution(); i++)
{
auto& res = fullscrn::resolution_array[i];
snprintf(buffer, sizeof buffer - 1, "%d x %d", res.ScreenWidth, res.ScreenHeight);
if (ImGui::MenuItem(buffer, nullptr, Options.Resolution == i))
{
options::toggle(static_cast<Menu1>(static_cast<int>(Menu1::R640x480) + i));
}
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Game Data"))
{
if (ImGui::MenuItem("Prefer 3DPB Data", nullptr, Options.Prefer3DPBGameData))