mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2025-03-06 06:17:43 +00:00
SPU: Implement volume sweep phase inversion
This commit is contained in:
parent
8db0fcee99
commit
a4a58c6ba2
|
@ -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:
|
||||||
|
|
Loading…
Reference in a new issue