SPU: Implement volume sweep phase inversion

This commit is contained in:
Stenzek 2024-03-20 21:55:55 +10:00
parent 8db0fcee99
commit a4a58c6ba2
No known key found for this signature in database

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com> // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#include "spu.h" #include "spu.h"
@ -213,8 +213,9 @@ struct VolumeEnvelope
u8 rate; u8 rate;
bool decreasing; bool decreasing;
bool exponential; bool exponential;
bool phase_invert;
void Reset(u8 rate_, bool decreasing_, bool exponential_); void Reset(u8 rate_, bool decreasing_, bool exponential_, bool phase_invert_);
s16 Tick(s16 current_level); s16 Tick(s16 current_level);
}; };
@ -516,7 +517,7 @@ void SPU::Reset()
v.is_first_block = 0; v.is_first_block = 0;
v.current_block_samples.fill(s16(0)); v.current_block_samples.fill(s16(0));
v.adpcm_last_samples.fill(s32(0)); v.adpcm_last_samples.fill(s32(0));
v.adsr_envelope.Reset(0, false, false); v.adsr_envelope.Reset(0, false, false, false);
v.adsr_phase = ADSRPhase::Off; v.adsr_phase = ADSRPhase::Off;
v.adsr_target = 0; v.adsr_target = 0;
v.has_samples = false; v.has_samples = false;
@ -1662,11 +1663,12 @@ static constexpr ADSRTableEntries ComputeADSRTableEntries()
static constexpr ADSRTableEntries s_adsr_table = ComputeADSRTableEntries(); static constexpr ADSRTableEntries s_adsr_table = ComputeADSRTableEntries();
void SPU::VolumeEnvelope::Reset(u8 rate_, bool decreasing_, bool exponential_) void SPU::VolumeEnvelope::Reset(u8 rate_, bool decreasing_, bool exponential_, bool phase_invert_)
{ {
rate = rate_; rate = rate_;
decreasing = decreasing_; decreasing = decreasing_;
exponential = exponential_; exponential = exponential_;
phase_invert = phase_invert_;
const ADSRTableEntry& table_entry = s_adsr_table[BoolToUInt8(decreasing)][rate]; const ADSRTableEntry& table_entry = s_adsr_table[BoolToUInt8(decreasing)][rate];
counter = table_entry.ticks; counter = table_entry.ticks;
@ -1709,8 +1711,16 @@ s16 SPU::VolumeEnvelope::Tick(s16 current_level)
} }
} }
return static_cast<s16>( if (phase_invert) [[unlikely]]
std::clamp<s32>(static_cast<s32>(current_level) + this_step, ENVELOPE_MIN_VOLUME, ENVELOPE_MAX_VOLUME)); {
return static_cast<s16>(
std::clamp<s32>(static_cast<s32>(current_level) - this_step, -ENVELOPE_MAX_VOLUME, ENVELOPE_MIN_VOLUME));
}
else
{
return static_cast<s16>(
std::clamp<s32>(static_cast<s32>(current_level) + this_step, ENVELOPE_MIN_VOLUME, ENVELOPE_MAX_VOLUME));
}
} }
void SPU::VolumeSweep::Reset(VolumeRegister reg) void SPU::VolumeSweep::Reset(VolumeRegister reg)
@ -1722,7 +1732,8 @@ void SPU::VolumeSweep::Reset(VolumeRegister reg)
return; return;
} }
envelope.Reset(reg.sweep_rate, reg.sweep_direction_decrease, reg.sweep_exponential); envelope.Reset(reg.sweep_rate, reg.sweep_direction_decrease, reg.sweep_exponential,
!(reg.sweep_exponential && reg.sweep_direction_decrease) && reg.sweep_phase_negative);
envelope_active = true; envelope_active = true;
} }
@ -1742,28 +1753,29 @@ void SPU::Voice::UpdateADSREnvelope()
{ {
case ADSRPhase::Off: case ADSRPhase::Off:
adsr_target = 0; adsr_target = 0;
adsr_envelope.Reset(0, false, false); adsr_envelope.Reset(0, false, false, false);
return; return;
case ADSRPhase::Attack: case ADSRPhase::Attack:
adsr_target = 32767; // 0 -> max adsr_target = 32767; // 0 -> max
adsr_envelope.Reset(regs.adsr.attack_rate, false, regs.adsr.attack_exponential); adsr_envelope.Reset(regs.adsr.attack_rate, false, regs.adsr.attack_exponential, false);
break; break;
case ADSRPhase::Decay: case ADSRPhase::Decay:
adsr_target = static_cast<s16>(std::min<s32>((u32(regs.adsr.sustain_level.GetValue()) + 1) * 0x800, adsr_target = static_cast<s16>(std::min<s32>((u32(regs.adsr.sustain_level.GetValue()) + 1) * 0x800,
ENVELOPE_MAX_VOLUME)); // max -> sustain level ENVELOPE_MAX_VOLUME)); // max -> sustain level
adsr_envelope.Reset(regs.adsr.decay_rate_shr2 << 2, true, true); adsr_envelope.Reset(regs.adsr.decay_rate_shr2 << 2, true, true, false);
break; break;
case ADSRPhase::Sustain: case ADSRPhase::Sustain:
adsr_target = 0; adsr_target = 0;
adsr_envelope.Reset(regs.adsr.sustain_rate, regs.adsr.sustain_direction_decrease, regs.adsr.sustain_exponential); adsr_envelope.Reset(regs.adsr.sustain_rate, regs.adsr.sustain_direction_decrease, regs.adsr.sustain_exponential,
false);
break; break;
case ADSRPhase::Release: case ADSRPhase::Release:
adsr_target = 0; adsr_target = 0;
adsr_envelope.Reset(regs.adsr.release_rate_shr2 << 2, true, regs.adsr.release_exponential); adsr_envelope.Reset(regs.adsr.release_rate_shr2 << 2, true, regs.adsr.release_exponential, false);
break; break;
default: default: