From 99c264947db190001c5f07d1bb4e0a1e06bc211d Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Mon, 11 Nov 2019 14:06:28 +1000 Subject: [PATCH] SPU: Implement capture buffers Fixes Crash Team Racing and lipsyncing within. --- src/core/spu.cpp | 44 +++++++++++++++++++++++++++++++++++----- src/core/spu.h | 3 +++ src/duckstation/main.cpp | 2 +- 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/core/spu.cpp b/src/core/spu.cpp index 659900ae4..cb79fa411 100644 --- a/src/core/spu.cpp +++ b/src/core/spu.cpp @@ -35,6 +35,7 @@ void SPU::Reset() m_transfer_address = 0; m_transfer_address_reg = 0; m_irq_address = 0; + m_capture_buffer_position = 0; m_main_volume_left.bits = 0; m_main_volume_right.bits = 0; m_cd_audio_volume_left = 0; @@ -75,6 +76,7 @@ bool SPU::DoState(StateWrapper& sw) sw.Do(&m_transfer_address); sw.Do(&m_transfer_address_reg); sw.Do(&m_irq_address); + sw.Do(&m_capture_buffer_position); sw.Do(&m_main_volume_left.bits); sw.Do(&m_main_volume_right.bits); sw.Do(&m_cd_audio_volume_left); @@ -575,6 +577,21 @@ void SPU::CheckRAMIRQ(u32 address) } } +void SPU::WriteToCaptureBuffer(u32 index, s16 value) +{ + const u32 ram_address = (index * CAPTURE_BUFFER_SIZE_PER_CHANNEL) | ZeroExtend16(m_capture_buffer_position); + // Log_DebugPrintf("write to capture buffer %u (0x%08X) <- 0x%04X", index, ram_address, u16(value)); + std::memcpy(&m_ram[ram_address], &value, sizeof(value)); + CheckRAMIRQ(ram_address); +} + +void SPU::IncrementCaptureBufferPosition() +{ + m_capture_buffer_position += sizeof(s16); + m_capture_buffer_position %= CAPTURE_BUFFER_SIZE_PER_CHANNEL; + m_SPUSTAT.second_half_capture_buffer = m_capture_buffer_position >= (CAPTURE_BUFFER_SIZE_PER_CHANNEL / 2); +} + void SPU::Execute(TickCount ticks) { TickCount num_samples = (ticks + m_ticks_carry) / SYSCLK_TICKS_PER_SPU_TICK; @@ -947,12 +964,22 @@ void SPU::GenerateSample() } // Mix in CD audio. - if (m_SPUCNT.cd_audio_enable && !m_cd_audio_buffer.IsEmpty()) + s16 cd_audio_left; + s16 cd_audio_right; + if (!m_cd_audio_buffer.IsEmpty()) { - const s32 left = m_cd_audio_buffer.Pop(); - const s32 right = m_cd_audio_buffer.Pop(); - left_sum += ApplyVolume(left, m_cd_audio_volume_left); - right_sum += ApplyVolume(right, m_cd_audio_volume_right); + cd_audio_left = m_cd_audio_buffer.Pop(); + cd_audio_right = m_cd_audio_buffer.Pop(); + if (m_SPUCNT.cd_audio_enable) + { + left_sum += ApplyVolume(s32(cd_audio_left), m_cd_audio_volume_left); + right_sum += ApplyVolume(s32(cd_audio_right), m_cd_audio_volume_right); + } + } + else + { + cd_audio_left = 0; + cd_audio_right = 0; } // Apply main volume before clamping. @@ -961,6 +988,13 @@ void SPU::GenerateSample() out_samples[1] = Clamp16(ApplyVolume(right_sum, m_main_volume_right.GetVolume())); m_audio_stream->WriteSamples(out_samples.data(), 1); + // Write to capture buffers. + WriteToCaptureBuffer(0, cd_audio_left); + WriteToCaptureBuffer(1, cd_audio_right); + WriteToCaptureBuffer(2, Clamp16(m_voices[1].last_amplitude)); + WriteToCaptureBuffer(3, Clamp16(m_voices[3].last_amplitude)); + IncrementCaptureBufferPosition(); + #if 0 static FILE* fp = nullptr; if (!fp) diff --git a/src/core/spu.h b/src/core/spu.h index 95a2f1bf8..bfe22b42e 100644 --- a/src/core/spu.h +++ b/src/core/spu.h @@ -53,6 +53,7 @@ private: static constexpr s16 ADSR_MIN_VOLUME = 0; static constexpr s16 ADSR_MAX_VOLUME = 0x7FFF; static constexpr u32 CD_AUDIO_SAMPLE_BUFFER_SIZE = 44100 * 2; + static constexpr u32 CAPTURE_BUFFER_SIZE_PER_CHANNEL = 0x400; enum class RAMTransferMode : u8 { @@ -269,6 +270,8 @@ private: u16 RAMTransferRead(); void RAMTransferWrite(u16 value); void CheckRAMIRQ(u32 address); + void WriteToCaptureBuffer(u32 index, s16 value); + void IncrementCaptureBufferPosition(); void ReadADPCMBlock(u16 address, ADPCMBlock* block); std::tuple SampleVoice(u32 voice_index); diff --git a/src/duckstation/main.cpp b/src/duckstation/main.cpp index 5ea805e82..ee1e25a59 100644 --- a/src/duckstation/main.cpp +++ b/src/duckstation/main.cpp @@ -83,7 +83,7 @@ int main(int argc, char* argv[]) #else g_pLog->SetConsoleOutputParams(true, nullptr, LOGLEVEL_DEBUG); // g_pLog->SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL SPU Pad DigitalController", LOGLEVEL_DEBUG); - g_pLog->SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL Pad DigitalController InterruptController SPU MDEC", LOGLEVEL_DEBUG); + g_pLog->SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL Pad DigitalController MemoryCard InterruptController SPU MDEC", LOGLEVEL_DEBUG); // g_pLog->SetFilterLevel(LOGLEVEL_TRACE); g_pLog->SetFilterLevel(LOGLEVEL_DEBUG); // g_pLog->SetFilterLevel(LOGLEVEL_DEV);