diff --git a/.clang-format-ignore b/.clang-format-ignore index ce20a0e4..712da2bc 100644 --- a/.clang-format-ignore +++ b/.clang-format-ignore @@ -1,2 +1,4 @@ # Ignore JSystem Files - Copied From TP **/JSystem/** +# Ignore SND Files - Copied from https://github.com/muff1n1634/nw4r_snd_mid2010 +**/nw4r/snd/** diff --git a/include/nw4r/NW4RAssert.hpp b/include/nw4r/NW4RAssert.hpp new file mode 100644 index 00000000..dfa2461a --- /dev/null +++ b/include/nw4r/NW4RAssert.hpp @@ -0,0 +1,169 @@ +// clang-format off + +/******************************************************************************* + * anti-header guard + */ + +#ifdef __MWERKS__ +# pragma notonce +#endif + +/******************************************************************************* + * NW4RAssert/NW4RCheck base macros + */ + +// behavior +#undef NW4RAssertMessage_FileLine +#undef NW4RCheckMessage_FileLine + +#if defined(NW4R_ASSERT) +# define NW4RAssertMessage_FileLine(file_, line_, expr_, ...) ((void)((expr_) || (::nw4r::db::Panic (file_, line_, __VA_ARGS__), 0))) +# define NW4RCheckMessage_FileLine(file_, line_, expr_, ...) ((void)((expr_) || (::nw4r::db::Warning(file_, line_, __VA_ARGS__), 0))) +#else +# define NW4RAssertMessage_FileLine(file_, line_, expr_, ...) ((void)(0)) +# define NW4RCheckMessage_FileLine(file_, line_, expr_, ...) ((void)(0)) +#endif // defined(NW4R_ASSERT) + +/******************************************************************************* + * Derived macros + */ + +/* Main asserts */ + +// NW4RAssert family +#define NW4RAssertMessage_File(file_, expr_, ...) NW4RAssertMessage_FileLine( file_ , __LINE__, expr_, __VA_ARGS__) +#define NW4RAssertMessage_Line(line_, expr_, ...) NW4RAssertMessage_FileLine(__FILE__, line_ , expr_, __VA_ARGS__) +#define NW4RAssertMessage(expr_, ...) NW4RAssertMessage_FileLine(__FILE__, __LINE__, expr_, __VA_ARGS__) + +#define NW4RAssert_FileLine(file_, line_, expr_) NW4RAssertMessage_FileLine( file_ , line_ , expr_, "NW4R:Failed assertion " #expr_) +#define NW4RAssert_File(file_, expr_) NW4RAssertMessage_FileLine( file_ , __LINE__, expr_, "NW4R:Failed assertion " #expr_) +#define NW4RAssert_Line(line_, expr_) NW4RAssertMessage_FileLine(__FILE__, line_ , expr_, "NW4R:Failed assertion " #expr_) +#define NW4RAssert(expr_) NW4RAssertMessage_FileLine(__FILE__, __LINE__, expr_, "NW4R:Failed assertion " #expr_) + +#define NW4RAssertHeader_FileLine(file_, line_, expr_) NW4RAssertMessage_FileLine( file_ , line_ , expr_, "Failed assertion " #expr_) +#define NW4RAssertHeader_File(file_, expr_) NW4RAssertMessage_FileLine( file_ , __LINE__, expr_, "Failed assertion " #expr_) +#define NW4RAssertHeader_Line(line_, expr_) NW4RAssertMessage_FileLine(__FILE__, line_ , expr_, "Failed assertion " #expr_) +#define NW4RAssertHeader(expr_) NW4RAssertMessage_FileLine(__FILE__, __LINE__, expr_, "Failed assertion " #expr_) + +// NW4RPanic family +#define NW4RPanicMessage_FileLine(file_, line_, ...) NW4RAssertMessage_FileLine( file_ , line_ , false, __VA_ARGS__) +#define NW4RPanicMessage_File(file_, ...) NW4RAssertMessage_FileLine( file_ , __LINE__, false, __VA_ARGS__) +#define NW4RPanicMessage_Line(line_, ...) NW4RAssertMessage_FileLine(__FILE__, line_ , false, __VA_ARGS__) +#define NW4RPanicMessage(...) NW4RAssertMessage_FileLine(__FILE__, __LINE__, false, __VA_ARGS__) + +#define NW4RPanic_FileLine(file_, line_) NW4RAssert_FileLine( file_ , line_ , false) +#define NW4RPanic_File(file_) NW4RAssert_FileLine( file_ , __LINE__, false) +#define NW4RPanic_Line(line_) NW4RAssert_FileLine(__FILE__, line_ , false) +#define NW4RPanic() NW4RAssert_FileLine(__FILE__, __LINE__, false) + +// NW4RCheck family +#define NW4RCheckMessage_File(file_, expr_, ...) NW4RCheckMessage_FileLine( file_ , __LINE__, expr_, __VA_ARGS__) +#define NW4RCheckMessage_Line(line_, expr_, ...) NW4RCheckMessage_FileLine(__FILE__, line_ , expr_, __VA_ARGS__) +#define NW4RCheckMessage(expr_, ...) NW4RCheckMessage_FileLine(__FILE__, __LINE__, expr_, __VA_ARGS__) + +#define NW4RCheck_FileLine(file_, line_, expr_) NW4RCheckMessage_FileLine( file_ , line_ , expr_, "NW4R:Failed check " #expr_) +#define NW4RCheck_File(file_, expr_) NW4RCheckMessage_FileLine( file_ , __LINE__, expr_, "NW4R:Failed check " #expr_) +#define NW4RCheck_Line(line_, expr_) NW4RCheckMessage_FileLine(__FILE__, line_ , expr_, "NW4R:Failed check " #expr_) +#define NW4RCheck(expr_) NW4RCheckMessage_FileLine(__FILE__, __LINE__, expr_, "NW4R:Failed check " #expr_) + +#define NW4RCheckHeader_FileLine(file_, line_, expr_) NW4RCheckMessage_FileLine( file_ , line_ , expr_, "Failed check " #expr_) +#define NW4RCheckHeader_File(file_, expr_) NW4RCheckMessage_FileLine( file_ , __LINE__, expr_, "Failed check " #expr_) +#define NW4RCheckHeader_Line(line_, expr_) NW4RCheckMessage_FileLine(__FILE__, line_ , expr_, "Failed check " #expr_) +#define NW4RCheckHeader(expr_) NW4RCheckMessage_FileLine(__FILE__, __LINE__, expr_, "Failed check " #expr_) + +// NW4RWarning family +#define NW4RWarningMessage_FileLine(file_, line_, ...) NW4RCheckMessage_FileLine( file_ , line_ , false, __VA_ARGS__) +#define NW4RWarningMessage_File(file_, ...) NW4RCheckMessage_FileLine( file_ , __LINE__, false, __VA_ARGS__) +#define NW4RWarningMessage_Line(line_, ...) NW4RCheckMessage_FileLine(__FILE__, line_ , false, __VA_ARGS__) +#define NW4RWarningMessage(...) NW4RCheckMessage_FileLine(__FILE__, __LINE__, false, __VA_ARGS__) + +#define NW4RWarning_FileLine(file_, line_) NW4RCheck_FileLine( file_ , line_ , false) +#define NW4RWarning_File(file_) NW4RCheck_FileLine( file_ , __LINE__, false) +#define NW4RWarning_Line(line_) NW4RCheck_FileLine(__FILE__, line_ , false) +#define NW4RWarning() NW4RCheck_FileLine(__FILE__, __LINE__, false) + +/* Extended asserts */ + +// PointerNonnull +#define NW4RAssertPointerNonnull_FileLine(file_, line_, ptr_) NW4RAssertMessage_FileLine( file_ , line_ , (ptr_) != 0, "NW4R:Pointer must not be NULL (" #ptr_ ")") +#define NW4RAssertPointerNonnull_File(file_, ptr_) NW4RAssertMessage_FileLine( file_ , __LINE__, (ptr_) != 0, "NW4R:Pointer must not be NULL (" #ptr_ ")") +#define NW4RAssertPointerNonnull_Line(line_, ptr_) NW4RAssertMessage_FileLine(__FILE__, line_ , (ptr_) != 0, "NW4R:Pointer must not be NULL (" #ptr_ ")") +#define NW4RAssertPointerNonnull(ptr_) NW4RAssertMessage_FileLine(__FILE__, __LINE__, (ptr_) != 0, "NW4R:Pointer must not be NULL (" #ptr_ ")") + +#define NW4RAssertHeaderPointerNonnull_FileLine(file_, line_, ptr_) NW4RAssertMessage_FileLine( file_ , line_ , (ptr_) != 0, "Pointer must not be NULL (" #ptr_ ")") +#define NW4RAssertHeaderPointerNonnull_File(file_, ptr_) NW4RAssertMessage_FileLine( file_ , __LINE__, (ptr_) != 0, "Pointer must not be NULL (" #ptr_ ")") +#define NW4RAssertHeaderPointerNonnull_Line(line_, ptr_) NW4RAssertMessage_FileLine(__FILE__, line_ , (ptr_) != 0, "Pointer must not be NULL (" #ptr_ ")") +#define NW4RAssertHeaderPointerNonnull(ptr_) NW4RAssertMessage_FileLine(__FILE__, __LINE__, (ptr_) != 0, "Pointer must not be NULL (" #ptr_ ")") + +// Aligned +#define NW4R_IS_ALIGNED_(x, align) (((unsigned long)(x) & ((align) - 1)) == 0) // just redefine instead of pulling in + +#define NW4RAssertAligned_FileLine(file_, line_, val_, align_) NW4RAssertMessage_FileLine( file_ , line_ , NW4R_IS_ALIGNED_(val_, align_), "NW4R:Alignment Error(0x%x)\n" #val_ " must be aligned to " #align_ " bytes boundary.", val_) +#define NW4RAssertAligned_File(file_, val_, align_) NW4RAssertMessage_FileLine( file_ , __LINE__, NW4R_IS_ALIGNED_(val_, align_), "NW4R:Alignment Error(0x%x)\n" #val_ " must be aligned to " #align_ " bytes boundary.", val_) +#define NW4RAssertAligned_Line(line_, val_, align_) NW4RAssertMessage_FileLine(__FILE__, line_ , NW4R_IS_ALIGNED_(val_, align_), "NW4R:Alignment Error(0x%x)\n" #val_ " must be aligned to " #align_ " bytes boundary.", val_) +#define NW4RAssertAligned(val_, align_) NW4RAssertMessage_FileLine(__FILE__, __LINE__, NW4R_IS_ALIGNED_(val_, align_), "NW4R:Alignment Error(0x%x)\n" #val_ " must be aligned to " #align_ " bytes boundary.", val_) + +#define NW4RAssertHeaderAligned_FileLine(file_, line_, val_, align_) NW4RAssertMessage_FileLine( file_ , line_ , NW4R_IS_ALIGNED_(val_, align_), "Alignment Error(0x%x)\n" #val_ " must be aligned to " #align_ " bytes boundary.", val_) +#define NW4RAssertHeaderAligned_File(file_, val_, align_) NW4RAssertMessage_FileLine( file_ , __LINE__, NW4R_IS_ALIGNED_(val_, align_), "Alignment Error(0x%x)\n" #val_ " must be aligned to " #align_ " bytes boundary.", val_) +#define NW4RAssertHeaderAligned_Line(line_, val_, align_) NW4RAssertMessage_FileLine(__FILE__, line_ , NW4R_IS_ALIGNED_(val_, align_), "Alignment Error(0x%x)\n" #val_ " must be aligned to " #align_ " bytes boundary.", val_) +#define NW4RAssertHeaderAligned(val_, align_) NW4RAssertMessage_FileLine(__FILE__, __LINE__, NW4R_IS_ALIGNED_(val_, align_), "Alignment Error(0x%x)\n" #val_ " must be aligned to " #align_ " bytes boundary.", val_) + +// ClampedLValue +#define NW4RAssertClampedLValue_FileLine(file_, line_, var_, minValue_, maxValue_) NW4RAssertMessage_FileLine( file_ , line_ , (var_) >= (minValue_) && (var_) < (maxValue_), "NW4R:" #var_ " is out of bounds(%d)\n%d <= " #var_ " < %d not satisfied.", (int)(var_), (int)(minValue_), (int)(maxValue_)) +#define NW4RAssertClampedLValue_File(file_, var_, minValue_, maxValue_) NW4RAssertMessage_FileLine( file_ , __LINE__, (var_) >= (minValue_) && (var_) < (maxValue_), "NW4R:" #var_ " is out of bounds(%d)\n%d <= " #var_ " < %d not satisfied.", (int)(var_), (int)(minValue_), (int)(maxValue_)) +#define NW4RAssertClampedLValue_Line(line_, var_, minValue_, maxValue_) NW4RAssertMessage_FileLine(__FILE__, line_ , (var_) >= (minValue_) && (var_) < (maxValue_), "NW4R:" #var_ " is out of bounds(%d)\n%d <= " #var_ " < %d not satisfied.", (int)(var_), (int)(minValue_), (int)(maxValue_)) +#define NW4RAssertClampedLValue(var_, minValue_, maxValue_) NW4RAssertMessage_FileLine(__FILE__, __LINE__, (var_) >= (minValue_) && (var_) < (maxValue_), "NW4R:" #var_ " is out of bounds(%d)\n%d <= " #var_ " < %d not satisfied.", (int)(var_), (int)(minValue_), (int)(maxValue_)) + +#define NW4RAssertHeaderClampedLValue_FileLine(file_, line_, var_, minValue_, maxValue_) NW4RAssertMessage_FileLine( file_ , line_ , (var_) >= (minValue_) && (var_) < (maxValue_), #var_ " is out of bounds(%d)\n%d <= " #var_ " < %d not satisfied.", (int)(var_), (int)(minValue_), (int)(maxValue_)) +#define NW4RAssertHeaderClampedLValue_File(file_, var_, minValue_, maxValue_) NW4RAssertMessage_FileLine( file_ , __LINE__, (var_) >= (minValue_) && (var_) < (maxValue_), #var_ " is out of bounds(%d)\n%d <= " #var_ " < %d not satisfied.", (int)(var_), (int)(minValue_), (int)(maxValue_)) +#define NW4RAssertHeaderClampedLValue_Line(line_, var_, minValue_, maxValue_) NW4RAssertMessage_FileLine(__FILE__, line_ , (var_) >= (minValue_) && (var_) < (maxValue_), #var_ " is out of bounds(%d)\n%d <= " #var_ " < %d not satisfied.", (int)(var_), (int)(minValue_), (int)(maxValue_)) +#define NW4RAssertHeaderClampedLValue(var_, minValue_, maxValue_) NW4RAssertMessage_FileLine(__FILE__, __LINE__, (var_) >= (minValue_) && (var_) < (maxValue_), #var_ " is out of bounds(%d)\n%d <= " #var_ " < %d not satisfied.", (int)(var_), (int)(minValue_), (int)(maxValue_)) + +// ClampedLRValue +#define NW4RAssertClampedLRValue_FileLine(file_, line_, var_, minValue_, maxValue_) NW4RAssertMessage_FileLine( file_ , line_ , (var_) >= (minValue_) && (var_) <= (maxValue_), "NW4R:" #var_ " is out of bounds(%d)\n%d <= " #var_ " <= %d not satisfied.", (int)(var_), (int)(minValue_), (int)(maxValue_)) +#define NW4RAssertClampedLRValue_File(file_, var_, minValue_, maxValue_) NW4RAssertMessage_FileLine( file_ , __LINE__, (var_) >= (minValue_) && (var_) <= (maxValue_), "NW4R:" #var_ " is out of bounds(%d)\n%d <= " #var_ " <= %d not satisfied.", (int)(var_), (int)(minValue_), (int)(maxValue_)) +#define NW4RAssertClampedLRValue_Line(line_, var_, minValue_, maxValue_) NW4RAssertMessage_FileLine(__FILE__, line_ , (var_) >= (minValue_) && (var_) <= (maxValue_), "NW4R:" #var_ " is out of bounds(%d)\n%d <= " #var_ " <= %d not satisfied.", (int)(var_), (int)(minValue_), (int)(maxValue_)) +#define NW4RAssertClampedLRValue(var_, minValue_, maxValue_) NW4RAssertMessage_FileLine(__FILE__, __LINE__, (var_) >= (minValue_) && (var_) <= (maxValue_), "NW4R:" #var_ " is out of bounds(%d)\n%d <= " #var_ " <= %d not satisfied.", (int)(var_), (int)(minValue_), (int)(maxValue_)) + +#define NW4RAssertHeaderClampedLRValue_FileLine(file_, line_, var_, minValue_, maxValue_) NW4RAssertMessage_FileLine( file_ , line_ , (var_) >= (minValue_) && (var_) <= (maxValue_), #var_ " is out of bounds(%d)\n%d <= " #var_ " <= %d not satisfied.", (int)(var_), (int)(minValue_), (int)(maxValue_)) +#define NW4RAssertHeaderClampedLRValue_File(file_, var_, minValue_, maxValue_) NW4RAssertMessage_FileLine( file_ , __LINE__, (var_) >= (minValue_) && (var_) <= (maxValue_), #var_ " is out of bounds(%d)\n%d <= " #var_ " <= %d not satisfied.", (int)(var_), (int)(minValue_), (int)(maxValue_)) +#define NW4RAssertHeaderClampedLRValue_Line(line_, var_, minValue_, maxValue_) NW4RAssertMessage_FileLine(__FILE__, line_ , (var_) >= (minValue_) && (var_) <= (maxValue_), #var_ " is out of bounds(%d)\n%d <= " #var_ " <= %d not satisfied.", (int)(var_), (int)(minValue_), (int)(maxValue_)) +#define NW4RAssertHeaderClampedLRValue(var_, minValue_, maxValue_) NW4RAssertMessage_FileLine(__FILE__, __LINE__, (var_) >= (minValue_) && (var_) <= (maxValue_), #var_ " is out of bounds(%d)\n%d <= " #var_ " <= %d not satisfied.", (int)(var_), (int)(minValue_), (int)(maxValue_)) + +/******************************************************************************* + * Strings, for deadstripping + */ + +/* Main asserts */ + +#define NW4RAssert_String(expr_) "NW4R:Failed assertion " #expr_ +#define NW4RAssertHeader_String(expr_) "Failed assertion " #expr_ + +#define NW4RCheck_String(expr_) "NW4R:Failed check " #expr_ +#define NW4RCheckHeader_String(expr_) "Failed check " #expr_ + +/* Extended asserts */ + +#define NW4RAssertPointerNonnull_String(ptr_) "NW4R:Pointer must not be NULL (" #ptr_ ")" +#define NW4RAssertHeaderPointerNonnull_String(ptr_) "Pointer must not be NULL (" #ptr_ ")" + +#define NW4RAssertAligned_String(val_, align_) "NW4R:Alignment Error(0x%x)\n" #val_ " must be aligned to " #align_ " bytes boundary." +#define NW4RAssertHeaderAligned_String(val_, align_) "Alignment Error(0x%x)\n" #val_ " must be aligned to " #align_ " bytes boundary." + +#define NW4RAssertClampedLValue_String(var_) "NW4R:" #var_ " is out of bounds(%d)\n%d <= " #var_ " < %d not satisfied." +#define NW4RAssertHeaderClampedLValue_String(var_) #var_ " is out of bounds(%d)\n%d <= " #var_ " < %d not satisfied." + +#define NW4RAssertClampedLRValue_String(var_) "NW4R:" #var_ " is out of bounds(%d)\n%d <= " #var_ " <= %d not satisfied." +#define NW4RAssertHeaderClampedLRValue_String(var_) #var_ " is out of bounds(%d)\n%d <= " #var_ " <= %d not satisfied." + +/******************************************************************************* + * Declarations + */ + +namespace nw4r { namespace db +{ + __attribute__((weak)) extern void Panic(char const *file, int line, char const *msg, ...); + __attribute__((weak)) extern void Warning(char const *file, int line, char const *msg, ...); +}} // namespace nw4r::db + +// clang-format on diff --git a/include/nw4r/snd/snd_AnimSound.h b/include/nw4r/snd/snd_AnimSound.h new file mode 100644 index 00000000..2b25ea5b --- /dev/null +++ b/include/nw4r/snd/snd_AnimSound.h @@ -0,0 +1 @@ +/* Only implemented to the extent necessary to match data sections. */ diff --git a/include/nw4r/snd/snd_AxManager.h b/include/nw4r/snd/snd_AxManager.h index 016a9716..c4a248fb 100644 --- a/include/nw4r/snd/snd_AxManager.h +++ b/include/nw4r/snd/snd_AxManager.h @@ -1,109 +1,150 @@ #ifndef NW4R_SND_AX_MANAGER_H #define NW4R_SND_AX_MANAGER_H -#include "nw4r/snd/snd_Common.h" -#include "nw4r/snd/snd_FxBase.h" -#include "nw4r/snd/snd_MoveValue.h" -#include "nw4r/types_nw4r.h" -#include "nw4r/ut.h" // IWYU pragma: export -#include "rvl/AI.h" // IWYU pragma: export -#include "rvl/AX.h" // IWYU pragma: export -namespace nw4r { -namespace snd { -namespace detail { +/******************************************************************************* + * headers + */ -class AxManager { -public: - struct CallbackListNode { - NW4R_UT_LIST_NODE_DECL(); // at 0x0 - AXOutCallback callback; // at 0x8 - }; +#include - NW4R_UT_LIST_TYPEDEF_DECL(CallbackListNode); +#include "global.h" +#include "FxBase.h" +#include "MoveValue.h" -public: - static AxManager &GetInstance(); - - void Init(); - void Shutdown(); - void Update(); - void PrepareReset(); - - bool CheckInit() { - return mInitialized; - } - - bool IsDiskError() const { - return mDiskError; - } - - bool IsResetReady() const { - return mResetReadyCounter == 0; - } - - f32 GetOutputVolume() const; - void *GetZeroBufferAddress(); - - void RegisterCallback(CallbackListNode *pNode, AXOutCallback pCallback); - void UnregisterCallback(CallbackListNode *pNode); - - void SetOutputMode(OutputMode mode); - OutputMode GetOutputMode(); - - f32 GetMasterVolume() const { - return mMasterVolume.GetValue(); - } - void SetMasterVolume(f32 volume, int frame); - - bool AppendEffect(AuxBus bus, FxBase *pFx); - void ClearEffect(AuxBus bus, int frame); - void ShutdownEffect(AuxBus bus); - - FxBaseList &GetEffectList(AuxBus bus) { - return mFxList[bus]; - } - -private: - static const u8 AUX_CALLBACK_WAIT_FRAME = 6; - - static const int FX_SAMPLE_RATE = AX_SAMPLE_RATE; - static const SampleFormat FX_SAMPLE_FORMAT = SAMPLE_FORMAT_PCM_S32; - static const int FX_BUFFER_SIZE = AX_FRAME_SIZE; - - static const int ZERO_BUFFER_SIZE = 256; - -private: - AxManager(); - - static void AxCallbackFunc(); - static void AuxCallbackFunc(void *pChans, void *pContext); - static void AiDmaCallbackFunc(); - -private: - OutputMode mOutputMode; // at 0x0 - void *mZeroBufferAddress; // at 0x4 - CallbackListNodeList mCallbackList; // at 0x8 - AXOutCallback mNextAxRegisterCallback; // at 0x14 - bool mInitialized; // at 0x18 - bool mUpdateVoicePrioFlag; // at 0x19 - bool mDiskError; // at 0x1A - MoveValue mMasterVolume; // at 0x1C - MoveValue mMainOutVolume; // at 0x2C - MoveValue mVolumeForReset; // at 0x3C - AIDMACallback mOldAidCallback; // at 0x4C - volatile s32 mResetReadyCounter; // at 0x50 - MoveValue mAuxFadeVolume[AUX_BUS_NUM]; // at 0x54 - MoveValue mAuxUserVolume[AUX_BUS_NUM]; // at 0x84 - FxBaseList mFxList[AUX_BUS_NUM]; // at 0xB4 - AXAuxCallback mAuxCallback[AUX_BUS_NUM]; // at 0xD8 - void *mAuxCallbackContext[AUX_BUS_NUM]; // at 0xE4 - u8 mAuxCallbackWaitCounter[AUX_BUS_NUM]; // at 0xF0 - - static u8 sZeroBuffer[ZERO_BUFFER_SIZE]; -}; - -} // namespace detail -} // namespace snd -} // namespace nw4r +#include "../ut/LinkList.h" +#if 0 +#include // AIDMACallback +#include // AXAuxCallback +#include // AXFrameCallback +#else +#include #endif + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * types + */ + +// forward declarations +namespace nw4r { namespace snd { namespace detail { class BiquadFilterLpf; }}} +namespace nw4r { namespace snd { namespace detail { class BiquadFilterHpf; }}} +namespace nw4r { namespace snd { namespace detail { class BiquadFilterBpf512; }}} +namespace nw4r { namespace snd { namespace detail { class BiquadFilterBpf1024; }}} +namespace nw4r { namespace snd { namespace detail { class BiquadFilterBpf2048; }}} +namespace nw4r { namespace snd { class BiquadFilterCallback; }} + +/******************************************************************************* + * classes and functions + */ + +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2a5afe + class AxManager + { + // nested types + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2a6808 + struct CallbackListNode + { + // typedefs + public: + typedef ut::LinkList LinkList; + + // members + public: + ut::LinkListNode node; // size 0x08, offset 0x00 + AXFrameCallback *callback; // size 0x04, offset 0x08 + }; // size 0x0c + + // methods + public: + // instance accessors + static AxManager &GetInstance(); + + // methods + void Init(); + void Update(); + void Shutdown(); + + OutputMode GetOutputMode(); + void const *GetZeroBufferAddress(); + bool CheckInit() { return mInitialized; } + f32 GetOutputVolume() const; + FxBase::LinkList &GetEffectList(AuxBus bus) + { + NW4RAssertHeaderClampedLValue_Line(173, bus, AUX_A, AUX_BUS_NUM); + + return mFxList[bus]; + } + BiquadFilterCallback const *GetBiquadFilterCallback(int index) + { + return sBiquadFilterCallbackTable[static_cast(index)]; + } + + void SetOutputMode(OutputMode mode); + void SetMainOutVolume(f32 volume, int frames); + void SetBiquadFilterCallback(int type, + BiquadFilterCallback const *callback); + + void RegisterCallback(CallbackListNode *node, + AXFrameCallback *callback); + void UnregisterCallback(CallbackListNode *node); + + void ShutdownEffect(AuxBus bus); + + private: + // cdtors + AxManager(); + + // callbacks + static void AxCallbackFunc(); + + // static members + private: + static u8 const AUX_CALLBACK_WAIT_FRAME; + static u16 const AUX_RETURN_VOLUME_MAX = AX_MAX_VOLUME; + static int const FX_SAMPLE_RATE; + static SampleFormat const FX_SAMPLE_FORMAT; + static int const FX_BUFFER_SIZE; + static int const ZERO_BUFFER_SIZE = 256; + static int const SAMPLES_PAR_AUDIO_FRAME; + static int const AUDIO_FRAME_INTERVAL; + + static byte_t sZeroBuffer[ZERO_BUFFER_SIZE]; + static BiquadFilterCallback const *sBiquadFilterCallbackTable[128]; + static BiquadFilterLpf sBiquadFilterLpf; + static BiquadFilterHpf sBiquadFilterHpf; + static BiquadFilterBpf512 sBiquadFilterBpf512; + static BiquadFilterBpf1024 sBiquadFilterBpf1024; + static BiquadFilterBpf2048 sBiquadFilterBpf2048; + + // members + private: + OutputMode mOutputMode; // size 0x04, offset 0x00 + void *mZeroBufferAddress; // size 0x04, offset 0x04 + CallbackListNode::LinkList mCallbackList; // size 0x0c, offset 0x08 + AXFrameCallback *mNextAxRegisterCallback; // size 0x04, offset 0x14 + bool mInitialized; // size 0x01, offset 0x18 + bool mUpdateVoicePrioFlag; // size 0x01, offset 0x19 + /* 2 bytes padding */ + MoveValue mMasterVolume; // size 0x10, offset 0x1c + MoveValue mMainOutVolume; // size 0x10, offset 0x2c + MoveValue mVolumeForReset; // size 0x10, offset 0x3c + AIDMACallback *mOldAidCallback; // size 0x04, offset 0x4c + s32 mResetReadyCounter; // size 0x04, offset 0x50 + MoveValue mAuxFadeVolume[AUX_BUS_NUM]; // size 0x30, offset 0x54 + MoveValue mAuxUserVolume[AUX_BUS_NUM]; // size 0x30, offset 0x84 + FxBase::LinkList mFxList[AUX_BUS_NUM]; // size 0x24, offset 0xb4 + AXAuxCallback *mAuxCallback[AUX_BUS_NUM]; // size 0x0c, offset 0xd8 + void *mAuxCallbackContext[AUX_BUS_NUM]; // size 0x0c, offset 0xe4 + u8 mAuxCallbackWaitCounter[AUX_BUS_NUM]; // size 0x03, offset 0xf0 + /* 1 byte padding */ + u32 mEffectProcessTick[AUX_BUS_NUM]; // size 0x0c, offset 0xf4 + }; // size 0x100 +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_AX_MANAGER_H diff --git a/include/nw4r/snd/snd_AxVoice.h b/include/nw4r/snd/snd_AxVoice.h index bd743f9d..1a9bf7d3 100644 --- a/include/nw4r/snd/snd_AxVoice.h +++ b/include/nw4r/snd/snd_AxVoice.h @@ -1,275 +1,307 @@ #ifndef NW4R_SND_AX_VOICE_H #define NW4R_SND_AX_VOICE_H -#include "nw4r/snd/snd_Common.h" -#include "nw4r/types_nw4r.h" -#include "nw4r/ut.h" // IWYU pragma: export -#include "rvl/AX.h" // IWYU pragma: export -namespace nw4r { -namespace snd { -namespace detail { +/******************************************************************************* + * headers + */ -class AxVoiceParamBlock { -public: - AxVoiceParamBlock(); +#include - operator AXVPB *() { - return mVpb; - } +#include "adpcm.h" +#include "global.h" // SampleFormat - bool IsAvailable() const { - return mVpb != NULL; - } - - bool IsRun() const { - return IsAvailable() && mVpb->pb.state == AX_VOICE_RUN; - } - - bool IsLpfEnable() const { - return IsAvailable() && mVpb->pb.lpf.on == AX_PB_LPF_ON; - } - - bool IsBiquadEnable() const { - return IsAvailable() && mVpb->pb.biquad.on == AX_PB_BIQUAD_ON; - } - - u32 GetCurrentAddress() const { - if (!IsAvailable()) { - return 0; - } - - return (mVpb->pb.addr.currentAddressHi << 16) + mVpb->pb.addr.currentAddressLo; - } - - u32 GetLoopAddress() const { - if (!IsAvailable()) { - return 0; - } - - return (mVpb->pb.addr.loopAddressHi << 16) + mVpb->pb.addr.loopAddressLo; - } - - u32 GetEndAddress() const { - if (!IsAvailable()) { - return 0; - } - - return (mVpb->pb.addr.endAddressHi << 16) + mVpb->pb.addr.endAddressLo; - } - - void SetVoiceAddr(const AXPBADDR &rAddr) { - if (IsAvailable()) { - // AXSetVoiceAddr doesn't actually modify the object - AXSetVoiceAddr(mVpb, const_cast(&rAddr)); - } - } - - void SetVoicePriority(u32 priority) { - if (IsAvailable()) { - AXSetVoicePriority(mVpb, priority); - } - } - - void SetVoiceStateRun() { - if (IsAvailable()) { - AXSetVoiceState(mVpb, AX_VOICE_RUN); - } - } - - void SetVoiceStateStop() { - if (IsRun()) { - AXSetVoiceState(mVpb, AX_VOICE_STOP); - } - } - - void Sync(); - bool IsRmtIirEnable() const; - - void Set(AXVPB *pVpb); - void Clear(); - - void SetVoiceType(u16 type); - void SetVoiceVe(u16 volume, u16 initVolume); - void SetVoiceMix(const AXPBMIX &rMix, bool syncNow); - void SetVoiceLoop(u16 loop); - void SetVoiceLoopAddr(u32 addr); - void SetVoiceEndAddr(u32 addr); - void SetVoiceAdpcm(const AXPBADPCM &rAdpcm); - void SetVoiceSrcType(u32 type); - void SetVoiceSrc(const AXPBSRC &rSrc); - void SetVoiceSrcRatio(f32 ratio); - void SetVoiceAdpcmLoop(const AXPBADPCMLOOP &rLoop); - void SetVoiceLpf(const AXPBLPF &rLpf); - void SetVoiceLpfCoefs(u16 a0, u16 b0); - void SetVoiceRmtOn(u16 on); - void SetVoiceRmtMix(const AXPBRMTMIX &rMix); - void SetVoiceRmtIIR(const AXPBRMTIIR &rIir); - void SetVoiceRmtIIRCoefs(u16 type, ...); - void UpdateDelta(); - -private: - static const u16 DEFAULT_VOLUME = AX_MAX_VOLUME; - -private: - AXVPB *mVpb; // at 0x0 - u32 mSync; // at 0x4 - volatile AXPBVE mPrevVeSetting; // at 0x8 - bool mFirstVeUpdateFlag; // at 0xC - u16 mVolume; // at 0xE -}; - -class AxVoice { - friend class AxVoiceManager; - -public: - enum AxVoiceCallbackStatus { - CALLBACK_STATUS_CANCEL, - CALLBACK_STATUS_DROP_DSP - }; - - typedef void (*AxVoiceCallback)(AxVoice *pDropVoice, AxVoiceCallbackStatus status, void *pArg); - - enum Format { - FORMAT_ADPCM = 0, - FORMAT_PCM16 = 10, - FORMAT_PCM8 = 25, - }; - - enum VoiceType { - VOICE_TYPE_NORMAL, - VOICE_TYPE_STREAM, - }; - - enum SrcType { - SRC_NONE, - SRC_LINEAR, - SRC_4TAP_8K, - SRC_4TAP_12K, - SRC_4TAP_16K, - SRC_4TAP_AUTO, - }; - - struct MixParam { - u16 vL; // at 0x0 - u16 vR; // at 0x2 - u16 vS; // at 0x4 - u16 vAuxAL; // at 0x6 - u16 vAuxAR; // at 0x8 - u16 vAuxAS; // at 0xA - u16 vAuxBL; // at 0xC - u16 vAuxBR; // at 0xE - u16 vAuxBS; // at 0x10 - u16 vAuxCL; // at 0x12 - u16 vAuxCR; // at 0x14 - u16 vAuxCS; // at 0x16 - }; - - struct RemoteMixParam { - u16 vMain0; // at 0x0 - u16 vAux0; // at 0x2 - u16 vMain1; // at 0x4 - u16 vAux1; // at 0x6 - u16 vMain2; // at 0x8 - u16 vAux2; // at 0xA - u16 vMain3; // at 0xC - u16 vAux3; // at 0xE - }; - -public: - AxVoice(); - ~AxVoice(); - - bool IsRun() const { - return mVpb.IsRun(); - } - void Run() { - mVpb.SetVoiceStateRun(); - } - void Stop() { - mVpb.SetVoiceStateStop(); - } - void Sync() { - mVpb.Sync(); - } - - Format GetFormat() const { - return mFormat; - } - - void SetBaseAddress(const void *pBase) { - mWaveData = pBase; - } - - f32 GetDspRatio(f32 ratio) const { - return (ratio * mSampleRate) / AX_SAMPLE_RATE; - } - - void Setup(const void *pWave, Format fmt, int rate); - - bool IsPlayFinished() const; - void SetLoopStart(const void *pBase, u32 samples); - void SetLoopEnd(const void *pBase, u32 samples); - void SetLoopFlag(bool loop); - void StopAtPoint(const void *pBase, u32 samples); - - bool IsDataAddressCoverd(const void *pBegin, const void *pEnd) const; - u32 GetCurrentPlayingSample() const; - u32 GetCurrentPlayingDspAddress() const; - u32 GetLoopEndDspAddress() const; - - void SetPriority(u32 priority); - void SetVoiceType(VoiceType type); - void EnableRemote(bool enable); - void ResetDelta(); - void SetAddr(bool loop, const void *pWave, u32 offset, u32 loopStart, u32 loopEnd); - void SetSrcType(SrcType type, f32 pitch); - void SetAdpcm(const AdpcmParam *pParam); - void SetAdpcmLoop(const AdpcmLoopParam *pParam); - bool SetMix(const MixParam &rParam); - void SetRmtMix(const RemoteMixParam &rParam); - void SetSrc(f32 ratio, bool initial); - void SetVe(f32 volume, f32 initVolume); - void SetLpf(u16 freq); - void SetRemoteFilter(u8 filter); - - static u32 GetDspAddressBySample(const void *pBase, u32 samples, Format fmt); - static u32 GetSampleByDspAddress(const void *pBase, u32 addr, Format fmt); - static u32 GetSampleByByte(u32 addr, Format fmt); - - static void CalcOffsetAdpcmParam( - u16 *pPredScale, u16 *pYN1, u16 *pYN2, u32 offset, const void *pData, const AdpcmParam &rParam - ); - -private: - static void VoiceCallback(void *pArg); - -private: - AxVoiceParamBlock mVpb; // at 0x0 - const void *mWaveData; // at 0x10 - Format mFormat; // at 0x14 - int mSampleRate; // at 0x18 - bool mFirstMixUpdateFlag; // at 0x1C - bool mReserveForFreeFlag; // at 0x1D - MixParam mMixPrev; // at 0x1E - AxVoiceCallback mCallback; // at 0x38 - void *mCallbackData; // at 0x3C - -public: - NW4R_UT_LIST_NODE_DECL(); // at 0x40 -}; - -NW4R_UT_LIST_TYPEDEF_DECL(AxVoice); - -inline int CalcAxvpbDelta(u16 init, u16 target) { - return (target - init) / AX_SAMPLES_PER_FRAME; -} - -inline u16 CalcMixVolume(f32 volume) { - return ut::Min(0xFFFF, AX_MAX_VOLUME * volume); -} - -} // namespace detail -} // namespace snd -} // namespace nw4r +#include "../ut/LinkList.h" +#if 0 +#include +#include // AXSetVoicePriority +#include +#else +#include #endif + +/******************************************************************************* + * classes and functions + */ + +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2ae88 + class AxVoiceParamBlock + { + // methods + public: + // cdtors + AxVoiceParamBlock(); + + // conversion operators + operator AXVPB *() { return mVpb; } + + // methods + bool IsAvailable() const { return mVpb != nullptr; } + bool IsRun() const + { + return IsAvailable() && mVpb->pb.state == AX_VOICE_RUN; + } + u32 GetLoopAddress() const + { + if (!IsAvailable()) + return 0; + + return (mVpb->pb.addr.loopAddressHi << 16) + + mVpb->pb.addr.loopAddressLo; + } + u32 GetEndAddress() const + { + if (!IsAvailable()) + return 0; + + return (mVpb->pb.addr.endAddressHi << 16) + + mVpb->pb.addr.endAddressLo; + } + u32 GetCurrentAddress() const + { + if (!IsAvailable()) + return 0; + + return (mVpb->pb.addr.currentAddressHi << 16) + + mVpb->pb.addr.currentAddressLo; + } + bool IsLpfEnable() const + { + return IsAvailable() && mVpb->pb.lpf.on == AX_PB_LPF_ON; + } + + bool IsBiquadEnable() const + { + return IsAvailable() && mVpb->pb.biquad.on == AX_PB_BIQUAD_ON; + } + bool IsRmtIirEnable() const; + + void SetVoiceAddr(AXPBADDR const &addr) + { + if (!IsAvailable()) + return; + + // AXSetVoiceAddr doesn't actually modify the object + AXSetVoiceAddr(mVpb, const_cast(&addr)); + } + + void SetVoicePriority(u32 priority) + { + if (!IsAvailable()) + return; + + AXSetVoicePriority(mVpb, priority); + } + void SetVoiceSrcType(u32 type); + void SetVoiceStateRun() + { + if (!IsAvailable()) + return; + + AXSetVoiceState(mVpb, AX_VOICE_RUN); + } + void SetVoiceStateStop() + { + if (!IsRun()) + return; + + AXSetVoiceState(mVpb, AX_VOICE_STOP); + } + void SetVoiceType(u16 type); + void SetVoiceMix(AXPBMIX const &mix, bool immediatelySync); + void SetVoiceVe(u16 volume, u16 initVolume); + void SetVoiceLoop(u16 loop); + void SetVoiceLoopAddr(u32 addr); + void SetVoiceEndAddr(u32 addr); + void SetVoiceAdpcm(AXPBADPCM const &adpcm); + void SetVoiceSrc(AXPBSRC const &src); + void SetVoiceSrcRatio(f32 ratio); + void SetVoiceAdpcmLoop(AXPBADPCMLOOP const &adpcmloop); + void SetVoiceLpf(AXPBLPF const &lpf); + void SetVoiceLpfCoefs(u16 a0, u16 b0); + void SetVoiceBiquad(AXPBBIQUAD const &biquad); + void SetVoiceBiquadCoefs(u16 b0, u16 b1, u16 b2, u16 a1, u16 a2); + void SetVoiceRmtIIR(__AXPBRMTIIR const &iir); + void SetVoiceRmtIIRCoefs(u16 type, ...); + + void Set(AXVPB *vpb); + void Clear(); + + void UpdateDelta(); + void Sync(); + + // static members + public: + static u16 const DEFAULT_VOLUME = AX_MAX_VOLUME; + + // members + private: + AXVPB *mVpb; // size 0x04, offset 0x00 + byte4_t mSync; // size 0x04, offset 0x04 + AXPBVE volatile mPrevVeSetting; // size 0x04, offset 0x08 + bool mFirstVeUpdateFlag; // size 0x01, offset 0x0c + /* 1 byte padding */ + u16 mVolume; // size 0x02, offset 0x0e + }; // size 0x10 + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b45f + class AxVoice + { + // enums + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b187 + enum AxVoiceCallbackStatus + { + CALLBACK_STATUS_CANCEL, + CALLBACK_STATUS_DROP_DSP, + }; + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b23a + enum VoiceType + { + VOICE_TYPE_NORMAL, + VOICE_TYPE_STREAM, + }; + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b3dd + enum SrcType + { + SRC_NONE, + SRC_LINEAR, + SRC_4TAP_8K, + SRC_4TAP_12K, + SRC_4TAP_16K, + SRC_4TAP_AUTO, + }; + + // typedefs + public: + typedef ut::LinkList LinkList; + + typedef void Callback(AxVoice *dropVoice, AxVoiceCallbackStatus status, + void *callbackData); + + // nested types + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2afaa + struct MixParam + { + u16 vL; // size 0x02, offset 0x00 + u16 vR; // size 0x02, offset 0x02 + u16 vS; // size 0x02, offset 0x04 + u16 vAuxAL; // size 0x02, offset 0x06 + u16 vAuxAR; // size 0x02, offset 0x08 + u16 vAuxAS; // size 0x02, offset 0x0a + u16 vAuxBL; // size 0x02, offset 0x0c + u16 vAuxBR; // size 0x02, offset 0x0e + u16 vAuxBS; // size 0x02, offset 0x10 + u16 vAuxCL; // size 0x02, offset 0x12 + u16 vAuxCR; // size 0x02, offset 0x14 + u16 vAuxCS; // size 0x02, offset 0x16 + }; // size 0x18 + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b28a + struct RemoteMixParam + { + u16 vMain0; // size 0x02, offset 0x00 + u16 vAux0; // size 0x02, offset 0x02 + u16 vMain1; // size 0x02, offset 0x04 + u16 vAux1; // size 0x02, offset 0x06 + u16 vMain2; // size 0x02, offset 0x08 + u16 vAux2; // size 0x02, offset 0x0a + u16 vMain3; // size 0x02, offset 0x0c + u16 vAux3; // size 0x02, offset 0x0e + }; // size 0x10 + + // methods + public: + // cdtors + AxVoice(); + ~AxVoice(); + + // methods + void Setup(void const *waveAddr, SampleFormat format, int sampleRate); + + SampleFormat GetFormat() const { return mFormat; } + u32 GetCurrentPlayingSample() const; + u32 GetCurrentPlayingDspAddress() const; + u32 GetLoopEndDspAddress() const; + f32 GetDspRatio(f32 ratio) const + { + return ratio * mSampleRate / AX_SAMPLE_RATE; + } + + void SetLoopStart(void const *baseAddress, u32 samples); + void SetLoopEnd(void const *baseAddress, u32 samples); + void SetLoopFlag(bool loopFlag); + void SetPriority(u32 priority); + void SetVoiceType(VoiceType type); + void ResetDelta(); + void SetAddr(bool loopFlag, void const *waveAddr, u32 startOffset, + u32 loopStart, u32 loopEnd); + void SetSrcType(SrcType type, f32 pitch); + void SetAdpcm(AdpcmParam const *param); + void SetAdpcmLoop(AdpcmLoopParam const *param); + bool SetMix(MixParam const ¶m); + void SetSrc(f32 ratio, bool initialUpdate); + void SetVe(f32 volume, f32 initVolume); + void SetLpf(u16 freq); + void SetBiquad(u8 filterType, f32 value); + void SetRemoteFilter(u8 filter); + + /* NOTE: covered misspelled as coverd */ + bool IsDataAddressCoverd(void const *beginAddress, + void const *endAddress) const; + bool IsNeedNextUpdate(MixParam const ¶m) const; + bool IsPlayFinished() const; + + void Run() { mVpb.SetVoiceStateRun(); } + bool IsRun() const { return mVpb.IsRun(); } + void Stop() { mVpb.SetVoiceStateStop(); } + void StopAtPoint(void const *baseAddress, u32 samples); + void Sync() { mVpb.Sync(); } + + static u32 GetDspAddressBySample(void const *baseAddress, u32 samples, + SampleFormat format); + static u32 GetSampleByDspAddress(void const *baseAddress, u32 addr, + SampleFormat format); + + static u16 GetAxFormatFromSampleFormat(SampleFormat sampleFormat); + + static void CalcOffsetAdpcmParam(u16 *outPredScale, u16 *outYn1, + u16 *outYn2, u32 offset, + void *dataAddr, + AdpcmParam const &adpcmParam); + + private: + // callbacks + static void VoiceCallback(void *callbackData); + + // static members + public: + static u16 const VOICE_GAIN_MAX; + + // methods + private: + AxVoiceParamBlock mVpb; // size 0x10, offset 0x00 + void const *mWaveData; // size 0x04, offset 0x10 + SampleFormat mFormat; // size 0x04, offset 0x14 + int mSampleRate; // size 0x04, offset 0x18 + bool mFirstMixUpdateFlag; // size 0x01, offset 0x1c + bool mReserveForFreeFlag; // size 0x01, offset 0x1d + /* 2 bytes padding */ + MixParam mMixPrev; // size 0x18, offset 0x20 + Callback *mCallback; // size 0x04, offset 0x38 + void *mCallbackData; // size 0x04, offset 0x3c + public: + ut::LinkListNode node; // size 0x08, offset 0x40 + + // friends + private: + friend class AxVoiceManager; + }; // size 0x48 +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_AX_VOICE_H diff --git a/include/nw4r/snd/snd_AxVoiceManager.h b/include/nw4r/snd/snd_AxVoiceManager.h index c9ab081b..d0cba1de 100644 --- a/include/nw4r/snd/snd_AxVoiceManager.h +++ b/include/nw4r/snd/snd_AxVoiceManager.h @@ -1,49 +1,62 @@ #ifndef NW4R_SND_AX_VOICE_MANAGER_H #define NW4R_SND_AX_VOICE_MANAGER_H -#include "nw4r/snd/snd_AxVoice.h" -#include "nw4r/types_nw4r.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { -namespace detail { +#include // u32 -class AxVoiceManager { -public: - static const int VOICE_MARGIN = 16; - static const int VOICE_MAX = AX_VOICE_MAX + VOICE_MARGIN; - static const int WORK_SIZE_MAX = VOICE_MAX * sizeof(AxVoice); +#include "AxVoice.h" -public: - static AxVoiceManager &GetInstance(); +/******************************************************************************* + * classes and functions + */ - u32 GetRequiredMemSize(); - void Setup(void *pBuffer, u32 size); - void Shutdown(); +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2adb3b + class AxVoiceManager + { + // methods + public: + // instance accessors + static AxVoiceManager &GetInstance(); - AxVoice *AcquireAxVoice(u32 priority, AxVoice::AxVoiceCallback pCallback, void *pArg); - void FreeAxVoice(AxVoice *pVoice); + // methods + void Setup(void *mem, u32 memSize); + void Shutdown(); - void ReserveForFreeAxVoice(AxVoice *pVoice); - void FreeAllReservedAxVoice(); + u32 GetRequiredMemSize(int axVoiceCount); -private: - AxVoiceManager(); + AxVoice *AcquireAxVoice(u32 priority, AxVoice::Callback *callback, + void *callbackData); + void ReserveForFreeAxVoice(AxVoice *voice); + void FreeAxVoice(AxVoice *voice); + void FreeAllReservedAxVoice(); - AxVoice *Alloc(); - void Free(AxVoice *pVoice); - void ReserveForFree(AxVoice *pVoice); + private: + // cdtors + AxVoiceManager(); -private: - AxVoiceList mActiveVoiceList; // at 0x0 - AxVoiceList mFreeVoiceList; // at 0xC - AxVoiceList mFreeReservedVoiceList; // at 0x18 - bool mInitialized; // at 0x24 - int mVoiceCount; // at 0x28 -}; + // methods + AxVoice *Alloc(); + void ReserveForFree(AxVoice *voice); + void Free(AxVoice *voice); -} // namespace detail -} // namespace snd -} // namespace nw4r + // static members + public: + static int const VOICE_COUNT_MARGIN = 16; -#endif + // members + private: + AxVoice::LinkList mActiveVoiceList; // size 0x0c, offset 0x00 + AxVoice::LinkList mFreeVoiceList; // size 0x0c, offset 0x0c + AxVoice::LinkList mFreeReservedVoiceList; // size 0x0c, offset 0x18 + bool mInitialized; // size 0x01, offset 0x24 + /* 3 bytes padding */ + int mVoiceCount; // size 0x04, offset 0x28 + }; // size 0x2c +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_AX_VOICE_MANAGER_H diff --git a/include/nw4r/snd/snd_Bank.h b/include/nw4r/snd/snd_Bank.h index 4b84c4f0..1099134a 100644 --- a/include/nw4r/snd/snd_Bank.h +++ b/include/nw4r/snd/snd_Bank.h @@ -1,35 +1,53 @@ #ifndef NW4R_SND_BANK_H #define NW4R_SND_BANK_H -#include "nw4r/snd/snd_BankFile.h" -#include "nw4r/snd/snd_NoteOnCallback.h" -#include "nw4r/types_nw4r.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { -namespace detail { +#include "BankFile.h" // BankFileReader -// Forward declarations -class Channel; +#include "nw4r/NW4RAssert.hpp" -class Bank { -public: - explicit Bank(const void *pBankBin); - ~Bank(); +/******************************************************************************* + * types + */ - Channel *NoteOn(const NoteOnInfo &rInfo) const; +// forward declarations +namespace nw4r { namespace snd { namespace detail { class Channel; }}} +namespace nw4r { namespace snd { namespace detail { struct NoteOnInfo; }}} - void SetWaveDataAddress(const void *pData) { - mWaveDataAddress = pData; - } +/******************************************************************************* + * classes and functions + */ -private: - BankFileReader mBankReader; // at 0x0 - const void *mWaveDataAddress; // at 0xC -}; +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b0af4 + class Bank + { + // methods + public: + // cdtors + Bank(void const *bankData); + ~Bank(); -} // namespace detail -} // namespace snd -} // namespace nw4r + // methods + void SetWaveDataAddress(void const *waveData) + { + // specifically not the header variant + NW4RAssertPointerNonnull_Line(50, waveData); -#endif + mWaveDataAddress = waveData; + } + + Channel *NoteOn(NoteOnInfo const ¬eOnInfo) const; + + // members + private: + BankFileReader mBankReader; // size 0x0c, offset 0x00 + void const *mWaveDataAddress; // size 0x04, offset 0x0c + }; // size 0x10 +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_BANK_H diff --git a/include/nw4r/snd/snd_BankFile.h b/include/nw4r/snd/snd_BankFile.h index ccca3669..af565729 100644 --- a/include/nw4r/snd/snd_BankFile.h +++ b/include/nw4r/snd/snd_BankFile.h @@ -1,104 +1,217 @@ #ifndef NW4R_SND_BANK_FILE_H #define NW4R_SND_BANK_FILE_H -#include "nw4r/snd/snd_Util.h" -#include "nw4r/snd/snd_WaveFile.h" -#include "nw4r/types_nw4r.h" -#include "nw4r/ut.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { -namespace detail { -namespace BankFile { +#include +#include -struct InstParam { - s32 waveIndex; // at 0x0 - u8 attack; // at 0x4 - u8 decay; // at 0x5 - u8 sustain; // at 0x6 - u8 release; // at 0x7 - char UNK_0x8[0x4]; - u8 originalKey; // at 0xC - u8 volume; // at 0xD - u8 pan; // at 0xE - f32 tune; // at 0x10 -}; +#include "WaveFile.h" -struct RangeTable { - u8 tableSize; // at 0x0 - u8 key[]; // at 0x1 -}; +#include "Util.h" -struct IndexTable { - u8 min; // at 0x0 - u8 max; // at 0x1 - u16 reserved; // at 0x2 - u8 ref[]; // at 0x4 -}; +#include "../ut/binaryFileFormat.h" -typedef Util::DataRef DataRegion; -typedef Util::DataRef WaveRegion; +/******************************************************************************* + * types + */ -struct Header { - ut::BinaryFileHeader fileHeader; // at 0x0 - u32 dataBlockOffset; // at 0x10 - u32 dataBlockSize; // at 0x14 - u32 waveBlockOffset; // at 0x18 - u32 waveBlockSize; // at 0x1C -}; +namespace nw4r { namespace snd { namespace detail +{ + struct BankFile + { + /* Header */ -struct DataBlock { - ut::BinaryBlockHeader blockHeader; // at 0x0 - Util::Table instTable; // at 0x8 -}; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2afe21 + struct Header + { + ut::BinaryFileHeader fileHeader; // size 0x10, offset 0x00 + u32 dataBlockOffset; // size 0x04, offset 0x10 + u32 dataBlockSize; // size 0x04, offset 0x14 + u32 waveBlockOffset; // size 0x04, offset 0x18 + u32 waveBlockSize; // size 0x04, offset 0x1c + }; // size 0x20 -struct WaveBlock { - ut::BinaryBlockHeader blockHeader; // at 0x0 - Util::Table waveInfoTable; // at 0x8 -}; + /* DataBlock */ -} // namespace BankFile + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b0666 + struct InstParam + { + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b04df + union + { + s32 waveIndex; // size 0x04 + WaveInfo const *waveInfoAddress; // size 0x04 + WaveDataLocationCallback *waveDataLocationCallback; // size 0x04 + }; // size 0x04, offset 0x00 + u8 attack; // size 0x01, offset 0x04 + u8 decay; // size 0x01, offset 0x05 + u8 sustain; // size 0x01, offset 0x06 + u8 release; // size 0x01, offset 0x07 + u8 hold; // size 0x01, offset 0x08 + u8 waveDataLocationType; // size 0x01, offset 0x09 + u8 noteOffType; // size 0x01, offset 0x0a + u8 alternateAssign; // size 0x01, offset 0x0b + u8 originalKey; // size 0x01, offset 0x0c + u8 volume; // size 0x01, offset 0x0d + u8 pan; // size 0x01, offset 0x0e + byte1_t padding2; // 2? + f32 tune; // size 0x04, offset 0x10 + // TODO: template parameters + Util::DataRef lfoTableRef; // size 0x08, offset 0x14 + Util::DataRef graphEnvTablevRef; // size 0x08, offset 0x1c + Util::DataRef randomizerTableRef; // size 0x08, offset 0x24 + byte4_t reserved; + }; // size 0x30 -struct InstInfo { - s32 waveIndex; // at 0x0 - u8 attack; // at 0x4 - u8 decay; // at 0x5 - u8 sustain; // at 0x6 - u8 release; // at 0x7 - u8 originalKey; // at 0x8 - u8 pan; // at 0x9 - u8 volume; // at 0xA - f32 tune; // at 0xC -}; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b2114 + struct RangeTable + { + u8 tableSize; // size 0x01, offset 0x00 + u8 key[]; // flexible, offset 0x01 (unit size 0x01) + }; // size 0x01 -inline u8 ReadByte(const void *pAddr) { - return *static_cast(pAddr); -} + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b203e + struct IndexTable + { + u8 min; // size 0x01, offset 0x00 + u8 max; // size 0x01, offset 0x01 + byte2_t reserved; + byte_t ref[]; // flexible, offset 0x04 (unit size 0x01) + }; // size 0x04 -class BankFileReader { -public: - static const u32 SIGNATURE = 'RBNK'; - static const int VERSION = NW4R_VERSION(1, 1); + typedef Util::DataRef + DataRegion; -public: - explicit BankFileReader(const void *pBankBin); + static byte4_t const SIGNATURE_DATA_BLOCK = + NW4R_FOUR_BYTE('D', 'A', 'T', 'A'); - bool IsValidFileHeader(const void *pBankBin); + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b006b + struct DataBlock + { + ut::BinaryBlockHeader blockHeader; // size 0x08, offset 0x00 + Util::Table instTable; // size 0x0c, offset 0x08 + }; // size 0x14 - bool ReadInstInfo(InstInfo *pInfo, int prgNo, int key, int velocity) const; - bool ReadWaveParam(WaveData *pData, int waveIndex, const void *pWaveAddr) const; + /* WaveBlock */ -private: - const BankFile::DataRegion *GetReferenceToSubRegion(const BankFile::DataRegion *pRef, int splitKey) const; + static byte4_t const SIGNATURE_WAVE_BLOCK = + NW4R_FOUR_BYTE('W', 'A', 'V', 'E'); + typedef Util::DataRef WaveRegion; -private: - const BankFile::Header *mHeader; // at 0x0 - const BankFile::DataBlock *mDataBlock; // at 0x4 - const BankFile::WaveBlock *mWaveBlock; // at 0x8 -}; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b022c + struct WaveBlock + { + ut::BinaryBlockHeader blockHeader; // size 0x08, offset 0x00 + Util::Table waveInfoTable; // size 0x0c, offset 0x08 + }; // size 0x14 -} // namespace detail -} // namespace snd -} // namespace nw4r + /* BankFile */ -#endif + static byte4_t const SIGNATURE_FILE = + NW4R_FOUR_BYTE('R', 'B', 'N', 'K'); + static int const FILE_VERSION = NW4R_FILE_VERSION(1, 2); + }; // "namespace" BankFile + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b0cdd + struct InstInfo + { + // enums + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b0c84 + enum NoteOffType + { + NOTE_OFF_TYPE_RELEASE, + NOTE_OFF_TYPE_IGNORE, + }; + + // nested types + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b03e4 + struct WaveDataLocation + { + // enums + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b02aa + enum WaveDataLocationType + { + WAVE_DATA_LOCATION_INDEX, + WAVE_DATA_LOCATION_ADDRESS, + WAVE_DATA_LOCATION_CALLBACK, + }; + + // members + public: + WaveDataLocationType type; // size 0x04, offset 0x00 + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b0335 + union + { + s32 index; // size 0x04 + WaveInfo const *address; // size 0x04 + WaveDataLocationCallback *callback; // size 0x04 + }; // size 0x04, offset 0x04 + }; // size 0x08 + + // members + public: + WaveDataLocation waveDataLocation; // size 0x08, offset 0x00 + u8 attack; // size 0x01, offset 0x08 + u8 hold; // size 0x01, offset 0x09 + u8 decay; // size 0x01, offset 0x0a + u8 sustain; // size 0x01, offset 0x0b + u8 release; // size 0x01, offset 0x0c + /* 3 bytes padding */ + NoteOffType noteOffType; // size 0x04, offset 0x10 + u8 alternateAssign; // size 0x01, offset 0x14 + u8 originalKey; // size 0x01, offset 0x15 + u8 pan; // size 0x01, offset 0x16 + u8 volume; // size 0x01, offset 0x17 + f32 tune; // size 0x04, offset 0x18 + }; // size 0x1c +}}} // namespace nw4r::snd::detail + +/******************************************************************************* + * classes and functions + */ + +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b0a17 + class BankFileReader + { + // methods + public: + // cdtors + BankFileReader(void const *bankData); + + // methods + bool ReadInstInfo(InstInfo *instInfo, int prgNo, int key, + int velocity) const; + bool ReadWaveInfo(WaveInfo *waveParam, + InstInfo::WaveDataLocation const &waveDataLocation, + void const *waveDataAddress, + WaveInfo const **waveInfoAddress) const; + + private: + BankFile::InstParam const *GetInstParam(int prgNo, int key, + int velocity) const; + BankFile::DataRegion const *GetReferenceToSubRegion( + BankFile::DataRegion const *ref, int splitKey) const; + + static bool IsValidFileHeader(void const *bankData); + + // static members + public: + static int const SUPPORTED_FILE_VERSION = NW4R_FILE_VERSION(1, 2); + + // members + private: + BankFile::Header const *mHeader; // size 0x04, offset 0x00 + BankFile::DataBlock const *mDataBlock; // size 0x04, offset 0x04 + BankFile::WaveBlock const *mWaveBlock; // size 0x04, offset 0x08 + }; // size 0x0c +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_BANK_FILE_H diff --git a/include/nw4r/snd/snd_BasicPlayer.h b/include/nw4r/snd/snd_BasicPlayer.h index 39ff10bd..e22fbe30 100644 --- a/include/nw4r/snd/snd_BasicPlayer.h +++ b/include/nw4r/snd/snd_BasicPlayer.h @@ -1,145 +1,126 @@ #ifndef NW4R_SND_BASIC_PLAYER_H #define NW4R_SND_BASIC_PLAYER_H -#include "nw4r/snd/snd_Common.h" -#include "nw4r/types_nw4r.h" -#include "nw4r/ut.h" // IWYU pragma: export -#include "rvl/WPAD.h" // IWYU pragma: export -namespace nw4r { -namespace snd { -namespace detail { +/******************************************************************************* + * headers + */ -class BasicPlayer { -public: - BasicPlayer(); - virtual ~BasicPlayer() {} // at 0x8 +#include - virtual bool Start() = 0; // at 0xC - virtual void Stop() = 0; // at 0x10 - virtual void Pause(bool flag) = 0; // at 0x14 - virtual bool IsActive() const = 0; // at 0x18 - virtual bool IsStarted() const = 0; // at 0x1C - virtual bool IsPause() const = 0; // at 0x20 +#include "global.h" - void InitParam(); +/******************************************************************************* + * types + */ - u32 GetId() const { - return mId; - } - void SetId(u32 id) { - mId = id; - } +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x27db8 + // NOTE: different from ketteiban: no remote fields + struct PlayerParamSet + { + // methods + public: + // cdtors + PlayerParamSet() { Init(); } - f32 GetVolume() const { - return mVolume; - } - void SetVolume(f32 volume) { - mVolume = volume; - } + // methods + void Init(); - f32 GetPitch() const { - return mPitch; - } - void SetPitch(f32 pitch) { - mPitch = pitch; - } + // members + public: + f32 volume; // size 0x04, offset 0x00 + f32 pitch; // size 0x04, offset 0x04 + f32 pan; // size 0x04, offset 0x08 + f32 surroundPan; // size 0x04, offset 0x0c + f32 lpfFreq; // size 0x04, offset 0x10 + f32 biquadValue; // size 0x04, offset 0x14 + u8 biquadType; // size 0x01, offset 0x18 + u8 remoteFilter; // size 0x01, offset 0x19 + /* 2 bytes padding */ + int outputLineFlag; // size 0x04, offset 0x1c + f32 mainOutVolume; // size 0x04, offset 0x20 + f32 mainSend; // size 0x04, offset 0x24 + PanMode panMode; // size 0x04, offset 0x28 + PanCurve panCurve; // size 0x04, offset 0x2c + f32 fxSend[AUX_BUS_NUM]; // size 0x0c, offset 0x30 + VoiceOutParam voiceOutParam[4]; // size 0x60, offset 0x3c + }; // size 0x9c +}}} // namespace nw4r::snd::detail - f32 GetPan() const { - return mPan; - } - void SetPan(f32 pan) { - mPan = pan; - } +/******************************************************************************* + * classes and functions + */ - f32 GetSurroundPan() const { - return mSurroundPan; - } - void SetSurroundPan(f32 pan) { - mSurroundPan = pan; - } +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x280d8 + // NOTE: different from ketteiban due to PlayerParamSet being different + class BasicPlayer + { + // methods + public: + // cdtors + BasicPlayer(); + virtual ~BasicPlayer() {} - f32 GetLpfFreq() const { - return mLpfFreq; - } - void SetLpfFreq(f32 freq) { - mLpfFreq = freq; - } + // virtual function ordering + // vtable BasicPlayer + virtual bool Start() = 0; + virtual void Stop() = 0; + virtual void Pause(bool flag) = 0; + virtual bool IsActive() const = 0; + virtual bool IsStarted() const = 0; + virtual bool IsPause() const = 0; - int GetOutputLine() const { - return mOutputLine; - } - void SetOutputLine(int flags) { - mOutputLine = flags; - } + // methods + void InitParam(); - f32 GetMainOutVolume() const { - return mMainOutVolume; - } - void SetMainOutVolume(f32 volume) { - mMainOutVolume = volume; - } + f32 GetVolume() const { return mPlayerParamSet.volume; } + f32 GetPitch() const { return mPlayerParamSet.pitch; } + f32 GetPan() const { return mPlayerParamSet.pan; } + f32 GetSurroundPan() const { return mPlayerParamSet.surroundPan; } + f32 GetLpfFreq() const { return mPlayerParamSet.lpfFreq; } + f32 GetBiquadValue() const { return mPlayerParamSet.biquadValue; } + int GetBiquadType() const { return mPlayerParamSet.biquadType; } + int GetRemoteFilter() const { return mPlayerParamSet.remoteFilter; } + int GetOutputLine() const { return mPlayerParamSet.outputLineFlag; } + f32 GetMainOutVolume() const { return mPlayerParamSet.mainOutVolume; } + f32 GetMainSend() const { return mPlayerParamSet.mainSend; } + PanMode GetPanMode() const { return mPlayerParamSet.panMode; } + PanCurve GetPanCurve() const { return mPlayerParamSet.panCurve; } + f32 GetFxSend(AuxBus bus) const; + VoiceOutParam const &GetVoiceOutParam(int index) const + { + return mPlayerParamSet.voiceOutParam[index]; + } + u32 GetId() const { return mId; } - f32 GetMainSend() const { - return mMainSend; - } - void SetMainSend(f32 send) { - mMainSend = send; - } + void SetVolume(f32 volume) { mPlayerParamSet.volume = volume; } + void SetPitch(f32 pitch) { mPlayerParamSet.pitch = pitch; } + void SetPan(f32 pan) { mPlayerParamSet.pan = pan; } + void SetSurroundPan(f32 surroundPan) { mPlayerParamSet.surroundPan = surroundPan; } + void SetLpfFreq(f32 lpfFreq) { mPlayerParamSet.lpfFreq = lpfFreq; } + void SetBiquadFilter(int type, f32 value); + void SetRemoteFilter(int filter); + void SetOutputLine(int lineFlag) { mPlayerParamSet.outputLineFlag = lineFlag; } + void SetMainOutVolume(f32 volume) { mPlayerParamSet.mainOutVolume = volume; } + void SetMainSend(f32 send) { mPlayerParamSet.mainSend = send; } + void SetPanMode(PanMode panMode) { mPlayerParamSet.panMode = panMode; } + void SetPanCurve(PanCurve panCurve) { mPlayerParamSet.panCurve = panCurve; } + void SetFxSend(AuxBus bus, f32 send); + void SetVoiceOutParam(int index, VoiceOutParam const ¶m) + { + mPlayerParamSet.voiceOutParam[index] = param; + } + void SetId(u32 id) { mId = id; } - void SetFxSend(AuxBus bus, f32 send); - f32 GetFxSend(AuxBus bus) const; + // members + private: + /* vtable */ // size 0x04, offset 0x00 + PlayerParamSet mPlayerParamSet; // size 0x9c, offset 0x04 + u32 mId; // size 0x04, offset 0xa0 + }; // size 0xa4 +}}} // namespace nw4r::snd::detail - void SetRemoteOutVolume(int remote, f32 volume); - f32 GetRemoteOutVolume(int remote) const; - - f32 GetRemoteSend(int remote) const; - f32 GetRemoteFxSend(int remote) const; - - int GetRemoteFilter() const { - return mRemoteFilter; - } - void SetRemoteFilter(int filter) { - mRemoteFilter = ut::Clamp(filter, 0, REMOTE_FILTER_MAX); - } - - PanMode GetPanMode() const { - return mPanMode; - } - void SetPanMode(PanMode mode) { - mPanMode = mode; - } - - PanCurve GetPanCurve() const { - return mPanCurve; - } - void SetPanCurve(PanCurve curve) { - mPanCurve = curve; - } - -private: - u32 mId; // at 0x4 - - f32 mVolume; // at 0x8 - f32 mPitch; // at 0xC - f32 mPan; // at 0x10 - f32 mSurroundPan; // at 0x14 - f32 mLpfFreq; // at 0x18 - char UNK_0x1C[0x4]; - - int mOutputLine; // at 0x20 - f32 mMainOutVolume; // at 0x24 - f32 mMainSend; // at 0x28 - f32 mFxSend[AUX_BUS_NUM]; // at 0x2C - f32 mRemoteOutVolume[WPAD_MAX_CONTROLLERS]; // at 0x38 - f32 mRemoteSend[WPAD_MAX_CONTROLLERS]; // at 0x48 - f32 mRemoteFxSend[WPAD_MAX_CONTROLLERS]; // at 0x58 - u8 mRemoteFilter; // at 0x68 - PanMode mPanMode; // at 0x6C - PanCurve mPanCurve; // at 0x70 -}; - -} // namespace detail -} // namespace snd -} // namespace nw4r - -#endif +#endif // NW4R_SND_BASIC_PLAYER_H diff --git a/include/nw4r/snd/snd_BasicSound.h b/include/nw4r/snd/snd_BasicSound.h index 5115f0a8..7a043893 100644 --- a/include/nw4r/snd/snd_BasicSound.h +++ b/include/nw4r/snd/snd_BasicSound.h @@ -1,231 +1,346 @@ #ifndef NW4R_SND_BASIC_SOUND_H #define NW4R_SND_BASIC_SOUND_H -#include "nw4r/snd/snd_Common.h" -#include "nw4r/snd/snd_MoveValue.h" -#include "nw4r/types_nw4r.h" -#include "nw4r/ut.h" // IWYU pragma: export -#include "rvl/WPAD.h" // IWYU pragma: export -namespace nw4r { -namespace snd { -namespace detail { +/******************************************************************************* + * headers + */ -class BasicSound { - friend class SoundHandle; +#include -public: - NW4R_UT_RTTI_DECL(BasicSound); +#include "global.h" +#include "MoveValue.h" - struct AmbientParamUpdateCallback { - enum ParamUpdateFlags { - PARAM_UPDATE_VOLUME = (1 << 0), - PARAM_UPDATE_PAN = (1 << 1), - PARAM_UPDATE_SURROUND_PAN = (1 << 2), - PARAM_UPDATE_PRIORITY = (1 << 3), - }; +#include "../ut/inlines.h" // ut::Clamp +#include "../ut/LinkList.h" - virtual void detail_Update( - SoundParam *pParam, u32 id, BasicSound *pSound, const void *pArg, - u32 flags - ) = 0; // at 0xC - }; +/******************************************************************************* + * types + */ - struct AmbientArgUpdateCallback { - virtual void detail_Update(void *pArg, - const BasicSound *pSound) = 0; // at 0xC - }; +// forward declarations +namespace nw4r { namespace snd { namespace detail { class BasicPlayer; }}} +namespace nw4r { namespace snd { namespace detail { class ExternalSoundPlayer; }}} +namespace nw4r { namespace snd { namespace detail { class PlayerHeap; }}} +namespace nw4r { namespace snd { class SoundActor; }} +namespace nw4r { namespace snd { class SoundHandle; }} +namespace nw4r { namespace snd { class SoundPlayer; }} - struct AmbientArgAllocaterCallback { - virtual void *detail_AllocAmbientArg(u32 size) = 0; // at 0xC +namespace nw4r { namespace ut { namespace detail { class RuntimeTypeInfo; }}} - virtual void detail_FreeAmbientArg(void *pArg, - const BasicSound *pSound) = 0; // at 0x10 - }; +namespace nw4r { namespace snd +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x27a05 + struct SoundParam + { + // methods + public: + // cdtors + SoundParam() : + volume (1.0f), + pitch (1.0f), + pan (0.0f), + surroundPan (0.0f), + fxSend (0.0f), + lpf (0.0f), + biquadFilterValue (0.0f), + biquadFilterType (0), + priority (0) + { + } - struct AmbientArgInfo { - AmbientParamUpdateCallback *paramUpdateCallback; // at 0x0 - AmbientArgUpdateCallback *argUpdateCallback; // at 0x4 - AmbientArgAllocaterCallback *argAllocaterCallback; // at 0x8 - void *arg; // at 0xC - u32 argSize; // at 0x10 - }; + // members + public: + f32 volume; // size 0x04, offset 0x00 + f32 pitch; // size 0x04, offset 0x04 + f32 pan; // size 0x04, offset 0x08 + f32 surroundPan; // size 0x04, offset 0x0c + f32 fxSend; // size 0x04, offset 0x10 + f32 lpf; // size 0x04, offset 0x14 + f32 biquadFilterValue; // size 0x04, offset 0x18 + int biquadFilterType; // size 0x04, offset 0x1c + int priority; // size 0x04, offset 0x20 + }; // size 0x24 - static const u32 INVALID_ID = 0xFFFFFFFF; - static const int PRIORITY_MAX = 127; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x27514 + struct SoundAmbientParam + { + // methods + public: + // cdtors + SoundAmbientParam() : // where is my nsdmi !!! + volume (1.0f), + pitch (1.0f), + pan (0.0f), + surroundPan (0.0f), + fxSend (0.0f), + lpf (0.0f), + biquadFilterValue (0.0f), + biquadFilterType (0), + priority (0) + { + } -public: - BasicSound(); - virtual ~BasicSound() {} // at 0xC + // members + public: + f32 volume; // size 0x04, offset 0x00 + f32 pitch; // size 0x04, offset 0x04 + f32 pan; // size 0x04, offset 0x08 + f32 surroundPan; // size 0x04, offset 0x0c + f32 fxSend; // size 0x04, offset 0x10 + f32 lpf; // size 0x04, offset 0x14 + f32 biquadFilterValue; // size 0x04, offset 0x18 + int biquadFilterType; // size 0x04, offset 0x1c + int priority; // size 0x04, offset 0x20 + VoiceOutParam voiceOutParam[4]; // size 0x60, offset 0x24 + }; // size 0x84 - virtual void Update(); // at 0x10 - virtual void StartPrepared(); // at 0x14 - virtual void Stop(int frames); // at 0x18 - virtual void Pause(bool flag, int frames); // at 0x1C - virtual void SetAutoStopCounter(int count); // at 0x20 - virtual void FadeIn(int frames); // at 0x24 - virtual void Shutdown(); // at 0x28 - virtual bool IsPrepared() const = 0; // at 0x2C - virtual bool IsPause() const; // at 0x30 + namespace detail + { + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x270f1 + struct SoundActorParam + { + // methods + public: + // cdtors + SoundActorParam() : + volume (1.0f), + pitch (1.0f), + pan (0.0f) + { + } - virtual void SetInitialVolume(f32 vol); // at 0x34 - virtual void SetVolume(f32 vol, int frames); // at 0x38 - virtual void SetPitch(f32 pitch); // at 0x3C - virtual void SetPan(f32 pan); // at 0x40 - virtual void SetSurroundPan(f32 pan); // at 0x44 - virtual void SetLpfFreq(f32 freq); // at 0x48 - virtual void SetPlayerPriority(int priority); // at 0x4C - virtual void SetRemoteFilter(int filter); // at 0x50 - virtual void SetPanMode(PanMode mode); // at 0x54 - virtual void SetPanCurve(PanCurve curve); // at 0x58 + // members + public: + f32 volume; // size 0x04, offset 0x00 + f32 pitch; // size 0x04, offset 0x04 + f32 pan; // size 0x04, offset 0x08 + }; // size 0x0c + } // namespace detail +}} // namespace nw4r::snd - virtual bool IsAttachedTempSpecialHandle() = 0; // at 0x5C - virtual void DetachTempSpecialHandle() = 0; // at 0x60 +/******************************************************************************* + * classes and functions + */ - virtual void InitParam(); // at 0x64 - virtual BasicPlayer &GetBasicPlayer() = 0; // at 0x68 - virtual const BasicPlayer &GetBasicPlayer() const = 0; // at 0x6C +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x28177 + // NOTE: different from ketteiban: no remote fields. something else instead + class BasicSound + { + // enums + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x27c51 + enum PauseState + { + PAUSE_STATE_NORMAL, + PAUSE_STATE_PAUSING, + PAUSE_STATE_PAUSED, + PAUSE_STATE_UNPAUSING, + }; - PlayerHeap *GetPlayerHeap() { - return mHeap; - } - void SetPlayerHeap(PlayerHeap *pHeap) { - mHeap = pHeap; - } + // typedefs + public: + typedef ut::LinkList PriorityLinkList; + typedef ut::LinkList SoundPlayerPlayLinkList; + typedef ut::LinkList SoundPlayerPriorityLinkList; + typedef ut::LinkList ExtSoundPlayerPlayLinkList; - bool IsAttachedGeneralHandle(); - void DetachGeneralHandle(); + // nested types + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x277e5 + struct AmbientInfo + { + // nested types + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x276d8 + class AmbientParamUpdateCallback + { + // methods + public: + virtual void at_0x08(); + virtual void at_0x0c(void *, int, int, void *); + virtual int at_0x10(void *, int); + virtual int at_0x14(void *, int); - bool IsAttachedTempGeneralHandle(); - void DetachTempGeneralHandle(); + // members + private: + /* vtable */ // size 0x04, offset 0x00 + }; // size 0x04 - SoundPlayer *GetSoundPlayer() { - return mSoundPlayer; - } - void SetSoundPlayer(SoundPlayer *pPlayer) { - mSoundPlayer = pPlayer; - } + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x27732 + class AmbientArgUpdateCallback + { + // methods + public: + // virtual function ordering + // vtable AmbientArgUpdateCallback + virtual void at_0x08(); + virtual void at_0x0c(void *, void *); - ExternalSoundPlayer *GetExternalSoundPlayer() { - return mExtSoundPlayer; - } - void SetExternalSoundPlayer(ExternalSoundPlayer *pExtPlayer) { - mExtSoundPlayer = pExtPlayer; - } + // members + private: + /* vtable */ // size 0x04, offset 0x00 + }; // size 0x04 - AmbientParamUpdateCallback *GetAmbientParamUpdateCallback() { - return mAmbientParamUpdateCallback; - } + // NOTE: "Allocator" is misspelled as "Allocater" + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2778a + class AmbientArgAllocaterCallback + { + // methods + public: + // virtual function ordering + // vtable AmbientArgAllocaterCallback + virtual void at_0x08(); + virtual void *at_0x0c(int); + virtual void at_0x10(void *, void *); - AmbientArgUpdateCallback *GetAmbientArgUpdateCallback() { - return mAmbientArgUpdateCallback; - } - void ClearAmbientArgUpdateCallback() { - mAmbientArgUpdateCallback = NULL; - } + // members + private: + /* vtable */ // size 0x04, offset 0x00 + }; // size 0x04 - AmbientArgAllocaterCallback *GetAmbientArgAllocaterCallback() { - return mAmbientArgAllocaterCallback; - } + // members + public: + AmbientParamUpdateCallback *paramUpdateCallback; // size 0x04, offset 0x00 + AmbientArgUpdateCallback *argUpdateCallback; // size 0x04, offset 0x04 + AmbientArgAllocaterCallback *argAllocaterCallback; // size 0x04, offset 0x08 + void *arg; // size 0x04, offset 0x0c + u32 argSize; // size 0x04, offset 0x10 + }; // size 0x14 - void *GetAmbientArg() { - return mAmbientArg; - } + // methods + public: + // cdtors + BasicSound(int priority, int ambientPriority); + /* virtual ~BasicSound() {} */ // virtual function ordering - SoundParam &GetAmbientParam() { - return mAmbientParam; - } + // virtual function ordering + // vtable BasicSound + virtual ut::detail::RuntimeTypeInfo const *GetRuntimeTypeInfo() const + { + return &typeInfo; + } + virtual ~BasicSound() {} + virtual void Shutdown(); + virtual bool IsPrepared() const = 0; + virtual bool IsAttachedTempSpecialHandle() = 0; + virtual void DetachTempSpecialHandle() = 0; + virtual void InitParam(); + virtual BasicPlayer &GetBasicPlayer() = 0; + virtual BasicPlayer const &GetBasicPlayer() const = 0; + virtual void OnUpdatePlayerPriority() {} + virtual void UpdateMoveValue(); + virtual void UpdateParam(); - void SetAmbientParamCallback( - AmbientParamUpdateCallback *pParamUpdate, AmbientArgUpdateCallback *pArgUpdate, - AmbientArgAllocaterCallback *pArgAlloc, void *pArg - ); + // methods + void StartPrepared(); + void Update(); + void Pause(bool flag, int fadeFrames); + bool IsPaused() const; + void Stop(int fadeFrames); + void SetAutoStopCounter(int count); - void SetPriority(int priority) { - mPriority = priority; - } + u32 GetId() const { return mId; } + PlayerHeap *GetPlayerHeap() { return mPlayerHeap; } + SoundPlayer *GetSoundPlayer() { return mSoundPlayer; } + int GetVoiceOutCount() const; - u32 GetId() const { - return mId; - } - void SetId(u32 id); + void SetPlayerPriority(int priority); + void SetInitialVolume(f32 volume); + void SetVolume(f32 volume, int frames); + void SetPitch(f32 pitch); + void SetFxSend(AuxBus bus, f32 send); + void SetRemoteFilter(int filter); + void SetPanMode(PanMode mode); + void SetPanCurve(PanCurve curve); + void SetAmbientInfo(AmbientInfo const &ambientArgInfo); + void SetId(u32 id); - f32 GetMoveVolume() { - return mExtMoveVolume.GetValue(); - } + void AttachPlayerHeap(PlayerHeap *heap); + void AttachSoundPlayer(SoundPlayer *player); + void AttachSoundActor(SoundActor *actor); + void AttachExternalSoundPlayer(ExternalSoundPlayer *extPlayer); - f32 GetInitialVolume() const; - f32 GetPan() const; - f32 GetSurroundPan() const; - f32 GetPitch() const; + bool IsAttachedGeneralHandle(); + bool IsAttachedTempGeneralHandle(); - void SetOutputLine(int flag); - bool IsEnabledOutputLine() const; - int GetOutputLine() const; + void DetachPlayerHeap(PlayerHeap *heap); + void DetachSoundPlayer(SoundPlayer *player); + void DetachSoundActor(SoundActor *actor); + void DetachExternalSoundPlayer(ExternalSoundPlayer *extPlayer); + void DetachGeneralHandle(); + void DetachTempGeneralHandle(); - f32 GetMainOutVolume() const; - void SetMainOutVolume(f32 vol); + int CalcCurrentPlayerPriority() const + { + return ut::Clamp(mPriority + mAmbientParam.priority, PRIORITY_MIN, + PRIORITY_MAX); + } - f32 GetRemoteOutVolume(int remote) const; - void SetRemoteOutVolume(int remote, f32 vol); + static int GetAmbientPriority(AmbientInfo const &ambientInfo, + u32 soundId); - void SetFxSend(AuxBus bus, f32 send); + // static members + public: + // NOTE: PRIORITY_MAX is a dependent name (see SoundInstanceManager) + static u32 const INVALID_ID = -1; + static int const PRIORITY_MAX = 127; + static int const PRIORITY_MIN = 0; - int CalcCurrentPlayerPriority() const { - return ut::Clamp(mPriority + mAmbientParam.priority, 0, PRIORITY_MAX); - } + static ut::detail::RuntimeTypeInfo const typeInfo; -private: - PlayerHeap *mHeap; // at 0x4 - SoundHandle *mGeneralHandle; // at 0x8 - SoundHandle *mTempGeneralHandle; // at 0xC - SoundPlayer *mSoundPlayer; // at 0x10 - ExternalSoundPlayer *mExtSoundPlayer; // at 0x14 + // members + private: + /* vtable */ // size 0x04, offset 0x00 + PlayerHeap *mPlayerHeap; // size 0x04, offset 0x04 + SoundHandle *mGeneralHandle; // size 0x04, offset 0x08 + SoundHandle *mTempGeneralHandle; // size 0x04, offset 0x0c + SoundPlayer *mSoundPlayer; // size 0x04, offset 0x10 + SoundActor *mSoundActor; // size 0x04, offset 0x14 + ExternalSoundPlayer *mExtSoundPlayer; // size 0x04, offset 0x18 + AmbientInfo mAmbientInfo; // size 0x14, offset 0x1c + SoundParam mAmbientParam; // size 0x24, offset 0x30 + SoundActorParam mActorParam; // size 0x0c, offset 0x54 + MoveValue mFadeVolume; // size 0x10, offset 0x60 + MoveValue mPauseFadeVolume; // size 0x10, offset 0x70 + bool mStartFlag; // size 0x01, offset 0x80 + bool mStartedFlag; // size 0x01, offset 0x81 + bool mAutoStopFlag; // size 0x01, offset 0x82 + bool mFadeOutFlag; // size 0x01, offset 0x83 + PauseState mPauseState; // size 0x04, offset 0x84 + bool mUnPauseFlag; // size 0x01, offset 0x88 + /* 3 bytes padding */ + s32 mAutoStopCounter; // size 0x04, offset 0x8c + u32 mUpdateCounter; // size 0x04, offset 0x90 + u8 mPriority; // size 0x01, offset 0x94 + u8 mVoiceOutCount; // size 0x01, offset 0x95 + u8 mBiquadFilterType; // size 0x01, offset 0x96 + /* 1 byte padding */ + u32 mId; // size 0x04, offset 0x98 + MoveValue mExtMoveVolume; // size 0x10, offset 0x9c + f32 mInitVolume; // size 0x04, offset 0xac + f32 mExtPan; // size 0x04, offset 0xb0 + f32 mExtSurroundPan; // size 0x04, offset 0xb4 + f32 mExtPitch; // size 0x04, offset 0xb8 + f32 mLpfFreq; // size 0x04, offset 0xbc + f32 mBiquadFilterValue; // size 0x04, offset 0xc0 + int mOutputLineFlag; // size 0x04, offset 0xc4 + f32 mMainOutVolume; // size 0x04, offset 0xc8 + f32 mMainSend; // size 0x04, offset 0xcc + f32 mFxSend[AUX_BUS_NUM]; // size 0x0c, offset 0xd0 + // NOTE: Name is not from DWARF; derived from usage and other nearby names + u32 mPauseNestCounter; // size 0x04, offset 0xdc + public: + ut::LinkListNode mPriorityLink; // size 0x08, offset 0xe0 + ut::LinkListNode mSoundPlayerPlayLink; // size 0x08, offset 0xe8 + ut::LinkListNode mSoundPlayerPriorityLink; // size 0x08, offset 0xf0 + ut::LinkListNode mExtSoundPlayerPlayLink; // size 0x08, offset 0xf8 - AmbientParamUpdateCallback *mAmbientParamUpdateCallback; // at 0x18 - AmbientArgUpdateCallback *mAmbientArgUpdateCallback; // at 0x1C - AmbientArgAllocaterCallback *mAmbientArgAllocaterCallback; // at 0x20 - void *mAmbientArg; // at 0x24 - SoundParam mAmbientParam; // at 0x28 + // friends + private: + friend class snd::SoundHandle; + }; // size 0x100 +}}} // namespace nw4r::snd::detail - MoveValue mFadeVolume; // at 0x44 - MoveValue mPauseFadeVolume; // at 0x54 - bool mStartFlag; // at 0x64 - bool mStartedFlag; // at 0x65 - bool mAutoStopFlag; // at 0x66 - bool mPauseFlag; // at 0x67 - bool mPauseFadeFlag; // at 0x68 - bool mFadeOutFlag; // at 0x69 - int mAutoStopCounter; // at 0x6C - u32 mUpdateCounter; // at 0x70 - - u8 mPriority; // at 0x74 - u32 mId; // at 0x78 - - MoveValue mExtMoveVolume; // at 0x7C - f32 mInitVolume; // at 0x8C - f32 mExtPan; // at 0x90 - f32 mExtSurroundPan; // at 0x94 - f32 mExtPitch; // at 0x98 - - bool mOutputLineFlagEnable; // at 0x9C - int mOutputLineFlag; // at 0xA0 - f32 mMainOutVolume; // at 0xA4 - f32 mRemoteOutVolume[WPAD_MAX_CONTROLLERS]; // at 0xA8 - -public: - NW4R_UT_LIST_NODE_DECL_EX(Prio); // at 0xB8 - NW4R_UT_LIST_NODE_DECL_EX(PlayerPlay); // at 0xC0 - NW4R_UT_LIST_NODE_DECL_EX(PlayerPrio); // at 0xC8 - NW4R_UT_LIST_NODE_DECL_EX(ExtPlay); // at 0xD0 -}; - -NW4R_UT_LIST_TYPEDEF_DECL_EX(BasicSound, Prio); -NW4R_UT_LIST_TYPEDEF_DECL_EX(BasicSound, PlayerPlay); -NW4R_UT_LIST_TYPEDEF_DECL_EX(BasicSound, PlayerPrio); -NW4R_UT_LIST_TYPEDEF_DECL_EX(BasicSound, ExtPlay); - -} // namespace detail -} // namespace snd -} // namespace nw4r - -#endif +#endif // NW4R_SND_BASIC_SOUND_H diff --git a/include/nw4r/snd/snd_BiquadFilterCallback.h b/include/nw4r/snd/snd_BiquadFilterCallback.h new file mode 100644 index 00000000..4c8d341c --- /dev/null +++ b/include/nw4r/snd/snd_BiquadFilterCallback.h @@ -0,0 +1,46 @@ +#ifndef NW4R_SND_BIQUAD_FILTER_CALLBACK_H +#define NW4R_SND_BIQUAD_FILTER_CALLBACK_H + +/******************************************************************************* + * headers + */ + +#include + +/******************************************************************************* + * classes and functions + */ + +namespace nw4r { namespace snd +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x4b4e3 + class BiquadFilterCallback + { + // nested types + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x4b416 + struct BiquadCoef + { + u16 b0; // size 0x02, offset 0x00 + u16 b1; // size 0x02, offset 0x02 + u16 b2; // size 0x02, offset 0x04 + u16 a1; // size 0x02, offset 0x06 + u16 a2; // size 0x02, offset 0x08 + }; // size 0x0a + + // methods + public: + // cdtors + virtual ~BiquadFilterCallback() {} + + // virtual function ordering + // vtable BiquadFilterCallback + virtual void GetCoef(int type, f32 value, BiquadCoef *coef) const = 0; + + // members + public: + /* vtable */ // size 0x04, offset 0x00 + }; // size 0x04 +}} // namespace nw4r::snd + +#endif // NW4R_SND_BIQUAD_FILTER_CALLBACK_H diff --git a/include/nw4r/snd/snd_BiquadFilterPreset.h b/include/nw4r/snd/snd_BiquadFilterPreset.h new file mode 100644 index 00000000..85c15a33 --- /dev/null +++ b/include/nw4r/snd/snd_BiquadFilterPreset.h @@ -0,0 +1,124 @@ +#ifndef NW4R_SND_BIQUAD_FILTER_PRESET_H +#define NW4R_SND_BIQUAD_FILTER_PRESET_H + +/******************************************************************************* + * headers + */ + +#include // f32 + +#include "BiquadFilterCallback.h" + +/******************************************************************************* + * classes and functions + */ + +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2a5425 + class BiquadFilterLpf : public BiquadFilterCallback + { + // methods + public: + // virtual function ordering + // vtable BiquadFilterCallback + virtual void GetCoef(int type, f32 value, BiquadCoef *coef) const; + + // static members + private: + /* technically it seems that COEF_TABLE_SIZE should be defined in terms + * of ARRAY_LENGTH(coefTable) (see the order of the typedef tags), but + * that seems needlessly more complicated, and I like putting static + * constexpr members first + */ + static int const COEF_TABLE_SIZE = 112; + + static BiquadCoef const coefTable[COEF_TABLE_SIZE]; + + // members + private: + /* vtable */ // size 0x04, offset 0x00 + }; // size 0x04 + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2a5544 + class BiquadFilterHpf : public BiquadFilterCallback + { + // methods + public: + // virtual function ordering + // vtable BiquadFilterCallback + virtual void GetCoef(int type, f32 value, BiquadCoef *coef) const; + + // static members + private: + static int const COEF_TABLE_SIZE = 97; + + static BiquadCoef const coefTable[COEF_TABLE_SIZE]; + + // members + private: + /* vtable */ // size 0x04, offset 0x00 + }; // size 0x04 + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2a5663 + class BiquadFilterBpf512 : public BiquadFilterCallback + { + // methods + public: + // virtual function ordering + // vtable BiquadFilterCallback + virtual void GetCoef(int type, f32 value, BiquadCoef *coef) const; + + // static members + private: + static int const COEF_TABLE_SIZE = 122; + + static BiquadCoef const coefTable[COEF_TABLE_SIZE]; + + // members + private: + /* vtable */ // size 0x04, offset 0x00 + }; // size 0x04 + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2a578b + class BiquadFilterBpf1024 : public BiquadFilterCallback + { + // methods + public: + // virtual function ordering + // vtable BiquadFilterCallback + virtual void GetCoef(int type, f32 value, BiquadCoef *coef) const; + + // static members + private: + static int const COEF_TABLE_SIZE = 93; + + static BiquadCoef const coefTable[COEF_TABLE_SIZE]; + + // members + private: + /* vtable */ // size 0x04, offset 0x00 + }; // size 0x04 + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2a58b6 + class BiquadFilterBpf2048 : public BiquadFilterCallback + { + // methods + public: + // virtual function ordering + // vtable BiquadFilterCallback + virtual void GetCoef(int type, f32 value, BiquadCoef *coef) const; + + // static members + private: + static int const COEF_TABLE_SIZE = 93; + + static BiquadCoef const coefTable[COEF_TABLE_SIZE]; + + // members + private: + /* vtable */ // size 0x04, offset 0x00 + }; // size 0x04 +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_BIQUAD_FILTER_PRESET_H diff --git a/include/nw4r/snd/snd_Channel.h b/include/nw4r/snd/snd_Channel.h index c26d8661..2f2014ac 100644 --- a/include/nw4r/snd/snd_Channel.h +++ b/include/nw4r/snd/snd_Channel.h @@ -1,254 +1,276 @@ #ifndef NW4R_SND_CHANNEL_H #define NW4R_SND_CHANNEL_H -#include "nw4r/snd/snd_Common.h" -#include "nw4r/snd/snd_EnvGenerator.h" -#include "nw4r/snd/snd_Lfo.h" -#include "nw4r/snd/snd_MoveValue.h" -#include "nw4r/snd/snd_Voice.h" -#include "nw4r/types_nw4r.h" -#include "nw4r/ut.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { -namespace detail { +#include // USHRT_MAX -class Channel { -public: - enum ChannelCallbackStatus { - CALLBACK_STATUS_STOPPED, - CALLBACK_STATUS_DROP, - CALLBACK_STATUS_FINISH, - CALLBACK_STATUS_CANCEL - }; +#include - typedef void (*ChannelCallback)(Channel *pDropChannel, ChannelCallbackStatus status, u32 callbackArg); +#include "EnvGenerator.h" +#include "InstancePool.h" +#include "global.h" +#include "Lfo.h" +#include "MoveValue.h" +#include "Voice.h" - enum LfoTarget { - LFO_TARGET_PITCH, - LFO_TARGET_VOLUME, - LFO_TARGET_PAN - }; +#include "../ut/LinkList.h" -public: - Channel(); - ~Channel(); +#include "nw4r/NW4RAssert.hpp" - void InitParam(ChannelCallback pCallback, u32 callbackArg); - void Update(bool periodic); - void Start(const WaveData &rData, int length, u32 offset); - void Release(); - void Stop(); +/******************************************************************************* + * types + */ - void SetAttack(int attack) { - mEnvelope.SetAttack(attack); - } - void SetDecay(int decay) { - mEnvelope.SetDecay(decay); - } - void SetSustain(int sustain) { - mEnvelope.SetSustain(sustain); - } - void SetRelease(int release) { - mEnvelope.SetRelease(release); - } - bool IsRelease() const { - return mEnvelope.GetStatus() == EnvGenerator::STATUS_RELEASE; - } +// forward declarations +namespace nw4r { namespace snd { namespace detail { class WaveDataLocationCallback; }}} +namespace nw4r { namespace snd { namespace detail { struct WaveInfo; }}} - void SetLfoParam(const LfoParam &rParam) { - mLfo.SetParam(rParam); - } - void SetLfoTarget(LfoTarget target) { - mLfoTarget = target; - } +/******************************************************************************* + * classes and functions + */ - void Pause(bool pause) { - mPauseFlag = pause; - mVoice->Pause(pause); - } - void SetReleasePriorityFix(bool fix) { - mReleasePriorityFixFlag = fix; - } +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2c353 + // NOTE: different from ketteiban: no remote fields + class Channel + { + // enums + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x290ad + enum ChannelCallbackStatus + { + CALLBACK_STATUS_STOPPED, + CALLBACK_STATUS_DROP, + CALLBACK_STATUS_FINISH, + CALLBACK_STATUS_CANCEL, + }; - bool IsPause() const { - return mPauseFlag != false; - } - bool IsActive() const { - return mActiveFlag; - } - bool IsAutoUpdateSweep() const { - return mAutoSweep; - } + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2c2f1 + enum LfoTarget + { + LFO_TARGET_PITCH, + LFO_TARGET_VOLUME, + LFO_TARGET_PAN, + }; - void SetUserVolume(f32 volume) { - mUserVolume = volume; - } - void SetUserPitch(f32 pitch) { - mUserPitch = pitch; - } - void SetUserPitchRatio(f32 ratio) { - mUserPitchRatio = ratio; - } - void SetUserPan(f32 pan) { - mUserPan = pan; - } - void SetUserSurroundPan(f32 pan) { - mUserSurroundPan = pan; - } - void SetUserLpfFreq(f32 freq) { - mUserLpfFreq = freq; - } + // typedefs + public: + typedef void Callback(Channel *dropChannel, + ChannelCallbackStatus status, + register_t userData); - void SetRemoteFilter(int filter) { - mRemoteFilter = filter; - } - void SetOutputLine(int flag) { - mOutputLineFlag = flag; - } + typedef ut::LinkList LinkList; - void SetMainOutVolume(f32 volume) { - mMainOutVolume = volume; - } - void SetMainSend(f32 send) { - mMainSend = send; - } - void SetFxSend(AuxBus bus, f32 send) { - mFxSend[bus] = send; - } + // methods + public: + // instance managers + static Channel *AllocChannel(int voiceChannelCount, int voiceOutCount, + int priority, Callback *callback, + register_t callbackData); - void SetRemoteOutVolume(int remote, f32 volume) { - mRemoteOutVolume[remote] = volume; - } - void SetRemoteSend(int remote, f32 send) { - mRemoteSend[remote] = send; - } - void SetRemoteFxSend(int remote, f32 send) { - mRemoteFxSend[remote] = send; - } + static void FreeChannel(Channel *channel); - void UpdateSweep(int count); - void SetSweepParam(f32 pitch, int time, bool autoUpdate); - f32 GetSweepValue() const; + // methods + void InitParam(Callback *callback, register_t callbackData); + void Start(WaveInfo const &waveParam, int length, u32 startOffset); + void Pause(bool flag) + { + mPauseFlag = flag; + mVoice->Pause(flag); + } + void Stop(); - void SetInitVolume(f32 volume) { - mInitVolume = volume; - } - void SetInitPan(f32 pan) { - mInitPan = pan; - } - void SetInitSurroundPan(f32 pan) { - mInitSurroundPan = pan; - } - void SetTune(f32 tune) { - mTune = tune; - } - void SetSilence(bool silence, int fadeTime) { - mSilenceVolume.SetTarget(silence ? 0 : SILENCE_VOLUME_MAX, fadeTime); - } + bool IsRelease() const + { + return mEnvelope.GetStatus() == EnvGenerator::STATUS_RELEASE; + } + bool IsPause() const { return mPauseFlag != false; } + bool IsActive() const { return mActiveFlag; } + bool IsAutoUpdateSweep() const { return mAutoSweep; } + f32 GetSweepValue() const; + s32 GetLength() const { return mLength; } + int GetAlternateAssignId() const { return mAlternateAssign; } + Voice *GetVoice() const { return mVoice; } + Channel *GetNextTrackChannel() const { return mNextLink; } - void SetKey(int key) { - mKey = key; - } - void SetOriginalKey(int key) { - mOriginalKey = key; - } + void SetDecay(int decay) { mEnvelope.SetDecay(decay); } + void SetRelease(int release) { mEnvelope.SetRelease(release); } + void SetAttack(int attack) { mEnvelope.SetAttack(attack); } + void SetSustain(int sustain) { mEnvelope.SetSustain(sustain); } + void SetHold(int hold) { mEnvelope.SetHold(hold); } + void SetLfoParam(LfoParam const ¶m) { mLfo.SetParam(param); } + void SetLfoTarget(LfoTarget type) { mLfoTarget = type; } + void SetReleasePriorityFix(bool fix) { mReleasePriorityFixFlag = fix; } + void SetReleaseIgnore(bool ignore) { mReleaseIgnoreFlag = ignore; } + void SetBiquadFilter(int type, f32 value); + void SetRemoteFilter(int filter) { mRemoteFilter = filter; } + void SetUserVolume(f32 volume) { mUserVolume = volume; } + void SetUserPitchRatio(f32 pitchRatio) { mUserPitchRatio = pitchRatio; } + void SetUserPan(f32 pan) { mUserPan = pan; } + void SetUserSurroundPan(f32 surroundPan) + { + mUserSurroundPan = surroundPan; + } + void SetUserLpfFreq(f32 lpfFreq) { mUserLpfFreq = lpfFreq; } + void SetOutputLine(int lineFlag) { mOutputLineFlag = lineFlag; } + void SetMainOutVolume(f32 volume) { mMainOutVolume = volume; } + void SetMainSend(f32 send) { mMainSend = send; } + void SetFxSend(AuxBus bus, f32 send) { mFxSend[bus] = send; } + void SetUserPitch(f32 pitch) { mUserPitch = pitch; } + void SetSweepParam(f32 sweepPitch, int sweepTime, bool autoUpdate); + void SetInitVolume(f32 volume) { mInitVolume = volume; } + void SetInitPan(f32 pan) { mInitPan = pan; } + void SetInitSurroundPan(f32 surroundPan) + { + mInitSurroundPan = surroundPan; + } + void SetTune(f32 tune) { mTune = tune; } + void SetSilence(bool silenceFlag, int fadeTimes) + { + NW4RAssertHeaderClampedLRValue_Line(138, fadeTimes, 0, USHRT_MAX); - s32 GetLength() const { - return mLength; - } - void SetLength(s32 length) { - mLength = length; - } + mSilenceVolume.SetTarget(silenceFlag ? SILENCE_VOLUME_MIN + : SILENCE_VOLUME_MAX, + fadeTimes); + } + void SetKey(int key) { mKey = key; } + void SetOriginalKey(int key) { mOriginalKey = key; } + void SetLength(s32 length) { mLength = length; } + void SetPanMode(PanMode panMode) { mPanMode = panMode; } + void SetPanCurve(PanCurve panCurve) { mPanCurve = panCurve; } + void SetAlternateAssignId(int id) { mAlternateAssign = id; } + void SetWaveDataLocationCallback(WaveDataLocationCallback *callback, + WaveInfo const *waveInfo) + { + mWaveDataLocationCallback = callback; + mWaveInfo = waveInfo; + } + void SetVoiceOutParam(int index, VoiceOutParam const ¶m) + { + mVoice->SetVoiceOutParam(index, param); + } + void SetNextTrackChannel(Channel *channel) { mNextLink = channel; } - void SetPanMode(PanMode mode) { - mPanMode = mode; - } - void SetPanCurve(PanCurve curve) { - mPanCurve = curve; - } + void Update(bool doPeriodicProc); + void UpdateSweep(int count); + void Release(); + void NoteOff(); - Channel *GetNextTrackChannel() const { - return mNextLink; - } - void SetNextTrackChannel(Channel *pChannel) { - mNextLink = pChannel; - } + private: + // cdtors + Channel(); + ~Channel(); - static Channel *AllocChannel(int channels, int voices, int priority, ChannelCallback pCallback, u32 callbackArg); - static void FreeChannel(Channel *pChannel); + // callbacks + static void VoiceCallbackFunc(Voice *voice, + Voice::VoiceCallbackStatus status, + void *arg); -private: - static const u8 SILENCE_VOLUME_MAX = 255; + // static members + public: + static u8 const SILENCE_VOLUME_MIN = 0; + static u8 const SILENCE_VOLUME_MAX = 255; - static const int KEY_INIT = 60; - static const int ORIGINAL_KEY_INIT = 60; + static int const ORIGINAL_KEY_INIT = 60; + static int const KEY_INIT = 60; - static const int PRIORITY_RELEASE = 1; + static int const PRIORITY_RELEASE = 1; -private: - static void VoiceCallbackFunc(Voice *pDropVoice, Voice::VoiceCallbackStatus status, void *pCallbackArg); + static int const CHANNEL_MAX = Voice::CHANNEL_MAX; + static int const CHANNEL_MIN = 1; + static int const CHANNEL_NUM; -private: - EnvGenerator mEnvelope; // at 0x0 - Lfo mLfo; // at 0x18 - u8 mLfoTarget; // at 0x30 + // members + private: + EnvGenerator mEnvelope; // size 0x1c, offset 0x00 + Lfo mLfo; // size 0x18, offset 0x1c + u8 mLfoTarget; // size 0x01, offset 0x34 + bool mPauseFlag; // size 0x01, offset 0x35 + bool mActiveFlag; // size 0x01, offset 0x36 + bool mAllocFlag; // size 0x01, offset 0x37 + bool mAutoSweep; // size 0x01, offset 0x38 + bool mReleasePriorityFixFlag; // size 0x01, offset 0x39 + u8 mReleaseIgnoreFlag; // size 0x01, offset 0x3a // not a bool? + u8 mBiquadType; // size 0x01, offset 0x3b + u8 mRemoteFilter; // size 0x01, offset 0x3c + /* 3 bytes padding */ + f32 mUserVolume; // size 0x04, offset 0x40 + f32 mUserPitchRatio; // size 0x04, offset 0x44 + f32 mUserPan; // size 0x04, offset 0x48 + f32 mUserSurroundPan; // size 0x04, offset 0x4c + f32 mUserLpfFreq; // size 0x04, offset 0x50 + f32 mBiquadValue; // size 0x04, offset 0x54 + int mOutputLineFlag; // size 0x04, offset 0x58 + f32 mMainOutVolume; // size 0x04, offset 0x5c + f32 mMainSend; // size 0x04, offset 0x60 + f32 mFxSend[AUX_BUS_NUM]; // size 0x0c, offset 0x64 + f32 mUserPitch; // size 0x04, offset 0x70 + f32 mSweepPitch; // size 0x04, offset 0x74 + s32 mSweepCounter; // size 0x04, offset 0x78 + s32 mSweepLength; // size 0x04, offset 0x7c + f32 mInitVolume; // size 0x04, offset 0x80 + f32 mInitPan; // size 0x04, offset 0x84 + f32 mInitSurroundPan; // size 0x04, offset 0x88 + f32 mTune; // size 0x04, offset 0x8c + MoveValue mSilenceVolume; // size 0x06, offset 0x90 + /* 2 bytes padding */ + int mKey; // size 0x04, offset 0x98 + int mOriginalKey; // size 0x04, offset 0x9c + s32 mLength; // size 0x04, offset 0xa0 + PanMode mPanMode; // size 0x04, offset 0xa4 + PanCurve mPanCurve; // size 0x04, offset 0xa8 + int mAlternateAssign; // size 0x04, offset 0xac + Callback *mCallback; // size 0x04, offset 0xb0 + register_t mCallbackData; // size 0x04, offset 0xb4 + WaveDataLocationCallback *mWaveDataLocationCallback; // size 0x04, offset 0xb8 + WaveInfo const *mWaveInfo; // size 0x04, offset 0xbc + Voice *mVoice; // size 0x04, offset 0xc0 + Channel *mNextLink; // size 0x04, offset 0xc4 + public: + ut::LinkListNode mLink; // size 0x08, offset 0xc8 - bool mPauseFlag; // at 0x31 - bool mActiveFlag; // at 0x32 - bool mAllocFlag; // at 0x33 - bool mAutoSweep; // at 0x34 - bool mReleasePriorityFixFlag; // at 0x35 + // friends + private: + // for calling private constructor + template friend class InstancePool; + }; // size 0xd0 - f32 mUserVolume; // at 0x38 - f32 mUserPitchRatio; // at 0x3C - f32 mUserPan; // at 0x40 - f32 mUserSurroundPan; // at 0x44 - f32 mUserLpfFreq; // at 0x48 + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b895c + class ChannelManager + { + // methods + public: + // instance accessors + static ChannelManager &GetInstance(); - int mRemoteFilter; // at 0x4C - int mOutputLineFlag; // at 0x50 + // methods + u32 GetRequiredMemSize(int channelCount); + void Setup(void *mem, u32 memSize); + void Shutdown(); - f32 mMainOutVolume; // at 0x54 - f32 mMainSend; // at 0x58 - f32 mFxSend[AUX_BUS_NUM]; // at 0x5C + Channel* Alloc(); + void Free(Channel *channel); - f32 mRemoteOutVolume[WPAD_MAX_CONTROLLERS]; // at 0x68 - f32 mRemoteSend[WPAD_MAX_CONTROLLERS]; // at 0x78 - f32 mRemoteFxSend[WPAD_MAX_CONTROLLERS]; // at 0x88 + void UpdateAllChannel(); - f32 mUserPitch; // at 0x98 - f32 mSweepPitch; // at 0x9C - int mSweepCounter; // at 0xA0 - int mSweepLength; // at 0xA4 + private: + // cdtors + ChannelManager(); - f32 mInitVolume; // at 0xA8 - f32 mInitPan; // at 0xAC - f32 mInitSurroundPan; // at 0xB0 - f32 mTune; // at 0xB4 - MoveValue mSilenceVolume; // at 0xB8 + // members + private: + InstancePool mPool; // size 0x04, offset 0x00 + Channel::LinkList mChannelList; // size 0x0c, offset 0x04 + bool mInitialized; // size 0x01, offset 0x10 + /* 3 bytes padding */ + u32 mChannelCount; // size 0x04, offset 0x14 + void *mMem; // size 0x04, offset 0x18 + u32 mMemSize; // size 0x04, offset 0x1c + }; // size 0x20 +}}} // namespace nw4r::snd::detail - int mKey; // at 0xC0 - int mOriginalKey; // at 0xC4 - int mLength; // at 0xC8 - - PanMode mPanMode; // at 0xCC - PanCurve mPanCurve; // at 0xD0 - - ChannelCallback mCallback; // at 0xD4 - u32 mCallbackData; // at 0xD8 - - Voice *mVoice; // at 0xDC - Channel *mNextLink; // at 0xE0 - -public: - NW4R_UT_LIST_NODE_DECL(); // at 0xE4 -}; - -NW4R_UT_LIST_TYPEDEF_DECL(Channel); - -} // namespace detail -} // namespace snd -} // namespace nw4r - -#endif +#endif // NW4R_SND_CHANNEL_H diff --git a/include/nw4r/snd/snd_Common.h b/include/nw4r/snd/snd_Common.h deleted file mode 100644 index cf8cef1c..00000000 --- a/include/nw4r/snd/snd_Common.h +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef NW4R_SND_COMMON_H -#define NW4R_SND_COMMON_H -#include "nw4r/types_nw4r.h" - -namespace nw4r { -namespace snd { - -static const int CHANNEL_MIN = 1; -static const int CHANNEL_MAX = 2; - -static const int REMOTE_FILTER_MAX = 127; - -// 12 notes each with 256 microtones -static const int SEMITONE_MAX = 12; -static const int MICROTONE_MAX = 256; - -// Volume in range [-90.4db, 6.0db] -static const f32 VOLUME_MIN_DB = -90.4f; -static const f32 VOLUME_MAX_DB = 6.0f; -static const f32 VOLUME_RANGE_DB = -(VOLUME_MIN_DB - VOLUME_MAX_DB); -static const int VOLUME_RANGE_MB = static_cast(10 * VOLUME_RANGE_DB); - -enum OutputLineFlag { - OUTPUT_LINE_MAIN = (1 << 0), - OUTPUT_LINE_REMOTE_N = (1 << 1), -}; - -enum AuxBus { - AUX_A, - AUX_B, - AUX_C, - AUX_BUS_NUM -}; - -enum OutputMode { - OUTPUT_MODE_STEREO, - OUTPUT_MODE_SURROUND, - OUTPUT_MODE_DPL2, - OUTPUT_MODE_MONO -}; - -struct SoundParam { - f32 volume; // at 0x0 - f32 pitch; // at 0x4 - f32 pan; // at 0x8 - f32 surroundPan; // at 0xC - f32 fxSend; // at 0x10 - f32 lpf; // at 0x14 - int priority; // at 0x18 -}; - -namespace detail { - -enum PanMode { - PAN_MODE_DUAL, - PAN_MODE_BALANCE, -}; - -enum PanCurve { - PAN_CURVE_SQRT, - PAN_CURVE_SQRT_0DB, - PAN_CURVE_SQRT_0DB_CLAMP, - PAN_CURVE_SINCOS, - PAN_CURVE_SINCOS_0DB, - PAN_CURVE_SINCOS_0DB_CLAMP, - PAN_CURVE_LINEAR, - PAN_CURVE_LINEAR_0DB, - PAN_CURVE_LINEAR_0DB_CLAMP, -}; - -struct AdpcmParam { - u16 coef[16]; // at 0x0 - u16 gain; // at 0x20 - u16 pred_scale; // at 0x22 - u16 yn1; // at 0x24 - u16 yn2; // at 0x26 -}; - -struct AdpcmLoopParam { - u16 loop_pred_scale; // at 0x0 - u16 loop_yn1; // at 0x2 - u16 loop_yn2; // at 0x4 -}; - -struct AdpcmInfo { - AdpcmParam param; // at 0x0 - AdpcmLoopParam loopParam; // at 0x28 - u16 padding; -}; - -} // namespace detail -} // namespace snd -} // namespace nw4r - -#endif diff --git a/include/nw4r/snd/snd_DisposeCallbackManager.h b/include/nw4r/snd/snd_DisposeCallbackManager.h index d4e512fc..329a7ed0 100644 --- a/include/nw4r/snd/snd_DisposeCallbackManager.h +++ b/include/nw4r/snd/snd_DisposeCallbackManager.h @@ -1,32 +1,70 @@ #ifndef NW4R_SND_DISPOSE_CALLBACK_MANAGER_H #define NW4R_SND_DISPOSE_CALLBACK_MANAGER_H -#include "nw4r/snd/snd_DisposeCallback.h" -#include "nw4r/types_nw4r.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { -namespace detail { +#include // u32 -class DisposeCallbackManager { -public: - static DisposeCallbackManager &GetInstance(); +#include "../ut/LinkList.h" - void RegisterDisposeCallback(DisposeCallback *pCallback); - void UnregisterDisposeCallback(DisposeCallback *pCallback); +/******************************************************************************* + * classes and functions + */ - void Dispose(void *pData, u32 size, void *pArg); - void DisposeWave(void *pData, u32 size, void *pArg); +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x26773 + class DisposeCallback + { + // typedefs + public: + typedef ut::LinkList LinkList; -private: - DisposeCallbackManager(); + // methods + public: + // cdtors + /* virtual ~DisposeCallback() {} */ // virtual function ordering -private: - DisposeCallbackList mCallbackList; // at 0x0 -}; + // members + public: + ut::LinkListNode mDisposeLink; // size 0x08, offset 0x00 + /* vtable */ // size 0x04, offset 0x08 -} // namespace detail -} // namespace snd -} // namespace nw4r + // late virtual methods + public: + // virtual function ordering + // vtable DisposeCallback + virtual ~DisposeCallback() {} + virtual void InvalidateData(void const *pStart, void const *pEnd) = 0; + virtual void InvalidateWaveData(void const *pStart, + void const *pEnd) = 0; + }; // size 0x0c -#endif + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2bb987 + class DisposeCallbackManager + { + // methods + public: + // instance accessors + static DisposeCallbackManager &GetInstance(); + + // methods + void RegisterDisposeCallback(DisposeCallback *callback); + void UnregisterDisposeCallback(DisposeCallback *callback); + + void Dispose(void *mem, u32 size, void *arg); + void DisposeWave(void *mem, u32 size, void *arg); + + private: + // cdtors + DisposeCallbackManager(); + + // members + private: + DisposeCallback::LinkList mCallbackList; // size 0x0c, offset 0x00 + }; // size 0x0c +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_DISPOSE_CALLBACK_MANAGER_H diff --git a/include/nw4r/snd/snd_DvdSoundArchive.h b/include/nw4r/snd/snd_DvdSoundArchive.h index e0af0859..5f05da48 100644 --- a/include/nw4r/snd/snd_DvdSoundArchive.h +++ b/include/nw4r/snd/snd_DvdSoundArchive.h @@ -1,65 +1,53 @@ #ifndef NW4R_SND_DVD_SOUND_ARCHIVE_H #define NW4R_SND_DVD_SOUND_ARCHIVE_H -#include "nw4r/snd/snd_SoundArchive.h" -#include "nw4r/snd/snd_SoundArchiveFile.h" -#include "nw4r/types_nw4r.h" -#include "nw4r/ut.h" // IWYU pragma: export -#include "rvl/DVD.h" // IWYU pragma: export -namespace nw4r { -namespace snd { +/******************************************************************************* + * headers + */ -class DvdSoundArchive : public SoundArchive { -private: - class DvdFileStream; +#include -public: - DvdSoundArchive(); - virtual ~DvdSoundArchive(); // at 0x8 +// WARNING: DO NOT REORDER these #include directives, data pooling depends on it - virtual const void *detail_GetFileAddress(u32 /* id */) const { - return NULL; - } // at 0xC +// clang-format off +#include "../ut/FileStream.h" // This needs to be +#include "SoundArchive.h" // before this +// clang-format on - virtual const void *detail_GetWaveDataFileAddress(u32 /* id */) const { - return NULL; - } // at 0x10 +/******************************************************************************* + * classes and functions + */ - virtual int detail_GetRequiredStreamBufferSize() const; // at 0x14 +namespace nw4r { namespace snd +{ + class DvdSoundArchive : public SoundArchive + { + class DvdFileStream; + DvdSoundArchive(); + }; - virtual ut::FileStream *OpenStream(void *pBuffer, int size, u32 offset, - u32 length) const; // at 0x18 + class DvdSoundArchive::DvdFileStream : public ut::FileStream + { + DvdFileStream(); + virtual ut::detail::RuntimeTypeInfo const *GetRuntimeTypeInfo() const { return 0; } + virtual void Close() {} + virtual s32 Read(void *, u32) { return 0; } + virtual bool IsBusy() const { return 0; } + virtual bool CanAsync() const { return 0; } + virtual bool CanRead() const { return 0; } + virtual bool CanWrite() const { return 0; } + virtual u32 GetOffsetAlign() const { return 0; } + virtual u32 GetSizeAlign() const { return 0; } + virtual u32 GetBufferAlign() const { return 0; } + virtual u32 GetSize() const { return 0; } + virtual void Seek(s32, u32) {} + virtual void Cancel() {} + virtual bool CancelAsync(StreamCallback *, void *) { return 0; } + virtual bool CanSeek() const { return 0; } + virtual bool CanCancel() const { return 0; } + virtual u32 Tell() const { return 0; } + friend class DvdSoundArchive; + }; +}} // namespace nw4r::snd - virtual ut::FileStream *OpenExtStream( - void *pBuffer, int size, const char *pExtPath, u32 offset, - u32 length - ) const; // at 0x1C - - bool Open(s32 entrynum); - bool Open(const char *pPath); - - void Close(); - - bool LoadHeader(void *pBuffer, u32 size); - bool LoadLabelStringData(void *pBuffer, u32 size); - - u32 GetHeaderSize() const { - return mFileReader.GetInfoChunkSize(); - } - u32 GetLabelStringDataSize() const { - return mFileReader.GetLabelStringChunkSize(); - } - -private: - bool LoadFileHeader(); - -private: - detail::SoundArchiveFileReader mFileReader; // at 0x108 - DVDFileInfo mFileInfo; // at 0x14C - bool mOpen; // at 0x188 -}; - -} // namespace snd -} // namespace nw4r - -#endif +#endif // NW4R_SND_DVD_SOUND_ARCHIVE_H diff --git a/include/nw4r/snd/snd_EnvGenerator.h b/include/nw4r/snd/snd_EnvGenerator.h index 8ca233bb..1081431c 100644 --- a/include/nw4r/snd/snd_EnvGenerator.h +++ b/include/nw4r/snd/snd_EnvGenerator.h @@ -1,66 +1,86 @@ #ifndef NW4R_SND_ENV_GENERATOR_H #define NW4R_SND_ENV_GENERATOR_H -#include "nw4r/types_nw4r.h" -namespace nw4r { -namespace snd { -namespace detail { +/******************************************************************************* + * headers + */ -class EnvGenerator { -public: - enum Status { - STATUS_ATTACK, - STATUS_DECAY, - STATUS_SUSTAIN, - STATUS_RELEASE - }; +#include -public: - EnvGenerator(); +/******************************************************************************* + * classes and functions + */ - void Init(f32 db = VOLUME_INIT); - void Reset(f32 db = VOLUME_INIT); - f32 GetValue() const; - void Update(int msec); +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x28b11 + class EnvGenerator + { + // enums + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x28a77 + enum Status + { + STATUS_ATTACK, + STATUS_HOLD, + STATUS_DECAY, + STATUS_SUSTAIN, + STATUS_RELEASE, + }; - Status GetStatus() const { - return mStatus; - } - void SetStatus(Status status) { - mStatus = status; - } + // methods + public: + // cdtors + EnvGenerator(); - void SetAttack(int attack); - void SetDecay(int decay); - void SetSustain(int sustain); - void SetRelease(int release); + // methods + void Init(f32 initDecibel); + void Update(int msec); + void Reset(f32 initDecibel); -private: - static const int DECIBEL_SQUARE_TABLE_SIZE = 128; + Status GetStatus() const { return mStatus; } + f32 GetValue() const; - static const volatile f32 VOLUME_INIT; - static const int ATTACK_INIT = 127; - static const int DECAY_INIT = 127; - static const int SUSTAIN_INIT = 127; - static const int RELEASE_INIT = 127; + void SetStatus(Status status) { mStatus = status; } + void SetAttack(int attack); + void SetHold(int hold); + void SetDecay(int decay); + void SetSustain(int sustain); + void SetRelease(int release); -private: - f32 CalcRelease(int release); - int CalcDecibelSquare(int scale); + private: + f32 CalcRelease(int release); + s16 CalcDecibelSquare(int scale); -private: - Status mStatus; // at 0x0 - f32 mValue; // at 0x4 - f32 mDecay; // at 0x8 - f32 mRelease; // at 0xC - f32 mAttack; // at 0x10 - u8 mSustain; // at 0x14 + // static members + public: + static int const CALC_DECIBEL_SCALE_MAX; - static const s16 DecibelSquareTable[DECIBEL_SQUARE_TABLE_SIZE]; -}; + static int const DECIBEL_SQUARE_TABLE_SIZE = 128; -} // namespace detail -} // namespace snd -} // namespace nw4r + static int const RELEASE_INIT = 127; + static int const SUSTAIN_INIT = 127; + static int const DECAY_INIT = 127; + static int const HOLD_INIT = 0; + static int const ATTACK_INIT = 127; + static f32 const volatile VOLUME_INIT; -#endif + private: + static s16 const DecibelSquareTable[DECIBEL_SQUARE_TABLE_SIZE]; + + // members + private: + Status mStatus; // size 0x04, offset 0x00 + f32 mValue; // size 0x04, offset 0x04 + f32 mDecay; // size 0x04, offset 0x08 + f32 mRelease; // size 0x04, offset 0x0c + f32 mAttack; // size 0x04, offset 0x10 + u8 mSustain; // size 0x01, offset 0x14 + /* 1 byte padding */ + u16 mHold; // size 0x02, offset 0x16 + u16 mHoldCounter; // size 0x02, offset 0x18 + /* 2 bytes padding */ + }; // size 0x1c +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_ENV_GENERATOR_H diff --git a/include/nw4r/snd/snd_ExternalSoundPlayer.h b/include/nw4r/snd/snd_ExternalSoundPlayer.h index de2c8ca9..9def7269 100644 --- a/include/nw4r/snd/snd_ExternalSoundPlayer.h +++ b/include/nw4r/snd/snd_ExternalSoundPlayer.h @@ -1,67 +1,40 @@ #ifndef NW4R_SND_EXTERNAL_SOUND_PLAYER_H #define NW4R_SND_EXTERNAL_SOUND_PLAYER_H -#include "nw4r/snd/snd_BasicSound.h" -#include "nw4r/types_nw4r.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { -namespace detail { +#include "BasicSound.h" -class ExternalSoundPlayer { -public: - ExternalSoundPlayer(); - ~ExternalSoundPlayer(); +/******************************************************************************* + * classes and functions + */ - int GetPlayableSoundCount() const { - return mPlayableCount; - } - void SetPlayableSoundCount(int count); +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x27049 + class ExternalSoundPlayer + { + // methods + public: + // methods + int GetPlayingSoundCount() const { return mSoundList.GetSize(); } + int GetPlayableSoundCount() const { return mPlayableCount; } - int GetPlayingSoundCount() const { - return mSoundList.GetSize(); - } + bool AppendSound(BasicSound *sound); + void RemoveSound(BasicSound *sound); - f32 detail_GetVolume() const { - return mVolume; - } - BasicSound *GetLowestPrioritySound(); + bool detail_CanPlaySound(int startPriority); - void InsertSoundList(BasicSound *pSound); - void RemoveSoundList(BasicSound *pSound); + private: + BasicSound *GetLowestPrioritySound(); - template - TForEachFunc ForEachSound(TForEachFunc pFunction, bool reverse) { - if (reverse) { - BasicSoundExtPlayList::RevIterator it = mSoundList.GetBeginReverseIter(); + // members + private: + BasicSound::ExtSoundPlayerPlayLinkList mSoundList; // size 0x0c, offset 0x00 + int mPlayableCount; // size 0x04, offset 0x0c + }; // size 0x10 +}}} // namespace nw4r::snd::detail - while (it != mSoundList.GetEndReverseIter()) { - BasicSoundExtPlayList::RevIterator curr = it; - - SoundHandle handle; - handle.detail_AttachSoundAsTempHandle(&*curr); - pFunction(handle); - - if (handle.IsAttachedSound()) { - ++it; - } - } - } else { - NW4R_UT_LIST_SAFE_FOREACH(mSoundList, SoundHandle handle; handle.detail_AttachSoundAsTempHandle(&*it); - pFunction(handle);); - } - - return pFunction; - } - -private: - BasicSoundExtPlayList mSoundList; // at 0x0 - u16 mPlayableCount; // at 0xC - f32 mVolume; // at 0x10 -}; - -} // namespace detail -} // namespace snd -} // namespace nw4r - -#endif +#endif // NW4R_SND_EXTERNAL_SOUND_PLAYER_H diff --git a/include/nw4r/snd/snd_FxBase.h b/include/nw4r/snd/snd_FxBase.h index 3ba41214..97b0ff0d 100644 --- a/include/nw4r/snd/snd_FxBase.h +++ b/include/nw4r/snd/snd_FxBase.h @@ -1,41 +1,49 @@ #ifndef NW4R_SND_FX_BASE_H #define NW4R_SND_FX_BASE_H -#include "nw4r/snd/snd_Common.h" -#include "nw4r/types_nw4r.h" -#include "nw4r/ut.h" // IWYU pragma: export -namespace nw4r { -namespace snd { +/******************************************************************************* + * headers + */ -enum SampleFormat { - SAMPLE_FORMAT_PCM_S32, - SAMPLE_FORMAT_PCM_S16, - SAMPLE_FORMAT_PCM_S8, - SAMPLE_FORMAT_DSP_ADPCM -}; +#include -class FxBase : private ut::NonCopyable { -public: - virtual ~FxBase() {} // at 0x8 +#include "global.h" - virtual bool StartUp() { - return true; - } // at 0xC +#include "../ut/inlines.h" // NonCopyable +#include "../ut/LinkList.h" - virtual void Shutdown() {} // at 0x10 +/******************************************************************************* + * classes and functions + */ - virtual void UpdateBuffer( - int /* channels */, void ** /* ppBuffer */, u32 /* size */, SampleFormat /* format */, f32 /* sampleRate */, - OutputMode /* mode */ - ) {} // at 0x14 +namespace nw4r { namespace snd +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x20f98 + class FxBase : private ut::NonCopyable + { + // typedefs + public: + typedef ut::LinkList LinkList; -public: - NW4R_UT_LIST_NODE_DECL(); // at 0x4 -}; + // methods + public: + // cdtors + virtual ~FxBase() {} -NW4R_UT_LIST_TYPEDEF_DECL(FxBase); + // virtual function ordering + // vtable FxBase + virtual bool StartUp(); + virtual void Shutdown(); + virtual void UpdateBuffer(int, void *, u32, SampleFormat, f32, + OutputMode); + virtual void OnChangeOutputMode(); -} // namespace snd -} // namespace nw4r + private: + /* base ut::NonCopyable */ // size 0x00, offset 0x00 + /* vtable */ // size 0x04, offset 0x00 + public: + ut::LinkListNode mFxLink; // size 0x08, offset 0x04 + }; // size 0x0c +}} // namespace nw4r::snd -#endif +#endif // NW4R_SND_FX_BASE_H diff --git a/include/nw4r/snd/snd_InstancePool.h b/include/nw4r/snd/snd_InstancePool.h index 949e2198..6a3d0f8b 100644 --- a/include/nw4r/snd/snd_InstancePool.h +++ b/include/nw4r/snd/snd_InstancePool.h @@ -1,85 +1,105 @@ #ifndef NW4R_SND_INSTANCE_POOL_H #define NW4R_SND_INSTANCE_POOL_H -#include "nw4r/types_nw4r.h" -namespace nw4r { -namespace snd { -namespace detail { +/******************************************************************************* + * headers + */ -class PoolImpl { -public: - PoolImpl() : mNext(NULL) {} +#include -protected: - u32 CreateImpl(void *pBuffer, u32 size, u32 stride); - void DestroyImpl(void *pBuffer, u32 size); - int CountImpl() const; +#include - void *AllocImpl(); - void FreeImpl(void *pElem); +/******************************************************************************* + * classes and functions + */ -private: - PoolImpl *mNext; // at 0x0 -}; +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2e96b + class PoolImpl + { + // methods + protected: + // cdtors + PoolImpl() : mNext(nullptr) {} -template -class InstancePool : private PoolImpl { -public: - u32 Create(void *pBuffer, u32 size) { - return CreateImpl(pBuffer, size, sizeof(T)); - } + // methods + u32 CreateImpl(void *buffer, u32 size, u32 objSize); + void DestroyImpl(void *buffer, u32 size); - void Destroy(void *pPtr, u32 size) { - DestroyImpl(pPtr, size); - } + int CountImpl() const; - int Count() const { - return CountImpl(); - } + void *AllocImpl(); + void FreeImpl(void *ptr); - T *Alloc() { - void *pPtr = AllocImpl(); - if (pPtr == NULL) { - return NULL; - } + // members + private: + PoolImpl *mNext; // size 0x04, offset 0x00 + }; // size 0x04 - return new (pPtr) T; - } + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2f735, 0x31491... + template + class InstancePool : private PoolImpl + { + // methods + public: + // methods + u32 Create(void *buffer, u32 size) + { + u32 objSize = sizeof(T); - void Free(T *pElem) { - if (pElem != NULL) { - pElem->~T(); - FreeImpl(pElem); - } - } -}; + return CreateImpl(buffer, size, objSize); + } + void Destroy(void *buffer, u32 size) { DestroyImpl(buffer, size); } -template -class MemoryPool : private PoolImpl { -public: - u32 Create(void *pBuffer, u32 size) { - return CreateImpl(pBuffer, size, sizeof(T)); - } + int Count() const { return CountImpl(); } - void Destroy(void *pPtr, u32 size) { - DestroyImpl(pPtr, size); - } + T *Alloc() + { + void *ptr = AllocImpl(); + if (!ptr) + return nullptr; - int Count() const { - return CountImpl(); - } + return new (ptr) T; + } + void Free(T *obj) + { + if (obj) + { + obj->~T(); + FreeImpl(obj); + } + } - T *Alloc() { - return static_cast(AllocImpl()); - } + // members + private: + /* base PoolImpl */ // size 0x04, offset 0x00 + }; // size 0x04 - void Free(T *pElem) { - FreeImpl(pElem); - } -}; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2efa0, 0x309c7... + template + class MemoryPool : private PoolImpl + { + // methods + public: + // methods + u32 Create(void *buffer, u32 size) + { + u32 objSize = sizeof(T); -} // namespace detail -} // namespace snd -} // namespace nw4r + return CreateImpl(buffer, size, objSize); + } + void Destroy(void *buffer, u32 size) { DestroyImpl(buffer, size); } -#endif + int Count() const { return CountImpl(); } // Presumably + + T *Alloc() { return static_cast(AllocImpl()); } + void Free(T *obj) { FreeImpl(obj); } + + // members + private: + /* base PoolImpl */ // size 0x04, offset 0x00 + }; // size 0x04 +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_INSTANCE_POOL_H diff --git a/include/nw4r/snd/snd_Lfo.h b/include/nw4r/snd/snd_Lfo.h index 8c6c73f9..8fe8544b 100644 --- a/include/nw4r/snd/snd_Lfo.h +++ b/include/nw4r/snd/snd_Lfo.h @@ -1,55 +1,74 @@ #ifndef NW4R_SND_LFO_H #define NW4R_SND_LFO_H -#include "nw4r/types_nw4r.h" -namespace nw4r { -namespace snd { -namespace detail { +/******************************************************************************* + * headers + */ -struct LfoParam { - LfoParam() { - Init(); - } +#include - void Init(); +/******************************************************************************* + * classes and functions + */ - f32 depth; // at 0x0 - f32 speed; // at 0x4 - u32 delay; // at 0x8 - u8 range; // at 0xC - u8 padding[3]; // at 0xD -}; +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x28dc2 + struct LfoParam + { + // methods + public: + // cdtors + LfoParam() { Init(); } -class Lfo { -public: - Lfo() : mDelayCounter(0), mCounter(0.0f) {} + // methods + void Init(); - LfoParam &GetParam() { - return mParam; - } - void SetParam(const LfoParam &rParam) { - mParam = rParam; - } + // members + public: + f32 depth; // size 0x04, offset 0x00 + f32 speed; // size 0x04, offset 0x04 + u32 delay; // size 0x04, offset 0x08 + u8 range; // size 0x01, offset 0x0c + byte1_t padding[3]; + }; // size 0x10 - void Reset(); - void Update(int msec); + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x28ea0 + class Lfo + { + // methods + public: + // cdtors + Lfo() : + mDelayCounter (0), + mCounter (0.0f) + { + } - f32 GetValue() const; + // methods + void Update(int msec); + void Reset(); -private: - static const int TABLE_SIZE = 32; + LfoParam &GetParam() { return mParam; } -private: - static s8 GetSinIdx(int i); + void SetParam(LfoParam const ¶m) { mParam = param; } -private: - LfoParam mParam; // at 0x0 - u32 mDelayCounter; // at 0x10 - f32 mCounter; // at 0x14 -}; + f32 GetValue() const; -} // namespace detail -} // namespace snd -} // namespace nw4r + private: + static s8 GetSinIdx(int i); -#endif + // static members + private: + static int const PERIOD; + static int const TABLE_SIZE = 32; + + // members + private: + LfoParam mParam; // size 0x10, offset 0x00 + u32 mDelayCounter; // size 0x04, offset 0x10 + f32 mCounter; // size 0x04, offset 0x14 + }; // size 0x18 +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_LFO_H diff --git a/include/nw4r/snd/snd_McsSoundArchive.h b/include/nw4r/snd/snd_McsSoundArchive.h new file mode 100644 index 00000000..1990634a --- /dev/null +++ b/include/nw4r/snd/snd_McsSoundArchive.h @@ -0,0 +1,47 @@ +#ifndef NW4R_SND_MCS_SOUND_ARCHIVE_H +#define NW4R_SND_MCS_SOUND_ARCHIVE_H + +/******************************************************************************* + * headers + */ + +#include + +#include "SoundArchive.h" + +#include "../ut/FileStream.h" + +/******************************************************************************* + * classes and functions + */ + +namespace nw4r { namespace snd +{ + class McsSoundArchive : public SoundArchive + { + class McsFileStream; + McsSoundArchive(); + }; + + class McsSoundArchive::McsFileStream : public ut::FileStream + { + McsFileStream(); + virtual ut::detail::RuntimeTypeInfo const *GetRuntimeTypeInfo() const { return 0; } + virtual void Close() {} + virtual s32 Read(void *, u32) { return 0; } + virtual bool IsBusy() const { return 0; } + virtual bool CanAsync() const { return 0; } + virtual bool CanRead() const { return 0; } + virtual bool CanWrite() const { return 0; } + virtual u32 GetSize() const { return 0; } + virtual void Seek(s32, u32) {} + virtual void Cancel() {} + virtual bool CancelAsync(StreamCallback *, void *) { return 0; } + virtual bool CanSeek() const { return 0; } + virtual bool CanCancel() const { return 0; } + virtual u32 Tell() const { return 0; } + friend class McsSoundArchive; + }; +}} // namespace nw4r::snd + +#endif // NW4R_SND_MCS_SOUND_ARCHIVE_H diff --git a/include/nw4r/snd/snd_MemorySoundArchive.h b/include/nw4r/snd/snd_MemorySoundArchive.h index 02b414c9..48a86ec5 100644 --- a/include/nw4r/snd/snd_MemorySoundArchive.h +++ b/include/nw4r/snd/snd_MemorySoundArchive.h @@ -1,44 +1,107 @@ #ifndef NW4R_SND_MEMORY_SOUND_ARCHIVE_H #define NW4R_SND_MEMORY_SOUND_ARCHIVE_H -#include "nw4r/snd/snd_SoundArchive.h" -#include "nw4r/snd/snd_SoundArchiveFile.h" -#include "nw4r/types_nw4r.h" -#include "nw4r/ut.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { +#include -class MemorySoundArchive : public SoundArchive { -private: - class MemoryFileStream; +// WARNING: DO NOT REORDER these #include directives, data pooling depends on it -public: - MemorySoundArchive(); - virtual ~MemorySoundArchive(); // at 0x8 +// clang-format off +#include "../ut/FileStream.h" // This needs to be +#include "SoundArchive.h" // before this +// clang-format on - virtual const void *detail_GetFileAddress(u32 id) const; // at 0xC - virtual const void *detail_GetWaveDataFileAddress(u32 id) const; // at 0x10 +#include "SoundArchiveFile.h" - virtual int detail_GetRequiredStreamBufferSize() const; // at 0x14 +/******************************************************************************* + * classes and functions + */ - virtual ut::FileStream *OpenStream(void *pBuffer, int size, u32 offset, - u32 length) const; // at 0x18 +namespace nw4r { namespace snd +{ + // TODO: get a specific source (name, game id) + /* dwarfv1_megadump.txt:284789-284793 + * This is the struct info for nw4hbm::snd::MemorySoundArchive from an + * older version of the HBM library that actually uses it, but it seems + * similar enough to the current version, so it is used as a reference here. + */ + class MemorySoundArchive : public SoundArchive + { + // nested types + private: + class MemoryFileStream; - virtual ut::FileStream *OpenExtStream( - void *pBuffer, int size, const char *pExtPath, u32 offset, - u32 length - ) const; // at 0x1C + // methods + public: + // cdtors + MemorySoundArchive(); + virtual ~MemorySoundArchive(); - bool Setup(const void *pBuffer); - void Shutdown(); + // virtual function ordering + // vtable SoundArchive + virtual void const *detail_GetFileAddress(u32 fileId) const; + virtual void const *detail_GetWaveDataFileAddress(u32 fileId) const; + virtual int detail_GetRequiredStreamBufferSize() const; + virtual ut::FileStream *OpenStream(void *buffer, int size, u32 begin, + u32 length) const; + virtual ut::FileStream *OpenExtStream(void *buffer, int size, + char const *extFilePath, + u32 begin, u32 length) const; -private: - const void *mData; // at 0x108 - detail::SoundArchiveFileReader mFileReader; // at 0x10C -}; + // methods + bool Setup(void const *soundArchiveData); + void Shutdown(); -} // namespace snd -} // namespace nw4r + // members + private: + /* base SoundArchive */ // size 0x108, offset 0x000 + void const *mData; // size 0x004, offset 0x108 + detail::SoundArchiveFileReader mFileReader; // size 0x044, offset 0x10c + }; // size 0x150 -#endif + // NOTE: Must be completed after MemorySoundArchive for data ordering + // TODO: get a specific source (name, game id) + /* dwarfv1_megadump.txt:293462-293467 + * This is the struct info for + * nw4hbm::snd::MemorySoundArchive::MemoryFileStream from an older version + * of the HBM library that actually uses it, but it seems similar enough to + * the current version, so it is used as a reference here. + */ + class MemorySoundArchive::MemoryFileStream : public ut::FileStream + { + // methods + public: + // cdtors + MemoryFileStream(void const *buffer, u32 size); + virtual ~MemoryFileStream() {} + + // virtual function ordering + // vtable ut::IOStream + virtual void Close(); + virtual s32 Read(void *buf, u32 length); + + // vtable ut::FileStream + virtual void Seek(s32 offset, u32 origin); + + // For instantiation ordering (WARNING: Do not rearrange) + virtual bool CanSeek() const { return true; } + virtual bool CanCancel() const { return true; } + virtual bool CanAsync() const { return false; } + virtual bool CanRead() const { return true; } + virtual bool CanWrite() const { return false; } + virtual u32 Tell() const { return mPosition; } + virtual u32 GetSize() const { return mSize; } + + // members + private: + /* base ut::FileStream */ // size 0x14, offset 0x00 + void const *mBuffer; // size 0x04, offset 0x14 + u32 mSize; // size 0x04, offset 0x18 + u32 mPosition; // size 0x04, offset 0x1c + }; // size 0x20 +}} // namespace nw4r::snd + +#endif // NW4R_SND_MEMORY_SOUND_ARCHIVE_H diff --git a/include/nw4r/snd/snd_MidiSeqPlayer.h b/include/nw4r/snd/snd_MidiSeqPlayer.h new file mode 100644 index 00000000..744537b0 --- /dev/null +++ b/include/nw4r/snd/snd_MidiSeqPlayer.h @@ -0,0 +1,23 @@ +#ifndef NW4R_SND_MIDI_SEQ_PLAYER_H +#define NW4R_SND_MIDI_SEQ_PLAYER_H + +/******************************************************************************* + * headers + */ + +#include "SeqPlayer.h" + +/******************************************************************************* + * classes and functions + */ + +namespace nw4r { namespace snd { namespace detail +{ + class MidiSeqPlayer : public SeqPlayer + { + MidiSeqPlayer(); + virtual void ChannelCallback(Channel *) {} + }; +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_MIDI_SEQ_PLAYER_H diff --git a/include/nw4r/snd/snd_MidiSeqTrack.h b/include/nw4r/snd/snd_MidiSeqTrack.h new file mode 100644 index 00000000..f0a40952 --- /dev/null +++ b/include/nw4r/snd/snd_MidiSeqTrack.h @@ -0,0 +1,22 @@ +#ifndef NW4R_SND_MIDI_SEQ_TRACK_H +#define NW4R_SND_MIDI_SEQ_TRACK_H + +/******************************************************************************* + * headers + */ + +#include "SeqTrack.h" + +/******************************************************************************* + * classes and functions + */ + +namespace nw4r { namespace snd { namespace detail +{ + class MidiSeqTrack : public SeqTrack + { + MidiSeqTrack(); + }; +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_MIDI_SEQ_TRACK_H diff --git a/include/nw4r/snd/snd_MidiSeqTrackAllocator.h b/include/nw4r/snd/snd_MidiSeqTrackAllocator.h new file mode 100644 index 00000000..b0669161 --- /dev/null +++ b/include/nw4r/snd/snd_MidiSeqTrackAllocator.h @@ -0,0 +1,22 @@ +#ifndef NW4R_SND_MIDI_SEQ_TRACK_ALLOCATOR_H +#define NW4R_SND_MIDI_SEQ_TRACK_ALLOCATOR_H + +/******************************************************************************* + * headers + */ + +#include "SeqTrackAllocator.h" + +/******************************************************************************* + * classes and functions + */ + +namespace nw4r { namespace snd { namespace detail +{ + class MidiSeqTrackAllocator : public SeqTrackAllocator + { + MidiSeqTrackAllocator(); + }; +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_MIDI_SEQ_TRACK_ALLOCATOR_H diff --git a/include/nw4r/snd/snd_MmlParser.h b/include/nw4r/snd/snd_MmlParser.h index d8610f2e..b4884a21 100644 --- a/include/nw4r/snd/snd_MmlParser.h +++ b/include/nw4r/snd/snd_MmlParser.h @@ -1,160 +1,195 @@ #ifndef NW4R_SND_MML_PARSER_H #define NW4R_SND_MML_PARSER_H -#include "nw4r/snd/snd_SeqTrack.h" -#include "nw4r/types_nw4r.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { -namespace detail { +#include -// Forward declarations -class MmlSeqTrack; -class SeqPlayer; +#include "MmlSeqTrack.h" -class MmlParser { -public: - enum SeqArgType { - SEQ_ARG_U8, - SEQ_ARG_S16, - SEQ_ARG_VMIDI, - SEQ_ARG_RANDOM, - SEQ_ARG_VARIABLE - }; +/******************************************************************************* + * classes and functions + */ - static const int CALL_STACK_MAX_DEPTH = 3; +// forward declarations +namespace nw4r { namespace snd { namespace detail { class Channel; }}} +namespace nw4r { namespace snd { namespace detail { class SeqPlayer; }}} +namespace nw4r { namespace snd { namespace detail { class SeqTrack; }}} -public: - virtual void CommandProc(MmlSeqTrack *pTrack, u32 command, s32 arg1, - s32 arg2) const; // at 0x8 +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x31383 + class MmlParser + { + // enums + private: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x31279 + enum SeqArgType + { + SEQ_ARG_NONE, - virtual Channel *NoteOnCommandProc( - MmlSeqTrack *pTrack, int key, int velocity, s32 length, - bool tie - ) const; // at 0xC + SEQ_ARG_U8, + SEQ_ARG_S16, + SEQ_ARG_VMIDI, + SEQ_ARG_RANDOM, + SEQ_ARG_VARIABLE, + }; - ParseResult Parse(MmlSeqTrack *pTrack, bool doNoteOn) const; + enum MmlCommand + { + MML_CMD_MIN = 0x80, // <80 -> MML note, not a command + MML_CMD_MAX = 0xff, - volatile s16 *GetVariablePtr(SeqPlayer *pPlayer, SeqTrack *pTrack, int i) const; + MML_CMD_MASK = 0x80, + MML_CMD_SET_MASK = 0xf0, - static void EnablePrintVar(bool enable) { - mPrintVarEnabledFlag = enable; - } + MML_WAIT = 0x80, + MML_SET_PRGNO, -private: - enum MmlSeqData { - MML_CMD_MIN = 0x80, // <80 -> MML note, not a command + MML_OPEN_TRACK = 0x88, + MML_JUMP, + MML_CALL, - MML_WAIT = 0x80, // 0x80 - MML_SET_PRGNO, // 0x81 - MML_OPENTRACK = 0x88, // 0x88 - MML_JUMP, // 0x89 - MML_CALL, // 0x8A + MML_ARG_1_RANDOM = 0xa0, + MML_ARG_1_VARIABLE, + MML_EXEC_IF, + MML_ARG_2_S16, + MML_ARG_2_RANDOM, + MML_ARG_2_VARIABLE, - MML_RNDPARAM = 0xA0, // 0xA0 - MML_INDPARAM, // 0xA1 - MML_EXECIF, // 0xA2 + MML_SET_TIMEBASE = 0xb0, + MML_SET_ENV_HOLD, + MML_SET_MONOPHONIC, + MML_SET_TRACK_VELOCITY_RANGE, + MML_SET_BIQUAD_TYPE, + MML_SET_BIQUAD_VALUE, - MML_SET_TIMEBASE = 0xB0, // 0xB0 + MML_SET_PAN = 0xc0, + MML_SET_TRACK_VOLUME, + MML_SET_PLAYER_VOLUME, + MML_SET_TRANSPOSE, + MML_SET_PITCH_BEND, + MML_SET_BEND_RANGE, + MML_SET_PRIORITY, + MML_SET_NOTE_WAIT, + MML_SET_TIE, + MML_SET_PORTAMENTO, + MML_SET_LFO_DEPTH, + MML_SET_LFO_SPEED, + MML_SET_LFO_TARGET, + MML_SET_LFO_RANGE, + MML_SET_PORTASPEED, + MML_SET_PORTATIME, - MML_SET_PAN = 0xC0, // 0xC0 - MML_SET_TRACK_VOLUME, // 0xC1 - MML_SET_PLAYER_VOLUME, // 0xC2 - MML_SET_TRANSPOSE, // 0xC3 - MML_SET_PITCHBEND, // 0xC4 - MML_SET_BENDRANGE, // 0xC5 - MML_SET_PRIORITY, // 0xC6 - MML_SET_NOTEWAIT, // 0xC7 - MML_SET_TIE, // 0xC8 - MML_SET_PORTAMENTO, // 0xC9 - MML_SET_LFODEPTH, // 0xCA - MML_SET_LFOSPEED, // 0xCB - MML_SET_LFOTARGET, // 0xCC - MML_SET_LFORANGE, // 0xCD - MML_SET_PORTASPEED, // 0xCE - MML_SET_PORTATIME, // 0xCF + MML_SET_ATTACK = 0xd0, + MML_SET_DECAY, + MML_SET_SUSTAIN, + MML_SET_RELEASE, + MML_LOOP_START, + MML_SET_TRACK_VOLUME2, + MML_PRINT_VAR, + MML_SET_SURROUND_PAN, + MML_SET_LPF_FREQ, + MML_SET_FX_SEND_A, + MML_SET_FX_SEND_B, + MML_SET_MAIN_SEND, + MML_SET_INIT_PAN, + MML_SET_MUTE, + MML_SET_FX_SEND_C, + MML_SET_DAMPER, - MML_SET_ATTACK = 0xD0, // 0xD0 - MML_SET_DECAY, // 0xD1 - MML_SET_SUSTAIN, // 0xD2 - MML_SET_RELEASE, // 0xD3 - MML_LOOP_START, // 0xD4 - MML_SET_TRACK_VOLUME2, // 0xD5 - MML_PRINTVAR, // 0xD6 - MML_SET_SURROUNDPAN, // 0xD7 - MML_SET_LPFFREQ, // 0xD8 - MML_SET_FXSEND_A, // 0xD9 - MML_SET_FXSEND_B, // 0xDA - MML_SET_MAINSEND, // 0xDB - MML_SET_INITPAN, // 0xDC - MML_SET_MUTE, // 0xDD - MML_SET_FXSEND_C, // 0xDE - MML_SET_DAMPER, // 0xDF + MML_SET_LFO_DELAY = 0xe0, + MML_SET_TEMPO, + MML_SET_E2, + MML_SET_SWEEP_PITCH, - MML_SET_LFODELAY = 0xE0, // 0xE0 - MML_SET_TEMPO, // 0xE1 - MML_CMD_E2h, // 0xE2 - MML_SET_SWEEPPITCH, // 0xE3 + MML_RESET_ADSR = 0xfb, + MML_LOOP_END, + MML_RET, + MML_ALLOC_TRACK, + MML_EOF + }; - MML_LOOP_END = 0xFC, // 0xFC - MML_RET, // 0xFD - MML_ALLOCTRACK, // 0xFE - MML_EOF, // 0xFF + enum MmlExCommand + { + MML_EX_COMMAND = 0xf0, - MML_CMD_MASK = 0x80, - MML_CMD_SET_MASK = 0xF0, + MML_EX_CMD_MAX = 0xffff, - MML_CMD_MAX = 0xFF, // >FF -> EX command + MML_EX_ARITHMETIC = 0x80, + MML_EX_SET = 0x80, + MML_EX_APL, + MML_EX_AMI, + MML_EX_AMU, + MML_EX_ADV, + MML_EX_ALS, + MML_EX_RND, + MML_EX_AAD, + MML_EX_AOR, + MML_EX_AER, + MML_EX_ACO, + MML_EX_AMD, - // (MML_EX_COMMAND << 8) | Command - MML_EX_COMMAND = 0xF0, + MML_EX_LOGIC = 0x90, + MML_EX_EQ = 0x90, + MML_EX_GE, + MML_EX_GT, + MML_EX_LE, + MML_EX_LT, + MML_EX_NE, - // MML EX arithmetic command set - MML_EX_ARITHMETIC = 0x80, // 0xF0 0x8X - MML_EX_SET = 0x80, // 0xF0 0x80 - MML_EX_APL, // 0xF0 0x81 - MML_EX_AMI, // 0xF0 0x82 - MML_EX_AMU, // 0xF0 0x83 - MML_EX_ADV, // 0xF0 0x84 - MML_EX_ALS, // 0xF0 0x85 - MML_EX_RND, // 0xF0 0x86 - MML_EX_AAD, // 0xF0 0x87 - MML_EX_AOR, // 0xF0 0x88 - MML_EX_AER, // 0xF0 0x89 - MML_EX_ACO, // 0xF0 0x8A - MML_EX_AMD, // 0xF0 0x8B + MML_EX_USERPROC = 0xe0, + }; - // MML EX logic command set - MML_EX_LOGIC = 0x90, // 0xF0 0x9X - MML_EX_EQ = 0x90, // 0xF0 0x90 - MML_EX_GE, // 0xF0 0x91 - MML_EX_GT, // 0xF0 0x92 - MML_EX_LE, // 0xF0 0x93 - MML_EX_LT, // 0xF0 0x94 - MML_EX_NE, // 0xF0 0x95 + // methods + public: + // virtual function ordering + // vtable MmlParser + virtual void CommandProc(MmlSeqTrack *track, u32 command, + s32 commandArg1, s32 commandArg2) const; + virtual Channel *NoteOnCommandProc(MmlSeqTrack *track, int key, + int velocity, s32 length, + bool tieFlag) const; - // MML EX userproc callback - MML_EX_USERPROC = 0xE0, // 0xF0 0xE0 + // methods + MmlSeqTrack::ParseResult Parse(MmlSeqTrack *track, bool doNoteOn) const; - MML_EX_CMD_MAX = 0xFFFF, // >FFFF -> Invalid command - }; + s16 volatile *GetVariablePtr(SeqPlayer *player, SeqTrack *track, + int varNo) const; -private: - u8 ReadByte(const u8 **ppData) const { - return *(*ppData)++; - } + static void EnablePrintVar(bool enable) + { + mPrintVarEnabledFlag = enable; + } - u16 Read16(const u8 **ppData) const; - u32 Read24(const u8 **ppData) const; - s32 ReadVar(const u8 **ppData) const; - s32 ReadArg(const u8 **ppData, SeqPlayer *pPlayer, SeqTrack *pTrack, SeqArgType type) const; + static u32 ParseAllocTrack(void const *baseAddress, u32 seqOffset, + byte4_t *allocTrack); -private: - static bool mPrintVarEnabledFlag; -}; + private: + byte1_t ReadByte(byte_t const **ptr) const { return *(*ptr)++; } + byte2_t Read16(byte_t const **ptr) const; + byte4_t Read24(byte_t const **ptr) const; + s32 ReadVar(byte_t const **ptr) const; + s32 ReadArg(byte_t const **ptr, SeqPlayer *player, SeqTrack *track, + SeqArgType argType) const; -} // namespace detail -} // namespace snd -} // namespace nw4r + // static members + public: + static int const TEMPO_MAX = 1023; + static int const TEMPO_MIN = 0; + static int const CALL_STACK_DEPTH = 3; + static int const SURROUND_PAN_CENTER; + static int const PAN_CENTER = 64; -#endif + private: + static bool mPrintVarEnabledFlag; + + // members + private: + /* vtable */ // size 0x04, offset 0x00 + }; // size 0x04 +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_MML_PARSER_H diff --git a/include/nw4r/snd/snd_MmlSeqTrack.h b/include/nw4r/snd/snd_MmlSeqTrack.h index 547cdd0f..3011d1fb 100644 --- a/include/nw4r/snd/snd_MmlSeqTrack.h +++ b/include/nw4r/snd/snd_MmlSeqTrack.h @@ -1,47 +1,41 @@ #ifndef NW4R_SND_MML_SEQ_TRACK_H #define NW4R_SND_MML_SEQ_TRACK_H -#include "nw4r/snd/snd_MmlParser.h" -#include "nw4r/snd/snd_SeqTrack.h" -#include "nw4r/types_nw4r.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { -namespace detail { +#include "SeqTrack.h" -// Forward declarations -class MmlParser; +/******************************************************************************* + * classes and functions + */ -class MmlSeqTrack : public SeqTrack { -public: - struct MmlParserParam { - bool cmpFlag; // at 0x0 - bool noteWaitFlag; // at 0x1 - bool tieFlag; // at 0x2 - u8 loopCount[MmlParser::CALL_STACK_MAX_DEPTH]; // at 0x3 - u8 callStackDepth; // at 0x6 - const u8 *callStack[MmlParser::CALL_STACK_MAX_DEPTH]; // at 0x8 - }; +// forward declarations +namespace nw4r { namespace snd { namespace detail { class MmlParser; }}} -public: - MmlSeqTrack(); - virtual ParseResult Parse(bool doNoteOn); // at 0xC +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x31308 + class MmlSeqTrack : public SeqTrack + { + // methods + public: + // cdtors + MmlSeqTrack(); - void SetMmlParser(const MmlParser *pParser) { - mParser = pParser; - } + // virtual function ordering + // vtable SeqTrack + virtual ParseResult Parse(bool doNoteOn); - MmlParserParam &GetMmlParserParam() { - return mMmlParserParam; - } + // methods + void SetMmlParser(MmlParser const *parser) { mParser = parser; } -private: - const MmlParser *mParser; // at 0xC0 - MmlParserParam mMmlParserParam; // at 0xC4 -}; + // members + private: + /* base SeqTrack */ // size 0xc8, offset 0x00 + MmlParser const *mParser; // size 0x04, offset 0xc8 + }; // size 0xcc +}}} // namespace nw4r::snd::detail -} // namespace detail -} // namespace snd -} // namespace nw4r - -#endif +#endif // NW4R_SND_MML_SEQ_TRACK_H diff --git a/include/nw4r/snd/snd_MmlSeqTrackAllocator.h b/include/nw4r/snd/snd_MmlSeqTrackAllocator.h index a4ae2918..d9fa8fb6 100644 --- a/include/nw4r/snd/snd_MmlSeqTrackAllocator.h +++ b/include/nw4r/snd/snd_MmlSeqTrackAllocator.h @@ -1,40 +1,52 @@ #ifndef NW4R_SND_MML_SEQ_TRACK_ALLOCATOR_H #define NW4R_SND_MML_SEQ_TRACK_ALLOCATOR_H -#include "nw4r/snd/snd_InstancePool.h" -#include "nw4r/snd/snd_SeqTrackAllocator.h" -#include "nw4r/types_nw4r.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { -namespace detail { +#include // u32 -// Forward declarations -class MmlParser; -class SeqPlayer; -class SeqTrack; +#include "InstancePool.h" +#include "SeqTrackAllocator.h" -class MmlSeqTrackAllocator : public SeqTrackAllocator { -public: - MmlSeqTrackAllocator(MmlParser *pParser) : mParser(pParser) {} +/******************************************************************************* + * classes and functions + */ - virtual SeqTrack *AllocTrack(SeqPlayer *pPlayer); // at 0xC - virtual void FreeTrack(SeqTrack *pTrack); // at 0x10 +// forward declarations +namespace nw4r { namespace snd { namespace detail { class MmlParser; }}} +namespace nw4r { namespace snd { namespace detail { class MmlSeqTrack; }}} - virtual int GetAllocatableTrackCount() const { - return mTrackPool.Count(); - } // at 0x14 +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x314e0 + class MmlSeqTrackAllocator : public SeqTrackAllocator + { + // methods + public: + // cdtors + MmlSeqTrackAllocator(MmlParser *parser) : mParser(parser) {} - u32 Create(void *pBuffer, u32 size); - void Destroy(void *pBuffer, u32 size); + // virtual function ordering + // vtable SeqTrackAllocator + virtual SeqTrack *AllocTrack(SeqPlayer *player); + virtual void FreeTrack(SeqTrack *track); + virtual int GetAllocatableTrackCount() const + { + return mTrackPool.Count(); + } -private: - MmlParser *mParser; // at 0x4 - InstancePool mTrackPool; // at 0x8 -}; + // methods + u32 Create(void *buffer, u32 size); + void Destroy(void *buffer, u32 size); -} // namespace detail -} // namespace snd -} // namespace nw4r + // members + private: + /* base SeqTrackAllocator */ // size 0x04, offset 0x00 + MmlParser *mParser; // size 0x04, offset 0x04 + InstancePool mTrackPool; // size 0x04, offset 0x08 + }; // size 0x0c +}}} // namespace nw4r::snd::detail -#endif +#endif // NW4R_SND_MML_SEQ_TRACK_ALLOCATOR_H diff --git a/include/nw4r/snd/snd_MoveValue.h b/include/nw4r/snd/snd_MoveValue.h index 0ee38d2d..3d2def72 100644 --- a/include/nw4r/snd/snd_MoveValue.h +++ b/include/nw4r/snd/snd_MoveValue.h @@ -1,57 +1,67 @@ #ifndef NW4R_SND_MOVE_VALUE_H #define NW4R_SND_MOVE_VALUE_H -#include "nw4r/types_nw4r.h" -namespace nw4r { -namespace snd { -namespace detail { +/******************************************************************************* + * classes and functions + */ -template -class MoveValue { -public: - MoveValue() : mOrigin(TValue()), mTarget(TValue()), mFrame(TTime()), mCounter(TTime()) {} +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x27b93, 0x28fef... + template + class MoveValue + { + // methods + public: + // cdtors + MoveValue() : + mOrigin (TValue()), + mTarget (TValue()), + mFrame (TTime ()), + mCounter (TTime ()) + { + } - void InitValue(TValue t1) { - mOrigin = t1; - mTarget = t1; - mFrame = 0; - mCounter = 0; - } + // methods + TValue GetValue() const + { + if (IsFinished()) + return mTarget; - bool IsFinished() const { - return mCounter >= mFrame; - } + return mOrigin + mCounter * (mTarget - mOrigin) / mFrame; + } - TValue GetValue() const { - if (IsFinished()) { - return mTarget; - } + void SetTarget(TValue targetValue, TTime frames) + { + mOrigin = GetValue(); + mTarget = targetValue; + mFrame = frames; + mCounter = TTime(); + } - return mOrigin + mCounter * (mTarget - mOrigin) / mFrame; - } + bool IsFinished() const { return mCounter >= mFrame; } - void Update() { - if (mCounter < mFrame) { - mCounter++; - } - } + void InitValue(TValue value) + { + mOrigin = value; + mTarget = value; + mFrame = TTime(); + mCounter = TTime(); + } - void SetTarget(TValue target, TTime frame) { - mOrigin = GetValue(); - mTarget = target; - mFrame = frame; - mCounter = 0; - } + void Update() + { + if (mCounter < mFrame) + mCounter++; + } -private: - TValue mOrigin; // at 0x0 - TValue mTarget; // at 0x4 - TTime mFrame; // at 0x8 - TTime mCounter; // at 0xC -}; + // members + private: + TValue mOrigin; // size TValue, offset 0x00 + TValue mTarget; // size TValue, offset TValue + TTime mFrame; // size TTime, offset TValue * 2 + TTime mCounter; // size TTine, offset TValue * 2 + TTime + }; // size TValue * 2 + TTime * 2 +}}} // namespace nw4r::snd::detail -} // namespace detail -} // namespace snd -} // namespace nw4r - -#endif +#endif // NW4R_SND_MOVE_VALUE_H diff --git a/include/nw4r/snd/snd_NoteOnCallback.h b/include/nw4r/snd/snd_NoteOnCallback.h index e6845a5f..5d22ab65 100644 --- a/include/nw4r/snd/snd_NoteOnCallback.h +++ b/include/nw4r/snd/snd_NoteOnCallback.h @@ -1,38 +1,61 @@ #ifndef NW4R_SND_NOTE_ON_CALLBACK_H #define NW4R_SND_NOTE_ON_CALLBACK_H -#include "nw4r/snd/snd_Channel.h" -#include "nw4r/types_nw4r.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { -namespace detail { +#include // register_t -// Forward declarations -class SeqPlayer; +#include "Channel.h" -struct NoteOnInfo { - int prgNo; // at 0x0 - int key; // at 0x4 - int velocity; // at 0x8 - int length; // at 0xC - int initPan; // at 0x10 - int priority; // at 0x14 - int voiceOutCount; // at 0x18 - Channel::ChannelCallback channelCallback; // at 0x1C - u32 channelCallbackData; // at 0x20 -}; +/******************************************************************************* + * types + */ -class NoteOnCallback { -public: - virtual ~NoteOnCallback() {} // at 0x8 +// forward declarations +namespace nw4r { namespace snd { namespace detail { class SeqPlayer; }}} - virtual Channel *NoteOn(SeqPlayer *pPlayer, int bankNo, - const NoteOnInfo &rInfo) = 0; // at 0xC -}; +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2dd4c + struct NoteOnInfo + { + int prgNo; // size 0x04, offset 0x00 + int key; // size 0x04, offset 0x04 + int velocity; // size 0x04, offset 0x08 + int length; // size 0x04, offset 0x0c + int initPan; // size 0x04, offset 0x10 + int priority; // size 0x04, offset 0x14 + int voiceOutCount; // size 0x04, offset 0x18 + Channel::Callback *channelCallback; // size 0x04, offset 0x1c + register_t channelCallbackData; // size 0x04, offset 0x20 + }; // size 0x24 +}}} // namespace nw4r::snd::detail -} // namespace detail -} // namespace snd -} // namespace nw4r +/******************************************************************************* + * classes and functions + */ -#endif +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2e4ab + class NoteOnCallback + { + // methods + public: + // cdtors + virtual ~NoteOnCallback() {} + + // virtual function ordering + // vtable NoteOnCallback + virtual Channel *NoteOn(SeqPlayer *seqPlayer, int bankNo, + NoteOnInfo const ¬eOnInfo) = 0; + + // members + private: + /* vtable */ // size 0x04, offset 0x00 + }; // size 0x04 +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_NOTE_ON_CALLBACK_H diff --git a/include/nw4r/snd/snd_PlayerHeap.h b/include/nw4r/snd/snd_PlayerHeap.h index 7c3dc735..3fa0e766 100644 --- a/include/nw4r/snd/snd_PlayerHeap.h +++ b/include/nw4r/snd/snd_PlayerHeap.h @@ -1,45 +1,70 @@ #ifndef NW4R_SND_PLAYER_HEAP_H #define NW4R_SND_PLAYER_HEAP_H -#include "nw4r/snd/snd_SoundHeap.h" -#include "nw4r/types_nw4r.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { +#include // u32 -// Forward declarations -class SoundPlayer; +#include "SoundMemoryAllocatable.h" -namespace detail { +#include "../ut/LinkList.h" -// Forward declarations -class BasicSound; +/******************************************************************************* + * types + */ -class PlayerHeap : public SoundHeap { -public: - PlayerHeap() : mSound(NULL), mPlayer(NULL) {} - virtual ~PlayerHeap() {} // at 0x8 +// forward declarations +namespace nw4r { namespace snd { namespace detail { class BasicSound; }}} +namespace nw4r { namespace snd { class SoundPlayer; }} - void SetSound(BasicSound *pSound) { - mSound = pSound; - } +/******************************************************************************* + * classes and functions + */ - void SetSoundPlayer(SoundPlayer *pPlayer) { - mPlayer = pPlayer; - } +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x26e9c + class PlayerHeap : public SoundMemoryAllocatable + { + // typedefs + public: + typedef ut::LinkList LinkList; -public: - NW4R_UT_LIST_NODE_DECL(); // at 0x2C + // methods + public: + // cdtors + PlayerHeap(); + virtual ~PlayerHeap(); -private: - BasicSound *mSound; // at 0x34 - SoundPlayer *mPlayer; // at 0x38 -}; + // virtual function ordering + // vtable SoundMemoryAllocatable + virtual void *Alloc(u32 size); -NW4R_UT_LIST_TYPEDEF_DECL(PlayerHeap); + // methods + bool Create(void *startAddress, u32 size); + void Clear(); + void Destroy(); -} // namespace detail -} // namespace snd -} // namespace nw4r + u32 GetFreeSize() const; -#endif + void AttachSound(BasicSound *sound); + void DetachSound(BasicSound *sound); + + void AttachSoundPlayer(SoundPlayer *player) { mPlayer = player; } + + // members + private: + /* base SoundMemoryAllocatable */ // size 0x04, offset 0x00 + BasicSound *mSound; // size 0x04, offset 0x04 + SoundPlayer *mPlayer; // size 0x04, offset 0x08 + void *mStartAddress; // size 0x04, offset 0x0c + void *mEndAddress; // size 0x04, offset 0x10 + void *mAllocAddress; // size 0x04, offset 0x14 + public: + ut::LinkListNode mLink; // size 0x08, offset 0x18 + }; // size 0x20 +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_PLAYER_HEAP_H diff --git a/include/nw4r/snd/snd_SeqFile.h b/include/nw4r/snd/snd_SeqFile.h index f6ac4eb3..51801403 100644 --- a/include/nw4r/snd/snd_SeqFile.h +++ b/include/nw4r/snd/snd_SeqFile.h @@ -1,48 +1,108 @@ #ifndef NW4R_SND_SEQ_FILE_H #define NW4R_SND_SEQ_FILE_H -#include "nw4r/types_nw4r.h" -#include "nw4r/ut.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { -namespace detail { -namespace SeqFile { +#include +#include -struct Header { - ut::BinaryFileHeader fileHeader; // at 0x0 - u32 dataBlockOffset; // at 0x10 - u32 dataBlockSize; // at 0x14 - u32 labelBlockOffset; // at 0x18 - u32 labelBlockSize; // at 0x1C -}; +#include "Util.h" // Util::Table -struct DataBlock { - ut::BinaryBlockHeader blockHeader; // at 0x0 - u32 baseOffset; // at 0x8 -}; +#include "../ut/binaryFileFormat.h" -} // namespace SeqFile +/******************************************************************************* + * types + */ -class SeqFileReader { -public: - static const u32 SIGNATURE = 'RSEQ'; - static const int VERSION = NW4R_VERSION(1, 0); +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2d23df (yes, really!) + struct SeqFile + { + /* Header */ -public: - explicit SeqFileReader(const void *pSeqBin); + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2ce38c + struct Header + { + ut::BinaryFileHeader fileHeader; // size 0x10, offset 0x00 + u32 dataBlockOffset; // size 0x04, offset 0x10 + u32 dataBlockSize; // size 0x04, offset 0x14 + u32 labelBlockOffset; // size 0x04, offset 0x18 + u32 labelBlockSize; // size 0x04, offset 0x1c + }; // size 0x20 - bool IsValidFileHeader(const void *pSeqBin); + /* LabelBlock */ - const void *GetBaseAddress() const; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2ce87c + struct LabelInfo + { + u32 offset; // size 0x04, offset 0x00 + u32 nameLen; // size 0x04, offset 0x04 + char name[1]; // size 0x01, offset 0x08 + /* 3 bytes padding */ + }; // size 0x0c -private: - const SeqFile::Header *mHeader; // at 0x0 - const SeqFile::DataBlock *mDataBlock; // at 0x4 -}; + static byte4_t const SIGNATURE_LABEL_BLOCK; -} // namespace detail -} // namespace snd -} // namespace nw4r + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2ce7dc + struct LabelBlock + { + ut::BinaryBlockHeader blockHeader; // size 0x08, offset 0x00 + Util::Table labelInfoTable; // size 0x08, offset 0x08 + }; // size 0x10 -#endif + /* DataBlock */ + + static byte4_t const SIGNATURE_DATA_BLOCK = + NW4R_FOUR_BYTE('D', 'A', 'T', 'A'); + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2ce491 + struct DataBlock + { + ut::BinaryBlockHeader blockHeader; // size 0x08, offset 0x00 + u32 baseOffset; // size 0x04, offset 0x08 + }; // size 0x0c + + /* SeqFile */ + + static byte4_t const SIGNATURE_FILE = + NW4R_FOUR_BYTE('R', 'S', 'E', 'Q'); + static int const FILE_VERSION = NW4R_FILE_VERSION(1, 2); + }; // "namespace" SeqFile +}}} // namespace nw4r::snd::detail + +/******************************************************************************* + * classes and functions + */ + +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2ce50a + class SeqFileReader + { + // methods + public: + // cdtors + SeqFileReader(void const *seqData); + + // methods + void const *GetBaseAddress() const; + + bool IsValidFileHeader(void const *seqData); + + bool ReadOffsetByLabel(char const *labelName, u32 *offsetPtr) const; + + // static members + public: + static u16 const SUPPORTED_FILE_VERSION = NW4R_FILE_VERSION(1, 0); + + // members + private: + SeqFile::Header const *mHeader; // size 0x04, offset 0x00 + SeqFile::DataBlock const *mDataBlock; // size 0x04, offset 0x04 + }; // size 0x08 +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_SEQ_FILE_H diff --git a/include/nw4r/snd/snd_SeqPlayer.h b/include/nw4r/snd/snd_SeqPlayer.h index 353d5402..45d1411d 100644 --- a/include/nw4r/snd/snd_SeqPlayer.h +++ b/include/nw4r/snd/snd_SeqPlayer.h @@ -1,188 +1,203 @@ #ifndef NW4R_SND_SEQ_PLAYER_H #define NW4R_SND_SEQ_PLAYER_H -#include "nw4r/snd/snd_BasicPlayer.h" -#include "nw4r/snd/snd_DisposeCallback.h" -#include "nw4r/snd/snd_SoundThread.h" -#include "nw4r/types_nw4r.h" -#include "nw4r/ut.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { -namespace detail { +#include // ATTR_UNUSED +#include -// Forward declarations -class Channel; -class NoteOnInfo; -class NoteOnCallback; -class SeqTrack; -class SeqTrackAllocator; +#include "BasicPlayer.h" +#include "DisposeCallbackManager.h" // DisposeCallback +#include "SoundThread.h" -class SeqPlayer : public BasicPlayer, public DisposeCallback, public SoundThread::PlayerCallback { -public: - struct ParserPlayerParam { - u8 volume; // at 0x0 - u8 priority; // at 0x1 - u8 timebase; // at 0x2 - u16 tempo; // at 0x4 - NoteOnCallback *callback; // at 0x8 - }; +/******************************************************************************* + * types + */ - enum OffsetType { - OFFSET_TYPE_TICK, - OFFSET_TYPE_MILLISEC - }; +// forward declarations +namespace nw4r { namespace snd { namespace detail { class Channel; }}} +namespace nw4r { namespace snd { namespace detail { class NoteOnCallback; }}} +namespace nw4r { namespace snd { namespace detail { struct NoteOnInfo; }}} +namespace nw4r { namespace snd { namespace detail { class SeqTrack; }}} +namespace nw4r { namespace snd { namespace detail { class SeqTrackAllocator; }}} - enum SetupResult { - SETUP_SUCCESS, - SETUP_ERR_CANNOT_ALLOCATE_TRACK, - SETUP_ERR_UNKNOWN - }; +namespace nw4r { namespace snd +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2dbb2 + struct SeqUserprocCallbackParam + { + s16 volatile *localVariable; // size 0x04, offset 0x00 + s16 volatile *globalVariable; // size 0x04, offset 0x04 + s16 volatile *trackVariable; // size 0x04, offset 0x08 + bool cmpFlag; // size 0x01, offset 0x0C + /* 3 bytes padding */ + }; // size 0x10 +}} // namespace nw4r::snd - static const int LOCAL_VARIABLE_NUM = 16; - static const int GLOBAL_VARIABLE_NUM = 16; - static const int VARIABLE_NUM = LOCAL_VARIABLE_NUM + GLOBAL_VARIABLE_NUM; +/******************************************************************************* + * classes + */ - static const int TRACK_NUM = 16; +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2dfad + class SeqPlayer : public BasicPlayer, + public DisposeCallback, + public SoundThread::PlayerCallback + { + // enums + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2dee8 + enum OffsetType + { + OFFSET_TYPE_TICK, + OFFSET_TYPE_MILLISEC, + }; -public: - SeqPlayer(); - virtual ~SeqPlayer(); // at 0x8 + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2df3b + enum SetupResult + { + SETUP_SUCCESS, - virtual bool Start(); // at 0xC - virtual void Stop(); // at 0x10 - virtual void Pause(bool flag); // at 0x14 + SETUP_ERR_CANNOT_ALLOCATE_TRACK, - virtual bool IsActive() const { - return mActiveFlag; - } // at 0x18 + SETUP_ERR_UNKNOWN + }; - virtual bool IsStarted() const { - return mStartedFlag; - } // at 0x1C + // nested types + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2cce4 + struct ParserPlayerParam + { + u8 volume; // size 0x01, offset 0x00 + u8 priority; // size 0x01, offset 0x01 + u8 timebase; // size 0x01, offset 0x02 + /* 1 byte padding */ + u16 tempo; // size 0x02, offset 0x04 + /* 2 bytes padding */ + NoteOnCallback *callback; // size 0x04, offset 0x08 + }; // size 0x0c - virtual bool IsPause() const { - return mPauseFlag; - } // at 0x20 + // typedefs + public: + typedef void SeqUserprocCallback(u16 procId, + SeqUserprocCallbackParam *param, + void *callbackData); - virtual void InvalidateData(const void *pStart, - const void *pEnd); // at 0x50 + // methods + public: + // cdtors + SeqPlayer(); + virtual ~SeqPlayer(); - virtual void InvalidateWaveData(const void * /* pStart */, const void * /* pEnd */) {} // at 0x54 + // virtual function ordering + // vtable BasicPlayer + virtual bool Start(); + virtual void Stop(); + virtual void Pause(bool flag); + virtual bool IsActive() const { return mActiveFlag; } + virtual bool IsStarted() const { return mStartedFlag; } + virtual bool IsPause() const { return mPauseFlag; } - virtual void ChannelCallback(Channel * /* pChannel */) {} // at 0x58 + // vtable DisposeCallback + virtual void InvalidateData(void const *start, void const *end); + virtual void InvalidateWaveData(void const *, void const *) {} - virtual void OnUpdateFrameSoundThread() { - Update(); - } // at 0x5C + // vtable SeqPlayer + /* WARNING: must come before SoundThread::PlayerCallback virtual + * functions + */ + virtual void ChannelCallback(Channel *channel ATTR_UNUSED) {} - virtual void OnShutdownSoundThread() { - Stop(); - } // at 0x60 + // vtable SoundThread::PlayerCallback + virtual void OnUpdateFrameSoundThread() { Update(); } + virtual void OnShutdownSoundThread() { Stop(); } - void InitParam(int voices, NoteOnCallback *pCallback); + // methods + SetupResult Setup(SeqTrackAllocator *trackAllocator, u32 allocTracks, + int voiceOutCount, NoteOnCallback *callback); + void Update(); + void Skip(OffsetType offsetType, int offset); + void Shutdown(); - SetupResult Setup(SeqTrackAllocator *pAllocator, u32 allocTrackFlags, int voices, NoteOnCallback *pCallback); - void SetSeqData(const void *pBase, s32 offset); + bool IsReleasePriorityFix() const { return mReleasePriorityFixFlag; } + f32 GetPanRange() const { return mPanRange; } + int GetVoiceOutCount() const { return mVoiceOutCount; } + ParserPlayerParam &GetParserPlayerParam() { return mParserParam; } + SeqTrack *GetPlayerTrack(int trackNo); + s16 volatile *GetVariablePtr(int varNo); - void Skip(OffsetType type, int offset); + void SetReleasePriorityFix(bool fix); + void SetChannelPriority(int priority); + void SetSeqUserprocCallback(SeqUserprocCallback *callback, void *arg); + void SetPlayerTrack(int trackNo, SeqTrack *track); - void SetTempoRatio(f32 tempo); - void SetChannelPriority(int priority); - void SetReleasePriorityFix(bool flag); + f32 CalcTickPerMsec() const + { + return CalcTickPerMinute() / (60 * 1000); + } + f32 CalcTickPerMinute() const + { + return mParserParam.timebase * mParserParam.tempo * mTempoRatio; + } - void SetLocalVariable(int i, s16 value); - static void SetGlobalVariable(int i, s16 value); + void SetSeqData(void const *seqBase, s32 seqOffset); - void SetTrackVolume(u32 trackFlags, f32 volume); - void SetTrackPitch(u32 trackFlags, f32 pitch); + void CallSeqUserprocCallback(u16 procId, SeqTrack *track); + Channel *NoteOn(int bankNo, NoteOnInfo const ¬eOnInfo); - SeqTrack *GetPlayerTrack(int i); - volatile s16 *GetVariablePtr(int i); - void Update(); + static void InitSeqPlayer(); - Channel *NoteOn(int bankNo, const NoteOnInfo &rInfo); + private: + void InitParam(int voiceOutCount, NoteOnCallback *callback); - template - void SetTrackParam(u32 trackFlags, void (SeqTrack::*pSetter)(T), T param) { - ut::AutoInterruptLock lock; + void UpdateChannelParam(); - for (int i = 0; i < TRACK_NUM && trackFlags != 0; trackFlags >>= 1, i++) { - if (trackFlags & 1) { - SeqTrack *pTrack = GetPlayerTrack(i); + void CloseTrack(int trackNo); + void FinishPlayer(); - if (pTrack != NULL) { - (pTrack->*pSetter)(param); - } - } - } - } + int ParseNextTick(bool doNoteOn); // meant to be bool? + void UpdateTick(int msec); + void SkipTick(); - bool IsReleasePriorityFix() const { - return mReleasePriorityFixFlag; - } + // static members + public: + static int const MAX_SKIP_TICK_PER_FRAME = 768; + static int const DEFAULT_TEMPO = 120; + static int const DEFAULT_TIMEBASE = 48; + static int const VARIABLE_DEFAULT_VALUE = -1; + static int const TRACK_NUM_PER_PLAYER = 16; + static int const GLOBAL_VARIABLE_NUM = 16; + static int const PLAYER_VARIABLE_NUM = 16; - f32 GetPanRange() const { - return mPanRange; - } + private: + static s16 mGlobalVariable[GLOBAL_VARIABLE_NUM]; - f32 GetBaseTempo() const { - return mTempoRatio * (mParserParam.timebase * mParserParam.tempo) / 60000.0f; - } + // members + private: + /* base BasicPlayer */ // size 0x0a4, offset 0x000 + /* base DisposeCallback */ // size 0x00c, offset 0x0a4 + /* base SoundThread::PlayerCallback */ // size 0x00c, offset 0x0b0 + bool mActiveFlag; // size 0x001, offset 0x0ec + bool mStartedFlag; // size 0x001, offset 0x0ed + bool mPauseFlag; // size 0x001, offset 0x0ee + bool mReleasePriorityFixFlag; // size 0x001, offset 0x0ef + f32 mPanRange; // size 0x004, offset 0x0f0 + f32 mTempoRatio; // size 0x004, offset 0x0f4 + f32 mTickFraction; // size 0x004, offset 0x0f8 + u32 mSkipTickCounter; // size 0x004, offset 0x0fc + f32 mSkipTimeCounter; // size 0x004, offset 0x100 + s32 mVoiceOutCount; // size 0x004, offset 0x104 + ParserPlayerParam mParserParam; // size 0x00c, offset 0x108 + SeqTrackAllocator *mSeqTrackAllocator; // size 0x004, offset 0x114 + SeqUserprocCallback *mSeqUserprocCallback; // size 0x004, offset 0x118 + void *mSeqUserprocCallbackArg; // size 0x004, offset 0x11c + SeqTrack *mTracks[TRACK_NUM_PER_PLAYER]; // size 0x040, offset 0x120 + s16 volatile mLocalVariable[PLAYER_VARIABLE_NUM]; // size 0x020, offset 0x160 + u32 mTickCounter; // size 0x004, offset 0x180 + }; // size 0x184 +}}} // namespace nw4r::snd::detail - int GetVoiceOutCount() const { - return mVoiceOutCount; - } - - ParserPlayerParam &GetParserPlayerParam() { - return mParserParam; - } - -private: - static const int DEFAULT_TEMPO = 120; - static const int DEFAULT_TIMEBASE = 48; - static const int DEFAULT_PRIORITY = 64; - static const int DEFAULT_VARIABLE_VALUE = -1; - - static const int MAX_SKIP_TICK_PER_FRAME = 768; - -private: - void CloseTrack(int i); - void SetPlayerTrack(int i, SeqTrack *pTrack); - - void FinishPlayer(); - void UpdateChannelParam(); - int ParseNextTick(bool doNoteOn); - - void UpdateTick(int msec); - void SkipTick(); - - static void InitGlobalVariable(); - -private: - bool mActiveFlag; // at 0x8C - bool mStartedFlag; // at 0x8D - bool mPauseFlag; // at 0x8E - bool mReleasePriorityFixFlag; // at 0x8F - - f32 mPanRange; // at 0x90 - f32 mTempoRatio; // at 0x94 - f32 mTickFraction; // at 0x98 - u32 mSkipTickCounter; // at 0x9C - f32 mSkipTimeCounter; // at 0xA0 - int mVoiceOutCount; // at 0xA4 - ParserPlayerParam mParserParam; // at 0xA8 - SeqTrackAllocator *mSeqTrackAllocator; // at 0xB4 - SeqTrack *mTracks[TRACK_NUM]; // at 0xB8 - volatile s16 mLocalVariable[LOCAL_VARIABLE_NUM]; // at 0xF8 - u32 mTickCounter; // at 0x118 - - static volatile s16 mGlobalVariable[LOCAL_VARIABLE_NUM]; - static bool mGobalVariableInitialized; // TYPO -}; - -} // namespace detail -} // namespace snd -} // namespace nw4r - -#endif +#endif // NW4R_SND_SEQ_PLAYER_H diff --git a/include/nw4r/snd/snd_SeqSound.h b/include/nw4r/snd/snd_SeqSound.h index 3c933343..4a258ca1 100644 --- a/include/nw4r/snd/snd_SeqSound.h +++ b/include/nw4r/snd/snd_SeqSound.h @@ -1,123 +1,156 @@ #ifndef NW4R_SND_SEQ_SOUND_H #define NW4R_SND_SEQ_SOUND_H -#include "nw4r/snd/snd_BasicSound.h" -#include "nw4r/snd/snd_SeqPlayer.h" -#include "nw4r/snd/snd_Task.h" -#include "nw4r/types_nw4r.h" -#include "nw4r/ut.h" // IWYU pragma: export -#include "rvl/OS.h" // IWYU pragma: export -namespace nw4r { -namespace snd { +/******************************************************************************* + * headers + */ -// Forward declarations -class SeqSoundHandle; +#include -namespace detail { +#include "BasicSound.h" +#include "debug.h" +#include "SeqPlayer.h" +#include "Task.h" -// Forward declarations -template -class SoundInstanceManager; +#include "../ut/LinkList.h" -class SeqSound : public BasicSound { - friend class SeqSoundHandle; +/******************************************************************************* + * types + */ -public: - NW4R_UT_RTTI_DECL(SeqSound); +// forward declarations +namespace nw4r { namespace snd { namespace detail { class NoteOnCallback; }}} +namespace nw4r { namespace snd { namespace detail { class SeqTrackAllocator; }}} +namespace nw4r { namespace snd { namespace detail { template class SoundInstanceManager; }}} +namespace nw4r { namespace snd { class SeqSoundHandle; }} -public: - explicit SeqSound(SoundInstanceManager *pManager); +namespace nw4r { namespace ut { namespace detail { class RuntimeTypeInfo; }}} +namespace nw4r { namespace ut { class FileStream; }} - virtual void Shutdown(); // at 0x28 - virtual bool IsPrepared() const { - return mPreparedFlag; - } // at 0x2C +/******************************************************************************* + * classes and functions + */ - virtual void SetPlayerPriority(int priority); // at 0x4C +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2ed0a + class SeqSound : public BasicSound + { + // typedefs + public: + /* redeclare with this class instead of BasicSound for + * SoundInstanceManager + */ + typedef ut::LinkList PriorityLinkList; - virtual bool IsAttachedTempSpecialHandle(); // at 0x5C - virtual void DetachTempSpecialHandle(); // at 0x60 + // nested types + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2eb5e + class SeqLoadTask : public Task + { + // typedefs + public: + typedef void Callback(bool result, void const *seqBase, + void *userData); - virtual void InitParam(); // at 0x64 + // methods + public: + // cdtors + SeqLoadTask(); - virtual BasicPlayer &GetBasicPlayer() { - return mSeqPlayer; - } // at 0x68 - virtual const BasicPlayer &GetBasicPlayer() const { - return mSeqPlayer; - } // at 0x6C + // virtual function ordering + // vtable Task + virtual void Execute(); + virtual void Cancel(); + virtual void OnCancel(); - SeqPlayer::SetupResult - Setup(SeqTrackAllocator *pAllocator, u32 allocTrackFlags, int voices, NoteOnCallback *pCallback); + // members + public: + /* base Task */ // size 0x10, offset 0x00 + ut::FileStream *mFileStream; // size 0x04, offset 0x10 + void *mBuffer; // size 0x04, offset 0x14 + int mBufferSize; // size 0x04, offset 0x18 + Callback *mCallback; // size 0x04, offset 0x1c + void *mCallbackData; // size 0x04, offset 0x20 + }; // size 0x24 - void Prepare(const void *pBase, s32 seqOffset, SeqPlayer::OffsetType startType, int startOffset); + // methods + public: + // cdtors + SeqSound(SoundInstanceManager *manager, int priority, + int ambientPriority); - void Prepare(ut::FileStream *pStream, s32 seqOffset, SeqPlayer::OffsetType startType, int startOffset); + // virtual function ordering + // vtable BasicSound + virtual ut::detail::RuntimeTypeInfo const *GetRuntimeTypeInfo() const + { + return &typeInfo; + } + virtual void Shutdown(); + virtual bool IsPrepared() const { return mPreparedFlag; } + virtual bool IsAttachedTempSpecialHandle(); + virtual void DetachTempSpecialHandle(); + virtual void InitParam(); + virtual BasicPlayer &GetBasicPlayer() { return mSeqPlayer; } + virtual BasicPlayer const &GetBasicPlayer() const { return mSeqPlayer; } + virtual void OnUpdatePlayerPriority(); - void Skip(SeqPlayer::OffsetType offsetType, int offset); + // methods + SeqPlayer::SetupResult Setup(SeqTrackAllocator *trackAllocator, + u32 allocTracks, + NoteOnCallback *callback); + void Prepare(void const *seqBase, s32 seqOffset, + SeqPlayer::OffsetType startOffsetType, int startOffset); + void Prepare(ut::FileStream *fileStream, s32 seqOffset, + SeqPlayer::OffsetType startOffsetType, int startOffset); - void SetTempoRatio(f32 tempo); - void SetChannelPriority(int priority); - void SetReleasePriorityFix(bool flag); + void *GetFileStreamBuffer() { return mFileStreamBuffer; } + s32 GetFileStreamBufferSize() { return sizeof mFileStreamBuffer; } - void SetTrackVolume(u32 trackFlags, f32 volume); - void SetTrackPitch(u32 trackFlags, f32 pitch); + void SetReleasePriorityFix(bool fix); + void SetChannelPriority(int priority); + void SetSeqUserprocCallback(SeqPlayer::SeqUserprocCallback *callback, + void *arg); - bool WriteVariable(int i, s16 value); - static bool WriteGlobalVariable(int i, s16 value); + void Skip(SeqPlayer::OffsetType offsetType, int offset); - void *GetFileStreamBuffer() { - return mFileStreamBuffer; - } - s32 GetFileStreamBufferSize() { - return sizeof(mFileStreamBuffer); - } + bool LoadData(SeqLoadTask::Callback *callback, + void *callbackArg); -private: - typedef void (*SeqLoadCallback)(bool success, const void *pBase, void *pCallbackArg); + static void NotifyLoadAsyncEndSeqData(bool result, void const *seqBase, + void *userData); - struct SeqLoadTask : public Task { - SeqLoadTask(); + static DebugSoundType GetSoundType() + { + return DEBUG_SOUND_TYPE_SEQSOUND; + } - virtual void Execute(); // at 0xC - virtual void Cancel(); // at 0x10 - virtual void OnCancel(); // at 0x14 + // static members + public: + static int const FILE_STREAM_BUFFER_SIZE = 128; - ut::FileStream *fileStream; // at 0x10 - void *buffer; // at 0x14 - int bufferSize; // at 0x18 - SeqLoadCallback callback; // at 0x1C - SeqSound *callbackData; // at 0x20 - }; + static ut::detail::RuntimeTypeInfo const typeInfo; - static const int FILE_STREAM_BUFFER_SIZE = 512; + // members + private: + /* base BasicSound */ // size 0x100, offset 0x000 + SeqPlayer mSeqPlayer; // size 0x154, offset 0x100 + SeqSoundHandle *mTempSpecialHandle; // size 0x004, offset 0x254 + SoundInstanceManager *mManager; // size 0x004, offset 0x258 + s32 mSeqOffset; // size 0x004, offset 0x25c + SeqPlayer::OffsetType mStartOffsetType; // size 0x004, offset 0x260 + int mStartOffset; // size 0x004, offset 0x264 + bool volatile mLoadingFlag; // size 0x001, offset 0x268 + bool volatile mPreparedFlag; // size 0x001, offset 0x269 + /* 2 bytes padding */ + ut::FileStream *mFileStream; // size 0x004, offset 0x26c + int mFileStreamBuffer[FILE_STREAM_BUFFER_SIZE]; // size 0x200, offset 0x2a0 + SeqLoadTask mSeqLoadTask; // size 0x024, offset 0x4a0 -private: - bool LoadData(SeqLoadCallback pCalllback, void *pCallbackArg); + // friends + private: + friend class snd::SeqSoundHandle; + }; // size 0x4c4 +}}} // namespace nw4r::snd::detail - static void NotifyLoadAsyncEndSeqData(bool success, const void *pBase, void *pCallbackArg); - -private: - SeqPlayer mSeqPlayer; // at 0xD8 - SeqSoundHandle *mTempSpecialHandle; // at 0x1F4 - SoundInstanceManager *mManager; // at 0x1F8 - - s32 mSeqOffset; // at 0x1FC - SeqPlayer::OffsetType mStartOffsetType; // at 0x200 - int mStartOffset; // at 0x204 - - bool mLoadingFlag; // at 0x208 - volatile bool mPreparedFlag; // at 0x209 - - ut::FileStream *mFileStream; // at 0x20C - char mFileStreamBuffer[FILE_STREAM_BUFFER_SIZE]; // at 0x210 - - SeqLoadTask mSeqLoadTask; // at 0x410 - mutable OSMutex mMutex; // at 0x434 -}; - -} // namespace detail -} // namespace snd -} // namespace nw4r - -#endif +#endif // NW4R_SND_SEQ_SOUND_H diff --git a/include/nw4r/snd/snd_SeqSoundHandle.h b/include/nw4r/snd/snd_SeqSoundHandle.h index bc4b67cf..c4054a31 100644 --- a/include/nw4r/snd/snd_SeqSoundHandle.h +++ b/include/nw4r/snd/snd_SeqSoundHandle.h @@ -1,52 +1,42 @@ #ifndef NW4R_SND_SEQ_SOUND_HANDLE_H #define NW4R_SND_SEQ_SOUND_HANDLE_H -#include "nw4r/snd/snd_SeqSound.h" -#include "nw4r/types_nw4r.h" -#include "nw4r/ut.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { +#include // nullptr -// Forward declarations -class SoundHandle; +#include "../ut/inlines.h" // ut::NonCopyable -class SeqSoundHandle : private ut::NonCopyable { -public: - explicit SeqSoundHandle(SoundHandle *pHandle); - ~SeqSoundHandle() { - DetachSound(); - } +/******************************************************************************* + * types + */ - bool IsAttachedSound() const { - return mSound != NULL; - } +// forward declarations +namespace nw4r { namespace snd { namespace detail { class SeqSound; }}} - void DetachSound(); +/******************************************************************************* + * classes and functions + */ - void SetTempoRatio(f32 tempo) { - if (IsAttachedSound()) { - mSound->SetTempoRatio(tempo); - } - } +namespace nw4r { namespace snd +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2e9b6 + class SeqSoundHandle : private ut::NonCopyable + { + // methods + public: + // methods + bool IsAttachedSound() const { return mSound != nullptr; } - void SetTrackVolume(u32 trackFlags, f32 volume) { - if (IsAttachedSound()) { - mSound->SetTrackVolume(trackFlags, volume); - } - } + void DetachSound(); - void WriteVariable(int i, s16 value) { - if (IsAttachedSound()) { - mSound->WriteVariable(i, value); - } - } + // members + private: + /* base ut::NonCopyable */ // size 0x00, offset 0x00 + detail::SeqSound *mSound; // size 0x04, offset 0x00 + }; // size 0x04 +}} // namespace nw4r::snd -private: - detail::SeqSound *mSound; // at 0x0 -}; - -} // namespace snd -} // namespace nw4r - -#endif +#endif // NW4R_SND_SEQ_SOUND_HANDLE_H diff --git a/include/nw4r/snd/snd_SeqTrack.h b/include/nw4r/snd/snd_SeqTrack.h index ad54b9f6..32b5fa87 100644 --- a/include/nw4r/snd/snd_SeqTrack.h +++ b/include/nw4r/snd/snd_SeqTrack.h @@ -1,166 +1,211 @@ #ifndef NW4R_SND_SEQ_TRACK_H #define NW4R_SND_SEQ_TRACK_H -#include "nw4r/snd/snd_Channel.h" -#include "nw4r/snd/snd_Lfo.h" -#include "nw4r/types_nw4r.h" -#include "rvl/WPAD.h" // IWYU pragma: export +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { +#include -enum SeqMute { - MUTE_OFF, - MUTE_NO_STOP, - MUTE_RELEASE, - MUTE_STOP -}; +#include "Channel.h" +#include "global.h" // AUX_BUS_NUM +#include "Lfo.h" // LfoParam +#include "MoveValue.h" -enum ParseResult { - PARSE_RESULT_CONTINUE, - PARSE_RESULT_FINISH -}; +/******************************************************************************* + * types + */ -namespace detail { +// forward declarations +namespace nw4r { namespace snd { namespace detail { class SeqPlayer; }}} -class SeqTrack { -public: - static const int VARIABLE_NUM = 16; - static const int PRGNO_MAX = 0xFFFF; +namespace nw4r { namespace snd +{ + // why is this not attacked to SeqTrack or something? + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2d764 + enum SeqMute + { + MUTE_OFF, + MUTE_NO_STOP, + MUTE_RELEASE, + MUTE_STOP, + }; +}} // namespace nw4r::snd - struct ParserTrackParam { - const u8 *baseAddr; // at 0x0 - const u8 *currentAddr; // at 0x4 - s32 wait; // at 0x8 +/******************************************************************************* + * classes and functions + */ - bool muteFlag; // at 0xC - bool silenceFlag; // at 0xD - bool noteFinishWait; // at 0xE - bool portaFlag; // at 0xF - bool damperFlag; // at 0x10 +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2d7c3 + class SeqTrack + { + // early constants + public: + static int const CALL_STACK_DEPTH = 3; - int bankNo; // at 0x14 - int prgNo; // at 0x18 + // enums + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2d70c + enum ParseResult + { + PARSE_RESULT_CONTINUE, + PARSE_RESULT_FINISH, + }; - LfoParam lfoParam; // at 0x1C + // nested types + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2cdd6 + struct CallStack + { + bool loopFlag; // size 0x01, offset 0x00 + u8 loopCount; // size 0x01, offset 0x01 + /* 2 bytes padding */ + byte_t const *address; // size 0x04, offset 0x04 + }; // size 0x08 - u8 lfoTarget; // at 0x2C - f32 sweepPitch; // at 0x30 + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2d032 + struct ParserTrackParam + { + byte_t const *baseAddr; // size 0x04, offset 0x00 + byte_t const *currentAddr; // size 0x04, offset 0x04 + bool cmpFlag; // size 0x01, offset 0x08 + bool noteWaitFlag; // size 0x01, offset 0x09 + bool tieFlag; // size 0x01, offset 0x0a + bool monophonicFlag; // size 0x01, offset 0x0b + CallStack callStack[CALL_STACK_DEPTH]; // size 0x18, offset 0x0c + u8 callStackDepth; // size 0x01, offset 0x24 + /* 3 bytes padding */ + s32 wait; // size 0x04, offset 0x28 + bool muteFlag; // size 0x01, offset 0x2c + bool silenceFlag; // size 0x01, offset 0x2d + bool noteFinishWait; // size 0x01, offset 0x2e + bool portaFlag; // size 0x01, offset 0x2f + bool damperFlag; // size 0x01, offset 0x30 + /* 3 bytes padding */ + int bankNo; // size 0x04, offset 0x34 + int prgNo; // size 0x04, offset 0x38 + LfoParam lfoParam; // size 0x10, offset 0x3c + u8 lfoTarget; // size 0x01, offset 0x4c + /* 3 bytes padding */ + f32 sweepPitch; // size 0x04, offset 0x50 + MoveValue volume; // size 0x06, offset 0x54 + MoveValue pan; // size 0x06, offset 0x5a + MoveValue surroundPan; // size 0x06, offset 0x60 + u8 volume2; // size 0x01, offset 0x66 + u8 velocityRange; // size 0x01, offset 0x67 + s8 pitchBend; // size 0x01, offset 0x68 + u8 bendRange; // size 0x01, offset 0x69 + s8 initPan; // size 0x01, offset 0x6a + s8 transpose; // size 0x01, offset 0x6b + u8 priority; // size 0x01, offset 0x6c + u8 portaKey; // size 0x01, offset 0x6d + u8 portaTime; // size 0x01, offset 0x6e + u8 attack; // size 0x01, offset 0x6f + u8 decay; // size 0x01, offset 0x70 + u8 sustain; // size 0x01, offset 0x71 + u8 release; // size 0x01, offset 0x72 + /* 1 byte padding */ + s16 envHold; // size 0x02, offset 0x74 + u8 mainSend; // size 0x01, offset 0x76 + u8 fxSend[AUX_BUS_NUM]; // size 0x03, offset 0x77 + u8 biquadType; // size 0x01, offset 0x7a + /* 1 byte padding */ + f32 lpfFreq; // size 0x04, offset 0x7c + f32 biquadValue; // size 0x04, offset 0x80 + }; // size 0x84 - u8 volume; // at 0x34 - u8 volume2; // at 0x35 + // methods + public: + // cdtors + SeqTrack(); + virtual ~SeqTrack(); - s8 pitchBend; // at 0x36 - u8 bendRange; // at 0x37 + // virtual function ordering + // vtable SeqTrack + virtual ParseResult Parse(bool doNoteOn) = 0; - s8 pan; // at 0x38 - s8 initPan; // at 0x39 - s8 surroundPan; // at 0x3A + // methods + void InitParam(); + void SetSeqData(void const *seqBase, s32 seqOffset); + void Open(); + void Close(); - s8 transpose; // at 0x3B - u8 priority; // at 0x3C + u8 GetPlayerTrackNo() const { return mPlayerTrackNo; } + bool IsOpened() const { return mOpenFlag; } + ParserTrackParam &GetParserTrackParam() { return mParserTrackParam; } + s16 volatile *GetVariablePtr(int varNo); + SeqPlayer *GetSeqPlayer() { return mSeqPlayer; } + Channel *GetLastChannel() const { return mChannelList; } - u8 portaKey; // at 0x3D - u8 portaTime; // at 0x3E + void SetPlayerTrackNo(int playerTrackNo); + void SetVolume(f32); + void SetPitch(f32); + void SetPan(f32); + void SetSurroundPan(f32); + void SetPanRange(f32); + void SetMute(SeqMute mute); + void SetSilence(bool, int); + void SetModDepth(f32); + void SetModSpeed(f32); + void SetBiquadFilter(int, f32); + void SetLpfFreq(f32); + void SetSeqPlayer(SeqPlayer *player) { mSeqPlayer = player; } - u8 attack; // at 0x3F - u8 decay; // at 0x40 - u8 sustain; // at 0x41 - u8 release; // at 0x42 + void AddChannel(Channel *channel); - u8 mainSend; // at 0x43 - u8 fxSend[AUX_BUS_NUM]; // at 0x44 - u8 lpfFreq; // at 0x47 - }; + void UpdateChannelLength(); + void UpdateChannelParam(); + void UpdateChannelRelease(Channel *channel); -public: - SeqTrack(); - virtual ~SeqTrack(); // at 0x8 + void ReleaseAllChannel(int release); + void PauseAllChannel(bool flag); + void StopAllChannel(); + void FreeAllChannel(); - virtual ParseResult Parse(bool doNoteOn) = 0; // at 0xC + Channel *NoteOn(int key, int velocity, s32 length, bool tieFlag); - bool IsOpened() const { - return mOpenFlag; - } + int ParseNextTick(bool doNoteOn); - void SetPlayerTrackNo(int no); - u8 GetPlayerTrackNo() const { - return mPlayerTrackNo; - } + static void ChannelCallbackFunc(Channel *dropChannel, + Channel::ChannelCallbackStatus status, + register_t userData); - void InitParam(); - void SetSeqData(const void *pBase, s32 offset); + // static members + public: + static int const MUTE_RELEASE_VALUE = -1; // maybe? + static int const PAUSE_RELEASE_VALUE; - void Open(); - void Close(); + static int const TRACK_VARIABLE_NUM = 16; - void UpdateChannelLength(); - void UpdateChannelRelease(Channel *pChannel); + static int const PARSER_PARAM_SIZE; // sizeof(ParserTrackParam) probably - int ParseNextTick(bool doNoteOn); + static int const MAX_ENVELOPE_VALUE = 127; + static int const INVALID_ENVELOPE = 255; - void StopAllChannel(); - void ReleaseAllChannel(int release); - void PauseAllChannel(bool flag); - void AddChannel(Channel *pChannel); - void UpdateChannelParam(); - void FreeAllChannel(); + static int const DEFAULT_PORTA_KEY = 60; + static int const DEFAULT_BENDRANGE = 2; + static int const DEFAULT_PRIORITY = 64; - void SetMute(SeqMute mute); - void SetVolume(f32 volume); - void SetPitch(f32 pitch); + /* static int const CALL_STACK_DEPTH = 3; */ // see above - ParserTrackParam &GetParserTrackParam() { - return mParserTrackParam; - } + // members + private: + /* vtable */ // size 0x04, offset 0x00 + u8 mPlayerTrackNo; // size 0x01, offset 0x04 + bool mOpenFlag; // size 0x01, offset 0x05 + /* 2 bytes padding */ + f32 mExtVolume; // size 0x04, offset 0x08 + f32 mExtPitch; // size 0x04, offset 0x0c + f32 mExtPan; // size 0x04, offset 0x10 + f32 mExtSurroundPan; // size 0x04, offset 0x14 + f32 mPanRange; // size 0x04, offset 0x18 + ParserTrackParam mParserTrackParam; // size 0x84, offset 0x1c + s16 volatile mTrackVariable[TRACK_VARIABLE_NUM]; // size 0x20, offset 0xa0 + SeqPlayer *mSeqPlayer; // size 0x04, offset 0xc0 + Channel *mChannelList; // size 0x04, offset 0xc4 + }; // size 0xc8 +}}} // namespace nw4r::snd::detail - volatile s16 *GetVariablePtr(int i); - - SeqPlayer *GetSeqPlayer() { - return mPlayer; - } - void SetSeqPlayer(SeqPlayer *pPlayer) { - mPlayer = pPlayer; - } - - Channel *GetLastChannel() const { - return mChannelList; - } - - Channel *NoteOn(int key, int velocity, s32 length, bool tie); - -private: - static const int DEFAULT_PRIORITY = 64; - static const int DEFAULT_BENDRANGE = 2; - static const int DEFAULT_PORTA_KEY = 60; - static const int DEFAULT_VARIABLE_VALUE = -1; - -private: - static void ChannelCallbackFunc(Channel *pDropChannel, Channel::ChannelCallbackStatus status, u32 callbackArg); - -private: - u8 mPlayerTrackNo; // at 0x4 - bool mOpenFlag; // at 0x5 - - f32 mExtVolume; // at 0x8 - f32 mExtPitch; // at 0xC - f32 mExtPan; // at 0x10 - f32 mExtSurroundPan; // at 0x14 - f32 mPanRange; // at 0x18 - f32 mExtLpfFreq; // at 0x1C - f32 mExtMainSend; // at 0x20 - f32 mExtFxSend[AUX_BUS_NUM]; // at 0x24 - f32 mExtRemoteSend[WPAD_MAX_CONTROLLERS]; // at 0x30 - f32 mExtRemoteFxSend[WPAD_MAX_CONTROLLERS]; // at 0x40 - - ParserTrackParam mParserTrackParam; // at 0x50 - volatile s16 mTrackVariable[VARIABLE_NUM]; // at 0x98 - SeqPlayer *mPlayer; // at 0xB8 - Channel *mChannelList; // at 0xBC -}; - -} // namespace detail -} // namespace snd -} // namespace nw4r - -#endif +#endif // NW4R_SND_SEQ_TRACK_H diff --git a/include/nw4r/snd/snd_SeqTrackAllocator.h b/include/nw4r/snd/snd_SeqTrackAllocator.h index a4daf439..87b7ecee 100644 --- a/include/nw4r/snd/snd_SeqTrackAllocator.h +++ b/include/nw4r/snd/snd_SeqTrackAllocator.h @@ -1,26 +1,39 @@ #ifndef NW4R_SND_SEQ_TRACK_ALLOCATOR_H #define NW4R_SND_SEQ_TRACK_ALLOCATOR_H -#include "nw4r/types_nw4r.h" -namespace nw4r { -namespace snd { -namespace detail { +/******************************************************************************* + * types + */ -// Forward declarations -class SeqPlayer; -class SeqTrack; +// forward declarations +namespace nw4r { namespace snd { namespace detail { class SeqPlayer; }}} +namespace nw4r { namespace snd { namespace detail { class SeqTrack; }}} -class SeqTrackAllocator { -public: - virtual ~SeqTrackAllocator() {} // at 0x8 +/******************************************************************************* + * classes and functions + */ - virtual SeqTrack *AllocTrack(SeqPlayer *pPlayer) = 0; // at 0xC - virtual void FreeTrack(SeqTrack *pTrack) = 0; // at 0x10 - virtual int GetAllocatableTrackCount() const = 0; // at 0x14 -}; +namespace nw4r { namespace snd { namespace detail +{ +// [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2db61 -} // namespace detail -} // namespace snd -} // namespace nw4r + class SeqTrackAllocator + { + // methods + public: + // cdtors + virtual ~SeqTrackAllocator() {} -#endif + // virtual function ordering + // vtable SeqTrackAllocator + virtual SeqTrack *AllocTrack(SeqPlayer *player) = 0; + virtual void FreeTrack(SeqTrack *track) = 0; + virtual int GetAllocatableTrackCount() const = 0; + + // members + private: + /* vtable */ // size 0x04, offset 0x00 + }; // size 0x04 +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_SEQ_TRACK_ALLOCATOR_H diff --git a/include/nw4r/snd/snd_SoundActor.h b/include/nw4r/snd/snd_SoundActor.h index 625417cf..96754904 100644 --- a/include/nw4r/snd/snd_SoundActor.h +++ b/include/nw4r/snd/snd_SoundActor.h @@ -1,62 +1,62 @@ #ifndef NW4R_SND_SOUND_ACTOR_H #define NW4R_SND_SOUND_ACTOR_H -#include "limits.h" -#include "nw4r/snd/snd_ExternalSoundPlayer.h" -#include "nw4r/snd/snd_SoundStartable.h" -#include "nw4r/types_nw4r.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { -namespace detail { +#include // nullptr -class SoundActor : public SoundStartable { -public: - explicit SoundActor(SoundStartable &rStartable) : mStartable(rStartable) { - detail_GetActorSoundPlayer(0)->SetPlayableSoundCount(INT_MAX); - } +#include "BasicSound.h" +#include "ExternalSoundPlayer.h" +#include "SoundStartable.h" - virtual StartResult detail_SetupSound( - SoundHandle *pHandle, u32 id, detail::BasicSound::AmbientArgInfo *pArgInfo, - detail::ExternalSoundPlayer *pPlayer, bool hold, - const StartInfo *pStartInfo - ); // at 0xC +/******************************************************************************* + * types + */ - virtual u32 detail_ConvertLabelStringToSoundId(const char *pLabel) { - return mStartable.detail_ConvertLabelStringToSoundId(pLabel); - } // at 0x10 +// forward declarations +namespace nw4r { namespace snd { class SoundArchivePlayer; }} - ExternalSoundPlayer *detail_GetActorSoundPlayer(int i) { - if (i < 0 || i >= ACTOR_PLAYER_COUNT) { - return NULL; - } +/******************************************************************************* + * classes and functions + */ - return &mActorPlayer[i]; - } +namespace nw4r { namespace snd +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x278fa + class SoundActor : public SoundStartable + { + // methods + public: + // cdtors + SoundActor(); - template - TForEachFunc ForEachSound(TForEachFunc pFunction, bool reverse) { - int i; - ExternalSoundPlayer *pPlayer = detail_GetActorSoundPlayer(0); + // methods + detail::ExternalSoundPlayer *detail_GetActorPlayer(int actorPlayerId) + { + if (actorPlayerId < 0 || ACTOR_PLAYER_COUNT <= actorPlayerId) + return nullptr; - for (i = 0; i < ACTOR_PLAYER_COUNT; i++) { - pPlayer->ForEachSound(pFunction, reverse); - pPlayer++; - } + return &mActorPlayer[actorPlayerId]; + } - return pFunction; - } + detail::SoundActorParam const &detail_GetActorParam() const + { + return mActorParam; + } -private: - static const int ACTOR_PLAYER_COUNT = 8; + // static members + public: + static int const ACTOR_PLAYER_COUNT = 4; -private: - SoundStartable &mStartable; // at 0x4 - ExternalSoundPlayer mActorPlayer[ACTOR_PLAYER_COUNT]; // at 0x8 -}; + // members + private: + /* base SoundStartable */ // size 0x04, offset 0x00 + SoundArchivePlayer &mSoundArchivePlayer; // size 0x04, offset 0x04 + detail::ExternalSoundPlayer mActorPlayer[ACTOR_PLAYER_COUNT]; // size 0x40, offset 0x08 + detail::SoundActorParam mActorParam; // size 0x0c, offset 0x48 + }; // size 0x54 +}} // namespace nw4r::snd -} // namespace detail -} // namespace snd -} // namespace nw4r - -#endif +#endif // NW4R_SND_SOUND_ACTOR_H diff --git a/include/nw4r/snd/snd_SoundArchive.h b/include/nw4r/snd/snd_SoundArchive.h index 5f9169a8..2da9939c 100644 --- a/include/nw4r/snd/snd_SoundArchive.h +++ b/include/nw4r/snd/snd_SoundArchive.h @@ -1,170 +1,214 @@ #ifndef NW4R_SND_SOUND_ARCHIVE_H #define NW4R_SND_SOUND_ARCHIVE_H -#include "nw4r/snd/snd_Common.h" -#include "nw4r/types_nw4r.h" -#include "nw4r/ut.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { +#include -enum SoundType { - SOUND_TYPE_INVALID, - SOUND_TYPE_SEQ, - SOUND_TYPE_STRM, - SOUND_TYPE_WAVE -}; +#include "global.h" -class SoundArchive { -public: - struct SoundInfo { - u32 fileId; // at 0x0 - u32 playerId; // at 0x4 - int playerPriority; // at 0x8 - int volume; // at 0xC - int remoteFilter; // at 0x10 - detail::PanMode panMode; // at 0x14 - detail::PanCurve panCurve; // at 0x18 - }; +/******************************************************************************* + * types + */ - struct SeqSoundInfo { - u32 dataOffset; // at 0x0 - u32 bankId; // at 0x4 - u32 allocTrack; // at 0x8 - int channelPriority; // at 0xC - bool releasePriorityFixFlag; // at 0x10 - }; +// forward declarations +namespace nw4r { namespace snd { namespace detail { class SoundArchiveFileReader; }}} - struct StrmSoundInfo {}; +namespace nw4r { namespace ut { class FileStream; }} - struct WaveSoundInfo { - int subNo; // at 0x0 - int channelPriority; // at 0x4 - bool releasePriorityFixFlag; // at 0x8 - }; +/******************************************************************************* + * classes and functions + */ - struct Sound3DParam { - u32 flags; // at 0x0 - u8 decayCurve; // at 0x4 - u8 decayRatio; // at 0x5 - }; +namespace nw4r { namespace snd +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x265b9 + class SoundArchive + { + // enums + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x26182 + enum SoundType + { + SOUND_TYPE_INVALID, - struct BankInfo { - u32 fileId; // at 0x0 - }; + SOUND_TYPE_SEQ, + SOUND_TYPE_STRM, + SOUND_TYPE_WAVE, + }; - struct PlayerInfo { - int playableSoundCount; // at 0x0 - u32 heapSize; // at 0x4 - }; + // nested types + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x25c18 + struct BankInfo + { + u32 fileId; // size 0x04, offset 0x00 + }; // size 0x04 - struct GroupInfo { - u32 itemCount; // at 0x0 - const char *extFilePath; // at 0x4 - u32 offset; // at 0x8 - u32 size; // at 0xC - u32 waveDataOffset; // at 0x10 - u32 waveDataSize; // at 0x14 - }; - struct GroupItemInfo { - u32 fileId; // at 0x0 - u32 offset; // at 0x4 - u32 size; // at 0x8 - u32 waveDataOffset; // at 0xC - u32 waveDataSize; // at 0x10 - }; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x25ac7 + struct FileInfo + { + u32 fileSize; // size 0x04, offset 0x00 + u32 waveDataFileSize; // size 0x04, offset 0x04 + char const *extFilePath; // size 0x04, offset 0x08 + u32 filePosCount; // size 0x04, offset 0x0c + }; // size 0x10 - struct FileInfo { - u32 fileSize; // at 0x0 - u32 waveDataFileSize; // at 0x4 - const char *extFilePath; // at 0x8 - u32 filePosCount; // at 0xC - }; - struct FilePos { - u32 groupId; // at 0x0 - u32 index; // at 0x4 - }; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x25a5b + struct FilePos + { + u32 groupId; // size 0x04, offset 0x00 + u32 index; // size 0x04, offset 0x04 + }; // size 0x08 - struct SoundArchivePlayerInfo { - int seqSoundCount; // at 0x0 - int seqTrackCount; // at 0x4 - int strmSoundCount; // at 0x8 - int strmTrackCount; // at 0xC - int strmChannelCount; // at 0x10 - int waveSoundCount; // at 0x14 - int waveTrackCount; // at 0x18 - }; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2593d + struct GroupInfo + { + u32 itemCount; // size 0x04, offset 0x00 + char const *extFilePath; // size 0x04, offset 0x04 + u32 offset; // size 0x04, offset 0x08 + u32 size; // size 0x04, offset 0x0c + u32 waveDataOffset; // size 0x04, offset 0x10 + u32 waveDataSize; // size 0x04, offset 0x14 + }; // size 0x18 - static const u32 INVALID_ID = 0xFFFFFFFF; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2584d + struct GroupItemInfo + { + u32 fileId; // size 0x04, offset 0x00 + u32 offset; // size 0x04, offset 0x04 + u32 size; // size 0x04, offset 0x08 + u32 waveDataOffset; // size 0x04, offset 0x0c + u32 waveDataSize; // size 0x04, offset 0x10 + }; // size 0x14 -public: - SoundArchive(); - virtual ~SoundArchive(); // at 0x8 + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x25b9b + struct PlayerInfo + { + int playableSoundCount; // size 0x04, offset 0x00 + u32 heapSize; // size 0x04, offset 0x04 + }; // size 0x08 - virtual const void *detail_GetFileAddress(u32 id) const = 0; // at 0xC + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x25dc1 + struct SeqSoundInfo + { + u32 dataOffset; // size 0x04, offset 0x00 + u32 bankId; // size 0x04, offset 0x04 + u32 allocTrack; // size 0x04, offset 0x08 + int channelPriority; // size 0x04, offset 0x0c + bool releasePriorityFixFlag; // size 0x01, offset 0x10 + /* 3 bytes padding */ + }; // size 0x14 - virtual const void *detail_GetWaveDataFileAddress(u32 id) const = 0; // at 0x10 + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x25d0f + struct StrmSoundInfo + { + u32 startPosition; // size 0x04, offset 0x00 + u16 allocChannelCount; // size 0x02, offset 0x04 + byte2_t allocTrackFlag; // size 0x02, offset 0x06 + }; // size 0x08 - virtual int detail_GetRequiredStreamBufferSize() const = 0; // at 0x14 + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x25c5f + struct WaveSoundInfo + { + s32 subNo; // size 0x04, offset 0x00 + int channelPriority; // size 0x04, offset 0x04 + bool releasePriorityFixFlag; // size 0x01, offset 0x08 + /* 3 bytes padding */ + }; // size 0x0c - virtual ut::FileStream *OpenStream(void *pBuffer, int bufferSize, u32 offset, - u32 length) const = 0; // at 0x18 + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x256dc + struct SoundArchivePlayerInfo + { + int seqSoundCount; // size 0x04, offset 0x00 + int seqTrackCount; // size 0x04, offset 0x04 + int strmSoundCount; // size 0x04, offset 0x08 + int strmTrackCount; // size 0x04, offset 0x0c + int strmChannelCount; // size 0x04, offset 0x10 + int waveSoundCount; // size 0x04, offset 0x14 + int waveTrackCount; // size 0x04, offset 0x18 + }; // size 0x1c - virtual ut::FileStream *OpenExtStream( - void *pBuffer, int bufferSize, const char *pExtPath, u32 offset, - u32 length - ) const = 0; // at 0x1C + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x26012 + struct SoundInfo + { + u32 fileId; // size 0x04, offset 0x00 + u32 playerId; // size 0x04, offset 0x04 + int actorPlayerId; // size 0x04, offset 0x08 + int playerPriority; // size 0x04, offset 0x0c + int volume; // size 0x04, offset 0x10 + int remoteFilter; // size 0x04, offset 0x14 + PanMode panMode; // size 0x04, offset 0x18 + PanCurve panCurve; // size 0x04, offset 0x1c + }; // size 0x20 - bool IsAvailable() const; + // methods + public: + // cdtors + SoundArchive(); + virtual ~SoundArchive(); - void Setup(detail::SoundArchiveFileReader *pReader); - void Shutdown(); + // virtual function ordering + // vtable SoundArchive + virtual void const *detail_GetFileAddress(u32 fileId) const = 0; + virtual void const *detail_GetWaveDataFileAddress(u32 fileId) const = 0; + virtual int detail_GetRequiredStreamBufferSize() const = 0; + virtual ut::FileStream *OpenStream(void *buffer, int size, u32 begin, + u32 length) const = 0; + virtual ut::FileStream *OpenExtStream(void *buffer, int size, + char const *extFilePath, + u32 begin, u32 length) const = 0; - u32 GetPlayerCount() const; - u32 GetGroupCount() const; + // methods + void Setup(detail::SoundArchiveFileReader *fileReader); + void Shutdown(); - const char *GetSoundLabelString(u32 id) const; - u32 ConvertLabelStringToSoundId(const char *pLabel) const; - u32 ConvertLabelStringToPlayerId(const char *pLabel) const; - u32 ConvertLabelStringToGroupId(const char *pLabel) const; + u32 GetPlayerCount() const; + u32 GetGroupCount() const; + bool IsAvailable() const; + SoundType GetSoundType(u32 soundId) const; - u32 GetSoundUserParam(u32 id) const; - SoundType GetSoundType(u32 id) const; + u32 ConvertLabelStringToSoundId(char const *label) const; - bool ReadSoundInfo(u32 id, SoundInfo *pInfo) const; - bool detail_ReadSeqSoundInfo(u32 id, SeqSoundInfo *pInfo) const; - bool detail_ReadStrmSoundInfo(u32 id, StrmSoundInfo *pInfo) const; - bool detail_ReadWaveSoundInfo(u32 id, WaveSoundInfo *pInfo) const; + bool ReadSoundInfo(u32 soundId, SoundInfo *info) const; + bool ReadSeqSoundInfo(u32 soundId, SeqSoundInfo *info) const; + bool detail_ReadStrmSoundInfo(u32 soundId, StrmSoundInfo *info) const; + bool detail_ReadWaveSoundInfo(u32 soundId, WaveSoundInfo *info) const; - bool ReadPlayerInfo(u32 id, PlayerInfo *pInfo) const; - bool ReadSoundArchivePlayerInfo(SoundArchivePlayerInfo *pInfo) const; + bool ReadPlayerInfo(u32 playerId, PlayerInfo *info) const; + bool ReadSoundArchivePlayerInfo(SoundArchivePlayerInfo *info) const; - bool detail_ReadSound3DParam(u32 id, Sound3DParam *pParam) const; - bool detail_ReadBankInfo(u32 id, BankInfo *pInfo) const; - bool detail_ReadGroupInfo(u32 id, GroupInfo *pInfo) const; - bool detail_ReadGroupItemInfo(u32 groupId, u32 itemId, GroupItemInfo *pInfo) const; + bool ReadBankInfo(u32 bankId, BankInfo *info) const; - bool detail_ReadFileInfo(u32 id, FileInfo *pInfo) const; - bool detail_ReadFilePos(u32 fileId, u32 posId, FilePos *pPos) const; + bool detail_ReadGroupInfo(u32 groupId, GroupInfo *info) const; + bool detail_ReadGroupItemInfo(u32 groupId, u32 index, + GroupItemInfo *info) const; - ut::FileStream *detail_OpenFileStream(u32 id, void *pBuffer, int bufferSize) const; - ut::FileStream *detail_OpenGroupStream(u32 id, void *pBuffer, int bufferSize) const; - ut::FileStream *detail_OpenGroupWaveDataStream(u32 id, void *pBuffer, int bufferSize) const; + u32 detail_GetFileCount() const; + bool detail_ReadFileInfo(u32 fileId, FileInfo *info) const; + bool detail_ReadFilePos(u32 fileId, u32 index, FilePos *info) const; - void SetExternalFileRoot(const char *pExtFileRoot); + ut::FileStream *detail_OpenFileStream(u32 fileId, void *buffer, + int size) const; -protected: - static const int FILE_PATH_MAX = 256; + private: + ut::FileStream *OpenExtStreamImpl(void *buffer, int size, + char const *extFilePath, u32 begin, + u32 length) const; -private: - ut::FileStream *OpenExtStreamImpl(void *pBuffer, int bufferSize, const char *pExtPath, u32 offset, u32 size) const; + // static members + public: + static int const FILE_PATH_MAX = 255; + static u32 const INVALID_ID = -1; -private: - detail::SoundArchiveFileReader *mFileReader; // at 0x4 - char mExtFileRoot[FILE_PATH_MAX]; // at 0x8 -}; + // members + private: + /* vtable */ // size 0x004, offset 0x000 + detail::SoundArchiveFileReader *mFileReader; // size 0x004, offset 0x004 + char mExtFileRoot[FILE_PATH_MAX + 1]; // size 0x100, offset 0x008 + }; // size 0x108 +}} // namespace nw4r::snd -} // namespace snd -} // namespace nw4r - -#endif +#endif // NW4R_SND_SOUND_ARCHIVE_H diff --git a/include/nw4r/snd/snd_SoundArchiveFile.h b/include/nw4r/snd/snd_SoundArchiveFile.h index 67c9069b..478c80f0 100644 --- a/include/nw4r/snd/snd_SoundArchiveFile.h +++ b/include/nw4r/snd/snd_SoundArchiveFile.h @@ -1,294 +1,380 @@ #ifndef NW4R_SND_SOUND_ARCHIVE_FILE_H #define NW4R_SND_SOUND_ARCHIVE_FILE_H -#include "nw4r/snd/snd_SoundArchive.h" -#include "nw4r/snd/snd_Util.h" -#include "nw4r/types_nw4r.h" -namespace nw4r { -namespace snd { -namespace detail { -namespace SoundArchiveFile { +/******************************************************************************* + * headers + */ -/****************************************************************************** - * - * Symbol (SYMB) block - * - ******************************************************************************/ -struct StringTreeNode { - u16 flags; // at 0x0 - u16 bit; // at 0x2 - u32 leftIdx; // at 0x4 - u32 rightIdx; // at 0x8 - u32 strIdx; // at 0xC - u32 id; // at 0x10 -}; +#include +#include -struct StringTree { - u32 rootIdx; // at 0x0 - Util::Table nodeTable; // at 0x4 -}; +#include "SoundArchive.h" +#include "Util.h" -struct StringTable { - Util::Table offsetTable; // at 0x0 -}; +#include "../ut/binaryFileFormat.h" +#include "../ut/inlines.h" -struct StringChunk { - u32 tableOffset; // at 0x0 - u32 soundTreeOffset; // at 0x4 - u32 playerTreeOffset; // at 0x8 - u32 groupTreeOffset; // at 0xC - u32 bankTreeOffset; // at 0x10 -}; +/******************************************************************************* + * types + */ -struct StringBlock { - union { - StringTable stringTable; - StringChunk stringChunk; - }; // at 0x0 -}; +namespace nw4r { namespace snd { namespace detail +{ + struct SoundArchiveFile + { + /* Header */ -struct SymbolBlock { - ut::BinaryBlockHeader blockHeader; // at 0x0 - StringBlock stringBlock; // at 0x8 -}; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x240a6 + struct Header + { + ut::BinaryFileHeader fileHeader; // size 0x10, offset 0x00 + u32 symbolDataOffset; // size 0x04, offset 0x10 + u32 symbolDataSize; // size 0x04, offset 0x14 + u32 infoOffset; // size 0x04, offset 0x18 + u32 infoSize; // size 0x04, offset 0x1c + u32 fileImageOffset; // size 0x04, offset 0x20 + u32 fileImageSize; // size 0x04, offset 0x24 + }; // size 0x28 -/****************************************************************************** - * - * Info (INFO) block - * - ******************************************************************************/ -struct SeqSoundInfo { - u32 dataOffset; // at 0x0 - u32 bankId; // at 0x4 - u32 allocTrack; // at 0x8 - u8 channelPriority; // at 0xC - u8 releasePriorityFix; // at 0xD -}; + /* SymbolBlock */ -struct StrmSoundInfo {}; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x24881 + struct StringTreeNode + { + byte2_t flags; // size 0x02, offset 0x00 + byte2_t bit; // size 0x02, offset 0x02 + u32 leftIdx; // size 0x04, offset 0x04 + u32 rightIdx; // size 0x04, offset 0x08 + u32 strIdx; // size 0x04, offset 0x0c + u32 id; // size 0x04, offset 0x10 + }; // size 0x14 -struct WaveSoundInfo { - s32 subNo; // at 0x0 - u32 allocTrack; // at 0x4 - u8 channelPriority; // at 0x8 - u8 releasePriorityFix; // at 0x9 -}; -typedef Util::DataRef SoundInfoOffset; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x24a2e + struct StringTree + { + u32 rootIdx; // size 0x04, offset 0x00 + Util::Table nodeTable; // size 0x18, offset 0x04 + }; // size 0x1c -struct Sound3DParam { - u32 flags; // at 0x0 - u8 decayCurve; // at 0x4 - u8 decayRatio; // at 0x5 -}; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x24830 + struct StringTable + { + Util::Table offsetTable; // size 0x08, offset 0x00 + }; // size 0x08 -struct SoundCommonInfo { - u32 stringId; // at 0x0 - u32 fileId; // at 0x4 - u32 playerId; // at 0x8 - Util::DataRef param3dRef; // at 0xC - u8 volume; // at 0x14 - u8 playerPriority; // at 0x15 - u8 soundType; // at 0x16 - u8 remoteFilter; // at 0x17 - SoundInfoOffset soundInfoRef; // at 0x18 - u32 userParam[2]; // at 0x20 - u8 panMode; // at 0x28 - u8 panCurve; // at 0x29 -}; -typedef Util::Table > SoundCommonTable; + struct StringChunk + { + u32 tableOffset; // size 0x04, offset 0x00 + u32 soundTreeOffset; // size 0x04, offset 0x04 + u32 playerTreeOffset; // size 0x04, offset 0x08 + u32 groupTreeOffset; // size 0x04, offset 0x0c + u32 bankTreeOffset; // size 0x04, offset 0x10 + }; // size 0x14 -struct BankInfo { - u32 stringId; // at 0x0 - u32 fileId; // at 0x4 -}; -typedef Util::Table > BankTable; + union StringBlock + { + StringTable stringTable; // size 0x08 + StringChunk stringChunk; // size 0x14 + }; // size 0x14 -struct PlayerInfo { - u32 stringId; // at 0x0 - u8 playableSoundCount; // at 0x4 - u32 heapSize; // at 0x8 -}; -typedef Util::Table > PlayerTable; + static byte4_t const SIGNATURE_SYMB_BLOCK = + NW4R_FOUR_BYTE('S', 'Y', 'M', 'B'); -typedef Util::Table > FilePosTable; + struct SymbolBlock + { + ut::BinaryBlockHeader blockHeader; // size 0x08, offset 0x00 + StringBlock stringBlock; // size 0x14, offset 0x08 + }; // size 0x20 -struct FileInfo { - u32 fileSize; // at 0x0 - u32 waveDataSize; // at 0x4 - s32 entryNum; // at 0x8 - Util::DataRef extFilePathRef; // at 0xC - Util::DataRef filePosTableRef; // at 0x14 -}; -typedef Util::Table > FileTable; + /* InfoBlock */ -struct GroupItemInfo { - u32 fileId; // at 0x0 - u32 offset; // at 0x4 - u32 size; // at 0x8 - u32 waveDataOffset; // at 0xC - u32 waveDataSize; // at 0x10 -}; -typedef Util::Table > GroupItemTable; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2da8f9 + enum SoundType + { + SOUND_TYPE_INVALID, -struct GroupInfo { - u32 stringId; // at 0x0 - s32 entryNum; // at 0x4 - Util::DataRef extFilePathRef; // at 0x8 - u32 offset; // at 0x10 - u32 size; // at 0x14 - u32 waveDataOffset; // at 0x18 - u32 waveDataSize; // at 0x1C - Util::DataRef itemTableRef; // at 0x20 -}; -typedef Util::Table > GroupTable; + SOUND_TYPE_SEQ, + SOUND_TYPE_STRM, + SOUND_TYPE_WAVE, + }; -struct SoundArchivePlayerInfo { - u16 seqSoundCount; // at 0x0 - u16 seqTrackCount; // at 0x2 - u16 strmSoundCount; // at 0x4 - u16 strmTrackCount; // at 0x6 - u16 strmChannelCount; // at 0x8 - u16 waveSoundCount; // at 0xA - u16 waveTrackCount; // at 0xC -}; + struct Sound3DParam; -struct Info { - Util::DataRef soundTableRef; // at 0x0 - Util::DataRef bankTableRef; // at 0x8 - Util::DataRef playerTableRef; // at 0x10 - Util::DataRef fileTableRef; // at 0x18 - Util::DataRef groupTableRef; // at 0x20 - Util::DataRef soundArchivePlayerInfoRef; // at 0x28 -}; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x25183 + struct SeqSoundInfo + { + u32 dataOffset; // size 0x04, offset 0x00 + u32 bankId; // size 0x04, offset 0x04 + u32 allocTrack; // size 0x04, offset 0x08 + u8 channelPriority; // size 0x01, offset 0x0c + u8 releasePriorityFix; // size 0x01, offset 0x0d + byte1_t padding[2]; + byte4_t reserved; + }; // size 0x14 -struct InfoBlock { - ut::BinaryBlockHeader blockHeader; // at 0x0 - Info info; // at 0x8 -}; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x25089 + struct StrmSoundInfo + { + u32 startPosition; // size 0x04, offset 0x00 + u16 allocChannelCount; // size 0x02, offset 0x04 + byte2_t allocTrackFlag; // size 0x02, offset 0x06 + byte4_t reserved; + }; // size 0x0c -/****************************************************************************** - * - * File header - * - ******************************************************************************/ -struct Header { - ut::BinaryFileHeader fileHeader; // at 0x0 - u32 symbolDataOffset; // at 0x10 - u32 symbolDataSize; // at 0x14 - u32 infoOffset; // at 0x18 - u32 infoSize; // at 0x1C - u32 fileImageOffset; // at 0x20 - u32 fileImageSize; // at 0x24 -}; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x24f62 + struct WaveSoundInfo + { + s32 subNo; // size 0x04, offset 0x00 + u32 allocTrack; // size 0x04, offset 0x04 + u8 channelPriority; // size 0x01, offset 0x08 + u8 releasePriorityFix; // size 0x01, offset 0x09 + byte1_t padding[2]; + byte4_t reserved; + }; // size 0x10 -// TODO: How is this calculated? -static const int HEADER_AREA_SIZE = ROUND_UP(sizeof(Header), 32) + 40; + typedef Util::DataRef + SoundInfoRef; -} // namespace SoundArchiveFile + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2546c + struct SoundCommonInfo + { + u32 stringId; // size 0x04, offset 0x00 + u32 fileId; // size 0x04, offset 0x04 + u32 playerId; // size 0x04, offset 0x08 + Util::DataRef param3dRef; // size 0x08, offset 0x0c + u8 volume; // size 0x01, offset 0x14 + u8 playerPriority; // size 0x01, offset 0x15 + u8 soundType; // size 0x01, offset 0x16 + u8 remoteFilter; // size 0x01, offset 0x17 + SoundInfoRef soundInfoRef; // size 0x08, offset 0x18 + register_t userParam[2]; // size 0x08, offset 0x20 + u8 panMode; // size 0x01, offset 0x28 + u8 panCurve; // size 0x01, offset 0x29 + u8 actorPlayerId; // size 0x01, offset 0x2a + byte1_t reserved; + }; // size 0x2c -class SoundArchiveFileReader { -public: - static const u32 SIGNATURE = 'RSAR'; - static const int VERSION = NW4R_VERSION(1, 3); + typedef Util::Table > + SoundCommonInfoTable; -public: - SoundArchiveFileReader(); + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2db9b5 + struct SoundArchivePlayerInfo + { + u16 seqSoundCount; // size 0x02, offset 0x00 + u16 seqTrackCount; // size 0x02, offset 0x02 + u16 strmSoundCount; // size 0x02, offset 0x04 + u16 strmTrackCount; // size 0x02, offset 0x06 + u16 strmChannelCount; // size 0x02, offset 0x08 + u16 waveSoundCount; // size 0x02, offset 0x0a + u16 waveTrackCount; // size 0x02, offset 0x0c + byte2_t padding; + byte4_t reserved; + }; // size 0x14 - void Init(const void *pSoundArchiveBin); - bool IsValidFileHeader(const void *pSoundArchiveBin); + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x24eab + struct BankInfo + { + u32 stringId; // size 0x04, offset 0x00 + u32 fileId; // size 0x04, offset 0x04 + byte4_t reserved; + }; // size 0x0c - void SetStringChunk(const void *pChunk, u32 size); - void SetInfoChunk(const void *pChunk, u32 size); + typedef Util::Table > BankInfoTable; - SoundType GetSoundType(u32 id) const; - bool ReadSoundInfo(u32 id, SoundArchive::SoundInfo *pInfo) const; - bool ReadSound3DParam(u32 id, SoundArchive::Sound3DParam *pParam) const; - bool ReadSeqSoundInfo(u32 id, SoundArchive::SeqSoundInfo *pInfo) const; - bool ReadStrmSoundInfo(u32 id, SoundArchive::StrmSoundInfo *pInfo) const; - bool ReadWaveSoundInfo(u32 id, SoundArchive::WaveSoundInfo *pInfo) const; - bool ReadBankInfo(u32 id, SoundArchive::BankInfo *pInfo) const; - bool ReadPlayerInfo(u32 id, SoundArchive::PlayerInfo *pInfo) const; - bool ReadGroupInfo(u32 id, SoundArchive::GroupInfo *pInfo) const; - bool ReadGroupItemInfo(u32 groupId, u32 itemId, SoundArchive::GroupItemInfo *pInfo) const; - bool ReadSoundArchivePlayerInfo(SoundArchive::SoundArchivePlayerInfo *pInfo) const; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x24d8f + struct PlayerInfo + { + u32 stringId; // size 0x04, offset 0x00 + u8 playableSoundCount; // size 0x01, offset 0x04 + byte1_t padding; + byte2_t padding2; + u32 heapSize; // size 0x04, offset 0x08 + byte4_t reserved; + }; // size 0x10 - u32 GetSoundStringId(u32 id) const; - u32 GetPlayerCount() const; - u32 GetGroupCount() const; + typedef Util::Table > PlayerInfoTable; - const char *GetSoundLabelString(u32 id) const; - u32 GetSoundUserParam(u32 id) const; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2db6e3 + struct GroupItemInfo + { + u32 fileId; // size 0x04, offset 0x00 + u32 offset; // size 0x04, offset 0x04 + u32 size; // size 0x04, offset 0x08 + u32 waveDataOffset; // size 0x04, offset 0x0c + u32 waveDataSize; // size 0x04, offset 0x10 + byte4_t reserved; + }; // size 0x18 - bool ReadFileInfo(u32 id, SoundArchive::FileInfo *pInfo) const; - bool ReadFilePos(u32 fileId, u32 id, SoundArchive::FilePos *pPos) const; + typedef Util::Table > GroupItemInfoTable; - const char *GetString(u32 id) const; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x24c1b + struct GroupInfo + { + u32 stringId; // size 0x04, offset 0x00 + s32 entryNum; // size 0x04, offset 0x04 + Util::DataRef extFilePathRef; // size 0x08, offset 0x08 + u32 offset; // size 0x04, offset 0x10 + u32 size; // size 0x04, offset 0x14 + u32 waveDataOffset; // size 0x04, offset 0x18 + u32 waveDataSize; // size 0x04, offset 0x1c + Util::DataRef itemTableRef; // size 0x08, offset 0x20 + }; // size 0x28 - u32 ConvertLabelStringToSoundId(const char *pLabel) const { - return ConvertLabelStringToId(mStringTreeSound, pLabel); - } - u32 ConvertLabelStringToPlayerId(const char *pLabel) const { - return ConvertLabelStringToId(mStringTreePlayer, pLabel); - } - u32 ConvertLabelStringToGroupId(const char *pLabel) const { - return ConvertLabelStringToId(mStringTreeGroup, pLabel); - } + typedef Util::Table > GroupInfoTable; - u16 GetVersion() const { - return mHeader.fileHeader.version; - } + typedef Util::Table > FilePosTable; - u32 GetLabelStringChunkOffset() const { - return mHeader.symbolDataOffset; - } + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2dc40b + struct FileInfo + { + u32 fileSize; // size 0x04, offset 0x00 + u32 waveDataFileSize; // size 0x04, offset 0x04 + s32 entryNum; // size 0x04, offset 0x08 + Util::DataRef extFilePathRef; // size 0x08, offset 0x0c + Util::DataRef filePosTableRef; // size 0x08, offset 0x14 + }; // size 0x1c - u32 GetLabelStringChunkSize() const { - return mHeader.symbolDataSize; - } + typedef Util::Table > FileInfoTable; - u32 GetInfoChunkOffset() const { - return mHeader.infoOffset; - } + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x24665 + struct Info + { + Util::DataRef soundTableRef; // size 0x08, offset 0x00 + Util::DataRef bankTableRef; // size 0x08, offset 0x08 + Util::DataRef playerTableRef; // size 0x08, offset 0x10 + Util::DataRef fileTableRef; // size 0x08, offset 0x18 + Util::DataRef groupTableRef; // size 0x08, offset 0x20 + Util::DataRef soundArchivePlayerInfoRef; // size 0x08, offset 0x28 + }; // size 0x30 - u32 GetInfoChunkSize() const { - return mHeader.infoSize; - } + static byte4_t const SIGNATURE_INFO_BLOCK = + NW4R_FOUR_BYTE('I', 'N', 'F', 'O'); -private: - u32 ConvertLabelStringToId(const SoundArchiveFile::StringTree *pTree, const char *pLabel) const; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2da76d + struct InfoBlock + { + ut::BinaryBlockHeader blockHeader; // size 0x08, offset 0x00 + Info info; // size 0x30, offset 0x08 + }; // size 0x38 - const SoundArchiveFile::SoundCommonInfo *impl_GetSoundInfo(u32 id) const; - SoundArchiveFile::SoundInfoOffset impl_GetSoundInfoOffset(u32 id) const DONT_INLINE; + /* SoundArchiveFile */ - const SoundArchiveFile::SeqSoundInfo *impl_GetSeqSoundInfo(u32 id) const; - const SoundArchiveFile::StrmSoundInfo *impl_GetStrmSoundInfo(u32 id) const; - const SoundArchiveFile::WaveSoundInfo *impl_GetWaveSoundInfo(u32 id) const; + static byte4_t const SIGNATURE_FILE = + NW4R_FOUR_BYTE('R', 'S', 'A', 'R'); + static int const FILE_VERSION = NW4R_FILE_VERSION(1, 4); + }; // "namespace" SoundArchiveFile +}}} // namespace nw4r::snd::detail - const SoundArchiveFile::BankInfo *impl_GetBankInfo(u32 id) const; - const SoundArchiveFile::PlayerInfo *impl_GetPlayerInfo(u32 id) const; - const SoundArchiveFile::GroupInfo *impl_GetGroupInfo(u32 id) const; +/******************************************************************************* + * classes and functions + */ - const void *GetPtrConst(const void *pBase, u32 offset) const { - if (offset == 0) { - return NULL; - } +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x261f8 + class SoundArchiveFileReader + { + // methods + public: + // cdtors + SoundArchiveFileReader(); + ~SoundArchiveFileReader() {} // not default? - return ut::AddOffsetToPtr(pBase, offset); - } + // methods + void Init(void const *soundArchiveData); -private: - SoundArchiveFile::Header mHeader; // at 0x0 - const SoundArchiveFile::Info *mInfo; // at 0x28 - const void *mStringBase; // at 0x2C - const SoundArchiveFile::StringTable *mStringTable; // at 0x30 - const SoundArchiveFile::StringTree *mStringTreeSound; // at 0x34 - const SoundArchiveFile::StringTree *mStringTreePlayer; // at 0x38 - const SoundArchiveFile::StringTree *mStringTreeGroup; // at 0x3C - const SoundArchiveFile::StringTree *mStringTreeBank; // at 0x40 -}; + u16 GetVersion() const { return mHeader.fileHeader.version; } + u32 GetLabelStringChunkOffset() const + { + return mHeader.symbolDataOffset; + } + u32 GetLabelStringChunkSize() const { return mHeader.symbolDataSize; } + u32 GetInfoChunkOffset() const { return mHeader.infoOffset; } + u32 GetInfoChunkSize() const { return mHeader.infoSize; } -} // namespace detail -} // namespace snd -} // namespace nw4r + char const *GetString(u32 id) const; + SoundArchive::SoundType GetSoundType(u32 soundId) const; + u32 GetBankCount() const; + u32 GetPlayerCount() const; + u32 GetGroupCount() const; + u32 GetFileCount() const; + void const *GetPtrConst(void const *base, u32 offset) const + { + if (offset == 0) + return nullptr; -#endif + return ut::AddOffsetToPtr(base, offset); + } + + void SetStringChunk(void const *stringChunk, u32 stringChunkSize); + void SetInfoChunk(void const *infoChunk, u32 infoChunkSize); + + bool ReadSeqSoundInfo(u32 soundId, + SoundArchive::SeqSoundInfo *info) const; + bool ReadStrmSoundInfo(u32 soundId, + SoundArchive::StrmSoundInfo *info) const; + bool ReadWaveSoundInfo(u32 soundId, + SoundArchive::WaveSoundInfo *info) const; + bool ReadSoundArchivePlayerInfo( + SoundArchive::SoundArchivePlayerInfo *info) const; + bool ReadSoundInfo(u32 soundId, SoundArchive::SoundInfo *info) const; + + bool ReadBankInfo(u32 bankId, SoundArchive::BankInfo *info) const; + + bool ReadPlayerInfo(u32 playerId, SoundArchive::PlayerInfo *info) const; + + bool ReadGroupItemInfo(u32 groupId, u32 index, + SoundArchive::GroupItemInfo *info) const; + bool ReadGroupInfo(u32 groupId, SoundArchive::GroupInfo *info) const; + + bool ReadFileInfo(u32 fileId, SoundArchive::FileInfo *info) const; + bool ReadFilePos(u32 fileId, u32 index, + SoundArchive::FilePos *filePos) const; + + u32 ConvertLabelStringToId(SoundArchiveFile::StringTree const *tree, + char const *str) const; + u32 ConvertLabelStringToSoundId(char const *label) const + { + return ConvertLabelStringToId(mStringTreeSound, label); + } + + private: + static bool IsValidFileHeader(void const *soundArchiveData); + + SoundArchiveFile::SeqSoundInfo const *impl_GetSeqSoundInfo( + u32 soundId) const; + SoundArchiveFile::StrmSoundInfo const *impl_GetStrmSoundInfo( + u32 soundId) const; + SoundArchiveFile::WaveSoundInfo const *impl_GetWaveSoundInfo( + u32 soundId) const; + SoundArchiveFile::SoundCommonInfo const *impl_GetSoundInfo( + u32 soundId) const; + + bool impl_GetSoundInfoOffset( + u32 soundId, SoundArchiveFile::SoundInfoRef *soundInfoRef) const; + + SoundArchiveFile::BankInfo const *impl_GetBankInfo(u32 bankId) const; + + SoundArchiveFile::PlayerInfo const *impl_GetPlayerInfo( + u32 playerId) const; + + SoundArchiveFile::GroupInfo const *impl_GetGroupInfo(u32 groupId) const; + + // static members + public: + static u16 const SUPPORTED_FILE_VERSION = NW4R_FILE_VERSION(1, 4); + + // members + private: + SoundArchiveFile::Header mHeader; // size 0x28, offset 0x00 + SoundArchiveFile::Info const *mInfo; // size 0x04, offset 0x28 + void const *mStringBase; // size 0x04, offset 0x2c + SoundArchiveFile::StringTable const *mStringTable; // size 0x04, offset 0x30 + SoundArchiveFile::StringTree const *mStringTreeSound; // size 0x04, offset 0x34 + SoundArchiveFile::StringTree const *mStringTreePlayer; // size 0x04, offset 0x38 + SoundArchiveFile::StringTree const *mStringTreeGroup; // size 0x04, offset 0x3c + SoundArchiveFile::StringTree const *mStringTreeBank; // size 0x04, offset 0x40 + }; // size 0x44 +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_SOUND_ARCHIVE_FILE_H diff --git a/include/nw4r/snd/snd_SoundArchivePlayer.h b/include/nw4r/snd/snd_SoundArchivePlayer.h index c60cb995..8ef77365 100644 --- a/include/nw4r/snd/snd_SoundArchivePlayer.h +++ b/include/nw4r/snd/snd_SoundArchivePlayer.h @@ -1,205 +1,266 @@ #ifndef NW4R_SND_SOUND_ARCHIVE_PLAYER_H #define NW4R_SND_SOUND_ARCHIVE_PLAYER_H -#include "nw4r/snd/snd_DisposeCallback.h" -#include "nw4r/snd/snd_MmlParser.h" -#include "nw4r/snd/snd_MmlSeqTrackAllocator.h" -#include "nw4r/snd/snd_NoteOnCallback.h" -#include "nw4r/snd/snd_SeqSound.h" -#include "nw4r/snd/snd_SoundArchive.h" -#include "nw4r/snd/snd_SoundInstanceManager.h" -#include "nw4r/snd/snd_SoundStartable.h" -#include "nw4r/snd/snd_StrmChannel.h" -#include "nw4r/snd/snd_StrmSound.h" -#include "nw4r/snd/snd_Util.h" -#include "nw4r/snd/snd_WaveSound.h" -#include "nw4r/snd/snd_WsdPlayer.h" -#include "nw4r/types_nw4r.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { +#include -// Forward declarations -namespace detail { -class SeqTrackAllocator; -} -class SoundMemoryAllocatable; -class SoundPlayer; +// WARNING: DO NOT REORDER these #include directives, data pooling depends on it -class SoundArchivePlayer_FileManager { -public: - virtual const void *GetFileAddress(u32 id) = 0; // at 0x8 - virtual const void *GetFileWaveDataAddress(u32 id) = 0; // at 0x8 -}; +// clang-format off +#include "BasicSound.h" +#include "DisposeCallbackManager.h" // detail::DisposeCallback +#include "MmlParser.h" +#include "NoteOnCallback.h" // This needs to be +#include "MmlSeqTrackAllocator.h" // before this +#include "SeqPlayer.h" +#include "SeqSound.h" +#include "SoundArchive.h" +#include "SoundInstanceManager.h" +#include "WsdPlayer.h" // and this needs to be +#include "SoundStartable.h" // before this +#include "StrmChannel.h" // detail::StrmBufferPool +#include "StrmSound.h" +#include "Util.h" // Util::Table +#include "WaveFile.h" +#include "WaveSound.h" +// clang-format on -class SoundArchivePlayer : public detail::DisposeCallback, public SoundStartable { -public: - SoundArchivePlayer(); - virtual ~SoundArchivePlayer(); // at 0x8 +#include - virtual void InvalidateData(const void *pStart, - const void *pEnd); // at 0xC +/******************************************************************************* + * types + */ - virtual void InvalidateWaveData(const void *pStart, - const void *pEnd); // at 0x10 +// forward declarations +namespace nw4r { namespace snd { namespace detail { class PlayerHeap; }}} +namespace nw4r { namespace snd { namespace detail { class SeqTrackAllocator; }}} +namespace nw4r { namespace snd { class SoundActor; }} +namespace nw4r { namespace snd { class SoundHandle; }} +namespace nw4r { namespace snd { class SoundPlayer; }} - virtual u32 detail_ConvertLabelStringToSoundId(const char *pLabel) { - return mSoundArchive->ConvertLabelStringToSoundId(pLabel); - } // at 0x28 +/******************************************************************************* + * classes and functions + */ - virtual StartResult detail_SetupSound( - SoundHandle *pHandle, u32 id, bool hold, - const StartInfo *pStartInfo - ); // at 0x2C +namespace nw4r { namespace snd +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x28a19 + class SoundArchivePlayer_FileManager + { + // methods + public: + // virtual function ordering + // vtable SoundArchivePlayer_FileManager + virtual void const *at_0x08(int) = 0; + virtual void const *at_0x0c(int) = 0; - bool IsAvailable() const; + // members + private: + /* vtable */ // size 0x04, offset 0x00 + }; // size 0x04 - bool - Setup(const SoundArchive *pArchive, void *pMramBuffer, u32 mramBufferSize, void *pStrmBuffer, u32 strmBufferSize); + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x31598 + class SoundArchivePlayer : public detail::DisposeCallback, + public SoundStartable + { + // nested types + private: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2e0ad7 + struct GroupAddress + { + void const *address; // size 0x04, offset 0x00 + void const *waveDataAddress; // size 0x04, offset 0x04 + }; // size 0x08 - void Shutdown(); + typedef detail::Util::Table GroupAddressTable; - u32 GetRequiredMemSize(const SoundArchive *pArchive); - u32 GetRequiredStrmBufferSize(const SoundArchive *pArchive); + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2e0bdd + struct FileAddress + { + void const *address; // size 0x04, offset 0x00 + void const *waveDataAddress; // size 0x04, offset 0x04 + }; // size 0x08 - void Update(); + typedef detail::Util::Table FileAddressTable; - const SoundArchive &GetSoundArchive() const; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2e4f9 + class SeqNoteOnCallback : public detail::NoteOnCallback + { + // methods + public: + // cdtors + SeqNoteOnCallback(SoundArchivePlayer const &player) : + mSoundArchivePlayer(player) + { + } - SoundPlayer &GetSoundPlayer(u32 i); - SoundPlayer &GetSoundPlayer(int i) { - return GetSoundPlayer(static_cast(i)); - } + // virtual function ordering + // vtable NoteOnCallback + virtual detail::Channel *NoteOn( + detail::SeqPlayer *seqPlayer, int bankNo, + detail::NoteOnInfo const ¬eOnInfo); - const void *detail_GetFileAddress(u32 id) const; - const void *detail_GetFileWaveDataAddress(u32 id) const; + // members + private: + /* base NoteOnCallback */ // size 0x04, offset 0x00 + SoundArchivePlayer const &mSoundArchivePlayer; // size 0x04, offset 0x04 + }; // size 0x08 - const void *GetGroupAddress(u32 id) const; - void SetGroupAddress(u32 id, const void *pAddr); + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2e8e1 + class WsdCallback : public detail::WsdPlayer::WsdCallback + { + // methods + public: + // cdtors + WsdCallback(SoundArchivePlayer const &player) : + mSoundArchivePlayer(player) + { + } + virtual ~WsdCallback() {} - const void *GetGroupWaveDataAddress(u32 id) const; - void SetGroupWaveDataAddress(u32 id, const void *pAddr); + // virtual function ordering + // vtable detail::WsdPlayer::WsdCallback + virtual bool GetWaveSoundData(detail::WaveSoundInfo *info, + detail::WaveSoundNoteInfo *noteInfo, + detail::WaveInfo *waveData, + void const *waveSoundData, int index, + int noteIndex, + register_t userData) const; - bool LoadGroup(u32 id, SoundMemoryAllocatable *pAllocatable, u32 blockSize); - bool LoadGroup(const char *pLabel, SoundMemoryAllocatable *pAllocatable, u32 blockSize); + // members + private: + /* base detail::WsdPlayer::WsdCallback */ // size 0x04, offset 0x00 + SoundArchivePlayer const &mSoundArchivePlayer; // size 0x04, offset 0x04 + }; // size 0x08 - bool LoadGroup(int id, SoundMemoryAllocatable *pAllocatable, u32 blockSize) { - return LoadGroup(static_cast(id), pAllocatable, blockSize); - } - bool LoadGroup(unsigned int id, SoundMemoryAllocatable *pAllocatable, u32 blockSize) { - return LoadGroup(static_cast(id), pAllocatable, blockSize); - } + // methods + public: + // cdtors + SoundArchivePlayer(); + virtual ~SoundArchivePlayer(); - u32 GetSoundPlayerCount() const { - return mSoundPlayerCount; - } + // virtual function ordering + // vtable detail::DisposeCallback + virtual void InvalidateData(void const *start, void const *end); + virtual void InvalidateWaveData(void const *start, void const *end); - u32 GetFreeSeqSoundCount() const { - return mSeqSoundInstanceManager.GetFreeCount(); - } - u32 GetFreeStrmSoundCount() const { - return mStrmSoundInstanceManager.GetFreeCount(); - } - u32 GetFreeWaveSoundCount() const { - return mWaveSoundInstanceManager.GetFreeCount(); - } + // vtable SoundStartable + // WARNING: DO NOT REORDER these declarations + virtual u32 detail_ConvertLabelStringToSoundId(char const *label) + { + // specifically not the header variant + NW4RAssertPointerNonnull_Line(194, mSoundArchive); -private: - struct Group { - const void *address; // at 0x0 - const void *waveDataAddress; // at 0x4 - }; + return mSoundArchive->ConvertLabelStringToSoundId(label); + } + virtual StartResult detail_SetupSound(SoundHandle *handle, u32 soundId, + bool holdFlag, + StartInfo const *startInfo); - typedef detail::Util::Table GroupTable; + // methods + bool Setup(SoundArchive const *arc, void *buffer, u32 size, + void *strmBuffer, u32 strmBufferSize); + void Shutdown(); - class SeqNoteOnCallback : public detail::NoteOnCallback { - public: - explicit SeqNoteOnCallback(const SoundArchivePlayer &rPlayer) : mSoundArchivePlayer(rPlayer) {} + SoundArchive const &GetSoundArchive() const; + u32 GetSoundPlayerCount() const { return mSoundPlayerCount; } + SoundPlayer &GetSoundPlayer(u32 playerId); - virtual detail::Channel *NoteOn( - detail::SeqPlayer *pPlayer, int bankNo, - const detail::NoteOnInfo &rInfo - ); // at 0xC + bool IsAvailable() const; + u32 GetRequiredMemSize(SoundArchive const *arc); + u32 GetRequiredStrmBufferSize(SoundArchive const *arc); + void const *GetGroupAddress(u32 groupId) const; + void const *GetGroupWaveDataAddress(u32 groupId) const; + void const *GetFileAddress(u32 fileId) const; + void const *GetFileWaveDataAddress(u32 fileId) const; + void const *detail_GetFileAddress(u32 fileId) const; + void const *detail_GetFileWaveDataAddress(u32 fileId) const; - private: - const SoundArchivePlayer &mSoundArchivePlayer; // at 0x0 - }; + bool SetupMram(SoundArchive const *arc, void *buffer, u32 size); + bool SetupStrmBuffer(SoundArchive const *arc, void *buffer, u32 size); + bool SetupSoundPlayer(SoundArchive const *arc, void **buffer, + void *endp); - class WsdCallback : public detail::WsdPlayer::WsdCallback { - public: - explicit WsdCallback(const SoundArchivePlayer &rPlayer) : mSoundArchivePlayer(rPlayer) {} + bool SetupSeqSound(SoundArchive const *arc, int numSounds, + void **buffer, void *endp); + bool SetupWaveSound(SoundArchive const *arc, int numSounds, + void **buffer, void *endp); + bool SetupStrmSound(SoundArchive const *arc, int numSounds, + void **buffer, void *endp); + bool SetupSeqTrack(SoundArchive const *arc, int numTracks, + void **buffer, void *endp); - virtual bool GetWaveSoundData( - detail::WaveSoundInfo *pSoundInfo, detail::WaveSoundNoteInfo *pNoteInfo, detail::WaveData *pWaveData, - const void *pWsdData, int index, int noteIndex, - u32 callbackArg - ) const; // at 0xC + bool CreateGroupAddressTable(SoundArchive const *arc, void **buffer, + void *endp); + bool CreateFileAddressTable(SoundArchive const *arc, void **buffer, + void *endp); - private: - const SoundArchivePlayer &mSoundArchivePlayer; // at 0x0 - }; + detail::PlayerHeap *CreatePlayerHeap(void **buffer, void *endp, + u32 heapSize); -private: - bool SetupMram(const SoundArchive *pArchive, void *pBuffer, u32 bufferSize); + void Update(); + void UpdateCommonSoundParam(detail::BasicSound *sound, + SoundArchive::SoundInfo const *commonInfo); - detail::PlayerHeap *CreatePlayerHeap(void *pBuffer, u32 bufferSize); + private: + template + Sound *AllocSound( + detail::SoundInstanceManager *manager, u32 soundId, + int priority, int ambientPriority, + detail::BasicSound::AmbientInfo *ambientArgInfo); - bool SetupSoundPlayer(const SoundArchive *pArchive, void **ppBuffer, void *pEnd); + SoundStartable::StartResult detail_SetupSoundImpl( + SoundHandle *handle, u32 soundId, + detail::BasicSound::AmbientInfo *ambientArgInfo, SoundActor *actor, + bool holdFlag, SoundStartable::StartInfo const *startInfo); + SoundStartable::StartResult PrepareSeqImpl( + detail::SeqSound *sound, SoundArchive::SoundInfo const *commonInfo, + SoundArchive::SeqSoundInfo const *info, + SoundStartable::StartInfo::StartOffsetType startOffsetType, + int startOffset, void const *externalSeqDataAddress, + char const *externalSeqStartLabel); + SoundStartable::StartResult PrepareStrmImpl( + detail::StrmSound *sound, SoundArchive::SoundInfo const *commonInfo, + SoundArchive::StrmSoundInfo const *info, + SoundStartable::StartInfo::StartOffsetType startOffsetType, + int startOffset); + SoundStartable::StartResult PrepareWaveSoundImpl( + detail::WaveSound *sound, SoundArchive::SoundInfo const *commonInfo, + SoundArchive::WaveSoundInfo const *info, + SoundStartable::StartInfo::StartOffsetType startOffsetType, + int startOffset); - bool CreateGroupAddressTable(const SoundArchive *pArchive, void **ppBuffer, void *pEnd); + // static members + public: + static int const DEFAULT_STREAM_BLOCK_COUNT; - bool SetupSeqSound(const SoundArchive *pArchive, int sounds, void **ppBuffer, void *pEnd); - bool SetupWaveSound(const SoundArchive *pArchive, int sounds, void **ppBuffer, void *pEnd); - bool SetupStrmSound(const SoundArchive *pArchive, int sounds, void **ppBuffer, void *pEnd); - bool SetupSeqTrack(const SoundArchive *pArchive, int tracks, void **ppBuffer, void *pEnd); - bool SetupStrmBuffer(const SoundArchive *pArchive, void *pBuffer, u32 bufferSize); + // members + private: + /* base detail::DisposeCallback */ // size 0x0c, offset 0x00 + /* base SoundStartable */ // size 0x04, offset 0x0c + SoundArchive const *mSoundArchive; // size 0x04, offset 0x10 + GroupAddressTable *mGroupTable; // size 0x04, offset 0x14 + FileAddressTable *mFileTable; // size 0x04, offset 0x18 + SoundArchivePlayer_FileManager *mFileManager; // size 0x04, offset 0x1c + SeqNoteOnCallback mSeqCallback; // size 0x08, offset 0x20 + WsdCallback mWsdCallback; // size 0x08, offset 0x28 + detail::SeqTrackAllocator *mSeqTrackAllocator; // size 0x04, offset 0x30 + detail::SeqPlayer::SeqUserprocCallback *mSeqUserprocCallback; // size 0x04, offset 0x34 + void *mSeqUserprocCallbackArg; // size 0x04, offset 0x38 + u32 mSoundPlayerCount; // size 0x04, offset 0x3c + SoundPlayer *mSoundPlayers; // size 0x04, offset 0x40 + detail::SoundInstanceManager mSeqSoundInstanceManager; // size 0x28, offset 0x44 + detail::SoundInstanceManager mStrmSoundInstanceManager; // size 0x28, offset 0x6c + detail::SoundInstanceManager mWaveSoundInstanceManager; // size 0x28, offset 0x94 + detail::MmlSeqTrackAllocator mMmlSeqTrackAllocator; // size 0x0c, offset 0xbc + detail::StrmBufferPool mStrmBufferPool; // size 0x18, offset 0xc8 + detail::MmlParser mMmlParser; // size 0x04, offset 0xe0 + void *mSetupBufferAddress; // size 0x04, offset 0xe4 + u32 mSetupBufferSize; // size 0x04, offset 0xe8 + }; // size 0xec +}} // namespace nw4r::snd - StartResult PrepareSeqImpl( - detail::SeqSound *pSound, const SoundArchive::SoundInfo *pSndInfo, const SoundArchive::SeqSoundInfo *pSeqInfo, - SoundStartable::StartInfo::StartOffsetType startType, int startOffset, int voices - ); - - StartResult PrepareStrmImpl( - detail::StrmSound *pSound, const SoundArchive::SoundInfo *pSndInfo, - const SoundArchive::StrmSoundInfo *pStrmInfo, SoundStartable::StartInfo::StartOffsetType startType, - int startOffset, int voices - ); - - StartResult PrepareWaveSoundImpl( - detail::WaveSound *pSound, const SoundArchive::SoundInfo *pSndInfo, const SoundArchive::WaveSoundInfo *pWsdInfo, - SoundStartable::StartInfo::StartOffsetType startType, int startOffset, int voices - ); - -private: - const SoundArchive *mSoundArchive; // at 0x10 - GroupTable *mGroupTable; // at 0x14 - SoundArchivePlayer_FileManager *mFileManager; // at 0x18 - - detail::SeqTrackAllocator *mSeqTrackAllocator; // at 0x1C - SeqNoteOnCallback mSeqCallback; // at 0x20 - WsdCallback mWsdCallback; // at 0x28 - - // @TODO The comments here are wrong - u8 field_0x30[0x3C - 0x30]; - - u32 mSoundPlayerCount; // at 0x30 - SoundPlayer *mSoundPlayers; // at 0x34 - - detail::SoundInstanceManager mSeqSoundInstanceManager; // at 0x38 - - detail::SoundInstanceManager mStrmSoundInstanceManager; // at 0x60 - - detail::SoundInstanceManager mWaveSoundInstanceManager; // at 0x88 - - detail::MmlSeqTrackAllocator mMmlSeqTrackAllocator; // at 0xB0 - detail::StrmBufferPool mStrmBufferPool; // at 0xBC - detail::MmlParser mMmlParser; // at 0xD4 - - void *mSetupBufferAddress; // at 0xD8 - u32 mSetupBufferSize; // at 0xDC -}; - -} // namespace snd -} // namespace nw4r - -#endif +#endif // NW4R_SND_SOUND_ARCHIVE_PLAYER_H diff --git a/include/nw4r/snd/snd_SoundHandle.h b/include/nw4r/snd/snd_SoundHandle.h index ff16cbaf..9a219f3d 100644 --- a/include/nw4r/snd/snd_SoundHandle.h +++ b/include/nw4r/snd/snd_SoundHandle.h @@ -1,110 +1,56 @@ #ifndef NW4R_SND_SOUND_HANDLE_H #define NW4R_SND_SOUND_HANDLE_H -#include "nw4r/snd/snd_BasicSound.h" -#include "nw4r/types_nw4r.h" -#include "nw4r/ut.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { +#include -class SoundHandle : private ut::NonCopyable { -public: - SoundHandle() : mSound(NULL) {} - ~SoundHandle() { - DetachSound(); - } +#include "BasicSound.h" - void detail_AttachSound(detail::BasicSound *pSound); - void detail_AttachSoundAsTempHandle(detail::BasicSound *pSound); +#include "../ut/inlines.h" // ut::NonCopyable - bool IsAttachedSound() const { - return mSound != NULL; - } +/******************************************************************************* + * classes and functions + */ - detail::BasicSound *detail_GetAttachedSound() { - return mSound; - } +namespace nw4r { namespace snd +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2894f + class SoundHandle : private ut::NonCopyable + { + // methods + public: + // cdtors + SoundHandle() : mSound() {} + ~SoundHandle() { DetachSound(); } - void DetachSound(); + // methods + void detail_AttachSound(detail::BasicSound *sound); + bool IsAttachedSound() const { return mSound != nullptr; } + detail::BasicSound *detail_GetAttachedSound() { return mSound; } + void DetachSound(); - bool IsPrepared() const { - if (IsAttachedSound()) { - return mSound->IsPrepared(); - } + u32 GetId() const + { + if (IsAttachedSound()) + return mSound->GetId(); - return false; - } + return detail::BasicSound::INVALID_ID; + } - u32 GetId() const { - if (IsAttachedSound()) { - return mSound->GetId(); - } + void StartPrepared() + { + if (IsAttachedSound()) + mSound->StartPrepared(); + } - return -1; - } + // members + private: + /* base NonCopyable */ // size 0x00, offset 0x00 + detail::BasicSound *mSound; // size 0x04, offset 0x00 + }; // size 0x04 +}} // namespace nw4r::snd - void StartPrepared() { - if (IsAttachedSound()) { - mSound->StartPrepared(); - } - } - void Stop(int frames) { - if (IsAttachedSound()) { - mSound->Stop(frames); - } - } - - void Pause(bool flag, int frames) { - if (IsAttachedSound()) { - mSound->Pause(flag, frames); - } - } - - void SetVolume(f32 volume, int frames) { - if (IsAttachedSound()) { - mSound->SetVolume(volume, frames); - } - } - void SetPan(f32 pan) { - if (IsAttachedSound()) { - mSound->SetPan(pan); - } - } - void SetPitch(f32 pitch) { - if (IsAttachedSound()) { - mSound->SetPitch(pitch); - } - } - - void SetOutputLine(int flag) { - if (IsAttachedSound()) { - mSound->SetOutputLine(flag); - } - } - - void SetMainOutVolume(f32 volume) { - if (IsAttachedSound()) { - mSound->SetMainOutVolume(volume); - } - } - void SetRemoteOutVolume(int remote, f32 volume) { - if (IsAttachedSound()) { - mSound->SetRemoteOutVolume(remote, volume); - } - } - - void SetFxSend(AuxBus bus, f32 send) { - if (IsAttachedSound()) { - mSound->SetFxSend(bus, send); - } - } - -private: - detail::BasicSound *mSound; // at 0x0 -}; - -} // namespace snd -} // namespace nw4r - -#endif +#endif // NW4R_SND_SOUND_HANDLE_H diff --git a/include/nw4r/snd/snd_SoundInstanceManager.h b/include/nw4r/snd/snd_SoundInstanceManager.h index d6aa4f2b..d7984f26 100644 --- a/include/nw4r/snd/snd_SoundInstanceManager.h +++ b/include/nw4r/snd/snd_SoundInstanceManager.h @@ -1,142 +1,191 @@ #ifndef NW4R_SND_SOUND_INSTANCE_MANAGER_H #define NW4R_SND_SOUND_INSTANCE_MANAGER_H -#include "nw4r/snd/snd_InstancePool.h" -#include "nw4r/types_nw4r.h" -#include "nw4r/ut.h" // IWYU pragma: export -#include "rvl/OS.h" // IWYU pragma: export -namespace nw4r { -namespace snd { -namespace detail { +/******************************************************************************* + * headers + */ -template -class SoundInstanceManager { -private: - NW4R_UT_LIST_TYPEDEF_DECL_EX(T, Prio); +#include -public: - SoundInstanceManager() { - OSInitMutex(&mMutex); - } +#include +#include - u32 Create(void *pBuffer, u32 size) { - ut::detail::AutoLock lock(mMutex); - return mPool.Create(pBuffer, size); - } +#include "InstancePool.h" - void Destroy(void *pBuffer, u32 size) { - ut::detail::AutoLock lock(mMutex); - mPool.Destroy(pBuffer, size); - } - - T *Alloc(int priority) { - ut::detail::AutoLock lock(mMutex); - T *pSound = NULL; - - while (pSound == NULL) { - void *pBuffer = mPool.Alloc(); - - if (pBuffer != NULL) { - pSound = new (pBuffer) T(this); - } else { - T *pLowest = GetLowestPrioritySound(); - - if (pLowest == NULL) { - return NULL; - } - - if (priority < pLowest->CalcCurrentPlayerPriority()) { - return NULL; - } - - OSUnlockMutex(&mMutex); - pLowest->Stop(0); - OSLockMutex(&mMutex); - } - } - - InsertPriorityList(pSound, priority); - return pSound; - } - - void Free(T *pSound) { - ut::detail::AutoLock lock(mMutex); - - if (mPriorityList.IsEmpty()) { - return; - } - - RemovePriorityList(pSound); - pSound->~T(); - mPool.Free(pSound); - } - - u32 GetActiveCount() const { - return mPriorityList.GetSize(); - } - - u32 GetFreeCount() const { - return mPool.Count(); - } - - T *GetLowestPrioritySound() { - if (mPriorityList.IsEmpty()) { - return NULL; - } - - return static_cast(&mPriorityList.GetFront()); - } - - void InsertPriorityList(T *pSound, int priority) { - TPrioList::Iterator it = mPriorityList.GetBeginIter(); - - for (; it != mPriorityList.GetEndIter(); ++it) { - if (priority < it->CalcCurrentPlayerPriority()) { - break; - } - } - - mPriorityList.Insert(it, pSound); - } - - void RemovePriorityList(T *pSound) { - mPriorityList.Erase(pSound); - } - - void SortPriorityList() { - TPrioList listsByPrio[T::PRIORITY_MAX + 1]; - ut::detail::AutoLock lock(mMutex); - - while (!mPriorityList.IsEmpty()) { - T &rSound = mPriorityList.GetFront(); - mPriorityList.PopFront(); - listsByPrio[rSound.CalcCurrentPlayerPriority()].PushBack(&rSound); - } - - for (int i = 0; i < T::PRIORITY_MAX + 1; i++) { - while (!listsByPrio[i].IsEmpty()) { - T &rSound = listsByPrio[i].GetFront(); - listsByPrio[i].PopFront(); - mPriorityList.PushBack(&rSound); - } - } - } - - void UpdatePriority(T *pSound, int priority) { - ut::detail::AutoLock lock(mMutex); - - RemovePriorityList(pSound); - InsertPriorityList(pSound, priority); - } - -private: - MemoryPool mPool; // at 0x0 - TPrioList mPriorityList; // at 0x4 - mutable OSMutex mMutex; // at 0x10 -}; - -} // namespace detail -} // namespace snd -} // namespace nw4r +#include "../ut/Lock.h" +#include "../ut/inlines.h" +#if 0 +#include +#else +#include #endif + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * classes and functions + */ + +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2f03c, 0x30a63... + template + class SoundInstanceManager + { + // methods + public: + // cdtors + SoundInstanceManager() { OSInitMutex(&mMutex); } + + // methods + u32 Create(void *buffer, u32 size) + { + NW4RAssertPointerNonnull_Line(67, buffer); + + ut::detail::AutoLock lock(mMutex); + + return mPool.Create(buffer, size); + } + + void Destroy(void *buffer, u32 size) + { + NW4RAssertPointerNonnull_Line(86, buffer); + + ut::detail::AutoLock lock(mMutex); + + mPool.Destroy(buffer, size); + } + + Sound *Alloc(int priority, int ambientPriority) + { + int allocPriority = + ut::Clamp(priority + ambientPriority, Sound::PRIORITY_MIN, + Sound::PRIORITY_MAX); + + ut::detail::AutoLock lock(mMutex); + Sound *sound = nullptr; + + while (!sound) + { + if (void *ptr = mPool.Alloc()) + { + sound = new (ptr) Sound(this, priority, ambientPriority); + continue; + } + + Sound *sound = GetLowestPrioritySound(); + if (!sound) + return nullptr; + + if (allocPriority < sound->CalcCurrentPlayerPriority()) + return nullptr; + + OSUnlockMutex(&mMutex); + + NW4RCheckMessage_Line(133, + !Debug_GetWarningFlag( + Debug_GetDebugWarningFlagFromSoundType( + sound->GetSoundType())), + "Sound (id:%d) is stopped for not enough %s sound " + "instance.", + sound->GetId(), + Debug_GetSoundTypeString(sound->GetSoundType())); + + sound->Stop(0); + + OSLockMutex(&mMutex); + } + + InsertPriorityList(sound, allocPriority); + return sound; + } + + void Free(Sound *sound) + { + NW4RAssertPointerNonnull_Line(158, sound); + + ut::detail::AutoLock lock(mMutex); + + if (mPriorityList.IsEmpty()) + return; + + RemovePriorityList(sound); + sound->~Sound(); + mPool.Free(sound); + } + + u32 GetActiveCount() const { return mPriorityList.GetSize(); } + + Sound *GetLowestPrioritySound() + { + if (mPriorityList.IsEmpty()) + return nullptr; + + return &mPriorityList.GetFront(); + } + + void InsertPriorityList(Sound *sound, int priority) + { + decltype(mPriorityList.GetBeginIter()) itr = + mPriorityList.GetBeginIter(); + + for (; itr != mPriorityList.GetEndIter(); ++itr) + { + if (priority < itr->CalcCurrentPlayerPriority()) + break; + } + + mPriorityList.Insert(itr, sound); + } + + void SortPriorityList() + { + int const TMP_NUM = Sound::PRIORITY_MAX + 1; + + if (mPriorityList.GetSize() < 2) + return; + + ut::detail::AutoLock lock(mMutex); + + typename Sound::PriorityLinkList tmpList[TMP_NUM]; + + while (!mPriorityList.IsEmpty()) + { + Sound &front = mPriorityList.GetFront(); + mPriorityList.PopFront(); + + tmpList[front.CalcCurrentPlayerPriority()].PushBack(&front); + } + + for (int i = 0; i < TMP_NUM; i++) + { + while (!tmpList[i].IsEmpty()) + { + Sound &front = tmpList[i].GetFront(); + tmpList[i].PopFront(); + + mPriorityList.PushBack(&front); + } + } + } + + void RemovePriorityList(Sound *sound) { mPriorityList.Erase(sound); } + + void UpdatePriority(Sound *sound, int priority) + { + ut::detail::AutoLock lock(mMutex); + + RemovePriorityList(sound); + InsertPriorityList(sound, priority); + } + + // members + private: + MemoryPool mPool; // size 0x04, offset 0x00 + typename Sound::PriorityLinkList mPriorityList; // size 0x0c, offset 0x04 + mutable OSMutex mMutex; // size 0x18, offset 0x10 + }; // size 0x28 +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_SOUND_INSTANCE_MANAGER_H diff --git a/include/nw4r/snd/snd_SoundMemoryAllocatable.h b/include/nw4r/snd/snd_SoundMemoryAllocatable.h index 5fae7595..68a568d1 100644 --- a/include/nw4r/snd/snd_SoundMemoryAllocatable.h +++ b/include/nw4r/snd/snd_SoundMemoryAllocatable.h @@ -1,18 +1,34 @@ #ifndef NW4R_SND_SOUND_MEMORY_ALLOCATABLE_H #define NW4R_SND_SOUND_MEMORY_ALLOCATABLE_H -#include "nw4r/types_nw4r.h" -namespace nw4r { -namespace snd { +/******************************************************************************* + * headers + */ -class SoundMemoryAllocatable { -public: - virtual ~SoundMemoryAllocatable() {} // at 0x8 +#include // u32 - virtual void *Alloc(u32 size) = 0; // at 0xC -}; +/******************************************************************************* + * classes and functions + */ -} // namespace snd -} // namespace nw4r +namespace nw4r { namespace snd +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x269c0 + class SoundMemoryAllocatable + { + // methods + public: + // cdtors + virtual ~SoundMemoryAllocatable() {} -#endif + // virtual function ordering + // vtable SoundMemoryAllocatable + virtual void *Alloc(u32 size) = 0; + + // members + private: + /* vtable */ // size 0x04, offset 0x00 + }; // size 0x04 +}} // namespace nw4r::snd + +#endif // NW4R_SND_SOUND_MEMORY_ALLOCATABLE_H diff --git a/include/nw4r/snd/snd_SoundPlayer.h b/include/nw4r/snd/snd_SoundPlayer.h index ff276794..fc10bc5d 100644 --- a/include/nw4r/snd/snd_SoundPlayer.h +++ b/include/nw4r/snd/snd_SoundPlayer.h @@ -1,117 +1,86 @@ #ifndef NW4R_SND_SOUND_PLAYER_H #define NW4R_SND_SOUND_PLAYER_H -#include "nw4r/snd/snd_BasicSound.h" -#include "nw4r/snd/snd_PlayerHeap.h" -#include "nw4r/types_nw4r.h" -#include "rvl/OS.h" // IWYU pragma: export -#include "rvl/WPAD.h" // IWYU pragma: export -namespace nw4r { -namespace snd { +/******************************************************************************* + * headers + */ -// Forward declarations -namespace detail { -class ExternalSoundPlayer; -class SeqSound; -class StrmSound; -class WaveSound; -template -class SoundInstanceManager; -} // namespace detail +#include // f32 -class SoundPlayer { -public: - SoundPlayer(); - ~SoundPlayer(); +#include "BasicSound.h" +#include "global.h" // AUX_BUS_NUM +#include "PlayerHeap.h" - void InitParam(); - void Update(); +/******************************************************************************* + * classes + */ - void StopAllSound(int frames); - void PauseAllSound(bool flag, int frames); +namespace nw4r { namespace snd +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x26c0f + // NOTE: different from ketteiban: no remote fields + class SoundPlayer + { + // methods + public: + // cdtors + SoundPlayer(); + ~SoundPlayer(); - void SetVolume(f32 volume); + // methods + int GetPlayableSoundCount() const { return mPlayableCount; } + f32 GetVolume() const { return mVolume; } + f32 GetLpfFreq() const { return mLpfFreq; } + int GetDefaultOutputLine() const { return mOutputLineFlag; } + f32 GetMainOutVolume() const { return mMainOutVolume; } + int GetBiquadFilterType() const { return mBiquadType; } + f32 GetBiquadFilterValue() const { return mBiquadValue; } + f32 GetMainSend() const { return mMainSend; } + f32 GetFxSend(int index) const { return mFxSend[index]; } - int detail_GetOutputLine() const; - bool detail_IsEnabledOutputLine() const; + void SetPlayableSoundCount(int count); - f32 detail_GetRemoteOutVolume(int i) const; + int GetPlayingSoundCount() const { return mSoundList.GetSize(); } + detail::BasicSound *GetLowestPrioritySound() + { + return &mPriorityList.GetFront(); + } - void detail_InsertSoundList(detail::BasicSound *pSound); - void detail_RemoveSoundList(detail::BasicSound *pSound); + void Update(); + void PauseAllSound(bool flag, int fadeFrames); + void StopAllSound(int fadeFrames); - void detail_InsertPriorityList(detail::BasicSound *pSound); - void detail_RemovePriorityList(detail::BasicSound *pSound); + void RemoveSoundList(detail::BasicSound *sound); + bool detail_AppendSound(detail::BasicSound *sound); + void detail_RemoveSound(detail::BasicSound *sound); + void detail_SetPlayableSoundLimit(int limit); + bool detail_CanPlaySound(int startPriority); - void detail_SortPriorityList(); + void InsertPriorityList(detail::BasicSound *sound); + void RemovePriorityList(detail::BasicSound *sound); + void detail_SortPriorityList(detail::BasicSound *sound); + void detail_SortPriorityList(); - detail::SeqSound *detail_AllocSeqSound( - int priority, int startPriority, detail::BasicSound::AmbientArgInfo *pArgInfo, - detail::ExternalSoundPlayer *pExtPlayer, u32 id, detail::SoundInstanceManager *pManager - ); + detail::PlayerHeap *detail_AllocPlayerHeap(detail::BasicSound *sound); + void detail_FreePlayerHeap(detail::BasicSound *sound); + void detail_AppendPlayerHeap(detail::PlayerHeap *heap); - detail::StrmSound *detail_AllocStrmSound( - int priority, int startPriority, detail::BasicSound::AmbientArgInfo *pArgInfo, - detail::ExternalSoundPlayer *pExtPlayer, u32 id, detail::SoundInstanceManager *pManager - ); + // members + private: + detail::BasicSound::SoundPlayerPlayLinkList mSoundList; // size 0x0c, offset 0x00 + detail::BasicSound::SoundPlayerPriorityLinkList mPriorityList; // size 0x0c, offset 0x0c + detail::PlayerHeap::LinkList mHeapList; // size 0x0c, offset 0x18 + int mPlayableCount; // size 0x04, offset 0x24 + int mPlayableLimit; // size 0x04, offset 0x28 + f32 mVolume; // size 0x04, offset 0x2c + f32 mLpfFreq; // size 0x04, offset 0x30 + int mOutputLineFlag; // size 0x04, offset 0x34 + f32 mMainOutVolume; // size 0x04, offset 0x38 + int mBiquadType; // size 0x04, offset 0x3c + f32 mBiquadValue; // size 0x04, offset 0x40 + f32 mMainSend; // size 0x04, offset 0x44 + f32 mFxSend[AUX_BUS_NUM]; // size 0x0c, offset 0x48 + }; // size 0x54 +}} // namespace nw4r::snd - detail::WaveSound *detail_AllocWaveSound( - int priority, int startPriority, detail::BasicSound::AmbientArgInfo *pArgInfo, - detail::ExternalSoundPlayer *pExtPlayer, u32 id, detail::SoundInstanceManager *pManager - ); - - int CalcPriorityReduction(detail::BasicSound::AmbientArgInfo *pArgInfo, u32 id); - - void InitAmbientArg(detail::BasicSound *pSound, detail::BasicSound::AmbientArgInfo *pArgInfo); - - void SetPlayableSoundCount(int count); - void detail_SetPlayableSoundLimit(int limit); - - bool CheckPlayableSoundCount(int startPriority, detail::ExternalSoundPlayer *pExtPlayer); - - void detail_AppendPlayerHeap(detail::PlayerHeap *pHeap); - detail::PlayerHeap *detail_AllocPlayerHeap(detail::BasicSound *pSound); - void detail_FreePlayerHeap(detail::BasicSound *pSound); - - int GetPlayingSoundCount() const { - return mSoundList.GetSize(); - } - int GetPlayableSoundCount() const { - return mPlayableCount; - } - - f32 GetVolume() const { - return mVolume; - } - - detail::BasicSound *detail_GetLowestPrioritySound() { - // @bug UB when the list is empty - return &mPriorityList.GetFront(); - } - - f32 detail_GetMainOutVolume() const { - return mMainOutVolume; - } - -private: - detail::BasicSoundPlayerPlayList mSoundList; // at 0x0 - detail::BasicSoundPlayerPrioList mPriorityList; // at 0xC - detail::PlayerHeapList mHeapList; // at 0x18 - - u16 mPlayableCount; // at 0x24 - u16 mPlayableLimit; // at 0x26 - - f32 mVolume; // at 0x28 - bool mOutputLineFlagEnable; // at 0x2C - bool mUsePlayerHeap; // at 0x2D - int mOutputLineFlag; // at 0x30 - f32 mMainOutVolume; // at 0x34 - f32 mRemoteOutVolume[WPAD_MAX_CONTROLLERS]; // at 0x38 - - mutable OSMutex mMutex; // at 0x48 -}; - -} // namespace snd -} // namespace nw4r - -#endif +#endif // NW4R_SND_SOUND_PLAYER_H diff --git a/include/nw4r/snd/snd_SoundStartable.h b/include/nw4r/snd/snd_SoundStartable.h index 7a2ad15d..5c2f3a8f 100644 --- a/include/nw4r/snd/snd_SoundStartable.h +++ b/include/nw4r/snd/snd_SoundStartable.h @@ -1,117 +1,116 @@ #ifndef NW4R_SND_SOUND_STARTABLE_H #define NW4R_SND_SOUND_STARTABLE_H -#include "nw4r/snd/snd_BasicSound.h" -#include "nw4r/types_nw4r.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { +#include // u32 -// Forward declarations -class SoundHandle; -namespace detail { -class ExternalSoundPlayer; -} +/******************************************************************************* + * types + */ -class SoundStartable { -public: - enum StartResult { - START_SUCCESS, - START_ERR_LOW_PRIORITY, - START_ERR_INVALID_LABEL_STRING, - START_ERR_INVALID_SOUNDID, - START_ERR_NOT_DATA_LOADED, - START_ERR_NOT_ENOUGH_PLAYER_HEAP, - START_ERR_CANNOT_OPEN_FILE, - START_ERR_NOT_AVAILABLE, - START_ERR_CANNOT_ALLOCATE_TRACK, - START_ERR_NOT_ENOUGH_INSTANCE, - START_ERR_INVALID_PARAMETER, - START_ERR_INVALID_SEQ_START_LOCATION_LABEL, +// forward declarations +namespace nw4r { namespace snd { class SoundHandle; }} - START_ERR_USER = 128, - START_ERR_UNKNOWN = 255, - }; +/******************************************************************************* + * classes and functions + */ - struct StartInfo { - enum EnableFlag { - ENABLE_START_OFFSET = (1 << 0), - ENABLE_PLAYER_ID = (1 << 1), - ENABLE_PLAYER_PRIORITY = (1 << 2) - }; +namespace nw4r { namespace snd +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x289cb + class SoundStartable + { + // enums + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x267f0 + enum StartResult + { + START_SUCCESS, - enum StartOffsetType { - START_OFFSET_TYPE_MILLISEC, - START_OFFSET_TYPE_TICK, - START_OFFSET_TYPE_SAMPLE - }; + START_ERR_LOW_PRIORITY, + START_ERR_INVALID_LABEL_STRING, + START_ERR_INVALID_SOUNDID, + START_ERR_NOT_DATA_LOADED, + START_ERR_NOT_ENOUGH_PLAYER_HEAP, + START_ERR_CANNOT_OPEN_FILE, + START_ERR_NOT_AVAILABLE, + START_ERR_CANNOT_ALLOCATE_TRACK, + START_ERR_NOT_ENOUGH_INSTANCE, + START_ERR_INVALID_PARAMETER, + START_ERR_INVALID_SEQ_START_LOCATION_LABEL, - u32 enableFlag; // at 0x0 - StartOffsetType startOffsetType; // at 0x4 - int startOffset; // at 0x8 - u32 playerId; // at 0xC - int playerPriority; // at 0x10 - int voiceOutCount; // at 0x14 - }; + START_ERR_USER = 128, -public: - virtual ~SoundStartable() {} // at 0x8 + START_ERR_UNKNOWN = 255, + }; - virtual StartResult detail_SetupSound( - SoundHandle *pHandle, u32 id, bool hold, - const StartInfo *pStartInfo - ) = 0; // at 0xC + // nested types + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x27291 + struct StartInfo + { + // enums + public: + enum EnableFlag + { + ENABLE_START_OFFSET = 1 << 0, + ENABLE_PLAYER_ID = 1 << 1, + ENABLE_PLAYER_PRIORITY = 1 << 2, + ENABLE_ACTOR_PLAYER_ID = 1 << 3, + ENABLE_SEQ_SOUND_INFO = 1 << 4, + }; - virtual u32 detail_ConvertLabelStringToSoundId(const char *pLabel) = 0; // at 0x10 + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x27187 + enum StartOffsetType + { + START_OFFSET_TYPE_MILLISEC, + START_OFFSET_TYPE_TICK, + START_OFFSET_TYPE_SAMPLE, + }; - bool StartSound(SoundHandle *pHandle, u32 id) { - return detail_StartSound(pHandle, id, NULL, NULL, NULL) == START_SUCCESS; - } - bool StartSound(SoundHandle *pHandle, unsigned int id) { - return detail_StartSound(pHandle, id, NULL, NULL, NULL) == START_SUCCESS; - } - bool StartSound(SoundHandle *pHandle, int id) { - return detail_StartSound(pHandle, id, NULL, NULL, NULL) == START_SUCCESS; - } + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x27208 + struct SeqSoundInfo + { + void *seqDataAddress; // size 0x04, offset 0x00 + char const *startLocationLabel; // size 0x04, offset 0x04 + }; // size 0x08 - bool HoldSound(SoundHandle *pHandle, u32 id) { - return detail_HoldSound(pHandle, id, NULL, NULL, NULL) == START_SUCCESS; - } - bool HoldSound(SoundHandle *pHandle, unsigned int id) { - return detail_HoldSound(pHandle, id, NULL, NULL, NULL) == START_SUCCESS; - } - bool HoldSound(SoundHandle *pHandle, int id) { - return detail_HoldSound(pHandle, id, NULL, NULL, NULL) == START_SUCCESS; - } + // members + public: + u32 enableFlag; // size 0x04, offset 0x00 + StartOffsetType startOffsetType; // size 0x04, offset 0x04 + int startOffset; // size 0x04, offset 0x08 + u32 playerId; // size 0x04, offset 0x0c + int playerPriority; // size 0x04, offset 0x10 + int actorPlayerId; // size 0x04, offset 0x14 + SeqSoundInfo seqSoundInfo; // size 0x08, offset 0x18 + }; // size 0x20 - bool PrepareSound(SoundHandle *pHandle, u32 id) { - return detail_PrepareSound(pHandle, id, NULL, NULL, NULL) == START_SUCCESS; - } - bool PrepareSound(SoundHandle *pHandle, unsigned int id) { - return detail_PrepareSound(pHandle, id, NULL, NULL, NULL) == START_SUCCESS; - } - bool PrepareSound(SoundHandle *pHandle, int id) { - return detail_PrepareSound(pHandle, id, NULL, NULL, NULL) == START_SUCCESS; - } + // methods + public: + // cdtors + virtual ~SoundStartable() {} -private: - StartResult detail_StartSound( - SoundHandle *pHandle, u32 id, detail::BasicSound::AmbientArgInfo *pArgInfo, - detail::ExternalSoundPlayer *pPlayer, const StartInfo *pStartInfo - ); + // virtual function ordering + // vtable SoundStartable + virtual StartResult detail_SetupSound(SoundHandle *handle, u32 soundId, + bool holdFlag, + StartInfo const *startInfo) = 0; + virtual u32 detail_ConvertLabelStringToSoundId(char const *label) = 0; - StartResult detail_HoldSound( - SoundHandle *pHandle, u32 id, detail::BasicSound::AmbientArgInfo *pArgInfo, - detail::ExternalSoundPlayer *pPlayer, const StartInfo *pStartInfo - ); + // methods + StartResult detail_StartSound(SoundHandle *handle, u32 soundId, + StartInfo const *startInfo); + StartResult detail_HoldSound(SoundHandle *handle, u32 soundId, + StartInfo const *startInfo); - StartResult detail_PrepareSound( - SoundHandle *pHandle, u32 id, detail::BasicSound::AmbientArgInfo *pArgInfo, - detail::ExternalSoundPlayer *pPlayer, const StartInfo *pStartInfo - ); -}; + // members + private: + /* vtable */ // size 0x04, offset 0x00 + }; // size 0x04 +}} // namespace nw4r::snd -} // namespace snd -} // namespace nw4r - -#endif +#endif // NW4R_SND_SOUND_STARTABLE_H diff --git a/include/nw4r/snd/snd_SoundSystem.h b/include/nw4r/snd/snd_SoundSystem.h index d71fd5e6..9c3b7306 100644 --- a/include/nw4r/snd/snd_SoundSystem.h +++ b/include/nw4r/snd/snd_SoundSystem.h @@ -1,73 +1,68 @@ #ifndef NW4R_SND_SOUND_SYSTEM_H #define NW4R_SND_SOUND_SYSTEM_H -#include "nw4r/snd/snd_AxManager.h" -#include "nw4r/snd/snd_RemoteSpeakerManager.h" -#include "nw4r/snd/snd_TaskThread.h" -#include "nw4r/types_nw4r.h" -namespace nw4r { -namespace snd { +/******************************************************************************* + * headers + */ -class SoundSystem { -public: - struct SoundSystemParam { - s32 soundThreadPriority; // at 0x0 - u32 soundThreadStackSize; // at 0x4 - s32 dvdThreadPriority; // at 0x8 - u32 dvdThreadStackSize; // at 0xC +#include - SoundSystemParam() - : soundThreadPriority(DEFAULT_SOUND_THREAD_PRIORITY), soundThreadStackSize(DEFAULT_SOUND_THREAD_STACK_SIZE), - dvdThreadPriority(DEFAULT_DVD_THREAD_PRIORITY), dvdThreadStackSize(DEFAULT_DVD_THREAD_STACK_SIZE) {} - }; +/******************************************************************************* + * classes and functions + */ -public: - static void InitSoundSystem(s32 soundThreadPrio, s32 dvdThreadPriority); - static u32 GetRequiredMemSize(const SoundSystemParam &rParam); +// forward declarations +namespace nw4r { namespace snd { namespace detail { class TaskThread; }}} - static void InitSoundSystem(const SoundSystemParam &rParam, void *pWork, u32 workSize); +namespace nw4r { namespace snd +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x4bd66 + class SoundSystem + { + // nested types + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x4bc73 + struct SoundSystemParam + { + // methods + public: + // cdtors + SoundSystemParam() : + soundThreadPriority (DEFAULT_SOUND_THREAD_PRIORITY), + soundThreadStackSize (DEFAULT_SOUND_THREAD_STACK_SIZE), + dvdThreadPriority (DEFAULT_DVD_THREAD_PRIORITY), + dvdThreadStackSize (DEFAULT_DVD_THREAD_STACK_SIZE) + { + } - static void ShutdownSoundSystem(); - static void WaitForResetReady(); + // members + public: + s32 soundThreadPriority; // size 0x04, offset 0x00 + u32 soundThreadStackSize; // size 0x04, offset 0x04 + s32 dvdThreadPriority; // size 0x04, offset 0x08 + u32 dvdThreadStackSize; // size 0x04, offset 0x0c + }; // size 0x10 - static void PrepareReset() { - detail::AxManager::GetInstance().PrepareReset(); - } + // static members + private: + static int const DEFAULT_DVD_THREAD_STACK_SIZE = 0x4000; + static int const DEFAULT_SOUND_THREAD_STACK_SIZE = 0x4000; + static int const DEFAULT_DVD_THREAD_PRIORITY = 3; + static int const DEFAULT_SOUND_THREAD_PRIORITY = 4; - static void SetOutputMode(OutputMode mode) { - detail::AxManager::GetInstance().SetOutputMode(mode); - } + static int sMaxVoices; + static detail::TaskThread sTaskThread; - static f32 GetMasterVolume() { - return detail::AxManager::GetInstance().GetMasterVolume(); - } - static void SetMasterVolume(f32 volume, int frame) { - detail::AxManager::GetInstance().SetMasterVolume(volume, frame); - } + // methods + public: + static void InitSoundSystem(SoundSystemParam const ¶m, + void *workMem, u32 workMemSize); + static void ShutdownSoundSystem(); - static RemoteSpeaker &GetRemoteSpeaker(int i) { - return detail::RemoteSpeakerManager::GetInstance().GetRemoteSpeaker(i); - } + static bool IsInitializedSoundSystem(); - static void AppendEffect(AuxBus bus, FxBase *pFx) { - detail::AxManager::GetInstance().AppendEffect(bus, pFx); - } - static void ClearEffect(AuxBus bus, int frame) { - detail::AxManager::GetInstance().ClearEffect(bus, frame); - } + static u32 GetRequiredMemSize(SoundSystemParam const ¶m); + }; // size 0x01 (0x00 for inheritance) +}} // namespace nw4r::snd -private: - static const int DEFAULT_DVD_THREAD_PRIORITY = 3; - static const int DEFAULT_DVD_THREAD_STACK_SIZE = 0x4000; - - static const int DEFAULT_SOUND_THREAD_PRIORITY = 4; - static const int DEFAULT_SOUND_THREAD_STACK_SIZE = 0x4000; - -private: - static detail::TaskThread sTaskThread; -}; - -} // namespace snd -} // namespace nw4r - -#endif +#endif // NW4R_SND_SOUND_SYSTEM_H diff --git a/include/nw4r/snd/snd_SoundThread.h b/include/nw4r/snd/snd_SoundThread.h index 84004e16..0f853603 100644 --- a/include/nw4r/snd/snd_SoundThread.h +++ b/include/nw4r/snd/snd_SoundThread.h @@ -1,104 +1,132 @@ #ifndef NW4R_SND_SOUND_THREAD_H #define NW4R_SND_SOUND_THREAD_H -#include "nw4r/snd/snd_AxManager.h" -#include "nw4r/types_nw4r.h" -#include "nw4r/ut.h" // IWYU pragma: export -#include "rvl/OS.h" // IWYU pragma: export -namespace nw4r { -namespace snd { -namespace detail { +/******************************************************************************* + * headers + */ -class SoundThread { - friend class AutoLock; // Prevent locking without AutoLock +#include -public: - class SoundFrameCallback { - public: - NW4R_UT_LIST_NODE_DECL(); // at 0x0 +#include "AxManager.h" - virtual ~SoundFrameCallback() {} // at 0x8 - virtual void OnBeginSoundFrame() {} // at 0xC - virtual void OnEndSoundFrame() {} // at 0x10 - }; - - NW4R_UT_LIST_TYPEDEF_DECL(SoundFrameCallback); - - class PlayerCallback { - public: - NW4R_UT_LIST_NODE_DECL(); // at 0x0 - - virtual ~PlayerCallback() {} // at 0x8 - virtual void OnUpdateFrameSoundThread() {} // at 0xC - virtual void OnUpdateVoiceSoundThread() {} // at 0x10 - virtual void OnShutdownSoundThread() {} // at 0x14 - }; - - NW4R_UT_LIST_TYPEDEF_DECL(PlayerCallback); - - class AutoLock : private ut::NonCopyable { - public: - AutoLock() { - SoundThread::GetInstance().Lock(); - } - - ~AutoLock() { - SoundThread::GetInstance().Unlock(); - } - }; - -public: - static SoundThread &GetInstance(); - - bool Create(s32 priority, void *pStack, u32 stackSize); - void Shutdown(); - - void RegisterPlayerCallback(PlayerCallback *pCallback); - void UnregisterPlayerCallback(PlayerCallback *pCallback); - -private: - enum ThreadMessage { - MSG_NONE, - MSG_AX_CALLBACK, - MSG_SHUTDOWN, - }; - - static const int MSG_QUEUE_CAPACITY = 4; - -private: - SoundThread(); - - static void AxCallbackFunc(); - void AxCallbackProc(); - - static void *SoundThreadFunc(void *pArg); - void SoundThreadProc(); - - void Lock() { - OSLockMutex(&mMutex); - } - void Unlock() { - OSUnlockMutex(&mMutex); - } - -private: - OSThread mThread; // at 0x0 - OSThreadQueue mThreadQueue; // at 0x318 - OSMessageQueue mMsgQueue; // at 0x320 - OSMessage mMsgBuffer[MSG_QUEUE_CAPACITY]; // at 0x340 - void *mStackEnd; // at 0x350 - mutable OSMutex mMutex; // at 0x354 - - AxManager::CallbackListNode mAxCallbackNode; // at 0x36C - SoundFrameCallbackList mSoundFrameCallbackList; // at 0x378 - PlayerCallbackList mPlayerCallbackList; // at 0x384 - - u32 mProcessTick; // at 0x390 - bool mCreateFlag; // at 0x394 -}; - -} // namespace detail -} // namespace snd -} // namespace nw4r +#include "../ut/inlines.h" // ut::NonCopyable +#include "../ut/LinkList.h" +#if 0 +#include +#include +#include +#else +#include #endif + +/******************************************************************************* + * classes + */ + +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2bc156 + class SoundThread + { + // nested types + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2bbe45 + class AutoLock : private ut::NonCopyable + { + // methods + public: + // cdtors + AutoLock() { SoundThread::GetInstance().Lock(); } + ~AutoLock() { SoundThread::GetInstance().Unlock(); } + }; // size 0x01 (0x00 for inheritance) + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2bc03f + class SoundFrameCallback + { + // typedefs + public: + typedef ut::LinkList LinkList; + + // members + private: + ut::LinkListNode mLink; // size 0x08, offset 0x00 + /* vtable */ // size 0x04, offset 0x08 + + // late virtual methods + public: + virtual void at_0x08(); + virtual void at_0x0c(); + virtual void at_0x10(); + }; // size 0x0c + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2cc6f + class PlayerCallback + { + // typedefs + public: + typedef ut::LinkList LinkList; + + // members + private: + ut::LinkListNode mLink; // size 0x08, offset 0x00 + /* vtable */ // size 0x04, offset 0x08 + + // late virtual methods + public: + virtual ~PlayerCallback() {} + virtual void OnUpdateFrameSoundThread() {} + virtual void OnUpdateVoiceSoundThread() {} + virtual void OnShutdownSoundThread() {} + }; // size 0x0c + + // methods + public: + // instance accessors + static SoundThread &GetInstance(); + + // methods + bool Create(s32 priority, void *stack, u32 stackSize); + void Shutdown(); + + void Lock() { OSLockMutex(&mMutex); } + void Unlock() { OSUnlockMutex(&mMutex); } + + void RegisterPlayerCallback(PlayerCallback *callback); + void UnregisterPlayerCallback(PlayerCallback *callback); + + private: + // cdtors + SoundThread(); + + // fibers, callbacks, and procedures + static void AxCallbackFunc(); + void AxCallbackProc(); + + static void *SoundThreadFunc(void *arg); + void SoundThreadProc(); + + // static members + private: + static int const MESSAGE_SHUTDOWN = 1 << 1; + static int const MESSAGE_AX_CALLBACK = 1 << 0; + static int const THREAD_MESSAGE_BUFSIZE = 4; + + // members + private: + OSThread mThread; // size 0x318, offset 0x000 + OSThreadQueue mThreadQueue; // size 0x008, offset 0x318 + OSMessageQueue mMsgQueue; // size 0x020, offset 0x320 + OSMessage mMsgBuffer[THREAD_MESSAGE_BUFSIZE]; // size 0x010, offset 0x340 + byte4_t *mStackEnd; // size 0x004, offset 0x350 + OSMutex mMutex; // size 0x018, offset 0x354 + AxManager::CallbackListNode mAxCallbackNode; // size 0x00c, offset 0x36c + SoundFrameCallback::LinkList mSoundFrameCallbackList; // size 0x00c, offset 0x378 + PlayerCallback::LinkList mPlayerCallbackList; // size 0x00c, offset 0x384 + u32 mProcessTick; // size 0x004, offset 0x390 + bool mCreateFlag; // size 0x001, offset 0x394 + /* 3 bytes padding */ + }; // size 0x398 +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_SOUND_THREAD_H diff --git a/include/nw4r/snd/snd_StrmChannel.h b/include/nw4r/snd/snd_StrmChannel.h index ccc0da32..056f8f0a 100644 --- a/include/nw4r/snd/snd_StrmChannel.h +++ b/include/nw4r/snd/snd_StrmChannel.h @@ -1,48 +1,67 @@ #ifndef NW4R_SND_STRM_CHANNEL_H #define NW4R_SND_STRM_CHANNEL_H -#include "nw4r/snd/snd_Common.h" -#include "nw4r/types_nw4r.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { -namespace detail { +#include // CHAR_BIT -struct StrmChannel { - void *bufferAddress; // at 0x0 - u32 bufferSize; // at 0x4 - AdpcmInfo adpcmInfo; // at 0x8 -}; +#include // ROUND_UP +#include -class StrmBufferPool { -public: - void Setup(void *pBase, u32 size, int count); - void Shutdown(); +#include "adpcm.h" - void *Alloc(); - void Free(void *pBuffer); +/******************************************************************************* + * types + */ - u32 GetBlockSize() const { - return mBlockSize; - } +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2f927 + struct StrmChannel + { + void *bufferAddress; // size 0x04, offset 0x00 + AdpcmParam adpcmParam; // size 0x28, offset 0x04 + AdpcmLoopParam adpcmLoopParam; // size 0x06, offset 0x2c + u16 adpcmPredScale; // size 0x02, offset 0x32 + }; // size 0x34 +}}} // namespace nw4r::snd::detail -private: - static const int BLOCK_MAX = 32; - static const int BITS_PER_BYTE = 8; +/******************************************************************************* + * classes and functions + */ -private: - void *mBuffer; // at 0x0 - u32 mBufferSize; // at 0x4 +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2f7c6 + class StrmBufferPool + { + // methods + public: + // methods + void Setup(void *buffer, u32 size, int blockCount); + void Shutdown(); - u32 mBlockSize; // at 0x8 - int mBlockCount; // at 0xC + u32 GetBlockSize() const { return mBlockSize; } - int mAllocCount; // at 0x10 - u8 mAllocFlags[BLOCK_MAX / BITS_PER_BYTE]; // at 0x14 -}; + void *Alloc(); + void Free(void *p); -} // namespace detail -} // namespace snd -} // namespace nw4r + // static members + private: + static int const BIT_PER_BYTE = CHAR_BIT; // uhhh ok + static int const BLOCK_MAX = 32; -#endif + // members + private: + void *mBuffer; // size 0x04, offset 0x00 + u32 mBufferSize; // size 0x04, offset 0x04 + u32 mBlockSize; // size 0x04, offset 0x08 + int mBlockCount; // size 0x04, offset 0x0c + int mAllocCount; // size 0x04, offset 0x10 + byte_t mAllocFlags[ROUND_UP(BLOCK_MAX, BIT_PER_BYTE) / BIT_PER_BYTE]; // size 0x04, offset 0x14 + }; // size 0x18 +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_STRM_CHANNEL_H diff --git a/include/nw4r/snd/snd_StrmFile.h b/include/nw4r/snd/snd_StrmFile.h index e8a37d50..b70e521c 100644 --- a/include/nw4r/snd/snd_StrmFile.h +++ b/include/nw4r/snd/snd_StrmFile.h @@ -1,158 +1,254 @@ #ifndef NW4R_SND_STRM_FILE_H #define NW4R_SND_STRM_FILE_H -#include "nw4r/snd/snd_Common.h" -#include "nw4r/snd/snd_Util.h" -#include "nw4r/types_nw4r.h" -#include "nw4r/ut.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { -namespace detail { -namespace StrmFile { +#include +#include -struct StrmDataInfo { - u8 format; // at 0x0 - u8 loopFlag; // at 0x1 - u8 numChannels; // at 0x2 - u8 sampleRate24; // at 0x3 - u16 sampleRate; // at 0x4 - u16 blockHeaderOffset; // at 0x6 - u32 loopStart; // at 0x8 - u32 loopEnd; // at 0xC - u32 dataOffset; // at 0x10 - u32 numBlocks; // at 0x14 - u32 blockSize; // at 0x18 - u32 blockSamples; // at 0x1C - u32 lastBlockSize; // at 0x20 - u32 lastBlockSamples; // at 0x24 - u32 lastBlockPaddedSize; // at 0x28 - u32 adpcmDataInterval; // at 0x2C - u32 adpcmDataSize; // at 0x30 -}; +#include "adpcm.h" +#include "global.h" // SampleFormat +#include "Util.h" // Util::DataRef -struct TrackInfo { - u8 channelCount; // at 0x0 - u8 channelIndexTable[]; // at 0x1 -}; +#include "../ut/binaryFileFormat.h" -struct TrackTable { - u8 trackCount; // at 0x0 - u8 trackDataType; // at 0x1 - Util::DataRef refTrackHeader[]; // at 0x4 -}; +/******************************************************************************* + * types + */ -struct ChannelInfo { - Util::DataRef refAdpcmInfo; // at 0x0 -}; +// forward declarations +namespace nw4r { namespace ut { class FileStream; }} -struct ChannelTable { - u8 channelCount; // at 0x0 - Util::DataRef refChannelHeader[]; // at 0x4 -}; +namespace nw4r { namespace snd { namespace detail +{ + struct StrmFile + { + /* Header */ -struct Header { - ut::BinaryFileHeader fileHeader; // at 0x0 - u32 headBlockOffset; // at 0x10 - u32 headBlockSize; // at 0x14 - u32 adpcBlockOffset; // at 0x18 - u32 adpcBlockSize; // at 0x1C - u32 dataBlockOffset; // at 0x20 - u32 dataBlockSize; // at 0x24 -}; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2f271d + struct Header + { + ut::BinaryFileHeader fileHeader; // size 0x10, offset 0x00 + u32 headBlockOffset; // size 0x04, offset 0x10 + u32 headBlockSize; // size 0x04, offset 0x14 + u32 adpcBlockOffset; // size 0x04, offset 0x18 + u32 adpcBlockSize; // size 0x04, offset 0x1c + u32 dataBlockOffset; // size 0x04, offset 0x20 + u32 dataBlockSize; // size 0x04, offset 0x24 + }; // size 0x28 -struct HeadBlock { - ut::BinaryBlockHeader blockHeader; // at 0x0 - Util::DataRef refDataHeader; // at 0x8 - Util::DataRef refTrackTable; // at 0x10 - Util::DataRef refChannelTable; // at 0x18 -}; + /* HeadBlock */ -} // namespace StrmFile + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2f2d81 + struct StrmDataInfo + { + u8 format; // size 0x01, offset 0x00 + u8 loopFlag; // size 0x01, offset 0x01 + u8 numChannels; // size 0x01, offset 0x02 + u8 sampleRate24; // size 0x01, offset 0x03 + u16 sampleRate; // size 0x02, offset 0x04 + u16 blockHeaderOffset; // size 0x02, offset 0x06 + u32 loopStart; // size 0x04, offset 0x08 + u32 loopEnd; // size 0x04, offset 0x0c + u32 dataOffset; // size 0x04, offset 0x10 + u32 numBlocks; // size 0x04, offset 0x14 + u32 blockSize; // size 0x04, offset 0x18 + u32 blockSamples; // size 0x04, offset 0x1c + u32 lastBlockSize; // size 0x04, offset 0x20 + u32 lastBlockSamples; // size 0x04, offset 0x24 + u32 lastBlockPaddedSize; // size 0x04, offset 0x28 + u32 adpcmDataInterval; // size 0x04, offset 0x2c + u32 adpcmDataSize; // size 0x04, offset 0x30 + }; // size 0x34 -struct StrmInfo { - u8 format; // at 0x0 - u8 loopFlag; // at 0x1 - u8 numChannels; // at 0x2 - int sampleRate; // at 0x4 - u16 blockHeaderOffset; // at 0x8 - u32 loopStart; // at 0xC - u32 loopEnd; // at 0x10 - u32 dataOffset; // at 0x14 - u32 numBlocks; // at 0x18 - u32 blockSize; // at 0x1C - u32 blockSamples; // at 0x20 - u32 lastBlockSize; // at 0x24 - u32 lastBlockSamples; // at 0x28 - u32 lastBlockPaddedSize; // at 0x2C - u32 adpcmDataInterval; // at 0x30 - u32 adpcmDataSize; // at 0x34 -}; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2f3a1e + struct TrackInfo + { + u8 channelCount; // size 0x01, offset 0x00 + u8 channelIndexTable[]; // flexible, offset 0x01 (unit size 0x01) + }; // size 0x01 -class StrmFileReader { -public: - static const u32 SIGNATURE = 'RSTM'; - static const int VERSION = NW4R_VERSION(1, 0); + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2f3196 + struct TrackInfoEx + { + u8 volume; // size 0x01, offset 0x00 + u8 pan; // size 0x01, offset 0x01 + byte1_t padding[2]; + byte4_t reserved; + u8 channelCount; // size 0x01, offset 0x08 + u8 channelIndexTable[]; // flexible, offset 0x09 (unit size 0x01) + /* 3 bytes padding */ + }; // size 0x0c -public: - StrmFileReader(); + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2f34ef + struct TrackTable + { + u8 trackCount; // size 0x01, offset 0x00 + u8 trackDataType; // size 0x01, offset 0x01 + byte1_t padding[2]; + Util::DataRef refTrackHeader[]; // flexible, offset 0x04 (unit size 0x08) + }; // size 0x04 - bool IsAvailable() const { - return mHeader != NULL; - } + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2f336f + struct AdpcmParamSet + { + AdpcmParam adpcmParam; // size 0x28, offset 0x00 + AdpcmLoopParam adpcmLoopParam; // size 0x06, offset 0x28 + }; // size 0x2e - bool IsValidFileHeader(const void *pStrmBin); - void Setup(const void *pStrmBin); + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2f3cbb + struct ChannelInfo + { + Util::DataRef refAdpcmInfo; // size 0x08, offset 0x00 + }; // size 0x08 - bool ReadStrmInfo(StrmInfo *pStrmInfo) const; - bool ReadAdpcmInfo(AdpcmInfo *pAdpcmInfo, int channels) const; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2f3780 + struct ChannelTable + { + u8 channelCount; // size 0x01, offset 0x00 + byte1_t padding[3]; + Util::DataRef refChannelHeader[]; // flexible, offset 0x04 (unit size 0x08) + }; // size 0x04 - u32 GetAdpcBlockOffset() const { - if (IsAvailable()) { - return mHeader->adpcBlockOffset; - } + static byte4_t const SIGNATURE_HEAD_BLOCK = + NW4R_FOUR_BYTE('H', 'E', 'A', 'D'); - return 0; - } + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2f2ab0 + struct HeadBlock + { + ut::BinaryBlockHeader blockHeader; // size 0x08, offset 0x00 + Util::DataRef refDataHeader; // size 0x08, offset 0x08 + Util::DataRef refTrackTable; // size 0x08, offset 0x10 + Util::DataRef refChannelTable; // size 0x08, offset 0x18 + }; // size 0x20 -private: - const StrmFile::Header *mHeader; // at 0x0 - const StrmFile::HeadBlock *mHeadBlock; // at 0x4 -}; + /* StrmFile */ -class StrmFileLoader { -public: - explicit StrmFileLoader(ut::FileStream &rFileStream) : mStream(rFileStream) {} + static byte4_t const SIGNATURE_FILE = + NW4R_FOUR_BYTE('R', 'S', 'T', 'M'); + static int const FILE_VERSION = NW4R_FILE_VERSION(1, 0); + }; // "namespace" StrmFile +}}} // namespace nw4r::snd::detail - bool LoadFileHeader(void *pStrmBin, u32 size); - bool ReadAdpcBlockData(u16 *pYN1, u16 *pYN2, int block, int channels); +/******************************************************************************* + * classes and functions + */ - bool ReadStrmInfo(StrmInfo *pStrmInfo) const { - if (!mReader.IsAvailable()) { - return false; - } +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2f2b8e + class StrmFileReader + { + // nested types + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2f0e7 + struct StrmInfo + { + SampleFormat sampleFormat; // size 0x04, offset 0x00 + bool loopFlag; // size 0x01, offset 0x04 + /* 3 bytes padding */ + int numChannels; // size 0x04, offset 0x08 + int sampleRate; // size 0x04, offset 0x0c + u16 blockHeaderOffset; // size 0x02, offset 0x10 + /* 2 bytes padding */ + u32 loopStart; // size 0x04, offset 0x14 + u32 loopEnd; // size 0x04, offset 0x18 + u32 dataOffset; // size 0x04, offset 0x1c + u32 numBlocks; // size 0x04, offset 0x20 + u32 blockSize; // size 0x04, offset 0x24 + u32 blockSamples; // size 0x04, offset 0x28 + u32 lastBlockSize; // size 0x04, offset 0x2c + u32 lastBlockSamples; // size 0x04, offset 0x30 + u32 lastBlockPaddedSize; // size 0x04, offset 0x34 + u32 adpcmDataInterval; // size 0x04, offset 0x38 + u32 adpcmDataSize; // size 0x04, offset 0x3c + }; // size 0x40 - return mReader.ReadStrmInfo(pStrmInfo); - } + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2fa44 + struct StrmTrackInfo + { + u8 volume; // size 0x01, offset 0x00 + u8 pan; // size 0x01, offset 0x01 + /* 2 bytes padding */ + int channelCount; // size 0x04, offset 0x04 + u8 channelIndexTable[32]; // size 0x20, offset 0x08 + }; // size 0x28 - bool ReadAdpcmInfo(AdpcmInfo *pAdpcmInfo, int channel) const { - if (!mReader.IsAvailable()) { - return false; - } + // methods + public: + // cdtors + StrmFileReader(); - return mReader.ReadAdpcmInfo(pAdpcmInfo, channel); - } + // methods + void Setup(void const *strmData); -private: - static const int HEADER_ALIGNED_SIZE = ROUND_UP(sizeof(StrmFile::Header), 32); + bool IsAvailable() const { return mHeader != nullptr; } + u32 GetAdpcBlockOffset() const + { + if (IsAvailable()) + return mHeader->adpcBlockOffset; -private: - ut::FileStream &mStream; // at 0x0 - StrmFileReader mReader; // at 0x4 -}; + return 0; + } -} // namespace detail -} // namespace snd -} // namespace nw4r + int GetTrackCount() const; + int GetChannelCount() const; -#endif + bool ReadStrmInfo(StrmInfo *strmInfo) const; + bool ReadStrmTrackInfo(StrmTrackInfo *trackInfo, int trackIndex) const; + bool ReadAdpcmInfo(AdpcmParam *adpcmParam, + AdpcmLoopParam *adpcmLoopParam, + int channelIndex) const; + + private: + static bool IsValidFileHeader(void const *strmData); + + static SampleFormat GetSampleFormatFromStrmFileFormat(u8 format); + + // static members + public: + static int const SUPPORTED_FILE_VERSION = NW4R_FILE_VERSION(1, 0); + + // members + private: + StrmFile::Header const *mHeader; // size 0x04, offset 0x00 + StrmFile::HeadBlock const *mHeadBlock; // size 0x04, offset 0x04 + + // friends + private: + friend class StrmFileLoader; + }; // size 0x08 + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2f3fc4 + class StrmFileLoader + { + // methods + public: + // cdtors + StrmFileLoader(ut::FileStream &stream) : mStream(stream) {} + + // methods + bool LoadFileHeader(void *buffer, u32 size); + + int GetTrackCount() const; + int GetChannelCount() const; + + bool ReadStrmInfo(StrmFileReader::StrmInfo *strmInfo) const; + bool ReadStrmTrackInfo(StrmFileReader::StrmTrackInfo *trackInfo, + int trackIndex) const; + bool ReadAdpcmInfo(AdpcmParam *adpcmParam, + AdpcmLoopParam *adpcmLoopParam, + int channelIndex) const; + bool ReadAdpcBlockData(u16 *yn1, u16 *yn2, int blockIndex, + int channelCount); + + // members + private: + ut::FileStream &mStream; // size 0x04, offset 0x00 + StrmFileReader mReader; // size 0x08, offset 0x04 + }; // size 0x0c +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_STRM_FILE_H diff --git a/include/nw4r/snd/snd_StrmPlayer.h b/include/nw4r/snd/snd_StrmPlayer.h index 342dd140..bc9ab661 100644 --- a/include/nw4r/snd/snd_StrmPlayer.h +++ b/include/nw4r/snd/snd_StrmPlayer.h @@ -1,219 +1,275 @@ #ifndef NW4R_SND_STRM_PLAYER_H #define NW4R_SND_STRM_PLAYER_H -#include "common.h" -#include "nw4r/snd/snd_BasicPlayer.h" -#include "nw4r/snd/snd_InstancePool.h" -#include "nw4r/snd/snd_SoundThread.h" -#include "nw4r/snd/snd_StrmChannel.h" -#include "nw4r/snd/snd_StrmFile.h" -#include "nw4r/snd/snd_Task.h" -#include "nw4r/snd/snd_Voice.h" -#include "nw4r/types_nw4r.h" -#include "nw4r/ut.h" // IWYU pragma: export -#include "rvl/OS.h" // IWYU pragma: export -namespace nw4r { -namespace snd { -namespace detail { +/******************************************************************************* + * headers + */ -class StrmPlayer : public BasicPlayer, public SoundThread::PlayerCallback { - friend class StrmHeaderLoadTask; - friend class StrmDataLoadTask; +#include -public: - enum StartOffsetType { - START_OFFSET_TYPE_SAMPLE, - START_OFFSET_TYPE_MILLISEC - }; +#include "BasicPlayer.h" +#include "global.h" +#include "InstancePool.h" +#include "Task.h" +#include "SoundThread.h" +#include "StrmChannel.h" +#include "StrmFile.h" +#include "Voice.h" - struct StrmHeader { - // TODO: Why 8 if the player only supports 2??? - static const int STRM_CHANNEL_MAX = 8; - - StrmInfo strmInfo; // at 0x0 - AdpcmInfo adpcmInfo[STRM_CHANNEL_MAX]; // at 0x38 - u16 loopYn1[STRM_CHANNEL_MAX]; // at 0x1B8 - u16 loopYn2[STRM_CHANNEL_MAX]; // at 0x1D8 - }; - -public: - StrmPlayer(); - virtual ~StrmPlayer(); // at 0x8 - - virtual bool Start(); // at 0xc - virtual void Stop(); // at 0x10 - virtual void Pause(bool flag); // at 0x14 - - virtual bool IsActive() const { - return mActiveFlag; - } // at 0x18 - virtual bool IsStarted() const { - return mStartedFlag; - } // at 0x1c - virtual bool IsPause() const { - return mPauseFlag; - }; // at 0x20 - - virtual void OnUpdateFrameSoundThread() { - Update(); - } // at 0xc - virtual void OnUpdateVoiceSoundThread() { - UpdateBuffer(); - } // at 0x10 - virtual void OnShutdownSoundThread() { - Stop(); - } // at 0x14 - - bool IsPrepared() const { - return mPreparedFlag; - } - - void SetTaskErrorFlag() { - mTaskErrorFlag = true; - } - void SetTaskCancelFlag() { - mTaskCancelFlag = true; - } - - bool Setup(StrmBufferPool *pBufferPool); - void Shutdown(); - - bool Prepare(ut::FileStream *pFileStream, int voices, StartOffsetType offsetType, int offset); - void InitParam(); - - void Update(); - -private: - struct StrmHeaderLoadTask : public Task { - StrmHeaderLoadTask(); - - virtual void Execute(); // at 0xC - virtual void Cancel(); // at 0x10 - virtual void OnCancel(); // at 0x14 - - StrmPlayer *strmPlayer; // at 0x10 - ut::FileStream *fileStream; // at 0x14 - StartOffsetType startOffsetType; // at 0x18 - s32 startOffset; // at 0x1C - }; - - struct StrmDataLoadTask : public Task { - StrmDataLoadTask(); - - virtual void Execute(); // at 0xC - virtual void Cancel(); // at 0x10 - virtual void OnCancel(); // at 0x14 - - StrmPlayer *strmPlayer; // at 0x10 - ut::FileStream *fileStream; // at 0x14 - u32 size; // at 0x18 - s32 offset; // at 0x1C - u32 blockSize; // at 0x20 - s32 bufferBlockIndex; // at 0x24 - bool needUpdateAdpcmLoop; // at 0x28 - - NW4R_UT_LIST_NODE_DECL(); // at 0x2C - }; - - NW4R_UT_LIST_TYPEDEF_DECL(StrmDataLoadTask); - - static const int DATA_BLOCK_COUNT_MIN = 4; - static const int DATA_BLOCK_COUNT_MAX = 32; - static const int DATA_BLOCK_SIZE_MAX = 0x2000; - - // TODO: How is this calculated? - static const int LOAD_BUFFER_SIZE = 0x4000 + 32; - -private: - bool LoadHeader(ut::FileStream *pFileStream, StartOffsetType offsetType, int offset); - bool LoadStreamData( - ut::FileStream *pFileStream, int offset, u32 size, u32 blockSize, int blockIndex, bool needUpdateAdpcmLoop - ); - - bool SetupPlayer(const StrmHeader *pStrmHeader); - - bool AllocChannels(int channels, int voices); - void FreeChannels(); - - void UpdateBuffer(); - void UpdateLoopAddress(u32 startSample, u32 endSample); - void UpdatePlayingBlockIndex(); - void UpdateDataLoopAddress(s32 endBlock); - void SetLoopEndToZeroBuffer(int endBlock); - void UpdateLoadingBlockIndex(); - void UpdatePauseStatus(); - - int CalcLoadingBufferBlockCount() const; - bool CalcStartOffset(s32 *pBlockIndex, u32 *pBlockOffset, s32 *pLoopCount); - - static void VoiceCallbackFunc(Voice *pDropVoice, Voice::VoiceCallbackStatus status, void *pCallbackArg); - - void SetAdpcmLoopContext(int channels, u16 *pPredScale); - -private: - StrmInfo mStrmInfo; // at 0x80 - - bool mSetupFlag; // at 0xB8 - bool mActiveFlag; // at 0xB9 - bool mStartedFlag; // at 0xBA - bool mPreparedFlag; // at 0xBB - bool mTaskErrorFlag; // at 0xBC - bool mTaskCancelFlag; // at 0xBD - bool mLoadingDelayFlag; // at 0xBE - bool mPauseFlag; // at 0xBF - bool mPauseStatus; // at 0xC0 - bool mLoadWaitFlag; // at 0xC1 - bool mNoRealtimeLoadFlag; // at 0xC2 - bool mSkipUpdateAdpcmLoop; // at 0xC3 - bool mValidAdpcmLoop; // at 0xC4 - bool mPlayFinishFlag; // at 0xC5 - bool mLoadFinishFlag; // at 0xC6 - - s32 mLoopCounter; // at 0xC8 - int mPrepareCounter; // at 0xCC - - int mChangeNumBlocks; // at 0xD0 - int mDataBlockSize; // at 0xD4 - int mBufferBlockCount; // at 0xD8 - int mBufferBlockCountBase; // at 0xDC - - int mLoadingBufferBlockCount; // at 0xE0 - int mLoadingBufferBlockIndex; // at 0xE4 - int mLoadingDataBlockIndex; // at 0xE8 - - int mPlayingBufferBlockCount; // at 0xEC - int mPlayingBufferBlockIndex; // at 0xF0 - int mPlayingDataBlockIndex; // at 0xF4 - int mLoopStartBlockIndex; // at 0xF8 - int mLastBlockIndex; // at 0xFC - - StartOffsetType mStartOffsetType; // at 0x100 - int mStartOffset; // at 0x104 - - StrmHeaderLoadTask mStrmHeaderLoadTask; // at 0x108 - StrmDataLoadTaskList mStrmDataLoadTaskList; // at 0x128 - InstancePool mStrmDataLoadTaskPool; // at 0x134 - StrmDataLoadTask mStrmDataLoadTaskArea[DATA_BLOCK_COUNT_MAX]; // at 0x138 - - StrmBufferPool *mBufferPool; // at 0x7B8 - ut::FileStream *mFileStream; // at 0x7BC - Voice *mVoice; // at 0x7C0 - s32 mChannelCount; // at 0x7C4 - s32 mVoiceOutCount; // at 0x7C8 - - StrmChannel mChannels[CHANNEL_MAX]; // at 0x7CC - u16 mAdpcmLoopPredScale[CHANNEL_MAX]; // at 0x83C - u16 mAdpcmLoopYn1[CHANNEL_MAX]; // at 0x840 - u16 mAdpcmLoopYn2[CHANNEL_MAX]; // at 0x844 - - static u8 sLoadBuffer[LOAD_BUFFER_SIZE] ALIGN_DECL(32); - static OSMutex sLoadBufferMutex; - - static bool sStaticInitFlag; -}; - -} // namespace detail -} // namespace snd -} // namespace nw4r +#include "../ut/LinkList.h" +#if 0 +#include +#else +#include #endif + +/******************************************************************************* + * types + */ + +// forward declarations +namespace nw4r { namespace ut { class FileStream; }} + +/******************************************************************************* + * classes and functions + */ + +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2fcda + class StrmPlayer : public BasicPlayer, public SoundThread::PlayerCallback + { + // enums + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2f3ca + enum StartOffsetType + { + START_OFFSET_TYPE_SAMPLE, + START_OFFSET_TYPE_MILLISEC, + }; + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2fc67 + enum SetupResult + { + SETUP_SUCCESS, + + SETUP_ERR_CANNOT_ALLOCATE_BUFFER, + + SETUP_ERR_UNKNOWN + }; + + // nested types + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2fb36 + struct StrmTrack + { + bool mActiveFlag; // size 0x01, offset 0x00 + Voice *mVoice; // size 0x04, offset 0x04 + StrmFileReader::StrmTrackInfo mTrackInfo; // size 0x28, offset 0x08 + f32 mVolume; // size 0x04, offset 0x30 + f32 mPan; // size 0x04, offset 0x34 + }; // size 0x38 + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2f430 + class StrmHeaderLoadTask : public Task + { + // methods + public: + // cdtors + StrmHeaderLoadTask(); + + // virtual function ordering + // vtable Task + virtual void Execute(); + virtual void Cancel(); + virtual void OnCancel(); + + // members + public: + /* base Task */ // size 0x10, offset 0x00 + StrmPlayer *player; // size 0x04, offset 0x10 + ut::FileStream *fileStream; // size 0x04, offset 0x14 + StartOffsetType startOffsetType; // size 0x04, offset 0x18 + s32 startOffset; // size 0x04, offset 0x1c + }; // size 0x20 + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2f537 + class StrmDataLoadTask : public Task + { + // typedefs + public: + typedef ut::LinkList LinkList; + + // methods + public: + // cdtors + StrmDataLoadTask(); + + // virtual function ordering + // vtable Task + virtual void Execute(); + virtual void Cancel(); + virtual void OnCancel(); + + // members + public: + /* base Task */ // size 0x10, offset 0x00 + StrmPlayer *mStrmPlayer; // size 0x04, offset 0x10 + ut::FileStream *fileStream; // size 0x04, offset 0x14 + u32 mSize; // size 0x04, offset 0x18 + s32 mOffset; // size 0x04, offset 0x1c + u32 mBlockSize; // size 0x04, offset 0x20 + s32 mBufferBlockIndex; // size 0x04, offset 0x24 + bool mNeedUpdateAdpcmLoop; // size 0x01, offset 0x28 + /* 3 bytes padding */ + ut::LinkListNode link; // size 0x08, offset 0x2c + }; // size 0x34 + + // methods + public: + StrmPlayer(); + virtual ~StrmPlayer(); + + // virtual function ordering + // vtable BasicPlayer + virtual bool Start(); + virtual void Stop(); + virtual void Pause(bool flag); + virtual bool IsActive() const { return mActiveFlag; } + virtual bool IsStarted() const { return mStartedFlag; } + virtual bool IsPause() const { return mPauseFlag; } + + // vtable SoundThread::PlayerCallback + virtual void OnUpdateFrameSoundThread() { Update(); } + virtual void OnUpdateVoiceSoundThread() { UpdateBuffer(); } + virtual void OnShutdownSoundThread() { Stop(); } + + // methods + SetupResult Setup(StrmBufferPool *bufferPool, int allocChannelCount, + byte2_t allocTrackFlag, int voiceOutCount); + void Update(); + void Shutdown(); + + bool IsPrepared() const { return mPreparedFlag; } + StrmChannel *GetTrackChannel(StrmTrack const &track, int channelIndex); + StrmTrack *GetPlayerTrack(int trackNo); + + void SetTaskErrorFlag() { mTaskErrorFlag = true; } + void SetTaskCancelFlag() { mTaskCancelFlag = true; } + + bool IsAdpcm() const + { + return mStrmInfo.sampleFormat == SAMPLE_FORMAT_DSP_ADPCM; + } + bool CheckDiskDriveError() const; + + void SetAdpcmLoopContext(int channelNum, u16 *predScale); + void SetTrackVolume(byte4_t trackBitFlag, f32 volume); + + void InitParam(); + bool SetupPlayer(); + bool Prepare(ut::FileStream *fileStream, + StartOffsetType startOffsetType, int startOffset); + + bool LoadHeader(ut::FileStream *fileStream, + StartOffsetType startOffsetType, int startOffset); + bool LoadStreamData(ut::FileStream *fileStream, int offset, u32 size, + u32 blockSize, int bufferBlockIndex, + bool needUpdateAdpcmLoop); + + bool AllocStrmBuffers(); + void FreeStrmBuffers(); + + bool AllocVoices(int voiceOutCount); + void FreeVoices(); + + int CalcLoadingBufferBlockCount() const; + bool CalcStartOffset(s32 *startBlockIndex, u32 *startBlockOffset, + s32 *loopCount); + + void SetLoopEndToZeroBuffer(int endBufferBlockIndex); + + void UpdateBuffer(); + void UpdatePauseStatus(); + void UpdateLoadingBlockIndex(); + void UpdatePlayingBlockIndex(); + void UpdateLoopAddress(u32 loopStartSamples, u32 loopEndSamples); + void UpdateDataLoopAddress(s32 endBlockBufferIndex); + void UpdateVoiceParams(StrmTrack *track); + + static u32 GetSampleByByte(byte4_t byte, SampleFormat format); + + private: + static void VoiceCallbackFunc(Voice *voice, + Voice::VoiceCallbackStatus status, + void *arg); + + // static members + public: + static int const LOAD_BUFFER_SIZE = 0x4000; + static int const LOAD_BUFFER_CHANNEL_NUM; + static int const BUFFER_BLOCK_COUNT_MAX = 32; + static int const STRM_CHANNEL_NUM_PER_TRACK; + static int const STRM_CHANNEL_NUM = 16; + static int const DATA_BLOCK_SIZE_MAX = 0x2000; + static int const STRM_TRACK_NUM = 8; + + private: + static bool sStaticInitFlag; + static OSMutex sLoadBufferMutex; + static byte_t sLoadBuffer[LOAD_BUFFER_SIZE]; + + // members + private: + /* base BasicPlayer */ // size 0x0a4, offset 0x000 + /* base SoundThread::PlayerCallback */ // size 0x00c, offset 0x0a4 + StrmFileReader::StrmInfo mStrmInfo; // size 0x040, offset 0x0b0 + bool mSetupFlag; // size 0x001, offset 0x0f0 + bool volatile mActiveFlag; // size 0x001, offset 0x0f1 + bool mStartedFlag; // size 0x001, offset 0x0f2 + bool mPreparedFlag; // size 0x001, offset 0x0f3 + bool mTaskErrorFlag; // size 0x001, offset 0x0f4 + bool mTaskCancelFlag; // size 0x001, offset 0x0f5 + bool mLoadingDelayFlag; // size 0x001, offset 0x0f6 + bool mPauseFlag; // size 0x001, offset 0x0f7 + bool mPauseStatus; // size 0x001, offset 0x0f8 + bool mLoadWaitFlag; // size 0x001, offset 0x0f9 + bool mNoRealtimeLoadFlag; // size 0x001, offset 0x0fa + bool mSkipUpdateAdpcmLoop; // size 0x001, offset 0x0fb + bool mValidAdpcmLoop; // size 0x001, offset 0x0fc + bool mPlayFinishFlag; // size 0x001, offset 0x0fd + bool mLoadFinishFlag; // size 0x001, offset 0x0fe + bool mAllocStrmBufferFlag; // size 0x001, offset 0x0ff + s32 mLoopCounter; // size 0x004, offset 0x100 + int mPrepareCounter; // size 0x004, offset 0x104 + int mChangeNumBlocks; // size 0x004, offset 0x108 + int mDataBlockSize; // size 0x004, offset 0x10c + int mBufferBlockCount; // size 0x004, offset 0x110 + int mBufferBlockCountBase; // size 0x004, offset 0x114 + int mLoadingBufferBlockCount; // size 0x004, offset 0x118 + int mLoadingBufferBlockIndex; // size 0x004, offset 0x11c + int mLoadingDataBlockIndex; // size 0x004, offset 0x120 + int mPlayingBufferBlockCount; // size 0x004, offset 0x124 + int mPlayingBufferBlockIndex; // size 0x004, offset 0x128 + int mPlayingDataBlockIndex; // size 0x004, offset 0x12c + int mLoopStartBlockIndex; // size 0x004, offset 0x130 + int mLastBlockIndex; // size 0x004, offset 0x134 + int mLoadWaitCount; // size 0x004, offset 0x138 + StartOffsetType mStartOffsetType; // size 0x004, offset 0x13c + int mStartOffset; // size 0x004, offset 0x140 + StrmHeaderLoadTask mStrmHeaderLoadTask; // size 0x020, offset 0x144 + StrmDataLoadTask::LinkList mStrmDataLoadTaskList; // size 0x00c, offset 0x164 + InstancePool mStrmDataLoadTaskPool; // size 0x004, offset 0x170 + StrmDataLoadTask mStrmDataLoadTaskArea[BUFFER_BLOCK_COUNT_MAX]; // size 0x680, offset 0x174 + StrmBufferPool *mBufferPool; // size 0x004, offset 0x7f4 + ut::FileStream *mFileStream; // size 0x004, offset 0x7f8 + s32 mTrackCount; // size 0x004, offset 0x7fc + s32 mChannelCount; // size 0x004, offset 0x800 + s32 mVoiceOutCount; // size 0x004, offset 0x804 + StrmChannel mChannels[STRM_CHANNEL_NUM]; // size 0x340, offset 0x808 + StrmTrack mTracks[STRM_TRACK_NUM]; // size 0x1c0, offset 0xb48 + }; // size 0xd08 +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_STRM_PLAYER_H diff --git a/include/nw4r/snd/snd_StrmSound.h b/include/nw4r/snd/snd_StrmSound.h index 443e154b..ac28216d 100644 --- a/include/nw4r/snd/snd_StrmSound.h +++ b/include/nw4r/snd/snd_StrmSound.h @@ -1,72 +1,111 @@ #ifndef NW4R_SND_STRM_SOUND_H #define NW4R_SND_STRM_SOUND_H -#include "nw4r/snd/snd_BasicSound.h" -#include "nw4r/snd/snd_StrmPlayer.h" -#include "nw4r/types_nw4r.h" -#include "nw4r/ut.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { +#include -// Forward declarations -class StrmSoundHandle; +#include "BasicSound.h" +#include "debug.h" +#include "MoveValue.h" +#include "StrmPlayer.h" -namespace detail { +#include "../ut/LinkList.h" -// Forward declarations -template -class SoundInstanceManager; +/******************************************************************************* + * types + */ -class StrmSound : public BasicSound { - friend class StrmSoundHandle; +// forward declarations +namespace nw4r { namespace snd { namespace detail { template class SoundInstanceManager; }}} +namespace nw4r { namespace snd { namespace detail { class StrmBufferPool; }}} +namespace nw4r { namespace snd { class StrmSoundHandle; }} -public: - NW4R_UT_RTTI_DECL(StrmSound); +namespace nw4r { namespace ut { namespace detail { class RuntimeTypeInfo; }}} +namespace nw4r { namespace ut { class FileStream; }} -public: - explicit StrmSound(SoundInstanceManager *pManager); +/******************************************************************************* + * classes and functions + */ - virtual void Shutdown(); // at 0x28 - virtual bool IsPrepared() const { - return mStrmPlayer.IsPrepared(); - } // at 0x2C +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x30810 + class StrmSound : public BasicSound + { + // typedefs + public: + /* redeclare with this class instead of BasicSound for + * SoundInstanceManager + */ + typedef ut::LinkList PriorityLinkList; - virtual void SetPlayerPriority(int priority); // at 0x4C - virtual bool IsAttachedTempSpecialHandle(); // at 0x5C - virtual void DetachTempSpecialHandle(); // at 0x60 + // methods + public: + // cdtors + StrmSound(SoundInstanceManager *manager, int priority, + int ambientPriority); + /* virtual ~StrmSound() {} */ // virtual function ordering - virtual BasicPlayer &GetBasicPlayer() { - return mStrmPlayer; - } // at 0x68 - virtual const BasicPlayer &GetBasicPlayer() const { - return mStrmPlayer; - } // at 0x6C + // virtual function ordering + // vtable BasicSound + virtual ut::detail::RuntimeTypeInfo const *GetRuntimeTypeInfo() const + { + return &typeInfo; + } + virtual ~StrmSound() {} + virtual void Shutdown(); + virtual bool IsPrepared() const { return mStrmPlayer.IsPrepared(); } + virtual bool IsAttachedTempSpecialHandle(); + virtual void DetachTempSpecialHandle(); + virtual void InitParam(); + virtual BasicPlayer &GetBasicPlayer() { return mStrmPlayer; } + virtual BasicPlayer const &GetBasicPlayer() const + { + return mStrmPlayer; + } + virtual void OnUpdatePlayerPriority(); + virtual void UpdateMoveValue(); + virtual void UpdateParam(); - bool Prepare( - StrmBufferPool *pPool, StrmPlayer::StartOffsetType offsetType, s32 offset, int voices, ut::FileStream *pStream - ); + // methods + StrmPlayer::SetupResult Setup(StrmBufferPool *bufferPool, + int allocChannelCount, + byte2_t allocTrackFlag); - void *GetFileStreamBuffer() { - return mFileStreamBuffer; - } - s32 GetFileStreamBufferSize() { - return sizeof(mFileStreamBuffer); - } + void *GetFileStreamBuffer() { return mFileStreamBuffer; } + s32 GetFileStreamBufferSize() { return sizeof mFileStreamBuffer; } -private: - static const int FILE_STREAM_BUFFER_SIZE = 512; + bool Prepare(StrmPlayer::StartOffsetType startOffsetType, s32 offset, + ut::FileStream *fileStream); -private: - StrmPlayer mStrmPlayer; // at 0xD8 - StrmSoundHandle *mTempSpecialHandle; // at 0x920 - SoundInstanceManager *mManager; // at 0x924 - char UNK_0x928[0x93C - 0x928]; - char mFileStreamBuffer[FILE_STREAM_BUFFER_SIZE]; // at 0x93C -}; + static DebugSoundType GetSoundType() + { + return DEBUG_SOUND_TYPE_STRMSOUND; + } -} // namespace detail -} // namespace snd -} // namespace nw4r + // static members + public: + static int const FILE_STREAM_BUFFER_SIZE = 128; -#endif + static ut::detail::RuntimeTypeInfo const typeInfo; + + // members + private: + /* base BasicSound */ // size 0x100, offset 0x000 + StrmPlayer mStrmPlayer; // size 0xd08, offset 0x100 + StrmSoundHandle *mTempSpecialHandle; // size 0x004, offset 0xe08 + SoundInstanceManager *mManager; // size 0x004, offset 0xe0c + MoveValue mTrackVolume[8]; // size 0x080, offset 0xe10 + ut::FileStream *mFileStream; // size 0x004, offset 0xe90 + int mFileStreamBuffer[FILE_STREAM_BUFFER_SIZE]; // size 0x200, offset 0xe94 + + // friends + private: + friend class snd::StrmSoundHandle; + }; // size 0x1094 +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_STRM_SOUND_H diff --git a/include/nw4r/snd/snd_StrmSoundHandle.h b/include/nw4r/snd/snd_StrmSoundHandle.h index 3c578d9a..dda83156 100644 --- a/include/nw4r/snd/snd_StrmSoundHandle.h +++ b/include/nw4r/snd/snd_StrmSoundHandle.h @@ -1,26 +1,42 @@ #ifndef NW4R_SND_STRM_SOUND_HANDLE_H #define NW4R_SND_STRM_SOUND_HANDLE_H -#include "nw4r/snd/snd_StrmSound.h" -#include "nw4r/types_nw4r.h" -#include "nw4r/ut.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { +#include // nullptr -class StrmSoundHandle : private ut::NonCopyable { -public: - void DetachSound(); +#include "../ut/inlines.h" // ut::NonCopyable - bool IsAttachedSound() { - return mSound != NULL; - } +/******************************************************************************* + * types + */ -private: - detail::StrmSound *mSound; // at 0x0 -}; +// forward declarations +namespace nw4r { namespace snd { namespace detail { class StrmSound; }}} -} // namespace snd -} // namespace nw4r +/******************************************************************************* + * classes and functions + */ -#endif +namespace nw4r { namespace snd +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x3074e + class StrmSoundHandle : private ut::NonCopyable + { + // methods + public: + // methods + bool IsAttachedSound() const { return mSound != nullptr; } + + void DetachSound(); + + // members + private: + /* base NonCopyable */ // size 0x00, offset 0x00 + detail::StrmSound *mSound; // size 0x04, offset 0x00 + }; // size 0x04 +}} // namespace nw4r::snd + +#endif // NW4R_SND_STRM_SOUND_HANDLE_H diff --git a/include/nw4r/snd/snd_Task.h b/include/nw4r/snd/snd_Task.h index c34a265d..ccc35765 100644 --- a/include/nw4r/snd/snd_Task.h +++ b/include/nw4r/snd/snd_Task.h @@ -1,35 +1,50 @@ #ifndef NW4R_SND_TASK_H #define NW4R_SND_TASK_H -#include "nw4r/types_nw4r.h" -#include "nw4r/ut.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { -namespace detail { +#include "../ut/inlines.h" // ut::NonCopyable +#include "../ut/LinkList.h" -class Task : private ut::NonCopyable { - friend class TaskManager; +/******************************************************************************* + * classes and functions + */ -public: - Task() : mBusyFlag(false) {} - virtual ~Task(); // at 0x8 +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2ea55 + class Task : private ut::NonCopyable + { + // typedefs + public: + typedef ut::LinkList LinkList; - virtual void Execute() = 0; // at 0xC - virtual void Cancel() = 0; // at 0x10 - virtual void OnCancel() = 0; // at 0x14 + // methods + public: + // cdtors + Task() : mBusyFlag(false) {} + virtual ~Task(); -public: - NW4R_UT_LIST_NODE_DECL(); // at 0x4 + // virtual function ordering + // vtable Task + virtual void Execute() = 0; + virtual void Cancel() = 0; + virtual void OnCancel() = 0; -private: - bool mBusyFlag; // at 0xC -}; + // members + private: + /* base NonCopyable */ // size 0x00, offset 0x00 + /* vtable */ // size 0x04, offset 0x00 + ut::LinkListNode mTaskLink; // size 0x08, offset 0x04 + bool mBusyFlag; // size 0x01, offset 0x0c + /* 3 bytes padding */ -NW4R_UT_LIST_TYPEDEF_DECL(Task); + // friends + private: + friend class TaskManager; + }; // size 0x10 +}}} // namespace nw4r::snd::detail -} // namespace detail -} // namespace snd -} // namespace nw4r - -#endif +#endif // NW4R_SND_TASK_H diff --git a/include/nw4r/snd/snd_TaskManager.h b/include/nw4r/snd/snd_TaskManager.h index df09c48d..16dabe97 100644 --- a/include/nw4r/snd/snd_TaskManager.h +++ b/include/nw4r/snd/snd_TaskManager.h @@ -1,53 +1,75 @@ #ifndef NW4R_SND_TASK_MANAGER_H #define NW4R_SND_TASK_MANAGER_H -#include "nw4r/snd/snd_Task.h" -#include "nw4r/ut.h" // IWYU pragma: export -#include "rvl/OS.h" // IWYU pragma: export +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { -namespace detail { - -class TaskManager { -public: - enum TaskPriority { - PRIORITY_LOW = 0, - PRIORITY_MIDDLE = 1, - PRIORITY_HIGH = 2, - - PRIORITY_MAX - }; - -public: - static TaskManager &GetInstance(); - - void AppendTask(Task *pTask, TaskPriority priority = PRIORITY_MIDDLE); - - Task *ExecuteTask(); - void CancelTask(Task *pTask); - void CancelAllTask(); - - void WaitTask(); - void CancelWaitTask(); - -private: - TaskManager(); - - Task *PopTask(); - Task *GetNextTask(); - Task *GetNextTask(TaskPriority priority, bool remove); - -private: - TaskList mTaskList[PRIORITY_MAX]; // at 0x0 - Task *volatile mCurrentTask; // at 0x24 - bool mCancelWaitTaskFlag; // at 0x28 - OSThreadQueue mAppendThreadQueue; // at 0x2C - OSThreadQueue mDoneThreadQueue; // at 0x34 -}; - -} // namespace detail -} // namespace snd -} // namespace nw4r +#include "Task.h" +#if 0 +#include // OSThreadQueue +#else +#include #endif + +/******************************************************************************* + * classes and functions + */ + +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2fd1de + class TaskManager + { + // enums + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2fd480 + enum TaskPriority + { + PRIORITY_LOW, + PRIORITY_MIDDLE, + PRIORITY_HIGH, + }; + + // methods + public: + // instance accessors + static TaskManager &GetInstance(); + + // methods + void AppendTask(Task *task, TaskPriority priority); + + Task *ExecuteTask(); + void CancelTask(Task *task); + void CancelAllTask(); + + void WaitTask(); + void CancelWaitTask(); + + private: + // cdtors + TaskManager(); + + // methods + Task *GetNextTask(); + Task *GetNextTask(TaskPriority priority, bool doRemove); + + Task *PopTask(); + + // static members + public: + static int const PRIORITY_NUM = 3; + + // members + private: + Task::LinkList mTaskList[PRIORITY_NUM]; // size 0x24, offset 0x00 + Task * volatile mCurrentTask; // size 0x04, offset 0x24 + bool mCancelWaitTaskFlag; // size 0x01, offset 0x28 // TODO: volatile? (see WaitTask) + /* 3 bytes padding */ + OSThreadQueue mAppendThreadQueue; // size 0x08, offset 0x2c + OSThreadQueue mDoneThreadQueue; // size 0x08, offset 0x34 + }; // size 0x3c +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_TASK_MANAGER_H diff --git a/include/nw4r/snd/snd_TaskThread.h b/include/nw4r/snd/snd_TaskThread.h index 4f6f5f6e..40ce1d96 100644 --- a/include/nw4r/snd/snd_TaskThread.h +++ b/include/nw4r/snd/snd_TaskThread.h @@ -1,34 +1,50 @@ #ifndef NW4R_SND_TASK_THREAD_H #define NW4R_SND_TASK_THREAD_H -#include "rvl/OS.h" // IWYU pragma: export +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { -namespace detail { - -class TaskThread { -public: - TaskThread(); - ~TaskThread(); - - bool Create(s32 priority, void *pStack, u32 stackSize); - void Destroy(); - -private: - static void *ThreadFunc(void *pArg); - void ThreadProc(); - -private: - OSThread mThread; // at 0x0 - u32 *mStackEnd; // at 0x318 - - volatile bool mFinishFlag; // at 0x31C - bool mCreateFlag; // at 0x31D -}; - -} // namespace detail -} // namespace snd -} // namespace nw4r +#include +#if 0 +#include +#else +#include #endif + +/******************************************************************************* + * classes and functions + */ + +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x4b348 + class TaskThread + { + // methods + public: + // cdtors + TaskThread(); + ~TaskThread(); + + // methods + bool Create(s32 priority, void *stack, u32 stackSize); + void Destroy(); + + private: + // fibers, callbacks, and procedures + static void *ThreadFunc(void *arg); + void ThreadProc(); + + // members + private: + OSThread mThread; // size 0x318, offset 0x000 + byte4_t *mStackEnd; // size 0x004, offset 0x318 + bool mFinishFlag; // size 0x001, offset 0x31c // TODO: volatile? (see ThreadProc) + bool mCreateFlag; // size 0x001, offset 0x31d + /* 2 bytes padding */ + }; // size 0x320 +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_TASK_THREAD_H diff --git a/include/nw4r/snd/snd_Util.h b/include/nw4r/snd/snd_Util.h index ca100f18..dbf4fefe 100644 --- a/include/nw4r/snd/snd_Util.h +++ b/include/nw4r/snd/snd_Util.h @@ -1,105 +1,150 @@ #ifndef NW4R_SND_UTIL_H #define NW4R_SND_UTIL_H -#include "nw4r/snd/snd_Common.h" -#include "nw4r/types_nw4r.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { -namespace detail { -namespace Util { +#include // static_assert +#include -enum PanCurve { - PAN_CURVE_SQRT, - PAN_CURVE_SINCOS, - PAN_CURVE_LINEAR, -}; +#include "nw4r/NW4RAssert.hpp" -struct PanInfo { - PanCurve curve; // at 0x0 - bool centerZero; // at 0x4 - bool zeroClamp; // at 0x5 +/******************************************************************************* + * classes and functions + */ - PanInfo() : curve(PAN_CURVE_SQRT), centerZero(false), zeroClamp(false) {} -}; +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2a4bc2 + class Util + { + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2a4932 + enum RefType + { + REFTYPE_ADDRESS, + REFTYPE_OFFSET, + }; -enum RefType { - REFTYPE_ADDRESS, - REFTYPE_OFFSET, -}; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2a497b + enum PanCurve + { + PAN_CURVE_SQRT, + PAN_CURVE_SINCOS, + PAN_CURVE_LINEAR, + }; -enum DataType { - DATATYPE_T0, - DATATYPE_T1, - DATATYPE_T2, - DATATYPE_T3, - DATATYPE_INVALID -}; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x241fd, 0x242b9... + template + struct DataRef + { + u8 refType; // size 0x01, offset 0x00 + u8 dataType; // size 0x01, offset 0x01 + byte2_t reserved; + u32 value; // size 0x04, offset 0x04 + }; // size 0x08 -template -struct DataRef { - u8 refType; // at 0x0 - u8 dataType; // at 0x1 - u16 reserved; // at 0x2 - u32 value; // at 0x4 -}; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x247c7, 0x249c5... + template + struct Table + { + u32 count; // size 0x04, offset 0x00 + T item[1]; // size T, offset 0x04 + }; // size 0x04 + T -template -struct Table { - u32 count; // at 0x0 - T items[1]; // at 0x4 -}; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2a49db + struct PanInfo + { + PanCurve curve; // size 0x04, offset 0x00 + bool centerZeroFlag; // size 0x01, offset 0x04 + bool zeroClampFlag; // size 0x01, offset 0x05 + /* 2 bytes padding */ + }; // size 0x08 -template -inline const T0 *GetDataRefAddress0(const DataRef &rRef, const void *pBase) { - return static_cast(GetDataRefAddressImpl(static_cast(rRef.refType), rRef.value, pBase)); -} + static f32 CalcPitchRatio(int pitch); + static f32 CalcVolumeRatio(f32 dB); + static f32 CalcPanRatio(f32 pan, PanInfo const &info); + static f32 CalcSurroundPanRatio(f32 pan, PanInfo const &info); -template -inline const T1 *GetDataRefAddress1(const DataRef &rRef, const void *pBase) { - return static_cast(GetDataRefAddressImpl(static_cast(rRef.refType), rRef.value, pBase)); -} + static u16 CalcLpfFreq(f32 scale); -template -inline const T2 *GetDataRefAddress2(const DataRef &rRef, const void *pBase) { - return static_cast(GetDataRefAddressImpl(static_cast(rRef.refType), rRef.value, pBase)); -} + static void GetRemoteFilterCoefs(int filter, u16 *b0, u16 *b1, u16 *b2, + u16 *a1, u16 *a2); -template -inline const T3 *GetDataRefAddress3(const DataRef &rRef, const void *pBase) { - return static_cast(GetDataRefAddressImpl(static_cast(rRef.refType), rRef.value, pBase)); -} + static u16 CalcRandom(); -#ifdef NW4R_LITLE_ENDIAN -inline u16 ReadBigEndian(u16 x) { - return x >> 8 | x << 8; -} + static void const *GetDataRefAddressImpl(RefType refType, byte4_t value, + void const *baseAddress); -inline u32 ReadBigEndian(u32 x) { - return (x >> 24) & 0x000000FF | (x >> 8) & 0x0000FF00 | (x << 8) & 0x00FF0000 | (x << 24) & 0xFF000000; -} -#else -inline u16 ReadBigEndian(u16 x) { - return x; -} +// The only way these asserts happen one line after the other is with macros +#define DEF_GET_DATA_REF_ADDRESS_(line_, index_) \ + template \ + static inline T ## index_ const *GetDataRefAddress ## index_( \ + Util::DataRef const &ref, void const *baseAddress) \ + { \ + /* specifically not the header variant */ \ + NW4RAssert_Line(line_, ref.dataType == index_); \ + \ + return static_cast(GetDataRefAddressImpl( \ + static_cast(ref.refType), ref.value, baseAddress)); \ + } \ + static_assert(true, "") // swallow semicolon -inline u32 ReadBigEndian(u32 x) { - return x; -} -#endif + DEF_GET_DATA_REF_ADDRESS_(141, 0); + DEF_GET_DATA_REF_ADDRESS_(142, 1); + DEF_GET_DATA_REF_ADDRESS_(143, 2); + DEF_GET_DATA_REF_ADDRESS_(144, 3); -f32 CalcPitchRatio(int pitch); -f32 CalcVolumeRatio(f32 db); -f32 CalcPanRatio(f32 pan, const PanInfo &rInfo); -f32 CalcSurroundPanRatio(f32 pan, const PanInfo &rInfo); -int CalcLpfFreq(f32 scale); -void GetRemoteFilterCoefs(int filter, u16 *pB0, u16 *pB1, u16 *pB2, u16 *pA1, u16 *pA2); -u16 CalcRandom(); -const void *GetDataRefAddressImpl(RefType type, u32 value, const void *pBase); +#undef DEF_GET_DATA_REF_ADDRESS_ -} // namespace Util -} // namespace detail -} // namespace snd -} // namespace nw4r + static inline byte2_t ReadBigEndian(byte2_t x) + { + return x; + } -#endif + static inline byte4_t ReadBigEndian(byte4_t x) + { + return x; + } + + static f32 const CALC_LPF_FREQ_INTERCEPT; + static int const CALC_LPF_FREQ_TABLE_SIZE = 3 * 8; + static int const BIQUAD_COEF_COUNT = 5; + static int const IIR_COEF_COUNT; + static int const COEF_TABLE_SIZE = 128; + static int const COEF_TABLE_MAX = 127; + static int const COEF_TABLE_MIN = 0; + static int const PAN_TABLE_SIZE = 256 + 1; + static int const PAN_TABLE_CENTER = 128; + static int const PAN_TABLE_MIN = 0; + static int const PAN_TABLE_MAX = 256; + static int const DECIBEL_SQUARE_TABLE_SIZE; + static int const DECIBEL_TABLE_SIZE = 964 + 1; + static int const VOLUME_TABLE_SIZE; + static int const OCTAVE_DIVISION = 12; // ? makes sense to me + static int const PAN_CURVE_NUM = 3; + static int const CALC_DECIBEL_SCALE_MAX; + static int const PITCH_DIVISION_RANGE = 256; // ? maybe makes sense? + static int const PITCH_DIVISION_BIT; + static int const PAN_CENTER; + static int const PAN_MAX; + static int const PAN_MIN; + static int const VOLUME_DB_MAX = 60; + static int const VOLUME_DB_MIN = -904; + static u16 const VOLUME_MAX; + static u16 const VOLUME_MIN; + + static u16 const CalcLpfFreqTable[CALC_LPF_FREQ_TABLE_SIZE]; + static u16 const RemoteFilterCoefTable[COEF_TABLE_SIZE][BIQUAD_COEF_COUNT]; + static f32 const Pan2RatioTableLinear[PAN_TABLE_SIZE]; + static f32 const Pan2RatioTableSinCos[PAN_TABLE_SIZE]; + static f32 const Pan2RatioTableSqrt[PAN_TABLE_SIZE]; + static f32 const *PanTableTable[PAN_CURVE_NUM]; + static f32 const Decibel2RatioTable[DECIBEL_TABLE_SIZE]; + static f32 const PitchTable[PITCH_DIVISION_RANGE]; + static f32 const NoteTable[OCTAVE_DIVISION]; + }; // "namespace" Util +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_UTIL_H diff --git a/include/nw4r/snd/snd_Voice.h b/include/nw4r/snd/snd_Voice.h index 8657ea28..207ad86c 100644 --- a/include/nw4r/snd/snd_Voice.h +++ b/include/nw4r/snd/snd_Voice.h @@ -1,183 +1,235 @@ #ifndef NW4R_SND_VOICE_H #define NW4R_SND_VOICE_H -#include "nw4r/snd/snd_AxVoice.h" -#include "nw4r/snd/snd_Common.h" -#include "nw4r/snd/snd_DisposeCallback.h" -#include "nw4r/ut.h" // IWYU pragma: export -#include "rvl/WPAD.h" // IWYU pragma: export +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { -namespace detail { +#include -// Forward declarations -class WaveData; +#include "adpcm.h" // AdpcmLoopParam +#include "AxVoice.h" +#include "global.h" +#include "DisposeCallbackManager.h" // DisposeCallback -class Voice : public DisposeCallback { - friend class VoiceManager; +#include "../ut/LinkList.h" -public: - enum VoiceCallbackStatus { - CALLBACK_STATUS_FINISH_WAVE, - CALLBACK_STATUS_CANCEL, - CALLBACK_STATUS_DROP_VOICE, - CALLBACK_STATUS_DROP_DSP, - }; +/******************************************************************************* + * types + */ - typedef void (*VoiceCallback)(Voice *pDropVoice, VoiceCallbackStatus status, void *pCallbackArg); +// forward declarations +namespace nw4r { namespace snd { namespace detail { struct WaveInfo; }}} - enum VoiceSyncFlag { - SYNC_AX_SRC_INITIAL = (1 << 0), - SYNC_AX_VOICE = (1 << 1), - SYNC_AX_SRC = (1 << 2), - SYNC_AX_VE = (1 << 3), - SYNC_AX_MIX = (1 << 4), - SYNC_AX_LPF = (1 << 5), - SYNC_AX_REMOTE = (1 << 7), - SYNC_AX_BIQUAD = (1 << 8), - }; +/******************************************************************************* + * classes and functions + */ - static const int PRIORITY_MAX = 255; +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b832 + // NOTE: different from ketteiban: no remote fields + class Voice : public DisposeCallback + { + // enums + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b6c2 + enum VoiceCallbackStatus + { + CALLBACK_STATUS_FINISH_WAVE, + CALLBACK_STATUS_CANCEL, + CALLBACK_STATUS_DROP_VOICE, + CALLBACK_STATUS_DROP_DSP, + }; -public: - Voice(); - virtual ~Voice(); // at 0x8 + // typedefs + public: + typedef ut::LinkList LinkList; - virtual void InvalidateData(const void * /* pStart */, const void * /* pEnd */) {} // at 0xC + typedef void Callback(Voice *dropVoice, VoiceCallbackStatus status, + void *callbackData); - virtual void InvalidateWaveData(const void *pStart, - const void *pEnd); // at 0x10 + // methods + public: + // cdtors + Voice(); + virtual ~Voice(); - int GetPriority() const { - return mPriority; - } - int GetAxVoiceCount() const { - return mChannelCount * mVoiceOutCount; - } + // virtual function ordering + // vtable DisposeCallback + virtual void InvalidateData(void const *, void const *) {} + virtual void InvalidateWaveData(void const *start, void const *end); - bool IsActive() const { - return mAxVoice[0][0] != NULL; - } - bool IsPlayFinished() const { - return IsActive() && mAxVoice[0][0]->IsPlayFinished(); - } + // methods + void Setup(WaveInfo const &waveParam, u32 startOffset); - void InitParam(int channels, int voices, VoiceCallback pCallback, void *pCallbackArg); - void StopFinished(); + int GetPriority() const { return mPriority; } - void Calc(); - void Update(); + void SetVoiceOutParam(int voiceOutIndex, + VoiceOutParam const &voiceOutParam); + void SetVoiceOutParamPitchDisableFlag(bool flag) + { + mVoiceOutParamPitchDisableFlag = flag; + } + void SetRemoteFilter(int filter); + void SetBiquadFilter(int type, f32 value); + void SetPriority(int priority); + void SetPan(f32 pan); + void SetSurroundPan(f32 surroundPan); + void SetLpfFreq(f32 lpfFreq); + void SetOutputLine(int flag); + void SetMainOutVolume(f32 volume); + void SetMainSend(f32 send); + void SetFxSend(AuxBus bus, f32 send); + void SetPitch(f32 pitch); + void SetVolume(f32 volume); + void SetVeVolume(f32 targetVolume, f32 initVolume); + void SetPanMode(PanMode panMode); + void SetPanCurve(PanCurve panCurve); - bool Acquire(int channels, int voices, int priority, VoiceCallback pCallback, void *pCallbackArg); - void Free(); + bool IsActive() const { return mAxVoice[0][0] != nullptr; } + bool IsPlayFinished() const + { + return IsActive() && mAxVoice[0][0]->IsPlayFinished(); + } + SampleFormat GetFormat() const; + u32 GetCurrentPlayingSample() const; + int GetPhysicalVoiceCount() const + { + return mChannelCount * mVoiceOutCount; + } - void Setup(const WaveData &rData, u32 offset); + void SetVoiceType(AxVoice::VoiceType type); + void SetLoopFlag(bool loopFlag); + void SetLoopStart(int channelIndex, void const *baseAddress, + u32 samples); + void SetLoopEnd(int channelIndex, void const *baseAddress, u32 samples); + void SetAdpcmLoop(int channelIndex, AdpcmLoopParam const *param); - void Start(); - void Stop(); - void Pause(bool flag); + bool Acquire(int channelCount, int voiceOutCount, int priority, + Callback *callback, void *callbackData); + void Free(); - AxVoice::Format GetFormat() const; + void InitParam(int channelCount, int voiceOutCount, Callback *callback, + void *callbackData); - void SetVolume(f32 volume); - void SetVeVolume(f32 target, f32 init); - void SetPitch(f32 pitch); + void Start(); + void Pause(bool flag); + void Stop(); + void StopAtPoint(int channelIndex, void const *baseAddress, + u32 samples); - void SetPanMode(PanMode mode); - void SetPanCurve(PanCurve curve); - void SetPan(f32 pan); - void SetSurroundPan(f32 pan); + private: + void RunAllAxVoice(); + void StopAllAxVoice(); + void SyncAxVoice(); + void StopFinished(); - void SetLpfFreq(f32 freq); - void SetRemoteFilter(int filter); - void SetOutputLine(int flag); + void Calc(); + void CalcAxSrc(bool initialUpdate); + void CalcAxVe(); + bool CalcAxMix(); + void CalcAxLpf(); + void CalcAxBiquadFilter(); + void CalcAxRemoteFilter(); - void SetMainOutVolume(f32 volume); - void SetMainSend(f32 send); - void SetFxSend(AuxBus bus, f32 send); + void CalcMixParam(int channelIndex, int voiceOutIndex, + AxVoice::MixParam *mix, + AxVoice::RemoteMixParam *rmtmix); - void SetRemoteOutVolume(int remote, f32 volume); - void SetRemoteSend(int remote, f32 send); - void SetRemoteFxSend(int remote, f32 send); + void Update(); + void UpdateVoicesPriority(); - void SetPriority(int priority); - void UpdateVoicesPriority(); + void ResetDelta(); - void SetAdpcmLoop(int channel, const AdpcmLoopParam *pParam); - u32 GetCurrentPlayingSample() const; - void SetLoopStart(int channel, const void *pBase, u32 samples); - void SetLoopEnd(int channel, const void *pBase, u32 samples); - void SetLoopFlag(bool loop); - void StopAtPoint(int channel, const void *pBase, u32 samples); - void SetVoiceType(AxVoice::VoiceType type); + void TransformDpl2Pan(f32 *outPan, f32 *outSurroundPan, f32 inPan, + f32 inSurroundPan); - void CalcAxSrc(bool initial); - void CalcAxVe(); - bool CalcAxMix(); - void CalcAxLpf(); - void CalcAxRemoteFilter(); + static void AxVoiceCallbackFunc(AxVoice *dropVoice, + AxVoice::AxVoiceCallbackStatus status, + void *callbackData); -private: - static const int VOICES_MIN = 1; - static const int VOICES_MAX = 4; + // static members + public: + static f32 const SURROUND_ATTENUATED_DB; + static u32 const VOICE_PRIORITY_RELEASE; + static u32 const VOICE_PRIORITY_ALLOC; + static u32 const VOICE_PRIORITY_USE; + static int const VOICE_PRIORITY_FREE; + static u32 const VOICE_PRIORITY_NODROP; + static u32 const VOICE_PRIORITY_MAX = 31; // just going to put it here + static int const PRIORITY_RELEASE; + static int const PRIORITY_NODROP; + static int const PRIORITY_MIN = 0; + static int const PRIORITY_MAX = 255; + static int const REMOTE_FILTER_MAX = 127; + static int const REMOTE_FILTER_MIN = 0; + static f32 const SEND_MAX; + static f32 const SEND_MIN; + static f32 const BIQUAD_VALUE_MAX; + static f32 const BIQUAD_VALUE_MIN; + static f32 const CUTOFF_FREQ_MAX; + static f32 const CUTOFF_FREQ_MIN; + static f32 const SPAN_CENTER; + static f32 const SPAN_REAR; + static f32 const SPAN_FRONT; + static f32 const PAN_CENTER; + static f32 const PAN_RIGHT; + static f32 const PAN_LEFT; + static f32 const VOLUME_MAX; + static f32 const VOLUME_MIN; + static int const CHANNEL_MAX = 2; + static int const UPDATE_BIQUAD = 1 << 8; + static int const UPDATE_REMOTE_FILTER = 1 << 7; + // 1 << 6 skipped + static int const UPDATE_LPF = 1 << 5; + static int const UPDATE_MIX = 1 << 4; + static int const UPDATE_VE = 1 << 3; + static int const UPDATE_SRC = 1 << 2; + static int const UPDATE_PAUSE = 1 << 1; + static int const UPDATE_START = 1 << 0; -private: - void SyncAxVoice(); - void ResetDelta(); + // members + private: + /* base DisposeCallback */ // size 0x0c, offset 0x00 + AxVoice *mAxVoice[CHANNEL_MAX][4]; // size 0x20, offset 0x0c + VoiceOutParam mVoiceOutParam[4]; // size 0x60, offset 0x2c + int mChannelCount; // size 0x04, offset 0x8c + int mVoiceOutCount; // size 0x04, offset 0x90 + Callback *mCallback; // size 0x04, offset 0x94 + void *mCallbackData; // size 0x04, offset 0x98 + bool mActiveFlag; // size 0x01, offset 0x9c + bool mStartFlag; // size 0x01, offset 0x9d + bool mStartedFlag; // size 0x01, offset 0x9e + bool mPauseFlag; // size 0x01, offset 0x9f + bool mPausingFlag; // size 0x01, offset 0xa0 + bool mVoiceOutParamPitchDisableFlag; // size 0x01, offset 0xa1 + byte2_t mSyncFlag; // size 0x02, offset 0xa2 + u8 mRemoteFilter; // size 0x01, offset 0xa4 + u8 mBiquadType; // size 0x01, offset 0xa5 + /* 2 bytes padding */ + int mPriority; // size 0x04, offset 0xa8 + f32 mPan; // size 0x04, offset 0xac + f32 mSurroundPan; // size 0x04, offset 0xb0 + f32 mLpfFreq; // size 0x04, offset 0xb4 + f32 mBiquadValue; // size 0x04, offset 0xb8 + int mOutputLineFlag; // size 0x04, offset 0xbc + f32 mMainOutVolume; // size 0x04, offset 0xc0 + f32 mMainSend; // size 0x04, offset 0xc4 + f32 mFxSend[AUX_BUS_NUM]; // size 0x0c, offset 0xc8 + f32 mPitch; // size 0x04, offset 0xd4 + f32 mVolume; // size 0x04, offset 0xd8 + f32 mVeInitVolume; // size 0x04, offset 0xdc + f32 mVeTargetVolume; // size 0x04, offset 0xe0 + PanMode mPanMode; // size 0x04, offset 0xe4 + PanCurve mPanCurve; // size 0x04, offset 0xe8 + public: + ut::LinkListNode mLinkNode; // size 0x08, offset 0xec - static void AxVoiceCallbackFunc(AxVoice *pVoice, AxVoice::AxVoiceCallbackStatus status, void *pCallbackArg); + // friends + private: + friend class VoiceManager; + }; // size 0xf4 +}}} // namespace nw4r::snd::detail - void TransformDpl2Pan(f32 *pPan, f32 *pSurroundPan, f32 pan, f32 surroundPan); - void CalcMixParam(int channel, int voice, AxVoice::MixParam *pMix, AxVoice::RemoteMixParam *pRmtMix); - - void RunAllAxVoice(); - void StopAllAxVoice(); - -private: - AxVoice *mAxVoice[CHANNEL_MAX][VOICES_MAX]; // at 0xC - SoundParam mVoiceOutParam[VOICES_MAX]; // at 0x2C - int mChannelCount; // at 0x9C - int mVoiceOutCount; // at 0xA0 - - VoiceCallback mCallback; // at 0xA4 - void *mCallbackArg; // at 0xA8 - - bool mIsActive; // at 0xAC - bool mIsStarting; // at 0xAD - bool mIsStarted; // at 0xAE - bool mIsPause; // at 0xAF - bool mIsPausing; // at 0xB0 - - u8 mSyncFlag; // at 0xB1 - u8 mRemoteFilter; // at 0xB2 - u8 mBiquadType; // at 0xB3 - int mPriority; // at 0xB4 - f32 mPan; // at 0xB8 - f32 mSurroundPan; // at 0xBC - f32 mLpfFreq; // at 0xC0 - int mOutputLineFlag; // at 0xC4 - f32 mMainOutVolume; // at 0xC8 - f32 mMainSend; // at 0xCC - f32 mFxSend[AUX_BUS_NUM]; // at 0xD0 - f32 mRemoteOutVolume[WPAD_MAX_CONTROLLERS]; // at 0xDC - f32 mRemoteSend[WPAD_MAX_CONTROLLERS]; // at 0xEC - f32 mRemoteFxSend[WPAD_MAX_CONTROLLERS]; // at 0xFC - f32 mPitch; // at 0x10C - f32 mVolume; // at 0x110 - f32 mVeInitVolume; // at 0x114 - f32 mVeTargetVolume; // at 0x118 - PanMode mPanMode; // at 0x11C - PanCurve mPanCurve; // at 0x120 - -public: - NW4R_UT_LIST_NODE_DECL(); // at 0x124 -}; - -NW4R_UT_LIST_TYPEDEF_DECL(Voice); - -} // namespace detail -} // namespace snd -} // namespace nw4r - -#endif +#endif // NW4R_SND_VOICE_H diff --git a/include/nw4r/snd/snd_VoiceManager.h b/include/nw4r/snd/snd_VoiceManager.h index 854ae9ac..f6cb56f1 100644 --- a/include/nw4r/snd/snd_VoiceManager.h +++ b/include/nw4r/snd/snd_VoiceManager.h @@ -1,57 +1,69 @@ #ifndef NW4R_SND_VOICE_MANAGER_H #define NW4R_SND_VOICE_MANAGER_H -#include "nw4r/snd/snd_Voice.h" -#include "nw4r/types_nw4r.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { -namespace detail { +#include // u32 -class VoiceManager { -public: - static const int VOICE_MAX = AX_VOICE_MAX; - static const int WORK_SIZE_MAX = VOICE_MAX * sizeof(Voice); +#include "Voice.h" -public: - static VoiceManager &GetInstance(); +/******************************************************************************* + * classes and functions + */ - u32 GetRequiredMemSize(); +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2ffb36 + class VoiceManager + { + // methods + public: + // instance accessors + static VoiceManager &GetInstance(); - void Setup(void *pBuffer, u32 size); - void Shutdown(); - void StopAllVoices(); + // methods + void Setup(void *mem, u32 memSize); + void Shutdown(); - Voice *AllocVoice(int channels, int voices, int priority, Voice::VoiceCallback pCallback, void *pCallbackArg); - void FreeVoice(Voice *pVoice); + Voice::LinkList const &GetVoiceList() const { return mPrioVoiceList; } + u32 GetRequiredMemSize(int voiceCount); - void UpdateAllVoices(); - void NotifyVoiceUpdate(); - void ChangeVoicePriority(Voice *pVoice); - void UpdateAllVoicesSync(u32 syncFlag); + Voice *AllocVoice(int voiceChannelCount, int voiceOutCount, + int priority, Voice::Callback *callback, + void *callbackData); + void FreeVoice(Voice *voice); - const VoiceList &GetVoiceList() const { - return mPrioVoiceList; - } + void StopAllVoices(); -private: - VoiceManager(); + void ChangeVoicePriority(Voice *voice); - void AppendVoiceList(Voice *pVoice); - void RemoveVoiceList(Voice *pVoice); + void UpdateAllVoices(); + void UpdateAllVoicesSync(byte4_t syncFlag); - void UpdateEachVoicePriority(const VoiceList::Iterator &rBegin, const VoiceList::Iterator &rEnd); + void NotifyVoiceUpdate(); - int DropLowestPriorityVoice(int priority); + private: + // cdtors + VoiceManager(); -private: - bool mInitialized; // at 0x0 - VoiceList mPrioVoiceList; // at 0x4 - VoiceList mFreeVoiceList; // at 0x10 -}; + // methods + void AppendVoiceList(Voice *voice); + void RemoveVoiceList(Voice *voice); -} // namespace detail -} // namespace snd -} // namespace nw4r + void UpdateEachVoicePriority(Voice::LinkList::Iterator const &beginItr, + Voice::LinkList::Iterator const &endItr); -#endif + int DropLowestPriorityVoice(int priority); + + // members + private: + bool mInitialized; // size 0x01, offset 0x00 + /* 3 bytes padding */ + Voice::LinkList mPrioVoiceList; // size 0x0c, offset 0x04 + Voice::LinkList mFreeVoiceList; // size 0x0c, offset 0x10 + }; // size 0x1c +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_VOICE_MANAGER_H diff --git a/include/nw4r/snd/snd_WaveArchive.h b/include/nw4r/snd/snd_WaveArchive.h new file mode 100644 index 00000000..5bd2d5c7 --- /dev/null +++ b/include/nw4r/snd/snd_WaveArchive.h @@ -0,0 +1,108 @@ +#ifndef NW4R_SND_WAVE_ARCHIVE_H +#define NW4R_SND_WAVE_ARCHIVE_H + +/******************************************************************************* + * headers + */ + +#include +#include + +#include "Util.h" +#include "WaveFile.h" + +#include "../ut/binaryFileFormat.h" + +/******************************************************************************* + * types + */ + +namespace nw4r { namespace snd { namespace detail +{ + struct WaveArchive + { + /* Header */ + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x307f00 + struct FileHeader + { + ut::BinaryFileHeader fileHeader; // size 0x10, offset 0x00 + u32 tableChunkOffset; // size 0x04, offset 0x10 + u32 tableChunkSize; // size 0x04, offset 0x14 + u32 dataChunkOffset; // size 0x04, offset 0x18 + u32 dataChunkSize; // size 0x04, offset 0x1c + }; // size 0x20 + + /* TableBlock */ + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b289b + struct TableItem + { + Util::DataRef waveFileRef; // size 0x08, offset 0x00 + u32 waveFileSize; // size 0x04, offset 0x08 + }; // size 0x0c + + static byte4_t const SIGNATURE_TABLE_BLOCK = + NW4R_FOUR_BYTE('T', 'A', 'B', 'L'); + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b29a1 + struct TableBlock + { + ut::BinaryBlockHeader blockHeader; // size 0x08, offset 0x00 + Util::Table fileTable; // size 0x10, offset 0x08 + }; // size 0x18 + + /* DataBlock */ + + static byte4_t const SIGNATURE_DATA_BLOCK = + NW4R_FOUR_BYTE('D', 'A', 'T', 'A'); + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b2a3c + struct DataBlock + { + ut::BinaryBlockHeader blockHeader; // size 0x08, offset 0x00 + byte_t data[1]; // size 0x01, offset 0x08 + /* 3 bytes padding */ + }; // size 0x0c + + /* WaveArchive */ + + static byte4_t const SIGNATURE_FILE = + NW4R_FOUR_BYTE('R', 'W', 'A', 'R'); + static int const FILE_VERSION = NW4R_FILE_VERSION(1, 0); + }; // "namespace" WaveArchive +}}} // namespace nw4r::snd::detail + +/******************************************************************************* + * classes and functions + */ + +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b2bb8 + class WaveArchiveReader + { + // methods + public: + // cdtors + WaveArchiveReader(void const *waveArc); + + // methods + WaveFile::FileHeader const *GetWaveFile(int index) const; + + private: + bool VerifyFileHeader(void const *waveArc); + + // static members + public: + static u16 const SUPPORTED_FILE_VERSION_MAX = NW4R_FILE_VERSION(1, 0); + static u16 const SUPPORTED_FILE_VERSION_MIN = NW4R_FILE_VERSION(1, 0); + + // members + private: + WaveArchive::TableBlock const *mTableBlock; // size 0x04, offset 0x00 + WaveArchive::DataBlock const *mDataBlock; // size 0x04, offset 0x04 + }; // size 0x08 +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_WAVE_ARCHIVE_H diff --git a/include/nw4r/snd/snd_WaveFile.h b/include/nw4r/snd/snd_WaveFile.h index cd5de7a2..00ac032c 100644 --- a/include/nw4r/snd/snd_WaveFile.h +++ b/include/nw4r/snd/snd_WaveFile.h @@ -1,93 +1,198 @@ #ifndef NW4R_SND_WAVE_FILE_H #define NW4R_SND_WAVE_FILE_H -#include "nw4r/snd/snd_AxVoice.h" -#include "nw4r/snd/snd_Common.h" -#include "nw4r/types_nw4r.h" -#include "nw4r/ut.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { -namespace detail { -namespace WaveFile { +#include // NW4R_FOUR_BYTE +#include -enum Format { - FORMAT_PCM8, - FORMAT_PCM16, - FORMAT_ADPCM -}; +#include "Channel.h" +#include "global.h" // SampleFormat +#include "adpcm.h" -struct WaveInfo { - u8 format; // at 0x0 - u8 loopFlag; // at 0x1 - u8 numChannels; // at 0x2 - u8 sampleRate24; // at 0x3 - u16 sampleRate; // at 0x4 - u16 padding2; // at 0x6 - u32 loopStart; // at 0x8 - u32 loopEnd; // at 0xC - u32 channelInfoTableOffset; // at 0x10 - u32 dataOffset; // at 0x14 - u32 reserved; // at 0x18 -}; +#include "../ut/binaryFileFormat.h" -struct WaveChannelInfo { - u32 channelDataOffset; // at 0x0 - u32 adpcmOffset; // at 0x4 - u32 volumeFrontLeft; // at 0x8 - u32 volumeFrontRight; // at 0xC - u32 volumeRearLeft; // at 0x10 - u32 volumeRearRight; // at 0x14 - u32 reserved; // at 0x18 -}; +/******************************************************************************* + * types + */ -}; // namespace WaveFile +namespace nw4r { namespace snd { namespace detail +{ + struct WaveFile + { + /* Header */ -struct ChannelParam { - void *dataAddr; // at 0x0 - u32 volumeFrontLeft; // at 0x4 - u32 volumeFrontRight; // at 0x8 - u32 volumeRearLeft; // at 0xC - u32 volumeRearRight; // at 0x10 - AdpcmInfo adpcmInfo; // at 0x14 -}; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b2ab1 + struct FileHeader + { + ut::BinaryFileHeader fileHeader; // size 0x10, offset 0x00 + u32 infoChunkOffset; // size 0x04, offset 0x10 + u32 infoChunkSize; // size 0x04, offset 0x14 + u32 dataChunkOffset; // size 0x04, offset 0x18 + u32 dataChunkSize; // size 0x04, offset 0x1c + }; // size 0x20 -struct WaveData { - u8 sampleFormat; // at 0x0 - u8 loopFlag; // at 0x1 - u8 numChannels; // at 0x2 - u32 sampleRate; // at 0x4 - u32 loopStart; // at 0x8 - u32 loopEnd; // at 0xC - ChannelParam channelParam[CHANNEL_MAX]; // at 0x10 -}; + /* InfoBlock */ -class WaveFileReader { -public: - explicit WaveFileReader(const WaveFile::WaveInfo *pWaveInfo); + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b2185 + struct WaveInfo + { + u8 format; // size 0x01, offset 0x00 + u8 loopFlag; // size 0x01, offset 0x01 + u8 numChannels; // size 0x01, offset 0x02 + u8 sampleRate24; // size 0x01, offset 0x03 + u16 sampleRate; // size 0x02, offset 0x04 + u8 dataLocationType; // size 0x01, offset 0x06 + byte1_t padding; + u32 loopStart; // size 0x04, offset 0x08 + u32 loopEnd; // size 0x04, offset 0x0c + u32 channelInfoTableOffset; // size 0x04, offset 0x10 + u32 dataLocation; // size 0x04, offset 0x14 + byte4_t reserved; + }; // size 0x1c - bool ReadWaveParam(WaveData *pWaveData, const void *pWaveAddr) const; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b2c9d + struct WaveChannelInfo + { + u32 channelDataOffset; // size 0x04, offset 0x00 + u32 adpcmOffset; // size 0x04, offset 0x04 + u32 volumeFrontLeft; // size 0x04, offset 0x08 + u32 volumeFrontRight; // size 0x04, offset 0x0c + u32 volumeRearLeft; // size 0x04, offset 0x10 + u32 volumeRearRight; // size 0x04, offset 0x14 + u32 reserved; + }; // size 0x1c - static AxVoice::Format GetAxVoiceFormatFromWaveFileFormat(u32 format); + static byte4_t const SIGNATURE_INFO_BLOCK = + NW4R_FOUR_BYTE('I', 'N', 'F', 'O'); -private: - const WaveFile::WaveInfo *mWaveInfo; // at 0x0 -}; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x3087c5 + struct InfoBlock + { + ut::BinaryBlockHeader blockHeader; // size 0x08, offset 0x00 + WaveInfo waveInfo; // size 0x1c, offset 0x08 + }; // size 0x24 -inline AxVoice::Format WaveFormatToAxFormat(u32 format) { - if (format == WaveFile::FORMAT_PCM16) { - return AxVoice::FORMAT_PCM16; - } + /* Other */ - if (format == WaveFile::FORMAT_PCM8) { - return AxVoice::FORMAT_PCM8; - } + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x308a10 + struct AdpcmParamSet + { + AdpcmParam adpcmParam; // size 0x28, offset 0x00 + AdpcmLoopParam adpcmLoopParam; // size 0x06, offset 0x28 + }; // size 0x2e - return AxVoice::FORMAT_ADPCM; -} + /* WaveFile */ -} // namespace detail -} // namespace snd -} // namespace nw4r + static byte4_t const SIGNATURE_FILE = + NW4R_FOUR_BYTE('R', 'W', 'A', 'V'); + static int const FILE_VERSION; // does it even have one? + }; // "namespace" WaveFile -#endif + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x29336 + struct ChannelParam + { + void *dataAddr; // size 0x04, offset 0x00 + AdpcmParam adpcmParam; // size 0x28, offset 0x04 + AdpcmLoopParam adpcmLoopParam; // size 0x06, offset 0x2c + /* 2 bytes padding */ + }; // size 0x34 + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x29401 + struct WaveInfo + { + SampleFormat sampleFormat; // size 0x04, offset 0x00 + bool loopFlag; // size 0x01, offset 0x04 + /* 3 bytes padding */ + int numChannels; // size 0x04, offset 0x08 + int sampleRate; // size 0x04, offset 0x0c + u32 loopStart; // size 0x04, offset 0x10 + u32 loopEnd; // size 0x04, offset 0x14 + ChannelParam channelParam[Channel::CHANNEL_MAX]; // size 0x68, offset 0x18 + }; // size 0x80 + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2e58c + struct WaveSoundInfo + { + f32 pitch; // size 0x04, offset 0x00 + u8 pan; // size 0x01, offset 0x04 + u8 surroundPan; // size 0x01, offset 0x05 + u8 fxSendA; // size 0x01, offset 0x06 + u8 fxSendB; // size 0x01, offset 0x07 + u8 fxSendC; // size 0x01, offset 0x08 + u8 mainSend; // size 0x01, offset 0x09 + /* 2 bytes padding */ + }; // size 0x0c + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2e6c2 + struct WaveSoundNoteInfo + { + s32 waveIndex; // size 0x04, offset 0x00 + u8 attack; // size 0x01, offset 0x04 + u8 hold; // size 0x01, offset 0x05 + u8 decay; // size 0x01, offset 0x06 + u8 sustain; // size 0x01, offset 0x07 + u8 release; // size 0x01, offset 0x08 + u8 originalKey; // size 0x01, offset 0x09 + u8 pan; // size 0x01, offset 0x0a + u8 surroundPan; // size 0x01, offset 0x0b + u8 volume; // size 0x01, offset 0x0c + /* 3 bytes padding */ + f32 pitch; // size 0x04, offset 0x10 + }; // size 0x14 +}}} // namespace nw4r::snd::detail + +/******************************************************************************* + * classes and functions + */ + +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b2e05 + class WaveFileReader + { + // methods + public: + // cdtors + WaveFileReader(WaveFile::FileHeader const *waveFileHeader); + WaveFileReader(WaveFile::WaveInfo const *waveInfo); + + // methods + bool ReadWaveInfo(WaveInfo *waveData) const + { + return ReadWaveInfo(waveData, nullptr); + } + + bool ReadWaveInfo(WaveInfo *waveData, + void const *waveDataOffsetOrigin) const; + + void const *GetWaveDataAddress( + WaveFile::WaveChannelInfo const *waveChannelInfo, + void const *waveDataOffsetOrigin) const; + + static SampleFormat GetSampleFormatFromWaveFileFormat(u8 format); + + // members + private: + WaveFile::WaveInfo const *mWaveInfo; // size 0x04, offset 0x00 + }; // size 0x04 + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2954b + class WaveDataLocationCallback + { + // methods + public: + // virtual function ordering + // vtable WaveDataLocationCallback + virtual WaveInfo *at_0x08(); + virtual void at_0x0c(WaveInfo const *); + + // members + private: + /* vtable */ // size 0x04, offset 0x00 + }; // size 0x04 +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_WAVE_FILE_H diff --git a/include/nw4r/snd/snd_WaveSound.h b/include/nw4r/snd/snd_WaveSound.h index d0ec2c31..b5e2d2b8 100644 --- a/include/nw4r/snd/snd_WaveSound.h +++ b/include/nw4r/snd/snd_WaveSound.h @@ -1,65 +1,96 @@ #ifndef NW4R_SND_WAVE_SOUND_H #define NW4R_SND_WAVE_SOUND_H -#include "nw4r/snd/snd_BasicSound.h" -#include "nw4r/snd/snd_WsdPlayer.h" -#include "nw4r/types_nw4r.h" -#include "nw4r/ut.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { +#include -// Forward declarations -class WaveSoundHandle; +#include "BasicSound.h" +#include "debug.h" +#include "WsdPlayer.h" -namespace detail { +#include "../ut/LinkList.h" -// Forward declarations -template -class SoundInstanceManager; +/******************************************************************************* + * types + */ -class WaveSound : public BasicSound { - friend class WaveSoundHandle; +// forward declarations +namespace nw4r { namespace snd { namespace detail { template class SoundInstanceManager; }}} +namespace nw4r { namespace snd { class WaveSoundHandle; }} -public: - NW4R_UT_RTTI_DECL(WaveSound); +namespace nw4r { namespace ut { namespace detail { class RuntimeTypeInfo; }}} -public: - explicit WaveSound(SoundInstanceManager *pManager); +/******************************************************************************* + * classes and functions + */ - virtual void Shutdown(); // at 0x28 - virtual bool IsPrepared() const { - return mPreparedFlag; - } // at 0x2C +namespace nw4r { namespace snd { namespace detail +{ + class WaveSound : public BasicSound + { + // typedefs + public: + /* redeclare with this class instead of BasicSound for + * SoundInstanceManager + */ + typedef ut::LinkList PriorityLinkList; - virtual void SetPlayerPriority(int priority); // at 0x4C - virtual bool IsAttachedTempSpecialHandle(); // at 0x5C - virtual void DetachTempSpecialHandle(); // at 0x60 + // methods + public: + // cdtors + WaveSound(SoundInstanceManager *manager, int priority, + int ambientPriority); + /* virtual ~WaveSound() {} */ // virtual function ordering - virtual BasicPlayer &GetBasicPlayer() { - return mWsdPlayer; - } // at 0x68 - virtual const BasicPlayer &GetBasicPlayer() const { - return mWsdPlayer; - } // at 0x6C + // virtual function ordering + // vtable BasicSound + virtual ut::detail::RuntimeTypeInfo const *GetRuntimeTypeInfo() const + { + return &typeInfo; + } + virtual ~WaveSound() {} + virtual void Shutdown(); + virtual bool IsPrepared() const { return mPreparedFlag; } + virtual bool IsAttachedTempSpecialHandle(); + virtual void DetachTempSpecialHandle(); + virtual BasicPlayer &GetBasicPlayer() { return mWsdPlayer; } + virtual BasicPlayer const &GetBasicPlayer() const { return mWsdPlayer; } + virtual void OnUpdatePlayerPriority(); - bool Prepare( - const void *pWsdData, s32 wsdOffset, WsdPlayer::StartOffsetType startType, s32 startOffset, int voices, - const WsdPlayer::WsdCallback *pCallback, u32 callbackArg - ); + // methods + bool Prepare(void const *waveSoundBase, s32 waveSoundOffset, + WsdPlayer::StartOffsetType startOffsetType, s32 offset, + WsdPlayer::WsdCallback const *callback, + register_t callbackData); - void SetChannelPriority(int priority); - void SetReleasePriorityFix(bool flag); + void SetChannelPriority(int priority); + void SetReleasePriorityFix(bool flag); -private: - WsdPlayer mWsdPlayer; // at 0xD8 - WaveSoundHandle *mTempSpecialHandle; // at 0x1B0 - SoundInstanceManager *mManager; // at 0x1B4 - bool mPreparedFlag; // at 0x1B8 -}; + static DebugSoundType GetSoundType() + { + return DEBUG_SOUND_TYPE_WAVESOUND; + } -} // namespace detail -} // namespace snd -} // namespace nw4r + // static members + public: + static ut::detail::RuntimeTypeInfo const typeInfo; -#endif + // members + private: + /* base BasicSound */ // size 0x100, offset 0x000 + WsdPlayer mWsdPlayer; // size 0x138, offset 0x100 + WaveSoundHandle *mTempSpecialHandle; // size 0x004, offset 0x238 + SoundInstanceManager *mManager; // size 0x004, offset 0x23c + bool mPreparedFlag; // size 0x001, offset 0x240 + /* 3 bytes padding */ + + // friends + private: + friend class snd::WaveSoundHandle; + }; // size 0x244 +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_WAVE_SOUND_H diff --git a/include/nw4r/snd/snd_WaveSoundHandle.h b/include/nw4r/snd/snd_WaveSoundHandle.h index 2f373a52..8ae3d06f 100644 --- a/include/nw4r/snd/snd_WaveSoundHandle.h +++ b/include/nw4r/snd/snd_WaveSoundHandle.h @@ -1,30 +1,46 @@ #ifndef NW4R_SND_WAVE_SOUND_HANDLE_H #define NW4R_SND_WAVE_SOUND_HANDLE_H -#include "nw4r/snd/snd_WaveSound.h" -#include "nw4r/types_nw4r.h" -#include "nw4r/ut.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { +#include // nullptr -class WaveSoundHandle : private ut::NonCopyable { -public: - ~WaveSoundHandle() { - DetachSound(); - } +#include "nw4r/ut/inlines.h" // ut::NonCopyable - void DetachSound(); +/******************************************************************************* + * types + */ - bool IsAttachedSound() const { - return mSound != NULL; - } +// forward declarations +namespace nw4r { namespace snd { namespace detail { class WaveSound; }}} +namespace nw4r { namespace snd { class SoundHandle; }} -private: - detail::WaveSound *mSound; // at 0x0 -}; +/******************************************************************************* + * classes and functions + */ -} // namespace snd -} // namespace nw4r +namespace nw4r { namespace snd +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x30f8a + class WaveSoundHandle : private ut::NonCopyable + { + // methods + public: + // cdtors + WaveSoundHandle(SoundHandle *handle); -#endif + // methods + bool IsAttachedSound() const { return mSound != nullptr; } + + void DetachSound(); + + // members + private: + /* base NonCopyable */ // size 0x00, offset 0x00 + detail::WaveSound *mSound; // size 0x04, offset 0x00 + }; // size 0x04 +}} // namespace nw4r::snd + +#endif // NW4R_SND_WAVE_SOUND_HANDLE_H diff --git a/include/nw4r/snd/snd_WsdFile.h b/include/nw4r/snd/snd_WsdFile.h index 7d9024a3..a47970cb 100644 --- a/include/nw4r/snd/snd_WsdFile.h +++ b/include/nw4r/snd/snd_WsdFile.h @@ -1,131 +1,173 @@ #ifndef NW4R_SND_WSD_FILE_H #define NW4R_SND_WSD_FILE_H -#include "nw4r/snd/snd_Util.h" -#include "nw4r/ut.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { -namespace detail { -namespace WsdFile { +#include +#include -struct WsdInfo { - f32 pitch; // at 0x0 - u8 pan; // at 0x4 - u8 surroundPan; // at 0x5 - u8 fxSendA; // at 0x6 - u8 fxSendB; // at 0x7 - u8 fxSendC; // at 0x8 - u8 mainSend; // at 0x9 - Util::DataRef graphEnvTableRef; // at 0xC - Util::DataRef randomizerTableRef; // at 0x14 - u32 reserved; // at 0x1C -}; +#include "Util.h" -struct TrackInfo {}; +#include "../ut/binaryFileFormat.h" -struct NoteInfo { - s32 waveIndex; // at 0x0 - u8 attack; // at 0x4 - u8 decay; // at 0x5 - u8 sustain; // at 0x6 - u8 release; // at 0x7 - u16 hold; // at 0x8 - u16 padding; // at 0xA - u8 originalKey; // at 0xC - u8 volume; // at 0xD - u8 pan; // at 0xE - u8 surroundPan; // at 0xF - f32 pitch; // at 0x10 - Util::DataRef lfoTableRef; // at 0x14 - Util::DataRef graphEnvTablevRef; // at 0x1C - Util::DataRef randomizerTableRef; // at 0x24 - u32 reserved; // at 0x2C -}; +/******************************************************************************* + * types + */ -typedef Util::Table > TrackTable; +// forward declarations +namespace nw4r { namespace snd { namespace detail { struct WaveInfo; }}} +namespace nw4r { namespace snd { namespace detail { struct WaveSoundInfo; }}} +namespace nw4r { namespace snd { namespace detail { struct WaveSoundNoteInfo; }}} -typedef Util::Table > NoteTable; +namespace nw4r { namespace snd { namespace detail +{ + struct WsdFile + { + /* Header */ -struct Wsd { - Util::DataRef refWsdInfo; // at 0x0 - Util::DataRef refTrackTable; // at 0x8 - Util::DataRef refNoteTable; // at 0x10 -}; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2e4ce7 + struct Header + { + ut::BinaryFileHeader fileHeader; // size 0x10, offset 0x00 + u32 dataBlockOffset; // size 0x04, offset 0x10 + u32 dataBlockSize; // size 0x04, offset 0x14 + u32 waveBlockOffset; // size 0x04, offset 0x18 + u32 waveBlockSize; // size 0x04, offset 0x1c + }; // size 0x20 -struct Header { - ut::BinaryFileHeader fileHeader; // at 0x0 - u32 dataBlockOffset; // at 0x10 - u32 dataBlockSize; // at 0x14 - u32 waveBlockOffset; // at 0x18 - u32 waveBlockSize; // at 0x1C -}; + /* DataBlock */ -struct DataBlock { - ut::BinaryBlockHeader blockHeader; // at 0x0 - u32 wsdCount; // at 0x8 - Util::DataRef refWsd[]; // at 0xC -}; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x30abf7 + struct WsdInfo + { + f32 pitch; // size 0x04, offset 0x00 + u8 pan; // size 0x01, offset 0x04 + u8 surroundPan; // size 0x01, offset 0x05 + u8 fxSendA; // size 0x01, offset 0x06 + u8 fxSendB; // size 0x01, offset 0x07 + u8 fxSendC; // size 0x01, offset 0x08 + u8 mainSend; // size 0x01, offset 0x09 + byte1_t padding[2]; + // TODO: template parameters + Util::DataRef graphEnvTablevRef; // size 0x08, offset 0x0c + Util::DataRef randomizerTableRef; // size 0x08, offset 0x14 + byte4_t reserved; + }; // size 0x20 -struct WaveBlock { - ut::BinaryBlockHeader blockHeader; // at 0x0 - u32 waveCount; // at 0x8 - u32 offsetTable[]; // at 0xC -}; + struct TrackInfo; -// <= NW4R_VERSION(1, 0) -struct WaveBlockOld { - ut::BinaryBlockHeader blockHeader; // at 0x0 - u32 offsetTable[]; // at 0x8 -}; + typedef Util::Table > TrackInfoTable; -}; // namespace WsdFile + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x30af52 + struct NoteInfo + { + s32 waveIndex; // size 0x04, offset 0x00 + u8 attack; // size 0x01, offset 0x04 + u8 decay; // size 0x01, offset 0x05 + u8 sustain; // size 0x01, offset 0x06 + u8 release; // size 0x01, offset 0x07 + u8 hold; // size 0x01, offset 0x08 + byte1_t padding[3]; + u8 originalKey; // size 0x01, offset 0x0c + u8 volume; // size 0x01, offset 0x0d + u8 pan; // size 0x01, offset 0x0e + u8 surroundPan; // size 0x01, offset 0x0f + f32 pitch; // size 0x04, offset 0x10 + // TODO: template parameters + Util::DataRef lfoTableRef; // size 0x08, offset 0x14 + Util::DataRef graphEnvTablevRef; // size 0x08, offset 0x1c + Util::DataRef randomizerTableRef; // size 0x08, offset 0x24 + byte4_t reserved; + }; // size 0x30 -struct WaveSoundInfo { - f32 pitch; // at 0x0 - u8 pan; // at 0x4 - u8 surroundPan; // at 0x5 - u8 fxSendA; // at 0x6 - u8 fxSendB; // at 0x7 - u8 fxSendC; // at 0x8 - u8 mainSend; // at 0x9 -}; + typedef Util::Table > NoteInfoTable; -struct WaveSoundNoteInfo { - s32 waveIndex; // at 0x0 - u8 attack; // at 0x4 - u8 decay; // at 0x6 - u8 sustain; // at 0x7 - u8 release; // at 0x8 - u8 originalKey; // at 0x9 - u8 pan; // at 0xA - u8 surroundPan; // at 0xB - u8 volume; // at 0xC - f32 pitch; // at 0x10 -}; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x30ab32 + struct Wsd + { + Util::DataRef refWsdInfo; // size 0x08, offset 0x00 + Util::DataRef refTrackTable; // size 0x08, offset 0x08 + Util::DataRef refNoteTable; // size 0x08, offset 0x10 + }; // size 0x18 -class WsdFileReader { -public: - static const u32 SIGNATURE = 'RWSD'; - static const int VERSION = NW4R_VERSION(1, 2); + static byte4_t const SIGNATURE_DATA_BLOCK = + NW4R_FOUR_BYTE('D', 'A', 'T', 'A'); -public: - explicit WsdFileReader(const void *pWsdBin); + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2e4ec8 + struct DataBlock + { + ut::BinaryBlockHeader blockHeader; // size 0x08, offset 0x00 + u32 wsdCount; // size 0x04, offset 0x08 + Util::DataRef refWsd[]; // flexible, offset 0x0c (unit size 0x08) + }; // size 0x0c - bool IsValidFileHeader(const void *pWsdBin); + /* WaveBlock */ - bool ReadWaveSoundInfo(WaveSoundInfo *pSoundInfo, int id) const; - bool ReadWaveSoundNoteInfo(WaveSoundNoteInfo *pSoundNoteInfo, int id, int note) const; - bool ReadWaveParam(int id, WaveData *pWaveData, const void *pWaveAddr) const; + static byte4_t const SIGNATURE_WAVE_BLOCK = + NW4R_FOUR_BYTE('W', 'A', 'V', 'E'); -private: - const WsdFile::Header *mHeader; // at 0x0 - const WsdFile::DataBlock *mDataBlock; // at 0x4 - const WsdFile::WaveBlock *mWaveBlock; // at 0x8 -}; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2e4f87 + struct WaveBlock + { + ut::BinaryBlockHeader blockHeader; // size 0x04, offset 0x00 + u32 waveCount; // size 0x04, offset 0x08 + u32 offsetTable[]; // flexible, offset 0x0c (unit size 0x04) + }; // size 0x0c -} // namespace detail -} // namespace snd -} // namespace nw4r + /* WaveBlockOld */ -#endif + // FILE_VERSION <= NW4R_FILE_VERSION(1, 0) + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x30b523 + struct WaveBlockOld + { + ut::BinaryBlockHeader blockHeader; // size 0x08, offset 0x00 + u32 offsetTable[]; // flexible, offset 0x08 (unit size 0x04) + }; // size 0x08 + + /* WsdFile */ + + static byte4_t const SIGNATURE_FILE = + NW4R_FOUR_BYTE('R', 'W', 'S', 'D'); + static int const FILE_VERSION = NW4R_FILE_VERSION(1, 3); + }; // "namespace" WsdFile +}}} // namespace nw4r::snd::detail + +/******************************************************************************* + * classes and functions + */ + +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2e502c + class WsdFileReader + { + // methods + public: + // cdtors + WsdFileReader(void const *wsdData); + + // methods + bool ReadWaveInfo(int waveIndex, WaveInfo *waveData, + void const *waveDataAddress) const; + bool ReadWaveSoundInfo(WaveSoundInfo *info, int index) const; + bool ReadWaveSoundNoteInfo(WaveSoundNoteInfo *noteInfo, int index, + int noteIndex) const; + + private: + bool IsValidFileHeader(void const *wsdData); + + // static members + private: + static int const SUPPORTED_FILE_VERSION = NW4R_FILE_VERSION(1, 3); + + // members + private: + WsdFile::Header const *mHeader; // size 0x04, offset 0x00 + WsdFile::DataBlock const *mDataBlock; // size 0x04, offset 0x04 + WsdFile::WaveBlock const *mWaveBlock; // size 0x04, offset 0x08 + }; // size 0x0c +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_WSD_FILE_H diff --git a/include/nw4r/snd/snd_WsdPlayer.h b/include/nw4r/snd/snd_WsdPlayer.h index 27370242..5ceec546 100644 --- a/include/nw4r/snd/snd_WsdPlayer.h +++ b/include/nw4r/snd/snd_WsdPlayer.h @@ -1,139 +1,152 @@ #ifndef NW4R_SND_WSD_PLAYER_H #define NW4R_SND_WSD_PLAYER_H -#include "nw4r/snd/snd_BasicPlayer.h" -#include "nw4r/snd/snd_Channel.h" -#include "nw4r/snd/snd_DisposeCallback.h" -#include "nw4r/snd/snd_Lfo.h" -#include "nw4r/snd/snd_SoundThread.h" -#include "nw4r/snd/snd_WsdFile.h" -#include "nw4r/types_nw4r.h" -#include "nw4r/ut.h" +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { -namespace detail { +#include -class WsdPlayer : public BasicPlayer, public DisposeCallback, public SoundThread::PlayerCallback { -public: - class WsdCallback { - public: - virtual ~WsdCallback() {} // at 0x8 +#include "BasicPlayer.h" +#include "Channel.h" +#include "DisposeCallbackManager.h" // DisposeCallback +#include "Lfo.h" // LfoParam +#include "SoundThread.h" +#include "WaveFile.h" - virtual bool GetWaveSoundData( - WaveSoundInfo *pSoundInfo, WaveSoundNoteInfo *pNoteInfo, WaveData *pWaveData, const void *pWsdData, - int index, int noteIndex, - u32 callbackArg - ) const = 0; // at 0xC - }; +/******************************************************************************* + * classes and functions + */ - enum StartOffsetType { - START_OFFSET_TYPE_SAMPLE, - START_OFFSET_TYPE_MILLISEC - }; +namespace nw4r { namespace snd { namespace detail +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x30b74 + class WsdPlayer : public BasicPlayer, + public DisposeCallback, + public SoundThread::PlayerCallback + { + // enums + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x30b0e + enum StartOffsetType + { + START_OFFSET_TYPE_SAMPLE, + START_OFFSET_TYPE_MILLISEC, + }; -public: - WsdPlayer(); + // nested types + public: + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2e896 + class WsdCallback + { + // methods + public: + // cdtors + virtual ~WsdCallback() {} - virtual bool Start(); // at 0xC - virtual void Stop(); // at 0x10 - virtual void Pause(bool flag); // at 0x14 + // virtual function ordering + // vtable WsdCallback + virtual bool GetWaveSoundData(WaveSoundInfo *info, + WaveSoundNoteInfo *noteInfo, + WaveInfo *waveData, + void const *waveSoundData, int index, + int noteIndex, + register_t userData) const = 0; - virtual bool IsActive() const { - return mActiveFlag != 0; - } // at 0x18 + // members + private: + /* vtable */ // size 0x04, offset 0x00 + }; // size 0x04 - virtual bool IsStarted() const { - return mStartedFlag != 0; - } // at 0x1C + // methods + public: + // cdtors + WsdPlayer(); - virtual bool IsPause() const { - return mPauseFlag != 0; - } // at 0x20 + // virtual function ordering + // vtable BasicPlayer + virtual bool Start(); + virtual void Stop(); + virtual void Pause(bool flag); + virtual bool IsActive() const { return mActiveFlag != false; } + virtual bool IsStarted() const { return mStartedFlag != false; } + virtual bool IsPause() const { return mPauseFlag != false; } - virtual void InvalidateData(const void *pStart, - const void *pEnd); // at 0x50 + // vtable DisposeCallback + virtual void InvalidateData(void const *start, void const *end); + virtual void InvalidateWaveData(void const *, void const *) {} - virtual void InvalidateWaveData(const void * /* pStart */, const void * /* pEnd */) {} // at 0x54 + // vtable SoundThread::PlayerCallback + virtual void OnUpdateFrameSoundThread() { Update(); } + virtual void OnShutdownSoundThread() { Stop(); } - virtual void OnUpdateFrameSoundThread() { - Update(); - } // at 0x58 + // methods + bool Prepare(void const *waveSoundBase, int index, + StartOffsetType startOffsetType, int startOffset, + int voiceOutCount, WsdCallback const *callback, + register_t callbackData); - virtual void OnShutdownSoundThread() { - Stop(); - } // at 0x60 + f32 GetPanRange() const { return mPanRange; } + int GetVoiceOutCount() const { return mVoiceOutCount; } + int GetChannelPriority() const { return mPriority; } + void const *GetWsdDataAddress() const { return mWsdData; } - void InitParam(int voices, const WsdCallback *pCallback, u32 callbackArg); + void SetReleasePriorityFix(bool flag); + void SetChannelPriority(int priority); - bool Prepare( - const void *pWsdData, int index, StartOffsetType startType, int startOffset, int voices, - const WsdCallback *pCallback, u32 callbackArg - ); + bool IsChannelActive() const + { + return mChannel != nullptr && mChannel->IsActive(); + } + u32 GetPlaySamplePosition() const; - void SetChannelPriority(int priority); - void SetReleasePriorityFix(bool flag); + private: + static void ChannelCallbackFunc(Channel *dropChannel, + Channel::ChannelCallbackStatus status, + register_t userData); - f32 GetPanRange() const { - return mPanRange; - } + void InitParam(int voiceOutCount, WsdCallback const *callback, + register_t callbackData); - int GetVoiceOutCount() const { - return mVoiceOutCount; - } + void Update(); - int GetChannelPriority() const { - return mPriority; - } + bool StartChannel(WsdCallback const *callback, register_t callbackData); + void UpdateChannel(); + void CloseChannel(); - const void *GetWsdDataAddress() const { - return mWsdData; - } + void FinishPlayer(); - bool IsChannelActive() const { - return mChannel != NULL && mChannel->IsActive(); - } + // static members + public: + static int const DEFAULT_PRIORITY = 64; + static int const MUTE_RELEASE_VALUE; + static int const PAUSE_RELEASE_VALUE; -private: - static const int DEFAULT_PRIORITY = 64; + // members + private: + /* base BasicPlayer */ // size 0x0a4, offset 0x000 + /* base DisposeCallback */ // size 0x00c, offset 0x0a4 + /* base SoundThread::PlayerCallback */ // size 0x00c, offset 0x0b0 + bool mActiveFlag; // size 0x001, offset 0x0bc + bool mStartedFlag; // size 0x001, offset 0x0bd + bool mPauseFlag; // size 0x001, offset 0x0be + bool mWavePlayFlag; // size 0x001, offset 0x0bf + bool mReleasePriorityFixFlag; // size 0x001, offset 0x0c0 + /* 3 bytes padding */ + f32 mPanRange; // size 0x004, offset 0x0c4 + int mVoiceOutCount; // size 0x004, offset 0x0c8 + u8 mPriority; // size 0x001, offset 0x0cc + /* 3 bytes padding */ + WsdCallback const *mCallback; // size 0x004, offset 0x0d0 + register_t mCallbackData; // size 0x004, offset 0x0d4 + void const *mWsdData; // size 0x004, offset 0x0d8 + int mWsdIndex; // size 0x004, offset 0x0dc + StartOffsetType mStartOffsetType; // size 0x004, offset 0x0e0 + int mStartOffset; // size 0x004, offset 0x0e4 + LfoParam mLfoParam; // size 0x010, offset 0x0e8 + WaveSoundInfo mWaveSoundInfo; // size 0x00c, offset 0x0f8 + Channel *mChannel; // size 0x004, offset 0x104 + }; // size 0x108 +}}} // namespace nw4r::snd::detail -private: - void FinishPlayer(); - void Update(); - - bool StartChannel(const WsdCallback *pCallback, u32 callbackArg); - void CloseChannel(); - void UpdateChannel(); - - static void ChannelCallbackFunc(Channel *pDropChannel, Channel::ChannelCallbackStatus status, u32 callbackArg); - -private: - bool mActiveFlag; // at 0x8C - bool mStartedFlag; // at 0x8D - bool mPauseFlag; // at 0x8E - bool mWavePlayFlag; // at 0x8F - bool mReleasePriorityFixFlag; // at 0x90 - - f32 mPanRange; // at 0x94 - int mVoiceOutCount; // at 0x98 - u8 mPriority; // at 0x9C - - const WsdCallback *mCallback; // at 0xA0 - u32 mCallbackData; // at 0xA4 - - const void *mWsdData; // at 0xA8 - int mWsdIndex; // at 0xAC - - StartOffsetType mStartOffsetType; // at 0xB0 - int mStartOffset; // at 0xB4 - - LfoParam mLfoParam; // at 0xB8 - WaveSoundInfo mWaveSoundInfo; // at 0xC8 - Channel *mChannel; // at 0xD4 -}; - -} // namespace detail -} // namespace snd -} // namespace nw4r - -#endif +#endif // NW4R_SND_WSD_PLAYER_H diff --git a/include/nw4r/snd/snd_adpcm.h b/include/nw4r/snd/snd_adpcm.h index f38a2eb5..f35a1b5b 100644 --- a/include/nw4r/snd/snd_adpcm.h +++ b/include/nw4r/snd/snd_adpcm.h @@ -1,16 +1,52 @@ #ifndef NW4R_SND_ADPCM_H #define NW4R_SND_ADPCM_H -#include "rvl/AX.h" // IWYU pragma: export +/******************************************************************************* + * headers + */ -namespace nw4r { -namespace snd { -namespace detail { - -s16 DecodeDspAdpcm(AXPBADPCM *pAdpcm, u8 bits); - -} // namespace detail -} // namespace snd -} // namespace nw4r +#include +#if 0 +#include // AXPBADPCM +#else +#include #endif + +/******************************************************************************* + * types + */ + +namespace nw4r { namespace snd { namespace detail +{ + // Why are these just the AX types + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x291b3 + struct AdpcmParam // AXPBADPCM + { + u16 coef[8 * 2]; // size 0x20, offset 0x00 + u16 gain; // size 0x02, offset 0x20 + u16 pred_scale; // size 0x02, offset 0x22 + u16 yn1; // size 0x02, offset 0x24 + u16 yn2; // size 0x02, offset 0x26 + }; // size 0x28 + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x29290 + struct AdpcmLoopParam // AXPBADPCMLOOP + { + u16 loop_pred_scale; // size 0x02, offset 0x00 + u16 loop_yn1; // size 0x02, offset 0x02 + u16 loop_yn2; // size 0x02, offset 0x04 + }; // size 0x06 +}}} // namespace nw4r::snd::detail + +/******************************************************************************* + * classes and functions + */ + +namespace nw4r { namespace snd { namespace detail +{ + s16 DecodeDspAdpcm(AXPBADPCM *adpcm, byte_t bits); +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_ADPCM_H diff --git a/include/nw4r/snd/snd_debug.h b/include/nw4r/snd/snd_debug.h new file mode 100644 index 00000000..06ae307e --- /dev/null +++ b/include/nw4r/snd/snd_debug.h @@ -0,0 +1,45 @@ +#ifndef NW4R_SND_DEBUG_H +#define NW4R_SND_DEBUG_H + +/******************************************************************************* + * types + */ + +namespace nw4r { namespace snd +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2bd7b3 + enum DebugWarningFlag + { + DEBUG_WARNING_NOT_ENOUGH_INSTANCE, + DEBUG_WARNING_NOT_ENOUGH_SEQSOUND, + DEBUG_WARNING_NOT_ENOUGH_STRMSOUND, + DEBUG_WARNING_NOT_ENOUGH_WAVESOUND, + DEBUG_WARNING_NOT_ENOUGH_SEQTRACK, + DEBUG_WARNING_NOT_ENOUGH_STRMCHANNEL, + }; + + namespace detail + { + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2ec85 + enum DebugSoundType + { + DEBUG_SOUND_TYPE_SEQSOUND, + DEBUG_SOUND_TYPE_STRMSOUND, + DEBUG_SOUND_TYPE_WAVESOUND, + }; + } // namespace detail +}} // namespace nw4r::snd + +/******************************************************************************* + * classes and functions + */ + +namespace nw4r { namespace snd { namespace detail +{ + bool Debug_GetWarningFlag(DebugWarningFlag warning); + DebugWarningFlag Debug_GetDebugWarningFlagFromSoundType( + DebugSoundType type); + char const *Debug_GetSoundTypeString(DebugSoundType type); +}}} // namespace nw4r::snd::detail + +#endif // NW4R_SND_DEBUG_H diff --git a/include/nw4r/snd/snd_global.h b/include/nw4r/snd/snd_global.h new file mode 100644 index 00000000..57c73b77 --- /dev/null +++ b/include/nw4r/snd/snd_global.h @@ -0,0 +1,95 @@ +#ifndef NW4R_SND_GLOBAL_H +#define NW4R_SND_GLOBAL_H + +/******************************************************************************* + * headers + */ + +#include // f32 + +/******************************************************************************* + * types + */ + +namespace nw4r { namespace snd +{ + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x26bd0 + enum AuxBus + { + AUX_A, + AUX_B, + AUX_C, + }; + + // TODO: would this fit as AX_MAX_AUX in AX? + static int const AUX_BUS_NUM = 3; + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x20f19 + enum OutputMode + { + OUTPUT_MODE_STEREO, + OUTPUT_MODE_SURROUND, + OUTPUT_MODE_DPL2, + OUTPUT_MODE_MONO, + }; + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x25ec5 + enum PanMode + { + PAN_MODE_DUAL, + PAN_MODE_BALANCE, + }; + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x25f0e + enum PanCurve + { + PAN_CURVE_SQRT, + PAN_CURVE_SQRT_0DB, + PAN_CURVE_SQRT_0DB_CLAMP, + + PAN_CURVE_SINCOS, + PAN_CURVE_SINCOS_0DB, + PAN_CURVE_SINCOS_0DB_CLAMP, + + PAN_CURVE_LINEAR, + PAN_CURVE_LINEAR_0DB, + PAN_CURVE_LINEAR_0DB_CLAMP, + }; + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x20e89 + enum SampleFormat + { + SAMPLE_FORMAT_PCM_S32, + SAMPLE_FORMAT_PCM_S16, + SAMPLE_FORMAT_PCM_S8, + SAMPLE_FORMAT_DSP_ADPCM, + }; + + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x273ea + struct VoiceOutParam + { + // methods + public: + // cdtors + VoiceOutParam() : + volume (1.0f), + pitch (1.0f), + pan (0.0f), + surroundPan (0.0f), + fxSend (0.0f), + lpf (0.0f) + { + } + + // members + public: + f32 volume; // size 0x04, offset 0x00 + f32 pitch; // size 0x04, offset 0x04 + f32 pan; // size 0x04, offset 0x08 + f32 surroundPan; // size 0x04, offset 0x0c + f32 fxSend; // size 0x04, offset 0x10 + f32 lpf; // size 0x04, offset 0x14 + }; // size 0x18 +}} // namespace nw4r::snd + +#endif // NW4R_SND_GLOBAL_H 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 // nullptr +#include + +#include "nw4r/snd/Util.h" + +DECOMP_FORCE(nw4r::snd::detail::Util::GetDataRefAddress0( + *static_cast 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 // std::memset + +#include +#include + +#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 // DCFlushRange +#include // AICheckInit +#include +#include +#include +#else +#include +#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(i)); + + updateFlag = true; + } + + if (updateFlag) + { + f32 returnVolumeFloat = 1.0f; + returnVolumeFloat *= + ut::Clamp(mAuxUserVolume[i].GetValue(), 0.0f, 1.0f); + returnVolumeFloat *= + ut::Clamp(mAuxFadeVolume[i].GetValue(), 0.0f, 1.0f); + + u16 returnVolume = AUX_RETURN_VOLUME_MAX * returnVolumeFloat; + + switch (i) + { + case AUX_A: + AXSetAuxAReturnVolume(returnVolume); + break; + + case AUX_B: + AXSetAuxBReturnVolume(returnVolume); + break; + + case AUX_C: + AXSetAuxCReturnVolume(returnVolume); + break; + } + } + } + + if (!mMasterVolume.IsFinished()) + { + mMasterVolume.Update(); + + VoiceManager::GetInstance().UpdateAllVoicesSync(Voice::UPDATE_VE); + } + + if (!mVolumeForReset.IsFinished()) + mVolumeForReset.Update(); + + if (!mMainOutVolume.IsFinished()) + mMainOutVolume.Update(); + + f32 volume = mMainOutVolume.GetValue(); + volume *= mVolumeForReset.GetValue(); + volume = ut::Clamp(volume, 0.0f, 1.0f); + + AXSetMasterVolume(AUX_RETURN_VOLUME_MAX * volume); +} + +void const *AxManager::GetZeroBufferAddress() +{ + NW4RAssertMessage_Line(261, mZeroBufferAddress, + "Zero buffer is not created."); + + return mZeroBufferAddress; +} + +void AxManager::RegisterCallback(CallbackListNode *node, + AXFrameCallback *callback) +{ + ut::AutoInterruptLock lock; + + node->callback = callback; + mCallbackList.PushBack(node); +} + +void AxManager::UnregisterCallback(CallbackListNode *node) +{ + ut::AutoInterruptLock lock; + + mCallbackList.Erase(node); +} + +void AxManager::SetOutputMode(OutputMode mode) +{ + if (mOutputMode == mode) + return; + + ut::AutoInterruptLock lock; + + mOutputMode = mode; + + switch (mode) + { + case OUTPUT_MODE_STEREO: + AXSetMode(AX_CL_MODE_STEREO); + break; + + case OUTPUT_MODE_SURROUND: + AXSetMode(AX_CL_MODE_SURROUND); + break; + + case OUTPUT_MODE_DPL2: + AXSetMode(AX_CL_MODE_DPL2); + break; + + case OUTPUT_MODE_MONO: + AXSetMode(AX_CL_MODE_STEREO); + break; + } + + VoiceManager::GetInstance().UpdateAllVoicesSync(Voice::UPDATE_MIX); + + for (int bus = AUX_A; bus < AUX_BUS_NUM; bus++) + { + FxBase::LinkList &fxList = GetEffectList(static_cast(bus)); + + NW4R_RANGE_FOR(itr, fxList) + itr->OnChangeOutputMode(); + } + + if (mode == OUTPUT_MODE_DPL2) + mEffectProcessTick[AUX_C] = 0; +} + +OutputMode AxManager::GetOutputMode() +{ + return mOutputMode; +} + +void AxManager::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 +#include + +#include // BOOLIFY_TERNARY +#include + +#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 +#include +#include +#else +#include +#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(callbackData); + AxVoice *voice = reinterpret_cast(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(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(baseAddress) * sizeof(u16)) + sizeof(u16); + break; + + case SAMPLE_FORMAT_PCM_S8: + addr = reinterpret_cast(baseAddress) + samples; + break; + + case SAMPLE_FORMAT_PCM_S16: + addr = reinterpret_cast(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(baseAddress)); + + u32 samples = 0; + + switch (format) + { + case SAMPLE_FORMAT_DSP_ADPCM: + samples = addr - reinterpret_cast(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(baseAddress); + break; + + case SAMPLE_FORMAT_PCM_S16: + samples = addr - reinterpret_cast(baseAddress) / sizeof(u16); + break; + + default: + NW4RPanicMessage_Line(350, "Invalid format\n"); + break; + } + + return samples; +} + +void AxVoice::SetPriority(u32 priority) +{ + mVpb.SetVoicePriority(priority); +} + +void AxVoice::SetVoiceType(VoiceType type) +{ + mVpb.SetVoiceType(type); +} + +void AxVoice::ResetDelta() +{ + ut::AutoInterruptLock lock; + + if (!mVpb.IsAvailable()) + return; + + mVpb.UpdateDelta(); + + AXPBMIX mix; + mix.vL = mMixPrev.vL; + mix.vDeltaL = 0; + mix.vR = mMixPrev.vR; + mix.vDeltaR = 0; + mix.vAuxAL = mMixPrev.vAuxAL; + mix.vDeltaAuxAL = 0; + mix.vAuxAR = mMixPrev.vAuxAR; + mix.vDeltaAuxAR = 0; + mix.vAuxBL = mMixPrev.vAuxBL; + mix.vDeltaAuxBL = 0; + mix.vAuxBR = mMixPrev.vAuxBR; + mix.vDeltaAuxBR = 0; + mix.vAuxCL = mMixPrev.vAuxCL; + mix.vDeltaAuxCL = 0; + mix.vAuxCR = mMixPrev.vAuxCR; + mix.vDeltaAuxCR = 0; + mix.vS = mMixPrev.vS; + mix.vDeltaS = 0; + mix.vAuxAS = mMixPrev.vAuxAS; + mix.vDeltaAuxAS = 0; + mix.vAuxBS = mMixPrev.vAuxBS; + mix.vDeltaAuxBS = 0; + mix.vAuxCS = mMixPrev.vAuxCS; + mix.vDeltaAuxCS = 0; + + mVpb.SetVoiceMix(mix, true); +} + +void AxVoice::SetAddr(bool loopFlag, void const *waveAddr, u32 startOffset, + u32 loopStart, u32 loopEnd) +{ + ut::AutoInterruptLock lock; + + if (!mVpb.IsAvailable()) + return; + + u32 startPos; + u32 endPos; + u32 loopPos; + + if (startOffset > loopEnd) + { + void const *zeroBuffer = + AxManager::GetInstance().GetZeroBufferAddress(); + loopFlag = false; + + startPos = GetDspAddressBySample(zeroBuffer, 0, mFormat); + loopPos = GetDspAddressBySample(zeroBuffer, 0, mFormat); + endPos = GetDspAddressBySample(zeroBuffer, 1, mFormat); + } + else + { + if (loopFlag) + { + loopPos = GetDspAddressBySample(waveAddr, loopStart, mFormat); + } + else + { + void const *zeroBuffer = + AxManager::GetInstance().GetZeroBufferAddress(); + + loopPos = GetDspAddressBySample(zeroBuffer, 0, mFormat); + } + + startPos = GetDspAddressBySample(waveAddr, startOffset, mFormat); + endPos = GetDspAddressBySample(waveAddr, loopEnd - 1, mFormat); + } + + AXPBADDR addr; + addr.loopFlag = loopFlag; + addr.format = GetAxFormatFromSampleFormat(mFormat); + addr.loopAddressHi = loopPos >> 16; + addr.loopAddressLo = loopPos & 0xffff; + addr.endAddressHi = endPos >> 16; + addr.endAddressLo = endPos & 0xffff; + addr.currentAddressHi = startPos >> 16; + addr.currentAddressLo = startPos & 0xffff; + + mVpb.SetVoiceAddr(addr); +} + +u16 AxVoice::GetAxFormatFromSampleFormat(SampleFormat sampleFormat) +{ + switch (sampleFormat) + { + case SAMPLE_FORMAT_DSP_ADPCM: + return 0; + + case SAMPLE_FORMAT_PCM_S8: + return 25; + + case SAMPLE_FORMAT_PCM_S16: + return 10; + + default: + NW4RPanicMessage_Line(503, "Invalid format\n"); + return 0; + } +} + +void AxVoice::SetSrcType(SrcType type, f32 pitch) +{ + ut::AutoInterruptLock lock; + + if (!mVpb.IsAvailable()) + return; + + if (type == SRC_4TAP_AUTO) + { + f32 ratio = GetDspRatio(pitch); + + if (ratio > 4.0f / 3.0f) + type = SRC_4TAP_8K; + else if (ratio > 1.0f) + type = SRC_4TAP_12K; + else + type = SRC_4TAP_16K; + } + + mVpb.SetVoiceSrcType(type); +} + +void AxVoice::SetAdpcm(AdpcmParam const *param) +{ + ut::AutoInterruptLock lock; + + if (!mVpb.IsAvailable()) + return; + + AXPBADPCM adpcm; + + switch (mFormat) + { + case SAMPLE_FORMAT_DSP_ADPCM: + NW4RAssertPointerNonnull_Line(533, param); + + std::memcpy(adpcm.a, param->coef, sizeof adpcm.a); + + adpcm.gain = param->gain; + adpcm.pred_scale = param->pred_scale; + adpcm.yn1 = param->yn1; + adpcm.yn2 = param->yn2; + + break; + + case SAMPLE_FORMAT_PCM_S16: + std::memset(adpcm.a, 0, sizeof adpcm.a); + + adpcm.gain = 0x800; + adpcm.pred_scale = 0; + adpcm.yn1 = 0; + adpcm.yn2 = 0; + + break; + + case SAMPLE_FORMAT_PCM_S8: + std::memset(adpcm.a, 0, sizeof adpcm.a); + + adpcm.gain = 0x100; + adpcm.pred_scale = 0; + adpcm.yn1 = 0; + adpcm.yn2 = 0; + + break; + + default: + NW4RPanicMessage_Line(555, "Invalid format\n"); + break; + } + + mVpb.SetVoiceAdpcm(adpcm); +} + +bool AxVoice::IsNeedNextUpdate(MixParam const ¶m) const +{ + if (mMixPrev.vL != param.vL) + return true; + + if (mMixPrev.vR != param.vR) + return true; + + if (mMixPrev.vS != param.vS) + return true; + + if (mMixPrev.vAuxAL != param.vAuxAL) + return true; + + if (mMixPrev.vAuxAR != param.vAuxAR) + return true; + + if (mMixPrev.vAuxAS != param.vAuxAS) + return true; + + if (mMixPrev.vAuxBL != param.vAuxBL) + return true; + + if (mMixPrev.vAuxBR != param.vAuxBR) + return true; + + if (mMixPrev.vAuxBS != param.vAuxBS) + return true; + + if (mMixPrev.vAuxCL != param.vAuxCL) + return true; + + if (mMixPrev.vAuxCR != param.vAuxCR) + return true; + + if (mMixPrev.vAuxCS != param.vAuxCS) + return true; + + return false; +} + +void AxVoice::SetAdpcmLoop(AdpcmLoopParam const *param) +{ + ut::AutoInterruptLock lock; + + if (!mVpb.IsAvailable()) + return; + + AXPBADPCMLOOP adpcmloop; + + if (mFormat == SAMPLE_FORMAT_DSP_ADPCM) + { + NW4RAssertPointerNonnull_Line(587, param); + + adpcmloop.loop_pred_scale = param->loop_pred_scale; + adpcmloop.loop_yn1 = param->loop_yn1; + adpcmloop.loop_yn2 = param->loop_yn2; + } + else + { + adpcmloop.loop_pred_scale = 0; + adpcmloop.loop_yn1 = 0; + adpcmloop.loop_yn2 = 0; + } + + mVpb.SetVoiceAdpcmLoop(adpcmloop); +} + +bool AxVoice::SetMix(MixParam const ¶m) +{ + ut::AutoInterruptLock lock; + + if (!mVpb.IsAvailable()) + return false; + + AXPBMIX mix; + + if (mFirstMixUpdateFlag || !IsRun()) + { + mMixPrev = param; + mFirstMixUpdateFlag = false; + } + + bool needUpdateFlag = IsNeedNextUpdate(param); + + mix.vL = mMixPrev.vL; + mix.vR = mMixPrev.vR; + mix.vS = mMixPrev.vS; + mix.vAuxAL = mMixPrev.vAuxAL; + mix.vAuxAR = mMixPrev.vAuxAR; + mix.vAuxAS = mMixPrev.vAuxAS; + mix.vAuxBL = mMixPrev.vAuxBL; + mix.vAuxBR = mMixPrev.vAuxBR; + mix.vAuxBS = mMixPrev.vAuxBS; + mix.vAuxCL = mMixPrev.vAuxCL; + mix.vAuxCR = mMixPrev.vAuxCR; + mix.vAuxCS = mMixPrev.vAuxCS; + + int vDeltaL = CalcAxvpbDelta(mMixPrev.vL, param.vL); + int vDeltaR = CalcAxvpbDelta(mMixPrev.vR, param.vR); + int vDeltaS = CalcAxvpbDelta(mMixPrev.vS, param.vS); + int vDeltaAuxAL = CalcAxvpbDelta(mMixPrev.vAuxAL, param.vAuxAL); + int vDeltaAuxAR = CalcAxvpbDelta(mMixPrev.vAuxAR, param.vAuxAR); + int vDeltaAuxAS = CalcAxvpbDelta(mMixPrev.vAuxAS, param.vAuxAS); + int vDeltaAuxBL = CalcAxvpbDelta(mMixPrev.vAuxBL, param.vAuxBL); + int vDeltaAuxBR = CalcAxvpbDelta(mMixPrev.vAuxBR, param.vAuxBR); + int vDeltaAuxBS = CalcAxvpbDelta(mMixPrev.vAuxBS, param.vAuxBS); + int vDeltaAuxCL = CalcAxvpbDelta(mMixPrev.vAuxCL, param.vAuxCL); + int vDeltaAuxCR = CalcAxvpbDelta(mMixPrev.vAuxCR, param.vAuxCR); + int vDeltaAuxCS = CalcAxvpbDelta(mMixPrev.vAuxCS, param.vAuxCS); + + mix.vDeltaL = vDeltaL; + mix.vDeltaR = vDeltaR; + mix.vDeltaS = vDeltaS; + mix.vDeltaAuxAL = vDeltaAuxAL; + mix.vDeltaAuxAR = vDeltaAuxAR; + mix.vDeltaAuxAS = vDeltaAuxAS; + mix.vDeltaAuxBL = vDeltaAuxBL; + mix.vDeltaAuxBR = vDeltaAuxBR; + mix.vDeltaAuxBS = vDeltaAuxBS; + mix.vDeltaAuxCL = vDeltaAuxCL; + mix.vDeltaAuxCR = vDeltaAuxCR; + mix.vDeltaAuxCS = vDeltaAuxCS; + + mVpb.SetVoiceMix(mix, false); + + if (param.vL == 0 || vDeltaL == 0) + mMixPrev.vL = param.vL; + else + mMixPrev.vL += vDeltaL * AX_SAMPLES_PER_FRAME; + + if (param.vR == 0 || vDeltaR == 0) + mMixPrev.vR = param.vR; + else + mMixPrev.vR += vDeltaR * AX_SAMPLES_PER_FRAME; + + if (param.vS == 0 || vDeltaS == 0) + mMixPrev.vS = param.vS; + else + mMixPrev.vS += vDeltaS * AX_SAMPLES_PER_FRAME; + + if (param.vAuxAL == 0 || vDeltaAuxAL == 0) + mMixPrev.vAuxAL = param.vAuxAL; + else + mMixPrev.vAuxAL += vDeltaAuxAL * AX_SAMPLES_PER_FRAME; + + if (param.vAuxAR == 0 || vDeltaAuxAR == 0) + mMixPrev.vAuxAR = param.vAuxAR; + else + mMixPrev.vAuxAR += vDeltaAuxAR * AX_SAMPLES_PER_FRAME; + + if (param.vAuxAS == 0 || vDeltaAuxAS == 0) + mMixPrev.vAuxAS = param.vAuxAS; + else + mMixPrev.vAuxAS += vDeltaAuxAS * AX_SAMPLES_PER_FRAME; + + if (param.vAuxBL == 0 || vDeltaAuxBL == 0) + mMixPrev.vAuxBL = param.vAuxBL; + else + mMixPrev.vAuxBL += vDeltaAuxBL * AX_SAMPLES_PER_FRAME; + + if (param.vAuxBR == 0 || vDeltaAuxBR == 0) + mMixPrev.vAuxBR = param.vAuxBR; + else + mMixPrev.vAuxBR += vDeltaAuxBR * AX_SAMPLES_PER_FRAME; + + if (param.vAuxBS == 0 || vDeltaAuxBS == 0) + mMixPrev.vAuxBS = param.vAuxBS; + else + mMixPrev.vAuxBS += vDeltaAuxBS * AX_SAMPLES_PER_FRAME; + + if (param.vAuxCL == 0 || vDeltaAuxCL == 0) + mMixPrev.vAuxCL = param.vAuxCL; + else + mMixPrev.vAuxCL += vDeltaAuxCL * AX_SAMPLES_PER_FRAME; + + if (param.vAuxCR == 0 || vDeltaAuxCR == 0) + mMixPrev.vAuxCR = param.vAuxCR; + else + mMixPrev.vAuxCR += vDeltaAuxCR * AX_SAMPLES_PER_FRAME; + + if (param.vAuxCS == 0 || vDeltaAuxCS == 0) + mMixPrev.vAuxCS = param.vAuxCS; + else + mMixPrev.vAuxCS += vDeltaAuxCS * AX_SAMPLES_PER_FRAME; + + return needUpdateFlag; +} + +void AxVoice::SetSrc(f32 ratio, bool initialUpdate) +{ + ut::AutoInterruptLock lock; + + if (!mVpb.IsAvailable()) + return; + + if (initialUpdate) + { + ratio = ut::Clamp(GetDspRatio(ratio), 0.0f, 65535.0f); + + u32 srcBits = 65536 * ratio; + + AXPBSRC src; + src.ratioHi = srcBits >> 16; + src.ratioLo = srcBits & 0xffff; + src.currentAddressFrac = 0; + src.last_samples[0] = 0; + src.last_samples[1] = 0; + src.last_samples[2] = 0; + src.last_samples[3] = 0; + + mVpb.SetVoiceSrc(src); + } + else + { + ratio = GetDspRatio(ratio); + + mVpb.SetVoiceSrcRatio(ratio); + } +} + +void AxVoice::SetVe(f32 volume, f32 initVolume) +{ + ut::AutoInterruptLock lock; + + if (!mVpb.IsAvailable()) + return; + + mVpb.SetVoiceVe(volume * (AX_MAX_VOLUME - 1), + initVolume * (AX_MAX_VOLUME - 1)); +} + +void AxVoice::SetLpf(u16 freq) +{ + ut::AutoInterruptLock lock; + + if (!mVpb.IsAvailable()) + return; + + if (freq >= 16000) + { + AXPBLPF lpf; + lpf.on = AX_PB_OFF; + lpf.yn1 = 0; + + mVpb.SetVoiceLpf(lpf); + } + else if (mVpb.IsLpfEnable()) + { + u16 a0, b0; + AXGetLpfCoefs(freq, &a0, &b0); + + mVpb.SetVoiceLpfCoefs(a0, b0); + } + else + { + AXPBLPF lpf; + lpf.on = AX_PB_LPF_ON; + lpf.yn1 = 0; + AXGetLpfCoefs(freq, &lpf.a0, &lpf.b0); + + mVpb.SetVoiceLpf(lpf); + } +} + +void AxVoice::SetBiquad(u8 filterType, f32 value) +{ + ut::AutoInterruptLock lock; + + if (!mVpb.IsAvailable()) + return; + + BiquadFilterCallback const *biquadFilter = + AxManager::GetInstance().GetBiquadFilterCallback(filterType); + + bool filterEnable = true; + + if (filterType == 0) + filterEnable = false; + + if (!biquadFilter) + filterEnable = false; + + if (!filterEnable) + { + AXPBBIQUAD biquad; + biquad.on = AX_PB_OFF; + biquad.xn1 = 0; + biquad.xn2 = 0; + biquad.yn1 = 0; + biquad.yn2 = 0; + + mVpb.SetVoiceBiquad(biquad); + } + else + { + BiquadFilterCallback::BiquadCoef coef; + biquadFilter->GetCoef(filterType, value, &coef); + + if (mVpb.IsBiquadEnable()) + { + mVpb.SetVoiceBiquadCoefs(coef.b0, coef.b1, coef.b2, coef.a1, + coef.a2); + } + else + { + AXPBBIQUAD biquad; + biquad.on = AX_PB_BIQUAD_ON; + biquad.xn1 = 0; + biquad.xn2 = 0; + biquad.yn1 = 0; + biquad.yn2 = 0; + biquad.b0 = coef.b0; + biquad.b1 = coef.b1; + biquad.b2 = coef.b2; + biquad.a1 = coef.a1; + biquad.a2 = coef.a2; + + mVpb.SetVoiceBiquad(biquad); + } + } +} + +void AxVoice::SetRemoteFilter(u8 filter) +{ + ut::AutoInterruptLock lock; + + if (!mVpb.IsAvailable()) + return; + + if (filter == 0) + { + __AXPBRMTIIR iir; + iir.lpf.on = AX_PB_OFF; + + mVpb.SetVoiceRmtIIR(iir); + } + else if (mVpb.IsRmtIirEnable()) + { + u16 b0, b1, b2, a1, a2; + Util::GetRemoteFilterCoefs(filter, &b0, &b1, &b2, &a1, &a2); + + mVpb.SetVoiceRmtIIRCoefs(AX_PB_BIQUAD_ON, b0, b1, b2, a1, a2); + } + else + { + __AXPBRMTIIR iir; + iir.biquad.on = AX_PB_BIQUAD_ON; + iir.biquad.xn1 = 0; + iir.biquad.xn2 = 0; + iir.biquad.yn1 = 0; + iir.biquad.yn2 = 0; + + Util::GetRemoteFilterCoefs(filter, &iir.biquad.b0, &iir.biquad.b1, + &iir.biquad.b2, &iir.biquad.a1, + &iir.biquad.a2); + + mVpb.SetVoiceRmtIIR(iir); + } +} + +void AxVoice::CalcOffsetAdpcmParam(u16 *outPredScale, u16 *outYn1, u16 *outYn2, + u32 offset, void *dataAddr, + AdpcmParam const &adpcmParam) +{ + AXPBADPCM adpcm; + std::memcpy(adpcm.a, adpcmParam.coef, sizeof adpcm.a); + adpcm.gain = adpcmParam.gain; + adpcm.pred_scale = adpcmParam.pred_scale; + adpcm.yn1 = adpcmParam.yn1; + adpcm.yn2 = adpcmParam.yn2; + + u32 currentPos = + GetDspAddressBySample(dataAddr, 0, SAMPLE_FORMAT_DSP_ADPCM); + u32 endPos = + GetDspAddressBySample(dataAddr, offset, SAMPLE_FORMAT_DSP_ADPCM); + + while (currentPos < endPos) + { + if (currentPos % AX_ADPCM_NIBBLES_PER_FRAME == 0) + { + byte_t byte = *static_cast(OSPhysicalToCached( + reinterpret_cast(currentPos / sizeof(u16)))); + + adpcm.pred_scale = byte; + currentPos += sizeof(u16); + } + + byte_t byte = *static_cast(OSPhysicalToCached( + reinterpret_cast(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(reinterpret_cast(&mix)); + u16 *dst = reinterpret_cast(&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 + +#include + +#include "nw4r/snd/AxVoice.h" + +#include "nw4r/ut/Lock.h" // ut::AutoInterruptLock + +#if 0 +#include +#include +#else +#include +#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(mem); + + for (u32 i = 0; i < mVoiceCount; i++) + { + mFreeVoiceList.PushBack(new (ptr) AxVoice); + + ptr += sizeof(AxVoice); + } + + NW4RAssert_Line(65, ptr <= reinterpret_cast( 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(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 + +#include "nw4r/snd/BankFile.h" // InstInfo +#include "nw4r/snd/Channel.h" +#include "nw4r/snd/NoteOnCallback.h" // NoteOnInfo +#include "nw4r/snd/WaveFile.h" // WaveInfo + +#include "nw4r/ut/inlines.h" // ut::Min + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +Bank::Bank(void const *bankData) : + mBankReader (bankData), + mWaveDataAddress (nullptr) +{ +} + +Bank::~Bank() {} + +Channel *Bank::NoteOn(NoteOnInfo const ¬eOnInfo) const +{ + bool result; + + InstInfo instInfo; + result = mBankReader.ReadInstInfo(&instInfo, noteOnInfo.prgNo, + noteOnInfo.key, noteOnInfo.velocity); + if (!result) + return nullptr; + + WaveInfo waveParam; + WaveInfo const *waveInfoAddress; + result = mBankReader.ReadWaveInfo(&waveParam, instInfo.waveDataLocation, + mWaveDataAddress, &waveInfoAddress); + if (!result) + return nullptr; + + int voiceChannelCount = + ut::Min(waveParam.numChannels, Channel::CHANNEL_MAX); + Channel *ch_p = Channel::AllocChannel(voiceChannelCount, + noteOnInfo.voiceOutCount, noteOnInfo.priority, + noteOnInfo.channelCallback, noteOnInfo.channelCallbackData); + if (!ch_p) + return nullptr; + + ch_p->SetKey(noteOnInfo.key); + ch_p->SetOriginalKey(instInfo.originalKey); + + f32 initVolume = noteOnInfo.velocity / 127.0f; + initVolume *= initVolume; + initVolume *= instInfo.volume / 127.0f; + ch_p->SetInitVolume(initVolume); + + ch_p->SetTune(instInfo.tune); + ch_p->SetAttack(instInfo.attack); + ch_p->SetHold(instInfo.hold); + ch_p->SetDecay(instInfo.decay); + ch_p->SetSustain(instInfo.sustain); + ch_p->SetRelease(instInfo.release); + + f32 initPan = (instInfo.pan - 64) / 63.0f; + initPan += noteOnInfo.initPan / 63.0f; + ch_p->SetInitPan(initPan); + + ch_p->SetInitSurroundPan(0.0f); + ch_p->SetAlternateAssignId(instInfo.alternateAssign); + ch_p->SetReleaseIgnore(instInfo.noteOffType + == InstInfo::NOTE_OFF_TYPE_IGNORE); + + if (instInfo.waveDataLocation.type + == InstInfo::WaveDataLocation::WAVE_DATA_LOCATION_CALLBACK) + { + ch_p->SetWaveDataLocationCallback(instInfo.waveDataLocation.callback, + waveInfoAddress); + } + + ch_p->Start(waveParam, noteOnInfo.length, 0); + + return ch_p; +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_BankFile.cpp b/src/nw4r/snd/snd_BankFile.cpp index fbb3e65d..971efb1e 100644 --- a/src/nw4r/snd/snd_BankFile.cpp +++ b/src/nw4r/snd/snd_BankFile.cpp @@ -1 +1,374 @@ -#include "nw4r/snd/snd_BankFile.h" +#include "nw4r/snd/BankFile.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_BankFile.cpp + */ + +/******************************************************************************* + * headers + */ + +#include // NW4R_FILE_VERSION +#include + +#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(address); + } +}}} // namespace nw4r::snd::detail + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +bool BankFileReader::IsValidFileHeader(void const *bankData) +{ + ut::BinaryFileHeader const *fileHeader = + static_cast(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(bankData); + + if (mHeader->dataBlockOffset) + { + mDataBlock = static_cast( + ut::AddOffsetToPtr(mHeader, mHeader->dataBlockOffset)); + + NW4RAssert_Line(105, mDataBlock->blockHeader.kind + == BankFile::SIGNATURE_DATA_BLOCK); + } + + if (mHeader->waveBlockOffset) + { + mWaveBlock = static_cast( + 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(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( + ut::AddOffsetToPtr(reinterpret_cast(refOffset), + reinterpret_cast(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( + 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 // ARRAY_LENGTH +#include // 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 // ULONG_MAX +#include + +#include +#include + +#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(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 // ATTR_UNUSED +#include // 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 +#include + +#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(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(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(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 +#include + +#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(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(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 + +#include + +/******************************************************************************* + * 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 +#include // NW4R_RANGE_FOR +#include // 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(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 + +#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(ut::RoundUp(buffer, 4)); + objSize = ut::RoundUp(objSize, 4); + + u32 numObjects = (size - (ptr - static_cast(buffer))) / objSize; + + for (u32 i = 0; i < numObjects; i++, ptr += objSize) + { + PoolImpl *head = reinterpret_cast(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(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(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 + +#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(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(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 // std::memcpy +#include + +#include + +#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 +#include + +#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 +#else +#include +#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(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(&arg); + break; + + default: + commandArg1 = *reinterpret_cast(&arg); + break; + } + + CommandProc(track, cmd, commandArg1, commandArg2); + break; + } + + case 0x90: + if (doExecCommand) + CommandProc(track, cmd, commandArg1, commandArg2); + + break; + + case 0xe0: + commandArg1 = + static_cast(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( + 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( + 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(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(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(commandArg1) / 128.0f; + break; + + case MML_SET_LFO_SPEED: + trackParam.lfoParam.speed = + static_cast(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(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(-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(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(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 + +#include "nw4r/snd/InstancePool.h" +#include "nw4r/snd/MmlSeqTrack.h" +#include "nw4r/snd/SeqTrack.h" + +#include + +/******************************************************************************* + * 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(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 // NULL + +#include + +#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 + +#include // NW4R_FILE_VERSION +#include + +#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(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(seqData); + mDataBlock = static_cast(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(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( + 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 +#include + +#include "nw4r/snd/BasicPlayer.h" +#include "nw4r/snd/DisposeCallbackManager.h" +#include "nw4r/snd/NoteOnCallback.h" +#include "nw4r/snd/SeqTrack.h" +#include "nw4r/snd/SeqTrackAllocator.h" +#include "nw4r/snd/SoundThread.h" + +#include "nw4r/ut/Lock.h" // ut::AutoInterruptLock + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * types + */ + +// forward declarations +namespace nw4r { namespace snd { namespace detail { class Channel; }}} + +/******************************************************************************* + * variables + */ + +namespace nw4r { namespace snd { namespace detail +{ + // .bss + s16 SeqPlayer::mGlobalVariable[GLOBAL_VARIABLE_NUM]; +}}} // namespace nw4r::snd::detail + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +void SeqPlayer::InitSeqPlayer() +{ + for (int variableNo = 0; variableNo < GLOBAL_VARIABLE_NUM; variableNo++) + mGlobalVariable[variableNo] = VARIABLE_DEFAULT_VALUE; +} + +SeqPlayer::SeqPlayer() : + mActiveFlag (false), + mStartedFlag (false), + mPauseFlag (false), + mReleasePriorityFixFlag (false), + mTempoRatio (1.0f), + mTickFraction (0.0f), + mSkipTickCounter (0), + mSkipTimeCounter (0.0f) +{ + mPanRange = 1.0f; + mTickCounter = 0; + mVoiceOutCount = 0; + + mSeqUserprocCallback = nullptr; + mSeqUserprocCallbackArg = nullptr; + + mParserParam.tempo = DEFAULT_TEMPO; + mParserParam.timebase = DEFAULT_TIMEBASE; + mParserParam.volume = 127; + mParserParam.priority = 64; + mParserParam.callback = nullptr; + + for (int varNo = 0; varNo < PLAYER_VARIABLE_NUM; varNo++) + mLocalVariable[varNo] = VARIABLE_DEFAULT_VALUE; + + for (int trackNo = 0; trackNo < TRACK_NUM_PER_PLAYER; trackNo++) + mTracks[trackNo] = nullptr; +} + +SeqPlayer::~SeqPlayer() +{ + Shutdown(); +} + +void SeqPlayer::InitParam(int voiceOutCount, NoteOnCallback *callback) +{ + BasicPlayer::InitParam(); + + mStartedFlag = false; + mPauseFlag = false; + mTempoRatio = 1.0f; + mSkipTickCounter = 0; + mSkipTimeCounter = 0.0f; + mPanRange = 1.0f; + mTickCounter = 0; + mVoiceOutCount = voiceOutCount; + + mParserParam.tempo = DEFAULT_TEMPO; + mParserParam.timebase = DEFAULT_TIMEBASE; + mParserParam.volume = 127; + mParserParam.priority = 64; + mParserParam.callback = callback; + + mTickFraction = 0.0f; + + for (int varNo = 0; varNo < PLAYER_VARIABLE_NUM; varNo++) + mLocalVariable[varNo] = VARIABLE_DEFAULT_VALUE; + + for (int trackNo = 0; trackNo < TRACK_NUM_PER_PLAYER; trackNo++) + mTracks[trackNo] = nullptr; +} + +SeqPlayer::SetupResult SeqPlayer::Setup(SeqTrackAllocator *trackAllocator, + u32 allocTracks, int voiceOutCount, + NoteOnCallback *callback) +{ + SoundThread::AutoLock lock; + + SeqPlayer::Stop(); // NOTE: qualified name to inhibit dynamic dispatch + InitParam(voiceOutCount, callback); + + { + ut::AutoInterruptLock lockIntr; + + int trackCount = 0; + + // popcnt, pretty sure + for (u32 trackBitMask = allocTracks; trackBitMask; trackBitMask >>= 1) + { + if (trackBitMask & 1) + trackCount++; + } + + if (trackCount > trackAllocator->GetAllocatableTrackCount()) + return SETUP_ERR_CANNOT_ALLOCATE_TRACK; + + u32 trackBitMask = allocTracks; + + // popcnt again, pretty sure + for (int trackNo = 0; trackBitMask; trackBitMask >>= 1, trackNo++) + { + if (trackBitMask & 1) + { + SeqTrack *track = trackAllocator->AllocTrack(this); + NW4RAssertPointerNonnull_Line(199, track); + + SetPlayerTrack(trackNo, track); + } + } + + // end of lockIntr's scope + } + + DisposeCallbackManager::GetInstance().RegisterDisposeCallback(this); + + mSeqTrackAllocator = trackAllocator; + mActiveFlag = true; + + return SETUP_SUCCESS; +} + +void SeqPlayer::Shutdown() +{ + SoundThread::AutoLock lock; + + FinishPlayer(); +} + +void SeqPlayer::SetSeqData(void const *seqBase, s32 seqOffset) +{ + SoundThread::AutoLock lock; + + SeqTrack *seqTrack = GetPlayerTrack(0); + NW4RAssertPointerNonnull_Line(245, seqTrack); + + if (seqBase) + { + seqTrack->SetSeqData(seqBase, seqOffset); + seqTrack->Open(); + } +} + +bool SeqPlayer::Start() +{ + SoundThread::AutoLock lock; + + SoundThread::GetInstance().RegisterPlayerCallback(this); + mStartedFlag = true; + + return true; +} + +void SeqPlayer::Stop() +{ + SoundThread::AutoLock lock; + + FinishPlayer(); +} + +void SeqPlayer::Pause(bool flag) +{ + SoundThread::AutoLock lock; + + mPauseFlag = flag; + + SeqTrack *track; // declared before trackNo in ketteiban dwarf + + for (int trackNo = 0; trackNo < TRACK_NUM_PER_PLAYER; trackNo++) + { + track = GetPlayerTrack(trackNo); + + if (track) + track->PauseAllChannel(flag); + } +} + +void SeqPlayer::Skip(OffsetType offsetType, int offset) +{ + SoundThread::AutoLock lock; + + if (!mActiveFlag) + return; + + switch (offsetType) + { + case OFFSET_TYPE_TICK: + mSkipTickCounter += offset; + break; + + case OFFSET_TYPE_MILLISEC: + mSkipTimeCounter += offset; + break; + } +} + +// SeqPlayer::SetTempoRatio ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13781) +DECOMP_FORCE(NW4RAssert_String(tempoRatio >= 0.0f)); + +void SeqPlayer::SetChannelPriority(int priority) +{ + // specifically not the source variant + NW4RAssertHeaderClampedLRValue_Line(358, priority, 0, 127); + + mParserParam.priority = priority; +} + +void SeqPlayer::SetReleasePriorityFix(bool fix) +{ + mReleasePriorityFixFlag = fix; +} + +void SeqPlayer::SetSeqUserprocCallback(SeqUserprocCallback *callback, void *arg) +{ + mSeqUserprocCallback = callback; + mSeqUserprocCallbackArg = arg; +} + +void SeqPlayer::CallSeqUserprocCallback(u16 procId, SeqTrack *track) +{ + if (!mSeqUserprocCallback) + return; + + NW4RAssertPointerNonnull_Line(377, track); + + SeqTrack::ParserTrackParam &trackParam = track->GetParserTrackParam(); + + SeqUserprocCallbackParam param; + param.localVariable = GetVariablePtr(0); + param.globalVariable = GetVariablePtr(16); + param.trackVariable = track->GetVariablePtr(0); + param.cmpFlag = trackParam.cmpFlag; + + (*mSeqUserprocCallback)(procId, ¶m, mSeqUserprocCallbackArg); + + trackParam.cmpFlag = param.cmpFlag; +} + +// SeqPlayer::GetLocalVariable? maybe all of them? +DECOMP_FORCE(NW4RAssertHeaderClampedLValue_String(varNo)); + +// SeqPlayer::SetTrackMute ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13791) +DECOMP_FORCE(&SeqTrack::SetMute); + +// SeqPlayer::SetTrackSilence ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13792) +DECOMP_FORCE(&SeqTrack::SetSilence); + +// SeqPlayer::SetTrackVolume ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13793) +DECOMP_FORCE(&SeqTrack::SetVolume); +DECOMP_FORCE(NW4RAssert_String(volume >= 0.0f)); + +// SeqPlayer::SetTrackPitch ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13794) +DECOMP_FORCE(&SeqTrack::SetPitch); +DECOMP_FORCE(NW4RAssert_String(pitch >= 0.0f)); + +// SeqPlayer::SetTrackPan ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13795) +DECOMP_FORCE(&SeqTrack::SetPan); + +// SeqPlayer::SetTrackSurroundPan ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13796) +DECOMP_FORCE(&SeqTrack::SetSurroundPan); + +// SeqPlayer::SetTrackLpfFreq ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13797) +DECOMP_FORCE(&SeqTrack::SetLpfFreq); + +// SeqPlayer::SetTrackBiquadFilter ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13798) +DECOMP_FORCE(&SeqTrack::SetBiquadFilter); + +// SeqPlayer::SetTrackPanRange ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13799) +DECOMP_FORCE(&SeqTrack::SetPanRange); + +// SeqPlayer::SetTrackModDepth ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13800) +DECOMP_FORCE(&SeqTrack::SetModDepth); + +// SeqPlayer::SetTrackModSpeed ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13801) +DECOMP_FORCE(&SeqTrack::SetModSpeed); + +void SeqPlayer::InvalidateData(void const *start, void const *end) +{ + SoundThread::AutoLock lock; + + if (mActiveFlag) + { + for (int trackNo = 0; trackNo < TRACK_NUM_PER_PLAYER; trackNo++) + { + SeqTrack *track = GetPlayerTrack(trackNo); + if (!track) + continue; + + byte_t const *cur = track->GetParserTrackParam().baseAddr; + if (start <= cur && cur <= end) + { + // NOTE: qualified name to inhibit dynamic dispatch + SeqPlayer::Stop(); + break; + } + } + } +} + +SeqTrack *SeqPlayer::GetPlayerTrack(int trackNo) +{ + if (trackNo > TRACK_NUM_PER_PLAYER - 1) + return nullptr; + + return mTracks[trackNo]; +} + +void SeqPlayer::CloseTrack(int trackNo) +{ + SoundThread::AutoLock lock; + + // specifically not the source variant + NW4RAssertHeaderClampedLValue_Line(609, trackNo, 0, TRACK_NUM_PER_PLAYER); + + SeqTrack *track = GetPlayerTrack(trackNo); + if (!track) + return; + + track->Close(); + + mSeqTrackAllocator->FreeTrack(mTracks[trackNo]); + mTracks[trackNo] = nullptr; +} + +void SeqPlayer::SetPlayerTrack(int trackNo, SeqTrack *track) +{ + SoundThread::AutoLock lock; + + if (trackNo > TRACK_NUM_PER_PLAYER - 1) + return; + + mTracks[trackNo] = track; + track->SetPlayerTrackNo(trackNo); +} + +void SeqPlayer::FinishPlayer() +{ + SoundThread::AutoLock lock; + + if (mStartedFlag) + { + SoundThread::GetInstance().UnregisterPlayerCallback(this); + mStartedFlag = false; + } + + if (mActiveFlag) + { + DisposeCallbackManager::GetInstance().UnregisterDisposeCallback(this); + mActiveFlag = false; + } + + for (int trackNo = 0; trackNo < TRACK_NUM_PER_PLAYER; trackNo++) + CloseTrack(trackNo); +} + +void SeqPlayer::UpdateChannelParam() +{ + SoundThread::AutoLock lock; + + SeqTrack *track; // declared before trackNo in ketteiban dwarf + + for (int trackNo = 0; trackNo < TRACK_NUM_PER_PLAYER; trackNo++) + { + track = GetPlayerTrack(trackNo); + + if (track) + track->UpdateChannelParam(); + } +} + +BOOL SeqPlayer::ParseNextTick(bool doNoteOn) +{ + SoundThread::AutoLock lock; + + bool activeFlag = false; + + for (int trackNo = 0; trackNo < TRACK_NUM_PER_PLAYER; trackNo++) + { + SeqTrack *track = GetPlayerTrack(trackNo); + if (!track) + continue; + + track->UpdateChannelLength(); + + if (track->ParseNextTick(doNoteOn) < 0) + CloseTrack(trackNo); + + if (track->IsOpened()) + activeFlag = true; + } + + if (!activeFlag) + return TRUE; + + return FALSE; +} + +s16 volatile *SeqPlayer::GetVariablePtr(int varNo) +{ + // specifically not the source variant + NW4RAssertHeaderClampedLRValue_Line( + 746, varNo, 0, PLAYER_VARIABLE_NUM + GLOBAL_VARIABLE_NUM); + + if (varNo < PLAYER_VARIABLE_NUM) + return &mLocalVariable[varNo]; + + if (varNo < PLAYER_VARIABLE_NUM + GLOBAL_VARIABLE_NUM) + return &mGlobalVariable[varNo - PLAYER_VARIABLE_NUM]; + + return nullptr; +} + +void SeqPlayer::Update() +{ + SoundThread::AutoLock lock; + + NW4RAssert_Line(772, mActiveFlag); + + if (!mActiveFlag) + return; + + if (!mStartedFlag) + return; + + if (mSkipTickCounter || mSkipTimeCounter > 0.0f) + SkipTick(); + else if (!mPauseFlag) + UpdateTick(3); + + UpdateChannelParam(); +} + +void SeqPlayer::UpdateTick(int msec) +{ + f32 tickPerMsec = CalcTickPerMsec(); + if (tickPerMsec == 0.0f) + return; + + f32 restMsec = static_cast(msec); + f32 nextMsec = mTickFraction / tickPerMsec; + + while (nextMsec < restMsec) + { + restMsec -= nextMsec; + + if (ParseNextTick(true)) + { + FinishPlayer(); + return; + } + + mTickCounter++; + + tickPerMsec = CalcTickPerMsec(); + if (tickPerMsec == 0.0f) + return; + + nextMsec = 1.0f / tickPerMsec; + } + + nextMsec -= restMsec; + mTickFraction = nextMsec * tickPerMsec; +} + +void SeqPlayer::SkipTick() +{ + for (int trackNo = 0; trackNo < TRACK_NUM_PER_PLAYER; trackNo++) + { + SeqTrack *track = GetPlayerTrack(trackNo); + + if (track) + { + track->ReleaseAllChannel(127); + track->FreeAllChannel(); + } + } + + // TODO: can combine into for loop? + int skipCount = 0; + while (mSkipTickCounter || mSkipTimeCounter * CalcTickPerMsec() >= 1.0f) + { + if (skipCount >= MAX_SKIP_TICK_PER_FRAME) + return; + + if (mSkipTickCounter) + { + mSkipTickCounter--; + } + else + { + f32 tickPerMsec = CalcTickPerMsec(); + NW4RAssert_Line(856, tickPerMsec > 0.0f); + + f32 msecPerTick = 1.0f / tickPerMsec; + + mSkipTimeCounter -= msecPerTick; + } + + if (ParseNextTick(false)) + { + FinishPlayer(); + return; + } + + skipCount++; + mTickCounter++; + } + + mSkipTimeCounter = 0.0f; +} + +Channel *SeqPlayer::NoteOn(int bankNo, NoteOnInfo const ¬eOnInfo) +{ + Channel *channel = mParserParam.callback->NoteOn(this, bankNo, noteOnInfo); + + return channel; +} + +}}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_SeqSound.cpp b/src/nw4r/snd/snd_SeqSound.cpp index b6c65099..15152a92 100644 --- a/src/nw4r/snd/snd_SeqSound.cpp +++ b/src/nw4r/snd/snd_SeqSound.cpp @@ -1 +1,306 @@ -#include "nw4r/snd/snd_SeqSound.h" +#include "nw4r/snd/SeqSound.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_SeqSound.cpp + */ + +/******************************************************************************* + * headers + */ + +#include +#include + +#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 // DVD_ECANCELED +#else +#include +#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 *manager, int priority, + int ambientPriority) : +#if 1 + BasicSound (priority, ambientPriority), + mTempSpecialHandle ( + reinterpret_cast(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(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(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(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 // 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 +#include + +#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(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(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(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(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(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(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(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(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(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 + +#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 + +#include + +#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 + +#include +#include +#include + +#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 + +/******************************************************************************* + * 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( + soundArchiveData); + } +} + +bool SoundArchiveFileReader::IsValidFileHeader(void const *soundArchiveData) +{ + ut::BinaryFileHeader const *fileHeader = + static_cast(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(stringChunk); + NW4RAssert_Line(102, symbolBlock->blockHeader.kind + == SoundArchiveFile::SIGNATURE_SYMB_BLOCK); + + SoundArchiveFile::StringBlock const *stringBlock = + &symbolBlock->stringBlock; + + mStringBase = stringBlock; + + mStringTable = static_cast( + GetPtrConst(mStringBase, stringBlock->stringChunk.tableOffset)); + + mStringTreeSound = static_cast( + GetPtrConst(mStringBase, stringBlock->stringChunk.soundTreeOffset)); + + mStringTreePlayer = static_cast( + GetPtrConst(mStringBase, stringBlock->stringChunk.playerTreeOffset)); + + mStringTreeGroup = static_cast( + GetPtrConst(mStringBase, stringBlock->stringChunk.groupTreeOffset)); + + mStringTreeBank = static_cast( + 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(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( + soundCommonInfo->soundType); + } + else + { + soundType = static_cast( + 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(src->panMode); + info->panCurve = static_cast(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 *)(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 *)(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( + 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(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( + 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 + +#include +#include +#include + +#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(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(buf) - static_cast(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(buf); + mSoundPlayerCount = playerCount; + + byte_t *ptr = static_cast(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(*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(*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(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(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 +Sound *SoundArchivePlayer::AllocSound( + detail::SoundInstanceManager *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(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_GetFileAddress(commonInfo->fileId)); + seqOffset = info->dataOffset; + + if (externalSeqStartLabel) + { + detail::SeqFileReader seqFileReader(seqFile); + + bool result = seqFileReader.ReadOffsetByLabel(externalSeqStartLabel, + &seqOffset); + if (!result) + return START_ERR_INVALID_SEQ_START_LOCATION_LABEL; + } + } + + if (!seqFile) + { + detail::PlayerHeap *heap = sound->GetPlayerHeap(); + if (!heap) + return START_ERR_NOT_DATA_LOADED; + + void *fileStreamBuffer = sound->GetFileStreamBuffer(); + s32 fileStreamBufferSize = sound->GetFileStreamBufferSize(); + NW4RAssertPointerNonnull_Line(1477, fileStreamBuffer); + + fileStream = mSoundArchive->detail_OpenFileStream( + commonInfo->fileId, fileStreamBuffer, fileStreamBufferSize); + if (!fileStream) + return START_ERR_CANNOT_OPEN_FILE; + + if (heap->GetFreeSize() < fileStream->GetSize()) + { + fileStream->Close(); + return SoundStartable::START_ERR_NOT_ENOUGH_PLAYER_HEAP; + } + } + + detail::SeqPlayer::SetupResult result = + sound->Setup(mSeqTrackAllocator, allocTrack, &mSeqCallback); + + while (result != detail::SeqPlayer::SETUP_SUCCESS) + { + if (result == detail::SeqPlayer::SETUP_ERR_CANNOT_ALLOCATE_TRACK) + { + if (mSeqSoundInstanceManager.GetActiveCount() == 1) + { + if (fileStream) + fileStream->Close(); + + NW4RCheckMessage_Line(1520, + !detail::Debug_GetWarningFlag( + DEBUG_WARNING_NOT_ENOUGH_SEQTRACK), + "Failed to start sound (id:%d) for not " + "enough SeqTrack instance.", + sound->GetId()); + + return START_ERR_NOT_ENOUGH_INSTANCE; + } + + detail::SeqSound *lowest = + mSeqSoundInstanceManager.GetLowestPrioritySound(); + + if (sound == lowest) + { + if (fileStream) + fileStream->Close(); + + NW4RCheckMessage_Line(1536, + !detail::Debug_GetWarningFlag( + DEBUG_WARNING_NOT_ENOUGH_SEQTRACK), + "Failed to start sound (id:%d) for not " + "enough SeqTrack instance.", + sound->GetId()); + + return START_ERR_NOT_ENOUGH_INSTANCE; + } + + NW4RCheckMessage_Line( + 1545, + !detail::Debug_GetWarningFlag( + DEBUG_WARNING_NOT_ENOUGH_SEQTRACK), + "Sound (id:%d) is stopped for not enough SeqTrack instance.", + lowest->GetId()); + + lowest->Stop(0); + result = sound->Setup(mSeqTrackAllocator, allocTrack, + &mSeqCallback); + } + else + { + return SoundStartable::START_ERR_UNKNOWN; + } + } + + UpdateCommonSoundParam(sound, commonInfo); + + sound->SetChannelPriority(info->channelPriority); + sound->SetReleasePriorityFix(info->releasePriorityFixFlag); + sound->SetSeqUserprocCallback(mSeqUserprocCallback, + mSeqUserprocCallbackArg); + + detail::SeqPlayer::OffsetType seqOffsetType; + switch (startOffsetType) + { + case SoundStartable::StartInfo::START_OFFSET_TYPE_MILLISEC: + seqOffsetType = detail::SeqPlayer::OFFSET_TYPE_MILLISEC; + break; + + case SoundStartable::StartInfo::START_OFFSET_TYPE_TICK: + seqOffsetType = detail::SeqPlayer::OFFSET_TYPE_TICK; + break; + + case SoundStartable::StartInfo::START_OFFSET_TYPE_SAMPLE: + seqOffsetType = detail::SeqPlayer::OFFSET_TYPE_TICK; + startOffset = 0; + break; + + default: + seqOffsetType = detail::SeqPlayer::OFFSET_TYPE_TICK; + startOffset = 0; + break; + } + + if (seqFile) + { + detail::SeqFileReader seqFileReader(seqFile); + + sound->Prepare(seqFileReader.GetBaseAddress(), seqOffset, seqOffsetType, + startOffset); + } + else + { + sound->Prepare(fileStream, seqOffset, seqOffsetType, startOffset); + } + + return SoundStartable::START_SUCCESS; +} + +SoundStartable::StartResult SoundArchivePlayer::PrepareStrmImpl( + detail::StrmSound *sound, SoundArchive::SoundInfo const *commonInfo, + SoundArchive::StrmSoundInfo const *info, + SoundStartable::StartInfo::StartOffsetType startOffsetType, int startOffset) +{ + detail::StrmPlayer::SetupResult setupResult = sound->Setup( + &mStrmBufferPool, info->allocChannelCount, info->allocTrackFlag); + + while (setupResult != detail::StrmPlayer::SETUP_SUCCESS) + { + if (setupResult == detail::StrmPlayer::SETUP_ERR_CANNOT_ALLOCATE_BUFFER) + { + if (mStrmSoundInstanceManager.GetActiveCount() == 1) + { + NW4RCheckMessage_Line(1648, + !detail::Debug_GetWarningFlag( + DEBUG_WARNING_NOT_ENOUGH_SEQTRACK), + "Failed to start sound (id:%d) for not " + "enough StrmChannel instance.", + sound->GetId()); + + return START_ERR_NOT_ENOUGH_INSTANCE; + } + + detail::StrmSound *lowest = + mStrmSoundInstanceManager.GetLowestPrioritySound(); + if (sound == lowest) + { + NW4RCheckMessage_Line(1660, + !detail::Debug_GetWarningFlag( + DEBUG_WARNING_NOT_ENOUGH_SEQTRACK), + "Failed to start sound (id:%d) for not " + "enough StrmChannel instance.", + sound->GetId()); + + return START_ERR_NOT_ENOUGH_INSTANCE; + } + + NW4RCheckMessage_Line( + 1669, + !detail::Debug_GetWarningFlag( + DEBUG_WARNING_NOT_ENOUGH_STRMCHANNEL), + "Sound (id:%d) is stopped for not enough StrmChannel instance.", + lowest->GetId()); + + lowest->Stop(0); + setupResult = + sound->Setup(&mStrmBufferPool, info->allocChannelCount, + info->allocTrackFlag); + } + else + { + return SoundStartable::START_ERR_UNKNOWN; + } + } + + detail::StrmPlayer::StartOffsetType strmStartOffsetType; + switch (startOffsetType) + { + case SoundStartable::StartInfo::START_OFFSET_TYPE_MILLISEC: + strmStartOffsetType = detail::StrmPlayer::START_OFFSET_TYPE_MILLISEC; + break; + + case SoundStartable::StartInfo::START_OFFSET_TYPE_TICK: + strmStartOffsetType = detail::StrmPlayer::START_OFFSET_TYPE_SAMPLE; + startOffset = 0; + break; + + case SoundStartable::StartInfo::START_OFFSET_TYPE_SAMPLE: + strmStartOffsetType = detail::StrmPlayer::START_OFFSET_TYPE_SAMPLE; + break; + + default: + strmStartOffsetType = detail::StrmPlayer::START_OFFSET_TYPE_SAMPLE; + startOffset = 0; + break; + } + + void *fileStreamBuffer = sound->GetFileStreamBuffer(); + s32 fileStreamBufferSize = sound->GetFileStreamBufferSize(); + NW4RAssertPointerNonnull_Line(1704, fileStreamBuffer); + + ut::FileStream *fileStream = mSoundArchive->detail_OpenFileStream( + commonInfo->fileId, fileStreamBuffer, fileStreamBufferSize); + if (!fileStream) + return START_ERR_CANNOT_OPEN_FILE; + + bool result = sound->Prepare(strmStartOffsetType, startOffset, fileStream); + if (!result) + return START_ERR_UNKNOWN; + + UpdateCommonSoundParam(sound, commonInfo); + + return SoundStartable::START_SUCCESS; +} + +SoundStartable::StartResult SoundArchivePlayer::PrepareWaveSoundImpl( + detail::WaveSound *sound, SoundArchive::SoundInfo const *commonInfo, + SoundArchive::WaveSoundInfo const *info, + SoundStartable::StartInfo::StartOffsetType startOffsetType, int startOffset) +{ + NW4RAssertPointerNonnull_Line(1751, info); + + void const *wsdData = detail_GetFileAddress(commonInfo->fileId); + if (!wsdData) + return START_ERR_NOT_DATA_LOADED; + + detail::WsdPlayer::StartOffsetType wsdStartOffsetType; + switch (startOffsetType) + { + case SoundStartable::StartInfo::START_OFFSET_TYPE_MILLISEC: + wsdStartOffsetType = detail::WsdPlayer::START_OFFSET_TYPE_MILLISEC; + break; + + case SoundStartable::StartInfo::START_OFFSET_TYPE_TICK: + wsdStartOffsetType = detail::WsdPlayer::START_OFFSET_TYPE_SAMPLE; + startOffset = 0; + break; + + case SoundStartable::StartInfo::START_OFFSET_TYPE_SAMPLE: + wsdStartOffsetType = detail::WsdPlayer::START_OFFSET_TYPE_SAMPLE; + break; + + default: + wsdStartOffsetType = detail::WsdPlayer::START_OFFSET_TYPE_SAMPLE; + startOffset = 0; + break; + } + + bool result = + sound->Prepare(wsdData, info->subNo, wsdStartOffsetType, startOffset, + &mWsdCallback, commonInfo->fileId); + if (!result) + return START_ERR_UNKNOWN; + + UpdateCommonSoundParam(sound, commonInfo); + + sound->SetChannelPriority(info->channelPriority); + sound->SetReleasePriorityFix(info->releasePriorityFixFlag); + + return SoundStartable::START_SUCCESS; +} + +void SoundArchivePlayer::UpdateCommonSoundParam( + detail::BasicSound *sound, SoundArchive::SoundInfo const *commonInfo) +{ + NW4RAssertPointerNonnull_Line(1814, sound); + NW4RAssertPointerNonnull_Line(1815, commonInfo); + + sound->SetInitialVolume(commonInfo->volume / 127.0f); + sound->SetRemoteFilter(commonInfo->remoteFilter); + sound->SetPanMode(commonInfo->panMode); + sound->SetPanCurve(commonInfo->panCurve); +} + +/* SoundArchivePlayer::LoadGroup(u32, SoundMemoryAllocatable *, u32) + * ([R89JEL]:/bin/RVL/Debug/mainD.MAP:14234) + */ +DECOMP_FORCE(NW4RAssertAligned_String(loadBlockSize, 32)); + +void SoundArchivePlayer::InvalidateData(void const *start, void const *end) +{ + if (mFileTable) + { + for (int i = 0; i < mFileTable->count; i++) + { + void const *addr = mFileTable->item[i].address; + + if (start <= addr && addr <= end) + mFileTable->item[i].address = nullptr; + } + } + + if (mGroupTable) + { + for (int i = 0; i < mGroupTable->count; i++) + { + void const *addr = mGroupTable->item[i].address; + + if (start <= addr && addr <= end) + mGroupTable->item[i].address = nullptr; + } + } +} + +void SoundArchivePlayer::InvalidateWaveData(void const *start, void const *end) +{ + if (mFileTable) + { + for (int i = 0; i < mFileTable->count; i++) + { + void const *addr = mFileTable->item[i].waveDataAddress; + + if (start <= addr && addr <= end) + mFileTable->item[i].waveDataAddress = nullptr; + } + } + + if (mGroupTable) + { + for (int i = 0; i < mGroupTable->count; i++) + { + void const *addr = mGroupTable->item[i].waveDataAddress; + + if (start <= addr && addr <= end) + mGroupTable->item[i].waveDataAddress = nullptr; + } + } +} + +detail::Channel *SoundArchivePlayer::SeqNoteOnCallback::NoteOn( + detail::SeqPlayer *seqPlayer, int bankNo ATTR_UNUSED, + detail::NoteOnInfo const ¬eOnInfo) +{ + if (!mSoundArchivePlayer.IsAvailable()) + return nullptr; + + SoundArchive const &sndArc = mSoundArchivePlayer.GetSoundArchive(); + u32 soundId = seqPlayer->GetId(); + + SoundArchive::SeqSoundInfo seqInfo; + if (!sndArc.ReadSeqSoundInfo(soundId, &seqInfo)) + return nullptr; + + SoundArchive::BankInfo bankInfo; + if (!sndArc.ReadBankInfo(seqInfo.bankId, &bankInfo)) + return nullptr; + + void const *bankData = + mSoundArchivePlayer.detail_GetFileAddress(bankInfo.fileId); + if (!bankData) + return nullptr; + + detail::Bank bank(bankData); + + void const *waveData = + mSoundArchivePlayer.detail_GetFileWaveDataAddress(bankInfo.fileId); + if (!waveData) + return nullptr; + + bank.SetWaveDataAddress(waveData); + + detail::Channel *channel = bank.NoteOn(noteOnInfo); + return channel; +} + +bool SoundArchivePlayer::WsdCallback::GetWaveSoundData( + detail::WaveSoundInfo *info, detail::WaveSoundNoteInfo *noteInfo, + detail::WaveInfo *waveData, void const *waveSoundData, int index, + int noteIndex, register_t userData) const +{ + u32 fileID = userData; + + if (!mSoundArchivePlayer.IsAvailable()) + return false; + + SoundArchive const &sndArc ATTR_UNUSED = + mSoundArchivePlayer.GetSoundArchive(); + + void const *dataAddr = + mSoundArchivePlayer.detail_GetFileWaveDataAddress(fileID); + if (!dataAddr) + return false; + + detail::WsdFileReader reader(waveSoundData); + + if (!reader.ReadWaveSoundInfo(info, index)) + return false; + + if (!reader.ReadWaveSoundNoteInfo(noteInfo, index, noteIndex)) + return false; + + if (!reader.ReadWaveInfo(noteInfo->waveIndex, waveData, dataAddr)) + return false; + + return true; +} + +}} // namespace nw4r::snd diff --git a/src/nw4r/snd/snd_SoundHandle.cpp b/src/nw4r/snd/snd_SoundHandle.cpp index eff7be7d..4483355d 100644 --- a/src/nw4r/snd/snd_SoundHandle.cpp +++ b/src/nw4r/snd/snd_SoundHandle.cpp @@ -1 +1,51 @@ -#include "nw4r/snd/snd_SoundHandle.h" +#include "nw4r/snd/SoundHandle.h" + +/* Original source: + * kiwi515/ogws + * src/nw4r/snd/snd_SoundHandle.cpp + */ + +/******************************************************************************* + * headers + */ + +#include // 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 // INT_MAX + +#include +#include +#include // 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 + +/******************************************************************************* + * 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 // 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 +#include // STR +#include + +#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 // OSRegisterVersion +#include // AXGetMaxVoices +#include +#include +#else +#include +#endif + +#include "nw4r/NW4RAssert.hpp" + +/******************************************************************************* + * macros + */ + +#define BUILDSTAMP_LIB_GROUP_NAME "NW4R " +#define BUILDSTAMP_LIB_NAME "SND" + +// You probably want to change these +#define BUILDSTAMP_BUILD_TYPE "release" +#define BUILDSTAMP_DATE __DATE__ +#define BUILDSTAMP_TIME __TIME__ +#define BUILDSTAMP_CW_MAJOR_REV STR(__CWCC__) +#define BUILDSTAMP_CW_MINOR_REV STR(__CWBUILD__) + +#define BUILDSTAMP_STRING \ + "<< " \ + BUILDSTAMP_LIB_GROUP_NAME " - " BUILDSTAMP_LIB_NAME " \t" \ + BUILDSTAMP_BUILD_TYPE " build: " \ + BUILDSTAMP_DATE " " BUILDSTAMP_TIME \ + " (" BUILDSTAMP_CW_MAJOR_REV "_" BUILDSTAMP_CW_MINOR_REV ")" \ + " >>" + +/******************************************************************************* + * variables + */ + +namespace nw4r { namespace snd +{ + // .data, .sdata + extern "C" char const *NW4R_SND_Version_ = BUILDSTAMP_STRING; + + // .bss + detail::TaskThread SoundSystem::sTaskThread; + + // .sbss + namespace + { + bool sInitialized; + } // unnamed namespace + + int SoundSystem::sMaxVoices; +}} // namespace nw4r::snd + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { + +u32 SoundSystem::GetRequiredMemSize(SoundSystemParam const ¶m) +{ + // could have just used align assert? idk + NW4RAssert_Line(106, param.soundThreadStackSize % 8 == 0); + NW4RAssert_Line(107, param.dvdThreadStackSize % 8 == 0); + + int maxVoices = AXGetMaxVoices(); + + return param.soundThreadStackSize + param.dvdThreadStackSize + + detail::AxVoiceManager::GetInstance().GetRequiredMemSize(maxVoices) + + detail::VoiceManager::GetInstance().GetRequiredMemSize(maxVoices) + + detail::ChannelManager::GetInstance().GetRequiredMemSize(maxVoices); +} + +void SoundSystem::InitSoundSystem(SoundSystemParam const ¶m, void *workMem, + u32 workMemSize) +{ + bool result; // presumably up here + + NW4RAssertAligned_Line(144, workMem, 32); + NW4RAssert_Line(145, workMemSize >= GetRequiredMemSize( param )); + + if (sInitialized) + return; + + sInitialized = true; + + OSRegisterVersion(NW4R_SND_Version_); + + detail::AxManager::GetInstance().Init(); + + SCInit(); + + SCStatus initStatus; + do + initStatus = SCCheckStatus(); + while (initStatus == SC_STATUS_BUSY); + + NW4RAssert_Line(171, initStatus == SC_STATUS_OK); + + SCSoundMode soundMode = SCGetSoundMode(); + switch (soundMode) + { + case SC_SND_MONO: + detail::AxManager::GetInstance().SetOutputMode(OUTPUT_MODE_MONO); + break; + + case SC_SND_STEREO: + detail::AxManager::GetInstance().SetOutputMode(OUTPUT_MODE_STEREO); + break; + + case SC_SND_SURROUND: + detail::AxManager::GetInstance().SetOutputMode(OUTPUT_MODE_DPL2); + break; + + default: + detail::AxManager::GetInstance().SetOutputMode(OUTPUT_MODE_STEREO); + break; + } + + byte_t *ptr = static_cast(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( 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 +#include +#include + +#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 +#include +#include +#include +#else +#include +#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(stack); + + BOOL result = OSCreateThread(&mThread, &SoundThreadFunc, &GetInstance(), + static_cast(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(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 lock(mMutex); + + mPlayerCallbackList.PushBack(callback); +} + +void SoundThread::UnregisterPlayerCallback(PlayerCallback *callback) +{ + ut::detail::AutoLock lock(mMutex); + + mPlayerCallbackList.Erase(callback); +} + +void SoundThread::SoundThreadProc() +{ + OSMessage message; + + while (true) + { + OSReceiveMessage(&mMsgQueue, &message, OS_MESSAGE_FLAG_PERSISTENT); + + if (reinterpret_cast(message) == MESSAGE_AX_CALLBACK) + { + ut::detail::AutoLock 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(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 // std::memset + +#include + +#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(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 +#include + +#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(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(strmData); + mHeadBlock = static_cast( + 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(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 // LONG_MAX +#include // std::memcpy + +#include +#include // ATTR_UNUSED +#include + +#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 +#include +#else +#include +#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(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 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(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(fileStream)) + { + dvdStream->SetPriority(1); + } + + ut::detail::AutoLock 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(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(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(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(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(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(mStrmInfo.blockSamples); + + *startBlockOffset = startOffsetSamples % mStrmInfo.blockSamples; + + return true; +} + +void StrmPlayer::VoiceCallbackFunc(Voice *voice, + Voice::VoiceCallbackStatus status, void *arg) +{ + StrmTrack *track = static_cast(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 // ARRAY_LENGTH +#include + +#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 *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 // 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 +#include // NW4R_RANGE_FOR_NO_AUTO_INC +#include // nullptr + +#include "nw4r/snd/Task.h" + +#include "nw4r/ut/Lock.h" // ut::AutoInterruptLock + +#if 0 +#include +#else +#include +#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(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(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 + +#include "nw4r/snd/TaskManager.h" + +#if 0 +#include +#else +#include +#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(stack) + stackSize, + stackSize, priority, OS_THREAD_NO_FLAGS); + if (!result) + return false; + + mStackEnd = static_cast(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(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 // ARRAY_LENGTH +#include + +#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(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(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(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(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 +#include +#include + +#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 // AX_MAX_VOLUME +#else +#include +#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(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(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 + +#include // NW4R_RANGE_FOR_NO_AUTO_INC +#include + +#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(mem); + + for (int i = 0; i < voiceCount; i++) + { + mFreeVoiceList.PushBack(new (ptr) Voice); + + ptr += sizeof(Voice); + } + + NW4RAssert_Line(64, ptr <= reinterpret_cast( 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 // 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 + +/******************************************************************************* + * 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(waveArc); + + WaveArchive::TableBlock const *tableBlock = + static_cast( + ut::AddOffsetToPtr(fileHeader, fileHeader->tableChunkOffset)); + + NW4RAssert_Line(53, tableBlock->blockHeader.kind + == WaveArchive::SIGNATURE_TABLE_BLOCK); + + WaveArchive::DataBlock const *dataBlock = + static_cast( + 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 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(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 + +#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(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( + 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( + ut::AddOffsetToPtr(mWaveInfo, channelInfoOffset[channelIndex])); + + if (waveChannelInfo->adpcmOffset) + { + WaveFile::AdpcmParamSet const *adpcmParamSet = + static_cast(ut::AddOffsetToPtr( + mWaveInfo, waveChannelInfo->adpcmOffset)); + + channelParam.adpcmParam = adpcmParamSet->adpcmParam; + channelParam.adpcmLoopParam = adpcmParamSet->adpcmLoopParam; + } + + channelParam.dataAddr = const_cast( + 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(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 + +#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 *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 // 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(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 // NW4R_FILE_VERSION +#include // 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(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(wsdData); + + if (mHeader->dataBlockOffset) + { + mDataBlock = static_cast( + ut::AddOffsetToPtr(mHeader, mHeader->dataBlockOffset)); + + NW4RAssert_Line(105, mDataBlock->blockHeader.kind + == WsdFile::SIGNATURE_DATA_BLOCK); + } + + if (mHeader->waveBlockOffset) + { + mWaveBlock = static_cast( + 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(ut::AddOffsetToPtr( + mWaveBlock, mWaveBlock->offsetTable[waveIndex])); + } + else + { + WsdFile::WaveBlockOld const *waveBlockOld = + reinterpret_cast(mWaveBlock); + + waveInfo = + static_cast(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 + +#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(flag); // ??? + + if (IsChannelActive() && flag != mChannel->IsPause()) + mChannel->Pause(flag); +} + +void WsdPlayer::SetChannelPriority(int priority) +{ + // specificallly not the source variant + NW4RAssertHeaderClampedLRValue_Line(229, priority, 0, 127); + + mPriority = priority; +} + +void WsdPlayer::SetReleasePriorityFix(bool fix) +{ + mReleasePriorityFixFlag = fix; +} + +void WsdPlayer::InvalidateData(void const *start, void const *end) +{ + SoundThread::AutoLock lock; + + if (mActiveFlag) + { + void const *current = GetWsdDataAddress(); + + if (start <= current && current <= end) + FinishPlayer(); + } +} + +void WsdPlayer::FinishPlayer() +{ + SoundThread::AutoLock lock; + + if (mStartedFlag) + { + SoundThread::GetInstance().UnregisterPlayerCallback(this); + + mStartedFlag = false; + } + + if (mActiveFlag) + { + DisposeCallbackManager::GetInstance().UnregisterDisposeCallback(this); + + mActiveFlag = false; + } + + CloseChannel(); +} + +u32 WsdPlayer::GetPlaySamplePosition() const +{ + SoundThread::AutoLock lock; + + if (!mChannel) + return -1; + + return mChannel->GetVoice()->GetCurrentPlayingSample(); +} + +void WsdPlayer::Update() +{ + SoundThread::AutoLock lock; + + NW4RAssert_Line(362, mActiveFlag); + if (!mActiveFlag) + return; + + if (!mStartedFlag) + return; + + if (!mPauseFlag) + { + if (mWavePlayFlag && !mChannel) + { + FinishPlayer(); + return; + } + + if (!mWavePlayFlag && !StartChannel(mCallback, mCallbackData)) + { + FinishPlayer(); + return; + } + } + + UpdateChannel(); +} + +bool WsdPlayer::StartChannel(WsdCallback const *callback, + register_t callbackData) +{ + SoundThread::AutoLock lock; + + int priority = DEFAULT_PRIORITY + GetChannelPriority(); + + WaveInfo waveData; + WaveSoundNoteInfo noteInfo; + bool result = + callback->GetWaveSoundData(&mWaveSoundInfo, ¬eInfo, &waveData, + mWsdData, mWsdIndex, 0, callbackData); + if (!result) + return false; + + u32 startOffsetSamples; + if (mStartOffsetType == START_OFFSET_TYPE_SAMPLE) + { + startOffsetSamples = mStartOffset; + } + else if (mStartOffsetType == START_OFFSET_TYPE_MILLISEC) + { + startOffsetSamples = + static_cast(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(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(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(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(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 + +#include + +#if 0 +#include // AXPBADPCM +#else +#include +#endif + +/******************************************************************************* + * functions + */ + +namespace nw4r { namespace snd { namespace detail { + +s16 DecodeDspAdpcm(AXPBADPCM *adpcm, byte_t bits) +{ + s16 yn1 = static_cast(adpcm->yn1); + s16 yn2 = static_cast(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 // byte4_t + +#include + +/******************************************************************************* + * 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. */