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"
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_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<s32, s32> 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<s32, s32> 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<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 = std::min<u16>(step, 0x4000);
@ -1273,8 +1287,8 @@ std::tuple<s32, s32> 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<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
{
// 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();

View File

@ -86,8 +86,7 @@ private:
BitField<u16, bool, 15, 1> enable;
BitField<u16, bool, 14, 1> mute_n;
BitField<u16, u8, 10, 4> noise_frequency_shift;
BitField<u16, u8, 8, 2> noise_frequency_step;
BitField<u16, u8, 8, 6> noise_clock;
BitField<u16, bool, 7, 1> reverb_master_enable;
BitField<u16, bool, 6, 1> irq9_enable;
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, 3> previous_block_last_samples;
std::array<s32, 2> 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<s16>(static_cast<u16>(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{};