mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2025-01-17 22:25:37 +00:00
SPU: Add audio dumping support
This commit is contained in:
parent
198a64eb5e
commit
8e20d0d4ff
|
@ -108,6 +108,9 @@ bool HostInterface::BootSystem(const SystemBootParameters& parameters)
|
||||||
if (m_paused)
|
if (m_paused)
|
||||||
OnSystemPaused(true);
|
OnSystemPaused(true);
|
||||||
|
|
||||||
|
if (m_settings.audio_dump_on_boot)
|
||||||
|
StartDumpingAudio();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -673,6 +676,8 @@ void HostInterface::CreateUserDirectorySubdirectories()
|
||||||
result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("cache").c_str(), false);
|
result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("cache").c_str(), false);
|
||||||
result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("savestates").c_str(), false);
|
result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("savestates").c_str(), false);
|
||||||
result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("memcards").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)
|
if (!result)
|
||||||
ReportError("Failed to create one or more user directories. This may cause issues at runtime.");
|
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.SetStringValue("Audio", "Backend", Settings::GetAudioBackendName(AudioBackend::Cubeb));
|
||||||
si.SetBoolValue("Audio", "Sync", true);
|
si.SetBoolValue("Audio", "Sync", true);
|
||||||
|
si.SetBoolValue("Audio", "DumpOnBoot", false);
|
||||||
|
|
||||||
si.SetStringValue("BIOS", "Path", "bios/scph1001.bin");
|
si.SetStringValue("BIOS", "Path", "bios/scph1001.bin");
|
||||||
si.SetBoolValue("BIOS", "PatchTTYEnable", false);
|
si.SetBoolValue("BIOS", "PatchTTYEnable", false);
|
||||||
|
@ -1087,3 +1093,50 @@ bool HostInterface::SaveResumeSaveState()
|
||||||
const bool global = m_system->GetRunningCode().empty();
|
const bool global = m_system->GetRunningCode().empty();
|
||||||
return SaveState(global, -1);
|
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);
|
||||||
|
}
|
|
@ -69,6 +69,15 @@ public:
|
||||||
/// be called as a result of an error.
|
/// be called as a result of an error.
|
||||||
bool SaveResumeSaveState();
|
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 ReportError(const char* message);
|
||||||
virtual void ReportMessage(const char* message);
|
virtual void ReportMessage(const char* message);
|
||||||
virtual bool ConfirmMessage(const char* message);
|
virtual bool ConfirmMessage(const char* message);
|
||||||
|
|
|
@ -40,6 +40,7 @@ void Settings::Load(SettingsInterface& si)
|
||||||
audio_backend =
|
audio_backend =
|
||||||
ParseAudioBackend(si.GetStringValue("Audio", "Backend", "Cubeb").c_str()).value_or(AudioBackend::Cubeb);
|
ParseAudioBackend(si.GetStringValue("Audio", "Backend", "Cubeb").c_str()).value_or(AudioBackend::Cubeb);
|
||||||
audio_sync_enabled = si.GetBoolValue("Audio", "Sync", true);
|
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_path = si.GetStringValue("BIOS", "Path", "bios/scph1001.bin");
|
||||||
bios_patch_tty_enable = si.GetBoolValue("BIOS", "PatchTTYEnable", true);
|
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.SetStringValue("Audio", "Backend", GetAudioBackendName(audio_backend));
|
||||||
si.SetBoolValue("Audio", "Sync", audio_sync_enabled);
|
si.SetBoolValue("Audio", "Sync", audio_sync_enabled);
|
||||||
|
si.SetBoolValue("Audio", "DumpOnBoot", audio_dump_on_boot);
|
||||||
|
|
||||||
si.SetStringValue("BIOS", "Path", bios_path.c_str());
|
si.SetStringValue("BIOS", "Path", bios_path.c_str());
|
||||||
si.SetBoolValue("BIOS", "PatchTTYEnable", bios_patch_tty_enable);
|
si.SetBoolValue("BIOS", "PatchTTYEnable", bios_patch_tty_enable);
|
||||||
|
|
|
@ -60,6 +60,7 @@ struct Settings
|
||||||
|
|
||||||
AudioBackend audio_backend = AudioBackend::Cubeb;
|
AudioBackend audio_backend = AudioBackend::Cubeb;
|
||||||
bool audio_sync_enabled = true;
|
bool audio_sync_enabled = true;
|
||||||
|
bool audio_dump_on_boot = true;
|
||||||
|
|
||||||
struct DebugSettings
|
struct DebugSettings
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "common/audio_stream.h"
|
#include "common/audio_stream.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "common/state_wrapper.h"
|
#include "common/state_wrapper.h"
|
||||||
|
#include "common/wav_writer.h"
|
||||||
#include "dma.h"
|
#include "dma.h"
|
||||||
#include "host_interface.h"
|
#include "host_interface.h"
|
||||||
#include "interrupt_controller.h"
|
#include "interrupt_controller.h"
|
||||||
|
@ -631,10 +632,11 @@ void SPU::Execute(TickCount ticks)
|
||||||
while (remaining_frames > 0)
|
while (remaining_frames > 0)
|
||||||
{
|
{
|
||||||
AudioStream* const output_stream = m_system->GetHostInterface()->GetAudioStream();
|
AudioStream* const output_stream = m_system->GetHostInterface()->GetAudioStream();
|
||||||
s16* output_frame;
|
s16* output_frame_start;
|
||||||
u32 output_frame_space;
|
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);
|
const u32 frames_in_this_batch = std::min(remaining_frames, output_frame_space);
|
||||||
for (u32 i = 0; i < frames_in_this_batch; i++)
|
for (u32 i = 0; i < frames_in_this_batch; i++)
|
||||||
{
|
{
|
||||||
|
@ -687,6 +689,9 @@ void SPU::Execute(TickCount ticks)
|
||||||
IncrementCaptureBufferPosition();
|
IncrementCaptureBufferPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_dump_writer)
|
||||||
|
m_dump_writer->WriteFrames(output_frame_start, frames_in_this_batch);
|
||||||
|
|
||||||
output_stream->EndWrite(frames_in_this_batch);
|
output_stream->EndWrite(frames_in_this_batch);
|
||||||
remaining_frames -= frames_in_this_batch;
|
remaining_frames -= frames_in_this_batch;
|
||||||
}
|
}
|
||||||
|
@ -725,6 +730,31 @@ void SPU::GeneratePendingSamples()
|
||||||
m_tick_event->InvokeEarly();
|
m_tick_event->InvokeEarly();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SPU::StartDumpingAudio(const char* filename)
|
||||||
|
{
|
||||||
|
if (m_dump_writer)
|
||||||
|
m_dump_writer.reset();
|
||||||
|
|
||||||
|
m_dump_writer = std::make_unique<Common::WAVWriter>();
|
||||||
|
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()
|
void SPU::Voice::KeyOn()
|
||||||
{
|
{
|
||||||
current_address = regs.adpcm_start_address;
|
current_address = regs.adpcm_start_address;
|
||||||
|
|
|
@ -7,6 +7,11 @@
|
||||||
|
|
||||||
class StateWrapper;
|
class StateWrapper;
|
||||||
|
|
||||||
|
namespace Common
|
||||||
|
{
|
||||||
|
class WAVWriter;
|
||||||
|
}
|
||||||
|
|
||||||
class System;
|
class System;
|
||||||
class TimingEvent;
|
class TimingEvent;
|
||||||
class DMA;
|
class DMA;
|
||||||
|
@ -42,6 +47,15 @@ public:
|
||||||
// Executes the SPU, generating any pending samples.
|
// Executes the SPU, generating any pending samples.
|
||||||
void GeneratePendingSamples();
|
void GeneratePendingSamples();
|
||||||
|
|
||||||
|
/// Returns true if currently dumping audio.
|
||||||
|
ALWAYS_INLINE bool IsDumpingAudio() const { return static_cast<bool>(m_dump_writer); }
|
||||||
|
|
||||||
|
/// Starts dumping audio to file.
|
||||||
|
bool StartDumpingAudio(const char* filename);
|
||||||
|
|
||||||
|
/// Stops dumping audio to file, if started.
|
||||||
|
bool StopDumpingAudio();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr u32 RAM_SIZE = 512 * 1024;
|
static constexpr u32 RAM_SIZE = 512 * 1024;
|
||||||
static constexpr u32 RAM_MASK = RAM_SIZE - 1;
|
static constexpr u32 RAM_MASK = RAM_SIZE - 1;
|
||||||
|
@ -284,7 +298,9 @@ private:
|
||||||
DMA* m_dma = nullptr;
|
DMA* m_dma = nullptr;
|
||||||
InterruptController* m_interrupt_controller = nullptr;
|
InterruptController* m_interrupt_controller = nullptr;
|
||||||
std::unique_ptr<TimingEvent> m_tick_event;
|
std::unique_ptr<TimingEvent> m_tick_event;
|
||||||
|
std::unique_ptr<Common::WAVWriter> m_dump_writer;
|
||||||
u32 m_tick_counter = 0;
|
u32 m_tick_counter = 0;
|
||||||
|
TickCount m_ticks_carry = 0;
|
||||||
|
|
||||||
SPUCNT m_SPUCNT = {};
|
SPUCNT m_SPUCNT = {};
|
||||||
SPUSTAT m_SPUSTAT = {};
|
SPUSTAT m_SPUSTAT = {};
|
||||||
|
@ -309,8 +325,6 @@ private:
|
||||||
u32 m_noise_mode_register = 0;
|
u32 m_noise_mode_register = 0;
|
||||||
u32 m_pitch_modulation_enable_register = 0;
|
u32 m_pitch_modulation_enable_register = 0;
|
||||||
|
|
||||||
TickCount m_ticks_carry = 0;
|
|
||||||
|
|
||||||
std::array<Voice, NUM_VOICES> m_voices{};
|
std::array<Voice, NUM_VOICES> m_voices{};
|
||||||
std::array<u8, NUM_VOICES> m_voice_key_on_off_delay{};
|
std::array<u8, NUM_VOICES> m_voice_key_on_off_delay{};
|
||||||
std::array<u8, RAM_SIZE> m_ram{};
|
std::array<u8, RAM_SIZE> m_ram{};
|
||||||
|
|
Loading…
Reference in a new issue