summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorrobojumper <robojumper@gmail.com>2025-05-24 21:53:14 +0200
committerrobojumper <robojumper@gmail.com>2025-05-25 08:52:12 +0200
commit04d5527ebab1a17348c5030dc21df481ed49b1c6 (patch)
treede2e15924ef2ca447b0739e826ac142fb73e8d4b /src
parent4c62e0102547907becb945138356f401b150120e (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')
-rw-r--r--src/nw4r/snd/snd_AnimSound.cpp11
-rw-r--r--src/nw4r/snd/snd_AxManager.cpp374
-rw-r--r--src/nw4r/snd/snd_AxVoice.cpp1359
-rw-r--r--src/nw4r/snd/snd_AxVoiceManager.cpp236
-rw-r--r--src/nw4r/snd/snd_Bank.cpp97
-rw-r--r--src/nw4r/snd/snd_BankFile.cpp375
-rw-r--r--src/nw4r/snd/snd_BasicPlayer.cpp104
-rw-r--r--src/nw4r/snd/snd_BasicSound.cpp681
-rw-r--r--src/nw4r/snd/snd_BiquadFilterCallback.cpp1
-rw-r--r--src/nw4r/snd/snd_BiquadFilterPreset.cpp634
-rw-r--r--src/nw4r/snd/snd_Channel.cpp473
-rw-r--r--src/nw4r/snd/snd_DisposeCallbackManager.cpp82
-rw-r--r--src/nw4r/snd/snd_DvdSoundArchive.cpp7
-rw-r--r--src/nw4r/snd/snd_EnvGenerator.cpp251
-rw-r--r--src/nw4r/snd/snd_ExternalSoundPlayer.cpp112
-rw-r--r--src/nw4r/snd/snd_FxBase.cpp2
-rw-r--r--src/nw4r/snd/snd_InstancePool.cpp103
-rw-r--r--src/nw4r/snd/snd_Lfo.cpp94
-rw-r--r--src/nw4r/snd/snd_McsSoundArchive.cpp8
-rw-r--r--src/nw4r/snd/snd_MemorySoundArchive.cpp184
-rw-r--r--src/nw4r/snd/snd_MidiSeqPlayer.cpp7
-rw-r--r--src/nw4r/snd/snd_MidiSeqTrack.cpp6
-rw-r--r--src/nw4r/snd/snd_MidiSeqTrackAllocator.cpp6
-rw-r--r--src/nw4r/snd/snd_MmlParser.cpp984
-rw-r--r--src/nw4r/snd/snd_MmlSeqTrack.cpp28
-rw-r--r--src/nw4r/snd/snd_MmlSeqTrackAllocator.cpp57
-rw-r--r--src/nw4r/snd/snd_MoveValue.cpp1
-rw-r--r--src/nw4r/snd/snd_NoteOnCallback.cpp1
-rw-r--r--src/nw4r/snd/snd_PlayerHeap.cpp119
-rw-r--r--src/nw4r/snd/snd_SeqFile.cpp128
-rw-r--r--src/nw4r/snd/snd_SeqPlayer.cpp564
-rw-r--r--src/nw4r/snd/snd_SeqSound.cpp307
-rw-r--r--src/nw4r/snd/snd_SeqSoundHandle.cpp35
-rw-r--r--src/nw4r/snd/snd_SeqTrack.cpp644
-rw-r--r--src/nw4r/snd/snd_SeqTrackAllocator.cpp1
-rw-r--r--src/nw4r/snd/snd_SoundActor.cpp17
-rw-r--r--src/nw4r/snd/snd_SoundArchive.cpp230
-rw-r--r--src/nw4r/snd/snd_SoundArchiveFile.cpp678
-rw-r--r--src/nw4r/snd/snd_SoundArchivePlayer.cpp1450
-rw-r--r--src/nw4r/snd/snd_SoundHandle.cpp52
-rw-r--r--src/nw4r/snd/snd_SoundInstanceManager.cpp1
-rw-r--r--src/nw4r/snd/snd_SoundMemoryAllocatable.cpp1
-rw-r--r--src/nw4r/snd/snd_SoundPlayer.cpp296
-rw-r--r--src/nw4r/snd/snd_SoundStartable.cpp53
-rw-r--r--src/nw4r/snd/snd_SoundSystem.cpp222
-rw-r--r--src/nw4r/snd/snd_SoundThread.cpp221
-rw-r--r--src/nw4r/snd/snd_StrmChannel.cpp112
-rw-r--r--src/nw4r/snd/snd_StrmFile.cpp361
-rw-r--r--src/nw4r/snd/snd_StrmPlayer.cpp1449
-rw-r--r--src/nw4r/snd/snd_StrmSound.cpp143
-rw-r--r--src/nw4r/snd/snd_StrmSoundHandle.cpp35
-rw-r--r--src/nw4r/snd/snd_Task.cpp27
-rw-r--r--src/nw4r/snd/snd_TaskManager.cpp217
-rw-r--r--src/nw4r/snd/snd_TaskThread.cpp105
-rw-r--r--src/nw4r/snd/snd_Util.cpp768
-rw-r--r--src/nw4r/snd/snd_Voice.cpp1384
-rw-r--r--src/nw4r/snd/snd_VoiceManager.cpp269
-rw-r--r--src/nw4r/snd/snd_WaveArchive.cpp103
-rw-r--r--src/nw4r/snd/snd_WaveFile.cpp164
-rw-r--r--src/nw4r/snd/snd_WaveSound.cpp106
-rw-r--r--src/nw4r/snd/snd_WaveSoundHandle.cpp65
-rw-r--r--src/nw4r/snd/snd_WsdFile.cpp216
-rw-r--r--src/nw4r/snd/snd_WsdPlayer.cpp377
-rw-r--r--src/nw4r/snd/snd_adpcm.cpp59
-rw-r--r--src/nw4r/snd/snd_debug.cpp149
-rw-r--r--src/nw4r/snd/snd_global.cpp1
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 &param) 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 &param)
+{
+ 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 &noteOnInfo) 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, &param, 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 &noteOnInfo)
+{
+ 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 &noteOnInfo)
+{
+ 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 &param)
+{
+ // 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 &param, 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, &noteInfo, &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. */