SPU: Add audio dumping support

This commit is contained in:
Connor McLaughlin 2020-03-15 22:04:17 +10:00
parent 198a64eb5e
commit 8e20d0d4ff
6 changed files with 113 additions and 4 deletions

View file

@ -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);
}

View file

@ -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);

View file

@ -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);

View file

@ -60,6 +60,7 @@ struct Settings
AudioBackend audio_backend = AudioBackend::Cubeb;
bool audio_sync_enabled = true;
bool audio_dump_on_boot = true;
struct DebugSettings
{

View file

@ -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<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()
{
current_address = regs.adpcm_start_address;

View file

@ -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<bool>(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<TimingEvent> m_tick_event;
std::unique_ptr<Common::WAVWriter> 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<Voice, NUM_VOICES> m_voices{};
std::array<u8, NUM_VOICES> m_voice_key_on_off_delay{};
std::array<u8, RAM_SIZE> m_ram{};