Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- struct ADSRTableEntry
- {
- s32 ticks;
- s32 step;
- };
- enum : u32
- {
- NUM_ADSR_TABLE_ENTRIES = 128,
- NUM_ADSR_DIRECTIONS = 2 // increasing, decreasing
- };
- using ADSRTableEntries = std::array<std::array<ADSRTableEntry, NUM_ADSR_TABLE_ENTRIES>, NUM_ADSR_DIRECTIONS>;
- static constexpr ADSRTableEntries ComputeADSRTableEntries()
- {
- ADSRTableEntries entries = {};
- for (u32 decreasing = 0; decreasing < 2; decreasing++)
- {
- for (u32 rate = 0; rate < NUM_ADSR_TABLE_ENTRIES; rate++)
- {
- if (rate < 48)
- {
- entries[decreasing][rate].ticks = 1;
- if (decreasing != 0)
- entries[decreasing][rate].step =
- static_cast<s32>(static_cast<u32>(-8 + static_cast<s32>(rate & 3)) << (11 - (rate >> 2)));
- else
- entries[decreasing][rate].step = (7 - static_cast<s32>(rate & 3)) << (11 - (rate >> 2));
- }
- else
- {
- entries[decreasing][rate].ticks = 1 << (static_cast<s32>(rate >> 2) - 11);
- if (decreasing != 0)
- entries[decreasing][rate].step = (-8 + static_cast<s32>(rate & 3));
- else
- entries[decreasing][rate].step = (7 - static_cast<s32>(rate & 3));
- }
- }
- }
- return entries;
- }
- static constexpr ADSRTableEntries s_adsr_table = ComputeADSRTableEntries();
- void SPU::VolumeEnvelope::Reset(u8 rate_, bool decreasing_, bool exponential_)
- {
- rate = rate_;
- decreasing = decreasing_;
- exponential = exponential_;
- const ADSRTableEntry& table_entry = s_adsr_table[BoolToUInt8(decreasing)][rate];
- counter = table_entry.ticks;
- }
- s16 SPU::VolumeEnvelope::Tick(s16 current_level)
- {
- counter--;
- if (counter > 0)
- return current_level;
- const ADSRTableEntry& table_entry = s_adsr_table[BoolToUInt8(decreasing)][rate];
- s32 this_step = table_entry.step;
- counter = table_entry.ticks;
- if (exponential)
- {
- if (decreasing)
- {
- this_step = (this_step * current_level) >> 15;
- }
- else
- {
- if (current_level >= 0x6000)
- {
- if (rate < 40)
- {
- this_step >>= 2;
- }
- else if (rate >= 44)
- {
- counter >>= 2;
- }
- else
- {
- this_step >>= 1;
- counter >>= 1;
- }
- }
- }
- }
- return static_cast<s16>(
- std::clamp<s32>(static_cast<s32>(current_level) + this_step, ENVELOPE_MIN_VOLUME, ENVELOPE_MAX_VOLUME));
- }
- void SPU::Voice::UpdateADSREnvelope()
- {
- switch (adsr_phase)
- {
- case ADSRPhase::Off:
- adsr_target = 0;
- adsr_envelope.Reset(0, false, false);
- return;
- case ADSRPhase::Attack:
- adsr_target = 32767; // 0 -> max
- adsr_envelope.Reset(regs.adsr.attack_rate, false, regs.adsr.attack_exponential);
- break;
- case ADSRPhase::Decay:
- adsr_target = static_cast<s16>(std::min<s32>((u32(regs.adsr.sustain_level.GetValue()) + 1) * 0x800,
- ENVELOPE_MAX_VOLUME)); // max -> sustain level
- adsr_envelope.Reset(regs.adsr.decay_rate_shr2 << 2, true, true);
- break;
- case ADSRPhase::Sustain:
- adsr_target = 0;
- adsr_envelope.Reset(regs.adsr.sustain_rate, regs.adsr.sustain_direction_decrease, regs.adsr.sustain_exponential);
- break;
- case ADSRPhase::Release:
- adsr_target = 0;
- adsr_envelope.Reset(regs.adsr.release_rate_shr2 << 2, true, regs.adsr.release_exponential);
- break;
- default:
- break;
- }
- }
- void SPU::Voice::TickADSR()
- {
- regs.adsr_volume = adsr_envelope.Tick(regs.adsr_volume);
- if (adsr_phase != ADSRPhase::Sustain)
- {
- const bool reached_target =
- adsr_envelope.decreasing ? (regs.adsr_volume <= adsr_target) : (regs.adsr_volume >= adsr_target);
- if (reached_target)
- {
- adsr_phase = GetNextADSRPhase(adsr_phase);
- UpdateADSREnvelope();
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement