diff --git a/Core/CmdLine.cpp b/Core/CmdLine.cpp index 7d7b2d2547..44801d626a 100644 --- a/Core/CmdLine.cpp +++ b/Core/CmdLine.cpp @@ -212,6 +212,7 @@ static const CommandLineParam g_autoParams[] = { {POFF(screenshotRenderTarget), CmdParamType::Bool, "screenshot-render-target", '\0', "Take screenshots of the current render target instead of the displayed framebuffer", CmdLineMode::Headless}, {POFF(screenshotFilenameDiff), CmdParamType::String, "screenshot-diff", '\0', "Save a visual comparison image to FILE when comparing screenshots", CmdLineMode::Headless}, {POFF(screenshotSaveKeepAlpha), CmdParamType::Bool, "screenshot-keep-alpha", '\0', "Preserve the alpha channel when saving PNG screenshots (default: alpha is forced to 255)", CmdLineMode::Headless}, + {POFF(dumpAudioFilename), CmdParamType::String, "dump-audio", '\0', "Record the mixed audio output to a WAV file", CmdLineMode::Headless}, {POFF(timeoutWall), CmdParamType::Double, "timeout-wall", '\0', "Stop the run after this many real seconds", CmdLineMode::Headless}, {POFF(timeoutEmulated), CmdParamType::Double, "timeout-emulated", '\0', "Stop the run after this many emulated seconds", CmdLineMode::Headless}, // The old name for --timeout-wall, kept working because it's in a lot of scripts. diff --git a/Core/CmdLine.h b/Core/CmdLine.h index d9f2367b20..e0b7e03148 100644 --- a/Core/CmdLine.h +++ b/Core/CmdLine.h @@ -180,6 +180,7 @@ struct CommandLineOptions { std::optional screenshotFilename; std::optional screenshotFilenameSave; std::optional depthFilenameSave; + std::optional dumpAudioFilename; std::optional replayEnd; // Headless: apply the game's compatibility settings when replaying a GE frame dump. std::optional dumpCompat; diff --git a/docs/debugging.md b/docs/debugging.md index ce00f9c646..53742585fc 100644 --- a/docs/debugging.md +++ b/docs/debugging.md @@ -220,6 +220,10 @@ To smoke-test a CPU or GPU change on real games, run the old and the new binary run to run, so a different screenshot means different behaviour. God of War: Chains of Olympus isn't: its menu animation follows wall-clock time, so even the same binary lands on different frames. +For audio, `--dump-audio=FILE.wav` records the mixed output (before the game volume setting) for the whole run. +An audio glitch you can hear usually shows in the waveform: a click is a jump between neighbouring samples, and +a repeated or dropped mixer block is two identical 64-sample runs in a row. + Pass `--graphics` explicitly even when you want the default, so a copied command line doesn't depend on it. If a run goes silent with no CPU use, a host thread is blocked, and neither `--timeout-wall` nor `--timeout-emulated` will end it, as both are only checked when the emulation loop comes around. diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 76ccdc3c59..f6284ae9fa 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -58,6 +58,7 @@ #include "Core/CoreTiming.h" #include "Core/EmuThread.h" #include "Core/HLE/HLE.h" +#include "Core/HLE/__sceAudio.h" #include "Core/MIPS/MIPSTables.h" #include "Core/System.h" #include "Core/Util/PSARUnpack.h" @@ -1187,6 +1188,12 @@ int main(int argc, const char* argv[]) { if (cmdLineOptions.screenshotFilenameSave.has_value()) { SetScreenshotSavePath(Path(std::string(cmdLineOptions.screenshotFilenameSave.value()))); } + if (cmdLineOptions.dumpAudioFilename.has_value()) { + // The mixer only feeds the dump while sound is enabled. + g_Config.bEnableSound = true; + g_Config.bDumpAudio = true; + __StartLogAudio(Path(std::string(cmdLineOptions.dumpAudioFilename.value()))); + } if (cmdLineOptions.replayEnd.has_value()) { GPURecord::SetReplayDrawLimit(cmdLineOptions.replayEnd.value()); }