diff options
| author | robojumper <robojumper@gmail.com> | 2025-05-24 21:53:14 +0200 |
|---|---|---|
| committer | robojumper <robojumper@gmail.com> | 2025-05-25 08:52:12 +0200 |
| commit | 04d5527ebab1a17348c5030dc21df481ed49b1c6 (patch) | |
| tree | de2e15924ef2ca447b0739e826ac142fb73e8d4b /src | |
| parent | 4c62e0102547907becb945138356f401b150120e (diff) | |
Initial import of nw4r::snd from https://github.com/muff1n1634/nw4r_snd_mid2010
Co-authored-by: muff1nOS <19197077+muff1n1634@users.noreply.github.com>
Diffstat (limited to 'src')
66 files changed, 17351 insertions, 56 deletions
diff --git a/src/nw4r/snd/snd_AnimSound.cpp b/src/nw4r/snd/snd_AnimSound.cpp index 12826469..524da018 100644 --- a/src/nw4r/snd/snd_AnimSound.cpp +++ b/src/nw4r/snd/snd_AnimSound.cpp @@ -1 +1,10 @@ -// #include "nw4r/snd/snd_AnimSound.h" +/* Only implemented to the extent necessary to match data sections. */ + +#include <types.h> // nullptr +#include <decomp.h> + +#include "nw4r/snd/Util.h" + +DECOMP_FORCE(nw4r::snd::detail::Util::GetDataRefAddress0( + *static_cast<nw4r::snd::detail::Util::DataRef<char> const *>(nullptr), + nullptr)); diff --git a/src/nw4r/snd/snd_AxManager.cpp b/src/nw4r/snd/snd_AxManager.cpp index 191fbc1b..5d87fb3b 100644 --- a/src/nw4r/snd/snd_AxManager.cpp +++ b/src/nw4r/snd/snd_AxManager.cpp @@ -1 +1,373 @@ -#include "nw4r/snd/snd_AxManager.h" +#include "nw4r/snd/AxManager.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_AxManager.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <cstring> // std::memset + +#include <macros.h> +#include <types.h> + +#include "nw4r/snd/BiquadFilterPreset.h" +#include "nw4r/snd/FxBase.h" +#include "nw4r/snd/global.h" +#include "nw4r/snd/MoveValue.h" +#include "nw4r/snd/Voice.h" +#include "nw4r/snd/VoiceManager.h" + +#include "nw4r/ut/inlines.h" // ut::Clamp +#include "nw4r/ut/Lock.h" // ut::AutoInterruptLock + +#if 0 +#include <revolution/OS/OSCache.h> // DCFlushRange +#include <revolution/AI/ai.h> // AICheckInit +#include <revolution/AX/AXAux.h> +#include <revolution/AX/AXCL.h> +#include <revolution/AX/AXOut.h> +#else +#include <context_rvl.h> +#endif + +#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) +{ + 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<AuxBus>(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<AuxBus>(bus)); + + NW4R_RANGE_FOR(itr, fxList) + itr->OnChangeOutputMode(); + } + + if (mode == OUTPUT_MODE_DPL2) + mEffectProcessTick[AUX_C] = 0; +} + +OutputMode AxManager::GetOutputMode() +{ + return mOutputMode; +} + +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)(); +} + +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; +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_AxVoice.cpp b/src/nw4r/snd/snd_AxVoice.cpp index dd9877af..e87e1562 100644 --- a/src/nw4r/snd/snd_AxVoice.cpp +++ b/src/nw4r/snd/snd_AxVoice.cpp @@ -1 +1,1358 @@ -#include "nw4r/snd/snd_AxVoice.h" +#include "nw4r/snd/AxVoice.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_AxVoice.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <cstdarg> +#include <cstring> + +#include <macros.h> // BOOLIFY_TERNARY +#include <types.h> + +#include "nw4r/snd/adpcm.h" // DecodeDspAdpcm +#include "nw4r/snd/AxManager.h" +#include "nw4r/snd/AxVoiceManager.h" +#include "nw4r/snd/BiquadFilterCallback.h" +#include "nw4r/snd/global.h" +#include "nw4r/snd/Util.h" // Util::GetRemoteFilterCoefs + +#include "nw4r/ut/inlines.h" +#include "nw4r/ut/Lock.h" // ut::AutoInterruptLock + +#if 0 +#include <revolution/AX/AX.h> +#include <revolution/AX/AXVPB.h> +#include <revolution/OS/OSAddress.h> +#else +#include <context_rvl.h> +#endif + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * local function declarations + */ + +namespace nw4r { namespace snd { namespace detail +{ + inline int CalcAxvpbDelta(u16 initValue, u16 targetValue) + { + if (initValue == targetValue) + return 0; + + return (targetValue - initValue) / AX_SAMPLES_PER_FRAME; + } +}}} // namespace nw4r::snd::detail + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +AxVoice::AxVoice() : + mWaveData (nullptr), + mFirstMixUpdateFlag (false), + mReserveForFreeFlag (false), + mCallback (nullptr), + mCallbackData (nullptr) +{ +} + +AxVoice::~AxVoice() {} + +void AxVoice::Setup(void const *waveAddr, SampleFormat format, int sampleRate) +{ + ut::AutoInterruptLock lock; + + mWaveData = waveAddr; + mFormat = format; + mSampleRate = sampleRate; + + std::memset(&mMixPrev, 0, sizeof mMixPrev); + mFirstMixUpdateFlag = true; +} + +bool AxVoice::IsPlayFinished() const +{ + ut::AutoInterruptLock lock; + + if (!mWaveData) + return false; + + u32 dspAddr = GetCurrentPlayingDspAddress(); + void const *zeroBuffer = AxManager::GetInstance().GetZeroBufferAddress(); + + u32 beginPos = GetDspAddressBySample(zeroBuffer, 0, mFormat); + u32 endPos = beginPos; + + switch (mFormat) + { + case SAMPLE_FORMAT_DSP_ADPCM: + endPos += 0x200; + break; + + case SAMPLE_FORMAT_PCM_S8: + endPos += 0x100; + break; + + case SAMPLE_FORMAT_PCM_S16: + endPos += 0x80; + break; + + default: + NW4RPanicMessage_Line(103, "Invalid format\n"); + return false; + } + + if (beginPos <= dspAddr && dspAddr < endPos) + return true; + else + return false; +} + +void AxVoice::SetLoopStart(void const *baseAddress, u32 samples) +{ + ut::AutoInterruptLock lock; + + if (!mVpb.IsAvailable()) + return; + + u32 dspAddress = GetDspAddressBySample(baseAddress, samples, mFormat); + mVpb.SetVoiceLoopAddr(dspAddress); +} + +void AxVoice::SetLoopEnd(void const *baseAddress, u32 samples) +{ + ut::AutoInterruptLock lock; + + if (!mVpb.IsAvailable()) + return; + + u32 dspAddress = GetDspAddressBySample(baseAddress, samples - 1, mFormat); + mVpb.SetVoiceEndAddr(dspAddress); +} + +void AxVoice::SetLoopFlag(bool loopFlag) +{ + ut::AutoInterruptLock lock; + + if (!mVpb.IsAvailable()) + return; + + mVpb.SetVoiceLoop(loopFlag); +} + +void AxVoice::StopAtPoint(void const *baseAddress, u32 samples) +{ + ut::AutoInterruptLock lock; + + if (!mVpb.IsAvailable()) + return; + + void const *zeroBuffer = AxManager::GetInstance().GetZeroBufferAddress(); + u32 beginPos = GetDspAddressBySample(zeroBuffer, 0, mFormat); + u32 endPos = GetDspAddressBySample(baseAddress, samples - 1, mFormat); + + mVpb.SetVoiceLoopAddr(beginPos); + mVpb.SetVoiceEndAddr(endPos); + mVpb.SetVoiceLoop(false); +} + +bool AxVoice::IsDataAddressCoverd(void const *beginAddress, + void const *endAddress) const +{ + ut::AutoInterruptLock lock; + + if (!mVpb.IsAvailable()) + return false; + + // NOTE: parentheses necessary, do not remove + return mWaveData && (beginAddress <= mWaveData && mWaveData <= endAddress); +} + +u32 AxVoice::GetCurrentPlayingSample() const +{ + ut::AutoInterruptLock lock; + + if (!mVpb.IsAvailable()) + return 0; + + if (!mWaveData) + return 0; + + if (IsPlayFinished()) + { + u32 samples = + GetSampleByDspAddress(mWaveData, GetLoopEndDspAddress(), mFormat); + + return samples + 1; + } + else + { + u32 samples = GetSampleByDspAddress( + mWaveData, GetCurrentPlayingDspAddress(), mFormat); + + return samples; + } +} + +u32 AxVoice::GetCurrentPlayingDspAddress() const +{ + u32 dspAddr = mVpb.GetCurrentAddress(); + + return dspAddr; +} + +u32 AxVoice::GetLoopEndDspAddress() const +{ + u32 dspAddr = mVpb.GetEndAddress(); + + return dspAddr; +} + +void AxVoice::VoiceCallback(void *callbackData) +{ + ut::AutoInterruptLock lock; + + AXVPB *dropVpb = static_cast<AXVPB *>(callbackData); + AxVoice *voice = reinterpret_cast<AxVoice *>(dropVpb->userContext); + NW4RAssertPointerNonnull_Line(276, voice); + + voice->mVpb.Clear(); + AxVoiceManager::GetInstance().ReserveForFreeAxVoice(voice); +} + +u32 AxVoice::GetDspAddressBySample(void const *baseAddress, u32 samples, + SampleFormat format) +{ + if (baseAddress) + baseAddress = OSCachedToPhysical(const_cast<void *>(baseAddress)); + + u32 addr = 0; + + switch (format) + { + case SAMPLE_FORMAT_DSP_ADPCM: + addr = + (samples / AX_ADPCM_SAMPLES_PER_FRAME * AX_ADPCM_NIBBLES_PER_FRAME) + + (samples % AX_ADPCM_SAMPLES_PER_FRAME) + + (reinterpret_cast<u32>(baseAddress) * sizeof(u16)) + sizeof(u16); + break; + + case SAMPLE_FORMAT_PCM_S8: + addr = reinterpret_cast<u32>(baseAddress) + samples; + break; + + case SAMPLE_FORMAT_PCM_S16: + addr = reinterpret_cast<u32>(baseAddress) / sizeof(u16) + samples; + break; + + default: + NW4RPanicMessage_Line(318, "Invalid format\n"); + break; + } + + return addr; +} + +u32 AxVoice::GetSampleByDspAddress(void const *baseAddress, u32 addr, + SampleFormat format) +{ + if (baseAddress) + baseAddress = OSCachedToPhysical(const_cast<void *>(baseAddress)); + + u32 samples = 0; + + switch (format) + { + case SAMPLE_FORMAT_DSP_ADPCM: + samples = addr - reinterpret_cast<u32>(baseAddress) * sizeof(u16); + samples = (samples % AX_ADPCM_NIBBLES_PER_FRAME) + + (samples / AX_ADPCM_NIBBLES_PER_FRAME + * AX_ADPCM_SAMPLES_PER_FRAME) + - sizeof(u16); + break; + + case SAMPLE_FORMAT_PCM_S8: + samples = addr - reinterpret_cast<u32>(baseAddress); + break; + + case SAMPLE_FORMAT_PCM_S16: + samples = addr - reinterpret_cast<u32>(baseAddress) / sizeof(u16); + break; + + default: + NW4RPanicMessage_Line(350, "Invalid format\n"); + break; + } + + return samples; +} + +void AxVoice::SetPriority(u32 priority) +{ + mVpb.SetVoicePriority(priority); +} + +void AxVoice::SetVoiceType(VoiceType type) +{ + mVpb.SetVoiceType(type); +} + +void AxVoice::ResetDelta() +{ + ut::AutoInterruptLock lock; + + if (!mVpb.IsAvailable()) + return; + + mVpb.UpdateDelta(); + + AXPBMIX mix; + mix.vL = mMixPrev.vL; + mix.vDeltaL = 0; + mix.vR = mMixPrev.vR; + mix.vDeltaR = 0; + mix.vAuxAL = mMixPrev.vAuxAL; + mix.vDeltaAuxAL = 0; + mix.vAuxAR = mMixPrev.vAuxAR; + mix.vDeltaAuxAR = 0; + mix.vAuxBL = mMixPrev.vAuxBL; + mix.vDeltaAuxBL = 0; + mix.vAuxBR = mMixPrev.vAuxBR; + mix.vDeltaAuxBR = 0; + mix.vAuxCL = mMixPrev.vAuxCL; + mix.vDeltaAuxCL = 0; + mix.vAuxCR = mMixPrev.vAuxCR; + mix.vDeltaAuxCR = 0; + mix.vS = mMixPrev.vS; + mix.vDeltaS = 0; + mix.vAuxAS = mMixPrev.vAuxAS; + mix.vDeltaAuxAS = 0; + mix.vAuxBS = mMixPrev.vAuxBS; + mix.vDeltaAuxBS = 0; + mix.vAuxCS = mMixPrev.vAuxCS; + mix.vDeltaAuxCS = 0; + + mVpb.SetVoiceMix(mix, true); +} + +void AxVoice::SetAddr(bool loopFlag, void const *waveAddr, u32 startOffset, + u32 loopStart, u32 loopEnd) +{ + ut::AutoInterruptLock lock; + + if (!mVpb.IsAvailable()) + return; + + u32 startPos; + u32 endPos; + u32 loopPos; + + if (startOffset > loopEnd) + { + void const *zeroBuffer = + AxManager::GetInstance().GetZeroBufferAddress(); + loopFlag = false; + + startPos = GetDspAddressBySample(zeroBuffer, 0, mFormat); + loopPos = GetDspAddressBySample(zeroBuffer, 0, mFormat); + endPos = GetDspAddressBySample(zeroBuffer, 1, mFormat); + } + else + { + if (loopFlag) + { + loopPos = GetDspAddressBySample(waveAddr, loopStart, mFormat); + } + else + { + void const *zeroBuffer = + AxManager::GetInstance().GetZeroBufferAddress(); + + loopPos = GetDspAddressBySample(zeroBuffer, 0, mFormat); + } + + startPos = GetDspAddressBySample(waveAddr, startOffset, mFormat); + endPos = GetDspAddressBySample(waveAddr, loopEnd - 1, mFormat); + } + + AXPBADDR addr; + addr.loopFlag = loopFlag; + addr.format = GetAxFormatFromSampleFormat(mFormat); + addr.loopAddressHi = loopPos >> 16; + addr.loopAddressLo = loopPos & 0xffff; + addr.endAddressHi = endPos >> 16; + addr.endAddressLo = endPos & 0xffff; + addr.currentAddressHi = startPos >> 16; + addr.currentAddressLo = startPos & 0xffff; + + mVpb.SetVoiceAddr(addr); +} + +u16 AxVoice::GetAxFormatFromSampleFormat(SampleFormat sampleFormat) +{ + switch (sampleFormat) + { + case SAMPLE_FORMAT_DSP_ADPCM: + return 0; + + case SAMPLE_FORMAT_PCM_S8: + return 25; + + case SAMPLE_FORMAT_PCM_S16: + return 10; + + default: + NW4RPanicMessage_Line(503, "Invalid format\n"); + return 0; + } +} + +void AxVoice::SetSrcType(SrcType type, f32 pitch) +{ + ut::AutoInterruptLock lock; + + if (!mVpb.IsAvailable()) + return; + + if (type == SRC_4TAP_AUTO) + { + f32 ratio = GetDspRatio(pitch); + + if (ratio > 4.0f / 3.0f) + type = SRC_4TAP_8K; + else if (ratio > 1.0f) + type = SRC_4TAP_12K; + else + type = SRC_4TAP_16K; + } + + mVpb.SetVoiceSrcType(type); +} + +void AxVoice::SetAdpcm(AdpcmParam const *param) +{ + ut::AutoInterruptLock lock; + + if (!mVpb.IsAvailable()) + return; + + AXPBADPCM adpcm; + + switch (mFormat) + { + case SAMPLE_FORMAT_DSP_ADPCM: + NW4RAssertPointerNonnull_Line(533, param); + + std::memcpy(adpcm.a, param->coef, sizeof adpcm.a); + + adpcm.gain = param->gain; + adpcm.pred_scale = param->pred_scale; + adpcm.yn1 = param->yn1; + adpcm.yn2 = param->yn2; + + break; + + case SAMPLE_FORMAT_PCM_S16: + std::memset(adpcm.a, 0, sizeof adpcm.a); + + adpcm.gain = 0x800; + adpcm.pred_scale = 0; + adpcm.yn1 = 0; + adpcm.yn2 = 0; + + break; + + case SAMPLE_FORMAT_PCM_S8: + std::memset(adpcm.a, 0, sizeof adpcm.a); + + adpcm.gain = 0x100; + adpcm.pred_scale = 0; + adpcm.yn1 = 0; + adpcm.yn2 = 0; + + break; + + default: + NW4RPanicMessage_Line(555, "Invalid format\n"); + break; + } + + mVpb.SetVoiceAdpcm(adpcm); +} + +bool AxVoice::IsNeedNextUpdate(MixParam const ¶m) const +{ + if (mMixPrev.vL != param.vL) + return true; + + if (mMixPrev.vR != param.vR) + return true; + + if (mMixPrev.vS != param.vS) + return true; + + if (mMixPrev.vAuxAL != param.vAuxAL) + return true; + + if (mMixPrev.vAuxAR != param.vAuxAR) + return true; + + if (mMixPrev.vAuxAS != param.vAuxAS) + return true; + + if (mMixPrev.vAuxBL != param.vAuxBL) + return true; + + if (mMixPrev.vAuxBR != param.vAuxBR) + return true; + + if (mMixPrev.vAuxBS != param.vAuxBS) + return true; + + if (mMixPrev.vAuxCL != param.vAuxCL) + return true; + + if (mMixPrev.vAuxCR != param.vAuxCR) + return true; + + if (mMixPrev.vAuxCS != param.vAuxCS) + return true; + + return false; +} + +void AxVoice::SetAdpcmLoop(AdpcmLoopParam const *param) +{ + ut::AutoInterruptLock lock; + + if (!mVpb.IsAvailable()) + return; + + AXPBADPCMLOOP adpcmloop; + + if (mFormat == SAMPLE_FORMAT_DSP_ADPCM) + { + NW4RAssertPointerNonnull_Line(587, param); + + adpcmloop.loop_pred_scale = param->loop_pred_scale; + adpcmloop.loop_yn1 = param->loop_yn1; + adpcmloop.loop_yn2 = param->loop_yn2; + } + else + { + adpcmloop.loop_pred_scale = 0; + adpcmloop.loop_yn1 = 0; + adpcmloop.loop_yn2 = 0; + } + + mVpb.SetVoiceAdpcmLoop(adpcmloop); +} + +bool AxVoice::SetMix(MixParam const ¶m) +{ + ut::AutoInterruptLock lock; + + if (!mVpb.IsAvailable()) + return false; + + AXPBMIX mix; + + if (mFirstMixUpdateFlag || !IsRun()) + { + mMixPrev = param; + mFirstMixUpdateFlag = false; + } + + bool needUpdateFlag = IsNeedNextUpdate(param); + + mix.vL = mMixPrev.vL; + mix.vR = mMixPrev.vR; + mix.vS = mMixPrev.vS; + mix.vAuxAL = mMixPrev.vAuxAL; + mix.vAuxAR = mMixPrev.vAuxAR; + mix.vAuxAS = mMixPrev.vAuxAS; + mix.vAuxBL = mMixPrev.vAuxBL; + mix.vAuxBR = mMixPrev.vAuxBR; + mix.vAuxBS = mMixPrev.vAuxBS; + mix.vAuxCL = mMixPrev.vAuxCL; + mix.vAuxCR = mMixPrev.vAuxCR; + mix.vAuxCS = mMixPrev.vAuxCS; + + int vDeltaL = CalcAxvpbDelta(mMixPrev.vL, param.vL); + int vDeltaR = CalcAxvpbDelta(mMixPrev.vR, param.vR); + int vDeltaS = CalcAxvpbDelta(mMixPrev.vS, param.vS); + int vDeltaAuxAL = CalcAxvpbDelta(mMixPrev.vAuxAL, param.vAuxAL); + int vDeltaAuxAR = CalcAxvpbDelta(mMixPrev.vAuxAR, param.vAuxAR); + int vDeltaAuxAS = CalcAxvpbDelta(mMixPrev.vAuxAS, param.vAuxAS); + int vDeltaAuxBL = CalcAxvpbDelta(mMixPrev.vAuxBL, param.vAuxBL); + int vDeltaAuxBR = CalcAxvpbDelta(mMixPrev.vAuxBR, param.vAuxBR); + int vDeltaAuxBS = CalcAxvpbDelta(mMixPrev.vAuxBS, param.vAuxBS); + int vDeltaAuxCL = CalcAxvpbDelta(mMixPrev.vAuxCL, param.vAuxCL); + int vDeltaAuxCR = CalcAxvpbDelta(mMixPrev.vAuxCR, param.vAuxCR); + int vDeltaAuxCS = CalcAxvpbDelta(mMixPrev.vAuxCS, param.vAuxCS); + + mix.vDeltaL = vDeltaL; + mix.vDeltaR = vDeltaR; + mix.vDeltaS = vDeltaS; + mix.vDeltaAuxAL = vDeltaAuxAL; + mix.vDeltaAuxAR = vDeltaAuxAR; + mix.vDeltaAuxAS = vDeltaAuxAS; + mix.vDeltaAuxBL = vDeltaAuxBL; + mix.vDeltaAuxBR = vDeltaAuxBR; + mix.vDeltaAuxBS = vDeltaAuxBS; + mix.vDeltaAuxCL = vDeltaAuxCL; + mix.vDeltaAuxCR = vDeltaAuxCR; + mix.vDeltaAuxCS = vDeltaAuxCS; + + mVpb.SetVoiceMix(mix, false); + + if (param.vL == 0 || vDeltaL == 0) + mMixPrev.vL = param.vL; + else + mMixPrev.vL += vDeltaL * AX_SAMPLES_PER_FRAME; + + if (param.vR == 0 || vDeltaR == 0) + mMixPrev.vR = param.vR; + else + mMixPrev.vR += vDeltaR * AX_SAMPLES_PER_FRAME; + + if (param.vS == 0 || vDeltaS == 0) + mMixPrev.vS = param.vS; + else + mMixPrev.vS += vDeltaS * AX_SAMPLES_PER_FRAME; + + if (param.vAuxAL == 0 || vDeltaAuxAL == 0) + mMixPrev.vAuxAL = param.vAuxAL; + else + mMixPrev.vAuxAL += vDeltaAuxAL * AX_SAMPLES_PER_FRAME; + + if (param.vAuxAR == 0 || vDeltaAuxAR == 0) + mMixPrev.vAuxAR = param.vAuxAR; + else + mMixPrev.vAuxAR += vDeltaAuxAR * AX_SAMPLES_PER_FRAME; + + if (param.vAuxAS == 0 || vDeltaAuxAS == 0) + mMixPrev.vAuxAS = param.vAuxAS; + else + mMixPrev.vAuxAS += vDeltaAuxAS * AX_SAMPLES_PER_FRAME; + + if (param.vAuxBL == 0 || vDeltaAuxBL == 0) + mMixPrev.vAuxBL = param.vAuxBL; + else + mMixPrev.vAuxBL += vDeltaAuxBL * AX_SAMPLES_PER_FRAME; + + if (param.vAuxBR == 0 || vDeltaAuxBR == 0) + mMixPrev.vAuxBR = param.vAuxBR; + else + mMixPrev.vAuxBR += vDeltaAuxBR * AX_SAMPLES_PER_FRAME; + + if (param.vAuxBS == 0 || vDeltaAuxBS == 0) + mMixPrev.vAuxBS = param.vAuxBS; + else + mMixPrev.vAuxBS += vDeltaAuxBS * AX_SAMPLES_PER_FRAME; + + if (param.vAuxCL == 0 || vDeltaAuxCL == 0) + mMixPrev.vAuxCL = param.vAuxCL; + else + mMixPrev.vAuxCL += vDeltaAuxCL * AX_SAMPLES_PER_FRAME; + + if (param.vAuxCR == 0 || vDeltaAuxCR == 0) + mMixPrev.vAuxCR = param.vAuxCR; + else + mMixPrev.vAuxCR += vDeltaAuxCR * AX_SAMPLES_PER_FRAME; + + if (param.vAuxCS == 0 || vDeltaAuxCS == 0) + mMixPrev.vAuxCS = param.vAuxCS; + else + mMixPrev.vAuxCS += vDeltaAuxCS * AX_SAMPLES_PER_FRAME; + + return needUpdateFlag; +} + +void AxVoice::SetSrc(f32 ratio, bool initialUpdate) +{ + ut::AutoInterruptLock lock; + + if (!mVpb.IsAvailable()) + return; + + if (initialUpdate) + { + ratio = ut::Clamp(GetDspRatio(ratio), 0.0f, 65535.0f); + + u32 srcBits = 65536 * ratio; + + AXPBSRC src; + src.ratioHi = srcBits >> 16; + src.ratioLo = srcBits & 0xffff; + src.currentAddressFrac = 0; + src.last_samples[0] = 0; + src.last_samples[1] = 0; + src.last_samples[2] = 0; + src.last_samples[3] = 0; + + mVpb.SetVoiceSrc(src); + } + else + { + ratio = GetDspRatio(ratio); + + mVpb.SetVoiceSrcRatio(ratio); + } +} + +void AxVoice::SetVe(f32 volume, f32 initVolume) +{ + ut::AutoInterruptLock lock; + + if (!mVpb.IsAvailable()) + return; + + mVpb.SetVoiceVe(volume * (AX_MAX_VOLUME - 1), + initVolume * (AX_MAX_VOLUME - 1)); +} + +void AxVoice::SetLpf(u16 freq) +{ + ut::AutoInterruptLock lock; + + if (!mVpb.IsAvailable()) + return; + + if (freq >= 16000) + { + AXPBLPF lpf; + lpf.on = AX_PB_OFF; + lpf.yn1 = 0; + + mVpb.SetVoiceLpf(lpf); + } + else if (mVpb.IsLpfEnable()) + { + u16 a0, b0; + AXGetLpfCoefs(freq, &a0, &b0); + + mVpb.SetVoiceLpfCoefs(a0, b0); + } + else + { + AXPBLPF lpf; + lpf.on = AX_PB_LPF_ON; + lpf.yn1 = 0; + AXGetLpfCoefs(freq, &lpf.a0, &lpf.b0); + + mVpb.SetVoiceLpf(lpf); + } +} + +void AxVoice::SetBiquad(u8 filterType, f32 value) +{ + ut::AutoInterruptLock lock; + + if (!mVpb.IsAvailable()) + return; + + BiquadFilterCallback const *biquadFilter = + AxManager::GetInstance().GetBiquadFilterCallback(filterType); + + bool filterEnable = true; + + if (filterType == 0) + filterEnable = false; + + if (!biquadFilter) + filterEnable = false; + + if (!filterEnable) + { + AXPBBIQUAD biquad; + biquad.on = AX_PB_OFF; + biquad.xn1 = 0; + biquad.xn2 = 0; + biquad.yn1 = 0; + biquad.yn2 = 0; + + mVpb.SetVoiceBiquad(biquad); + } + else + { + BiquadFilterCallback::BiquadCoef coef; + biquadFilter->GetCoef(filterType, value, &coef); + + if (mVpb.IsBiquadEnable()) + { + mVpb.SetVoiceBiquadCoefs(coef.b0, coef.b1, coef.b2, coef.a1, + coef.a2); + } + else + { + AXPBBIQUAD biquad; + biquad.on = AX_PB_BIQUAD_ON; + biquad.xn1 = 0; + biquad.xn2 = 0; + biquad.yn1 = 0; + biquad.yn2 = 0; + biquad.b0 = coef.b0; + biquad.b1 = coef.b1; + biquad.b2 = coef.b2; + biquad.a1 = coef.a1; + biquad.a2 = coef.a2; + + mVpb.SetVoiceBiquad(biquad); + } + } +} + +void AxVoice::SetRemoteFilter(u8 filter) +{ + ut::AutoInterruptLock lock; + + if (!mVpb.IsAvailable()) + return; + + if (filter == 0) + { + __AXPBRMTIIR iir; + iir.lpf.on = AX_PB_OFF; + + mVpb.SetVoiceRmtIIR(iir); + } + else if (mVpb.IsRmtIirEnable()) + { + u16 b0, b1, b2, a1, a2; + Util::GetRemoteFilterCoefs(filter, &b0, &b1, &b2, &a1, &a2); + + mVpb.SetVoiceRmtIIRCoefs(AX_PB_BIQUAD_ON, b0, b1, b2, a1, a2); + } + else + { + __AXPBRMTIIR iir; + iir.biquad.on = AX_PB_BIQUAD_ON; + iir.biquad.xn1 = 0; + iir.biquad.xn2 = 0; + iir.biquad.yn1 = 0; + iir.biquad.yn2 = 0; + + Util::GetRemoteFilterCoefs(filter, &iir.biquad.b0, &iir.biquad.b1, + &iir.biquad.b2, &iir.biquad.a1, + &iir.biquad.a2); + + mVpb.SetVoiceRmtIIR(iir); + } +} + +void AxVoice::CalcOffsetAdpcmParam(u16 *outPredScale, u16 *outYn1, u16 *outYn2, + u32 offset, void *dataAddr, + AdpcmParam const &adpcmParam) +{ + AXPBADPCM adpcm; + std::memcpy(adpcm.a, adpcmParam.coef, sizeof adpcm.a); + adpcm.gain = adpcmParam.gain; + adpcm.pred_scale = adpcmParam.pred_scale; + adpcm.yn1 = adpcmParam.yn1; + adpcm.yn2 = adpcmParam.yn2; + + u32 currentPos = + GetDspAddressBySample(dataAddr, 0, SAMPLE_FORMAT_DSP_ADPCM); + u32 endPos = + GetDspAddressBySample(dataAddr, offset, SAMPLE_FORMAT_DSP_ADPCM); + + while (currentPos < endPos) + { + if (currentPos % AX_ADPCM_NIBBLES_PER_FRAME == 0) + { + byte_t byte = *static_cast<byte_t *>(OSPhysicalToCached( + reinterpret_cast<void *>(currentPos / sizeof(u16)))); + + adpcm.pred_scale = byte; + currentPos += sizeof(u16); + } + + byte_t byte = *static_cast<byte_t *>(OSPhysicalToCached( + reinterpret_cast<void *>(currentPos / sizeof(u16)))); + + u8 nibble; + if (currentPos % sizeof(u16) != 0) + nibble = byte & 0x0f; + else + nibble = byte >> 4; + + DecodeDspAdpcm(&adpcm, nibble); + currentPos++; + } + + *outPredScale = adpcm.pred_scale; + *outYn1 = adpcm.yn1; + *outYn2 = adpcm.yn2; +} + +void AxVoiceParamBlock::Sync() +{ + ut::AutoInterruptLock lock; + + if (!IsAvailable()) + return; + + mVpb->pb.ve.currentVolume = mPrevVeSetting.currentVolume; + + s16 deltaIn = + (mVolume - mPrevVeSetting.currentVolume) / AX_SAMPLES_PER_FRAME; + s16 deltaOut = deltaIn + BOOLIFY_TERNARY(deltaIn > 0) ? 1 : -1; + + int predIn = mPrevVeSetting.currentVolume + deltaIn * AX_SAMPLES_PER_FRAME; + int predOut = + mPrevVeSetting.currentVolume + deltaOut * AX_SAMPLES_PER_FRAME; + + if (ut::Abs(mVolume - predIn) < ut::Abs(mVolume - predOut)) + mVpb->pb.ve.currentDelta = deltaIn; + else + mVpb->pb.ve.currentDelta = deltaOut; + + int nextVolume = mPrevVeSetting.currentVolume + + mVpb->pb.ve.currentDelta * AX_SAMPLES_PER_FRAME; + + if (nextVolume < 0) + { + mVpb->pb.ve.currentDelta = + -mPrevVeSetting.currentVolume / AX_SAMPLES_PER_FRAME; + } + else if (nextVolume > 32767) + { + mVpb->pb.ve.currentDelta = + (32767 - mPrevVeSetting.currentVolume) / AX_SAMPLES_PER_FRAME; + } + + if (mVpb->pb.ve.currentDelta == 0 && mPrevVeSetting.currentDelta == 0) + mVpb->pb.ve.currentVolume = mVolume; + + mSync &= ~AX_VPB_SYNC_FLAG_VE_DELTA; + mSync |= AX_VPB_SYNC_FLAG_VE; + + mPrevVeSetting.currentVolume = mVpb->pb.ve.currentVolume; + mPrevVeSetting.currentDelta = mVpb->pb.ve.currentDelta; + + mVpb->sync |= mSync; + mSync = 0; +} + +bool AxVoiceParamBlock::IsRmtIirEnable() const +{ + return IsAvailable() && mVpb->pb.rmtIIR.biquad.on == AX_PB_BIQUAD_ON; +} + +AxVoiceParamBlock::AxVoiceParamBlock() : + mVpb (nullptr), + mSync (), + mFirstVeUpdateFlag (false), + mVolume (DEFAULT_VOLUME) +{ + mPrevVeSetting.currentVolume = DEFAULT_VOLUME; + mPrevVeSetting.currentDelta = 0; +} + +void AxVoiceParamBlock::Set(AXVPB *vpb) +{ + NW4RAssertPointerNonnull_Line(1044, vpb); + + mVpb = vpb; + mSync = 0; + mFirstVeUpdateFlag = true; + mVolume = DEFAULT_VOLUME; + + mPrevVeSetting.currentVolume = DEFAULT_VOLUME; + mPrevVeSetting.currentDelta = 0; +} + +void AxVoiceParamBlock::Clear() +{ + mVpb = nullptr; + mSync = 0; +} + +void AxVoiceParamBlock::SetVoiceType(u16 type) +{ + ut::AutoInterruptLock lock; + + if (!IsAvailable()) + return; + + mVpb->pb.type = type; + + mSync |= AX_VPB_SYNC_FLAG_TYPE; +} + +void AxVoiceParamBlock::SetVoiceVe(u16 volume, u16 initVolume) +{ + ut::AutoInterruptLock lock; + + if (!IsAvailable()) + return; + + if (mFirstVeUpdateFlag) + { + mPrevVeSetting.currentVolume = initVolume; + mFirstVeUpdateFlag = false; + } + + mVolume = volume; +} + +void AxVoiceParamBlock::SetVoiceMix(AXPBMIX const &mix, bool immediatelySync) +{ + ut::AutoInterruptLock lock; + + if (!IsAvailable()) + return; + + u16 *src = const_cast<u16 *>(reinterpret_cast<u16 const *>(&mix)); + u16 *dst = reinterpret_cast<u16 *>(&mVpb->pb.mix); + + byte4_t mixerCtrl = 0; + + if ((*dst++ = *src++)) // vL + mixerCtrl |= AX_MIXER_CTRL_L; + + if ((*dst++ = *src++)) // vDeltaL + mixerCtrl |= AX_MIXER_CTRL_L | AX_MIXER_CTRL_DELTA; + + if ((*dst++ = *src++)) // vR + mixerCtrl |= AX_MIXER_CTRL_R; + + if ((*dst++ = *src++)) // vDeltaR + mixerCtrl |= AX_MIXER_CTRL_R | AX_MIXER_CTRL_DELTA; + + if ((*dst++ = *src++)) // vAuxAL + mixerCtrl |= AX_MIXER_CTRL_A_L; + + if ((*dst++ = *src++)) // vDeltaAuxAL + mixerCtrl |= AX_MIXER_CTRL_A_L | AX_MIXER_CTRL_A_DELTA; + + if ((*dst++ = *src++)) // vAuxAR + mixerCtrl |= AX_MIXER_CTRL_A_R; + + if ((*dst++ = *src++)) // vDeltaAuxAR + mixerCtrl |= AX_MIXER_CTRL_A_R | AX_MIXER_CTRL_A_DELTA; + + if ((*dst++ = *src++)) // vAuxBL + mixerCtrl |= AX_MIXER_CTRL_B_L; + + if ((*dst++ = *src++)) // vDeltaAuxBL + mixerCtrl |= AX_MIXER_CTRL_B_L | AX_MIXER_CTRL_B_DELTA; + + if ((*dst++ = *src++)) // vAuxBR + mixerCtrl |= AX_MIXER_CTRL_B_R; + + if ((*dst++ = *src++)) // vDeltaAuxBR + mixerCtrl |= AX_MIXER_CTRL_B_R | AX_MIXER_CTRL_B_DELTA; + + if ((*dst++ = *src++)) // vAuxCL + mixerCtrl |= AX_MIXER_CTRL_C_L; + + if ((*dst++ = *src++)) // vDeltaAuxCL + mixerCtrl |= AX_MIXER_CTRL_C_L | AX_MIXER_CTRL_C_DELTA; + + if ((*dst++ = *src++)) // vAuxCR + mixerCtrl |= AX_MIXER_CTRL_C_R; + + if ((*dst++ = *src++)) // vDeltaAuxCR + mixerCtrl |= AX_MIXER_CTRL_C_R | AX_MIXER_CTRL_C_DELTA; + + if ((*dst++ = *src++)) // vS + mixerCtrl |= AX_MIXER_CTRL_S; + + if ((*dst++ = *src++)) // vDeltaS + mixerCtrl |= AX_MIXER_CTRL_S | AX_MIXER_CTRL_DELTA_S; + + if ((*dst++ = *src++)) // vAuxAS + mixerCtrl |= AX_MIXER_CTRL_A_S; + + if ((*dst++ = *src++)) // vDeltaAuxAS + mixerCtrl |= AX_MIXER_CTRL_A_S | AX_MIXER_CTRL_A_DELTA_S; + + if ((*dst++ = *src++)) // vAuxBS + mixerCtrl |= AX_MIXER_CTRL_B_S; + + if ((*dst++ = *src++)) // vDeltaAuxBS + mixerCtrl |= AX_MIXER_CTRL_B_S | AX_MIXER_CTRL_B_DELTA_S; + + if ((*dst++ = *src++)) // vAuxCS + mixerCtrl |= AX_MIXER_CTRL_C_S; + + if ((*dst++ = *src++)) // vDeltaAuxCS + mixerCtrl |= AX_MIXER_CTRL_C_S | AX_MIXER_CTRL_C_DELTA_S; + + mVpb->pb.mixerCtrl = mixerCtrl; + + if (immediatelySync) + mVpb->sync |= AX_VPB_SYNC_FLAG_MIX | AX_VPB_SYNC_FLAG_MIXER_CTRL; + else + mSync |= AX_VPB_SYNC_FLAG_MIX | AX_VPB_SYNC_FLAG_MIXER_CTRL; +} + +void AxVoiceParamBlock::SetVoiceLoop(u16 loop) +{ + ut::AutoInterruptLock lock; + + if (!IsAvailable()) + return; + + mVpb->pb.addr.loopFlag = loop; + + if (!(mVpb->sync & AX_VPB_SYNC_FLAG_ADDR)) + mVpb->sync |= AX_VPB_SYNC_FLAG_ADDR_LOOP_FLAG; +} + +void AxVoiceParamBlock::SetVoiceLoopAddr(u32 addr) +{ + ut::AutoInterruptLock lock; + + if (!IsAvailable()) + return; + + mVpb->pb.addr.loopAddressHi = addr >> 16; + mVpb->pb.addr.loopAddressLo = addr & 0xffff; + + if (!(mVpb->sync & AX_VPB_SYNC_FLAG_ADDR)) + mVpb->sync |= AX_VPB_SYNC_FLAG_ADDR_LOOP_ADDR; +} + +void AxVoiceParamBlock::SetVoiceEndAddr(u32 addr) +{ + ut::AutoInterruptLock lock; + + if (!IsAvailable()) + return; + + mVpb->pb.addr.endAddressHi = addr >> 16; + mVpb->pb.addr.endAddressLo = addr & 0xffff; + + if (!(mVpb->sync & AX_VPB_SYNC_FLAG_ADDR)) + mVpb->sync |= AX_VPB_SYNC_FLAG_ADDR_END_ADDR; +} + +void AxVoiceParamBlock::SetVoiceAdpcm(AXPBADPCM const &adpcm) +{ + ut::AutoInterruptLock lock; + + if (!IsAvailable()) + return; + + std::memcpy(&mVpb->pb.adpcm, &adpcm, sizeof mVpb->pb.adpcm); + + mSync |= AX_VPB_SYNC_FLAG_ADPCM; +} + +void AxVoiceParamBlock::SetVoiceSrcType(u32 type) +{ + ut::AutoInterruptLock lock; + + if (!IsAvailable()) + return; + + switch (type) + { + case AxVoice::SRC_NONE: + mVpb->pb.srcSelect = 2; + break; + + case AxVoice::SRC_LINEAR: + mVpb->pb.srcSelect = 1; + break; + + case AxVoice::SRC_4TAP_8K: + mVpb->pb.srcSelect = 0; + mVpb->pb.coefSelect = 0; + break; + + case AxVoice::SRC_4TAP_12K: + mVpb->pb.srcSelect = 0; + mVpb->pb.coefSelect = 1; + break; + + case AxVoice::SRC_4TAP_16K: + mVpb->pb.srcSelect = 0; + mVpb->pb.coefSelect = 2; + break; + } + + mSync |= AX_VPB_SYNC_FLAG_SRC_TYPE; +} + +void AxVoiceParamBlock::SetVoiceSrc(AXPBSRC const &src) +{ + ut::AutoInterruptLock lock; + + if (!IsAvailable()) + return; + + std::memcpy(&mVpb->pb.src, &src, sizeof mVpb->pb.src); + + mSync &= ~AX_VPB_SYNC_FLAG_SRC_RATIO; + mSync |= AX_VPB_SYNC_FLAG_SRC; +} + +void AxVoiceParamBlock::SetVoiceSrcRatio(f32 ratio) +{ + ut::AutoInterruptLock lock; + + if (!IsAvailable()) + return; + + u32 r = 65536 * ratio; + mVpb->pb.src.ratioHi = r >> 16; + mVpb->pb.src.ratioLo = r & 0xffff; + + if (!(mSync & AX_VPB_SYNC_FLAG_SRC)) + mSync |= AX_VPB_SYNC_FLAG_SRC_RATIO; +} + +void AxVoiceParamBlock::SetVoiceAdpcmLoop(AXPBADPCMLOOP const &adpcmloop) +{ + ut::AutoInterruptLock lock; + + if (!IsAvailable()) + return; + + std::memcpy(&mVpb->pb.adpcmLoop, &adpcmloop, sizeof mVpb->pb.adpcmLoop); + + mSync |= AX_VPB_SYNC_FLAG_ADPCM_LOOP; +} + +void AxVoiceParamBlock::SetVoiceLpf(AXPBLPF const &lpf) +{ + ut::AutoInterruptLock lock; + + if (!IsAvailable()) + return; + + std::memcpy(&mVpb->pb.lpf, &lpf, sizeof mVpb->pb.lpf); + + mSync |= AX_VPB_SYNC_FLAG_LPF; +} + +void AxVoiceParamBlock::SetVoiceLpfCoefs(u16 a0, u16 b0) +{ + ut::AutoInterruptLock lock; + + if (!IsAvailable()) + return; + + mVpb->pb.lpf.a0 = a0; + mVpb->pb.lpf.b0 = b0; + + mSync |= AX_VPB_SYNC_FLAG_LPF_COEFS; +} + +void AxVoiceParamBlock::SetVoiceBiquad(AXPBBIQUAD const &biquad) +{ + ut::AutoInterruptLock lock; + + if (!IsAvailable()) + return; + + std::memcpy(&mVpb->pb.biquad, &biquad, sizeof mVpb->pb.biquad); + + mSync |= AX_VPB_SYNC_FLAG_BIQUAD; +} + +void AxVoiceParamBlock::SetVoiceBiquadCoefs(u16 b0, u16 b1, u16 b2, u16 a1, + u16 a2) +{ + ut::AutoInterruptLock lock; + + if (!IsAvailable()) + return; + + mVpb->pb.biquad.b0 = b0; + mVpb->pb.biquad.b1 = b1; + mVpb->pb.biquad.b2 = b2; + mVpb->pb.biquad.a1 = a1; + mVpb->pb.biquad.a2 = a2; + + mSync |= AX_VPB_SYNC_FLAG_BIQUAD_COEFS; +} + +void AxVoiceParamBlock::SetVoiceRmtIIR(__AXPBRMTIIR const &iir) +{ + ut::AutoInterruptLock lock; + + if (!IsAvailable()) + return; + + std::memcpy(&mVpb->pb.rmtIIR, &iir, sizeof mVpb->pb.rmtIIR); + + mSync |= AX_VPB_SYNC_FLAG_RMT_IIR; +} + +void AxVoiceParamBlock::SetVoiceRmtIIRCoefs(u16 type, ...) +{ + ut::AutoInterruptLock lock; + + if (!IsAvailable()) + return; + + std::va_list argp; + s32 ii; + s32 num; + + if (type == AX_PB_LPF_ON) + num = 2; + else if (type == AX_PB_BIQUAD_ON) + num = 5; + else + return; + + u16 coefs[5]; + + va_start(argp, type); + + for (ii = 0; ii < num; ii++) + coefs[ii] = va_arg(argp, unsigned long); + + va_end(argp); + + if (type == AX_PB_LPF_ON) + { + mVpb->pb.rmtIIR.lpf.a0 = coefs[0]; + mVpb->pb.rmtIIR.lpf.b0 = coefs[1]; + + mSync |= AX_VPB_SYNC_FLAG_RMT_IIR_LPF_COEFS; + } + else + { + mVpb->pb.rmtIIR.biquad.b0 = coefs[0]; + mVpb->pb.rmtIIR.biquad.b1 = coefs[1]; + mVpb->pb.rmtIIR.biquad.b2 = coefs[2]; + mVpb->pb.rmtIIR.biquad.a1 = coefs[3]; + mVpb->pb.rmtIIR.biquad.a2 = coefs[4]; + + mSync |= AX_VPB_SYNC_FLAG_RMT_IIR_BIQUAD_COEFS; + } +} + +void AxVoiceParamBlock::UpdateDelta() +{ + ut::AutoInterruptLock lock; + + if (!IsAvailable()) + return; + + mPrevVeSetting.currentVolume += + mVpb->pb.ve.currentDelta * AX_SAMPLES_PER_FRAME; + + mVpb->pb.ve.currentVolume = mPrevVeSetting.currentVolume; + mVpb->pb.ve.currentDelta = 0; + + mVpb->sync |= AX_VPB_SYNC_FLAG_VE; +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_AxVoiceManager.cpp b/src/nw4r/snd/snd_AxVoiceManager.cpp index 227c9434..f617a11c 100644 --- a/src/nw4r/snd/snd_AxVoiceManager.cpp +++ b/src/nw4r/snd/snd_AxVoiceManager.cpp @@ -1 +1,235 @@ -#include "nw4r/snd/snd_AxVoiceManager.h" +#include "nw4r/snd/AxVoiceManager.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_AxVoiceManager.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <new> + +#include <types.h> + +#include "nw4r/snd/AxVoice.h" + +#include "nw4r/ut/Lock.h" // ut::AutoInterruptLock + +#if 0 +#include <revolution/AX/AXAlloc.h> +#include <revolution/AX/AXVPB.h> +#else +#include <context_rvl.h> +#endif + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +AxVoiceManager &AxVoiceManager::GetInstance() +{ + static AxVoiceManager instance; + + return instance; +} + +AxVoiceManager::AxVoiceManager() : + mInitialized (false) +{ +} + +u32 AxVoiceManager::GetRequiredMemSize(int axVoiceCount) +{ + return sizeof(AxVoice) * (VOICE_COUNT_MARGIN + axVoiceCount); +} + +void AxVoiceManager::Setup(void *mem, u32 memSize) +{ + if (mInitialized) + return; + + mVoiceCount = memSize / sizeof(AxVoice); + byte_t *ptr = static_cast<byte_t *>(mem); + + for (u32 i = 0; i < mVoiceCount; i++) + { + mFreeVoiceList.PushBack(new (ptr) AxVoice); + + ptr += sizeof(AxVoice); + } + + NW4RAssert_Line(65, ptr <= reinterpret_cast<u8*>( mem ) + memSize); + + mInitialized = true; +} + +void AxVoiceManager::Shutdown() +{ + if (!mInitialized) + return; + + while (!mActiveVoiceList.IsEmpty()) + { + AxVoice &voice = mActiveVoiceList.GetFront(); + mActiveVoiceList.PopFront(); + + if (voice.mVpb.IsAvailable()) + { + voice.Stop(); + + if (voice.mCallback) + { + (*voice.mCallback)(&voice, AxVoice::CALLBACK_STATUS_CANCEL, + voice.mCallbackData); + } + + FreeAxVoice(&voice); + } + } + + while (!mFreeReservedVoiceList.IsEmpty()) + { + AxVoice &voice = mFreeReservedVoiceList.GetFront(); + // ERRATUM: Pop from wrong list + mActiveVoiceList.PopFront(); + + if (voice.mVpb.IsAvailable()) + { + voice.Stop(); + + if (voice.mCallback) + { + (*voice.mCallback)(&voice, AxVoice::CALLBACK_STATUS_CANCEL, + voice.mCallbackData); + } + + FreeAxVoice(&voice); + } + } + + while (!mFreeVoiceList.IsEmpty()) + { + AxVoice &voice = mFreeVoiceList.GetFront(); + mFreeVoiceList.PopFront(); + + voice.~AxVoice(); + } + + mInitialized = false; +} + +AxVoice *AxVoiceManager::Alloc() +{ + ut::AutoInterruptLock lock; + + FreeAllReservedAxVoice(); + + if (mFreeVoiceList.IsEmpty()) + return nullptr; + + AxVoice *ptr = &mFreeVoiceList.GetFront(); + mFreeVoiceList.PopFront(); + + AxVoice *voice = new (ptr) AxVoice(); + mActiveVoiceList.PushBack(ptr); + + return voice; +} + +void AxVoiceManager::Free(AxVoice *voice) +{ + NW4RAssert_Line(154, voice); + + voice->~AxVoice(); + + ut::AutoInterruptLock lock; + + if (voice->mReserveForFreeFlag) + mFreeReservedVoiceList.Erase(voice); + else + mActiveVoiceList.Erase(voice); + + mFreeVoiceList.PushBack(voice); +} + +void AxVoiceManager::ReserveForFree(AxVoice *voice) +{ + NW4RAssert_Line(169, voice); + + ut::AutoInterruptLock lock; + + mActiveVoiceList.Erase(voice); + mFreeReservedVoiceList.PushBack(voice); +} + +AxVoice *AxVoiceManager::AcquireAxVoice(u32 priority, + AxVoice::Callback *callback, + void *callbackData) +{ + ut::AutoInterruptLock lock; + + AxVoice *voice = Alloc(); + if (!voice) + return nullptr; + + AXVPB *vpb = AXAcquireVoice(priority, &AxVoice::VoiceCallback, + reinterpret_cast<register_t>(voice)); + if (!vpb) + { + Free(voice); + return nullptr; + } + + voice->mVpb.Set(vpb); + voice->mCallback = callback; + voice->mCallbackData = callbackData; + + return voice; +} + +void AxVoiceManager::FreeAxVoice(AxVoice *voice) +{ + ut::AutoInterruptLock lock; + + NW4RAssertPointerNonnull_Line(212, voice); + + if (voice->mVpb.IsAvailable()) + AXFreeVoice(voice->mVpb); + + Free(voice); +} + +void AxVoiceManager::ReserveForFreeAxVoice(AxVoice *voice) +{ + ut::AutoInterruptLock lock; + + NW4RAssert_Line(226, voice); + + voice->mReserveForFreeFlag = true; + ReserveForFree(voice); +} + +void AxVoiceManager::FreeAllReservedAxVoice() +{ + while (!mFreeReservedVoiceList.IsEmpty()) + { + AxVoice &voice = mFreeReservedVoiceList.GetFront(); + + if (voice.mCallback) + { + (*voice.mCallback)(&voice, AxVoice::CALLBACK_STATUS_DROP_DSP, + voice.mCallbackData); + } + + // NOTE: unnecessary call to GetInstance from instance-method + GetInstance().FreeAxVoice(&voice); + } +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_Bank.cpp b/src/nw4r/snd/snd_Bank.cpp index 6842d6ac..02e25094 100644 --- a/src/nw4r/snd/snd_Bank.cpp +++ b/src/nw4r/snd/snd_Bank.cpp @@ -1 +1,96 @@ -#include "nw4r/snd/snd_Bank.h" +#include "nw4r/snd/Bank.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_Bank.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <types.h> + +#include "nw4r/snd/BankFile.h" // InstInfo +#include "nw4r/snd/Channel.h" +#include "nw4r/snd/NoteOnCallback.h" // NoteOnInfo +#include "nw4r/snd/WaveFile.h" // WaveInfo + +#include "nw4r/ut/inlines.h" // ut::Min + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +Bank::Bank(void const *bankData) : + mBankReader (bankData), + mWaveDataAddress (nullptr) +{ +} + +Bank::~Bank() {} + +Channel *Bank::NoteOn(NoteOnInfo const ¬eOnInfo) const +{ + bool result; + + InstInfo instInfo; + result = mBankReader.ReadInstInfo(&instInfo, noteOnInfo.prgNo, + noteOnInfo.key, noteOnInfo.velocity); + if (!result) + return nullptr; + + WaveInfo waveParam; + WaveInfo const *waveInfoAddress; + result = mBankReader.ReadWaveInfo(&waveParam, instInfo.waveDataLocation, + mWaveDataAddress, &waveInfoAddress); + if (!result) + return nullptr; + + int voiceChannelCount = + ut::Min(waveParam.numChannels, Channel::CHANNEL_MAX); + Channel *ch_p = Channel::AllocChannel(voiceChannelCount, + noteOnInfo.voiceOutCount, noteOnInfo.priority, + noteOnInfo.channelCallback, noteOnInfo.channelCallbackData); + if (!ch_p) + return nullptr; + + ch_p->SetKey(noteOnInfo.key); + ch_p->SetOriginalKey(instInfo.originalKey); + + f32 initVolume = noteOnInfo.velocity / 127.0f; + initVolume *= initVolume; + initVolume *= instInfo.volume / 127.0f; + ch_p->SetInitVolume(initVolume); + + ch_p->SetTune(instInfo.tune); + ch_p->SetAttack(instInfo.attack); + ch_p->SetHold(instInfo.hold); + ch_p->SetDecay(instInfo.decay); + ch_p->SetSustain(instInfo.sustain); + ch_p->SetRelease(instInfo.release); + + f32 initPan = (instInfo.pan - 64) / 63.0f; + initPan += noteOnInfo.initPan / 63.0f; + ch_p->SetInitPan(initPan); + + ch_p->SetInitSurroundPan(0.0f); + ch_p->SetAlternateAssignId(instInfo.alternateAssign); + ch_p->SetReleaseIgnore(instInfo.noteOffType + == InstInfo::NOTE_OFF_TYPE_IGNORE); + + if (instInfo.waveDataLocation.type + == InstInfo::WaveDataLocation::WAVE_DATA_LOCATION_CALLBACK) + { + ch_p->SetWaveDataLocationCallback(instInfo.waveDataLocation.callback, + waveInfoAddress); + } + + ch_p->Start(waveParam, noteOnInfo.length, 0); + + return ch_p; +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_BankFile.cpp b/src/nw4r/snd/snd_BankFile.cpp index fbb3e65d..971efb1e 100644 --- a/src/nw4r/snd/snd_BankFile.cpp +++ b/src/nw4r/snd/snd_BankFile.cpp @@ -1 +1,374 @@ -#include "nw4r/snd/snd_BankFile.h" +#include "nw4r/snd/BankFile.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_BankFile.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <macros.h> // NW4R_FILE_VERSION +#include <types.h> + +#include "nw4r/snd/Util.h" +#include "nw4r/snd/WaveArchive.h" // WaveArchiveReader +#include "nw4r/snd/WaveFile.h" + +#include "nw4r/ut/binaryFileFormat.h" // ut::BinaryFileHeader +#include "nw4r/ut/inlines.h" + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * local function declarations + */ + +namespace nw4r { namespace snd { namespace detail +{ + inline byte_t ReadByte(void const *address) + { + return *static_cast<byte_t const *>(address); + } +}}} // namespace nw4r::snd::detail + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +bool BankFileReader::IsValidFileHeader(void const *bankData) +{ + ut::BinaryFileHeader const *fileHeader = + static_cast<ut::BinaryFileHeader const *>(bankData); + + NW4RAssertMessage_Line( + 59, fileHeader->signature == BankFile::SIGNATURE_FILE, + "invalid file signature. bank data is not available."); + + if (fileHeader->signature != BankFile::SIGNATURE_FILE) + return false; + + NW4RAssertMessage_Line(67, fileHeader->version >= NW4R_FILE_VERSION(1, 0), + "bank file is not supported version.\n please " + "reconvert file using new version tools.\n"); + + if (fileHeader->version < NW4R_FILE_VERSION(1, 0)) + return false; + + NW4RAssertMessage_Line(73, fileHeader->version <= SUPPORTED_FILE_VERSION, + "bank file is not supported version.\n please " + "reconvert file using new version tools.\n"); + + if (fileHeader->version > SUPPORTED_FILE_VERSION) + return false; + + return true; +} + +BankFileReader::BankFileReader(void const *bankData) : + mHeader (nullptr), + mDataBlock (nullptr), + mWaveBlock (nullptr) +{ + NW4RAssertPointerNonnull_Line(93, bankData); + + if (!IsValidFileHeader(bankData)) + return; + + mHeader = static_cast<BankFile::Header const *>(bankData); + + if (mHeader->dataBlockOffset) + { + mDataBlock = static_cast<BankFile::DataBlock const *>( + ut::AddOffsetToPtr(mHeader, mHeader->dataBlockOffset)); + + NW4RAssert_Line(105, mDataBlock->blockHeader.kind + == BankFile::SIGNATURE_DATA_BLOCK); + } + + if (mHeader->waveBlockOffset) + { + mWaveBlock = static_cast<BankFile::WaveBlock const *>( + ut::AddOffsetToPtr(mHeader, mHeader->waveBlockOffset)); + + NW4RAssert_Line(113, mWaveBlock->blockHeader.kind + == BankFile::SIGNATURE_WAVE_BLOCK); + } +} + +BankFile::InstParam const *BankFileReader::GetInstParam(int prgNo, int key, + int velocity) const +{ + NW4RAssertPointerNonnull_Line(135, mHeader); + if (!mHeader) + return nullptr; + + if (prgNo < 0 || prgNo >= static_cast<int>(mDataBlock->instTable.count)) + return nullptr; + + BankFile::DataRegion const *ref = &mDataBlock->instTable.item[prgNo]; + + if (ref->dataType == 4) + return nullptr; + + if (ref->dataType != 1) + { + ref = GetReferenceToSubRegion(ref, key); + if (!ref) + return nullptr; + } + + if (ref->dataType == 4) + return nullptr; + + if (ref->dataType != 1) + { + ref = GetReferenceToSubRegion(ref, velocity); + if (!ref) + return nullptr; + } + + if (ref->dataType != 1) + return nullptr; + + BankFile::InstParam const *instParam = + Util::GetDataRefAddress1(*ref, &mDataBlock->instTable); + + return instParam; +} + +bool BankFileReader::ReadInstInfo(InstInfo *instInfo, int prgNo, int key, + int velocity) const +{ + NW4RAssertPointerNonnull_Line(188, instInfo); + + BankFile::InstParam const *instParam = GetInstParam(prgNo, key, velocity); + if (!instParam) + return false; + + if (instParam->waveDataLocationType + == InstInfo::WaveDataLocation::WAVE_DATA_LOCATION_INDEX) + { + if (instParam->waveIndex < 0) + return false; + + instInfo->waveDataLocation.type = + InstInfo::WaveDataLocation::WAVE_DATA_LOCATION_INDEX; + instInfo->waveDataLocation.index = instParam->waveIndex; + } + else if (instParam->waveDataLocationType + == InstInfo::WaveDataLocation::WAVE_DATA_LOCATION_ADDRESS) + { + if (!instParam->waveInfoAddress) + return false; + + instInfo->waveDataLocation.type = + InstInfo::WaveDataLocation::WAVE_DATA_LOCATION_ADDRESS; + instInfo->waveDataLocation.address = instParam->waveInfoAddress; + } + else if (instParam->waveDataLocationType + == InstInfo::WaveDataLocation::WAVE_DATA_LOCATION_CALLBACK) + { + if (!instParam->waveDataLocationCallback) + return false; + + instInfo->waveDataLocation.type = + InstInfo::WaveDataLocation::WAVE_DATA_LOCATION_CALLBACK; + instInfo->waveDataLocation.callback = + instParam->waveDataLocationCallback; + } + else + { + NW4RPanicMessage_Line(210, "Invalid waveDataLocationType %d", + instParam->waveDataLocationType); + return false; + } + + instInfo->attack = instParam->attack; + instInfo->hold = instParam->hold; + instInfo->decay = instParam->decay; + instInfo->sustain = instParam->sustain; + instInfo->release = instParam->release; + instInfo->originalKey = instParam->originalKey; + instInfo->pan = instParam->pan; + + if (mHeader->fileHeader.version >= NW4R_FILE_VERSION(1, 1)) + { + instInfo->volume = instParam->volume; + instInfo->tune = instParam->tune; + } + else + { + instInfo->volume = 127; + instInfo->tune = 1.0f; + } + + switch (instParam->noteOffType) + { + case 0: + instInfo->noteOffType = InstInfo::NOTE_OFF_TYPE_RELEASE; + break; + + case 1: + instInfo->noteOffType = InstInfo::NOTE_OFF_TYPE_IGNORE; + break; + + default: + NW4RPanicMessage_Line(240, "Invalid noteOffType %d", + instParam->noteOffType); + return false; + } + + instInfo->alternateAssign = instParam->alternateAssign; + + return true; +} + +BankFile::DataRegion const *BankFileReader::GetReferenceToSubRegion( + BankFile::DataRegion const *ref, int splitKey) const +{ + BankFile::DataRegion const *subRef = nullptr; + + switch (ref->dataType) + { + case 0: + break; + + case 1: + subRef = ref; + break; + + case 2: + { + BankFile::RangeTable const *table = + Util::GetDataRefAddress2(*ref, &mDataBlock->instTable); + + if (!table) + return nullptr; + + int index = 0; + while (splitKey > ReadByte(table->key + index)) + { + if (++index >= table->tableSize) + return nullptr; + } + + u32 refOffset = sizeof(BankFile::DataRegion) * index + + ut::RoundUp(table->tableSize + 1, 4); + + /* TODO: fake: how to properly match this call? (the arguments to + * AddOffsetToPtr are supposed to be the other way around) + */ + subRef = static_cast<BankFile::DataRegion const *>( + ut::AddOffsetToPtr(reinterpret_cast<void const *>(refOffset), + reinterpret_cast<u32>(table))); + } + break; + + case 3: + { + BankFile::IndexTable const *table = + Util::GetDataRefAddress3(*ref, &mDataBlock->instTable); + + if (!table) + return nullptr; + + if (splitKey < table->min || splitKey > table->max) + return nullptr; + + subRef = reinterpret_cast<BankFile::DataRegion const *>( + table->ref + + (splitKey - table->min) * sizeof(BankFile::DataRegion)); + } + break; + } + + return subRef; +} + +bool BankFileReader::ReadWaveInfo( + WaveInfo *waveParam, InstInfo::WaveDataLocation const &waveDataLocation, + void const *waveDataAddress, WaveInfo const **waveInfoAddress) const +{ + NW4RAssertPointerNonnull_Line(331, waveParam); + + if (waveInfoAddress) + *waveInfoAddress = nullptr; + + if (!mHeader) + return false; + + if (waveDataLocation.type + == InstInfo::WaveDataLocation::WAVE_DATA_LOCATION_INDEX) + { + int waveIndex = waveDataLocation.index; + + if (!mWaveBlock) + { + WaveArchiveReader waveArchiveReader(waveDataAddress); + WaveFile::FileHeader const *fileHeader = + waveArchiveReader.GetWaveFile(waveIndex); + + if (!fileHeader) + return false; + + WaveFileReader waveFileReader(fileHeader); + return waveFileReader.ReadWaveInfo(waveParam); + } + else if (waveIndex >= mWaveBlock->waveInfoTable.count) + { + return false; + } + else + { + WaveFile::WaveInfo const *waveInfo = Util::GetDataRefAddress0( + mWaveBlock->waveInfoTable.item[waveIndex], + &mWaveBlock->waveInfoTable); + + if (!waveInfo) + return false; + + WaveFileReader waveFileReader(waveInfo); + return waveFileReader.ReadWaveInfo(waveParam, waveDataAddress); + } + } + else if (waveDataLocation.type + == InstInfo::WaveDataLocation::WAVE_DATA_LOCATION_ADDRESS) + { + if (!waveDataLocation.address) + return false; + + if (waveInfoAddress) + *waveInfoAddress = waveDataLocation.address; + + *waveParam = *waveDataLocation.address; + return true; + } + else if (waveDataLocation.type + == InstInfo::WaveDataLocation::WAVE_DATA_LOCATION_CALLBACK) + { + if (!waveDataLocation.callback) + return false; + + WaveInfo *waveInfo = waveDataLocation.callback->at_0x08(); + if (!waveInfo) + return false; + + if (waveInfoAddress) + *waveInfoAddress = waveInfo; + + *waveParam = *waveInfo; + return true; + } + else + { + NW4RPanicMessage_Line(389, "Invalid waveDataLocation.type %d", + waveDataLocation.type); + return false; + } +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_BasicPlayer.cpp b/src/nw4r/snd/snd_BasicPlayer.cpp index 7e14d54e..bcac6152 100644 --- a/src/nw4r/snd/snd_BasicPlayer.cpp +++ b/src/nw4r/snd/snd_BasicPlayer.cpp @@ -1 +1,103 @@ -#include "nw4r/snd/snd_BasicPlayer.h" +#include "nw4r/snd/BasicPlayer.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_BasicPlayer.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <macros.h> // ARRAY_LENGTH +#include <types.h> // f32 + +#include "nw4r/snd/BasicSound.h" +#include "nw4r/snd/global.h" + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +void PlayerParamSet::Init() +{ + volume = 1.0f; + pitch = 1.0f; + pan = 0.0f; + surroundPan = 0.0f; + lpfFreq = 0.0f; + biquadType = 0; + biquadValue = 0.0f; + remoteFilter = 0; + outputLineFlag = 1; + mainOutVolume = 1.0f; + mainSend = 0.0f; + panMode = PAN_MODE_DUAL; + panCurve = PAN_CURVE_SQRT; + + for (int i = 0; i < AUX_BUS_NUM; i++) + fxSend[i] = 0.0f; + + for (int i = 0; i < (int)ARRAY_LENGTH(voiceOutParam); i++) + { + VoiceOutParam *param = &voiceOutParam[i]; + + param->volume = 1.0f; + param->pitch = 1.0f; + param->pan = 0.0f; + param->surroundPan = 0.0f; + param->fxSend = 0.0f; + param->lpf = 0.0f; + } +} + +BasicPlayer::BasicPlayer() : + mId (BasicSound::INVALID_ID) +{ + InitParam(); +} + +void BasicPlayer::InitParam() +{ + mPlayerParamSet.Init(); +} + +void BasicPlayer::SetFxSend(AuxBus bus, f32 send) +{ + // specifically not the source variant + NW4RAssertHeaderClampedLValue_Line(81, bus, AUX_A, AUX_BUS_NUM); + + mPlayerParamSet.fxSend[bus] = send; +} + +f32 BasicPlayer::GetFxSend(AuxBus bus) const +{ + // specifically not the source variant + NW4RAssertHeaderClampedLValue_Line(87, bus, AUX_A, AUX_BUS_NUM); + + return mPlayerParamSet.fxSend[bus]; +} + +void BasicPlayer::SetBiquadFilter(int type, f32 value) +{ + // specifically not the source variants + NW4RAssertHeaderClampedLRValue_Line(93, type, 0, 127); + NW4RAssertHeaderClampedLRValue_Line(94, value, 0.0f, 1.0f); + + mPlayerParamSet.biquadType = type; + mPlayerParamSet.biquadValue = value; +} + +void BasicPlayer::SetRemoteFilter(int filter) +{ + // specifically not the source variant + NW4RAssertHeaderClampedLRValue_Line(102, filter, 0, 127); + + mPlayerParamSet.remoteFilter = filter; +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_BasicSound.cpp b/src/nw4r/snd/snd_BasicSound.cpp index 468d1264..69bb2e1f 100644 --- a/src/nw4r/snd/snd_BasicSound.cpp +++ b/src/nw4r/snd/snd_BasicSound.cpp @@ -1 +1,680 @@ -#include "nw4r/snd/snd_BasicSound.h" +#include "nw4r/snd/BasicSound.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_BasicSound.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <climits> // ULONG_MAX +#include <cstring> + +#include <macros.h> +#include <types.h> + +#include "nw4r/snd/BasicPlayer.h" +#include "nw4r/snd/ExternalSoundPlayer.h" +#include "nw4r/snd/global.h" +#include "nw4r/snd/MoveValue.h" +#include "nw4r/snd/SoundActor.h" +#include "nw4r/snd/SoundHandle.h" +#include "nw4r/snd/SoundPlayer.h" + +#include "nw4r/ut/inlines.h" // ut::Clamp +#include "nw4r/ut/RuntimeTypeInfo.h" + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * types + */ + +// forward declarations +namespace nw4r { namespace snd { namespace detail { class PlayerHeap; }}} + +/******************************************************************************* + * variables + */ + +namespace nw4r { namespace snd { namespace detail +{ + ut::detail::RuntimeTypeInfo const BasicSound::typeInfo(nullptr); +}}} // namespace nw4r::snd::detail + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +BasicSound::BasicSound(int priority, int ambientPriority) : + mPlayerHeap (nullptr), + mGeneralHandle (nullptr), + mTempGeneralHandle (nullptr), + mSoundPlayer (nullptr), + mSoundActor (nullptr), + mExtSoundPlayer (nullptr), + mId (INVALID_ID), + mPauseNestCounter (0) +{ + // specifically not the source variant + NW4RAssertHeaderClampedLRValue_Line(53, priority, PRIORITY_MIN, + PRIORITY_MAX); + + mAmbientInfo.paramUpdateCallback = nullptr; + mAmbientInfo.argUpdateCallback = nullptr; + mAmbientInfo.argAllocaterCallback = nullptr; + mAmbientInfo.arg = nullptr; + mAmbientInfo.argSize = 0; + + mVoiceOutCount = 1; + mPriority = priority; + mAmbientParam.priority = ambientPriority; +} + +void BasicSound::InitParam() +{ + mPauseState = PAUSE_STATE_NORMAL; + mUnPauseFlag = false; + mStartFlag = false; + mStartedFlag = false; + mAutoStopFlag = false; + mFadeOutFlag = false; + mAutoStopCounter = 0; + mUpdateCounter = 0; + + mFadeVolume.InitValue(0.0f); + mPauseFadeVolume.InitValue(1.0f); + mFadeVolume.SetTarget(1.0f, 1); + + mInitVolume = 1.0f; + mExtPitch = 1.0f; + mExtPan = 0.0f; + mExtSurroundPan = 0.0f; + + mExtMoveVolume.InitValue(1.0f); + + // clang-format off + mLpfFreq = 0.0f; + mBiquadFilterType = 0; + mBiquadFilterValue = 0.0f; + mOutputLineFlag = mSoundPlayer ? mSoundPlayer->GetDefaultOutputLine() : 1; + mMainOutVolume = 1.0f; + mMainSend = 0.0f; + // clang-format on + + for (int i = 0; i < AUX_BUS_NUM; i++) + mFxSend[i] = 0.0f; + + mAmbientParam.volume = 1.0f; + mAmbientParam.pitch = 1.0f; + mAmbientParam.pan = 0.0f; + mAmbientParam.surroundPan = 0.0f; + mAmbientParam.fxSend = 0.0f; + mAmbientParam.lpf = 0.0f; + mAmbientParam.biquadFilterValue = 0.0f; + mAmbientParam.biquadFilterType = 0; + mAmbientParam.priority = 0; + + mPauseNestCounter = 0; +} + +void BasicSound::StartPrepared() +{ + if (!mStartedFlag) + mStartFlag = true; +} + +void BasicSound::Stop(int fadeFrames) +{ + BasicPlayer &basicPlayer = GetBasicPlayer(); + + if (fadeFrames == 0 || !basicPlayer.IsActive() || !basicPlayer.IsStarted() + || basicPlayer.IsPause()) + { + Shutdown(); + return; + } + + int frames = fadeFrames * mFadeVolume.GetValue(); + mFadeVolume.SetTarget(0.0f, frames); + + SetPlayerPriority(0); + + mAutoStopFlag = false; + mPauseState = PAUSE_STATE_NORMAL; + mUnPauseFlag = false; + mFadeOutFlag = true; +} + +void BasicSound::Pause(bool flag, int fadeFrames) +{ + int frames; + + if (flag) + { + mPauseNestCounter++; + + switch (mPauseState) + { + case PAUSE_STATE_NORMAL: + case PAUSE_STATE_PAUSING: + case PAUSE_STATE_UNPAUSING: + frames = fadeFrames * mPauseFadeVolume.GetValue(); + if (frames <= 0) + frames = 1; + + mPauseFadeVolume.SetTarget(0.0f, frames); + mPauseState = PAUSE_STATE_PAUSING; + mUnPauseFlag = false; + + return; + + case PAUSE_STATE_PAUSED: + return; + + default: + NW4RPanicMessage_Line(207, "Unexpected pause state %d", + mPauseState); + return; + } + } + else + { + if (mPauseNestCounter && --mPauseNestCounter) + return; + + switch (mPauseState) + { + case PAUSE_STATE_NORMAL: + return; + + case PAUSE_STATE_PAUSING: + case PAUSE_STATE_PAUSED: + case PAUSE_STATE_UNPAUSING: + frames = fadeFrames * (1.0f - mPauseFadeVolume.GetValue()); + if (frames <= 0) + frames = 1; + + mPauseFadeVolume.SetTarget(1.0f, frames); + mPauseState = PAUSE_STATE_UNPAUSING; + mUnPauseFlag = true; + + return; + + default: + NW4RPanicMessage_Line(234, "Unexpected pause state %d", + mPauseState); + return; + } + } +} + +void BasicSound::SetAutoStopCounter(int count) +{ + mAutoStopCounter = count; + mAutoStopFlag = count > 0; +} + +bool BasicSound::IsPaused() const +{ + return mPauseState == PAUSE_STATE_PAUSING + || mPauseState == PAUSE_STATE_PAUSED; +} + +void BasicSound::Update() +{ + BasicPlayer &basicPlayer = GetBasicPlayer(); + + if (mAutoStopFlag && basicPlayer.IsActive()) + { + if (mAutoStopCounter == 0) + { + if (mPauseState == PAUSE_STATE_NORMAL + || mPauseState == PAUSE_STATE_UNPAUSING) + { + Stop(0); + return; + } + } + else + { + mAutoStopCounter--; + } + } + + bool playerStartFlag = false; + if (!mStartedFlag) + { + if (!mStartFlag) + return; + + if (!IsPrepared()) + return; + + playerStartFlag = true; + } + + if (basicPlayer.IsStarted() && mUpdateCounter < ULONG_MAX) + mUpdateCounter++; + + if (!basicPlayer.IsActive()) + { + Shutdown(); + return; + } + + switch (mPauseState) + { + case PAUSE_STATE_PAUSING: + mPauseFadeVolume.Update(); + break; + + case PAUSE_STATE_UNPAUSING: + mPauseFadeVolume.Update(); + UpdateMoveValue(); + break; + + case PAUSE_STATE_NORMAL: + UpdateMoveValue(); + break; + } + + if (mAmbientInfo.argUpdateCallback) + mAmbientInfo.argUpdateCallback->at_0x0c(mAmbientInfo.arg, this); + + if (mAmbientInfo.paramUpdateCallback) + { + SoundAmbientParam ambientParam; + + mAmbientInfo.paramUpdateCallback->at_0x0c( + mAmbientInfo.arg, mId, mVoiceOutCount, &ambientParam); + + mAmbientParam.volume = ambientParam.volume; + mAmbientParam.pitch = ambientParam.pitch; + mAmbientParam.pan = ambientParam.pan; + mAmbientParam.surroundPan = ambientParam.surroundPan; + mAmbientParam.fxSend = ambientParam.fxSend; + mAmbientParam.lpf = ambientParam.lpf; + mAmbientParam.biquadFilterValue = ambientParam.biquadFilterValue; + mAmbientParam.biquadFilterType = ambientParam.biquadFilterType; + mAmbientParam.priority = ambientParam.priority; + + for (int i = 0; i < mVoiceOutCount; i++) + basicPlayer.SetVoiceOutParam(i, ambientParam.voiceOutParam[i]); + } + + if (mSoundActor) + mActorParam = mSoundActor->detail_GetActorParam(); + + UpdateParam(); + + if (mFadeOutFlag && mFadeVolume.IsFinished()) + { + mFadeOutFlag = false; + + Shutdown(); + return; + } + + if (playerStartFlag) + { + if (basicPlayer.Start()) + { + mStartedFlag = true; + mStartFlag = false; + } + else + { + Shutdown(); + return; + } + } + + if (mPauseState == PAUSE_STATE_PAUSING) + { + if (mPauseFadeVolume.IsFinished()) + { + basicPlayer.Pause(true); + mPauseState = PAUSE_STATE_PAUSED; + } + } + else if (mPauseState == PAUSE_STATE_UNPAUSING) + { + if (mPauseFadeVolume.IsFinished()) + mPauseState = PAUSE_STATE_NORMAL; + } + + if (mUnPauseFlag) + { + basicPlayer.Pause(false); + mUnPauseFlag = false; + } +} + +void BasicSound::UpdateMoveValue() +{ + mFadeVolume.Update(); + mExtMoveVolume.Update(); +} + +void BasicSound::UpdateParam() +{ + f32 volume = 1.0f; + volume *= mInitVolume; + volume *= GetSoundPlayer()->GetVolume(); + volume *= mExtMoveVolume.GetValue(); + volume *= mFadeVolume.GetValue(); + volume *= mPauseFadeVolume.GetValue(); + volume *= mAmbientParam.volume; + volume *= mActorParam.volume; + + f32 pan = 0.0f; + pan += mExtPan; + pan += mAmbientParam.pan; + pan += mActorParam.pan; + + f32 surroundPan = 0.0f; + surroundPan += mExtSurroundPan; + surroundPan += mAmbientParam.surroundPan; + + f32 pitch = 1.0f; + pitch *= mExtPitch; + pitch *= mAmbientParam.pitch; + pitch *= mActorParam.pitch; + + f32 lpfFreq = mLpfFreq; + lpfFreq += mAmbientParam.lpf; + lpfFreq += GetSoundPlayer()->GetLpfFreq(); + + int biquadFilterType = mBiquadFilterType; + f32 biquadFilterValue = mBiquadFilterValue; + + if (biquadFilterType == 0) + { + biquadFilterType = GetSoundPlayer()->GetBiquadFilterType(); + biquadFilterValue = GetSoundPlayer()->GetBiquadFilterValue(); + + if (biquadFilterType == 0) + { + biquadFilterType = mAmbientParam.biquadFilterType; + biquadFilterValue = mAmbientParam.biquadFilterValue; + } + } + + int outputLineFlag = mOutputLineFlag; + + f32 mainOutVolume = 1.0f; + mainOutVolume *= mMainOutVolume; + mainOutVolume *= GetSoundPlayer()->GetMainOutVolume(); + + f32 mainSend = 0.0f; + mainSend += mMainSend; + mainSend += GetSoundPlayer()->GetMainSend(); + + f32 fxSend[AUX_BUS_NUM]; + for (int i = 0; i < AUX_BUS_NUM; i++) + { + fxSend[i] = 0.0f; + fxSend[i] += mFxSend[i]; + fxSend[i] += GetSoundPlayer()->GetFxSend(i); + } + + fxSend[AUX_A] += mAmbientParam.fxSend; + + BasicPlayer &basicPlayer = GetBasicPlayer(); + basicPlayer.SetVolume(volume); + basicPlayer.SetPan(pan); + basicPlayer.SetSurroundPan(surroundPan); + basicPlayer.SetPitch(pitch); + basicPlayer.SetLpfFreq(lpfFreq); + basicPlayer.SetBiquadFilter(biquadFilterType, biquadFilterValue); + basicPlayer.SetOutputLine(outputLineFlag); + basicPlayer.SetMainOutVolume(mainOutVolume); + + basicPlayer.SetMainSend(mainSend); + + for (int i = 0; i < AUX_BUS_NUM; i++) + basicPlayer.SetFxSend(static_cast<AuxBus>(i), fxSend[i]); +} + +void BasicSound::Shutdown() +{ + BasicPlayer &basicPlayer = GetBasicPlayer(); + + if (basicPlayer.IsActive()) + { + if (mFadeOutFlag) + basicPlayer.SetVolume(0.0f); + + basicPlayer.Stop(); + } + + SetId(INVALID_ID); + + if (IsAttachedGeneralHandle()) + DetachGeneralHandle(); + + if (IsAttachedTempGeneralHandle()) + DetachTempGeneralHandle(); + + if (IsAttachedTempSpecialHandle()) + DetachTempSpecialHandle(); + + if (mPlayerHeap) + mSoundPlayer->detail_FreePlayerHeap(this); + + if (mSoundPlayer) + mSoundPlayer->detail_RemoveSound(this); + + if (mExtSoundPlayer) + mExtSoundPlayer->RemoveSound(this); + + if (mAmbientInfo.argAllocaterCallback) + { + mAmbientInfo.argAllocaterCallback->at_0x10(mAmbientInfo.arg, this); + + mAmbientInfo.arg = nullptr; + } + + mStartedFlag = false; + mFadeOutFlag = false; +} + +void BasicSound::AttachPlayerHeap(PlayerHeap *heap) +{ + NW4RAssertPointerNonnull_Line(615, heap); + NW4RAssert_Line(616, mPlayerHeap == NULL); + + mPlayerHeap = heap; +} + +void BasicSound::DetachPlayerHeap(PlayerHeap *heap) +{ + NW4RAssertPointerNonnull_Line(632, heap); + NW4RAssert_Line(633, heap == mPlayerHeap); + + mPlayerHeap = nullptr; +} + +void BasicSound::AttachSoundPlayer(SoundPlayer *player) +{ + NW4RAssertPointerNonnull_Line(650, player); + NW4RAssert_Line(651, mSoundPlayer == NULL); + + mSoundPlayer = player; +} + +void BasicSound::DetachSoundPlayer(SoundPlayer *player) +{ + NW4RAssertPointerNonnull_Line(667, player); + NW4RAssert_Line(668, player == mSoundPlayer); + + mSoundPlayer = nullptr; +} + +void BasicSound::AttachSoundActor(SoundActor *actor) +{ + NW4RAssertPointerNonnull_Line(685, actor); + NW4RAssert_Line(686, mSoundActor == NULL); + + mSoundActor = actor; +} + +void BasicSound::DetachSoundActor(SoundActor *actor) +{ + NW4RAssertPointerNonnull_Line(702, actor); + NW4RAssert_Line(703, actor == mSoundActor); + + mSoundActor = nullptr; +} + +void BasicSound::AttachExternalSoundPlayer(ExternalSoundPlayer *extPlayer) +{ + NW4RAssertPointerNonnull_Line(720, extPlayer); + NW4RAssert_Line(721, mExtSoundPlayer == NULL); + + mExtSoundPlayer = extPlayer; +} + +void BasicSound::DetachExternalSoundPlayer(ExternalSoundPlayer *extPlayer) +{ + NW4RAssertPointerNonnull_Line(737, extPlayer); + NW4RAssert_Line(738, extPlayer == mExtSoundPlayer); + + mExtSoundPlayer = nullptr; +} + +int BasicSound::GetVoiceOutCount() const +{ + return mVoiceOutCount; +} + +void BasicSound::SetPlayerPriority(int priority) +{ + // specifically not the source variant + NW4RAssertHeaderClampedLRValue_Line(797, priority, PRIORITY_MIN, + PRIORITY_MAX); + + mPriority = priority; + + if (mSoundPlayer) + mSoundPlayer->detail_SortPriorityList(this); + + OnUpdatePlayerPriority(); +} + +void BasicSound::SetInitialVolume(f32 volume) +{ + NW4RAssert_Line(818, volume >= 0.0f); + + mInitVolume = ut::Clamp(volume, 0.0f, 1.0f); +} + +void BasicSound::SetVolume(f32 volume, int frames) +{ + NW4RAssert_Line(833, volume >= 0.0f); + + mExtMoveVolume.SetTarget(ut::Clamp(volume, 0.0f, 1.0f), frames); +} + +void BasicSound::SetPitch(f32 pitch) +{ + NW4RAssert_Line(848, pitch >= 0.0f); + + mExtPitch = pitch; +} + +void BasicSound::SetFxSend(AuxBus bus, f32 send) +{ + // specifically not the source variant + NW4RAssertHeaderClampedLValue_Line(979, bus, AUX_A, AUX_BUS_NUM); + + GetBasicPlayer().SetFxSend(bus, send); +} + +void BasicSound::SetRemoteFilter(int filter) +{ + GetBasicPlayer().SetRemoteFilter(filter); +} + +void BasicSound::SetPanMode(PanMode panMode) +{ + GetBasicPlayer().SetPanMode(panMode); +} + +void BasicSound::SetPanCurve(PanCurve panCurve) +{ + GetBasicPlayer().SetPanCurve(panCurve); +} + +void BasicSound::SetAmbientInfo(AmbientInfo const &ambientArgInfo) +{ + NW4RAssertPointerNonnull_Line(1090, ambientArgInfo.argAllocaterCallback); + + void *ambientArg = + ambientArgInfo.argAllocaterCallback->at_0x0c(ambientArgInfo.argSize); + if (!ambientArg) + { + NW4RCheckMessage_Line(1093, ambientArg, "Failed to alloc AmbientArg."); + return; + } + + std::memcpy(ambientArg, ambientArgInfo.arg, ambientArgInfo.argSize); + mAmbientInfo = ambientArgInfo; + mAmbientInfo.arg = ambientArg; + + if (ambientArgInfo.paramUpdateCallback) + { + int voiceOutCount = + mAmbientInfo.paramUpdateCallback->at_0x14(mAmbientInfo.arg, mId); + + if (voiceOutCount > 4) + voiceOutCount = 4; + + mVoiceOutCount = voiceOutCount; + } +} + +int BasicSound::GetAmbientPriority(AmbientInfo const &ambientInfo, u32 soundId) +{ + if (!ambientInfo.paramUpdateCallback) + return PRIORITY_MIN; + + int priority = + ambientInfo.paramUpdateCallback->at_0x10(ambientInfo.arg, soundId); + + return priority; +} + +bool BasicSound::IsAttachedGeneralHandle() +{ + return mGeneralHandle != nullptr; +} + +bool BasicSound::IsAttachedTempGeneralHandle() +{ + return mTempGeneralHandle != nullptr; +} + +void BasicSound::DetachGeneralHandle() +{ + mGeneralHandle->DetachSound(); +} + +void BasicSound::DetachTempGeneralHandle() +{ + mTempGeneralHandle->DetachSound(); +} + +void BasicSound::SetId(u32 id) +{ + mId = id; + + GetBasicPlayer().SetId(id); +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_BiquadFilterCallback.cpp b/src/nw4r/snd/snd_BiquadFilterCallback.cpp new file mode 100644 index 00000000..2a924f1d --- /dev/null +++ b/src/nw4r/snd/snd_BiquadFilterCallback.cpp @@ -0,0 +1 @@ +/* This file intentionally left blank. */ diff --git a/src/nw4r/snd/snd_BiquadFilterPreset.cpp b/src/nw4r/snd/snd_BiquadFilterPreset.cpp index a20b76bb..4f1d8374 100644 --- a/src/nw4r/snd/snd_BiquadFilterPreset.cpp +++ b/src/nw4r/snd/snd_BiquadFilterPreset.cpp @@ -1 +1,633 @@ -// #include "nw4r/snd/snd_BiquadFilterPreset.h" +#include "nw4r/snd/BiquadFilterPreset.h" + +/******************************************************************************* + * headers + */ + +#include <macros.h> // ATTR_UNUSED +#include <types.h> // f32 + +#include "nw4r/snd/BiquadFilterCallback.h" + +#include "nw4r/ut/inlines.h" // ut::Clamp + +/******************************************************************************* + * variables + */ + +namespace nw4r { namespace snd { namespace detail +{ + // .rodata + + // clang-format off + BiquadFilterCallback::BiquadCoef const + BiquadFilterLpf::coefTable[COEF_TABLE_SIZE] = + { + /* b0 b1 b2 a1 a2 */ + {0x3ab3, 0x7566, 0x3ab3, 0x83b8, 0xc391}, + {0x371b, 0x6e36, 0x371b, 0x8bf8, 0xca8c}, + {0x33c7, 0x678d, 0x33c7, 0x9428, 0xd078}, + {0x30b0, 0x6161, 0x30b0, 0x9c32, 0xd582}, + {0x2dd2, 0x5ba5, 0x2dd2, 0xa40c, 0xd9cf}, + {0x2b27, 0x564e, 0x2b27, 0xabae, 0xdd7d}, + {0x28a9, 0x5152, 0x28a9, 0xb316, 0xe0a3}, + {0x2654, 0x4ca8, 0x2654, 0xba45, 0xe355}, + {0x2425, 0x4849, 0x2425, 0xc13a, 0xe5a2}, + {0x2217, 0x442f, 0x2217, 0xc7f8, 0xe797}, + {0x2029, 0x4051, 0x2029, 0xce7f, 0xe93e}, + {0x1e57, 0x3cad, 0x1e57, 0xd4d3, 0xeaa2}, + {0x1c9f, 0x393d, 0x1c9f, 0xdaf5, 0xebc8}, + {0x1aff, 0x35fd, 0x1aff, 0xe0e6, 0xecb9}, + {0x1975, 0x32eb, 0x1975, 0xe6aa, 0xed78}, + {0x1801, 0x3002, 0x1801, 0xec40, 0xee0b}, + {0x16a0, 0x2d40, 0x16a0, 0xf1ab, 0xee77}, + {0x1552, 0x2aa4, 0x1552, 0xf6ed, 0xeebe}, + {0x1415, 0x282a, 0x1415, 0xfc05, 0xeee5}, + {0x12e8, 0x25d0, 0x12e8, 0x00f6, 0xeeee}, + {0x11cb, 0x2396, 0x11cb, 0x05c1, 0xeedb}, + {0x10bd, 0x217a, 0x10bd, 0x0a65, 0xeeb0}, + {0x0fbd, 0x1f79, 0x0fbd, 0x0ee5, 0xee6d}, + {0x0eca, 0x1d93, 0x0eca, 0x1341, 0xee17}, + {0x0de3, 0x1bc7, 0x0de3, 0x177a, 0xedad}, + {0x0d09, 0x1a13, 0x0d09, 0x1b90, 0xed33}, + {0x0c3b, 0x1876, 0x0c3b, 0x1f85, 0xeca9}, + {0x0b78, 0x16ef, 0x0b78, 0x2358, 0xec12}, + {0x0abf, 0x157e, 0x0abf, 0x270b, 0xeb6e}, + {0x0a10, 0x1421, 0x0a10, 0x2a9d, 0xeabf}, + {0x096b, 0x12d7, 0x096b, 0x2e11, 0xea06}, + {0x08d0, 0x119f, 0x08d0, 0x3166, 0xe945}, + {0x083d, 0x107a, 0x083d, 0x349d, 0xe87c}, + {0x07b2, 0x0f65, 0x07b2, 0x37b7, 0xe7ad}, + {0x0730, 0x0e60, 0x0730, 0x3ab4, 0xe6d8}, + {0x06b5, 0x0d6a, 0x06b5, 0x3d95, 0xe5ff}, + {0x0642, 0x0c83, 0x0642, 0x405b, 0xe522}, + {0x05d5, 0x0baa, 0x05d5, 0x4306, 0xe443}, + {0x056f, 0x0adf, 0x056f, 0x4598, 0xe361}, + {0x0510, 0x0a1f, 0x0510, 0x4810, 0xe27e}, + {0x04b6, 0x096c, 0x04b6, 0x4a70, 0xe19b}, + {0x0462, 0x08c4, 0x0462, 0x4cb8, 0xe0b7}, + {0x0413, 0x0826, 0x0413, 0x4ee9, 0xdfd3}, + {0x03c9, 0x0793, 0x03c9, 0x5104, 0xdef1}, + {0x0384, 0x0709, 0x0384, 0x5309, 0xde10}, + {0x0344, 0x0688, 0x0344, 0x54f8, 0xdd31}, + {0x0308, 0x0610, 0x0308, 0x56d4, 0xdc54}, + {0x02d0, 0x059f, 0x02d0, 0x589c, 0xdb7a}, + {0x029b, 0x0537, 0x029b, 0x5a51, 0xdaa3}, + {0x026b, 0x04d5, 0x026b, 0x5bf4, 0xd9cf}, + {0x023d, 0x047a, 0x023d, 0x5d85, 0xd8fe}, + {0x0213, 0x0426, 0x0213, 0x5f06, 0xd831}, + {0x01eb, 0x03d7, 0x01eb, 0x6076, 0xd768}, + {0x01c7, 0x038d, 0x01c7, 0x61d6, 0xd6a3}, + {0x01a5, 0x0349, 0x01a5, 0x6327, 0xd5e2}, + {0x0185, 0x030a, 0x0185, 0x646a, 0xd526}, + {0x0168, 0x02cf, 0x0168, 0x659f, 0xd46d}, + {0x014c, 0x0299, 0x014c, 0x66c6, 0xd3ba}, + {0x0133, 0x0266, 0x0133, 0x67e1, 0xd30a}, + {0x011c, 0x0237, 0x011c, 0x68ef, 0xd25f}, + {0x0106, 0x020c, 0x0106, 0x69f1, 0xd1b9}, + {0x00f2, 0x01e4, 0x00f2, 0x6ae9, 0xd117}, + {0x00df, 0x01be, 0x00df, 0x6bd5, 0xd07a}, + {0x00ce, 0x019c, 0x00ce, 0x6cb7, 0xcfe1}, + {0x00be, 0x017c, 0x00be, 0x6d8f, 0xcf4d}, + {0x00af, 0x015e, 0x00af, 0x6e5d, 0xcebd}, + {0x00a1, 0x0143, 0x00a1, 0x6f23, 0xce31}, + {0x0095, 0x012a, 0x0095, 0x6fe0, 0xcdaa}, + {0x0089, 0x0112, 0x0089, 0x7094, 0xcd27}, + {0x007e, 0x00fd, 0x007e, 0x7140, 0xcca8}, + {0x0074, 0x00e9, 0x0074, 0x71e5, 0xcc2e}, + {0x006b, 0x00d6, 0x006b, 0x7283, 0xcbb7}, + {0x0063, 0x00c5, 0x0063, 0x7319, 0xcb44}, + {0x005b, 0x00b6, 0x005b, 0x73a9, 0xcad5}, + {0x0054, 0x00a7, 0x0054, 0x7433, 0xca6a}, + {0x004d, 0x009a, 0x004d, 0x74b7, 0xca03}, + {0x0047, 0x008e, 0x0047, 0x7534, 0xc99f}, + {0x0041, 0x0082, 0x0041, 0x75ac, 0xc93f}, + {0x003c, 0x0078, 0x003c, 0x761f, 0xc8e2}, + {0x0037, 0x006e, 0x0037, 0x768d, 0xc889}, + {0x0033, 0x0066, 0x0033, 0x76f6, 0xc833}, + {0x002f, 0x005d, 0x002f, 0x775a, 0xc7e0}, + {0x002b, 0x0056, 0x002b, 0x77ba, 0xc790}, + {0x0027, 0x004f, 0x0027, 0x7816, 0xc743}, + {0x0024, 0x0049, 0x0024, 0x786e, 0xc6f8}, + {0x0021, 0x0043, 0x0021, 0x78c2, 0xc6b1}, + {0x001f, 0x003d, 0x001f, 0x7912, 0xc66c}, + {0x001c, 0x0038, 0x001c, 0x795f, 0xc62a}, + {0x001a, 0x0034, 0x001a, 0x79a8, 0xc5ea}, + {0x0018, 0x0030, 0x0018, 0x79ee, 0xc5ad}, + {0x0016, 0x002c, 0x0016, 0x7a31, 0xc572}, + {0x0014, 0x0028, 0x0014, 0x7a71, 0xc53a}, + {0x0012, 0x0025, 0x0012, 0x7aae, 0xc503}, + {0x0011, 0x0022, 0x0011, 0x7ae9, 0xc4cf}, + {0x0010, 0x001f, 0x0010, 0x7b21, 0xc49d}, + {0x000e, 0x001d, 0x000e, 0x7b57, 0xc46d}, + {0x000d, 0x001a, 0x000d, 0x7b8a, 0xc43e}, + {0x000c, 0x0018, 0x000c, 0x7bbb, 0xc412}, + {0x000b, 0x0016, 0x000b, 0x7bea, 0xc3e7}, + {0x000a, 0x0014, 0x000a, 0x7c17, 0xc3be}, + {0x0009, 0x0013, 0x0009, 0x7c42, 0xc396}, + {0x0009, 0x0011, 0x0009, 0x7c6b, 0xc371}, + {0x0008, 0x0010, 0x0008, 0x7c92, 0xc34c}, + {0x0007, 0x000e, 0x0007, 0x7cb8, 0xc329}, + {0x0007, 0x000d, 0x0007, 0x7cdc, 0xc308}, + {0x0006, 0x000c, 0x0006, 0x7cfe, 0xc2e8}, + {0x0006, 0x000b, 0x0006, 0x7d1f, 0xc2c9}, + {0x0005, 0x000a, 0x0005, 0x7d3f, 0xc2ab}, + {0x0005, 0x0009, 0x0005, 0x7d5d, 0xc28f}, + {0x0004, 0x0009, 0x0004, 0x7d7a, 0xc274}, + {0x0004, 0x0008, 0x0004, 0x7d96, 0xc25a}, + {0x0004, 0x0007, 0x0004, 0x7db0, 0xc241}, + }; + + BiquadFilterCallback::BiquadCoef const + BiquadFilterHpf::coefTable[COEF_TABLE_SIZE] = + { + /* b0 b1 b2 a1 a2 */ + {0x3bb5, 0x8895, 0x3bb5, 0x7e7b, 0xc17f}, + {0x3bad, 0x88a6, 0x3bad, 0x7e69, 0xc18f}, + {0x3ba5, 0x88b6, 0x3ba5, 0x7e57, 0xc1a1}, + {0x3b9c, 0x88c8, 0x3b9c, 0x7e44, 0xc1b3}, + {0x3b93, 0x88da, 0x3b93, 0x7e30, 0xc1c6}, + {0x3b89, 0x88ed, 0x3b89, 0x7e1c, 0xc1da}, + {0x3b7f, 0x8901, 0x3b7f, 0x7e06, 0xc1ee}, + {0x3b75, 0x8916, 0x3b75, 0x7df0, 0xc204}, + {0x3b6a, 0x892c, 0x3b6a, 0x7dd8, 0xc21a}, + {0x3b5f, 0x8942, 0x3b5f, 0x7dbf, 0xc232}, + {0x3b53, 0x895a, 0x3b53, 0x7da6, 0xc24a}, + {0x3b47, 0x8973, 0x3b47, 0x7d8b, 0xc264}, + {0x3b3a, 0x898d, 0x3b3a, 0x7d6f, 0xc27e}, + {0x3b2c, 0x89a8, 0x3b2c, 0x7d51, 0xc29a}, + {0x3b1e, 0x89c4, 0x3b1e, 0x7d33, 0xc2b7}, + {0x3b10, 0x89e1, 0x3b10, 0x7d13, 0xc2d5}, + {0x3b00, 0x8a00, 0x3b00, 0x7cf1, 0xc2f4}, + {0x3af0, 0x8a20, 0x3af0, 0x7cce, 0xc315}, + {0x3ae0, 0x8a41, 0x3ae0, 0x7ca9, 0xc337}, + {0x3ace, 0x8a64, 0x3ace, 0x7c83, 0xc35a}, + {0x3abc, 0x8a88, 0x3abc, 0x7c5b, 0xc37f}, + {0x3aa9, 0x8aae, 0x3aa9, 0x7c31, 0xc3a6}, + {0x3a95, 0x8ad6, 0x3a95, 0x7c05, 0xc3ce}, + {0x3a81, 0x8aff, 0x3a81, 0x7bd8, 0xc3f8}, + {0x3a6b, 0x8b2a, 0x3a6b, 0x7ba8, 0xc423}, + {0x3a55, 0x8b57, 0x3a55, 0x7b76, 0xc451}, + {0x3a3d, 0x8b86, 0x3a3d, 0x7b42, 0xc480}, + {0x3a25, 0x8bb7, 0x3a25, 0x7b0b, 0xc4b1}, + {0x3a0b, 0x8bea, 0x3a0b, 0x7ad2, 0xc4e4}, + {0x39f0, 0x8c1f, 0x39f0, 0x7a96, 0xc519}, + {0x39d5, 0x8c57, 0x39d5, 0x7a58, 0xc550}, + {0x39b8, 0x8c91, 0x39b8, 0x7a16, 0xc58a}, + {0x3999, 0x8ccd, 0x3999, 0x79d2, 0xc5c6}, + {0x397a, 0x8d0c, 0x397a, 0x798b, 0xc604}, + {0x3959, 0x8d4e, 0x3959, 0x7940, 0xc645}, + {0x3937, 0x8d93, 0x3937, 0x78f2, 0xc688}, + {0x3913, 0x8dda, 0x3913, 0x78a0, 0xc6ce}, + {0x38ed, 0x8e25, 0x38ed, 0x784a, 0xc716}, + {0x38c7, 0x8e73, 0x38c7, 0x77f1, 0xc762}, + {0x389e, 0x8ec4, 0x389e, 0x7793, 0xc7b0}, + {0x3874, 0x8f19, 0x3874, 0x7731, 0xc802}, + {0x3847, 0x8f71, 0x3847, 0x76cb, 0xc856}, + {0x3819, 0x8fcd, 0x3819, 0x7660, 0xc8ae}, + {0x37e9, 0x902d, 0x37e9, 0x75f0, 0xc909}, + {0x37b7, 0x9091, 0x37b7, 0x757a, 0xc968}, + {0x3783, 0x90f9, 0x3783, 0x74ff, 0xc9ca}, + {0x374d, 0x9166, 0x374d, 0x747f, 0xca2f}, + {0x3714, 0x91d7, 0x3714, 0x73f8, 0xca98}, + {0x36d9, 0x924d, 0x36d9, 0x736b, 0xcb05}, + {0x369c, 0x92c8, 0x369c, 0x72d8, 0xcb76}, + {0x365c, 0x9348, 0x365c, 0x723e, 0xcbeb}, + {0x3619, 0x93cd, 0x3619, 0x719c, 0xcc64}, + {0x35d4, 0x9458, 0x35d4, 0x70f3, 0xcce1}, + {0x358c, 0x94e9, 0x358c, 0x7042, 0xcd63}, + {0x3540, 0x957f, 0x3540, 0x6f89, 0xcde8}, + {0x34f2, 0x961c, 0x34f2, 0x6ec7, 0xce73}, + {0x34a0, 0x96bf, 0x34a0, 0x6dfb, 0xcf01}, + {0x344b, 0x9769, 0x344b, 0x6d27, 0xcf94}, + {0x33f3, 0x981a, 0x33f3, 0x6c48, 0xd02c}, + {0x3397, 0x98d2, 0x3397, 0x6b5f, 0xd0c9}, + {0x3337, 0x9992, 0x3337, 0x6a6a, 0xd16a}, + {0x32d4, 0x9a59, 0x32d4, 0x696a, 0xd210}, + {0x326c, 0x9b28, 0x326c, 0x685e, 0xd2bb}, + {0x3200, 0x9bff, 0x3200, 0x6746, 0xd36b}, + {0x3190, 0x9cdf, 0x3190, 0x6620, 0xd41f}, + {0x311c, 0x9dc8, 0x311c, 0x64ec, 0xd4d9}, + {0x30a3, 0x9eba, 0x30a3, 0x63a9, 0xd597}, + {0x3025, 0x9fb6, 0x3025, 0x6258, 0xd65a}, + {0x2fa3, 0xa0bb, 0x2fa3, 0x60f6, 0xd721}, + {0x2f1b, 0xa1ca, 0x2f1b, 0x5f83, 0xd7ee}, + {0x2e8e, 0xa2e4, 0x2e8e, 0x5dff, 0xd8be}, + {0x2dfc, 0xa409, 0x2dfc, 0x5c68, 0xd993}, + {0x2d64, 0xa539, 0x2d64, 0x5abe, 0xda6d}, + {0x2cc6, 0xa674, 0x2cc6, 0x58ff, 0xdb4a}, + {0x2c22, 0xa7bb, 0x2c22, 0x572b, 0xdc2b}, + {0x2b79, 0xa90e, 0x2b79, 0x5541, 0xdd10}, + {0x2ac9, 0xaa6e, 0x2ac9, 0x533f, 0xddf8}, + {0x2a12, 0xabdc, 0x2a12, 0x5125, 0xdee3}, + {0x2955, 0xad56, 0x2955, 0x4ef0, 0xdfd1}, + {0x2891, 0xaedf, 0x2891, 0x4ca1, 0xe0c0}, + {0x27c5, 0xb075, 0x27c5, 0x4a34, 0xe1b1}, + {0x26f3, 0xb21b, 0x26f3, 0x47aa, 0xe2a4}, + {0x2618, 0xb3cf, 0x2618, 0x4500, 0xe396}, + {0x2536, 0xb593, 0x2536, 0x4234, 0xe489}, + {0x244c, 0xb768, 0x244c, 0x3f45, 0xe57a}, + {0x235a, 0xb94d, 0x235a, 0x3c31, 0xe669}, + {0x225f, 0xbb43, 0x225f, 0x38f6, 0xe756}, + {0x215b, 0xbd4a, 0x215b, 0x3591, 0xe83e}, + {0x204e, 0xbf64, 0x204e, 0x3200, 0xe920}, + {0x1f38, 0xc191, 0x1f38, 0x2e40, 0xe9fc}, + {0x1e17, 0xc3d1, 0x1e17, 0x2a4e, 0xeacf}, + {0x1ced, 0xc625, 0x1ced, 0x2628, 0xeb97}, + {0x1bb9, 0xc88e, 0x1bb9, 0x21c8, 0xec52}, + {0x1a7a, 0xcb0b, 0x1a7a, 0x1d2d, 0xecfd}, + {0x1930, 0xcd9f, 0x1930, 0x1850, 0xed96}, + {0x17dc, 0xd049, 0x17dc, 0x132e, 0xee18}, + {0x167b, 0xd30a, 0x167b, 0x0dc1, 0xee80} + }; + + BiquadFilterCallback::BiquadCoef const + BiquadFilterBpf512::coefTable[COEF_TABLE_SIZE] = + { + /* b0 b1 b2 a1 a2 */ + {0x2efa, 0x0000, 0xd106, 0x21d7, 0x1df4}, + {0x2e8f, 0x0000, 0xd171, 0x22ad, 0x1d1e}, + {0x2e24, 0x0000, 0xd1dc, 0x2381, 0x1c49}, + {0x2dba, 0x0000, 0xd246, 0x2455, 0x1b74}, + {0x2d50, 0x0000, 0xd2b0, 0x2527, 0x1aa1}, + {0x2ce7, 0x0000, 0xd319, 0x25f9, 0x19ce}, + {0x2c7e, 0x0000, 0xd382, 0x26cb, 0x18fc}, + {0x2c15, 0x0000, 0xd3eb, 0x279c, 0x182a}, + {0x2bad, 0x0000, 0xd453, 0x286c, 0x1759}, + {0x2b44, 0x0000, 0xd4bc, 0x293c, 0x1689}, + {0x2add, 0x0000, 0xd523, 0x2a0b, 0x15b9}, + {0x2a75, 0x0000, 0xd58b, 0x2ada, 0x14ea}, + {0x2a0d, 0x0000, 0xd5f3, 0x2ba8, 0x141a}, + {0x29a6, 0x0000, 0xd65a, 0x2c76, 0x134c}, + {0x293f, 0x0000, 0xd6c1, 0x2d43, 0x127d}, + {0x28d8, 0x0000, 0xd728, 0x2e11, 0x11af}, + {0x2871, 0x0000, 0xd78f, 0x2ede, 0x10e2}, + {0x280a, 0x0000, 0xd7f6, 0x2faa, 0x1014}, + {0x27a4, 0x0000, 0xd85c, 0x3076, 0x0f47}, + {0x273d, 0x0000, 0xd8c3, 0x3142, 0x0e7a}, + {0x26d7, 0x0000, 0xd929, 0x320e, 0x0dae}, + {0x2671, 0x0000, 0xd98f, 0x32da, 0x0ce2}, + {0x260b, 0x0000, 0xd9f5, 0x33a5, 0x0c16}, + {0x25a5, 0x0000, 0xda5b, 0x3470, 0x0b4a}, + {0x253f, 0x0000, 0xdac1, 0x353a, 0x0a7e}, + {0x24da, 0x0000, 0xdb26, 0x3605, 0x09b3}, + {0x2474, 0x0000, 0xdb8c, 0x36cf, 0x08e8}, + {0x240f, 0x0000, 0xdbf1, 0x3799, 0x081d}, + {0x23a9, 0x0000, 0xdc57, 0x3863, 0x0753}, + {0x2344, 0x0000, 0xdcbc, 0x392c, 0x0689}, + {0x22df, 0x0000, 0xdd21, 0x39f5, 0x05bf}, + {0x227b, 0x0000, 0xdd85, 0x3abe, 0x04f5}, + {0x2216, 0x0000, 0xddea, 0x3b86, 0x042c}, + {0x21b1, 0x0000, 0xde4f, 0x3c4f, 0x0363}, + {0x214d, 0x0000, 0xdeb3, 0x3d17, 0x029a}, + {0x20e9, 0x0000, 0xdf17, 0x3dde, 0x01d1}, + {0x2085, 0x0000, 0xdf7b, 0x3ea6, 0x0109}, + {0x2021, 0x0000, 0xdfdf, 0x3f6d, 0x0041}, + {0x1fbd, 0x0000, 0xe043, 0x4033, 0xff7a}, + {0x1f59, 0x0000, 0xe0a7, 0x40fa, 0xfeb3}, + {0x1ef6, 0x0000, 0xe10a, 0x41c0, 0xfdec}, + {0x1e93, 0x0000, 0xe16d, 0x4285, 0xfd25}, + {0x1e30, 0x0000, 0xe1d0, 0x434a, 0xfc5f}, + {0x1dcd, 0x0000, 0xe233, 0x440f, 0xfb9a}, + {0x1d6a, 0x0000, 0xe296, 0x44d4, 0xfad4}, + {0x1d08, 0x0000, 0xe2f8, 0x4597, 0xfa10}, + {0x1ca6, 0x0000, 0xe35a, 0x465b, 0xf94b}, + {0x1c44, 0x0000, 0xe3bc, 0x471e, 0xf887}, + {0x1be2, 0x0000, 0xe41e, 0x47e1, 0xf7c4}, + {0x1b80, 0x0000, 0xe480, 0x48a3, 0xf701}, + {0x1b1f, 0x0000, 0xe4e1, 0x4964, 0xf63f}, + {0x1abe, 0x0000, 0xe542, 0x4a25, 0xf57d}, + {0x1a5e, 0x0000, 0xe5a2, 0x4ae6, 0xf4bb}, + {0x19fd, 0x0000, 0xe603, 0x4ba6, 0xf3fa}, + {0x199d, 0x0000, 0xe663, 0x4c65, 0xf33a}, + {0x193d, 0x0000, 0xe6c3, 0x4d24, 0xf27a}, + {0x18dd, 0x0000, 0xe723, 0x4de3, 0xf1bb}, + {0x187e, 0x0000, 0xe782, 0x4ea0, 0xf0fc}, + {0x181f, 0x0000, 0xe7e1, 0x4f5e, 0xf03e}, + {0x17c0, 0x0000, 0xe840, 0x501a, 0xef81}, + {0x1762, 0x0000, 0xe89e, 0x50d6, 0xeec4}, + {0x1704, 0x0000, 0xe8fc, 0x5192, 0xee07}, + {0x16a6, 0x0000, 0xe95a, 0x524d, 0xed4c}, + {0x1648, 0x0000, 0xe9b8, 0x5307, 0xec91}, + {0x15eb, 0x0000, 0xea15, 0x53c0, 0xebd6}, + {0x158e, 0x0000, 0xea72, 0x5479, 0xeb1c}, + {0x1532, 0x0000, 0xeace, 0x5532, 0xea63}, + {0x14d5, 0x0000, 0xeb2b, 0x55ea, 0xe9ab}, + {0x1479, 0x0000, 0xeb87, 0x56a1, 0xe8f3}, + {0x141e, 0x0000, 0xebe2, 0x5757, 0xe83b}, + {0x13c2, 0x0000, 0xec3e, 0x580d, 0xe785}, + {0x1367, 0x0000, 0xec99, 0x58c2, 0xe6ce}, + {0x130d, 0x0000, 0xecf3, 0x5977, 0xe619}, + {0x12b2, 0x0000, 0xed4e, 0x5a2b, 0xe564}, + {0x1258, 0x0000, 0xeda8, 0x5ade, 0xe4b0}, + {0x11fe, 0x0000, 0xee02, 0x5b91, 0xe3fc}, + {0x11a5, 0x0000, 0xee5b, 0x5c43, 0xe349}, + {0x114b, 0x0000, 0xeeb5, 0x5cf5, 0xe297}, + {0x10f2, 0x0000, 0xef0e, 0x5da6, 0xe1e5}, + {0x109a, 0x0000, 0xef66, 0x5e56, 0xe134}, + {0x1041, 0x0000, 0xefbf, 0x5f06, 0xe083}, + {0x0fe9, 0x0000, 0xf017, 0x5fb6, 0xdfd3}, + {0x0f91, 0x0000, 0xf06f, 0x6065, 0xdf23}, + {0x0f3a, 0x0000, 0xf0c6, 0x6113, 0xde74}, + {0x0ee2, 0x0000, 0xf11e, 0x61c1, 0xddc5}, + {0x0e8b, 0x0000, 0xf175, 0x626f, 0xdd16}, + {0x0e34, 0x0000, 0xf1cc, 0x631c, 0xdc69}, + {0x0dde, 0x0000, 0xf222, 0x63c8, 0xdbbb}, + {0x0d87, 0x0000, 0xf279, 0x6475, 0xdb0e}, + {0x0d31, 0x0000, 0xf2cf, 0x6520, 0xda61}, + {0x0cda, 0x0000, 0xf326, 0x65cc, 0xd9b5}, + {0x0c84, 0x0000, 0xf37c, 0x6677, 0xd909}, + {0x0c2f, 0x0000, 0xf3d1, 0x6722, 0xd85d}, + {0x0bd9, 0x0000, 0xf427, 0x67cd, 0xd7b2}, + {0x0b83, 0x0000, 0xf47d, 0x6878, 0xd706}, + {0x0b2d, 0x0000, 0xf4d3, 0x6922, 0xd65b}, + {0x0ad8, 0x0000, 0xf528, 0x69cc, 0xd5b0}, + {0x0a82, 0x0000, 0xf57e, 0x6a77, 0xd505}, + {0x0a2d, 0x0000, 0xf5d3, 0x6b21, 0xd45a}, + {0x09d7, 0x0000, 0xf629, 0x6bcb, 0xd3af}, + {0x0982, 0x0000, 0xf67e, 0x6c75, 0xd304}, + {0x092c, 0x0000, 0xf6d4, 0x6d1f, 0xd259}, + {0x08d7, 0x0000, 0xf729, 0x6dca, 0xd1ae}, + {0x0881, 0x0000, 0xf77f, 0x6e74, 0xd102}, + {0x082b, 0x0000, 0xf7d5, 0x6f1f, 0xd056}, + {0x07d5, 0x0000, 0xf82b, 0x6fca, 0xcfaa}, + {0x077f, 0x0000, 0xf881, 0x7076, 0xcefe}, + {0x0729, 0x0000, 0xf8d7, 0x7122, 0xce51}, + {0x06d2, 0x0000, 0xf92e, 0x71ce, 0xcda4}, + {0x067b, 0x0000, 0xf985, 0x727b, 0xccf6}, + {0x0624, 0x0000, 0xf9dc, 0x7329, 0xcc48}, + {0x05cc, 0x0000, 0xfa34, 0x73d7, 0xcb98}, + {0x0574, 0x0000, 0xfa8c, 0x7487, 0xcae8}, + {0x051c, 0x0000, 0xfae4, 0x7537, 0xca38}, + {0x04c3, 0x0000, 0xfb3d, 0x75e7, 0xc986}, + {0x046a, 0x0000, 0xfb96, 0x7699, 0xc8d3}, + {0x0410, 0x0000, 0xfbf0, 0x774c, 0xc81f}, + {0x03b5, 0x0000, 0xfc4b, 0x7800, 0xc76a}, + {0x035a, 0x0000, 0xfca6, 0x78b6, 0xc6b4}, + {0x02fe, 0x0000, 0xfd02, 0x796d, 0xc5fc}, + {0x02a2, 0x0000, 0xfd5e, 0x7a25, 0xc543}, + {0x0244, 0x0000, 0xfdbc, 0x7adf, 0xc488} + }; + + BiquadFilterCallback::BiquadCoef const + BiquadFilterBpf1024::coefTable[COEF_TABLE_SIZE] = + { + /* b0 b1 b2 a1 a2 */ + {0x2f04, 0x0000, 0xd0fc, 0x21de, 0x1e08}, + {0x2e99, 0x0000, 0xd167, 0x22b3, 0x1d32}, + {0x2e2f, 0x0000, 0xd1d1, 0x2386, 0x1c5d}, + {0x2dc5, 0x0000, 0xd23b, 0x2459, 0x1b89}, + {0x2d5b, 0x0000, 0xd2a5, 0x252b, 0x1ab6}, + {0x2cf2, 0x0000, 0xd30e, 0x25fd, 0x19e4}, + {0x2c89, 0x0000, 0xd377, 0x26ce, 0x1912}, + {0x2c20, 0x0000, 0xd3e0, 0x279e, 0x1841}, + {0x2bb8, 0x0000, 0xd448, 0x286d, 0x1770}, + {0x2b50, 0x0000, 0xd4b0, 0x293c, 0x16a0}, + {0x2ae8, 0x0000, 0xd518, 0x2a0b, 0x15d0}, + {0x2a80, 0x0000, 0xd580, 0x2ad9, 0x1500}, + {0x2a19, 0x0000, 0xd5e7, 0x2ba7, 0x1431}, + {0x29b1, 0x0000, 0xd64f, 0x2c74, 0x1363}, + {0x294a, 0x0000, 0xd6b6, 0x2d41, 0x1294}, + {0x28e3, 0x0000, 0xd71d, 0x2e0e, 0x11c6}, + {0x287c, 0x0000, 0xd784, 0x2eda, 0x10f8}, + {0x2815, 0x0000, 0xd7eb, 0x2fa6, 0x102a}, + {0x27af, 0x0000, 0xd851, 0x3072, 0x0f5d}, + {0x2748, 0x0000, 0xd8b8, 0x313e, 0x0e90}, + {0x26e1, 0x0000, 0xd91f, 0x3209, 0x0dc3}, + {0x267b, 0x0000, 0xd985, 0x32d4, 0x0cf6}, + {0x2614, 0x0000, 0xd9ec, 0x339f, 0x0c29}, + {0x25ae, 0x0000, 0xda52, 0x346a, 0x0b5c}, + {0x2548, 0x0000, 0xdab8, 0x3535, 0x0a8f}, + {0x24e1, 0x0000, 0xdb1f, 0x35ff, 0x09c3}, + {0x247b, 0x0000, 0xdb85, 0x36ca, 0x08f6}, + {0x2415, 0x0000, 0xdbeb, 0x3794, 0x082a}, + {0x23af, 0x0000, 0xdc51, 0x385e, 0x075d}, + {0x2349, 0x0000, 0xdcb7, 0x3928, 0x0691}, + {0x22e2, 0x0000, 0xdd1e, 0x39f3, 0x05c5}, + {0x227c, 0x0000, 0xdd84, 0x3abd, 0x04f8}, + {0x2216, 0x0000, 0xddea, 0x3b86, 0x042c}, + {0x21b0, 0x0000, 0xde50, 0x3c50, 0x035f}, + {0x2149, 0x0000, 0xdeb7, 0x3d1a, 0x0293}, + {0x20e3, 0x0000, 0xdf1d, 0x3de4, 0x01c6}, + {0x207d, 0x0000, 0xdf83, 0x3eae, 0x00f9}, + {0x2016, 0x0000, 0xdfea, 0x3f78, 0x002c}, + {0x1fb0, 0x0000, 0xe050, 0x4042, 0xff5f}, + {0x1f49, 0x0000, 0xe0b7, 0x410c, 0xfe92}, + {0x1ee2, 0x0000, 0xe11e, 0x41d7, 0xfdc4}, + {0x1e7b, 0x0000, 0xe185, 0x42a1, 0xfcf7}, + {0x1e14, 0x0000, 0xe1ec, 0x436c, 0xfc29}, + {0x1dad, 0x0000, 0xe253, 0x4436, 0xfb5b}, + {0x1d46, 0x0000, 0xe2ba, 0x4501, 0xfa8c}, + {0x1cdf, 0x0000, 0xe321, 0x45cd, 0xf9bd}, + {0x1c77, 0x0000, 0xe389, 0x4698, 0xf8ee}, + {0x1c0f, 0x0000, 0xe3f1, 0x4764, 0xf81e}, + {0x1ba7, 0x0000, 0xe459, 0x4831, 0xf74d}, + {0x1b3e, 0x0000, 0xe4c2, 0x48fd, 0xf67c}, + {0x1ad5, 0x0000, 0xe52b, 0x49cb, 0xf5ab}, + {0x1a6c, 0x0000, 0xe594, 0x4a99, 0xf4d8}, + {0x1a03, 0x0000, 0xe5fd, 0x4b67, 0xf405}, + {0x1998, 0x0000, 0xe668, 0x4c37, 0xf331}, + {0x192e, 0x0000, 0xe6d2, 0x4d07, 0xf25c}, + {0x18c3, 0x0000, 0xe73d, 0x4dd8, 0xf186}, + {0x1857, 0x0000, 0xe7a9, 0x4eaa, 0xf0af}, + {0x17eb, 0x0000, 0xe815, 0x4f7d, 0xefd6}, + {0x177e, 0x0000, 0xe882, 0x5051, 0xeefc}, + {0x1710, 0x0000, 0xe8f0, 0x5127, 0xee21}, + {0x16a2, 0x0000, 0xe95e, 0x51fe, 0xed44}, + {0x1632, 0x0000, 0xe9ce, 0x52d7, 0xec65}, + {0x15c2, 0x0000, 0xea3e, 0x53b2, 0xeb84}, + {0x1550, 0x0000, 0xeab0, 0x548e, 0xeaa1}, + {0x14de, 0x0000, 0xeb22, 0x556d, 0xe9bc}, + {0x146a, 0x0000, 0xeb96, 0x564e, 0xe8d4}, + {0x13f5, 0x0000, 0xec0b, 0x5731, 0xe7e9}, + {0x137e, 0x0000, 0xec82, 0x5817, 0xe6fc}, + {0x1306, 0x0000, 0xecfa, 0x5900, 0xe60b}, + {0x128c, 0x0000, 0xed74, 0x59ed, 0xe517}, + {0x1210, 0x0000, 0xedf0, 0x5adc, 0xe41f}, + {0x1192, 0x0000, 0xee6e, 0x5bd0, 0xe323}, + {0x1111, 0x0000, 0xeeef, 0x5cc8, 0xe223}, + {0x108f, 0x0000, 0xef71, 0x5dc4, 0xe11e}, + {0x100a, 0x0000, 0xeff6, 0x5ec5, 0xe013}, + {0x0f82, 0x0000, 0xf07e, 0x5fcb, 0xdf04}, + {0x0ef7, 0x0000, 0xf109, 0x60d7, 0xddee}, + {0x0e69, 0x0000, 0xf197, 0x61e9, 0xdcd1}, + {0x0dd7, 0x0000, 0xf229, 0x6301, 0xdbae}, + {0x0d41, 0x0000, 0xf2bf, 0x6421, 0xda83}, + {0x0ca8, 0x0000, 0xf358, 0x6549, 0xd94f}, + {0x0c09, 0x0000, 0xf3f7, 0x6679, 0xd813}, + {0x0b66, 0x0000, 0xf49a, 0x67b3, 0xd6cd}, + {0x0abe, 0x0000, 0xf542, 0x68f6, 0xd57c}, + {0x0a10, 0x0000, 0xf5f0, 0x6a45, 0xd420}, + {0x095b, 0x0000, 0xf6a5, 0x6b9f, 0xd2b7}, + {0x08a0, 0x0000, 0xf760, 0x6d06, 0xd141}, + {0x07de, 0x0000, 0xf822, 0x6e7b, 0xcfbc}, + {0x0714, 0x0000, 0xf8ec, 0x6fff, 0xce27}, + {0x0640, 0x0000, 0xf9c0, 0x7195, 0xcc81}, + {0x0564, 0x0000, 0xfa9c, 0x733c, 0xcac7}, + {0x047d, 0x0000, 0xfb83, 0x74f7, 0xc8f9}, + {0x038a, 0x0000, 0xfc76, 0x76c8, 0xc714}, + }; + + BiquadFilterCallback::BiquadCoef const + BiquadFilterBpf2048::coefTable[COEF_TABLE_SIZE] = + { + {0x3f42, 0x0000, 0xc0be, 0x0136, 0x3e83}, + {0x3e8a, 0x0000, 0xc176, 0x02a3, 0x3d14}, + {0x3dd9, 0x0000, 0xc227, 0x0401, 0x3bb3}, + {0x3d2f, 0x0000, 0xc2d1, 0x0553, 0x3a5e}, + {0x3c8b, 0x0000, 0xc375, 0x0699, 0x3916}, + {0x3bec, 0x0000, 0xc414, 0x07d5, 0x37d7}, + {0x3b51, 0x0000, 0xc4af, 0x0907, 0x36a2}, + {0x3abb, 0x0000, 0xc545, 0x0a31, 0x3576}, + {0x3a28, 0x0000, 0xc5d8, 0x0b53, 0x3451}, + {0x399a, 0x0000, 0xc666, 0x0c6d, 0x3333}, + {0x390e, 0x0000, 0xc6f2, 0x0d81, 0x321c}, + {0x3885, 0x0000, 0xc77b, 0x0e8f, 0x310b}, + {0x37ff, 0x0000, 0xc801, 0x0f97, 0x2fff}, + {0x377c, 0x0000, 0xc884, 0x109b, 0x2ef7}, + {0x36fa, 0x0000, 0xc906, 0x119a, 0x2df5}, + {0x367b, 0x0000, 0xc985, 0x1294, 0x2cf6}, + {0x35fe, 0x0000, 0xca02, 0x138b, 0x2bfb}, + {0x3582, 0x0000, 0xca7e, 0x147e, 0x2b04}, + {0x3508, 0x0000, 0xcaf8, 0x156e, 0x2a0f}, + {0x348f, 0x0000, 0xcb71, 0x165b, 0x291e}, + {0x3417, 0x0000, 0xcbe9, 0x1745, 0x282f}, + {0x33a1, 0x0000, 0xcc5f, 0x182d, 0x2742}, + {0x332c, 0x0000, 0xccd4, 0x1913, 0x2657}, + {0x32b7, 0x0000, 0xcd49, 0x19f6, 0x256f}, + {0x3244, 0x0000, 0xcdbc, 0x1ad7, 0x2488}, + {0x31d1, 0x0000, 0xce2f, 0x1bb7, 0x23a2}, + {0x315f, 0x0000, 0xcea1, 0x1c95, 0x22be}, + {0x30ee, 0x0000, 0xcf12, 0x1d72, 0x21dc}, + {0x307d, 0x0000, 0xcf83, 0x1e4d, 0x20fa}, + {0x300c, 0x0000, 0xcff4, 0x1f28, 0x2019}, + {0x2f9c, 0x0000, 0xd064, 0x2001, 0x1f39}, + {0x2f2d, 0x0000, 0xd0d3, 0x20d9, 0x1e59}, + {0x2ebd, 0x0000, 0xd143, 0x21b1, 0x1d7a}, + {0x2e4e, 0x0000, 0xd1b2, 0x2288, 0x1c9c}, + {0x2ddf, 0x0000, 0xd221, 0x235e, 0x1bbe}, + {0x2d70, 0x0000, 0xd290, 0x2434, 0x1ae0}, + {0x2d01, 0x0000, 0xd2ff, 0x250a, 0x1a02}, + {0x2c92, 0x0000, 0xd36e, 0x25df, 0x1923}, + {0x2c23, 0x0000, 0xd3dd, 0x26b4, 0x1845}, + {0x2bb3, 0x0000, 0xd44d, 0x2789, 0x1767}, + {0x2b44, 0x0000, 0xd4bc, 0x285e, 0x1688}, + {0x2ad4, 0x0000, 0xd52c, 0x2934, 0x15a8}, + {0x2a64, 0x0000, 0xd59c, 0x2a09, 0x14c8}, + {0x29f4, 0x0000, 0xd60c, 0x2adf, 0x13e8}, + {0x2983, 0x0000, 0xd67d, 0x2bb6, 0x1306}, + {0x2912, 0x0000, 0xd6ee, 0x2c8d, 0x1224}, + {0x28a0, 0x0000, 0xd760, 0x2d64, 0x1140}, + {0x282e, 0x0000, 0xd7d2, 0x2e3c, 0x105c}, + {0x27bb, 0x0000, 0xd845, 0x2f15, 0x0f76}, + {0x2747, 0x0000, 0xd8b9, 0x2ff0, 0x0e8e}, + {0x26d3, 0x0000, 0xd92d, 0x30cb, 0x0da5}, + {0x265d, 0x0000, 0xd9a3, 0x31a7, 0x0cbb}, + {0x25e7, 0x0000, 0xda19, 0x3285, 0x0bce}, + {0x2570, 0x0000, 0xda90, 0x3364, 0x0ae0}, + {0x24f7, 0x0000, 0xdb09, 0x3445, 0x09ef}, + {0x247e, 0x0000, 0xdb82, 0x3528, 0x08fc}, + {0x2403, 0x0000, 0xdbfd, 0x360c, 0x0806}, + {0x2387, 0x0000, 0xdc79, 0x36f3, 0x070d}, + {0x2309, 0x0000, 0xdcf7, 0x37dc, 0x0612}, + {0x228a, 0x0000, 0xdd76, 0x38c8, 0x0513}, + {0x2208, 0x0000, 0xddf8, 0x39b6, 0x0411}, + {0x2185, 0x0000, 0xde7b, 0x3aa8, 0x030b}, + {0x2100, 0x0000, 0xdf00, 0x3b9d, 0x0201}, + {0x2079, 0x0000, 0xdf87, 0x3c95, 0x00f2}, + {0x1ff0, 0x0000, 0xe010, 0x3d91, 0xffdf}, + {0x1f63, 0x0000, 0xe09d, 0x3e91, 0xfec7}, + {0x1ed5, 0x0000, 0xe12b, 0x3f96, 0xfda9}, + {0x1e43, 0x0000, 0xe1bd, 0x409f, 0xfc85}, + {0x1dae, 0x0000, 0xe252, 0x41ae, 0xfb5b}, + {0x1d15, 0x0000, 0xe2eb, 0x42c3, 0xfa2a}, + {0x1c79, 0x0000, 0xe387, 0x43de, 0xf8f2}, + {0x1bd9, 0x0000, 0xe427, 0x44ff, 0xf7b1}, + {0x1b34, 0x0000, 0xe4cc, 0x4628, 0xf668}, + {0x1a8b, 0x0000, 0xe575, 0x4759, 0xf515}, + {0x19dc, 0x0000, 0xe624, 0x4893, 0xf3b8}, + {0x1928, 0x0000, 0xe6d8, 0x49d6, 0xf250}, + {0x186e, 0x0000, 0xe792, 0x4b24, 0xf0dc}, + {0x17ad, 0x0000, 0xe853, 0x4c7d, 0xef5a}, + {0x16e5, 0x0000, 0xe91b, 0x4de3, 0xedca}, + {0x1615, 0x0000, 0xe9eb, 0x4f56, 0xec2a}, + {0x153d, 0x0000, 0xeac3, 0x50d7, 0xea79}, + {0x145a, 0x0000, 0xeba6, 0x526a, 0xe8b5}, + {0x136e, 0x0000, 0xec92, 0x540e, 0xe6dc}, + {0x1276, 0x0000, 0xed8a, 0x55c6, 0xe4ec}, + {0x1172, 0x0000, 0xee8e, 0x5794, 0xe2e3}, + {0x105f, 0x0000, 0xefa1, 0x597a, 0xe0be}, + {0x0f3d, 0x0000, 0xf0c3, 0x5b7b, 0xde7a}, + {0x0e0a, 0x0000, 0xf1f6, 0x5d9b, 0xdc14}, + {0x0cc3, 0x0000, 0xf33d, 0x5fdc, 0xd987}, + {0x0b67, 0x0000, 0xf499, 0x6242, 0xd6cf}, + {0x09f3, 0x0000, 0xf60d, 0x64d3, 0xd3e7}, + {0x0864, 0x0000, 0xf79c, 0x6793, 0xd0c8}, + {0x06b6, 0x0000, 0xf94a, 0x6a89, 0xcd6c} + }; + // clang-format on +}}} // namespace nw4r::snd::detail + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +void BiquadFilterLpf::GetCoef(int type ATTR_UNUSED, f32 value, + BiquadCoef *coef) const +{ + int coefSize = COEF_TABLE_SIZE; + int coefIndex = (coefSize - 1) * value; + coefIndex = ut::Clamp(coefIndex, 0, coefSize - 1); + + *coef = coefTable[coefIndex]; +} + +void BiquadFilterHpf::GetCoef(int type ATTR_UNUSED, f32 value, + BiquadCoef *coef) const +{ + int coefSize = COEF_TABLE_SIZE; + int coefIndex = (coefSize - 1) * value; + coefIndex = ut::Clamp(coefIndex, 0, coefSize - 1); + + *coef = coefTable[coefIndex]; +} + +void BiquadFilterBpf512::GetCoef(int type ATTR_UNUSED, f32 value, + BiquadCoef *coef) const +{ + value *= 2.0f - value; + + int coefSize = COEF_TABLE_SIZE; + int coefIndex = (coefSize - 1) * value; + coefIndex = ut::Clamp(coefIndex, 0, coefSize - 1); + + *coef = coefTable[coefIndex]; +} + +void BiquadFilterBpf1024::GetCoef(int type ATTR_UNUSED, f32 value, + BiquadCoef *coef) const +{ + value *= 2.0f - value; + + int coefSize = COEF_TABLE_SIZE; + int coefIndex = (coefSize - 1) * value; + coefIndex = ut::Clamp(coefIndex, 0, coefSize - 1); + + *coef = coefTable[coefIndex]; +} + +void BiquadFilterBpf2048::GetCoef(int type ATTR_UNUSED, f32 value, + BiquadCoef *coef) const +{ + value *= 2.0f - value; + + int coefSize = COEF_TABLE_SIZE; + int coefIndex = (coefSize - 1) * value; + coefIndex = ut::Clamp(coefIndex, 0, coefSize - 1); + + *coef = coefTable[coefIndex]; +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_Channel.cpp b/src/nw4r/snd/snd_Channel.cpp index 884cb22e..07622a08 100644 --- a/src/nw4r/snd/snd_Channel.cpp +++ b/src/nw4r/snd/snd_Channel.cpp @@ -1 +1,472 @@ -#include "nw4r/snd/snd_Channel.h" +#include "nw4r/snd/Channel.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_Channel.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <macros.h> +#include <types.h> + +#include "nw4r/snd/EnvGenerator.h" +#include "nw4r/snd/global.h" +#include "nw4r/snd/InstancePool.h" +#include "nw4r/snd/Lfo.h" +#include "nw4r/snd/MoveValue.h" +#include "nw4r/snd/Util.h" +#include "nw4r/snd/Voice.h" +#include "nw4r/snd/VoiceManager.h" +#include "nw4r/snd/WaveFile.h" + +#include "nw4r/ut/Lock.h" + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +ChannelManager &ChannelManager::GetInstance() +{ + static ChannelManager instance; + + return instance; +} + +ChannelManager::ChannelManager() : + mInitialized (false), + mChannelCount (0) +{ +} + +u32 ChannelManager::GetRequiredMemSize(int channelCount) +{ + return sizeof(Channel) * (1 + channelCount); +} + +void ChannelManager::Setup(void *mem, u32 memSize) +{ + ut::AutoInterruptLock lock; + + if (mInitialized) + return; + + mChannelCount = mPool.Create(mem, memSize); + mMem = mem; + mMemSize = memSize; + mInitialized = true; +} + +void ChannelManager::Shutdown() +{ + ut::AutoInterruptLock lock; + + if (!mInitialized) + return; + + NW4R_RANGE_FOR_NO_AUTO_INC(itr, mChannelList) + { + decltype(itr) curItr = itr++; + + curItr->Stop(); + } + + NW4RAssert_Line(78, mChannelList.IsEmpty()); + + mPool.Destroy(mMem, mMemSize); + mInitialized = false; +} + +Channel *ChannelManager::Alloc() +{ + Channel *channel = mPool.Alloc(); + + mChannelList.PushBack(channel); + return channel; +} + +void ChannelManager::Free(Channel *channel) +{ + mChannelList.Erase(channel); + mPool.Free(channel); +} + +void ChannelManager::UpdateAllChannel() +{ + NW4R_RANGE_FOR_NO_AUTO_INC(itr, mChannelList) + { + decltype(itr) curItr = itr++; + + curItr->Update(true); + } +} + +Channel::Channel() : + mPauseFlag (false), + mActiveFlag (false), + mAllocFlag (false), + mVoice (nullptr) +{ +} + +Channel::~Channel() {} + +void Channel::InitParam(Callback *callback, register_t callbackData) +{ + mNextLink = nullptr; + mCallback = callback; + mCallbackData = callbackData; + mWaveDataLocationCallback = nullptr; + mWaveInfo = nullptr; + mPauseFlag = false; + mAutoSweep = true; + mReleasePriorityFixFlag = false; + mReleaseIgnoreFlag = false; + mLength = 0; + mKey = KEY_INIT; + mOriginalKey = ORIGINAL_KEY_INIT; + mInitVolume = 1.0f; + mInitPan = 0.0f; + mInitSurroundPan = 0.0f; + mTune = 1.0f; + mUserVolume = 1.0f; + mUserPitch = 0.0f; + mUserPitchRatio = 1.0f; + mUserPan = 0.0f; + mUserSurroundPan = 0.0f; + mUserLpfFreq = 0.0f; + mBiquadType = 0; + mBiquadValue = 0.0f; + mRemoteFilter = 0; + mOutputLineFlag = 1; + mMainOutVolume = 1.0f; + mMainSend = 0.0f; + + for (int i = 0; i < AUX_BUS_NUM; i++) + mFxSend[i] = 0.0f; + + mSilenceVolume.InitValue(SILENCE_VOLUME_MAX); + + mSweepPitch = 0.0f; + mSweepLength = 0; + mSweepCounter = 0; + + mEnvelope.Init(EnvGenerator::VOLUME_INIT); + mLfo.GetParam().Init(); + + mLfoTarget = LFO_TARGET_PITCH; + mPanMode = PAN_MODE_DUAL; + mPanCurve = PAN_CURVE_SQRT; + mAlternateAssign = 0; +} + +void Channel::Update(bool doPeriodicProc) +{ + if (!mActiveFlag) + return; + + if (mPauseFlag) + doPeriodicProc = false; + + f32 lfoValue = mLfo.GetValue(); + + mSilenceVolume.Update(); + + f32 volume = 1.0f; + volume *= mInitVolume; + volume *= mUserVolume; + volume *= + mSilenceVolume.GetValue() / static_cast<float>(SILENCE_VOLUME_MAX); + + f32 veInitVolume = 1.0f; + veInitVolume *= Util::CalcVolumeRatio(mEnvelope.GetValue()); + + if (mLfoTarget == LFO_TARGET_VOLUME) + veInitVolume *= Util::CalcVolumeRatio(lfoValue * 6.0f); + + if (mEnvelope.GetStatus() == EnvGenerator::STATUS_RELEASE) + { + if (mCallback) + { + if (veInitVolume == 0.0f) + { + Stop(); + return; + } + } + else + { + if (volume * veInitVolume == 0.0f) + { + Stop(); + return; + } + } + } + + f32 cent = 0.0f; + cent += mKey - mOriginalKey; + cent += GetSweepValue(); + cent += mUserPitch; + if (mLfoTarget == LFO_TARGET_PITCH) + cent += lfoValue; + + f32 pitchRatio = 1.0f; + pitchRatio *= mTune; + pitchRatio *= mUserPitchRatio; + + f32 pitch = Util::CalcPitchRatio(cent * 256.0f); + pitch *= pitchRatio; + + f32 pan = 0.0f; + pan += mInitPan; + pan += mUserPan; + if (mLfoTarget == LFO_TARGET_PAN) + pan += lfoValue; + + f32 surroundPan = 0.0f; + surroundPan += mInitSurroundPan; + surroundPan += mUserSurroundPan; + + f32 lpfFreq = 1.0f; + lpfFreq += mUserLpfFreq; + + int remoteFilter = 0; + remoteFilter += mRemoteFilter; + + f32 mainOutVolume = 1.0f; + mainOutVolume *= mMainOutVolume; + + f32 mainSend = 0.0f; + mainSend += mMainSend; + + f32 fxSend[AUX_BUS_NUM]; + for (int i = 0; i < AUX_BUS_NUM; i++) + { + fxSend[i] = 0.0f; + fxSend[i] += mFxSend[i]; + } + + if (doPeriodicProc) + { + if (mAutoSweep) + UpdateSweep(3); + + mLfo.Update(3); + mEnvelope.Update(3); + } + + f32 nextLfoValue = mLfo.GetValue(); + + f32 veTargetVolume = 1.0f; + veTargetVolume *= Util::CalcVolumeRatio(mEnvelope.GetValue()); + + if (mLfoTarget == LFO_TARGET_VOLUME) + veTargetVolume *= Util::CalcVolumeRatio(nextLfoValue * 6.0f); + + if (mVoice) + { + mVoice->SetPanMode(mPanMode); + mVoice->SetPanCurve(mPanCurve); + mVoice->SetVolume(volume); + mVoice->SetVeVolume(veTargetVolume, veInitVolume); + mVoice->SetPitch(pitch); + mVoice->SetPan(pan); + mVoice->SetSurroundPan(surroundPan); + mVoice->SetLpfFreq(lpfFreq); + mVoice->SetBiquadFilter(mBiquadType, mBiquadValue); + mVoice->SetRemoteFilter(remoteFilter); + mVoice->SetOutputLine(mOutputLineFlag); + mVoice->SetMainOutVolume(mainOutVolume); + mVoice->SetMainSend(mainSend); + + for (int i = 0; i < AUX_BUS_NUM; i++) + mVoice->SetFxSend(static_cast<AuxBus>(i), fxSend[i]); + } +} + +void Channel::Start(WaveInfo const &waveParam, int length, u32 startOffset) +{ + mLength = length; + + mLfo.Reset(); + mEnvelope.Reset(EnvGenerator::VOLUME_INIT); + mSweepCounter = 0; + + mVoice->Setup(waveParam, startOffset); + mVoice->Start(); + mActiveFlag = true; +} + +void Channel::Release() +{ + if (mEnvelope.GetStatus() != EnvGenerator::STATUS_RELEASE) + { + if (mVoice && !mReleasePriorityFixFlag) + mVoice->SetPriority(PRIORITY_RELEASE); + + mEnvelope.SetStatus(EnvGenerator::STATUS_RELEASE); + } + + mPauseFlag = false; +} + +void Channel::NoteOff() +{ + if (!mReleaseIgnoreFlag) + Release(); +} + +void Channel::Stop() +{ + if (!mVoice) + return; + + mVoice->Stop(); + mVoice->Free(); + + mVoice = nullptr; + + mPauseFlag = false; + mActiveFlag = false; + + if (mCallback) + (*mCallback)(this, CALLBACK_STATUS_STOPPED, mCallbackData); + + if (mWaveDataLocationCallback) + mWaveDataLocationCallback->at_0x0c(mWaveInfo); + + if (mAllocFlag) + { + mAllocFlag = false; + ChannelManager::GetInstance().Free(this); + } +} + +void Channel::UpdateSweep(int count) +{ + mSweepCounter += count; + + if (mSweepCounter > mSweepLength) + mSweepCounter = mSweepLength; +} + +void Channel::SetSweepParam(f32 sweepPitch, int sweepTime, bool autoUpdate) +{ + mSweepPitch = sweepPitch; + mSweepLength = sweepTime; + mAutoSweep = autoUpdate; + mSweepCounter = 0; +} + +f32 Channel::GetSweepValue() const +{ + if (mSweepPitch == 0.0f) + return 0.0f; + + if (mSweepCounter >= mSweepLength) + return 0.0f; + + f32 sweep = mSweepPitch * (mSweepLength - mSweepCounter); + + NW4RAssert_Line(520, mSweepLength != 0); + + sweep /= mSweepLength; + + return sweep; +} + +void Channel::SetBiquadFilter(int type, f32 value) +{ + mBiquadType = type; + mBiquadValue = value; +} + +void Channel::VoiceCallbackFunc(Voice *voice, Voice::VoiceCallbackStatus status, + void *arg) +{ + NW4RAssertPointerNonnull_Line(547, arg); + + ChannelCallbackStatus chStatus; + switch (status) + { + case Voice::CALLBACK_STATUS_FINISH_WAVE: + chStatus = CALLBACK_STATUS_FINISH; + voice->Free(); + break; + + case Voice::CALLBACK_STATUS_CANCEL: + chStatus = CALLBACK_STATUS_CANCEL; + voice->Free(); + break; + + case Voice::CALLBACK_STATUS_DROP_VOICE: + chStatus = CALLBACK_STATUS_DROP; + break; + + case Voice::CALLBACK_STATUS_DROP_DSP: + chStatus = CALLBACK_STATUS_DROP; + break; + } + + Channel *channel = static_cast<Channel *>(arg); + + if (channel->mCallback) + (*channel->mCallback)(channel, chStatus, channel->mCallbackData); + + if (channel->mWaveDataLocationCallback) + channel->mWaveDataLocationCallback->at_0x0c(channel->mWaveInfo); + + channel->mVoice = nullptr; + channel->mPauseFlag = false; + channel->mActiveFlag = false; + channel->mAllocFlag = false; + + ChannelManager::GetInstance().Free(channel); +} + +Channel *Channel::AllocChannel(int voiceChannelCount, int voiceOutCount, + int priority, Callback *callback, + register_t callbackData) +{ + NW4RAssertHeaderClampedLRValue_Line(606, priority, 0, 255); + + Channel *channel = ChannelManager::GetInstance().Alloc(); + if (!channel) + { + NW4RCheckMessage_Line(611, channel, "Channel Allocation failed!"); + return nullptr; + } + + channel->mAllocFlag = true; + + Voice *voice = VoiceManager::GetInstance().AllocVoice( + voiceChannelCount, voiceOutCount, priority, VoiceCallbackFunc, channel); + if (!voice) + { + ChannelManager::GetInstance().Free(channel); + return nullptr; + } + + channel->mVoice = voice; + channel->InitParam(callback, callbackData); + + return channel; +} + +void Channel::FreeChannel(Channel *channel) +{ + if (channel) + { + channel->mCallback = nullptr; + channel->mCallbackData = 0; + } +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_DisposeCallbackManager.cpp b/src/nw4r/snd/snd_DisposeCallbackManager.cpp index 377f3dfb..be384d69 100644 --- a/src/nw4r/snd/snd_DisposeCallbackManager.cpp +++ b/src/nw4r/snd/snd_DisposeCallbackManager.cpp @@ -1 +1,81 @@ -#include "nw4r/snd/snd_DisposeCallbackManager.h" +#include "nw4r/snd/DisposeCallbackManager.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_DisposeCallbackManager.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <macros.h> +#include <types.h> + +#include "nw4r/snd/SoundThread.h" + +#include "nw4r/ut/Lock.h" // ut::AutoInterruptLock + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +DisposeCallbackManager &DisposeCallbackManager::GetInstance() +{ + ut::AutoInterruptLock lock; // What + + static DisposeCallbackManager instance; + + return instance; +} + +DisposeCallbackManager::DisposeCallbackManager() {} + +void DisposeCallbackManager::RegisterDisposeCallback(DisposeCallback *callback) +{ + mCallbackList.PushBack(callback); +} + +void DisposeCallbackManager::UnregisterDisposeCallback( + DisposeCallback *callback) +{ + mCallbackList.Erase(callback); +} + +void DisposeCallbackManager::Dispose(void *mem, u32 size, void *arg ATTR_UNUSED) +{ + void *start = mem; + void *end = static_cast<byte_t *>(mem) + size; + + SoundThread::AutoLock lock; + + // NOTE: unnecessary call to GetInstance from instance-method + NW4R_RANGE_FOR_NO_AUTO_INC(itr, GetInstance().mCallbackList) + { + decltype(itr) curItr = itr++; + + // the post-increment is in ketteiban + curItr++->InvalidateData(start, end); + } +} + +void DisposeCallbackManager::DisposeWave(void *mem, u32 size, + void *arg ATTR_UNUSED) +{ + void *start = mem; + void *end = static_cast<byte_t *>(mem) + size; + + SoundThread::AutoLock lock; + + // same stuff here as the stuff over there + NW4R_RANGE_FOR_NO_AUTO_INC(itr, GetInstance().mCallbackList) + { + decltype(itr) curItr = itr++; + + curItr++->InvalidateWaveData(start, end); + } +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_DvdSoundArchive.cpp b/src/nw4r/snd/snd_DvdSoundArchive.cpp index 84a871c6..7b9adf2a 100644 --- a/src/nw4r/snd/snd_DvdSoundArchive.cpp +++ b/src/nw4r/snd/snd_DvdSoundArchive.cpp @@ -1 +1,6 @@ -#include "nw4r/snd/snd_DvdSoundArchive.h" +/* Only implemented to the extent necessary to match data sections. */ + +#include "nw4r/snd/DvdSoundArchive.h" + +nw4r::snd::DvdSoundArchive::DvdSoundArchive() {} +nw4r::snd::DvdSoundArchive::DvdFileStream::DvdFileStream() {} diff --git a/src/nw4r/snd/snd_EnvGenerator.cpp b/src/nw4r/snd/snd_EnvGenerator.cpp index fa494de5..fe40431b 100644 --- a/src/nw4r/snd/snd_EnvGenerator.cpp +++ b/src/nw4r/snd/snd_EnvGenerator.cpp @@ -1 +1,250 @@ -#include "nw4r/snd/snd_EnvGenerator.h" +#include "nw4r/snd/EnvGenerator.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_EnvGenerator.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <types.h> + +#include <nw4r/NW4RAssert.hpp> + +/******************************************************************************* + * variables + */ + +namespace nw4r { namespace snd { namespace detail +{ + // .rodata + + // clang-format off + s16 const EnvGenerator::DecibelSquareTable[DECIBEL_SQUARE_TABLE_SIZE] = + { + -723, -722, -721, -651, -601, -562, -530, -503, + -480, -460, -442, -425, -410, -396, -383, -371, + -360, -349, -339, -330, -321, -313, -305, -297, + -289, -282, -276, -269, -263, -257, -251, -245, + -239, -234, -229, -224, -219, -214, -210, -205, + -201, -196, -192, -188, -184, -180, -176, -173, + -169, -165, -162, -158, -155, -152, -149, -145, + -142, -139, -136, -133, -130, -127, -125, -122, + -119, -116, -114, -111, -109, -106, -103, -101, + -99, -96, -94, -91, -89, -87, -85, -82, + -80, -78, -76, -74, -72, -70, -68, -66, + -64, -62, -60, -58, -56, -54, -52, -50, + -49, -47, -45, -43, -42, -40, -38, -36, + -35, -33, -31, -30, -28, -27, -25, -23, + -22, -20, -19, -17, -16, -14, -13, -11, + -10, -8, -7, -6, -4, -3, -1, 0 + }; + // clang-format on + + // .sdata2 + f32 const volatile EnvGenerator::VOLUME_INIT = -90.4f; +}}} // namespace nw4r::snd::detail + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +EnvGenerator::EnvGenerator() +{ + Init(VOLUME_INIT); +} + +void EnvGenerator::Init(f32 initDecibel) +{ + SetAttack(ATTACK_INIT); + SetHold(HOLD_INIT); + SetDecay(DECAY_INIT); + SetSustain(SUSTAIN_INIT); + SetRelease(RELEASE_INIT); + Reset(initDecibel); +} + +void EnvGenerator::Reset(f32 initDecibel) +{ + mValue = initDecibel * 10.0f; + mStatus = STATUS_ATTACK; +} + +f32 EnvGenerator::GetValue() const +{ + if (mStatus == STATUS_ATTACK && mAttack == 0.0f) + return 0.0f; + + return mValue / 10.0f; +} + +void EnvGenerator::Update(int msec) +{ + switch (mStatus) + { + case STATUS_ATTACK: + while (msec > 0) + { + mValue *= mAttack; + msec--; + + if (mValue > -1.0f / 32.0f) + { + mValue = 0.0f; + mStatus = STATUS_HOLD; + mHoldCounter = mHold; + + break; + } + } + + break; + + case STATUS_HOLD: + if (msec < mHoldCounter) + { + mHoldCounter -= msec; + } + else + { + msec -= mHoldCounter; + mHoldCounter = 0; + mStatus = STATUS_DECAY; + } + + if (mStatus != STATUS_DECAY) + break; + + /* fallthrough */; + + case STATUS_DECAY: + { + f32 sustainDecay = CalcDecibelSquare(mSustain); + mValue -= mDecay * msec; + + if (mValue < sustainDecay) + { + mValue = sustainDecay; + mStatus = STATUS_SUSTAIN; + } + + break; + } + + case STATUS_SUSTAIN: + break; + + case STATUS_RELEASE: + mValue -= mRelease * msec; + break; + } +} + +void EnvGenerator::SetAttack(int attack) +{ + // clang-format off + static f32 const attackTable[128] = + { + 0.9992175f, 0.9984326f, 0.9976452f, 0.9968553f, + 0.9960629f, 0.9952679f, 0.9944704f, 0.9936704f, + 0.9928677f, 0.9920625f, 0.9912546f, 0.9904441f, + 0.9896309f, 0.9888151f, 0.9879965f, 0.9871752f, + 0.9863512f, 0.9855244f, 0.9846949f, 0.9838625f, + 0.9830273f, 0.9821893f, 0.9813483f, 0.9805045f, + 0.9796578f, 0.9788081f, 0.9779555f, 0.9770999f, + 0.9762413f, 0.9753797f, 0.9745150f, 0.9736472f, + 0.9727763f, 0.9719023f, 0.9710251f, 0.9701448f, + 0.9692612f, 0.9683744f, 0.9674844f, 0.9665910f, + 0.9656944f, 0.9647944f, 0.9638910f, 0.9629842f, + 0.9620740f, 0.9611604f, 0.9602433f, 0.9593226f, + 0.9583984f, 0.9574706f, 0.9565392f, 0.9556042f, + 0.9546655f, 0.9537231f, 0.9527769f, 0.9518270f, + 0.9508732f, 0.9499157f, 0.9489542f, 0.9479888f, + 0.9470195f, 0.9460462f, 0.9450689f, 0.9440875f, + 0.9431020f, 0.9421124f, 0.9411186f, 0.9401206f, + 0.9391184f, 0.9381118f, 0.9371009f, 0.9360856f, + 0.9350659f, 0.9340417f, 0.9330131f, 0.9319798f, + 0.9309420f, 0.9298995f, 0.9288523f, 0.9278004f, + 0.9267436f, 0.9256821f, 0.9246156f, 0.9235442f, + 0.9224678f, 0.9213864f, 0.9202998f, 0.9192081f, + 0.9181112f, 0.9170091f, 0.9159016f, 0.9147887f, + 0.9136703f, 0.9125465f, 0.9114171f, 0.9102821f, + 0.9091414f, 0.9079949f, 0.9068427f, 0.9056845f, + 0.9045204f, 0.9033502f, 0.9021740f, 0.9009916f, + 0.8998029f, 0.8986080f, 0.8974066f, 0.8961988f, + 0.8949844f, 0.8900599f, 0.8824622f, 0.8759247f, + 0.8691861f, 0.8636406f, 0.8535788f, 0.8430189f, + 0.8286135f, 0.8149099f, 0.8002172f, 0.7780663f, + 0.7554750f, 0.7242125f, 0.6828239f, 0.6329169f, + 0.5592135f, 0.4551411f, 0.3298770f, 0.0000000f + }; + // clang-format on + + // specifically not the source variant + NW4RAssertHeaderClampedLRValue_Line(263, attack, 0, 127); + + mAttack = attackTable[attack]; +} + +void EnvGenerator::SetHold(int hold) +{ + // specifically not the source variant + NW4RAssertHeaderClampedLRValue_Line(278, hold, 0, 127); + + mHold = ((hold + 1) * (hold + 1)) / 4; +} + +void EnvGenerator::SetDecay(int decay) +{ + // specifically not the source variant + NW4RAssertHeaderClampedLRValue_Line(294, decay, 0, 127); + + mDecay = CalcRelease(decay); +} + +void EnvGenerator::SetSustain(int sustain) +{ + // specifically not the source variant + NW4RAssertHeaderClampedLRValue_Line(310, sustain, 0, 127); + + mSustain = sustain; +} + +void EnvGenerator::SetRelease(int release) +{ + // specifically not the source variant + NW4RAssertHeaderClampedLRValue_Line(326, release, 0, 127); + + mRelease = CalcRelease(release); +} + +f32 EnvGenerator::CalcRelease(int release) +{ + // specifically not the source variant + NW4RAssertHeaderClampedLRValue_Line(337, release, 0, 127); + + if (release == 127) + return 65535.0f; + + if (release == 127 - 1) + return 24.0f; + + if (release < 50) + return (release * 2 + 1) / 128.0f / 5.0f; + else + return 60.0f / (127 - 1 - release) / 5.0f; +} + +s16 EnvGenerator::CalcDecibelSquare(int scale) +{ + // specifically not the source variant + NW4RAssertHeaderClampedLRValue_Line(352, scale, 0, 127); + + return DecibelSquareTable[scale]; +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_ExternalSoundPlayer.cpp b/src/nw4r/snd/snd_ExternalSoundPlayer.cpp index e8e9240a..afb6f117 100644 --- a/src/nw4r/snd/snd_ExternalSoundPlayer.cpp +++ b/src/nw4r/snd/snd_ExternalSoundPlayer.cpp @@ -1 +1,111 @@ -#include "nw4r/snd/snd_ExternalSoundPlayer.h" +#include "nw4r/snd/ExternalSoundPlayer.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_ExternalSoundPlayer.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <decomp.h> +#include <macros.h> // NW4R_RANGE_FOR +#include <types.h> // nullptr + +#include "nw4r/snd/BasicSound.h" +#include "nw4r/snd/SoundThread.h" + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * types + */ + +// forward declarations +namespace nw4r { namespace ut { struct LinkListNode; }} + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +// not sure which one uses this exactly, maybe StopAllSound? +DECOMP_FORCE_CLASS_METHOD( + BasicSound::ExtSoundPlayerPlayLinkList, + GetPointerFromNode(static_cast<ut::LinkListNode *>(nullptr))); + +bool ExternalSoundPlayer::AppendSound(BasicSound *sound) +{ + NW4RAssertPointerNonnull_Line(129, sound); + + SoundThread::AutoLock lock; + + int allocPriority = sound->CalcCurrentPlayerPriority(); + + if (GetPlayableSoundCount() == 0) + return false; + + while (GetPlayingSoundCount() >= GetPlayableSoundCount()) + { + BasicSound *dropSound = GetLowestPrioritySound(); + if (!dropSound) + return false; + + if (allocPriority < dropSound->CalcCurrentPlayerPriority()) + return false; + + dropSound->Shutdown(); + } + + mSoundList.PushBack(sound); + sound->AttachExternalSoundPlayer(this); + + return true; +} + +void ExternalSoundPlayer::RemoveSound(BasicSound *sound) +{ + mSoundList.Erase(sound); + sound->DetachExternalSoundPlayer(this); +} + +bool ExternalSoundPlayer::detail_CanPlaySound(int startPriority) +{ + if (GetPlayableSoundCount() == 0) + return false; + + if (GetPlayingSoundCount() >= GetPlayableSoundCount()) + { + BasicSound *dropSound = GetLowestPrioritySound(); + if (!dropSound) + return false; + + if (startPriority < dropSound->CalcCurrentPlayerPriority()) + return false; + } + + return true; +} + +BasicSound *ExternalSoundPlayer::GetLowestPrioritySound() +{ + int priority = 128; + BasicSound *sound = nullptr; + + NW4R_RANGE_FOR(itr, mSoundList) + { + int itrPriority = itr->CalcCurrentPlayerPriority(); + + if (priority > itrPriority) + { + sound = &(*itr); + priority = itrPriority; + } + } + + return sound; +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_FxBase.cpp b/src/nw4r/snd/snd_FxBase.cpp index 40cc0276..7d9cede0 100644 --- a/src/nw4r/snd/snd_FxBase.cpp +++ b/src/nw4r/snd/snd_FxBase.cpp @@ -1 +1 @@ -#include "nw4r/snd/snd_FxBase.h" +/* Only implemented to the extent necessary to match other files. */ diff --git a/src/nw4r/snd/snd_InstancePool.cpp b/src/nw4r/snd/snd_InstancePool.cpp index d89aed59..e81c5f4c 100644 --- a/src/nw4r/snd/snd_InstancePool.cpp +++ b/src/nw4r/snd/snd_InstancePool.cpp @@ -1 +1,102 @@ -#include "nw4r/snd/snd_InstancePool.h" +#include "nw4r/snd/InstancePool.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_InstancePool.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <types.h> + +#include "nw4r/ut/Lock.h" // ut::AutoInterruptLock +#include "nw4r/ut/inlines.h" // ut::RoundUp + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +u32 PoolImpl::CreateImpl(void *buffer, u32 size, u32 objSize) +{ + NW4RAssertPointerNonnull_Line(38, buffer); + + ut::AutoInterruptLock lock; + + // alignas(4) + char *ptr = static_cast<char *>(ut::RoundUp(buffer, 4)); + objSize = ut::RoundUp(objSize, 4); + + u32 numObjects = (size - (ptr - static_cast<char *>(buffer))) / objSize; + + for (u32 i = 0; i < numObjects; i++, ptr += objSize) + { + PoolImpl *head = reinterpret_cast<PoolImpl *>(ptr); + + head->mNext = mNext; + mNext = head; + } + + return numObjects; +} + +void PoolImpl::DestroyImpl(void *buffer, u32 size) +{ + NW4RAssertPointerNonnull_Line(68, buffer); + + ut::AutoInterruptLock lock; + + void *begin = buffer; + void *end = static_cast<char *>(begin) + size; + + for (PoolImpl *ptr = mNext, *prev = this; ptr; ptr = ptr->mNext) + { + if (begin <= ptr && ptr < end) + prev->mNext = ptr->mNext; + else + prev = ptr; + } +} + +int PoolImpl::CountImpl() const +{ + ut::AutoInterruptLock lock; + + int count = 0; + + for (PoolImpl *ptr = mNext; ptr; ptr = ptr->mNext) + count++; + + return count; +} + +void *PoolImpl::AllocImpl() +{ + ut::AutoInterruptLock lock; + + if (!mNext) + return nullptr; + + PoolImpl *head = mNext; + + mNext = head->mNext; + + return head; +} + +void PoolImpl::FreeImpl(void *ptr) +{ + ut::AutoInterruptLock lock; + + PoolImpl *head = static_cast<PoolImpl *>(ptr); + + head->mNext = mNext; + mNext = head; +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_Lfo.cpp b/src/nw4r/snd/snd_Lfo.cpp index a4058492..a02b717d 100644 --- a/src/nw4r/snd/snd_Lfo.cpp +++ b/src/nw4r/snd/snd_Lfo.cpp @@ -1 +1,93 @@ -#include "nw4r/snd/snd_Lfo.h" +#include "nw4r/snd/Lfo.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_Lfo.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <types.h> + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +void LfoParam::Init() +{ + depth = 0.0f; + range = 1; + speed = 6.25f; + delay = 0; +} + +void Lfo::Reset() +{ + mCounter = 0.0f; + mDelayCounter = 0; +} + +void Lfo::Update(int msec) +{ + if (mDelayCounter < mParam.delay) + { + if (mDelayCounter + msec <= mParam.delay) + { + mDelayCounter += msec; + return; + } + + msec -= mParam.delay - mDelayCounter; + mDelayCounter = mParam.delay; + } + + mCounter += mParam.speed * msec / 1000; + mCounter -= static_cast<int>(mCounter); +} + +f32 Lfo::GetValue() const +{ + if (mParam.depth == 0.0f) + return 0.0f; + + if (mDelayCounter < mParam.delay) + return 0.0f; + + f32 value = GetSinIdx(4 * (TABLE_SIZE * mCounter)) + / static_cast<f32>(TABLE_SIZE * 4 - 1); + + value *= mParam.depth; + value *= mParam.range; + + return value; +} + +s8 Lfo::GetSinIdx(int index) +{ + static s8 const sinTable[TABLE_SIZE + 1] = + { + 0, 6, 12, 19, 25, 31, 37, 43, 49, 54, 60, + 65, 71, 76, 81, 85, 90, 94, 98, 102, 106, 109, + 112, 115, 117, 120, 122, 123, 125, 126, 126, 127, 127 + }; + + // specifically not the source variant + NW4RAssertHeaderClampedLValue_Line(123, index, 0, 128); + + if (index < TABLE_SIZE) + return sinTable[index]; + else if (index < TABLE_SIZE * 2) + return sinTable[TABLE_SIZE - (index - TABLE_SIZE)]; + else if (index < TABLE_SIZE * 3) + return -sinTable[index - TABLE_SIZE * 2]; + else + return -sinTable[TABLE_SIZE - (index - TABLE_SIZE * 3)]; +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_McsSoundArchive.cpp b/src/nw4r/snd/snd_McsSoundArchive.cpp new file mode 100644 index 00000000..b2e0d36e --- /dev/null +++ b/src/nw4r/snd/snd_McsSoundArchive.cpp @@ -0,0 +1,8 @@ +/* Only implemented to the extent necessary to match early instantiations of + * inline functions and data sections. + */ + +#include "nw4r/snd/McsSoundArchive.h" + +nw4r::snd::McsSoundArchive::McsSoundArchive() {} +nw4r::snd::McsSoundArchive::McsFileStream::McsFileStream() {} diff --git a/src/nw4r/snd/snd_MemorySoundArchive.cpp b/src/nw4r/snd/snd_MemorySoundArchive.cpp index f031ff79..a1cf5406 100644 --- a/src/nw4r/snd/snd_MemorySoundArchive.cpp +++ b/src/nw4r/snd/snd_MemorySoundArchive.cpp @@ -1 +1,183 @@ -#include "nw4r/snd/snd_MemorySoundArchive.h" +#include "nw4r/snd/MemorySoundArchive.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_MemorySoundArchive.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <cstring> // std::memcpy +#include <new> + +#include <types.h> + +#include "nw4r/snd/SoundArchive.h" +#include "nw4r/snd/SoundArchiveFile.h" // SoundArchiveFileReader + +#include "nw4r/ut/FileStream.h" +#include "nw4r/ut/inlines.h" +#include "nw4r/ut/RuntimeTypeInfo.h" // IWYU pragma: keep (need the complete type) + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { + +MemorySoundArchive::MemorySoundArchive() : + mData (nullptr) +{ +} + +MemorySoundArchive::~MemorySoundArchive() {} + +bool MemorySoundArchive::Setup(void const *soundArchiveData) +{ + NW4RAssertPointerNonnull_Line(65, soundArchiveData); + NW4RAssertAligned_Line(66, soundArchiveData, 4); + + mFileReader.Init(soundArchiveData); + SoundArchive::Setup(&mFileReader); + + void const *infoChunk = + ut::AddOffsetToPtr(soundArchiveData, mFileReader.GetInfoChunkOffset()); + + mFileReader.SetInfoChunk(infoChunk, mFileReader.GetInfoChunkSize()); + + void const *stringChunk = ut::AddOffsetToPtr( + soundArchiveData, mFileReader.GetLabelStringChunkOffset()); + + mFileReader.SetStringChunk(stringChunk, + mFileReader.GetLabelStringChunkSize()); + + mData = soundArchiveData; + + return true; +} + +void MemorySoundArchive::Shutdown() +{ + mData = nullptr; + + SoundArchive::Shutdown(); +} + +void const *MemorySoundArchive::detail_GetFileAddress(u32 fileId) const +{ + SoundArchive::FilePos filePos; + if (!detail_ReadFilePos(fileId, 0, &filePos)) + return nullptr; + + SoundArchive::GroupInfo groupInfo; + if (!detail_ReadGroupInfo(filePos.groupId, &groupInfo)) + return nullptr; + + SoundArchive::GroupItemInfo itemInfo; + if (!detail_ReadGroupItemInfo(filePos.groupId, filePos.index, &itemInfo)) + return nullptr; + + if (groupInfo.extFilePath) + return nullptr; + + return ut::AddOffsetToPtr(mData, groupInfo.offset + itemInfo.offset); +} + +void const *MemorySoundArchive::detail_GetWaveDataFileAddress(u32 fileId) const +{ + SoundArchive::FilePos filePos; + if (!detail_ReadFilePos(fileId, 0, &filePos)) + return nullptr; + + SoundArchive::GroupInfo groupInfo; + if (!detail_ReadGroupInfo(filePos.groupId, &groupInfo)) + return nullptr; + + SoundArchive::GroupItemInfo itemInfo; + if (!detail_ReadGroupItemInfo(filePos.groupId, filePos.index, &itemInfo)) + return nullptr; + + if (groupInfo.extFilePath) + return nullptr; + + return ut::AddOffsetToPtr(mData, groupInfo.waveDataOffset + + itemInfo.waveDataOffset); +} + +ut::FileStream *MemorySoundArchive::OpenStream(void *buffer, int size, + u32 begin, u32 length) const +{ + if (!mData) + return nullptr; + + if (size < sizeof(MemoryFileStream)) + return nullptr; + + return new (buffer) + MemoryFileStream(ut::AddOffsetToPtr(mData, begin), length); +} + +ut::FileStream *MemorySoundArchive::OpenExtStream(void *, int, char const *, + u32, u32) const +{ + NW4RWarningMessage_Line(187, + "Cannot OpenExtStream for MemorySoundArchive\n"); + + return nullptr; +} + +int MemorySoundArchive::detail_GetRequiredStreamBufferSize() const +{ + return sizeof(MemoryFileStream); +} + +MemorySoundArchive::MemoryFileStream::MemoryFileStream(void const *buffer, + u32 size) : + mBuffer (buffer), + mSize (size), + mPosition (0) +{ +} + +void MemorySoundArchive::MemoryFileStream::Close() +{ + mBuffer = nullptr; + mSize = 0; + mPosition = 0; +} + +s32 MemorySoundArchive::MemoryFileStream::Read(void *buf, u32 length) +{ + s32 readBytes = ut::Min(length, mSize - mPosition); + std::memcpy(buf, ut::AddOffsetToPtr(mBuffer, mPosition), readBytes); + + return readBytes; +} + +void MemorySoundArchive::MemoryFileStream::Seek(s32 offset, u32 origin) +{ + switch (origin) + { + case FileStream::SEEK_ORIGIN_SET: + mPosition = offset; + break; + + case FileStream::SEEK_ORIGIN_CUR: + mPosition += offset; + break; + + case FileStream::SEEK_ORIGIN_END: + mPosition = mSize - offset; + break; + + default: + NW4RPanicMessage_Line(234, "Unsupported Seek origin"); + break; + } +} + +}} // namespace nw4r::snd diff --git a/src/nw4r/snd/snd_MidiSeqPlayer.cpp b/src/nw4r/snd/snd_MidiSeqPlayer.cpp index 61ffb281..29f85f4f 100644 --- a/src/nw4r/snd/snd_MidiSeqPlayer.cpp +++ b/src/nw4r/snd/snd_MidiSeqPlayer.cpp @@ -1 +1,6 @@ -// #include "nw4r/snd/snd_MidiSeqPlayer.h" +/* Only implemented to the extent necessary to match early instantiations of + * inline functions and data sections. */ + +#include "nw4r/snd/MidiSeqPlayer.h" + +nw4r::snd::detail::MidiSeqPlayer::MidiSeqPlayer() {} diff --git a/src/nw4r/snd/snd_MidiSeqTrack.cpp b/src/nw4r/snd/snd_MidiSeqTrack.cpp new file mode 100644 index 00000000..fa4c1c11 --- /dev/null +++ b/src/nw4r/snd/snd_MidiSeqTrack.cpp @@ -0,0 +1,6 @@ +/* Only implemented to the extent necessary to match early instantiations of + * inline functions and data sections. */ + +#include "nw4r/snd/MidiSeqTrack.h" + +nw4r::snd::detail::MidiSeqTrack::MidiSeqTrack() {} diff --git a/src/nw4r/snd/snd_MidiSeqTrackAllocator.cpp b/src/nw4r/snd/snd_MidiSeqTrackAllocator.cpp new file mode 100644 index 00000000..c21af6d6 --- /dev/null +++ b/src/nw4r/snd/snd_MidiSeqTrackAllocator.cpp @@ -0,0 +1,6 @@ +/* Only implemented to the extent necessary to match early instantiations of + * inline functions and data sections. */ + +#include "nw4r/snd/MidiSeqTrackAllocator.h" + +nw4r::snd::detail::MidiSeqTrackAllocator::MidiSeqTrackAllocator() {} diff --git a/src/nw4r/snd/snd_MmlParser.cpp b/src/nw4r/snd/snd_MmlParser.cpp index 3bd3237b..ab336e35 100644 --- a/src/nw4r/snd/snd_MmlParser.cpp +++ b/src/nw4r/snd/snd_MmlParser.cpp @@ -1 +1,983 @@ -#include "nw4r/snd/snd_MmlParser.h" +#include "nw4r/snd/MmlParser.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_MmlParser.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <macros.h> +#include <types.h> + +#include "nw4r/snd/global.h" +#include "nw4r/snd/Lfo.h" // LfoParam +#include "nw4r/snd/MmlSeqTrack.h" +#include "nw4r/snd/MoveValue.h" +#include "nw4r/snd/SeqPlayer.h" +#include "nw4r/snd/SeqTrack.h" +#include "nw4r/snd/Util.h" // Util::CalcRandom + +#include "nw4r/ut/inlines.h" + +#if 0 +#include <revolution/OS/OSReport.h> +#else +#include <context_rvl.h> +#endif + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * macros + */ + +// player/global/track variable limit stuff + +// clang-format off +#define AllVarMin_ 0 + +#define PlayerVarMin_ AllVarMin_ +#define PlayerVarMax_ (PlayerVarMin_ + SeqPlayer::PLAYER_VARIABLE_NUM) + +#define GlobalVarMin_ PlayerVarMax_ +#define GlobalVarMax_ (GlobalVarMin_ + SeqPlayer::GLOBAL_VARIABLE_NUM) + +#define TrackVarMin_ GlobalVarMax_ +#define TrackVarMax_ (TrackVarMin_ + SeqTrack::TRACK_VARIABLE_NUM) + +#define AllVarMax_ TrackVarMax_ +// clang-format on + +/******************************************************************************* + * types + */ + +// forward declarations +namespace nw4r { namespace snd { namespace detail { class Channel; }}} + +/******************************************************************************* + * variables + */ + +namespace nw4r { namespace snd { namespace detail +{ + // .sbss + bool MmlParser::mPrintVarEnabledFlag; +}}} // namespace nw4r::snd::detail + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +MmlSeqTrack::ParseResult MmlParser::Parse(MmlSeqTrack *track, + bool doNoteOn) const +{ + NW4RAssertPointerNonnull_Line(49, track); + + SeqPlayer *player = track->GetSeqPlayer(); + NW4RAssertPointerNonnull_Line(51, player); + + SeqTrack::ParserTrackParam &trackParam = track->GetParserTrackParam(); + SeqPlayer::ParserPlayerParam &playerParam ATTR_UNUSED = + player->GetParserPlayerParam(); + + SeqArgType argType; + SeqArgType argType2 = SEQ_ARG_NONE; + + bool useArgType = false; + bool doExecCommand = true; + + u32 cmd = ReadByte(&trackParam.currentAddr); + + if (cmd == MML_EXEC_IF) + { + cmd = ReadByte(&trackParam.currentAddr); + doExecCommand = trackParam.cmpFlag != false; + } + + if (cmd == MML_ARG_2_S16) + { + cmd = ReadByte(&trackParam.currentAddr); + argType2 = SEQ_ARG_S16; + } + else if (cmd == MML_ARG_2_RANDOM) + { + cmd = ReadByte(&trackParam.currentAddr); + argType2 = SEQ_ARG_RANDOM; + } + else if (cmd == MML_ARG_2_VARIABLE) + { + cmd = ReadByte(&trackParam.currentAddr); + argType2 = SEQ_ARG_VARIABLE; + } + + if (cmd == MML_ARG_1_RANDOM) + { + cmd = ReadByte(&trackParam.currentAddr); + argType = SEQ_ARG_RANDOM; + useArgType = true; + } + else if (cmd == MML_ARG_1_VARIABLE) + { + cmd = ReadByte(&trackParam.currentAddr); + argType = SEQ_ARG_VARIABLE; + useArgType = true; + } + + if (!(cmd & MML_CMD_MASK)) + { + // MML note data, not a command + u8 velocity = ReadByte(&trackParam.currentAddr); + + s32 length = ReadArg(&trackParam.currentAddr, player, track, + useArgType ? argType : SEQ_ARG_VMIDI); + + int key = cmd + trackParam.transpose; + + if (!doExecCommand) + return MmlSeqTrack::PARSE_RESULT_CONTINUE; + + key = ut::Clamp(key, 0, 127); + + if (!trackParam.muteFlag && doNoteOn) + { + NoteOnCommandProc(track, key, velocity, length > 0 ? length : -1, + trackParam.tieFlag); + } + + if (trackParam.noteWaitFlag) + { + trackParam.wait = length; + + if (length == 0) + trackParam.noteFinishWait = true; + } + } + else + { + // MML command + s32 commandArg1 = 0; + s32 commandArg2 = 0; + + switch (static_cast<int>(cmd & MML_CMD_SET_MASK)) + { + case 0x80: + { + switch (cmd) + { + case MML_WAIT: + { + s32 arg = ReadArg(&trackParam.currentAddr, player, track, + useArgType ? argType : SEQ_ARG_VMIDI); + + if (doExecCommand) + trackParam.wait = arg; + } + break; + + case MML_SET_PRGNO: + commandArg1 = ReadArg(&trackParam.currentAddr, player, track, + useArgType ? argType : SEQ_ARG_VMIDI); + + if (doExecCommand) + CommandProc(track, cmd, commandArg1, commandArg2); + + break; + + case MML_OPEN_TRACK: + { + u8 trackNo = ReadByte(&trackParam.currentAddr); + u32 offset = Read24(&trackParam.currentAddr); + + if (doExecCommand) + { + commandArg1 = trackNo; + commandArg2 = offset; + CommandProc(track, cmd, commandArg1, commandArg2); + } + } + break; + + case MML_JUMP: + { + u32 offset = Read24(&trackParam.currentAddr); + + if (doExecCommand) + { + commandArg1 = offset; + CommandProc(track, cmd, commandArg1, commandArg2); + } + } + break; + + case MML_CALL: + { + u32 offset = Read24(&trackParam.currentAddr); + + if (doExecCommand) + { + commandArg1 = offset; + CommandProc(track, cmd, commandArg1, commandArg2); + } + } + break; + } + + break; + } + + case 0xb0: + case 0xc0: + case 0xd0: + { + u8 arg = ReadArg(&trackParam.currentAddr, player, track, + useArgType ? argType : SEQ_ARG_U8); + + if (argType2 != SEQ_ARG_NONE) + { + commandArg2 = + ReadArg(&trackParam.currentAddr, player, track, argType2); + } + + if (!doExecCommand) + break; + + switch (cmd) + { + case MML_SET_TRANSPOSE: + case MML_SET_PITCH_BEND: + commandArg1 = *reinterpret_cast<s8 *>(&arg); + break; + + default: + commandArg1 = *reinterpret_cast<u8 *>(&arg); + break; + } + + CommandProc(track, cmd, commandArg1, commandArg2); + break; + } + + case 0x90: + if (doExecCommand) + CommandProc(track, cmd, commandArg1, commandArg2); + + break; + + case 0xe0: + commandArg1 = + static_cast<s16>(ReadArg(&trackParam.currentAddr, player, track, + useArgType ? argType : SEQ_ARG_S16)); + + if (doExecCommand) + CommandProc(track, cmd, commandArg1, commandArg2); + + break; + + case 0xf0: + { + switch (cmd) + { + case MML_ALLOC_TRACK: + Read16(&trackParam.currentAddr); + NW4RPanicMessage_Line( + 312, "seq: must use alloctrack in startup code"); + + break; + + case MML_EOF: + if (doExecCommand) + return MmlSeqTrack::PARSE_RESULT_FINISH; + + break; + + case MML_EX_COMMAND: + { + u32 cmdex = ReadByte(&trackParam.currentAddr); + + switch (cmdex & MML_CMD_SET_MASK) + { + case MML_EX_USERPROC: + commandArg1 = static_cast<u16>( + ReadArg(&trackParam.currentAddr, player, track, + useArgType ? argType : SEQ_ARG_S16)); + + if (doExecCommand) + { + CommandProc(track, (cmd << 8) + cmdex, commandArg1, + commandArg2); + } + + break; + + case MML_EX_ARITHMETIC: + case MML_EX_LOGIC: + commandArg1 = ReadByte(&trackParam.currentAddr); + commandArg2 = static_cast<s16>( + ReadArg(&trackParam.currentAddr, player, track, + useArgType ? argType : SEQ_ARG_S16)); + + if (doExecCommand) + { + CommandProc(track, (cmd << 8) + cmdex, commandArg1, + commandArg2); + } + + break; + } + } + ATTR_FALLTHROUGH; + + default: + if (doExecCommand) + CommandProc(track, cmd, commandArg1, commandArg2); + + break; + } + } + break; + + case 0xa0: + NW4RPanicMessage_Line(392, "Invalid seqdata command: %d", cmd); + break; + } + + } + + return MmlSeqTrack::PARSE_RESULT_CONTINUE; +} + +void MmlParser::CommandProc(MmlSeqTrack *track, u32 command, s32 commandArg1, + s32 commandArg2) const +{ + NW4RAssertPointerNonnull_Line(421, track); + + SeqPlayer *player = track->GetSeqPlayer(); + NW4RAssertPointerNonnull_Line(423, player); + + SeqTrack::ParserTrackParam &trackParam = track->GetParserTrackParam(); + SeqPlayer::ParserPlayerParam &playerParam = player->GetParserPlayerParam(); + + if (command <= MML_CMD_MAX) + { + switch (command) + { + case MML_SET_TEMPO: + playerParam.tempo = + ut::Clamp<int>(commandArg1, TEMPO_MIN, TEMPO_MAX); + break; + + case MML_SET_TIMEBASE: + playerParam.timebase = commandArg1; + break; + + case MML_SET_PRGNO: + if (commandArg1 < 0x10000) + { + trackParam.prgNo = commandArg1 & 0xffff; + } + else + { + NW4RWarningMessage_Line( + 449, "nw4r::snd::MmlParser: too large prg No. %d", + commandArg1); + } + + break; + + case MML_SET_MUTE: + track->SetMute(static_cast<SeqMute>(commandArg1)); + break; + + case MML_SET_TRACK_VOLUME: + trackParam.volume.SetTarget(commandArg1, commandArg2); + break; + + case MML_SET_TRACK_VOLUME2: + trackParam.volume2 = commandArg1; + break; + + case MML_SET_TRACK_VELOCITY_RANGE: + trackParam.velocityRange = commandArg1; + break; + + case MML_SET_PLAYER_VOLUME: + playerParam.volume = commandArg1; + break; + + case MML_SET_TRANSPOSE: + trackParam.transpose = commandArg1; + break; + + case MML_SET_PITCH_BEND: + trackParam.pitchBend = commandArg1; + break; + + case MML_SET_BEND_RANGE: + trackParam.bendRange = commandArg1; + break; + + case MML_SET_PAN: + trackParam.pan.SetTarget(commandArg1 - PAN_CENTER, commandArg2); + break; + + case MML_SET_INIT_PAN: + trackParam.initPan = commandArg1 - PAN_CENTER; + break; + + case MML_SET_SURROUND_PAN: + trackParam.surroundPan.SetTarget(commandArg1, commandArg2); + break; + + case MML_SET_PRIORITY: + trackParam.priority = commandArg1; + break; + + case MML_SET_NOTE_WAIT: + trackParam.noteWaitFlag = commandArg1; + break; + + case MML_SET_PORTATIME: + trackParam.portaTime = commandArg1; + break; + + case MML_SET_LFO_DEPTH: + trackParam.lfoParam.depth = static_cast<u8>(commandArg1) / 128.0f; + break; + + case MML_SET_LFO_SPEED: + trackParam.lfoParam.speed = + static_cast<u8>(commandArg1) * (100.0f / 256.0f); + break; + + case MML_SET_LFO_TARGET: + trackParam.lfoTarget = commandArg1; + break; + + case MML_SET_LFO_RANGE: + trackParam.lfoParam.range = commandArg1; + break; + + case MML_SET_LFO_DELAY: + trackParam.lfoParam.delay = commandArg1 * 5; + break; + + case MML_SET_SWEEP_PITCH: + trackParam.sweepPitch = commandArg1 / 64.0f; + break; + + case MML_SET_ATTACK: + trackParam.attack = commandArg1; + break; + + case MML_SET_DECAY: + trackParam.decay = commandArg1; + break; + + case MML_SET_SUSTAIN: + trackParam.sustain = commandArg1; + break; + + case MML_SET_RELEASE: + trackParam.release = commandArg1; + break; + + case MML_SET_ENV_HOLD: + trackParam.envHold = commandArg1 & 0xff; + break; + + case MML_RESET_ADSR: + trackParam.attack = 0xff; + trackParam.decay = 0xff; + trackParam.sustain = 0xff; + trackParam.release = 0xff; + trackParam.envHold = 0xff; + + break; + + case MML_SET_DAMPER: + trackParam.damperFlag = static_cast<u8>(commandArg1) >= 64; + break; + + case MML_SET_TIE: + trackParam.tieFlag = commandArg1; + track->ReleaseAllChannel(-1); + track->FreeAllChannel(); + break; + + case MML_SET_MONOPHONIC: + trackParam.monophonicFlag = commandArg1; + + if (trackParam.monophonicFlag) + { + track->ReleaseAllChannel(-1); + track->FreeAllChannel(); + } + + break; + + case MML_SET_PORTAMENTO: + trackParam.portaKey = commandArg1 + trackParam.transpose; + trackParam.portaFlag = true; + break; + + case MML_SET_PORTASPEED: + trackParam.portaFlag = commandArg1 != 0; + break; + + case MML_SET_LPF_FREQ: + trackParam.lpfFreq = (commandArg1 - 64) / 64.0f; + break; + + case MML_SET_BIQUAD_TYPE: + trackParam.biquadType = commandArg1; + break; + + case MML_SET_BIQUAD_VALUE: + trackParam.biquadValue = commandArg1 / 127.0f; + break; + + case MML_SET_FX_SEND_A: + trackParam.fxSend[AUX_A] = commandArg1; + break; + + case MML_SET_FX_SEND_B: + trackParam.fxSend[AUX_B] = commandArg1; + break; + + case MML_SET_FX_SEND_C: + trackParam.fxSend[AUX_C] = commandArg1; + break; + + case MML_SET_MAIN_SEND: + trackParam.mainSend = commandArg1; + break; + + case MML_PRINT_VAR: + if (mPrintVarEnabledFlag) + { + s16 const volatile * const varPtr = + GetVariablePtr(player, track, commandArg1); + +#define GetVarType_(varNo_) \ + ((varNo_) >= TrackVarMin_ ? "T" : (varNo_) >= GlobalVarMin_ ? "G" : "") + +#define GetAdjustedVarNo_(varNo_) \ + ((varNo_) >= TrackVarMin_ ? (varNo_) - TrackVarMin_ \ + : (varNo_) >= GlobalVarMin_ ? (varNo_) - GlobalVarMin_ \ + : (varNo_)) + + OSReport("#%08x[%d]: printvar %sVAR_%d(%d) = %d\n", player, + track->GetPlayerTrackNo(), GetVarType_(commandArg1), + GetAdjustedVarNo_(commandArg1), commandArg1, *varPtr); + +#undef GetVarType_ +#undef GetAdjustedVarNo_ + } + + break; + + case MML_OPEN_TRACK: + { + SeqTrack *newTrack = player->GetPlayerTrack(commandArg1); + + if (!newTrack) + { + NW4RWarningMessage_Line( + 644, + "nw4r::snd::MmlParser: opentrack for not allocated track"); + break; + } + + if (newTrack == track) + { + NW4RWarningMessage_Line( + 649, "nw4r::snd::MmlParser: opentrack for self track"); + break; + } + + newTrack->Close(); + newTrack->SetSeqData(trackParam.baseAddr, commandArg2); + newTrack->Open(); + } + break; + + case MML_JUMP: + trackParam.currentAddr = trackParam.baseAddr + commandArg1; + break; + + case MML_CALL: + { + if (trackParam.callStackDepth >= CALL_STACK_DEPTH) + { + NW4RWarningMessage_Line(665, + "nw4r::snd::MmlParser: cannot \'call\' " + "because already too deep"); + break; + } + + SeqTrack::CallStack *callStack = + &trackParam.callStack[trackParam.callStackDepth]; + + callStack->address = trackParam.currentAddr; + callStack->loopFlag = false; + + trackParam.callStackDepth++; + trackParam.currentAddr = trackParam.baseAddr + commandArg1; + break; + } + + case MML_RET: + { + SeqTrack::CallStack *callStack = nullptr; + + while (trackParam.callStackDepth) + { + trackParam.callStackDepth--; + + if (!trackParam.callStack[trackParam.callStackDepth].loopFlag) + { + callStack = &trackParam.callStack[trackParam.callStackDepth]; + break; + } + } + + if (!callStack) + { + NW4RWarningMessage_Line( + 688, + "nw4r::snd::MmlParser: unmatched sequence command \'ret\'"); + + break; + } + + trackParam.currentAddr = callStack->address; + } + break; + + case MML_LOOP_START: + { + if (trackParam.callStackDepth >= CALL_STACK_DEPTH) + { + NW4RWarningMessage_Line( + 698, "nw4r::snd::MmlParser: cannot \'loop_start\' because " + "already too deep"); + + break; + } + + SeqTrack::CallStack *callStack = + &trackParam.callStack[trackParam.callStackDepth]; + + callStack->address = trackParam.currentAddr; + callStack->loopCount = commandArg1; + callStack->loopFlag = true; + + trackParam.callStackDepth++; + } + break; + + case MML_LOOP_END: + { + if (trackParam.callStackDepth == 0) + { + NW4RWarningMessage_Line(713, "nw4r::snd::MmlParser: unmatched " + "sequence command \'loop_end\'"); + break; + } + + SeqTrack::CallStack *callStack = + &trackParam.callStack[trackParam.callStackDepth - 1]; + + if (!callStack->loopFlag) + { + NW4RWarningMessage_Line(719, "nw4r::snd::MmlParser: unmatched " + "sequence command \'loop_end\'"); + break; + } + + u8 loop_count = callStack->loopCount; + + if (loop_count && --loop_count == 0) + { + trackParam.callStackDepth--; + } + else + { + callStack->loopCount = loop_count; + + trackParam.currentAddr = callStack->address; + } + } + break; + } + } + else if (command <= MML_EX_CMD_MAX) + { + u32 cmd = command >> 8; + u32 cmdex = command & 0xff; + + NW4RAssert_Line(742, cmd == MML_EX_COMMAND); + + s16 volatile *varPtr = nullptr; + + if ((cmdex & 0xf0) == MML_EX_ARITHMETIC + || (cmdex & 0xf0) == MML_EX_LOGIC) + { + varPtr = GetVariablePtr(player, track, commandArg1); + if (!varPtr) + return; + } + + switch (cmdex) + { + case MML_EX_SET: + *varPtr = commandArg2; + break; + + case MML_EX_APL: + *varPtr += commandArg2; + break; + + case MML_EX_AMI: + *varPtr -= commandArg2; + break; + + case MML_EX_AMU: + *varPtr *= commandArg2; + break; + + case MML_EX_ADV: + if (commandArg2 != 0) + *varPtr /= commandArg2; + + break; + + case MML_EX_ALS: + if (commandArg2 >= 0) + *varPtr <<= commandArg2; + else + *varPtr >>= -commandArg2; + + break; + + case MML_EX_RND: + { + bool minus_flag = false; + + if (commandArg2 < 0) + { + minus_flag = true; + commandArg2 = static_cast<s16>(-commandArg2); + } + + s32 rand = Util::CalcRandom(); + rand *= commandArg2 + 1; + rand >>= 16; + + if (minus_flag) + rand = -rand; + + *varPtr = rand; + break; + } + + case MML_EX_AAD: + *varPtr &= commandArg2; + break; + + case MML_EX_AOR: + *varPtr |= commandArg2; + break; + + case MML_EX_AER: + *varPtr ^= commandArg2; + break; + + case MML_EX_ACO: + *varPtr = ~static_cast<u16>(commandArg2); + break; + + case MML_EX_AMD: + if (commandArg2 != 0) + *varPtr %= commandArg2; + + break; + + case MML_EX_EQ: + trackParam.cmpFlag = *varPtr == commandArg2; + break; + + case MML_EX_GE: + trackParam.cmpFlag = *varPtr >= commandArg2; + break; + + case MML_EX_GT: + trackParam.cmpFlag = *varPtr > commandArg2; + break; + + case MML_EX_LE: + trackParam.cmpFlag = *varPtr <= commandArg2; + break; + + case MML_EX_LT: + trackParam.cmpFlag = *varPtr < commandArg2; + break; + + case MML_EX_NE: + trackParam.cmpFlag = *varPtr != commandArg2; + break; + + case MML_EX_USERPROC: + player->CallSeqUserprocCallback(commandArg1, track); + break; + } + } +} + +Channel *MmlParser::NoteOnCommandProc(MmlSeqTrack *track, int key, int velocity, + s32 length, bool tieFlag) const +{ + return track->NoteOn(key, velocity, length, tieFlag); +} + +byte2_t MmlParser::Read16(byte_t const **ptr) const +{ + byte2_t ret = ReadByte(ptr); + + ret <<= 8; + ret |= ReadByte(ptr); + + return ret; +} + +byte4_t MmlParser::Read24(byte_t const **ptr) const +{ + byte4_t ret = ReadByte(ptr); + + ret <<= 8; + ret |= ReadByte(ptr); + + ret <<= 8; + ret |= ReadByte(ptr); + + return ret; +} + +s32 MmlParser::ReadVar(byte_t const **ptr) const +{ + s32 ret = 0; + byte_t b; + + for (int i = 0;; i++) + { + NW4RAssert_Line(940, i < 4); + + b = ReadByte(ptr); + ret <<= 7; + ret |= b & 0x7f; + + if (!(b & 0x80)) + break; + } + + return ret; +} + +s32 MmlParser::ReadArg(byte_t const **ptr, SeqPlayer *player, SeqTrack *track, + SeqArgType argType) const +{ + s32 var; + + switch (argType) + { + case SEQ_ARG_U8: + var = ReadByte(ptr); + break; + + case SEQ_ARG_S16: + var = Read16(ptr); + break; + + case SEQ_ARG_VMIDI: + var = ReadVar(ptr); + break; + + case SEQ_ARG_VARIABLE: + { + u8 varNo = ReadByte(ptr); + + s16 const volatile *varPtr = GetVariablePtr(player, track, varNo); + + // ERRATUM: if varPtr is not valid then ReadArg returns garbage + if (varPtr) + var = *varPtr; + } + break; + + case SEQ_ARG_RANDOM: + { + s32 rand; + + s16 min = Read16(ptr); + s16 max = Read16(ptr); + + rand = Util::CalcRandom(); + rand *= max - min + 1; + rand >>= 16; + rand += min; + + var = rand; + } + break; + } + + return var; +} + +s16 volatile *MmlParser::GetVariablePtr(SeqPlayer *player, SeqTrack *track, + int varNo) const +{ + NW4RAssertHeaderClampedLRValue_Line(1014, varNo, AllVarMin_, AllVarMax_); + + if (varNo < GlobalVarMax_) + return player->GetVariablePtr(varNo); + + if (varNo < TrackVarMax_) + return track->GetVariablePtr(varNo - TrackVarMin_); + + return nullptr; +} + +u32 MmlParser::ParseAllocTrack(void const *baseAddress, u32 seqOffset, + byte4_t *allocTrack) +{ + NW4RAssertPointerNonnull_Line(1051, baseAddress); + NW4RAssertPointerNonnull_Line(1052, allocTrack); + + byte_t const *ptr = + static_cast<byte_t const *>(ut::AddOffsetToPtr(baseAddress, seqOffset)); + + if (*ptr != MML_ALLOC_TRACK) + { + *allocTrack = 1; + return seqOffset; + } + else + { + u32 tracks = *++ptr; + + tracks <<= 8; + tracks |= *++ptr; + + *allocTrack = tracks; + return seqOffset + 3; + } +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_MmlSeqTrack.cpp b/src/nw4r/snd/snd_MmlSeqTrack.cpp index 5f86bf4c..e2eee4ce 100644 --- a/src/nw4r/snd/snd_MmlSeqTrack.cpp +++ b/src/nw4r/snd/snd_MmlSeqTrack.cpp @@ -1 +1,27 @@ -#include "nw4r/snd/snd_MmlSeqTrack.h" +#include "nw4r/snd/MmlSeqTrack.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_MmlSeqTrack.cpp + */ + +/******************************************************************************* + * headers + */ + +#include "nw4r/snd/MmlParser.h" + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +MmlSeqTrack::MmlSeqTrack() {} + +MmlSeqTrack::ParseResult MmlSeqTrack::Parse(bool doNoteOn) +{ + return mParser->Parse(this, doNoteOn); +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_MmlSeqTrackAllocator.cpp b/src/nw4r/snd/snd_MmlSeqTrackAllocator.cpp index 4869b377..3363143a 100644 --- a/src/nw4r/snd/snd_MmlSeqTrackAllocator.cpp +++ b/src/nw4r/snd/snd_MmlSeqTrackAllocator.cpp @@ -1 +1,56 @@ -#include "nw4r/snd/snd_MmlSeqTrackAllocator.h" +#include "nw4r/snd/MmlSeqTrackAllocator.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_MmlSeqTrackAllocator.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <types.h> + +#include "nw4r/snd/InstancePool.h" +#include "nw4r/snd/MmlSeqTrack.h" +#include "nw4r/snd/SeqTrack.h" + +#include <nw4r/NW4RAssert.hpp> + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +SeqTrack *MmlSeqTrackAllocator::AllocTrack(SeqPlayer *player) +{ + MmlSeqTrack *track = mTrackPool.Alloc(); + if (track) + { + track->SetSeqPlayer(player); + track->SetMmlParser(mParser); + } + + return track; +} + +void MmlSeqTrackAllocator::FreeTrack(SeqTrack *track) +{ + NW4RAssertPointerNonnull_Line(59, track); + + track->SetSeqPlayer(nullptr); + mTrackPool.Free(static_cast<MmlSeqTrack *>(track)); +} + +u32 MmlSeqTrackAllocator::Create(void *buffer, u32 size) +{ + return mTrackPool.Create(buffer, size); +} + +void MmlSeqTrackAllocator::Destroy(void *buffer, u32 size) +{ + mTrackPool.Destroy(buffer, size); +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_MoveValue.cpp b/src/nw4r/snd/snd_MoveValue.cpp new file mode 100644 index 00000000..2a924f1d --- /dev/null +++ b/src/nw4r/snd/snd_MoveValue.cpp @@ -0,0 +1 @@ +/* This file intentionally left blank. */ diff --git a/src/nw4r/snd/snd_NoteOnCallback.cpp b/src/nw4r/snd/snd_NoteOnCallback.cpp new file mode 100644 index 00000000..2a924f1d --- /dev/null +++ b/src/nw4r/snd/snd_NoteOnCallback.cpp @@ -0,0 +1 @@ +/* This file intentionally left blank. */ diff --git a/src/nw4r/snd/snd_PlayerHeap.cpp b/src/nw4r/snd/snd_PlayerHeap.cpp index e7f0677a..677b4f37 100644 --- a/src/nw4r/snd/snd_PlayerHeap.cpp +++ b/src/nw4r/snd/snd_PlayerHeap.cpp @@ -1 +1,118 @@ -#include "nw4r/snd/snd_PlayerHeap.h" +#include "nw4r/snd/PlayerHeap.h" + +/******************************************************************************* + * headers + */ + +#include <cstddef> // NULL + +#include <types.h> + +#include "nw4r/snd/DisposeCallbackManager.h" +#include "nw4r/snd/SoundThread.h" // SoundThread::AutoLock + +#include "nw4r/ut/inlines.h" + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * types + */ + +// forward declarations +namespace nw4r { namespace snd { namespace detail { class BasicSound; }}} + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +PlayerHeap::PlayerHeap() : + mSound (nullptr), + mPlayer (nullptr), + mStartAddress (nullptr), + mEndAddress (nullptr), + mAllocAddress (nullptr) +{ +} + +PlayerHeap::~PlayerHeap() +{ + Destroy(); +} + +bool PlayerHeap::Create(void *startAddress, u32 size) +{ + void *endAddress = ut::AddOffsetToPtr(startAddress, size); + startAddress = ut::RoundUp(startAddress, 32); + + if (startAddress > endAddress) + return false; + + mStartAddress = startAddress; + mEndAddress = endAddress; + mAllocAddress = mStartAddress; + + return true; +} + +void PlayerHeap::Destroy() +{ + Clear(); + mAllocAddress = nullptr; +} + +void *PlayerHeap::Alloc(u32 size) +{ + NW4RAssertAligned_Line(108, mAllocAddress, 32); + + void *endp = ut::AddOffsetToPtr(mAllocAddress, size); + if (endp > mEndAddress) + return nullptr; + + void *allocAddress = mAllocAddress; + mAllocAddress = ut::RoundUp(endp, 32); + return allocAddress; +} + +void PlayerHeap::Clear() +{ + SoundThread::AutoLock lockForDispose; + + DisposeCallbackManager::GetInstance().Dispose( + mStartAddress, ut::GetOffsetFromPtr(mStartAddress, mAllocAddress), + nullptr); + + DisposeCallbackManager::GetInstance().DisposeWave( + mStartAddress, ut::GetOffsetFromPtr(mStartAddress, mAllocAddress), + nullptr); + + mAllocAddress = mStartAddress; +} + +u32 PlayerHeap::GetFreeSize() const +{ + s32 offset = ut::GetOffsetFromPtr(mAllocAddress, mEndAddress); + NW4RAssert_Line(157, offset >= 0); + + return offset; +} + +void PlayerHeap::AttachSound(BasicSound *sound) +{ + NW4RAssertPointerNonnull_Line(172, sound); + NW4RAssert_Line(173, mSound == NULL); + + mSound = sound; +} + +void PlayerHeap::DetachSound(BasicSound *sound) +{ + NW4RAssertPointerNonnull_Line(189, sound); + NW4RAssert_Line(190, sound == mSound); + + mSound = nullptr; +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_SeqFile.cpp b/src/nw4r/snd/snd_SeqFile.cpp index 8d56b0c9..027a3343 100644 --- a/src/nw4r/snd/snd_SeqFile.cpp +++ b/src/nw4r/snd/snd_SeqFile.cpp @@ -1 +1,127 @@ -#include "nw4r/snd/snd_SeqFile.h" +#include "nw4r/snd/SeqFile.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_SeqFile.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <cstring> + +#include <macros.h> // NW4R_FILE_VERSION +#include <types.h> + +#include "nw4r/snd/Util.h" + +#include "nw4r/ut/binaryFileFormat.h" +#include "nw4r/ut/inlines.h" // ut::AddOffsetToPtr + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +bool SeqFileReader::IsValidFileHeader(void const *seqData) +{ + ut::BinaryFileHeader const *fileHeader = + static_cast<ut::BinaryFileHeader const *>(seqData); + + NW4RAssertMessage_Line( + 43, fileHeader->signature == SeqFile::SIGNATURE_FILE, + "invalid file signature. seq data is not available."); + + if (fileHeader->signature != SeqFile::SIGNATURE_FILE) + return false; + + u16 version = Util::ReadBigEndian(fileHeader->version); + + NW4RAssertMessage_Line( + 51, version >= NW4R_FILE_VERSION(1, 0), + "seq file is not supported version.\n" + " please reconvert file using new version tools.\n"); + + version = Util::ReadBigEndian(fileHeader->version); // ? again? + if (version < NW4R_FILE_VERSION(1, 0)) + return false; + + NW4RAssertMessage_Line( + 59, version <= SUPPORTED_FILE_VERSION, + "seq file is not supported version.\n" + " please reconvert file using new version tools.\n"); + + if (version > SUPPORTED_FILE_VERSION) + return false; + + return true; +} + +SeqFileReader::SeqFileReader(void const *seqData) : + mHeader (nullptr), + mDataBlock (nullptr) +{ + NW4RAssertPointerNonnull_Line(78, seqData); + + if (!IsValidFileHeader(seqData)) + return; + + mHeader = static_cast<SeqFile::Header const *>(seqData); + mDataBlock = static_cast<SeqFile::DataBlock const *>(ut::AddOffsetToPtr( + mHeader, Util::ReadBigEndian(mHeader->dataBlockOffset))); + + NW4RAssert_Line(87, mDataBlock->blockHeader.kind + == SeqFile::SIGNATURE_DATA_BLOCK); +} + +void const *SeqFileReader::GetBaseAddress() const +{ + NW4RAssertPointerNonnull_Line(101, mHeader); + + return ut::AddOffsetToPtr(mDataBlock, + Util::ReadBigEndian(mDataBlock->baseOffset)); +} + +bool SeqFileReader::ReadOffsetByLabel(char const *labelName, + u32 *offsetPtr) const +{ + NW4RAssertPointerNonnull_Line(117, offsetPtr); + + // NOTE: reinterpret_cast necessary instead of static_cast for regalloc(???) + SeqFile::LabelBlock const *labelBlock = + reinterpret_cast<SeqFile::LabelBlock const *>(ut::AddOffsetToPtr( + mHeader, Util::ReadBigEndian(mHeader->labelBlockOffset))); + + if (!labelBlock) + return false; + + u32 labelNameLen = std::strlen(labelName); + + for (int index = 0; + index < Util::ReadBigEndian(labelBlock->labelInfoTable.count); ++index) + { + u32 ofs = labelBlock->labelInfoTable.item[index]; + + // NOTE: reinterpret_cast necessary here too + SeqFile::LabelInfo const *labelInfo = + reinterpret_cast<SeqFile::LabelInfo const *>( + ut::AddOffsetToPtr(labelBlock, Util::ReadBigEndian(ofs) + 8)); + + NW4RAssertPointerNonnull_Line(133, labelInfo); + + if (labelNameLen == Util::ReadBigEndian(labelInfo->nameLen) + && std::strncmp(labelName, labelInfo->name, labelNameLen) == 0) + { + *offsetPtr = Util::ReadBigEndian(labelInfo->offset); + return true; + } + } + + return false; +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_SeqPlayer.cpp b/src/nw4r/snd/snd_SeqPlayer.cpp index bbe51d54..8ed60033 100644 --- a/src/nw4r/snd/snd_SeqPlayer.cpp +++ b/src/nw4r/snd/snd_SeqPlayer.cpp @@ -1 +1,563 @@ -#include "nw4r/snd/snd_SeqPlayer.h" +#include "nw4r/snd/SeqPlayer.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_SeqPlayer.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <decomp.h> +#include <types.h> + +#include "nw4r/snd/BasicPlayer.h" +#include "nw4r/snd/DisposeCallbackManager.h" +#include "nw4r/snd/NoteOnCallback.h" +#include "nw4r/snd/SeqTrack.h" +#include "nw4r/snd/SeqTrackAllocator.h" +#include "nw4r/snd/SoundThread.h" + +#include "nw4r/ut/Lock.h" // ut::AutoInterruptLock + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * types + */ + +// forward declarations +namespace nw4r { namespace snd { namespace detail { class Channel; }}} + +/******************************************************************************* + * variables + */ + +namespace nw4r { namespace snd { namespace detail +{ + // .bss + s16 SeqPlayer::mGlobalVariable[GLOBAL_VARIABLE_NUM]; +}}} // namespace nw4r::snd::detail + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +void SeqPlayer::InitSeqPlayer() +{ + for (int variableNo = 0; variableNo < GLOBAL_VARIABLE_NUM; variableNo++) + mGlobalVariable[variableNo] = VARIABLE_DEFAULT_VALUE; +} + +SeqPlayer::SeqPlayer() : + mActiveFlag (false), + mStartedFlag (false), + mPauseFlag (false), + mReleasePriorityFixFlag (false), + mTempoRatio (1.0f), + mTickFraction (0.0f), + mSkipTickCounter (0), + mSkipTimeCounter (0.0f) +{ + mPanRange = 1.0f; + mTickCounter = 0; + mVoiceOutCount = 0; + + mSeqUserprocCallback = nullptr; + mSeqUserprocCallbackArg = nullptr; + + mParserParam.tempo = DEFAULT_TEMPO; + mParserParam.timebase = DEFAULT_TIMEBASE; + mParserParam.volume = 127; + mParserParam.priority = 64; + mParserParam.callback = nullptr; + + for (int varNo = 0; varNo < PLAYER_VARIABLE_NUM; varNo++) + mLocalVariable[varNo] = VARIABLE_DEFAULT_VALUE; + + for (int trackNo = 0; trackNo < TRACK_NUM_PER_PLAYER; trackNo++) + mTracks[trackNo] = nullptr; +} + +SeqPlayer::~SeqPlayer() +{ + Shutdown(); +} + +void SeqPlayer::InitParam(int voiceOutCount, NoteOnCallback *callback) +{ + BasicPlayer::InitParam(); + + mStartedFlag = false; + mPauseFlag = false; + mTempoRatio = 1.0f; + mSkipTickCounter = 0; + mSkipTimeCounter = 0.0f; + mPanRange = 1.0f; + mTickCounter = 0; + mVoiceOutCount = voiceOutCount; + + mParserParam.tempo = DEFAULT_TEMPO; + mParserParam.timebase = DEFAULT_TIMEBASE; + mParserParam.volume = 127; + mParserParam.priority = 64; + mParserParam.callback = callback; + + mTickFraction = 0.0f; + + for (int varNo = 0; varNo < PLAYER_VARIABLE_NUM; varNo++) + mLocalVariable[varNo] = VARIABLE_DEFAULT_VALUE; + + for (int trackNo = 0; trackNo < TRACK_NUM_PER_PLAYER; trackNo++) + mTracks[trackNo] = nullptr; +} + +SeqPlayer::SetupResult SeqPlayer::Setup(SeqTrackAllocator *trackAllocator, + u32 allocTracks, int voiceOutCount, + NoteOnCallback *callback) +{ + SoundThread::AutoLock lock; + + SeqPlayer::Stop(); // NOTE: qualified name to inhibit dynamic dispatch + InitParam(voiceOutCount, callback); + + { + ut::AutoInterruptLock lockIntr; + + int trackCount = 0; + + // popcnt, pretty sure + for (u32 trackBitMask = allocTracks; trackBitMask; trackBitMask >>= 1) + { + if (trackBitMask & 1) + trackCount++; + } + + if (trackCount > trackAllocator->GetAllocatableTrackCount()) + return SETUP_ERR_CANNOT_ALLOCATE_TRACK; + + u32 trackBitMask = allocTracks; + + // popcnt again, pretty sure + for (int trackNo = 0; trackBitMask; trackBitMask >>= 1, trackNo++) + { + if (trackBitMask & 1) + { + SeqTrack *track = trackAllocator->AllocTrack(this); + NW4RAssertPointerNonnull_Line(199, track); + + SetPlayerTrack(trackNo, track); + } + } + + // end of lockIntr's scope + } + + DisposeCallbackManager::GetInstance().RegisterDisposeCallback(this); + + mSeqTrackAllocator = trackAllocator; + mActiveFlag = true; + + return SETUP_SUCCESS; +} + +void SeqPlayer::Shutdown() +{ + SoundThread::AutoLock lock; + + FinishPlayer(); +} + +void SeqPlayer::SetSeqData(void const *seqBase, s32 seqOffset) +{ + SoundThread::AutoLock lock; + + SeqTrack *seqTrack = GetPlayerTrack(0); + NW4RAssertPointerNonnull_Line(245, seqTrack); + + if (seqBase) + { + seqTrack->SetSeqData(seqBase, seqOffset); + seqTrack->Open(); + } +} + +bool SeqPlayer::Start() +{ + SoundThread::AutoLock lock; + + SoundThread::GetInstance().RegisterPlayerCallback(this); + mStartedFlag = true; + + return true; +} + +void SeqPlayer::Stop() +{ + SoundThread::AutoLock lock; + + FinishPlayer(); +} + +void SeqPlayer::Pause(bool flag) +{ + SoundThread::AutoLock lock; + + mPauseFlag = flag; + + SeqTrack *track; // declared before trackNo in ketteiban dwarf + + for (int trackNo = 0; trackNo < TRACK_NUM_PER_PLAYER; trackNo++) + { + track = GetPlayerTrack(trackNo); + + if (track) + track->PauseAllChannel(flag); + } +} + +void SeqPlayer::Skip(OffsetType offsetType, int offset) +{ + SoundThread::AutoLock lock; + + if (!mActiveFlag) + return; + + switch (offsetType) + { + case OFFSET_TYPE_TICK: + mSkipTickCounter += offset; + break; + + case OFFSET_TYPE_MILLISEC: + mSkipTimeCounter += offset; + break; + } +} + +// SeqPlayer::SetTempoRatio ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13781) +DECOMP_FORCE(NW4RAssert_String(tempoRatio >= 0.0f)); + +void SeqPlayer::SetChannelPriority(int priority) +{ + // specifically not the source variant + NW4RAssertHeaderClampedLRValue_Line(358, priority, 0, 127); + + mParserParam.priority = priority; +} + +void SeqPlayer::SetReleasePriorityFix(bool fix) +{ + mReleasePriorityFixFlag = fix; +} + +void SeqPlayer::SetSeqUserprocCallback(SeqUserprocCallback *callback, void *arg) +{ + mSeqUserprocCallback = callback; + mSeqUserprocCallbackArg = arg; +} + +void SeqPlayer::CallSeqUserprocCallback(u16 procId, SeqTrack *track) +{ + if (!mSeqUserprocCallback) + return; + + NW4RAssertPointerNonnull_Line(377, track); + + SeqTrack::ParserTrackParam &trackParam = track->GetParserTrackParam(); + + SeqUserprocCallbackParam param; + param.localVariable = GetVariablePtr(0); + param.globalVariable = GetVariablePtr(16); + param.trackVariable = track->GetVariablePtr(0); + param.cmpFlag = trackParam.cmpFlag; + + (*mSeqUserprocCallback)(procId, ¶m, mSeqUserprocCallbackArg); + + trackParam.cmpFlag = param.cmpFlag; +} + +// SeqPlayer::GetLocalVariable? maybe all of them? +DECOMP_FORCE(NW4RAssertHeaderClampedLValue_String(varNo)); + +// SeqPlayer::SetTrackMute ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13791) +DECOMP_FORCE(&SeqTrack::SetMute); + +// SeqPlayer::SetTrackSilence ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13792) +DECOMP_FORCE(&SeqTrack::SetSilence); + +// SeqPlayer::SetTrackVolume ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13793) +DECOMP_FORCE(&SeqTrack::SetVolume); +DECOMP_FORCE(NW4RAssert_String(volume >= 0.0f)); + +// SeqPlayer::SetTrackPitch ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13794) +DECOMP_FORCE(&SeqTrack::SetPitch); +DECOMP_FORCE(NW4RAssert_String(pitch >= 0.0f)); + +// SeqPlayer::SetTrackPan ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13795) +DECOMP_FORCE(&SeqTrack::SetPan); + +// SeqPlayer::SetTrackSurroundPan ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13796) +DECOMP_FORCE(&SeqTrack::SetSurroundPan); + +// SeqPlayer::SetTrackLpfFreq ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13797) +DECOMP_FORCE(&SeqTrack::SetLpfFreq); + +// SeqPlayer::SetTrackBiquadFilter ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13798) +DECOMP_FORCE(&SeqTrack::SetBiquadFilter); + +// SeqPlayer::SetTrackPanRange ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13799) +DECOMP_FORCE(&SeqTrack::SetPanRange); + +// SeqPlayer::SetTrackModDepth ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13800) +DECOMP_FORCE(&SeqTrack::SetModDepth); + +// SeqPlayer::SetTrackModSpeed ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13801) +DECOMP_FORCE(&SeqTrack::SetModSpeed); + +void SeqPlayer::InvalidateData(void const *start, void const *end) +{ + SoundThread::AutoLock lock; + + if (mActiveFlag) + { + for (int trackNo = 0; trackNo < TRACK_NUM_PER_PLAYER; trackNo++) + { + SeqTrack *track = GetPlayerTrack(trackNo); + if (!track) + continue; + + byte_t const *cur = track->GetParserTrackParam().baseAddr; + if (start <= cur && cur <= end) + { + // NOTE: qualified name to inhibit dynamic dispatch + SeqPlayer::Stop(); + break; + } + } + } +} + +SeqTrack *SeqPlayer::GetPlayerTrack(int trackNo) +{ + if (trackNo > TRACK_NUM_PER_PLAYER - 1) + return nullptr; + + return mTracks[trackNo]; +} + +void SeqPlayer::CloseTrack(int trackNo) +{ + SoundThread::AutoLock lock; + + // specifically not the source variant + NW4RAssertHeaderClampedLValue_Line(609, trackNo, 0, TRACK_NUM_PER_PLAYER); + + SeqTrack *track = GetPlayerTrack(trackNo); + if (!track) + return; + + track->Close(); + + mSeqTrackAllocator->FreeTrack(mTracks[trackNo]); + mTracks[trackNo] = nullptr; +} + +void SeqPlayer::SetPlayerTrack(int trackNo, SeqTrack *track) +{ + SoundThread::AutoLock lock; + + if (trackNo > TRACK_NUM_PER_PLAYER - 1) + return; + + mTracks[trackNo] = track; + track->SetPlayerTrackNo(trackNo); +} + +void SeqPlayer::FinishPlayer() +{ + SoundThread::AutoLock lock; + + if (mStartedFlag) + { + SoundThread::GetInstance().UnregisterPlayerCallback(this); + mStartedFlag = false; + } + + if (mActiveFlag) + { + DisposeCallbackManager::GetInstance().UnregisterDisposeCallback(this); + mActiveFlag = false; + } + + for (int trackNo = 0; trackNo < TRACK_NUM_PER_PLAYER; trackNo++) + CloseTrack(trackNo); +} + +void SeqPlayer::UpdateChannelParam() +{ + SoundThread::AutoLock lock; + + SeqTrack *track; // declared before trackNo in ketteiban dwarf + + for (int trackNo = 0; trackNo < TRACK_NUM_PER_PLAYER; trackNo++) + { + track = GetPlayerTrack(trackNo); + + if (track) + track->UpdateChannelParam(); + } +} + +BOOL SeqPlayer::ParseNextTick(bool doNoteOn) +{ + SoundThread::AutoLock lock; + + bool activeFlag = false; + + for (int trackNo = 0; trackNo < TRACK_NUM_PER_PLAYER; trackNo++) + { + SeqTrack *track = GetPlayerTrack(trackNo); + if (!track) + continue; + + track->UpdateChannelLength(); + + if (track->ParseNextTick(doNoteOn) < 0) + CloseTrack(trackNo); + + if (track->IsOpened()) + activeFlag = true; + } + + if (!activeFlag) + return TRUE; + + return FALSE; +} + +s16 volatile *SeqPlayer::GetVariablePtr(int varNo) +{ + // specifically not the source variant + NW4RAssertHeaderClampedLRValue_Line( + 746, varNo, 0, PLAYER_VARIABLE_NUM + GLOBAL_VARIABLE_NUM); + + if (varNo < PLAYER_VARIABLE_NUM) + return &mLocalVariable[varNo]; + + if (varNo < PLAYER_VARIABLE_NUM + GLOBAL_VARIABLE_NUM) + return &mGlobalVariable[varNo - PLAYER_VARIABLE_NUM]; + + return nullptr; +} + +void SeqPlayer::Update() +{ + SoundThread::AutoLock lock; + + NW4RAssert_Line(772, mActiveFlag); + + if (!mActiveFlag) + return; + + if (!mStartedFlag) + return; + + if (mSkipTickCounter || mSkipTimeCounter > 0.0f) + SkipTick(); + else if (!mPauseFlag) + UpdateTick(3); + + UpdateChannelParam(); +} + +void SeqPlayer::UpdateTick(int msec) +{ + f32 tickPerMsec = CalcTickPerMsec(); + if (tickPerMsec == 0.0f) + return; + + f32 restMsec = static_cast<f32>(msec); + f32 nextMsec = mTickFraction / tickPerMsec; + + while (nextMsec < restMsec) + { + restMsec -= nextMsec; + + if (ParseNextTick(true)) + { + FinishPlayer(); + return; + } + + mTickCounter++; + + tickPerMsec = CalcTickPerMsec(); + if (tickPerMsec == 0.0f) + return; + + nextMsec = 1.0f / tickPerMsec; + } + + nextMsec -= restMsec; + mTickFraction = nextMsec * tickPerMsec; +} + +void SeqPlayer::SkipTick() +{ + for (int trackNo = 0; trackNo < TRACK_NUM_PER_PLAYER; trackNo++) + { + SeqTrack *track = GetPlayerTrack(trackNo); + + if (track) + { + track->ReleaseAllChannel(127); + track->FreeAllChannel(); + } + } + + // TODO: can combine into for loop? + int skipCount = 0; + while (mSkipTickCounter || mSkipTimeCounter * CalcTickPerMsec() >= 1.0f) + { + if (skipCount >= MAX_SKIP_TICK_PER_FRAME) + return; + + if (mSkipTickCounter) + { + mSkipTickCounter--; + } + else + { + f32 tickPerMsec = CalcTickPerMsec(); + NW4RAssert_Line(856, tickPerMsec > 0.0f); + + f32 msecPerTick = 1.0f / tickPerMsec; + + mSkipTimeCounter -= msecPerTick; + } + + if (ParseNextTick(false)) + { + FinishPlayer(); + return; + } + + skipCount++; + mTickCounter++; + } + + mSkipTimeCounter = 0.0f; +} + +Channel *SeqPlayer::NoteOn(int bankNo, NoteOnInfo const ¬eOnInfo) +{ + Channel *channel = mParserParam.callback->NoteOn(this, bankNo, noteOnInfo); + + return channel; +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_SeqSound.cpp b/src/nw4r/snd/snd_SeqSound.cpp index b6c65099..15152a92 100644 --- a/src/nw4r/snd/snd_SeqSound.cpp +++ b/src/nw4r/snd/snd_SeqSound.cpp @@ -1 +1,306 @@ -#include "nw4r/snd/snd_SeqSound.h" +#include "nw4r/snd/SeqSound.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_SeqSound.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <decomp.h> +#include <types.h> + +#include "nw4r/snd/BasicSound.h" +#include "nw4r/snd/PlayerHeap.h" +#include "nw4r/snd/SeqFile.h" +#include "nw4r/snd/SeqPlayer.h" +#include "nw4r/snd/SeqSoundHandle.h" +#include "nw4r/snd/SoundInstanceManager.h" +#include "nw4r/snd/TaskManager.h" + +#include "nw4r/ut/FileStream.h" +#include "nw4r/ut/RuntimeTypeInfo.h" + +#if 0 +#include <revolution/DVD/dvd.h> // DVD_ECANCELED +#else +#include <context_rvl.h> +#endif + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * types + */ + +// forward declarations +namespace nw4r { namespace snd { namespace detail { class NoteOnCallback; }}} +namespace nw4r { namespace snd { namespace detail { class SeqTrackAllocator; }}} + +/******************************************************************************* + * variables + */ + +namespace nw4r { namespace snd { namespace detail +{ + ut::detail::RuntimeTypeInfo const SeqSound::typeInfo(&BasicSound::typeInfo); +}}} // namespace nw4r::snd::detail + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +#pragma push + +// TODO: fake + +SeqSound::SeqSound(SoundInstanceManager<SeqSound> *manager, int priority, + int ambientPriority) : +#if 1 + BasicSound (priority, ambientPriority), + mTempSpecialHandle ( + reinterpret_cast<SeqSoundHandle *>(mPreparedFlag = mLoadingFlag = 0)), + mManager (manager), + mStartOffset (0), + mFileStream (nullptr) +#else + BasicSound (priority, ambientPriority), + mTempSpecialHandle (nullptr), + mManager (manager), + mStartOffset (0), + mLoadingFlag (false), + mPreparedFlag (false), + mFileStream (nullptr) +#endif +{ +} + +#pragma pop + +void SeqSound::InitParam() +{ + BasicSound::InitParam(); + + mStartOffset = 0; +} + +SeqPlayer::SetupResult SeqSound::Setup(SeqTrackAllocator *trackAllocator, + u32 allocTracks, + NoteOnCallback *callback) +{ + NW4RAssertPointerNonnull_Line(95, callback); + + InitParam(); + + return mSeqPlayer.Setup(trackAllocator, allocTracks, GetVoiceOutCount(), + callback); +} + +void SeqSound::Prepare(void const *seqBase, s32 seqOffset, + SeqPlayer::OffsetType startOffsetType, int startOffset) +{ + NW4RAssertPointerNonnull_Line(124, seqBase); + + mSeqPlayer.SetSeqData(seqBase, seqOffset); + + if (startOffset > 0) + Skip(startOffsetType, startOffset); + + mPreparedFlag = true; +} + +void SeqSound::Prepare(ut::FileStream *fileStream, s32 seqOffset, + SeqPlayer::OffsetType startOffsetType, int startOffset) +{ + mFileStream = fileStream; + mSeqOffset = seqOffset; + mStartOffsetType = startOffsetType; + mStartOffset = startOffset; + + mLoadingFlag = true; + + if (!LoadData(&NotifyLoadAsyncEndSeqData, this)) + Shutdown(); +} + +void SeqSound::NotifyLoadAsyncEndSeqData(bool result, void const *seqBase, + void *userData) +{ + SeqSound *sound = static_cast<SeqSound *>(userData); + NW4RAssertPointerNonnull_Line(178, sound); + + sound->mLoadingFlag = false; + + if (result == false) + { + sound->Stop(0); + return; + } + + NW4RAssertPointerNonnull_Line(189, seqBase); + sound->mSeqPlayer.SetSeqData(seqBase, sound->mSeqOffset); + + if (sound->mStartOffset > 0) + sound->mSeqPlayer.Skip(sound->mStartOffsetType, sound->mStartOffset); + + sound->mPreparedFlag = true; +} + +void SeqSound::Skip(SeqPlayer::OffsetType offsetType, int offset) +{ + mSeqPlayer.Skip(offsetType, offset); +} + +void SeqSound::Shutdown() +{ + if (mLoadingFlag) + TaskManager::GetInstance().CancelTask(&mSeqLoadTask); + + if (mFileStream) + { + mFileStream->Close(); + mFileStream = nullptr; + } + + BasicSound::Shutdown(); + mManager->Free(this); +} + +// SeqSound::SetTempoRatio ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13849) +DECOMP_FORCE(NW4RAssert_String(tempoRatio >= 0.0f)); + +void SeqSound::SetChannelPriority(int priority) +{ + // specifically not the source variant + NW4RAssertHeaderClampedLRValue_Line(266, priority, 0, 127); + + mSeqPlayer.SetChannelPriority(priority); +} + +void SeqSound::SetReleasePriorityFix(bool fix) +{ + mSeqPlayer.SetReleasePriorityFix(fix); +} + +void SeqSound::SetSeqUserprocCallback(SeqPlayer::SeqUserprocCallback *callback, + void *arg) +{ + mSeqPlayer.SetSeqUserprocCallback(callback, arg); +} + +void SeqSound::OnUpdatePlayerPriority() +{ + mManager->UpdatePriority(this, CalcCurrentPlayerPriority()); +} + +// SeqSound::SetTrackVolume ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13857) +DECOMP_FORCE(NW4RAssert_String(volume >= 0.0f)); + +// SeqSound::SetTrackPitch ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13858) +DECOMP_FORCE(NW4RAssert_String(pitch >= 0.0f)); + +// SeqSound::ReadVariable? maybe all of them? +DECOMP_FORCE(NW4RAssertPointerNonnull_String(var)); + +// SeqSound::ReadGlobalVariable? maybe both of them? +DECOMP_FORCE(NW4RAssertHeaderClampedLValue_String(varNo)); + +// SeqSound::ReadTrackVariable? maybe both of them? +DECOMP_FORCE(NW4RAssertHeaderClampedLValue_String(trackNo)); + +bool SeqSound::IsAttachedTempSpecialHandle() +{ + return mTempSpecialHandle != nullptr; +} + +void SeqSound::DetachTempSpecialHandle() +{ + mTempSpecialHandle->DetachSound(); +} + +bool SeqSound::LoadData(SeqLoadTask::Callback *callback, void *callbackArg) +{ + PlayerHeap *heap = static_cast<BasicSound *>(callbackArg)->GetPlayerHeap(); + if (!heap) + return false; + + int fileSize = mFileStream->GetSize(); + void *buffer = heap->Alloc(fileSize); + if (!buffer) + return false; + + mSeqLoadTask.mFileStream = mFileStream; + mSeqLoadTask.mBuffer = buffer; + mSeqLoadTask.mBufferSize = fileSize; + mSeqLoadTask.mCallback = callback; + mSeqLoadTask.mCallbackData = this; + + TaskManager::GetInstance().AppendTask(&mSeqLoadTask, + TaskManager::PRIORITY_MIDDLE); + + return true; +} + +SeqSound::SeqLoadTask::SeqLoadTask() : + mFileStream (nullptr), + mBuffer (nullptr), + mCallback (nullptr), + mCallbackData (nullptr) +{ +} + +void SeqSound::SeqLoadTask::Execute() +{ + mFileStream->Seek(0, ut::FileStream::SEEK_ORIGIN_SET); + + s32 readSize = mFileStream->Read(mBuffer, mBufferSize); + + mFileStream = nullptr; + + if (readSize == DVD_ECANCELED) + { + if (mCallback) + (*mCallback)(false, nullptr, mCallbackData); + + return; + } + + if (readSize != mBufferSize) + { + NW4RCheckMessage_Line(716, readSize != 0, + "failed to load sequence data\n"); + + if (mCallback) + (*mCallback)(false, nullptr, mCallbackData); + + return; + } + + SeqFile const *seq = static_cast<SeqFile const *>(mBuffer); // What + SeqFileReader reader(seq); + void const *seqBase = reader.GetBaseAddress(); + + if (mCallback) + (*mCallback)(true, seqBase, mCallbackData); +} + +void SeqSound::SeqLoadTask::Cancel() +{ + if (mCallback) + (*mCallback)(false, nullptr, mCallbackData); +} + +void SeqSound::SeqLoadTask::OnCancel() +{ + mCallback = nullptr; + + ut::FileStream *fileStream = mFileStream; + if (fileStream) + fileStream->Cancel(); +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_SeqSoundHandle.cpp b/src/nw4r/snd/snd_SeqSoundHandle.cpp index 6e8ba32b..d8b8d8f3 100644 --- a/src/nw4r/snd/snd_SeqSoundHandle.cpp +++ b/src/nw4r/snd/snd_SeqSoundHandle.cpp @@ -1 +1,34 @@ -#include "nw4r/snd/snd_SeqSoundHandle.h" +#include "nw4r/snd/SeqSoundHandle.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_SeqSoundHandle.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <types.h> // nullptr + +#include "nw4r/snd/SeqSound.h" + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { + +void SeqSoundHandle::DetachSound() +{ + if (IsAttachedSound()) + { + if (mSound->mTempSpecialHandle == this) + mSound->mTempSpecialHandle = nullptr; + } + + if (mSound) + mSound = nullptr; +} + +}} // namespace nw4r::snd diff --git a/src/nw4r/snd/snd_SeqTrack.cpp b/src/nw4r/snd/snd_SeqTrack.cpp index a38a94a6..09904026 100644 --- a/src/nw4r/snd/snd_SeqTrack.cpp +++ b/src/nw4r/snd/snd_SeqTrack.cpp @@ -1 +1,643 @@ -#include "nw4r/snd/snd_SeqTrack.h" +#include "nw4r/snd/SeqTrack.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_SeqTrack.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <decomp.h> +#include <types.h> + +#include "nw4r/snd/Channel.h" +#include "nw4r/snd/global.h" +#include "nw4r/snd/Lfo.h" +#include "nw4r/snd/MoveValue.h" +#include "nw4r/snd/NoteOnCallback.h" +#include "nw4r/snd/SeqPlayer.h" +#include "nw4r/snd/SoundThread.h" + +#include "nw4r/ut/inlines.h" + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +void SeqTrack::SetPlayerTrackNo(int playerTrackNo) +{ + // specifically not the source variant + NW4RAssertHeaderClampedLRValue_Line(38, playerTrackNo, 0, + SeqPlayer::TRACK_NUM_PER_PLAYER); + + mPlayerTrackNo = playerTrackNo; +} + +SeqTrack::SeqTrack() : + mOpenFlag (false), + mSeqPlayer (nullptr), + mChannelList (nullptr) +{ + InitParam(); +} + +SeqTrack::~SeqTrack() +{ + Close(); +} + +void SeqTrack::InitParam() +{ + mExtVolume = 1.0f; + mExtPitch = 1.0f; + mExtPan = 0.0f; + mExtSurroundPan = 0.0f; + mPanRange = 1.0f; + + mParserTrackParam.baseAddr = nullptr; + mParserTrackParam.currentAddr = nullptr; + mParserTrackParam.cmpFlag = true; + mParserTrackParam.noteWaitFlag = true; + mParserTrackParam.tieFlag = false; + mParserTrackParam.monophonicFlag = false; + mParserTrackParam.callStackDepth = 0; + mParserTrackParam.wait = 0; + mParserTrackParam.muteFlag = false; + mParserTrackParam.silenceFlag = false; + mParserTrackParam.noteFinishWait = false; + mParserTrackParam.portaFlag = false; + mParserTrackParam.damperFlag = false; + mParserTrackParam.bankNo = 0; + mParserTrackParam.prgNo = 0; + mParserTrackParam.lfoParam.Init(); + mParserTrackParam.lfoTarget = 0; + mParserTrackParam.sweepPitch = 0.0f; + mParserTrackParam.volume.InitValue(127); + mParserTrackParam.pan.InitValue(0); + mParserTrackParam.surroundPan.InitValue(0); + mParserTrackParam.volume2 = 127; + mParserTrackParam.velocityRange = 127; + mParserTrackParam.pitchBend = 0; + mParserTrackParam.bendRange = DEFAULT_BENDRANGE; + mParserTrackParam.initPan = 0; + mParserTrackParam.transpose = 0; + mParserTrackParam.priority = DEFAULT_PRIORITY; + mParserTrackParam.portaKey = DEFAULT_PORTA_KEY; + mParserTrackParam.portaTime = 0; + mParserTrackParam.attack = INVALID_ENVELOPE; + mParserTrackParam.decay = INVALID_ENVELOPE; + mParserTrackParam.sustain = INVALID_ENVELOPE; + mParserTrackParam.release = INVALID_ENVELOPE; + mParserTrackParam.envHold = INVALID_ENVELOPE; + mParserTrackParam.mainSend = 127; + + for (int i = 0; i < AUX_BUS_NUM; i++) + mParserTrackParam.fxSend[i] = 0; + + mParserTrackParam.lpfFreq = 0.0f; + mParserTrackParam.biquadType = 0; + mParserTrackParam.biquadValue = 0.0f; + + for (int varNo = 0; varNo < TRACK_VARIABLE_NUM; varNo++) + mTrackVariable[varNo] = SeqPlayer::VARIABLE_DEFAULT_VALUE; +} + +void SeqTrack::SetSeqData(void const *seqBase, s32 seqOffset) +{ + mParserTrackParam.baseAddr = static_cast<byte_t const *>(seqBase); + mParserTrackParam.currentAddr = mParserTrackParam.baseAddr + seqOffset; +} + +void SeqTrack::Open() +{ + mParserTrackParam.noteFinishWait = false; + mParserTrackParam.callStackDepth = 0; + mParserTrackParam.wait = 0; + + mOpenFlag = true; +} + +void SeqTrack::Close() +{ + SoundThread::AutoLock lock; + + ReleaseAllChannel(MUTE_RELEASE_VALUE); + FreeAllChannel(); + + mOpenFlag = false; +} + +void SeqTrack::UpdateChannelLength() +{ + SoundThread::AutoLock lock; + + if (!mOpenFlag) + return; + + for (Channel *channel = mChannelList; channel; + channel = channel->GetNextTrackChannel()) + { + if (channel->GetLength() > 0) + channel->SetLength(channel->GetLength() - 1); + + UpdateChannelRelease(channel); + + if (!channel->IsAutoUpdateSweep()) + channel->UpdateSweep(1); + } +} + +void SeqTrack::UpdateChannelRelease(Channel *channel) +{ + if (channel->GetLength() == 0 && !channel->IsRelease() + && !mParserTrackParam.damperFlag) + { + channel->NoteOff(); + } +} + +int SeqTrack::ParseNextTick(bool doNoteOn) +{ + SoundThread::AutoLock lock; + + if (!mOpenFlag) + return 0; + + mParserTrackParam.volume.Update(); + mParserTrackParam.pan.Update(); + mParserTrackParam.surroundPan.Update(); + + if (mParserTrackParam.noteFinishWait) + { + if (mChannelList) + return 1; + + mParserTrackParam.noteFinishWait = false; + } + + if (mParserTrackParam.wait > 0 && --mParserTrackParam.wait > 0) + return 1; + + if (mParserTrackParam.currentAddr) + { + while (mParserTrackParam.wait == 0 && !mParserTrackParam.noteFinishWait) + { + ParseResult result = Parse(doNoteOn); + if (result == PARSE_RESULT_FINISH) + return -1; + } + } + + return 1; +} + +void SeqTrack::StopAllChannel() +{ + SoundThread::AutoLock lock; + + for (Channel *channel = mChannelList; channel; + channel = channel->GetNextTrackChannel()) + { + Channel::FreeChannel(channel); + channel->Stop(); + } + + mChannelList = nullptr; +} + +void SeqTrack::ReleaseAllChannel(int release) +{ + SoundThread::AutoLock lock; + + UpdateChannelParam(); + + for (Channel *channel = mChannelList; channel; + channel = channel->GetNextTrackChannel()) + { + if (channel->IsActive()) + { + if (release >= 0) + { + NW4RAssertHeaderClampedLRValue_Line(329, release, 0, + MAX_ENVELOPE_VALUE); + + channel->SetRelease(static_cast<u8>(release)); + } + + channel->Release(); + } + } +} + +void SeqTrack::PauseAllChannel(bool flag) +{ + SoundThread::AutoLock lock; + + for (Channel *channel = mChannelList; channel; + channel = channel->GetNextTrackChannel()) + { + if (channel->IsActive() && flag != channel->IsPause()) + channel->Pause(flag); + } +} + +void SeqTrack::AddChannel(Channel *channel) +{ + SoundThread::AutoLock lock; + + channel->SetNextTrackChannel(mChannelList); + mChannelList = channel; +} + +void SeqTrack::UpdateChannelParam() +{ + SoundThread::AutoLock lock; + + if (!mOpenFlag) + return; + + if (!mChannelList) + return; + + f32 volume = 1.0f; + f32 parserVolume = mParserTrackParam.volume.GetValue() / 127.0f; + f32 parserVolume2 = mParserTrackParam.volume2 / 127.0f; + f32 parserMainVolume = mSeqPlayer->GetParserPlayerParam().volume / 127.0f; + + volume *= parserVolume * parserVolume; + volume *= parserVolume2 * parserVolume2; + volume *= parserMainVolume * parserMainVolume; + volume *= mExtVolume; + volume *= mSeqPlayer->GetVolume(); + + f32 pitch = + mParserTrackParam.pitchBend / 128.0f * mParserTrackParam.bendRange; + + f32 pitchRatio = 1.0f; + pitchRatio *= mSeqPlayer->GetPitch(); + pitchRatio *= mExtPitch; + + f32 pan = 0.0f; + pan += ut::Clamp(mParserTrackParam.pan.GetValue() / 63.0f, -1.0f, 1.0f); + pan *= mPanRange; + pan *= mSeqPlayer->GetPanRange(); + pan += mExtPan; + pan += mSeqPlayer->GetPan(); + + f32 surroundPan = 0.0f; + surroundPan += + ut::Clamp(mParserTrackParam.surroundPan.GetValue() / 63.0f, 0.0f, 2.0f); + surroundPan += mExtSurroundPan; + surroundPan += mSeqPlayer->GetSurroundPan(); + + f32 lpfFreq = 0.0f; + lpfFreq += mParserTrackParam.lpfFreq; + lpfFreq += mSeqPlayer->GetLpfFreq(); + + int biquadType = mParserTrackParam.biquadType; + f32 biquadValue = mParserTrackParam.biquadValue; + + if (mSeqPlayer->GetBiquadType() != 0) + { + biquadType = mSeqPlayer->GetBiquadType(); + biquadValue = mSeqPlayer->GetBiquadValue(); + } + + int remoteFilter = 0; + remoteFilter += mSeqPlayer->GetRemoteFilter(); + + f32 mainSend = 0.0f; + mainSend += mParserTrackParam.mainSend / 127.0f - 1.0f; + mainSend += mSeqPlayer->GetMainSend(); + + f32 fxSend[AUX_BUS_NUM]; + for (int i = 0; i < AUX_BUS_NUM; i++) + { + AuxBus bus = static_cast<AuxBus>(i); + fxSend[i] = 0.0f; + fxSend[i] += mParserTrackParam.fxSend[i] / 127.0f; + fxSend[i] += mSeqPlayer->GetFxSend(bus); + } + + for (Channel *channel = mChannelList; channel; + channel = channel->GetNextTrackChannel()) + { + channel->SetUserVolume(volume); + channel->SetUserPitch(pitch); + channel->SetUserPitchRatio(pitchRatio); + channel->SetUserPan(pan); + channel->SetUserSurroundPan(surroundPan); + channel->SetUserLpfFreq(lpfFreq); + channel->SetBiquadFilter(biquadType, biquadValue); + channel->SetRemoteFilter(remoteFilter); + channel->SetOutputLine(mSeqPlayer->GetOutputLine()); + channel->SetMainOutVolume(mSeqPlayer->GetMainOutVolume()); + channel->SetMainSend(mainSend); + + for (int i = 0; i < AUX_BUS_NUM; i++) + { + AuxBus bus = static_cast<AuxBus>(i); + channel->SetFxSend(bus, fxSend[i]); + } + + for (int i = 0; i < mSeqPlayer->GetVoiceOutCount(); i++) + channel->SetVoiceOutParam(i, mSeqPlayer->GetVoiceOutParam(i)); + + channel->SetLfoParam(mParserTrackParam.lfoParam); + channel->SetLfoTarget( + static_cast<Channel::LfoTarget>(mParserTrackParam.lfoTarget)); + } +} + +void SeqTrack::FreeAllChannel() +{ + SoundThread::AutoLock lock; + + for (Channel *channel = mChannelList; channel; + channel = channel->GetNextTrackChannel()) + { + Channel::FreeChannel(channel); + } + + mChannelList = nullptr; +} + +void SeqTrack::ChannelCallbackFunc(Channel *dropChannel, + Channel::ChannelCallbackStatus status, + register_t userData) +{ + SoundThread::AutoLock lock; + + SeqTrack *track = reinterpret_cast<SeqTrack *>(userData); + + NW4RAssertPointerNonnull_Line(586, dropChannel); + NW4RAssertPointerNonnull_Line(587, track); + + switch (status) + { + case Channel::CALLBACK_STATUS_STOPPED: + case Channel::CALLBACK_STATUS_FINISH: + Channel::FreeChannel(dropChannel); + break; + } + + if (track->mSeqPlayer) + track->mSeqPlayer->ChannelCallback(dropChannel); + + if (track->mChannelList == dropChannel) + { + track->mChannelList = dropChannel->GetNextTrackChannel(); + return; + } + + Channel *channel = track->mChannelList; + NW4RAssertPointerNonnull_Line(608, channel); + + for (; channel->GetNextTrackChannel(); + channel = channel->GetNextTrackChannel()) + { + if (channel->GetNextTrackChannel() == dropChannel) + { + channel->SetNextTrackChannel(dropChannel->GetNextTrackChannel()); + return; + } + } + + NW4RAssert_Line(617, false); +} + +void SeqTrack::SetMute(SeqMute mute) +{ + SoundThread::AutoLock lock; + + switch (mute) + { + case MUTE_OFF: + mParserTrackParam.muteFlag = false; + break; + + case MUTE_STOP: + StopAllChannel(); + mParserTrackParam.muteFlag = true; + break; + + case MUTE_RELEASE: + ReleaseAllChannel(-1); + FreeAllChannel(); + mParserTrackParam.muteFlag = true; + break; + + case MUTE_NO_STOP: + mParserTrackParam.muteFlag = true; + break; + } +} + +void SeqTrack::SetSilence(bool silenceFlag, int fadeTimes) +{ + SoundThread::AutoLock lock; + + mParserTrackParam.silenceFlag = silenceFlag; + + for (Channel *channel = mChannelList; channel; + channel = channel->GetNextTrackChannel()) + { + channel->SetSilence(silenceFlag, (fadeTimes + 2) / 3); + } +} + +void SeqTrack::SetVolume(f32 volume) +{ + mExtVolume = volume; +} + +void SeqTrack::SetPitch(f32 pitch) +{ + NW4RAssert_Line(668, pitch >= 0.0f); + + mExtPitch = pitch; +} + +void SeqTrack::SetPan(f32 pan) +{ + mExtPan = pan; +} + +void SeqTrack::SetSurroundPan(f32 surroundPan) +{ + mExtSurroundPan = surroundPan; +} + +void SeqTrack::SetPanRange(f32 panRange) +{ + mPanRange = panRange; +} + +void SeqTrack::SetLpfFreq(f32 lpfFreq) +{ + mParserTrackParam.lpfFreq = lpfFreq; +} + +void SeqTrack::SetBiquadFilter(int type, f32 value) +{ + mParserTrackParam.biquadType = type; + mParserTrackParam.biquadValue = value; +} + +void SeqTrack::SetModDepth(f32 modDepth) +{ + mParserTrackParam.lfoParam.depth = modDepth; +} + +void SeqTrack::SetModSpeed(f32 modSpeed) +{ + mParserTrackParam.lfoParam.speed = modSpeed; +} + +// SeqTrack::GetTrackVariable? maybe both? +DECOMP_FORCE(NW4RAssertHeaderClampedLValue_String(varNo)); + +s16 volatile *SeqTrack::GetVariablePtr(int varNo) +{ + NW4RAssertHeaderClampedLRValue_Line(754, varNo, 0, TRACK_VARIABLE_NUM); + + if (varNo < TRACK_VARIABLE_NUM) + return &mTrackVariable[varNo]; + + return nullptr; +} + +Channel *SeqTrack::NoteOn(int key, int velocity, s32 length, bool tieFlag) +{ + SoundThread::AutoLock lock; + + NW4RAssertHeaderClampedLRValue_Line(787, key, 0, 127); + NW4RAssertHeaderClampedLRValue_Line(788, velocity, 0, 127); + + SeqPlayer::ParserPlayerParam const &playerParam = + mSeqPlayer->GetParserPlayerParam(); + + Channel *channel = nullptr; + velocity = velocity * mParserTrackParam.velocityRange / 127; + + if (tieFlag) + { + channel = GetLastChannel(); + if (channel) + { + channel->SetKey(static_cast<u8>(key)); + + f32 initVolume = velocity / 127.0f; + channel->SetInitVolume(initVolume * initVolume); + } + } + + if (GetParserTrackParam().monophonicFlag) + { + channel = GetLastChannel(); + if (channel) + { + if (channel->IsRelease()) + { + channel->Stop(); + channel = nullptr; + } + else + { + channel->SetKey(static_cast<u8>(key)); + + f32 initVolume = velocity / 127.0f; + channel->SetInitVolume(initVolume * initVolume); + channel->SetLength(length); + } + } + } + + if (!channel) + { + NoteOnInfo info = + { + mParserTrackParam.prgNo, + key, + velocity, + tieFlag ? -1 : length, + mParserTrackParam.initPan, + playerParam.priority + GetParserTrackParam().priority, + mSeqPlayer->GetVoiceOutCount(), + &ChannelCallbackFunc, + reinterpret_cast<register_t>(this) + }; + + channel = mSeqPlayer->NoteOn(mParserTrackParam.bankNo, info); + if (!channel) + return nullptr; + + if (channel->GetAlternateAssignId() > 0) + { + for (Channel *itr = mChannelList; itr; + itr = itr->GetNextTrackChannel()) + { + if (itr->GetAlternateAssignId() + == channel->GetAlternateAssignId()) + { + itr->Release(); + } + } + } + + AddChannel(channel); + } + + if (mParserTrackParam.attack <= MAX_ENVELOPE_VALUE) + channel->SetAttack(mParserTrackParam.attack); + + if (mParserTrackParam.decay <= MAX_ENVELOPE_VALUE) + channel->SetDecay(mParserTrackParam.decay); + + if (mParserTrackParam.sustain <= MAX_ENVELOPE_VALUE) + channel->SetSustain(mParserTrackParam.sustain); + + if (mParserTrackParam.release <= MAX_ENVELOPE_VALUE) + channel->SetRelease(mParserTrackParam.release); + + if (mParserTrackParam.envHold <= MAX_ENVELOPE_VALUE) + channel->SetHold(mParserTrackParam.envHold); + + f32 sweepPitch = mParserTrackParam.sweepPitch; + if (mParserTrackParam.portaFlag) + sweepPitch += mParserTrackParam.portaKey - key; + + if (!mParserTrackParam.portaTime) + { + NW4RCheckMessage_Line(905, length != 0, "portatime zero is invalid."); + + channel->SetSweepParam(sweepPitch, length, false); + } + else + { + int length = mParserTrackParam.portaTime; + length *= length; + length *= sweepPitch >= 0.0f ? sweepPitch : -sweepPitch; + length >>= 5; + length *= 5; + + channel->SetSweepParam(sweepPitch, length, true); + } + + mParserTrackParam.portaKey = key; + + channel->SetSilence(mParserTrackParam.silenceFlag != false, 0); + channel->SetReleasePriorityFix(mSeqPlayer->IsReleasePriorityFix()); + channel->SetPanMode(mSeqPlayer->GetPanMode()); + channel->SetPanCurve(mSeqPlayer->GetPanCurve()); + + return channel; +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_SeqTrackAllocator.cpp b/src/nw4r/snd/snd_SeqTrackAllocator.cpp new file mode 100644 index 00000000..2a924f1d --- /dev/null +++ b/src/nw4r/snd/snd_SeqTrackAllocator.cpp @@ -0,0 +1 @@ +/* This file intentionally left blank. */ diff --git a/src/nw4r/snd/snd_SoundActor.cpp b/src/nw4r/snd/snd_SoundActor.cpp index c65cd722..04bbfb04 100644 --- a/src/nw4r/snd/snd_SoundActor.cpp +++ b/src/nw4r/snd/snd_SoundActor.cpp @@ -1 +1,16 @@ -#include "nw4r/snd/snd_SoundActor.h" +/* Only implemented to the extent necessary to match early instantiations of + * inline functions and data sections. + */ + +#include "nw4r/snd/SoundActor.h" + +#include <types.h> + +#include "nw4r/snd/SoundArchivePlayer.h" + +nw4r::snd::SoundActor::SoundActor() : + mSoundArchivePlayer (*new SoundArchivePlayer) +{ + // DECOMP_FORCE_CLASS_METHOD + (void)mSoundArchivePlayer.detail_ConvertLabelStringToSoundId(nullptr); +} diff --git a/src/nw4r/snd/snd_SoundArchive.cpp b/src/nw4r/snd/snd_SoundArchive.cpp index 988abab9..8d3b5cc3 100644 --- a/src/nw4r/snd/snd_SoundArchive.cpp +++ b/src/nw4r/snd/snd_SoundArchive.cpp @@ -1 +1,229 @@ -#include "nw4r/snd/snd_SoundArchive.h" +#include "nw4r/snd/SoundArchive.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_SoundArchive.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <cstring> + +#include <types.h> + +#include "nw4r/snd/SoundArchiveFile.h" // detail::SoundArchiveFileReader + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * types + */ + +// forward declarations +namespace nw4r { namespace ut { class FileStream; }} + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { + +SoundArchive::SoundArchive() : + mFileReader (nullptr) +{ + mExtFileRoot[0] = '/'; + mExtFileRoot[1] = '\0'; +} + +SoundArchive::~SoundArchive() {} + +bool SoundArchive::IsAvailable() const +{ + if (!mFileReader) + return false; + + return true; +} + +void SoundArchive::Setup(detail::SoundArchiveFileReader *fileReader) +{ + NW4RAssertPointerNonnull_Line(70, fileReader); + + mFileReader = fileReader; +} + +void SoundArchive::Shutdown() +{ + mFileReader = nullptr; + + mExtFileRoot[0] = '/'; + mExtFileRoot[1] = '\0'; +} + +u32 SoundArchive::GetPlayerCount() const +{ + return mFileReader->GetPlayerCount(); +} + +u32 SoundArchive::GetGroupCount() const +{ + return mFileReader->GetGroupCount(); +} + +u32 SoundArchive::ConvertLabelStringToSoundId(char const *label) const +{ + return mFileReader->ConvertLabelStringToSoundId(label); +} + +SoundArchive::SoundType SoundArchive::GetSoundType(u32 soundId) const +{ + return mFileReader->GetSoundType(soundId); +} + +bool SoundArchive::ReadSoundInfo(u32 soundId, SoundInfo *info) const +{ + return mFileReader->ReadSoundInfo(soundId, info); +} + +bool SoundArchive::ReadSeqSoundInfo(u32 soundId, SeqSoundInfo *info) const +{ + return mFileReader->ReadSeqSoundInfo(soundId, info); +} + +bool SoundArchive::detail_ReadStrmSoundInfo(u32 soundId, + StrmSoundInfo *info) const +{ + return mFileReader->ReadStrmSoundInfo(soundId, info); +} + +bool SoundArchive::detail_ReadWaveSoundInfo(u32 soundId, + WaveSoundInfo *info) const +{ + return mFileReader->ReadWaveSoundInfo(soundId, info); +} + +bool SoundArchive::ReadPlayerInfo(u32 playerId, PlayerInfo *info) const +{ + return mFileReader->ReadPlayerInfo(playerId, info); +} + +bool SoundArchive::ReadSoundArchivePlayerInfo( + SoundArchivePlayerInfo *info) const +{ + return mFileReader->ReadSoundArchivePlayerInfo(info); +} + +bool SoundArchive::ReadBankInfo(u32 bankId, BankInfo *info) const +{ + return mFileReader->ReadBankInfo(bankId, info); +} + +bool SoundArchive::detail_ReadGroupInfo(u32 groupId, GroupInfo *info) const +{ + return mFileReader->ReadGroupInfo(groupId, info); +} + +bool SoundArchive::detail_ReadGroupItemInfo(u32 groupId, u32 index, + GroupItemInfo *info) const +{ + return mFileReader->ReadGroupItemInfo(groupId, index, info); +} + +u32 SoundArchive::detail_GetFileCount() const +{ + return mFileReader->GetFileCount(); +} + +bool SoundArchive::detail_ReadFileInfo(u32 fileId, FileInfo *info) const +{ + return mFileReader->ReadFileInfo(fileId, info); +} + +bool SoundArchive::detail_ReadFilePos(u32 fileId, u32 index, + FilePos *info) const +{ + return mFileReader->ReadFilePos(fileId, index, info); +} + +ut::FileStream *SoundArchive::detail_OpenFileStream(u32 fileId, void *buffer, + int size) const +{ + FileInfo fileInfo; + if (!detail_ReadFileInfo(fileId, &fileInfo)) + return nullptr; + + if (fileInfo.extFilePath) + { + ut::FileStream *stream = OpenExtStreamImpl( + buffer, size, fileInfo.extFilePath, 0, fileInfo.fileSize); + + return stream; + } + + FilePos filePos; + if (!detail_ReadFilePos(fileId, 0, &filePos)) + return nullptr; + + GroupInfo groupInfo; + if (!detail_ReadGroupInfo(filePos.groupId, &groupInfo)) + return nullptr; + + GroupItemInfo itemInfo; + if (!detail_ReadGroupItemInfo(filePos.groupId, filePos.index, &itemInfo)) + return nullptr; + + u32 itemOffset = groupInfo.offset + itemInfo.offset; + u32 itemSize = itemInfo.size; + + if (groupInfo.extFilePath) + { + ut::FileStream *stream = OpenExtStreamImpl( + buffer, size, groupInfo.extFilePath, itemOffset, itemSize); + + return stream; + } + else + { + ut::FileStream *stream = OpenStream(buffer, size, itemOffset, itemSize); + + return stream; + } +} + +ut::FileStream *SoundArchive::OpenExtStreamImpl(void *buffer, int size, + char const *extFilePath, + u32 begin, u32 length) const +{ + char const *fullPath; + char pathBuffer[FILE_PATH_MAX + 1]; + + if (extFilePath[0] == '/') + { + // absolute path + fullPath = extFilePath; + } + else + { + u32 fileLen = std::strlen(extFilePath); + u32 dirLen = std::strlen(mExtFileRoot); + + if (fileLen + dirLen >= FILE_PATH_MAX + 1) + { + NW4RWarningMessage_Line(349, "Too long file path \"%s/%s\"", + mExtFileRoot, extFilePath); + + return nullptr; + } + + std::strncpy(pathBuffer, mExtFileRoot, dirLen + 1); + std::strncat(pathBuffer, extFilePath, fileLen + 1); + + fullPath = pathBuffer; + } + + return OpenExtStream(buffer, size, fullPath, begin, length); +} + +}} // namespace nw4r::snd diff --git a/src/nw4r/snd/snd_SoundArchiveFile.cpp b/src/nw4r/snd/snd_SoundArchiveFile.cpp index d51d58fa..8c244dd0 100644 --- a/src/nw4r/snd/snd_SoundArchiveFile.cpp +++ b/src/nw4r/snd/snd_SoundArchiveFile.cpp @@ -1 +1,677 @@ -#include "nw4r/snd/snd_SoundArchiveFile.h" +#include "nw4r/snd/SoundArchiveFile.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_SoundArchiveFile.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <cstring> + +#include <decomp.h> +#include <macros.h> +#include <types.h> + +#include "nw4r/snd/global.h" +#include "nw4r/snd/SoundArchive.h" +#include "nw4r/snd/Util.h" + +#include "nw4r/ut/binaryFileFormat.h" // ut::BinaryFileHeader +#include "nw4r/ut/inlines.h" // ut::AddOffsetToPtr + +#include <nw4r/NW4RAssert.hpp> + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +SoundArchiveFileReader::SoundArchiveFileReader() : + mInfo (nullptr), + mStringBase (nullptr), + mStringTable (nullptr), + mStringTreeSound (nullptr), + mStringTreePlayer (nullptr), + mStringTreeGroup (nullptr), + mStringTreeBank (nullptr) +{ +} + +void SoundArchiveFileReader::Init(void const *soundArchiveData) +{ + NW4RAssertPointerNonnull_Line(50, soundArchiveData); + + if (IsValidFileHeader(soundArchiveData)) + { + mHeader = *static_cast<SoundArchiveFile::Header const *>( + soundArchiveData); + } +} + +bool SoundArchiveFileReader::IsValidFileHeader(void const *soundArchiveData) +{ + ut::BinaryFileHeader const *fileHeader = + static_cast<ut::BinaryFileHeader const *>(soundArchiveData); + + NW4RAssertMessage_Line( + 75, fileHeader->signature == SoundArchiveFile::SIGNATURE_FILE, + "invalid file signature. sound archive data is not available."); + + if (fileHeader->signature != SoundArchiveFile::SIGNATURE_FILE) + return false; + + NW4RAssertMessage_Line( + 83, fileHeader->version >= NW4R_FILE_VERSION(1, 0), + "sound archive file is not supported version.\n" + " please reconvert file using new version tools.\n"); + + if (fileHeader->version < NW4R_FILE_VERSION(1, 0)) + return false; + + NW4RAssertMessage_Line( + 89, fileHeader->version <= SoundArchiveFile::FILE_VERSION, + "sound archive file is not supported version.\n" + " please reconvert file using new version tools.\n"); + + if (fileHeader->version > SoundArchiveFile::FILE_VERSION) + return false; + + return true; +} + +void SoundArchiveFileReader::SetStringChunk(void const *stringChunk, + u32 stringChunkSize ATTR_UNUSED) +{ + NW4RAssertPointerNonnull_Line(99, stringChunk); + + SoundArchiveFile::SymbolBlock const *symbolBlock = + static_cast<SoundArchiveFile::SymbolBlock const *>(stringChunk); + NW4RAssert_Line(102, symbolBlock->blockHeader.kind + == SoundArchiveFile::SIGNATURE_SYMB_BLOCK); + + SoundArchiveFile::StringBlock const *stringBlock = + &symbolBlock->stringBlock; + + mStringBase = stringBlock; + + mStringTable = static_cast<SoundArchiveFile::StringTable const *>( + GetPtrConst(mStringBase, stringBlock->stringChunk.tableOffset)); + + mStringTreeSound = static_cast<SoundArchiveFile::StringTree const *>( + GetPtrConst(mStringBase, stringBlock->stringChunk.soundTreeOffset)); + + mStringTreePlayer = static_cast<SoundArchiveFile::StringTree const *>( + GetPtrConst(mStringBase, stringBlock->stringChunk.playerTreeOffset)); + + mStringTreeGroup = static_cast<SoundArchiveFile::StringTree const *>( + GetPtrConst(mStringBase, stringBlock->stringChunk.groupTreeOffset)); + + mStringTreeBank = static_cast<SoundArchiveFile::StringTree const *>( + GetPtrConst(mStringBase, stringBlock->stringChunk.bankTreeOffset)); +} + +void SoundArchiveFileReader::SetInfoChunk(void const *infoChunk, + u32 infoChunkSize ATTR_UNUSED) +{ + NW4RAssertPointerNonnull_Line(123, infoChunk); + + SoundArchiveFile::InfoBlock const *infoBlock = + static_cast<SoundArchiveFile::InfoBlock const *>(infoChunk); + + NW4RAssert_Line(126, infoBlock->blockHeader.kind + == SoundArchiveFile::SIGNATURE_INFO_BLOCK); + + mInfo = &infoBlock->info; +} + +SoundArchive::SoundType SoundArchiveFileReader::GetSoundType(u32 soundId) const +{ + SoundArchiveFile::SoundType soundType; + + SoundArchiveFile::SoundCommonInfoTable const *table = + Util::GetDataRefAddress0(mInfo->soundTableRef, mInfo); + + if (!table) + return SoundArchive::SOUND_TYPE_INVALID; + + if (soundId >= table->count) + return SoundArchive::SOUND_TYPE_INVALID; + + if (GetVersion() >= NW4R_FILE_VERSION(1, 1)) + { + SoundArchiveFile::SoundCommonInfo const *soundCommonInfo = + Util::GetDataRefAddress0(table->item[soundId], mInfo); + + if (!soundCommonInfo) + return SoundArchive::SOUND_TYPE_INVALID; + + soundType = static_cast<SoundArchiveFile::SoundType>( + soundCommonInfo->soundType); + } + else + { + soundType = static_cast<SoundArchiveFile::SoundType>( + table->item[soundId].dataType); + } + + switch (soundType) + { + case SoundArchiveFile::SOUND_TYPE_SEQ: + return SoundArchive::SOUND_TYPE_SEQ; + + case SoundArchiveFile::SOUND_TYPE_STRM: + return SoundArchive::SOUND_TYPE_STRM; + + case SoundArchiveFile::SOUND_TYPE_WAVE: + return SoundArchive::SOUND_TYPE_WAVE; + + default: + return SoundArchive::SOUND_TYPE_INVALID; + } +} + +bool SoundArchiveFileReader::ReadSoundInfo(u32 soundId, + SoundArchive::SoundInfo *info) const +{ + SoundArchiveFile::SoundCommonInfo const *src = impl_GetSoundInfo(soundId); + if (!src) + return false; + + info->fileId = src->fileId; + info->playerId = src->playerId; + info->actorPlayerId = src->actorPlayerId; + info->playerPriority = src->playerPriority; + info->volume = src->volume; + info->remoteFilter = src->remoteFilter; + + if (GetVersion() >= NW4R_FILE_VERSION(1, 2)) + { + info->panMode = static_cast<PanMode>(src->panMode); + info->panCurve = static_cast<PanCurve>(src->panCurve); + } + else + { + info->panMode = PAN_MODE_BALANCE; + info->panCurve = PAN_CURVE_SQRT; + } + + return true; +} + +/* SoundArchiveFileReader::ReadSound3DParam + * ([R89JEL]:/bin/RVL/Debug/mainD.MAP:14110) + */ +DECOMP_FORCE(Util::GetDataRefAddress0( + *(Util::DataRef<SoundArchiveFile::Sound3DParam> *)(nullptr), nullptr)); + +bool SoundArchiveFileReader::ReadSeqSoundInfo( + u32 soundId, SoundArchive::SeqSoundInfo *info) const +{ + SoundArchiveFile::SeqSoundInfo const *src = impl_GetSeqSoundInfo(soundId); + if (!src) + return false; + + info->dataOffset = src->dataOffset; + info->bankId = src->bankId; + info->channelPriority = src->channelPriority; + info->allocTrack = src->allocTrack; + + if (GetVersion() >= NW4R_FILE_VERSION(1, 3)) + info->releasePriorityFixFlag = src->releasePriorityFix; + else + info->releasePriorityFixFlag = false; + + return true; +} + +bool SoundArchiveFileReader::ReadStrmSoundInfo( + u32 soundId, SoundArchive::StrmSoundInfo *info) const +{ + SoundArchiveFile::StrmSoundInfo const *src = impl_GetStrmSoundInfo(soundId); + if (!src) + return false; + + info->startPosition = src->startPosition; + info->allocTrackFlag = src->allocTrackFlag; + + if (GetVersion() >= NW4R_FILE_VERSION(1, 4)) + { + info->allocChannelCount = src->allocChannelCount; + } + else + { + info->allocChannelCount = 0; + + // is this meant to be src->allocTrackFlag? + for (byte2_t bitflag = src->allocChannelCount; bitflag; bitflag >>= 1) + { + if (bitflag & 1) + info->allocChannelCount++; + else if (bitflag) + return false; + } + } + + return true; +} + +bool SoundArchiveFileReader::ReadWaveSoundInfo( + u32 soundId, SoundArchive::WaveSoundInfo *info) const +{ + SoundArchiveFile::WaveSoundInfo const *src = impl_GetWaveSoundInfo(soundId); + if (!src) + return false; + + info->subNo = src->subNo; + info->channelPriority = src->channelPriority; + + if (GetVersion() >= NW4R_FILE_VERSION(1, 3)) + info->releasePriorityFixFlag = src->releasePriorityFix; + else + info->releasePriorityFixFlag = false; + + return true; +} + +bool SoundArchiveFileReader::ReadBankInfo(u32 bankId, + SoundArchive::BankInfo *info) const +{ + SoundArchiveFile::BankInfo const *src = impl_GetBankInfo(bankId); + if (!src) + return false; + + info->fileId = src->fileId; + + return true; +} + +bool SoundArchiveFileReader::ReadPlayerInfo( + u32 playerId, SoundArchive::PlayerInfo *info) const +{ + SoundArchiveFile::PlayerInfo const *src = impl_GetPlayerInfo(playerId); + if (!src) + return false; + + info->playableSoundCount = src->playableSoundCount; + info->heapSize = src->heapSize; + + return true; +} + +bool SoundArchiveFileReader::ReadGroupInfo(u32 groupId, + SoundArchive::GroupInfo *info) const +{ + SoundArchiveFile::GroupInfo const *src = impl_GetGroupInfo(groupId); + if (!src) + return false; + + SoundArchiveFile::GroupItemInfoTable const *itemTable = + Util::GetDataRefAddress0(src->itemTableRef, mInfo); + if (!itemTable) + return false; + + info->extFilePath = + Util::GetDataRefAddress0(src->extFilePathRef, mInfo); + info->offset = src->offset; + info->size = src->size; + info->waveDataOffset = src->waveDataOffset; + info->waveDataSize = src->waveDataSize; + info->itemCount = itemTable->count; + + return true; +} + +bool SoundArchiveFileReader::ReadGroupItemInfo( + u32 groupId, u32 index, SoundArchive::GroupItemInfo *info) const +{ + SoundArchiveFile::GroupInfo const *groupInfo = impl_GetGroupInfo(groupId); + if (!groupInfo) + return false; + + SoundArchiveFile::GroupItemInfoTable const *itemTable = + Util::GetDataRefAddress0(groupInfo->itemTableRef, mInfo); + if (!itemTable) + return false; + + if (index >= itemTable->count) + return false; + + SoundArchiveFile::GroupItemInfo const *src = + Util::GetDataRefAddress0(itemTable->item[index], mInfo); + if (!src) + return false; + + info->fileId = src->fileId; + info->offset = src->offset; + info->size = src->size; + info->waveDataOffset = src->waveDataOffset; + info->waveDataSize = src->waveDataSize; + + return true; +} + +bool SoundArchiveFileReader::ReadSoundArchivePlayerInfo( + SoundArchive::SoundArchivePlayerInfo *info) const +{ + SoundArchiveFile::SoundArchivePlayerInfo const *src = + Util::GetDataRefAddress0(mInfo->soundArchivePlayerInfoRef, mInfo); + + // ERRATUM: checks info instead of src like the other functions + if (!info) + return false; + + info->seqSoundCount = src->seqSoundCount; + info->seqTrackCount = src->seqTrackCount; + info->strmSoundCount = src->strmSoundCount; + info->strmTrackCount = src->strmTrackCount; + info->strmChannelCount = src->strmChannelCount; + info->waveSoundCount = src->waveSoundCount; + info->waveTrackCount = src->waveTrackCount; + + return true; +} + +u32 SoundArchiveFileReader::GetPlayerCount() const +{ + SoundArchiveFile::PlayerInfoTable const *table = + Util::GetDataRefAddress0(mInfo->playerTableRef, mInfo); + if (!table) + return false; + + return table->count; +} + +u32 SoundArchiveFileReader::GetGroupCount() const +{ + SoundArchiveFile::GroupInfoTable const *table = + Util::GetDataRefAddress0(mInfo->groupTableRef, mInfo); + if (!table) + return false; + + // TODO: why - 1? + return table->count - 1; +} + +DECOMP_FORCE(Util::GetDataRefAddress0( + *(Util::DataRef<SoundArchiveFile::BankInfoTable> *)(nullptr), nullptr)); + +u32 SoundArchiveFileReader::GetFileCount() const +{ + SoundArchiveFile::FileInfoTable const *table = + Util::GetDataRefAddress0(mInfo->fileTableRef, mInfo); + if (!table) + return false; + + return table->count; +} + +bool SoundArchiveFileReader::ReadFileInfo(u32 fileId, + SoundArchive::FileInfo *info) const +{ + SoundArchiveFile::FileInfoTable const *table = + Util::GetDataRefAddress0(mInfo->fileTableRef, mInfo); + if (!table) + return false; + + if (fileId >= table->count) + return false; + + SoundArchiveFile::FileInfo const *fileInfo = + Util::GetDataRefAddress0(table->item[fileId], mInfo); + if (!fileInfo) + return false; + + SoundArchiveFile::FilePosTable const *filePosTable = + Util::GetDataRefAddress0(fileInfo->filePosTableRef, mInfo); + if (!filePosTable) + return false; + + info->fileSize = fileInfo->fileSize; + info->waveDataFileSize = fileInfo->waveDataFileSize; + info->extFilePath = + Util::GetDataRefAddress0(fileInfo->extFilePathRef, mInfo); + info->filePosCount = filePosTable->count; + + return true; +} + +bool SoundArchiveFileReader::ReadFilePos(u32 fileId, u32 index, + SoundArchive::FilePos *filePos) const +{ + SoundArchiveFile::FileInfoTable const *table = + Util::GetDataRefAddress0(mInfo->fileTableRef, mInfo); + if (!table) + return false; + + if (fileId >= table->count) + return false; + + SoundArchiveFile::FileInfo const *fileInfo = + Util::GetDataRefAddress0(table->item[fileId], mInfo); + if (!fileInfo) + return false; + + SoundArchiveFile::FilePosTable const *filePosTable = + Util::GetDataRefAddress0(fileInfo->filePosTableRef, mInfo); + if (!filePosTable) + return false; + + if (index >= filePosTable->count) + return false; + + SoundArchive::FilePos const *pos = + Util::GetDataRefAddress0(filePosTable->item[index], mInfo); + if (!pos) + return false; + + *filePos = *pos; + + return true; +} + +char const *SoundArchiveFileReader::GetString(u32 id) const +{ + if (id == SoundArchive::INVALID_ID) + return nullptr; + + if (!mStringTable) + return nullptr; + + NW4RAssert_Line(508, id < mStringTable->offsetTable.count); + + char const *str = static_cast<char const *>( + GetPtrConst(mStringBase, mStringTable->offsetTable.item[id])); + + return str; +} + +u32 SoundArchiveFileReader::ConvertLabelStringToId( + SoundArchiveFile::StringTree const *tree, char const *str) const +{ + if (!tree) + return SoundArchive::INVALID_ID; + + if (tree->rootIdx >= tree->nodeTable.count) + return SoundArchive::INVALID_ID; + + SoundArchiveFile::StringTreeNode const *node = + &tree->nodeTable.item[tree->rootIdx]; + + u32 strlen = std::strlen(str); + + while (!(node->flags & 1)) + { + int pos = node->bit >> 3; + int bit = node->bit & 7; + + u32 nodeIdx; + if (pos < static_cast<int>(strlen) && (1 << (7 - bit)) & str[pos]) + nodeIdx = node->rightIdx; + else + nodeIdx = node->leftIdx; + + node = &tree->nodeTable.item[nodeIdx]; + } + + char const *str_cmp = GetString(node->strIdx); + + if (std::strcmp(str, str_cmp) == 0) + return node->id; + else + return SoundArchive::INVALID_ID; +} + +SoundArchiveFile::SoundCommonInfo const * +SoundArchiveFileReader::impl_GetSoundInfo(u32 soundId) const +{ + SoundArchiveFile::SoundCommonInfoTable const *table = + Util::GetDataRefAddress0(mInfo->soundTableRef, mInfo); + + if (!table) + return nullptr; + + if (soundId >= table->count) + return nullptr; + + if (GetVersion() >= NW4R_FILE_VERSION(1, 1)) + { + return Util::GetDataRefAddress0(table->item[soundId], mInfo); + } + else + { + return static_cast<SoundArchiveFile::SoundCommonInfo const *>( + ut::AddOffsetToPtr(mInfo, table->item[soundId].value)); + } +} + +bool SoundArchiveFileReader::impl_GetSoundInfoOffset( + u32 soundId, SoundArchiveFile::SoundInfoRef *soundInfoRef) const +{ + SoundArchiveFile::SoundCommonInfoTable const *table = + Util::GetDataRefAddress0(mInfo->soundTableRef, mInfo); + + if (!table) + return false; + + if (soundId >= table->count) + return false; + + if (GetVersion() >= NW4R_FILE_VERSION(1, 1)) + { + SoundArchiveFile::SoundCommonInfo const *soundCommonInfo = + Util::GetDataRefAddress0(table->item[soundId], mInfo); + + if (!soundCommonInfo) + return false; + + *soundInfoRef = soundCommonInfo->soundInfoRef; + + return true; + } + else + { + SoundArchiveFile::SoundInfoRef ref; + + // TODO (from ogws): Why is 28 added to value? + ref.refType = table->item[soundId].refType; + ref.dataType = table->item[soundId].dataType; + ref.value = table->item[soundId].value + 28; + + *soundInfoRef = ref; + + return true; + } +} + +SoundArchiveFile::SeqSoundInfo const * +SoundArchiveFileReader::impl_GetSeqSoundInfo(u32 soundId) const +{ + if (GetSoundType(soundId) != SoundArchive::SOUND_TYPE_SEQ) + return nullptr; + + SoundArchiveFile::SoundInfoRef dataRef; + bool result = impl_GetSoundInfoOffset(soundId, &dataRef); + if (!result) + return nullptr; + + return Util::GetDataRefAddress1(dataRef, mInfo); +} + +SoundArchiveFile::StrmSoundInfo const * +SoundArchiveFileReader::impl_GetStrmSoundInfo(u32 soundId) const +{ + if (GetSoundType(soundId) != SoundArchive::SOUND_TYPE_STRM) + return nullptr; + + SoundArchiveFile::SoundInfoRef dataRef; + bool result = impl_GetSoundInfoOffset(soundId, &dataRef); + if (!result) + return nullptr; + + return Util::GetDataRefAddress2(dataRef, mInfo); +} + +SoundArchiveFile::WaveSoundInfo const * +SoundArchiveFileReader::impl_GetWaveSoundInfo(u32 soundId) const +{ + if (GetSoundType(soundId) != SoundArchive::SOUND_TYPE_WAVE) + return nullptr; + + SoundArchiveFile::SoundInfoRef dataRef; + bool result = impl_GetSoundInfoOffset(soundId, &dataRef); + if (!result) + return nullptr; + + return Util::GetDataRefAddress3(dataRef, mInfo); +} + +SoundArchiveFile::BankInfo const *SoundArchiveFileReader::impl_GetBankInfo( + u32 bankId) const +{ + SoundArchiveFile::BankInfoTable const *table = + Util::GetDataRefAddress0(mInfo->bankTableRef, mInfo); + + if (!table) + return nullptr; + + if (bankId >= table->count) + return nullptr; + + return Util::GetDataRefAddress0(table->item[bankId], mInfo); +} + +SoundArchiveFile::PlayerInfo const *SoundArchiveFileReader::impl_GetPlayerInfo( + u32 playerId) const +{ + SoundArchiveFile::PlayerInfoTable const *table = + Util::GetDataRefAddress0(mInfo->playerTableRef, mInfo); + + if (!table) + return nullptr; + + if (playerId >= table->count) + return nullptr; + + return Util::GetDataRefAddress0(table->item[playerId], mInfo); +} + +SoundArchiveFile::GroupInfo const *SoundArchiveFileReader::impl_GetGroupInfo( + u32 groupId) const +{ + SoundArchiveFile::GroupInfoTable const *table = + Util::GetDataRefAddress0(mInfo->groupTableRef, mInfo); + + if (!table) + return nullptr; + + if (groupId >= table->count) + return nullptr; + + return Util::GetDataRefAddress0(table->item[groupId], mInfo); +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_SoundArchivePlayer.cpp b/src/nw4r/snd/snd_SoundArchivePlayer.cpp index 4b894881..6d0e8eae 100644 --- a/src/nw4r/snd/snd_SoundArchivePlayer.cpp +++ b/src/nw4r/snd/snd_SoundArchivePlayer.cpp @@ -1 +1,1449 @@ -#include "nw4r/snd/snd_SoundArchivePlayer.h" +#include "nw4r/snd/SoundArchivePlayer.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_SoundArchivePlayer.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <new> + +#include <decomp.h> +#include <macros.h> +#include <types.h> + +#include "nw4r/snd/Bank.h" +#include "nw4r/snd/BasicSound.h" +#include "nw4r/snd/debug.h" +#include "nw4r/snd/DisposeCallbackManager.h" +#include "nw4r/snd/ExternalSoundPlayer.h" +#include "nw4r/snd/MmlParser.h" +#include "nw4r/snd/MmlSeqTrack.h" +#include "nw4r/snd/MmlSeqTrackAllocator.h" +#include "nw4r/snd/PlayerHeap.h" +#include "nw4r/snd/SeqFile.h" +#include "nw4r/snd/SeqPlayer.h" +#include "nw4r/snd/SeqSound.h" +#include "nw4r/snd/SoundActor.h" +#include "nw4r/snd/SoundArchive.h" +#include "nw4r/snd/SoundHandle.h" +#include "nw4r/snd/SoundInstanceManager.h" +#include "nw4r/snd/SoundPlayer.h" +#include "nw4r/snd/SoundStartable.h" +#include "nw4r/snd/SoundSystem.h" +#include "nw4r/snd/SoundThread.h" +#include "nw4r/snd/StrmChannel.h" +#include "nw4r/snd/StrmPlayer.h" +#include "nw4r/snd/StrmSound.h" +#include "nw4r/snd/WaveFile.h" +#include "nw4r/snd/WaveSound.h" +#include "nw4r/snd/WsdFile.h" +#include "nw4r/snd/WsdPlayer.h" + +#include "nw4r/ut/FileStream.h" +#include "nw4r/ut/inlines.h" + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { + +SoundArchivePlayer::SoundArchivePlayer() : + mSoundArchive (nullptr), + mGroupTable (nullptr), + mFileTable (nullptr), + mFileManager (nullptr), + mSeqCallback (*this), + mWsdCallback (*this), + mSeqUserprocCallback (nullptr), + mSeqUserprocCallbackArg (nullptr), + mSoundPlayerCount (0), + mSoundPlayers (nullptr), + mMmlSeqTrackAllocator (&mMmlParser), + mSetupBufferAddress (nullptr), + mSetupBufferSize (0) +{ + detail::DisposeCallbackManager::GetInstance().RegisterDisposeCallback(this); +} + +SoundArchivePlayer::~SoundArchivePlayer() +{ + detail::DisposeCallbackManager::GetInstance().UnregisterDisposeCallback(this); +} + +bool SoundArchivePlayer::IsAvailable() const +{ + if (mSoundArchive == nullptr) + return false; + + return mSoundArchive->IsAvailable(); +} + +bool SoundArchivePlayer::Setup(SoundArchive const *arc, void *buffer, u32 size, + void *strmBuffer, u32 strmBufferSize) +{ + NW4RAssert_Line(131, SoundSystem::IsInitializedSoundSystem()); + if (!SoundSystem::IsInitializedSoundSystem()) + return false; + + NW4RAssertPointerNonnull_Line(137, arc); + NW4RAssertPointerNonnull_Line(138, buffer); + if (strmBufferSize) + NW4RAssertPointerNonnull_Line(140, strmBuffer); + + NW4RAssert_Line(142, strmBufferSize >= GetRequiredStrmBufferSize( arc )); + + if (!SetupMram(arc, buffer, size)) + return false; + + if (!SetupStrmBuffer(arc, strmBuffer, strmBufferSize)) + return false; + + mSeqTrackAllocator = &mMmlSeqTrackAllocator; + + return true; +} + +void SoundArchivePlayer::Shutdown() +{ + mSoundArchive = nullptr; + mGroupTable = nullptr; + mFileTable = nullptr; + mFileManager = nullptr; + mSeqTrackAllocator = nullptr; + + for (u32 playerId = 0; playerId < mSoundPlayerCount; playerId++) + mSoundPlayers[playerId].~SoundPlayer(); + + mSoundPlayerCount = 0; + mSoundPlayers = nullptr; + + mStrmBufferPool.Shutdown(); + + if (mSetupBufferAddress) + { + mSeqSoundInstanceManager.Destroy(mSetupBufferAddress, mSetupBufferSize); + mStrmSoundInstanceManager.Destroy(mSetupBufferAddress, + mSetupBufferSize); + mWaveSoundInstanceManager.Destroy(mSetupBufferAddress, + mSetupBufferSize); + mMmlSeqTrackAllocator.Destroy(mSetupBufferAddress, mSetupBufferSize); + + mSetupBufferAddress = nullptr; + mSetupBufferSize = 0; + } +} + +u32 SoundArchivePlayer::GetRequiredMemSize(SoundArchive const *arc) +{ + NW4RAssertPointerNonnull_Line(210, arc); + + u32 size = 0; + + u32 playerCount = arc->GetPlayerCount(); + size += ut::RoundUp(sizeof(SoundPlayer) * playerCount, 4); + + for (u32 playerId = 0; playerId < playerCount; playerId++) + { + SoundArchive::PlayerInfo playerInfo; + if (!arc->ReadPlayerInfo(playerId, &playerInfo)) + continue; + + for (int i = 0; i < playerInfo.playableSoundCount; i++) + { + if (playerInfo.heapSize == 0) + continue; + + size += ut::RoundUp(sizeof(detail::PlayerHeap), 4); + size = ut::RoundUp(size, 32); + size += ut::RoundUp(playerInfo.heapSize, 4); + } + } + + size += ut::RoundUp(sizeof(u32) + arc->GetGroupCount() * 8, 4); + + SoundArchive::SoundArchivePlayerInfo soundArchivePlayerInfo; + if (arc->ReadSoundArchivePlayerInfo(&soundArchivePlayerInfo)) + { + size += ut::RoundUp(soundArchivePlayerInfo.seqSoundCount + * sizeof(detail::SeqSound), + 4); + size += ut::RoundUp(soundArchivePlayerInfo.strmSoundCount + * sizeof(detail::StrmSound), + 4); + size += ut::RoundUp(soundArchivePlayerInfo.waveSoundCount + * sizeof(detail::WaveSound), + 4); + size += ut::RoundUp(soundArchivePlayerInfo.seqTrackCount + * sizeof(detail::MmlSeqTrack), + 4); + } + + size += ut::RoundUp(sizeof(u32) + arc->detail_GetFileCount() * 8, 4); + + return size; +} + +u32 SoundArchivePlayer::GetRequiredStrmBufferSize(SoundArchive const *arc) +{ + NW4RAssertPointerNonnull_Line(272, arc); + + int strmChannelCount = 0; + + SoundArchive::SoundArchivePlayerInfo soundArchivePlayerInfo; + if (arc->ReadSoundArchivePlayerInfo(&soundArchivePlayerInfo)) + strmChannelCount = soundArchivePlayerInfo.strmChannelCount; + + // TODO (from ogws): How is this calculated? + // is this the size of one strmChannel? (whatever that is?) + u32 memSize = strmChannelCount * 0xa000; + return memSize; +} + +bool SoundArchivePlayer::SetupMram(SoundArchive const *arc, void *buffer, + u32 size) +{ + NW4RAssertPointerNonnull_Line(304, arc); + NW4RAssertPointerNonnull_Line(305, buffer); + NW4RAssertAligned_Line(306, buffer, 4); + NW4RAssert_Line(307, size >= GetRequiredMemSize( arc )); + + void *endp = static_cast<byte_t *>(buffer) + size; + void *buf = buffer; + + if (!SetupSoundPlayer(arc, &buf, endp)) + return false; + if (!CreateGroupAddressTable(arc, &buf, endp)) + return false; + if (!CreateFileAddressTable(arc, &buf, endp)) + return false; + + SoundArchive::SoundArchivePlayerInfo soundArchivePlayerInfo; + if (arc->ReadSoundArchivePlayerInfo(&soundArchivePlayerInfo)) + { + if (!SetupSeqSound(arc, soundArchivePlayerInfo.seqSoundCount, &buf, + endp)) + { + return false; + } + + if (!SetupStrmSound(arc, soundArchivePlayerInfo.strmSoundCount, &buf, + endp)) + { + return false; + } + + if (!SetupWaveSound(arc, soundArchivePlayerInfo.waveSoundCount, &buf, + endp)) + { + return false; + } + + if (!SetupSeqTrack(arc, soundArchivePlayerInfo.seqTrackCount, &buf, + endp)) + { + return false; + } + } + + NW4RAssert_Line(351, static_cast<char*>(buf) - static_cast<char*>(buffer) + == GetRequiredMemSize( arc )); + + mSoundArchive = arc; + mSetupBufferAddress = buffer; + mSetupBufferSize = size; + + return true; +} + +detail::PlayerHeap *SoundArchivePlayer::CreatePlayerHeap(void **buffer, + void *endp, + u32 heapSize) +{ + void *ep = ut::RoundUp(ut::AddOffsetToPtr(*buffer, 32), 4); + if (ut::ComparePtr(ep, endp) > 0) + return nullptr; + + void *buf = *buffer; + *buffer = ep; + + detail::PlayerHeap *playerHeap = new (buf) detail::PlayerHeap; + *buffer = ut::RoundUp(*buffer, 32); + + ep = ut::RoundUp(ut::AddOffsetToPtr(*buffer, heapSize), 4); + if (ut::ComparePtr(ep, endp) > 0) + return nullptr; + + buf = *buffer; + *buffer = ep; + + bool result = playerHeap->Create(buf, heapSize); + if (!result) + return nullptr; + + return playerHeap; +} + +bool SoundArchivePlayer::SetupSoundPlayer(SoundArchive const *arc, + void **buffer, void *endp) +{ + u32 playerCount = arc->GetPlayerCount(); + u32 requireSize = sizeof(SoundPlayer) * playerCount; + + void *ep = ut::RoundUp(ut::AddOffsetToPtr(*buffer, requireSize), 4); + + if (ut::ComparePtr(ep, endp) > 0) + return false; + + void *buf = *buffer; + *buffer = ep; + + mSoundPlayers = static_cast<SoundPlayer *>(buf); + mSoundPlayerCount = playerCount; + + byte_t *ptr = static_cast<byte_t *>(buf); + + for (u32 playerId = 0; playerId < playerCount; + playerId++, ptr += sizeof(SoundPlayer)) + { + SoundPlayer *player = new (ptr) SoundPlayer; + + SoundArchive::PlayerInfo playerInfo; + if (!arc->ReadPlayerInfo(playerId, &playerInfo)) + continue; + + player->SetPlayableSoundCount(playerInfo.playableSoundCount); + + if (!playerInfo.heapSize) + continue; + + for (int i = 0; i < playerInfo.playableSoundCount; i++) + { + detail::PlayerHeap *playerHeap = + CreatePlayerHeap(buffer, endp, playerInfo.heapSize); + + NW4RCheckMessage_Line( + 442, playerHeap, + "failed to create player heap. ( player id = %d )", playerId); + if (!playerHeap) + return false; + + player->detail_AppendPlayerHeap(playerHeap); + } + + player->detail_SetPlayableSoundLimit(playerInfo.playableSoundCount); + } + + return true; +} + +bool SoundArchivePlayer::CreateGroupAddressTable(SoundArchive const *arc, + void **buffer, void *endp) +{ + u32 requireSize = sizeof(u32) + sizeof(GroupAddress) * arc->GetGroupCount(); + + void *ep = ut::RoundUp(ut::AddOffsetToPtr(*buffer, requireSize), 4); + + if (ut::ComparePtr(ep, endp) > 0) + return false; + + mGroupTable = static_cast<GroupAddressTable *>(*buffer); + *buffer = ep; + + mGroupTable->count = arc->GetGroupCount(); + + for (int i = 0; i < mGroupTable->count; i++) + { + mGroupTable->item[i].address = nullptr; + mGroupTable->item[i].waveDataAddress = nullptr; + } + + return true; +} + +bool SoundArchivePlayer::CreateFileAddressTable(SoundArchive const *arc, + void **buffer, void *endp) +{ + u32 requireSize = + sizeof(u32) + sizeof(FileAddress) * arc->detail_GetFileCount(); + + void *ep = ut::RoundUp(ut::AddOffsetToPtr(*buffer, requireSize), 4); + + if (ut::ComparePtr(ep, endp) > 0) + return false; + + mFileTable = static_cast<FileAddressTable *>(*buffer); + *buffer = ep; + + mFileTable->count = arc->detail_GetFileCount(); + + for (int i = 0; i < mFileTable->count; i++) + { + mFileTable->item[i].address = nullptr; + mFileTable->item[i].waveDataAddress = nullptr; + } + + return true; +} + +bool SoundArchivePlayer::SetupSeqSound(SoundArchive const *arc ATTR_UNUSED, + int numSounds, void **buffer, void *endp) +{ + u32 requireSize = sizeof(detail::SeqSound) * numSounds; + + void *ep = ut::RoundUp(ut::AddOffsetToPtr(*buffer, requireSize), 4); + + if (ut::ComparePtr(ep, endp) > 0) + return false; + + u32 createNum ATTR_MAYBE_UNUSED = + mSeqSoundInstanceManager.Create(*buffer, requireSize); + + NW4RAssert_Line(536, createNum == numSounds); + + *buffer = ep; + + return true; +} + +bool SoundArchivePlayer::SetupWaveSound(SoundArchive const *arc ATTR_UNUSED, + int numSounds, void **buffer, + void *endp) +{ + u32 requireSize = sizeof(detail::WaveSound) * numSounds; + + void *ep = ut::RoundUp(ut::AddOffsetToPtr(*buffer, requireSize), 4); + + if (ut::ComparePtr(ep, endp) > 0) + return false; + + u32 createNum ATTR_MAYBE_UNUSED = + mWaveSoundInstanceManager.Create(*buffer, requireSize); + + NW4RAssert_Line(568, createNum == numSounds); + + *buffer = ep; + + return true; +} + +bool SoundArchivePlayer::SetupStrmSound(SoundArchive const *arc ATTR_UNUSED, + int numSounds, void **buffer, + void *endp) +{ + u32 requireSize = sizeof(detail::StrmSound) * numSounds; + + void *ep = ut::RoundUp(ut::AddOffsetToPtr(*buffer, requireSize), 4); + + if (ut::ComparePtr(ep, endp) > 0) + return false; + + u32 createNum ATTR_MAYBE_UNUSED = + mStrmSoundInstanceManager.Create(*buffer, requireSize); + + NW4RAssert_Line(600, createNum == numSounds); + + *buffer = ep; + + return true; +} + +bool SoundArchivePlayer::SetupSeqTrack(SoundArchive const *arc ATTR_UNUSED, + int numTracks, void **buffer, void *endp) +{ + // TODO: why is this sizeof(MmlSeqTrack) instead of sizeof(SeqTrack)? + u32 requireSize = sizeof(detail::MmlSeqTrack) * numTracks; + + void *ep = ut::RoundUp(ut::AddOffsetToPtr(*buffer, requireSize), 4); + + if (ut::ComparePtr(ep, endp) > 0) + return false; + + u32 createNum ATTR_MAYBE_UNUSED = + mMmlSeqTrackAllocator.Create(*buffer, requireSize); + + NW4RAssert_Line(632, createNum == numTracks); + + *buffer = ep; + + return true; +} + +bool SoundArchivePlayer::SetupStrmBuffer(SoundArchive const *arc, void *buffer, + u32 size) +{ + if (size < GetRequiredStrmBufferSize(arc)) + return false; + + int strmChannelCount = 0; + + SoundArchive::SoundArchivePlayerInfo soundArchivePlayerInfo; + if (arc->ReadSoundArchivePlayerInfo(&soundArchivePlayerInfo)) + strmChannelCount = soundArchivePlayerInfo.strmChannelCount; + + mStrmBufferPool.Setup(buffer, size, strmChannelCount); + + return true; +} + +void SoundArchivePlayer::Update() +{ + for (u32 playerId = 0; playerId < mSoundPlayerCount; playerId++) + GetSoundPlayer(playerId).Update(); + + mSeqSoundInstanceManager.SortPriorityList(); + mStrmSoundInstanceManager.SortPriorityList(); + mWaveSoundInstanceManager.SortPriorityList(); +} + +SoundArchive const &SoundArchivePlayer::GetSoundArchive() const +{ + NW4RAssertMessage_Line(691, mSoundArchive, "Setup is not completed."); + + return *mSoundArchive; +} + +SoundPlayer &SoundArchivePlayer::GetSoundPlayer(u32 playerId) +{ + // specifically not the source variant + NW4RAssertHeaderClampedLValue_Line(697, playerId, 0, mSoundPlayerCount); + + return mSoundPlayers[playerId]; +} + +/* SoundArchivePlayer::GetSoundPlayer(char const *) + * ([R89JEL]:/bin/RVL/Debug/mainD.MAP:14216) + */ +DECOMP_FORCE(NW4RAssertPointerNonnull_String(mSoundArchive)); + +void const *SoundArchivePlayer::detail_GetFileAddress(u32 fileId) const +{ + if (mFileManager) + { + if (void const *addr = mFileManager->at_0x08(fileId)) + return addr; + } + + if (void const *addr = mSoundArchive->detail_GetFileAddress(fileId)) + return addr; + + if (void const *fileData = GetFileAddress(fileId)) + return fileData; + + SoundArchive::FileInfo fileInfo; + if (!mSoundArchive->detail_ReadFileInfo(fileId, &fileInfo)) + return nullptr; + + for (unsigned i = 0; i < fileInfo.filePosCount; i++) + { + SoundArchive::FilePos filePos; + if (!mSoundArchive->detail_ReadFilePos(fileId, i, &filePos)) + continue; + + void const *groupData = GetGroupAddress(filePos.groupId); + if (!groupData) + continue; + + SoundArchive::GroupItemInfo itemInfo; + if (!mSoundArchive->detail_ReadGroupItemInfo(filePos.groupId, + filePos.index, &itemInfo)) + { + continue; + } + + return static_cast<byte_t const *>(groupData) + itemInfo.offset; + } + + return nullptr; +} + +void const *SoundArchivePlayer::detail_GetFileWaveDataAddress(u32 fileId) const +{ + if (mFileManager) + { + if (void const *addr = mFileManager->at_0x0c(fileId)) + return addr; + } + + if (void const *addr = mSoundArchive->detail_GetWaveDataFileAddress(fileId)) + return addr; + + if (void const *fileData = GetFileWaveDataAddress(fileId)) + return fileData; + + SoundArchive::FileInfo fileInfo; + if (!mSoundArchive->detail_ReadFileInfo(fileId, &fileInfo)) + return nullptr; + + for (unsigned i = 0; i < fileInfo.filePosCount; i++) + { + SoundArchive::FilePos filePos; + if (!mSoundArchive->detail_ReadFilePos(fileId, i, &filePos)) + continue; + + void const *groupData = GetGroupWaveDataAddress(filePos.groupId); + if (!groupData) + continue; + + SoundArchive::GroupItemInfo itemInfo; + if (!mSoundArchive->detail_ReadGroupItemInfo(filePos.groupId, + filePos.index, &itemInfo)) + { + continue; + } + + return static_cast<byte_t const *>(groupData) + itemInfo.waveDataOffset; + } + + return nullptr; +} + +void const *SoundArchivePlayer::GetGroupAddress(u32 groupId) const +{ + if (!mGroupTable) + { + NW4RCheckMessage_Line(840, mGroupTable, + "Failed to SoundArchivePlayer::GetGroupAddress " + "because group table is not allocated.\n"); + + return nullptr; + } + + if (groupId >= mGroupTable->count) + return nullptr; + + return mGroupTable->item[groupId].address; +} + +// SoundArchivePlayer::SetGroupAddress ([R89JEL]:/bin/RVL/Debug/mainD.MAP:14220) +DECOMP_FORCE("Failed to SoundArchivePlayer::SetGroupAddress because group " + "table is not allocated.\n"); + +void const *SoundArchivePlayer::GetGroupWaveDataAddress(u32 groupId) const +{ + if (!mGroupTable) + { + NW4RCheckMessage_Line( + 891, mGroupTable, + "Failed to SoundArchivePlayer::GetGroupWaveDataAddress " + "because group table is not allocated.\n"); + + return nullptr; + } + + if (groupId >= mGroupTable->count) + return nullptr; + + return mGroupTable->item[groupId].waveDataAddress; +} + +/* SoundArchivePlayer::SetGroupWaveDataAddress + * ([R89JEL]:/bin/RVL/Debug/mainD.MAP:14222) + */ +DECOMP_FORCE(NW4RAssertHeaderClampedLValue_String(groupId)); +DECOMP_FORCE("Failed to SoundArchivePlayer::SetGroupWaveDataAddress because " + "group table is not allocated.\n"); + +// SoundArchivePlayer::SetFileAddress ([R89JEL]:/bin/RVL/Debug/mainD.MAP:14224) +DECOMP_FORCE("Failed to SoundArchivePlayer::SetFileAddress because file table " + "is not allocated.\n"); + +/* SoundArchivePlayer::SetFileWaveDataAddress + * ([R89JEL]:/bin/RVL/Debug/mainD.MAP:14226) + */ +DECOMP_FORCE(NW4RAssertHeaderClampedLValue_String(fileId)); +DECOMP_FORCE("Failed to SoundArchivePlayer::SetFileWaveDataAddress because " + "file table is not allocated.\n"); + +void const *SoundArchivePlayer::GetFileAddress(u32 fileId) const +{ + if (!mFileTable) + { + NW4RCheckMessage_Line(942, mFileTable, + "Failed to SoundArchivePlayer::GetFileAddress " + "because file table is not allocated.\n"); + + return nullptr; + } + + if (fileId >= mFileTable->count) + return nullptr; + + return mFileTable->item[fileId].address; +} + +void const *SoundArchivePlayer::GetFileWaveDataAddress(u32 fileId) const +{ + if (!mFileTable) + { + NW4RCheckMessage_Line( + 993, mFileTable, + "Failed to SoundArchivePlayer::GetFileWaveDataAddress " + "because file table is not allocated.\n"); + + return nullptr; + } + + if (fileId >= mFileTable->count) + return nullptr; + + return mFileTable->item[fileId].waveDataAddress; +} + +SoundStartable::StartResult SoundArchivePlayer::detail_SetupSound( + SoundHandle *handle, u32 soundId, bool holdFlag, + SoundStartable::StartInfo const *startInfo) +{ + return detail_SetupSoundImpl(handle, soundId, nullptr, nullptr, holdFlag, + startInfo); +} + +template <class Sound> +Sound *SoundArchivePlayer::AllocSound( + detail::SoundInstanceManager<Sound> *manager, u32 soundId, + int priority, int ambientPriority, + detail::BasicSound::AmbientInfo *ambientArgInfo) +{ + NW4RAssertPointerNonnull_Line(1050, manager); + + Sound *sound = manager->Alloc(priority, ambientPriority); + if (!sound) + return nullptr; + + sound->SetId(soundId); + + if (ambientArgInfo) + sound->SetAmbientInfo(*ambientArgInfo); + + return sound; +} + +SoundStartable::StartResult SoundArchivePlayer::detail_SetupSoundImpl( + SoundHandle *handle, u32 soundId, + detail::BasicSound::AmbientInfo *ambientArgInfo, SoundActor *actor, + bool holdFlag, SoundStartable::StartInfo const *startInfo) +{ + NW4RAssertPointerNonnull_Line(1108, handle); + + if (!IsAvailable()) + return START_ERR_NOT_AVAILABLE; + + if (handle->IsAttachedSound()) + handle->DetachSound(); + + SoundArchive::SoundInfo soundInfo; + if (!mSoundArchive->ReadSoundInfo(soundId, &soundInfo)) + return START_ERR_INVALID_SOUNDID; + + SoundStartable::StartInfo::StartOffsetType startOffsetType = + SoundStartable::StartInfo::START_OFFSET_TYPE_MILLISEC; + + int startOffset = 0; + int playerPriority = soundInfo.playerPriority; + u32 playerId = soundInfo.playerId; + int actorPlayerId = soundInfo.actorPlayerId; + void const *externalSeqDataAddress = nullptr; + char const *externalSeqStartLabel = nullptr; + + if (startInfo) + { + if (startInfo->enableFlag + & SoundStartable::StartInfo::ENABLE_START_OFFSET) + { + startOffsetType = startInfo->startOffsetType; + startOffset = startInfo->startOffset; + } + + if (startInfo->enableFlag + & SoundStartable::StartInfo::ENABLE_PLAYER_PRIORITY) + { + playerPriority = startInfo->playerPriority; + } + + if (startInfo->enableFlag & SoundStartable::StartInfo::ENABLE_PLAYER_ID) + playerId = startInfo->playerId; + + if (startInfo->enableFlag + & SoundStartable::StartInfo::ENABLE_ACTOR_PLAYER_ID) + { + actorPlayerId = startInfo->actorPlayerId; + } + + if (startInfo->enableFlag + & SoundStartable::StartInfo::ENABLE_SEQ_SOUND_INFO) + { + externalSeqDataAddress = startInfo->seqSoundInfo.seqDataAddress; + externalSeqStartLabel = startInfo->seqSoundInfo.startLocationLabel; + } + } + + int priority = playerPriority; + if (holdFlag) + priority--; + + int ambientPriority = 0; + if (ambientArgInfo) + { + ambientPriority = + detail::BasicSound::GetAmbientPriority(*ambientArgInfo, soundId); + } + + int allocPriority = priority + ambientPriority; + allocPriority = ut::Clamp(allocPriority, 0, 127); + + detail::ExternalSoundPlayer *extPlayer = nullptr; + if (actor) + { + extPlayer = actor->detail_GetActorPlayer(actorPlayerId); + if (!extPlayer) + { + // this feels like a macro since it has the name in the string + NW4RWarningMessage_Line(1181, + "actorPlayerId(%d) is out of range. (0-%d)", + actorPlayerId, 3); + + return START_ERR_INVALID_PARAMETER; + } + } + + detail::SoundThread::AutoLock lock; + + SoundPlayer &player = GetSoundPlayer(playerId); + if (!player.detail_CanPlaySound(allocPriority)) + return START_ERR_LOW_PRIORITY; + + if (extPlayer) + { + if (!extPlayer->detail_CanPlaySound(allocPriority)) + return START_ERR_LOW_PRIORITY; + } + + detail::BasicSound *sound = nullptr; + detail::SeqSound *seqSound = nullptr; + detail::StrmSound *strmSound = nullptr; + detail::WaveSound *waveSound = nullptr; + + switch (mSoundArchive->GetSoundType(soundId)) + { + case SoundArchive::SOUND_TYPE_SEQ: + seqSound = AllocSound(&mSeqSoundInstanceManager, soundId, priority, + ambientPriority, ambientArgInfo); + if (!seqSound) + { + NW4RCheckMessage_Line( + 1220, + !detail::Debug_GetWarningFlag( + DEBUG_WARNING_NOT_ENOUGH_SEQSOUND), + "Failed to start sound (id:%d) for not enough SeqSound " + "instance.", + soundId); + + return START_ERR_NOT_ENOUGH_INSTANCE; + } + + sound = seqSound; + break; + + case SoundArchive::SOUND_TYPE_STRM: + strmSound = AllocSound(&mStrmSoundInstanceManager, soundId, priority, + ambientPriority, ambientArgInfo); + if (!strmSound) + { + NW4RCheckMessage_Line( + 1240, + !detail::Debug_GetWarningFlag( + DEBUG_WARNING_NOT_ENOUGH_STRMSOUND), + "Failed to start sound (id:%d) for not enough StrmSound " + "instance.", + soundId); + + return START_ERR_NOT_ENOUGH_INSTANCE; + } + + sound = strmSound; + break; + + case SoundArchive::SOUND_TYPE_WAVE: + waveSound = AllocSound(&mWaveSoundInstanceManager, soundId, priority, + ambientPriority, ambientArgInfo); + if (!waveSound) + { + NW4RCheckMessage_Line( + 1260, + !detail::Debug_GetWarningFlag( + DEBUG_WARNING_NOT_ENOUGH_WAVESOUND), + "Failed to start sound (id:%d) for not enough WaveSound " + "instance.", + soundId); + + return START_ERR_NOT_ENOUGH_INSTANCE; + } + + sound = waveSound; + break; + + default: + return START_ERR_INVALID_SOUNDID; + } + + if (!player.detail_AppendSound(sound)) + { + sound->Shutdown(); + + return START_ERR_UNKNOWN; + } + + switch (mSoundArchive->GetSoundType(soundId)) + { + case SoundArchive::SOUND_TYPE_SEQ: + { + NW4RAssertPointerNonnull_Line(1282, seqSound); + + player.detail_AllocPlayerHeap(seqSound); + + SoundArchive::SeqSoundInfo info; + if (!mSoundArchive->ReadSeqSoundInfo(soundId, &info)) + { + seqSound->Shutdown(); + return START_ERR_INVALID_SOUNDID; + } + + SoundStartable::StartResult result = PrepareSeqImpl( + seqSound, &soundInfo, &info, startOffsetType, + startOffset, externalSeqDataAddress, externalSeqStartLabel); + + if (result == SoundStartable::START_SUCCESS) + break; + + seqSound->Shutdown(); + return result; + } + + case SoundArchive::SOUND_TYPE_STRM: + { + NW4RAssertPointerNonnull_Line(1314, strmSound); + + SoundArchive::StrmSoundInfo info; + if (!mSoundArchive->detail_ReadStrmSoundInfo(soundId, &info)) + { + strmSound->Shutdown(); + return START_ERR_INVALID_SOUNDID; + } + + SoundStartable::StartResult result = PrepareStrmImpl( + strmSound, &soundInfo, &info, startOffsetType, + startOffset); + + if (result == SoundStartable::START_SUCCESS) + break; + + strmSound->Shutdown(); + return result; + } + + case SoundArchive::SOUND_TYPE_WAVE: + { + NW4RAssertPointerNonnull_Line(1341, waveSound); + + SoundArchive::WaveSoundInfo info; + if (!mSoundArchive->detail_ReadWaveSoundInfo(soundId, &info)) + { + waveSound->Shutdown(); + return START_ERR_INVALID_SOUNDID; + } + + SoundStartable::StartResult result = PrepareWaveSoundImpl( + waveSound, &soundInfo, &info, startOffsetType, + startOffset); + + if (result == SoundStartable::START_SUCCESS) + break; + + waveSound->Shutdown(); + return result; + } + + default: + NW4RPanic_Line(1367); + + sound->Shutdown(); + return START_ERR_INVALID_SOUNDID; + } + + if (extPlayer) + { + if (!extPlayer->AppendSound(sound)) + { + sound->Shutdown(); + return START_ERR_UNKNOWN; + } + } + + if (actor) + sound->AttachSoundActor(actor); + + if (holdFlag) + sound->SetPlayerPriority(playerPriority); + + handle->detail_AttachSound(sound); + + return START_SUCCESS; +} + +SoundStartable::StartResult SoundArchivePlayer::PrepareSeqImpl( + detail::SeqSound *sound, SoundArchive::SoundInfo const *commonInfo, + SoundArchive::SeqSoundInfo const *info, + SoundStartable::StartInfo::StartOffsetType startOffsetType, int startOffset, + void const *externalSeqDataAddress, char const *externalSeqStartLabel) +{ + NW4RAssertPointerNonnull_Line(1422, info); + + detail::SeqFile const *seqFile = nullptr; + ut::FileStream *fileStream = nullptr; + u32 seqOffset = 0; + u32 allocTrack = info->allocTrack; + + if (externalSeqDataAddress) + { + seqFile = static_cast<detail::SeqFile const *>(externalSeqDataAddress); + seqOffset = 0; + + detail::SeqFileReader seqFileReader(seqFile); + + if (externalSeqStartLabel) + { + bool result = seqFileReader.ReadOffsetByLabel(externalSeqStartLabel, + &seqOffset); + if (!result) + return START_ERR_INVALID_SEQ_START_LOCATION_LABEL; + } + + seqOffset = detail::MmlParser::ParseAllocTrack( + seqFileReader.GetBaseAddress(), seqOffset, &allocTrack); + } + + if (!seqFile) + { + seqFile = static_cast<detail::SeqFile const *>( + detail_GetFileAddress(commonInfo->fileId)); + seqOffset = info->dataOffset; + + if (externalSeqStartLabel) + { + detail::SeqFileReader seqFileReader(seqFile); + + bool result = seqFileReader.ReadOffsetByLabel(externalSeqStartLabel, + &seqOffset); + if (!result) + return START_ERR_INVALID_SEQ_START_LOCATION_LABEL; + } + } + + if (!seqFile) + { + detail::PlayerHeap *heap = sound->GetPlayerHeap(); + if (!heap) + return START_ERR_NOT_DATA_LOADED; + + void *fileStreamBuffer = sound->GetFileStreamBuffer(); + s32 fileStreamBufferSize = sound->GetFileStreamBufferSize(); + NW4RAssertPointerNonnull_Line(1477, fileStreamBuffer); + + fileStream = mSoundArchive->detail_OpenFileStream( + commonInfo->fileId, fileStreamBuffer, fileStreamBufferSize); + if (!fileStream) + return START_ERR_CANNOT_OPEN_FILE; + + if (heap->GetFreeSize() < fileStream->GetSize()) + { + fileStream->Close(); + return SoundStartable::START_ERR_NOT_ENOUGH_PLAYER_HEAP; + } + } + + detail::SeqPlayer::SetupResult result = + sound->Setup(mSeqTrackAllocator, allocTrack, &mSeqCallback); + + while (result != detail::SeqPlayer::SETUP_SUCCESS) + { + if (result == detail::SeqPlayer::SETUP_ERR_CANNOT_ALLOCATE_TRACK) + { + if (mSeqSoundInstanceManager.GetActiveCount() == 1) + { + if (fileStream) + fileStream->Close(); + + NW4RCheckMessage_Line(1520, + !detail::Debug_GetWarningFlag( + DEBUG_WARNING_NOT_ENOUGH_SEQTRACK), + "Failed to start sound (id:%d) for not " + "enough SeqTrack instance.", + sound->GetId()); + + return START_ERR_NOT_ENOUGH_INSTANCE; + } + + detail::SeqSound *lowest = + mSeqSoundInstanceManager.GetLowestPrioritySound(); + + if (sound == lowest) + { + if (fileStream) + fileStream->Close(); + + NW4RCheckMessage_Line(1536, + !detail::Debug_GetWarningFlag( + DEBUG_WARNING_NOT_ENOUGH_SEQTRACK), + "Failed to start sound (id:%d) for not " + "enough SeqTrack instance.", + sound->GetId()); + + return START_ERR_NOT_ENOUGH_INSTANCE; + } + + NW4RCheckMessage_Line( + 1545, + !detail::Debug_GetWarningFlag( + DEBUG_WARNING_NOT_ENOUGH_SEQTRACK), + "Sound (id:%d) is stopped for not enough SeqTrack instance.", + lowest->GetId()); + + lowest->Stop(0); + result = sound->Setup(mSeqTrackAllocator, allocTrack, + &mSeqCallback); + } + else + { + return SoundStartable::START_ERR_UNKNOWN; + } + } + + UpdateCommonSoundParam(sound, commonInfo); + + sound->SetChannelPriority(info->channelPriority); + sound->SetReleasePriorityFix(info->releasePriorityFixFlag); + sound->SetSeqUserprocCallback(mSeqUserprocCallback, + mSeqUserprocCallbackArg); + + detail::SeqPlayer::OffsetType seqOffsetType; + switch (startOffsetType) + { + case SoundStartable::StartInfo::START_OFFSET_TYPE_MILLISEC: + seqOffsetType = detail::SeqPlayer::OFFSET_TYPE_MILLISEC; + break; + + case SoundStartable::StartInfo::START_OFFSET_TYPE_TICK: + seqOffsetType = detail::SeqPlayer::OFFSET_TYPE_TICK; + break; + + case SoundStartable::StartInfo::START_OFFSET_TYPE_SAMPLE: + seqOffsetType = detail::SeqPlayer::OFFSET_TYPE_TICK; + startOffset = 0; + break; + + default: + seqOffsetType = detail::SeqPlayer::OFFSET_TYPE_TICK; + startOffset = 0; + break; + } + + if (seqFile) + { + detail::SeqFileReader seqFileReader(seqFile); + + sound->Prepare(seqFileReader.GetBaseAddress(), seqOffset, seqOffsetType, + startOffset); + } + else + { + sound->Prepare(fileStream, seqOffset, seqOffsetType, startOffset); + } + + return SoundStartable::START_SUCCESS; +} + +SoundStartable::StartResult SoundArchivePlayer::PrepareStrmImpl( + detail::StrmSound *sound, SoundArchive::SoundInfo const *commonInfo, + SoundArchive::StrmSoundInfo const *info, + SoundStartable::StartInfo::StartOffsetType startOffsetType, int startOffset) +{ + detail::StrmPlayer::SetupResult setupResult = sound->Setup( + &mStrmBufferPool, info->allocChannelCount, info->allocTrackFlag); + + while (setupResult != detail::StrmPlayer::SETUP_SUCCESS) + { + if (setupResult == detail::StrmPlayer::SETUP_ERR_CANNOT_ALLOCATE_BUFFER) + { + if (mStrmSoundInstanceManager.GetActiveCount() == 1) + { + NW4RCheckMessage_Line(1648, + !detail::Debug_GetWarningFlag( + DEBUG_WARNING_NOT_ENOUGH_SEQTRACK), + "Failed to start sound (id:%d) for not " + "enough StrmChannel instance.", + sound->GetId()); + + return START_ERR_NOT_ENOUGH_INSTANCE; + } + + detail::StrmSound *lowest = + mStrmSoundInstanceManager.GetLowestPrioritySound(); + if (sound == lowest) + { + NW4RCheckMessage_Line(1660, + !detail::Debug_GetWarningFlag( + DEBUG_WARNING_NOT_ENOUGH_SEQTRACK), + "Failed to start sound (id:%d) for not " + "enough StrmChannel instance.", + sound->GetId()); + + return START_ERR_NOT_ENOUGH_INSTANCE; + } + + NW4RCheckMessage_Line( + 1669, + !detail::Debug_GetWarningFlag( + DEBUG_WARNING_NOT_ENOUGH_STRMCHANNEL), + "Sound (id:%d) is stopped for not enough StrmChannel instance.", + lowest->GetId()); + + lowest->Stop(0); + setupResult = + sound->Setup(&mStrmBufferPool, info->allocChannelCount, + info->allocTrackFlag); + } + else + { + return SoundStartable::START_ERR_UNKNOWN; + } + } + + detail::StrmPlayer::StartOffsetType strmStartOffsetType; + switch (startOffsetType) + { + case SoundStartable::StartInfo::START_OFFSET_TYPE_MILLISEC: + strmStartOffsetType = detail::StrmPlayer::START_OFFSET_TYPE_MILLISEC; + break; + + case SoundStartable::StartInfo::START_OFFSET_TYPE_TICK: + strmStartOffsetType = detail::StrmPlayer::START_OFFSET_TYPE_SAMPLE; + startOffset = 0; + break; + + case SoundStartable::StartInfo::START_OFFSET_TYPE_SAMPLE: + strmStartOffsetType = detail::StrmPlayer::START_OFFSET_TYPE_SAMPLE; + break; + + default: + strmStartOffsetType = detail::StrmPlayer::START_OFFSET_TYPE_SAMPLE; + startOffset = 0; + break; + } + + void *fileStreamBuffer = sound->GetFileStreamBuffer(); + s32 fileStreamBufferSize = sound->GetFileStreamBufferSize(); + NW4RAssertPointerNonnull_Line(1704, fileStreamBuffer); + + ut::FileStream *fileStream = mSoundArchive->detail_OpenFileStream( + commonInfo->fileId, fileStreamBuffer, fileStreamBufferSize); + if (!fileStream) + return START_ERR_CANNOT_OPEN_FILE; + + bool result = sound->Prepare(strmStartOffsetType, startOffset, fileStream); + if (!result) + return START_ERR_UNKNOWN; + + UpdateCommonSoundParam(sound, commonInfo); + + return SoundStartable::START_SUCCESS; +} + +SoundStartable::StartResult SoundArchivePlayer::PrepareWaveSoundImpl( + detail::WaveSound *sound, SoundArchive::SoundInfo const *commonInfo, + SoundArchive::WaveSoundInfo const *info, + SoundStartable::StartInfo::StartOffsetType startOffsetType, int startOffset) +{ + NW4RAssertPointerNonnull_Line(1751, info); + + void const *wsdData = detail_GetFileAddress(commonInfo->fileId); + if (!wsdData) + return START_ERR_NOT_DATA_LOADED; + + detail::WsdPlayer::StartOffsetType wsdStartOffsetType; + switch (startOffsetType) + { + case SoundStartable::StartInfo::START_OFFSET_TYPE_MILLISEC: + wsdStartOffsetType = detail::WsdPlayer::START_OFFSET_TYPE_MILLISEC; + break; + + case SoundStartable::StartInfo::START_OFFSET_TYPE_TICK: + wsdStartOffsetType = detail::WsdPlayer::START_OFFSET_TYPE_SAMPLE; + startOffset = 0; + break; + + case SoundStartable::StartInfo::START_OFFSET_TYPE_SAMPLE: + wsdStartOffsetType = detail::WsdPlayer::START_OFFSET_TYPE_SAMPLE; + break; + + default: + wsdStartOffsetType = detail::WsdPlayer::START_OFFSET_TYPE_SAMPLE; + startOffset = 0; + break; + } + + bool result = + sound->Prepare(wsdData, info->subNo, wsdStartOffsetType, startOffset, + &mWsdCallback, commonInfo->fileId); + if (!result) + return START_ERR_UNKNOWN; + + UpdateCommonSoundParam(sound, commonInfo); + + sound->SetChannelPriority(info->channelPriority); + sound->SetReleasePriorityFix(info->releasePriorityFixFlag); + + return SoundStartable::START_SUCCESS; +} + +void SoundArchivePlayer::UpdateCommonSoundParam( + detail::BasicSound *sound, SoundArchive::SoundInfo const *commonInfo) +{ + NW4RAssertPointerNonnull_Line(1814, sound); + NW4RAssertPointerNonnull_Line(1815, commonInfo); + + sound->SetInitialVolume(commonInfo->volume / 127.0f); + sound->SetRemoteFilter(commonInfo->remoteFilter); + sound->SetPanMode(commonInfo->panMode); + sound->SetPanCurve(commonInfo->panCurve); +} + +/* SoundArchivePlayer::LoadGroup(u32, SoundMemoryAllocatable *, u32) + * ([R89JEL]:/bin/RVL/Debug/mainD.MAP:14234) + */ +DECOMP_FORCE(NW4RAssertAligned_String(loadBlockSize, 32)); + +void SoundArchivePlayer::InvalidateData(void const *start, void const *end) +{ + if (mFileTable) + { + for (int i = 0; i < mFileTable->count; i++) + { + void const *addr = mFileTable->item[i].address; + + if (start <= addr && addr <= end) + mFileTable->item[i].address = nullptr; + } + } + + if (mGroupTable) + { + for (int i = 0; i < mGroupTable->count; i++) + { + void const *addr = mGroupTable->item[i].address; + + if (start <= addr && addr <= end) + mGroupTable->item[i].address = nullptr; + } + } +} + +void SoundArchivePlayer::InvalidateWaveData(void const *start, void const *end) +{ + if (mFileTable) + { + for (int i = 0; i < mFileTable->count; i++) + { + void const *addr = mFileTable->item[i].waveDataAddress; + + if (start <= addr && addr <= end) + mFileTable->item[i].waveDataAddress = nullptr; + } + } + + if (mGroupTable) + { + for (int i = 0; i < mGroupTable->count; i++) + { + void const *addr = mGroupTable->item[i].waveDataAddress; + + if (start <= addr && addr <= end) + mGroupTable->item[i].waveDataAddress = nullptr; + } + } +} + +detail::Channel *SoundArchivePlayer::SeqNoteOnCallback::NoteOn( + detail::SeqPlayer *seqPlayer, int bankNo ATTR_UNUSED, + detail::NoteOnInfo const ¬eOnInfo) +{ + if (!mSoundArchivePlayer.IsAvailable()) + return nullptr; + + SoundArchive const &sndArc = mSoundArchivePlayer.GetSoundArchive(); + u32 soundId = seqPlayer->GetId(); + + SoundArchive::SeqSoundInfo seqInfo; + if (!sndArc.ReadSeqSoundInfo(soundId, &seqInfo)) + return nullptr; + + SoundArchive::BankInfo bankInfo; + if (!sndArc.ReadBankInfo(seqInfo.bankId, &bankInfo)) + return nullptr; + + void const *bankData = + mSoundArchivePlayer.detail_GetFileAddress(bankInfo.fileId); + if (!bankData) + return nullptr; + + detail::Bank bank(bankData); + + void const *waveData = + mSoundArchivePlayer.detail_GetFileWaveDataAddress(bankInfo.fileId); + if (!waveData) + return nullptr; + + bank.SetWaveDataAddress(waveData); + + detail::Channel *channel = bank.NoteOn(noteOnInfo); + return channel; +} + +bool SoundArchivePlayer::WsdCallback::GetWaveSoundData( + detail::WaveSoundInfo *info, detail::WaveSoundNoteInfo *noteInfo, + detail::WaveInfo *waveData, void const *waveSoundData, int index, + int noteIndex, register_t userData) const +{ + u32 fileID = userData; + + if (!mSoundArchivePlayer.IsAvailable()) + return false; + + SoundArchive const &sndArc ATTR_UNUSED = + mSoundArchivePlayer.GetSoundArchive(); + + void const *dataAddr = + mSoundArchivePlayer.detail_GetFileWaveDataAddress(fileID); + if (!dataAddr) + return false; + + detail::WsdFileReader reader(waveSoundData); + + if (!reader.ReadWaveSoundInfo(info, index)) + return false; + + if (!reader.ReadWaveSoundNoteInfo(noteInfo, index, noteIndex)) + return false; + + if (!reader.ReadWaveInfo(noteInfo->waveIndex, waveData, dataAddr)) + return false; + + return true; +} + +}} // namespace nw4r::snd diff --git a/src/nw4r/snd/snd_SoundHandle.cpp b/src/nw4r/snd/snd_SoundHandle.cpp index eff7be7d..4483355d 100644 --- a/src/nw4r/snd/snd_SoundHandle.cpp +++ b/src/nw4r/snd/snd_SoundHandle.cpp @@ -1 +1,51 @@ -#include "nw4r/snd/snd_SoundHandle.h" +#include "nw4r/snd/SoundHandle.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_SoundHandle.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <types.h> // nullptr + +#include "nw4r/snd/BasicSound.h" + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { + +void SoundHandle::detail_AttachSound(detail::BasicSound *sound) +{ + NW4RAssertPointerNonnull_Line(81, sound); + + mSound = sound; + + if (sound->IsAttachedGeneralHandle()) + mSound->DetachGeneralHandle(); + + mSound->mGeneralHandle = this; +} + +void SoundHandle::DetachSound() +{ + if (IsAttachedSound()) + { + if (mSound->mGeneralHandle == this) + mSound->mGeneralHandle = nullptr; + + if (mSound->mTempGeneralHandle == this) + mSound->mTempGeneralHandle = nullptr; + } + + if (mSound) + mSound = nullptr; +} + +}} // namespace nw4r::snd diff --git a/src/nw4r/snd/snd_SoundInstanceManager.cpp b/src/nw4r/snd/snd_SoundInstanceManager.cpp new file mode 100644 index 00000000..2a924f1d --- /dev/null +++ b/src/nw4r/snd/snd_SoundInstanceManager.cpp @@ -0,0 +1 @@ +/* This file intentionally left blank. */ diff --git a/src/nw4r/snd/snd_SoundMemoryAllocatable.cpp b/src/nw4r/snd/snd_SoundMemoryAllocatable.cpp new file mode 100644 index 00000000..2a924f1d --- /dev/null +++ b/src/nw4r/snd/snd_SoundMemoryAllocatable.cpp @@ -0,0 +1 @@ +/* This file intentionally left blank. */ diff --git a/src/nw4r/snd/snd_SoundPlayer.cpp b/src/nw4r/snd/snd_SoundPlayer.cpp index dfdff590..6f195226 100644 --- a/src/nw4r/snd/snd_SoundPlayer.cpp +++ b/src/nw4r/snd/snd_SoundPlayer.cpp @@ -1 +1,295 @@ -#include "nw4r/snd/snd_SoundPlayer.h" +#include "nw4r/snd/SoundPlayer.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_SoundPlayer.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <climits> // INT_MAX + +#include <decomp.h> +#include <macros.h> +#include <types.h> // nullptr + +#include "nw4r/snd/BasicSound.h" +#include "nw4r/snd/global.h" // AUX_BUS_NUM +#include "nw4r/snd/PlayerHeap.h" +#include "nw4r/snd/SoundThread.h" + +#include "nw4r/ut/inlines.h" // ut::Clamp + +#include <nw4r/NW4RAssert.hpp> + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { + +SoundPlayer::SoundPlayer() : + mPlayableCount (1), + mPlayableLimit (INT_MAX), + mVolume (1.0f), + mLpfFreq (0.0f), + mOutputLineFlag (1), + mMainOutVolume (1.0f), + mBiquadType (0), + mBiquadValue (0.0f), + mMainSend (0.0f) +{ + for (int i = 0; i < AUX_BUS_NUM; i++) + mFxSend[i] = 0.0f; +} + +SoundPlayer::~SoundPlayer() +{ + StopAllSound(0); +} + +void SoundPlayer::Update() +{ + detail::SoundThread::AutoLock lock; + + NW4R_RANGE_FOR_NO_AUTO_INC(itr, mSoundList) + { + decltype(itr) curItr = itr++; + + curItr->Update(); + } + + detail_SortPriorityList(); +} + +void SoundPlayer::StopAllSound(int fadeFrames) +{ + detail::SoundThread::AutoLock lock; + + NW4R_RANGE_FOR_NO_AUTO_INC(itr, mSoundList) + { + decltype(itr) curItr = itr++; + + curItr->Stop(fadeFrames); + } +} + +void SoundPlayer::PauseAllSound(bool flag, int fadeFrames) +{ + detail::SoundThread::AutoLock lock; + + NW4R_RANGE_FOR_NO_AUTO_INC(itr, mSoundList) + { + decltype(itr) curItr = itr++; + + curItr->Pause(flag, fadeFrames); + } +} + +// SoundPlayer::SetVolume ([R89JEL]:/bin/RVL/Debug/mainD.MAP:14397) +/* also __FILE__ because this is the first assert in the file so it needs to + * reference that first as well + */ +DECOMP_FORCE(__FILE__); +DECOMP_FORCE(NW4RAssert_String(volume >= 0.0f)); + +// SoundPlayer::SetFxSend ([R89JEL]:/bin/RVL/Debug/mainD.MAP:14405) +DECOMP_FORCE(NW4RAssertHeaderClampedLValue_String(bus)); + +void SoundPlayer::RemoveSoundList(detail::BasicSound *sound) +{ + detail::SoundThread::AutoLock lock; + + mSoundList.Erase(sound); + sound->DetachSoundPlayer(this); +} + +void SoundPlayer::InsertPriorityList(detail::BasicSound *sound) +{ + detail::SoundThread::AutoLock lock; + + decltype(mPriorityList.GetBeginIter()) itr = mPriorityList.GetBeginIter(); + for (; itr != mPriorityList.GetEndIter(); ++itr) + { + if (sound->CalcCurrentPlayerPriority() + < itr->CalcCurrentPlayerPriority()) + { + break; + } + } + + mPriorityList.Insert(itr, sound); +} + +void SoundPlayer::RemovePriorityList(detail::BasicSound *sound) +{ + detail::SoundThread::AutoLock lock; + + mPriorityList.Erase(sound); +} + +void SoundPlayer::detail_SortPriorityList(detail::BasicSound *sound) +{ + RemovePriorityList(sound); + InsertPriorityList(sound); +} + +void SoundPlayer::detail_SortPriorityList() +{ + detail::SoundThread::AutoLock lock; + + if (mPriorityList.GetSize() < 2) + return; + + static detail::BasicSound::SoundPlayerPriorityLinkList + tmplist[detail::BasicSound::PRIORITY_MAX + 1]; + + while (!mPriorityList.IsEmpty()) + { + detail::BasicSound &front = mPriorityList.GetFront(); + mPriorityList.PopFront(); + tmplist[front.CalcCurrentPlayerPriority()].PushBack(&front); + } + + for (int i = 0; i < (int)ARRAY_LENGTH(tmplist); i++) + { + while (!tmplist[i].IsEmpty()) + { + detail::BasicSound &front = tmplist[i].GetFront(); + + tmplist[i].PopFront(); + mPriorityList.PushBack(&front); + } + } +} + +bool SoundPlayer::detail_AppendSound(detail::BasicSound *sound) +{ + NW4RAssertPointerNonnull_Line(402, sound); + + detail::SoundThread::AutoLock lock; + + int allocPriority = sound->CalcCurrentPlayerPriority(); + + if (GetPlayableSoundCount() == 0) + return false; + + while (GetPlayingSoundCount() >= GetPlayableSoundCount()) + { + detail::BasicSound *dropSound = GetLowestPrioritySound(); + if (!dropSound) + return false; + + if (allocPriority < dropSound->CalcCurrentPlayerPriority()) + return false; + + dropSound->Shutdown(); + } + + mSoundList.PushBack(sound); + InsertPriorityList(sound); + sound->AttachSoundPlayer(this); + + return true; +} + +void SoundPlayer::detail_RemoveSound(detail::BasicSound *sound) +{ + RemovePriorityList(sound); + RemoveSoundList(sound); +} + +void SoundPlayer::SetPlayableSoundCount(int count) +{ + NW4RAssert_Line(453, count >= 0); + + detail::SoundThread::AutoLock lock; + + NW4RCheckMessage_Line(458, count <= mPlayableLimit, + "playable sound count is over limit."); + + mPlayableCount = ut::Clamp(count, 0, mPlayableLimit); + + while (GetPlayingSoundCount() > GetPlayableSoundCount()) + { + detail::BasicSound *dropSound = GetLowestPrioritySound(); + NW4RAssertPointerNonnull_Line(467, dropSound); + + dropSound->Shutdown(); + } +} + +void SoundPlayer::detail_SetPlayableSoundLimit(int limit) +{ + NW4RAssert_Line(483, limit >= 0); + + mPlayableLimit = limit; +} + +bool SoundPlayer::detail_CanPlaySound(int startPriority) +{ + detail::SoundThread::AutoLock lock; + + if (GetPlayableSoundCount() == 0) + return false; + + if (GetPlayingSoundCount() >= GetPlayableSoundCount()) + { + detail::BasicSound *dropSound = GetLowestPrioritySound(); + if (!dropSound) + return false; + + if (startPriority < dropSound->CalcCurrentPlayerPriority()) + return false; + } + + return true; +} + +void SoundPlayer::detail_AppendPlayerHeap(detail::PlayerHeap *heap) +{ + NW4RAssertPointerNonnull_Line(524, heap); + + detail::SoundThread::AutoLock lock; + + heap->AttachSoundPlayer(this); + mHeapList.PushBack(heap); +} + +detail::PlayerHeap *SoundPlayer::detail_AllocPlayerHeap(detail::BasicSound *sound) +{ + NW4RAssertPointerNonnull_Line(557, sound); + + detail::SoundThread::AutoLock lock; + + if (mHeapList.IsEmpty()) + return nullptr; + + detail::PlayerHeap &playerHeap = mHeapList.GetFront(); + mHeapList.PopFront(); + + playerHeap.AttachSound(sound); + sound->AttachPlayerHeap(&playerHeap); + playerHeap.Clear(); + + return &playerHeap; +} + +void SoundPlayer::detail_FreePlayerHeap(detail::BasicSound *sound) +{ + NW4RAssertPointerNonnull_Line(587, sound); + + detail::SoundThread::AutoLock lock; + + detail::PlayerHeap *heap = sound->GetPlayerHeap(); + if (!heap) + return; + + heap->DetachSound(sound); + sound->DetachPlayerHeap(heap); + mHeapList.PushBack(heap); +} + +}} // namespace nw4r::snd diff --git a/src/nw4r/snd/snd_SoundStartable.cpp b/src/nw4r/snd/snd_SoundStartable.cpp index 2d5a8f4c..236a1220 100644 --- a/src/nw4r/snd/snd_SoundStartable.cpp +++ b/src/nw4r/snd/snd_SoundStartable.cpp @@ -1 +1,52 @@ -#include "nw4r/snd/snd_SoundStartable.h" +#include "nw4r/snd/SoundStartable.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_SoundStartable.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <types.h> // u32 + +#include "nw4r/snd/BasicSound.h" +#include "nw4r/snd/SoundHandle.h" + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { + +SoundStartable::StartResult SoundStartable::detail_StartSound( + SoundHandle *handle, u32 soundId, StartInfo const *startInfo) +{ + StartResult result = detail_SetupSound(handle, soundId, false, startInfo); + if (result != START_SUCCESS) + return result; + + handle->StartPrepared(); + return START_SUCCESS; +} + +SoundStartable::StartResult SoundStartable::detail_HoldSound( + SoundHandle *handle, u32 soundId, StartInfo const *startInfo) +{ + if (handle->IsAttachedSound() && soundId == handle->GetId()) + { + handle->detail_GetAttachedSound()->SetAutoStopCounter(1); + return START_SUCCESS; + } + + StartResult result = detail_SetupSound(handle, soundId, true, startInfo); + if (result != START_SUCCESS) + return result; + + handle->StartPrepared(); + handle->detail_GetAttachedSound()->SetAutoStopCounter(1); + return START_SUCCESS; +} + +}} // namespace nw4r::snd diff --git a/src/nw4r/snd/snd_SoundSystem.cpp b/src/nw4r/snd/snd_SoundSystem.cpp index 6510587b..9a6d592a 100644 --- a/src/nw4r/snd/snd_SoundSystem.cpp +++ b/src/nw4r/snd/snd_SoundSystem.cpp @@ -1 +1,221 @@ -#include "nw4r/snd/snd_SoundSystem.h" +#include "nw4r/snd/SoundSystem.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_SoundSystem.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <decomp.h> +#include <macros.h> // STR +#include <types.h> + +#include "nw4r/snd/AxVoiceManager.h" +#include "nw4r/snd/AxManager.h" +#include "nw4r/snd/Channel.h" // ChannelManager +#include "nw4r/snd/global.h" +#include "nw4r/snd/SeqPlayer.h" +#include "nw4r/snd/SoundThread.h" +#include "nw4r/snd/TaskManager.h" +#include "nw4r/snd/TaskThread.h" +#include "nw4r/snd/VoiceManager.h" + +#if 0 +#include <revolution/OS/OS.h> // OSRegisterVersion +#include <revolution/AX/AXVPB.h> // AXGetMaxVoices +#include <revolution/SC/scsystem.h> +#include <revolution/SC/scapi.h> +#else +#include <context_rvl.h> +#endif + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * macros + */ + +#define BUILDSTAMP_LIB_GROUP_NAME "NW4R " +#define BUILDSTAMP_LIB_NAME "SND" + +// You probably want to change these +#define BUILDSTAMP_BUILD_TYPE "release" +#define BUILDSTAMP_DATE __DATE__ +#define BUILDSTAMP_TIME __TIME__ +#define BUILDSTAMP_CW_MAJOR_REV STR(__CWCC__) +#define BUILDSTAMP_CW_MINOR_REV STR(__CWBUILD__) + +#define BUILDSTAMP_STRING \ + "<< " \ + BUILDSTAMP_LIB_GROUP_NAME " - " BUILDSTAMP_LIB_NAME " \t" \ + BUILDSTAMP_BUILD_TYPE " build: " \ + BUILDSTAMP_DATE " " BUILDSTAMP_TIME \ + " (" BUILDSTAMP_CW_MAJOR_REV "_" BUILDSTAMP_CW_MINOR_REV ")" \ + " >>" + +/******************************************************************************* + * variables + */ + +namespace nw4r { namespace snd +{ + // .data, .sdata + extern "C" char const *NW4R_SND_Version_ = BUILDSTAMP_STRING; + + // .bss + detail::TaskThread SoundSystem::sTaskThread; + + // .sbss + namespace + { + bool sInitialized; + } // unnamed namespace + + int SoundSystem::sMaxVoices; +}} // namespace nw4r::snd + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { + +u32 SoundSystem::GetRequiredMemSize(SoundSystemParam const ¶m) +{ + // could have just used align assert? idk + NW4RAssert_Line(106, param.soundThreadStackSize % 8 == 0); + NW4RAssert_Line(107, param.dvdThreadStackSize % 8 == 0); + + int maxVoices = AXGetMaxVoices(); + + return param.soundThreadStackSize + param.dvdThreadStackSize + + detail::AxVoiceManager::GetInstance().GetRequiredMemSize(maxVoices) + + detail::VoiceManager::GetInstance().GetRequiredMemSize(maxVoices) + + detail::ChannelManager::GetInstance().GetRequiredMemSize(maxVoices); +} + +void SoundSystem::InitSoundSystem(SoundSystemParam const ¶m, void *workMem, + u32 workMemSize) +{ + bool result; // presumably up here + + NW4RAssertAligned_Line(144, workMem, 32); + NW4RAssert_Line(145, workMemSize >= GetRequiredMemSize( param )); + + if (sInitialized) + return; + + sInitialized = true; + + OSRegisterVersion(NW4R_SND_Version_); + + detail::AxManager::GetInstance().Init(); + + SCInit(); + + SCStatus initStatus; + do + initStatus = SCCheckStatus(); + while (initStatus == SC_STATUS_BUSY); + + NW4RAssert_Line(171, initStatus == SC_STATUS_OK); + + SCSoundMode soundMode = SCGetSoundMode(); + switch (soundMode) + { + case SC_SND_MONO: + detail::AxManager::GetInstance().SetOutputMode(OUTPUT_MODE_MONO); + break; + + case SC_SND_STEREO: + detail::AxManager::GetInstance().SetOutputMode(OUTPUT_MODE_STEREO); + break; + + case SC_SND_SURROUND: + detail::AxManager::GetInstance().SetOutputMode(OUTPUT_MODE_DPL2); + break; + + default: + detail::AxManager::GetInstance().SetOutputMode(OUTPUT_MODE_STEREO); + break; + } + + byte_t *ptr = static_cast<byte_t *>(workMem); + + void *dvdThreadStack = ptr; + ptr += param.dvdThreadStackSize; + + void *soundThreadStack = ptr; + ptr += param.soundThreadStackSize; + + sMaxVoices = AXGetMaxVoices(); + + void *axVoiceWork = ptr; + ptr += detail::AxVoiceManager::GetInstance().GetRequiredMemSize( + sMaxVoices); + + detail::AxVoiceManager::GetInstance().Setup( + axVoiceWork, + detail::AxVoiceManager::GetInstance().GetRequiredMemSize( + sMaxVoices)); + + void *voiceWork = ptr; + ptr += detail::VoiceManager::GetInstance().GetRequiredMemSize(sMaxVoices); + + detail::VoiceManager::GetInstance().Setup( + voiceWork, + detail::VoiceManager::GetInstance().GetRequiredMemSize(sMaxVoices)); + + void *channelWork = ptr; + ptr += detail::ChannelManager::GetInstance().GetRequiredMemSize( + sMaxVoices); + + detail::ChannelManager::GetInstance().Setup( + channelWork, + detail::ChannelManager::GetInstance().GetRequiredMemSize( + sMaxVoices)); + + detail::SeqPlayer::InitSeqPlayer(); + + result = sTaskThread.Create(param.dvdThreadPriority, dvdThreadStack, + param.dvdThreadStackSize); + NW4RAssert_Line(247, result); + + result = detail::SoundThread::GetInstance().Create( + param.soundThreadPriority, soundThreadStack, + param.soundThreadStackSize); + NW4RAssert_Line(255, result); + + NW4RAssert_Line(257, ptr <= reinterpret_cast<u8*>( workMem ) + workMemSize); +} + +void SoundSystem::ShutdownSoundSystem() +{ + if (!sInitialized) + return; + + detail::SoundThread::GetInstance().Shutdown(); + + detail::TaskManager::GetInstance().CancelAllTask(); + sTaskThread.Destroy(); + + detail::ChannelManager::GetInstance().Shutdown(); + detail::VoiceManager::GetInstance().Shutdown(); + detail::AxVoiceManager::GetInstance().Shutdown(); + detail::AxManager::GetInstance().Shutdown(); + + sInitialized = false; +} + +bool SoundSystem::IsInitializedSoundSystem() +{ + return sInitialized; +} + +// SoundSystem::WaitForResetReady ([R89JEL]:/bin/RVL/Debug/mainD.MAP:14493) +DECOMP_FORCE("SoundSystem::WaitForResetReady is TIME OUT.\n"); + +}} // namespace nw4r::snd diff --git a/src/nw4r/snd/snd_SoundThread.cpp b/src/nw4r/snd/snd_SoundThread.cpp index 3a8b3a65..7477deb2 100644 --- a/src/nw4r/snd/snd_SoundThread.cpp +++ b/src/nw4r/snd/snd_SoundThread.cpp @@ -1 +1,220 @@ -#include "nw4r/snd/snd_SoundThread.h" +#include "nw4r/snd/SoundThread.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_SoundThread.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <decomp.h> +#include <macros.h> +#include <types.h> + +#include "nw4r/snd/AxManager.h" +#include "nw4r/snd/AxVoiceManager.h" +#include "nw4r/snd/Channel.h" // ChannelManager +#include "nw4r/snd/Util.h" // Util::CalcRandom +#include "nw4r/snd/VoiceManager.h" + +#include "nw4r/ut/Lock.h" // ut::detail::AutoLock + +#if 0 +#include <revolution/OS/OSMessage.h> +#include <revolution/OS/OSMutex.h> +#include <revolution/OS/OSThread.h> +#include <revolution/OS/OSTime.h> +#else +#include <context_rvl.h> +#endif + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +SoundThread::SoundThread() : + mStackEnd (nullptr), + mCreateFlag (false) +{ + OSInitMessageQueue(&mMsgQueue, mMsgBuffer, THREAD_MESSAGE_BUFSIZE); + OSInitThreadQueue(&mThreadQueue); + OSInitMutex(&mMutex); +} + +SoundThread &SoundThread::GetInstance() +{ + static SoundThread instance; + + return instance; +} + +bool SoundThread::Create(s32 priority, void *stack, u32 stackSize) +{ + NW4RAssertMessage_Line(78, AxManager::GetInstance().CheckInit(), + "not initialized nw4r::AxManager.\n"); + NW4RAssertPointerNonnull_Line(79, stack); + NW4RAssertAligned_Line(80, stack, 4); + + if (mCreateFlag) + return true; + + mCreateFlag = true; + mStackEnd = static_cast<byte4_t *>(stack); + + BOOL result = OSCreateThread(&mThread, &SoundThreadFunc, &GetInstance(), + static_cast<byte_t *>(stack) + stackSize, + stackSize, priority, OS_THREAD_NO_FLAGS); + + if (result) + OSResumeThread(&mThread); + + return result; +} + +void SoundThread::Shutdown() +{ + if (!mCreateFlag) + return; + + BOOL result = OSJamMessageAny(&GetInstance().mMsgQueue, MESSAGE_SHUTDOWN, + OS_MESSAGE_FLAG_PERSISTENT); + NW4RAssert_Line(124, result); + + result = OSJoinThread(&mThread, nullptr); + NW4RAssert_Line(128, result); + + mCreateFlag = false; +} + +void SoundThread::AxCallbackFunc() +{ + SoundThread *soundThread = &GetInstance(); + + soundThread->AxCallbackProc(); +} + +void SoundThread::AxCallbackProc() +{ + BOOL result ATTR_UNUSED = + OSSendMessageAny(&mMsgQueue, MESSAGE_AX_CALLBACK, OS_MESSAGE_NO_FLAGS); + + NW4R_RANGE_FOR_NO_AUTO_INC(itr, mPlayerCallbackList) + { + decltype(itr) curItr = itr++; + + curItr->OnUpdateVoiceSoundThread(); + } + + VoiceManager::GetInstance().NotifyVoiceUpdate(); +} + +void *SoundThread::SoundThreadFunc(void *arg) +{ + SoundThread *th = static_cast<SoundThread *>(arg); + + AxManager::GetInstance().RegisterCallback(&th->mAxCallbackNode, + &AxCallbackFunc); + + th->SoundThreadProc(); + + AxManager::GetInstance().UnregisterCallback(&th->mAxCallbackNode); + + return nullptr; +} + +/* SoundThread::RegisterSoundFrameCallback + * ([R89JEL]:/bin/RVL/Debug/mainD.MAP:14509) + */ +DECOMP_FORCE_CLASS_METHOD(SoundThread::SoundFrameCallback::LinkList, + PushBack(nullptr)); + +/* SoundThread::UnregisterSoundFrameCallback + * ([R89JEL]:/bin/RVL/Debug/mainD.MAP:14510) + */ +DECOMP_FORCE_CLASS_METHOD(SoundThread::SoundFrameCallback::LinkList, + Erase(nullptr)); + +void SoundThread::RegisterPlayerCallback(PlayerCallback *callback) +{ + ut::detail::AutoLock<OSMutex> lock(mMutex); + + mPlayerCallbackList.PushBack(callback); +} + +void SoundThread::UnregisterPlayerCallback(PlayerCallback *callback) +{ + ut::detail::AutoLock<OSMutex> lock(mMutex); + + mPlayerCallbackList.Erase(callback); +} + +void SoundThread::SoundThreadProc() +{ + OSMessage message; + + while (true) + { + OSReceiveMessage(&mMsgQueue, &message, OS_MESSAGE_FLAG_PERSISTENT); + + if (reinterpret_cast<register_t>(message) == MESSAGE_AX_CALLBACK) + { + ut::detail::AutoLock<OSMutex> lock(mMutex); + + NW4R_RANGE_FOR_NO_AUTO_INC(itr, mSoundFrameCallbackList) + { + decltype(itr) curItr = itr++; + + curItr->at_0x0c(); + } + + OSTick tick = OSGetTick(); + + { + // Sound frame + AxVoiceManager::GetInstance().FreeAllReservedAxVoice(); + AxManager::GetInstance().Update(); + + NW4R_RANGE_FOR_NO_AUTO_INC(itr, mPlayerCallbackList) + { + decltype(itr) curItr = itr++; + + curItr->OnUpdateFrameSoundThread(); + } + + ChannelManager::GetInstance().UpdateAllChannel(); + (void)Util::CalcRandom(); // ? + VoiceManager::GetInstance().UpdateAllVoices(); + } + + mProcessTick = OSGetTick() - tick; + + NW4R_RANGE_FOR_NO_AUTO_INC(itr, mSoundFrameCallbackList) + { + decltype(itr) curItr = itr++; + + curItr->at_0x10(); + } + } + else if (reinterpret_cast<register_t>(message) == MESSAGE_SHUTDOWN) + { + break; + } + + NW4RAssert_Line(313, *mStackEnd == OS_THREAD_STACK_MAGIC); + } + + NW4R_RANGE_FOR_NO_AUTO_INC(itr, mPlayerCallbackList) + { + decltype(itr) curItr = itr++; + + curItr->OnShutdownSoundThread(); + } +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_StrmChannel.cpp b/src/nw4r/snd/snd_StrmChannel.cpp index d8ef1022..6aa4db99 100644 --- a/src/nw4r/snd/snd_StrmChannel.cpp +++ b/src/nw4r/snd/snd_StrmChannel.cpp @@ -1 +1,111 @@ -#include "nw4r/snd/snd_StrmChannel.h" +#include "nw4r/snd/StrmChannel.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_StrmChannel.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <cstring> // std::memset + +#include <types.h> + +#include "nw4r/ut/inlines.h" +#include "nw4r/ut/Lock.h" // ut::AutoInterruptLock + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +void StrmBufferPool::Setup(void *buffer, u32 size, int blockCount) +{ + if (!blockCount) + return; + + ut::AutoInterruptLock lock; + + mBuffer = buffer; + mBufferSize = size; + mBlockSize = size / blockCount; + mBlockCount = blockCount; + mAllocCount = 0; + std::memset(mAllocFlags, 0, sizeof mAllocFlags); + + NW4RAssertMessage_Line(42, mBlockCount <= BLOCK_MAX, + "Too large stream buffer size."); +} + +void StrmBufferPool::Shutdown() +{ + ut::AutoInterruptLock lock; + + mBuffer = nullptr; + mBufferSize = 0; + mBlockSize = 0; + mBlockCount = 0; +} + +void *StrmBufferPool::Alloc() +{ + ut::AutoInterruptLock lock; + + if (mAllocCount >= mBlockCount) + return nullptr; + + int availableByte = ut::RoundUp(mBlockCount, BIT_PER_BYTE) / BIT_PER_BYTE; + + for (int byteIndex = 0; byteIndex < availableByte; byteIndex++) + { + byte_t byte = static_cast<byte_t>(mAllocFlags[byteIndex]); + + // All blocks allocated in this flag set + if (byte == 0xff) + continue; + + byte_t mask = 1 << 0; + + for (int bitIndex = 0; bitIndex < BIT_PER_BYTE; bitIndex++, mask <<= 1) + { + // Block represented by this bit is in use + if (byte & mask) + continue; + + mAllocFlags[byteIndex] |= mask; + mAllocCount++; + + int totalIndex = byteIndex * BIT_PER_BYTE + bitIndex; + + return ut::AddOffsetToPtr(mBuffer, mBlockSize * totalIndex); + } + } + + return nullptr; +} + +void StrmBufferPool::Free(void *p) +{ + ut::AutoInterruptLock lock; + + s32 offset = ut::GetOffsetFromPtr(mBuffer, p); + u32 totalIndex = offset / mBlockSize; + NW4RAssert_Line(92, totalIndex < BLOCK_MAX); + + u32 byteIndex = totalIndex / BIT_PER_BYTE; + u32 bitIndex = totalIndex % BIT_PER_BYTE; + int mask = 1 << bitIndex; + + NW4RAssert_Line(97, ( mAllocFlags[ byteIndex ] & mask ) != 0); + mAllocFlags[byteIndex] &= ~mask; + + mAllocCount--; + NW4RAssert_Line(100, mAllocCount >= 0); +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_StrmFile.cpp b/src/nw4r/snd/snd_StrmFile.cpp index 938e397c..8283cf45 100644 --- a/src/nw4r/snd/snd_StrmFile.cpp +++ b/src/nw4r/snd/snd_StrmFile.cpp @@ -1 +1,360 @@ -// #include "nw4r/snd/snd_StrmFile.h" +#include "nw4r/snd/StrmFile.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_StrmFile.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <macros.h> +#include <types.h> + +#include "nw4r/snd/adpcm.h" +#include "nw4r/snd/global.h" +#include "nw4r/snd/Util.h" +#include "nw4r/snd/WaveFile.h" + +#include "nw4r/ut/binaryFileFormat.h" +#include "nw4r/ut/FileStream.h" +#include "nw4r/ut/inlines.h" + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +bool StrmFileReader::IsValidFileHeader(void const *strmData) +{ + NW4RAssertPointerNonnull_Line(42, strmData); + + ut::BinaryFileHeader const *fileHeader = + static_cast<ut::BinaryFileHeader const *>(strmData); + + NW4RAssertMessage_Line( + 51, fileHeader->signature == StrmFile::SIGNATURE_FILE, + "invalid file signature. strm data is not available."); + + if (fileHeader->signature != StrmFile::SIGNATURE_FILE) + return false; + + NW4RAssertMessage_Line(59, fileHeader->version >= NW4R_FILE_VERSION(1, 0), + "strm file is not supported version.\n" + " please reconvert file using new version tools.\n"); + + if (fileHeader->version < NW4R_FILE_VERSION(1, 0)) + return false; + + NW4RAssertMessage_Line(65, fileHeader->version <= SUPPORTED_FILE_VERSION, + "strm file is not supported version.\n" + " please reconvert file using new version tools.\n"); + + if (fileHeader->version > SUPPORTED_FILE_VERSION) + return false; + + return true; +} + +StrmFileReader::StrmFileReader() : + mHeader (nullptr), + mHeadBlock (nullptr) +{ +} + +void StrmFileReader::Setup(void const *strmData) +{ + NW4RAssertPointerNonnull_Line(97, strmData); + + if (!IsValidFileHeader(strmData)) + return; + + mHeader = static_cast<StrmFile::Header const *>(strmData); + mHeadBlock = static_cast<StrmFile::HeadBlock const *>( + ut::AddOffsetToPtr(mHeader, mHeader->headBlockOffset)); + + NW4RAssert_Line(106, mHeadBlock->blockHeader.kind + == StrmFile::SIGNATURE_HEAD_BLOCK); + + StrmFile::StrmDataInfo const *info = Util::GetDataRefAddress0( + mHeadBlock->refDataHeader, &mHeadBlock->refDataHeader); + + // definitely could have just used align assert here + NW4RAssert_Line(113, info->blockSize % 32 == 0); +} + +int StrmFileReader::GetTrackCount() const +{ + NW4RAssertPointerNonnull_Line(142, mHeader); + + StrmFile::TrackTable const *trackTable = Util::GetDataRefAddress0( + mHeadBlock->refTrackTable, &mHeadBlock->refDataHeader); + + return trackTable->trackCount; +} + +int StrmFileReader::GetChannelCount() const +{ + NW4RAssertPointerNonnull_Line(163, mHeader); + + StrmFile::ChannelTable const *channelTable = Util::GetDataRefAddress0( + mHeadBlock->refChannelTable, &mHeadBlock->refDataHeader); + + return channelTable->channelCount; +} + +bool StrmFileReader::ReadStrmInfo(StrmInfo *strmInfo) const +{ + NW4RAssertPointerNonnull_Line(184, mHeader); + + StrmFile::StrmDataInfo const *info = Util::GetDataRefAddress0( + mHeadBlock->refDataHeader, &mHeadBlock->refDataHeader); + + NW4RAssertAligned_Line(192, info->blockHeaderOffset, 32); + NW4RAssertAligned_Line(193, info->blockSize, 32); + NW4RAssertAligned_Line(194, info->lastBlockPaddedSize, 32); + + // clang-format off + strmInfo->sampleFormat = GetSampleFormatFromStrmFileFormat(info->format); + strmInfo->loopFlag = info->loopFlag; + strmInfo->numChannels = info->numChannels; + strmInfo->sampleRate = (info->sampleRate24 << 16) + info->sampleRate; + strmInfo->blockHeaderOffset = info->blockHeaderOffset; + strmInfo->loopStart = info->loopStart; + strmInfo->loopEnd = info->loopEnd; + strmInfo->dataOffset = info->dataOffset; + strmInfo->numBlocks = info->numBlocks; + strmInfo->blockSize = info->blockSize; + strmInfo->blockSamples = info->blockSamples; + strmInfo->lastBlockSize = info->lastBlockSize; + strmInfo->lastBlockSamples = info->lastBlockSamples; + strmInfo->lastBlockPaddedSize = info->lastBlockPaddedSize; + strmInfo->adpcmDataInterval = info->adpcmDataInterval; + strmInfo->adpcmDataSize = info->adpcmDataSize; + // clang-format on + + return true; +} + +bool StrmFileReader::ReadStrmTrackInfo(StrmTrackInfo *trackInfo, + int trackIndex) const +{ + NW4RAssertPointerNonnull_Line(218, mHeader); + + StrmFile::TrackTable const *trackTable = Util::GetDataRefAddress0( + mHeadBlock->refTrackTable, &mHeadBlock->refDataHeader); + if (trackIndex >= trackTable->trackCount) + return false; + + switch (trackTable->trackDataType) + { + case 0: + { + StrmFile::TrackInfo const *src = Util::GetDataRefAddress0( + trackTable->refTrackHeader[trackIndex], &mHeadBlock->refDataHeader); + if (!src) + return false; + + trackInfo->volume = 127; + trackInfo->pan = 64; + trackInfo->channelCount = src->channelCount; + + int count = ut::Min(trackInfo->channelCount, 32); + + for (int i = 0; i < count; i++) + trackInfo->channelIndexTable[i] = src->channelIndexTable[i]; + } + break; + + case 1: + { + StrmFile::TrackInfoEx const *src = Util::GetDataRefAddress1( + trackTable->refTrackHeader[trackIndex], &mHeadBlock->refDataHeader); + if (!src) + return false; + + trackInfo->volume = src->volume; + trackInfo->pan = src->pan; + trackInfo->channelCount = src->channelCount; + + int count = ut::Min(trackInfo->channelCount, 32); + + for (int i = 0; i < count; i++) + trackInfo->channelIndexTable[i] = src->channelIndexTable[i]; + } + break; + + default: + NW4RPanic_Line(268); + // return false here for NDEBUG? + break; + } + + return true; +} + +bool StrmFileReader::ReadAdpcmInfo(AdpcmParam *adpcmParam, + AdpcmLoopParam *adpcmLoopParam, + int channelIndex) const +{ + NW4RAssertPointerNonnull_Line(289, mHeader); + NW4RAssertPointerNonnull_Line(290, adpcmParam); + NW4RAssertPointerNonnull_Line(291, adpcmLoopParam); + + StrmFile::StrmDataInfo const *info = Util::GetDataRefAddress0( + mHeadBlock->refDataHeader, &mHeadBlock->refDataHeader); + if (info->format != 2) + return false; + + StrmFile::ChannelTable const *channelTable = Util::GetDataRefAddress0( + mHeadBlock->refChannelTable, &mHeadBlock->refDataHeader); + if (channelIndex >= channelTable->channelCount) + return false; + + StrmFile::ChannelInfo const *channelInfo = + Util::GetDataRefAddress0(channelTable->refChannelHeader[channelIndex], + &mHeadBlock->refDataHeader); + + StrmFile::AdpcmParamSet const *src = Util::GetDataRefAddress0( + channelInfo->refAdpcmInfo, &mHeadBlock->refDataHeader); + + *adpcmParam = src->adpcmParam; + *adpcmLoopParam = src->adpcmLoopParam; + + return true; +} + +SampleFormat StrmFileReader::GetSampleFormatFromStrmFileFormat(u8 format) +{ + switch (format) + { + case 2: + return SAMPLE_FORMAT_DSP_ADPCM; + + case 1: + return SAMPLE_FORMAT_PCM_S16; + + case 0: + return SAMPLE_FORMAT_PCM_S8; + + default: + NW4RPanicMessage_Line(333, "Unknown strm data format %d", format); + return SAMPLE_FORMAT_DSP_ADPCM; + } +} + +bool StrmFileLoader::LoadFileHeader(void *buffer, u32 size) +{ + byte_t buffer2[32 + ROUND_UP(sizeof(StrmFile::Header), 0x20)]; + + mStream.Seek(0, ut::FileStream::SEEK_ORIGIN_SET); + + s32 readSize = mStream.Read(ut::RoundUp(buffer2, 32), + ROUND_UP(sizeof(StrmFile::Header), 0x20)); + if (readSize != ROUND_UP(sizeof(StrmFile::Header), 0x20)) + return false; + + StrmFile::Header *header = + static_cast<StrmFile::Header *>(ut::RoundUp(buffer2, 32)); + + StrmFileReader reader; + if (!reader.IsValidFileHeader(header)) + return false; + + if (header->adpcBlockOffset > size) + return false; + + u32 loadSize = header->headBlockOffset + header->headBlockSize; + + mStream.Seek(0, ut::FileStream::SEEK_ORIGIN_SET); + + readSize = mStream.Read(buffer, loadSize); + if (readSize != loadSize) + return false; + + mReader.Setup(buffer); + + return true; +} + +int StrmFileLoader::GetTrackCount() const +{ + if (!mReader.IsAvailable()) + return 0; + + return mReader.GetTrackCount(); +} + +int StrmFileLoader::GetChannelCount() const +{ + if (!mReader.IsAvailable()) + return 0; + + return mReader.GetChannelCount(); +} + +bool StrmFileLoader::ReadStrmInfo(StrmFileReader::StrmInfo *strmInfo) const +{ + if (!mReader.IsAvailable()) + return false; + + mReader.ReadStrmInfo(strmInfo); + return true; +} + +bool StrmFileLoader::ReadStrmTrackInfo(StrmFileReader::StrmTrackInfo *trackInfo, + int trackIndex) const +{ + if (!mReader.IsAvailable()) + return false; + + mReader.ReadStrmTrackInfo(trackInfo, trackIndex); + return true; +} + +bool StrmFileLoader::ReadAdpcmInfo(AdpcmParam *adpcmParam, + AdpcmLoopParam *adpcmLoopParam, + int channelIndex) const +{ + if (!mReader.IsAvailable()) + return false; + + mReader.ReadAdpcmInfo(adpcmParam, adpcmLoopParam, channelIndex); + return true; +} + +bool StrmFileLoader::ReadAdpcBlockData(u16 *yn1, u16 *yn2, int blockIndex, + int channelCount) +{ + if (!mReader.IsAvailable()) + return false; + + s32 readOffset = mReader.GetAdpcBlockOffset() + + blockIndex * channelCount * (sizeof(u16) * 2) + + sizeof(ut::BinaryBlockHeader); + + mStream.Seek(readOffset, ut::FileStream::SEEK_ORIGIN_SET); + + u32 readDataSize = channelCount * (sizeof(u16) * 2); + NW4RAssert_Line(499, readDataSize <= 32); + + alignas(32) u16 buffer[2 * 8]; + + int readSize = mStream.Read(buffer, sizeof buffer); + if (readSize != 32u) + return false; + + for (int i = 0; i < channelCount; i++) + { + yn1[i] = buffer[i * 2]; + yn2[i] = buffer[i * 2 + 1]; + } + + return true; +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_StrmPlayer.cpp b/src/nw4r/snd/snd_StrmPlayer.cpp index 9dc713ef..51b04a81 100644 --- a/src/nw4r/snd/snd_StrmPlayer.cpp +++ b/src/nw4r/snd/snd_StrmPlayer.cpp @@ -1 +1,1448 @@ -#include "nw4r/snd/snd_StrmPlayer.h" +#include "nw4r/snd/StrmPlayer.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_StrmPlayer.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <climits> // LONG_MAX +#include <cstring> // std::memcpy + +#include <decomp.h> +#include <macros.h> // ATTR_UNUSED +#include <types.h> + +#include "nw4r/snd/StrmSound.h" +#include "nw4r/snd/adpcm.h" +#include "nw4r/snd/AxVoice.h" +#include "nw4r/snd/BasicPlayer.h" +#include "nw4r/snd/Channel.h" +#include "nw4r/snd/global.h" +#include "nw4r/snd/InstancePool.h" +#include "nw4r/snd/SoundThread.h" +#include "nw4r/snd/StrmChannel.h" +#include "nw4r/snd/StrmFile.h" +#include "nw4r/snd/TaskManager.h" +#include "nw4r/snd/Voice.h" +#include "nw4r/snd/VoiceManager.h" +#include "nw4r/snd/WaveFile.h" + +#include "nw4r/ut/DvdFileStream.h" +#include "nw4r/ut/FileStream.h" +#include "nw4r/ut/inlines.h" +#include "nw4r/ut/Lock.h" +#include "nw4r/ut/RuntimeTypeInfo.h" + +#if 0 +#include <revolution/OS/OSCache.h> +#include <revolution/DVD/dvd.h> +#else +#include <context_rvl.h> +#endif + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * variables + */ + +namespace nw4r { namespace snd { namespace detail +{ + // .bss + byte_t StrmPlayer::sLoadBuffer[LOAD_BUFFER_SIZE]; + OSMutex StrmPlayer::sLoadBufferMutex; + + // .sbss + bool StrmPlayer::sStaticInitFlag; +}}} // namespace nw4r::snd::detail + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +StrmPlayer::StrmPlayer() : + mSetupFlag (false), + mActiveFlag (false), + mFileStream (nullptr) +{ + if (!sStaticInitFlag) + { + OSInitMutex(&sLoadBufferMutex); + sStaticInitFlag = true; + } + + u32 taskCount = mStrmDataLoadTaskPool.Create(mStrmDataLoadTaskArea, + sizeof mStrmDataLoadTaskArea); + + NW4RAssert_Line(70, taskCount == BUFFER_BLOCK_COUNT_MAX); +} + +StrmPlayer::~StrmPlayer() +{ + Shutdown(); +} + +StrmPlayer::SetupResult StrmPlayer::Setup(StrmBufferPool *bufferPool, + int allocChannelCount, + byte2_t allocTrackFlag, + int voiceOutCount) +{ + SoundThread::AutoLock lock; + + NW4RAssertPointerNonnull_Line(105, bufferPool); + + if (mSetupFlag) + Shutdown(); + + InitParam(); + mChannelCount = ut::Min(allocChannelCount, STRM_CHANNEL_NUM); + + byte4_t bitMask = allocTrackFlag; + + int trackIndex; + for (trackIndex = 0; bitMask; bitMask >>= 1, trackIndex++) + { + if (!(bitMask & 1)) + continue; + + if (trackIndex >= 8) + { + NW4RWarningMessage_Line( + 133, "Too large track index (%d). Max track index is %d.", + trackIndex, STRM_TRACK_NUM - 1); + + break; + } + + mTracks[trackIndex].mActiveFlag = true; + } + + mTrackCount = ut::Min(trackIndex, STRM_TRACK_NUM); + if (mTrackCount == 0) + return SETUP_ERR_UNKNOWN; + + mVoiceOutCount = voiceOutCount; + mBufferPool = bufferPool; + + { + ut::AutoInterruptLock lockIntr; + + if (mChannelCount > 0) + { + if (!AllocStrmBuffers()) + return SETUP_ERR_CANNOT_ALLOCATE_BUFFER; + mAllocStrmBufferFlag = true; + } + } + + mSetupFlag = true; + + return SETUP_SUCCESS; +} + +void StrmPlayer::Shutdown() +{ + Stop(); + + SoundThread::AutoLock lock; + + if (!mSetupFlag) + return; + + mBufferPool = nullptr; + + NW4RAssert_Line(191, + mStrmDataLoadTaskPool.Count() == BUFFER_BLOCK_COUNT_MAX); + mStrmDataLoadTaskPool.Destroy(mStrmDataLoadTaskArea, + sizeof mStrmDataLoadTaskArea); + + mSetupFlag = false; +} + +bool StrmPlayer::Prepare(ut::FileStream *fileStream, + StartOffsetType startOffsetType, int startOffset) +{ + SoundThread::AutoLock lock; + + NW4RAssert_Line(218, mSetupFlag); + NW4RAssertPointerNonnull_Line(219, fileStream); + NW4RAssert_Line(220, fileStream->CanRead()); + NW4RAssert_Line(221, fileStream->CanSeek()); + + mFileStream = fileStream; + mStartOffsetType = startOffsetType; + mStartOffset = startOffset; + mTaskErrorFlag = false; + mTaskCancelFlag = false; + mLoadingDelayFlag = false; + mActiveFlag = true; + + SoundThread::GetInstance().RegisterPlayerCallback(this); + + StrmHeaderLoadTask *task = &mStrmHeaderLoadTask; + + task->player = this; + task->fileStream = mFileStream; + task->startOffsetType = mStartOffsetType; + task->startOffset = mStartOffset; + + TaskManager::GetInstance().AppendTask(task, TaskManager::PRIORITY_MIDDLE); + + return true; +} + +bool StrmPlayer::Start() +{ + SoundThread::AutoLock lock; + + if (!mPreparedFlag) + return false; + + if (!mStartedFlag) + { + if (!AllocVoices(mVoiceOutCount)) + { + FreeStrmBuffers(); + return false; + } + + s32 blockIndex = 0; + u32 blockOffset = 0; + s32 loopCount = 0; + + if (!CalcStartOffset(&blockIndex, &blockOffset, &loopCount)) + { + NW4RPanic_Line(276); + return false; + } + + mLoopCounter += loopCount; + + u32 sampleBufferLen = mDataBlockSize * mPlayingBufferBlockCount; + u32 sampleCount = + GetSampleByByte(sampleBufferLen, mStrmInfo.sampleFormat); + + for (int trackIndex = 0; trackIndex < mTrackCount; trackIndex++) + { + StrmTrack &track = mTracks[trackIndex]; + if (!track.mActiveFlag) + continue; + + WaveInfo waveData; + + waveData.sampleFormat = mStrmInfo.sampleFormat; + waveData.loopFlag = true; + waveData.numChannels = track.mTrackInfo.channelCount; + waveData.sampleRate = mStrmInfo.sampleRate; + waveData.loopStart = 0; + waveData.loopEnd = sampleCount; + + for (int channelIndex = 0; + channelIndex < track.mTrackInfo.channelCount; channelIndex++) + { + StrmChannel *channel = GetTrackChannel(track, channelIndex); + if (!channel) + continue; + + ChannelParam &channelParam = + waveData.channelParam[channelIndex]; + + channelParam.dataAddr = channel->bufferAddress; + channelParam.adpcmParam = channel->adpcmParam; + channelParam.adpcmLoopParam = channel->adpcmLoopParam; + channelParam.adpcmParam.pred_scale = + *static_cast<u8 *>(channel->bufferAddress); + } + + { + ut::AutoInterruptLock lock; + + if (track.mVoice) + { + track.mVoice->Setup(waveData, blockOffset); + track.mVoice->SetVoiceType(AxVoice::VOICE_TYPE_STREAM); + track.mVoice->Start(); + } + } + } + + if (blockIndex == mStrmInfo.numBlocks - 2) + UpdateDataLoopAddress(1); + else if (blockIndex == mStrmInfo.numBlocks - 1) + UpdateDataLoopAddress(0); + + UpdatePauseStatus(); + + mStartedFlag = true; + } + + return true; +} + +u32 StrmPlayer::GetSampleByByte(byte4_t byte, SampleFormat format) +{ + u32 samples = 0; + + switch (format) + { + case SAMPLE_FORMAT_DSP_ADPCM: + { + samples = (byte >> 3) * 14; + + if (u32 frac = byte & 0x07) + samples += (frac - 1) * 2; + } + break; + + case SAMPLE_FORMAT_PCM_S8: + samples = byte; + break; + + case SAMPLE_FORMAT_PCM_S16: + samples = byte / 2; + break; + + default: + NW4RPanicMessage_Line(368, "Invalid format\n"); + break; + } + + return samples; +} + +void StrmPlayer::Stop() +{ + { + SoundThread::AutoLock lock; + + for (int trackIndex = 0; trackIndex < STRM_TRACK_NUM; trackIndex++) + { + if (mTracks[trackIndex].mActiveFlag) + { + if (Voice *voice = mTracks[trackIndex].mVoice) + voice->Stop(); + } + } + + if (mActiveFlag) + SoundThread::GetInstance().UnregisterPlayerCallback(this); + } + + TaskManager::GetInstance().CancelTask(&mStrmHeaderLoadTask); + + { + ut::AutoInterruptLock lock; + + while (!mStrmDataLoadTaskList.IsEmpty()) + { + StrmDataLoadTask *task = &mStrmDataLoadTaskList.GetBack(); + + TaskManager::GetInstance().CancelTask(task); + } + } + + FreeStrmBuffers(); + FreeVoices(); + + { + SoundThread::AutoLock lock; + + if (mFileStream) + { + mFileStream->Close(); + mFileStream = nullptr; + } + } + + mStartedFlag = false; + mPreparedFlag = false; + mActiveFlag = false; + + NW4RAssert_Line(435, + mStrmDataLoadTaskPool.Count() == BUFFER_BLOCK_COUNT_MAX); +} + +void StrmPlayer::Pause(bool flag) +{ + SoundThread::AutoLock lock; + + mPauseFlag = flag; + + if (flag) + mLoadWaitFlag = true; + + UpdatePauseStatus(); +} + +// Some functions in between idk +DECOMP_FORCE(0.0f); +DECOMP_FORCE(SI2D_CONSTANT); +DECOMP_FORCE(UI2D_CONSTANT); + +void StrmPlayer::InitParam() +{ + BasicPlayer::InitParam(); + + mStartedFlag = false; + mPreparedFlag = false; + mLoadFinishFlag = false; + mPauseFlag = false; + mPauseStatus = false; + mLoadWaitFlag = false; + mNoRealtimeLoadFlag = false; + mPlayFinishFlag = false; + mSkipUpdateAdpcmLoop = false; + mValidAdpcmLoop = false; + mAllocStrmBufferFlag = false; + mLoopCounter = 0; + mVoiceOutCount = 1; + mLoadWaitCount = 0; + + for (int trackIndex = 0; trackIndex < STRM_TRACK_NUM; trackIndex++) + { + StrmTrack &track = mTracks[trackIndex]; + + track.mActiveFlag = false; + track.mVolume = 1.0f; + track.mPan = 0.0f; + track.mVoice = nullptr; + } + + for (int channelIndex = 0; channelIndex < STRM_CHANNEL_NUM; channelIndex++) + { + StrmChannel &channel = mChannels[channelIndex]; + + channel.bufferAddress = nullptr; + } +} + +#pragma push + +#pragma ppc_iro_level 0 // somehow this got turned off??? + +bool StrmPlayer::LoadHeader(ut::FileStream *fileStream, + StartOffsetType startOffsetType, int startOffset) +{ + NW4RAssertPointerNonnull_Line(619, fileStream); + + ut::detail::AutoLock<OSMutex> lock(sLoadBufferMutex); + + StrmFileLoader loader(*fileStream); + if (!loader.LoadFileHeader(sLoadBuffer, LOAD_BUFFER_SIZE)) + return false; + + if (!loader.ReadStrmInfo(&mStrmInfo)) + return false; + + if (mChannelCount == 0) + mChannelCount = ut::Min(loader.GetChannelCount(), STRM_CHANNEL_NUM); + + NW4RAssert_Line(643, mTrackCount == ut::Min( loader.GetTrackCount(), + STRM_TRACK_NUM )); + NW4RAssert_Line(644, mChannelCount == ut::Min( loader.GetChannelCount(), + STRM_CHANNEL_NUM )); + + for (int i = 0; i < mTrackCount; i++) + { + if (!loader.ReadStrmTrackInfo(&mTracks[i].mTrackInfo, i)) + return false; + } + + if (IsAdpcm()) + { + for (int i = 0; i < mChannelCount; i++) + { + if (!loader.ReadAdpcmInfo(&mChannels[i].adpcmParam, + &mChannels[i].adpcmLoopParam, i)) + { + return false; + } + } + + if (startOffset != 0) + { + int startOffsetSamples; + + if (startOffsetType == START_OFFSET_TYPE_SAMPLE) + startOffsetSamples = startOffset; + else if (startOffsetType == START_OFFSET_TYPE_MILLISEC) + startOffsetSamples = startOffset * mStrmInfo.sampleRate / 1000; + + /* NOTE: startOffsetSamples is used uninitialized if neither + * branch is taken (asserted externally or ERRATUM?) + */ + + s32 blockIndex = + startOffsetSamples / static_cast<s32>(mStrmInfo.blockSamples); + + u16 yn1[16]; + u16 yn2[16]; + if (!loader.ReadAdpcBlockData(yn1, yn2, blockIndex, + mStrmInfo.numChannels)) + { + return false; + } + + for (int i = 0; i < mStrmInfo.numChannels; i++) + { + mChannels[i].adpcmParam.yn1 = yn1[i]; + mChannels[i].adpcmParam.yn2 = yn2[i]; + } + } + } + + if (!SetupPlayer()) + return false; + + mPrepareCounter = 0; + + for (int i = 0; i < mBufferBlockCountBase; i++) + { + UpdateLoadingBlockIndex(); + + mPrepareCounter++; + + if (mLoadFinishFlag) + break; + } + + if (mStrmInfo.numBlocks <= 2 && !mStrmInfo.loopFlag) + SetLoopEndToZeroBuffer(mStrmInfo.numBlocks - 1); + + return true; +} + +#pragma pop + +#pragma push + +#pragma ppc_iro_level 0 // somehow this got turned off??? + +bool StrmPlayer::LoadStreamData(ut::FileStream *fileStream, int offset, + u32 size ATTR_UNUSED, u32 blockSize, + int bufferBlockIndex, bool needUpdateAdpcmLoop) +{ + NW4RAssertPointerNonnull_Line(746, fileStream); + NW4RAssertAligned_Line(747, offset, 32); + NW4RAssertAligned_Line(748, blockSize, 32); + + if (ut::DvdFileStream *dvdStream = + ut::DynamicCast<ut::DvdFileStream *>(fileStream)) + { + dvdStream->SetPriority(1); + } + + ut::detail::AutoLock<OSMutex> lock(sLoadBufferMutex); + + DCInvalidateRange(sLoadBuffer, LOAD_BUFFER_SIZE); + + int loadOffset = offset + mStrmInfo.blockHeaderOffset; + u16 adpcmPredScale[STRM_CHANNEL_NUM]; + + int currentChannel = 0; + while (currentChannel < mChannelCount) + { + NW4RAssertAligned_Line(773, loadOffset, 32); + + int loadChannelCount = Channel::CHANNEL_MAX; + + if (currentChannel + loadChannelCount > mChannelCount) + loadChannelCount = mChannelCount - currentChannel; + + u32 loadSize = blockSize * loadChannelCount; + NW4RAssert_Line(781, loadSize <= LOAD_BUFFER_SIZE); + + fileStream->Seek(loadOffset, ut::FileStream::SEEK_ORIGIN_SET); + + s32 resultSize = fileStream->Read(sLoadBuffer, loadSize); + if (resultSize != loadSize) + return false; + + for (int i = 0; i < loadChannelCount; i++) + { + if (needUpdateAdpcmLoop) + adpcmPredScale[currentChannel] = sLoadBuffer[blockSize * i]; + + u32 len = blockSize; + void *source = ut::AddOffsetToPtr(sLoadBuffer, blockSize * i); + void *dest = + ut::AddOffsetToPtr(mChannels[currentChannel].bufferAddress, + mDataBlockSize * bufferBlockIndex); + + std::memcpy(dest, source, len); + DCFlushRange(dest, len); + + currentChannel++; + } + + loadOffset += loadSize; + } + + if (needUpdateAdpcmLoop) + SetAdpcmLoopContext(mChannelCount, adpcmPredScale); + + if (!mPreparedFlag) + { + mPrepareCounter--; + + if (mPrepareCounter == 0) + mPreparedFlag = true; + } + + return true; +} + +#pragma pop // ????? + +bool StrmPlayer::SetupPlayer() +{ + NW4RAssertPointerNonnull_Line(850, mBufferPool); + + u32 strmBufferSize = mBufferPool->GetBlockSize(); + + s32 blockIndex = 0; + u32 blockOffset = 0; + s32 loopCount = 0; + if (!CalcStartOffset(&blockIndex, &blockOffset, &loopCount)) + return false; + + mLoopStartBlockIndex = mStrmInfo.loopStart / mStrmInfo.blockSamples; + mLastBlockIndex = mStrmInfo.numBlocks - 1; + + mDataBlockSize = mStrmInfo.blockSize; + if (mDataBlockSize > DATA_BLOCK_SIZE_MAX) + { + NW4RWarningMessage_Line(870, "Too large stream data block size."); + return false; + } + + mBufferBlockCount = strmBufferSize / mDataBlockSize; + if (mBufferBlockCount < 4) + { + NW4RWarningMessage_Line(876, "Too small stream buffer size."); + return false; + } + + if (mBufferBlockCount > BUFFER_BLOCK_COUNT_MAX) + mBufferBlockCount = BUFFER_BLOCK_COUNT_MAX; + + mBufferBlockCountBase = mBufferBlockCount - 1; + mChangeNumBlocks = mBufferBlockCountBase; + + mPlayingDataBlockIndex = blockIndex; + mLoadingDataBlockIndex = blockIndex; + + mLoadingBufferBlockIndex = 0; + mPlayingBufferBlockIndex = 0; + + if (mNoRealtimeLoadFlag) + mLoadingBufferBlockCount = mStrmInfo.numBlocks; + else + mLoadingBufferBlockCount = CalcLoadingBufferBlockCount(); + + mPlayingBufferBlockCount = mLoadingBufferBlockCount; + + ut::AutoInterruptLock lock; + + if (!mAllocStrmBufferFlag) + { + if (!AllocStrmBuffers()) + return false; + + mAllocStrmBufferFlag = true; + } + + return true; +} + +bool StrmPlayer::AllocStrmBuffers() +{ + for (int index = 0; index < mChannelCount; index++) + { + void *strmBuffer = mBufferPool->Alloc(); + + if (!strmBuffer) + { + for (int i = 0; i < index; i++) + { + mBufferPool->Free(mChannels[i].bufferAddress); + mChannels[i].bufferAddress = nullptr; + } + + return false; + } + + mChannels[index].bufferAddress = strmBuffer; + } + + return true; +} + +void StrmPlayer::FreeStrmBuffers() +{ + for (int index = 0; index < mChannelCount; index++) + { + if (!mChannels[index].bufferAddress) + continue; + + mBufferPool->Free(mChannels[index].bufferAddress); + mChannels[index].bufferAddress = nullptr; + } +} + +bool StrmPlayer::AllocVoices(int voiceOutCount) +{ + ut::AutoInterruptLock lock; + + NW4RAssertPointerNonnull_Line(992, mBufferPool); + + for (int trackIndex = 0; trackIndex < mTrackCount; trackIndex++) + { + StrmTrack &track = mTracks[trackIndex]; + if (!track.mActiveFlag) + continue; + + Voice *voice = VoiceManager::GetInstance().AllocVoice( + track.mTrackInfo.channelCount, voiceOutCount, Voice::PRIORITY_MAX, + &VoiceCallbackFunc, &mTracks[trackIndex]); + + if (!voice) + { + for (int i = 0; i < trackIndex; i++) + { + StrmTrack &t = mTracks[i]; + + if (t.mVoice) + { + t.mVoice->Free(); + t.mVoice = nullptr; + } + } + + return false; + } + + track.mVoice = voice; + voice->SetVoiceOutParamPitchDisableFlag(true); + } + + return true; +} + +void StrmPlayer::FreeVoices() +{ + ut::AutoInterruptLock lock; + + for (int trackIndex = 0; trackIndex < mTrackCount; trackIndex++) + { + StrmTrack &track = mTracks[trackIndex]; + if (!track.mActiveFlag) + continue; + + if (track.mVoice) + { + track.mVoice->Free(); + track.mVoice = nullptr; + } + } +} + +void StrmPlayer::Update() +{ + if (!mActiveFlag) + return; + + if (mTaskErrorFlag && !mTaskCancelFlag) + { + NW4RWarningMessage_Line(1076, "Task error is occured."); + + Stop(); + return; + } + + if (mStartedFlag) + { + for (int trackIndex = 0; trackIndex < mTrackCount; trackIndex++) + { + StrmTrack &track = mTracks[trackIndex]; + + if (track.mActiveFlag && !track.mVoice) + { + Stop(); + return; + } + } + } + + if (mLoadWaitFlag && mStrmDataLoadTaskList.IsEmpty() + && !CheckDiskDriveError()) + { + mLoadWaitFlag = false; + UpdatePauseStatus(); + } + + if (mLoadingDelayFlag) + { + NW4RWarningMessage_Line(1109, "Pause stream because of loading delay."); + mLoadingDelayFlag = false; + } + + for (int trackIndex = 0; trackIndex < mTrackCount; trackIndex++) + UpdateVoiceParams(&mTracks[trackIndex]); +} + +void StrmPlayer::UpdateVoiceParams(StrmTrack *track) +{ + if (!track->mActiveFlag) + return; + + f32 volume = 1.0f; + volume *= GetVolume(); + volume *= track->mTrackInfo.volume / 127.0f; + volume *= track->mVolume; + + f32 pitchRatio = 1.0f; + pitchRatio *= GetPitch(); + + f32 pan = 0.0f; + pan += GetPan(); + if (track->mTrackInfo.pan <= 1) + pan += (track->mTrackInfo.pan - 63) / 63.0f; + else + pan += (track->mTrackInfo.pan - 64) / 63.0f; + + pan += track->mPan; + + f32 surroundPan = 0.0f; + surroundPan += GetSurroundPan(); + + f32 lpfFreq = 1.0f; + lpfFreq += GetLpfFreq(); + + int biquadType = GetBiquadType(); + f32 biquadValue = GetBiquadValue(); + + int remoteFilter = 0; + remoteFilter += GetRemoteFilter(); + + f32 mainSend = 0.0f; + mainSend += GetMainSend(); + + f32 fxsend[AUX_BUS_NUM]; + for (int i = 0; i < AUX_BUS_NUM; i++) + { + fxsend[i] = 0.0f; + fxsend[i] += GetFxSend(static_cast<AuxBus>(i)); + } + + ut::AutoInterruptLock lock; + + if (Voice *voice = track->mVoice) + { + voice->SetVolume(volume); + voice->SetPitch(pitchRatio); + voice->SetPan(pan); + voice->SetSurroundPan(surroundPan); + voice->SetLpfFreq(lpfFreq); + voice->SetBiquadFilter(biquadType, biquadValue); + voice->SetRemoteFilter(remoteFilter); + voice->SetOutputLine(GetOutputLine()); + voice->SetMainOutVolume(GetMainOutVolume()); + voice->SetMainSend(mainSend); + + for (int i = 0; i < AUX_BUS_NUM; i++) + { + AuxBus bus = static_cast<AuxBus>(i); + voice->SetFxSend(bus, fxsend[i]); + } + + for (int i = 0; i < mVoiceOutCount; i++) + voice->SetVoiceOutParam(i, GetVoiceOutParam(i)); + } +} + +bool StrmPlayer::CheckDiskDriveError() const +{ + ut::DvdFileStream *dvdFileStream = + ut::DynamicCast<ut::DvdFileStream *>(mFileStream); + if (!dvdFileStream) + return false; + + DVDState driveStatus = DVDGetDriveStatus(); + switch (driveStatus) + { + case DVD_STATE_IDLE: + case DVD_STATE_BUSY: + return false; + + default: + return true; + } +} + +void StrmPlayer::UpdateBuffer() +{ + if (!mStartedFlag) + return; + + if (!mTracks[0].mActiveFlag) + return; + + Voice *voice = mTracks[0].mVoice; + if (!voice) + return; + + if (CheckDiskDriveError()) + { + mLoadWaitFlag = true; + + UpdatePauseStatus(); + } + + if (!mPlayFinishFlag && !mNoRealtimeLoadFlag && !mLoadWaitFlag) + { + u32 playingSample = voice->GetCurrentPlayingSample(); + int axCurrentBlockIndex = playingSample / mStrmInfo.blockSamples; + + while (mPlayingBufferBlockIndex != axCurrentBlockIndex) + { + if (!mLoadWaitFlag && !mStrmDataLoadTaskList.IsEmpty() + && mLoadWaitCount >= mBufferBlockCountBase - 2) + { + mLoadingDelayFlag = true; + mLoadWaitFlag = true; + + UpdatePauseStatus(); + + break; + } + else + { + UpdatePlayingBlockIndex(); + UpdateLoadingBlockIndex(); + } + } + } +} + +void StrmPlayer::UpdateLoopAddress(u32 loopStartSamples, u32 loopEndSamples) +{ + ut::AutoInterruptLock lock; + + for (int trackIndex = 0; trackIndex < mTrackCount; trackIndex++) + { + StrmTrack &track = mTracks[trackIndex]; + + if (!track.mActiveFlag) + continue; + + Voice *voice = track.mVoice; + if (!voice) + continue; + + for (int channelIndex = 0; + channelIndex < track.mTrackInfo.channelCount; + channelIndex++) + { + // mTracks[trackIndex] againinstead of track? + StrmChannel *channel = + GetTrackChannel(mTracks[trackIndex], channelIndex); + + voice->SetLoopStart(channelIndex, channel->bufferAddress, + loopStartSamples); + voice->SetLoopEnd(channelIndex, channel->bufferAddress, + loopEndSamples); + } + + voice->SetLoopFlag(true); + } +} + +void StrmPlayer::UpdatePlayingBlockIndex() +{ + mPlayingDataBlockIndex++; + if (mPlayingDataBlockIndex > mLastBlockIndex) + { + if (mStrmInfo.loopFlag) + { + mPlayingDataBlockIndex = mLoopStartBlockIndex; + + if (mLoopCounter < LONG_MAX) + mLoopCounter++; + + UpdateLoopAddress(0, mPlayingBufferBlockCount + * mStrmInfo.blockSamples); + } + else + { + NW4RPanic_Line(1379); + } + } + + mPlayingBufferBlockIndex++; + if (mPlayingBufferBlockIndex >= mPlayingBufferBlockCount) + { + mPlayingBufferBlockIndex = 0; + mPlayingBufferBlockCount = mLoadingBufferBlockCount; + + UpdateLoopAddress(0, mPlayingBufferBlockCount * mStrmInfo.blockSamples); + } + + if (mPlayingBufferBlockIndex == mPlayingBufferBlockCount - 1) + { + if (!mSkipUpdateAdpcmLoop && mValidAdpcmLoop) + { + for (int trackIndex = 0; trackIndex < mTrackCount; trackIndex++) + { + StrmTrack &track = mTracks[trackIndex]; + + if (!track.mActiveFlag) + continue; + + Voice *voice = track.mVoice; + if (!voice) + continue; + + if (voice->GetFormat() == SAMPLE_FORMAT_DSP_ADPCM) + { + NW4RCheckMessage_Line(1411, mValidAdpcmLoop, + "AdpcmLoop can not update!"); + + ut::AutoInterruptLock lock; + + for (int channelIndex = 0; + channelIndex < track.mTrackInfo.channelCount; + channelIndex++) + { + StrmChannel *channel = + GetTrackChannel(track, channelIndex); + + AdpcmLoopParam loop; + loop.loop_pred_scale = channel->adpcmPredScale; + loop.loop_yn1 = 0; + loop.loop_yn2 = 0; + + voice->SetAdpcmLoop(channelIndex, &loop); + } + + voice->SetVoiceType(AxVoice::VOICE_TYPE_STREAM); + } + } + } + mValidAdpcmLoop = false; + mSkipUpdateAdpcmLoop = false; + } + + if (mPlayingDataBlockIndex == mLastBlockIndex - 1) + { + s32 endBufferBlockIndex = mPlayingBufferBlockIndex + 1; + UpdateDataLoopAddress(endBufferBlockIndex); + } +} + +void StrmPlayer::UpdateDataLoopAddress(s32 endBlockBufferIndex) +{ + if (mStrmInfo.loopFlag) + { + s32 startBlockNum = endBlockBufferIndex + 1; + + if (startBlockNum >= mPlayingBufferBlockCount) + startBlockNum -= mPlayingBufferBlockCount; + + ut::AutoInterruptLock lock; + + UpdateLoopAddress(startBlockNum * mStrmInfo.blockSamples, + mStrmInfo.lastBlockSamples + + (endBlockBufferIndex * mStrmInfo.blockSamples)); + + if (IsAdpcm()) + { + for (int trackIndex = 0; trackIndex < mTrackCount; trackIndex++) + { + StrmTrack &track = mTracks[trackIndex]; + + if (!track.mActiveFlag) + continue; + + Voice *voice = track.mVoice; + if (!voice) + continue; + + if (voice->GetFormat() == SAMPLE_FORMAT_DSP_ADPCM) + { + voice->SetVoiceType(AxVoice::VOICE_TYPE_NORMAL); + + for (int channelIndex = 0; + channelIndex < track.mTrackInfo.channelCount; + channelIndex++) + { + StrmChannel *channel = + GetTrackChannel(track, channelIndex); + + voice->SetAdpcmLoop(channelIndex, + &channel->adpcmLoopParam); + } + } + } + + if (endBlockBufferIndex == mPlayingBufferBlockCount - 1) + mSkipUpdateAdpcmLoop = true; + } + } + else + { + SetLoopEndToZeroBuffer(endBlockBufferIndex); + } +} + +void StrmPlayer::SetLoopEndToZeroBuffer(int endBufferBlockIndex) +{ + { + ut::AutoInterruptLock lock; + + for (int trackIndex = 0; trackIndex < mTrackCount; trackIndex++) + { + StrmTrack &track = mTracks[trackIndex]; + + if (!track.mActiveFlag) + continue; + + Voice *voice = track.mVoice; + if (!voice) + continue; + + for (int channelIndex = 0; + channelIndex < track.mTrackInfo.channelCount; channelIndex++) + { + StrmChannel *channel = GetTrackChannel(track, channelIndex); + + voice->StopAtPoint(channelIndex, channel->bufferAddress, + mStrmInfo.lastBlockSamples + + endBufferBlockIndex + * mStrmInfo.blockSamples); + } + } + } + + mPlayFinishFlag = true; +} + +void StrmPlayer::UpdateLoadingBlockIndex() +{ + mLoadWaitCount++; + + if (mLoadFinishFlag) + return; + + u32 blockSize = + mLoadingDataBlockIndex < static_cast<s32>(mStrmInfo.numBlocks - 1) + ? mStrmInfo.blockSize + : mStrmInfo.lastBlockPaddedSize; + + u32 loadSize = mStrmInfo.blockHeaderOffset + blockSize * mChannelCount; + + s32 loadOffset = mStrmInfo.dataOffset + + mLoadingDataBlockIndex + * (mStrmInfo.blockHeaderOffset + + mStrmInfo.blockSize * mStrmInfo.numChannels); + + NW4RAssertAligned_Line(1576, blockSize, 32); + NW4RAssertAligned_Line(1577, loadSize, 32); + NW4RAssertAligned_Line(1578, loadOffset, 32); + + bool needUpdateAdpcmLoop = mLoadingBufferBlockIndex == 0 && IsAdpcm(); + + StrmDataLoadTask *task = mStrmDataLoadTaskPool.Alloc(); + NW4RAssertPointerNonnull_Line(1584, task); + + task->mStrmPlayer = this; + task->fileStream = mFileStream; + task->mSize = loadSize; + task->mOffset = loadOffset; + task->mBlockSize = blockSize; + task->mBufferBlockIndex = mLoadingBufferBlockIndex; + task->mNeedUpdateAdpcmLoop = needUpdateAdpcmLoop; + + ut::AutoInterruptLock lock; + + mStrmDataLoadTaskList.PushBack(task); + + TaskManager::GetInstance().AppendTask( + task, mStartedFlag ? TaskManager::PRIORITY_HIGH + : TaskManager::PRIORITY_MIDDLE); + + mLoadingDataBlockIndex++; + + if (mLoadingDataBlockIndex > mLastBlockIndex) + { + if (mStrmInfo.loopFlag) + { + mLoadingDataBlockIndex = mLoopStartBlockIndex; + } + else + { + mLoadFinishFlag = true; + + return; + } + } + + mLoadingBufferBlockIndex++; + + if (mLoadingBufferBlockIndex >= mLoadingBufferBlockCount) + { + mLoadingBufferBlockIndex = 0; + mLoadingBufferBlockCount = CalcLoadingBufferBlockCount(); + } +} + +void StrmPlayer::UpdatePauseStatus() +{ + ut::AutoInterruptLock lock; + + bool pauseStatus = false; + + if (mPauseFlag) + pauseStatus = true; + + if (mLoadWaitFlag) + pauseStatus = true; + + if (pauseStatus != mPauseStatus) + { + for (int trackIndex = 0; trackIndex < mTrackCount; trackIndex++) + { + if (!mTracks[trackIndex].mActiveFlag) + continue; + + if (Voice *voice = mTracks[trackIndex].mVoice) + voice->Pause(pauseStatus); + } + + mPauseStatus = pauseStatus; + } +} + +int StrmPlayer::CalcLoadingBufferBlockCount() const +{ + int restBlockCount = mLastBlockIndex - mLoadingDataBlockIndex + 1; + int loopBlockCount = mLastBlockIndex - mLoopStartBlockIndex + 1; + + if ((mBufferBlockCountBase + 1 - restBlockCount) % loopBlockCount == 0) + return mBufferBlockCountBase + 1; + else + return mBufferBlockCountBase; +} + +bool StrmPlayer::CalcStartOffset(s32 *startBlockIndex, u32 *startBlockOffset, + s32 *loopCount) +{ + if (mStrmInfo.blockSamples == 0) + return false; + + int startOffsetSamples; + if (mStartOffsetType == START_OFFSET_TYPE_SAMPLE) + { + startOffsetSamples = mStartOffset; + } + else if (mStartOffsetType == START_OFFSET_TYPE_MILLISEC) + { + startOffsetSamples = + mStartOffset * static_cast<s64>(mStrmInfo.sampleRate) / 1000; + } + + *loopCount = 0; + + if (startOffsetSamples >= mStrmInfo.loopEnd) + { + if (mStrmInfo.loopFlag) + { + s32 loopStart = mStrmInfo.loopStart; + s32 loopEnd = mStrmInfo.loopEnd; + s32 loopLen = loopEnd - loopStart; + s32 startOffset2 = startOffsetSamples - loopEnd; + + *loopCount = startOffset2 / loopLen + 1; + + startOffsetSamples = loopStart + startOffset2 % loopLen; + } + else + { + return false; + } + } + + *startBlockIndex = + startOffsetSamples / static_cast<int>(mStrmInfo.blockSamples); + + *startBlockOffset = startOffsetSamples % mStrmInfo.blockSamples; + + return true; +} + +void StrmPlayer::VoiceCallbackFunc(Voice *voice, + Voice::VoiceCallbackStatus status, void *arg) +{ + StrmTrack *track = static_cast<StrmTrack *>(arg); + NW4RAssertPointerNonnull_Line(1771, track); + NW4RAssert_Line(1773, track->mVoice == voice); + + ut::AutoInterruptLock lock; + + switch (status) + { + case Voice::CALLBACK_STATUS_FINISH_WAVE: + case Voice::CALLBACK_STATUS_CANCEL: + voice->Free(); + track->mVoice = nullptr; + + break; + + case Voice::CALLBACK_STATUS_DROP_VOICE: + case Voice::CALLBACK_STATUS_DROP_DSP: + track->mVoice = nullptr; + + break; + + default: + NW4RPanicMessage_Line(1789, "Unknown Voice callback status %d", status); + return; // NOTE: do not change (invokes scope guard destructor twice) + } +} + +void StrmPlayer::SetAdpcmLoopContext(int channelNum, u16 *predScale) +{ + if (!IsAdpcm()) + return; + + for (int channelIndex = 0; + channelIndex < channelNum && channelIndex < STRM_CHANNEL_NUM; + channelIndex++) + { + mChannels[channelIndex].adpcmPredScale = predScale[channelIndex]; + } + + mValidAdpcmLoop = true; +} + +StrmChannel *StrmPlayer::GetTrackChannel(StrmTrack const &track, + int channelIndex) +{ + if (channelIndex >= Channel::CHANNEL_MAX) + return nullptr; + + int index = track.mTrackInfo.channelIndexTable[channelIndex]; + if (index >= STRM_CHANNEL_NUM) + return nullptr; + + return &mChannels[index]; +} + +void StrmPlayer::SetTrackVolume(byte4_t trackBitFlag, f32 volume) +{ + ut::AutoInterruptLock lock; + + for (int trackNo = 0; trackNo < mTrackCount && trackBitFlag; + trackNo++, trackBitFlag >>= 1) + { + if (trackBitFlag & 1) + mTracks[trackNo].mVolume = volume; + } +} + +StrmPlayer::StrmTrack *StrmPlayer::GetPlayerTrack(int trackNo) +{ + if (trackNo > STRM_TRACK_NUM - 1) + return nullptr; + + return &mTracks[trackNo]; +} + +StrmPlayer::StrmHeaderLoadTask::StrmHeaderLoadTask() : + player (nullptr), + fileStream (nullptr), + startOffset (0) +{ +} + +void StrmPlayer::StrmHeaderLoadTask::Execute() +{ + NW4RAssertPointerNonnull_Line(1894, player); + + bool result = player->LoadHeader(fileStream, startOffsetType, startOffset); + if (!result) + player->SetTaskErrorFlag(); +} + +void StrmPlayer::StrmHeaderLoadTask::Cancel() +{ + /* ... */ +} + +void StrmPlayer::StrmHeaderLoadTask::OnCancel() +{ + player->SetTaskCancelFlag(); + + if (fileStream && fileStream->CanCancel()) + { + if (fileStream->CanAsync()) + fileStream->CancelAsync(nullptr, nullptr); + else + fileStream->Cancel(); + } +} + +StrmPlayer::StrmDataLoadTask::StrmDataLoadTask() : + mStrmPlayer (nullptr), + fileStream (nullptr), + mSize (0), + mOffset (0), + mBlockSize (0), + mBufferBlockIndex (-1), + mNeedUpdateAdpcmLoop (false) +{ +} + +void StrmPlayer::StrmDataLoadTask::Execute() +{ + bool result = + mStrmPlayer->LoadStreamData(fileStream, mOffset, mSize, mBlockSize, + mBufferBlockIndex, mNeedUpdateAdpcmLoop); + if (!result) + mStrmPlayer->SetTaskErrorFlag(); + + ut::AutoInterruptLock lock; + mStrmPlayer->mStrmDataLoadTaskList.Erase(this); + mStrmPlayer->mStrmDataLoadTaskPool.Free(this); + + mStrmPlayer->mLoadWaitCount--; +} + +void StrmPlayer::StrmDataLoadTask::Cancel() +{ + ut::AutoInterruptLock lock; + + mStrmPlayer->mStrmDataLoadTaskList.Erase(this); + mStrmPlayer->mStrmDataLoadTaskPool.Free(this); +} + +void StrmPlayer::StrmDataLoadTask::OnCancel() +{ + mStrmPlayer->SetTaskCancelFlag(); + + if (fileStream && fileStream->CanCancel()) + { + if (fileStream->CanAsync()) + fileStream->CancelAsync(nullptr, nullptr); + else + fileStream->Cancel(); + } +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_StrmSound.cpp b/src/nw4r/snd/snd_StrmSound.cpp index 90c6fe5a..8c5675ed 100644 --- a/src/nw4r/snd/snd_StrmSound.cpp +++ b/src/nw4r/snd/snd_StrmSound.cpp @@ -1 +1,142 @@ -#include "nw4r/snd/snd_StrmSound.h" +#include "nw4r/snd/StrmSound.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_StrmSound.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <macros.h> // ARRAY_LENGTH +#include <types.h> + +#include "nw4r/snd/BasicSound.h" +#include "nw4r/snd/SoundInstanceManager.h" +#include "nw4r/snd/StrmPlayer.h" +#include "nw4r/snd/StrmSoundHandle.h" +#include "nw4r/snd/MoveValue.h" + +#include "nw4r/ut/RuntimeTypeInfo.h" + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * types + */ + +// forward declarations +namespace nw4r { namespace snd { namespace detail { class StrmBufferPool; }}} + +namespace nw4r { namespace ut { class FileStream; }} + +/******************************************************************************* + * variables + */ + +namespace nw4r { namespace snd { namespace detail +{ + // .sbss + ut::detail::RuntimeTypeInfo const StrmSound::typeInfo( + &BasicSound::typeInfo); +}}} // namespace nw4r::snd::detail + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +StrmSound::StrmSound(SoundInstanceManager<StrmSound> *manager, int priority, + int ambientPriority) : + BasicSound (priority, ambientPriority), + mTempSpecialHandle (nullptr), + mManager (manager) +{ +} + +void StrmSound::InitParam() +{ + BasicSound::InitParam(); + + for (int i = 0; i < ARRAY_LENGTH(mTrackVolume); i++) + { + mTrackVolume[i].InitValue(0.0f); + mTrackVolume[i].SetTarget(1.0f, 1); + } +} + +StrmPlayer::SetupResult StrmSound::Setup(StrmBufferPool *bufferPool, + int allocChannelCount, + byte2_t allocTrackFlag) +{ + NW4RAssertPointerNonnull_Line(90, bufferPool); + + InitParam(); + + return mStrmPlayer.Setup(bufferPool, allocChannelCount, allocTrackFlag, + GetVoiceOutCount()); +} + +bool StrmSound::Prepare(StrmPlayer::StartOffsetType startOffsetType, s32 offset, + ut::FileStream *fileStream) +{ + bool result = mStrmPlayer.Prepare(fileStream, startOffsetType, offset); + if (!result) + { + mStrmPlayer.Shutdown(); + return false; + } + + return true; +} + +void StrmSound::UpdateMoveValue() +{ + BasicSound::UpdateMoveValue(); + + for (int trackNo = 0; trackNo < (int)ARRAY_LENGTH(mTrackVolume); trackNo++) + { + if (mStrmPlayer.GetPlayerTrack(trackNo)) + mTrackVolume[trackNo].Update(); + } +} + +void StrmSound::UpdateParam() +{ + BasicSound::UpdateParam(); + + for (int trackNo = 0; trackNo < (int)ARRAY_LENGTH(mTrackVolume); trackNo++) + { + if (mStrmPlayer.GetPlayerTrack(trackNo)) + { + mStrmPlayer.SetTrackVolume(1 << trackNo, + mTrackVolume[trackNo].GetValue()); + } + } +} + +void StrmSound::Shutdown() +{ + BasicSound::Shutdown(); + + mManager->Free(this); +} + +void StrmSound::OnUpdatePlayerPriority() +{ + mManager->UpdatePriority(this, CalcCurrentPlayerPriority()); +} + +bool StrmSound::IsAttachedTempSpecialHandle() +{ + return mTempSpecialHandle != nullptr; +} + +void StrmSound::DetachTempSpecialHandle() +{ + mTempSpecialHandle->DetachSound(); +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_StrmSoundHandle.cpp b/src/nw4r/snd/snd_StrmSoundHandle.cpp index 2161fba2..ff419896 100644 --- a/src/nw4r/snd/snd_StrmSoundHandle.cpp +++ b/src/nw4r/snd/snd_StrmSoundHandle.cpp @@ -1 +1,34 @@ -#include "nw4r/snd/snd_StrmSoundHandle.h" +#include "nw4r/snd/StrmSoundHandle.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_StrmSoundHandle.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <types.h> // nullptr + +#include "nw4r/snd/StrmSound.h" + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { + +void StrmSoundHandle::DetachSound() +{ + if (IsAttachedSound()) + { + if (mSound->mTempSpecialHandle == this) + mSound->mTempSpecialHandle = nullptr; + } + + if (mSound) + mSound = nullptr; +} + +}} // namespace nw4r::snd diff --git a/src/nw4r/snd/snd_Task.cpp b/src/nw4r/snd/snd_Task.cpp index af1196c6..2e54241e 100644 --- a/src/nw4r/snd/snd_Task.cpp +++ b/src/nw4r/snd/snd_Task.cpp @@ -1 +1,26 @@ -#include "nw4r/snd/snd_Task.h" +#include "nw4r/snd/Task.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_Task.cpp + */ + +/******************************************************************************* + * headers + */ + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +// Interesting +Task::~Task() +{ + NW4RAssert_Line(24, ! mBusyFlag); +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_TaskManager.cpp b/src/nw4r/snd/snd_TaskManager.cpp index ad40ec9b..6b324c25 100644 --- a/src/nw4r/snd/snd_TaskManager.cpp +++ b/src/nw4r/snd/snd_TaskManager.cpp @@ -1 +1,216 @@ -#include "nw4r/snd/snd_TaskManager.h" +#include "nw4r/snd/TaskManager.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_TaskManager.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <decomp.h> +#include <macros.h> // NW4R_RANGE_FOR_NO_AUTO_INC +#include <types.h> // nullptr + +#include "nw4r/snd/Task.h" + +#include "nw4r/ut/Lock.h" // ut::AutoInterruptLock + +#if 0 +#include <revolution/OS/OSThread.h> +#else +#include <context_rvl.h> +#endif + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +TaskManager &TaskManager::GetInstance() +{ + static TaskManager instance; + + return instance; +} + +TaskManager::TaskManager() : + mCurrentTask (nullptr), + mCancelWaitTaskFlag (false) +{ + OSInitThreadQueue(&mAppendThreadQueue); + OSInitThreadQueue(&mDoneThreadQueue); +} + +void TaskManager::AppendTask(Task *task, TaskPriority priority) +{ + // specifically not the source variant + NW4RAssertHeaderClampedLValue_Line(67, priority, PRIORITY_LOW, + PRIORITY_NUM); + + ut::AutoInterruptLock lock; + + task->mBusyFlag = true; + mTaskList[priority].PushBack(task); + + OSWakeupThread(&mAppendThreadQueue); +} + +// TaskManager::FindTask, probably +DECOMP_FORCE_CLASS_METHOD(Task::LinkList::Iterator, operator *()); + +Task *TaskManager::GetNextTask(TaskPriority priority, bool doRemove) +{ + ut::AutoInterruptLock lock; + + if (mTaskList[priority].IsEmpty()) + return nullptr; + + Task *task = &mTaskList[priority].GetFront(); + + if (doRemove) + mTaskList[priority].PopFront(); + + return task; +} + +Task *TaskManager::PopTask() +{ + ut::AutoInterruptLock lock; + + Task *task; + + if ((task = GetNextTask(PRIORITY_HIGH, true))) + return task; + + if ((task = GetNextTask(PRIORITY_MIDDLE, true))) + return task; + + if ((task = GetNextTask(PRIORITY_LOW, true))) + return task; + + return nullptr; +} + +Task *TaskManager::GetNextTask() +{ + ut::AutoInterruptLock lock; + + Task *task; + + if ((task = GetNextTask(PRIORITY_HIGH, false))) + return task; + + if ((task = GetNextTask(PRIORITY_MIDDLE, false))) + return task; + + if ((task = GetNextTask(PRIORITY_LOW, false))) + return task; + + return nullptr; +} + +Task *TaskManager::ExecuteTask() +{ + Task *task = PopTask(); + if (!task) + return nullptr; + + mCurrentTask = task; + + task->mBusyFlag = false; + task->Execute(); + + mCurrentTask = nullptr; + + OSWakeupThread(&mDoneThreadQueue); + + return task; +} + +void TaskManager::CancelTask(Task *task) +{ + ut::AutoInterruptLock lock; + + if (task == mCurrentTask) + { + task->OnCancel(); + + while (task == mCurrentTask) + OSSleepThread(&mDoneThreadQueue); + } + else + { + for (int i = 0; i < PRIORITY_NUM; i++) + { + TaskPriority priority = static_cast<TaskPriority>(i); + + NW4R_RANGE_FOR_NO_AUTO_INC(itr, mTaskList[priority]) + { + decltype(itr) curItr = itr++; + + if (&(*curItr) == task) + { + mTaskList[priority].Erase(curItr); + + curItr->mBusyFlag = false; + curItr->Cancel(); + + break; + } + } + } + } +} + +void TaskManager::CancelAllTask() +{ + ut::AutoInterruptLock lock; + + for (int i = 0; i < PRIORITY_NUM; i++) + { + TaskPriority priority = static_cast<TaskPriority>(i); + + Task::LinkList &list = mTaskList[priority]; + while (!list.IsEmpty()) + { + Task &task = list.GetBack(); + list.PopBack(); + + task.mBusyFlag = false; + task.Cancel(); + } + } + + if (mCurrentTask) + { + mCurrentTask->OnCancel(); + + while (mCurrentTask) + OSSleepThread(&mDoneThreadQueue); + } +} + +void TaskManager::WaitTask() +{ + ut::AutoInterruptLock lockIntr; + + mCancelWaitTaskFlag = false; + + while (!GetNextTask() && !mCancelWaitTaskFlag) // TODO: implies volatile? + OSSleepThread(&mAppendThreadQueue); +} + +void TaskManager::CancelWaitTask() +{ + ut::AutoInterruptLock lockIntr; + + mCancelWaitTaskFlag = true; + OSWakeupThread(&mAppendThreadQueue); +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_TaskThread.cpp b/src/nw4r/snd/snd_TaskThread.cpp index 237b3bc0..1cb40c79 100644 --- a/src/nw4r/snd/snd_TaskThread.cpp +++ b/src/nw4r/snd/snd_TaskThread.cpp @@ -1 +1,104 @@ -#include "nw4r/snd/snd_TaskThread.h" +#include "nw4r/snd/TaskThread.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_TaskThread.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <types.h> + +#include "nw4r/snd/TaskManager.h" + +#if 0 +#include <revolution/OS/OSThread.h> +#else +#include <context_rvl.h> +#endif + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +TaskThread::TaskThread() : + mStackEnd (nullptr), + mFinishFlag (false), + mCreateFlag (false) +{ +} + +TaskThread::~TaskThread() +{ + if (mCreateFlag) + Destroy(); +} + +bool TaskThread::Create(s32 priority, void *stack, u32 stackSize) +{ + NW4RAssertPointerNonnull_Line(59, stack); + NW4RAssertAligned_Line(60, stack, 4); + + if (mCreateFlag) + Destroy(); + + BOOL result = OSCreateThread(&mThread, &ThreadFunc, this, + static_cast<byte_t *>(stack) + stackSize, + stackSize, priority, OS_THREAD_NO_FLAGS); + if (!result) + return false; + + mStackEnd = static_cast<byte4_t *>(stack); + mFinishFlag = false; + mCreateFlag = true; + + OSResumeThread(&mThread); + + return true; +} + +void TaskThread::Destroy() +{ + if (!mCreateFlag) + return; + + mFinishFlag = true; + TaskManager::GetInstance().CancelWaitTask(); + + BOOL result = OSJoinThread(&mThread, nullptr); + NW4RAssert_Line(105, result); + + mCreateFlag = false; +} + +void *TaskThread::ThreadFunc(void *arg) +{ + TaskThread *taskThread = static_cast<TaskThread *>(arg); + + taskThread->ThreadProc(); + + return nullptr; +} + +void TaskThread::ThreadProc() +{ + while (!mFinishFlag) // TODO: implies volatile? + { + TaskManager::GetInstance().WaitTask(); + + if (mFinishFlag) + break; + + TaskManager::GetInstance().ExecuteTask(); + + NW4RAssert_Line(160, *mStackEnd == OS_THREAD_STACK_MAGIC); + } +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_Util.cpp b/src/nw4r/snd/snd_Util.cpp index ccdb8035..a697c541 100644 --- a/src/nw4r/snd/snd_Util.cpp +++ b/src/nw4r/snd/snd_Util.cpp @@ -1 +1,767 @@ -#include "nw4r/snd/snd_Util.h" +#include "nw4r/snd/Util.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_Util.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <macros.h> // ARRAY_LENGTH +#include <types.h> + +#include "nw4r/ut/inlines.h" + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * variables + */ + +namespace nw4r { namespace snd { namespace detail +{ + // just assuming this is what this number is + f32 const Util::CALC_LPF_FREQ_INTERCEPT = 0.13561438f; + + // .rodata + + // clang-format off + f32 const Util::NoteTable[OCTAVE_DIVISION] = + { + 1.0f, + 1.0594631f, + 1.122462f, + 1.1892071f, + 1.2599211f, + 1.3348398f, + 1.4142135f, + 1.4983071f, + 1.587401f, + 1.6817929f, + 1.7817974f, + 1.8877486f + }; + + f32 const Util::PitchTable[PITCH_DIVISION_RANGE] = + { + 1.0f, 1.0002257f, 1.0004513f, 1.0006771f, 1.0009029f, 1.0011288f, + 1.0013547f, 1.0015807f, 1.0018067f, 1.0020328f, 1.0022589f, 1.002485f, + 1.0027113f, 1.0029376f, 1.0031638f, 1.0033902f, 1.0036167f, 1.0038432f, + 1.0040697f, 1.0042963f, 1.0045229f, 1.0047495f, 1.0049763f, 1.005203f, + 1.0054299f, 1.0056568f, 1.0058837f, 1.0061107f, 1.0063378f, 1.0065649f, + 1.006792f, 1.0070192f, 1.0072464f, 1.0074737f, 1.007701f, 1.0079285f, + 1.008156f, 1.0083834f, 1.008611f, 1.0088385f, 1.0090662f, 1.0092939f, + 1.0095217f, 1.0097495f, 1.0099773f, 1.0102053f, 1.0104332f, 1.0106612f, + 1.0108893f, 1.0111175f, 1.0113456f, 1.0115738f, 1.0118021f, 1.0120304f, + 1.0122588f, 1.0124872f, 1.0127157f, 1.0129442f, 1.0131727f, 1.0134014f, + 1.01363f, 1.0138588f, 1.0140876f, 1.0143164f, 1.0145453f, 1.0147743f, + 1.0150033f, 1.0152323f, 1.0154614f, 1.0156906f, 1.0159198f, 1.016149f, + 1.0163783f, 1.0166076f, 1.0168371f, 1.0170665f, 1.0172961f, 1.0175256f, + 1.0177553f, 1.0179849f, 1.0182146f, 1.0184444f, 1.0186743f, 1.0189041f, + 1.019134f, 1.019364f, 1.019594f, 1.019824f, 1.0200542f, 1.0202844f, + 1.0205146f, 1.0207449f, 1.0209752f, 1.0212057f, 1.0214361f, 1.0216666f, + 1.0218972f, 1.0221277f, 1.0223584f, 1.0225891f, 1.0228199f, 1.0230507f, + 1.0232816f, 1.0235125f, 1.0237434f, 1.0239744f, 1.0242054f, 1.0244366f, + 1.0246677f, 1.024899f, 1.0251303f, 1.0253617f, 1.025593f, 1.0258244f, + 1.0260559f, 1.0262874f, 1.0265191f, 1.0267507f, 1.0269824f, 1.0272142f, + 1.0274459f, 1.0276778f, 1.0279098f, 1.0281416f, 1.0283737f, 1.0286057f, + 1.0288378f, 1.02907f, 1.0293022f, 1.0295345f, 1.0297668f, 1.0299993f, + 1.0302316f, 1.0304642f, 1.0306966f, 1.0309292f, 1.0311619f, 1.0313946f, + 1.0316273f, 1.0318601f, 1.0320929f, 1.0323259f, 1.0325588f, 1.0327919f, + 1.0330249f, 1.033258f, 1.0334911f, 1.0337244f, 1.0339576f, 1.034191f, + 1.0344243f, 1.0346577f, 1.0348912f, 1.0351248f, 1.0353583f, 1.035592f, + 1.0358257f, 1.0360594f, 1.0362933f, 1.036527f, 1.0367609f, 1.0369949f, + 1.037229f, 1.037463f, 1.0376971f, 1.0379313f, 1.0381655f, 1.0383998f, + 1.0386341f, 1.0388684f, 1.0391029f, 1.0393374f, 1.0395719f, 1.0398065f, + 1.0400412f, 1.0402758f, 1.0405107f, 1.0407454f, 1.0409802f, 1.0412152f, + 1.0414501f, 1.0416851f, 1.0419202f, 1.0421553f, 1.0423905f, 1.0426257f, + 1.042861f, 1.0430963f, 1.0433317f, 1.0435672f, 1.0438026f, 1.0440382f, + 1.0442737f, 1.0445094f, 1.0447451f, 1.0449809f, 1.0452167f, 1.0454526f, + 1.0456885f, 1.0459244f, 1.0461605f, 1.0463965f, 1.0466326f, 1.0468688f, + 1.0471051f, 1.0473413f, 1.0475777f, 1.0478141f, 1.0480505f, 1.048287f, + 1.0485237f, 1.0487603f, 1.0489969f, 1.0492337f, 1.0494704f, 1.0497072f, + 1.049944f, 1.050181f, 1.050418f, 1.050655f, 1.0508921f, 1.0511292f, + 1.0513664f, 1.0516037f, 1.051841f, 1.0520784f, 1.0523158f, 1.0525533f, + 1.0527908f, 1.0530283f, 1.0532659f, 1.0535036f, 1.0537413f, 1.0539792f, + 1.054217f, 1.0544549f, 1.0546929f, 1.0549308f, 1.0551689f, 1.055407f, + 1.0556452f, 1.0558834f, 1.0561217f, 1.05636f, 1.0565984f, 1.0568368f, + 1.0570753f, 1.0573138f, 1.0575525f, 1.0577911f, 1.0580298f, 1.0582685f, + 1.0585073f, 1.0587462f, 1.0589851f, 1.0592241f + }; + + f32 const Util::Decibel2RatioTable[DECIBEL_TABLE_SIZE] = + { + 0.0f, 3.05492e-5f, 3.0903e-5f, 3.12608e-5f, 3.16228e-5f, + 3.1989e-5f, 3.23594e-5f, 3.27341e-5f, 3.31131e-5f, 3.34965e-5f, + 3.38844e-5f, 3.42768e-5f, 3.46737e-5f, 3.50752e-5f, 3.54813e-5f, + 3.58922e-5f, 3.63078e-5f, 3.67282e-5f, 3.71535e-5f, 3.75837e-5f, + 3.80189e-5f, 3.84592e-5f, 3.89045e-5f, 3.9355e-5f, 3.98107e-5f, + 4.02717e-5f, 4.0738e-5f, 4.12098e-5f, 4.16869e-5f, 4.21697e-5f, + 4.2658e-5f, 4.31519e-5f, 4.36516e-5f, 4.4157e-5f, 4.46684e-5f, + 4.51856e-5f, 4.57088e-5f, 4.62381e-5f, 4.67735e-5f, 4.73151e-5f, + 4.7863e-5f, 4.84172e-5f, 4.89779e-5f, 4.9545e-5f, 5.01187e-5f, + 5.06991e-5f, 5.12861e-5f, 5.188e-5f, 5.24807e-5f, 5.30884e-5f, + 5.37032e-5f, 5.4325e-5f, 5.49541e-5f, 5.55904e-5f, 5.62341e-5f, + 5.68853e-5f, 5.7544e-5f, 5.82103e-5f, 5.88844e-5f, 5.95662e-5f, + 6.0256e-5f, 6.09537e-5f, 6.16595e-5f, 6.23735e-5f, 6.30957e-5f, + 6.38263e-5f, 6.45654e-5f, 6.53131e-5f, 6.60693e-5f, 6.68344e-5f, + 6.76083e-5f, 6.83912e-5f, 6.91831e-5f, 6.99842e-5f, 7.07946e-5f, + 7.16143e-5f, 7.24436e-5f, 7.32825e-5f, 7.4131e-5f, 7.49894e-5f, + 7.58578e-5f, 7.67361e-5f, 7.76247e-5f, 7.85236e-5f, 7.94328e-5f, + 8.03526e-5f, 8.12831e-5f, 8.22243e-5f, 8.31764e-5f, 8.41395e-5f, + 8.51138e-5f, 8.60994e-5f, 8.70964e-5f, 8.81049e-5f, 8.91251e-5f, + 9.01571e-5f, 9.12011e-5f, 9.22571e-5f, 9.33254e-5f, 9.44061e-5f, + 9.54993e-5f, 9.66051e-5f, 9.77237e-5f, 9.88553e-5f, 1.0e-4f, + 1.01158e-4f, 1.02329e-4f, 1.03514e-4f, 1.04713e-4f, 1.05925e-4f, + 1.07152e-4f, 1.08393e-4f, 1.09648e-4f, 1.10917e-4f, 1.12202e-4f, + 1.13501e-4f, 1.14815e-4f, 1.16145e-4f, 1.1749e-4f, 1.1885e-4f, + 1.20226e-4f, 1.21619e-4f, 1.23027e-4f, 1.24451e-4f, 1.25893e-4f, + 1.2735e-4f, 1.28825e-4f, 1.30317e-4f, 1.31826e-4f, 1.33352e-4f, + 1.34896e-4f, 1.36458e-4f, 1.38038e-4f, 1.39637e-4f, 1.41254e-4f, + 1.42889e-4f, 1.44544e-4f, 1.46218e-4f, 1.47911e-4f, 1.49624e-4f, + 1.51356e-4f, 1.53109e-4f, 1.54882e-4f, 1.56675e-4f, 1.58489e-4f, + 1.60325e-4f, 1.62181e-4f, 1.64059e-4f, 1.65959e-4f, 1.6788e-4f, + 1.69824e-4f, 1.71791e-4f, 1.7378e-4f, 1.75792e-4f, 1.77828e-4f, + 1.79887e-4f, 1.8197e-4f, 1.84077e-4f, 1.86209e-4f, 1.88365e-4f, + 1.90546e-4f, 1.92752e-4f, 1.94984e-4f, 1.97242e-4f, 1.99526e-4f, + 2.01837e-4f, 2.04174e-4f, 2.06538e-4f, 2.0893e-4f, 2.11349e-4f, + 2.13796e-4f, 2.16272e-4f, 2.18776e-4f, 2.21309e-4f, 2.23872e-4f, + 2.26464e-4f, 2.29087e-4f, 2.31739e-4f, 2.34423e-4f, 2.37137e-4f, + 2.39883e-4f, 2.42661e-4f, 2.45471e-4f, 2.48313e-4f, 2.51189e-4f, + 2.54097e-4f, 2.5704e-4f, 2.60016e-4f, 2.63027e-4f, 2.66073e-4f, + 2.69153e-4f, 2.7227e-4f, 2.75423e-4f, 2.78612e-4f, 2.81838e-4f, + 2.85102e-4f, 2.88403e-4f, 2.91743e-4f, 2.95121e-4f, 2.98538e-4f, + 3.01995e-4f, 3.05492e-4f, 3.0903e-4f, 3.12608e-4f, 3.16228e-4f, + 3.1989e-4f, 3.23594e-4f, 3.27341e-4f, 3.31131e-4f, 3.34965e-4f, + 3.38844e-4f, 3.42768e-4f, 3.46737e-4f, 3.50752e-4f, 3.54813e-4f, + 3.58922e-4f, 3.63078e-4f, 3.67282e-4f, 3.71535e-4f, 3.75837e-4f, + 3.80189e-4f, 3.84592e-4f, 3.89045e-4f, 3.9355e-4f, 3.98107e-4f, + 4.02717e-4f, 4.0738e-4f, 4.12098e-4f, 4.16869e-4f, 4.21697e-4f, + 4.2658e-4f, 4.31519e-4f, 4.36516e-4f, 4.4157e-4f, 4.46684e-4f, + 4.51856e-4f, 4.57088e-4f, 4.62381e-4f, 4.67735e-4f, 4.73151e-4f, + 4.7863e-4f, 4.84172e-4f, 4.89779e-4f, 4.9545e-4f, 5.01187e-4f, + 5.06991e-4f, 5.12861e-4f, 5.188e-4f, 5.24807e-4f, 5.30884e-4f, + 5.37032e-4f, 5.4325e-4f, 5.49541e-4f, 5.55904e-4f, 5.62341e-4f, + 5.68853e-4f, 5.7544e-4f, 5.82103e-4f, 5.88844e-4f, 5.95662e-4f, + 6.0256e-4f, 6.09537e-4f, 6.16595e-4f, 6.23735e-4f, 6.30957e-4f, + 6.38263e-4f, 6.45654e-4f, 6.53131e-4f, 6.60693e-4f, 6.68344e-4f, + 6.76083e-4f, 6.83912e-4f, 6.91831e-4f, 6.99842e-4f, 7.07946e-4f, + 7.16143e-4f, 7.24436e-4f, 7.32825e-4f, 7.4131e-4f, 7.49894e-4f, + 7.58578e-4f, 7.67361e-4f, 7.76247e-4f, 7.85236e-4f, 7.94328e-4f, + 8.03526e-4f, 8.12831e-4f, 8.22243e-4f, 8.31764e-4f, 8.41395e-4f, + 8.51138e-4f, 8.60994e-4f, 8.70964e-4f, 8.81049e-4f, 8.91251e-4f, + 9.01571e-4f, 9.12011e-4f, 9.22571e-4f, 9.33254e-4f, 9.44061e-4f, + 9.54993e-4f, 9.66051e-4f, 9.77237e-4f, 9.88553e-4f, 0.001f, + 0.001011579f, 0.001023293f, 0.001035142f, 0.001047129f, 0.001059254f, + 0.001071519f, 0.001083927f, 0.001096478f, 0.001109175f, 0.001122018f, + 0.001135011f, 0.001148154f, 0.001161449f, 0.001174898f, 0.001188502f, + 0.001202264f, 0.001216186f, 0.001230269f, 0.001244515f, 0.001258925f, + 0.001273503f, 0.00128825f, 0.001303167f, 0.001318257f, 0.001333521f, + 0.001348963f, 0.001364583f, 0.001380384f, 0.001396368f, 0.001412538f, + 0.001428894f, 0.00144544f, 0.001462177f, 0.001479108f, 0.001496236f, + 0.001513561f, 0.001531087f, 0.001548817f, 0.001566751f, 0.001584893f, + 0.001603245f, 0.00162181f, 0.00164059f, 0.001659587f, 0.001678804f, + 0.001698244f, 0.001717908f, 0.001737801f, 0.001757924f, 0.001778279f, + 0.001798871f, 0.001819701f, 0.001840772f, 0.001862087f, 0.001883649f, + 0.001905461f, 0.001927525f, 0.001949845f, 0.001972423f, 0.001995262f, + 0.002018366f, 0.002041738f, 0.00206538f, 0.002089296f, 0.002113489f, + 0.002137962f, 0.002162719f, 0.002187762f, 0.002213095f, 0.002238721f, + 0.002264644f, 0.002290868f, 0.002317395f, 0.002344229f, 0.002371374f, + 0.002398833f, 0.00242661f, 0.002454709f, 0.002483133f, 0.002511886f, + 0.002540973f, 0.002570396f, 0.00260016f, 0.002630268f, 0.002660725f, + 0.002691535f, 0.002722701f, 0.002754229f, 0.002786121f, 0.002818383f, + 0.002851018f, 0.002884032f, 0.002917427f, 0.002951209f, 0.002985383f, + 0.003019952f, 0.003054921f, 0.003090295f, 0.003126079f, 0.003162278f, + 0.003198895f, 0.003235937f, 0.003273407f, 0.003311311f, 0.003349654f, + 0.003388442f, 0.003427678f, 0.003467369f, 0.003507519f, 0.003548134f, + 0.003589219f, 0.003630781f, 0.003672823f, 0.003715352f, 0.003758374f, + 0.003801894f, 0.003845918f, 0.003890451f, 0.003935501f, 0.003981072f, + 0.00402717f, 0.004073803f, 0.004120975f, 0.004168694f, 0.004216965f, + 0.004265795f, 0.004315191f, 0.004365158f, 0.004415704f, 0.004466836f, + 0.004518559f, 0.004570882f, 0.00462381f, 0.004677351f, 0.004731513f, + 0.004786301f, 0.004841724f, 0.004897788f, 0.004954502f, 0.005011872f, + 0.005069907f, 0.005128614f, 0.005188f, 0.005248075f, 0.005308844f, + 0.005370318f, 0.005432503f, 0.005495409f, 0.005559043f, 0.005623413f, + 0.005688529f, 0.005754399f, 0.005821032f, 0.005888437f, 0.005956621f, + 0.006025596f, 0.006095369f, 0.00616595f, 0.006237348f, 0.006309573f, + 0.006382635f, 0.006456542f, 0.006531306f, 0.006606934f, 0.006683439f, + 0.00676083f, 0.006839116f, 0.00691831f, 0.00699842f, 0.007079458f, + 0.007161434f, 0.00724436f, 0.007328245f, 0.007413102f, 0.007498942f, + 0.007585776f, 0.007673615f, 0.007762471f, 0.007852356f, 0.007943282f, + 0.008035261f, 0.008128305f, 0.008222426f, 0.008317638f, 0.008413951f, + 0.00851138f, 0.008609938f, 0.008709636f, 0.008810489f, 0.008912509f, + 0.009015711f, 0.009120108f, 0.009225714f, 0.009332543f, 0.009440609f, + 0.009549926f, 0.009660509f, 0.009772372f, 0.009885531f, 0.01f, + 0.010115795f, 0.01023293f, 0.010351422f, 0.010471285f, 0.010592537f, + 0.010715193f, 0.010839269f, 0.010964782f, 0.011091748f, 0.011220185f, + 0.011350108f, 0.011481536f, 0.011614486f, 0.011748976f, 0.011885022f, + 0.012022644f, 0.01216186f, 0.012302688f, 0.012445146f, 0.012589254f, + 0.012735031f, 0.012882496f, 0.013031668f, 0.013182567f, 0.013335214f, + 0.013489629f, 0.013645831f, 0.013803843f, 0.013963684f, 0.014125375f, + 0.01428894f, 0.014454398f, 0.014621772f, 0.014791084f, 0.014962357f, + 0.015135612f, 0.015310875f, 0.015488166f, 0.015667511f, 0.015848933f, + 0.016032454f, 0.016218102f, 0.016405897f, 0.016595868f, 0.01678804f, + 0.016982436f, 0.017179083f, 0.017378008f, 0.017579235f, 0.017782794f, + 0.01798871f, 0.01819701f, 0.01840772f, 0.018620871f, 0.01883649f, + 0.019054607f, 0.01927525f, 0.019498445f, 0.019724227f, 0.019952623f, + 0.020183664f, 0.02041738f, 0.020653803f, 0.020892961f, 0.02113489f, + 0.021379622f, 0.021627186f, 0.021877617f, 0.022130948f, 0.02238721f, + 0.022646444f, 0.022908676f, 0.023173947f, 0.023442289f, 0.023713738f, + 0.023988329f, 0.024266101f, 0.024547089f, 0.02483133f, 0.025118863f, + 0.025409726f, 0.025703957f, 0.026001597f, 0.02630268f, 0.02660725f, + 0.026915347f, 0.027227012f, 0.027542287f, 0.027861211f, 0.028183829f, + 0.028510183f, 0.028840315f, 0.02917427f, 0.029512092f, 0.029853826f, + 0.030199517f, 0.030549211f, 0.030902954f, 0.031260792f, 0.03162278f, + 0.031988952f, 0.032359365f, 0.03273407f, 0.03311311f, 0.033496544f, + 0.033884417f, 0.03427678f, 0.034673683f, 0.035075188f, 0.035481337f, + 0.035892192f, 0.036307804f, 0.03672823f, 0.037153523f, 0.03758374f, + 0.03801894f, 0.038459178f, 0.038904514f, 0.03935501f, 0.039810717f, + 0.040271703f, 0.040738028f, 0.041209754f, 0.041686937f, 0.04216965f, + 0.042657953f, 0.043151908f, 0.043651585f, 0.044157047f, 0.044668358f, + 0.045185596f, 0.04570882f, 0.046238102f, 0.046773516f, 0.047315124f, + 0.04786301f, 0.048417237f, 0.04897788f, 0.04954502f, 0.050118722f, + 0.05069907f, 0.05128614f, 0.051880006f, 0.052480746f, 0.053088445f, + 0.05370318f, 0.054325033f, 0.054954085f, 0.055590425f, 0.056234132f, + 0.056885295f, 0.057543993f, 0.05821032f, 0.058884367f, 0.059566215f, + 0.06025596f, 0.06095369f, 0.0616595f, 0.062373485f, 0.06309573f, + 0.06382635f, 0.06456542f, 0.065313056f, 0.06606934f, 0.06683439f, + 0.0676083f, 0.06839117f, 0.0691831f, 0.0699842f, 0.070794575f, + 0.07161434f, 0.0724436f, 0.07328245f, 0.07413103f, 0.07498942f, + 0.07585776f, 0.07673615f, 0.07762471f, 0.07852356f, 0.07943282f, + 0.08035261f, 0.081283055f, 0.082224265f, 0.083176374f, 0.08413951f, + 0.0851138f, 0.08609937f, 0.087096356f, 0.08810489f, 0.0891251f, + 0.090157114f, 0.09120108f, 0.09225714f, 0.09332543f, 0.09440609f, + 0.09549926f, 0.096605085f, 0.09772372f, 0.09885531f, 0.1f, + 0.10115795f, 0.1023293f, 0.10351422f, 0.10471285f, 0.105925374f, + 0.10715193f, 0.10839269f, 0.10964782f, 0.11091748f, 0.11220185f, + 0.11350108f, 0.11481536f, 0.11614486f, 0.117489755f, 0.11885022f, + 0.12022644f, 0.1216186f, 0.12302688f, 0.12445146f, 0.12589253f, + 0.1273503f, 0.12882495f, 0.13031667f, 0.13182567f, 0.13335215f, + 0.1348963f, 0.13645831f, 0.13803843f, 0.13963683f, 0.14125375f, + 0.1428894f, 0.14454398f, 0.14621772f, 0.14791083f, 0.14962357f, + 0.15135613f, 0.15310875f, 0.15488166f, 0.1566751f, 0.15848932f, + 0.16032454f, 0.162181f, 0.16405898f, 0.16595869f, 0.1678804f, + 0.16982436f, 0.17179084f, 0.17378008f, 0.17579237f, 0.17782794f, + 0.17988709f, 0.18197009f, 0.1840772f, 0.18620871f, 0.18836491f, + 0.19054607f, 0.1927525f, 0.19498447f, 0.19724227f, 0.19952624f, + 0.20183663f, 0.20417379f, 0.20653802f, 0.20892961f, 0.2113489f, + 0.21379621f, 0.21627185f, 0.21877617f, 0.22130947f, 0.22387211f, + 0.22646444f, 0.22908677f, 0.23173946f, 0.23442288f, 0.23713738f, + 0.23988329f, 0.24266101f, 0.2454709f, 0.24831331f, 0.25118864f, + 0.25409728f, 0.25703958f, 0.26001596f, 0.2630268f, 0.2660725f, + 0.26915348f, 0.27227014f, 0.27542287f, 0.2786121f, 0.2818383f, + 0.28510183f, 0.28840315f, 0.2917427f, 0.29512092f, 0.29853827f, + 0.30199516f, 0.3054921f, 0.30902955f, 0.31260794f, 0.31622776f, + 0.31988952f, 0.32359365f, 0.3273407f, 0.33113113f, 0.33496544f, + 0.33884415f, 0.34276778f, 0.34673685f, 0.35075188f, 0.3548134f, + 0.35892195f, 0.36307806f, 0.3672823f, 0.37153524f, 0.37583742f, + 0.3801894f, 0.3845918f, 0.38904515f, 0.39355007f, 0.39810717f, + 0.40271702f, 0.40738028f, 0.4120975f, 0.41686937f, 0.4216965f, + 0.4265795f, 0.4315191f, 0.43651584f, 0.44157046f, 0.4466836f, + 0.45185596f, 0.4570882f, 0.46238104f, 0.46773514f, 0.47315127f, + 0.4786301f, 0.48417237f, 0.48977882f, 0.4954502f, 0.5011872f, + 0.50699073f, 0.5128614f, 0.5188f, 0.52480745f, 0.53088444f, + 0.53703177f, 0.5432503f, 0.5495409f, 0.55590427f, 0.56234133f, + 0.5688529f, 0.57543993f, 0.5821032f, 0.58884364f, 0.5956621f, + 0.60255957f, 0.6095369f, 0.61659503f, 0.62373483f, 0.63095737f, + 0.63826346f, 0.6456542f, 0.65313053f, 0.66069347f, 0.6683439f, + 0.67608297f, 0.6839116f, 0.691831f, 0.699842f, 0.70794576f, + 0.7161434f, 0.724436f, 0.7328245f, 0.74131024f, 0.7498942f, + 0.7585776f, 0.76736146f, 0.77624714f, 0.78523564f, 0.7943282f, + 0.8035261f, 0.8128305f, 0.8222427f, 0.83176374f, 0.84139514f, + 0.85113806f, 0.86099374f, 0.8709636f, 0.88104886f, 0.8912509f, + 0.90157115f, 0.91201085f, 0.9225714f, 0.9332543f, 0.94406086f, + 0.9549926f, 0.96605086f, 0.9772372f, 0.9885531f, 1.0f, + 1.0115795f, 1.023293f, 1.0351422f, 1.0471286f, 1.0592537f, + 1.0715193f, 1.0839269f, 1.0964782f, 1.1091748f, 1.1220185f, + 1.1350108f, 1.1481537f, 1.1614486f, 1.1748976f, 1.1885022f, + 1.2022644f, 1.216186f, 1.2302687f, 1.2445146f, 1.2589254f, + 1.2735031f, 1.2882495f, 1.3031667f, 1.3182567f, 1.3335215f, + 1.3489629f, 1.3645831f, 1.3803842f, 1.3963684f, 1.4125376f, + 1.4288939f, 1.4454398f, 1.4621772f, 1.4791083f, 1.4962356f, + 1.5135612f, 1.5310875f, 1.5488166f, 1.5667511f, 1.5848932f, + 1.6032454f, 1.6218101f, 1.6405897f, 1.6595869f, 1.678804f, + 1.6982436f, 1.7179084f, 1.7378008f, 1.7579236f, 1.7782794f, + 1.7988709f, 1.8197008f, 1.840772f, 1.8620871f, 1.8836491f, + 1.9054607f, 1.9275249f, 1.9498446f, 1.9724227f, 1.9952623f + }; + + f32 const Util::Pan2RatioTableSqrt[PAN_TABLE_SIZE] = + { + 1.0f, 0.99804497f, 0.9960861f, 0.99412334f, 0.99215674f, + 0.9901862f, 0.98821175f, 0.98623335f, 0.98425096f, 0.9822646f, + 0.9802742f, 0.97827977f, 0.9762812f, 0.97427857f, 0.9722718f, + 0.9702609f, 0.96824586f, 0.9662266f, 0.96420306f, 0.96217525f, + 0.9601432f, 0.9581069f, 0.95606613f, 0.9540211f, 0.95197165f, + 0.94991773f, 0.9478594f, 0.9457966f, 0.9437293f, 0.9416574f, + 0.93958104f, 0.9375f, 0.9354144f, 0.93332404f, 0.93122905f, + 0.9291293f, 0.9270248f, 0.92491555f, 0.92280143f, 0.9206825f, + 0.91855866f, 0.9164299f, 0.91429615f, 0.9121575f, 0.91001374f, + 0.9078649f, 0.90571105f, 0.903552f, 0.9013878f, 0.89921844f, + 0.89704376f, 0.89486384f, 0.89267856f, 0.8904879f, 0.8882919f, + 0.88609046f, 0.8838835f, 0.881671f, 0.87945294f, 0.8772293f, + 0.875f, 0.872765f, 0.8705243f, 0.8682777f, 0.8660254f, + 0.8637672f, 0.86150306f, 0.85923296f, 0.85695684f, 0.85467464f, + 0.85238636f, 0.85009193f, 0.84779125f, 0.8454843f, 0.8431711f, + 0.8408515f, 0.8385255f, 0.836193f, 0.833854f, 0.8315084f, + 0.8291562f, 0.8267973f, 0.8244316f, 0.82205915f, 0.8196798f, + 0.8172935f, 0.8149003f, 0.8125f, 0.81009257f, 0.807678f, + 0.8052562f, 0.80282706f, 0.80039054f, 0.7979466f, 0.79549515f, + 0.7930361f, 0.7905694f, 0.788095f, 0.7856128f, 0.7831228f, + 0.78062475f, 0.7781187f, 0.7756046f, 0.7730823f, 0.77055174f, + 0.7680129f, 0.76546556f, 0.7629097f, 0.76034534f, 0.7577722f, + 0.7551904f, 0.75259966f, 0.75f, 0.7473913f, 0.74477345f, + 0.7421464f, 0.73951f, 0.73686415f, 0.73420876f, 0.7315437f, + 0.72886896f, 0.72618437f, 0.7234898f, 0.72078514f, 0.7180703f, + 0.7153452f, 0.71260965f, 0.70986354f, 0.70710677f, 0.7043392f, + 0.70156074f, 0.69877124f, 0.69597054f, 0.6931585f, 0.69033504f, + 0.6875f, 0.6846532f, 0.6817945f, 0.6789238f, 0.6760409f, + 0.6731456f, 0.67023784f, 0.6673174f, 0.6643841f, 0.6614378f, + 0.6584784f, 0.65550554f, 0.65251917f, 0.649519f, 0.646505f, + 0.6434769f, 0.64043444f, 0.63737744f, 0.6343057f, 0.631219f, + 0.6281172f, 0.625f, 0.6218671f, 0.61871845f, 0.6155536f, + 0.61237246f, 0.60917467f, 0.60596f, 0.6027282f, 0.59947896f, + 0.596212f, 0.59292704f, 0.5896238f, 0.586302f, 0.5829612f, + 0.57960117f, 0.5762215f, 0.572822f, 0.5694021f, 0.5659616f, + 0.5625f, 0.559017f, 0.55551213f, 0.551985f, 0.5484353f, + 0.5448624f, 0.5412659f, 0.53764534f, 0.5340002f, 0.53033006f, + 0.52663434f, 0.5229125f, 0.51916397f, 0.5153882f, 0.5115845f, + 0.5077524f, 0.5038911f, 0.5f, 0.49607837f, 0.49212548f, + 0.4881406f, 0.48412293f, 0.4800716f, 0.47598583f, 0.47186464f, + 0.4677072f, 0.4635124f, 0.45927933f, 0.45500687f, 0.4506939f, + 0.44633928f, 0.44194174f, 0.4375f, 0.4330127f, 0.42847842f, + 0.42389563f, 0.41926274f, 0.4145781f, 0.4098399f, 0.40504628f, + 0.40019527f, 0.3952847f, 0.39031237f, 0.38527587f, 0.38017267f, + 0.375f, 0.369755f, 0.36443448f, 0.35903516f, 0.35355338f, + 0.34798527f, 0.3423266f, 0.3365728f, 0.3307189f, 0.3247595f, + 0.31868872f, 0.3125f, 0.30618623f, 0.29973948f, 0.293151f, + 0.286411f, 0.2795085f, 0.2724312f, 0.26516503f, 0.2576941f, + 0.25f, 0.24206147f, 0.2338536f, 0.22534695f, 0.21650635f, + 0.20728905f, 0.19764236f, 0.1875f, 0.17677669f, 0.16535945f, + 0.15309311f, 0.13975425f, 0.125f, 0.10825317f, 0.088388346f, + 0.0625f, 0.0f + }; + + f32 const Util::Pan2RatioTableSinCos[PAN_TABLE_SIZE] = + { + 1.0f, 0.99998116f, 0.9999247f, 0.9998306f, 0.9996988f, + 0.9995294f, 0.9993224f, 0.99907774f, 0.99879545f, 0.99847555f, + 0.9981181f, 0.99772304f, 0.99729043f, 0.9968203f, 0.9963126f, + 0.9957674f, 0.9951847f, 0.9945646f, 0.993907f, 0.9932119f, + 0.99247956f, 0.99170977f, 0.99090266f, 0.9900582f, 0.9891765f, + 0.9882576f, 0.9873014f, 0.9863081f, 0.98527765f, 0.9842101f, + 0.9831055f, 0.9819639f, 0.98078525f, 0.9795698f, 0.9783174f, + 0.97702813f, 0.9757021f, 0.97433937f, 0.97293997f, 0.9715039f, + 0.97003126f, 0.9685221f, 0.96697646f, 0.96539444f, 0.96377605f, + 0.9621214f, 0.9604305f, 0.95870346f, 0.95694035f, 0.9551412f, + 0.953306f, 0.951435f, 0.94952816f, 0.9475856f, 0.9456073f, + 0.94359344f, 0.94154406f, 0.9394592f, 0.937339f, 0.9351835f, + 0.9329928f, 0.93076694f, 0.9285061f, 0.9262102f, 0.9238795f, + 0.92151403f, 0.9191139f, 0.9166791f, 0.9142098f, 0.91170603f, + 0.909168f, 0.9065957f, 0.9039893f, 0.9013488f, 0.8986745f, + 0.89596623f, 0.8932243f, 0.89044875f, 0.88763964f, 0.8847971f, + 0.8819213f, 0.8790122f, 0.8760701f, 0.873095f, 0.87008697f, + 0.86704624f, 0.86397284f, 0.86086696f, 0.8577286f, 0.854558f, + 0.8513552f, 0.84812033f, 0.8448536f, 0.841555f, 0.8382247f, + 0.8348629f, 0.8314696f, 0.82804507f, 0.8245893f, 0.8211025f, + 0.8175848f, 0.8140363f, 0.81045717f, 0.8068476f, 0.8032075f, + 0.79953724f, 0.7958369f, 0.79210657f, 0.7883464f, 0.78455657f, + 0.7807372f, 0.7768885f, 0.77301043f, 0.76910335f, 0.76516724f, + 0.7612024f, 0.7572088f, 0.7531868f, 0.7491364f, 0.74505776f, + 0.7409511f, 0.7368166f, 0.7326543f, 0.72846437f, 0.7242471f, + 0.72000253f, 0.71573085f, 0.7114322f, 0.70710677f, 0.70275474f, + 0.69837624f, 0.69397146f, 0.68954057f, 0.6850837f, 0.680601f, + 0.6760927f, 0.671559f, 0.66699994f, 0.6624158f, 0.6578067f, + 0.65317285f, 0.6485144f, 0.64383155f, 0.63912445f, 0.6343933f, + 0.62963825f, 0.6248595f, 0.6200572f, 0.6152316f, 0.6103828f, + 0.60551107f, 0.60061646f, 0.5956993f, 0.5907597f, 0.58579785f, + 0.58081394f, 0.57580817f, 0.57078075f, 0.5657318f, 0.56066155f, + 0.55557024f, 0.55045795f, 0.545325f, 0.54017144f, 0.53499764f, + 0.52980363f, 0.52458966f, 0.519356f, 0.51410276f, 0.50883013f, + 0.50353837f, 0.49822766f, 0.4928982f, 0.48755017f, 0.48218378f, + 0.47679922f, 0.47139674f, 0.4659765f, 0.46053872f, 0.45508358f, + 0.44961134f, 0.44412214f, 0.43861625f, 0.43309382f, 0.42755508f, + 0.42200026f, 0.41642955f, 0.41084316f, 0.4052413f, 0.3996242f, + 0.39399204f, 0.38834503f, 0.38268343f, 0.37700742f, 0.3713172f, + 0.36561298f, 0.35989505f, 0.35416353f, 0.34841868f, 0.34266073f, + 0.33688986f, 0.3311063f, 0.3253103f, 0.31950203f, 0.31368175f, + 0.30784965f, 0.30200595f, 0.2961509f, 0.29028466f, 0.28440753f, + 0.2785197f, 0.27262136f, 0.26671275f, 0.2607941f, 0.25486565f, + 0.24892761f, 0.24298018f, 0.2370236f, 0.2310581f, 0.22508392f, + 0.21910124f, 0.21311031f, 0.20711137f, 0.20110464f, 0.19509032f, + 0.18906866f, 0.18303989f, 0.17700422f, 0.17096189f, 0.16491312f, + 0.15885815f, 0.15279719f, 0.14673047f, 0.14065824f, 0.1345807f, + 0.1284981f, 0.12241068f, 0.11631863f, 0.110222206f, 0.10412163f, + 0.09801714f, 0.091908954f, 0.08579731f, 0.07968244f, 0.07356457f, + 0.06744392f, 0.061320737f, 0.055195242f, 0.049067672f, 0.04293826f, + 0.036807224f, 0.030674802f, 0.024541229f, 0.01840673f, 0.012271538f, + 0.006135885f, 0.0f + }; + + f32 const Util::Pan2RatioTableLinear[PAN_TABLE_SIZE] = + { + 1.0f, 0.99609375f, 0.9921875f, 0.98828125f, 0.984375f, + 0.98046875f, 0.9765625f, 0.97265625f, 0.96875f, 0.96484375f, + 0.9609375f, 0.95703125f, 0.953125f, 0.94921875f, 0.9453125f, + 0.94140625f, 0.9375f, 0.93359375f, 0.9296875f, 0.92578125f, + 0.921875f, 0.91796875f, 0.9140625f, 0.91015625f, 0.90625f, + 0.90234375f, 0.8984375f, 0.89453125f, 0.890625f, 0.88671875f, + 0.8828125f, 0.87890625f, 0.875f, 0.87109375f, 0.8671875f, + 0.86328125f, 0.859375f, 0.85546875f, 0.8515625f, 0.84765625f, + 0.84375f, 0.83984375f, 0.8359375f, 0.83203125f, 0.828125f, + 0.82421875f, 0.8203125f, 0.81640625f, 0.8125f, 0.80859375f, + 0.8046875f, 0.80078125f, 0.796875f, 0.79296875f, 0.7890625f, + 0.78515625f, 0.78125f, 0.77734375f, 0.7734375f, 0.76953125f, + 0.765625f, 0.76171875f, 0.7578125f, 0.75390625f, 0.75f, + 0.74609375f, 0.7421875f, 0.73828125f, 0.734375f, 0.73046875f, + 0.7265625f, 0.72265625f, 0.71875f, 0.71484375f, 0.7109375f, + 0.70703125f, 0.703125f, 0.69921875f, 0.6953125f, 0.69140625f, + 0.6875f, 0.68359375f, 0.6796875f, 0.67578125f, 0.671875f, + 0.66796875f, 0.6640625f, 0.66015625f, 0.65625f, 0.65234375f, + 0.6484375f, 0.64453125f, 0.640625f, 0.63671875f, 0.6328125f, + 0.62890625f, 0.625f, 0.62109375f, 0.6171875f, 0.61328125f, + 0.609375f, 0.60546875f, 0.6015625f, 0.59765625f, 0.59375f, + 0.58984375f, 0.5859375f, 0.58203125f, 0.578125f, 0.57421875f, + 0.5703125f, 0.56640625f, 0.5625f, 0.55859375f, 0.5546875f, + 0.55078125f, 0.546875f, 0.54296875f, 0.5390625f, 0.53515625f, + 0.53125f, 0.52734375f, 0.5234375f, 0.51953125f, 0.515625f, + 0.51171875f, 0.5078125f, 0.50390625f, 0.5f, 0.49609375f, + 0.4921875f, 0.48828125f, 0.484375f, 0.48046875f, 0.4765625f, + 0.47265625f, 0.46875f, 0.46484375f, 0.4609375f, 0.45703125f, + 0.453125f, 0.44921875f, 0.4453125f, 0.44140625f, 0.4375f, + 0.43359375f, 0.4296875f, 0.42578125f, 0.421875f, 0.41796875f, + 0.4140625f, 0.41015625f, 0.40625f, 0.40234375f, 0.3984375f, + 0.39453125f, 0.390625f, 0.38671875f, 0.3828125f, 0.37890625f, + 0.375f, 0.37109375f, 0.3671875f, 0.36328125f, 0.359375f, + 0.35546875f, 0.3515625f, 0.34765625f, 0.34375f, 0.33984375f, + 0.3359375f, 0.33203125f, 0.328125f, 0.32421875f, 0.3203125f, + 0.31640625f, 0.3125f, 0.30859375f, 0.3046875f, 0.30078125f, + 0.296875f, 0.29296875f, 0.2890625f, 0.28515625f, 0.28125f, + 0.27734375f, 0.2734375f, 0.26953125f, 0.265625f, 0.26171875f, + 0.2578125f, 0.25390625f, 0.25f, 0.24609375f, 0.2421875f, + 0.23828125f, 0.234375f, 0.23046875f, 0.2265625f, 0.22265625f, + 0.21875f, 0.21484375f, 0.2109375f, 0.20703125f, 0.203125f, + 0.19921875f, 0.1953125f, 0.19140625f, 0.1875f, 0.18359375f, + 0.1796875f, 0.17578125f, 0.171875f, 0.16796875f, 0.1640625f, + 0.16015625f, 0.15625f, 0.15234375f, 0.1484375f, 0.14453125f, + 0.140625f, 0.13671875f, 0.1328125f, 0.12890625f, 0.125f, + 0.12109375f, 0.1171875f, 0.11328125f, 0.109375f, 0.10546875f, + 0.1015625f, 0.09765625f, 0.09375f, 0.08984375f, 0.0859375f, + 0.08203125f, 0.078125f, 0.07421875f, 0.0703125f, 0.06640625f, + 0.0625f, 0.05859375f, 0.0546875f, 0.05078125f, 0.046875f, + 0.04296875f, 0.0390625f, 0.03515625f, 0.03125f, 0.02734375f, + 0.0234375f, 0.01953125f, 0.015625f, 0.01171875f, 0.0078125f, + 0.00390625f, 0.0f + }; + + u16 const Util::RemoteFilterCoefTable[COEF_TABLE_SIZE][BIQUAD_COEF_COUNT] = + { + /* b0 b1 b2 a1 a2 */ + {0x387c, 0x70f7, 0x387c, 0x8144, 0xc13e}, + {0x3549, 0x6a93, 0x3549, 0x88e9, 0xc7f2}, + {0x31c4, 0x6389, 0x31c4, 0x9211, 0xce95}, + {0x2e7c, 0x5cf9, 0x2e7c, 0x9b3e, 0xd422}, + {0x2b6f, 0x56de, 0x2b6f, 0xa44c, 0xd8c7}, + {0x2899, 0x5133, 0x2899, 0xad24, 0xdca7}, + {0x25f7, 0x4bee, 0x25f7, 0xb5ba, 0xdfe4}, + {0x2384, 0x4709, 0x2384, 0xbe07, 0xe294}, + {0x213d, 0x427a, 0x213d, 0xc608, 0xe4ce}, + {0x1f1d, 0x3e3a, 0x1f1d, 0xcdbc, 0xe6a2}, + {0x1d22, 0x3a44, 0x1d22, 0xd525, 0xe81d}, + {0x1b48, 0x3690, 0x1b48, 0xdc45, 0xe94c}, + {0x198d, 0x331a, 0x198d, 0xe31d, 0xea39}, + {0x17ee, 0x2fdc, 0x17ee, 0xe9b0, 0xeaec}, + {0x1669, 0x2cd2, 0x1669, 0xf001, 0xeb6c}, + {0x14fd, 0x29f9, 0x14fd, 0xf613, 0xebbe}, + {0x13a7, 0x274d, 0x13a7, 0xfbe7, 0xebe9}, + {0x1265, 0x24cb, 0x1265, 0x0180, 0xebf1}, + {0x1138, 0x2270, 0x1138, 0x06e2, 0xebd9}, + {0x101c, 0x2039, 0x101c, 0x0c0d, 0xeba6}, + {0x0f12, 0x1e24, 0x0f12, 0x1104, 0xeb5a}, + {0x0e18, 0x1c30, 0x0e18, 0x15c9, 0xeaf8}, + {0x0d2d, 0x1a59, 0x0d2d, 0x1a5d, 0xea84}, + {0x0c50, 0x189f, 0x0c50, 0x1ec3, 0xe9fe}, + {0x0b80, 0x1700, 0x0b80, 0x22fc, 0xe969}, + {0x0abd, 0x1579, 0x0abd, 0x270a, 0xe8c7}, + {0x0a05, 0x140b, 0x0a05, 0x2aed, 0xe81a}, + {0x0959, 0x12b3, 0x0959, 0x2ea9, 0xe763}, + {0x08b8, 0x1170, 0x08b8, 0x323d, 0xe6a3}, + {0x0820, 0x1041, 0x0820, 0x35ac, 0xe5dd}, + {0x0792, 0x0f25, 0x0792, 0x38f6, 0xe510}, + {0x070d, 0x0e1a, 0x070d, 0x3c1d, 0xe43e}, + {0x0690, 0x0d21, 0x0690, 0x3f23, 0xe369}, + {0x061b, 0x0c37, 0x061b, 0x4208, 0xe290}, + {0x05ae, 0x0b5c, 0x05ae, 0x44cd, 0xe1b6}, + {0x0548, 0x0a90, 0x0548, 0x4774, 0xe0da}, + {0x04e8, 0x09d0, 0x04e8, 0x49fe, 0xdffd}, + {0x048f, 0x091e, 0x048f, 0x4c6c, 0xdf20}, + {0x043b, 0x0877, 0x043b, 0x4ebf, 0xde44}, + {0x03ed, 0x07db, 0x03ed, 0x50f8, 0xdd69}, + {0x03a5, 0x0749, 0x03a5, 0x5317, 0xdc90}, + {0x0361, 0x06c2, 0x0361, 0x551f, 0xdbb8}, + {0x0322, 0x0643, 0x0322, 0x5710, 0xdae3}, + {0x02e7, 0x05ce, 0x02e7, 0x58ea, 0xda11}, + {0x02b0, 0x0560, 0x02b0, 0x5aaf, 0xd941}, + {0x027d, 0x04fa, 0x027d, 0x5c60, 0xd875}, + {0x024e, 0x049b, 0x024e, 0x5dfe, 0xd7ad}, + {0x0222, 0x0443, 0x0222, 0x5f88, 0xd6e8}, + {0x01f9, 0x03f1, 0x01f9, 0x6101, 0xd627}, + {0x01d3, 0x03a5, 0x01d3, 0x6269, 0xd56a}, + {0x01af, 0x035f, 0x01af, 0x63c0, 0xd4b1}, + {0x018f, 0x031d, 0x018f, 0x6507, 0xd3fc}, + {0x0170, 0x02e1, 0x0170, 0x6640, 0xd34c}, + {0x0154, 0x02a8, 0x0154, 0x676a, 0xd2a0}, + {0x013a, 0x0274, 0x013a, 0x6887, 0xd1f9}, + {0x0122, 0x0244, 0x0122, 0x6996, 0xd156}, + {0x010c, 0x0217, 0x010c, 0x6a99, 0xd0b7}, + {0x00f7, 0x01ee, 0x00f7, 0x6b90, 0xd01d}, + {0x00e4, 0x01c7, 0x00e4, 0x6c7c, 0xcf87}, + {0x00d2, 0x01a4, 0x00d2, 0x6d5d, 0xcef6}, + {0x00c2, 0x0183, 0x00c2, 0x6e33, 0xce69}, + {0x00b2, 0x0165, 0x00b2, 0x6f00, 0xcde0}, + {0x00a4, 0x0149, 0x00a4, 0x6fc3, 0xcd5c}, + {0x0098, 0x012f, 0x0098, 0x707d, 0xccdc}, + {0x008c, 0x0117, 0x008c, 0x712f, 0xcc60}, + {0x0081, 0x0101, 0x0081, 0x71d9, 0xcbe7}, + {0x0076, 0x00ed, 0x0076, 0x727a, 0xcb73}, + {0x006d, 0x00da, 0x006d, 0x7315, 0xcb03}, + {0x0064, 0x00c9, 0x0064, 0x73a8, 0xca97}, + {0x005c, 0x00b9, 0x005c, 0x7434, 0xca2e}, + {0x0055, 0x00aa, 0x0055, 0x74bb, 0xc9c9}, + {0x004e, 0x009c, 0x004e, 0x753b, 0xc967}, + {0x0048, 0x0090, 0x0048, 0x75b5, 0xc909}, + {0x0042, 0x0084, 0x0042, 0x7629, 0xc8af}, + {0x003d, 0x007a, 0x003d, 0x7699, 0xc857}, + {0x0038, 0x0070, 0x0038, 0x7703, 0xc803}, + {0x0033, 0x0067, 0x0033, 0x7768, 0xc7b1}, + {0x002f, 0x005f, 0x002f, 0x77c9, 0xc763}, + {0x002c, 0x0057, 0x002c, 0x7826, 0xc718}, + {0x0028, 0x0050, 0x0028, 0x787e, 0xc6cf}, + {0x0025, 0x004a, 0x0025, 0x78d2, 0xc689}, + {0x0022, 0x0044, 0x0022, 0x7923, 0xc646}, + {0x001f, 0x003e, 0x001f, 0x7970, 0xc606}, + {0x001d, 0x0039, 0x001d, 0x79b9, 0xc5c7}, + {0x001a, 0x0034, 0x001a, 0x7a00, 0xc58c}, + {0x0018, 0x0030, 0x0018, 0x7a43, 0xc552}, + {0x0016, 0x002c, 0x0016, 0x7a83, 0xc51b}, + {0x0014, 0x0029, 0x0014, 0x7ac0, 0xc4e6}, + {0x0013, 0x0025, 0x0013, 0x7afb, 0xc4b3}, + {0x0011, 0x0022, 0x0011, 0x7b32, 0xc482}, + {0x0010, 0x0020, 0x0010, 0x7b68, 0xc452}, + {0x000e, 0x001d, 0x000e, 0x7b9b, 0xc425}, + {0x000d, 0x001b, 0x000d, 0x7bcc, 0xc3fa}, + {0x000c, 0x0018, 0x000c, 0x7bfa, 0xc3d0}, + {0x000b, 0x0016, 0x000b, 0x7c27, 0xc3a8}, + {0x000a, 0x0015, 0x000a, 0x7c52, 0xc381}, + {0x0009, 0x0013, 0x0009, 0x7c7a, 0xc35c}, + {0x0009, 0x0011, 0x0009, 0x7ca1, 0xc339}, + {0x0008, 0x0010, 0x0008, 0x7cc7, 0xc317}, + {0x0007, 0x000f, 0x0007, 0x7cea, 0xc2f6}, + {0x0007, 0x000d, 0x0007, 0x7d0c, 0xc2d7}, + {0x0006, 0x000c, 0x0006, 0x7d2d, 0xc2b9}, + {0x0006, 0x000b, 0x0006, 0x7d4c, 0xc29c}, + {0x0005, 0x000a, 0x0005, 0x7d6a, 0xc280}, + {0x0005, 0x000a, 0x0005, 0x7d86, 0xc265}, + {0x0004, 0x0009, 0x0004, 0x7da1, 0xc24c}, + {0x0004, 0x0008, 0x0004, 0x7dbb, 0xc234}, + {0x0004, 0x0007, 0x0004, 0x7dd4, 0xc21c}, + {0x0003, 0x0007, 0x0003, 0x7dec, 0xc206}, + {0x0003, 0x0006, 0x0003, 0x7e03, 0xc1f0}, + {0x0003, 0x0006, 0x0003, 0x7e19, 0xc1db}, + {0x0003, 0x0005, 0x0003, 0x7e2e, 0xc1c8}, + {0x0002, 0x0005, 0x0002, 0x7e42, 0xc1b5}, + {0x0002, 0x0004, 0x0002, 0x7e55, 0xc1a2}, + {0x0002, 0x0004, 0x0002, 0x7e67, 0xc191}, + {0x0002, 0x0004, 0x0002, 0x7e79, 0xc180}, + {0x0002, 0x0003, 0x0002, 0x7e89, 0xc170}, + {0x0002, 0x0003, 0x0002, 0x7e99, 0xc161}, + {0x0001, 0x0003, 0x0001, 0x7ea9, 0xc152}, + {0x0001, 0x0003, 0x0001, 0x7eb7, 0xc144}, + {0x0001, 0x0002, 0x0001, 0x7ec5, 0xc136}, + {0x0001, 0x0002, 0x0001, 0x7ed3, 0xc129}, + {0x0001, 0x0002, 0x0001, 0x7ee0, 0xc11d}, + {0x0001, 0x0002, 0x0001, 0x7eec, 0xc111}, + {0x0001, 0x0002, 0x0001, 0x7ef8, 0xc105}, + {0x0001, 0x0002, 0x0001, 0x7f03, 0xc0fa}, + {0x0001, 0x0001, 0x0001, 0x7f0e, 0xc0f0}, + {0x0001, 0x0001, 0x0001, 0x7f18, 0xc0e6} + }; + + u16 const Util::CalcLpfFreqTable[CALC_LPF_FREQ_TABLE_SIZE] = + { + 80, 100, 128, + 160, 200, 256, + 320, 400, 500, + 640, 800, 1000, + 1280, 1600, 2000, + 2560, 3200, 4000, + 5120, 6400, 8000, + 10240, 12800, 16000 + }; + // clang-format on + + // .data + + // clang-format off + f32 const *Util::PanTableTable[PAN_CURVE_NUM] = + { + [PAN_CURVE_SQRT] = Pan2RatioTableSqrt, + [PAN_CURVE_SINCOS] = Pan2RatioTableSinCos, + [PAN_CURVE_LINEAR] = Pan2RatioTableLinear, + }; + // clang-format on +}}} // namespace nw4r::snd::detail + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +f32 Util::CalcPitchRatio(int pitch) +{ + f32 ratio; + + int octave = 0; + f32 octave_float = 1.0f; + + while (pitch < 0) + { + octave--; + pitch += OCTAVE_DIVISION * PITCH_DIVISION_RANGE; + } + + while (pitch >= OCTAVE_DIVISION * PITCH_DIVISION_RANGE) + { + octave++; + pitch -= OCTAVE_DIVISION * PITCH_DIVISION_RANGE; + } + + while (octave > 0) + { + octave_float *= 2.0f; + octave--; + } + + while (octave < 0) + { + octave_float /= 2.0f; + octave++; + } + + int note = pitch / PITCH_DIVISION_RANGE; + pitch %= PITCH_DIVISION_RANGE; + + ratio = octave_float; + + if (note != 0) + ratio *= NoteTable[note]; + + if (pitch != 0) + ratio *= PitchTable[pitch]; + + return ratio; +} + +f32 Util::CalcVolumeRatio(f32 dB) +{ + dB = ut::Clamp(dB, VOLUME_DB_MIN / 10.0f, VOLUME_DB_MAX / 10.0f); + + return Decibel2RatioTable[static_cast<int>(dB * 10.0f) - VOLUME_DB_MIN]; +} + +f32 Util::CalcPanRatio(f32 pan, PanInfo const &info) +{ + pan = (ut::Clamp(pan, -1.0f, 1.0f) + 1.0f) / 2.0f; + + f32 const *table = PanTableTable[info.curve]; + f32 ratio = table[static_cast<int>(pan * PAN_TABLE_MAX + 0.5f)]; + + if (info.centerZeroFlag) + ratio /= table[PAN_TABLE_CENTER]; + + return info.zeroClampFlag ? ut::Clamp(ratio, 0.0f, 1.0f) + : ut::Clamp(ratio, 0.0f, 2.0f); +} + +f32 Util::CalcSurroundPanRatio(f32 pan, PanInfo const &info) +{ + pan = ut::Clamp(pan, 0.0f, 2.0f) / 2.0f; + + f32 const *table = PanTableTable[info.curve]; + f32 ratio = table[static_cast<int>(pan * PAN_TABLE_MAX + 0.5f)]; + + return ut::Clamp(ratio, 0.0f, 2.0f); +} + +u16 Util::CalcLpfFreq(f32 scale) +{ + scale = ut::Clamp(scale, 0.0f, 1.0f); + + u16 freq = 0; + + if (scale < CALC_LPF_FREQ_INTERCEPT) + { + freq = CalcLpfFreqTable[0]; + } + else if (scale >= 0.9f) + { + freq = CalcLpfFreqTable[CALC_LPF_FREQ_TABLE_SIZE - 1]; + } + else + { + int idx = (scale - CALC_LPF_FREQ_INTERCEPT) / (1.0f / 3.0f / 10.0f); + + freq = CalcLpfFreqTable[idx]; + } + + return freq; +} + +void Util::GetRemoteFilterCoefs(int filter, u16 *b0, u16 *b1, u16 *b2, u16 *a1, + u16 *a2) +{ + filter = ut::Clamp(filter, COEF_TABLE_MIN, COEF_TABLE_MAX); + + *b0 = RemoteFilterCoefTable[filter][0]; + *b1 = RemoteFilterCoefTable[filter][1]; + *b2 = RemoteFilterCoefTable[filter][2]; + *a1 = RemoteFilterCoefTable[filter][3]; + *a2 = RemoteFilterCoefTable[filter][4]; +} + +u16 Util::CalcRandom() +{ + /* ogws leaves this note: + * + * randq1 :D + */ + + static unsigned long u = 0x12345678ul; + + u = u * 0x19660d + 0x3c6ef35f; + return u >> 16; +} + +void const *Util::GetDataRefAddressImpl(RefType refType, byte4_t value, + void const *baseAddress) +{ + if (refType == REFTYPE_OFFSET) + { + return ut::AddOffsetToPtr(baseAddress, Util::ReadBigEndian(value)); + } + else if (refType == REFTYPE_ADDRESS) + { + return reinterpret_cast<void const *>(Util::ReadBigEndian(value)); + } + else + { + NW4RPanicMessage_Line(758, "invalid DataRef::RefType"); + + return nullptr; + } +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_Voice.cpp b/src/nw4r/snd/snd_Voice.cpp index ef9e27ef..498cf8b3 100644 --- a/src/nw4r/snd/snd_Voice.cpp +++ b/src/nw4r/snd/snd_Voice.cpp @@ -1 +1,1383 @@ -#include "nw4r/snd/snd_Voice.h" +#include "nw4r/snd/Voice.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_Voice.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <decomp.h> +#include <macros.h> +#include <types.h> + +#include "nw4r/snd/AxManager.h" +#include "nw4r/snd/AxVoiceManager.h" +#include "nw4r/snd/AxVoice.h" +#include "nw4r/snd/adpcm.h" +#include "nw4r/snd/global.h" +#include "nw4r/snd/Util.h" +#include "nw4r/snd/VoiceManager.h" +#include "nw4r/snd/WaveFile.h" + +#include "nw4r/ut/inlines.h" +#include "nw4r/ut/Lock.h" + +#if 0 +#include <revolution/AX/AXAlloc.h> // AX_MAX_VOLUME +#else +#include <context_rvl.h> +#endif + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * local function declarations + */ + +namespace nw4r { namespace snd { namespace detail +{ + inline u16 CalcMixVolume(f32 volume) + { + if (volume <= 0.0f) + return 0; + + return ut::Min<u32>(65535, AX_MAX_VOLUME * volume); + } +}}} // namespace nw4r::snd::detail + +/******************************************************************************* + * variables + */ + +namespace nw4r { namespace snd { namespace detail +{ +#if defined(BETTER_OBJDIFF_DIFF) +# define SEND_MAX 1.0f +# define SEND_MIN 0.0f +# define BIQUAD_VALUE_MAX 1.0f +# define BIQUAD_VALUE_MIN 0.0f +# define PAN_CENTER 0.0f +# define PAN_RIGHT 1.0f +# define PAN_LEFT -1.0f +# define VOLUME_MAX 1.0f +# define VOLUME_MIN 0.0f +#else + f32 const Voice::SEND_MAX = 1.0f; + f32 const Voice::SEND_MIN = 0.0f; + f32 const Voice::BIQUAD_VALUE_MAX = 1.0f; + f32 const Voice::BIQUAD_VALUE_MIN = 0.0f; + f32 const Voice::PAN_CENTER = 0.0f; + f32 const Voice::PAN_RIGHT = 1.0f; + f32 const Voice::PAN_LEFT = -1.0f; + f32 const Voice::VOLUME_MAX = 1.0f; + f32 const Voice::VOLUME_MIN = 0.0f; +#endif +}}} // namespace nw4r::snd::detail + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +Voice::Voice() : + mCallback (nullptr), + mActiveFlag (false), + mStartFlag (false), + mStartedFlag (false), + mPauseFlag (false), + mSyncFlag (0) +{ + for (int channelIndex = 0; channelIndex < CHANNEL_MAX; channelIndex++) + { + for (int voiceOutIndex = 0; voiceOutIndex < 4; voiceOutIndex++) + mAxVoice[channelIndex][voiceOutIndex] = nullptr; + } +} + +Voice::~Voice() +{ + for (int channelIndex = 0; channelIndex < CHANNEL_MAX; channelIndex++) + { + for (int voiceOutIndex = 0; voiceOutIndex < 4; voiceOutIndex++) + { + if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) + AxVoiceManager::GetInstance().FreeAxVoice(axVoice); + } + } +} + +void Voice::InitParam(int channelCount, int voiceOutCount, Callback *callback, + void *callbackData) +{ + // specifically not the source variants + NW4RAssertHeaderClampedLRValue_Line(128, channelCount, 1, CHANNEL_MAX); + NW4RAssertHeaderClampedLRValue_Line(129, voiceOutCount, 1, 4); + + mChannelCount = channelCount; + mVoiceOutCount = voiceOutCount; + mCallback = callback; + mCallbackData = callbackData; + mSyncFlag = 0; + mPauseFlag = false; + mPausingFlag = false; + mStartedFlag = false; + mVoiceOutParamPitchDisableFlag = false; + mVolume = 1.0f; + mVeInitVolume = 0.0f; + mVeTargetVolume = 1.0f; + mLpfFreq = 1.0f; + mBiquadType = 0; + mBiquadValue = 0.0f; + mPan = 0.0f; + mSurroundPan = 0.0f; + mOutputLineFlag = 1; + mMainOutVolume = 1.0f; + mMainSend = 1.0f; + + for (int i = 0; i < AUX_BUS_NUM; i++) + mFxSend[i] = 0.0f; + + mPitch = 1.0f; + mRemoteFilter = 0; + mPanMode = PAN_MODE_DUAL; + mPanCurve = PAN_CURVE_SQRT; +} + +void Voice::StopFinished() +{ + if (mActiveFlag && mStartedFlag && IsPlayFinished()) + { + if (mCallback) + (*mCallback)(this, CALLBACK_STATUS_FINISH_WAVE, mCallbackData); + + mStartedFlag = false; + mStartFlag = false; + } +} + +void Voice::Calc() +{ + if (!mStartFlag) + return; + + if (mSyncFlag & UPDATE_SRC) + { + CalcAxSrc(false); + mSyncFlag &= ~UPDATE_SRC; + } + + if (mSyncFlag & UPDATE_VE) + { + CalcAxVe(); + mSyncFlag &= ~UPDATE_VE; + } + + if (mSyncFlag & UPDATE_MIX) + { + bool nextUpdateFlag = CalcAxMix(); + + if (!nextUpdateFlag) + mSyncFlag &= ~UPDATE_MIX; + } + + if (mSyncFlag & UPDATE_LPF) + { + CalcAxLpf(); + mSyncFlag &= ~UPDATE_LPF; + } + + if (mSyncFlag & UPDATE_BIQUAD) + { + CalcAxBiquadFilter(); + mSyncFlag &= ~UPDATE_BIQUAD; + } + + if (mSyncFlag & UPDATE_REMOTE_FILTER) + { + CalcAxRemoteFilter(); + mSyncFlag &= ~UPDATE_REMOTE_FILTER; + } +} + +void Voice::Update() +{ + ut::AutoInterruptLock lock; + + if (!mActiveFlag) + return; + + enum + { + NONE, + + RUN, + STOP, + } runFlag = NONE; + + if (mSyncFlag & UPDATE_START && mStartFlag && !mStartedFlag) + { + CalcAxSrc(true); + + runFlag = RUN; + + mStartedFlag = true; + + mSyncFlag &= ~UPDATE_START; + mSyncFlag &= ~UPDATE_SRC; + } + + if (mStartedFlag) + { + if (mSyncFlag & UPDATE_PAUSE && mStartFlag) + { + if (mPauseFlag) + { + mPausingFlag = true; + runFlag = STOP; + } + else + { + mPausingFlag = false; + runFlag = RUN; + } + + mSyncFlag &= ~UPDATE_PAUSE; + } + + SyncAxVoice(); + } + + switch (runFlag) + { + case RUN: + RunAllAxVoice(); + break; + + case STOP: + StopAllAxVoice(); + break; + } +} + +bool Voice::Acquire(int channelCount, int voiceOutCount, int priority, + Callback *callback, void *callbackData) +{ + + NW4RAssertHeaderClampedLRValue_Line(336, channelCount, 1, CHANNEL_MAX); + channelCount = ut::Clamp(channelCount, 1, CHANNEL_MAX); + + NW4RAssertHeaderClampedLRValue_Line(339, voiceOutCount, 1, 4); + voiceOutCount = ut::Clamp(voiceOutCount, 1, 4); + + ut::AutoInterruptLock lock; + + u32 axPriority = + priority == PRIORITY_MAX ? VOICE_PRIORITY_MAX : 16; + + NW4RAssert_Line(346, ! mActiveFlag); + + int requiredVoiceCount = channelCount * voiceOutCount; + AxVoice *voiceTable[CHANNEL_MAX * 4]; + + for (int i = 0; i < requiredVoiceCount; i++) + { + AxVoice *axVoice = nullptr; + + axVoice = AxVoiceManager::GetInstance().AcquireAxVoice( + axPriority, &AxVoiceCallbackFunc, this); + + if (!axVoice) + { + int restAXVPBCount = requiredVoiceCount - i; + + Voice::LinkList const &voiceList = + VoiceManager::GetInstance().GetVoiceList(); + + NW4R_RANGE_FOR(itr, voiceList) + { + if (priority < itr->GetPriority()) + break; + + restAXVPBCount -= itr->GetPhysicalVoiceCount(); + if (restAXVPBCount <= 0) + break; + } + + if (restAXVPBCount > 0) + { + for (int j = 0; j < i; j++) + AxVoiceManager::GetInstance().FreeAxVoice(voiceTable[j]); + + return false; + } + + u32 allocPriority = axPriority == VOICE_PRIORITY_MAX + ? VOICE_PRIORITY_MAX + : 17; + + axVoice = AxVoiceManager::GetInstance().AcquireAxVoice( + allocPriority, &AxVoiceCallbackFunc, this); + } + + NW4RAssertPointerNonnull_Line(399, axVoice); + + if (!axVoice) + { + for (int j = 0; j < i; j++) + AxVoiceManager::GetInstance().FreeAxVoice(voiceTable[j]); + + return false; + } + + voiceTable[i] = axVoice; + } + + int axVoiceIndex = 0; + for (int channelIndex = 0; channelIndex < channelCount; channelIndex++) + { + for (int voiceOutIndex = 0; voiceOutIndex < voiceOutCount; + voiceOutIndex++) + { + voiceTable[axVoiceIndex]->SetPriority(axPriority); + mAxVoice[channelIndex][voiceOutIndex] = voiceTable[axVoiceIndex]; + + axVoiceIndex++; + } + } + + InitParam(channelCount, voiceOutCount, callback, callbackData); + + mActiveFlag = true; + return true; +} + +void Voice::Free() +{ + ut::AutoInterruptLock lock; + + if (!mActiveFlag) + return; + + for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) + { + for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; + voiceOutIndex++) + { + if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) + { + AxVoiceManager::GetInstance().FreeAxVoice(axVoice); + mAxVoice[channelIndex][voiceOutIndex] = nullptr; + } + } + } + + mChannelCount = 0; + VoiceManager::GetInstance().FreeVoice(this); + + mActiveFlag = false; +} + +void Voice::Setup(WaveInfo const &waveParam, u32 startOffset) +{ + int sampleRate = waveParam.sampleRate; + + for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) + { + if (!mAxVoice[channelIndex][0]) + continue; + + NW4RAssertPointerNonnull_Line( + 477, waveParam.channelParam[channelIndex].dataAddr); + void *dataAddr = waveParam.channelParam[channelIndex].dataAddr; + + AdpcmParam adpcmParam; + if (waveParam.sampleFormat == SAMPLE_FORMAT_DSP_ADPCM) + { + adpcmParam = waveParam.channelParam[channelIndex].adpcmParam; + AxVoice::CalcOffsetAdpcmParam(&adpcmParam.pred_scale, + &adpcmParam.yn1, &adpcmParam.yn2, + startOffset, dataAddr, adpcmParam); + } + + for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; + voiceOutIndex++) + { + AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]; + if (!axVoice) + continue; + + axVoice->Setup(waveParam.channelParam[channelIndex].dataAddr, + waveParam.sampleFormat, sampleRate); + axVoice->SetAddr(waveParam.loopFlag, dataAddr, startOffset, + waveParam.loopStart, waveParam.loopEnd); + + if (waveParam.sampleFormat == SAMPLE_FORMAT_DSP_ADPCM) + { + axVoice->SetAdpcm(&adpcmParam); + axVoice->SetAdpcmLoop( + &waveParam.channelParam[channelIndex].adpcmLoopParam); + } + + axVoice->SetSrcType(AxVoice::SRC_4TAP_AUTO, mPitch); + axVoice->SetVoiceType(AxVoice::VOICE_TYPE_NORMAL); + } + } + + for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) + { + mVoiceOutParam[voiceOutIndex].volume = 1.0f; + mVoiceOutParam[voiceOutIndex].pitch = 1.0f; + mVoiceOutParam[voiceOutIndex].pan = 0.0f; + mVoiceOutParam[voiceOutIndex].surroundPan = 0.0f; + mVoiceOutParam[voiceOutIndex].fxSend = 0.0f; + mVoiceOutParam[voiceOutIndex].lpf = 0.0f; + } + + mPauseFlag = false; + mPausingFlag = false; + mStartFlag = false; + mStartedFlag = false; + + mSyncFlag |= UPDATE_MIX; + mSyncFlag |= UPDATE_VE; + mSyncFlag |= UPDATE_LPF; +} + +void Voice::Start() +{ + mStartFlag = true; + mPauseFlag = false; + + mSyncFlag |= UPDATE_START; +} + +void Voice::Stop() +{ + if (mStartedFlag) + { + StopAllAxVoice(); + + mStartedFlag = false; + } + + mPausingFlag = false; + mPauseFlag = false; + mStartFlag = false; +} + +void Voice::Pause(bool flag) +{ + if (mPauseFlag != flag) + { + mPauseFlag = flag; + + mSyncFlag |= UPDATE_PAUSE; + } +} + +SampleFormat Voice::GetFormat() const +{ + NW4RAssert_Line(583, IsActive()); + + if (IsActive()) + return mAxVoice[0][0]->GetFormat(); + + return SAMPLE_FORMAT_PCM_S16; +} + +void Voice::SetVolume(f32 volume) +{ + volume = ut::Clamp(volume, VOLUME_MIN, VOLUME_MAX); + + if (volume != mVolume) + { + mVolume = volume; + + mSyncFlag |= UPDATE_VE; + } +} + +void Voice::SetVeVolume(f32 targetVolume, f32 initVolume) +{ + targetVolume = ut::Clamp(targetVolume, VOLUME_MIN, VOLUME_MAX); + initVolume = ut::Clamp(initVolume, VOLUME_MIN, VOLUME_MAX); + + if (initVolume < 0.0f) + { + // NOTE: unreachable (initVolume was clamped) + + if (targetVolume == mVeTargetVolume) + return; + + mVeTargetVolume = targetVolume; + + mSyncFlag |= UPDATE_VE; + return; + } + + if (initVolume != mVeInitVolume || targetVolume != mVeTargetVolume) + { + mVeInitVolume = initVolume; + mVeTargetVolume = targetVolume; + + mSyncFlag |= UPDATE_VE; + } +} + +void Voice::SetPitch(f32 pitch) +{ + if (pitch != mPitch) + { + mPitch = pitch; + + mSyncFlag |= UPDATE_SRC; + } +} + +void Voice::SetPanMode(PanMode panMode) +{ + if (panMode != mPanMode) + { + mPanMode = panMode; + + mSyncFlag |= UPDATE_MIX; + } +} + +void Voice::SetPanCurve(PanCurve panCurve) +{ + if (panCurve != mPanCurve) + { + mPanCurve = panCurve; + + mSyncFlag |= UPDATE_MIX; + } +} + +void Voice::SetPan(f32 pan) +{ + if (pan != mPan) + { + mPan = pan; + + mSyncFlag |= UPDATE_MIX; + } +} + +void Voice::SetSurroundPan(f32 pan) +{ + if (pan != mSurroundPan) + { + mSurroundPan = pan; + + mSyncFlag |= UPDATE_MIX; + } +} + +void Voice::SetLpfFreq(f32 freq) +{ + if (freq != mLpfFreq) + { + mLpfFreq = freq; + + mSyncFlag |= UPDATE_LPF; + } +} + +void Voice::SetBiquadFilter(int type, f32 value) +{ + // specifically not the source variant + NW4RAssertHeaderClampedLRValue_Line(680, type, 0, 127); + + value = ut::Clamp(value, BIQUAD_VALUE_MIN, BIQUAD_VALUE_MAX); + + bool isUpdate = false; + + if (type != mBiquadType) + { + mBiquadType = type; + isUpdate = true; + } + + if (value != mBiquadValue) + { + mBiquadValue = value; + isUpdate = true; + } + + if (isUpdate) + mSyncFlag |= UPDATE_BIQUAD; +} + +void Voice::SetRemoteFilter(int filter) +{ + filter = ut::Clamp(filter, REMOTE_FILTER_MIN, REMOTE_FILTER_MAX); + + if (filter != mRemoteFilter) + { + mRemoteFilter = filter; + + mSyncFlag |= UPDATE_REMOTE_FILTER; + } +} + +void Voice::SetOutputLine(int lineFlag) +{ + if (lineFlag != mOutputLineFlag) + { + mOutputLineFlag = lineFlag; + + mSyncFlag |= UPDATE_MIX; + } +} + +void Voice::SetMainOutVolume(f32 volume) +{ + volume = ut::Clamp(volume, VOLUME_MIN, VOLUME_MAX); + + if (volume != mMainOutVolume) + { + mMainOutVolume = volume; + + mSyncFlag |= UPDATE_MIX; + } +} + +void Voice::SetMainSend(f32 send) +{ + send += 1.0f; + send = ut::Clamp(send, SEND_MIN, SEND_MAX); + + if (send != mMainSend) + { + mMainSend = send; + + mSyncFlag |= UPDATE_MIX; + } +} + +void Voice::SetFxSend(AuxBus bus, f32 send) +{ + // specifically not the source variant + NW4RAssertHeaderClampedLValue_Line(748, bus, 0, AUX_BUS_NUM); + + send = ut::Clamp(send, SEND_MIN, SEND_MAX); + + if (send != mFxSend[bus]) + { + mFxSend[bus] = send; + + mSyncFlag |= UPDATE_MIX; + } +} + +void Voice::SetVoiceOutParam(int voiceOutIndex, + VoiceOutParam const &voiceOutParam) +{ + // specifically not the source variant + NW4RAssertHeaderClampedLRValue_Line(820, voiceOutIndex, 0, 4); + + mVoiceOutParam[voiceOutIndex] = voiceOutParam; + + mSyncFlag |= UPDATE_SRC | UPDATE_VE | UPDATE_MIX | UPDATE_LPF; +} + +void Voice::SetPriority(int priority) +{ + // specifically not the source variant + NW4RAssertHeaderClampedLRValue_Line(828, priority, PRIORITY_MIN, + PRIORITY_MAX); + + mPriority = priority; + VoiceManager::GetInstance().ChangeVoicePriority(this); + + if (mPriority != AX_PRIORITY_MIN) + return; + + for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) + { + for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; + voiceOutIndex++) + { + if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) + axVoice->SetPriority(15); + } + } +} + +void Voice::UpdateVoicesPriority() +{ + if (mPriority == AX_PRIORITY_MIN) + return; + + for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) + { + for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; + voiceOutIndex++) + { + if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) + axVoice->SetPriority(16); + } + } +} + +// Voice::GetAxVoice ([R89JEL]:/bin/RVL/Debug/mainD.MAP:14824) +DECOMP_FORCE(NW4RAssert_String(channelIndex < CHANNEL_MAX)); + +void Voice::SetAdpcmLoop(int channelIndex, AdpcmLoopParam const *param) +{ + for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) + { + if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) + axVoice->SetAdpcmLoop(param); + } +} + +u32 Voice::GetCurrentPlayingSample() const +{ + if (IsActive()) + return mAxVoice[0][0]->GetCurrentPlayingSample(); + + return 0; +} + +void Voice::SetLoopStart(int channelIndex, void const *baseAddress, u32 samples) +{ + for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) + { + if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) + axVoice->SetLoopStart(baseAddress, samples); + } +} + +void Voice::SetLoopEnd(int channelIndex, void const *baseAddress, u32 samples) +{ + for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) + { + if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) + axVoice->SetLoopEnd(baseAddress, samples); + } +} + +void Voice::SetLoopFlag(bool loopFlag) +{ + for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) + { + for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; + voiceOutIndex++) + { + if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) + axVoice->SetLoopFlag(loopFlag); + } + } +} + +void Voice::StopAtPoint(int channelIndex, void const *baseAddress, u32 samples) +{ + for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) + { + if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) + axVoice->StopAtPoint(baseAddress, samples); + } +} + +void Voice::SetVoiceType(AxVoice::VoiceType type) +{ + for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) + { + for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; + voiceOutIndex++) + { + if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) + axVoice->SetVoiceType(type); + } + } +} + +void Voice::CalcAxSrc(bool initialUpdate) +{ + for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) + { + f32 ratio = mPitch; + + if (!mVoiceOutParamPitchDisableFlag) + ratio *= mVoiceOutParam[voiceOutIndex].pitch; + + for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) + { + if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) + axVoice->SetSrc(ratio, initialUpdate); + } + } +} + +void Voice::CalcAxVe() +{ + f32 baseVolume = 1.0f; + baseVolume *= mVolume; + baseVolume *= AxManager::GetInstance().GetOutputVolume(); + + for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) + { + f32 volume = baseVolume * mVoiceOutParam[voiceOutIndex].volume; + f32 targetVolume = volume * mVeTargetVolume; + f32 initVolume = volume * mVeInitVolume; + + for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) + { + if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) + axVoice->SetVe(targetVolume, initVolume); + } + } +} + +bool Voice::CalcAxMix() +{ + bool nextUpdateFlag = false; + + AxVoice::MixParam mix; + + /* The address is taken and the members are set, but the members aren't used + * after that + */ + AxVoice::RemoteMixParam rmtmix ATTR_UNUSED; + + for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) + { + for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; + voiceOutIndex++) + { + if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) + { + CalcMixParam(channelIndex, voiceOutIndex, &mix, &rmtmix); + + nextUpdateFlag |= axVoice->SetMix(mix); + } + } + } + + return nextUpdateFlag; +} + +void Voice::CalcAxLpf() +{ + for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) + { + u16 freq = + Util::CalcLpfFreq(mLpfFreq + mVoiceOutParam[voiceOutIndex].lpf); + + for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) + { + if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) + axVoice->SetLpf(freq); + } + } +} + +void Voice::CalcAxBiquadFilter() +{ + for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) + { + for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) + { + if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) + axVoice->SetBiquad(mBiquadType, mBiquadValue); + } + } +} + +void Voice::CalcAxRemoteFilter() +{ + for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) + { + for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) + { + if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) + axVoice->SetRemoteFilter(mRemoteFilter); + } + } +} + +void Voice::SyncAxVoice() +{ + for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) + { + for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; + voiceOutIndex++) + { + // What + if (mAxVoice[channelIndex][voiceOutIndex]) + mAxVoice[channelIndex][voiceOutIndex]->Sync(); + } + } +} + +void Voice::ResetDelta() +{ + for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) + { + for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) + { + if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) + axVoice->ResetDelta(); + } + } +} + +void Voice::AxVoiceCallbackFunc(AxVoice *dropVoice, + AxVoice::AxVoiceCallbackStatus status, + void *callbackData) +{ + Voice *voice = static_cast<Voice *>(callbackData); + NW4RAssertPointerNonnull_Line(1165, voice); + + VoiceCallbackStatus voiceStatus; + bool freeDropVoice = false; + + switch (status) + { + case AxVoice::CALLBACK_STATUS_CANCEL: + voiceStatus = CALLBACK_STATUS_CANCEL; + break; + + case AxVoice::CALLBACK_STATUS_DROP_DSP: + voiceStatus = CALLBACK_STATUS_DROP_DSP; + freeDropVoice = true; + break; + } + + for (int channelIndex = 0; channelIndex < voice->mChannelCount; + channelIndex++) + { + for (int voiceOutIndex = 0; voiceOutIndex < voice->mVoiceOutCount; + voiceOutIndex++) + { + AxVoice *axVoice = voice->mAxVoice[channelIndex][voiceOutIndex]; + if (!axVoice) + continue; + + if (axVoice == dropVoice) + { + if (!freeDropVoice) + AxVoiceManager::GetInstance().FreeAxVoice(axVoice); + } + else + { + axVoice->Stop(); + AxVoiceManager::GetInstance().FreeAxVoice(axVoice); + } + + voice->mAxVoice[channelIndex][voiceOutIndex] = nullptr; + } + } + + voice->mPauseFlag = false; + voice->mStartFlag = false; + voice->mChannelCount = 0; + + if (freeDropVoice) + voice->Free(); + + if (voice->mCallback) + (*voice->mCallback)(voice, voiceStatus, voice->mCallbackData); +} + +void Voice::TransformDpl2Pan(f32 *outPan, f32 *outSurroundPan, f32 inPan, + f32 inSurroundPan) +{ + inSurroundPan -= 1.0f; + + if (ut::Abs(inPan) <= ut::Abs(inSurroundPan)) + { + if (inSurroundPan <= 0.0f) + { + *outPan = inPan; + *outSurroundPan = -0.12f + 0.88f * inSurroundPan; + } + else + { + *outPan = 0.5f * inPan; + *outSurroundPan = -0.12f + 1.12f * inSurroundPan; + } + } + else if (inPan >= 0.0f) + { + /* NOTE: do not constant-fold 1.0f - 0.85f or 1.0f - 0.65f; they differ + * by 1 digit + */ + if (inSurroundPan <= 0.0f) + { + *outPan = (0.85f + (1.0f - 0.85f) * (-inSurroundPan / inPan)) + * ut::Abs(inPan); + *outSurroundPan = -0.12f + (2.0f * inSurroundPan + 0.88f * inPan); + } + else + { + *outPan = (0.85f + (1.0f - 0.65f) * (-inSurroundPan / inPan)) + * ut::Abs(inPan); + *outSurroundPan = -0.12f + 1.12f * inPan; + } + } + else + { + if (inSurroundPan <= 0.0f) + { + *outPan = ((1.0f - 0.85f) * (-inSurroundPan / inPan) - 0.85f) + * ut::Abs(inPan); + *outSurroundPan = -0.12f + (2.0f * inSurroundPan - 1.12f * inPan); + } + else + { + *outPan = ((1.0f - 0.65f) * (-inSurroundPan / inPan) - 0.85f) + * ut::Abs(inPan); + *outSurroundPan = -0.12f + 1.12f * -inPan; + } + } + + *outSurroundPan += 1.0f; +} + +void Voice::CalcMixParam(int channelIndex, int voiceOutIndex, + AxVoice::MixParam *mix, + AxVoice::RemoteMixParam *rmtmix) +{ + NW4RAssertPointerNonnull_Line(1284, mix); + + f32 mainVolume = 0.0f; + f32 mainSend = 0.0f; + + f32 fxSendA = 0.0f; + f32 fxSendB = 0.0f; + f32 fxSendC = 0.0f; + + if (mOutputLineFlag & 1) // OUTPUT_LINE_MAIN + { + mainVolume = mMainOutVolume; + mainSend = mMainSend; + + fxSendA = + ut::Clamp(mFxSend[AUX_A] + mVoiceOutParam[voiceOutIndex].fxSend, + SEND_MIN, SEND_MAX); + fxSendB = mFxSend[AUX_B]; + fxSendC = mFxSend[AUX_C]; + } + + f32 main = mainVolume * mainSend; + f32 fx_a = mainVolume * fxSendA; + f32 fx_b = mainVolume * fxSendB; + f32 fx_c = mainVolume * fxSendC; + + f32 left, right, surround, lrMixed; + f32 front, rear; + + Util::PanInfo panInfo; + panInfo.curve = Util::PAN_CURVE_SQRT; + panInfo.centerZeroFlag = false; + panInfo.zeroClampFlag = false; + + switch (mPanCurve) + { + case PAN_CURVE_SQRT: + panInfo.curve = Util::PAN_CURVE_SQRT; + break; + + case PAN_CURVE_SQRT_0DB: + panInfo.curve = Util::PAN_CURVE_SQRT; + panInfo.centerZeroFlag = true; + break; + + case PAN_CURVE_SQRT_0DB_CLAMP: + panInfo.curve = Util::PAN_CURVE_SQRT; + panInfo.centerZeroFlag = true; + panInfo.zeroClampFlag = true; + break; + + case PAN_CURVE_SINCOS: + panInfo.curve = Util::PAN_CURVE_SINCOS; + break; + + case PAN_CURVE_SINCOS_0DB: + panInfo.curve = Util::PAN_CURVE_SINCOS; + panInfo.centerZeroFlag = true; + break; + + case PAN_CURVE_SINCOS_0DB_CLAMP: + panInfo.curve = Util::PAN_CURVE_SINCOS; + panInfo.centerZeroFlag = true; + panInfo.zeroClampFlag = true; + break; + + case PAN_CURVE_LINEAR: + panInfo.curve = Util::PAN_CURVE_LINEAR; + break; + + case PAN_CURVE_LINEAR_0DB: + panInfo.curve = Util::PAN_CURVE_LINEAR; + panInfo.centerZeroFlag = true; + break; + + case PAN_CURVE_LINEAR_0DB_CLAMP: + panInfo.curve = Util::PAN_CURVE_LINEAR; + panInfo.centerZeroFlag = true; + panInfo.zeroClampFlag = true; + break; + + default: + panInfo.curve = Util::PAN_CURVE_SQRT; + break; + } + + if (mChannelCount > 1 && mPanMode == PAN_MODE_BALANCE) + { + f32 pan = mPan + mVoiceOutParam[voiceOutIndex].pan; + f32 surroundPan = + mSurroundPan + mVoiceOutParam[voiceOutIndex].surroundPan; + + if (channelIndex == 0) + { + left = Util::CalcPanRatio(pan, panInfo); + right = 0.0f; + } + else if (channelIndex == 1) + { + left = 0.0f; + right = Util::CalcPanRatio(-pan, panInfo); + } + + /* ERRATUM: left and right are used uninitialized if channelIndex is + * neither 0 nor 1 + */ + + front = Util::CalcSurroundPanRatio(surroundPan, panInfo); + rear = Util::CalcSurroundPanRatio(2.0f - surroundPan, panInfo); + } + else + { + f32 voicePan = PAN_CENTER; + f32 pan, surroundPan; + + if (mChannelCount == 2) + { + if (channelIndex == 0) + voicePan = PAN_LEFT; + if (channelIndex == 1) + voicePan = PAN_RIGHT; + } + + switch (AxManager::GetInstance().GetOutputMode()) + { + case OUTPUT_MODE_DPL2: + TransformDpl2Pan( + &pan, &surroundPan, + mPan + voicePan + mVoiceOutParam[voiceOutIndex].pan, + mSurroundPan + mVoiceOutParam[voiceOutIndex].surroundPan); + break; + + case OUTPUT_MODE_STEREO: + case OUTPUT_MODE_SURROUND: + case OUTPUT_MODE_MONO: + default: + pan = mPan + voicePan + mVoiceOutParam[voiceOutIndex].pan; + surroundPan = + mSurroundPan + mVoiceOutParam[voiceOutIndex].surroundPan; + break; + } + + left = Util::CalcPanRatio(pan, panInfo); + right = Util::CalcPanRatio(-pan, panInfo); + front = Util::CalcSurroundPanRatio(surroundPan, panInfo); + rear = Util::CalcSurroundPanRatio(2.0f - surroundPan, panInfo); + } + + surround = Util::CalcVolumeRatio(-3.0f); + lrMixed = 0.5f * (left + right); + + f32 m_l; + f32 m_r; + f32 m_s; + f32 a_l; + f32 a_r; + f32 a_s; + f32 b_l; + f32 b_r; + f32 b_s; + f32 c_l; + f32 c_r; + f32 c_s; + + f32 &m_sl = m_s; + f32 &m_sr = c_l; + + f32 &a_sl = a_s; + f32 &a_sr = c_r; + + f32 &b_sl = b_s; + f32 &b_sr = c_s; + + switch (AxManager::GetInstance().GetOutputMode()) + { + case OUTPUT_MODE_STEREO: + m_l = main * left; + m_r = main * right; + m_s = 0.0f; + + a_l = fx_a * left; + a_r = fx_a * right; + a_s = 0.0f; + + b_l = fx_b * left; + b_r = fx_b * right; + b_s = 0.0f; + + c_l = fx_c * left; + c_r = fx_c * right; + c_s = 0.0f; + + break; + + case OUTPUT_MODE_MONO: + m_l = main * lrMixed; + m_r = main * lrMixed; + m_s = 0.0f; + + a_l = fx_a * lrMixed; + a_r = fx_a * lrMixed; + a_s = 0.0f; + + b_l = fx_b * lrMixed; + b_r = fx_b * lrMixed; + b_s = 0.0f; + + c_l = fx_c * lrMixed; + c_r = fx_c * lrMixed; + c_s = 0.0f; + + break; + + case OUTPUT_MODE_SURROUND: + { + f32 fl = left * front; + f32 fr = right * front; + f32 rs = surround * rear; + + m_l = main * fl; + m_r = main * fr; + m_s = main * rs; + + a_l = fx_a * fl; + a_r = fx_a * fr; + a_s = fx_a * rs; + + b_l = fx_b * fl; + b_r = fx_b * fr; + b_s = fx_b * rs; + + c_l = fx_c * fl; + c_r = fx_c * fr; + c_s = fx_c * rs; + } + break; + + case OUTPUT_MODE_DPL2: + { + f32 fl = left * front; + f32 fr = right * front; + f32 rl = left * rear; + f32 rr = right * rear; + + m_l = main * fl; + m_r = main * fr; + m_sl = main * rl; + m_sr = main * rr; + + a_l = fx_a * fl; + a_r = fx_a * fr; + a_sl = fx_a * rl; + a_sr = fx_a * rr; + + b_l = fx_b * fl; + b_r = fx_b * fr; + b_sl = fx_b * rl; + b_sr = fx_b * rr; + } + break; + } + + mix->vL = CalcMixVolume(m_l); + mix->vR = CalcMixVolume(m_r); + mix->vS = CalcMixVolume(m_s); + mix->vAuxAL = CalcMixVolume(a_l); + mix->vAuxAR = CalcMixVolume(a_r); + mix->vAuxAS = CalcMixVolume(a_s); + mix->vAuxBL = CalcMixVolume(b_l); + mix->vAuxBR = CalcMixVolume(b_r); + mix->vAuxBS = CalcMixVolume(b_s); + mix->vAuxCL = CalcMixVolume(c_l); + mix->vAuxCR = CalcMixVolume(c_r); + mix->vAuxCS = CalcMixVolume(c_s); + + rmtmix->vMain0 = 0; + rmtmix->vAux0 = 0; + rmtmix->vMain1 = 0; + rmtmix->vAux1 = 0; + rmtmix->vMain2 = 0; + rmtmix->vAux2 = 0; + rmtmix->vMain3 = 0; + rmtmix->vAux3 = 0; +} + +void Voice::RunAllAxVoice() +{ + for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) + { + for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; + voiceOutIndex++) + { + if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) + axVoice->Run(); + } + } +} + +void Voice::StopAllAxVoice() +{ + for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) + { + for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; + voiceOutIndex++) + { + if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) + axVoice->Stop(); + } + } +} + +void Voice::InvalidateWaveData(void const *start, void const *end) +{ + bool disposeFlag = false; + + for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) + { + AxVoice *axVoice = mAxVoice[channelIndex][0]; + + if (axVoice && axVoice->IsDataAddressCoverd(start, end)) + { + disposeFlag = true; + break; + } + } + + if (disposeFlag) + { + Stop(); + + if (mCallback) + (*mCallback)(this, CALLBACK_STATUS_CANCEL, mCallbackData); + } +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_VoiceManager.cpp b/src/nw4r/snd/snd_VoiceManager.cpp index df1bd462..720c5c03 100644 --- a/src/nw4r/snd/snd_VoiceManager.cpp +++ b/src/nw4r/snd/snd_VoiceManager.cpp @@ -1 +1,268 @@ -#include "nw4r/snd/snd_VoiceManager.h" +#include "nw4r/snd/VoiceManager.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_VoiceManager.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <new> + +#include <macros.h> // NW4R_RANGE_FOR_NO_AUTO_INC +#include <types.h> + +#include "nw4r/snd/DisposeCallbackManager.h" +#include "nw4r/snd/Voice.h" + +#include "nw4r/ut/Lock.h" // ut::AutoInterruptLock + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +VoiceManager &VoiceManager::GetInstance() +{ + static VoiceManager instance; + + return instance; +} + +VoiceManager::VoiceManager() : + mInitialized(false) +{ +} + +u32 VoiceManager::GetRequiredMemSize(int voiceCount) +{ + return sizeof(Voice) * voiceCount; +} + +void VoiceManager::Setup(void *mem, u32 memSize) +{ + if (mInitialized) + return; + + u32 voiceCount = memSize / sizeof(Voice); + byte_t *ptr = static_cast<byte_t *>(mem); + + for (int i = 0; i < voiceCount; i++) + { + mFreeVoiceList.PushBack(new (ptr) Voice); + + ptr += sizeof(Voice); + } + + NW4RAssert_Line(64, ptr <= reinterpret_cast<u8*>( mem ) + memSize); + + mInitialized = true; +} + +void VoiceManager::Shutdown() +{ + if (!mInitialized) + return; + + StopAllVoices(); + + while (!mFreeVoiceList.IsEmpty()) + { + Voice &voice = mFreeVoiceList.GetFront(); + mFreeVoiceList.PopFront(); + + voice.~Voice(); + } + + mInitialized = false; +} + +void VoiceManager::StopAllVoices() +{ + ut::AutoInterruptLock lock; + + while (!mPrioVoiceList.IsEmpty()) + { + Voice &voice = mPrioVoiceList.GetFront(); + + voice.Stop(); + + if (voice.mCallback) + { + (*voice.mCallback)(&voice, Voice::CALLBACK_STATUS_CANCEL, + voice.mCallbackData); + } + + voice.Free(); + } +} + +Voice *VoiceManager::AllocVoice(int voiceChannelCount, int voiceOutCount, + int priority, Voice::Callback *callback, + void *callbackData) +{ + ut::AutoInterruptLock lock; + + if (mFreeVoiceList.IsEmpty() && !DropLowestPriorityVoice(priority)) + return nullptr; + + Voice &voice = mFreeVoiceList.GetFront(); + if (!voice.Acquire(voiceChannelCount, voiceOutCount, priority, callback, + callbackData)) + { + NW4RWarningMessage_Line(128, "Voice Acquisition failed!\n"); + return nullptr; + } + + voice.mPriority = priority & Voice::PRIORITY_MAX; + AppendVoiceList(&voice); + UpdateEachVoicePriority(mPrioVoiceList.GetIteratorFromPointer(&voice), + mPrioVoiceList.GetEndIter()); + DisposeCallbackManager::GetInstance().RegisterDisposeCallback(&voice); + + return &voice; +} + +void VoiceManager::FreeVoice(Voice *voice) +{ + NW4RAssertPointerNonnull_Line(148, voice); + + ut::AutoInterruptLock lock; + + DisposeCallbackManager::GetInstance().UnregisterDisposeCallback(voice); + RemoveVoiceList(voice); +} + +void VoiceManager::UpdateAllVoices() +{ + NW4R_RANGE_FOR_NO_AUTO_INC(itr, mPrioVoiceList) + { + decltype(itr) curItr = itr++; + + curItr->StopFinished(); + } + + NW4R_RANGE_FOR_NO_AUTO_INC(itr, mPrioVoiceList) + { + decltype(itr) curItr = itr++; + + curItr->Calc(); + } + + ut::AutoInterruptLock lock; + + NW4R_RANGE_FOR_NO_AUTO_INC(itr, mPrioVoiceList) + { + decltype(itr) curItr = itr++; + + curItr->Update(); + } +} + +void VoiceManager::NotifyVoiceUpdate() +{ + ut::AutoInterruptLock lock; + + NW4R_RANGE_FOR_NO_AUTO_INC(itr, mPrioVoiceList) + { + decltype(itr) curItr = itr++; + + curItr->ResetDelta(); + } +} + +void VoiceManager::AppendVoiceList(Voice *voice) +{ + ut::AutoInterruptLock lock; + + mFreeVoiceList.Erase(voice); + + Voice::LinkList::ReverseIterator it = mPrioVoiceList.GetEndReverseIter(); + for (; it != mPrioVoiceList.GetBeginReverseIter(); ++it) + { + if (it->GetPriority() <= voice->GetPriority()) + break; + } + + mPrioVoiceList.Insert(it.GetBase(), voice); +} + +void VoiceManager::RemoveVoiceList(Voice *voice) +{ + ut::AutoInterruptLock lock; + + mPrioVoiceList.Erase(voice); + mFreeVoiceList.PushBack(voice); +} + +void VoiceManager::ChangeVoicePriority(Voice *voice) +{ + ut::AutoInterruptLock lock; + + RemoveVoiceList(voice); + AppendVoiceList(voice); + + UpdateEachVoicePriority(mPrioVoiceList.GetIteratorFromPointer(voice), + mPrioVoiceList.GetEndIter()); +} + +void VoiceManager::UpdateEachVoicePriority( + Voice::LinkList::Iterator const &beginItr, + Voice::LinkList::Iterator const &endItr) +{ + for (Voice::LinkList::Iterator it = beginItr; it != endItr; ++it) + { + if (it->GetPriority() <= 1) + return; + + if (it->GetPriority() != Voice::PRIORITY_MAX) + it->UpdateVoicesPriority(); + } +} + +void VoiceManager::UpdateAllVoicesSync(byte4_t syncFlag) +{ + ut::AutoInterruptLock lock; + + NW4R_RANGE_FOR_NO_AUTO_INC(itr, mPrioVoiceList) + { + decltype(itr) curItr = itr++; + + if (curItr->mActiveFlag) + curItr->mSyncFlag |= syncFlag; + } +} + +int VoiceManager::DropLowestPriorityVoice(int priority) +{ + int dropCount = 0; + + if (mFreeVoiceList.IsEmpty()) + { + Voice &dropVoice = mPrioVoiceList.GetFront(); + + if (dropVoice.GetPriority() > priority) + return 0; + + dropCount = dropVoice.GetPhysicalVoiceCount(); + + dropVoice.Stop(); + dropVoice.Free(); + + if (dropVoice.mCallback) + { + (*dropVoice.mCallback)(&dropVoice, + Voice::CALLBACK_STATUS_DROP_VOICE, + dropVoice.mCallbackData); + } + } + + return dropCount; +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_WaveArchive.cpp b/src/nw4r/snd/snd_WaveArchive.cpp index 6f55e5f7..9d351503 100644 --- a/src/nw4r/snd/snd_WaveArchive.cpp +++ b/src/nw4r/snd/snd_WaveArchive.cpp @@ -1 +1,102 @@ -#include "nw4r/snd/snd_WaveArchive.h" +#include "nw4r/snd/WaveArchive.h" + +/******************************************************************************* + * headers + */ + +#include <types.h> // nullptr + +#include "nw4r/snd/Util.h" +#include "nw4r/snd/WaveFile.h" + +#include "nw4r/ut/binaryFileFormat.h" // ut::BinaryFileHeader +#include "nw4r/ut/inlines.h" // ut::AddOffsetToPtr + +#include <nw4r/NW4RAssert.hpp> + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +WaveArchiveReader::WaveArchiveReader(void const *waveArc) : + mTableBlock (nullptr), + mDataBlock (nullptr) +{ + NW4RAssertPointerNonnull_Line(44, waveArc); + + if (!VerifyFileHeader(waveArc)) + return; + + WaveArchive::FileHeader const *fileHeader = + static_cast<WaveArchive::FileHeader const *>(waveArc); + + WaveArchive::TableBlock const *tableBlock = + static_cast<WaveArchive::TableBlock const *>( + ut::AddOffsetToPtr(fileHeader, fileHeader->tableChunkOffset)); + + NW4RAssert_Line(53, tableBlock->blockHeader.kind + == WaveArchive::SIGNATURE_TABLE_BLOCK); + + WaveArchive::DataBlock const *dataBlock = + static_cast<WaveArchive::DataBlock const *>( + ut::AddOffsetToPtr(fileHeader, fileHeader->dataChunkOffset)); + + NW4RAssert_Line(58, dataBlock->blockHeader.kind + == WaveArchive::SIGNATURE_DATA_BLOCK); + + mTableBlock = tableBlock; + mDataBlock = dataBlock; +} + +WaveFile::FileHeader const *WaveArchiveReader::GetWaveFile(int index) const +{ + if (!mTableBlock) + return nullptr; + + if (!mDataBlock) + return nullptr; + + if (index < 0) + return nullptr; + + if (index >= mTableBlock->fileTable.count) + return nullptr; + + Util::DataRef<WaveFile::FileHeader> waveFileRef = + mTableBlock->fileTable.item[index].waveFileRef; + + WaveFile::FileHeader const *fileHeader = + Util::GetDataRefAddress0(waveFileRef, mDataBlock); + + return fileHeader; +} + +bool WaveArchiveReader::VerifyFileHeader(void const *waveArc) +{ + ut::BinaryFileHeader const *fileHeader = + static_cast<ut::BinaryFileHeader const *>(waveArc); + + NW4RAssertMessage_Line( + 92, fileHeader->signature == WaveArchive::SIGNATURE_FILE, + "invalid file signature. wave archive is not available."); + + if (fileHeader->signature != WaveArchive::SIGNATURE_FILE) + return false; + + // specifically not the source variant + NW4RAssertHeaderClampedLRValue_Line(96, fileHeader->version, + SUPPORTED_FILE_VERSION_MIN, + SUPPORTED_FILE_VERSION_MAX); + + if (fileHeader->version < SUPPORTED_FILE_VERSION_MIN) + return false; + + if (fileHeader->version > SUPPORTED_FILE_VERSION_MAX) + return false; + + return true; +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_WaveFile.cpp b/src/nw4r/snd/snd_WaveFile.cpp index 888d419e..0501d052 100644 --- a/src/nw4r/snd/snd_WaveFile.cpp +++ b/src/nw4r/snd/snd_WaveFile.cpp @@ -1 +1,163 @@ -#include "nw4r/snd/snd_WaveFile.h" +#include "nw4r/snd/WaveFile.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_WaveFile.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <types.h> + +#include "nw4r/snd/AxVoice.h" +#include "nw4r/snd/Channel.h" +#include "nw4r/snd/global.h" + +#include "nw4r/ut/inlines.h" // ut::AddOffsetToPtr +#include "nw4r/ut/binaryFileFormat.h" + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +WaveFileReader::WaveFileReader(WaveFile::FileHeader const *waveFileHeader) : + mWaveInfo (nullptr) +{ + NW4RAssert_Line(42, waveFileHeader->fileHeader.signature + == WaveFile::SIGNATURE_FILE); + + WaveFile::InfoBlock const *infoBlock = + static_cast<WaveFile::InfoBlock const *>(ut::AddOffsetToPtr( + waveFileHeader, waveFileHeader->infoChunkOffset)); + + if (!infoBlock) + return; + + NW4RAssert_Line(48, infoBlock->blockHeader.kind + == WaveFile::SIGNATURE_INFO_BLOCK); + + mWaveInfo = &infoBlock->waveInfo; +} + +WaveFileReader::WaveFileReader(WaveFile::WaveInfo const *waveInfo) : + mWaveInfo (waveInfo) +{ +} + +bool WaveFileReader::ReadWaveInfo(WaveInfo *waveData, + void const *waveDataOffsetOrigin) const +{ + NW4RAssertPointerNonnull_Line(79, mWaveInfo); + + SampleFormat sampleFormat = + GetSampleFormatFromWaveFileFormat(mWaveInfo->format); + + waveData->sampleFormat = sampleFormat; + waveData->numChannels = mWaveInfo->numChannels; + waveData->sampleRate = + (mWaveInfo->sampleRate24 << 16) + mWaveInfo->sampleRate; + waveData->loopFlag = mWaveInfo->loopFlag; + waveData->loopStart = AxVoice::GetSampleByDspAddress( + nullptr, mWaveInfo->loopStart, sampleFormat); + waveData->loopEnd = AxVoice::GetSampleByDspAddress( + nullptr, mWaveInfo->loopEnd, sampleFormat) + 1; + + u32 const *channelInfoOffset = static_cast<u32 const *>( + ut::AddOffsetToPtr(mWaveInfo, mWaveInfo->channelInfoTableOffset)); + + for (int channelIndex = 0; channelIndex < mWaveInfo->numChannels; + channelIndex++) + { + if (channelIndex >= Channel::CHANNEL_MAX) + continue; + + ChannelParam &channelParam = waveData->channelParam[channelIndex]; + + WaveFile::WaveChannelInfo const *waveChannelInfo = + static_cast<WaveFile::WaveChannelInfo const *>( + ut::AddOffsetToPtr(mWaveInfo, channelInfoOffset[channelIndex])); + + if (waveChannelInfo->adpcmOffset) + { + WaveFile::AdpcmParamSet const *adpcmParamSet = + static_cast<WaveFile::AdpcmParamSet const *>(ut::AddOffsetToPtr( + mWaveInfo, waveChannelInfo->adpcmOffset)); + + channelParam.adpcmParam = adpcmParamSet->adpcmParam; + channelParam.adpcmLoopParam = adpcmParamSet->adpcmLoopParam; + } + + channelParam.dataAddr = const_cast<void *>( + GetWaveDataAddress(waveChannelInfo, waveDataOffsetOrigin)); + } + + return true; +} + +void const *WaveFileReader::GetWaveDataAddress( + WaveFile::WaveChannelInfo const *waveChannelInfo, + void const *waveDataOffsetOrigin) const +{ + NW4RAssertPointerNonnull_Line(128, mWaveInfo); + NW4RAssertPointerNonnull_Line(129, waveChannelInfo); + + void const *waveDataAddress = nullptr; + bool offsetIsDataBlock = waveDataOffsetOrigin == nullptr; + + if (!waveDataOffsetOrigin) + waveDataOffsetOrigin = mWaveInfo; + + // TODO: InstInfo::WaveDataLocation::WaveDataLocationType? + switch (mWaveInfo->dataLocationType) + { + case 0: + waveDataAddress = + ut::AddOffsetToPtr(waveDataOffsetOrigin, mWaveInfo->dataLocation); + + // TODO: sizeof(InstInfo::WaveDataLocation)? + if (offsetIsDataBlock) + waveDataAddress = ut::AddOffsetToPtr(waveDataAddress, 8); + + break; + + case 1: + waveDataAddress = + reinterpret_cast<void const *>(mWaveInfo->dataLocation); + break; + + default: + return nullptr; + } + + waveDataAddress = ut::AddOffsetToPtr( + waveDataAddress, waveChannelInfo->channelDataOffset); + + return waveDataAddress; +} + +SampleFormat WaveFileReader::GetSampleFormatFromWaveFileFormat(u8 format) +{ + switch (format) + { + case 2: + return SAMPLE_FORMAT_DSP_ADPCM; + + case 1: + return SAMPLE_FORMAT_PCM_S16; + + case 0: + return SAMPLE_FORMAT_PCM_S8; + + default: + NW4RPanicMessage_Line(167, "Unknown wave data format %d", format); + return SAMPLE_FORMAT_DSP_ADPCM; + } +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_WaveSound.cpp b/src/nw4r/snd/snd_WaveSound.cpp index 6bfc8d64..5ac7eb7f 100644 --- a/src/nw4r/snd/snd_WaveSound.cpp +++ b/src/nw4r/snd/snd_WaveSound.cpp @@ -1 +1,105 @@ -#include "nw4r/snd/snd_WaveSound.h" +#include "nw4r/snd/WaveSound.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_WaveSound.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <types.h> + +#include "nw4r/snd/BasicSound.h" +#include "nw4r/snd/SoundInstanceManager.h" +#include "nw4r/snd/WaveSoundHandle.h" +#include "nw4r/snd/WsdPlayer.h" + +#include "nw4r/ut/RuntimeTypeInfo.h" + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * variables + */ + +namespace nw4r { namespace snd { namespace detail +{ + // .sbss + ut::detail::RuntimeTypeInfo const WaveSound::typeInfo( + &BasicSound::typeInfo); +}}} // namespace nw4r::snd::detail + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +WaveSound::WaveSound(SoundInstanceManager<WaveSound> *manager, int priority, + int ambientPriority) : + BasicSound (priority, ambientPriority), + mTempSpecialHandle (nullptr), + mManager (manager), + mPreparedFlag (false) +{ +} + +bool WaveSound::Prepare(void const *waveSoundBase, s32 waveSoundOffset, + WsdPlayer::StartOffsetType startOffsetType, s32 offset, + WsdPlayer::WsdCallback const *callback, + register_t callbackData) +{ + NW4RAssertPointerNonnull_Line(74, waveSoundBase); + NW4RAssertPointerNonnull_Line(75, callback); + + InitParam(); + + bool result = + mWsdPlayer.Prepare(waveSoundBase, waveSoundOffset, startOffsetType, + offset, GetVoiceOutCount(), callback, callbackData); + if (!result) + return false; + + mPreparedFlag = true; + return true; +} + +void WaveSound::Shutdown() +{ + BasicSound::Shutdown(); + + mManager->Free(this); +} + +void WaveSound::SetChannelPriority(int priority) +{ + // specifically not the source variant + NW4RAssertHeaderClampedLRValue_Line(124, priority, BasicSound::PRIORITY_MIN, + BasicSound::PRIORITY_MAX); + + mWsdPlayer.SetChannelPriority(priority); +} + +void WaveSound::SetReleasePriorityFix(bool flag) +{ + mWsdPlayer.SetReleasePriorityFix(flag); +} + +void WaveSound::OnUpdatePlayerPriority() +{ + mManager->UpdatePriority(this, CalcCurrentPlayerPriority()); +} + +bool WaveSound::IsAttachedTempSpecialHandle() +{ + return mTempSpecialHandle != nullptr; +} + +void WaveSound::DetachTempSpecialHandle() +{ + mTempSpecialHandle->DetachSound(); +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_WaveSoundHandle.cpp b/src/nw4r/snd/snd_WaveSoundHandle.cpp index b4080e69..cb032182 100644 --- a/src/nw4r/snd/snd_WaveSoundHandle.cpp +++ b/src/nw4r/snd/snd_WaveSoundHandle.cpp @@ -1 +1,64 @@ -#include "nw4r/snd/snd_WaveSoundHandle.h" +#include "nw4r/snd/WaveSoundHandle.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_WaveSoundHandle.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <types.h> // nullptr + +#include "nw4r/snd/BasicSound.h" +#include "nw4r/snd/SoundHandle.h" +#include "nw4r/snd/WaveSound.h" + +#include "nw4r/ut/RuntimeTypeInfo.h" // ut::DynamicCast + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { + +WaveSoundHandle::WaveSoundHandle(SoundHandle *handle) : + mSound (nullptr) +{ + if (!handle) + return; + + detail::BasicSound *basicSound = handle->detail_GetAttachedSound(); + if (!basicSound) + return; + + if (detail::WaveSound *sound = + ut::DynamicCast<detail::WaveSound *>(basicSound)) + { + NW4RAssertPointerNonnull_Line(50, sound); // ? + + mSound = sound; + + if (mSound->IsAttachedTempSpecialHandle()) + mSound->DetachTempSpecialHandle(); + + mSound->mTempSpecialHandle = this; + } +} + +void WaveSoundHandle::DetachSound() +{ + if (IsAttachedSound()) + { + if (mSound->mTempSpecialHandle == this) + mSound->mTempSpecialHandle = nullptr; + } + + if (mSound) + mSound = nullptr; +} + +}} // namespace nw4r::snd diff --git a/src/nw4r/snd/snd_WsdFile.cpp b/src/nw4r/snd/snd_WsdFile.cpp index 45bbc662..b8f22d37 100644 --- a/src/nw4r/snd/snd_WsdFile.cpp +++ b/src/nw4r/snd/snd_WsdFile.cpp @@ -1 +1,215 @@ -// #include "nw4r/snd/snd_WsdFile.h" +#include "nw4r/snd/WsdFile.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_WsdFile.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <macros.h> // NW4R_FILE_VERSION +#include <types.h> // nullptr + +#include "nw4r/snd/Util.h" // Util::GetDataRefAddress0 +#include "nw4r/snd/WaveFile.h" +#include "nw4r/snd/WaveArchive.h" // WaveArchiveReader + +#include "nw4r/ut/binaryFileFormat.h" // ut::BinaryFileHeader +#include "nw4r/ut/inlines.h" // ut::AddOffsetToPtr + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +bool WsdFileReader::IsValidFileHeader(void const *wsdData) +{ + ut::BinaryFileHeader const *fileHeader = + static_cast<ut::BinaryFileHeader const *>(wsdData); + + NW4RAssertMessage_Line( + 59, fileHeader->signature == WsdFile::SIGNATURE_FILE, + "invalid file signature. wsd data is not available."); + + if (fileHeader->signature != WsdFile::SIGNATURE_FILE) + return false; + + NW4RAssertMessage_Line(67, fileHeader->version >= NW4R_FILE_VERSION(1, 0), + "wsd file is not supported version.\n" + " please reconvert file using new version tools.\n"); + + if (fileHeader->version < NW4R_FILE_VERSION(1, 0)) + return false; + + NW4RAssertMessage_Line(73, fileHeader->version <= SUPPORTED_FILE_VERSION, + "wsd file is not supported version.\n" + " please reconvert file using new version tools.\n"); + + if (fileHeader->version > SUPPORTED_FILE_VERSION) + return false; + + return true; +} + +WsdFileReader::WsdFileReader(void const *wsdData) : + mHeader (nullptr), + mDataBlock (nullptr), + mWaveBlock (nullptr) +{ + NW4RAssertPointerNonnull_Line(93, wsdData); + + if (!IsValidFileHeader(wsdData)) + return; + + mHeader = static_cast<WsdFile::Header const *>(wsdData); + + if (mHeader->dataBlockOffset) + { + mDataBlock = static_cast<WsdFile::DataBlock const *>( + ut::AddOffsetToPtr(mHeader, mHeader->dataBlockOffset)); + + NW4RAssert_Line(105, mDataBlock->blockHeader.kind + == WsdFile::SIGNATURE_DATA_BLOCK); + } + + if (mHeader->waveBlockOffset) + { + mWaveBlock = static_cast<WsdFile::WaveBlock const *>( + ut::AddOffsetToPtr(mHeader, mHeader->waveBlockOffset)); + + NW4RAssert_Line(113, mWaveBlock->blockHeader.kind + == WsdFile::SIGNATURE_WAVE_BLOCK); + } +} + +bool WsdFileReader::ReadWaveSoundInfo(WaveSoundInfo *info, int index) const +{ + WsdFile::Wsd const *wsd = Util::GetDataRefAddress0( + mDataBlock->refWsd[index], &mDataBlock->wsdCount); + + WsdFile::WsdInfo const *src = + Util::GetDataRefAddress0(wsd->refWsdInfo, &mDataBlock->wsdCount); + + if (mHeader->fileHeader.version >= NW4R_FILE_VERSION(1, 2)) + { + info->pitch = src->pitch; + info->pan = src->pan; + info->surroundPan = src->surroundPan; + + info->fxSendA = src->fxSendA; + info->fxSendB = src->fxSendB; + info->fxSendC = src->fxSendC; + info->mainSend = src->mainSend; + } + else if (mHeader->fileHeader.version >= NW4R_FILE_VERSION(1, 1)) + { + info->pitch = src->pitch; + info->pan = src->pan; + info->surroundPan = src->surroundPan; + + info->fxSendA = 0; + info->fxSendB = 0; + info->fxSendC = 0; + info->mainSend = 127; + } + else + { + info->pitch = 1.0f; + info->pan = 64; + info->surroundPan = 0; + + info->fxSendA = 0; + info->fxSendB = 0; + info->fxSendC = 0; + info->mainSend = 127; + } + + return true; +} + +bool WsdFileReader::ReadWaveSoundNoteInfo(WaveSoundNoteInfo *noteInfo, + int index, int noteIndex) const +{ + WsdFile::Wsd const *wsd = Util::GetDataRefAddress0( + mDataBlock->refWsd[index], &mDataBlock->wsdCount); + + WsdFile::NoteInfoTable const *noteTable = + Util::GetDataRefAddress0(wsd->refNoteTable, &mDataBlock->wsdCount); + + WsdFile::NoteInfo const *src = Util::GetDataRefAddress0( + noteTable->item[noteIndex], &mDataBlock->wsdCount); + + noteInfo->waveIndex = src->waveIndex; + noteInfo->attack = src->attack; + noteInfo->hold = src->hold; + noteInfo->decay = src->decay; + noteInfo->sustain = src->sustain; + noteInfo->release = src->release; + noteInfo->originalKey = src->originalKey; + noteInfo->volume = src->volume; + + if (mHeader->fileHeader.version >= NW4R_FILE_VERSION(1, 1)) + { + noteInfo->pan = src->pan; + noteInfo->surroundPan = src->surroundPan; + noteInfo->pitch = src->pitch; + } + else + { + noteInfo->pan = 64; + noteInfo->surroundPan = 0; + noteInfo->pitch = 1.0f; + } + + return true; +} + +bool WsdFileReader::ReadWaveInfo(int waveIndex, WaveInfo *waveData, + void const *waveDataAddress) const +{ + if (!mWaveBlock) + { + WaveArchiveReader waveArchiveReader(waveDataAddress); + + WaveFile::FileHeader const *fileHeader = + waveArchiveReader.GetWaveFile(waveIndex); + if (!fileHeader) + return false; + + WaveFileReader waveFileReader(fileHeader); + return waveFileReader.ReadWaveInfo(waveData); + } + else + { + WaveFile::WaveInfo const *waveInfo; + + if (mHeader->fileHeader.version >= NW4R_FILE_VERSION(1, 1)) + { + if (waveIndex >= mWaveBlock->waveCount) + return false; + + waveInfo = + static_cast<WaveFile::WaveInfo const *>(ut::AddOffsetToPtr( + mWaveBlock, mWaveBlock->offsetTable[waveIndex])); + } + else + { + WsdFile::WaveBlockOld const *waveBlockOld = + reinterpret_cast<WsdFile::WaveBlockOld const *>(mWaveBlock); + + waveInfo = + static_cast<WaveFile::WaveInfo const *>(ut::AddOffsetToPtr( + waveBlockOld, waveBlockOld->offsetTable[waveIndex])); + } + + WaveFileReader waveFileReader(waveInfo); + return waveFileReader.ReadWaveInfo(waveData, waveDataAddress); + } +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_WsdPlayer.cpp b/src/nw4r/snd/snd_WsdPlayer.cpp index e45ee650..d6b9fea7 100644 --- a/src/nw4r/snd/snd_WsdPlayer.cpp +++ b/src/nw4r/snd/snd_WsdPlayer.cpp @@ -1 +1,376 @@ -#include "nw4r/snd/snd_WsdPlayer.h" +#include "nw4r/snd/WsdPlayer.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_WsdPlayer.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <types.h> + +#include "nw4r/snd/BasicPlayer.h" +#include "nw4r/snd/Channel.h" +#include "nw4r/snd/DisposeCallbackManager.h" +#include "nw4r/snd/global.h" +#include "nw4r/snd/Voice.h" +#include "nw4r/snd/SoundThread.h" +#include "nw4r/snd/WaveFile.h" + +#include "nw4r/ut/inlines.h" // ut::Min + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +WsdPlayer::WsdPlayer() : + mActiveFlag (false) +{ +} + +void WsdPlayer::InitParam(int voiceOutCount, WsdCallback const *callback, + register_t callbackData) +{ + BasicPlayer::InitParam(); + + mStartedFlag = false; + mPauseFlag = false; + mReleasePriorityFixFlag = false; + mPanRange = 1.0f; + mVoiceOutCount = voiceOutCount; + mPriority = DEFAULT_PRIORITY; + mCallback = callback; + mCallbackData = callbackData; + mWsdData = nullptr; + mWsdIndex = -1; + mWaveSoundInfo.pitch = 1.0f; + mWaveSoundInfo.pan = 64; + mWaveSoundInfo.surroundPan = 0; + mWaveSoundInfo.fxSendA = 0; + mWaveSoundInfo.fxSendB = 0; + mWaveSoundInfo.fxSendC = 0; + mWaveSoundInfo.mainSend = 127; + mLfoParam.Init(); + + mWavePlayFlag = false; + + mChannel = nullptr; +} + +bool WsdPlayer::Prepare(void const *waveSoundBase, int index, + StartOffsetType startOffsetType, int startOffset, + int voiceOutCount, WsdCallback const *callback, + register_t callbackData) +{ + SoundThread::AutoLock lock; + + if (mActiveFlag) + FinishPlayer(); + + InitParam(voiceOutCount, callback, callbackData); + + mWsdData = waveSoundBase; + mWsdIndex = index; + mStartOffsetType = startOffsetType; + mStartOffset = startOffset; + + DisposeCallbackManager::GetInstance().RegisterDisposeCallback(this); + + mActiveFlag = true; + + return true; +} + +bool WsdPlayer::Start() +{ + SoundThread::AutoLock lock; + + SoundThread::GetInstance().RegisterPlayerCallback(this); + + mStartedFlag = true; + + return true; +} + +void WsdPlayer::Stop() +{ + SoundThread::AutoLock lock; + + FinishPlayer(); +} + +void WsdPlayer::Pause(bool flag) +{ + SoundThread::AutoLock lock; + + mPauseFlag = static_cast<u8>(flag); // ??? + + if (IsChannelActive() && flag != mChannel->IsPause()) + mChannel->Pause(flag); +} + +void WsdPlayer::SetChannelPriority(int priority) +{ + // specificallly not the source variant + NW4RAssertHeaderClampedLRValue_Line(229, priority, 0, 127); + + mPriority = priority; +} + +void WsdPlayer::SetReleasePriorityFix(bool fix) +{ + mReleasePriorityFixFlag = fix; +} + +void WsdPlayer::InvalidateData(void const *start, void const *end) +{ + SoundThread::AutoLock lock; + + if (mActiveFlag) + { + void const *current = GetWsdDataAddress(); + + if (start <= current && current <= end) + FinishPlayer(); + } +} + +void WsdPlayer::FinishPlayer() +{ + SoundThread::AutoLock lock; + + if (mStartedFlag) + { + SoundThread::GetInstance().UnregisterPlayerCallback(this); + + mStartedFlag = false; + } + + if (mActiveFlag) + { + DisposeCallbackManager::GetInstance().UnregisterDisposeCallback(this); + + mActiveFlag = false; + } + + CloseChannel(); +} + +u32 WsdPlayer::GetPlaySamplePosition() const +{ + SoundThread::AutoLock lock; + + if (!mChannel) + return -1; + + return mChannel->GetVoice()->GetCurrentPlayingSample(); +} + +void WsdPlayer::Update() +{ + SoundThread::AutoLock lock; + + NW4RAssert_Line(362, mActiveFlag); + if (!mActiveFlag) + return; + + if (!mStartedFlag) + return; + + if (!mPauseFlag) + { + if (mWavePlayFlag && !mChannel) + { + FinishPlayer(); + return; + } + + if (!mWavePlayFlag && !StartChannel(mCallback, mCallbackData)) + { + FinishPlayer(); + return; + } + } + + UpdateChannel(); +} + +bool WsdPlayer::StartChannel(WsdCallback const *callback, + register_t callbackData) +{ + SoundThread::AutoLock lock; + + int priority = DEFAULT_PRIORITY + GetChannelPriority(); + + WaveInfo waveData; + WaveSoundNoteInfo noteInfo; + bool result = + callback->GetWaveSoundData(&mWaveSoundInfo, ¬eInfo, &waveData, + mWsdData, mWsdIndex, 0, callbackData); + if (!result) + return false; + + u32 startOffsetSamples; + if (mStartOffsetType == START_OFFSET_TYPE_SAMPLE) + { + startOffsetSamples = mStartOffset; + } + else if (mStartOffsetType == START_OFFSET_TYPE_MILLISEC) + { + startOffsetSamples = + static_cast<s64>(mStartOffset) * waveData.sampleRate / 1000; + } + + // NOTE: another case of start offset thing + + if (startOffsetSamples > waveData.loopEnd) + return false; + + Channel *channel = Channel::AllocChannel( + ut::Min(waveData.numChannels, Channel::CHANNEL_MAX), GetVoiceOutCount(), + priority, ChannelCallbackFunc, reinterpret_cast<register_t>(this)); + if (!channel) + return false; + + channel->SetAttack(noteInfo.attack); + channel->SetHold(noteInfo.hold); + channel->SetDecay(noteInfo.decay); + channel->SetSustain(noteInfo.sustain); + channel->SetRelease(noteInfo.release); + channel->SetReleasePriorityFix(mReleasePriorityFixFlag); + + channel->Start(waveData, -1, startOffsetSamples); + mChannel = channel; + + mWavePlayFlag = true; + + return true; +} + +void WsdPlayer::CloseChannel() +{ + SoundThread::AutoLock lock; + + if (IsChannelActive()) + { + UpdateChannel(); + + mChannel->Release(); + } + + if (mChannel) + Channel::FreeChannel(mChannel); + + mChannel = nullptr; +} + +void WsdPlayer::UpdateChannel() +{ + SoundThread::AutoLock lock; + + if (!mChannel) + return; + + f32 volume = 1.0f; + volume *= GetVolume(); + + f32 pitchRatio = 1.0f; + pitchRatio *= GetPitch(); + pitchRatio *= mWaveSoundInfo.pitch; + + f32 pan = 0.0f; + if (mWaveSoundInfo.pan <= 1) + pan += (mWaveSoundInfo.pan - 63) / 63.0f; + else + pan += (mWaveSoundInfo.pan - 64) / 63.0f; + + pan *= GetPanRange(); + pan += GetPan(); + + f32 surroundPan = 0.0f; + if (mWaveSoundInfo.surroundPan <= 1) + surroundPan += (mWaveSoundInfo.surroundPan + 1) / 63.0f; + else + surroundPan += mWaveSoundInfo.surroundPan / 63.0f; + + surroundPan += mWaveSoundInfo.surroundPan / 64.0f; + surroundPan += GetSurroundPan(); + + f32 lpfFreq = 0.0f; + lpfFreq += GetLpfFreq(); + + int biquadType = GetBiquadType(); + f32 biquadValue = GetBiquadValue(); + + int remoteFilter = 0; + remoteFilter += GetRemoteFilter(); + + f32 mainSend = 0.0f; + mainSend += mWaveSoundInfo.mainSend / 127.0f - 1.0f; + mainSend += GetMainSend(); + + f32 fxSend[AUX_BUS_NUM]; + + u8 infoSend[AUX_BUS_NUM]; + infoSend[AUX_A] = mWaveSoundInfo.fxSendA; + infoSend[AUX_B] = mWaveSoundInfo.fxSendB; + infoSend[AUX_C] = mWaveSoundInfo.fxSendC; + + for (int i = 0; i < AUX_BUS_NUM; i++) + { + fxSend[i] = 0.0f; + fxSend[i] += infoSend[i] / 127.0f; + fxSend[i] += GetFxSend(static_cast<AuxBus>(i)); + } + + mChannel->SetPanMode(GetPanMode()); + mChannel->SetPanCurve(GetPanCurve()); + mChannel->SetUserVolume(volume); + mChannel->SetUserPitchRatio(pitchRatio); + mChannel->SetUserPan(pan); + mChannel->SetUserSurroundPan(surroundPan); + mChannel->SetUserLpfFreq(lpfFreq); + mChannel->SetBiquadFilter(biquadType, biquadValue); + mChannel->SetRemoteFilter(remoteFilter); + mChannel->SetOutputLine(GetOutputLine()); + mChannel->SetMainOutVolume(GetMainOutVolume()); + mChannel->SetMainSend(mainSend); + + for (int i = 0; i < AUX_BUS_NUM; i++) + { + AuxBus bus = static_cast<AuxBus>(i); + + mChannel->SetFxSend(bus, fxSend[i]); + } + + for (int i = 0; i < mVoiceOutCount; i++) + mChannel->SetVoiceOutParam(i, GetVoiceOutParam(i)); + + mChannel->SetLfoParam(mLfoParam); +} + +void WsdPlayer::ChannelCallbackFunc(Channel *dropChannel, + Channel::ChannelCallbackStatus status, + register_t userData) +{ + SoundThread::AutoLock lock; + + WsdPlayer *player = reinterpret_cast<WsdPlayer *>(userData); + + NW4RAssertPointerNonnull_Line(643, dropChannel); + NW4RAssertPointerNonnull_Line(644, player); + NW4RAssert_Line(645, dropChannel == player->mChannel); + + if (status == Channel::CALLBACK_STATUS_FINISH) + Channel::FreeChannel(dropChannel); + + player->mChannel = nullptr; +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_adpcm.cpp b/src/nw4r/snd/snd_adpcm.cpp index bbcb0efa..433e8467 100644 --- a/src/nw4r/snd/snd_adpcm.cpp +++ b/src/nw4r/snd/snd_adpcm.cpp @@ -1 +1,58 @@ -#include "nw4r/snd/snd_adpcm.h" +#include "nw4r/snd/adpcm.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_adpcm.cpp + */ + +/******************************************************************************* + * headers + */ + +#include <climits> + +#include <types.h> + +#if 0 +#include <revolution/AX/AXVPB.h> // AXPBADPCM +#else +#include <context_rvl.h> +#endif + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +s16 DecodeDspAdpcm(AXPBADPCM *adpcm, byte_t bits) +{ + s16 yn1 = static_cast<s16>(adpcm->yn1); + s16 yn2 = static_cast<s16>(adpcm->yn2); + + s16 scale = 1 << (adpcm->pred_scale & 0x0f); + s16 bits2 = bits << 12; + u16 index = adpcm->pred_scale >> 4; + + s16 coef0 = adpcm->a[index][0]; + s16 coef1 = adpcm->a[index][1]; + + s32 sample = coef0 * yn1; + sample += coef1 * yn2; + sample += scale * (bits2 >> 1); + sample >>= 10; + sample += 1; + sample >>= 1; + + if (sample > SHRT_MAX) + sample = SHRT_MAX; + else if (sample < SHRT_MIN) + sample = SHRT_MIN; + + adpcm->yn2 = adpcm->yn1; + adpcm->yn1 = sample; + + return sample; +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_debug.cpp b/src/nw4r/snd/snd_debug.cpp index b86b404b..fbd4d3f9 100644 --- a/src/nw4r/snd/snd_debug.cpp +++ b/src/nw4r/snd/snd_debug.cpp @@ -1 +1,148 @@ -// #include "nw4r/snd/snd_debug.h" +#include "nw4r/snd/debug.h" + +/******************************************************************************* + * headers + */ + +#include <types.h> // byte4_t + +#include <nw4r/NW4RAssert.hpp> + +/******************************************************************************* + * types + */ + +namespace nw4r { namespace snd { namespace +{ + /* I just made all of this up, but this is a convenient place to store these + * constants + */ + enum DebugWarningBitFlag + { + DEBUG_WARNING_BIT_FLAG_NOT_ENOUGH_SEQSOUND = 1 << 0, + DEBUG_WARNING_BIT_FLAG_NOT_ENOUGH_STRMSOUND = 1 << 1, + DEBUG_WARNING_BIT_FLAG_NOT_ENOUGH_WAVESOUND = 1 << 2, + DEBUG_WARNING_BIT_FLAG_NOT_ENOUGH_SEQTRACK = 1 << 3, + DEBUG_WARNING_BIT_FLAG_NOT_ENOUGH_STRMCHANNEL = 1 << 4, + + DEBUG_WARNING_BIT_FLAG_NOT_ENOUGH_INSTANCE = + DEBUG_WARNING_BIT_FLAG_NOT_ENOUGH_SEQSOUND + | DEBUG_WARNING_BIT_FLAG_NOT_ENOUGH_STRMSOUND + | DEBUG_WARNING_BIT_FLAG_NOT_ENOUGH_WAVESOUND + | DEBUG_WARNING_BIT_FLAG_NOT_ENOUGH_SEQTRACK + | DEBUG_WARNING_BIT_FLAG_NOT_ENOUGH_STRMCHANNEL, + }; +}}} // namespace nw4r::snd::(unnamed) + +/******************************************************************************* + * local function declarations + */ + +namespace nw4r { namespace snd { namespace +{ + byte4_t GetWarningBitFlag(DebugWarningFlag warning); +}}} // namespace nw4r::snd::(unnamed) + +/******************************************************************************* + * variables + */ + +namespace nw4r { namespace snd { namespace +{ + byte4_t gWarningFlag = DEBUG_WARNING_BIT_FLAG_NOT_ENOUGH_INSTANCE; +}}} // namespace nw4r::snd::(unnamed) + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { + +namespace detail { + +bool Debug_GetWarningFlag(DebugWarningFlag warning) +{ + byte4_t bitFlag = GetWarningBitFlag(warning); + + return (gWarningFlag & bitFlag) == bitFlag; +} + +DebugWarningFlag Debug_GetDebugWarningFlagFromSoundType(DebugSoundType type) +{ + switch (type) + { + case DEBUG_SOUND_TYPE_SEQSOUND: + return DEBUG_WARNING_NOT_ENOUGH_SEQSOUND; + + case DEBUG_SOUND_TYPE_STRMSOUND: + return DEBUG_WARNING_NOT_ENOUGH_STRMSOUND; + + case DEBUG_SOUND_TYPE_WAVESOUND: + return DEBUG_WARNING_NOT_ENOUGH_WAVESOUND; + + default: + NW4RPanic_Line(91); + return DEBUG_WARNING_NOT_ENOUGH_SEQSOUND; + } +} + +char const *Debug_GetSoundTypeString(DebugSoundType type) +{ + switch (type) + { + case DEBUG_SOUND_TYPE_SEQSOUND: + return "seq"; + + case DEBUG_SOUND_TYPE_STRMSOUND: + return "strm"; + + case DEBUG_SOUND_TYPE_WAVESOUND: + return "wave"; + + default: + NW4RPanic_Line(107); + return ""; + } +} + +} // namespace detail + +namespace { + +byte4_t GetWarningBitFlag(DebugWarningFlag warning) +{ + byte4_t bitFlag = 0; + + switch (warning) + { + case DEBUG_WARNING_NOT_ENOUGH_INSTANCE: + bitFlag = DEBUG_WARNING_BIT_FLAG_NOT_ENOUGH_INSTANCE; + break; + + case DEBUG_WARNING_NOT_ENOUGH_SEQSOUND: + bitFlag = DEBUG_WARNING_BIT_FLAG_NOT_ENOUGH_SEQSOUND; + break; + + case DEBUG_WARNING_NOT_ENOUGH_STRMSOUND: + bitFlag = DEBUG_WARNING_BIT_FLAG_NOT_ENOUGH_STRMSOUND; + break; + + case DEBUG_WARNING_NOT_ENOUGH_WAVESOUND: + bitFlag = DEBUG_WARNING_BIT_FLAG_NOT_ENOUGH_WAVESOUND; + break; + + case DEBUG_WARNING_NOT_ENOUGH_SEQTRACK: + bitFlag = DEBUG_WARNING_BIT_FLAG_NOT_ENOUGH_SEQTRACK; + break; + + case DEBUG_WARNING_NOT_ENOUGH_STRMCHANNEL: + bitFlag = DEBUG_WARNING_BIT_FLAG_NOT_ENOUGH_STRMCHANNEL; + break; + } + + return bitFlag; +} + +} // unnamed namespace + +}} // namespace nw4r::snd diff --git a/src/nw4r/snd/snd_global.cpp b/src/nw4r/snd/snd_global.cpp new file mode 100644 index 00000000..2a924f1d --- /dev/null +++ b/src/nw4r/snd/snd_global.cpp @@ -0,0 +1 @@ +/* This file intentionally left blank. */ |
