diff --git a/src/core/bus.cpp b/src/core/bus.cpp index 0b6b1caa3..48689742b 100644 --- a/src/core/bus.cpp +++ b/src/core/bus.cpp @@ -1267,22 +1267,22 @@ ALWAYS_INLINE static TickCount DoAccessSPU(u32 offset, u32& value) case MemoryAccessSize::Word: { // 32-bit reads are read as two 16-bit accesses. - const u16 lsb = g_spu.ReadRegister(offset); - const u16 msb = g_spu.ReadRegister(offset + 2); + const u16 lsb = SPU::ReadRegister(offset); + const u16 msb = SPU::ReadRegister(offset + 2); value = ZeroExtend32(lsb) | (ZeroExtend32(msb) << 16); } break; case MemoryAccessSize::HalfWord: { - value = ZeroExtend32(g_spu.ReadRegister(offset)); + value = ZeroExtend32(SPU::ReadRegister(offset)); } break; case MemoryAccessSize::Byte: default: { - const u16 value16 = g_spu.ReadRegister(FIXUP_HALFWORD_OFFSET(size, offset)); + const u16 value16 = SPU::ReadRegister(FIXUP_HALFWORD_OFFSET(size, offset)); value = FIXUP_HALFWORD_READ_VALUE(size, offset, value16); } break; @@ -1299,21 +1299,21 @@ ALWAYS_INLINE static TickCount DoAccessSPU(u32 offset, u32& value) case MemoryAccessSize::Word: { DebugAssert(Common::IsAlignedPow2(offset, 2)); - g_spu.WriteRegister(offset, Truncate16(value)); - g_spu.WriteRegister(offset + 2, Truncate16(value >> 16)); + SPU::WriteRegister(offset, Truncate16(value)); + SPU::WriteRegister(offset + 2, Truncate16(value >> 16)); break; } case MemoryAccessSize::HalfWord: { DebugAssert(Common::IsAlignedPow2(offset, 2)); - g_spu.WriteRegister(offset, Truncate16(value)); + SPU::WriteRegister(offset, Truncate16(value)); break; } case MemoryAccessSize::Byte: { - g_spu.WriteRegister(FIXUP_HALFWORD_OFFSET(size, offset), + SPU::WriteRegister(FIXUP_HALFWORD_OFFSET(size, offset), Truncate16(FIXUP_HALFWORD_READ_VALUE(size, offset, value))); break; } diff --git a/src/core/cdrom.cpp b/src/core/cdrom.cpp index 589097f83..30bedd329 100644 --- a/src/core/cdrom.cpp +++ b/src/core/cdrom.cpp @@ -641,7 +641,7 @@ void CDROM::WriteRegister(u32 offset, u8 value) { if (HasPendingDiscEvent()) m_drive_event->InvokeEarly(); - g_spu.GeneratePendingSamples(); + SPU::GeneratePendingSamples(); } m_adpcm_muted = adpcm_muted; @@ -2583,7 +2583,7 @@ void CDROM::ProcessXAADPCMSector(const u8* raw_sector, const CDImage::SubChannel if (m_muted || m_adpcm_muted || g_settings.cdrom_mute_cd_audio) return; - g_spu.GeneratePendingSamples(); + SPU::GeneratePendingSamples(); if (m_last_sector_subheader.codinginfo.IsStereo()) { @@ -2689,7 +2689,7 @@ void CDROM::ProcessCDDASector(const u8* raw_sector, const CDImage::SubChannelQ& if (m_muted || g_settings.cdrom_mute_cd_audio) return; - g_spu.GeneratePendingSamples(); + SPU::GeneratePendingSamples(); constexpr bool is_stereo = true; constexpr u32 num_samples = CDImage::RAW_SECTOR_SIZE / sizeof(s16) / (is_stereo ? 2 : 1); diff --git a/src/core/dma.cpp b/src/core/dma.cpp index df1fb95bf..05c1996c0 100644 --- a/src/core/dma.cpp +++ b/src/core/dma.cpp @@ -530,7 +530,7 @@ TickCount DMA::TransferMemoryToDevice(Channel channel, u32 address, u32 incremen break; case Channel::SPU: - g_spu.DMAWrite(src_pointer, word_count); + SPU::DMAWrite(src_pointer, word_count); break; case Channel::MDECin: @@ -591,7 +591,7 @@ TickCount DMA::TransferDeviceToMemory(Channel channel, u32 address, u32 incremen break; case Channel::SPU: - g_spu.DMARead(dest_pointer, word_count); + SPU::DMARead(dest_pointer, word_count); break; case Channel::MDECout: diff --git a/src/core/spu.cpp b/src/core/spu.cpp index 37ee040f9..0caf6d4af 100644 --- a/src/core/spu.cpp +++ b/src/core/spu.cpp @@ -12,25 +12,401 @@ #include "util/wav_writer.h" Log_SetChannel(SPU); -SPU g_spu; +// Enable to dump all voices of the SPU audio individually. +// #define SPU_DUMP_ALL_VOICES 1 -SPU::SPU() = default; +ALWAYS_INLINE static constexpr s32 Clamp16(s32 value) +{ + return (value < -0x8000) ? -0x8000 : (value > 0x7FFF) ? 0x7FFF : value; +} -SPU::~SPU() = default; +ALWAYS_INLINE static constexpr s32 ApplyVolume(s32 sample, s16 volume) +{ + return (sample * s32(volume)) >> 15; +} + +namespace SPU { +enum : u32 +{ + SPU_BASE = 0x1F801C00, + NUM_CHANNELS = 2, + NUM_VOICES = 24, + NUM_VOICE_REGISTERS = 8, + VOICE_ADDRESS_SHIFT = 3, + NUM_SAMPLES_PER_ADPCM_BLOCK = 28, + NUM_SAMPLES_FROM_LAST_ADPCM_BLOCK = 3, + SYSCLK_TICKS_PER_SPU_TICK = System::MASTER_CLOCK / SAMPLE_RATE, // 0x300 + CAPTURE_BUFFER_SIZE_PER_CHANNEL = 0x400, + MINIMUM_TICKS_BETWEEN_KEY_ON_OFF = 2, + NUM_REVERB_REGS = 32, + FIFO_SIZE_IN_HALFWORDS = 32 +}; +enum : s16 +{ + ENVELOPE_MIN_VOLUME = 0, + ENVELOPE_MAX_VOLUME = 0x7FFF +}; +enum : TickCount +{ + TRANSFER_TICKS_PER_HALFWORD = 16 +}; + +enum class RAMTransferMode : u8 +{ + Stopped = 0, + ManualWrite = 1, + DMAWrite = 2, + DMARead = 3 +}; + +union SPUCNT +{ + u16 bits; + + BitField enable; + BitField mute_n; + BitField noise_clock; + BitField reverb_master_enable; + BitField irq9_enable; + BitField ram_transfer_mode; + BitField external_audio_reverb; + BitField cd_audio_reverb; + BitField external_audio_enable; + BitField cd_audio_enable; + + BitField mode; +}; + +union SPUSTAT +{ + u16 bits; + + BitField second_half_capture_buffer; + BitField transfer_busy; + BitField dma_write_request; + BitField dma_read_request; + BitField dma_request; + BitField irq9_flag; + BitField mode; +}; + +union TransferControl +{ + u16 bits; + + BitField mode; +}; + +union ADSRRegister +{ + u32 bits; + struct + { + u16 bits_low; + u16 bits_high; + }; + + BitField sustain_level; + BitField decay_rate_shr2; + BitField attack_rate; + BitField attack_exponential; + + BitField release_rate_shr2; + BitField release_exponential; + BitField sustain_rate; + BitField sustain_direction_decrease; + BitField sustain_exponential; +}; + +union VolumeRegister +{ + u16 bits; + + BitField sweep_mode; + BitField fixed_volume_shr1; // divided by 2 + + BitField sweep_exponential; + BitField sweep_direction_decrease; + BitField sweep_phase_negative; + BitField sweep_rate; +}; + +// organized so we can replace this with a u16 array in the future +union VoiceRegisters +{ + u16 index[NUM_VOICE_REGISTERS]; + + struct + { + VolumeRegister volume_left; + VolumeRegister volume_right; + + u16 adpcm_sample_rate; // VxPitch + u16 adpcm_start_address; // multiply by 8 + + ADSRRegister adsr; + s16 adsr_volume; + + u16 adpcm_repeat_address; // multiply by 8 + }; +}; + +union VoiceCounter +{ + // promoted to u32 because of overflow + u32 bits; + + BitField interpolation_index; + BitField sample_index; +}; + +union ADPCMFlags +{ + u8 bits; + + BitField loop_end; + BitField loop_repeat; + BitField loop_start; +}; + +struct ADPCMBlock +{ + union + { + u8 bits; + + BitField shift; + BitField filter; + } shift_filter; + ADPCMFlags flags; + u8 data[NUM_SAMPLES_PER_ADPCM_BLOCK / 2]; + + // For both 4bit and 8bit ADPCM, reserved shift values 13..15 will act same as shift=9). + u8 GetShift() const + { + const u8 shift = shift_filter.shift; + return (shift > 12) ? 9 : shift; + } + + u8 GetFilter() const { return std::min(shift_filter.filter, 4); } + + u8 GetNibble(u32 index) const { return (data[index / 2] >> ((index % 2) * 4)) & 0x0F; } +}; + +struct VolumeEnvelope +{ + s32 counter; + u8 rate; + bool decreasing; + bool exponential; + + void Reset(u8 rate_, bool decreasing_, bool exponential_); + s16 Tick(s16 current_level); +}; + +struct VolumeSweep +{ + VolumeEnvelope envelope; + bool envelope_active; + s16 current_level; + + void Reset(VolumeRegister reg); + void Tick(); +}; + +enum class ADSRPhase : u8 +{ + Off = 0, + Attack = 1, + Decay = 2, + Sustain = 3, + Release = 4 +}; + +struct Voice +{ + u16 current_address; + VoiceRegisters regs; + VoiceCounter counter; + ADPCMFlags current_block_flags; + bool is_first_block; + std::array current_block_samples; + std::array adpcm_last_samples; + s32 last_volume; + + VolumeSweep left_volume; + VolumeSweep right_volume; + + VolumeEnvelope adsr_envelope; + ADSRPhase adsr_phase; + s16 adsr_target; + bool has_samples; + bool ignore_loop_address; + + bool IsOn() const { return adsr_phase != ADSRPhase::Off; } + + void KeyOn(); + void KeyOff(); + void ForceOff(); + + void DecodeBlock(const ADPCMBlock& block); + s32 Interpolate() const; + + // Switches to the specified phase, filling in target. + void UpdateADSREnvelope(); + + // Updates the ADSR volume/phase. + void TickADSR(); +}; + +struct ReverbRegisters +{ + s16 vLOUT; + s16 vROUT; + u16 mBASE; + + union + { + struct + { + u16 FB_SRC_A; + u16 FB_SRC_B; + s16 IIR_ALPHA; + s16 ACC_COEF_A; + s16 ACC_COEF_B; + s16 ACC_COEF_C; + s16 ACC_COEF_D; + s16 IIR_COEF; + s16 FB_ALPHA; + s16 FB_X; + u16 IIR_DEST_A[2]; + u16 ACC_SRC_A[2]; + u16 ACC_SRC_B[2]; + u16 IIR_SRC_A[2]; + u16 IIR_DEST_B[2]; + u16 ACC_SRC_C[2]; + u16 ACC_SRC_D[2]; + u16 IIR_SRC_B[2]; + u16 MIX_DEST_A[2]; + u16 MIX_DEST_B[2]; + s16 IN_COEF[2]; + }; + + u16 rev[NUM_REVERB_REGS]; + }; +}; + +static ADSRPhase GetNextADSRPhase(ADSRPhase phase); + +bool IsVoiceReverbEnabled(u32 i); +bool IsVoiceNoiseEnabled(u32 i); +bool IsPitchModulationEnabled(u32 i); +s16 GetVoiceNoiseLevel(); + +u16 ReadVoiceRegister(u32 offset); +void WriteVoiceRegister(u32 offset, u16 value); + +static bool IsRAMIRQTriggerable(); +static bool CheckRAMIRQ(u32 address); +static void TriggerRAMIRQ(); +static void CheckForLateRAMIRQs(); + +static void WriteToCaptureBuffer(u32 index, s16 value); +static void IncrementCaptureBufferPosition(); + +static void ReadADPCMBlock(u16 address, ADPCMBlock* block); +static std::tuple SampleVoice(u32 voice_index); + +static void UpdateNoise(); + +static u32 ReverbMemoryAddress(u32 address); +static s16 ReverbRead(u32 address, s32 offset = 0); +static void ReverbWrite(u32 address, s16 data); +static void ProcessReverb(s16 left_in, s16 right_in, s32* left_out, s32* right_out); + +static void Execute(void* param, TickCount ticks, TickCount ticks_late); +static void UpdateEventInterval(); + +static void ExecuteFIFOWriteToRAM(TickCount& ticks); +static void ExecuteFIFOReadFromRAM(TickCount& ticks); +static void ExecuteTransfer(void* param, TickCount ticks, TickCount ticks_late); +static void ManualTransferWrite(u16 value); +static void UpdateTransferEvent(); +static void UpdateDMARequest(); + +static void CreateOutputStream(); + +static std::unique_ptr s_tick_event; +static std::unique_ptr s_transfer_event; +static std::unique_ptr s_dump_writer; +static std::unique_ptr s_audio_stream; +static std::unique_ptr s_null_audio_stream; +static bool s_audio_output_muted = false; + +static TickCount s_ticks_carry = 0; +static TickCount s_cpu_ticks_per_spu_tick = 0; +static TickCount s_cpu_tick_divider = 0; + +static SPUCNT s_SPUCNT = {}; +static SPUSTAT s_SPUSTAT = {}; + +static TransferControl s_transfer_control = {}; +static u16 s_transfer_address_reg = 0; +static u32 s_transfer_address = 0; + +static u16 s_irq_address = 0; +static u16 s_capture_buffer_position = 0; + +static VolumeRegister s_main_volume_left_reg = {}; +static VolumeRegister s_main_volume_right_reg = {}; +static VolumeSweep s_main_volume_left = {}; +static VolumeSweep s_main_volume_right = {}; + +static s16 s_cd_audio_volume_left = 0; +static s16 s_cd_audio_volume_right = 0; + +static s16 s_external_volume_left = 0; +static s16 s_external_volume_right = 0; + +static u32 s_key_on_register = 0; +static u32 s_key_off_register = 0; +static u32 s_endx_register = 0; +static u32 s_pitch_modulation_enable_register = 0; + +static u32 s_noise_mode_register = 0; +static u32 s_noise_count = 0; +static u32 s_noise_level = 0; + +static u32 s_reverb_on_register = 0; +static u32 s_reverb_base_address = 0; +static u32 s_reverb_current_address = 0; +static ReverbRegisters s_reverb_registers{}; +static std::array, 2> s_reverb_downsample_buffer; +static std::array, 2> s_reverb_upsample_buffer; +static s32 s_reverb_resample_buffer_position = 0; + +static std::array s_voices{}; + +static InlineFIFOQueue s_transfer_fifo; + +static std::array s_ram{}; + +#ifdef SPU_DUMP_ALL_VOICES +// +1 for reverb output +static std::array, NUM_VOICES + 1> s_voice_dump_writers; +#endif +} // namespace SPU void SPU::Initialize() { // (X * D) / N / 768 -> (X * D) / (N * 768) - m_cpu_ticks_per_spu_tick = System::ScaleTicksToOverclock(SYSCLK_TICKS_PER_SPU_TICK); - m_cpu_tick_divider = static_cast(g_settings.cpu_overclock_numerator * SYSCLK_TICKS_PER_SPU_TICK); - m_tick_event = TimingEvents::CreateTimingEvent( - "SPU Sample", m_cpu_ticks_per_spu_tick, m_cpu_ticks_per_spu_tick, - [](void* param, TickCount ticks, TickCount ticks_late) { static_cast(param)->Execute(ticks); }, this, false); - m_transfer_event = TimingEvents::CreateTimingEvent( - "SPU Transfer", TRANSFER_TICKS_PER_HALFWORD, TRANSFER_TICKS_PER_HALFWORD, - [](void* param, TickCount ticks, TickCount ticks_late) { static_cast(param)->ExecuteTransfer(ticks); }, this, - false); - m_null_audio_stream = AudioStream::CreateNullStream(SAMPLE_RATE, NUM_CHANNELS, g_settings.audio_buffer_ms); + s_cpu_ticks_per_spu_tick = System::ScaleTicksToOverclock(SYSCLK_TICKS_PER_SPU_TICK); + s_cpu_tick_divider = static_cast(g_settings.cpu_overclock_numerator * SYSCLK_TICKS_PER_SPU_TICK); + s_tick_event = TimingEvents::CreateTimingEvent("SPU Sample", s_cpu_ticks_per_spu_tick, s_cpu_ticks_per_spu_tick, + &SPU::Execute, nullptr, false); + s_transfer_event = TimingEvents::CreateTimingEvent( + "SPU Transfer", TRANSFER_TICKS_PER_HALFWORD, TRANSFER_TICKS_PER_HALFWORD, &SPU::ExecuteTransfer, nullptr, false); + s_null_audio_stream = AudioStream::CreateNullStream(SAMPLE_RATE, NUM_CHANNELS, g_settings.audio_buffer_ms); CreateOutputStream(); Reset(); @@ -43,81 +419,81 @@ void SPU::CreateOutputStream() Settings::GetAudioBackendName(g_settings.audio_backend), SAMPLE_RATE, NUM_CHANNELS, g_settings.audio_buffer_ms, g_settings.audio_output_latency_ms, AudioStream::GetStretchModeName(g_settings.audio_stretch_mode)); - m_audio_stream = + s_audio_stream = Host::CreateAudioStream(g_settings.audio_backend, SAMPLE_RATE, NUM_CHANNELS, g_settings.audio_buffer_ms, g_settings.audio_output_latency_ms, g_settings.audio_stretch_mode); - if (!m_audio_stream) + if (!s_audio_stream) { Host::ReportErrorAsync("Error", "Failed to create or configure audio stream, falling back to null output."); - m_audio_stream.reset(); - m_audio_stream = AudioStream::CreateNullStream(SAMPLE_RATE, NUM_CHANNELS, g_settings.audio_buffer_ms); + s_audio_stream.reset(); + s_audio_stream = AudioStream::CreateNullStream(SAMPLE_RATE, NUM_CHANNELS, g_settings.audio_buffer_ms); } - m_audio_stream->SetOutputVolume(System::GetAudioOutputVolume()); - m_audio_stream->SetPaused(System::IsPaused()); + s_audio_stream->SetOutputVolume(System::GetAudioOutputVolume()); + s_audio_stream->SetPaused(System::IsPaused()); } void SPU::RecreateOutputStream() { - m_audio_stream.reset(); + s_audio_stream.reset(); CreateOutputStream(); } void SPU::CPUClockChanged() { // (X * D) / N / 768 -> (X * D) / (N * 768) - m_cpu_ticks_per_spu_tick = System::ScaleTicksToOverclock(SYSCLK_TICKS_PER_SPU_TICK); - m_cpu_tick_divider = static_cast(g_settings.cpu_overclock_numerator * SYSCLK_TICKS_PER_SPU_TICK); - m_ticks_carry = 0; + s_cpu_ticks_per_spu_tick = System::ScaleTicksToOverclock(SYSCLK_TICKS_PER_SPU_TICK); + s_cpu_tick_divider = static_cast(g_settings.cpu_overclock_numerator * SYSCLK_TICKS_PER_SPU_TICK); + s_ticks_carry = 0; UpdateEventInterval(); } void SPU::Shutdown() { - m_tick_event.reset(); - m_transfer_event.reset(); - m_dump_writer.reset(); - m_audio_stream.reset(); + s_tick_event.reset(); + s_transfer_event.reset(); + s_dump_writer.reset(); + s_audio_stream.reset(); } void SPU::Reset() { - m_ticks_carry = 0; + s_ticks_carry = 0; - m_SPUCNT.bits = 0; - m_SPUSTAT.bits = 0; - m_transfer_address = 0; - m_transfer_address_reg = 0; - m_irq_address = 0; - m_capture_buffer_position = 0; - m_main_volume_left_reg.bits = 0; - m_main_volume_right_reg.bits = 0; - m_main_volume_left = {}; - m_main_volume_right = {}; - m_cd_audio_volume_left = 0; - m_cd_audio_volume_right = 0; - m_external_volume_left = 0; - m_external_volume_right = 0; - m_key_on_register = 0; - m_key_off_register = 0; - m_endx_register = 0; - m_pitch_modulation_enable_register = 0; + s_SPUCNT.bits = 0; + s_SPUSTAT.bits = 0; + s_transfer_address = 0; + s_transfer_address_reg = 0; + s_irq_address = 0; + s_capture_buffer_position = 0; + s_main_volume_left_reg.bits = 0; + s_main_volume_right_reg.bits = 0; + s_main_volume_left = {}; + s_main_volume_right = {}; + s_cd_audio_volume_left = 0; + s_cd_audio_volume_right = 0; + s_external_volume_left = 0; + s_external_volume_right = 0; + s_key_on_register = 0; + s_key_off_register = 0; + s_endx_register = 0; + s_pitch_modulation_enable_register = 0; - m_noise_mode_register = 0; - m_noise_count = 0; - m_noise_level = 1; + s_noise_mode_register = 0; + s_noise_count = 0; + s_noise_level = 1; - m_reverb_on_register = 0; - m_reverb_registers = {}; - m_reverb_registers.mBASE = 0; - m_reverb_base_address = m_reverb_current_address = ZeroExtend32(m_reverb_registers.mBASE) << 2; - m_reverb_downsample_buffer = {}; - m_reverb_upsample_buffer = {}; - m_reverb_resample_buffer_position = 0; + s_reverb_on_register = 0; + s_reverb_registers = {}; + s_reverb_registers.mBASE = 0; + s_reverb_base_address = s_reverb_current_address = ZeroExtend32(s_reverb_registers.mBASE) << 2; + s_reverb_downsample_buffer = {}; + s_reverb_upsample_buffer = {}; + s_reverb_resample_buffer_position = 0; for (u32 i = 0; i < NUM_VOICES; i++) { - Voice& v = m_voices[i]; + Voice& v = s_voices[i]; v.current_address = 0; std::fill_n(v.regs.index, NUM_VOICE_REGISTERS, u16(0)); v.counter.bits = 0; @@ -132,52 +508,52 @@ void SPU::Reset() v.ignore_loop_address = false; } - m_transfer_fifo.Clear(); - m_transfer_event->Deactivate(); - m_ram.fill(0); + s_transfer_fifo.Clear(); + s_transfer_event->Deactivate(); + s_ram.fill(0); UpdateEventInterval(); } bool SPU::DoState(StateWrapper& sw) { - sw.Do(&m_ticks_carry); - sw.Do(&m_SPUCNT.bits); - sw.Do(&m_SPUSTAT.bits); - sw.Do(&m_transfer_control.bits); - 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_reg.bits); - sw.Do(&m_main_volume_right_reg.bits); - sw.DoPOD(&m_main_volume_left); - sw.DoPOD(&m_main_volume_right); - sw.Do(&m_cd_audio_volume_left); - sw.Do(&m_cd_audio_volume_right); - sw.Do(&m_external_volume_left); - sw.Do(&m_external_volume_right); - sw.Do(&m_key_on_register); - sw.Do(&m_key_off_register); - sw.Do(&m_endx_register); - sw.Do(&m_pitch_modulation_enable_register); - sw.Do(&m_noise_mode_register); - sw.Do(&m_noise_count); - sw.Do(&m_noise_level); - sw.Do(&m_reverb_on_register); - sw.Do(&m_reverb_base_address); - sw.Do(&m_reverb_current_address); - sw.Do(&m_reverb_registers.vLOUT); - sw.Do(&m_reverb_registers.vROUT); - sw.Do(&m_reverb_registers.mBASE); - sw.DoArray(m_reverb_registers.rev, NUM_REVERB_REGS); + sw.Do(&s_ticks_carry); + sw.Do(&s_SPUCNT.bits); + sw.Do(&s_SPUSTAT.bits); + sw.Do(&s_transfer_control.bits); + sw.Do(&s_transfer_address); + sw.Do(&s_transfer_address_reg); + sw.Do(&s_irq_address); + sw.Do(&s_capture_buffer_position); + sw.Do(&s_main_volume_left_reg.bits); + sw.Do(&s_main_volume_right_reg.bits); + sw.DoPOD(&s_main_volume_left); + sw.DoPOD(&s_main_volume_right); + sw.Do(&s_cd_audio_volume_left); + sw.Do(&s_cd_audio_volume_right); + sw.Do(&s_external_volume_left); + sw.Do(&s_external_volume_right); + sw.Do(&s_key_on_register); + sw.Do(&s_key_off_register); + sw.Do(&s_endx_register); + sw.Do(&s_pitch_modulation_enable_register); + sw.Do(&s_noise_mode_register); + sw.Do(&s_noise_count); + sw.Do(&s_noise_level); + sw.Do(&s_reverb_on_register); + sw.Do(&s_reverb_base_address); + sw.Do(&s_reverb_current_address); + sw.Do(&s_reverb_registers.vLOUT); + sw.Do(&s_reverb_registers.vROUT); + sw.Do(&s_reverb_registers.mBASE); + sw.DoArray(s_reverb_registers.rev, NUM_REVERB_REGS); for (u32 i = 0; i < 2; i++) - sw.DoArray(m_reverb_downsample_buffer.data(), m_reverb_downsample_buffer.size()); + sw.DoArray(s_reverb_downsample_buffer.data(), s_reverb_downsample_buffer.size()); for (u32 i = 0; i < 2; i++) - sw.DoArray(m_reverb_upsample_buffer.data(), m_reverb_upsample_buffer.size()); - sw.Do(&m_reverb_resample_buffer_position); + sw.DoArray(s_reverb_upsample_buffer.data(), s_reverb_upsample_buffer.size()); + sw.Do(&s_reverb_resample_buffer_position); for (u32 i = 0; i < NUM_VOICES; i++) { - Voice& v = m_voices[i]; + Voice& v = s_voices[i]; sw.Do(&v.current_address); sw.DoArray(v.regs.index, NUM_VOICE_REGISTERS); sw.Do(&v.counter.bits); @@ -196,8 +572,8 @@ bool SPU::DoState(StateWrapper& sw) sw.Do(&v.ignore_loop_address); } - sw.Do(&m_transfer_fifo); - sw.DoBytes(m_ram.data(), RAM_SIZE); + sw.Do(&s_transfer_fifo); + sw.DoBytes(s_ram.data(), RAM_SIZE); if (sw.IsReading()) { @@ -213,100 +589,100 @@ u16 SPU::ReadRegister(u32 offset) switch (offset) { case 0x1F801D80 - SPU_BASE: - return m_main_volume_left_reg.bits; + return s_main_volume_left_reg.bits; case 0x1F801D82 - SPU_BASE: - return m_main_volume_right_reg.bits; + return s_main_volume_right_reg.bits; case 0x1F801D84 - SPU_BASE: - return m_reverb_registers.vLOUT; + return s_reverb_registers.vLOUT; case 0x1F801D86 - SPU_BASE: - return m_reverb_registers.vROUT; + return s_reverb_registers.vROUT; case 0x1F801D88 - SPU_BASE: - return Truncate16(m_key_on_register); + return Truncate16(s_key_on_register); case 0x1F801D8A - SPU_BASE: - return Truncate16(m_key_on_register >> 16); + return Truncate16(s_key_on_register >> 16); case 0x1F801D8C - SPU_BASE: - return Truncate16(m_key_off_register); + return Truncate16(s_key_off_register); case 0x1F801D8E - SPU_BASE: - return Truncate16(m_key_off_register >> 16); + return Truncate16(s_key_off_register >> 16); case 0x1F801D90 - SPU_BASE: - return Truncate16(m_pitch_modulation_enable_register); + return Truncate16(s_pitch_modulation_enable_register); case 0x1F801D92 - SPU_BASE: - return Truncate16(m_pitch_modulation_enable_register >> 16); + return Truncate16(s_pitch_modulation_enable_register >> 16); case 0x1F801D94 - SPU_BASE: - return Truncate16(m_noise_mode_register); + return Truncate16(s_noise_mode_register); case 0x1F801D96 - SPU_BASE: - return Truncate16(m_noise_mode_register >> 16); + return Truncate16(s_noise_mode_register >> 16); case 0x1F801D98 - SPU_BASE: - return Truncate16(m_reverb_on_register); + return Truncate16(s_reverb_on_register); case 0x1F801D9A - SPU_BASE: - return Truncate16(m_reverb_on_register >> 16); + return Truncate16(s_reverb_on_register >> 16); case 0x1F801D9C - SPU_BASE: - return Truncate16(m_endx_register); + return Truncate16(s_endx_register); case 0x1F801D9E - SPU_BASE: - return Truncate16(m_endx_register >> 16); + return Truncate16(s_endx_register >> 16); case 0x1F801DA2 - SPU_BASE: - return m_reverb_registers.mBASE; + return s_reverb_registers.mBASE; case 0x1F801DA4 - SPU_BASE: - Log_TracePrintf("SPU IRQ address -> 0x%04X", ZeroExtend32(m_irq_address)); - return m_irq_address; + Log_TracePrintf("SPU IRQ address -> 0x%04X", ZeroExtend32(s_irq_address)); + return s_irq_address; case 0x1F801DA6 - SPU_BASE: - Log_TracePrintf("SPU transfer address register -> 0x%04X", ZeroExtend32(m_transfer_address_reg)); - return m_transfer_address_reg; + Log_TracePrintf("SPU transfer address register -> 0x%04X", ZeroExtend32(s_transfer_address_reg)); + return s_transfer_address_reg; case 0x1F801DA8 - SPU_BASE: Log_TracePrintf("SPU transfer data register read"); return UINT16_C(0xFFFF); case 0x1F801DAA - SPU_BASE: - Log_TracePrintf("SPU control register -> 0x%04X", ZeroExtend32(m_SPUCNT.bits)); - return m_SPUCNT.bits; + Log_TracePrintf("SPU control register -> 0x%04X", ZeroExtend32(s_SPUCNT.bits)); + return s_SPUCNT.bits; case 0x1F801DAC - SPU_BASE: - Log_TracePrintf("SPU transfer control register -> 0x%04X", ZeroExtend32(m_transfer_control.bits)); - return m_transfer_control.bits; + Log_TracePrintf("SPU transfer control register -> 0x%04X", ZeroExtend32(s_transfer_control.bits)); + return s_transfer_control.bits; case 0x1F801DAE - SPU_BASE: GeneratePendingSamples(); - Log_TracePrintf("SPU status register -> 0x%04X", ZeroExtend32(m_SPUCNT.bits)); - return m_SPUSTAT.bits; + Log_TracePrintf("SPU status register -> 0x%04X", ZeroExtend32(s_SPUCNT.bits)); + return s_SPUSTAT.bits; case 0x1F801DB0 - SPU_BASE: - return m_cd_audio_volume_left; + return s_cd_audio_volume_left; case 0x1F801DB2 - SPU_BASE: - return m_cd_audio_volume_right; + return s_cd_audio_volume_right; case 0x1F801DB4 - SPU_BASE: - return m_external_volume_left; + return s_external_volume_left; case 0x1F801DB6 - SPU_BASE: - return m_external_volume_right; + return s_external_volume_right; case 0x1F801DB8 - SPU_BASE: GeneratePendingSamples(); - return m_main_volume_left.current_level; + return s_main_volume_left.current_level; case 0x1F801DBA - SPU_BASE: GeneratePendingSamples(); - return m_main_volume_right.current_level; + return s_main_volume_right.current_level; default: { @@ -314,16 +690,16 @@ u16 SPU::ReadRegister(u32 offset) return ReadVoiceRegister(offset); if (offset >= (0x1F801DC0 - SPU_BASE) && offset < (0x1F801E00 - SPU_BASE)) - return m_reverb_registers.rev[(offset - (0x1F801DC0 - SPU_BASE)) / 2]; + return s_reverb_registers.rev[(offset - (0x1F801DC0 - SPU_BASE)) / 2]; if (offset >= (0x1F801E00 - SPU_BASE) && offset < (0x1F801E60 - SPU_BASE)) { const u32 voice_index = (offset - (0x1F801E00 - SPU_BASE)) / 4; GeneratePendingSamples(); if (offset & 0x02) - return m_voices[voice_index].left_volume.current_level; + return s_voices[voice_index].left_volume.current_level; else - return m_voices[voice_index].right_volume.current_level; + return s_voices[voice_index].right_volume.current_level; } Log_DevPrintf("Unknown SPU register read: offset 0x%X (address 0x%08X)", offset, offset | SPU_BASE); @@ -340,8 +716,8 @@ void SPU::WriteRegister(u32 offset, u16 value) { Log_DebugPrintf("SPU main volume left <- 0x%04X", ZeroExtend32(value)); GeneratePendingSamples(); - m_main_volume_left_reg.bits = value; - m_main_volume_left.Reset(m_main_volume_left_reg); + s_main_volume_left_reg.bits = value; + s_main_volume_left.Reset(s_main_volume_left_reg); return; } @@ -349,8 +725,8 @@ void SPU::WriteRegister(u32 offset, u16 value) { Log_DebugPrintf("SPU main volume right <- 0x%04X", ZeroExtend32(value)); GeneratePendingSamples(); - m_main_volume_right_reg.bits = value; - m_main_volume_right.Reset(m_main_volume_right_reg); + s_main_volume_right_reg.bits = value; + s_main_volume_right.Reset(s_main_volume_right_reg); return; } @@ -358,7 +734,7 @@ void SPU::WriteRegister(u32 offset, u16 value) { Log_DebugPrintf("SPU reverb output volume left <- 0x%04X", ZeroExtend32(value)); GeneratePendingSamples(); - m_reverb_registers.vLOUT = value; + s_reverb_registers.vLOUT = value; return; } @@ -366,7 +742,7 @@ void SPU::WriteRegister(u32 offset, u16 value) { Log_DebugPrintf("SPU reverb output volume right <- 0x%04X", ZeroExtend32(value)); GeneratePendingSamples(); - m_reverb_registers.vROUT = value; + s_reverb_registers.vROUT = value; return; } @@ -374,7 +750,7 @@ void SPU::WriteRegister(u32 offset, u16 value) { Log_DebugPrintf("SPU key on low <- 0x%04X", ZeroExtend32(value)); GeneratePendingSamples(); - m_key_on_register = (m_key_on_register & 0xFFFF0000) | ZeroExtend32(value); + s_key_on_register = (s_key_on_register & 0xFFFF0000) | ZeroExtend32(value); } break; @@ -382,7 +758,7 @@ void SPU::WriteRegister(u32 offset, u16 value) { Log_DebugPrintf("SPU key on high <- 0x%04X", ZeroExtend32(value)); GeneratePendingSamples(); - m_key_on_register = (m_key_on_register & 0x0000FFFF) | (ZeroExtend32(value) << 16); + s_key_on_register = (s_key_on_register & 0x0000FFFF) | (ZeroExtend32(value) << 16); } break; @@ -390,7 +766,7 @@ void SPU::WriteRegister(u32 offset, u16 value) { Log_DebugPrintf("SPU key off low <- 0x%04X", ZeroExtend32(value)); GeneratePendingSamples(); - m_key_off_register = (m_key_off_register & 0xFFFF0000) | ZeroExtend32(value); + s_key_off_register = (s_key_off_register & 0xFFFF0000) | ZeroExtend32(value); } break; @@ -398,24 +774,24 @@ void SPU::WriteRegister(u32 offset, u16 value) { Log_DebugPrintf("SPU key off high <- 0x%04X", ZeroExtend32(value)); GeneratePendingSamples(); - m_key_off_register = (m_key_off_register & 0x0000FFFF) | (ZeroExtend32(value) << 16); + s_key_off_register = (s_key_off_register & 0x0000FFFF) | (ZeroExtend32(value) << 16); } break; case 0x1F801D90 - SPU_BASE: { GeneratePendingSamples(); - m_pitch_modulation_enable_register = (m_pitch_modulation_enable_register & 0xFFFF0000) | ZeroExtend32(value); - Log_DebugPrintf("SPU pitch modulation enable register <- 0x%08X", m_pitch_modulation_enable_register); + s_pitch_modulation_enable_register = (s_pitch_modulation_enable_register & 0xFFFF0000) | ZeroExtend32(value); + Log_DebugPrintf("SPU pitch modulation enable register <- 0x%08X", s_pitch_modulation_enable_register); } break; case 0x1F801D92 - SPU_BASE: { GeneratePendingSamples(); - m_pitch_modulation_enable_register = - (m_pitch_modulation_enable_register & 0x0000FFFF) | (ZeroExtend32(value) << 16); - Log_DebugPrintf("SPU pitch modulation enable register <- 0x%08X", m_pitch_modulation_enable_register); + s_pitch_modulation_enable_register = + (s_pitch_modulation_enable_register & 0x0000FFFF) | (ZeroExtend32(value) << 16); + Log_DebugPrintf("SPU pitch modulation enable register <- 0x%08X", s_pitch_modulation_enable_register); } break; @@ -423,7 +799,7 @@ void SPU::WriteRegister(u32 offset, u16 value) { Log_DebugPrintf("SPU noise mode register <- 0x%04X", ZeroExtend32(value)); GeneratePendingSamples(); - m_noise_mode_register = (m_noise_mode_register & 0xFFFF0000) | ZeroExtend32(value); + s_noise_mode_register = (s_noise_mode_register & 0xFFFF0000) | ZeroExtend32(value); } break; @@ -431,7 +807,7 @@ void SPU::WriteRegister(u32 offset, u16 value) { Log_DebugPrintf("SPU noise mode register <- 0x%04X", ZeroExtend32(value)); GeneratePendingSamples(); - m_noise_mode_register = (m_noise_mode_register & 0x0000FFFF) | (ZeroExtend32(value) << 16); + s_noise_mode_register = (s_noise_mode_register & 0x0000FFFF) | (ZeroExtend32(value) << 16); } break; @@ -439,7 +815,7 @@ void SPU::WriteRegister(u32 offset, u16 value) { Log_DebugPrintf("SPU reverb on register <- 0x%04X", ZeroExtend32(value)); GeneratePendingSamples(); - m_reverb_on_register = (m_reverb_on_register & 0xFFFF0000) | ZeroExtend32(value); + s_reverb_on_register = (s_reverb_on_register & 0xFFFF0000) | ZeroExtend32(value); } break; @@ -447,7 +823,7 @@ void SPU::WriteRegister(u32 offset, u16 value) { Log_DebugPrintf("SPU reverb on register <- 0x%04X", ZeroExtend32(value)); GeneratePendingSamples(); - m_reverb_on_register = (m_reverb_on_register & 0x0000FFFF) | (ZeroExtend32(value) << 16); + s_reverb_on_register = (s_reverb_on_register & 0x0000FFFF) | (ZeroExtend32(value) << 16); } break; @@ -455,9 +831,9 @@ void SPU::WriteRegister(u32 offset, u16 value) { Log_DebugPrintf("SPU reverb base address < 0x%04X", ZeroExtend32(value)); GeneratePendingSamples(); - m_reverb_registers.mBASE = value; - m_reverb_base_address = ZeroExtend32(value << 2) & 0x3FFFFu; - m_reverb_current_address = m_reverb_base_address; + s_reverb_registers.mBASE = value; + s_reverb_base_address = ZeroExtend32(value << 2) & 0x3FFFFu; + s_reverb_current_address = s_reverb_base_address; } break; @@ -465,7 +841,7 @@ void SPU::WriteRegister(u32 offset, u16 value) { Log_DebugPrintf("SPU IRQ address register <- 0x%04X", ZeroExtend32(value)); GeneratePendingSamples(); - m_irq_address = value; + s_irq_address = value; if (IsRAMIRQTriggerable()) CheckForLateRAMIRQs(); @@ -476,13 +852,13 @@ void SPU::WriteRegister(u32 offset, u16 value) case 0x1F801DA6 - SPU_BASE: { Log_DebugPrintf("SPU transfer address register <- 0x%04X", ZeroExtend32(value)); - m_transfer_event->InvokeEarly(); - m_transfer_address_reg = value; - m_transfer_address = ZeroExtend32(value) * 8; - if (IsRAMIRQTriggerable() && CheckRAMIRQ(m_transfer_address)) + s_transfer_event->InvokeEarly(); + s_transfer_address_reg = value; + s_transfer_address = ZeroExtend32(value) * 8; + if (IsRAMIRQTriggerable() && CheckRAMIRQ(s_transfer_address)) { - Log_DebugPrintf("Trigger IRQ @ %08X %04X from transfer address reg set", m_transfer_address, - m_transfer_address / 8); + Log_DebugPrintf("Trigger IRQ @ %08X %04X from transfer address reg set", s_transfer_address, + s_transfer_address / 8); TriggerRAMIRQ(); } return; @@ -491,7 +867,7 @@ void SPU::WriteRegister(u32 offset, u16 value) case 0x1F801DA8 - SPU_BASE: { Log_TracePrintf("SPU transfer data register <- 0x%04X (RAM offset 0x%08X)", ZeroExtend32(value), - m_transfer_address); + s_transfer_address); ManualTransferWrite(value); return; @@ -503,42 +879,42 @@ void SPU::WriteRegister(u32 offset, u16 value) GeneratePendingSamples(); const SPUCNT new_value{value}; - if (new_value.ram_transfer_mode != m_SPUCNT.ram_transfer_mode && + if (new_value.ram_transfer_mode != s_SPUCNT.ram_transfer_mode && new_value.ram_transfer_mode == RAMTransferMode::Stopped) { // clear the fifo here? - if (!m_transfer_fifo.IsEmpty()) + if (!s_transfer_fifo.IsEmpty()) { - if (m_SPUCNT.ram_transfer_mode == RAMTransferMode::DMAWrite) + if (s_SPUCNT.ram_transfer_mode == RAMTransferMode::DMAWrite) { // I would guess on the console it would gradually write the FIFO out. Hopefully nothing relies on this // level of timing granularity if we force it all out here. - Log_WarningPrintf("Draining write SPU transfer FIFO with %u bytes left", m_transfer_fifo.GetSize()); + Log_WarningPrintf("Draining write SPU transfer FIFO with %u bytes left", s_transfer_fifo.GetSize()); TickCount ticks = std::numeric_limits::max(); ExecuteFIFOWriteToRAM(ticks); - DebugAssert(m_transfer_fifo.IsEmpty()); + DebugAssert(s_transfer_fifo.IsEmpty()); } else { - Log_DebugPrintf("Clearing read SPU transfer FIFO with %u bytes left", m_transfer_fifo.GetSize()); - m_transfer_fifo.Clear(); + Log_DebugPrintf("Clearing read SPU transfer FIFO with %u bytes left", s_transfer_fifo.GetSize()); + s_transfer_fifo.Clear(); } } } - if (!new_value.enable && m_SPUCNT.enable) + if (!new_value.enable && s_SPUCNT.enable) { // Mute all voices. // Interestingly, hardware tests found this seems to happen immediately, not on the next 44100hz cycle. for (u32 i = 0; i < NUM_VOICES; i++) - m_voices[i].ForceOff(); + s_voices[i].ForceOff(); } - m_SPUCNT.bits = new_value.bits; - m_SPUSTAT.mode = m_SPUCNT.mode.GetValue(); + s_SPUCNT.bits = new_value.bits; + s_SPUSTAT.mode = s_SPUCNT.mode.GetValue(); - if (!m_SPUCNT.irq9_enable) - m_SPUSTAT.irq9_flag = false; + if (!s_SPUCNT.irq9_enable) + s_SPUSTAT.irq9_flag = false; else if (IsRAMIRQTriggerable()) CheckForLateRAMIRQs(); @@ -551,7 +927,7 @@ void SPU::WriteRegister(u32 offset, u16 value) case 0x1F801DAC - SPU_BASE: { Log_DebugPrintf("SPU transfer control register <- 0x%04X", ZeroExtend32(value)); - m_transfer_control.bits = value; + s_transfer_control.bits = value; return; } @@ -559,7 +935,7 @@ void SPU::WriteRegister(u32 offset, u16 value) { Log_DebugPrintf("SPU left cd audio register <- 0x%04X", ZeroExtend32(value)); GeneratePendingSamples(); - m_cd_audio_volume_left = value; + s_cd_audio_volume_left = value; } break; @@ -567,7 +943,7 @@ void SPU::WriteRegister(u32 offset, u16 value) { Log_DebugPrintf("SPU right cd audio register <- 0x%04X", ZeroExtend32(value)); GeneratePendingSamples(); - m_cd_audio_volume_right = value; + s_cd_audio_volume_right = value; } break; @@ -575,7 +951,7 @@ void SPU::WriteRegister(u32 offset, u16 value) { // External volumes aren't used, so don't bother syncing. Log_DebugPrintf("SPU left external volume register <- 0x%04X", ZeroExtend32(value)); - m_external_volume_left = value; + s_external_volume_left = value; } break; @@ -583,7 +959,7 @@ void SPU::WriteRegister(u32 offset, u16 value) { // External volumes aren't used, so don't bother syncing. Log_DebugPrintf("SPU right external volume register <- 0x%04X", ZeroExtend32(value)); - m_external_volume_right = value; + s_external_volume_right = value; } break; @@ -606,7 +982,7 @@ void SPU::WriteRegister(u32 offset, u16 value) const u32 reg = (offset - (0x1F801DC0 - SPU_BASE)) / 2; Log_DebugPrintf("SPU reverb register %u <- 0x%04X", reg, value); GeneratePendingSamples(); - m_reverb_registers.rev[reg] = value; + s_reverb_registers.rev[reg] = value; return; } @@ -624,8 +1000,8 @@ u16 SPU::ReadVoiceRegister(u32 offset) Assert(voice_index < 24); // ADSR volume needs to be updated when reading. A voice might be off as well, but key on is pending. - const Voice& voice = m_voices[voice_index]; - if (reg_index >= 6 && (voice.IsOn() || m_key_on_register & (1u << voice_index))) + const Voice& voice = s_voices[voice_index]; + if (reg_index >= 6 && (voice.IsOn() || s_key_on_register & (1u << voice_index))) GeneratePendingSamples(); Log_TracePrintf("Read voice %u register %u -> 0x%02X", voice_index, reg_index, voice.regs.index[reg_index]); @@ -639,8 +1015,8 @@ void SPU::WriteVoiceRegister(u32 offset, u16 value) const u32 voice_index = (offset / 0x10); DebugAssert(voice_index < 24); - Voice& voice = m_voices[voice_index]; - if (voice.IsOn() || m_key_on_register & (1u << voice_index)) + Voice& voice = s_voices[voice_index]; + if (voice.IsOn() || s_key_on_register & (1u << voice_index)) GeneratePendingSamples(); switch (reg_index) @@ -730,18 +1106,48 @@ void SPU::WriteVoiceRegister(u32 offset, u16 value) } } +bool SPU::IsVoiceReverbEnabled(u32 i) +{ + return ConvertToBoolUnchecked((s_reverb_on_register >> i) & u32(1)); +} + +bool SPU::IsVoiceNoiseEnabled(u32 i) +{ + return ConvertToBoolUnchecked((s_noise_mode_register >> i) & u32(1)); +} + +bool SPU::IsPitchModulationEnabled(u32 i) +{ + return ((i > 0) && ConvertToBoolUnchecked((s_pitch_modulation_enable_register >> i) & u32(1))); +} + +s16 SPU::GetVoiceNoiseLevel() +{ + return static_cast(static_cast(s_noise_level)); +} + +bool SPU::IsRAMIRQTriggerable() +{ + return s_SPUCNT.irq9_enable && !s_SPUSTAT.irq9_flag; +} + +bool SPU::CheckRAMIRQ(u32 address) +{ + return ((ZeroExtend32(s_irq_address) * 8) == address); +} + void SPU::TriggerRAMIRQ() { DebugAssert(IsRAMIRQTriggerable()); - m_SPUSTAT.irq9_flag = true; + s_SPUSTAT.irq9_flag = true; g_interrupt_controller.InterruptRequest(InterruptController::IRQ::SPU); } void SPU::CheckForLateRAMIRQs() { - if (CheckRAMIRQ(m_transfer_address)) + if (CheckRAMIRQ(s_transfer_address)) { - Log_DebugPrintf("Trigger IRQ @ %08X %04X from late transfer", m_transfer_address, m_transfer_address / 8); + Log_DebugPrintf("Trigger IRQ @ %08X %04X from late transfer", s_transfer_address, s_transfer_address / 8); TriggerRAMIRQ(); return; } @@ -750,7 +1156,7 @@ void SPU::CheckForLateRAMIRQs() { // we skip voices which haven't started this block yet - because they'll check // the next time they're sampled, and the delay might be important. - const Voice& v = m_voices[i]; + const Voice& v = s_voices[i]; if (!v.has_samples) continue; @@ -766,9 +1172,9 @@ void SPU::CheckForLateRAMIRQs() void SPU::WriteToCaptureBuffer(u32 index, s16 value) { - const u32 ram_address = (index * CAPTURE_BUFFER_SIZE_PER_CHANNEL) | ZeroExtend16(m_capture_buffer_position); + const u32 ram_address = (index * CAPTURE_BUFFER_SIZE_PER_CHANNEL) | ZeroExtend16(s_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)); + std::memcpy(&s_ram[ram_address], &value, sizeof(value)); if (IsRAMIRQTriggerable() && CheckRAMIRQ(ram_address)) { Log_DebugPrintf("Trigger IRQ @ %08X %04X from capture buffer", ram_address, ram_address / 8); @@ -778,24 +1184,24 @@ void SPU::WriteToCaptureBuffer(u32 index, s16 value) 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); + s_capture_buffer_position += sizeof(s16); + s_capture_buffer_position %= CAPTURE_BUFFER_SIZE_PER_CHANNEL; + s_SPUSTAT.second_half_capture_buffer = s_capture_buffer_position >= (CAPTURE_BUFFER_SIZE_PER_CHANNEL / 2); } void ALWAYS_INLINE SPU::ExecuteFIFOReadFromRAM(TickCount& ticks) { - while (ticks > 0 && !m_transfer_fifo.IsFull()) + while (ticks > 0 && !s_transfer_fifo.IsFull()) { u16 value; - std::memcpy(&value, &m_ram[m_transfer_address], sizeof(u16)); - m_transfer_address = (m_transfer_address + sizeof(u16)) & RAM_MASK; - m_transfer_fifo.Push(value); + std::memcpy(&value, &s_ram[s_transfer_address], sizeof(u16)); + s_transfer_address = (s_transfer_address + sizeof(u16)) & RAM_MASK; + s_transfer_fifo.Push(value); ticks -= TRANSFER_TICKS_PER_HALFWORD; - if (IsRAMIRQTriggerable() && CheckRAMIRQ(m_transfer_address)) + if (IsRAMIRQTriggerable() && CheckRAMIRQ(s_transfer_address)) { - Log_DebugPrintf("Trigger IRQ @ %08X %04X from transfer read", m_transfer_address, m_transfer_address / 8); + Log_DebugPrintf("Trigger IRQ @ %08X %04X from transfer read", s_transfer_address, s_transfer_address / 8); TriggerRAMIRQ(); } } @@ -803,29 +1209,29 @@ void ALWAYS_INLINE SPU::ExecuteFIFOReadFromRAM(TickCount& ticks) void ALWAYS_INLINE SPU::ExecuteFIFOWriteToRAM(TickCount& ticks) { - while (ticks > 0 && !m_transfer_fifo.IsEmpty()) + while (ticks > 0 && !s_transfer_fifo.IsEmpty()) { - u16 value = m_transfer_fifo.Pop(); - std::memcpy(&m_ram[m_transfer_address], &value, sizeof(u16)); - m_transfer_address = (m_transfer_address + sizeof(u16)) & RAM_MASK; + u16 value = s_transfer_fifo.Pop(); + std::memcpy(&s_ram[s_transfer_address], &value, sizeof(u16)); + s_transfer_address = (s_transfer_address + sizeof(u16)) & RAM_MASK; ticks -= TRANSFER_TICKS_PER_HALFWORD; - if (IsRAMIRQTriggerable() && CheckRAMIRQ(m_transfer_address)) + if (IsRAMIRQTriggerable() && CheckRAMIRQ(s_transfer_address)) { - Log_DebugPrintf("Trigger IRQ @ %08X %04X from transfer write", m_transfer_address, m_transfer_address / 8); + Log_DebugPrintf("Trigger IRQ @ %08X %04X from transfer write", s_transfer_address, s_transfer_address / 8); TriggerRAMIRQ(); } } } -void SPU::ExecuteTransfer(TickCount ticks) +void SPU::ExecuteTransfer(void* param, TickCount ticks, TickCount ticks_late) { - const RAMTransferMode mode = m_SPUCNT.ram_transfer_mode; + const RAMTransferMode mode = s_SPUCNT.ram_transfer_mode; Assert(mode != RAMTransferMode::Stopped); if (mode == RAMTransferMode::DMARead) { - while (ticks > 0 && !m_transfer_fifo.IsFull()) + while (ticks > 0 && !s_transfer_fifo.IsFull()) { ExecuteFIFOReadFromRAM(ticks); @@ -834,22 +1240,22 @@ void SPU::ExecuteTransfer(TickCount ticks) } // we're done if we have no more data to read - if (m_transfer_fifo.IsFull()) + if (s_transfer_fifo.IsFull()) { - m_SPUSTAT.transfer_busy = false; - m_transfer_event->Deactivate(); + s_SPUSTAT.transfer_busy = false; + s_transfer_event->Deactivate(); return; } - m_SPUSTAT.transfer_busy = true; + s_SPUSTAT.transfer_busy = true; const TickCount ticks_until_complete = - TickCount(m_transfer_fifo.GetSpace() * u32(TRANSFER_TICKS_PER_HALFWORD)) + ((ticks < 0) ? -ticks : 0); - m_transfer_event->Schedule(ticks_until_complete); + TickCount(s_transfer_fifo.GetSpace() * u32(TRANSFER_TICKS_PER_HALFWORD)) + ((ticks < 0) ? -ticks : 0); + s_transfer_event->Schedule(ticks_until_complete); } else { // write the fifo to ram, request dma again when empty - while (ticks > 0 && !m_transfer_fifo.IsEmpty()) + while (ticks > 0 && !s_transfer_fifo.IsEmpty()) { ExecuteFIFOWriteToRAM(ticks); @@ -858,86 +1264,86 @@ void SPU::ExecuteTransfer(TickCount ticks) } // we're done if we have no more data to write - if (m_transfer_fifo.IsEmpty()) + if (s_transfer_fifo.IsEmpty()) { - m_SPUSTAT.transfer_busy = false; - m_transfer_event->Deactivate(); + s_SPUSTAT.transfer_busy = false; + s_transfer_event->Deactivate(); return; } - m_SPUSTAT.transfer_busy = true; + s_SPUSTAT.transfer_busy = true; const TickCount ticks_until_complete = - TickCount(m_transfer_fifo.GetSize() * u32(TRANSFER_TICKS_PER_HALFWORD)) + ((ticks < 0) ? -ticks : 0); - m_transfer_event->Schedule(ticks_until_complete); + TickCount(s_transfer_fifo.GetSize() * u32(TRANSFER_TICKS_PER_HALFWORD)) + ((ticks < 0) ? -ticks : 0); + s_transfer_event->Schedule(ticks_until_complete); } } void SPU::ManualTransferWrite(u16 value) { - if (m_transfer_fifo.IsFull()) + if (s_transfer_fifo.IsFull()) { Log_WarningPrintf("FIFO full, dropping write of 0x%04X", value); return; } - m_transfer_fifo.Push(value); + s_transfer_fifo.Push(value); UpdateTransferEvent(); } void SPU::UpdateTransferEvent() { - const RAMTransferMode mode = m_SPUCNT.ram_transfer_mode; + const RAMTransferMode mode = s_SPUCNT.ram_transfer_mode; if (mode == RAMTransferMode::Stopped) { - m_transfer_event->Deactivate(); + s_transfer_event->Deactivate(); } else if (mode == RAMTransferMode::DMARead) { // transfer event fills the fifo - if (m_transfer_fifo.IsFull()) - m_transfer_event->Deactivate(); - else if (!m_transfer_event->IsActive()) - m_transfer_event->Schedule(TickCount(m_transfer_fifo.GetSpace() * u32(TRANSFER_TICKS_PER_HALFWORD))); + if (s_transfer_fifo.IsFull()) + s_transfer_event->Deactivate(); + else if (!s_transfer_event->IsActive()) + s_transfer_event->Schedule(TickCount(s_transfer_fifo.GetSpace() * u32(TRANSFER_TICKS_PER_HALFWORD))); } else { // transfer event copies from fifo to ram - if (m_transfer_fifo.IsEmpty()) - m_transfer_event->Deactivate(); - else if (!m_transfer_event->IsActive()) - m_transfer_event->Schedule(TickCount(m_transfer_fifo.GetSize() * u32(TRANSFER_TICKS_PER_HALFWORD))); + if (s_transfer_fifo.IsEmpty()) + s_transfer_event->Deactivate(); + else if (!s_transfer_event->IsActive()) + s_transfer_event->Schedule(TickCount(s_transfer_fifo.GetSize() * u32(TRANSFER_TICKS_PER_HALFWORD))); } - m_SPUSTAT.transfer_busy = m_transfer_event->IsActive(); + s_SPUSTAT.transfer_busy = s_transfer_event->IsActive(); } void SPU::UpdateDMARequest() { - switch (m_SPUCNT.ram_transfer_mode) + switch (s_SPUCNT.ram_transfer_mode) { case RAMTransferMode::DMARead: - m_SPUSTAT.dma_read_request = m_transfer_fifo.IsFull(); - m_SPUSTAT.dma_write_request = false; - m_SPUSTAT.dma_request = m_SPUSTAT.dma_read_request; + s_SPUSTAT.dma_read_request = s_transfer_fifo.IsFull(); + s_SPUSTAT.dma_write_request = false; + s_SPUSTAT.dma_request = s_SPUSTAT.dma_read_request; break; case RAMTransferMode::DMAWrite: - m_SPUSTAT.dma_read_request = false; - m_SPUSTAT.dma_write_request = m_transfer_fifo.IsEmpty(); - m_SPUSTAT.dma_request = m_SPUSTAT.dma_write_request; + s_SPUSTAT.dma_read_request = false; + s_SPUSTAT.dma_write_request = s_transfer_fifo.IsEmpty(); + s_SPUSTAT.dma_request = s_SPUSTAT.dma_write_request; break; case RAMTransferMode::Stopped: case RAMTransferMode::ManualWrite: default: - m_SPUSTAT.dma_read_request = false; - m_SPUSTAT.dma_write_request = false; - m_SPUSTAT.dma_request = false; + s_SPUSTAT.dma_read_request = false; + s_SPUSTAT.dma_write_request = false; + s_SPUSTAT.dma_request = false; break; } // This might call us back directly. - g_dma.SetRequest(DMA::Channel::SPU, m_SPUSTAT.dma_request); + g_dma.SetRequest(DMA::Channel::SPU, s_SPUSTAT.dma_request); } void SPU::DMARead(u32* words, u32 word_count) @@ -987,13 +1393,13 @@ void SPU::DMARead(u32* words, u32 word_count) u16* halfwords = reinterpret_cast(words); u32 halfword_count = word_count * 2; - const u32 size = m_transfer_fifo.GetSize(); + const u32 size = s_transfer_fifo.GetSize(); if (word_count > size) { u16 fill_value = 0; if (size > 0) { - m_transfer_fifo.PopRange(halfwords, size); + s_transfer_fifo.PopRange(halfwords, size); fill_value = halfwords[size - 1]; } @@ -1002,7 +1408,7 @@ void SPU::DMARead(u32* words, u32 word_count) } else { - m_transfer_fifo.PopRange(halfwords, halfword_count); + s_transfer_fifo.PopRange(halfwords, halfword_count); } UpdateDMARequest(); @@ -1014,8 +1420,8 @@ void SPU::DMAWrite(const u32* words, u32 word_count) const u16* halfwords = reinterpret_cast(words); u32 halfword_count = word_count * 2; - const u32 words_to_transfer = std::min(m_transfer_fifo.GetSpace(), halfword_count); - m_transfer_fifo.PushRange(halfwords, words_to_transfer); + const u32 words_to_transfer = std::min(s_transfer_fifo.GetSpace(), halfword_count); + s_transfer_fifo.PushRange(halfwords, words_to_transfer); if (words_to_transfer != halfword_count) Log_WarningPrintf("Transfer FIFO overflow, dropping %u halfwords", halfword_count - words_to_transfer); @@ -1026,42 +1432,47 @@ void SPU::DMAWrite(const u32* words, u32 word_count) void SPU::GeneratePendingSamples() { - if (m_transfer_event->IsActive()) - m_transfer_event->InvokeEarly(); + if (s_transfer_event->IsActive()) + s_transfer_event->InvokeEarly(); - const TickCount ticks_pending = m_tick_event->GetTicksSinceLastExecution(); + const TickCount ticks_pending = s_tick_event->GetTicksSinceLastExecution(); TickCount frames_to_execute; if (g_settings.cpu_overclock_active) { frames_to_execute = static_cast((static_cast(ticks_pending) * g_settings.cpu_overclock_denominator) + - static_cast(m_ticks_carry)) / - static_cast(m_cpu_tick_divider); + static_cast(s_ticks_carry)) / + static_cast(s_cpu_tick_divider); } else { - frames_to_execute = (m_tick_event->GetTicksSinceLastExecution() + m_ticks_carry) / SYSCLK_TICKS_PER_SPU_TICK; + frames_to_execute = (s_tick_event->GetTicksSinceLastExecution() + s_ticks_carry) / SYSCLK_TICKS_PER_SPU_TICK; } const bool force_exec = (frames_to_execute > 0); - m_tick_event->InvokeEarly(force_exec); + s_tick_event->InvokeEarly(force_exec); +} + +bool SPU::IsDumpingAudio() +{ + return static_cast(s_dump_writer); } bool SPU::StartDumpingAudio(const char* filename) { - m_dump_writer.reset(); - m_dump_writer = std::make_unique(); - if (!m_dump_writer->Open(filename, SAMPLE_RATE, 2)) + s_dump_writer.reset(); + s_dump_writer = std::make_unique(); + if (!s_dump_writer->Open(filename, SAMPLE_RATE, 2)) { Log_ErrorPrintf("Failed to open '%s'", filename); - m_dump_writer.reset(); + s_dump_writer.reset(); return false; } #ifdef SPU_DUMP_ALL_VOICES - for (size_t i = 0; i < m_voice_dump_writers.size(); i++) + for (size_t i = 0; i < s_voice_dump_writers.size(); i++) { - m_voice_dump_writers[i].reset(); - m_voice_dump_writers[i] = std::make_unique(); + s_voice_dump_writers[i].reset(); + s_voice_dump_writers[i] = std::make_unique(); TinyString new_suffix; if (i == NUM_VOICES) @@ -1070,10 +1481,10 @@ bool SPU::StartDumpingAudio(const char* filename) new_suffix.Format("voice%u.wav", i); std::string voice_filename(FileSystem::ReplaceExtension(filename, new_suffix)); - if (!m_voice_dump_writers[i]->Open(voice_filename.c_str(), SAMPLE_RATE, 2)) + if (!s_voice_dump_writers[i]->Open(voice_filename.c_str(), SAMPLE_RATE, 2)) { Log_ErrorPrintf("Failed to open voice dump filename '%s'", voice_filename.c_str()); - m_voice_dump_writers[i].reset(); + s_voice_dump_writers[i].reset(); } } #endif @@ -1083,19 +1494,44 @@ bool SPU::StartDumpingAudio(const char* filename) bool SPU::StopDumpingAudio() { - if (!m_dump_writer) + if (!s_dump_writer) return false; - m_dump_writer.reset(); + s_dump_writer.reset(); #ifdef SPU_DUMP_ALL_VOICES - for (size_t i = 0; i < m_voice_dump_writers.size(); i++) - m_voice_dump_writers[i].reset(); + for (size_t i = 0; i < s_voice_dump_writers.size(); i++) + s_voice_dump_writers[i].reset(); #endif return true; } +const std::array& SPU::GetRAM() +{ + return s_ram; +} + +std::array& SPU::GetWritableRAM() +{ + return s_ram; +} + +bool SPU::IsAudioOutputMuted() +{ + return s_audio_output_muted; +} + +void SPU::SetAudioOutputMuted(bool muted) +{ + s_audio_output_muted = muted; +} + +AudioStream* SPU::GetOutputStream() +{ + return s_audio_stream.get(); +} + void SPU::Voice::KeyOn() { current_address = regs.adpcm_start_address & ~u16(1); @@ -1448,33 +1884,33 @@ void SPU::ReadADPCMBlock(u16 address, ADPCMBlock* block) // fast path - no wrap-around if ((ram_address + sizeof(ADPCMBlock)) <= RAM_SIZE) { - std::memcpy(block, &m_ram[ram_address], sizeof(ADPCMBlock)); + std::memcpy(block, &s_ram[ram_address], sizeof(ADPCMBlock)); return; } - block->shift_filter.bits = m_ram[ram_address]; + block->shift_filter.bits = s_ram[ram_address]; ram_address = (ram_address + 1) & RAM_MASK; - block->flags.bits = m_ram[ram_address]; + block->flags.bits = s_ram[ram_address]; ram_address = (ram_address + 1) & RAM_MASK; for (u32 i = 0; i < 14; i++) { - block->data[i] = m_ram[ram_address]; + block->data[i] = s_ram[ram_address]; ram_address = (ram_address + 1) & RAM_MASK; } } ALWAYS_INLINE_RELEASE std::tuple SPU::SampleVoice(u32 voice_index) { - Voice& voice = m_voices[voice_index]; - if (!voice.IsOn() && !m_SPUCNT.irq9_enable) + Voice& voice = s_voices[voice_index]; + if (!voice.IsOn() && !s_SPUCNT.irq9_enable) { voice.last_volume = 0; #ifdef SPU_DUMP_ALL_VOICES - if (m_voice_dump_writers[voice_index]) + if (s_voice_dump_writers[voice_index]) { const s16 dump_samples[2] = {0, 0}; - m_voice_dump_writers[voice_index]->WriteFrames(dump_samples, 1); + s_voice_dump_writers[voice_index]->WriteFrames(dump_samples, 1); } #endif @@ -1522,7 +1958,7 @@ ALWAYS_INLINE_RELEASE std::tuple SPU::SampleVoice(u32 voice_index) u16 step = voice.regs.adpcm_sample_rate; if (IsPitchModulationEnabled(voice_index)) { - const s32 factor = std::clamp(m_voices[voice_index - 1].last_volume, -0x8000, 0x7FFF) + 0x8000; + const s32 factor = std::clamp(s_voices[voice_index - 1].last_volume, -0x8000, 0x7FFF) + 0x8000; step = Truncate16(static_cast((SignExtend32(step) * factor) >> 15)); } step = std::min(step, 0x3FFF); @@ -1544,7 +1980,7 @@ ALWAYS_INLINE_RELEASE std::tuple SPU::SampleVoice(u32 voice_index) // handle flags if (voice.current_block_flags.loop_end) { - m_endx_register |= (u32(1) << voice_index); + s_endx_register |= (u32(1) << voice_index); voice.current_address = voice.regs.adpcm_repeat_address & ~u16(1); if (!voice.current_block_flags.loop_repeat) @@ -1566,10 +2002,10 @@ ALWAYS_INLINE_RELEASE std::tuple SPU::SampleVoice(u32 voice_index) voice.right_volume.Tick(); #ifdef SPU_DUMP_ALL_VOICES - if (m_voice_dump_writers[voice_index]) + if (s_voice_dump_writers[voice_index]) { const s16 dump_samples[2] = {static_cast(Clamp16(left)), static_cast(Clamp16(right))}; - m_voice_dump_writers[voice_index]->WriteFrames(dump_samples, 1); + s_voice_dump_writers[voice_index]->WriteFrames(dump_samples, 1); } #endif @@ -1584,33 +2020,33 @@ void SPU::UpdateNoise() 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1}}; static constexpr std::array noise_freq_add = {{0, 84, 140, 180, 210}}; - const u32 noise_clock = m_SPUCNT.noise_clock; + const u32 noise_clock = s_SPUCNT.noise_clock; const u32 level = (0x8000u >> (noise_clock >> 2)) << 16; - m_noise_count += 0x10000u + noise_freq_add[noise_clock & 3u]; - if ((m_noise_count & 0xFFFFu) >= noise_freq_add[4]) + s_noise_count += 0x10000u + noise_freq_add[noise_clock & 3u]; + if ((s_noise_count & 0xFFFFu) >= noise_freq_add[4]) { - m_noise_count += 0x10000; - m_noise_count -= noise_freq_add[noise_clock & 3u]; + s_noise_count += 0x10000; + s_noise_count -= noise_freq_add[noise_clock & 3u]; } - if (m_noise_count < level) + if (s_noise_count < level) return; - m_noise_count %= level; - m_noise_level = (m_noise_level << 1) | noise_wave_add[(m_noise_level >> 10) & 63u]; + s_noise_count %= level; + s_noise_level = (s_noise_level << 1) | noise_wave_add[(s_noise_level >> 10) & 63u]; } /************************************************************************/ /* Reverb algorithm from Mednafen-PSX */ /************************************************************************/ -u32 SPU::ReverbMemoryAddress(u32 address) const +u32 SPU::ReverbMemoryAddress(u32 address) { // Ensures address does not leave the reverb work area. static constexpr u32 MASK = (RAM_SIZE - 1) / 2; - u32 offset = m_reverb_current_address + (address & MASK); - offset += m_reverb_base_address & ((s32)(offset << 13) >> 31); + u32 offset = s_reverb_current_address + (address & MASK); + offset += s_reverb_base_address & ((s32)(offset << 13) >> 31); // We address RAM in bytes. TODO: Change this to words. return (offset & MASK) * 2u; @@ -1622,7 +2058,7 @@ s16 SPU::ReverbRead(u32 address, s32 offset) const u32 real_address = ReverbMemoryAddress((address << 2) + offset); s16 data; - std::memcpy(&data, &m_ram[real_address], sizeof(data)); + std::memcpy(&data, &s_ram[real_address], sizeof(data)); return data; } @@ -1630,7 +2066,7 @@ void SPU::ReverbWrite(u32 address, s16 data) { // TODO: This should check interrupts. const u32 real_address = ReverbMemoryAddress(address << 2); - std::memcpy(&m_ram[real_address], &data, sizeof(data)); + std::memcpy(&s_ram[real_address], &data, sizeof(data)); } // Zeroes optimized out; middle removed too(it's 16384) @@ -1704,112 +2140,112 @@ void SPU::ProcessReverb(s16 left_in, s16 right_in, s32* left_out, s32* right_out { s_last_reverb_input[0] = left_in; s_last_reverb_input[1] = right_in; - m_reverb_downsample_buffer[0][m_reverb_resample_buffer_position | 0x00] = left_in; - m_reverb_downsample_buffer[0][m_reverb_resample_buffer_position | 0x40] = left_in; - m_reverb_downsample_buffer[1][m_reverb_resample_buffer_position | 0x00] = right_in; - m_reverb_downsample_buffer[1][m_reverb_resample_buffer_position | 0x40] = right_in; + s_reverb_downsample_buffer[0][s_reverb_resample_buffer_position | 0x00] = left_in; + s_reverb_downsample_buffer[0][s_reverb_resample_buffer_position | 0x40] = left_in; + s_reverb_downsample_buffer[1][s_reverb_resample_buffer_position | 0x00] = right_in; + s_reverb_downsample_buffer[1][s_reverb_resample_buffer_position | 0x40] = right_in; s32 out[2]; - if (m_reverb_resample_buffer_position & 1u) + if (s_reverb_resample_buffer_position & 1u) { std::array downsampled; for (unsigned lr = 0; lr < 2; lr++) - downsampled[lr] = Reverb4422(&m_reverb_downsample_buffer[lr][(m_reverb_resample_buffer_position - 38) & 0x3F]); + downsampled[lr] = Reverb4422(&s_reverb_downsample_buffer[lr][(s_reverb_resample_buffer_position - 38) & 0x3F]); for (unsigned lr = 0; lr < 2; lr++) { - if (m_SPUCNT.reverb_master_enable) + if (s_SPUCNT.reverb_master_enable) { const s16 IIR_INPUT_A = - ReverbSat((((ReverbRead(m_reverb_registers.IIR_SRC_A[lr ^ 0]) * m_reverb_registers.IIR_COEF) >> 14) + - ((downsampled[lr] * m_reverb_registers.IN_COEF[lr]) >> 14)) >> + ReverbSat((((ReverbRead(s_reverb_registers.IIR_SRC_A[lr ^ 0]) * s_reverb_registers.IIR_COEF) >> 14) + + ((downsampled[lr] * s_reverb_registers.IN_COEF[lr]) >> 14)) >> 1); const s16 IIR_INPUT_B = - ReverbSat((((ReverbRead(m_reverb_registers.IIR_SRC_B[lr ^ 1]) * m_reverb_registers.IIR_COEF) >> 14) + - ((downsampled[lr] * m_reverb_registers.IN_COEF[lr]) >> 14)) >> + ReverbSat((((ReverbRead(s_reverb_registers.IIR_SRC_B[lr ^ 1]) * s_reverb_registers.IIR_COEF) >> 14) + + ((downsampled[lr] * s_reverb_registers.IN_COEF[lr]) >> 14)) >> 1); const s16 IIR_A = - ReverbSat((((IIR_INPUT_A * m_reverb_registers.IIR_ALPHA) >> 14) + - (IIASM(m_reverb_registers.IIR_ALPHA, ReverbRead(m_reverb_registers.IIR_DEST_A[lr], -1)) >> 14)) >> + ReverbSat((((IIR_INPUT_A * s_reverb_registers.IIR_ALPHA) >> 14) + + (IIASM(s_reverb_registers.IIR_ALPHA, ReverbRead(s_reverb_registers.IIR_DEST_A[lr], -1)) >> 14)) >> 1); const s16 IIR_B = - ReverbSat((((IIR_INPUT_B * m_reverb_registers.IIR_ALPHA) >> 14) + - (IIASM(m_reverb_registers.IIR_ALPHA, ReverbRead(m_reverb_registers.IIR_DEST_B[lr], -1)) >> 14)) >> + ReverbSat((((IIR_INPUT_B * s_reverb_registers.IIR_ALPHA) >> 14) + + (IIASM(s_reverb_registers.IIR_ALPHA, ReverbRead(s_reverb_registers.IIR_DEST_B[lr], -1)) >> 14)) >> 1); - ReverbWrite(m_reverb_registers.IIR_DEST_A[lr], IIR_A); - ReverbWrite(m_reverb_registers.IIR_DEST_B[lr], IIR_B); + ReverbWrite(s_reverb_registers.IIR_DEST_A[lr], IIR_A); + ReverbWrite(s_reverb_registers.IIR_DEST_B[lr], IIR_B); } - const s32 ACC = ((ReverbRead(m_reverb_registers.ACC_SRC_A[lr]) * m_reverb_registers.ACC_COEF_A) >> 14) + - ((ReverbRead(m_reverb_registers.ACC_SRC_B[lr]) * m_reverb_registers.ACC_COEF_B) >> 14) + - ((ReverbRead(m_reverb_registers.ACC_SRC_C[lr]) * m_reverb_registers.ACC_COEF_C) >> 14) + - ((ReverbRead(m_reverb_registers.ACC_SRC_D[lr]) * m_reverb_registers.ACC_COEF_D) >> 14); + const s32 ACC = ((ReverbRead(s_reverb_registers.ACC_SRC_A[lr]) * s_reverb_registers.ACC_COEF_A) >> 14) + + ((ReverbRead(s_reverb_registers.ACC_SRC_B[lr]) * s_reverb_registers.ACC_COEF_B) >> 14) + + ((ReverbRead(s_reverb_registers.ACC_SRC_C[lr]) * s_reverb_registers.ACC_COEF_C) >> 14) + + ((ReverbRead(s_reverb_registers.ACC_SRC_D[lr]) * s_reverb_registers.ACC_COEF_D) >> 14); - const s16 FB_A = ReverbRead(m_reverb_registers.MIX_DEST_A[lr] - m_reverb_registers.FB_SRC_A); - const s16 FB_B = ReverbRead(m_reverb_registers.MIX_DEST_B[lr] - m_reverb_registers.FB_SRC_B); - const s16 MDA = ReverbSat((ACC + ((FB_A * ReverbNeg(m_reverb_registers.FB_ALPHA)) >> 14)) >> 1); + const s16 FB_A = ReverbRead(s_reverb_registers.MIX_DEST_A[lr] - s_reverb_registers.FB_SRC_A); + const s16 FB_B = ReverbRead(s_reverb_registers.MIX_DEST_B[lr] - s_reverb_registers.FB_SRC_B); + const s16 MDA = ReverbSat((ACC + ((FB_A * ReverbNeg(s_reverb_registers.FB_ALPHA)) >> 14)) >> 1); const s16 MDB = ReverbSat( FB_A + - ((((MDA * m_reverb_registers.FB_ALPHA) >> 14) + ((FB_B * ReverbNeg(m_reverb_registers.FB_X)) >> 14)) >> 1)); - const s16 IVB = ReverbSat(FB_B + ((MDB * m_reverb_registers.FB_X) >> 15)); + ((((MDA * s_reverb_registers.FB_ALPHA) >> 14) + ((FB_B * ReverbNeg(s_reverb_registers.FB_X)) >> 14)) >> 1)); + const s16 IVB = ReverbSat(FB_B + ((MDB * s_reverb_registers.FB_X) >> 15)); - if (m_SPUCNT.reverb_master_enable) + if (s_SPUCNT.reverb_master_enable) { - ReverbWrite(m_reverb_registers.MIX_DEST_A[lr], MDA); - ReverbWrite(m_reverb_registers.MIX_DEST_B[lr], MDB); + ReverbWrite(s_reverb_registers.MIX_DEST_A[lr], MDA); + ReverbWrite(s_reverb_registers.MIX_DEST_B[lr], MDB); } - m_reverb_upsample_buffer[lr][(m_reverb_resample_buffer_position >> 1) | 0x20] = - m_reverb_upsample_buffer[lr][m_reverb_resample_buffer_position >> 1] = IVB; + s_reverb_upsample_buffer[lr][(s_reverb_resample_buffer_position >> 1) | 0x20] = + s_reverb_upsample_buffer[lr][s_reverb_resample_buffer_position >> 1] = IVB; } - m_reverb_current_address = (m_reverb_current_address + 1) & 0x3FFFFu; - if (m_reverb_current_address == 0) - m_reverb_current_address = m_reverb_base_address; + s_reverb_current_address = (s_reverb_current_address + 1) & 0x3FFFFu; + if (s_reverb_current_address == 0) + s_reverb_current_address = s_reverb_base_address; for (unsigned lr = 0; lr < 2; lr++) out[lr] = - Reverb2244(&m_reverb_upsample_buffer[lr][((m_reverb_resample_buffer_position >> 1) - 19) & 0x1F]); + Reverb2244(&s_reverb_upsample_buffer[lr][((s_reverb_resample_buffer_position >> 1) - 19) & 0x1F]); } else { for (unsigned lr = 0; lr < 2; lr++) - out[lr] = Reverb2244(&m_reverb_upsample_buffer[lr][((m_reverb_resample_buffer_position >> 1) - 19) & 0x1F]); + out[lr] = Reverb2244(&s_reverb_upsample_buffer[lr][((s_reverb_resample_buffer_position >> 1) - 19) & 0x1F]); } - m_reverb_resample_buffer_position = (m_reverb_resample_buffer_position + 1) & 0x3F; + s_reverb_resample_buffer_position = (s_reverb_resample_buffer_position + 1) & 0x3F; - s_last_reverb_output[0] = *left_out = ApplyVolume(out[0], m_reverb_registers.vLOUT); - s_last_reverb_output[1] = *right_out = ApplyVolume(out[1], m_reverb_registers.vROUT); + s_last_reverb_output[0] = *left_out = ApplyVolume(out[0], s_reverb_registers.vLOUT); + s_last_reverb_output[1] = *right_out = ApplyVolume(out[1], s_reverb_registers.vROUT); #ifdef SPU_DUMP_ALL_VOICES - if (m_voice_dump_writers[NUM_VOICES]) + if (s_voice_dump_writers[NUM_VOICES]) { const s16 dump_samples[2] = {static_cast(Clamp16(s_last_reverb_output[0])), static_cast(Clamp16(s_last_reverb_output[1]))}; - m_voice_dump_writers[NUM_VOICES]->WriteFrames(dump_samples, 1); + s_voice_dump_writers[NUM_VOICES]->WriteFrames(dump_samples, 1); } #endif } -void SPU::Execute(TickCount ticks) +void SPU::Execute(void* param, TickCount ticks, TickCount ticks_late) { u32 remaining_frames; if (g_settings.cpu_overclock_active) { // (X * D) / N / 768 -> (X * D) / (N * 768) - const u64 num = (static_cast(ticks) * g_settings.cpu_overclock_denominator) + static_cast(m_ticks_carry); - remaining_frames = static_cast(num / m_cpu_tick_divider); - m_ticks_carry = static_cast(num % m_cpu_tick_divider); + const u64 num = (static_cast(ticks) * g_settings.cpu_overclock_denominator) + static_cast(s_ticks_carry); + remaining_frames = static_cast(num / s_cpu_tick_divider); + s_ticks_carry = static_cast(num % s_cpu_tick_divider); } else { - remaining_frames = static_cast((ticks + m_ticks_carry) / SYSCLK_TICKS_PER_SPU_TICK); - m_ticks_carry = (ticks + m_ticks_carry) % SYSCLK_TICKS_PER_SPU_TICK; + remaining_frames = static_cast((ticks + s_ticks_carry) / SYSCLK_TICKS_PER_SPU_TICK); + s_ticks_carry = (ticks + s_ticks_carry) % SYSCLK_TICKS_PER_SPU_TICK; } - AudioStream* output_stream = m_audio_output_muted ? m_null_audio_stream.get() : m_audio_stream.get(); + AudioStream* output_stream = s_audio_output_muted ? s_null_audio_stream.get() : s_audio_stream.get(); while (remaining_frames > 0) { @@ -1826,7 +2262,7 @@ void SPU::Execute(TickCount ticks) s32 reverb_in_left = 0; s32 reverb_in_right = 0; - u32 reverb_on_register = m_reverb_on_register; + u32 reverb_on_register = s_reverb_on_register; for (u32 voice = 0; voice < NUM_VOICES; voice++) { @@ -1842,7 +2278,7 @@ void SPU::Execute(TickCount ticks) reverb_on_register >>= 1; } - if (!m_SPUCNT.mute_n) + if (!s_SPUCNT.mute_n) { left_sum = 0; right_sum = 0; @@ -1853,15 +2289,15 @@ void SPU::Execute(TickCount ticks) // Mix in CD audio. const auto [cd_audio_left, cd_audio_right] = g_cdrom.GetAudioFrame(); - if (m_SPUCNT.cd_audio_enable) + if (s_SPUCNT.cd_audio_enable) { - const s32 cd_audio_volume_left = ApplyVolume(s32(cd_audio_left), m_cd_audio_volume_left); - const s32 cd_audio_volume_right = ApplyVolume(s32(cd_audio_right), m_cd_audio_volume_right); + const s32 cd_audio_volume_left = ApplyVolume(s32(cd_audio_left), s_cd_audio_volume_left); + const s32 cd_audio_volume_right = ApplyVolume(s32(cd_audio_right), s_cd_audio_volume_right); left_sum += cd_audio_volume_left; right_sum += cd_audio_volume_right; - if (m_SPUCNT.cd_audio_reverb) + if (s_SPUCNT.cd_audio_reverb) { reverb_in_left += cd_audio_volume_left; reverb_in_right += cd_audio_volume_right; @@ -1878,45 +2314,45 @@ void SPU::Execute(TickCount ticks) right_sum += reverb_out_right; // Apply main volume after clamping. A maximum volume should not overflow here because both are 16-bit values. - *(output_frame++) = static_cast(ApplyVolume(Clamp16(left_sum), m_main_volume_left.current_level)); - *(output_frame++) = static_cast(ApplyVolume(Clamp16(right_sum), m_main_volume_right.current_level)); - m_main_volume_left.Tick(); - m_main_volume_right.Tick(); + *(output_frame++) = static_cast(ApplyVolume(Clamp16(left_sum), s_main_volume_left.current_level)); + *(output_frame++) = static_cast(ApplyVolume(Clamp16(right_sum), s_main_volume_right.current_level)); + s_main_volume_left.Tick(); + s_main_volume_right.Tick(); // Write to capture buffers. WriteToCaptureBuffer(0, cd_audio_left); WriteToCaptureBuffer(1, cd_audio_right); - WriteToCaptureBuffer(2, static_cast(Clamp16(m_voices[1].last_volume))); - WriteToCaptureBuffer(3, static_cast(Clamp16(m_voices[3].last_volume))); + WriteToCaptureBuffer(2, static_cast(Clamp16(s_voices[1].last_volume))); + WriteToCaptureBuffer(3, static_cast(Clamp16(s_voices[3].last_volume))); IncrementCaptureBufferPosition(); // Key off/on voices after the first frame. - if (i == 0 && (m_key_off_register != 0 || m_key_on_register != 0)) + if (i == 0 && (s_key_off_register != 0 || s_key_on_register != 0)) { - u32 key_off_register = m_key_off_register; - m_key_off_register = 0; + u32 key_off_register = s_key_off_register; + s_key_off_register = 0; - u32 key_on_register = m_key_on_register; - m_key_on_register = 0; + u32 key_on_register = s_key_on_register; + s_key_on_register = 0; for (u32 voice = 0; voice < NUM_VOICES; voice++) { if (key_off_register & 1u) - m_voices[voice].KeyOff(); + s_voices[voice].KeyOff(); key_off_register >>= 1; if (key_on_register & 1u) { - m_endx_register &= ~(1u << voice); - m_voices[voice].KeyOn(); + s_endx_register &= ~(1u << voice); + s_voices[voice].KeyOn(); } key_on_register >>= 1; } } } - if (m_dump_writer) - m_dump_writer->WriteFrames(output_frame_start, frames_in_this_batch); + if (s_dump_writer) + s_dump_writer->WriteFrames(output_frame_start, frames_in_this_batch); output_stream->EndWrite(frames_in_this_batch); remaining_frames -= frames_in_this_batch; @@ -1928,23 +2364,23 @@ void SPU::UpdateEventInterval() // Don't generate more than the audio buffer since in a single slice, otherwise we'll both overflow the buffers when // we do write it, and the audio thread will underflow since it won't have enough data it the game isn't messing with // the SPU state. - const u32 max_slice_frames = m_audio_stream->GetBufferSize(); + const u32 max_slice_frames = s_audio_stream->GetBufferSize(); // TODO: Make this predict how long until the interrupt will be hit instead... - const u32 interval = (m_SPUCNT.enable && m_SPUCNT.irq9_enable) ? 1 : max_slice_frames; - const TickCount interval_ticks = static_cast(interval) * m_cpu_ticks_per_spu_tick; - if (m_tick_event->IsActive() && m_tick_event->GetInterval() == interval_ticks) + const u32 interval = (s_SPUCNT.enable && s_SPUCNT.irq9_enable) ? 1 : max_slice_frames; + const TickCount interval_ticks = static_cast(interval) * s_cpu_ticks_per_spu_tick; + if (s_tick_event->IsActive() && s_tick_event->GetInterval() == interval_ticks) return; // Ensure all pending ticks have been executed, since we won't get them back after rescheduling. - m_tick_event->InvokeEarly(true); - m_tick_event->SetInterval(interval_ticks); + s_tick_event->InvokeEarly(true); + s_tick_event->SetInterval(interval_ticks); TickCount downcount = interval_ticks; if (!g_settings.cpu_overclock_active) - downcount -= m_ticks_carry; + downcount -= s_ticks_carry; - m_tick_event->Schedule(downcount); + s_tick_event->Schedule(downcount); } void SPU::DrawDebugStateWindow() @@ -1971,56 +2407,56 @@ void SPU::DrawDebugStateWindow() ImGui::Text("Control: "); ImGui::SameLine(offsets[0]); - ImGui::TextColored(m_SPUCNT.enable ? active_color : inactive_color, "SPU Enable"); + ImGui::TextColored(s_SPUCNT.enable ? active_color : inactive_color, "SPU Enable"); ImGui::SameLine(offsets[1]); - ImGui::TextColored(m_SPUCNT.mute_n ? inactive_color : active_color, "Mute SPU"); + ImGui::TextColored(s_SPUCNT.mute_n ? inactive_color : active_color, "Mute SPU"); ImGui::SameLine(offsets[2]); - ImGui::TextColored(m_SPUCNT.external_audio_enable ? active_color : inactive_color, "External Audio"); + ImGui::TextColored(s_SPUCNT.external_audio_enable ? active_color : inactive_color, "External Audio"); ImGui::SameLine(offsets[3]); - ImGui::TextColored(m_SPUCNT.ram_transfer_mode != RAMTransferMode::Stopped ? active_color : inactive_color, "%s", - transfer_modes[static_cast(m_SPUCNT.ram_transfer_mode.GetValue())]); + ImGui::TextColored(s_SPUCNT.ram_transfer_mode != RAMTransferMode::Stopped ? active_color : inactive_color, "%s", + transfer_modes[static_cast(s_SPUCNT.ram_transfer_mode.GetValue())]); ImGui::Text("Status: "); ImGui::SameLine(offsets[0]); - ImGui::TextColored(m_SPUSTAT.irq9_flag ? active_color : inactive_color, "IRQ9"); + ImGui::TextColored(s_SPUSTAT.irq9_flag ? active_color : inactive_color, "IRQ9"); ImGui::SameLine(offsets[1]); - ImGui::TextColored(m_SPUSTAT.dma_request ? active_color : inactive_color, "DMA Request"); + ImGui::TextColored(s_SPUSTAT.dma_request ? active_color : inactive_color, "DMA Request"); ImGui::SameLine(offsets[2]); - ImGui::TextColored(m_SPUSTAT.dma_read_request ? active_color : inactive_color, "DMA Read"); + ImGui::TextColored(s_SPUSTAT.dma_read_request ? active_color : inactive_color, "DMA Read"); ImGui::SameLine(offsets[3]); - ImGui::TextColored(m_SPUSTAT.dma_write_request ? active_color : inactive_color, "DMA Write"); + ImGui::TextColored(s_SPUSTAT.dma_write_request ? active_color : inactive_color, "DMA Write"); ImGui::SameLine(offsets[4]); - ImGui::TextColored(m_SPUSTAT.transfer_busy ? active_color : inactive_color, "Transfer Busy"); + ImGui::TextColored(s_SPUSTAT.transfer_busy ? active_color : inactive_color, "Transfer Busy"); ImGui::SameLine(offsets[5]); - ImGui::TextColored(m_SPUSTAT.second_half_capture_buffer ? active_color : inactive_color, "Second Capture Buffer"); + ImGui::TextColored(s_SPUSTAT.second_half_capture_buffer ? active_color : inactive_color, "Second Capture Buffer"); ImGui::Text("Interrupt: "); ImGui::SameLine(offsets[0]); - ImGui::TextColored(m_SPUCNT.irq9_enable ? active_color : inactive_color, - m_SPUCNT.irq9_enable ? "Enabled @ 0x%04X (actual 0x%08X)" : "Disabled @ 0x%04X (actual 0x%08X)", - m_irq_address, (ZeroExtend32(m_irq_address) * 8) & RAM_MASK); + ImGui::TextColored(s_SPUCNT.irq9_enable ? active_color : inactive_color, + s_SPUCNT.irq9_enable ? "Enabled @ 0x%04X (actual 0x%08X)" : "Disabled @ 0x%04X (actual 0x%08X)", + s_irq_address, (ZeroExtend32(s_irq_address) * 8) & RAM_MASK); ImGui::Text("Volume: "); ImGui::SameLine(offsets[0]); - ImGui::Text("Left: %d%%", ApplyVolume(100, m_main_volume_left.current_level)); + ImGui::Text("Left: %d%%", ApplyVolume(100, s_main_volume_left.current_level)); ImGui::SameLine(offsets[1]); - ImGui::Text("Right: %d%%", ApplyVolume(100, m_main_volume_right.current_level)); + ImGui::Text("Right: %d%%", ApplyVolume(100, s_main_volume_right.current_level)); ImGui::Text("CD Audio: "); ImGui::SameLine(offsets[0]); - ImGui::TextColored(m_SPUCNT.cd_audio_enable ? active_color : inactive_color, - m_SPUCNT.cd_audio_enable ? "Enabled" : "Disabled"); + ImGui::TextColored(s_SPUCNT.cd_audio_enable ? active_color : inactive_color, + s_SPUCNT.cd_audio_enable ? "Enabled" : "Disabled"); ImGui::SameLine(offsets[1]); - ImGui::TextColored(m_SPUCNT.cd_audio_enable ? active_color : inactive_color, "Left Volume: %d%%", - ApplyVolume(100, m_cd_audio_volume_left)); + ImGui::TextColored(s_SPUCNT.cd_audio_enable ? active_color : inactive_color, "Left Volume: %d%%", + ApplyVolume(100, s_cd_audio_volume_left)); ImGui::SameLine(offsets[3]); - ImGui::TextColored(m_SPUCNT.cd_audio_enable ? active_color : inactive_color, "Right Volume: %d%%", - ApplyVolume(100, m_cd_audio_volume_left)); + ImGui::TextColored(s_SPUCNT.cd_audio_enable ? active_color : inactive_color, "Right Volume: %d%%", + ApplyVolume(100, s_cd_audio_volume_left)); ImGui::Text("Transfer FIFO: "); ImGui::SameLine(offsets[0]); - ImGui::TextColored(m_transfer_event->IsActive() ? active_color : inactive_color, "%u halfwords (%u bytes)", - m_transfer_fifo.GetSize(), m_transfer_fifo.GetSize() * 2); + ImGui::TextColored(s_transfer_event->IsActive() ? active_color : inactive_color, "%u halfwords (%u bytes)", + s_transfer_fifo.GetSize(), s_transfer_fifo.GetSize() * 2); } // draw voice states @@ -2044,7 +2480,7 @@ void SPU::DrawDebugStateWindow() // states for (u32 voice_index = 0; voice_index < NUM_VOICES; voice_index++) { - const Voice& v = m_voices[voice_index]; + const Voice& v = s_voices[voice_index]; ImVec4 color = v.IsOn() ? ImVec4(1.0f, 1.0f, 1.0f, 1.0f) : ImVec4(0.5f, 0.5f, 0.5f, 1.0f); ImGui::TextColored(color, "%u", ZeroExtend32(voice_index)); ImGui::NextColumn(); @@ -2080,8 +2516,8 @@ void SPU::DrawDebugStateWindow() if (ImGui::CollapsingHeader("Reverb", ImGuiTreeNodeFlags_DefaultOpen)) { - ImGui::TextColored(m_SPUCNT.reverb_master_enable ? active_color : inactive_color, "Master Enable: %s", - m_SPUCNT.reverb_master_enable ? "Yes" : "No"); + ImGui::TextColored(s_SPUCNT.reverb_master_enable ? active_color : inactive_color, "Master Enable: %s", + s_SPUCNT.reverb_master_enable ? "Yes" : "No"); ImGui::Text("Voices Enabled: "); for (u32 i = 0; i < NUM_VOICES; i++) @@ -2092,18 +2528,18 @@ void SPU::DrawDebugStateWindow() ImGui::TextColored(active ? active_color : inactive_color, "%u", i); } - ImGui::TextColored(m_SPUCNT.cd_audio_reverb ? active_color : inactive_color, "CD Audio Enable: %s", - m_SPUCNT.cd_audio_reverb ? "Yes" : "No"); + ImGui::TextColored(s_SPUCNT.cd_audio_reverb ? active_color : inactive_color, "CD Audio Enable: %s", + s_SPUCNT.cd_audio_reverb ? "Yes" : "No"); - ImGui::TextColored(m_SPUCNT.external_audio_reverb ? active_color : inactive_color, "External Audio Enable: %s", - m_SPUCNT.external_audio_reverb ? "Yes" : "No"); + ImGui::TextColored(s_SPUCNT.external_audio_reverb ? active_color : inactive_color, "External Audio Enable: %s", + s_SPUCNT.external_audio_reverb ? "Yes" : "No"); - ImGui::Text("Base Address: 0x%08X (%04X)", m_reverb_base_address, m_reverb_registers.mBASE); - ImGui::Text("Current Address: 0x%08X", m_reverb_current_address); + ImGui::Text("Base Address: 0x%08X (%04X)", s_reverb_base_address, s_reverb_registers.mBASE); + ImGui::Text("Current Address: 0x%08X", s_reverb_current_address); ImGui::Text("Current Amplitude: Input (%d, %d) Output (%d, %d)", s_last_reverb_input[0], s_last_reverb_input[1], s_last_reverb_output[0], s_last_reverb_output[1]); - ImGui::Text("Output Volume: Left %d%% Right %d%%", ApplyVolume(100, m_reverb_registers.vLOUT), - ApplyVolume(100, m_reverb_registers.vROUT)); + ImGui::Text("Output Volume: Left %d%% Right %d%%", ApplyVolume(100, s_reverb_registers.vLOUT), + ApplyVolume(100, s_reverb_registers.vROUT)); ImGui::Text("Pitch Modulation: "); for (u32 i = 1; i < NUM_VOICES; i++) @@ -2121,9 +2557,9 @@ void SPU::DrawDebugStateWindow() { for (u32 i = 0; i < NUM_VOICES; i++) { - m_voices[i].KeyOff(); - m_voices[i].adsr_envelope.counter = 0; - m_voices[i].regs.adsr_volume = 0; + s_voices[i].KeyOff(); + s_voices[i].adsr_envelope.counter = 0; + s_voices[i].regs.adsr_volume = 0; } } } diff --git a/src/core/spu.h b/src/core/spu.h index 5e8c8e5f4..65d76c8c3 100644 --- a/src/core/spu.h +++ b/src/core/spu.h @@ -6,445 +6,56 @@ #include #include -// Enable to dump all voices of the SPU audio individually. -// #define SPU_DUMP_ALL_VOICES 1 - class StateWrapper; class AudioStream; -namespace Common { -class WAVWriter; -} +namespace SPU { -class TimingEvent; - -class SPU +enum : u32 { -public: - enum : u32 - { - RAM_SIZE = 512 * 1024, - RAM_MASK = RAM_SIZE - 1, - SAMPLE_RATE = 44100, - }; - - SPU(); - ~SPU(); - - void Initialize(); - void CPUClockChanged(); - void Shutdown(); - void Reset(); - bool DoState(StateWrapper& sw); - - u16 ReadRegister(u32 offset); - void WriteRegister(u32 offset, u16 value); - - void DMARead(u32* words, u32 word_count); - void DMAWrite(const u32* words, u32 word_count); - - // Render statistics debug window. - void DrawDebugStateWindow(); - - // 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(); - - /// Access to SPU RAM. - const std::array& GetRAM() const { return m_ram; } - std::array& GetRAM() { return m_ram; } - - /// Change output stream - used for runahead. - // TODO: Make it use system "running ahead" flag - ALWAYS_INLINE bool IsAudioOutputMuted() const { return m_audio_output_muted; } - void SetAudioOutputMuted(bool muted) { m_audio_output_muted = muted; } - - ALWAYS_INLINE AudioStream* GetOutputStream() const { return m_audio_stream.get(); } - void RecreateOutputStream(); - -private: - static constexpr u32 SPU_BASE = 0x1F801C00; - static constexpr u32 NUM_CHANNELS = 2; - static constexpr u32 NUM_VOICES = 24; - static constexpr u32 NUM_VOICE_REGISTERS = 8; - static constexpr u32 VOICE_ADDRESS_SHIFT = 3; - static constexpr u32 NUM_SAMPLES_PER_ADPCM_BLOCK = 28; - static constexpr u32 NUM_SAMPLES_FROM_LAST_ADPCM_BLOCK = 3; - static constexpr u32 SYSCLK_TICKS_PER_SPU_TICK = System::MASTER_CLOCK / SAMPLE_RATE; // 0x300 - static constexpr s16 ENVELOPE_MIN_VOLUME = 0; - static constexpr s16 ENVELOPE_MAX_VOLUME = 0x7FFF; - static constexpr u32 CAPTURE_BUFFER_SIZE_PER_CHANNEL = 0x400; - static constexpr u32 MINIMUM_TICKS_BETWEEN_KEY_ON_OFF = 2; - static constexpr u32 NUM_REVERB_REGS = 32; - static constexpr u32 FIFO_SIZE_IN_HALFWORDS = 32; - static constexpr TickCount TRANSFER_TICKS_PER_HALFWORD = 16; - - enum class RAMTransferMode : u8 - { - Stopped = 0, - ManualWrite = 1, - DMAWrite = 2, - DMARead = 3 - }; - - union SPUCNT - { - u16 bits; - - BitField enable; - BitField mute_n; - BitField noise_clock; - BitField reverb_master_enable; - BitField irq9_enable; - BitField ram_transfer_mode; - BitField external_audio_reverb; - BitField cd_audio_reverb; - BitField external_audio_enable; - BitField cd_audio_enable; - - BitField mode; - }; - - union SPUSTAT - { - u16 bits; - - BitField second_half_capture_buffer; - BitField transfer_busy; - BitField dma_write_request; - BitField dma_read_request; - BitField dma_request; - BitField irq9_flag; - BitField mode; - }; - - union TransferControl - { - u16 bits; - - BitField mode; - }; - - union ADSRRegister - { - u32 bits; - struct - { - u16 bits_low; - u16 bits_high; - }; - - BitField sustain_level; - BitField decay_rate_shr2; - BitField attack_rate; - BitField attack_exponential; - - BitField release_rate_shr2; - BitField release_exponential; - BitField sustain_rate; - BitField sustain_direction_decrease; - BitField sustain_exponential; - }; - - union VolumeRegister - { - u16 bits; - - BitField sweep_mode; - BitField fixed_volume_shr1; // divided by 2 - - BitField sweep_exponential; - BitField sweep_direction_decrease; - BitField sweep_phase_negative; - BitField sweep_rate; - }; - - // organized so we can replace this with a u16 array in the future - union VoiceRegisters - { - u16 index[NUM_VOICE_REGISTERS]; - - struct - { - VolumeRegister volume_left; - VolumeRegister volume_right; - - u16 adpcm_sample_rate; // VxPitch - u16 adpcm_start_address; // multiply by 8 - - ADSRRegister adsr; - s16 adsr_volume; - - u16 adpcm_repeat_address; // multiply by 8 - }; - }; - - union VoiceCounter - { - // promoted to u32 because of overflow - u32 bits; - - BitField interpolation_index; - BitField sample_index; - }; - - union ADPCMFlags - { - u8 bits; - - BitField loop_end; - BitField loop_repeat; - BitField loop_start; - }; - - struct ADPCMBlock - { - union - { - u8 bits; - - BitField shift; - BitField filter; - } shift_filter; - ADPCMFlags flags; - u8 data[NUM_SAMPLES_PER_ADPCM_BLOCK / 2]; - - // For both 4bit and 8bit ADPCM, reserved shift values 13..15 will act same as shift=9). - u8 GetShift() const - { - const u8 shift = shift_filter.shift; - return (shift > 12) ? 9 : shift; - } - - u8 GetFilter() const { return std::min(shift_filter.filter, 4); } - - u8 GetNibble(u32 index) const { return (data[index / 2] >> ((index % 2) * 4)) & 0x0F; } - }; - - struct VolumeEnvelope - { - s32 counter; - u8 rate; - bool decreasing; - bool exponential; - - void Reset(u8 rate_, bool decreasing_, bool exponential_); - s16 Tick(s16 current_level); - }; - - struct VolumeSweep - { - VolumeEnvelope envelope; - bool envelope_active; - s16 current_level; - - void Reset(VolumeRegister reg); - void Tick(); - }; - - enum class ADSRPhase : u8 - { - Off = 0, - Attack = 1, - Decay = 2, - Sustain = 3, - Release = 4 - }; - - struct Voice - { - u16 current_address; - VoiceRegisters regs; - VoiceCounter counter; - ADPCMFlags current_block_flags; - bool is_first_block; - std::array current_block_samples; - std::array adpcm_last_samples; - s32 last_volume; - - VolumeSweep left_volume; - VolumeSweep right_volume; - - VolumeEnvelope adsr_envelope; - ADSRPhase adsr_phase; - s16 adsr_target; - bool has_samples; - bool ignore_loop_address; - - bool IsOn() const { return adsr_phase != ADSRPhase::Off; } - - void KeyOn(); - void KeyOff(); - void ForceOff(); - - void DecodeBlock(const ADPCMBlock& block); - s32 Interpolate() const; - - // Switches to the specified phase, filling in target. - void UpdateADSREnvelope(); - - // Updates the ADSR volume/phase. - void TickADSR(); - }; - - struct ReverbRegisters - { - s16 vLOUT; - s16 vROUT; - u16 mBASE; - - union - { - struct - { - u16 FB_SRC_A; - u16 FB_SRC_B; - s16 IIR_ALPHA; - s16 ACC_COEF_A; - s16 ACC_COEF_B; - s16 ACC_COEF_C; - s16 ACC_COEF_D; - s16 IIR_COEF; - s16 FB_ALPHA; - s16 FB_X; - u16 IIR_DEST_A[2]; - u16 ACC_SRC_A[2]; - u16 ACC_SRC_B[2]; - u16 IIR_SRC_A[2]; - u16 IIR_DEST_B[2]; - u16 ACC_SRC_C[2]; - u16 ACC_SRC_D[2]; - u16 IIR_SRC_B[2]; - u16 MIX_DEST_A[2]; - u16 MIX_DEST_B[2]; - s16 IN_COEF[2]; - }; - - u16 rev[NUM_REVERB_REGS]; - }; - }; - - static constexpr s32 Clamp16(s32 value) { return (value < -0x8000) ? -0x8000 : (value > 0x7FFF) ? 0x7FFF : value; } - - static constexpr s32 ApplyVolume(s32 sample, s16 volume) { return (sample * s32(volume)) >> 15; } - - static ADSRPhase GetNextADSRPhase(ADSRPhase phase); - - ALWAYS_INLINE bool IsVoiceReverbEnabled(u32 i) const - { - return ConvertToBoolUnchecked((m_reverb_on_register >> i) & u32(1)); - } - ALWAYS_INLINE bool IsVoiceNoiseEnabled(u32 i) const - { - return ConvertToBoolUnchecked((m_noise_mode_register >> i) & u32(1)); - } - ALWAYS_INLINE bool IsPitchModulationEnabled(u32 i) const - { - return ((i > 0) && ConvertToBoolUnchecked((m_pitch_modulation_enable_register >> i) & u32(1))); - } - ALWAYS_INLINE s16 GetVoiceNoiseLevel() const { return static_cast(static_cast(m_noise_level)); } - - u16 ReadVoiceRegister(u32 offset); - void WriteVoiceRegister(u32 offset, u16 value); - - ALWAYS_INLINE bool IsRAMIRQTriggerable() const { return m_SPUCNT.irq9_enable && !m_SPUSTAT.irq9_flag; } - ALWAYS_INLINE bool CheckRAMIRQ(u32 address) const { return ((ZeroExtend32(m_irq_address) * 8) == address); } - void TriggerRAMIRQ(); - void CheckForLateRAMIRQs(); - - void WriteToCaptureBuffer(u32 index, s16 value); - void IncrementCaptureBufferPosition(); - - void ReadADPCMBlock(u16 address, ADPCMBlock* block); - std::tuple SampleVoice(u32 voice_index); - - void UpdateNoise(); - - u32 ReverbMemoryAddress(u32 address) const; - s16 ReverbRead(u32 address, s32 offset = 0); - void ReverbWrite(u32 address, s16 data); - void ProcessReverb(s16 left_in, s16 right_in, s32* left_out, s32* right_out); - - void Execute(TickCount ticks); - void UpdateEventInterval(); - - void ExecuteFIFOWriteToRAM(TickCount& ticks); - void ExecuteFIFOReadFromRAM(TickCount& ticks); - void ExecuteTransfer(TickCount ticks); - void ManualTransferWrite(u16 value); - void UpdateTransferEvent(); - void UpdateDMARequest(); - - void CreateOutputStream(); - - std::unique_ptr m_tick_event; - std::unique_ptr m_transfer_event; - std::unique_ptr m_dump_writer; - std::unique_ptr m_audio_stream; - std::unique_ptr m_null_audio_stream; - bool m_audio_output_muted = false; - - TickCount m_ticks_carry = 0; - TickCount m_cpu_ticks_per_spu_tick = 0; - TickCount m_cpu_tick_divider = 0; - - SPUCNT m_SPUCNT = {}; - SPUSTAT m_SPUSTAT = {}; - - TransferControl m_transfer_control = {}; - u16 m_transfer_address_reg = 0; - u32 m_transfer_address = 0; - - u16 m_irq_address = 0; - u16 m_capture_buffer_position = 0; - - VolumeRegister m_main_volume_left_reg = {}; - VolumeRegister m_main_volume_right_reg = {}; - VolumeSweep m_main_volume_left = {}; - VolumeSweep m_main_volume_right = {}; - - s16 m_cd_audio_volume_left = 0; - s16 m_cd_audio_volume_right = 0; - - s16 m_external_volume_left = 0; - s16 m_external_volume_right = 0; - - u32 m_key_on_register = 0; - u32 m_key_off_register = 0; - u32 m_endx_register = 0; - u32 m_pitch_modulation_enable_register = 0; - - u32 m_noise_mode_register = 0; - u32 m_noise_count = 0; - u32 m_noise_level = 0; - - u32 m_reverb_on_register = 0; - u32 m_reverb_base_address = 0; - u32 m_reverb_current_address = 0; - ReverbRegisters m_reverb_registers{}; - std::array, 2> m_reverb_downsample_buffer; - std::array, 2> m_reverb_upsample_buffer; - s32 m_reverb_resample_buffer_position = 0; - - std::array m_voices{}; - - InlineFIFOQueue m_transfer_fifo; - - std::array m_ram{}; - -#ifdef SPU_DUMP_ALL_VOICES - // +1 for reverb output - std::array, NUM_VOICES + 1> m_voice_dump_writers; -#endif + RAM_SIZE = 512 * 1024, + RAM_MASK = RAM_SIZE - 1, + SAMPLE_RATE = 44100, }; -extern SPU g_spu; \ No newline at end of file +void Initialize(); +void CPUClockChanged(); +void Shutdown(); +void Reset(); +bool DoState(StateWrapper& sw); + +u16 ReadRegister(u32 offset); +void WriteRegister(u32 offset, u16 value); + +void DMARead(u32* words, u32 word_count); +void DMAWrite(const u32* words, u32 word_count); + +// Render statistics debug window. +void DrawDebugStateWindow(); + +// Executes the SPU, generating any pending samples. +void GeneratePendingSamples(); + +/// Returns true if currently dumping audio. +bool IsDumpingAudio(); + +/// Starts dumping audio to file. +bool StartDumpingAudio(const char* filename); + +/// Stops dumping audio to file, if started. +bool StopDumpingAudio(); + +/// Access to SPU RAM. +const std::array& GetRAM(); +std::array& GetWritableRAM(); + +/// Change output stream - used for runahead. +// TODO: Make it use system "running ahead" flag +bool IsAudioOutputMuted(); +void SetAudioOutputMuted(bool muted); + +AudioStream* GetOutputStream(); +void RecreateOutputStream(); + +}; // namespace SPU diff --git a/src/core/system.cpp b/src/core/system.cpp index 8e0eb210f..85cd8073e 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -270,7 +270,7 @@ void System::UpdateOverclock() { g_ticks_per_second = ScaleTicksToOverclock(MASTER_CLOCK); s_max_slice_ticks = ScaleTicksToOverclock(MASTER_CLOCK / 10); - g_spu.CPUClockChanged(); + SPU::CPUClockChanged(); g_cdrom.CPUClockChanged(); g_gpu->CPUClockChanged(); g_timers.CPUClocksChanged(); @@ -934,7 +934,7 @@ void System::PauseSystem(bool paused) return; SetState(paused ? State::Paused : State::Running); - g_spu.GetOutputStream()->SetPaused(paused); + SPU::GetOutputStream()->SetPaused(paused); if (paused) { @@ -1216,7 +1216,7 @@ bool System::BootSystem(SystemBootParameters parameters) s_state = (g_settings.start_paused || parameters.override_start_paused.value_or(false)) ? State::Paused : State::Running; UpdateSoftwareCursor(); - g_spu.GetOutputStream()->SetPaused(false); + SPU::GetOutputStream()->SetPaused(false); Host::OnSystemStarted(); if (s_state == State::Paused) @@ -1332,7 +1332,7 @@ bool System::Initialize(bool force_software_renderer) g_cdrom.Initialize(); g_pad.Initialize(); g_timers.Initialize(); - g_spu.Initialize(); + SPU::Initialize(); g_mdec.Initialize(); g_sio.Initialize(); @@ -1392,7 +1392,7 @@ void System::DestroySystem() g_sio.Shutdown(); g_mdec.Shutdown(); - g_spu.Shutdown(); + SPU::Shutdown(); g_timers.Shutdown(); g_pad.Shutdown(); g_cdrom.Shutdown(); @@ -1594,7 +1594,7 @@ bool System::DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool u if (!sw.DoMarker("Timers") || !g_timers.DoState(sw)) return false; - if (!sw.DoMarker("SPU") || !g_spu.DoState(sw)) + if (!sw.DoMarker("SPU") || !SPU::DoState(sw)) return false; if (!sw.DoMarker("MDEC") || !g_mdec.DoState(sw)) @@ -1678,7 +1678,7 @@ void System::InternalReset() g_cdrom.Reset(); g_pad.Reset(); g_timers.Reset(); - g_spu.Reset(); + SPU::Reset(); g_mdec.Reset(); g_sio.Reset(); s_frame_number = 1; @@ -1858,7 +1858,7 @@ bool System::DoLoadState(ByteStream* state, bool force_software_renderer, bool u if (s_state == State::Starting) s_state = State::Running; - g_spu.GetOutputStream()->EmptyBuffer(); + SPU::GetOutputStream()->EmptyBuffer(); ResetPerformanceCounters(); ResetThrottler(); return true; @@ -1977,7 +1977,7 @@ void System::SingleStepCPU() CPU::SingleStep(); - g_spu.GeneratePendingSamples(); + SPU::GeneratePendingSamples(); if (s_frame_number != old_frame_number && s_cheat_list) s_cheat_list->Apply(); @@ -2017,7 +2017,7 @@ void System::DoRunFrame() } // Generate any pending samples from the SPU before sleeping, this way we reduce the chances of underruns. - g_spu.GeneratePendingSamples(); + SPU::GeneratePendingSamples(); if (s_cheat_list) s_cheat_list->Apply(); @@ -2218,7 +2218,7 @@ void System::UpdateSpeedLimiterState() if (IsValid()) { - AudioStream* stream = g_spu.GetOutputStream(); + AudioStream* stream = SPU::GetOutputStream(); if (g_settings.audio_fast_forward_volume != g_settings.audio_output_volume) stream->SetOutputVolume(GetAudioOutputVolume()); @@ -2779,7 +2779,7 @@ bool System::DumpSPURAM(const char* filename) if (!IsValid()) return false; - return FileSystem::WriteBinaryFile(filename, g_spu.GetRAM().data(), SPU::RAM_SIZE); + return FileSystem::WriteBinaryFile(filename, SPU::GetRAM().data(), SPU::RAM_SIZE); } bool System::HasMedia() @@ -3060,15 +3060,15 @@ void System::CheckForSettingsChanges(const Settings& old_settings) Settings::GetAudioBackendName(g_settings.audio_backend)); } - g_spu.RecreateOutputStream(); + SPU::RecreateOutputStream(); } if (g_settings.audio_stretch_mode != old_settings.audio_stretch_mode) - g_spu.GetOutputStream()->SetStretchMode(g_settings.audio_stretch_mode); + SPU::GetOutputStream()->SetStretchMode(g_settings.audio_stretch_mode); if (g_settings.audio_buffer_ms != old_settings.audio_buffer_ms || g_settings.audio_output_latency_ms != old_settings.audio_output_latency_ms || g_settings.audio_stretch_mode != old_settings.audio_stretch_mode) { - g_spu.RecreateOutputStream(); + SPU::RecreateOutputStream(); UpdateSpeedLimiterState(); } @@ -3099,7 +3099,7 @@ void System::CheckForSettingsChanges(const Settings& old_settings) CPU::ClearICache(); } - g_spu.GetOutputStream()->SetOutputVolume(GetAudioOutputVolume()); + SPU::GetOutputStream()->SetOutputVolume(GetAudioOutputVolume()); if (g_settings.gpu_resolution_scale != old_settings.gpu_resolution_scale || g_settings.gpu_multisamples != old_settings.gpu_multisamples || @@ -3480,7 +3480,7 @@ void System::DoRunahead() const s32 temp = frames_to_run; #endif - g_spu.SetAudioOutputMuted(true); + SPU::SetAudioOutputMuted(true); while (frames_to_run > 0) { @@ -3489,7 +3489,7 @@ void System::DoRunahead() frames_to_run--; } - g_spu.SetAudioOutputMuted(false); + SPU::SetAudioOutputMuted(false); #ifdef PROFILE_MEMORY_SAVE_STATES Log_VerbosePrintf("Running %d frames to catch up took %.2f ms", temp, timer2.GetTimeMilliseconds()); @@ -3626,12 +3626,12 @@ void System::UpdateVolume() if (!IsValid()) return; - g_spu.GetOutputStream()->SetOutputVolume(GetAudioOutputVolume()); + SPU::GetOutputStream()->SetOutputVolume(GetAudioOutputVolume()); } bool System::IsDumpingAudio() { - return g_spu.IsDumpingAudio(); + return SPU::IsDumpingAudio(); } bool System::StartDumpingAudio(const char* filename) @@ -3657,7 +3657,7 @@ bool System::StartDumpingAudio(const char* filename) filename = auto_filename.c_str(); } - if (g_spu.StartDumpingAudio(filename)) + if (SPU::StartDumpingAudio(filename)) { Host::AddFormattedOSDMessage(5.0f, Host::TranslateString("OSDMessage", "Started dumping audio to '%s'."), filename); return true; @@ -3672,7 +3672,7 @@ bool System::StartDumpingAudio(const char* filename) void System::StopDumpingAudio() { - if (System::IsShutdown() || !g_spu.StopDumpingAudio()) + if (System::IsShutdown() || !SPU::StopDumpingAudio()) return; Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "Stopped dumping audio."), 5.0f); diff --git a/src/frontend-common/common_host.cpp b/src/frontend-common/common_host.cpp index dd7634779..e7bf5a3a8 100644 --- a/src/frontend-common/common_host.cpp +++ b/src/frontend-common/common_host.cpp @@ -405,7 +405,7 @@ void ImGuiManager::RenderDebugWindows() if (g_settings.debugging.show_timers_state) g_timers.DrawDebugStateWindow(); if (g_settings.debugging.show_spu_state) - g_spu.DrawDebugStateWindow(); + SPU::DrawDebugStateWindow(); if (g_settings.debugging.show_mdec_state) g_mdec.DrawDebugStateWindow(); if (g_settings.debugging.show_dma_state) @@ -908,7 +908,7 @@ DEFINE_HOTKEY("AudioMute", TRANSLATABLE("Hotkeys", "Audio"), TRANSLATABLE("Hotke { g_settings.audio_output_muted = !g_settings.audio_output_muted; const s32 volume = System::GetAudioOutputVolume(); - g_spu.GetOutputStream()->SetOutputVolume(volume); + SPU::GetOutputStream()->SetOutputVolume(volume); if (g_settings.audio_output_muted) { Host::AddIconOSDMessage("AudioControlHotkey", ICON_FA_VOLUME_MUTE, @@ -942,7 +942,7 @@ DEFINE_HOTKEY("AudioVolumeUp", TRANSLATABLE("Hotkeys", "Audio"), TRANSLATABLE("H const s32 volume = std::min(System::GetAudioOutputVolume() + 10, 100); g_settings.audio_output_volume = volume; g_settings.audio_fast_forward_volume = volume; - g_spu.GetOutputStream()->SetOutputVolume(volume); + SPU::GetOutputStream()->SetOutputVolume(volume); Host::AddIconOSDMessage("AudioControlHotkey", ICON_FA_VOLUME_UP, fmt::format(Host::TranslateString("OSDMessage", "Volume: {}%").GetCharArray(), volume), 5.0f); @@ -957,7 +957,7 @@ DEFINE_HOTKEY("AudioVolumeDown", TRANSLATABLE("Hotkeys", "Audio"), TRANSLATABLE( const s32 volume = std::max(System::GetAudioOutputVolume() - 10, 0); g_settings.audio_output_volume = volume; g_settings.audio_fast_forward_volume = volume; - g_spu.GetOutputStream()->SetOutputVolume(volume); + SPU::GetOutputStream()->SetOutputVolume(volume); Host::AddIconOSDMessage( "AudioControlHotkey", ICON_FA_VOLUME_DOWN, fmt::format(Host::TranslateString("OSDMessage", "Volume: {}%").GetCharArray(), volume), 5.0f);