#include "nw4r/snd/snd_AxManager.h" /* Original source: * kiwi515/ogws * src/nw4r/snd/snd_AxManager.cpp */ /******************************************************************************* * headers */ #include // std::memset #include "common.h" #include "nw4r/snd/snd_BiquadFilterPreset.h" #include "nw4r/snd/snd_FxBase.h" #include "nw4r/snd/snd_global.h" #include "nw4r/snd/snd_MoveValue.h" #include "nw4r/snd/snd_Voice.h" #include "nw4r/snd/snd_VoiceManager.h" #include "nw4r/ut/ut_algorithm.h" // ut::Clamp #include "nw4r/ut/ut_Lock.h" // ut::AutoInterruptLock #include // DCFlushRange #include // AICheckInit #include #include #include #include "nw4r/NW4RAssert.hpp" /******************************************************************************* * types */ // forward declarations namespace nw4r { namespace snd { class BiquadFilterCallback; }} /******************************************************************************* * variables */ namespace nw4r { namespace snd { namespace detail { // .bss byte_t AxManager::sZeroBuffer[ZERO_BUFFER_SIZE]; BiquadFilterCallback const *AxManager::sBiquadFilterCallbackTable[128]; // .sbss BiquadFilterLpf AxManager::sBiquadFilterLpf; BiquadFilterHpf AxManager::sBiquadFilterHpf; BiquadFilterBpf512 AxManager::sBiquadFilterBpf512; BiquadFilterBpf1024 AxManager::sBiquadFilterBpf1024; BiquadFilterBpf2048 AxManager::sBiquadFilterBpf2048; }}} // namespace nw4r::snd::detail /******************************************************************************* * functions */ namespace nw4r { namespace snd { namespace detail { AxManager::AxManager() : mOutputMode (OUTPUT_MODE_STEREO), mZeroBufferAddress (nullptr), mInitialized (false), mUpdateVoicePrioFlag (true), mOldAidCallback (nullptr), mResetReadyCounter (-1), mSrcType(AxVoice::SRC_4TAP_AUTO) { mMainOutVolume.InitValue(1.0f); mMasterVolume.InitValue(1.0f); mVolumeForReset.InitValue(1.0f); for (int i = 0; i < AUX_BUS_NUM; i++) { mAuxFadeVolume[i].InitValue(1.0f); mAuxUserVolume[i].InitValue(1.0f); mAuxCallback[i] = nullptr; mAuxCallbackContext[i] = 0; mEffectProcessTick[i] = 0; } } AxManager &AxManager::GetInstance() { static AxManager instance; return instance; } #pragma push #pragma ppc_iro_level 0 // somehow this got turned off??? void AxManager::Init() { if (!mInitialized) { NW4RAssertMessage_Line(104, AICheckInit(), "not initialized AI \n"); std::memset(sZeroBuffer, 0, ZERO_BUFFER_SIZE); DCFlushRange(sZeroBuffer, ZERO_BUFFER_SIZE); mZeroBufferAddress = sZeroBuffer; ut::AutoInterruptLock lock; AXGetAuxACallback(&mAuxCallback[AUX_A], &mAuxCallbackContext[AUX_A]); AXGetAuxBCallback(&mAuxCallback[AUX_B], &mAuxCallbackContext[AUX_B]); AXGetAuxCCallback(&mAuxCallback[AUX_C], &mAuxCallbackContext[AUX_C]); AXRegisterAuxACallback(nullptr, nullptr); AXRegisterAuxBCallback(nullptr, nullptr); AXRegisterAuxCCallback(nullptr, nullptr); mNextAxRegisterCallback = AXRegisterCallback(AxCallbackFunc); std::memset(sBiquadFilterCallbackTable, 0, sizeof sBiquadFilterCallbackTable); SetBiquadFilterCallback(1, &sBiquadFilterLpf); SetBiquadFilterCallback(2, &sBiquadFilterHpf); SetBiquadFilterCallback(3, &sBiquadFilterBpf512); SetBiquadFilterCallback(4, &sBiquadFilterBpf1024); SetBiquadFilterCallback(5, &sBiquadFilterBpf2048); mInitialized = true; } } #pragma pop void AxManager::Shutdown() { if (!mInitialized) return; AXRegisterCallback(mNextAxRegisterCallback); ShutdownEffect(AUX_A); ShutdownEffect(AUX_B); ShutdownEffect(AUX_C); AXRegisterAuxACallback(mAuxCallback[AUX_A], mAuxCallbackContext[AUX_A]); AXRegisterAuxBCallback(mAuxCallback[AUX_B], mAuxCallbackContext[AUX_B]); AXRegisterAuxCCallback(mAuxCallback[AUX_C], mAuxCallbackContext[AUX_C]); for (int i = 0; i < AUX_BUS_NUM; i++) { mAuxCallback[i] = nullptr; mAuxCallbackContext[i] = nullptr; } mZeroBufferAddress = nullptr; mInitialized = false; } f32 AxManager::GetOutputVolume() const { f32 volume = mMasterVolume.GetValue(); return volume; } void AxManager::Update() { for (int i = AUX_A; i < AUX_BUS_NUM; i++) { bool updateFlag = false; if (!mAuxUserVolume[i].IsFinished()) { mAuxUserVolume[i].Update(); updateFlag = true; } if (!mAuxFadeVolume[i].IsFinished()) { mAuxFadeVolume[i].Update(); if (mAuxFadeVolume[i].IsFinished()) ShutdownEffect(static_cast(i)); updateFlag = true; } if (updateFlag) { f32 returnVolumeFloat = 1.0f; returnVolumeFloat *= ut::Clamp(mAuxUserVolume[i].GetValue(), 0.0f, 1.0f); returnVolumeFloat *= ut::Clamp(mAuxFadeVolume[i].GetValue(), 0.0f, 1.0f); u16 returnVolume = AUX_RETURN_VOLUME_MAX * returnVolumeFloat; switch (i) { case AUX_A: AXSetAuxAReturnVolume(returnVolume); break; case AUX_B: AXSetAuxBReturnVolume(returnVolume); break; case AUX_C: AXSetAuxCReturnVolume(returnVolume); break; } } } if (!mMasterVolume.IsFinished()) { mMasterVolume.Update(); VoiceManager::GetInstance().UpdateAllVoicesSync(Voice::UPDATE_VE); } if (!mVolumeForReset.IsFinished()) mVolumeForReset.Update(); if (!mMainOutVolume.IsFinished()) mMainOutVolume.Update(); f32 volume = mMainOutVolume.GetValue(); volume *= mVolumeForReset.GetValue(); volume = ut::Clamp(volume, 0.0f, 1.0f); AXSetMasterVolume(AUX_RETURN_VOLUME_MAX * volume); } void const *AxManager::GetZeroBufferAddress() { NW4RAssertMessage_Line(261, mZeroBufferAddress, "Zero buffer is not created."); return mZeroBufferAddress; } void AxManager::RegisterCallback(CallbackListNode *node, AXFrameCallback *callback) { ut::AutoInterruptLock lock; node->callback = callback; mCallbackList.PushBack(node); } void AxManager::UnregisterCallback(CallbackListNode *node) { ut::AutoInterruptLock lock; mCallbackList.Erase(node); } void AxManager::SetOutputMode(OutputMode mode) { if (mOutputMode == mode) return; ut::AutoInterruptLock lock; mOutputMode = mode; switch (mode) { case OUTPUT_MODE_STEREO: AXSetMode(AX_CL_MODE_STEREO); break; case OUTPUT_MODE_SURROUND: AXSetMode(AX_CL_MODE_SURROUND); break; case OUTPUT_MODE_DPL2: AXSetMode(AX_CL_MODE_DPL2); break; case OUTPUT_MODE_MONO: AXSetMode(AX_CL_MODE_STEREO); break; } VoiceManager::GetInstance().UpdateAllVoicesSync(Voice::UPDATE_MIX); for (int bus = AUX_A; bus < AUX_BUS_NUM; bus++) { FxBase::LinkList &fxList = GetEffectList(static_cast(bus)); NW4R_RANGE_FOR(itr, fxList) itr->OnChangeOutputMode(); } if (mode == OUTPUT_MODE_DPL2) mEffectProcessTick[AUX_C] = 0; } OutputMode AxManager::GetOutputMode() { return mOutputMode; } void AxManager::SetMasterVolume(f32 volume, int frame) { if (volume < 0.0f) { volume = 0.0f; } mMasterVolume.SetTarget(volume, (frame + 2) / 3); if (frame == 0) { VoiceManager::GetInstance().UpdateAllVoicesSync(Voice::SYNC_AX_VE); } } void AxManager::SetMainOutVolume(f32 volume, int frames) { volume = ut::Clamp(volume, 0.0f, 1.0f); mMainOutVolume.SetTarget(volume, (frames + 2) / 3); } void AxManager::AxCallbackFunc() { NW4R_RANGE_FOR_NO_AUTO_INC(itr, GetInstance().mCallbackList) { decltype(itr) curItr = itr++; (*curItr->callback)(); } if (GetInstance().mNextAxRegisterCallback) (*GetInstance().mNextAxRegisterCallback)(); } bool AxManager::AppendEffect(AuxBus bus, FxBase* pFx) { if (!mAuxFadeVolume[bus].IsFinished()) { ShutdownEffect(bus); } mAuxFadeVolume[bus].SetTarget(1.0f, 0); switch (bus) { case AUX_A: { AXSetAuxAReturnVolume(AX_MAX_VOLUME); break; } case AUX_B: { AXSetAuxBReturnVolume(AX_MAX_VOLUME); break; } case AUX_C: { AXSetAuxCReturnVolume(AX_MAX_VOLUME); break; } } if (!pFx->StartUp()) { return false; } ut::AutoInterruptLock lock; if (GetEffectList(bus).IsEmpty()) { switch (bus) { case AUX_A: { AXRegisterAuxACallback(AuxCallbackFunc, reinterpret_cast(bus)); break; } case AUX_B: { AXRegisterAuxBCallback(AuxCallbackFunc, reinterpret_cast(bus)); break; } case AUX_C: { AXRegisterAuxCCallback(AuxCallbackFunc, reinterpret_cast(bus)); break; } } mAuxCallbackWaitCounter[bus] = 2; } GetEffectList(bus).PushBack(pFx); return true; } void AxManager::ClearEffect(AuxBus bus, int frame) { if (frame == 0) { ShutdownEffect(bus); if (!mAuxFadeVolume[bus].IsFinished()) { mAuxFadeVolume[bus].SetTarget(0.0f, 0); } return; } mAuxFadeVolume[bus].SetTarget(0.0f, (frame + 2) / 3); } void AxManager::ShutdownEffect(AuxBus bus) { ut::AutoInterruptLock lock; FxBase::LinkList &list = GetEffectList(bus); if (list.IsEmpty()) return; NW4R_RANGE_FOR(itr, list) itr->Shutdown(); list.Clear(); switch (bus) { case AUX_A: AXRegisterAuxACallback(nullptr, nullptr); mEffectProcessTick[AUX_A] = 0; break; case AUX_B: AXRegisterAuxBCallback(nullptr, nullptr); mEffectProcessTick[AUX_B] = 0; break; case AUX_C: AXRegisterAuxCCallback(nullptr, nullptr); mEffectProcessTick[AUX_C] = 0; break; } } void AxManager::SetBiquadFilterCallback(int type, BiquadFilterCallback const *biquad) { sBiquadFilterCallbackTable[type] = biquad; } void AxManager::AuxCallbackFunc(void* pChans, void* pContext) { int num; void* buffer[AX_DPL2_MAX]; void** ppChans = static_cast(pChans); AuxBus bus = static_cast(reinterpret_cast(pContext)); int tick = OSGetTick(); if (GetInstance().GetOutputMode() == OUTPUT_MODE_DPL2) { num = AX_DPL2_MAX; buffer[AX_DPL2_L] = ppChans[AX_DPL2_L]; buffer[AX_DPL2_R] = ppChans[AX_DPL2_R]; buffer[AX_DPL2_LS] = ppChans[AX_DPL2_LS]; buffer[AX_DPL2_RS] = ppChans[AX_DPL2_RS]; } else { num = AX_STEREO_MAX; buffer[AX_STEREO_L] = ppChans[AX_STEREO_L]; buffer[AX_STEREO_R] = ppChans[AX_STEREO_R]; buffer[AX_STEREO_S] = ppChans[AX_STEREO_S]; } if (GetInstance().mAuxCallbackWaitCounter[bus] > 0) { GetInstance().mAuxCallbackWaitCounter[bus]--; for (int i = 0; i < num; i++) { std::memset(buffer[i], 0, FX_BUFFER_SIZE); } GetInstance().mEffectProcessTick[bus] = OSGetTick() - tick; } else if (GetInstance().GetEffectList(bus).IsEmpty()) { for (int i = 0; i < num; i++) { std::memset(buffer[i], 0, FX_BUFFER_SIZE); } GetInstance().mEffectProcessTick[bus] = OSGetTick() - tick; } else { for (FxBase::LinkList::Iterator it = GetInstance().GetEffectList(bus).GetBeginIter(); it != GetInstance().GetEffectList(bus).GetEndIter(); ++it) { it->UpdateBuffer(num, buffer, FX_BUFFER_SIZE, FX_SAMPLE_FORMAT, FX_SAMPLE_RATE, GetInstance().GetOutputMode()); } GetInstance().mEffectProcessTick[bus] = OSGetTick() - tick; } } void AxManager::PrepareReset() { if (mOldAidCallback != NULL) { return; } mVolumeForReset.SetTarget(0.0f, 3); mResetReadyCounter = -1; mOldAidCallback = AIRegisterDMACallback(AiDmaCallbackFunc); } void AxManager::AiDmaCallbackFunc() { static bool finishedFlag = false; AxManager& r = GetInstance(); r.mOldAidCallback(); if (finishedFlag) { if (r.mResetReadyCounter < 0) { AXSetMaxDspCycles(0); r.mResetReadyCounter = AUX_CALLBACK_WAIT_FRAME; } } else if (r.mVolumeForReset.GetValue() == 0.0f) { finishedFlag = true; } if (r.mResetReadyCounter > 0) { r.mResetReadyCounter--; } } }}} // namespace nw4r::snd::detail