2019-11-12 20:25:21 +00:00
|
|
|
#include "Runtime/Audio/CSfxManager.hpp"
|
2022-02-18 07:37:54 +00:00
|
|
|
#include "Runtime/CInputStream.hpp"
|
2019-11-12 20:25:21 +00:00
|
|
|
|
|
|
|
#include "Runtime/CSimplePool.hpp"
|
2016-04-17 02:50:45 +00:00
|
|
|
|
2021-04-10 08:42:06 +00:00
|
|
|
namespace metaforce {
|
2018-11-02 08:16:16 +00:00
|
|
|
static TLockedToken<std::vector<u16>> mpSfxTranslationTableTok;
|
|
|
|
std::vector<u16>* CSfxManager::mpSfxTranslationTable = nullptr;
|
2016-04-17 02:50:45 +00:00
|
|
|
|
2017-01-23 07:22:17 +00:00
|
|
|
static amuse::EffectReverbHiInfo s_ReverbHiQueued;
|
|
|
|
static amuse::EffectChorusInfo s_ChorusQueued;
|
|
|
|
static amuse::EffectReverbStdInfo s_ReverbStdQueued;
|
|
|
|
static amuse::EffectDelayInfo s_DelayQueued;
|
|
|
|
|
|
|
|
static amuse::EffectReverbHi* s_ReverbHiState = nullptr;
|
|
|
|
static amuse::EffectChorus* s_ChorusState = nullptr;
|
|
|
|
static amuse::EffectReverbStd* s_ReverbStdState = nullptr;
|
|
|
|
static amuse::EffectDelay* s_DelayState = nullptr;
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
CFactoryFnReturn FAudioTranslationTableFactory(const SObjectTag& tag, CInputStream& in, const CVParamTransfer& vparms,
|
|
|
|
CObjectReference* selfRef) {
|
|
|
|
std::unique_ptr<std::vector<u16>> obj = std::make_unique<std::vector<u16>>();
|
2022-02-18 07:37:54 +00:00
|
|
|
u32 count = in.ReadLong();
|
2018-12-08 05:30:43 +00:00
|
|
|
obj->reserve(count);
|
|
|
|
for (u32 i = 0; i < count; ++i)
|
2022-02-18 07:37:54 +00:00
|
|
|
obj->push_back(in.ReadShort());
|
2018-12-08 05:30:43 +00:00
|
|
|
return TToken<std::vector<u16>>::GetIObjObjectFor(std::move(obj));
|
2017-01-20 03:53:32 +00:00
|
|
|
}
|
|
|
|
|
2020-04-07 16:47:55 +00:00
|
|
|
std::array<CSfxManager::CSfxChannel, 4> CSfxManager::m_channels;
|
2017-01-23 07:22:17 +00:00
|
|
|
CSfxManager::ESfxChannels CSfxManager::m_currentChannel = CSfxManager::ESfxChannels::Default;
|
2016-12-29 05:53:00 +00:00
|
|
|
bool CSfxManager::m_doUpdate;
|
|
|
|
void* CSfxManager::m_usedSounds;
|
|
|
|
bool CSfxManager::m_muted;
|
2017-01-23 07:22:17 +00:00
|
|
|
bool CSfxManager::m_auxProcessingEnabled = false;
|
2016-12-29 05:53:00 +00:00
|
|
|
float CSfxManager::m_reverbAmount = 1.f;
|
2017-01-23 07:22:17 +00:00
|
|
|
CSfxManager::EAuxEffect CSfxManager::m_activeEffect = CSfxManager::EAuxEffect::None;
|
|
|
|
CSfxManager::EAuxEffect CSfxManager::m_nextEffect = CSfxManager::EAuxEffect::None;
|
2018-08-28 05:44:16 +00:00
|
|
|
amuse::ObjToken<amuse::Listener> CSfxManager::m_listener;
|
2016-12-29 05:53:00 +00:00
|
|
|
|
|
|
|
u16 CSfxManager::kMaxPriority;
|
|
|
|
u16 CSfxManager::kMedPriority;
|
|
|
|
u16 CSfxManager::kInternalInvalidSfxId;
|
|
|
|
u32 CSfxManager::kAllAreas;
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
bool CSfxManager::LoadTranslationTable(CSimplePool* pool, const SObjectTag* tag) {
|
|
|
|
if (!tag)
|
|
|
|
return false;
|
|
|
|
mpSfxTranslationTable = nullptr;
|
|
|
|
mpSfxTranslationTableTok = pool->GetObj(*tag);
|
|
|
|
if (!mpSfxTranslationTableTok)
|
2016-12-29 05:53:00 +00:00
|
|
|
return false;
|
2018-12-08 05:30:43 +00:00
|
|
|
return true;
|
2016-12-29 05:53:00 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
bool CSfxManager::CSfxWrapper::IsPlaying() const {
|
|
|
|
if (CBaseSfxWrapper::IsPlaying() && x1c_voiceHandle)
|
|
|
|
return x1c_voiceHandle->state() == amuse::VoiceState::Playing;
|
|
|
|
return false;
|
2016-12-29 05:53:00 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CSfxManager::CSfxWrapper::Play() {
|
|
|
|
x1c_voiceHandle = CAudioSys::GetAmuseEngine().fxStart(x18_sfxId, x20_vol, x22_pan);
|
|
|
|
if (x1c_voiceHandle) {
|
|
|
|
if (CSfxManager::IsAuxProcessingEnabled() && UseAcoustics())
|
|
|
|
x1c_voiceHandle->setReverbVol(m_reverbAmount);
|
|
|
|
SetPlaying(true);
|
|
|
|
}
|
|
|
|
x24_ready = false;
|
2016-12-29 05:53:00 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CSfxManager::CSfxWrapper::Stop() {
|
|
|
|
if (x1c_voiceHandle) {
|
|
|
|
x1c_voiceHandle->keyOff();
|
|
|
|
SetPlaying(false);
|
|
|
|
x1c_voiceHandle.reset();
|
|
|
|
}
|
2016-12-29 05:53:00 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
bool CSfxManager::CSfxWrapper::Ready() {
|
|
|
|
if (IsLooped())
|
|
|
|
return true;
|
|
|
|
return x24_ready;
|
2017-01-23 07:22:17 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
u16 CSfxManager::CSfxWrapper::GetSfxId() const { return x18_sfxId; }
|
|
|
|
|
|
|
|
void CSfxManager::CSfxWrapper::UpdateEmitterSilent() {
|
|
|
|
if (x1c_voiceHandle)
|
|
|
|
x1c_voiceHandle->setVolume(1.f / 127.f);
|
2017-01-23 07:22:17 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CSfxManager::CSfxWrapper::UpdateEmitter() {
|
|
|
|
if (x1c_voiceHandle)
|
|
|
|
x1c_voiceHandle->setVolume(x20_vol);
|
2017-01-23 07:22:17 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CSfxManager::CSfxWrapper::SetReverb(float rev) {
|
|
|
|
if (x1c_voiceHandle && IsAuxProcessingEnabled() && UseAcoustics())
|
|
|
|
x1c_voiceHandle->setReverbVol(rev);
|
2017-01-23 07:22:17 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
bool CSfxManager::CSfxEmitterWrapper::IsPlaying() const {
|
|
|
|
if (IsLooped())
|
|
|
|
return CBaseSfxWrapper::IsPlaying();
|
|
|
|
if (CBaseSfxWrapper::IsPlaying() && x50_emitterHandle)
|
|
|
|
return x50_emitterHandle->getVoice()->state() == amuse::VoiceState::Playing;
|
|
|
|
return false;
|
2016-12-29 05:53:00 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CSfxManager::CSfxEmitterWrapper::Play() {
|
|
|
|
if (CSfxManager::IsAuxProcessingEnabled() && UseAcoustics())
|
|
|
|
x1a_reverb = m_reverbAmount;
|
|
|
|
else
|
|
|
|
x1a_reverb = 0.f;
|
2016-12-29 05:53:00 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
zeus::simd_floats pos(x24_parmData.x0_pos.mSimd);
|
|
|
|
zeus::simd_floats dir(x24_parmData.xc_dir.mSimd);
|
|
|
|
x50_emitterHandle = CAudioSys::GetAmuseEngine().addEmitter(
|
|
|
|
pos.data(), dir.data(), x24_parmData.x18_maxDist, x24_parmData.x1c_distComp, x24_parmData.x24_sfxId,
|
|
|
|
x24_parmData.x27_minVol, x24_parmData.x26_maxVol, (x24_parmData.x20_flags & 0x8) != 0);
|
2016-12-29 05:53:00 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
if (x50_emitterHandle)
|
|
|
|
SetPlaying(true);
|
|
|
|
x54_ready = false;
|
2016-12-29 05:53:00 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CSfxManager::CSfxEmitterWrapper::Stop() {
|
|
|
|
if (x50_emitterHandle) {
|
|
|
|
x50_emitterHandle->getVoice()->keyOff();
|
|
|
|
SetPlaying(false);
|
|
|
|
x50_emitterHandle.reset();
|
|
|
|
}
|
2016-12-29 05:53:00 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
bool CSfxManager::CSfxEmitterWrapper::Ready() {
|
|
|
|
if (IsLooped())
|
|
|
|
return true;
|
|
|
|
return x54_ready;
|
2017-01-23 07:22:17 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
CSfxManager::ESfxAudibility CSfxManager::CSfxEmitterWrapper::GetAudible(const zeus::CVector3f& vec) {
|
|
|
|
float magSq = (x24_parmData.x0_pos - vec).magSquared();
|
|
|
|
float maxDist = x24_parmData.x18_maxDist * x24_parmData.x18_maxDist;
|
|
|
|
if (magSq < maxDist * 0.25f)
|
|
|
|
return ESfxAudibility::Aud3;
|
|
|
|
else if (magSq < maxDist * 0.5f)
|
|
|
|
return ESfxAudibility::Aud2;
|
|
|
|
else if (magSq < maxDist)
|
|
|
|
return ESfxAudibility::Aud1;
|
|
|
|
return ESfxAudibility::Aud0;
|
2017-01-23 07:22:17 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
u16 CSfxManager::CSfxEmitterWrapper::GetSfxId() const { return x24_parmData.x24_sfxId; }
|
|
|
|
|
|
|
|
void CSfxManager::CSfxEmitterWrapper::UpdateEmitterSilent() {
|
|
|
|
if (x50_emitterHandle) {
|
|
|
|
zeus::simd_floats pos(x24_parmData.x0_pos.mSimd);
|
|
|
|
zeus::simd_floats dir(x24_parmData.xc_dir.mSimd);
|
|
|
|
x50_emitterHandle->setVectors(pos.data(), dir.data());
|
|
|
|
x50_emitterHandle->setMaxVol(1.f / 127.f);
|
|
|
|
}
|
|
|
|
x55_cachedMaxVol = x24_parmData.x26_maxVol;
|
2017-01-23 07:22:17 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CSfxManager::CSfxEmitterWrapper::UpdateEmitter() {
|
|
|
|
if (x50_emitterHandle) {
|
|
|
|
zeus::simd_floats pos(x24_parmData.x0_pos.mSimd);
|
|
|
|
zeus::simd_floats dir(x24_parmData.xc_dir.mSimd);
|
|
|
|
x50_emitterHandle->setVectors(pos.data(), dir.data());
|
|
|
|
x50_emitterHandle->setMaxVol(x55_cachedMaxVol);
|
|
|
|
}
|
2017-01-23 07:22:17 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CSfxManager::CSfxEmitterWrapper::SetReverb(float rev) {
|
|
|
|
if (IsAuxProcessingEnabled() && UseAcoustics())
|
|
|
|
x1a_reverb = rev;
|
2017-01-23 07:22:17 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CSfxManager::SetChannel(ESfxChannels chan) {
|
|
|
|
if (m_currentChannel == chan)
|
|
|
|
return;
|
|
|
|
if (m_currentChannel != ESfxChannels::Invalid)
|
|
|
|
TurnOffChannel(m_currentChannel);
|
|
|
|
TurnOnChannel(chan);
|
|
|
|
m_currentChannel = chan;
|
2017-04-15 05:32:25 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CSfxManager::KillAll(ESfxChannels chan) {
|
2020-04-07 16:47:55 +00:00
|
|
|
CSfxChannel& chanObj = m_channels[size_t(chan)];
|
2018-12-08 05:30:43 +00:00
|
|
|
for (auto it = chanObj.x48_handles.begin(); it != chanObj.x48_handles.end();) {
|
|
|
|
const CSfxHandle& handle = *it;
|
|
|
|
handle->Stop();
|
|
|
|
handle->Release();
|
|
|
|
handle->Close();
|
|
|
|
it = chanObj.x48_handles.erase(it);
|
|
|
|
}
|
2017-01-23 07:22:17 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CSfxManager::TurnOnChannel(ESfxChannels chan) {
|
2020-04-07 16:47:55 +00:00
|
|
|
CSfxChannel& chanObj = m_channels[size_t(chan)];
|
2018-12-08 05:30:43 +00:00
|
|
|
m_currentChannel = chan;
|
|
|
|
m_doUpdate = true;
|
|
|
|
if (chanObj.x44_listenerActive) {
|
|
|
|
for (const CSfxHandle& handle : chanObj.x48_handles) {
|
|
|
|
handle->UpdateEmitter();
|
2017-01-23 07:22:17 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSfxManager::TurnOffChannel(ESfxChannels chan) {
|
2020-04-07 16:47:55 +00:00
|
|
|
CSfxChannel& chanObj = m_channels[size_t(chan)];
|
2018-12-08 05:30:43 +00:00
|
|
|
for (auto it = chanObj.x48_handles.begin(); it != chanObj.x48_handles.end();) {
|
|
|
|
const CSfxHandle& handle = *it;
|
|
|
|
if (handle->IsLooped()) {
|
|
|
|
handle->UpdateEmitterSilent();
|
|
|
|
} else {
|
|
|
|
handle->Stop();
|
|
|
|
handle->Close();
|
|
|
|
it = chanObj.x48_handles.erase(it);
|
|
|
|
continue;
|
2017-01-23 07:22:17 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
++it;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto it = chanObj.x48_handles.begin(); it != chanObj.x48_handles.end();) {
|
|
|
|
const CSfxHandle& handle = *it;
|
|
|
|
if (!handle->IsLooped()) {
|
|
|
|
handle->Release();
|
|
|
|
handle->Close();
|
|
|
|
it = chanObj.x48_handles.erase(it);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSfxManager::AddListener(ESfxChannels channel, const zeus::CVector3f& pos, const zeus::CVector3f& dir,
|
|
|
|
const zeus::CVector3f& heading, const zeus::CVector3f& up, float frontRadius,
|
|
|
|
float surroundRadius, float soundSpeed, u32 flags /* 0x1 for doppler */, float vol) {
|
|
|
|
if (m_listener)
|
|
|
|
CAudioSys::GetAmuseEngine().removeListener(m_listener.get());
|
|
|
|
zeus::simd_floats p(pos.mSimd);
|
|
|
|
zeus::simd_floats d(dir.mSimd);
|
|
|
|
zeus::simd_floats h(heading.mSimd);
|
|
|
|
zeus::simd_floats u(up.mSimd);
|
|
|
|
m_listener = CAudioSys::GetAmuseEngine().addListener(p.data(), d.data(), h.data(), u.data(), frontRadius,
|
|
|
|
surroundRadius, soundSpeed, vol);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSfxManager::UpdateListener(const zeus::CVector3f& pos, const zeus::CVector3f& dir, const zeus::CVector3f& heading,
|
|
|
|
const zeus::CVector3f& up, float vol) {
|
|
|
|
if (m_listener) {
|
2018-12-08 01:49:15 +00:00
|
|
|
zeus::simd_floats p(pos.mSimd);
|
|
|
|
zeus::simd_floats d(dir.mSimd);
|
|
|
|
zeus::simd_floats h(heading.mSimd);
|
|
|
|
zeus::simd_floats u(up.mSimd);
|
2018-12-08 05:30:43 +00:00
|
|
|
m_listener->setVectors(p.data(), d.data(), h.data(), u.data());
|
|
|
|
m_listener->setVolume(vol);
|
|
|
|
}
|
2016-04-17 02:50:45 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
s16 CSfxManager::GetRank(CBaseSfxWrapper* sfx) {
|
2020-04-07 16:47:55 +00:00
|
|
|
const CSfxChannel& chanObj = m_channels[size_t(m_currentChannel)];
|
|
|
|
if (!sfx->IsInArea()) {
|
2018-12-08 05:30:43 +00:00
|
|
|
return 0;
|
2020-04-07 16:47:55 +00:00
|
|
|
}
|
2017-01-23 07:22:17 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
s16 rank = sfx->GetPriority() / 4;
|
2020-04-07 16:47:55 +00:00
|
|
|
if (sfx->IsPlaying()) {
|
2018-12-08 05:30:43 +00:00
|
|
|
++rank;
|
2020-04-07 16:47:55 +00:00
|
|
|
}
|
2017-01-23 07:22:17 +00:00
|
|
|
|
2020-04-07 16:47:55 +00:00
|
|
|
if (sfx->IsLooped()) {
|
2018-12-08 05:30:43 +00:00
|
|
|
rank -= 2;
|
2020-04-07 16:47:55 +00:00
|
|
|
}
|
2017-01-23 07:22:17 +00:00
|
|
|
|
2020-04-07 16:47:55 +00:00
|
|
|
if (sfx->Ready() && !sfx->IsPlaying()) {
|
2018-12-08 05:30:43 +00:00
|
|
|
rank += 3;
|
2020-04-07 16:47:55 +00:00
|
|
|
}
|
2017-01-23 07:22:17 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
if (chanObj.x44_listenerActive) {
|
2020-04-07 16:47:55 +00:00
|
|
|
const ESfxAudibility aud = sfx->GetAudible(chanObj.x0_pos);
|
|
|
|
if (aud == ESfxAudibility::Aud0) {
|
2018-12-08 05:30:43 +00:00
|
|
|
return 0;
|
2020-04-07 16:47:55 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
rank += int(aud) / 2;
|
|
|
|
}
|
2017-01-23 07:22:17 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
return rank;
|
2017-01-23 07:22:17 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CSfxManager::ApplyReverb() {
|
2020-04-07 16:47:55 +00:00
|
|
|
const CSfxChannel& chanObj = m_channels[size_t(m_currentChannel)];
|
2018-12-08 05:30:43 +00:00
|
|
|
for (const CSfxHandle& handle : chanObj.x48_handles) {
|
|
|
|
handle->SetReverb(m_reverbAmount);
|
|
|
|
}
|
2017-01-23 07:22:17 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
float CSfxManager::GetReverbAmount() { return m_reverbAmount; }
|
2017-01-23 07:22:17 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CSfxManager::PitchBend(const CSfxHandle& handle, float pitch) {
|
|
|
|
if (!handle)
|
|
|
|
return;
|
|
|
|
if (!handle->IsPlaying())
|
|
|
|
CSfxManager::Update(0.f);
|
|
|
|
if (handle->IsPlaying()) {
|
|
|
|
m_doUpdate = true;
|
|
|
|
handle->GetVoice()->setPitchWheel(pitch);
|
|
|
|
}
|
2017-04-08 20:40:36 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CSfxManager::SfxVolume(const CSfxHandle& handle, float vol) {
|
|
|
|
if (!handle)
|
|
|
|
return;
|
|
|
|
if (handle->IsEmitter()) {
|
|
|
|
CSfxWrapper& wrapper = static_cast<CSfxWrapper&>(*handle);
|
|
|
|
wrapper.SetVolume(vol);
|
|
|
|
}
|
|
|
|
if (handle->IsPlaying())
|
|
|
|
handle->GetVoice()->setVolume(vol);
|
2017-04-09 06:14:22 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CSfxManager::SfxSpan(const CSfxHandle& handle, float span) {
|
|
|
|
if (!handle)
|
|
|
|
return;
|
|
|
|
if (handle->IsPlaying())
|
|
|
|
handle->GetVoice()->setSurroundPan(span);
|
2017-08-25 06:18:09 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
u16 CSfxManager::TranslateSFXID(u16 id) {
|
|
|
|
if (mpSfxTranslationTable == nullptr)
|
|
|
|
return 0;
|
2016-04-28 04:52:41 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
u16 index = id;
|
|
|
|
if (index >= mpSfxTranslationTable->size())
|
|
|
|
return 0;
|
2016-04-25 05:03:38 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
u16 ret = (*mpSfxTranslationTable)[index];
|
|
|
|
if (ret == 0xffff)
|
|
|
|
return 0;
|
|
|
|
return ret;
|
2017-08-29 13:17:52 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
bool CSfxManager::PlaySound(const CSfxManager::CSfxHandle& handle) { return false; }
|
2017-01-23 07:22:17 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CSfxManager::StopSound(const CSfxHandle& handle) {
|
|
|
|
if (!handle)
|
|
|
|
return;
|
|
|
|
m_doUpdate = true;
|
|
|
|
handle->Stop();
|
|
|
|
handle->Release();
|
2020-04-07 16:47:55 +00:00
|
|
|
CSfxChannel& chanObj = m_channels[size_t(m_currentChannel)];
|
2018-12-08 05:30:43 +00:00
|
|
|
handle->Close();
|
|
|
|
chanObj.x48_handles.erase(handle);
|
2016-12-29 05:53:00 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CSfxManager::SfxStop(const CSfxHandle& handle) { StopSound(handle); }
|
2016-12-29 05:53:00 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
CSfxHandle CSfxManager::SfxStart(u16 id, float vol, float pan, bool useAcoustics, s16 prio, bool looped, s32 areaId) {
|
|
|
|
if (m_muted || id == 0xffff)
|
|
|
|
return {};
|
2017-06-12 04:23:34 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
m_doUpdate = true;
|
|
|
|
CSfxHandle wrapper = std::make_shared<CSfxWrapper>(looped, prio, id, vol, pan, useAcoustics, areaId);
|
2020-04-07 16:47:55 +00:00
|
|
|
CSfxChannel& chanObj = m_channels[size_t(m_currentChannel)];
|
2018-12-08 05:30:43 +00:00
|
|
|
chanObj.x48_handles.insert(wrapper);
|
|
|
|
return wrapper;
|
2017-04-13 19:28:31 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
bool CSfxManager::IsPlaying(const CSfxHandle& handle) {
|
|
|
|
if (!handle)
|
|
|
|
return false;
|
|
|
|
return handle->IsPlaying();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSfxManager::RemoveEmitter(const CSfxHandle& handle) { StopSound(handle); }
|
|
|
|
|
|
|
|
void CSfxManager::UpdateEmitter(const CSfxHandle& handle, const zeus::CVector3f& pos, const zeus::CVector3f& dir,
|
|
|
|
float maxVol) {
|
|
|
|
if (!handle || !handle->IsEmitter() || !handle->IsPlaying())
|
|
|
|
return;
|
|
|
|
m_doUpdate = true;
|
|
|
|
CSfxEmitterWrapper& emitter = static_cast<CSfxEmitterWrapper&>(*handle);
|
|
|
|
emitter.GetEmitterData().x0_pos = pos;
|
|
|
|
emitter.GetEmitterData().xc_dir = dir;
|
|
|
|
emitter.GetEmitterData().x26_maxVol = maxVol;
|
|
|
|
amuse::Emitter& h = *emitter.GetHandle();
|
|
|
|
zeus::simd_floats p(pos.mSimd);
|
|
|
|
zeus::simd_floats d(dir.mSimd);
|
|
|
|
h.setVectors(p.data(), d.data());
|
|
|
|
h.setMaxVol(maxVol);
|
|
|
|
}
|
|
|
|
|
|
|
|
CSfxHandle CSfxManager::AddEmitter(u16 id, const zeus::CVector3f& pos, const zeus::CVector3f& dir, bool useAcoustics,
|
|
|
|
bool looped, s16 prio, s32 areaId) {
|
2020-08-01 02:46:09 +00:00
|
|
|
const CAudioSys::C3DEmitterParmData parmData{
|
|
|
|
.x0_pos = pos,
|
|
|
|
.xc_dir = dir,
|
|
|
|
.x18_maxDist = 150.f,
|
|
|
|
.x1c_distComp = 0.1f,
|
|
|
|
.x20_flags = 1, // Continuous parameter update
|
|
|
|
.x24_sfxId = id,
|
|
|
|
.x26_maxVol = 1.f,
|
|
|
|
.x27_minVol = 0.165f,
|
|
|
|
.x28_important = false,
|
|
|
|
.x29_prio = 0x7f,
|
|
|
|
};
|
2018-12-08 05:30:43 +00:00
|
|
|
return AddEmitter(parmData, useAcoustics, prio, looped, areaId);
|
2017-08-19 06:52:13 +00:00
|
|
|
}
|
|
|
|
|
2017-03-18 05:12:27 +00:00
|
|
|
CSfxHandle CSfxManager::AddEmitter(u16 id, const zeus::CVector3f& pos, const zeus::CVector3f& dir, float vol,
|
2018-12-08 05:30:43 +00:00
|
|
|
bool useAcoustics, bool looped, s16 prio, s32 areaId) {
|
2020-08-01 02:46:09 +00:00
|
|
|
const CAudioSys::C3DEmitterParmData parmData{
|
|
|
|
.x0_pos = pos,
|
|
|
|
.xc_dir = dir,
|
|
|
|
.x18_maxDist = 150.f,
|
|
|
|
.x1c_distComp = 0.1f,
|
|
|
|
.x20_flags = 1, // Continuous parameter update
|
|
|
|
.x24_sfxId = id,
|
|
|
|
.x26_maxVol = std::max(vol, 0.165f),
|
|
|
|
.x27_minVol = 0.165f,
|
|
|
|
.x28_important = false,
|
|
|
|
.x29_prio = 0x7f,
|
|
|
|
};
|
2018-12-08 05:30:43 +00:00
|
|
|
return AddEmitter(parmData, useAcoustics, prio, looped, areaId);
|
|
|
|
}
|
|
|
|
|
|
|
|
CSfxHandle CSfxManager::AddEmitter(const CAudioSys::C3DEmitterParmData& parmData, bool useAcoustics, s16 prio,
|
|
|
|
bool looped, s32 areaId) {
|
|
|
|
if (m_muted || parmData.x24_sfxId == 0xffff)
|
|
|
|
return {};
|
|
|
|
|
|
|
|
CAudioSys::C3DEmitterParmData data = parmData;
|
|
|
|
if (looped)
|
|
|
|
data.x20_flags |= 0x6; // Pausable/restartable when inaudible
|
|
|
|
m_doUpdate = true;
|
|
|
|
CSfxHandle wrapper = std::make_shared<CSfxEmitterWrapper>(looped, prio, data, useAcoustics, areaId);
|
2020-04-07 16:47:55 +00:00
|
|
|
CSfxChannel& chanObj = m_channels[size_t(m_currentChannel)];
|
2018-12-08 05:30:43 +00:00
|
|
|
chanObj.x48_handles.insert(wrapper);
|
|
|
|
return wrapper;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSfxManager::StopAndRemoveAllEmitters() {
|
2020-04-07 16:47:55 +00:00
|
|
|
for (auto& chanObj : m_channels) {
|
2018-12-08 05:30:43 +00:00
|
|
|
for (auto it = chanObj.x48_handles.begin(); it != chanObj.x48_handles.end();) {
|
|
|
|
const CSfxHandle& handle = *it;
|
|
|
|
handle->Stop();
|
|
|
|
handle->Release();
|
|
|
|
handle->Close();
|
|
|
|
it = chanObj.x48_handles.erase(it);
|
2017-01-23 07:22:17 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
}
|
2016-12-29 05:53:00 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CSfxManager::EnableAuxCallback() {
|
|
|
|
m_reverbAmount = 0.f;
|
|
|
|
ApplyReverb();
|
|
|
|
if (m_activeEffect != EAuxEffect::None)
|
|
|
|
DisableAuxCallback();
|
2017-01-23 07:22:17 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
auto studio = CAudioSys::GetAmuseEngine().getDefaultStudio();
|
|
|
|
amuse::Submix& smix = studio->getAuxA();
|
|
|
|
|
|
|
|
m_activeEffect = m_nextEffect;
|
|
|
|
switch (m_activeEffect) {
|
|
|
|
case EAuxEffect::ReverbHi:
|
|
|
|
s_ReverbHiState = &smix.makeReverbHi(s_ReverbHiQueued);
|
|
|
|
break;
|
|
|
|
case EAuxEffect::Chorus:
|
|
|
|
s_ChorusState = &smix.makeChorus(s_ChorusQueued);
|
|
|
|
break;
|
|
|
|
case EAuxEffect::ReverbStd:
|
|
|
|
s_ReverbStdState = &smix.makeReverbStd(s_ReverbStdQueued);
|
|
|
|
break;
|
|
|
|
case EAuxEffect::Delay:
|
|
|
|
s_DelayState = &smix.makeDelay(s_DelayQueued);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_auxProcessingEnabled = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSfxManager::PrepareDelayCallback(const amuse::EffectDelayInfo& info) {
|
|
|
|
DisableAuxProcessing();
|
|
|
|
s_DelayQueued = info;
|
|
|
|
m_nextEffect = EAuxEffect::Delay;
|
|
|
|
if (m_reverbAmount == 0.f)
|
|
|
|
EnableAuxCallback();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSfxManager::PrepareReverbStdCallback(const amuse::EffectReverbStdInfo& info) {
|
|
|
|
DisableAuxProcessing();
|
|
|
|
s_ReverbStdQueued = info;
|
|
|
|
m_nextEffect = EAuxEffect::ReverbStd;
|
|
|
|
if (m_reverbAmount == 0.f)
|
|
|
|
EnableAuxCallback();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSfxManager::PrepareChorusCallback(const amuse::EffectChorusInfo& info) {
|
|
|
|
DisableAuxProcessing();
|
|
|
|
s_ChorusQueued = info;
|
|
|
|
m_nextEffect = EAuxEffect::Chorus;
|
|
|
|
if (m_reverbAmount == 0.f)
|
|
|
|
EnableAuxCallback();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSfxManager::PrepareReverbHiCallback(const amuse::EffectReverbHiInfo& info) {
|
|
|
|
DisableAuxProcessing();
|
|
|
|
s_ReverbHiQueued = info;
|
|
|
|
m_nextEffect = EAuxEffect::ReverbHi;
|
|
|
|
if (m_reverbAmount == 0.f)
|
|
|
|
EnableAuxCallback();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSfxManager::DisableAuxCallback() {
|
|
|
|
auto studio = CAudioSys::GetAmuseEngine().getDefaultStudio();
|
|
|
|
studio->getAuxA().clearEffects();
|
|
|
|
|
|
|
|
switch (m_activeEffect) {
|
|
|
|
case EAuxEffect::ReverbHi:
|
|
|
|
s_ReverbHiState = nullptr;
|
|
|
|
break;
|
|
|
|
case EAuxEffect::Chorus:
|
|
|
|
s_ChorusState = nullptr;
|
|
|
|
break;
|
|
|
|
case EAuxEffect::ReverbStd:
|
|
|
|
s_ReverbStdState = nullptr;
|
|
|
|
break;
|
|
|
|
case EAuxEffect::Delay:
|
|
|
|
s_DelayState = nullptr;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_activeEffect = EAuxEffect::None;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSfxManager::DisableAuxProcessing() {
|
|
|
|
m_nextEffect = EAuxEffect::None;
|
|
|
|
m_auxProcessingEnabled = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSfxManager::SetActiveAreas(const rstl::reserved_vector<TAreaId, 10>& areas) {
|
2020-04-07 16:47:55 +00:00
|
|
|
const CSfxChannel& chanObj = m_channels[size_t(m_currentChannel)];
|
2018-12-08 05:30:43 +00:00
|
|
|
|
|
|
|
for (const CSfxHandle& hnd : chanObj.x48_handles) {
|
2020-04-07 16:47:55 +00:00
|
|
|
const TAreaId sndArea = hnd->GetArea();
|
2018-12-08 05:30:43 +00:00
|
|
|
if (sndArea == kInvalidAreaId) {
|
|
|
|
hnd->SetInArea(true);
|
|
|
|
} else {
|
|
|
|
bool inArea = false;
|
2020-04-07 16:47:55 +00:00
|
|
|
for (const TAreaId id : areas) {
|
2018-12-08 05:30:43 +00:00
|
|
|
if (sndArea == id) {
|
|
|
|
inArea = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_doUpdate = true;
|
|
|
|
hnd->SetInArea(inArea);
|
2017-01-23 07:22:17 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
}
|
2017-01-22 01:40:12 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CSfxManager::Update(float dt) {
|
2020-04-07 16:47:55 +00:00
|
|
|
CSfxChannel& chanObj = m_channels[size_t(m_currentChannel)];
|
2017-03-31 19:51:40 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
for (auto it = chanObj.x48_handles.begin(); it != chanObj.x48_handles.end();) {
|
|
|
|
const CSfxHandle& handle = *it;
|
|
|
|
if (!handle->IsLooped()) {
|
|
|
|
float timeRem = handle->GetTimeRemaining();
|
|
|
|
handle->SetTimeRemaining(timeRem - dt);
|
|
|
|
if (timeRem < 0.f) {
|
|
|
|
handle->Stop();
|
|
|
|
m_doUpdate = true;
|
|
|
|
handle->Close();
|
|
|
|
it = chanObj.x48_handles.erase(it);
|
|
|
|
continue;
|
|
|
|
}
|
2017-03-31 19:51:40 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
++it;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_doUpdate) {
|
|
|
|
std::vector<CSfxHandle> rankedSfx;
|
|
|
|
rankedSfx.reserve(chanObj.x48_handles.size());
|
|
|
|
for (const CSfxHandle& handle : chanObj.x48_handles) {
|
|
|
|
rankedSfx.push_back(handle);
|
|
|
|
handle->SetRank(GetRank(handle.get()));
|
2017-01-23 07:22:17 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
std::sort(rankedSfx.begin(), rankedSfx.end(),
|
|
|
|
[](const CSfxHandle& a, const CSfxHandle& b) -> bool { return a->GetRank() < b->GetRank(); });
|
2017-01-23 07:22:17 +00:00
|
|
|
|
2019-06-12 02:05:17 +00:00
|
|
|
for (size_t i = 48; i < rankedSfx.size(); ++i) {
|
2018-12-08 05:30:43 +00:00
|
|
|
const CSfxHandle& handle = rankedSfx[i];
|
|
|
|
if (handle->IsPlaying()) {
|
|
|
|
handle->Stop();
|
|
|
|
handle->Close();
|
|
|
|
chanObj.x48_handles.erase(handle);
|
|
|
|
}
|
|
|
|
}
|
2017-01-23 07:22:17 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
for (const CSfxHandle& handle : rankedSfx) {
|
|
|
|
if (handle->IsPlaying() && !handle->IsInArea()) {
|
|
|
|
handle->Stop();
|
|
|
|
handle->Close();
|
|
|
|
chanObj.x48_handles.erase(handle);
|
|
|
|
}
|
2017-01-23 07:22:17 +00:00
|
|
|
}
|
|
|
|
|
2019-04-07 05:14:48 +00:00
|
|
|
#ifndef URDE_MSAN
|
2018-12-08 05:30:43 +00:00
|
|
|
for (const CSfxHandle& handle : chanObj.x48_handles) {
|
|
|
|
if (handle->IsPlaying())
|
|
|
|
continue;
|
|
|
|
if (handle->Ready() && handle->IsInArea())
|
|
|
|
handle->Play();
|
2017-01-23 07:22:17 +00:00
|
|
|
}
|
2019-04-07 05:14:48 +00:00
|
|
|
#endif
|
2017-01-23 07:22:17 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
m_doUpdate = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto it = chanObj.x48_handles.begin(); it != chanObj.x48_handles.end();) {
|
|
|
|
const CSfxHandle& handle = *it;
|
|
|
|
if (!handle->IsPlaying() && !handle->IsLooped()) {
|
|
|
|
handle->Stop();
|
|
|
|
handle->Release();
|
|
|
|
m_doUpdate = true;
|
|
|
|
handle->Close();
|
|
|
|
it = chanObj.x48_handles.erase(it);
|
|
|
|
continue;
|
2017-01-23 07:22:17 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
++it;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_auxProcessingEnabled && m_reverbAmount < 1.f) {
|
|
|
|
m_reverbAmount = std::min(1.f, dt / 0.1f + m_reverbAmount);
|
|
|
|
ApplyReverb();
|
|
|
|
} else if (!m_auxProcessingEnabled && m_reverbAmount > 0.f) {
|
|
|
|
m_reverbAmount = std::max(0.f, m_reverbAmount - dt / (2.f * 0.1f));
|
|
|
|
ApplyReverb();
|
|
|
|
if (m_reverbAmount == 0.f) {
|
|
|
|
DisableAuxCallback();
|
|
|
|
EnableAuxCallback();
|
2017-01-23 07:22:17 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
}
|
2016-12-29 05:53:00 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
if (mpSfxTranslationTableTok.IsLoaded() && !mpSfxTranslationTable)
|
|
|
|
mpSfxTranslationTable = mpSfxTranslationTableTok.GetObj();
|
2016-09-16 23:43:33 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CSfxManager::Shutdown() {
|
|
|
|
mpSfxTranslationTable = nullptr;
|
|
|
|
mpSfxTranslationTableTok = TLockedToken<std::vector<u16>>{};
|
|
|
|
StopAndRemoveAllEmitters();
|
|
|
|
DisableAuxCallback();
|
2017-01-22 01:40:12 +00:00
|
|
|
}
|
|
|
|
|
2021-04-10 08:42:06 +00:00
|
|
|
} // namespace metaforce
|