From fe1bebc12db1a2fe2836a2ca2948ae03e2def9ed Mon Sep 17 00:00:00 2001 From: Stenzek Date: Fri, 30 Dec 2022 15:35:16 +1000 Subject: [PATCH] SPU2: Namespace logging/debugging --- pcsx2/CMakeLists.txt | 5 - pcsx2/Config.h | 60 ++- pcsx2/Counters.cpp | 6 +- pcsx2/Frontend/CommonHotkeys.cpp | 1 - pcsx2/Hw.cpp | 2 +- pcsx2/HwWrite.cpp | 2 +- pcsx2/Pcsx2Config.cpp | 58 ++- pcsx2/SPU2/Config.cpp | 120 ------ pcsx2/SPU2/Config.h | 125 ------ pcsx2/SPU2/ConfigDebug.cpp | 100 ----- pcsx2/SPU2/ConfigSoundTouch.cpp | 62 --- pcsx2/SPU2/Debug.cpp | 93 ++--- pcsx2/SPU2/Debug.h | 65 ++- pcsx2/SPU2/Dma.cpp | 59 +-- pcsx2/SPU2/Dma.h | 1 + pcsx2/SPU2/Global.h | 27 -- pcsx2/SPU2/Mixer.cpp | 69 ++-- pcsx2/SPU2/Mixer.h | 57 +-- pcsx2/SPU2/ReadInput.cpp | 16 +- pcsx2/SPU2/RegLog.cpp | 22 +- pcsx2/SPU2/Reverb.cpp | 6 +- pcsx2/SPU2/SndOut.cpp | 659 ++++++++++++++++++++++++++++--- pcsx2/SPU2/SndOut.h | 201 +--------- pcsx2/SPU2/SndOut_Cubeb.cpp | 13 +- pcsx2/SPU2/SndOut_XAudio2.cpp | 20 +- pcsx2/SPU2/Timestretcher.cpp | 562 -------------------------- pcsx2/SPU2/Wavedump_wav.cpp | 2 +- pcsx2/SPU2/defs.h | 5 + pcsx2/SPU2/spu2.cpp | 254 ++++++------ pcsx2/SPU2/spu2.h | 44 ++- pcsx2/SPU2/spu2sys.cpp | 159 ++++---- pcsx2/SaveState.cpp | 6 +- pcsx2/VMManager.cpp | 59 +-- pcsx2/pcsx2core.vcxproj | 5 - pcsx2/pcsx2core.vcxproj.filters | 15 - 35 files changed, 1167 insertions(+), 1793 deletions(-) delete mode 100644 pcsx2/SPU2/Config.cpp delete mode 100644 pcsx2/SPU2/Config.h delete mode 100644 pcsx2/SPU2/ConfigDebug.cpp delete mode 100644 pcsx2/SPU2/ConfigSoundTouch.cpp delete mode 100644 pcsx2/SPU2/Timestretcher.cpp diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index bc7bff13e3..f87b0fa82c 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -277,9 +277,6 @@ set(pcsx2CDVDHeaders # SPU2 sources set(pcsx2SPU2Sources SPU2/ADSR.cpp - SPU2/Config.cpp - SPU2/ConfigDebug.cpp - SPU2/ConfigSoundTouch.cpp SPU2/Debug.cpp SPU2/DplIIdecoder.cpp SPU2/Dma.cpp @@ -292,13 +289,11 @@ set(pcsx2SPU2Sources SPU2/SndOut.cpp SPU2/spu2freeze.cpp SPU2/spu2sys.cpp - SPU2/Timestretcher.cpp SPU2/Wavedump_wav.cpp ) # SPU2 headers set(pcsx2SPU2Headers - SPU2/Config.h SPU2/Debug.h SPU2/defs.h SPU2/Dma.h diff --git a/pcsx2/Config.h b/pcsx2/Config.h index 7ff1d17d27..3f5a5690e6 100644 --- a/pcsx2/Config.h +++ b/pcsx2/Config.h @@ -794,27 +794,49 @@ struct Pcsx2Config NoSync, }; + static constexpr s32 MAX_VOLUME = 200; + + static constexpr s32 MIN_LATENCY = 3; + static constexpr s32 MIN_LATENCY_TIMESTRETCH = 15; + static constexpr s32 MAX_LATENCY = 750; + + static constexpr s32 MIN_SEQUENCE_LEN = 20; + static constexpr s32 MAX_SEQUENCE_LEN = 100; + static constexpr s32 MIN_SEEKWINDOW = 10; + static constexpr s32 MAX_SEEKWINDOW = 30; + static constexpr s32 MIN_OVERLAP = 5; + static constexpr s32 MAX_OVERLAP = 15; + BITFIELD32() bool - AdvancedVolumeControl : 1; + DebugEnabled : 1, + MsgToConsole : 1, + MsgKeyOnOff : 1, + MsgVoiceOff : 1, + MsgDMA : 1, + MsgAutoDMA : 1, + MsgOverruns : 1, + MsgCache : 1, + AccessLog : 1, + DMALog : 1, + WaveLog : 1, + CoresDump : 1, + MemDump : 1, + RegDump : 1, + VisualDebugEnabled : 1; BITFIELD_END InterpolationMode Interpolation = InterpolationMode::Gaussian; SynchronizationMode SynchMode = SynchronizationMode::TimeStretch; s32 FinalVolume = 100; - s32 Latency{100}; - s32 SpeakerConfiguration{0}; - s32 DplDecodingLevel{0}; + s32 Latency = 100; + s32 SpeakerConfiguration = 0; + s32 DplDecodingLevel = 0; - float VolumeAdjustC{ 0.0f }; - float VolumeAdjustFL{ 0.0f }; - float VolumeAdjustFR{ 0.0f }; - float VolumeAdjustBL{ 0.0f }; - float VolumeAdjustBR{ 0.0f }; - float VolumeAdjustSL{ 0.0f }; - float VolumeAdjustSR{ 0.0f }; - float VolumeAdjustLFE{ 0.0f }; + s32 SequenceLenMS = 30; + s32 SeekWindowMS = 20; + s32 OverlapMS = 10; std::string OutputModule; std::string BackendName; @@ -835,14 +857,9 @@ struct Pcsx2Config OpEqu(SpeakerConfiguration) && OpEqu(DplDecodingLevel) && - OpEqu(VolumeAdjustC) && - OpEqu(VolumeAdjustFL) && - OpEqu(VolumeAdjustFR) && - OpEqu(VolumeAdjustBL) && - OpEqu(VolumeAdjustBR) && - OpEqu(VolumeAdjustSL) && - OpEqu(VolumeAdjustSR) && - OpEqu(VolumeAdjustLFE) && + OpEqu(SequenceLenMS) && + OpEqu(SeekWindowMS) && + OpEqu(OverlapMS) && OpEqu(OutputModule) && OpEqu(BackendName); @@ -1295,6 +1312,9 @@ namespace EmuFolders void SetDefaults(SettingsInterface& si); void LoadConfig(SettingsInterface& si); bool EnsureFoldersExist(); + + /// Opens the specified log file for writing. + std::FILE* OpenLogFile(const std::string_view& name, const char* mode); } // namespace EmuFolders ///////////////////////////////////////////////////////////////////////////////////////// diff --git a/pcsx2/Counters.cpp b/pcsx2/Counters.cpp index 9089627a67..2538877f2d 100644 --- a/pcsx2/Counters.cpp +++ b/pcsx2/Counters.cpp @@ -349,7 +349,7 @@ static double AdjustToHostRefreshRate(double vertical_frequency, double frame_li { if (!EmuConfig.GS.SyncToHostRefreshRate || EmuConfig.GS.LimitScalar != 1.0f) { - SPU2SetDeviceSampleRateMultiplier(1.0); + SPU2::SetDeviceSampleRateMultiplier(1.0); s_use_vsync_for_timing = false; return frame_limit; } @@ -358,7 +358,7 @@ static double AdjustToHostRefreshRate(double vertical_frequency, double frame_li if (!g_host_display->GetHostRefreshRate(&host_refresh_rate)) { Console.Warning("Cannot sync to host refresh since the query failed."); - SPU2SetDeviceSampleRateMultiplier(1.0); + SPU2::SetDeviceSampleRateMultiplier(1.0); s_use_vsync_for_timing = false; return frame_limit; } @@ -374,7 +374,7 @@ static double AdjustToHostRefreshRate(double vertical_frequency, double frame_li return frame_limit; frame_limit *= ratio; - SPU2SetDeviceSampleRateMultiplier(ratio); + SPU2::SetDeviceSampleRateMultiplier(ratio); return frame_limit; } diff --git a/pcsx2/Frontend/CommonHotkeys.cpp b/pcsx2/Frontend/CommonHotkeys.cpp index 59e33d64a8..12c7b8829e 100644 --- a/pcsx2/Frontend/CommonHotkeys.cpp +++ b/pcsx2/Frontend/CommonHotkeys.cpp @@ -45,7 +45,6 @@ static void HotkeyAdjustTargetSpeed(double delta) const double min_speed = Achievements::ChallengeModeActive() ? 1.0 : 0.1; EmuConfig.Framerate.NominalScalar = std::max(min_speed, EmuConfig.GS.LimitScalar + delta); VMManager::SetLimiterMode(LimiterModeType::Nominal); - gsUpdateFrequency(EmuConfig); Host::AddIconOSDMessage("SpeedChanged", ICON_FA_CLOCK, fmt::format("Target speed set to {:.0f}%.", std::round(EmuConfig.Framerate.NominalScalar * 100.0)), Host::OSD_QUICK_DURATION); } diff --git a/pcsx2/Hw.cpp b/pcsx2/Hw.cpp index 53e0b30043..5c9c59567d 100644 --- a/pcsx2/Hw.cpp +++ b/pcsx2/Hw.cpp @@ -68,7 +68,7 @@ void hwReset() // Sets SPU2 sample rate to PS2 standard (48KHz) whenever emulator is reset. // For PSX mode sample rate setting, see HwWrite.cpp - SPU2reset(PS2Modes::PS2); + SPU2::Reset(false); sifReset(); diff --git a/pcsx2/HwWrite.cpp b/pcsx2/HwWrite.cpp index 761370c7e7..780e5dbfea 100644 --- a/pcsx2/HwWrite.cpp +++ b/pcsx2/HwWrite.cpp @@ -186,7 +186,7 @@ void _hwWrite32( u32 mem, u32 value ) //pgifInit(); psxReset(); PSXCLK = 33868800; - SPU2reset(PS2Modes::PSX); + SPU2::Reset(true); setPs1CDVDSpeed(cdvd.Speed); psxHu32(0x1f801450) = 0x8; psxHu32(0x1f801078) = 1; diff --git a/pcsx2/Pcsx2Config.cpp b/pcsx2/Pcsx2Config.cpp index db797cc25b..884c36e703 100644 --- a/pcsx2/Pcsx2Config.cpp +++ b/pcsx2/Pcsx2Config.cpp @@ -776,20 +776,49 @@ Pcsx2Config::SPU2Options::SPU2Options() void Pcsx2Config::SPU2Options::LoadSave(SettingsWrapper& wrap) { + { + SettingsWrapSection("SPU2/Debug"); + + SettingsWrapBitBoolEx(DebugEnabled, "Global_Enable"); + SettingsWrapBitBoolEx(MsgToConsole, "Show_Messages"); + SettingsWrapBitBoolEx(MsgKeyOnOff, "Show_Messages_Key_On_Off"); + SettingsWrapBitBoolEx(MsgVoiceOff, "Show_Messages_Voice_Off"); + SettingsWrapBitBoolEx(MsgDMA, "Show_Messages_DMA_Transfer"); + SettingsWrapBitBoolEx(MsgAutoDMA, "Show_Messages_AutoDMA"); + SettingsWrapBitBoolEx(MsgOverruns, "Show_Messages_Overruns"); + SettingsWrapBitBoolEx(MsgCache, "Show_Messages_CacheStats"); + + SettingsWrapBitBoolEx(AccessLog, "Log_Register_Access"); + SettingsWrapBitBoolEx(DMALog, "Log_DMA_Transfers"); + SettingsWrapBitBoolEx(WaveLog, "Log_WAVE_Output"); + + SettingsWrapBitBoolEx(CoresDump, "Dump_Info"); + SettingsWrapBitBoolEx(MemDump, "Dump_Memory"); + SettingsWrapBitBoolEx(RegDump, "Dump_Regs"); + + // If the global switch is off, save runtime checks. + if (wrap.IsLoading() && !DebugEnabled) + { + MsgToConsole = false; + MsgKeyOnOff = false; + MsgVoiceOff = false; + MsgDMA = false; + MsgAutoDMA = false; + MsgOverruns = false; + MsgCache = false; + AccessLog = false; + DMALog = false; + WaveLog = false; + CoresDump = false; + MemDump = false; + RegDump = false; + } + } { SettingsWrapSection("SPU2/Mixing"); Interpolation = static_cast(wrap.EntryBitfield(CURRENT_SETTINGS_SECTION, "Interpolation", static_cast(Interpolation), static_cast(Interpolation))); SettingsWrapEntry(FinalVolume); - - SettingsWrapEntry(VolumeAdjustC); - SettingsWrapEntry(VolumeAdjustFL); - SettingsWrapEntry(VolumeAdjustFR); - SettingsWrapEntry(VolumeAdjustBL); - SettingsWrapEntry(VolumeAdjustBR); - SettingsWrapEntry(VolumeAdjustSL); - SettingsWrapEntry(VolumeAdjustSR); - SettingsWrapEntry(VolumeAdjustLFE); } { @@ -802,6 +831,8 @@ void Pcsx2Config::SPU2Options::LoadSave(SettingsWrapper& wrap) SettingsWrapEntry(SpeakerConfiguration); SettingsWrapEntry(DplDecodingLevel); } + + // clampy clamp } const char* Pcsx2Config::DEV9Options::NetApiNames[] = { @@ -1445,3 +1476,12 @@ bool EmuFolders::EnsureFoldersExist() result = FileSystem::CreateDirectoryPath(InputProfiles.c_str(), false) && result; return result; } + +std::FILE* EmuFolders::OpenLogFile(const std::string_view& name, const char* mode) +{ + if (name.empty()) + return nullptr; + + const std::string path(Path::Combine(Logs, name)); + return FileSystem::OpenCFile(path.c_str(), mode); +} diff --git a/pcsx2/SPU2/Config.cpp b/pcsx2/SPU2/Config.cpp deleted file mode 100644 index 9520c0c5b7..0000000000 --- a/pcsx2/SPU2/Config.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 PCSX2 Dev Team - * - * PCSX2 is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" - -#include "common/StringUtil.h" - -#include - -#include "SPU2/Global.h" -#include "SPU2/Config.h" -#include "HostSettings.h" - -int AutoDMAPlayRate[2] = {0, 0}; - -// Default settings. - -// MIXING -int Interpolation = 5; -/* values: - 0: No interpolation (uses nearest) - 1. Linear interpolation - 2. Cubic interpolation - 3. Hermite interpolation - 4. Catmull-Rom interpolation - 5. Gaussian interpolation -*/ - -float FinalVolume; // global -bool AdvancedVolumeControl; -float VolumeAdjustFLdb; // Decibels settings, because audiophiles love that. -float VolumeAdjustCdb; -float VolumeAdjustFRdb; -float VolumeAdjustBLdb; -float VolumeAdjustBRdb; -float VolumeAdjustSLdb; -float VolumeAdjustSRdb; -float VolumeAdjustLFEdb; -float VolumeAdjustFL; // Linear coefficients calculated from decibels, -float VolumeAdjustC; -float VolumeAdjustFR; -float VolumeAdjustBL; -float VolumeAdjustBR; -float VolumeAdjustSL; -float VolumeAdjustSR; -float VolumeAdjustLFE; - -bool _visual_debug_enabled = false; // Windows-only feature - -// OUTPUT -u32 OutputModule = 0; -int SndOutLatencyMS = 100; -int SynchMode = 0; // Time Stretch, Async or Disabled. - -int numSpeakers = 0; -int dplLevel = 0; -bool temp_debug_state; - -/*****************************************************************************/ - -void ReadSettings() -{ - Interpolation = Host::GetIntSettingValue("SPU2/Mixing", "Interpolation", 5); - FinalVolume = ((float)Host::GetIntSettingValue("SPU2/Mixing", "FinalVolume", 100)) / 100; - if (FinalVolume > 2.0f) - FinalVolume = 2.0f; - - AdvancedVolumeControl = Host::GetBoolSettingValue("SPU2/Mixing", "AdvancedVolumeControl", false); - VolumeAdjustCdb = Host::GetFloatSettingValue("SPU2/Mixing", "VolumeAdjustC", 0); - VolumeAdjustFLdb = Host::GetFloatSettingValue("SPU2/Mixing", "VolumeAdjustFL", 0); - VolumeAdjustFRdb = Host::GetFloatSettingValue("SPU2/Mixing", "VolumeAdjustFR", 0); - VolumeAdjustBLdb = Host::GetFloatSettingValue("SPU2/Mixing", "VolumeAdjustBL", 0); - VolumeAdjustBRdb = Host::GetFloatSettingValue("SPU2/Mixing", "VolumeAdjustBR", 0); - VolumeAdjustSLdb = Host::GetFloatSettingValue("SPU2/Mixing", "VolumeAdjustSL", 0); - VolumeAdjustSRdb = Host::GetFloatSettingValue("SPU2/Mixing", "VolumeAdjustSR", 0); - VolumeAdjustLFEdb = Host::GetFloatSettingValue("SPU2/Mixing", "VolumeAdjustLFE", 0); - VolumeAdjustC = powf(10, VolumeAdjustCdb / 10); - VolumeAdjustFL = powf(10, VolumeAdjustFLdb / 10); - VolumeAdjustFR = powf(10, VolumeAdjustFRdb / 10); - VolumeAdjustBL = powf(10, VolumeAdjustBLdb / 10); - VolumeAdjustBR = powf(10, VolumeAdjustBRdb / 10); - VolumeAdjustSL = powf(10, VolumeAdjustSLdb / 10); - VolumeAdjustSR = powf(10, VolumeAdjustSRdb / 10); - VolumeAdjustLFE = powf(10, VolumeAdjustLFEdb / 10); - - const std::string modname(Host::GetStringSettingValue("SPU2/Output", "OutputModule", "cubeb")); - OutputModule = FindOutputModuleById(modname.c_str()); // Find the driver index of this module... - - SndOutLatencyMS = Host::GetIntSettingValue("SPU2/Output", "Latency", 100); - SynchMode = Host::GetIntSettingValue("SPU2/Output", "SynchMode", 0); - numSpeakers = Host::GetIntSettingValue("SPU2/Output", "SpeakerConfiguration", 0); - dplLevel = Host::GetIntSettingValue("SPU2/Output", "DplDecodingLevel", 0); - - SoundtouchCfg::ReadSettings(); - DebugConfig::ReadSettings(); - - // Sanity Checks - // ------------- - - Clampify(SndOutLatencyMS, LATENCY_MIN, LATENCY_MAX); - - if (mods[OutputModule] == nullptr) - { - Console.Warning("* SPU2: Unknown output module '%s' specified in configuration file.", modname.c_str()); - Console.Warning("* SPU2: Defaulting to Cubeb (%s).", CubebOut->GetIdent()); - OutputModule = FindOutputModuleById(CubebOut->GetIdent()); - } -} diff --git a/pcsx2/SPU2/Config.h b/pcsx2/SPU2/Config.h deleted file mode 100644 index b5a35e9a69..0000000000 --- a/pcsx2/SPU2/Config.h +++ /dev/null @@ -1,125 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#pragma once - -#include "Global.h" -#include - -namespace soundtouch -{ - class SoundTouch; -} - -extern bool DebugEnabled; - -extern bool _MsgToConsole; -extern bool _MsgKeyOnOff; -extern bool _MsgVoiceOff; -extern bool _MsgDMA; -extern bool _MsgAutoDMA; -extern bool _MsgOverruns; -extern bool _MsgCache; - -extern bool _AccessLog; -extern bool _DMALog; -extern bool _WaveLog; - -extern bool _CoresDump; -extern bool _MemDump; -extern bool _RegDump; -extern bool _visual_debug_enabled; - -static __forceinline bool MsgToConsole() { return _MsgToConsole & DebugEnabled; } - -static __forceinline bool MsgKeyOnOff() { return _MsgKeyOnOff & MsgToConsole(); } -static __forceinline bool MsgVoiceOff() { return _MsgVoiceOff & MsgToConsole(); } -static __forceinline bool MsgDMA() { return _MsgDMA & MsgToConsole(); } -static __forceinline bool MsgAutoDMA() { return _MsgAutoDMA & MsgToConsole(); } -static __forceinline bool MsgOverruns() { return _MsgOverruns & MsgToConsole(); } -static __forceinline bool MsgCache() { return _MsgCache & MsgToConsole(); } - -static __forceinline bool AccessLog() { return _AccessLog & DebugEnabled; } -static __forceinline bool DMALog() { return _DMALog & DebugEnabled; } -static __forceinline bool WaveLog() { return _WaveLog & DebugEnabled; } - -static __forceinline bool CoresDump() { return _CoresDump & DebugEnabled; } -static __forceinline bool MemDump() { return _MemDump & DebugEnabled; } -static __forceinline bool RegDump() { return _RegDump & DebugEnabled; } -static __forceinline bool VisualDebug() { return _visual_debug_enabled & DebugEnabled; } - -extern std::string AccessLogFileName; -extern std::string DMA4LogFileName; -extern std::string DMA7LogFileName; -extern std::string CoresDumpFileName; -extern std::string MemDumpFileName; -extern std::string RegDumpFileName; - -extern int Interpolation; - -extern int numSpeakers; -extern float FinalVolume; // Global / pre-scale -extern bool AdvancedVolumeControl; -extern float VolumeAdjustFLdb; -extern float VolumeAdjustCdb; -extern float VolumeAdjustFRdb; -extern float VolumeAdjustBLdb; -extern float VolumeAdjustBRdb; -extern float VolumeAdjustSLdb; -extern float VolumeAdjustSRdb; -extern float VolumeAdjustLFEdb; - -extern int dplLevel; - -extern int AutoDMAPlayRate[2]; - -extern u32 OutputModule; -extern int SndOutLatencyMS; -extern int SynchMode; - -#ifdef PCSX2_DEVBUILD -const int LATENCY_MAX = 3000; -#else -const int LATENCY_MAX = 750; -#endif - -const int LATENCY_MIN = 3; -const int LATENCY_MIN_TIMESTRETCH = 15; - -namespace SoundtouchCfg -{ - extern const int SequenceLen_Min; - extern const int SequenceLen_Max; - - extern const int SeekWindow_Min; - extern const int SeekWindow_Max; - - extern const int Overlap_Min; - extern const int Overlap_Max; - - extern int SequenceLenMS; - extern int SeekWindowMS; - extern int OverlapMS; - - extern void ReadSettings(); - extern void ApplySettings(soundtouch::SoundTouch& sndtouch); -}; // namespace SoundtouchCfg - -namespace DebugConfig -{ - extern void ReadSettings(); -} // namespace DebugConfig - -extern void ReadSettings(); diff --git a/pcsx2/SPU2/ConfigDebug.cpp b/pcsx2/SPU2/ConfigDebug.cpp deleted file mode 100644 index 3b0dad3335..0000000000 --- a/pcsx2/SPU2/ConfigDebug.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" - -#include "pcsx2/Config.h" - -#include "SPU2/Global.h" -#include "SPU2/Config.h" -#include "common/FileSystem.h" -#include "common/Path.h" -#include "common/StringUtil.h" -#include "HostSettings.h" - -bool DebugEnabled = false; -bool _MsgToConsole = false; -bool _MsgKeyOnOff = false; -bool _MsgVoiceOff = false; -bool _MsgDMA = false; -bool _MsgAutoDMA = false; -bool _MsgOverruns = false; -bool _MsgCache = false; - -bool _AccessLog = false; -bool _DMALog = false; -bool _WaveLog = false; - -bool _CoresDump = false; -bool _MemDump = false; -bool _RegDump = false; - -std::string AccessLogFileName; -std::string WaveLogFileName; -std::string DMA4LogFileName; -std::string DMA7LogFileName; - -std::string CoresDumpFileName; -std::string MemDumpFileName; -std::string RegDumpFileName; - -FILE* OpenBinaryLog(const char* logfile) -{ - return FileSystem::OpenCFile(Path::Combine(EmuFolders::Logs, logfile).c_str(), "wb"); -} - -FILE* OpenLog(const char* logfile) -{ - return FileSystem::OpenCFile(Path::Combine(EmuFolders::Logs, logfile).c_str(), "w"); -} - -FILE* OpenDump(const char* logfile) -{ - return FileSystem::OpenCFile(Path::Combine(EmuFolders::Logs, logfile).c_str(), "w"); -} - -namespace DebugConfig -{ - static const char* Section = "SPU/Debug"; - - void ReadSettings() - { - DebugEnabled = Host::GetBoolSettingValue(Section, "Global_Enable", 0); - _MsgToConsole = Host::GetBoolSettingValue(Section, "Show_Messages", 0); - _MsgKeyOnOff = Host::GetBoolSettingValue(Section, "Show_Messages_Key_On_Off", 0); - _MsgVoiceOff = Host::GetBoolSettingValue(Section, "Show_Messages_Voice_Off", 0); - _MsgDMA = Host::GetBoolSettingValue(Section, "Show_Messages_DMA_Transfer", 0); - _MsgAutoDMA = Host::GetBoolSettingValue(Section, "Show_Messages_AutoDMA", 0); - _MsgOverruns = Host::GetBoolSettingValue(Section, "Show_Messages_Overruns", 0); - _MsgCache = Host::GetBoolSettingValue(Section, "Show_Messages_CacheStats", 0); - - _AccessLog = Host::GetBoolSettingValue(Section, "Log_Register_Access", 0); - _DMALog = Host::GetBoolSettingValue(Section, "Log_DMA_Transfers", 0); - _WaveLog = Host::GetBoolSettingValue(Section, "Log_WAVE_Output", 0); - - _CoresDump = Host::GetBoolSettingValue(Section, "Dump_Info", 0); - _MemDump = Host::GetBoolSettingValue(Section, "Dump_Memory", 0); - _RegDump = Host::GetBoolSettingValue(Section, "Dump_Regs", 0); - - AccessLogFileName = Host::GetStringSettingValue(Section, "Access_Log_Filename", "SPU2Log.txt"); - WaveLogFileName = Host::GetStringSettingValue(Section, "WaveLog_Filename", "SPU2log.wav"); - DMA4LogFileName = Host::GetStringSettingValue(Section, "DMA4Log_Filename", "SPU2dma4.dat"); - DMA7LogFileName = Host::GetStringSettingValue(Section, "DMA7Log_Filename", "SPU2dma7.dat"); - - CoresDumpFileName = Host::GetStringSettingValue(Section, "Info_Dump_Filename", "SPU2Cores.txt"); - MemDumpFileName = Host::GetStringSettingValue(Section, "Mem_Dump_Filename", "SPU2mem.dat"); - RegDumpFileName = Host::GetStringSettingValue(Section, "Reg_Dump_Filename", "SPU2regs.dat"); - } -} // namespace DebugConfig diff --git a/pcsx2/SPU2/ConfigSoundTouch.cpp b/pcsx2/SPU2/ConfigSoundTouch.cpp deleted file mode 100644 index 94e8c94af3..0000000000 --- a/pcsx2/SPU2/ConfigSoundTouch.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" - -#include "SPU2/Global.h" -#include "SPU2/Config.h" -#include "SoundTouch.h" -#include "HostSettings.h" - -namespace SoundtouchCfg -{ - // Timestretch Slider Bounds, Min/Max - const int SequenceLen_Min = 20; - const int SequenceLen_Max = 100; - - const int SeekWindow_Min = 10; - const int SeekWindow_Max = 30; - - const int Overlap_Min = 5; - const int Overlap_Max = 15; - - int SequenceLenMS = 30; - int SeekWindowMS = 20; - int OverlapMS = 10; - - static void ClampValues() - { - Clampify(SequenceLenMS, SequenceLen_Min, SequenceLen_Max); - Clampify(SeekWindowMS, SeekWindow_Min, SeekWindow_Max); - Clampify(OverlapMS, Overlap_Min, Overlap_Max); - } - - void ApplySettings(soundtouch::SoundTouch& sndtouch) - { - sndtouch.setSetting(SETTING_SEQUENCE_MS, SequenceLenMS); - sndtouch.setSetting(SETTING_SEEKWINDOW_MS, SeekWindowMS); - sndtouch.setSetting(SETTING_OVERLAP_MS, OverlapMS); - } - - void ReadSettings() - { - SequenceLenMS = Host::GetIntSettingValue("Soundtouch", "SequenceLengthMS", 30); - SeekWindowMS = Host::GetIntSettingValue("Soundtouch", "SeekWindowMS", 20); - OverlapMS = Host::GetIntSettingValue("Soundtouch", "OverlapMS", 10); - - ClampValues(); - } - -} // namespace SoundtouchCfg diff --git a/pcsx2/SPU2/Debug.cpp b/pcsx2/SPU2/Debug.cpp index 9a59f9663c..180f223139 100644 --- a/pcsx2/SPU2/Debug.cpp +++ b/pcsx2/SPU2/Debug.cpp @@ -14,65 +14,68 @@ */ #include "PrecompiledHeader.h" -#include "Global.h" + +#include "SPU2/Global.h" +#include "Config.h" + #include "common/FileSystem.h" #include -int crazy_debug = 0; +#ifdef PCSX2_DEVBUILD -char s[4096]; +static FILE* spu2Log = nullptr; -FILE* spu2Log = nullptr; - -void FileLog(const char* fmt, ...) +void SPU2::OpenFileLog() { -#ifdef SPU2_LOG - va_list list; - - if (!AccessLog()) + if (spu2Log) return; + + spu2Log = EmuFolders::OpenLogFile("SPU2Log.txt", "w"); + setvbuf(spu2Log, nullptr, _IONBF, 0); +} + +void SPU2::CloseFileLog() +{ if (!spu2Log) return; - va_start(list, fmt); - vsprintf(s, fmt, list); - va_end(list); + std::fclose(spu2Log); + spu2Log = nullptr; +} - fputs(s, spu2Log); - fflush(spu2Log); +void SPU2::FileLog(const char* fmt, ...) +{ + if (!spu2Log) + return; -#if 0 - if(crazy_debug) - { - fputs(s,stderr); - fflush(stderr); - } -#endif -#endif + std::va_list ap; + va_start(ap, fmt); + std::vfprintf(spu2Log, fmt, ap); + std::fflush(spu2Log); + va_end(ap); } //Note to developer on the usage of ConLog: // while ConLog doesn't print anything if messages to console are disabled at the GUI, // it's still better to outright not call it on tight loop scenarios, by testing MsgToConsole() (which is inline and very quick). // Else, there's some (small) overhead in calling and returning from ConLog. -void ConLog(const char* fmt, ...) +void SPU2::ConLog(const char* fmt, ...) { - if (!MsgToConsole()) + if (!SPU2::MsgToConsole()) return; - va_list list; - va_start(list, fmt); - vsprintf(s, fmt, list); - va_end(list); - - fputs(s, stderr); - fflush(stderr); + std::va_list ap; + va_start(ap, fmt); + Console.FormatV(fmt, ap); + va_end(ap); if (spu2Log) { - fputs(s, spu2Log); - fflush(spu2Log); + va_start(ap, fmt); + std::vfprintf(spu2Log, fmt, ap); + std::fflush(spu2Log); + va_end(ap); } } @@ -97,24 +100,22 @@ void V_VolumeLR::DebugDump(FILE* dump, const char* title) fprintf(dump, "Volume for %s (%s Channel):\t%x\n", title, "Right", Right); } -void DoFullDump() +void SPU2::DoFullDump() { -#ifdef _MSC_VER -#ifdef SPU2_LOG FILE* dump; - if (MemDump()) + if (SPU2::MemDump()) { - dump = FileSystem::OpenCFile(MemDumpFileName.c_str(), "wb"); + dump = EmuFolders::OpenLogFile("SPU2mem.dat", "wb"); if (dump) { fwrite(_spu2mem, 0x200000, 1, dump); fclose(dump); } } - if (RegDump()) + if (SPU2::RegDump()) { - dump = FileSystem::OpenCFile(RegDumpFileName.c_str(), "wb"); + dump = EmuFolders::OpenLogFile("SPU2regs.dat", "wb"); if (dump) { fwrite(spu2regs, 0x2000, 1, dump); @@ -122,9 +123,9 @@ void DoFullDump() } } - if (!CoresDump()) + if (!SPU2::CoresDump()) return; - dump = FileSystem::OpenCFile(CoresDumpFileName.c_str(), "wt"); + dump = EmuFolders::OpenLogFile("SPU2Cores.txt", "wt"); if (dump) { for (u8 c = 0; c < 2; c++) @@ -217,7 +218,7 @@ void DoFullDump() fclose(dump); } - dump = fopen("logs/effects.txt", "wt"); + dump = EmuFolders::OpenLogFile("SPU2effects.txt", "wt"); if (dump) { for (u8 c = 0; c < 2; c++) @@ -264,6 +265,6 @@ void DoFullDump() } fclose(dump); } -#endif -#endif } + +#endif diff --git a/pcsx2/SPU2/Debug.h b/pcsx2/SPU2/Debug.h index 6284c5a061..77104f4a12 100644 --- a/pcsx2/SPU2/Debug.h +++ b/pcsx2/SPU2/Debug.h @@ -13,19 +13,64 @@ * If not, see . */ -#ifndef DEBUG_H_INCLUDED -#define DEBUG_H_INCLUDED +#pragma once -extern FILE* spu2Log; +#include "Config.h" -extern void FileLog(const char* fmt, ...); -extern void ConLog(const char* fmt, ...); +namespace SPU2 +{ +#ifdef PCSX2_DEVBUILD + __fi static bool MsgToConsole() + { + return EmuConfig.SPU2.DebugEnabled; + } -extern void DoFullDump(); + __fi static bool MsgKeyOnOff() { return EmuConfig.SPU2.MsgKeyOnOff; } + __fi static bool MsgVoiceOff() { return EmuConfig.SPU2.MsgVoiceOff; } + __fi static bool MsgDMA() { return EmuConfig.SPU2.MsgDMA; } + __fi static bool MsgAutoDMA() { return EmuConfig.SPU2.MsgAutoDMA; } + __fi static bool MsgOverruns() { return EmuConfig.SPU2.MsgOverruns; } + __fi static bool MsgCache() { return EmuConfig.SPU2.MsgCache; } -extern FILE* OpenBinaryLog(const char* logfile); -extern FILE* OpenLog(const char* logfile); -extern FILE* OpenDump(const char* logfile); + __fi static bool AccessLog() { return EmuConfig.SPU2.AccessLog; } + __fi static bool DMALog() { return EmuConfig.SPU2.DMALog; } + __fi static bool WaveLog() { return EmuConfig.SPU2.WaveLog; } + + __fi static bool CoresDump() { return EmuConfig.SPU2.CoresDump; } + __fi static bool MemDump() { return EmuConfig.SPU2.MemDump; } + __fi static bool RegDump() { return EmuConfig.SPU2.RegDump; } + __fi static bool VisualDebug() { return EmuConfig.SPU2.VisualDebugEnabled; } + + extern void OpenFileLog(); + extern void CloseFileLog(); + extern void FileLog(const char* fmt, ...); + extern void ConLog(const char* fmt, ...); + + extern void DoFullDump(); + +#else + __fi static constexpr bool MsgToConsole() { return false; } + + __fi static constexpr bool MsgKeyOnOff() { return false; } + __fi static constexpr bool MsgVoiceOff() { return false; } + __fi static constexpr bool MsgDMA() { return false; } + __fi static constexpr bool MsgAutoDMA() { return false; } + __fi static constexpr bool MsgOverruns() { return false; } + __fi static constexpr bool MsgCache() { return false; } + + __fi static constexpr bool AccessLog() { return false; } + __fi static constexpr bool DMALog() { return false; } + __fi static constexpr bool WaveLog() { return false; } + + __fi static constexpr bool CoresDump() { return false; } + __fi static constexpr bool MemDump() { return false; } + __fi static constexpr bool RegDump() { return false; } + __fi static constexpr bool VisualDebug() { return false; } + + __fi static void FileLog(const char* fmt, ...) {} + __fi static void ConLog(const char* fmt, ...) {} +#endif +} // namespace SPU2 namespace WaveDump { @@ -65,5 +110,3 @@ using WaveDump::CoreSrc_Input; using WaveDump::CoreSrc_PostReverb; using WaveDump::CoreSrc_PreReverb; using WaveDump::CoreSrc_WetVoiceMix; - -#endif // DEBUG_H_INCLUDED // diff --git a/pcsx2/SPU2/Dma.cpp b/pcsx2/SPU2/Dma.cpp index 722c6b20a9..3d6f2a8a83 100644 --- a/pcsx2/SPU2/Dma.cpp +++ b/pcsx2/SPU2/Dma.cpp @@ -14,12 +14,12 @@ */ #include "PrecompiledHeader.h" -#include "Global.h" -#include "Dma.h" +#include "SPU2/Global.h" +#include "SPU2/Dma.h" +#include "SPU2/spu2.h" #include "R3000A.h" #include "IopHw.h" - -#include "spu2.h" // temporary until I resolve cyclePtr/TimeUpdate dependencies. +#include "Config.h" extern u8 callirq; @@ -33,18 +33,18 @@ static FILE* REGWRTLogFile[2] = {0, 0}; void DMALogOpen() { - if (!DMALog()) + if (!SPU2::DMALog()) return; - DMA4LogFile = OpenBinaryLog(DMA4LogFileName.c_str()); - DMA7LogFile = OpenBinaryLog(DMA7LogFileName.c_str()); - ADMA4LogFile = OpenBinaryLog("adma4.raw"); - ADMA7LogFile = OpenBinaryLog("adma7.raw"); - ADMAOutLogFile = OpenBinaryLog("admaOut.raw"); + DMA4LogFile = EmuFolders::OpenLogFile("SPU2dma4.dat", "wb"); + DMA7LogFile = EmuFolders::OpenLogFile("SPU2dma7.dat", "wb"); + ADMA4LogFile = EmuFolders::OpenLogFile("adma4.raw", "wb"); + ADMA7LogFile = EmuFolders::OpenLogFile("adma7.raw", "wb"); + ADMAOutLogFile = EmuFolders::OpenLogFile("admaOut.raw", "wb"); } void DMA4LogWrite(void* lpData, u32 ulSize) { - if (!DMALog()) + if (!SPU2::DMALog()) return; if (!DMA4LogFile) return; @@ -53,7 +53,7 @@ void DMA4LogWrite(void* lpData, u32 ulSize) void DMA7LogWrite(void* lpData, u32 ulSize) { - if (!DMALog()) + if (!SPU2::DMALog()) return; if (!DMA7LogFile) return; @@ -62,7 +62,7 @@ void DMA7LogWrite(void* lpData, u32 ulSize) void ADMAOutLogWrite(void* lpData, u32 ulSize) { - if (!DMALog()) + if (!SPU2::DMALog()) return; if (!ADMAOutLogFile) return; @@ -71,7 +71,7 @@ void ADMAOutLogWrite(void* lpData, u32 ulSize) void RegWriteLog(u32 core, u16 value) { - if (!DMALog()) + if (!SPU2::DMALog()) return; if (!REGWRTLogFile[core]) return; @@ -91,7 +91,7 @@ void DMALogClose() void V_Core::LogAutoDMA(FILE* fp) { - if (!DMALog() || !fp || !DMAPtr) + if (!SPU2::DMALog() || !fp || !DMAPtr) return; fwrite(DMAPtr + InputDataProgress, 0x400, 1, fp); } @@ -157,9 +157,11 @@ void V_Core::StartADMAWrite(u16* pMem, u32 sz) TimeUpdate(psxRegs.cycle); - if (MsgAutoDMA()) - ConLog("* SPU2: DMA%c AutoDMA Transfer of %d bytes to %x (%02x %x %04x).OutPos %x\n", + if (SPU2::MsgAutoDMA()) + { + SPU2::ConLog("* SPU2: DMA%c AutoDMA Transfer of %d bytes to %x (%02x %x %04x).OutPos %x\n", GetDmaIndexChar(), size << 1, ActiveTSA, DMABits, AutoDMACtrl, (~Regs.ATTR) & 0xffff, OutPos); + } InputDataProgress = 0; TADR = MADR + (size << 1); @@ -191,7 +193,8 @@ void V_Core::StartADMAWrite(u16* pMem, u32 sz) } else { - ConLog("ADMA%c Error Size of %x too small\n", GetDmaIndexChar(), size); + if (SPU2::MsgToConsole()) + SPU2::ConLog("ADMA%c Error Size of %x too small\n", GetDmaIndexChar(), size); InputDataLeft = 0; DMAICounter = size * 4; LastClock = psxRegs.cycle; @@ -200,7 +203,7 @@ void V_Core::StartADMAWrite(u16* pMem, u32 sz) void V_Core::PlainDMAWrite(u16* pMem, u32 size) { - if (MsgToConsole()) + if (SPU2::MsgToConsole()) { // Don't need this anymore. Target may still be good to know though. /*if((uptr)pMem & 15) @@ -210,7 +213,7 @@ void V_Core::PlainDMAWrite(u16* pMem, u32 size) if (ActiveTSA & 7) { - ConLog("* SPU2 DMA Write > Misaligned target. Core: %d IOP: %p TSA: 0x%x Size: 0x%x\n", Index, (void*)DMAPtr, ActiveTSA, ReadSize); + SPU2::ConLog("* SPU2 DMA Write > Misaligned target. Core: %d IOP: %p TSA: 0x%x Size: 0x%x\n", Index, (void*)DMAPtr, ActiveTSA, ReadSize); } } @@ -224,10 +227,12 @@ void V_Core::PlainDMAWrite(u16* pMem, u32 size) Regs.STATX |= 0x400; TADR = MADR + (size << 1); - if (MsgDMA()) - ConLog("* SPU2: DMA%c Write Transfer of %d bytes to %x (%02x %x %04x). IRQE = %d IRQA = %x \n", + if (SPU2::MsgDMA()) + { + SPU2::ConLog("* SPU2: DMA%c Write Transfer of %d bytes to %x (%02x %x %04x). IRQE = %d IRQA = %x \n", GetDmaIndexChar(), size << 1, ActiveTSA, DMABits, AutoDMACtrl, Regs.ATTR & 0xffff, Cores[Index].IRQEnable, Cores[Index].IRQA); + } FinishDMAwrite(); } @@ -480,10 +485,12 @@ void V_Core::DoDMAread(u16* pMem, u32 size) psxNextCounter = psxCounters[6].CycleT; } - if (MsgDMA()) - ConLog("* SPU2: DMA%c Read Transfer of %d bytes from %x (%02x %x %04x). IRQE = %d IRQA = %x \n", + if (SPU2::MsgDMA()) + { + SPU2::ConLog("* SPU2: DMA%c Read Transfer of %d bytes from %x (%02x %x %04x). IRQE = %d IRQA = %x \n", GetDmaIndexChar(), size << 1, ActiveTSA, DMABits, AutoDMACtrl, Regs.ATTR & 0xffff, Cores[Index].IRQEnable, Cores[Index].IRQA); + } } void V_Core::DoDMAwrite(u16* pMem, u32 size) @@ -505,11 +512,11 @@ void V_Core::DoDMAwrite(u16* pMem, u32 size) DebugCores[Index].dmaFlag = 2; } - if (MsgToConsole()) + if (SPU2::MsgToConsole()) { if (TSA > 0xfffff) { - ConLog("* SPU2: Transfer Start Address out of bounds. TSA is %x\n", TSA); + SPU2::ConLog("* SPU2: Transfer Start Address out of bounds. TSA is %x\n", TSA); } } diff --git a/pcsx2/SPU2/Dma.h b/pcsx2/SPU2/Dma.h index d5ec9ce387..583d48db01 100644 --- a/pcsx2/SPU2/Dma.h +++ b/pcsx2/SPU2/Dma.h @@ -19,6 +19,7 @@ #define TADR (Index == 0 ? HW_DMA4_TADR : HW_DMA7_TADR) extern void DMALogOpen(); +extern void ADMAOutLogWrite(void* lpData, u32 ulSize); extern void DMA4LogWrite(void* lpData, u32 ulSize); extern void DMA7LogWrite(void* lpData, u32 ulSize); extern void DMALogClose(); diff --git a/pcsx2/SPU2/Global.h b/pcsx2/SPU2/Global.h index eb2a183184..5fd590821b 100644 --- a/pcsx2/SPU2/Global.h +++ b/pcsx2/SPU2/Global.h @@ -17,42 +17,15 @@ #include "PrecompiledHeader.h" -extern bool psxmode; - struct StereoOut16; struct StereoOut32; struct StereoOutFloat; struct V_Core; -namespace soundtouch -{ - class SoundTouch; -} - -template -static __forceinline void Clampify(T& src, T min, T max) -{ - src = std::min(std::max(src, min), max); -} - -template -static __forceinline T GetClamped(T src, T min, T max) -{ - return std::min(std::max(src, min), max); -} - - -// Uncomment to enable debug keys on numpad (0 to 5) -//#define DEBUG_KEYS -#ifdef PCSX2_DEVBUILD -#define SPU2_LOG -#endif - #include "defs.h" #include "regs.h" -#include "Config.h" #include "Debug.h" #include "Mixer.h" #include "SndOut.h" diff --git a/pcsx2/SPU2/Mixer.cpp b/pcsx2/SPU2/Mixer.cpp index f2d205cfec..2c9c9fa5bc 100644 --- a/pcsx2/SPU2/Mixer.cpp +++ b/pcsx2/SPU2/Mixer.cpp @@ -14,12 +14,11 @@ */ #include "PrecompiledHeader.h" -#include "Global.h" #include "common/Assertions.h" -void ADMAOutLogWrite(void* lpData, u32 ulSize); - -#include "interpolate_table.h" +#include "SPU2/Global.h" +#include "SPU2/spu2.h" +#include "SPU2/interpolate_table.h" static const s32 tbl_XA_Factor[16][2] = { @@ -29,6 +28,7 @@ static const s32 tbl_XA_Factor[16][2] = {98, -55}, {122, -60}}; +float SPU2::FinalVolume = 1.0f; // Performs a 64-bit multiplication between two values and returns the // high 32 bits as a result (discarding the fractional 32 bits). @@ -51,7 +51,7 @@ static __forceinline s32 MulShr32(s32 srcval, s32 mulval) __forceinline s32 clamp_mix(s32 x, u8 bitshift) { assert(bitshift <= 15); - return GetClamped(x, -(0x8000 << bitshift), 0x7fff << bitshift); + return std::clamp(x, -(0x8000 << bitshift), 0x7fff << bitshift); } #if _MSC_VER @@ -70,8 +70,8 @@ __forceinline // modules or sound drivers could (will :p) overshoot with that. So giving it a small safety. return StereoOut32( - GetClamped(sample.Left, -(0x7f00 << bitshift), 0x7f00 << bitshift), - GetClamped(sample.Right, -(0x7f00 << bitshift), 0x7f00 << bitshift)); + std::clamp(sample.Left, -(0x7f00 << bitshift), 0x7f00 << bitshift), + std::clamp(sample.Right, -(0x7f00 << bitshift), 0x7f00 << bitshift)); } static void __forceinline XA_decode_block(s16* buffer, const s16* block, s32& prev1, s32& prev2) @@ -79,8 +79,8 @@ static void __forceinline XA_decode_block(s16* buffer, const s16* block, s32& pr const s32 header = *block; const s32 shift = (header & 0xF) + 16; const int id = header >> 4 & 0xF; - if (id > 4 && MsgToConsole()) - ConLog("* SPU2: Unknown ADPCM coefficients table id %d\n", id); + if (id > 4 && SPU2::MsgToConsole()) + SPU2::ConLog("* SPU2: Unknown ADPCM coefficients table id %d\n", id); const s32 pred1 = tbl_XA_Factor[id][0]; const s32 pred2 = tbl_XA_Factor[id][1]; @@ -92,13 +92,13 @@ static void __forceinline XA_decode_block(s16* buffer, const s16* block, s32& pr s32 data = ((*blockbytes) << 28) & 0xF0000000; s32 pcm = (data >> shift) + (((pred1 * prev1) + (pred2 * prev2) + 32) >> 6); - Clampify(pcm, -0x8000, 0x7fff); + pcm = std::clamp(pcm, -0x8000, 0x7fff); *(buffer++) = pcm; data = ((*blockbytes) << 24) & 0xF0000000; s32 pcm2 = (data >> shift) + (((pred1 * pcm) + (pred2 * prev1) + 32) >> 6); - Clampify(pcm2, -0x8000, 0x7fff); + pcm2 = std::clamp(pcm2, -0x8000, 0x7fff); *(buffer++) = pcm2; prev2 = pcm; @@ -159,11 +159,15 @@ static __forceinline s32 GetNextDataBuffered(V_Core& thiscore, uint voiceidx) if (vc.LoopCycle < vc.PlayCycle) { vc.LoopStartA = vc.PendingLoopStartA; - ConLog("Core %d Voice %d Loop Written by HW within 4T of Key On, Now Applying\n", thiscore.Index, voiceidx); + if (SPU2::MsgToConsole()) + SPU2::ConLog("Core %d Voice %d Loop Written by HW within 4T of Key On, Now Applying\n", thiscore.Index, voiceidx); vc.LoopMode = 1; } else - ConLog("Loop point from waveform set within 4T's, ignoring HW write\n"); + { + if (SPU2::MsgToConsole()) + SPU2::ConLog("Loop point from waveform set within 4T's, ignoring HW write\n"); + } vc.PendingLoopStart = false; } @@ -182,8 +186,8 @@ static __forceinline s32 GetNextDataBuffered(V_Core& thiscore, uint voiceidx) if (IsDevBuild) { - if (MsgVoiceOff()) - ConLog("* SPU2: Voice Off by EndPoint: %d \n", voiceidx); + if (SPU2::MsgVoiceOff()) + SPU2::ConLog("* SPU2: Voice Off by EndPoint: %d \n", voiceidx); } } } @@ -327,7 +331,7 @@ static void __forceinline UpdatePitch(uint coreidx, uint voiceidx) if ((vc.Modulated == 0) || (voiceidx == 0)) pitch = vc.Pitch; else - pitch = GetClamped((vc.Pitch * (32768 + Cores[coreidx].Voices[voiceidx - 1].OutX)) >> 15, 0, 0x3fff); + pitch = std::clamp((vc.Pitch * (32768 + Cores[coreidx].Voices[voiceidx - 1].OutX)) >> 15, 0, 0x3fff); pitch = std::min(pitch, 0x3FFF); vc.SP += pitch; @@ -348,8 +352,8 @@ static __forceinline void CalculateADSR(V_Core& thiscore, uint voiceidx) { if (IsDevBuild) { - if (MsgVoiceOff()) - ConLog("* SPU2: Voice Off by ADSR: %d \n", voiceidx); + if (SPU2::MsgVoiceOff()) + SPU2::ConLog("* SPU2: Voice Off by ADSR: %d \n", voiceidx); } vc.Stop(); } @@ -588,24 +592,24 @@ static __forceinline StereoOut32 MixVoice(uint coreidx, uint voiceidx) // Optimization : Forceinline'd Templated Dispatch Table. Any halfwit compiler will // turn this into a clever jump dispatch table (no call/rets, no compares, uber-efficient!) - switch (Interpolation) + switch (EmuConfig.SPU2.Interpolation) { - case 0: + case Pcsx2Config::SPU2Options::InterpolationMode::Nearest: Value = GetVoiceValues<0>(thiscore, voiceidx); break; - case 1: + case Pcsx2Config::SPU2Options::InterpolationMode::Linear: Value = GetVoiceValues<1>(thiscore, voiceidx); break; - case 2: + case Pcsx2Config::SPU2Options::InterpolationMode::Cubic: Value = GetVoiceValues<2>(thiscore, voiceidx); break; - case 3: + case Pcsx2Config::SPU2Options::InterpolationMode::Hermite: Value = GetVoiceValues<3>(thiscore, voiceidx); break; - case 4: + case Pcsx2Config::SPU2Options::InterpolationMode::CatmullRom: Value = GetVoiceValues<4>(thiscore, voiceidx); break; - case 5: + case Pcsx2Config::SPU2Options::InterpolationMode::Gaussian: Value = GetVoiceValues<5>(thiscore, voiceidx); break; @@ -813,8 +817,8 @@ __forceinline } // Configurable output volume - Out.Left *= FinalVolume; - Out.Right *= FinalVolume; + Out.Left *= SPU2::FinalVolume; + Out.Right *= SPU2::FinalVolume; // Final Clamp! // Like any good audio system, the PS2 pumps the volume and incurs some distortion in its @@ -842,11 +846,13 @@ __forceinline if (p_cachestat_counter > (48000 * 10)) { p_cachestat_counter = 0; - if (MsgCache()) - ConLog(" * SPU2 > CacheStats > Hits: %d Misses: %d Ignores: %d\n", + if (SPU2::MsgCache()) + { + SPU2::ConLog(" * SPU2 > CacheStats > Hits: %d Misses: %d Ignores: %d\n", g_counter_cache_hits, g_counter_cache_misses, g_counter_cache_ignores); + } g_counter_cache_hits = g_counter_cache_misses = @@ -854,3 +860,8 @@ __forceinline } } } + +void SPU2::SetOutputVolume(s32 volume) +{ + FinalVolume = static_cast(std::clamp(volume, 0, Pcsx2Config::SPU2Options::MAX_VOLUME)) / 100.0f; +} diff --git a/pcsx2/SPU2/Mixer.h b/pcsx2/SPU2/Mixer.h index a01ceeb4b9..4531a8e3f8 100644 --- a/pcsx2/SPU2/Mixer.h +++ b/pcsx2/SPU2/Mixer.h @@ -15,9 +15,10 @@ #pragma once -// Implemented in Config.cpp -extern float VolumeAdjustFL; -extern float VolumeAdjustFR; +namespace SPU2 +{ +extern float FinalVolume; +} struct StereoOut32 { @@ -74,56 +75,6 @@ struct StereoOut32 this->Left = src.Left << 2; this->Right = src.Right << 2; } - - void AdjustFrom(const StereoOut32& src) - { - ResampleFrom(src); - - Left = (s32)(Left * VolumeAdjustFL); - Right = (s32)(Right * VolumeAdjustFR); - } -}; - -struct FrequencyResponseFilter -{ - static FrequencyResponseFilter Empty; - - StereoOut32 History_One_In; - StereoOut32 History_One_Out; - StereoOut32 History_Two_In; - StereoOut32 History_Two_Out; - - s32 lx1; - s32 lx2; - s32 ly1; - s32 ly2; - - float la0, la1, la2, lb1, lb2; - float ha0, ha1, ha2, hb1, hb2; - - FrequencyResponseFilter() - : History_One_In(0, 0) - , History_One_Out(0, 0) - , History_Two_In(0, 0) - , History_Two_Out(0, 0) - , lx1(0) - , lx2(0) - , ly1(0) - , ly2(0) - - , la0(1.00320890889339290000f) - , la1(-1.97516434134506300000f) - , la2(0.97243484967313087000f) - , lb1(-1.97525280404731810000f) - , lb2(0.97555529586426892000f) - - , ha0(1.52690772687271160000f) - , ha1(-1.62653918974914990000f) //-1.72 = "common equilizer curve" --____-- - , ha2(0.57997976029249387000f) - , hb1(-0.80955590379048203000f) - , hb2(0.28990420120653748000f) - { - } }; extern void Mix(); diff --git a/pcsx2/SPU2/ReadInput.cpp b/pcsx2/SPU2/ReadInput.cpp index 9275c5f1c9..14cc7793a4 100644 --- a/pcsx2/SPU2/ReadInput.cpp +++ b/pcsx2/SPU2/ReadInput.cpp @@ -32,8 +32,8 @@ // StereoOut32 V_Core::ReadInput_HiFi() { - if (psxmode) - ConLog("ReadInput_HiFi!!!!!\n"); + if (SPU2::IsRunningPSXMode() && SPU2::MsgToConsole()) + SPU2::ConLog("ReadInput_HiFi!!!!!\n"); u16 ReadIndex = (OutPos * 2) & 0x1FF; @@ -84,11 +84,11 @@ StereoOut32 V_Core::ReadInput_HiFi() { if (IsDevBuild) { - FileLog("[%10d] AutoDMA%c block end.\n", Cycles, GetDmaIndexChar()); + SPU2::FileLog("[%10d] AutoDMA%c block end.\n", Cycles, GetDmaIndexChar()); if (InputDataLeft > 0) { - if (MsgAutoDMA()) - ConLog("WARNING: adma buffer didn't finish with a whole block!!\n"); + if (SPU2::MsgAutoDMA()) + SPU2::ConLog("WARNING: adma buffer didn't finish with a whole block!!\n"); } } @@ -159,11 +159,11 @@ StereoOut32 V_Core::ReadInput() { if (IsDevBuild) { - FileLog("[%10d] AutoDMA%c block end.\n", Cycles, GetDmaIndexChar()); + SPU2::FileLog("[%10d] AutoDMA%c block end.\n", Cycles, GetDmaIndexChar()); if (InputDataLeft > 0) { - if (MsgAutoDMA()) - ConLog("WARNING: adma buffer didn't finish with a whole block!!\n"); + if (SPU2::MsgAutoDMA()) + SPU2::ConLog("WARNING: adma buffer didn't finish with a whole block!!\n"); } } diff --git a/pcsx2/SPU2/RegLog.cpp b/pcsx2/SPU2/RegLog.cpp index 65a1f06de5..daf58b2b74 100644 --- a/pcsx2/SPU2/RegLog.cpp +++ b/pcsx2/SPU2/RegLog.cpp @@ -22,7 +22,7 @@ const char* AddressNames[6] = {"SSAH", "SSAL", "LSAH", "LSAL", "NAXH", "NAXL"}; __forceinline void _RegLog_(const char* action, int level, const char* RName, u32 mem, u32 core, u16 value) { if (level > 1) - FileLog("[%10d] SPU2 %s mem %08x (core %d, register %s) value %04x\n", + SPU2::FileLog("[%10d] SPU2 %s mem %08x (core %d, register %s) value %04x\n", Cycles, action, mem, core, RName, value); } @@ -143,28 +143,28 @@ void SPU2writeLog(const char* action, u32 rmem, u16 value) RegLog(2, "SPDIF_IRQINFO", rmem, -1, value); break; case 0x7c4: - if (Spdif.Unknown1 != value) - ConLog("* SPU2: SPDIF Unknown Register 1 set to %04x\n", value); + if (Spdif.Unknown1 != value && SPU2::MsgToConsole()) + SPU2::ConLog("* SPU2: SPDIF Unknown Register 1 set to %04x\n", value); RegLog(2, "SPDIF_UNKNOWN1", rmem, -1, value); break; case SPDIF_MODE: - if (Spdif.Mode != value) - ConLog("* SPU2: SPDIF Mode set to %04x\n", value); + if (Spdif.Mode != value && SPU2::MsgToConsole()) + SPU2::ConLog("* SPU2: SPDIF Mode set to %04x\n", value); RegLog(2, "SPDIF_MODE", rmem, -1, value); break; case SPDIF_MEDIA: - if (Spdif.Media != value) - ConLog("* SPU2: SPDIF Media set to %04x\n", value); + if (Spdif.Media != value && SPU2::MsgToConsole()) + SPU2::ConLog("* SPU2: SPDIF Media set to %04x\n", value); RegLog(2, "SPDIF_MEDIA", rmem, -1, value); break; case 0x7ca: - if (Spdif.Unknown2 != value) - ConLog("* SPU2: SPDIF Unknown Register 2 set to %04x\n", value); + if (Spdif.Unknown2 != value && SPU2::MsgToConsole()) + SPU2::ConLog("* SPU2: SPDIF Unknown Register 2 set to %04x\n", value); RegLog(2, "SPDIF_UNKNOWN2", rmem, -1, value); break; case SPDIF_PROTECT: - if (Spdif.Protection != value) - ConLog("* SPU2: SPDIF Copy set to %04x\n", value); + if (Spdif.Protection != value && SPU2::MsgToConsole()) + SPU2::ConLog("* SPU2: SPDIF Copy set to %04x\n", value); RegLog(2, "SPDIF_PROTECT", rmem, -1, value); break; } diff --git a/pcsx2/SPU2/Reverb.cpp b/pcsx2/SPU2/Reverb.cpp index 4d8205c31d..9c61e36e1b 100644 --- a/pcsx2/SPU2/Reverb.cpp +++ b/pcsx2/SPU2/Reverb.cpp @@ -107,7 +107,7 @@ s32 __forceinline V_Core::ReverbDownsample(bool right) out += RevbDownBuf[right][((RevbSampleBufPos - NUM_TAPS) + 19) & 63] * filter_coefs[19]; out >>= 15; - Clampify(out, (s32)INT16_MIN, (s32)INT16_MAX); + out = std::clamp(out, INT16_MIN, INT16_MAX); return out; } @@ -136,9 +136,9 @@ StereoOut32 __forceinline V_Core::ReverbUpsample(bool phase) } ls >>= 14; - Clampify(ls, (s32)INT16_MIN, (s32)INT16_MAX); + ls = std::clamp(ls, INT16_MIN, INT16_MAX); rs >>= 14; - Clampify(rs, (s32)INT16_MIN, (s32)INT16_MAX); + rs = std::clamp(rs, INT16_MIN, INT16_MAX); return StereoOut32(ls, rs); } diff --git a/pcsx2/SPU2/SndOut.cpp b/pcsx2/SPU2/SndOut.cpp index 3bcba65b2c..15dfce443d 100644 --- a/pcsx2/SPU2/SndOut.cpp +++ b/pcsx2/SPU2/SndOut.cpp @@ -14,9 +14,14 @@ */ #include "PrecompiledHeader.h" -#include "Global.h" -#include "common/Assertions.h" +#include "SPU2/Global.h" +#include "SPU2/spu2.h" + +#include "common/Assertions.h" +#include "common/Timer.h" + +#include "SoundTouch.h" StereoOut32 StereoOut32::Empty(0, 0); @@ -46,7 +51,7 @@ StereoOut32 StereoOut16::UpSample() const Right << SndOutVolumeShift); } - +namespace { class NullOutModule final : public SndOutModule { public: @@ -70,11 +75,19 @@ public: return nullptr; } }; +} static NullOutModule s_NullOut; -SndOutModule* NullOut = &s_NullOut; +static SndOutModule* NullOut = &s_NullOut; -SndOutModule* mods[] = +#ifdef _WIN32 +extern SndOutModule* XAudio2Out; +#endif +#if defined(SPU2X_CUBEB) +extern SndOutModule* CubebOut; +#endif + +static SndOutModule* mods[] = { NullOut, #ifdef _WIN32 @@ -83,19 +96,19 @@ SndOutModule* mods[] = #if defined(SPU2X_CUBEB) CubebOut, #endif - nullptr // signals the end of our list }; -int FindOutputModuleById(const char* omodid) +static SndOutModule* s_output_module; + +static SndOutModule* FindOutputModule(const char* name) { - int modcnt = 0; - while (mods[modcnt] != nullptr) + for (u32 i = 0; i < std::size(mods); i++) { - if (std::strcmp(mods[modcnt]->GetIdent(), omodid) == 0) - break; - ++modcnt; + if (std::strcmp(mods[i]->GetIdent(), name) == 0) + return mods[i]; } - return modcnt; + + return nullptr; } const char* const* GetOutputModuleBackends(const char* omodid) @@ -135,7 +148,7 @@ bool SndBuffer::CheckUnderrunStatus(int& nSamples, int& quietSampleCount) int data = _GetApproximateDataInBuffer(); if (m_underrun_freeze) { - int toFill = m_size / ((SynchMode == 2) ? 32 : 400); // TimeStretch and Async off? + int toFill = m_size / ((EmuConfig.SPU2.SynchMode == Pcsx2Config::SPU2Options::SynchronizationMode::NoSync) ? 32 : 400); // TimeStretch and Async off? toFill = GetAlignedBufferSize(toFill); // toFill is now aligned to a SndOutPacket @@ -148,8 +161,8 @@ bool SndBuffer::CheckUnderrunStatus(int& nSamples, int& quietSampleCount) } m_underrun_freeze = false; - if (MsgOverruns()) - ConLog(" * SPU2 > Underrun compensation (%d packets buffered)\n", toFill / SndOutPacketSize); + if (SPU2::MsgOverruns()) + SPU2::ConLog(" * SPU2 > Underrun compensation (%d packets buffered)\n", toFill / SndOutPacketSize); lastPct = 0.0; // normalize timestretcher } else if (data < nSamples) @@ -158,7 +171,7 @@ bool SndBuffer::CheckUnderrunStatus(int& nSamples, int& quietSampleCount) nSamples = data; m_underrun_freeze = true; - if (SynchMode == 0) // TimeStrech on + if (EmuConfig.SPU2.SynchMode == Pcsx2Config::SPU2Options::SynchronizationMode::TimeStretch) // TimeStrech on timeStretchUnderrun(); return nSamples != 0; @@ -260,29 +273,15 @@ void SndBuffer::ReadSamples(T* bData, int nSamples) if (b1 > nSamples) b1 = nSamples; - if (AdvancedVolumeControl) - { - // First part - for (int i = 0; i < b1; i++) - bData[i].AdjustFrom(m_buffer[i + m_rpos]); - - // Second part - int b2 = nSamples - b1; - for (int i = 0; i < b2; i++) - bData[i + b1].AdjustFrom(m_buffer[i]); - } - else - { - // First part - for (int i = 0; i < b1; i++) - bData[i].ResampleFrom(m_buffer[i + m_rpos]); - - // Second part - int b2 = nSamples - b1; - for (int i = 0; i < b2; i++) - bData[i + b1].ResampleFrom(m_buffer[i]); - } + // First part + for (int i = 0; i < b1; i++) + bData[i].ResampleFrom(m_buffer[i + m_rpos]); + // Second part + int b2 = nSamples - b1; + for (int i = 0; i < b2; i++) + bData[i + b1].ResampleFrom(m_buffer[i]); + _DropSamples_Internal(nSamples); } @@ -357,8 +356,8 @@ void SndBuffer::_WriteSamples(StereoOut32* bData, int nSamples) ConLog(" * SPU2 > Overrun Compensation (%d packets tossed)\n", comp / SndOutPacketSize ); lastPct = 0.0; // normalize the timestretcher #else - if (MsgOverruns()) - ConLog(" * SPU2 > Overrun! 1 packet tossed)\n"); + if (SPU2::MsgOverruns()) + SPU2::ConLog(" * SPU2 > Overrun! 1 packet tossed)\n"); lastPct = 0.0; // normalize the timestretcher return; #endif @@ -367,9 +366,10 @@ void SndBuffer::_WriteSamples(StereoOut32* bData, int nSamples) _WriteSamples_Safe(bData, nSamples); } -bool SndBuffer::Init() +bool SndBuffer::Init(const char* modname) { - if (!mods[OutputModule]) + s_output_module = FindOutputModule(modname); + if (!s_output_module) return false; // initialize sound buffer @@ -379,7 +379,7 @@ bool SndBuffer::Init() m_rpos = 0; m_wpos = 0; - const float latencyMS = SndOutLatencyMS * 16; + const float latencyMS = EmuConfig.SPU2.Latency * 16; m_size = GetAlignedBufferSize((int)(latencyMS * SampleRate / 1000.0f)); m_buffer = new StereoOut32[m_size]; m_underrun_freeze = false; @@ -391,7 +391,7 @@ bool SndBuffer::Init() soundtouchInit(); // initializes the timestretching // initialize module - if (!mods[OutputModule]->Init()) + if (!s_output_module->Init()) { Cleanup(); return false; @@ -402,7 +402,11 @@ bool SndBuffer::Init() void SndBuffer::Cleanup() { - mods[OutputModule]->Close(); + if (s_output_module) + { + s_output_module->Close(); + s_output_module = nullptr; + } soundtouchCleanup(); @@ -422,9 +426,15 @@ void SndBuffer::ClearContents() SndBuffer::ssFreeze = 256; //Delays sound output for about 1 second. } -void SndBuffer::SetPaused(bool paused) +void SndBuffer::ResetBuffers() { - mods[OutputModule]->SetPaused(paused); + m_rpos = 0; + m_wpos = 0; +} + +void SPU2::SetOutputPaused(bool paused) +{ + s_output_module->SetPaused(paused); } void SndBuffer::Write(const StereoOut32& Sample) @@ -435,9 +445,6 @@ void SndBuffer::Write(const StereoOut32& Sample) if (WavRecordEnabled) RecordWrite(Sample.DownSample()); - if (mods[OutputModule] == NullOut) // null output doesn't need buffering or stretching! :p - return; - sndTempBuffer[sndTempProgress++] = Sample; // If we haven't accumulated a full packet yet, do nothing more: @@ -454,9 +461,557 @@ void SndBuffer::Write(const StereoOut32& Sample) } else { - if (SynchMode == 0) // TimeStrech on + if (EmuConfig.SPU2.SynchMode == Pcsx2Config::SPU2Options::SynchronizationMode::TimeStretch) // TimeStrech on timeStretchWrite(); else _WriteSamples(sndTempBuffer, SndOutPacketSize); } } + +////////////////////////////////////////////////////////////////////////// +// Time Stretching +////////////////////////////////////////////////////////////////////////// + +//Uncomment the next line to use the old time stretcher +//#define SPU2X_USE_OLD_STRETCHER + +static std::unique_ptr pSoundTouch = nullptr; + +// data prediction amount, used to "commit" data that hasn't +// finished timestretch processing. +s32 SndBuffer::m_predictData; + +// records last buffer status (fill %, range -100 to 100, with 0 being 50% full) +float SndBuffer::lastPct; +float SndBuffer::lastEmergencyAdj; + +float SndBuffer::cTempo = 1; +float SndBuffer::eTempo = 1; + +void SndBuffer::PredictDataWrite(int samples) +{ + m_predictData += samples; +} + +// Calculate the buffer status percentage. +// Returns range from -1.0 to 1.0 +// 1.0 = buffer overflow! +// 0.0 = buffer nominal (50% full) +// -1.0 = buffer underflow! +float SndBuffer::GetStatusPct() +{ + // Get the buffer status of the output driver too, so that we can + // obtain a more accurate overall buffer status. + + int drvempty = s_output_module->GetEmptySampleCount(); // / 2; + + //ConLog( "Data %d >>> driver: %d predict: %d\n", m_data, drvempty, m_predictData ); + + int data = _GetApproximateDataInBuffer(); + float result = (float)(data + m_predictData - drvempty) - (m_size / 16); + result /= (m_size / 16); + return result; +} + + +//Alternative simple tempo adjustment. Based only on the soundtouch buffer state. +//Base algorithm: aim at specific average number of samples at the buffer (by GUI), and adjust tempo simply by current/target. +//An extra mechanism is added to keep adjustment at perfect 1:1 ratio (when emulation speed is stable around 100%) +// to prevent constant stretching/shrinking of packets if possible. +// This mechanism is triggered when the adjustment is close to 1:1 for long enough (defaults to 100 iterations within hys_ok_factor - defaults to 3%). +// 1:1 state is aborted when required adjustment goes beyond hys_bad_factor (defaults to 20%). +// +//To compensate for wide variation of the ratio due to relatively small size of the buffer, +// The required tempo is a running average of STRETCH_AVERAGE_LEN (defaults to 50) last calculations. +// This averaging slows down the respons time of the algorithm, but greatly stablize it towards steady stretching. +// +//Keeping the buffer at required latency: +// This algorithm stabilises when the actual latency is *. While this is just fine at 100% speed, +// it's problematic especially for slow speeds, as the number of actual samples at the buffer gets very small on that case, +// which may lead to underruns (or just too much latency when running very fast). +//To compensate for that, the algorithm has a slowly moving compensation factor which will eventually bring the actual latency to the required one. +//compensationDivider defines how slow this compensation changes. By default it's set to 100, +// which will finalize the compensation after about 200 iterations. +// +// Note, this algorithm is intentionally simplified by not taking extreme actions at extreme scenarios (mostly underruns when speed drops sharply), +// and let's the overrun/underrun protections do what they should (doesn't happen much though in practice, even at big FPS variations). +// +// These params were tested to show good respond and stability, on all audio systems (dsound, wav, port audio, xaudio2), +// even at extreme small latency of 50ms which can handle 50%-100% variations without audible glitches. + +int targetIPS = 750; + +//Dynamic tuning changes the values of the base algorithm parameters (derived from targetIPS) to adapt, in real time, to +// different number of invocations/sec (mostly affects number of iterations to average). +// Dynamic tuning can have a slight negative effect on the behavior of the algorithm, so it's preferred to have it off. +//Currently it looks like it's around 750/sec on all systems when playing at 100% speed (50/60fps), +// and proportional to that speed otherwise. +//If changes are made to SPU2 which affects this number (but it's still the same on all systems), then just change targetIPS. +//If we find out that some systems are very different, we can turn on dynamic tuning by uncommenting the next line. +//#define NEWSTRETCHER_USE_DYNAMIC_TUNING + + +//Additional performance note: since MAX_STRETCH_AVERAGE_LEN = 128 (or any power of 2), the '%' below +//could be replaced with a faster '&'. The compiler is highly likely to do it since all the values are unsigned. +#define AVERAGING_BUFFER_SIZE 256U +unsigned int AVERAGING_WINDOW = 50.0 * targetIPS / 750; + + +#define STRETCHER_RESET_THRESHOLD 5 +int gRequestStretcherReset = STRETCHER_RESET_THRESHOLD; +//Adds a value to the running average buffer, and return the new running average. +static float addToAvg(float val) +{ + static float avg_fullness[AVERAGING_BUFFER_SIZE]; + static unsigned int nextAvgPos = 0; + static unsigned int available = 0; // Make sure we're not averaging AVERAGING_WINDOW items if we inserted less. + if (gRequestStretcherReset >= STRETCHER_RESET_THRESHOLD) + available = 0; + + if (available < AVERAGING_BUFFER_SIZE) + available++; + + avg_fullness[nextAvgPos] = val; + nextAvgPos = (nextAvgPos + 1U) % AVERAGING_BUFFER_SIZE; + + unsigned int actualWindow = std::min(available, AVERAGING_WINDOW); + unsigned int first = (nextAvgPos - actualWindow + AVERAGING_BUFFER_SIZE) % AVERAGING_BUFFER_SIZE; + + // Possible optimization: if we know that actualWindow hasn't changed since + // last invocation, we could calculate the running average in O(1) instead of O(N) + // by keeping a running sum between invocations, and then + // do "runningSum = runningSum + val - avg_fullness[(first-1)%...]" instead of the following loop. + // Few gotchas: val overwrites first-1, handling actualWindow changes, etc. + // However, this isn't hot code, so unless proven otherwise, we can live with unoptimized code. + float sum = 0; + for (unsigned int i = first; i < first + actualWindow; i++) + { + sum += avg_fullness[i % AVERAGING_BUFFER_SIZE]; + } + sum = sum / actualWindow; + + return sum ? sum : 1; // 1 because that's the 100% perfect speed value +} + +template +static bool IsInRange(const T& val, const T& min, const T& max) +{ + return (min <= val && val <= max); +} + +//actual stretch algorithm implementation +void SndBuffer::UpdateTempoChangeSoundTouch2() +{ + + long targetSamplesReservoir = 48 * EmuConfig.SPU2.Latency; //48000*SndOutLatencyMS/1000 + //base aim at buffer filled % + float baseTargetFullness = (double)targetSamplesReservoir; ///(double)m_size;//0.05; + + //state vars + static bool inside_hysteresis; //=false; + static int hys_ok_count; //=0; + static float dynamicTargetFullness; //=baseTargetFullness; + if (gRequestStretcherReset >= STRETCHER_RESET_THRESHOLD) + { + if (SPU2::MsgOverruns()) + SPU2::ConLog("______> stretch: Reset.\n"); + inside_hysteresis = false; + hys_ok_count = 0; + dynamicTargetFullness = baseTargetFullness; + } + + int data = _GetApproximateDataInBuffer(); + float bufferFullness = (float)data; ///(float)m_size; + +#ifdef NEWSTRETCHER_USE_DYNAMIC_TUNING + { //test current iterations/sec every 0.5s, and change algo params accordingly if different than previous IPS more than 30% + static long iters = 0; + static wxDateTime last = wxDateTime::UNow(); + wxDateTime unow = wxDateTime::UNow(); + wxTimeSpan delta = unow.Subtract(last); + if (delta.GetMilliseconds() > 500) + { + int pot_targetIPS = 1000.0 / delta.GetMilliseconds().ToDouble() * iters; + if (!IsInRange(pot_targetIPS, int((float)targetIPS / 1.3f), int((float)targetIPS * 1.3f))) + { + if (SPU2::MsgOverruns()) + SPU2::ConLog("Stretcher: setting iters/sec from %d to %d\n", targetIPS, pot_targetIPS); + targetIPS = pot_targetIPS; + AVERAGING_WINDOW = std::clamp((int)(50.0f * (float)targetIPS / 750.0f), 3, (int)AVERAGING_BUFFER_SIZE); + } + last = unow; + iters = 0; + } + iters++; + } +#endif + + //Algorithm params: (threshold params (hysteresis), etc) + const float hys_ok_factor = 1.04f; + const float hys_bad_factor = 1.2f; + int hys_min_ok_count = std::clamp((int)(50.0 * (float)targetIPS / 750.0), 2, 100); //consecutive iterations within hys_ok before going to 1:1 mode + int compensationDivider = std::clamp((int)(100.0 * (float)targetIPS / 750), 15, 150); + + float tempoAdjust = bufferFullness / dynamicTargetFullness; + float avgerage = addToAvg(tempoAdjust); + tempoAdjust = avgerage; + + // Dampen the adjustment to avoid overshoots (this means the average will compensate to the other side). + // This is different than simply bigger averaging window since bigger window also has bigger "momentum", + // so it's slower to slow down when it gets close to the equilibrium state and can therefore resonate. + // The dampening (sqrt was chosen for no very good reason) manages to mostly prevent that. + tempoAdjust = sqrt(tempoAdjust); + + tempoAdjust = std::clamp(tempoAdjust, 0.05f, 10.0f); + + if (tempoAdjust < 1) + baseTargetFullness /= sqrt(tempoAdjust); // slightly increase latency when running slow. + + dynamicTargetFullness += (baseTargetFullness / tempoAdjust - dynamicTargetFullness) / (double)compensationDivider; + if (IsInRange(tempoAdjust, 0.9f, 1.1f) && IsInRange(dynamicTargetFullness, baseTargetFullness * 0.9f, baseTargetFullness * 1.1f)) + dynamicTargetFullness = baseTargetFullness; + + if (!inside_hysteresis) + { + if (IsInRange(tempoAdjust, 1.0f / hys_ok_factor, hys_ok_factor)) + hys_ok_count++; + else + hys_ok_count = 0; + + if (hys_ok_count >= hys_min_ok_count) + { + inside_hysteresis = true; + if (SPU2::MsgOverruns()) + SPU2::ConLog("======> stretch: None (1:1)\n"); + } + } + else if (!IsInRange(tempoAdjust, 1.0f / hys_bad_factor, hys_bad_factor)) + { + if (SPU2::MsgOverruns()) + SPU2::ConLog("~~~~~~> stretch: Dynamic\n"); + inside_hysteresis = false; + hys_ok_count = 0; + } + + if (inside_hysteresis) + tempoAdjust = 1.0; + + if (SPU2::MsgOverruns()) + { + static int iters = 0; + static u64 last = 0; + + const u64 now = Common::Timer::GetCurrentValue(); + + if (Common::Timer::ConvertValueToSeconds(now - last) > 1.0f) + { //report buffers state and tempo adjust every second + SPU2::ConLog("buffers: %4d ms (%3.0f%%), tempo: %f, comp: %2.3f, iters: %d, (N-IPS:%d -> avg:%d, minokc:%d, div:%d) reset:%d\n", + (int)(data / 48), (double)(100.0 * bufferFullness / baseTargetFullness), (double)tempoAdjust, (double)(dynamicTargetFullness / baseTargetFullness), iters, (int)targetIPS, AVERAGING_WINDOW, hys_min_ok_count, compensationDivider, gRequestStretcherReset); + last = now; + iters = 0; + } + iters++; + } + + pSoundTouch->setTempo(tempoAdjust); + if (gRequestStretcherReset >= STRETCHER_RESET_THRESHOLD) + gRequestStretcherReset = 0; + + return; +} + + +void SndBuffer::UpdateTempoChangeSoundTouch() +{ + float statusPct = GetStatusPct(); + float pctChange = statusPct - lastPct; + + float tempoChange; + float emergencyAdj = 0; + float newcee = cTempo; // workspace var. for cTempo + + // IMPORTANT! + // If you plan to tweak these values, make sure you're using a release build + // OUTSIDE THE DEBUGGER to test it! The Visual Studio debugger can really cause + // erratic behavior in the audio buffers, and makes the timestretcher seem a + // lot more inconsistent than it really is. + + // We have two factors. + // * Distance from nominal buffer status (50% full) + // * The change from previous update to this update. + + // Prediction based on the buffer change: + // (linear seems to work better here) + + tempoChange = pctChange * 0.75f; + + if (statusPct * tempoChange < 0.0f) + { + // only apply tempo change if it is in synch with the buffer status. + // In other words, if the buffer is high (over 0%), and is decreasing, + // ignore it. It'll just muck things up. + + tempoChange = 0; + } + + // Sudden spikes in framerate can cause the nominal buffer status + // to go critical, in which case we have to enact an emergency + // stretch. The following cubic formulas do that. Values near + // the extremeites give much larger results than those near 0. + // And the value is added only this time, and does not accumulate. + // (otherwise a large value like this would cause problems down the road) + + // Constants: + // Weight - weights the statusPct's "emergency" consideration. + // higher values here will make the buffer perform more drastic + // compensations at the outer edges of the buffer (at -75 or +75% + // or beyond, for example). + + // Range - scales the adjustment to the given range (more or less). + // The actual range is dependent on the weight used, so if you increase + // Weight you'll usually want to decrease Range somewhat to compensate. + + // Prediction based on the buffer fill status: + + const float statusWeight = 2.99f; + const float statusRange = 0.068f; + + // "non-emergency" deadzone: In this area stretching will be strongly discouraged. + // Note: due tot he nature of timestretch latency, it's always a wee bit harder to + // cope with low fps (underruns) than it is high fps (overruns). So to help out a + // little, the low-end portions of this check are less forgiving than the high-sides. + + if (cTempo < 0.965f || cTempo > 1.060f || + pctChange < -0.38f || pctChange > 0.54f || + statusPct < -0.42f || statusPct > 0.70f || + eTempo < 0.89f || eTempo > 1.19f) + { + //printf("Emergency stretch: cTempo = %f eTempo = %f pctChange = %f statusPct = %f\n",cTempo,eTempo,pctChange,statusPct); + emergencyAdj = (pow(statusPct * statusWeight, 3.0f) * statusRange); + } + + // Smooth things out by factoring our previous adjustment into this one. + // It helps make the system 'feel' a little smarter by giving it at least + // one packet worth of history to help work off of: + + emergencyAdj = (emergencyAdj * 0.75f) + (lastEmergencyAdj * 0.25f); + + lastEmergencyAdj = emergencyAdj; + lastPct = statusPct; + + // Accumulate a fraction of the tempo change into the tempo itself. + // This helps the system run "smarter" to games that run consistently + // fast or slow by altering the base tempo to something closer to the + // game's active speed. In tests most games normalize within 2 seconds + // at 100ms latency, which is pretty good (larger buffers normalize even + // quicker). + + newcee += newcee * (tempoChange + emergencyAdj) * 0.03f; + + // Apply tempoChange as a scale of cTempo. That way the effect is proportional + // to the current tempo. (otherwise tempos rate of change at the extremes would + // be too drastic) + + float newTempo = newcee + (emergencyAdj * cTempo); + + // ... and as a final optimization, only stretch if the new tempo is outside + // a nominal threshold. Keep this threshold check small, because it could + // cause some serious side effects otherwise. (enlarging the cTempo check above + // is usually better/safer) + if (newTempo < 0.970f || newTempo > 1.045f) + { + cTempo = (float)newcee; + + if (newTempo < 0.10f) + newTempo = 0.10f; + else if (newTempo > 10.0f) + newTempo = 10.0f; + + if (cTempo < 0.15f) + cTempo = 0.15f; + else if (cTempo > 7.5f) + cTempo = 7.5f; + + pSoundTouch->setTempo(eTempo = (float)newTempo); + + /*ConLog("* SPU2: [Nominal %d%%] [Emergency: %d%%] (baseTempo: %d%% ) (newTempo: %d%%) (buffer: %d%%)\n", + //(relation < 0.0) ? "Normalize" : "", + (int)(tempoChange * 100.0 * 0.03), + (int)(emergencyAdj * 100.0), + (int)(cTempo * 100.0), + (int)(newTempo * 100.0), + (int)(statusPct * 100.0) + );*/ + } + else + { + // Nominal operation -- turn off stretching. + // note: eTempo 'slides' toward 1.0 for smoother audio and better + // protection against spikes. + if (cTempo != 1.0f) + { + cTempo = 1.0f; + eTempo = (1.0f + eTempo) * 0.5f; + pSoundTouch->setTempo(eTempo); + } + else + { + if (eTempo != cTempo) + pSoundTouch->setTempo(eTempo = cTempo); + } + } +} + +extern uint TickInterval; +void SndBuffer::UpdateTempoChangeAsyncMixing() +{ + float statusPct = GetStatusPct(); + + lastPct = statusPct; + if (statusPct < -0.1f) + { + TickInterval -= 4; + if (statusPct < -0.3f) + TickInterval = 64; + if (TickInterval < 64) + TickInterval = 64; + //printf("-- %d, %f\n",TickInterval,statusPct); + } + else if (statusPct > 0.2f) + { + TickInterval += 1; + if (TickInterval >= 7000) + TickInterval = 7000; + //printf("++ %d, %f\n",TickInterval,statusPct); + } + else + TickInterval = 768; +} + +void SndBuffer::timeStretchUnderrun() +{ + gRequestStretcherReset++; + // timeStretcher failed it's job. We need to slow down the audio some. + + cTempo -= (cTempo * 0.12f); + eTempo -= (eTempo * 0.30f); + if (eTempo < 0.1f) + eTempo = 0.1f; + // pSoundTouch->setTempo( eTempo ); + //pSoundTouch->setTempoChange(-30); // temporary (until stretcher is called) slow down +} + +s32 SndBuffer::timeStretchOverrun() +{ + // If we overran it means the timestretcher failed. We need to speed + // up audio playback. + cTempo += cTempo * 0.12f; + eTempo += eTempo * 0.40f; + if (eTempo > 7.5f) + eTempo = 7.5f; + //pSoundTouch->setTempo( eTempo ); + //pSoundTouch->setTempoChange(30);// temporary (until stretcher is called) speed up + + // Throw out just a little bit (two packets worth) to help + // give the TS some room to work: + gRequestStretcherReset++; + return SndOutPacketSize * 2; +} + +static void CvtPacketToFloat(StereoOut32* srcdest) +{ + StereoOutFloat* dest = (StereoOutFloat*)srcdest; + const StereoOut32* src = (StereoOut32*)srcdest; + for (uint i = 0; i < SndOutPacketSize; ++i, ++dest, ++src) + *dest = (StereoOutFloat)*src; +} + +// Parameter note: Size should always be a multiple of 128, thanks! +static void CvtPacketToInt(StereoOut32* srcdest, uint size) +{ + //pxAssume( (size & 127) == 0 ); + + const StereoOutFloat* src = (StereoOutFloat*)srcdest; + StereoOut32* dest = srcdest; + + for (uint i = 0; i < size; ++i, ++dest, ++src) + *dest = (StereoOut32)*src; +} + +void SndBuffer::timeStretchWrite() +{ + // data prediction helps keep the tempo adjustments more accurate. + // The timestretcher returns packets in belated "clump" form. + // Meaning that most of the time we'll get nothing back, and then + // suddenly we'll get several chunks back at once. Thus we use + // data prediction to make the timestretcher more responsive. + + PredictDataWrite((int)(SndOutPacketSize / eTempo)); + CvtPacketToFloat(sndTempBuffer); + + pSoundTouch->putSamples((float*)sndTempBuffer, SndOutPacketSize); + + int tempProgress; + while (tempProgress = pSoundTouch->receiveSamples((float*)sndTempBuffer, SndOutPacketSize), + tempProgress != 0) + { + // Hint: It's assumed that pSoundTouch will return chunks of 128 bytes (it always does as + // long as the SSE optimizations are enabled), which means we can do our own SSE opts here. + + CvtPacketToInt(sndTempBuffer, tempProgress); + _WriteSamples(sndTempBuffer, tempProgress); + } + +#ifdef SPU2X_USE_OLD_STRETCHER + UpdateTempoChangeSoundTouch(); +#else + UpdateTempoChangeSoundTouch2(); +#endif +} + +void SndBuffer::soundtouchInit() +{ + pSoundTouch = std::make_unique(); + pSoundTouch->setSampleRate(SampleRate); + pSoundTouch->setChannels(2); + + pSoundTouch->setSetting(SETTING_USE_QUICKSEEK, 0); + pSoundTouch->setSetting(SETTING_USE_AA_FILTER, 0); + + pSoundTouch->setSetting(SETTING_SEQUENCE_MS, EmuConfig.SPU2.SequenceLenMS); + pSoundTouch->setSetting(SETTING_SEEKWINDOW_MS, EmuConfig.SPU2.SeekWindowMS); + pSoundTouch->setSetting(SETTING_OVERLAP_MS, EmuConfig.SPU2.OverlapMS); + + pSoundTouch->setTempo(1); + + // some timestretch management vars: + + cTempo = 1.0; + eTempo = 1.0; + lastPct = 0; + lastEmergencyAdj = 0; + + m_predictData = 0; +} + +// reset timestretch management vars, and delay updates a bit: +void SndBuffer::soundtouchClearContents() +{ + if (pSoundTouch == nullptr) + return; + + pSoundTouch->clear(); + pSoundTouch->setTempo(1); + + cTempo = 1.0; + eTempo = 1.0; + lastPct = 0; + lastEmergencyAdj = 0; + + m_predictData = 0; +} + +void SndBuffer::soundtouchCleanup() +{ + pSoundTouch.reset(); +} diff --git a/pcsx2/SPU2/SndOut.h b/pcsx2/SPU2/SndOut.h index 532666cec0..d7791eb191 100644 --- a/pcsx2/SPU2/SndOut.h +++ b/pcsx2/SPU2/SndOut.h @@ -32,22 +32,10 @@ static const int SndOutVolumeShift32 = 16 - SndOutVolumeShift; // shift up, not // is too problematic. :) extern int SampleRate; -extern int FindOutputModuleById(const char* omodid); - // Returns a null-terminated list of backends for the specified module. // nullptr is returned if the specified module does not have multiple backends. extern const char* const* GetOutputModuleBackends(const char* omodid); -// Implemented in Config.cpp -extern float VolumeAdjustFL; -extern float VolumeAdjustC; -extern float VolumeAdjustFR; -extern float VolumeAdjustBL; -extern float VolumeAdjustBR; -extern float VolumeAdjustSL; -extern float VolumeAdjustSR; -extern float VolumeAdjustLFE; - struct Stereo51Out16DplII; struct Stereo51Out32DplII; @@ -90,14 +78,6 @@ struct StereoOut16 // Use StereoOut32's built in conversion *this = src.DownSample(); } - - void AdjustFrom(const StereoOut32& src) - { - ResampleFrom(src); - - Left = (s16)(Left * VolumeAdjustFL); - Right = (s16)(Right * VolumeAdjustFR); - } }; struct StereoOutFloat @@ -142,15 +122,6 @@ struct Stereo21Out16 Right = src.Right >> SndOutVolumeShift; LFE = (src.Left + src.Right) >> (SndOutVolumeShift + 1); } - - void AdjustFrom(const StereoOut32& src) - { - ResampleFrom(src); - - Left = (s16)(Left * VolumeAdjustFL); - Right = (s16)(Right * VolumeAdjustFR); - LFE = (s16)(LFE * VolumeAdjustLFE); - } }; struct Stereo40Out16 @@ -167,16 +138,6 @@ struct Stereo40Out16 LeftBack = src.Left >> SndOutVolumeShift; RightBack = src.Right >> SndOutVolumeShift; } - - void AdjustFrom(const StereoOut32& src) - { - ResampleFrom(src); - - Left = (s16)(Left * VolumeAdjustFL); - Right = (s16)(Right * VolumeAdjustFR); - LeftBack = (s16)(LeftBack * VolumeAdjustBL); - RightBack = (s16)(RightBack * VolumeAdjustBR); - } }; struct Stereo40Out32 @@ -193,16 +154,6 @@ struct Stereo40Out32 LeftBack = src.Left << SndOutVolumeShift32; RightBack = src.Right << SndOutVolumeShift32; } - - void AdjustFrom(const StereoOut32& src) - { - ResampleFrom(src); - - Left = (s32)(Left * VolumeAdjustFL); - Right = (s32)(Right * VolumeAdjustFR); - LeftBack = (s32)(LeftBack * VolumeAdjustBL); - RightBack = (s32)(RightBack * VolumeAdjustBR); - } }; struct Stereo41Out16 @@ -221,17 +172,6 @@ struct Stereo41Out16 LeftBack = src.Left >> SndOutVolumeShift; RightBack = src.Right >> SndOutVolumeShift; } - - void AdjustFrom(const StereoOut32& src) - { - ResampleFrom(src); - - Left = (s32)(Left * VolumeAdjustFL); - Right = (s32)(Right * VolumeAdjustFR); - LeftBack = (s32)(LeftBack * VolumeAdjustBL); - RightBack = (s32)(RightBack * VolumeAdjustBR); - LFE = (s32)(LFE * VolumeAdjustLFE); - } }; struct Stereo51Out16 @@ -257,18 +197,6 @@ struct Stereo51Out16 LeftBack = src.Left >> SndOutVolumeShift; RightBack = src.Right >> SndOutVolumeShift; } - - void AdjustFrom(const StereoOut32& src) - { - ResampleFrom(src); - - Left = (s16)(Left * VolumeAdjustFL); - Right = (s16)(Right * VolumeAdjustFR); - LeftBack = (s16)(LeftBack * VolumeAdjustBL); - RightBack = (s16)(RightBack * VolumeAdjustBR); - Center = (s16)(Center * VolumeAdjustC); - LFE = (s16)(LFE * VolumeAdjustLFE); - } }; struct Stereo51Out16DplII @@ -284,18 +212,6 @@ struct Stereo51Out16DplII { ProcessDplIISample16(src, this); } - - void AdjustFrom(const StereoOut32& src) - { - ResampleFrom(src); - - Left = (s32)(Left * VolumeAdjustFL); - Right = (s32)(Right * VolumeAdjustFR); - LeftBack = (s32)(LeftBack * VolumeAdjustBL); - RightBack = (s32)(RightBack * VolumeAdjustBR); - Center = (s32)(Center * VolumeAdjustC); - LFE = (s32)(LFE * VolumeAdjustLFE); - } }; struct Stereo51Out32DplII @@ -311,18 +227,6 @@ struct Stereo51Out32DplII { ProcessDplIISample32(src, this); } - - void AdjustFrom(const StereoOut32& src) - { - ResampleFrom(src); - - Left = (s32)(Left * VolumeAdjustFL); - Right = (s32)(Right * VolumeAdjustFR); - LeftBack = (s32)(LeftBack * VolumeAdjustBL); - RightBack = (s32)(RightBack * VolumeAdjustBR); - Center = (s32)(Center * VolumeAdjustC); - LFE = (s32)(LFE * VolumeAdjustLFE); - } }; struct Stereo51Out16Dpl @@ -338,18 +242,6 @@ struct Stereo51Out16Dpl { ProcessDplSample16(src, this); } - - void AdjustFrom(const StereoOut32& src) - { - ResampleFrom(src); - - Left = (s32)(Left * VolumeAdjustFL); - Right = (s32)(Right * VolumeAdjustFR); - LeftBack = (s32)(LeftBack * VolumeAdjustBL); - RightBack = (s32)(RightBack * VolumeAdjustBR); - Center = (s32)(Center * VolumeAdjustC); - LFE = (s32)(LFE * VolumeAdjustLFE); - } }; struct Stereo51Out32Dpl @@ -365,18 +257,6 @@ struct Stereo51Out32Dpl { ProcessDplSample32(src, this); } - - void AdjustFrom(const StereoOut32& src) - { - ResampleFrom(src); - - Left = (s32)(Left * VolumeAdjustFL); - Right = (s32)(Right * VolumeAdjustFR); - LeftBack = (s32)(LeftBack * VolumeAdjustBL); - RightBack = (s32)(RightBack * VolumeAdjustBR); - Center = (s32)(Center * VolumeAdjustC); - LFE = (s32)(LFE * VolumeAdjustLFE); - } }; struct Stereo71Out16 @@ -402,20 +282,6 @@ struct Stereo71Out16 LeftSide = src.Left >> (SndOutVolumeShift + 1); RightSide = src.Right >> (SndOutVolumeShift + 1); } - - void AdjustFrom(const StereoOut32& src) - { - ResampleFrom(src); - - Left = (s16)(Left * VolumeAdjustFL); - Right = (s16)(Right * VolumeAdjustFR); - LeftBack = (s16)(LeftBack * VolumeAdjustBL); - RightBack = (s16)(RightBack * VolumeAdjustBR); - LeftSide = (s16)(LeftBack * VolumeAdjustSL); - RightSide = (s16)(RightBack * VolumeAdjustSR); - Center = (s16)(Center * VolumeAdjustC); - LFE = (s16)(LFE * VolumeAdjustLFE); - } }; struct Stereo71Out32 @@ -441,20 +307,6 @@ struct Stereo71Out32 LeftSide = src.Left << (SndOutVolumeShift32 - 1); RightSide = src.Right << (SndOutVolumeShift32 - 1); } - - void AdjustFrom(const StereoOut32& src) - { - ResampleFrom(src); - - Left = (s32)(Left * VolumeAdjustFL); - Right = (s32)(Right * VolumeAdjustFR); - LeftBack = (s32)(LeftBack * VolumeAdjustBL); - RightBack = (s32)(RightBack * VolumeAdjustBR); - LeftSide = (s32)(LeftBack * VolumeAdjustSL); - RightSide = (s32)(RightBack * VolumeAdjustSR); - Center = (s32)(Center * VolumeAdjustC); - LFE = (s32)(LFE * VolumeAdjustLFE); - } }; struct Stereo20Out32 @@ -467,14 +319,6 @@ struct Stereo20Out32 Left = src.Left << SndOutVolumeShift32; Right = src.Right << SndOutVolumeShift32; } - - void AdjustFrom(const StereoOut32& src) - { - ResampleFrom(src); - - Left = (s32)(Left * VolumeAdjustFL); - Right = (s32)(Right * VolumeAdjustFR); - } }; struct Stereo21Out32 @@ -489,15 +333,6 @@ struct Stereo21Out32 Right = src.Right << SndOutVolumeShift32; LFE = (src.Left + src.Right) << (SndOutVolumeShift32 - 1); } - - void AdjustFrom(const StereoOut32& src) - { - ResampleFrom(src); - - Left = (s32)(Left * VolumeAdjustFL); - Right = (s32)(Right * VolumeAdjustFR); - LFE = (s32)(LFE * VolumeAdjustLFE); - } }; struct Stereo41Out32 @@ -517,17 +352,6 @@ struct Stereo41Out32 LeftBack = src.Left << SndOutVolumeShift32; RightBack = src.Right << SndOutVolumeShift32; } - - void AdjustFrom(const StereoOut32& src) - { - ResampleFrom(src); - - Left = (s32)(Left * VolumeAdjustFL); - Right = (s32)(Right * VolumeAdjustFR); - LeftBack = (s32)(LeftBack * VolumeAdjustBL); - RightBack = (s32)(RightBack * VolumeAdjustBR); - LFE = (s32)(LFE * VolumeAdjustLFE); - } }; struct Stereo51Out32 @@ -548,18 +372,6 @@ struct Stereo51Out32 LeftBack = src.Left << SndOutVolumeShift32; RightBack = src.Right << SndOutVolumeShift32; } - - void AdjustFrom(const StereoOut32& src) - { - ResampleFrom(src); - - Left = (s32)(Left * VolumeAdjustFL); - Right = (s32)(Right * VolumeAdjustFR); - LeftBack = (s32)(LeftBack * VolumeAdjustBL); - RightBack = (s32)(RightBack * VolumeAdjustBR); - Center = (s32)(Center * VolumeAdjustC); - LFE = (s32)(LFE * VolumeAdjustLFE); - } }; // Developer Note: This is a static class only (all static members). @@ -617,11 +429,11 @@ private: public: static void UpdateTempoChangeAsyncMixing(); - static bool Init(); + static bool Init(const char* modname); static void Cleanup(); static void Write(const StereoOut32& Sample); static void ClearContents(); - static void SetPaused(bool paused); + static void ResetBuffers(); // Note: When using with 32 bit output buffers, the user of this function is responsible // for shifting the values to where they need to be manually. The fixed point depth of @@ -659,15 +471,6 @@ public: virtual int GetEmptySampleCount() = 0; }; -extern SndOutModule* NullOut; -#ifdef _WIN32 -extern SndOutModule* XAudio2Out; -#endif -#if defined(SPU2X_CUBEB) -extern SndOutModule* CubebOut; -#endif -extern SndOutModule* mods[]; - // ===================================================================================================== extern bool WavRecordEnabled; diff --git a/pcsx2/SPU2/SndOut_Cubeb.cpp b/pcsx2/SPU2/SndOut_Cubeb.cpp index ff57df690c..69b0802f5c 100644 --- a/pcsx2/SPU2/SndOut_Cubeb.cpp +++ b/pcsx2/SPU2/SndOut_Cubeb.cpp @@ -15,8 +15,9 @@ #include "PrecompiledHeader.h" -#include "Global.h" -#include "SndOut.h" +#include "SPU2/Global.h" +#include "SPU2/SndOut.h" +#include "Host.h" #include "common/Console.h" #include "common/StringUtil.h" @@ -150,7 +151,7 @@ public: m_COMInitializedByUs = SUCCEEDED(hr); if (FAILED(hr) && hr != RPC_E_CHANGED_MODE) { - Console.Error("(Cubeb) Failed to initialize COM"); + Host::ReportErrorAsync("Cubeb Error", "Failed to initialize COM"); return false; } #endif @@ -162,11 +163,11 @@ public: int rv = cubeb_init(&m_context, "PCSX2", m_Backend.empty() ? nullptr : m_Backend.c_str()); if (rv != CUBEB_OK) { - Console.Error("(Cubeb) Could not initialize cubeb context: %d", rv); + Host::ReportFormattedErrorAsync("Cubeb Error", "Could not initialize cubeb context: %d", rv); return false; } - switch (numSpeakers) // speakers = (numSpeakers + 1) *2; ? + switch (EmuConfig.SPU2.SpeakerConfiguration) // speakers = (numSpeakers + 1) *2; ? { case 0: channels = 2; @@ -213,7 +214,7 @@ public: case 6: case 7: - switch (dplLevel) + switch (EmuConfig.SPU2.DplDecodingLevel) { case 0: Console.WriteLn("(Cubeb) 5.1 speaker expansion enabled."); diff --git a/pcsx2/SPU2/SndOut_XAudio2.cpp b/pcsx2/SPU2/SndOut_XAudio2.cpp index 203a4b3570..eb9bf56961 100644 --- a/pcsx2/SPU2/SndOut_XAudio2.cpp +++ b/pcsx2/SPU2/SndOut_XAudio2.cpp @@ -240,7 +240,7 @@ public: int speakers; // speakers = (numSpeakers + 1) *2; ? - switch (numSpeakers) + switch (EmuConfig.SPU2.SpeakerConfiguration) { case 0: // Stereo speakers = 2; @@ -270,41 +270,41 @@ public: switch (speakers) { case 2: - ConLog("* SPU2 > Using normal 2 speaker stereo output.\n"); + Console.WriteLn("* SPU2 > Using normal 2 speaker stereo output."); m_voiceContext = std::make_unique>(); break; case 3: - ConLog("* SPU2 > 2.1 speaker expansion enabled.\n"); + Console.WriteLn("* SPU2 > 2.1 speaker expansion enabled."); m_voiceContext = std::make_unique>(); break; case 4: - ConLog("* SPU2 > 4 speaker expansion enabled [quadraphenia]\n"); + Console.WriteLn("* SPU2 > 4 speaker expansion enabled [quadraphenia]"); m_voiceContext = std::make_unique>(); break; case 5: - ConLog("* SPU2 > 4.1 speaker expansion enabled.\n"); + Console.WriteLn("* SPU2 > 4.1 speaker expansion enabled."); m_voiceContext = std::make_unique>(); break; case 6: case 7: - switch (dplLevel) + switch (EmuConfig.SPU2.DplDecodingLevel) { case 0: // "normal" stereo upmix - ConLog("* SPU2 > 5.1 speaker expansion enabled.\n"); + Console.WriteLn("* SPU2 > 5.1 speaker expansion enabled."); m_voiceContext = std::make_unique>(); break; case 1: // basic Dpl decoder without rear stereo balancing - ConLog("* SPU2 > 5.1 speaker expansion with basic ProLogic dematrixing enabled.\n"); + Console.WriteLn("* SPU2 > 5.1 speaker expansion with basic ProLogic dematrixing enabled."); m_voiceContext = std::make_unique>(); break; case 2: // gigas PLII - ConLog("* SPU2 > 5.1 speaker expansion with experimental ProLogicII dematrixing enabled.\n"); + Console.WriteLn("* SPU2 > 5.1 speaker expansion with experimental ProLogicII dematrixing enabled."); m_voiceContext = std::make_unique>(); break; } break; default: // anything 8 or more gets the 7.1 treatment! - ConLog("* SPU2 > 7.1 speaker expansion enabled.\n"); + Console.WriteLn("* SPU2 > 7.1 speaker expansion enabled."); m_voiceContext = std::make_unique>(); break; } diff --git a/pcsx2/SPU2/Timestretcher.cpp b/pcsx2/SPU2/Timestretcher.cpp deleted file mode 100644 index 08c4c945d6..0000000000 --- a/pcsx2/SPU2/Timestretcher.cpp +++ /dev/null @@ -1,562 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 PCSX2 Dev Team - * - * PCSX2 is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "PrecompiledHeader.h" -#include "Global.h" -#include "SoundTouch.h" -#include "common/Timer.h" -#include -#include - -//Uncomment the next line to use the old time stretcher -//#define SPU2X_USE_OLD_STRETCHER - -static std::unique_ptr pSoundTouch = nullptr; - -// data prediction amount, used to "commit" data that hasn't -// finished timestretch processing. -s32 SndBuffer::m_predictData; - -// records last buffer status (fill %, range -100 to 100, with 0 being 50% full) -float SndBuffer::lastPct; -float SndBuffer::lastEmergencyAdj; - -float SndBuffer::cTempo = 1; -float SndBuffer::eTempo = 1; - -void SndBuffer::PredictDataWrite(int samples) -{ - m_predictData += samples; -} - -// Calculate the buffer status percentage. -// Returns range from -1.0 to 1.0 -// 1.0 = buffer overflow! -// 0.0 = buffer nominal (50% full) -// -1.0 = buffer underflow! -float SndBuffer::GetStatusPct() -{ - // Get the buffer status of the output driver too, so that we can - // obtain a more accurate overall buffer status. - - int drvempty = mods[OutputModule]->GetEmptySampleCount(); // / 2; - - //ConLog( "Data %d >>> driver: %d predict: %d\n", m_data, drvempty, m_predictData ); - - int data = _GetApproximateDataInBuffer(); - float result = (float)(data + m_predictData - drvempty) - (m_size / 16); - result /= (m_size / 16); - return result; -} - - -//Alternative simple tempo adjustment. Based only on the soundtouch buffer state. -//Base algorithm: aim at specific average number of samples at the buffer (by GUI), and adjust tempo simply by current/target. -//An extra mechanism is added to keep adjustment at perfect 1:1 ratio (when emulation speed is stable around 100%) -// to prevent constant stretching/shrinking of packets if possible. -// This mechanism is triggered when the adjustment is close to 1:1 for long enough (defaults to 100 iterations within hys_ok_factor - defaults to 3%). -// 1:1 state is aborted when required adjustment goes beyond hys_bad_factor (defaults to 20%). -// -//To compensate for wide variation of the ratio due to relatively small size of the buffer, -// The required tempo is a running average of STRETCH_AVERAGE_LEN (defaults to 50) last calculations. -// This averaging slows down the respons time of the algorithm, but greatly stablize it towards steady stretching. -// -//Keeping the buffer at required latency: -// This algorithm stabilises when the actual latency is *. While this is just fine at 100% speed, -// it's problematic especially for slow speeds, as the number of actual samples at the buffer gets very small on that case, -// which may lead to underruns (or just too much latency when running very fast). -//To compensate for that, the algorithm has a slowly moving compensation factor which will eventually bring the actual latency to the required one. -//compensationDivider defines how slow this compensation changes. By default it's set to 100, -// which will finalize the compensation after about 200 iterations. -// -// Note, this algorithm is intentionally simplified by not taking extreme actions at extreme scenarios (mostly underruns when speed drops sharply), -// and let's the overrun/underrun protections do what they should (doesn't happen much though in practice, even at big FPS variations). -// -// These params were tested to show good respond and stability, on all audio systems (dsound, wav, port audio, xaudio2), -// even at extreme small latency of 50ms which can handle 50%-100% variations without audible glitches. - -int targetIPS = 750; - -//Dynamic tuning changes the values of the base algorithm parameters (derived from targetIPS) to adapt, in real time, to -// different number of invocations/sec (mostly affects number of iterations to average). -// Dynamic tuning can have a slight negative effect on the behavior of the algorithm, so it's preferred to have it off. -//Currently it looks like it's around 750/sec on all systems when playing at 100% speed (50/60fps), -// and proportional to that speed otherwise. -//If changes are made to SPU2 which affects this number (but it's still the same on all systems), then just change targetIPS. -//If we find out that some systems are very different, we can turn on dynamic tuning by uncommenting the next line. -//#define NEWSTRETCHER_USE_DYNAMIC_TUNING - - -//Additional performance note: since MAX_STRETCH_AVERAGE_LEN = 128 (or any power of 2), the '%' below -//could be replaced with a faster '&'. The compiler is highly likely to do it since all the values are unsigned. -#define AVERAGING_BUFFER_SIZE 256U -unsigned int AVERAGING_WINDOW = 50.0 * targetIPS / 750; - - -#define STRETCHER_RESET_THRESHOLD 5 -int gRequestStretcherReset = STRETCHER_RESET_THRESHOLD; -//Adds a value to the running average buffer, and return the new running average. -float addToAvg(float val) -{ - static float avg_fullness[AVERAGING_BUFFER_SIZE]; - static unsigned int nextAvgPos = 0; - static unsigned int available = 0; // Make sure we're not averaging AVERAGING_WINDOW items if we inserted less. - if (gRequestStretcherReset >= STRETCHER_RESET_THRESHOLD) - available = 0; - - if (available < AVERAGING_BUFFER_SIZE) - available++; - - avg_fullness[nextAvgPos] = val; - nextAvgPos = (nextAvgPos + 1U) % AVERAGING_BUFFER_SIZE; - - unsigned int actualWindow = std::min(available, AVERAGING_WINDOW); - unsigned int first = (nextAvgPos - actualWindow + AVERAGING_BUFFER_SIZE) % AVERAGING_BUFFER_SIZE; - - // Possible optimization: if we know that actualWindow hasn't changed since - // last invocation, we could calculate the running average in O(1) instead of O(N) - // by keeping a running sum between invocations, and then - // do "runningSum = runningSum + val - avg_fullness[(first-1)%...]" instead of the following loop. - // Few gotchas: val overwrites first-1, handling actualWindow changes, etc. - // However, this isn't hot code, so unless proven otherwise, we can live with unoptimized code. - float sum = 0; - for (unsigned int i = first; i < first + actualWindow; i++) - { - sum += avg_fullness[i % AVERAGING_BUFFER_SIZE]; - } - sum = sum / actualWindow; - - return sum ? sum : 1; // 1 because that's the 100% perfect speed value -} - -template -bool IsInRange(const T& val, const T& min, const T& max) -{ - return (min <= val && val <= max); -} - -//actual stretch algorithm implementation -void SndBuffer::UpdateTempoChangeSoundTouch2() -{ - - long targetSamplesReservoir = 48 * SndOutLatencyMS; //48000*SndOutLatencyMS/1000 - //base aim at buffer filled % - float baseTargetFullness = (double)targetSamplesReservoir; ///(double)m_size;//0.05; - - //state vars - static bool inside_hysteresis; //=false; - static int hys_ok_count; //=0; - static float dynamicTargetFullness; //=baseTargetFullness; - if (gRequestStretcherReset >= STRETCHER_RESET_THRESHOLD) - { - ConLog("______> stretch: Reset.\n"); - inside_hysteresis = false; - hys_ok_count = 0; - dynamicTargetFullness = baseTargetFullness; - } - - int data = _GetApproximateDataInBuffer(); - float bufferFullness = (float)data; ///(float)m_size; - -#ifdef NEWSTRETCHER_USE_DYNAMIC_TUNING - { //test current iterations/sec every 0.5s, and change algo params accordingly if different than previous IPS more than 30% - static long iters = 0; - static wxDateTime last = wxDateTime::UNow(); - wxDateTime unow = wxDateTime::UNow(); - wxTimeSpan delta = unow.Subtract(last); - if (delta.GetMilliseconds() > 500) - { - int pot_targetIPS = 1000.0 / delta.GetMilliseconds().ToDouble() * iters; - if (!IsInRange(pot_targetIPS, int((float)targetIPS / 1.3f), int((float)targetIPS * 1.3f))) - { - if (MsgOverruns()) - ConLog("Stretcher: setting iters/sec from %d to %d\n", targetIPS, pot_targetIPS); - targetIPS = pot_targetIPS; - AVERAGING_WINDOW = GetClamped((int)(50.0f * (float)targetIPS / 750.0f), 3, (int)AVERAGING_BUFFER_SIZE); - } - last = unow; - iters = 0; - } - iters++; - } -#endif - - //Algorithm params: (threshold params (hysteresis), etc) - const float hys_ok_factor = 1.04f; - const float hys_bad_factor = 1.2f; - int hys_min_ok_count = GetClamped((int)(50.0 * (float)targetIPS / 750.0), 2, 100); //consecutive iterations within hys_ok before going to 1:1 mode - int compensationDivider = GetClamped((int)(100.0 * (float)targetIPS / 750), 15, 150); - - float tempoAdjust = bufferFullness / dynamicTargetFullness; - float avgerage = addToAvg(tempoAdjust); - tempoAdjust = avgerage; - - // Dampen the adjustment to avoid overshoots (this means the average will compensate to the other side). - // This is different than simply bigger averaging window since bigger window also has bigger "momentum", - // so it's slower to slow down when it gets close to the equilibrium state and can therefore resonate. - // The dampening (sqrt was chosen for no very good reason) manages to mostly prevent that. - tempoAdjust = sqrt(tempoAdjust); - - tempoAdjust = GetClamped(tempoAdjust, 0.05f, 10.0f); - - if (tempoAdjust < 1) - baseTargetFullness /= sqrt(tempoAdjust); // slightly increase latency when running slow. - - dynamicTargetFullness += (baseTargetFullness / tempoAdjust - dynamicTargetFullness) / (double)compensationDivider; - if (IsInRange(tempoAdjust, 0.9f, 1.1f) && IsInRange(dynamicTargetFullness, baseTargetFullness * 0.9f, baseTargetFullness * 1.1f)) - dynamicTargetFullness = baseTargetFullness; - - if (!inside_hysteresis) - { - if (IsInRange(tempoAdjust, 1.0f / hys_ok_factor, hys_ok_factor)) - hys_ok_count++; - else - hys_ok_count = 0; - - if (hys_ok_count >= hys_min_ok_count) - { - inside_hysteresis = true; - if (MsgOverruns()) - ConLog("======> stretch: None (1:1)\n"); - } - } - else if (!IsInRange(tempoAdjust, 1.0f / hys_bad_factor, hys_bad_factor)) - { - if (MsgOverruns()) - ConLog("~~~~~~> stretch: Dynamic\n"); - inside_hysteresis = false; - hys_ok_count = 0; - } - - if (inside_hysteresis) - tempoAdjust = 1.0; - - if (MsgOverruns()) - { - static int iters = 0; - static u64 last = 0; - - const u64 now = Common::Timer::GetCurrentValue(); - - if (Common::Timer::ConvertValueToSeconds(now - last) > 1.0f) - { //report buffers state and tempo adjust every second - ConLog("buffers: %4d ms (%3.0f%%), tempo: %f, comp: %2.3f, iters: %d, (N-IPS:%d -> avg:%d, minokc:%d, div:%d) reset:%d\n", - (int)(data / 48), (double)(100.0 * bufferFullness / baseTargetFullness), (double)tempoAdjust, (double)(dynamicTargetFullness / baseTargetFullness), iters, (int)targetIPS, AVERAGING_WINDOW, hys_min_ok_count, compensationDivider, gRequestStretcherReset); - last = now; - iters = 0; - } - iters++; - } - - pSoundTouch->setTempo(tempoAdjust); - if (gRequestStretcherReset >= STRETCHER_RESET_THRESHOLD) - gRequestStretcherReset = 0; - - return; -} - - -void SndBuffer::UpdateTempoChangeSoundTouch() -{ - float statusPct = GetStatusPct(); - float pctChange = statusPct - lastPct; - - float tempoChange; - float emergencyAdj = 0; - float newcee = cTempo; // workspace var. for cTempo - - // IMPORTANT! - // If you plan to tweak these values, make sure you're using a release build - // OUTSIDE THE DEBUGGER to test it! The Visual Studio debugger can really cause - // erratic behavior in the audio buffers, and makes the timestretcher seem a - // lot more inconsistent than it really is. - - // We have two factors. - // * Distance from nominal buffer status (50% full) - // * The change from previous update to this update. - - // Prediction based on the buffer change: - // (linear seems to work better here) - - tempoChange = pctChange * 0.75f; - - if (statusPct * tempoChange < 0.0f) - { - // only apply tempo change if it is in synch with the buffer status. - // In other words, if the buffer is high (over 0%), and is decreasing, - // ignore it. It'll just muck things up. - - tempoChange = 0; - } - - // Sudden spikes in framerate can cause the nominal buffer status - // to go critical, in which case we have to enact an emergency - // stretch. The following cubic formulas do that. Values near - // the extremeites give much larger results than those near 0. - // And the value is added only this time, and does not accumulate. - // (otherwise a large value like this would cause problems down the road) - - // Constants: - // Weight - weights the statusPct's "emergency" consideration. - // higher values here will make the buffer perform more drastic - // compensations at the outer edges of the buffer (at -75 or +75% - // or beyond, for example). - - // Range - scales the adjustment to the given range (more or less). - // The actual range is dependent on the weight used, so if you increase - // Weight you'll usually want to decrease Range somewhat to compensate. - - // Prediction based on the buffer fill status: - - const float statusWeight = 2.99f; - const float statusRange = 0.068f; - - // "non-emergency" deadzone: In this area stretching will be strongly discouraged. - // Note: due tot he nature of timestretch latency, it's always a wee bit harder to - // cope with low fps (underruns) than it is high fps (overruns). So to help out a - // little, the low-end portions of this check are less forgiving than the high-sides. - - if (cTempo < 0.965f || cTempo > 1.060f || - pctChange < -0.38f || pctChange > 0.54f || - statusPct < -0.42f || statusPct > 0.70f || - eTempo < 0.89f || eTempo > 1.19f) - { - //printf("Emergency stretch: cTempo = %f eTempo = %f pctChange = %f statusPct = %f\n",cTempo,eTempo,pctChange,statusPct); - emergencyAdj = (pow(statusPct * statusWeight, 3.0f) * statusRange); - } - - // Smooth things out by factoring our previous adjustment into this one. - // It helps make the system 'feel' a little smarter by giving it at least - // one packet worth of history to help work off of: - - emergencyAdj = (emergencyAdj * 0.75f) + (lastEmergencyAdj * 0.25f); - - lastEmergencyAdj = emergencyAdj; - lastPct = statusPct; - - // Accumulate a fraction of the tempo change into the tempo itself. - // This helps the system run "smarter" to games that run consistently - // fast or slow by altering the base tempo to something closer to the - // game's active speed. In tests most games normalize within 2 seconds - // at 100ms latency, which is pretty good (larger buffers normalize even - // quicker). - - newcee += newcee * (tempoChange + emergencyAdj) * 0.03f; - - // Apply tempoChange as a scale of cTempo. That way the effect is proportional - // to the current tempo. (otherwise tempos rate of change at the extremes would - // be too drastic) - - float newTempo = newcee + (emergencyAdj * cTempo); - - // ... and as a final optimization, only stretch if the new tempo is outside - // a nominal threshold. Keep this threshold check small, because it could - // cause some serious side effects otherwise. (enlarging the cTempo check above - // is usually better/safer) - if (newTempo < 0.970f || newTempo > 1.045f) - { - cTempo = (float)newcee; - - if (newTempo < 0.10f) - newTempo = 0.10f; - else if (newTempo > 10.0f) - newTempo = 10.0f; - - if (cTempo < 0.15f) - cTempo = 0.15f; - else if (cTempo > 7.5f) - cTempo = 7.5f; - - pSoundTouch->setTempo(eTempo = (float)newTempo); - - /*ConLog("* SPU2: [Nominal %d%%] [Emergency: %d%%] (baseTempo: %d%% ) (newTempo: %d%%) (buffer: %d%%)\n", - //(relation < 0.0) ? "Normalize" : "", - (int)(tempoChange * 100.0 * 0.03), - (int)(emergencyAdj * 100.0), - (int)(cTempo * 100.0), - (int)(newTempo * 100.0), - (int)(statusPct * 100.0) - );*/ - } - else - { - // Nominal operation -- turn off stretching. - // note: eTempo 'slides' toward 1.0 for smoother audio and better - // protection against spikes. - if (cTempo != 1.0f) - { - cTempo = 1.0f; - eTempo = (1.0f + eTempo) * 0.5f; - pSoundTouch->setTempo(eTempo); - } - else - { - if (eTempo != cTempo) - pSoundTouch->setTempo(eTempo = cTempo); - } - } -} - -extern uint TickInterval; -void SndBuffer::UpdateTempoChangeAsyncMixing() -{ - float statusPct = GetStatusPct(); - - lastPct = statusPct; - if (statusPct < -0.1f) - { - TickInterval -= 4; - if (statusPct < -0.3f) - TickInterval = 64; - if (TickInterval < 64) - TickInterval = 64; - //printf("-- %d, %f\n",TickInterval,statusPct); - } - else if (statusPct > 0.2f) - { - TickInterval += 1; - if (TickInterval >= 7000) - TickInterval = 7000; - //printf("++ %d, %f\n",TickInterval,statusPct); - } - else - TickInterval = 768; -} - -void SndBuffer::timeStretchUnderrun() -{ - gRequestStretcherReset++; - // timeStretcher failed it's job. We need to slow down the audio some. - - cTempo -= (cTempo * 0.12f); - eTempo -= (eTempo * 0.30f); - if (eTempo < 0.1f) - eTempo = 0.1f; - // pSoundTouch->setTempo( eTempo ); - //pSoundTouch->setTempoChange(-30); // temporary (until stretcher is called) slow down -} - -s32 SndBuffer::timeStretchOverrun() -{ - // If we overran it means the timestretcher failed. We need to speed - // up audio playback. - cTempo += cTempo * 0.12f; - eTempo += eTempo * 0.40f; - if (eTempo > 7.5f) - eTempo = 7.5f; - //pSoundTouch->setTempo( eTempo ); - //pSoundTouch->setTempoChange(30);// temporary (until stretcher is called) speed up - - // Throw out just a little bit (two packets worth) to help - // give the TS some room to work: - gRequestStretcherReset++; - return SndOutPacketSize * 2; -} - -static void CvtPacketToFloat(StereoOut32* srcdest) -{ - StereoOutFloat* dest = (StereoOutFloat*)srcdest; - const StereoOut32* src = (StereoOut32*)srcdest; - for (uint i = 0; i < SndOutPacketSize; ++i, ++dest, ++src) - *dest = (StereoOutFloat)*src; -} - -// Parameter note: Size should always be a multiple of 128, thanks! -static void CvtPacketToInt(StereoOut32* srcdest, uint size) -{ - //pxAssume( (size & 127) == 0 ); - - const StereoOutFloat* src = (StereoOutFloat*)srcdest; - StereoOut32* dest = srcdest; - - for (uint i = 0; i < size; ++i, ++dest, ++src) - *dest = (StereoOut32)*src; -} - -void SndBuffer::timeStretchWrite() -{ - // data prediction helps keep the tempo adjustments more accurate. - // The timestretcher returns packets in belated "clump" form. - // Meaning that most of the time we'll get nothing back, and then - // suddenly we'll get several chunks back at once. Thus we use - // data prediction to make the timestretcher more responsive. - - PredictDataWrite((int)(SndOutPacketSize / eTempo)); - CvtPacketToFloat(sndTempBuffer); - - pSoundTouch->putSamples((float*)sndTempBuffer, SndOutPacketSize); - - int tempProgress; - while (tempProgress = pSoundTouch->receiveSamples((float*)sndTempBuffer, SndOutPacketSize), - tempProgress != 0) - { - // Hint: It's assumed that pSoundTouch will return chunks of 128 bytes (it always does as - // long as the SSE optimizations are enabled), which means we can do our own SSE opts here. - - CvtPacketToInt(sndTempBuffer, tempProgress); - _WriteSamples(sndTempBuffer, tempProgress); - } - -#ifdef SPU2X_USE_OLD_STRETCHER - UpdateTempoChangeSoundTouch(); -#else - UpdateTempoChangeSoundTouch2(); -#endif -} - -void SndBuffer::soundtouchInit() -{ - pSoundTouch = std::make_unique(); - pSoundTouch->setSampleRate(SampleRate); - pSoundTouch->setChannels(2); - - pSoundTouch->setSetting(SETTING_USE_QUICKSEEK, 0); - pSoundTouch->setSetting(SETTING_USE_AA_FILTER, 0); - - SoundtouchCfg::ApplySettings(*pSoundTouch); - - pSoundTouch->setTempo(1); - - // some timestretch management vars: - - cTempo = 1.0; - eTempo = 1.0; - lastPct = 0; - lastEmergencyAdj = 0; - - m_predictData = 0; -} - -// reset timestretch management vars, and delay updates a bit: -void SndBuffer::soundtouchClearContents() -{ - if (pSoundTouch == nullptr) - return; - - pSoundTouch->clear(); - pSoundTouch->setTempo(1); - - cTempo = 1.0; - eTempo = 1.0; - lastPct = 0; - lastEmergencyAdj = 0; - - m_predictData = 0; -} - -void SndBuffer::soundtouchCleanup() -{ - pSoundTouch.reset(); -} diff --git a/pcsx2/SPU2/Wavedump_wav.cpp b/pcsx2/SPU2/Wavedump_wav.cpp index 2d7e0fcac9..32d169b5ff 100644 --- a/pcsx2/SPU2/Wavedump_wav.cpp +++ b/pcsx2/SPU2/Wavedump_wav.cpp @@ -43,7 +43,7 @@ namespace WaveDump { if (!IsDevBuild) return; - if (!WaveLog()) + if (!SPU2::WaveLog()) return; for (uint cidx = 0; cidx < 2; cidx++) diff --git a/pcsx2/SPU2/defs.h b/pcsx2/SPU2/defs.h index 5d8605de8a..89c3dff98d 100644 --- a/pcsx2/SPU2/defs.h +++ b/pcsx2/SPU2/defs.h @@ -71,7 +71,10 @@ public: void Update(); void RegSet(u16 src); // used to set the volume from a register source (16 bit signed) + +#ifdef PCSX2_DEVBUILD void DebugDump(FILE* dump, const char* title, const char* nameLR); +#endif }; struct V_VolumeSlideLR @@ -95,7 +98,9 @@ public: Right.Update(); } +#ifdef PCSX2_DEVBUILD void DebugDump(FILE* dump, const char* title); +#endif }; struct V_ADSR diff --git a/pcsx2/SPU2/spu2.cpp b/pcsx2/SPU2/spu2.cpp index 5cdc0ec8ea..bb80a32af4 100644 --- a/pcsx2/SPU2/spu2.cpp +++ b/pcsx2/SPU2/spu2.cpp @@ -19,13 +19,26 @@ #include "SPU2/Dma.h" #include "R3000A.h" -static int ConsoleSampleRate = 48000; +namespace SPU2 +{ +static int GetConsoleSampleRate(); +static void InitSndBuffer(); +static void UpdateSampleRate(); +static void InternalReset(bool psxmode); +} + +static double s_device_sample_rate_multiplier = 1.0; +static bool s_psxmode = false; + int SampleRate = 48000; -static double DeviceSampleRateMultiplier = 1.0; - u32 lClocks = 0; +int SPU2::GetConsoleSampleRate() +{ + return s_psxmode ? 44100 : 48000; +} + // -------------------------------------------------------------------------------------- // DMA 4/7 Callbacks from Core Emulator // -------------------------------------------------------------------------------------- @@ -35,7 +48,7 @@ void SPU2readDMA4Mem(u16* pMem, u32 size) // size now in 16bit units { TimeUpdate(psxRegs.cycle); - FileLog("[%10d] SPU2 readDMA4Mem size %x\n", Cycles, size << 1); + SPU2::FileLog("[%10d] SPU2 readDMA4Mem size %x\n", Cycles, size << 1); Cores[0].DoDMAread(pMem, size); } @@ -43,14 +56,14 @@ void SPU2writeDMA4Mem(u16* pMem, u32 size) // size now in 16bit units { TimeUpdate(psxRegs.cycle); - FileLog("[%10d] SPU2 writeDMA4Mem size %x at address %x\n", Cycles, size << 1, Cores[0].TSA); + SPU2::FileLog("[%10d] SPU2 writeDMA4Mem size %x at address %x\n", Cycles, size << 1, Cores[0].TSA); Cores[0].DoDMAwrite(pMem, size); } void SPU2interruptDMA4() { - FileLog("[%10d] SPU2 interruptDMA4\n", Cycles); + SPU2::FileLog("[%10d] SPU2 interruptDMA4\n", Cycles); if (Cores[0].DmaMode) Cores[0].Regs.STATX |= 0x80; Cores[0].Regs.STATX &= ~0x400; @@ -59,7 +72,7 @@ void SPU2interruptDMA4() void SPU2interruptDMA7() { - FileLog("[%10d] SPU2 interruptDMA7\n", Cycles); + SPU2::FileLog("[%10d] SPU2 interruptDMA7\n", Cycles); if (Cores[1].DmaMode) Cores[1].Regs.STATX |= 0x80; Cores[1].Regs.STATX &= ~0x400; @@ -70,7 +83,7 @@ void SPU2readDMA7Mem(u16* pMem, u32 size) { TimeUpdate(psxRegs.cycle); - FileLog("[%10d] SPU2 readDMA7Mem size %x\n", Cycles, size << 1); + SPU2::FileLog("[%10d] SPU2 readDMA7Mem size %x\n", Cycles, size << 1); Cores[1].DoDMAread(pMem, size); } @@ -78,51 +91,49 @@ void SPU2writeDMA7Mem(u16* pMem, u32 size) { TimeUpdate(psxRegs.cycle); - FileLog("[%10d] SPU2 writeDMA7Mem size %x at address %x\n", Cycles, size << 1, Cores[1].TSA); + SPU2::FileLog("[%10d] SPU2 writeDMA7Mem size %x at address %x\n", Cycles, size << 1, Cores[1].TSA); Cores[1].DoDMAwrite(pMem, size); } -static void SPU2InitSndBuffer() +void SPU2::InitSndBuffer() { Console.WriteLn("Initializing SndBuffer at sample rate of %u...", SampleRate); - if (SndBuffer::Init()) + if (SndBuffer::Init(EmuConfig.SPU2.OutputModule.c_str())) return; - if (SampleRate != ConsoleSampleRate) + if (SampleRate != GetConsoleSampleRate()) { - // TODO: Resample on our side... + // It'll get stretched instead.. const int original_sample_rate = SampleRate; Console.Error("Failed to init SPU2 at adjusted sample rate %u, trying console rate.", SampleRate); - SampleRate = ConsoleSampleRate; - if (SndBuffer::Init()) + SampleRate = GetConsoleSampleRate(); + if (SndBuffer::Init(EmuConfig.SPU2.OutputModule.c_str())) return; SampleRate = original_sample_rate; } // just use nullout - OutputModule = FindOutputModuleById(NullOut->GetIdent()); - if (!SndBuffer::Init()) + if (!SndBuffer::Init("nullout")) pxFailRel("Failed to initialize nullout."); } -static void SPU2UpdateSampleRate() +void SPU2::UpdateSampleRate() { - const int new_sample_rate = static_cast(std::round(static_cast(ConsoleSampleRate) * DeviceSampleRateMultiplier)); + const int new_sample_rate = static_cast(std::round(static_cast(GetConsoleSampleRate()) * s_device_sample_rate_multiplier)); if (SampleRate == new_sample_rate) return; SndBuffer::Cleanup(); SampleRate = new_sample_rate; - SPU2InitSndBuffer(); + InitSndBuffer(); } -static void SPU2InternalReset(PS2Modes isRunningPSXMode) +void SPU2::InternalReset(bool psxmode) { - ConsoleSampleRate = (isRunningPSXMode == PS2Modes::PSX) ? 44100 : 48000; - - if (isRunningPSXMode == PS2Modes::PS2) + s_psxmode = psxmode; + if (!s_psxmode) { memset(spu2regs, 0, 0x010000); memset(_spu2mem, 0, 0x200000); @@ -136,22 +147,28 @@ static void SPU2InternalReset(PS2Modes isRunningPSXMode) } } -void SPU2reset(PS2Modes isRunningPSXMode) +void SPU2::Reset(bool psxmode) { - SPU2InternalReset(isRunningPSXMode); - SPU2UpdateSampleRate(); + InternalReset(psxmode); + UpdateSampleRate(); } -void SPU2SetDeviceSampleRateMultiplier(double multiplier) +void SPU2::OnTargetSpeedChanged() { - if (DeviceSampleRateMultiplier == multiplier) + if (EmuConfig.SPU2.SynchMode != Pcsx2Config::SPU2Options::SynchronizationMode::TimeStretch) + SndBuffer::ResetBuffers(); +} + +void SPU2::SetDeviceSampleRateMultiplier(double multiplier) +{ + if (s_device_sample_rate_multiplier == multiplier) return; - DeviceSampleRateMultiplier = multiplier; - SPU2UpdateSampleRate(); + s_device_sample_rate_multiplier = multiplier; + UpdateSampleRate(); } -bool SPU2init() +bool SPU2::Initialize() { pxAssert(regtable[0x400] == nullptr); spu2regs = (s16*)malloc(0x010000); @@ -190,17 +207,11 @@ bool SPU2init() } -bool SPU2open(PS2Modes isRunningPSXMode) +bool SPU2::Open() { - ReadSettings(); - -#ifdef SPU2_LOG - if (AccessLog()) - { - spu2Log = OpenLog(AccessLogFileName.c_str()); - setvbuf(spu2Log, nullptr, _IONBF, 0); - FileLog("SPU2init\n"); - } +#ifdef PCSX2_DEVBUILD + if (SPU2::AccessLog()) + SPU2::OpenFileLog(); #endif DMALogOpen(); @@ -209,131 +220,45 @@ bool SPU2open(PS2Modes isRunningPSXMode) lClocks = psxRegs.cycle; - SPU2InternalReset(isRunningPSXMode); + InternalReset(false); - try - { - SampleRate = static_cast(std::round(static_cast(ConsoleSampleRate) * DeviceSampleRateMultiplier)); - SPU2InitSndBuffer(); - WaveDump::Open(); - } - catch (std::exception& ex) - { - Console.Error("SPU2 Error: Could not initialize device, or something.\nReason: %s", ex.what()); - SPU2close(); - return false; - } + SampleRate = static_cast(std::round(static_cast(GetConsoleSampleRate()) * s_device_sample_rate_multiplier)); + InitSndBuffer(); + WaveDump::Open(); return true; } -void SPU2close() +void SPU2::Close() { FileLog("[%10d] SPU2 Close\n", Cycles); SndBuffer::Cleanup(); -} -void SPU2shutdown() -{ - ConLog("* SPU2: Shutting down.\n"); - - SPU2close(); - - DoFullDump(); -#ifdef STREAM_DUMP - fclose(il0); - fclose(il1); -#endif -#ifdef EFFECTS_DUMP - fclose(el0); - fclose(el1); -#endif WaveDump::Close(); - DMALogClose(); +#ifdef PCSX2_DEVBUILD + DoFullDump(); + CloseFileLog(); +#endif +} + +void SPU2::Shutdown() +{ safe_free(spu2regs); safe_free(_spu2mem); safe_free(pcm_cache_data); - - -#ifdef SPU2_LOG - if (!AccessLog()) - return; - FileLog("[%10d] SPU2shutdown\n", Cycles); - if (spu2Log) - fclose(spu2Log); -#endif } -bool SPU2IsRunningPSXMode() +bool SPU2::IsRunningPSXMode() { - return (ConsoleSampleRate == 44100); + return s_psxmode; } -void SPU2SetOutputPaused(bool paused) -{ - SndBuffer::SetPaused(paused); -} - -#ifdef DEBUG_KEYS -static u32 lastTicks; -static bool lState[6]; -#endif - void SPU2async(u32 cycles) { TimeUpdate(psxRegs.cycle); - -#ifdef DEBUG_KEYS - u32 curTicks = GetTickCount(); - if ((curTicks - lastTicks) >= 50) - { - int oldI = Interpolation; - bool cState[6]; - for (int i = 0; i < 6; i++) - { - cState[i] = !!(GetAsyncKeyState(VK_NUMPAD0 + i) & 0x8000); - - if ((cState[i] && !lState[i]) && i != 5) - Interpolation = i; - - lState[i] = cState[i]; - } - - if (Interpolation != oldI) - { - printf("Interpolation set to %d", Interpolation); - switch (Interpolation) - { - case 0: - printf(" - Nearest.\n"); - break; - case 1: - printf(" - Linear.\n"); - break; - case 2: - printf(" - Cubic.\n"); - break; - case 3: - printf(" - Hermite.\n"); - break; - case 4: - printf(" - Catmull-Rom.\n"); - break; - case 5: - printf(" - Gaussian.\n"); - break; - default: - printf(" (unknown).\n"); - break; - } - } - - lastTicks = curTicks; - } -#endif } u16 SPU2read(u32 rmem) @@ -370,7 +295,8 @@ u16 SPU2read(u32 rmem) else if (mem >= 0x800) { ret = spu2Ru16(mem); - ConLog("* SPU2: Read from reg>=0x800: %x value %x\n", mem, ret); + if (SPU2::MsgToConsole()) + SPU2::ConLog("* SPU2: Read from reg>=0x800: %x value %x\n", mem, ret); } else { @@ -450,3 +376,43 @@ s32 SPU2freeze(FreezeAction mode, freezeData* data) // technically unreachable, but kills a warning: return 0; } + +void SPU2::CheckForConfigChanges(const Pcsx2Config& old_config) +{ + if (EmuConfig.SPU2 == old_config.SPU2) + return; + + const Pcsx2Config::SPU2Options& opts = EmuConfig.SPU2; + const Pcsx2Config::SPU2Options& oldopts = old_config.SPU2; + + // No need to reinit for volume change. + if (opts.FinalVolume != oldopts.FinalVolume) + SetOutputVolume(opts.FinalVolume); + + // Wipe buffer out when changing sync mode, so e.g. TS->none doesn't have a huge delay. + if (opts.SynchMode != oldopts.SynchMode) + SndBuffer::ResetBuffers(); + + // Things which require re-initialzing the output. + if (opts.Latency != oldopts.Latency || + opts.SpeakerConfiguration != oldopts.SpeakerConfiguration || + opts.DplDecodingLevel != oldopts.DplDecodingLevel || + opts.SequenceLenMS != oldopts.SequenceLenMS || + opts.SeekWindowMS != oldopts.SeekWindowMS || + opts.OverlapMS != oldopts.OverlapMS) + { + SndBuffer::Cleanup(); + InitSndBuffer(); + } + +#ifdef PCSX2_DEVBUILD + // AccessLog controls file output. + if (opts.AccessLog != oldopts.AccessLog) + { + if (AccessLog()) + OpenFileLog(); + else + CloseFileLog(); + } +#endif +} diff --git a/pcsx2/SPU2/spu2.h b/pcsx2/SPU2/spu2.h index 1eb051dc0e..a6bc069aaf 100644 --- a/pcsx2/SPU2/spu2.h +++ b/pcsx2/SPU2/spu2.h @@ -19,20 +19,40 @@ #include "IopCounters.h" #include -enum class PS2Modes +struct Pcsx2Config; + +namespace SPU2 { - PS2, - PSX, -}; +/// Initialization/cleanup, call at process startup/shutdown. +bool Initialize(); +void Shutdown(); + +/// Open/close, call at VM startup/shutdown. +bool Open(); +void Close(); + +/// Reset, rebooting VM or going into PSX mode. +void Reset(bool psxmode); + +/// Identifies any configuration changes and applies them. +void CheckForConfigChanges(const Pcsx2Config& old_config); + +/// Directly updates the output volume without going through the configuration. +void SetOutputVolume(s32 volume); + +/// Pauses/resumes the output stream. +void SetOutputPaused(bool paused); + +/// Clears output buffers in no-sync mode, prevents long delays after fast forwarding. +void OnTargetSpeedChanged(); + +/// Adjusts the premultiplier on the output sample rate. Used for syncing to host refresh rate. +void SetDeviceSampleRateMultiplier(double multiplier); + +/// Returns true if we're currently running in PSX mode. +bool IsRunningPSXMode(); +} // namespace SPU2 -bool SPU2init(); -void SPU2reset(PS2Modes isRunningPSXMode = PS2Modes::PS2); -bool SPU2open(PS2Modes isRunningPSXMode = PS2Modes::PS2); -void SPU2close(); -void SPU2shutdown(); -bool SPU2IsRunningPSXMode(); -void SPU2SetOutputPaused(bool paused); -void SPU2SetDeviceSampleRateMultiplier(double multiplier); void SPU2write(u32 mem, u16 value); u16 SPU2read(u32 mem); diff --git a/pcsx2/SPU2/spu2sys.cpp b/pcsx2/SPU2/spu2sys.cpp index bfe51352ff..17addfa678 100644 --- a/pcsx2/SPU2/spu2sys.cpp +++ b/pcsx2/SPU2/spu2sys.cpp @@ -43,10 +43,10 @@ u32 Cycles; int PlayMode; -bool has_to_call_irq[2] = { false, false }; -bool has_to_call_irq_dma[2] = { false, false }; +static bool has_to_call_irq[2] = { false, false }; +static bool has_to_call_irq_dma[2] = { false, false }; -bool psxmode = false; +static bool psxmode = false; void SetIrqCall(int core) { @@ -90,8 +90,8 @@ __forceinline void spu2M_Write(u32 addr, s16 value) const int cacheIdx = addr / pcm_WordsPerBlock; pcm_cache_data[cacheIdx].Validated = false; - if (MsgToConsole() && MsgCache()) - ConLog("* SPU2: PcmCache Block Clear at 0x%x (cacheIdx=0x%x)\n", addr, cacheIdx); + if (SPU2::MsgToConsole() && SPU2::MsgCache()) + SPU2::ConLog("* SPU2: PcmCache Block Clear at 0x%x (cacheIdx=0x%x)\n", addr, cacheIdx); } *GetMemPtr(addr) = value; } @@ -126,7 +126,9 @@ V_Core::~V_Core() throw() void V_Core::Init(int index) { - ConLog("* SPU2: Init SPU2 core %d \n", index); + if (SPU2::MsgToConsole()) + SPU2::ConLog("* SPU2: Init SPU2 core %d \n", index); + //memset(this, 0, sizeof(V_Core)); // Explicitly initializing variables instead. Mute = false; @@ -229,36 +231,36 @@ void V_Core::Init(int index) void V_Core::AnalyzeReverbPreset() { - ConLog("Reverb Parameter Update for Core %d:\n", Index); - ConLog("----------------------------------------------------------\n"); + SPU2::ConLog("Reverb Parameter Update for Core %d:\n", Index); + SPU2::ConLog("----------------------------------------------------------\n"); - ConLog(" IN_COEF_L, IN_COEF_R 0x%08x, 0x%08x\n", Revb.IN_COEF_L, Revb.IN_COEF_R); - ConLog(" APF1_SIZE, APF2_SIZE 0x%08x, 0x%08x\n", Revb.APF1_SIZE, Revb.APF2_SIZE); - ConLog(" APF1_VOL, APF2_VOL 0x%08x, 0x%08x\n", Revb.APF1_VOL, Revb.APF2_VOL); + SPU2::ConLog(" IN_COEF_L, IN_COEF_R 0x%08x, 0x%08x\n", Revb.IN_COEF_L, Revb.IN_COEF_R); + SPU2::ConLog(" APF1_SIZE, APF2_SIZE 0x%08x, 0x%08x\n", Revb.APF1_SIZE, Revb.APF2_SIZE); + SPU2::ConLog(" APF1_VOL, APF2_VOL 0x%08x, 0x%08x\n", Revb.APF1_VOL, Revb.APF2_VOL); - ConLog(" COMB1_VOL 0x%08x\n", Revb.COMB1_VOL); - ConLog(" COMB2_VOL 0x%08x\n", Revb.COMB2_VOL); - ConLog(" COMB3_VOL 0x%08x\n", Revb.COMB3_VOL); - ConLog(" COMB4_VOL 0x%08x\n", Revb.COMB4_VOL); + SPU2::ConLog(" COMB1_VOL 0x%08x\n", Revb.COMB1_VOL); + SPU2::ConLog(" COMB2_VOL 0x%08x\n", Revb.COMB2_VOL); + SPU2::ConLog(" COMB3_VOL 0x%08x\n", Revb.COMB3_VOL); + SPU2::ConLog(" COMB4_VOL 0x%08x\n", Revb.COMB4_VOL); - ConLog(" COMB1_L_SRC, COMB1_R_SRC 0x%08x, 0x%08x\n", Revb.COMB1_L_SRC, Revb.COMB1_R_SRC); - ConLog(" COMB2_L_SRC, COMB2_R_SRC 0x%08x, 0x%08x\n", Revb.COMB2_L_SRC, Revb.COMB2_R_SRC); - ConLog(" COMB3_L_SRC, COMB3_R_SRC 0x%08x, 0x%08x\n", Revb.COMB3_L_SRC, Revb.COMB3_R_SRC); - ConLog(" COMB4_L_SRC, COMB4_R_SRC 0x%08x, 0x%08x\n", Revb.COMB4_L_SRC, Revb.COMB4_R_SRC); + SPU2::ConLog(" COMB1_L_SRC, COMB1_R_SRC 0x%08x, 0x%08x\n", Revb.COMB1_L_SRC, Revb.COMB1_R_SRC); + SPU2::ConLog(" COMB2_L_SRC, COMB2_R_SRC 0x%08x, 0x%08x\n", Revb.COMB2_L_SRC, Revb.COMB2_R_SRC); + SPU2::ConLog(" COMB3_L_SRC, COMB3_R_SRC 0x%08x, 0x%08x\n", Revb.COMB3_L_SRC, Revb.COMB3_R_SRC); + SPU2::ConLog(" COMB4_L_SRC, COMB4_R_SRC 0x%08x, 0x%08x\n", Revb.COMB4_L_SRC, Revb.COMB4_R_SRC); - ConLog(" SAME_L_SRC, SAME_R_SRC 0x%08x, 0x%08x\n", Revb.SAME_L_SRC, Revb.SAME_R_SRC); - ConLog(" DIFF_L_SRC, DIFF_R_SRC 0x%08x, 0x%08x\n", Revb.DIFF_L_SRC, Revb.DIFF_R_SRC); - ConLog(" SAME_L_DST, SAME_R_DST 0x%08x, 0x%08x\n", Revb.SAME_L_DST, Revb.SAME_R_DST); - ConLog(" DIFF_L_DST, DIFF_R_DST 0x%08x, 0x%08x\n", Revb.DIFF_L_DST, Revb.DIFF_R_DST); - ConLog(" IIR_VOL, WALL_VOL 0x%08x, 0x%08x\n", Revb.IIR_VOL, Revb.WALL_VOL); + SPU2::ConLog(" SAME_L_SRC, SAME_R_SRC 0x%08x, 0x%08x\n", Revb.SAME_L_SRC, Revb.SAME_R_SRC); + SPU2::ConLog(" DIFF_L_SRC, DIFF_R_SRC 0x%08x, 0x%08x\n", Revb.DIFF_L_SRC, Revb.DIFF_R_SRC); + SPU2::ConLog(" SAME_L_DST, SAME_R_DST 0x%08x, 0x%08x\n", Revb.SAME_L_DST, Revb.SAME_R_DST); + SPU2::ConLog(" DIFF_L_DST, DIFF_R_DST 0x%08x, 0x%08x\n", Revb.DIFF_L_DST, Revb.DIFF_R_DST); + SPU2::ConLog(" IIR_VOL, WALL_VOL 0x%08x, 0x%08x\n", Revb.IIR_VOL, Revb.WALL_VOL); - ConLog(" APF1_L_DST 0x%08x\n", Revb.APF1_L_DST); - ConLog(" APF1_R_DST 0x%08x\n", Revb.APF1_R_DST); - ConLog(" APF2_L_DST 0x%08x\n", Revb.APF2_L_DST); - ConLog(" APF2_R_DST 0x%08x\n", Revb.APF2_R_DST); + SPU2::ConLog(" APF1_L_DST 0x%08x\n", Revb.APF1_L_DST); + SPU2::ConLog(" APF1_R_DST 0x%08x\n", Revb.APF1_R_DST); + SPU2::ConLog(" APF2_L_DST 0x%08x\n", Revb.APF2_L_DST); + SPU2::ConLog(" APF2_R_DST 0x%08x\n", Revb.APF2_R_DST); - ConLog(" EffectsBufferSize 0x%x\n", EffectsBufferSize); - ConLog("----------------------------------------------------------\n"); + SPU2::ConLog(" EffectsBufferSize 0x%x\n", EffectsBufferSize); + SPU2::ConLog("----------------------------------------------------------\n"); } s32 V_Core::EffectsBufferIndexer(s32 offset) const { @@ -288,7 +290,7 @@ void V_Core::UpdateEffectsBufferSize() return; // debug: shows reverb parameters in console - if (MsgToConsole()) + if (SPU2::MsgToConsole()) AnalyzeReverbPreset(); // Rebuild buffer indexers. @@ -396,13 +398,13 @@ __forceinline void TimeUpdate(u32 cClocks) if (dClocks > (u32)(TickInterval * SanityInterval)) { - if (MsgToConsole()) - ConLog(" * SPU2 > TimeUpdate Sanity Check (Tick Delta: %d) (PS2 Ticks: %d)\n", dClocks / TickInterval, cClocks / TickInterval); + if (SPU2::MsgToConsole()) + SPU2::ConLog(" * SPU2 > TimeUpdate Sanity Check (Tick Delta: %d) (PS2 Ticks: %d)\n", dClocks / TickInterval, cClocks / TickInterval); dClocks = TickInterval * SanityInterval; lClocks = cClocks - dClocks; } - if (SynchMode == 1) // AsyncMix on + if (EmuConfig.SPU2.SynchMode == Pcsx2Config::SPU2Options::SynchronizationMode::ASync) SndBuffer::UpdateTempoChangeAsyncMixing(); else TickInterval = 768; // Reset to default, in case the user hotswitched from async to something else. @@ -552,10 +554,10 @@ __forceinline void UpdateSpdifMode() { int OPM = PlayMode; - if (Spdif.Out & 0x4) // use 24/32bit PCM data streaming + if (Spdif.Out & 0x4 && SPU2::MsgToConsole()) // use 24/32bit PCM data streaming { PlayMode = 8; - ConLog("* SPU2: WARNING: Possibly CDDA mode set!\n"); + SPU2::ConLog("* SPU2: WARNING: Possibly CDDA mode set!\n"); return; } @@ -573,9 +575,9 @@ __forceinline void UpdateSpdifMode() PlayMode = 1; } } - if (OPM != PlayMode) + if (OPM != PlayMode && SPU2::MsgToConsole()) { - ConLog("* SPU2: Play Mode Set to %s (%d).\n", + SPU2::ConLog("* SPU2: Play Mode Set to %s (%d).\n", (PlayMode == 0) ? "Normal" : ((PlayMode == 1) ? "PCM Clone" : ((PlayMode == 2) ? "PCM Bypass" : "BitStream Bypass")), PlayMode); } } @@ -628,8 +630,8 @@ void V_Core::WriteRegPS1(u32 mem, u16 value) thisvol.Mode = (value & 0xF000) >> 12; thisvol.Increment = (value & 0x7F); // We're not sure slides work 100% - if (IsDevBuild) - ConLog("* SPU2: Voice uses Slides in Mode = %x, Increment = %x\n", thisvol.Mode, thisvol.Increment); + if (SPU2::MsgToConsole()) + SPU2::ConLog("* SPU2: Voice uses Slides in Mode = %x, Increment = %x\n", thisvol.Mode, thisvol.Increment); } else { @@ -665,7 +667,8 @@ void V_Core::WriteRegPS1(u32 mem, u16 value) case 0xc: // Voice 0..23 ADSR Current Volume // not commonly set by games Voices[voice].ADSR.Value = value * 0x10001U; - ConLog("voice %x ADSR.Value write: %x\n", voice, Voices[voice].ADSR.Value); + if (SPU2::MsgToConsole()) + SPU2::ConLog("voice %x ADSR.Value write: %x\n", voice, Voices[voice].ADSR.Value); break; case 0xe: Voices[voice].LoopStartA = map_spu1to2(value); @@ -713,27 +716,27 @@ void V_Core::WriteRegPS1(u32 mem, u16 value) case 0x1d90: // Channel FM (pitch lfo) mode (0-15) SPU2_FastWrite(REG_S_PMON, value); - if (value != 0) - ConLog("SPU2 warning: wants to set Pitch Modulation reg1 to %x \n", value); + if (value != 0 && SPU2::MsgToConsole()) + SPU2::ConLog("SPU2 warning: wants to set Pitch Modulation reg1 to %x \n", value); break; case 0x1d92: // Channel FM (pitch lfo) mode (16-23) SPU2_FastWrite(REG_S_PMON + 2, value); - if (value != 0) - ConLog("SPU2 warning: wants to set Pitch Modulation reg2 to %x \n", value); + if (value != 0 && SPU2::MsgToConsole()) + SPU2::ConLog("SPU2 warning: wants to set Pitch Modulation reg2 to %x \n", value); break; case 0x1d94: // Channel Noise mode (0-15) SPU2_FastWrite(REG_S_NON, value); - if (value != 0) - ConLog("SPU2 warning: wants to set Channel Noise mode reg1 to %x\n", value); + if (value != 0 && SPU2::MsgToConsole()) + SPU2::ConLog("SPU2 warning: wants to set Channel Noise mode reg1 to %x\n", value); break; case 0x1d96: // Channel Noise mode (16-23) SPU2_FastWrite(REG_S_NON + 2, value); - if (value != 0) - ConLog("SPU2 warning: wants to set Channel Noise mode reg2 to %x\n", value); + if (value != 0 && SPU2::MsgToConsole()) + SPU2::ConLog("SPU2 warning: wants to set Channel Noise mode reg2 to %x\n", value); break; case 0x1d98: // 1F801D98h - Voice 0..23 Reverb mode aka Echo On (EON) (R/W) @@ -762,12 +765,14 @@ void V_Core::WriteRegPS1(u32 mem, u16 value) //break; case 0x1d9c: // Voice 0..15 ON/OFF (status) (ENDX) (R) // writeable but hw overrides it shortly after //Regs.ENDX &= 0xff0000; - ConLog("SPU2 warning: wants to set ENDX reg1 to %x \n", value); + if (SPU2::MsgToConsole()) + SPU2::ConLog("SPU2 warning: wants to set ENDX reg1 to %x \n", value); break; case 0x1d9e: // // Voice 15..23 ON/OFF (status) (ENDX) (R) // writeable but hw overrides it shortly after //Regs.ENDX &= 0xffff; - ConLog("SPU2 warning: wants to set ENDX reg2 to %x \n", value); + if (SPU2::MsgToConsole()) + SPU2::ConLog("SPU2 warning: wants to set ENDX reg2 to %x \n", value); break; case 0x1da2: // Reverb work area start @@ -786,7 +791,8 @@ void V_Core::WriteRegPS1(u32 mem, u16 value) case 0x1da6: TSA = map_spu1to2(value); - ConLog("SPU2 Setting TSA to %x \n", TSA); + if (SPU2::MsgToConsole()) + SPU2::ConLog("SPU2 Setting TSA to %x \n", TSA); break; case 0x1da8: // Spu Write to Memory @@ -806,7 +812,8 @@ void V_Core::WriteRegPS1(u32 mem, u16 value) break; case 0x1dac: // 1F801DACh - Sound RAM Data Transfer Control (should be 0004h) - ConLog("SPU Sound RAM Data Transfer Control (should be 4) : value = %x \n", value); + if (SPU2::MsgToConsole()) + SPU2::ConLog("SPU Sound RAM Data Transfer Control (should be 4) : value = %x \n", value); psxSoundDataTransferControl = value; break; @@ -932,7 +939,7 @@ void V_Core::WriteRegPS1(u32 mem, u16 value) } if (show) - FileLog("[%10d] (!) SPU write mem %08x value %04x\n", Cycles, mem, value); + SPU2::FileLog("[%10d] (!) SPU write mem %08x value %04x\n", Cycles, mem, value); spu2Ru16(mem) = value; } @@ -1078,7 +1085,7 @@ u16 V_Core::ReadRegPS1(u32 mem) } if (show) - FileLog("[%10d] (!) SPU read mem %08x value %04x\n", Cycles, mem, value); + SPU2::FileLog("[%10d] (!) SPU read mem %08x value %04x\n", Cycles, mem, value); return value; } @@ -1127,8 +1134,8 @@ static void RegWrite_VoiceParams(u16 value) thisvol.Mode = (value & 0xF000) >> 12; thisvol.Increment = (value & 0x7F); // We're not sure slides work 100% - if (IsDevBuild) - ConLog("* SPU2: Voice uses Slides in Mode = %x, Increment = %x\n", thisvol.Mode, thisvol.Increment); + if (SPU2::MsgToConsole()) + SPU2::ConLog("* SPU2: Voice uses Slides in Mode = %x, Increment = %x\n", thisvol.Mode, thisvol.Increment); } else { @@ -1323,14 +1330,14 @@ static void RegWrite_Core(u16 value) if (value & 0x000E) { - if (MsgToConsole()) - ConLog("* SPU2: Core %d ATTR unknown bits SET! value=%04x\n", core, value); + if (SPU2::MsgToConsole()) + SPU2::ConLog("* SPU2: Core %d ATTR unknown bits SET! value=%04x\n", core, value); } if (thiscore.AttrBit0 != bit0) { - if (MsgToConsole()) - ConLog("* SPU2: ATTR bit 0 set to %d\n", thiscore.AttrBit0); + if (SPU2::MsgToConsole()) + SPU2::ConLog("* SPU2: ATTR bit 0 set to %d\n", thiscore.AttrBit0); } if (thiscore.IRQEnable != irqe) { @@ -1513,11 +1520,13 @@ static void RegWrite_Core(u16 value) break; case REG_S_ADMAS: - if (MsgToConsole()) - ConLog("* SPU2: Core %d AutoDMAControl set to %d (at cycle %d)\n", core, value, Cycles); + if (SPU2::MsgToConsole()) + { + SPU2::ConLog("* SPU2: Core %d AutoDMAControl set to %d (at cycle %d)\n", core, value, Cycles); - if (psxmode) - ConLog("* SPU2: Writing to REG_S_ADMAS while in PSX mode! value: %x", value); + if (psxmode) + SPU2::ConLog("* SPU2: Writing to REG_S_ADMAS while in PSX mode! value: %x", value); + } // hack for ps1driver which writes -1 (and never turns the adma off after psxlogo). // adma isn't available in psx mode either if (value == 32767) @@ -1966,7 +1975,8 @@ void StartVoices(int core, u32 value) if (value == 0) return; - ConLog("KeyOn Write %x\n", value); + if (SPU2::MsgToConsole()) + SPU2::ConLog("KeyOn Write %x\n", value); Cores[core].KeyOn |= value; Cores[core].Regs.ENDX &= ~value; @@ -1978,7 +1988,8 @@ void StartVoices(int core, u32 value) if ((Cycles - Cores[core].Voices[vc].PlayCycle) < 2) { - ConLog("Attempt to start voice %d on core %d in less than 2T since last KeyOn\n", vc, core); + if (SPU2::MsgToConsole()) + SPU2::ConLog("Attempt to start voice %d on core %d in less than 2T since last KeyOn\n", vc, core); continue; } @@ -1986,11 +1997,11 @@ void StartVoices(int core, u32 value) if (IsDevBuild) { - if (MsgKeyOnOff()) + if (SPU2::MsgKeyOnOff()) { V_Voice& thisvc(Cores[core].Voices[vc]); - ConLog("* SPU2: KeyOn: C%dV%02d: SSA: %8x; M: %s%s%s%s; H: %04x; P: %04x V: %04x/%04x; ADSR: %04x%04x\n", + SPU2::ConLog("* SPU2: KeyOn: C%dV%02d: SSA: %8x; M: %s%s%s%s; H: %04x; P: %04x V: %04x/%04x; ADSR: %04x%04x\n", core, vc, thisvc.StartA, (Cores[core].VoiceGates[vc].DryL) ? "+" : "-", (Cores[core].VoiceGates[vc].DryR) ? "+" : "-", (Cores[core].VoiceGates[vc].WetL) ? "+" : "-", (Cores[core].VoiceGates[vc].WetR) ? "+" : "-", @@ -2008,7 +2019,8 @@ void StopVoices(int core, u32 value) if (value == 0) return; - ConLog("KeyOff Write %x\n", value); + if (SPU2::MsgToConsole()) + SPU2::ConLog("KeyOff Write %x\n", value); for (u8 vc = 0; vc < V_Core::NumVoices; vc++) { @@ -2017,12 +2029,13 @@ void StopVoices(int core, u32 value) if (Cycles - Cores[core].Voices[vc].PlayCycle < 2) { - ConLog("Attempt to stop voice %d on core %d in less than 2T since KeyOn\n", vc, core); + if (SPU2::MsgToConsole()) + SPU2::ConLog("Attempt to stop voice %d on core %d in less than 2T since KeyOn\n", vc, core); continue; } Cores[core].Voices[vc].ADSR.Releasing = true; - if (MsgKeyOnOff()) - ConLog("* SPU2: KeyOff: Core %d; Voice %d.\n", core, vc); + if (SPU2::MsgKeyOnOff()) + SPU2::ConLog("* SPU2: KeyOff: Core %d; Voice %d.\n", core, vc); } } diff --git a/pcsx2/SaveState.cpp b/pcsx2/SaveState.cpp index 9668c266b7..9517dfc285 100644 --- a/pcsx2/SaveState.cpp +++ b/pcsx2/SaveState.cpp @@ -354,7 +354,7 @@ static int SysState_MTGSFreeze(FreezeAction mode, freezeData* fP) return sstate.retval; } -static constexpr SysState_Component SPU2{ "SPU2", SPU2freeze }; +static constexpr SysState_Component SPU2_{ "SPU2", SPU2freeze }; static constexpr SysState_Component PAD_{ "PAD", PADfreeze }; static constexpr SysState_Component GS{ "GS", SysState_MTGSFreeze }; @@ -601,8 +601,8 @@ public: virtual ~SavestateEntry_SPU2() = default; const char* GetFilename() const { return "SPU2.bin"; } - void FreezeIn(zip_file_t* zf) const { return SysState_ComponentFreezeIn(zf, SPU2); } - void FreezeOut(SaveStateBase& writer) const { return SysState_ComponentFreezeOut(writer, SPU2); } + void FreezeIn(zip_file_t* zf) const { return SysState_ComponentFreezeIn(zf, SPU2_); } + void FreezeOut(SaveStateBase& writer) const { return SysState_ComponentFreezeOut(writer, SPU2_); } bool IsRequired() const { return true; } }; diff --git a/pcsx2/VMManager.cpp b/pcsx2/VMManager.cpp index b58d3d63c4..d471f70ec7 100644 --- a/pcsx2/VMManager.cpp +++ b/pcsx2/VMManager.cpp @@ -83,7 +83,6 @@ namespace VMManager static void CheckForGSConfigChanges(const Pcsx2Config& old_config); static void CheckForFramerateConfigChanges(const Pcsx2Config& old_config); static void CheckForPatchConfigChanges(const Pcsx2Config& old_config); - static void CheckForSPU2ConfigChanges(const Pcsx2Config& old_config); static void CheckForDEV9ConfigChanges(const Pcsx2Config& old_config); static void CheckForMemoryCardConfigChanges(const Pcsx2Config& old_config); static void EnforceAchievementsChallengeModeSettings(); @@ -204,7 +203,7 @@ void VMManager::SetState(VMState state) frameLimitReset(); } - SPU2SetOutputPaused(state == VMState::Paused); + SPU2::SetOutputPaused(state == VMState::Paused); if (state == VMState::Paused) Host::OnVMPaused(); else @@ -268,9 +267,9 @@ bool VMManager::Internal::InitializeGlobals() return false; } - if (!SPU2init()) + if (!SPU2::Initialize()) { - Host::ReportErrorAsync("Error", "Failed to initialize SPU2 (SPU2init())."); + Host::ReportErrorAsync("Error", "Failed to initialize SPU2."); return false; } @@ -286,7 +285,7 @@ bool VMManager::Internal::InitializeGlobals() void VMManager::Internal::ReleaseGlobals() { USBshutdown(); - SPU2shutdown(); + SPU2::Shutdown(); GSshutdown(); #ifdef _WIN32 @@ -972,12 +971,12 @@ bool VMManager::Initialize(VMBootParameters boot_params) }; Console.WriteLn("Opening SPU2..."); - if (!SPU2open()) + if (!SPU2::Open()) { Host::ReportErrorAsync("Startup Error", "Failed to initialize SPU2."); return false; } - ScopedGuard close_spu2(&SPU2close); + ScopedGuard close_spu2(&SPU2::Close); Console.WriteLn("Opening PAD..."); if (PADinit() != 0 || PADopen(g_host_display->GetWindowInfo()) != 0) @@ -1129,7 +1128,7 @@ void VMManager::Shutdown(bool save_resume_state) R3000A::ioman::reset(); vtlb_Shutdown(); USBclose(); - SPU2close(); + SPU2::Close(); PADclose(); DEV9close(); DoCDVDclose(); @@ -1445,6 +1444,7 @@ void VMManager::SetLimiterMode(LimiterModeType type) EmuConfig.LimiterMode = type; gsUpdateFrequency(EmuConfig); + SPU2::OnTargetSpeedChanged(); } void VMManager::FrameAdvance(u32 num_frames /*= 1*/) @@ -1700,47 +1700,6 @@ void VMManager::CheckForPatchConfigChanges(const Pcsx2Config& old_config) ReloadPatches(true, true); } -void VMManager::CheckForSPU2ConfigChanges(const Pcsx2Config& old_config) -{ - if (EmuConfig.SPU2 == old_config.SPU2) - return; - - // TODO: Don't reinit on volume changes. - - Console.WriteLn("Updating SPU2 configuration"); - - // kinda lazy, but until we move spu2 over... - freezeData fd = {}; - if (SPU2freeze(FreezeAction::Size, &fd) != 0) - { - Console.Error("(CheckForSPU2ConfigChanges) Failed to get SPU2 freeze size"); - return; - } - - std::unique_ptr fd_data = std::make_unique(fd.size); - fd.data = fd_data.get(); - if (SPU2freeze(FreezeAction::Save, &fd) != 0) - { - Console.Error("(CheckForSPU2ConfigChanges) Failed to freeze SPU2"); - return; - } - - const bool psxmode = SPU2IsRunningPSXMode(); - - SPU2close(); - if (!SPU2open(psxmode ? PS2Modes::PSX : PS2Modes::PS2)) - { - Console.Error("(CheckForSPU2ConfigChanges) Failed to reopen SPU2, we'll probably crash :("); - return; - } - - if (SPU2freeze(FreezeAction::Load, &fd) != 0) - { - Console.Error("(CheckForSPU2ConfigChanges) Failed to unfreeze SPU2"); - return; - } -} - void VMManager::CheckForDEV9ConfigChanges(const Pcsx2Config& old_config) { if (EmuConfig.DEV9 == old_config.DEV9) @@ -1808,7 +1767,7 @@ void VMManager::CheckForConfigChanges(const Pcsx2Config& old_config) CheckForCPUConfigChanges(old_config); CheckForFramerateConfigChanges(old_config); CheckForPatchConfigChanges(old_config); - CheckForSPU2ConfigChanges(old_config); + SPU2::CheckForConfigChanges(old_config); CheckForDEV9ConfigChanges(old_config); CheckForMemoryCardConfigChanges(old_config); USB::CheckForConfigChanges(old_config); diff --git a/pcsx2/pcsx2core.vcxproj b/pcsx2/pcsx2core.vcxproj index 71669e86ff..73440c124d 100644 --- a/pcsx2/pcsx2core.vcxproj +++ b/pcsx2/pcsx2core.vcxproj @@ -243,9 +243,6 @@ - - - @@ -254,7 +251,6 @@ - @@ -599,7 +595,6 @@ - diff --git a/pcsx2/pcsx2core.vcxproj.filters b/pcsx2/pcsx2core.vcxproj.filters index b419dd8130..5b13ffeaa0 100644 --- a/pcsx2/pcsx2core.vcxproj.filters +++ b/pcsx2/pcsx2core.vcxproj.filters @@ -866,9 +866,6 @@ System\Ps2\SPU2 - - System\Ps2\SPU2 - System\Ps2\SPU2 @@ -1398,15 +1395,6 @@ System\Ps2\USB\usb-pad - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - System\Ps2\SPU2 @@ -1781,9 +1769,6 @@ System\Ps2\SPU2 - - System\Ps2\SPU2 - System\Ps2\SPU2