From 8e20d0d4ff47c33f970f561380009878132681e9 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sun, 15 Mar 2020 22:04:17 +1000 Subject: [PATCH] SPU: Add audio dumping support --- src/core/host_interface.cpp | 53 +++++++++++++++++++++++++++++++++++++ src/core/host_interface.h | 9 +++++++ src/core/settings.cpp | 2 ++ src/core/settings.h | 1 + src/core/spu.cpp | 34 ++++++++++++++++++++++-- src/core/spu.h | 18 +++++++++++-- 6 files changed, 113 insertions(+), 4 deletions(-) diff --git a/src/core/host_interface.cpp b/src/core/host_interface.cpp index 59fa59d40..2f17baeff 100644 --- a/src/core/host_interface.cpp +++ b/src/core/host_interface.cpp @@ -108,6 +108,9 @@ bool HostInterface::BootSystem(const SystemBootParameters& parameters) if (m_paused) OnSystemPaused(true); + if (m_settings.audio_dump_on_boot) + StartDumpingAudio(); + return true; } @@ -673,6 +676,8 @@ void HostInterface::CreateUserDirectorySubdirectories() result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("cache").c_str(), false); result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("savestates").c_str(), false); result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("memcards").c_str(), false); + result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("dump").c_str(), false); + result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("dump/audio").c_str(), false); if (!result) ReportError("Failed to create one or more user directories. This may cause issues at runtime."); @@ -862,6 +867,7 @@ void HostInterface::SetDefaultSettings(SettingsInterface& si) si.SetStringValue("Audio", "Backend", Settings::GetAudioBackendName(AudioBackend::Cubeb)); si.SetBoolValue("Audio", "Sync", true); + si.SetBoolValue("Audio", "DumpOnBoot", false); si.SetStringValue("BIOS", "Path", "bios/scph1001.bin"); si.SetBoolValue("BIOS", "PatchTTYEnable", false); @@ -1087,3 +1093,50 @@ bool HostInterface::SaveResumeSaveState() const bool global = m_system->GetRunningCode().empty(); return SaveState(global, -1); } + +bool HostInterface::IsDumpingAudio() const +{ + return m_system ? m_system->GetSPU()->IsDumpingAudio() : false; +} + +bool HostInterface::StartDumpingAudio(const char* filename) +{ + if (!m_system) + return false; + + std::string auto_filename; + if (!filename) + { + const auto& code = m_system->GetRunningCode(); + if (code.empty()) + { + auto_filename = GetUserDirectoryRelativePath("dump/audio/%s.wav", GetTimestampStringForFileName().GetCharArray()); + } + else + { + auto_filename = GetUserDirectoryRelativePath("dump/audio/%s_%s.wav", code.c_str(), + GetTimestampStringForFileName().GetCharArray()); + } + + filename = auto_filename.c_str(); + } + + if (m_system->GetSPU()->StartDumpingAudio(filename)) + { + AddFormattedOSDMessage(5.0f, "Started dumping audio to '%s'.", filename); + return true; + } + else + { + AddFormattedOSDMessage(10.0f, "Failed to start dumping audio to '%s'.", filename); + return false; + } +} + +void HostInterface::StopDumpingAudio() +{ + if (!m_system || !m_system->GetSPU()->StopDumpingAudio()) + return; + + AddOSDMessage("Stopped dumping audio.", 5.0f); +} \ No newline at end of file diff --git a/src/core/host_interface.h b/src/core/host_interface.h index 485c72aff..210e1dab8 100644 --- a/src/core/host_interface.h +++ b/src/core/host_interface.h @@ -69,6 +69,15 @@ public: /// be called as a result of an error. bool SaveResumeSaveState(); + /// Returns true if currently dumping audio. + bool IsDumpingAudio() const; + + /// Starts dumping audio to a file. If no file name is provided, one will be generated automatically. + bool StartDumpingAudio(const char* filename = nullptr); + + /// Stops dumping audio to file if it has been started. + void StopDumpingAudio(); + virtual void ReportError(const char* message); virtual void ReportMessage(const char* message); virtual bool ConfirmMessage(const char* message); diff --git a/src/core/settings.cpp b/src/core/settings.cpp index c9ca7c9c8..757e23f96 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -40,6 +40,7 @@ void Settings::Load(SettingsInterface& si) audio_backend = ParseAudioBackend(si.GetStringValue("Audio", "Backend", "Cubeb").c_str()).value_or(AudioBackend::Cubeb); audio_sync_enabled = si.GetBoolValue("Audio", "Sync", true); + audio_dump_on_boot = si.GetBoolValue("Audio", "DumpOnBoot", false); bios_path = si.GetStringValue("BIOS", "Path", "bios/scph1001.bin"); bios_patch_tty_enable = si.GetBoolValue("BIOS", "PatchTTYEnable", true); @@ -92,6 +93,7 @@ void Settings::Save(SettingsInterface& si) const si.SetStringValue("Audio", "Backend", GetAudioBackendName(audio_backend)); si.SetBoolValue("Audio", "Sync", audio_sync_enabled); + si.SetBoolValue("Audio", "DumpOnBoot", audio_dump_on_boot); si.SetStringValue("BIOS", "Path", bios_path.c_str()); si.SetBoolValue("BIOS", "PatchTTYEnable", bios_patch_tty_enable); diff --git a/src/core/settings.h b/src/core/settings.h index 0bbf0bfa0..04b6e704d 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -60,6 +60,7 @@ struct Settings AudioBackend audio_backend = AudioBackend::Cubeb; bool audio_sync_enabled = true; + bool audio_dump_on_boot = true; struct DebugSettings { diff --git a/src/core/spu.cpp b/src/core/spu.cpp index 570e1e176..463edaff8 100644 --- a/src/core/spu.cpp +++ b/src/core/spu.cpp @@ -2,6 +2,7 @@ #include "common/audio_stream.h" #include "common/log.h" #include "common/state_wrapper.h" +#include "common/wav_writer.h" #include "dma.h" #include "host_interface.h" #include "interrupt_controller.h" @@ -631,10 +632,11 @@ void SPU::Execute(TickCount ticks) while (remaining_frames > 0) { AudioStream* const output_stream = m_system->GetHostInterface()->GetAudioStream(); - s16* output_frame; + s16* output_frame_start; u32 output_frame_space; - output_stream->BeginWrite(&output_frame, &output_frame_space); + output_stream->BeginWrite(&output_frame_start, &output_frame_space); + s16* output_frame = output_frame_start; const u32 frames_in_this_batch = std::min(remaining_frames, output_frame_space); for (u32 i = 0; i < frames_in_this_batch; i++) { @@ -687,6 +689,9 @@ void SPU::Execute(TickCount ticks) IncrementCaptureBufferPosition(); } + if (m_dump_writer) + m_dump_writer->WriteFrames(output_frame_start, frames_in_this_batch); + output_stream->EndWrite(frames_in_this_batch); remaining_frames -= frames_in_this_batch; } @@ -725,6 +730,31 @@ void SPU::GeneratePendingSamples() m_tick_event->InvokeEarly(); } +bool SPU::StartDumpingAudio(const char* filename) +{ + if (m_dump_writer) + m_dump_writer.reset(); + + m_dump_writer = std::make_unique(); + if (!m_dump_writer->Open(filename, SAMPLE_RATE, 2)) + { + Log_ErrorPrintf("Failed to open '%s'", filename); + m_dump_writer.reset(); + return false; + } + + return true; +} + +bool SPU::StopDumpingAudio() +{ + if (!m_dump_writer) + return false; + + m_dump_writer.reset(); + return true; +} + void SPU::Voice::KeyOn() { current_address = regs.adpcm_start_address; diff --git a/src/core/spu.h b/src/core/spu.h index 16a267a1e..c10639a7a 100644 --- a/src/core/spu.h +++ b/src/core/spu.h @@ -7,6 +7,11 @@ class StateWrapper; +namespace Common +{ +class WAVWriter; +} + class System; class TimingEvent; class DMA; @@ -42,6 +47,15 @@ public: // Executes the SPU, generating any pending samples. void GeneratePendingSamples(); + /// Returns true if currently dumping audio. + ALWAYS_INLINE bool IsDumpingAudio() const { return static_cast(m_dump_writer); } + + /// Starts dumping audio to file. + bool StartDumpingAudio(const char* filename); + + /// Stops dumping audio to file, if started. + bool StopDumpingAudio(); + private: static constexpr u32 RAM_SIZE = 512 * 1024; static constexpr u32 RAM_MASK = RAM_SIZE - 1; @@ -284,7 +298,9 @@ private: DMA* m_dma = nullptr; InterruptController* m_interrupt_controller = nullptr; std::unique_ptr m_tick_event; + std::unique_ptr m_dump_writer; u32 m_tick_counter = 0; + TickCount m_ticks_carry = 0; SPUCNT m_SPUCNT = {}; SPUSTAT m_SPUSTAT = {}; @@ -309,8 +325,6 @@ private: u32 m_noise_mode_register = 0; u32 m_pitch_modulation_enable_register = 0; - TickCount m_ticks_carry = 0; - std::array m_voices{}; std::array m_voice_key_on_off_delay{}; std::array m_ram{};