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)
|
||||
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);
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -60,6 +60,7 @@ struct Settings
|
|||
|
||||
AudioBackend audio_backend = AudioBackend::Cubeb;
|
||||
bool audio_sync_enabled = true;
|
||||
bool audio_dump_on_boot = true;
|
||||
|
||||
struct DebugSettings
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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{};
|
||||
|
|
Loading…
Reference in New Issue