diff --git a/src/core/save_state_version.h b/src/core/save_state_version.h index f8fa7bd8a..ada144efb 100644 --- a/src/core/save_state_version.h +++ b/src/core/save_state_version.h @@ -2,4 +2,4 @@ #include "types.h" static constexpr u32 SAVE_STATE_MAGIC = 0x43435544; -static constexpr u32 SAVE_STATE_VERSION = 9; +static constexpr u32 SAVE_STATE_VERSION = 10; diff --git a/src/core/spu.cpp b/src/core/spu.cpp index 122d4ce7c..18a5caf86 100644 --- a/src/core/spu.cpp +++ b/src/core/spu.cpp @@ -46,9 +46,12 @@ void SPU::Reset() m_key_on_register = 0; m_key_off_register = 0; m_endx_register = 0; - m_noise_mode_register = 0; m_pitch_modulation_enable_register = 0; + m_noise_mode_register = 0; + m_noise_count = 0; + m_noise_level = 1; + m_reverb_on_register = 0; m_reverb_registers = {}; m_reverb_registers.mBASE = 0xE128; @@ -95,6 +98,8 @@ bool SPU::DoState(StateWrapper& sw) sw.Do(&m_key_off_register); sw.Do(&m_endx_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_current_address); sw.DoArray(m_reverb_registers.rev, NUM_REVERB_REGS); @@ -112,7 +117,7 @@ bool SPU::DoState(StateWrapper& sw) sw.Do(&v.current_block_samples); sw.Do(&v.previous_block_last_samples); sw.Do(&v.adpcm_last_samples); - sw.Do(&v.last_amplitude); + sw.Do(&v.last_volume); sw.DoPOD(&v.left_volume); sw.DoPOD(&v.right_volume); sw.DoPOD(&v.adsr_envelope); @@ -726,6 +731,9 @@ void SPU::Execute(TickCount ticks) } } + // Update noise once per frame. + UpdateNoise(); + // Mix in CD audio. s16 cd_audio_left; s16 cd_audio_right; @@ -779,8 +787,8 @@ void SPU::Execute(TickCount ticks) // Write to capture buffers. WriteToCaptureBuffer(0, cd_audio_left); WriteToCaptureBuffer(1, cd_audio_right); - WriteToCaptureBuffer(2, Clamp16(m_voices[1].last_amplitude)); - WriteToCaptureBuffer(3, Clamp16(m_voices[3].last_amplitude)); + WriteToCaptureBuffer(2, Clamp16(m_voices[1].last_volume)); + WriteToCaptureBuffer(3, Clamp16(m_voices[3].last_volume)); IncrementCaptureBufferPosition(); } @@ -1209,7 +1217,7 @@ std::tuple SPU::SampleVoice(u32 voice_index) Voice& voice = m_voices[voice_index]; if (!voice.IsOn()) { - voice.last_amplitude = 0; + voice.last_volume = 0; return {}; } @@ -1228,15 +1236,21 @@ std::tuple SPU::SampleVoice(u32 voice_index) } // interpolate/sample and apply ADSR volume - const s32 amplitude = ApplyVolume(voice.Interpolate(), voice.regs.adsr_volume); - voice.last_amplitude = amplitude; + s16 sample; + if (IsVoiceNoiseEnabled(voice_index)) + sample = GetVoiceNoiseLevel(); + else + sample = voice.Interpolate(); + + const s32 volume = ApplyVolume(sample, voice.regs.adsr_volume); + voice.last_volume = volume; voice.TickADSR(); // Pitch modulation u16 step = voice.regs.adpcm_sample_rate; if (IsPitchModulationEnabled(voice_index)) { - const u32 factor = u32(std::clamp(m_voices[voice_index - 1].last_amplitude, -0x8000, 0x7FFF) + 0x8000); + const u32 factor = u32(std::clamp(m_voices[voice_index - 1].last_volume, -0x8000, 0x7FFF) + 0x8000); step = Truncate16(step * factor) >> 15; } step = std::min(step, 0x4000); @@ -1273,8 +1287,8 @@ std::tuple SPU::SampleVoice(u32 voice_index) } // apply per-channel volume - const s32 left = ApplyVolume(amplitude, voice.left_volume.current_level); - const s32 right = ApplyVolume(amplitude, voice.right_volume.current_level); + const s32 left = ApplyVolume(volume, voice.left_volume.current_level); + const s32 right = ApplyVolume(volume, voice.right_volume.current_level); voice.left_volume.Tick(); voice.right_volume.Tick(); return std::make_tuple(left, right); @@ -1310,6 +1324,31 @@ void SPU::VoiceKeyOff(u32 voice_index) m_voice_key_on_off_delay[voice_index] = MINIMUM_TICKS_BETWEEN_KEY_ON_OFF; } +void SPU::UpdateNoise() +{ + // Dr Hell's noise waveform, implementation borrowed from pcsx-r. + static constexpr std::array noise_wave_add = { + {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, 0, 1, 1, 0, + 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 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]) + { + m_noise_count += 0x10000; + m_noise_count -= noise_freq_add[noise_clock & 3u]; + } + + if (m_noise_count < level) + return; + + m_noise_count %= level; + m_noise_level = (m_noise_level << 1) | noise_wave_add[(m_noise_level >> 10) & 63u]; +} + u32 SPU::ReverbMemoryAddress(u32 address) const { // Ensures address does not leave the reverb work area. @@ -1558,7 +1597,10 @@ void SPU::DrawDebugStateWindow() 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(); - ImGui::TextColored(color, "%u", ZeroExtend32(v.counter.interpolation_index.GetValue())); + if (IsVoiceNoiseEnabled(voice_index)) + ImGui::TextColored(color, "NOISE"); + else + ImGui::TextColored(color, "%u", ZeroExtend32(v.counter.interpolation_index.GetValue())); ImGui::NextColumn(); ImGui::TextColored(color, "%u", ZeroExtend32(v.counter.sample_index.GetValue())); ImGui::NextColumn(); diff --git a/src/core/spu.h b/src/core/spu.h index 1677f7a94..ecb529785 100644 --- a/src/core/spu.h +++ b/src/core/spu.h @@ -86,8 +86,7 @@ private: BitField enable; BitField mute_n; - BitField noise_frequency_shift; - BitField noise_frequency_step; + BitField noise_clock; BitField reverb_master_enable; BitField irq9_enable; BitField ram_transfer_mode; @@ -254,7 +253,7 @@ private: std::array current_block_samples; std::array previous_block_last_samples; std::array adpcm_last_samples; - s32 last_amplitude; + s32 last_volume; VolumeSweep left_volume; VolumeSweep right_volume; @@ -337,11 +336,19 @@ private: static ADSRPhase GetNextADSRPhase(ADSRPhase phase); - bool IsVoiceReverbEnabled(u32 i) const { return (m_reverb_on_register & (u32(1) << i)) != 0; } - bool IsPitchModulationEnabled(u32 i) const + ALWAYS_INLINE bool IsVoiceReverbEnabled(u32 i) const { - return (i > 0 && ((m_pitch_modulation_enable_register & (u32(1) << i)) != 0)); + 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); @@ -358,6 +365,8 @@ private: void VoiceKeyOn(u32 voice_index); void VoiceKeyOff(u32 voice_index); + void UpdateNoise(); + u32 ReverbMemoryAddress(u32 address) const; s16 ReverbRead(u32 address); void ReverbWrite(u32 address, s16 data); @@ -395,9 +404,12 @@ private: u32 m_key_on_register = 0; u32 m_key_off_register = 0; u32 m_endx_register = 0; - u32 m_noise_mode_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_current_address = 0; ReverbRegisters m_reverb_registers{};