#include "Runtime/Input/CRumbleVoice.hpp" namespace metaforce { CRumbleVoice::CRumbleVoice() : x0_datas(4), x10_deltas(4, SAdsrDelta::Stopped()) { x20_handleIds.resize(4); } s16 CRumbleVoice::CreateRumbleHandle(s16 idx) { ++x2e_lastId; if (x2e_lastId == 0) x2e_lastId = 1; x20_handleIds[idx] = x2e_lastId; return (x2e_lastId << 8) | idx; } bool CRumbleVoice::OwnsSustained(s16 handle) const { int idx = handle & 0xf; if (idx < 4) return x20_handleIds[idx] == ((handle >> 8) & 0xff); return false; } s16 CRumbleVoice::GetFreeChannel() const { for (s16 i = 0; i < 4; ++i) { if (!((1 << i) & x2c_usedChannels)) { return i; } } return 0; } float CRumbleVoice::GetIntensity() const { return std::min(2.f, std::max({x10_deltas[0].x0_curIntensity, x10_deltas[1].x0_curIntensity, x10_deltas[2].x0_curIntensity, x10_deltas[3].x0_curIntensity})); } bool CRumbleVoice::UpdateChannel(SAdsrDelta& delta, const SAdsrData& data, float dt) { switch (delta.x20_phase) { case SAdsrDelta::EPhase::PrePulse: if (delta.x4_attackTime < (1.f / 30.f)) { delta.x4_attackTime += dt; } else { delta.x20_phase = SAdsrDelta::EPhase::Attack; delta.x0_curIntensity = 0.f; delta.x4_attackTime = 0.f; } break; case SAdsrDelta::EPhase::Attack: if (delta.x4_attackTime < data.x8_attackDur) { float t = delta.x4_attackTime / data.x8_attackDur; delta.x0_curIntensity = t * delta.x14_attackIntensity; delta.x4_attackTime += dt; } else { delta.x0_curIntensity = delta.x14_attackIntensity; delta.x20_phase = SAdsrDelta::EPhase::Decay; } break; case SAdsrDelta::EPhase::Decay: if (data.x18_24_hasSustain) { if (delta.x8_decayTime < data.xc_decayDur) { float t = delta.x8_decayTime / data.xc_decayDur; delta.x0_curIntensity = (1.f - t) * delta.x14_attackIntensity + t * delta.x18_sustainIntensity; delta.x8_decayTime += dt; } else { delta.x0_curIntensity = delta.x18_sustainIntensity; delta.x20_phase = SAdsrDelta::EPhase::Sustain; } } else { if (delta.x8_decayTime < data.xc_decayDur) { float t = delta.x8_decayTime / data.xc_decayDur; delta.x0_curIntensity = (1.f - t) * delta.x14_attackIntensity; delta.x8_decayTime += dt; } else { delta.x0_curIntensity = 0.f; delta.x20_phase = SAdsrDelta::EPhase::Stop; } if (delta.x20_phase != SAdsrDelta::EPhase::Decay) { delta.x20_phase = SAdsrDelta::EPhase::Stop; return true; } } break; case SAdsrDelta::EPhase::Release: { float a = data.x18_24_hasSustain ? delta.x18_sustainIntensity : 0.f; if (delta.xc_releaseTime < data.x14_releaseDur) { float t = delta.xc_releaseTime / data.x14_releaseDur; delta.x0_curIntensity = (1.f - t) * a; delta.xc_releaseTime += dt; } else { delta.x0_curIntensity = 0.f; delta.x20_phase = SAdsrDelta::EPhase::Stop; } if (delta.x20_phase != SAdsrDelta::EPhase::Release) { delta.x20_phase = SAdsrDelta::EPhase::Stop; return true; } } break; default: break; } if (data.x18_25_autoRelease) { if (delta.x10_autoReleaseTime < data.x4_autoReleaseDur) delta.x10_autoReleaseTime += dt; else if (delta.x20_phase == SAdsrDelta::EPhase::Sustain) delta.x20_phase = SAdsrDelta::EPhase::Release; } return false; } bool CRumbleVoice::Update(float dt) { if (x2c_usedChannels != 0) { for (s16 i = 0; i < 4; ++i) { if ((1 << i) & x2c_usedChannels) { if (UpdateChannel(x10_deltas[i], x0_datas[i], dt)) { x2c_usedChannels &= ~(1 << i); x10_deltas[i] = SAdsrDelta::Stopped(); } } } return true; } return false; } void CRumbleVoice::HardReset() { x2c_usedChannels = 0; for (s16 i = 0; i < 4; ++i) { x10_deltas[i] = SAdsrDelta::Stopped(); x20_handleIds[i] = 0; } } s16 CRumbleVoice::Activate(const SAdsrData& data, s16 idx, float gain, ERumblePriority prio) { if (gain > 0.f) { x0_datas[idx] = data; x10_deltas[idx] = SAdsrDelta::Start(prio, x2c_usedChannels == 0); x10_deltas[idx].x14_attackIntensity = gain * x0_datas[idx].x0_attackGain; x10_deltas[idx].x18_sustainIntensity = gain * x0_datas[idx].x10_sustainGain; x2c_usedChannels |= 1 << idx; if (data.x18_24_hasSustain) return CreateRumbleHandle(idx); } return -1; } void CRumbleVoice::Deactivate(s16 id, bool b1) { if (id == -1) return; if (OwnsSustained(id)) { int handle = (id & 0xf); if (x2c_usedChannels & (1 << handle)) x10_deltas[handle].x20_phase = SAdsrDelta::EPhase::Release; } } } // namespace metaforce