diff --git a/src/Kyoto/Input/CRumbleVoice.cpp b/src/Kyoto/Input/CRumbleVoice.cpp new file mode 100644 index 00000000..912c94e0 --- /dev/null +++ b/src/Kyoto/Input/CRumbleVoice.cpp @@ -0,0 +1,174 @@ +#include "Kyoto/Input/CRumbleVoice.hpp" +#include "rstl/math.hpp" + +CRumbleVoice::CRumbleVoice() +: x0_datas(4, SAdsrData()) +, x10_deltas(4, SAdsrDelta::Stopped()) +, x20_handleIds(0) +, x2c_usedChannels(0) +, x2e_lastId(0) {} + +s16 CRumbleVoice::Activate(const SAdsrData& data, u16 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)) { + return; + } else if (x2c_usedChannels & (1 << (id & 0xf))) { + x10_deltas[(id & 0xf)].x20_phase = SAdsrDelta::kP_Release; + return; + } +} + +void CRumbleVoice::HardReset() { + x2c_usedChannels = 0; + for (u16 i = 0; i < 4; ++i) { + x10_deltas[i] = SAdsrDelta::Stopped(); + x20_handleIds[i] = 0; + } +} + +bool CRumbleVoice::UpdateChannel(SAdsrDelta& delta, const SAdsrData& data, float dt) { + switch (delta.x20_phase) { + case SAdsrDelta::kP_PrePulse: + if (delta.x4_attackTime < (1.f / 30.f)) { + delta.x4_attackTime += dt; + } else { + delta.x20_phase = SAdsrDelta::kP_Attack; + delta.x0_curIntensity = 0.f; + delta.x4_attackTime = 0.f; + } + break; + case SAdsrDelta::kP_Attack: + if (delta.x4_attackTime < data.x8_attackDur) { + float t = delta.x4_attackTime / data.x8_attackDur; + delta.x0_curIntensity = ((1.f - t) * 0.f + delta.x14_attackIntensity) * t; + delta.x4_attackTime += dt; + } else { + delta.x0_curIntensity = delta.x14_attackIntensity; + delta.x20_phase = SAdsrDelta::kP_Decay; + } + break; + case SAdsrDelta::kP_Decay: + if (data.x18_24_hasSustain) { + if (delta.x8_decayTime > data.xc_decayDur) { + delta.x0_curIntensity = delta.x18_sustainIntensity; + delta.x20_phase = SAdsrDelta::kP_Sustain; + } else { + 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 { + if (delta.x8_decayTime < data.xc_decayDur) { + float t = delta.x8_decayTime / data.xc_decayDur; + delta.x0_curIntensity = delta.x14_attackIntensity * (1.f - t) + t * 0.f; + delta.x8_decayTime += dt; + } else { + delta.x0_curIntensity = 0.f; + delta.x20_phase = SAdsrDelta::kP_Stop; + } + if (delta.x20_phase != SAdsrDelta::kP_Decay) { + delta.x20_phase = SAdsrDelta::kP_Stop; + return true; + } + } + break; + case SAdsrDelta::kP_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 = a * (1.f - t) + t * 0.f; + delta.xc_releaseTime += dt; + } else { + delta.x0_curIntensity = 0.f; + delta.x20_phase = SAdsrDelta::kP_Stop; + } + if (delta.x20_phase != SAdsrDelta::kP_Release) { + delta.x20_phase = SAdsrDelta::kP_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::kP_Sustain) + delta.x20_phase = SAdsrDelta::kP_Release; + } + + return false; +} +bool CRumbleVoice::Update(float dt) { + if (x2c_usedChannels != 0) { + for (u16 i = 0; i < 4; ++i) { + if (x2c_usedChannels & (1 << i)) { + if (UpdateChannel(x10_deltas[i], x0_datas[i], dt)) { + x2c_usedChannels &= ~(1 << i); + x10_deltas[i] = SAdsrDelta::Stopped(); + } + } + } + return true; + } + return false; +} + +uint CRumbleVoice::GetFreeChannel() const { + for (u16 i = 0; i < 4; ++i) { + if ((x2c_usedChannels & (1 << i)) == 0) { + return i; + } + } + return 0; +} +float CRumbleVoice::GetIntensity() const { + float ret = x10_deltas[0].x0_curIntensity; + if (ret < x10_deltas[1].x0_curIntensity) { + ret = x10_deltas[1].x0_curIntensity; + } + + if (ret < x10_deltas[2].x0_curIntensity) { + ret = x10_deltas[2].x0_curIntensity; + } + + if (ret < x10_deltas[3].x0_curIntensity) { + ret = x10_deltas[3].x0_curIntensity; + } + + if (ret > 2.f) { + return 2.f; + } + + return ret; +} +bool CRumbleVoice::OwnsSustained(s16 handle) const { + u16 idx = (((u16)handle >> 8) & 0xff); + if (idx < 4) + return x20_handleIds[idx] == (idx & 0xf); + return false; +} + +s16 CRumbleVoice::CreateRumbleHandle(u16 idx) { + ++x2e_lastId; + if (x2e_lastId == 0) + x2e_lastId = 1; + x20_handleIds[idx] = x2e_lastId; + return ((x2e_lastId << 8) | idx) & 0xFFFF; +}