SPU: Implement noise functionality

This commit is contained in:
Connor McLaughlin 2020-03-23 00:29:00 +10:00
parent 0a6295a9b4
commit d47a82d591
3 changed files with 73 additions and 19 deletions

View file

@ -2,4 +2,4 @@
#include "types.h" #include "types.h"
static constexpr u32 SAVE_STATE_MAGIC = 0x43435544; static constexpr u32 SAVE_STATE_MAGIC = 0x43435544;
static constexpr u32 SAVE_STATE_VERSION = 9; static constexpr u32 SAVE_STATE_VERSION = 10;

View file

@ -46,9 +46,12 @@ void SPU::Reset()
m_key_on_register = 0; m_key_on_register = 0;
m_key_off_register = 0; m_key_off_register = 0;
m_endx_register = 0; m_endx_register = 0;
m_noise_mode_register = 0;
m_pitch_modulation_enable_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_on_register = 0;
m_reverb_registers = {}; m_reverb_registers = {};
m_reverb_registers.mBASE = 0xE128; m_reverb_registers.mBASE = 0xE128;
@ -95,6 +98,8 @@ bool SPU::DoState(StateWrapper& sw)
sw.Do(&m_key_off_register); sw.Do(&m_key_off_register);
sw.Do(&m_endx_register); sw.Do(&m_endx_register);
sw.Do(&m_noise_mode_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_on_register);
sw.Do(&m_reverb_current_address); sw.Do(&m_reverb_current_address);
sw.DoArray(m_reverb_registers.rev, NUM_REVERB_REGS); 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.current_block_samples);
sw.Do(&v.previous_block_last_samples); sw.Do(&v.previous_block_last_samples);
sw.Do(&v.adpcm_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.left_volume);
sw.DoPOD(&v.right_volume); sw.DoPOD(&v.right_volume);
sw.DoPOD(&v.adsr_envelope); sw.DoPOD(&v.adsr_envelope);
@ -726,6 +731,9 @@ void SPU::Execute(TickCount ticks)
} }
} }
// Update noise once per frame.
UpdateNoise();
// Mix in CD audio. // Mix in CD audio.
s16 cd_audio_left; s16 cd_audio_left;
s16 cd_audio_right; s16 cd_audio_right;
@ -779,8 +787,8 @@ void SPU::Execute(TickCount ticks)
// Write to capture buffers. // Write to capture buffers.
WriteToCaptureBuffer(0, cd_audio_left); WriteToCaptureBuffer(0, cd_audio_left);
WriteToCaptureBuffer(1, cd_audio_right); WriteToCaptureBuffer(1, cd_audio_right);
WriteToCaptureBuffer(2, Clamp16(m_voices[1].last_amplitude)); WriteToCaptureBuffer(2, Clamp16(m_voices[1].last_volume));
WriteToCaptureBuffer(3, Clamp16(m_voices[3].last_amplitude)); WriteToCaptureBuffer(3, Clamp16(m_voices[3].last_volume));
IncrementCaptureBufferPosition(); IncrementCaptureBufferPosition();
} }
@ -1209,7 +1217,7 @@ std::tuple<s32, s32> SPU::SampleVoice(u32 voice_index)
Voice& voice = m_voices[voice_index]; Voice& voice = m_voices[voice_index];
if (!voice.IsOn()) if (!voice.IsOn())
{ {
voice.last_amplitude = 0; voice.last_volume = 0;
return {}; return {};
} }
@ -1228,15 +1236,21 @@ std::tuple<s32, s32> SPU::SampleVoice(u32 voice_index)
} }
// interpolate/sample and apply ADSR volume // interpolate/sample and apply ADSR volume
const s32 amplitude = ApplyVolume(voice.Interpolate(), voice.regs.adsr_volume); s16 sample;
voice.last_amplitude = amplitude; 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(); voice.TickADSR();
// Pitch modulation // Pitch modulation
u16 step = voice.regs.adpcm_sample_rate; u16 step = voice.regs.adpcm_sample_rate;
if (IsPitchModulationEnabled(voice_index)) if (IsPitchModulationEnabled(voice_index))
{ {
const u32 factor = u32(std::clamp<s32>(m_voices[voice_index - 1].last_amplitude, -0x8000, 0x7FFF) + 0x8000); const u32 factor = u32(std::clamp<s32>(m_voices[voice_index - 1].last_volume, -0x8000, 0x7FFF) + 0x8000);
step = Truncate16(step * factor) >> 15; step = Truncate16(step * factor) >> 15;
} }
step = std::min<u16>(step, 0x4000); step = std::min<u16>(step, 0x4000);
@ -1273,8 +1287,8 @@ std::tuple<s32, s32> SPU::SampleVoice(u32 voice_index)
} }
// apply per-channel volume // apply per-channel volume
const s32 left = ApplyVolume(amplitude, voice.left_volume.current_level); const s32 left = ApplyVolume(volume, voice.left_volume.current_level);
const s32 right = ApplyVolume(amplitude, voice.right_volume.current_level); const s32 right = ApplyVolume(volume, voice.right_volume.current_level);
voice.left_volume.Tick(); voice.left_volume.Tick();
voice.right_volume.Tick(); voice.right_volume.Tick();
return std::make_tuple(left, right); 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; 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<u8, 64> 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<u8, 5> 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 u32 SPU::ReverbMemoryAddress(u32 address) const
{ {
// Ensures address does not leave the reverb work area. // 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); 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::TextColored(color, "%u", ZeroExtend32(voice_index));
ImGui::NextColumn(); 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::NextColumn();
ImGui::TextColored(color, "%u", ZeroExtend32(v.counter.sample_index.GetValue())); ImGui::TextColored(color, "%u", ZeroExtend32(v.counter.sample_index.GetValue()));
ImGui::NextColumn(); ImGui::NextColumn();

View file

@ -86,8 +86,7 @@ private:
BitField<u16, bool, 15, 1> enable; BitField<u16, bool, 15, 1> enable;
BitField<u16, bool, 14, 1> mute_n; BitField<u16, bool, 14, 1> mute_n;
BitField<u16, u8, 10, 4> noise_frequency_shift; BitField<u16, u8, 8, 6> noise_clock;
BitField<u16, u8, 8, 2> noise_frequency_step;
BitField<u16, bool, 7, 1> reverb_master_enable; BitField<u16, bool, 7, 1> reverb_master_enable;
BitField<u16, bool, 6, 1> irq9_enable; BitField<u16, bool, 6, 1> irq9_enable;
BitField<u16, RAMTransferMode, 4, 2> ram_transfer_mode; BitField<u16, RAMTransferMode, 4, 2> ram_transfer_mode;
@ -254,7 +253,7 @@ private:
std::array<s16, NUM_SAMPLES_PER_ADPCM_BLOCK> current_block_samples; std::array<s16, NUM_SAMPLES_PER_ADPCM_BLOCK> current_block_samples;
std::array<s16, 3> previous_block_last_samples; std::array<s16, 3> previous_block_last_samples;
std::array<s32, 2> adpcm_last_samples; std::array<s32, 2> adpcm_last_samples;
s32 last_amplitude; s32 last_volume;
VolumeSweep left_volume; VolumeSweep left_volume;
VolumeSweep right_volume; VolumeSweep right_volume;
@ -337,11 +336,19 @@ private:
static ADSRPhase GetNextADSRPhase(ADSRPhase phase); static ADSRPhase GetNextADSRPhase(ADSRPhase phase);
bool IsVoiceReverbEnabled(u32 i) const { return (m_reverb_on_register & (u32(1) << i)) != 0; } ALWAYS_INLINE bool IsVoiceReverbEnabled(u32 i) const
bool IsPitchModulationEnabled(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<s16>(static_cast<u16>(m_noise_level)); }
u16 ReadVoiceRegister(u32 offset); u16 ReadVoiceRegister(u32 offset);
void WriteVoiceRegister(u32 offset, u16 value); void WriteVoiceRegister(u32 offset, u16 value);
@ -358,6 +365,8 @@ private:
void VoiceKeyOn(u32 voice_index); void VoiceKeyOn(u32 voice_index);
void VoiceKeyOff(u32 voice_index); void VoiceKeyOff(u32 voice_index);
void UpdateNoise();
u32 ReverbMemoryAddress(u32 address) const; u32 ReverbMemoryAddress(u32 address) const;
s16 ReverbRead(u32 address); s16 ReverbRead(u32 address);
void ReverbWrite(u32 address, s16 data); void ReverbWrite(u32 address, s16 data);
@ -395,9 +404,12 @@ private:
u32 m_key_on_register = 0; u32 m_key_on_register = 0;
u32 m_key_off_register = 0; u32 m_key_off_register = 0;
u32 m_endx_register = 0; u32 m_endx_register = 0;
u32 m_noise_mode_register = 0;
u32 m_pitch_modulation_enable_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_on_register = 0;
u32 m_reverb_current_address = 0; u32 m_reverb_current_address = 0;
ReverbRegisters m_reverb_registers{}; ReverbRegisters m_reverb_registers{};