diff --git a/pcsx2-qt/QtHost.cpp b/pcsx2-qt/QtHost.cpp index 07e53c2790..ca012221b9 100644 --- a/pcsx2-qt/QtHost.cpp +++ b/pcsx2-qt/QtHost.cpp @@ -879,38 +879,6 @@ void EmuThread::endCapture() MTGS::RunOnGSThread(&GSEndCapture); } -void EmuThread::setAudioOutputVolume(int volume, int fast_forward_volume) -{ - if (!isOnEmuThread()) - { - QMetaObject::invokeMethod(this, "setAudioOutputVolume", Qt::QueuedConnection, Q_ARG(int, volume), - Q_ARG(int, fast_forward_volume)); - return; - } - - if (!VMManager::HasValidVM()) - return; - - EmuConfig.SPU2.OutputVolume = static_cast(volume); - EmuConfig.SPU2.FastForwardVolume = static_cast(fast_forward_volume); - SPU2::SetOutputVolume(SPU2::GetResetVolume()); -} - -void EmuThread::setAudioOutputMuted(bool muted) -{ - if (!isOnEmuThread()) - { - QMetaObject::invokeMethod(this, "setAudioOutputMuted", Qt::QueuedConnection, Q_ARG(bool, muted)); - return; - } - - if (!VMManager::HasValidVM()) - return; - - EmuConfig.SPU2.OutputMuted = muted; - SPU2::SetOutputVolume(SPU2::GetResetVolume()); -} - std::optional EmuThread::acquireRenderWindow(bool recreate_window) { // Check if we're wanting to get exclusive fullscreen. This should be safe to read, since we're going to be calling from the GS thread. diff --git a/pcsx2-qt/QtHost.h b/pcsx2-qt/QtHost.h index 0d6bac097b..46a7ba290e 100644 --- a/pcsx2-qt/QtHost.h +++ b/pcsx2-qt/QtHost.h @@ -112,8 +112,6 @@ public Q_SLOTS: void queueSnapshot(quint32 gsdump_frames); void beginCapture(const QString& path); void endCapture(); - void setAudioOutputVolume(int volume, int fast_forward_volume); - void setAudioOutputMuted(bool muted); Q_SIGNALS: bool messageConfirmed(const QString& title, const QString& message); diff --git a/pcsx2/Hotkeys.cpp b/pcsx2/Hotkeys.cpp index cfad631873..daa3eef413 100644 --- a/pcsx2/Hotkeys.cpp +++ b/pcsx2/Hotkeys.cpp @@ -40,28 +40,58 @@ static void HotkeyAdjustTargetSpeed(double delta) Host::OSD_QUICK_DURATION); } -static void HotkeyAdjustVolume(s32 fixed, s32 delta) +static void HotkeyAdjustVolume(const s32 delta) { if (!VMManager::HasValidVM()) return; - const s32 current_vol = static_cast(SPU2::GetOutputVolume()); - const s32 new_volume = - std::clamp((fixed >= 0) ? fixed : (current_vol + delta), 0, static_cast(Pcsx2Config::SPU2Options::MAX_VOLUME)); - if (current_vol != new_volume) + // Volume-adjusting hotkeys override mute toggle hotkey. EmuConfig.SPU2.OutputMuted overrides hotkeys. + if (!SPU2::SetOutputMuted(false)) + { + Host::AddIconOSDMessage("VolumeChanged", ICON_FA_VOLUME_XMARK, TRANSLATE_STR("Hotkeys", "Volume: Muted in Settings")); + return; + } + + const s32 current_volume = static_cast(SPU2::GetOutputVolume()); + const s32 maximum_volume = static_cast(Pcsx2Config::SPU2Options::MAX_VOLUME); + const s32 new_volume = std::clamp(current_volume + delta, 0, maximum_volume); + + if (current_volume != new_volume) SPU2::SetOutputVolume(static_cast(new_volume)); - if (new_volume == 0) + if (new_volume > 0 && new_volume < maximum_volume) { - Host::AddIconOSDMessage("VolumeChanged", ICON_FA_VOLUME_XMARK, TRANSLATE_STR("Hotkeys", "Volume: Muted")); + Host::AddIconOSDMessage("VolumeChanged", new_volume < 100 ? ICON_FA_VOLUME_LOW : ICON_FA_VOLUME_HIGH, + fmt::format(TRANSLATE_FS("Hotkeys", "Volume: {} to {}%"), delta < 0 ? TRANSLATE_STR("Hotkeys", "Decreased") : TRANSLATE_STR("Hotkeys", "Increased"), new_volume)); } else { - Host::AddIconOSDMessage("VolumeChanged", (current_vol < new_volume) ? ICON_FA_VOLUME_HIGH : ICON_FA_VOLUME_LOW, - fmt::format(TRANSLATE_FS("Hotkeys", "Volume: {}%"), new_volume)); + Host::AddIconOSDMessage("VolumeChanged", delta < 0 ? ICON_FA_VOLUME_OFF : ICON_FA_VOLUME_HIGH, + fmt::format(TRANSLATE_FS("Hotkeys", "Volume: {} {}% Reached"), delta < 0 ? TRANSLATE_STR("Hotkeys", "Minimum") : TRANSLATE_STR("Hotkeys", "Maximum"), new_volume)); } } +static void HotkeyToggleMute() +{ + if (!VMManager::HasValidVM()) + return; + + // Attempt to toggle output muting. EmuConfig.SPU2.OutputMuted overrides hotkeys. + if (SPU2::SetOutputMuted(!SPU2::IsOutputMuted())) + { + if (SPU2::IsOutputMuted()) + Host::AddIconOSDMessage("VolumeChanged", ICON_FA_VOLUME_XMARK, TRANSLATE_STR("Hotkeys", "Volume: Muted")); + else + { + const u32 current_volume = SPU2::GetOutputVolume(); + Host::AddIconOSDMessage("VolumeChanged", current_volume < 100 ? (current_volume == 0 ? ICON_FA_VOLUME_OFF : ICON_FA_VOLUME_LOW) : ICON_FA_VOLUME_HIGH, + fmt::format(TRANSLATE_FS("Hotkeys", "Volume: Unmuted to {}%"), current_volume)); + } + } + else + Host::AddIconOSDMessage("VolumeChanged", ICON_FA_VOLUME_XMARK, TRANSLATE_STR("Hotkeys", "Volume: Muted in Settings")); +} + static void HotkeyLoadStateSlot(s32 slot) { // Can reapply settings and thus binds, therefore must be deferred. @@ -293,16 +323,16 @@ DEFINE_HOTKEY_LOADSTATE_X(10, TRANSLATE_NOOP("Hotkeys", "Load State From Slot 10 #undef DEFINE_HOTKEY_LOADSTATE_X DEFINE_HOTKEY("Mute", TRANSLATE_NOOP("Hotkeys", "Audio"), TRANSLATE_NOOP("Hotkeys", "Toggle Mute"), [](s32 pressed) { if (!pressed && VMManager::HasValidVM()) - HotkeyAdjustVolume((SPU2::GetOutputVolume() == 0) ? SPU2::GetResetVolume() : 0, 0); + HotkeyToggleMute(); }) DEFINE_HOTKEY("IncreaseVolume", TRANSLATE_NOOP("Hotkeys", "Audio"), TRANSLATE_NOOP("Hotkeys", "Increase Volume"), [](s32 pressed) { if (!pressed && VMManager::HasValidVM()) - HotkeyAdjustVolume(-1, 5); + HotkeyAdjustVolume(5); }) DEFINE_HOTKEY("DecreaseVolume", TRANSLATE_NOOP("Hotkeys", "Audio"), TRANSLATE_NOOP("Hotkeys", "Decrease Volume"), [](s32 pressed) { if (!pressed && VMManager::HasValidVM()) - HotkeyAdjustVolume(-1, -5); + HotkeyAdjustVolume(-5); }) END_HOTKEY_LIST() diff --git a/pcsx2/SPU2/spu2.cpp b/pcsx2/SPU2/spu2.cpp index 4da7e1c253..a0f1d1f6a3 100644 --- a/pcsx2/SPU2/spu2.cpp +++ b/pcsx2/SPU2/spu2.cpp @@ -28,10 +28,13 @@ u32 lClocks = 0; static bool s_audio_capture_active = false; static bool s_psxmode = false; +static bool s_output_muted = false; static std::unique_ptr s_output_stream; static std::array s_current_chunk; static u32 s_current_chunk_pos; +static u32 s_standard_volume = 0; +static u32 s_fast_forward_volume = 0; u32 SPU2::GetConsoleSampleRate() { @@ -97,8 +100,17 @@ void SPU2writeDMA7Mem(u16* pMem, u32 size) void SPU2::CreateOutputStream() { - // Persist volume through stream recreates. - const u32 volume = s_output_stream ? s_output_stream->GetOutputVolume() : GetResetVolume(); + // Initialize volume and mute settings on new session. + if (!s_output_stream) + { + s_standard_volume = EmuConfig.SPU2.OutputVolume; + s_fast_forward_volume = EmuConfig.SPU2.FastForwardVolume; + s_output_muted = EmuConfig.SPU2.OutputMuted; + } + // Else persist volume through stream recreates. + else if (!s_output_muted) + SPU2::SaveOutputVolume(); + const u32 sample_rate = GetConsoleSampleRate(); s_output_stream.reset(); @@ -114,7 +126,7 @@ void SPU2::CreateOutputStream() s_output_stream = AudioStream::CreateNullStream(sample_rate, EmuConfig.SPU2.StreamParameters.buffer_ms); } - s_output_stream->SetOutputVolume(volume); + SPU2::UpdateOutputVolume(); s_output_stream->SetNominalRate(GetNominalRate()); s_output_stream->SetPaused(VMManager::GetState() == VMState::Paused); } @@ -144,20 +156,52 @@ void SPU2::SetOutputVolume(u32 volume) s_output_stream->SetOutputVolume(volume); } -u32 SPU2::GetResetVolume() -{ - return EmuConfig.SPU2.OutputMuted ? 0 : - ((VMManager::GetTargetSpeed() != 1.0f) ? - EmuConfig.SPU2.FastForwardVolume : - EmuConfig.SPU2.OutputVolume); -} - float SPU2::GetNominalRate() { // Adjust nominal rate when syncing to host. return VMManager::IsTargetSpeedAdjustedToHost() ? VMManager::GetTargetSpeed() : 1.0f; } +bool SPU2::SetOutputMuted(const bool muted) +{ + // User setting takes precedence. Unmute not guaranteed by design. + if (!s_output_stream || (!muted && EmuConfig.SPU2.OutputMuted)) + return false; + + if (muted == s_output_muted) + return true; + + if (muted) + SPU2::SaveOutputVolume(); + + s_output_muted = muted; + SPU2::UpdateOutputVolume(); + return true; +} + +bool SPU2::IsOutputMuted() +{ + return s_output_muted; +} + +void SPU2::UpdateOutputVolume() +{ + s_output_stream->SetOutputVolume(s_output_muted ? + 0 : (VMManager::GetTargetSpeed() == 1.0f ? + s_standard_volume : s_fast_forward_volume)); +} + +void SPU2::SaveOutputVolume() +{ + if (!s_output_muted) + { + if (VMManager::GetTargetSpeed() == 1.0f) + s_standard_volume = s_output_stream->GetOutputVolume(); + else + s_fast_forward_volume = s_output_stream->GetOutputVolume(); + } +} + void SPU2::SetOutputPaused(bool paused) { s_output_stream->SetPaused(paused); @@ -210,8 +254,20 @@ void SPU2::OnTargetSpeedChanged() s_output_stream->SetNominalRate(GetNominalRate()); - if (EmuConfig.SPU2.OutputVolume != EmuConfig.SPU2.FastForwardVolume && !EmuConfig.SPU2.OutputMuted) - s_output_stream->SetOutputVolume(GetResetVolume()); + // Flipped save as speed has already changed. + if (!s_output_muted) + { + if (VMManager::GetTargetSpeed() == 1.0f) + { + s_fast_forward_volume = s_output_stream->GetOutputVolume(); + s_output_stream->SetOutputVolume(s_standard_volume); + } + else + { + s_standard_volume = s_output_stream->GetOutputVolume(); + s_output_stream->SetOutputVolume(s_fast_forward_volume); + } + } } bool SPU2::Open() @@ -265,13 +321,25 @@ void SPU2::CheckForConfigChanges(const Pcsx2Config& old_config) const Pcsx2Config::SPU2Options& oldopts = old_config.SPU2; // No need to reinit for volume change. - if ((opts.OutputVolume != oldopts.OutputVolume && VMManager::GetTargetSpeed() == 1.0f) || - (opts.FastForwardVolume != oldopts.FastForwardVolume && VMManager::GetTargetSpeed() != 1.0f) || - opts.OutputMuted != oldopts.OutputMuted) + if (opts.OutputMuted != oldopts.OutputMuted) + SPU2::SetOutputMuted(opts.OutputMuted); + + bool volume_settings_changed = false; + if (opts.OutputVolume != oldopts.OutputVolume) { - SetOutputVolume(GetResetVolume()); + s_standard_volume = opts.OutputVolume; + volume_settings_changed = true; } + if (opts.FastForwardVolume != oldopts.FastForwardVolume) + { + s_fast_forward_volume = opts.FastForwardVolume; + volume_settings_changed = true; + } + + if (volume_settings_changed) + SPU2::UpdateOutputVolume(); + // Things which require re-initialzing the output. if (opts.Backend != oldopts.Backend || opts.StreamParameters != oldopts.StreamParameters || diff --git a/pcsx2/SPU2/spu2.h b/pcsx2/SPU2/spu2.h index 3865b6ed3d..ca944569ca 100644 --- a/pcsx2/SPU2/spu2.h +++ b/pcsx2/SPU2/spu2.h @@ -36,8 +36,17 @@ u32 GetOutputVolume(); /// Directly updates the output volume without going through the configuration. void SetOutputVolume(u32 volume); -/// Returns the volume that we would reset the output to on startup. -u32 GetResetVolume(); +/// Sets up muting and unmuting and reports success or failure. +bool SetOutputMuted(const bool muted); + +/// Returns true if the output is muted (distinct from 0%). +bool IsOutputMuted(); + +/// Updates the current volume based on running state. +void UpdateOutputVolume(); + +/// Saves the current volume based on running state. +void SaveOutputVolume(); /// Pauses/resumes the output stream. void SetOutputPaused(bool paused); @@ -74,4 +83,3 @@ extern u32 lClocks; extern void TimeUpdate(u32 cClocks); extern void SPU2_FastWrite(u32 rmem, u16 value); -//#define PCM24_S1_INTERLEAVE