// Copyright: 2021 - 2022, Ziemas // SPDX-License-Identifier: ISC #include "envelope.h" #include #include namespace snd { void Envelope::Step() { // arbitrary number of bits, this is probably incorrect for the // "reserved" and infinite duration values // test hw or copy mednafen instead? u32 cStep = 0x800000; s32 shift = m_Shift - 11; if (shift > 0) cStep >>= shift; s16 step = static_cast(m_Step << std::max(0, 11 - m_Shift)); if (m_Exp) { if (!m_Decrease && m_Level > 0x6000) cStep >>= 2; if (m_Decrease) step = static_cast((step * m_Level) >> 15); } m_Counter += cStep; if (m_Counter >= 0x800000) { m_Counter = 0; m_Level = std::clamp(m_Level + step, 0, INT16_MAX); } } void ADSR::Run() { // Let's not waste time calculating silent voices if (m_Phase == Phase::Stopped) return; Step(); if (m_Phase == Phase::Sustain) return; if ((!m_Decrease && m_Level >= m_Target) || (m_Decrease && m_Level <= m_Target)) { switch (m_Phase) { case Phase::Attack: m_Phase = Phase::Decay; break; case Phase::Decay: m_Phase = Phase::Sustain; break; case Phase::Release: m_Phase = Phase::Stopped; break; default: break; } UpdateSettings(); } } void ADSR::UpdateSettings() { switch (m_Phase) { case Phase::Attack: m_Exp = m_Reg.AttackExp.get(); m_Decrease = false; m_Shift = m_Reg.AttackShift.get(); m_Step = static_cast(7 - m_Reg.AttackStep.get()); m_Target = 0x7FFF; break; case Phase::Decay: m_Exp = true; m_Decrease = true; m_Shift = m_Reg.DecayShift.get(); m_Step = -8; m_Target = (m_Reg.SustainLevel.get() + 1) << 11; break; case Phase::Sustain: m_Exp = m_Reg.SustainExp.get(); m_Decrease = m_Reg.SustainDecr.get(); m_Shift = m_Reg.SustainShift.get(); m_Step = m_Decrease ? static_cast(-8 + m_Reg.SustainStep.get()) : static_cast(7 - m_Reg.SustainStep.get()); m_Target = 0; // unused for sustain break; case Phase::Release: m_Exp = m_Reg.ReleaseExp.get(); m_Decrease = true; m_Shift = m_Reg.ReleaseShift.get(); m_Step = -8; m_Target = 0; break; default: break; } } void ADSR::Attack() { m_Phase = Phase::Attack; m_Level = 0; m_Counter = 0; UpdateSettings(); } void ADSR::Release() { m_Phase = Phase::Release; m_Counter = 0; UpdateSettings(); } void ADSR::Stop() { m_Phase = Phase::Stopped; m_Level = 0; } s16 ADSR::Level() const { return static_cast(m_Level); } void Volume::Run() { if (!m_Sweep.EnableSweep.get()) return; Step(); } void Volume::Set(u16 volume) { m_Sweep.bits = volume; if (!m_Sweep.EnableSweep.get()) { m_Level = static_cast(m_Sweep.bits << 1); return; } m_Exp = m_Sweep.SweepExp.get(); m_Decrease = m_Sweep.SweepDecrease.get(); m_Shift = m_Sweep.SweepShift.get(); m_Step = m_Decrease ? static_cast(-8 + m_Sweep.SweepStep.get()) : static_cast(7 - m_Sweep.SweepStep.get()); if (m_Exp && m_Decrease) { // if (m_Sweep.NegativePhase) // Console.WriteLn("Disqualified from inv"); m_Inv = false; } else { m_Inv = m_Sweep.NegativePhase.get(); } // Console.WriteLn(Color_Red, "start sweep, e:%d d:%d sh:%d st:%d inv:%d", m_Exp, m_Decrease, // m_Shift, m_Step, m_Inv); Console.WriteLn(Color_Red, "Current level %08x", m_Level); } u16 Volume::Get() const { return m_Sweep.bits; } s16 Volume::GetCurrent() const { return static_cast(m_Level); } } // namespace snd