diff --git a/.clang-format-ignore b/.clang-format-ignore index 712da2bc..0e5c5447 100644 --- a/.clang-format-ignore +++ b/.clang-format-ignore @@ -1,4 +1,4 @@ # Ignore JSystem Files - Copied From TP **/JSystem/** -# Ignore SND Files - Copied from https://github.com/muff1n1634/nw4r_snd_mid2010 +# Ignore SND Files - Copied from https://github.com/muff1n1634/nw4r_snd_mid2010, https://github.com/kiwi515/ogws **/nw4r/snd/** diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt index 86e155b2..0da61014 100644 --- a/config/SOUE01/symbols.txt +++ b/config/SOUE01/symbols.txt @@ -25202,7 +25202,7 @@ __ct__Q34nw4r3snd15DvdSoundArchiveFv = .text:0x8046BB60; // type:function size:0 __dt__Q34nw4r3snd15DvdSoundArchiveFv = .text:0x8046BBB0; // type:function size:0x8C Open__Q34nw4r3snd15DvdSoundArchiveFPCc = .text:0x8046BC40; // type:function size:0x14C Close__Q34nw4r3snd15DvdSoundArchiveFv = .text:0x8046BD90; // type:function size:0x4C -fn_8046BDE0 = .text:0x8046BDE0; // type:function size:0xA8 +OpenStream__Q34nw4r3snd15DvdSoundArchiveCFPviUlUl = .text:0x8046BDE0; // type:function size:0xA8 OpenExtStream__Q34nw4r3snd15DvdSoundArchiveCFPviPCcUlUl = .text:0x8046BE90; // type:function size:0xB8 detail_GetRequiredStreamBufferSize__Q34nw4r3snd15DvdSoundArchiveCFv = .text:0x8046BF50; // type:function size:0x8 LoadHeader__Q34nw4r3snd15DvdSoundArchiveFPvUl = .text:0x8046BF60; // type:function size:0x88 @@ -25241,7 +25241,7 @@ Alloc__Q44nw4r3snd6detail9FrameHeapFUlPFPvUlPv_vPv = .text:0x8046CEE0; // type:f SaveState__Q44nw4r3snd6detail9FrameHeapFv = .text:0x8046CF90; // type:function size:0xCC LoadState__Q44nw4r3snd6detail9FrameHeapFi = .text:0x8046D060; // type:function size:0x224 GetCurrentLevel__Q44nw4r3snd6detail9FrameHeapCFv = .text:0x8046D290; // type:function size:0xC -FUN_8046d2a0 = .text:0x8046D2A0; // type:function size:0x40 +GetFreeSize__Q44nw4r3snd6detail9FrameHeapCFv = .text:0x8046D2A0; // type:function size:0x40 __ct__Q34nw4r3snd15FxReverbStdDpl2Fv = .text:0x8046D2E0; // type:function size:0xC4 GetRequiredMemSize__Q34nw4r3snd15FxReverbStdDpl2Fv = .text:0x8046D3B0; // type:function size:0x54 AssignWorkBuffer__Q34nw4r3snd15FxReverbStdDpl2FPvUl = .text:0x8046D410; // type:function size:0x8 diff --git a/include/nw4r/snd/snd_AxManager.h b/include/nw4r/snd/snd_AxManager.h index fe7d02d5..e68069a0 100644 --- a/include/nw4r/snd/snd_AxManager.h +++ b/include/nw4r/snd/snd_AxManager.h @@ -102,12 +102,18 @@ namespace nw4r { namespace snd { namespace detail void SetMasterVolume(f32 volume, int frame); void PrepareReset(); + bool IsResetReady() const { + return mResetReadyCounter == 0; + } + private: // cdtors AxManager(); // callbacks static void AxCallbackFunc(); + static void AuxCallbackFunc(void* pChans, void* pContext); + static void AiDmaCallbackFunc(); // static members private: @@ -140,7 +146,7 @@ namespace nw4r { namespace snd { namespace detail MoveValue mMasterVolume; // size 0x10, offset 0x1c MoveValue mMainOutVolume; // size 0x10, offset 0x2c MoveValue mVolumeForReset; // size 0x10, offset 0x3c - AIDMACallback *mOldAidCallback; // size 0x04, offset 0x4c + 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 diff --git a/include/nw4r/snd/snd_AxVoiceManager.h b/include/nw4r/snd/snd_AxVoiceManager.h index 370a4281..5165e8ef 100644 --- a/include/nw4r/snd/snd_AxVoiceManager.h +++ b/include/nw4r/snd/snd_AxVoiceManager.h @@ -47,6 +47,9 @@ namespace nw4r { namespace snd { namespace detail // static members public: static int const VOICE_COUNT_MARGIN = 16; + 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); // members private: diff --git a/include/nw4r/snd/snd_BasicSound.h b/include/nw4r/snd/snd_BasicSound.h index bf09bcbe..eca939cd 100644 --- a/include/nw4r/snd/snd_BasicSound.h +++ b/include/nw4r/snd/snd_BasicSound.h @@ -156,6 +156,13 @@ namespace nw4r { namespace snd { namespace detail // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x276d8 class AmbientParamUpdateCallback { + public: + enum ParamUpdateFlags { + PARAM_UPDATE_VOLUME = (1 << 0), + PARAM_UPDATE_PAN = (1 << 1), + PARAM_UPDATE_SURROUND_PAN = (1 << 2), + PARAM_UPDATE_PRIORITY = (1 << 3), + }; // methods public: virtual void at_0x08(); diff --git a/include/nw4r/snd/snd_Channel.h b/include/nw4r/snd/snd_Channel.h index 553517c3..636d3a7c 100644 --- a/include/nw4r/snd/snd_Channel.h +++ b/include/nw4r/snd/snd_Channel.h @@ -242,6 +242,10 @@ namespace nw4r { namespace snd { namespace detail // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2b895c class ChannelManager { + public: + static const int VOICE_MARGIN = 1; + static const int VOICE_MAX = AX_VOICE_MAX + VOICE_MARGIN; + static const int WORK_SIZE_MAX = VOICE_MAX * sizeof(Channel); // methods public: // instance accessors diff --git a/include/nw4r/snd/snd_ChannelManager.h b/include/nw4r/snd/snd_ChannelManager.h index d3234dd3..956cb13d 100644 --- a/include/nw4r/snd/snd_ChannelManager.h +++ b/include/nw4r/snd/snd_ChannelManager.h @@ -9,38 +9,6 @@ namespace nw4r { namespace snd { namespace detail { -class ChannelManager { - friend class Channel; // Alloc/Free intended through Channel only - -public: - static const int VOICE_MARGIN = 1; - static const int VOICE_MAX = AX_VOICE_MAX + VOICE_MARGIN; - static const int WORK_SIZE_MAX = VOICE_MAX * sizeof(Channel); - -public: - static ChannelManager &GetInstance(); - - u32 GetRequiredMemSize(); - - void Setup(void *pWork, u32 workSize); - void Shutdown(); - void UpdateAllChannel(); - -private: - ChannelManager(); - - Channel *Alloc(); - void Free(Channel *pChannel); - -private: - InstancePool mPool; // at 0x0 - ChannelList mChannelList; // at 0x4 - bool mInitialized; // at 0x10 - u32 mChannelCount; // at 0x14 - void *mMem; // at 0x18 - u32 mMemSize; // at 0x1C -}; - } // namespace detail } // namespace snd } // namespace nw4r diff --git a/include/nw4r/snd/snd_DvdSoundArchive.h b/include/nw4r/snd/snd_DvdSoundArchive.h index e6a6eeb7..c59e73b5 100644 --- a/include/nw4r/snd/snd_DvdSoundArchive.h +++ b/include/nw4r/snd/snd_DvdSoundArchive.h @@ -70,29 +70,6 @@ namespace nw4r { namespace snd DVDFileInfo mFileInfo; // at 0x14C bool mOpen; // at 0x188 }; - - 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 #endif // NW4R_SND_DVD_SOUND_ARCHIVE_H diff --git a/include/nw4r/snd/snd_ExternalSoundPlayer.h b/include/nw4r/snd/snd_ExternalSoundPlayer.h index 870caa40..1fd2a9ba 100644 --- a/include/nw4r/snd/snd_ExternalSoundPlayer.h +++ b/include/nw4r/snd/snd_ExternalSoundPlayer.h @@ -19,8 +19,11 @@ namespace nw4r { namespace snd { namespace detail // methods public: // methods + ExternalSoundPlayer(); + ~ExternalSoundPlayer(); int GetPlayingSoundCount() const { return mSoundList.GetSize(); } int GetPlayableSoundCount() const { return mPlayableCount; } + void SetPlayableSoundCount(int count); bool AppendSound(BasicSound *sound); void RemoveSound(BasicSound *sound); diff --git a/include/nw4r/snd/snd_RemoteSpeaker.h b/include/nw4r/snd/snd_RemoteSpeaker.h index 09b393b4..f4460f3a 100644 --- a/include/nw4r/snd/snd_RemoteSpeaker.h +++ b/include/nw4r/snd/snd_RemoteSpeaker.h @@ -76,17 +76,17 @@ private: bool mFirstEncodeFlag; // at 0x3 bool mValidCallbackFlag; // at 0x4 bool mCommandBusyFlag; // at 0x5 - bool mForceResumeFlag; // at 0x6 - bool mContinueFlag; // at 0x7 // TODO commenting out a random flag to make eggAudioRmtSpeakerMgr match // TODO offsets are wrong as a result - // volatile bool mIntervalFlag; // at 0x8 + // bool mForceResumeFlag; // at 0x6 + bool mContinueFlag; // at 0x7 + volatile bool mIntervalFlag; // at 0x8 SpeakerState mState; // at 0xC SpeakerCommand mUserCommand; // at 0x10 SpeakerCommand mInternalCommand; // at 0x14 WENCInfo mEncodeInfo; // at 0x18 int mChannelIndex; // at 0x38 - WPADCallback mWpadCallback; // at 0x3C + WPADCallback *mWpadCallback; // at 0x3C OSAlarm mContinueAlarm; // at 0x40 OSAlarm mIntervalAlarm; // at 0x70 s64 mContinueBeginTime; // at 0xA0 diff --git a/include/nw4r/snd/snd_RemoteSpeakerManager.h b/include/nw4r/snd/snd_RemoteSpeakerManager.h index a00eb327..79ebd56d 100644 --- a/include/nw4r/snd/snd_RemoteSpeakerManager.h +++ b/include/nw4r/snd/snd_RemoteSpeakerManager.h @@ -21,7 +21,7 @@ public: private: static const int SPEAKER_ALARM_HZ = 150; - // static const int SPEAKER_ALARM_PERIOD_NSEC = 1.0f / SPEAKER_ALARM_HZ * 1000 * 1000 * 1000; + static const int SPEAKER_ALARM_PERIOD_NSEC = static_cast(1.0f / SPEAKER_ALARM_HZ * 1000 * 1000 * 1000); private: RemoteSpeakerManager(); diff --git a/include/nw4r/snd/snd_SeqPlayer.h b/include/nw4r/snd/snd_SeqPlayer.h index 037df652..50467dfa 100644 --- a/include/nw4r/snd/snd_SeqPlayer.h +++ b/include/nw4r/snd/snd_SeqPlayer.h @@ -128,6 +128,7 @@ namespace nw4r { namespace snd { namespace detail SeqTrack *GetPlayerTrack(int trackNo); s16 volatile *GetVariablePtr(int varNo); + void SetTempoRatio(f32 tempo); void SetReleasePriorityFix(bool fix); void SetChannelPriority(int priority); void SetSeqUserprocCallback(SeqUserprocCallback *callback, void *arg); diff --git a/include/nw4r/snd/snd_SeqSound.h b/include/nw4r/snd/snd_SeqSound.h index 9bc43214..0960cbb9 100644 --- a/include/nw4r/snd/snd_SeqSound.h +++ b/include/nw4r/snd/snd_SeqSound.h @@ -108,6 +108,7 @@ namespace nw4r { namespace snd { namespace detail s32 GetFileStreamBufferSize() { return sizeof mFileStreamBuffer; } void SetReleasePriorityFix(bool fix); + void SetTempoRatio(f32 tempo); void SetChannelPriority(int priority); void SetSeqUserprocCallback(SeqPlayer::SeqUserprocCallback *callback, void *arg); diff --git a/include/nw4r/snd/snd_Sound3DManager.h b/include/nw4r/snd/snd_Sound3DManager.h index 465e5ea3..2ec2204c 100644 --- a/include/nw4r/snd/snd_Sound3DManager.h +++ b/include/nw4r/snd/snd_Sound3DManager.h @@ -15,7 +15,7 @@ class Sound3DManager : public detail::BasicSound::AmbientInfo::AmbientParamUpdat public: struct Sound3DActorParam { u32 userParam; // at 0x0 - SoundParam soundParam; // at 0x4 + SoundArchive::Sound3DParam soundParam; // at 0x4 math::VEC3 position; // at 0xC Sound3DActorParam(); diff --git a/include/nw4r/snd/snd_SoundArchive.h b/include/nw4r/snd/snd_SoundArchive.h index ed586295..76b63469 100644 --- a/include/nw4r/snd/snd_SoundArchive.h +++ b/include/nw4r/snd/snd_SoundArchive.h @@ -119,6 +119,12 @@ namespace nw4r { namespace snd /* 3 bytes padding */ }; // size 0x0c + struct Sound3DParam { + u32 flags; // at 0x0 + u8 decayCurve; // at 0x4 + u8 decayRatio; // at 0x5 + }; + // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x256dc struct SoundArchivePlayerInfo { @@ -170,7 +176,11 @@ namespace nw4r { namespace snd bool IsAvailable() const; SoundType GetSoundType(u32 soundId) const; + const char* GetSoundLabelString(u32 id) const; u32 ConvertLabelStringToSoundId(char const *label) const; + u32 ConvertLabelStringToPlayerId(const char* pLabel) const; + u32 ConvertLabelStringToGroupId(const char* pLabel) const; + u32 GetSoundUserParam(u32 id) const; bool ReadSoundInfo(u32 soundId, SoundInfo *info) const; bool ReadSeqSoundInfo(u32 soundId, SeqSoundInfo *info) const; @@ -192,6 +202,12 @@ namespace nw4r { namespace snd ut::FileStream *detail_OpenFileStream(u32 fileId, void *buffer, int size) const; + ut::FileStream* detail_OpenGroupStream(u32 id, void* pBuffer, + int bufferSize) const; + ut::FileStream* detail_OpenGroupWaveDataStream(u32 id, void* pBuffer, + int bufferSize) const; + + void SetExternalFileRoot(const char* pExtFileRoot); private: ut::FileStream *OpenExtStreamImpl(void *buffer, int size, diff --git a/include/nw4r/snd/snd_SoundArchiveFile.h b/include/nw4r/snd/snd_SoundArchiveFile.h index 9ef9e60c..3e05a24a 100644 --- a/include/nw4r/snd/snd_SoundArchiveFile.h +++ b/include/nw4r/snd/snd_SoundArchiveFile.h @@ -35,6 +35,8 @@ namespace nw4r { namespace snd { namespace detail u32 fileImageSize; // size 0x04, offset 0x24 }; // size 0x28 + static const int HEADER_AREA_SIZE = ROUND_UP(sizeof(Header), 32) + 40; + /* SymbolBlock */ // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x24881 @@ -332,9 +334,17 @@ namespace nw4r { namespace snd { namespace detail u32 ConvertLabelStringToId(SoundArchiveFile::StringTree const *tree, char const *str) const; - u32 ConvertLabelStringToSoundId(char const *label) const - { - return ConvertLabelStringToId(mStringTreeSound, label); + const char* GetSoundLabelString(u32 id) const; + u32 GetSoundUserParam(u32 id) const; + + 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); } private: diff --git a/include/nw4r/snd/snd_SoundArchivePlayer.h b/include/nw4r/snd/snd_SoundArchivePlayer.h index 3fba7118..34231be7 100644 --- a/include/nw4r/snd/snd_SoundArchivePlayer.h +++ b/include/nw4r/snd/snd_SoundArchivePlayer.h @@ -184,7 +184,9 @@ namespace nw4r { namespace snd u32 GetRequiredMemSize(SoundArchive const *arc); u32 GetRequiredStrmBufferSize(SoundArchive const *arc); void const *GetGroupAddress(u32 groupId) const; + void SetGroupAddress(u32 id, const void* pAddr); void const *GetGroupWaveDataAddress(u32 groupId) const; + void SetGroupWaveDataAddress(u32 id, const void* pAddr); void const *GetFileAddress(u32 fileId) const; void const *GetFileWaveDataAddress(u32 fileId) const; void const *detail_GetFileAddress(u32 fileId) const; diff --git a/include/nw4r/snd/snd_SoundHandle.h b/include/nw4r/snd/snd_SoundHandle.h index e9673ca7..19040155 100644 --- a/include/nw4r/snd/snd_SoundHandle.h +++ b/include/nw4r/snd/snd_SoundHandle.h @@ -27,6 +27,7 @@ namespace nw4r { namespace snd ~SoundHandle() { DetachSound(); } // methods + void detail_AttachSoundAsTempHandle(detail::BasicSound* pSound); void detail_AttachSound(detail::BasicSound *sound); bool IsAttachedSound() const { return mSound != nullptr; } detail::BasicSound *detail_GetAttachedSound() { return mSound; } diff --git a/include/nw4r/snd/snd_Voice.h b/include/nw4r/snd/snd_Voice.h index 260acbdd..32e6a690 100644 --- a/include/nw4r/snd/snd_Voice.h +++ b/include/nw4r/snd/snd_Voice.h @@ -42,6 +42,17 @@ namespace nw4r { namespace snd { namespace detail CALLBACK_STATUS_DROP_DSP, }; + 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), + }; + // typedefs public: typedef ut::LinkList LinkList; diff --git a/include/nw4r/snd/snd_VoiceManager.h b/include/nw4r/snd/snd_VoiceManager.h index 3a691772..3e434b2a 100644 --- a/include/nw4r/snd/snd_VoiceManager.h +++ b/include/nw4r/snd/snd_VoiceManager.h @@ -18,6 +18,9 @@ namespace nw4r { namespace snd { namespace detail // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2ffb36 class VoiceManager { + public: + static const int VOICE_MAX = AX_VOICE_MAX; + static const int WORK_SIZE_MAX = VOICE_MAX * sizeof(Voice); // methods public: // instance accessors diff --git a/include/rvl/AX/AXAux.h b/include/rvl/AX/AXAux.h index 2830f23a..dc2cba4d 100644 --- a/include/rvl/AX/AXAux.h +++ b/include/rvl/AX/AXAux.h @@ -17,6 +17,27 @@ extern "C" { #define AX_SAMPLES_PER_FRAME_RMT 18 #define AX_FRAME_SIZE (AX_SAMPLES_PER_FRAME * AX_SAMPLE_DEPTH_BYTES) +/** + * Stereo: Left, Right, Surround + * DPL2: Left, Right, Left Surround, Right Surround + */ +typedef enum { + AX_STEREO_L, + AX_STEREO_R, + AX_STEREO_S, + + AX_STEREO_MAX +} AXStereoChannel; + +typedef enum { + AX_DPL2_L, + AX_DPL2_R, + AX_DPL2_LS, + AX_DPL2_RS, + + AX_DPL2_MAX +} AXDPL2Channel; + typedef void (*AXAuxCallback)(void *chans, void *context); void __AXAuxInit(void); diff --git a/include/rvl/WPAD/WPAD.h b/include/rvl/WPAD/WPAD.h index 683b0e2a..d62c7d45 100644 --- a/include/rvl/WPAD/WPAD.h +++ b/include/rvl/WPAD/WPAD.h @@ -32,8 +32,8 @@ enum WPADResult_et { WPAD_ERR_OK = 0, WPAD_ERR_NO_CONTROLLER = -1, /* name known from asserts */ - WPAD_ERR_COMMUNICATION_ERROR = -2, // [RT3P54] has this as WPAD_ERR_BUSY - WPAD_ERR_3 = -3, // [RT3P54] has this as WPAD_ERR_TRANSFER + WPAD_ERR_BUSY = -2, // [RT3P54] has this as WPAD_ERR_BUSY + WPAD_ERR_TRANSFER = -3, // [RT3P54] has this as WPAD_ERR_TRANSFER WPAD_ERR_INVALID = -4, /* name comes from [R89JEL] */ // WPAD_ERR_5 = -5, /* unknown */ // WPAD_ERR_6 = -6, /* unknown */ @@ -260,8 +260,8 @@ enum WPADMotorCommand_et { // WPADControlSpeaker typedef u32 WPADSpeakerCommand; enum WPADSpeakerCommand_et { - WPAD_SPEAKER_DISABLE = 0, - WPAD_SPEAKER_ENABLE = 1, // might be ON? see HBMRemoteSpk.cpp + WPAD_SPEAKER_OFF = 0, + WPAD_SPEAKER_ON = 1, // might be ON? see HBMRemoteSpk.cpp WPAD_SPEAKER_MUTE = 2, WPAD_SPEAKER_UNMUTE = 3, WPAD_SPEAKER_PLAY = 4, // figured out from HBM usage diff --git a/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/cstring.h b/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/cstring.h index e7bdf55e..4b35acdf 100644 --- a/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/cstring.h +++ b/src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/cstring.h @@ -8,6 +8,9 @@ inline size_t strlen(const char *str) { return ::strlen(str); } +using ::strncpy; +using ::strncat; + inline char *strcpy(char *dest, const char *src) { return ::strcpy(dest, src); } diff --git a/src/nw4r/snd/snd_AxManager.cpp b/src/nw4r/snd/snd_AxManager.cpp index 1acaeb7c..1cb2c4b1 100644 --- a/src/nw4r/snd/snd_AxManager.cpp +++ b/src/nw4r/snd/snd_AxManager.cpp @@ -306,6 +306,14 @@ OutputMode AxManager::GetOutputMode() return mOutputMode; } +void AxManager::SetMasterVolume(f32 volume, int frame) { + mMasterVolume.SetTarget(ut::Clamp(volume, 0.0f, 1.0f), (frame + 2) / 3); + + if (frame == 0) { + VoiceManager::GetInstance().UpdateAllVoicesSync(Voice::SYNC_AX_VE); + } +} + void AxManager::SetMainOutVolume(f32 volume, int frames) { volume = ut::Clamp(volume, 0.0f, 1.0f); @@ -326,6 +334,73 @@ void AxManager::AxCallbackFunc() (*GetInstance().mNextAxRegisterCallback)(); } +bool AxManager::AppendEffect(AuxBus bus, FxBase* pFx) { + if (!mAuxFadeVolume[bus].IsFinished()) { + ShutdownEffect(bus); + } + + mAuxFadeVolume[bus].SetTarget(1.0f, 0); + + switch (bus) { + case AUX_A: { + AXSetAuxAReturnVolume(AX_MAX_VOLUME); + break; + } + + case AUX_B: { + AXSetAuxBReturnVolume(AX_MAX_VOLUME); + break; + } + + case AUX_C: { + AXSetAuxCReturnVolume(AX_MAX_VOLUME); + break; + } + } + + if (!pFx->StartUp()) { + return false; + } + + ut::AutoInterruptLock lock; + + if (GetEffectList(bus).IsEmpty()) { + switch (bus) { + case AUX_A: { + AXRegisterAuxACallback(AuxCallbackFunc, + reinterpret_cast(bus)); + break; + } + + case AUX_B: { + AXRegisterAuxBCallback(AuxCallbackFunc, + reinterpret_cast(bus)); + break; + } + + case AUX_C: { + AXRegisterAuxCCallback(AuxCallbackFunc, + reinterpret_cast(bus)); + break; + } + } + + mAuxCallbackWaitCounter[bus] = 2; + } + + GetEffectList(bus).PushBack(pFx); + return true; +} + +void AxManager::ClearEffect(AuxBus bus, int frame) { + if (frame == 0) { + ShutdownEffect(bus); + return; + } + + mAuxFadeVolume[bus].SetTarget(0.0f, (frame + 2) / 3); +} + void AxManager::ShutdownEffect(AuxBus bus) { ut::AutoInterruptLock lock; @@ -365,4 +440,77 @@ void AxManager::SetBiquadFilterCallback(int type, sBiquadFilterCallbackTable[type] = biquad; } +void AxManager::AuxCallbackFunc(void* pChans, void* pContext) { + int num; + void* buffer[AX_DPL2_MAX]; + + void** ppChans = static_cast(pChans); + AuxBus bus = static_cast(reinterpret_cast(pContext)); + + if (GetInstance().GetOutputMode() == OUTPUT_MODE_DPL2) { + num = AX_DPL2_MAX; + + buffer[AX_DPL2_L] = ppChans[AX_DPL2_L]; + buffer[AX_DPL2_R] = ppChans[AX_DPL2_R]; + buffer[AX_DPL2_LS] = ppChans[AX_DPL2_LS]; + buffer[AX_DPL2_RS] = ppChans[AX_DPL2_RS]; + } else { + num = AX_STEREO_MAX; + + buffer[AX_STEREO_L] = ppChans[AX_STEREO_L]; + buffer[AX_STEREO_R] = ppChans[AX_STEREO_R]; + buffer[AX_STEREO_S] = ppChans[AX_STEREO_S]; + } + + if (GetInstance().mAuxCallbackWaitCounter[bus] > 0) { + GetInstance().mAuxCallbackWaitCounter[bus]--; + + for (int i = 0; i < num; i++) { + std::memset(buffer[i], 0, FX_BUFFER_SIZE); + } + } else if (GetInstance().GetEffectList(bus).IsEmpty()) { + for (int i = 0; i < num; i++) { + std::memset(buffer[i], 0, FX_BUFFER_SIZE); + } + } else { + for (FxBase::LinkList::Iterator it = + GetInstance().GetEffectList(bus).GetBeginIter(); + it != GetInstance().GetEffectList(bus).GetEndIter(); ++it) { + + it->UpdateBuffer(num, buffer, FX_BUFFER_SIZE, FX_SAMPLE_FORMAT, + FX_SAMPLE_RATE, GetInstance().GetOutputMode()); + } + } +} + +void AxManager::PrepareReset() { + if (mOldAidCallback != NULL) { + return; + } + + mVolumeForReset.SetTarget(0.0f, 3); + mResetReadyCounter = -1; + mOldAidCallback = AIRegisterDMACallback(AiDmaCallbackFunc); +} + +void AxManager::AiDmaCallbackFunc() { + static bool finishedFlag = false; + + AxManager& r = GetInstance(); + r.mOldAidCallback(); + + if (finishedFlag) { + if (r.mResetReadyCounter < 0) { + AXSetMaxDspCycles(0); + r.mResetReadyCounter = AUX_CALLBACK_WAIT_FRAME; + } + } else if (r.mVolumeForReset.GetValue() == 0.0f) { + finishedFlag = true; + } + + if (r.mResetReadyCounter > 0) { + r.mResetReadyCounter--; + } +} + }}} // namespace nw4r::snd::detail diff --git a/src/nw4r/snd/snd_AxfxImpl.cpp b/src/nw4r/snd/snd_AxfxImpl.cpp index 0ba51228..ba4a6394 100644 --- a/src/nw4r/snd/snd_AxfxImpl.cpp +++ b/src/nw4r/snd/snd_AxfxImpl.cpp @@ -1 +1,57 @@ #include "nw4r/snd/snd_AxfxImpl.h" + +#include "nw4r/ut/ut_algorithm.h" + +namespace nw4r { +namespace snd { +namespace detail { + +AxfxImpl* AxfxImpl::mCurrentFx = NULL; +u32 AxfxImpl::mAllocatedSize = 0; + +bool AxfxImpl::CreateHeap(void* pBuffer, u32 size) { + mHeap = MEMCreateFrmHeap(pBuffer, size); + return mHeap != NULL; +} + +void AxfxImpl::DestroyHeap() { + if (mHeap != NULL) { + MEMDestroyFrmHeap(mHeap); + } +} + +void AxfxImpl::HookAlloc(AXFXAllocHook* pAllocHook, AXFXFreeHook* pFreeHook) { + AXFXGetHooks(pAllocHook, pFreeHook); + AXFXSetHooks(Alloc, Free); + mCurrentFx = this; +} + +void AxfxImpl::RestoreAlloc(AXFXAllocHook allocHook, AXFXFreeHook freeHook) { + AXFXSetHooks(allocHook, freeHook); + mCurrentFx = NULL; +} + +void* AxfxImpl::Alloc(u32 size) { + void* pBlock = MEMAllocFromFrmHeap(mCurrentFx->mHeap, size); + + mCurrentFx->mAllocCount++; + mAllocatedSize += ut::RoundUp(size, 4); + + return pBlock; +} + +void AxfxImpl::Free(void* pBlock) { +#pragma unused(pBlock) + + if (mCurrentFx->mAllocCount != 0) { + mCurrentFx->mAllocCount--; + } + + if (mCurrentFx->mAllocCount == 0) { + MEMFreeToFrmHeap(mCurrentFx->mHeap, MEM_FRM_HEAP_FREE_ALL); + } +} + +} // namespace detail +} // namespace snd +} // namespace nw4r diff --git a/src/nw4r/snd/snd_DvdSoundArchive.cpp b/src/nw4r/snd/snd_DvdSoundArchive.cpp index 17187007..43a69d2e 100644 --- a/src/nw4r/snd/snd_DvdSoundArchive.cpp +++ b/src/nw4r/snd/snd_DvdSoundArchive.cpp @@ -1,6 +1,235 @@ /* Only implemented to the extent necessary to match data sections. */ #include "nw4r/snd/snd_DvdSoundArchive.h" +#include "nw4r/ut/ut_DvdLockedFileStream.h" +#include +#include -nw4r::snd::DvdSoundArchive::DvdSoundArchive() {} -nw4r::snd::DvdSoundArchive::DvdFileStream::DvdFileStream() {} +namespace nw4r { +namespace snd { + +class DvdSoundArchive::DvdFileStream : public ut::DvdLockedFileStream { +public: + DvdFileStream(const DVDFileInfo* pFileInfo, u32 offset, u32 size); + DvdFileStream(s32 entrynum, u32 offset, u32 size); + + virtual s32 Read(void* pDst, u32 size); // at 0x14 + virtual void Seek(s32 offset, u32 origin); // at 0x44 + + virtual u32 Tell() const { + return ut::DvdFileStream::Tell() - mOffset; + } // at 0x58 + + virtual u32 GetSize() const { + return mSize; + } // at 0x40 + +private: + s32 mOffset; // at 0x70 + s32 mSize; // at 0x74 +}; + +DvdSoundArchive::DvdSoundArchive() : mOpen(false) {} + +DvdSoundArchive::~DvdSoundArchive() { + Close(); +} + +bool DvdSoundArchive::Open(s32 entrynum) { + if (!DVDFastOpen(entrynum, &mFileInfo)) { + return false; + } + + mOpen = true; + return LoadFileHeader(); +} + +bool DvdSoundArchive::Open(const char* pPath) { + s32 entrynum = DVDConvertPathToEntrynum(pPath); + if (entrynum < 0) { + return false; + } + + if (!Open(entrynum)) { + return false; + } + + char extRoot[FILE_PATH_MAX]; + for (int i = std::strlen(pPath) - 1; i >= 0; i--) { + if (pPath[i] == '/' || pPath[i] == '\\') { + // @bug Long path can overflow extRoot buffer + std::strncpy(extRoot, pPath, i); + extRoot[i] = '\0'; + + SetExternalFileRoot(extRoot); + break; + } + } + + return true; +} + +void DvdSoundArchive::Close() { + DVDClose(&mFileInfo); + mOpen = false; + Shutdown(); +} + +ut::FileStream* DvdSoundArchive::OpenStream(void* pBuffer, int size, u32 offset, + u32 length) const { + if (!mOpen) { + return NULL; + } + + if (size < sizeof(DvdFileStream)) { + return NULL; + } + + return new (pBuffer) DvdFileStream(&mFileInfo, offset, length); +} + +ut::FileStream* DvdSoundArchive::OpenExtStream(void* pBuffer, int size, + const char* pExtPath, u32 offset, + u32 length) const { + if (!mOpen) { + return NULL; + } + + if (size < sizeof(DvdFileStream)) { + return NULL; + } + + s32 entrynum = DVDConvertPathToEntrynum(pExtPath); + if (entrynum < 0) { + return NULL; + } + + return new (pBuffer) DvdFileStream(entrynum, offset, length); +} + +int DvdSoundArchive::detail_GetRequiredStreamBufferSize() const { + return sizeof(DvdFileStream); +} + +bool DvdSoundArchive::LoadFileHeader() { + u8 headerArea[detail::SoundArchiveFile::HEADER_AREA_SIZE]; + + static const u32 headerAlignSize = + ut::RoundUp(sizeof(detail::SoundArchiveFile::Header), 32); + + void* pFile = ut::RoundUp(headerArea, 32); + + s32 bytesRead = + DVDReadPrio(&mFileInfo, pFile, headerAlignSize, 0, DVD_PRIO_MEDIUM); + + if (bytesRead != headerAlignSize) { + return false; + } + + mFileReader.Init(pFile); + Setup(&mFileReader); + + return true; +} + +bool DvdSoundArchive::LoadHeader(void* pBuffer, u32 size) { + u32 infoSize = mFileReader.GetInfoChunkSize(); + s32 infoOffset = mFileReader.GetInfoChunkOffset(); + + if (size < infoSize) { + return false; + } + + s32 bytesRead = + DVDReadPrio(&mFileInfo, pBuffer, infoSize, infoOffset, DVD_PRIO_MEDIUM); + + if (bytesRead != infoSize) { + return false; + } + + mFileReader.SetInfoChunk(pBuffer, infoSize); + return true; +} + +bool DvdSoundArchive::LoadLabelStringData(void* pBuffer, u32 size) { + u32 labelSize = mFileReader.GetLabelStringChunkSize(); + s32 labelOffset = mFileReader.GetLabelStringChunkOffset(); + + if (size < labelSize) { + return false; + } + + s32 bytesRead = DVDReadPrio(&mFileInfo, pBuffer, labelSize, labelOffset, + DVD_PRIO_MEDIUM); + + if (bytesRead != labelSize) { + return false; + } + + mFileReader.SetStringChunk(pBuffer, labelSize); + return true; +} + +DvdSoundArchive::DvdFileStream::DvdFileStream(const DVDFileInfo* pFileInfo, + u32 offset, u32 size) + : DvdLockedFileStream(pFileInfo, false), mOffset(offset), mSize(size) { + if (mSize == 0) { + mSize = ut::DvdFileStream::GetSize(); + } + + ut::DvdFileStream::Seek(mOffset, SEEKORG_BEG); +} + +DvdSoundArchive::DvdFileStream::DvdFileStream(s32 entrynum, u32 offset, + u32 size) + : DvdLockedFileStream(entrynum), mOffset(offset), mSize(size) { + if (mSize == 0) { + mSize = ut::DvdFileStream::GetSize(); + } + + ut::DvdFileStream::Seek(mOffset, SEEKORG_BEG); +} + +s32 DvdSoundArchive::DvdFileStream::Read(void* pDst, u32 size) { + u32 endOffset = mOffset + mSize; + u32 startOffset = ut::DvdFileStream::Tell(); + + if (startOffset + size > endOffset) { + size = ut::RoundUp(endOffset - ut::DvdFileStream::Tell(), 32); + } + + return DvdLockedFileStream::Read(pDst, size); +} + +void DvdSoundArchive::DvdFileStream::Seek(s32 offset, u32 origin) { + switch (origin) { + case SEEKORG_BEG: { + offset += mOffset; + break; + } + + case SEEKORG_CUR: { + offset += ut::DvdFileStream::Tell(); + break; + } + + case SEEKORG_END: { + offset = mOffset + mSize - offset; + break; + } + + default: { + return; + } + } + + if (offset < mOffset) { + offset = mOffset; + } else if (offset > mOffset + mSize) { + offset = mOffset + mSize; + } + + ut::DvdFileStream::Seek(offset, SEEKORG_BEG); +} + +}} diff --git a/src/nw4r/snd/snd_ExternalSoundPlayer.cpp b/src/nw4r/snd/snd_ExternalSoundPlayer.cpp index af7c95f3..2cd0c7e7 100644 --- a/src/nw4r/snd/snd_ExternalSoundPlayer.cpp +++ b/src/nw4r/snd/snd_ExternalSoundPlayer.cpp @@ -16,6 +16,7 @@ #include "nw4r/snd/snd_SoundThread.h" #include "nw4r/NW4RAssert.hpp" +#include "nw4r/snd/snd_global.h" /******************************************************************************* * types @@ -30,6 +31,17 @@ namespace nw4r { namespace ut { struct LinkListNode; }} namespace nw4r { namespace snd { namespace detail { +ExternalSoundPlayer::ExternalSoundPlayer() : mPlayableCount(1) {} + +ExternalSoundPlayer::~ExternalSoundPlayer() { + NW4R_RANGE_FOR_NO_AUTO_INC(it, mSoundList) + { + decltype(it) curItr = it++; + + curItr->DetachExternalSoundPlayer(this); + } +} + // not sure which one uses this exactly, maybe StopAllSound? DECOMP_FORCE_CLASS_METHOD( BasicSound::ExtSoundPlayerPlayLinkList, @@ -64,6 +76,14 @@ bool ExternalSoundPlayer::AppendSound(BasicSound *sound) return true; } +void ExternalSoundPlayer::SetPlayableSoundCount(int count) { + mPlayableCount = count; + + while (GetPlayingSoundCount() > GetPlayableSoundCount()) { + GetLowestPrioritySound()->Shutdown(); + } +} + void ExternalSoundPlayer::RemoveSound(BasicSound *sound) { mSoundList.Erase(sound); diff --git a/src/nw4r/snd/snd_FrameHeap.cpp b/src/nw4r/snd/snd_FrameHeap.cpp index 187b2b84..5c67fdad 100644 --- a/src/nw4r/snd/snd_FrameHeap.cpp +++ b/src/nw4r/snd/snd_FrameHeap.cpp @@ -1 +1,136 @@ #include "nw4r/snd/snd_FrameHeap.h" +#include + +namespace nw4r { +namespace snd { +namespace detail { + +FrameHeap::FrameHeap() : mHandle(NULL) {} + +FrameHeap::~FrameHeap() { + if (IsValid()) { + Destroy(); + } +} + +bool FrameHeap::Create(void* pBase, u32 size) { + if (IsValid()) { + Destroy(); + } + + void* pEnd = static_cast(pBase) + size; + pBase = ut::RoundUp(pBase, 4); + if (pBase > pEnd) { + return false; + } + + mHandle = MEMCreateFrmHeap(pBase, ut::GetOffsetFromPtr(pBase, pEnd)); + if (mHandle == NULL) { + return false; + } + + if (!NewSection()) { + return false; + } + + return true; +} + +void FrameHeap::Destroy() { + if (!IsValid()) { + return; + } + + ClearSection(); + MEMFreeToFrmHeap(mHandle, MEM_FRM_HEAP_FREE_ALL); + + MEMDestroyFrmHeap(mHandle); + mHandle = NULL; +} + +void FrameHeap::Clear() { + ClearSection(); + MEMFreeToFrmHeap(mHandle, MEM_FRM_HEAP_FREE_ALL); + + NewSection(); +} + +void* FrameHeap::Alloc(u32 size, FreeCallback pCallback, void* pCallbackArg) { + void* pBuffer = MEMAllocFromFrmHeapEx( + mHandle, BLOCK_BUFFER_SIZE + ut::RoundUp(size, HEAP_ALIGN), HEAP_ALIGN); + + if (pBuffer == NULL) { + return NULL; + } + + Block* pBlock = new (pBuffer) Block(size, pCallback, pCallbackArg); + mSectionList.GetBack().AppendBlock(pBlock); + + return pBlock->GetBufferAddr(); +} + +int FrameHeap::SaveState() { + if (!MEMRecordStateForFrmHeap(mHandle, mSectionList.GetSize())) { + return -1; + } + + if (!NewSection()) { + MEMFreeByStateToFrmHeap(mHandle, 0); + return -1; + } + + return GetCurrentLevel(); +} + +void FrameHeap::LoadState(int id) { + if (id == 0) { + Clear(); + return; + } + + while (id < static_cast(mSectionList.GetSize())) { + Section& rSection = mSectionList.GetBack(); + rSection.~Section(); + mSectionList.Erase(&rSection); + } + + MEMFreeByStateToFrmHeap(mHandle, id); + MEMRecordStateForFrmHeap(mHandle, mSectionList.GetSize()); + + NewSection(); +} + +int FrameHeap::GetCurrentLevel() const { + return mSectionList.GetSize() - 1; +} + +u32 FrameHeap::GetFreeSize() const { + u32 freeSize = MEMGetAllocatableSizeForFrmHeapEx(mHandle, HEAP_ALIGN); + if (freeSize < BLOCK_BUFFER_SIZE) { + return 0; + } + + return ut::RoundDown(freeSize - BLOCK_BUFFER_SIZE, HEAP_ALIGN); +} + +bool FrameHeap::NewSection() { + void* pSection = MEMAllocFromFrmHeap(mHandle, sizeof(Section)); + if (pSection == NULL) { + return false; + } + + mSectionList.PushBack(new (pSection) Section()); + return true; +} + +void FrameHeap::ClearSection() { + while (!mSectionList.IsEmpty()) { + Section& rSection = mSectionList.GetBack(); + rSection.~Section(); + mSectionList.Erase(&rSection); + } +} + +} // namespace detail +} // namespace snd +} // namespace nw4r diff --git a/src/nw4r/snd/snd_NandSoundArchive.cpp b/src/nw4r/snd/snd_NandSoundArchive.cpp index 36fad331..e37db9d9 100644 --- a/src/nw4r/snd/snd_NandSoundArchive.cpp +++ b/src/nw4r/snd/snd_NandSoundArchive.cpp @@ -1 +1,264 @@ #include "nw4r/snd/snd_NandSoundArchive.h" +#include +#include + +namespace nw4r { +namespace snd { + +class NandSoundArchive::NandFileStream : public ut::NandFileStream { +public: + NandFileStream(const NANDFileInfo* pFileInfo, u32 offset, u32 size); + NandFileStream(const char* pPath, u32 offset, u32 size); + + virtual s32 Read(void* pDst, u32 size); // at 0x14 + virtual void Seek(s32 offset, u32 origin); // at 0x44 + + virtual u32 Tell() const { + return ut::NandFileStream::Tell() - mOffset; + } // at 0x58 + + virtual u32 GetSize() const { + return mSize; + } // at 0x40 + +private: + s32 mOffset; // at 0x16C + s32 mSize; // at 0x170 +}; + +NandSoundArchive::NandSoundArchive() : mOpen(false) {} + +NandSoundArchive::~NandSoundArchive() { + Close(); +} + +bool NandSoundArchive::Open(const char* pPath) { + if (mOpen) { + Close(); + } + + if (NANDOpen(pPath, &mFileInfo, NAND_ACCESS_READ)) { + return false; + } + + mOpen = true; + + if (!LoadFileHeader()) { + return false; + } + + char currentDir[64]; + NANDGetCurrentDir(currentDir); + u32 currDirLen = std::strlen(currentDir); + + char extRoot[FILE_PATH_MAX]; + std::strncpy(extRoot, currentDir, currDirLen + 1); + + for (int i = std::strlen(pPath) - 1; i >= 0; i--) { + if (pPath[i] == '/' || pPath[i] == '\\') { + // @bug Long path can overflow extRoot buffer + std::strncat(extRoot, pPath, i); + extRoot[currDirLen + i] = '\0'; + break; + } + } + + SetExternalFileRoot(extRoot); + return true; +} + +void NandSoundArchive::Close() { + if (mOpen) { + NANDClose(&mFileInfo); + mOpen = false; + } + + Shutdown(); +} + +ut::FileStream* NandSoundArchive::OpenStream(void* pBuffer, int size, + u32 offset, u32 length) const { + if (!mOpen) { + return NULL; + } + + if (size < sizeof(NandFileStream)) { + return NULL; + } + + return new (pBuffer) NandFileStream(&mFileInfo, offset, length); +} + +ut::FileStream* NandSoundArchive::OpenExtStream(void* pBuffer, int size, + const char* pExtPath, + u32 offset, u32 length) const { + if (!mOpen) { + return NULL; + } + + if (size < sizeof(NandFileStream)) { + return NULL; + } + + NandFileStream* pExtStream = + new (pBuffer) NandFileStream(pExtPath, offset, length); + + if (!pExtStream->IsAvailable()) { + pExtStream->~NandFileStream(); + return NULL; + } + + return pExtStream; +} + +int NandSoundArchive::detail_GetRequiredStreamBufferSize() const { + return sizeof(NandFileStream); +} + +bool NandSoundArchive::LoadFileHeader() { + u8 headerArea[detail::SoundArchiveFile::HEADER_AREA_SIZE]; + + static const u32 headerAlignSize = + ut::RoundUp(sizeof(detail::SoundArchiveFile::Header), 32); + + void* pFile = ut::RoundUp(headerArea, 32); + + if (NANDSeek(&mFileInfo, 0, NAND_SEEK_BEG) != NAND_RESULT_OK) { + return false; + } + + s32 bytesRead = NANDRead(&mFileInfo, pFile, headerAlignSize); + if (bytesRead != headerAlignSize) { + return false; + } + + mFileReader.Init(pFile); + Setup(&mFileReader); + + return true; +} + +bool NandSoundArchive::LoadHeader(void* pBuffer, u32 size) { + u32 infoSize = mFileReader.GetInfoChunkSize(); + + u32 infoOffsetU = mFileReader.GetInfoChunkOffset(); + s32 infoOffset = *reinterpret_cast(&infoOffsetU); + + if (size < infoSize) { + return false; + } + + s32 currOffset = NANDSeek(&mFileInfo, infoOffset, NAND_SEEK_BEG); + if (currOffset != infoOffset) { + return false; + } + + s32 bytesRead = NANDRead(&mFileInfo, pBuffer, infoSize); + if (bytesRead != infoSize) { + return false; + } + + mFileReader.SetInfoChunk(pBuffer, infoSize); + return true; +} + +bool NandSoundArchive::LoadLabelStringData(void* pBuffer, u32 size) { + u32 labelSize = mFileReader.GetLabelStringChunkSize(); + + u32 labelOffsetU = mFileReader.GetLabelStringChunkOffset(); + s32 labelOffset = *reinterpret_cast(&labelOffsetU); + + if (size < labelSize) { + return false; + } + + s32 currOffset = NANDSeek(&mFileInfo, labelOffset, NAND_SEEK_BEG); + if (currOffset != labelOffset) { + return false; + } + + s32 bytesRead = NANDRead(&mFileInfo, pBuffer, labelSize); + if (bytesRead != labelSize) { + return false; + } + + mFileReader.SetStringChunk(pBuffer, labelSize); + return true; +} + +NandSoundArchive::NandFileStream::NandFileStream(const NANDFileInfo* pFileInfo, + u32 offset, u32 size) + : ut::NandFileStream(pFileInfo, NAND_ACCESS_READ, false), + mOffset(offset), + mSize(size) { + + if (IsAvailable()) { + if (mSize == 0) { + mSize = ut::NandFileStream::GetSize(); + } + + ut::NandFileStream::Seek(mOffset, SEEKORG_BEG); + } +} + +NandSoundArchive::NandFileStream::NandFileStream(const char* pPath, u32 offset, + u32 size) + : ut::NandFileStream(pPath, NAND_ACCESS_READ), + mOffset(offset), + mSize(size) { + + if (IsAvailable()) { + if (mSize == 0) { + mSize = ut::NandFileStream::GetSize(); + } + + ut::NandFileStream::Seek(mOffset, SEEKORG_BEG); + } +} + +s32 NandSoundArchive::NandFileStream::Read(void* pDst, u32 size) { + u32 endOffset = mOffset + mSize; + u32 startOffset = ut::NandFileStream::Tell(); + + if (startOffset + size > endOffset) { + size = ut::RoundUp(endOffset - ut::NandFileStream::Tell(), 32); + } + + s32 bytesRead = ut::NandFileStream::Read(pDst, size); + DCStoreRange(pDst, size); + + return bytesRead; +} + +void NandSoundArchive::NandFileStream::Seek(s32 offset, u32 origin) { + switch (origin) { + case SEEKORG_BEG: { + offset += mOffset; + break; + } + + case SEEKORG_CUR: { + offset += ut::NandFileStream::Tell(); + break; + } + + case SEEKORG_END: { + offset = mOffset + mSize - offset; + break; + } + + default: { + return; + } + } + + if (offset < mOffset) { + offset = mOffset; + } else if (offset > mOffset + mSize) { + offset = mOffset + mSize; + } + + ut::NandFileStream::Seek(offset, SEEKORG_BEG); +} + +}} diff --git a/src/nw4r/snd/snd_RemoteSpeaker.cpp b/src/nw4r/snd/snd_RemoteSpeaker.cpp index 0ff3e857..9f97bd70 100644 --- a/src/nw4r/snd/snd_RemoteSpeaker.cpp +++ b/src/nw4r/snd/snd_RemoteSpeaker.cpp @@ -1 +1,379 @@ #include "nw4r/snd/snd_RemoteSpeaker.h" +#include "common.h" +#include "nw4r/snd/snd_RemoteSpeakerManager.h" +#include "nw4r/ut/ut_Lock.h" +#include + +namespace nw4r { +namespace snd { + +RemoteSpeaker::RemoteSpeaker() + : mInitFlag(false), + mPlayFlag(false), + mEnableFlag(false), + mFirstEncodeFlag(false), + mValidCallbackFlag(false), + mCommandBusyFlag(false), + // mForceResumeFlag(false), + mState(STATE_INVALID), + mUserCommand(COMMAND_NONE), + mInternalCommand(COMMAND_NONE), + mWpadCallback(nullptr) { + + OSCreateAlarm(&mContinueAlarm); + OSSetAlarmUserData(&mContinueAlarm, this); + + OSCreateAlarm(&mIntervalAlarm); + OSSetAlarmUserData(&mIntervalAlarm, this); +} + +void RemoteSpeaker::InitParam() { + ClearParam(); + + // mForceResumeFlag = false; + mContinueFlag = false; + mPlayFlag = false; + mEnableFlag = true; + mIntervalFlag = false; +} + +void RemoteSpeaker::ClearParam() { + mPlayFlag = false; + mEnableFlag = false; + + OSCancelAlarm(&mContinueAlarm); + mContinueFlag = false; + + OSCancelAlarm(&mIntervalAlarm); + mIntervalFlag = false; +} + +bool RemoteSpeaker::Setup(WPADCallback pCallback) { + ut::AutoInterruptLock lock; + + InitParam(); + + if (mWpadCallback != NULL) { + mWpadCallback(mChannelIndex, WPAD_ERR_OK); + mValidCallbackFlag = false; + } + + mWpadCallback = pCallback; + mUserCommand = COMMAND_SPEAKER_ON; + mInitFlag = true; + + return true; +} + +void RemoteSpeaker::Shutdown(WPADCallback pCallback) { + ut::AutoInterruptLock lock; + + ClearParam(); + + if (mWpadCallback != NULL) { + mWpadCallback(mChannelIndex, WPAD_ERR_OK); + mValidCallbackFlag = false; + } + + mUserCommand = COMMAND_SPEAKER_OFF; + mWpadCallback = pCallback; + mInitFlag = false; +} + +bool RemoteSpeaker::EnableOutput(bool enable) { + if (!mInitFlag) { + return false; + } + + mEnableFlag = enable; + return true; +} + +bool RemoteSpeaker::IsEnabledOutput() const { + if (!mInitFlag) { + return false; + } + + return mEnableFlag; +} + +void RemoteSpeaker::Update() { + if (mCommandBusyFlag) { + return; + } + + SpeakerCommand command = + mUserCommand != COMMAND_NONE ? mUserCommand : mInternalCommand; + + mUserCommand = COMMAND_NONE; + mInternalCommand = COMMAND_NONE; + + ExecCommand(command); +} + +void RemoteSpeaker::ExecCommand(SpeakerCommand command) { + switch (command) { + case COMMAND_NONE: { + break; + } + + case COMMAND_SPEAKER_ON: { + mValidCallbackFlag = true; + mCommandBusyFlag = true; + mState = STATE_EXEC_SPEAKER_ON; + WPADControlSpeaker(mChannelIndex, WPAD_SPEAKER_ON, SpeakerOnCallback); + break; + } + + case COMMAND_SPEAKER_PLAY: { + mValidCallbackFlag = true; + mCommandBusyFlag = true; + mState = STATE_EXEC_SPEAKER_PLAY; + WPADControlSpeaker(mChannelIndex, WPAD_SPEAKER_PLAY, + SpeakerPlayCallback); + break; + } + + case COMMAND_SPEAKER_OFF: { + mValidCallbackFlag = true; + mCommandBusyFlag = true; + mState = STATE_EXEC_SPEAKER_OFF; + WPADControlSpeaker(mChannelIndex, WPAD_SPEAKER_OFF, SpeakerOffCallback); + break; + } + } +} + +void RemoteSpeaker::UpdateStreamData(const s16* pRmtSamples) { + if (!IsAvailable()) { + return; + } + + bool playFlag = true; + bool silentFlag = mEnableFlag ? IsAllSampleZero(pRmtSamples) : true; + + if (silentFlag /*|| mForceResumeFlag*/) { + playFlag = false; + } + + bool firstFlag = !mPlayFlag && playFlag; + bool lastFlag = mPlayFlag && !playFlag; + + if (playFlag) { + ut::AutoInterruptLock lock; + + if (!WPADCanSendStreamData(mChannelIndex)) { + return; + } + + u32 wencMode = !mFirstEncodeFlag ? WENC_FLAG_USER_INFO : 0; + mFirstEncodeFlag = false; + + u8 adpcmBuffer[SAMPLES_PER_ENCODED_PACKET]; + WENCGetEncodeData(&mEncodeInfo, wencMode, pRmtSamples, + SAMPLES_PER_AUDIO_PACKET, adpcmBuffer); + + s32 result = WPADSendStreamData(mChannelIndex, adpcmBuffer, + SAMPLES_PER_ENCODED_PACKET); + if (result != WPAD_ERR_OK) { + mInternalCommand = COMMAND_SPEAKER_ON; + mState = STATE_INVALID; + InitParam(); + + return; + } + } + + if (firstFlag) { + ut::AutoInterruptLock lock; + + if (!mContinueFlag) { + OSSetAlarm(&mContinueAlarm, + OS_SEC_TO_TICKS(CONTINUOUS_PLAY_INTERVAL_MINUTES * 60LL), + ContinueAlarmHandler); + + mContinueBeginTime = OSGetTime(); + mContinueFlag = true; + } + + OSCancelAlarm(&mIntervalAlarm); + mIntervalFlag = false; + } + + if (lastFlag) { + ut::AutoInterruptLock lock; + + mIntervalFlag = true; + OSCancelAlarm(&mIntervalAlarm); + OSSetAlarm(&mIntervalAlarm, OS_SEC_TO_TICKS(1LL), IntervalAlarmHandler); + } + + mPlayFlag = playFlag; +} + +bool RemoteSpeaker::IsAllSampleZero(const s16* pSample) { + const u32* pBuffer = reinterpret_cast(pSample); + bool zeroFlag = true; + + for (int i = 0; i < SAMPLES_PER_ENCODED_PACKET; i++) { + if (pBuffer[i] != 0) { + zeroFlag = false; + break; + } + } + + return zeroFlag; +} + +void RemoteSpeaker::SpeakerOnCallback(s32 chan, s32 result) { + RemoteSpeaker& r = + detail::RemoteSpeakerManager::GetInstance().GetRemoteSpeaker(chan); + + switch (result) { + case WPAD_ERR_OK: { + r.mFirstEncodeFlag = true; + std::memset(&r.mEncodeInfo, 0, sizeof(WENCInfo)); + + r.mState = STATE_SPEAKER_ON; + r.mInternalCommand = COMMAND_SPEAKER_PLAY; + break; + } + + case WPAD_ERR_BUSY: { + r.mInternalCommand = COMMAND_SPEAKER_ON; + break; + } + + case WPAD_ERR_TRANSFER: { + r.mState = STATE_INVALID; + break; + } + + case WPAD_ERR_NO_CONTROLLER: { + r.mState = STATE_INVALID; + break; + } + + default: { + r.mState = STATE_INVALID; + break; + } + } + + if (result != WPAD_ERR_OK && result != WPAD_ERR_BUSY) { + r.NotifyCallback(chan, result); + } + + r.mCommandBusyFlag = false; +} + +void RemoteSpeaker::SpeakerPlayCallback(s32 chan, s32 result) { + RemoteSpeaker& r = + detail::RemoteSpeakerManager::GetInstance().GetRemoteSpeaker(chan); + + switch (result) { + case WPAD_ERR_OK: { + r.mState = STATE_SPEAKER_PLAY; + break; + } + + case WPAD_ERR_BUSY: { + r.mInternalCommand = COMMAND_SPEAKER_PLAY; + break; + } + + case WPAD_ERR_TRANSFER: { + r.mState = STATE_INVALID; + break; + } + + case WPAD_ERR_NO_CONTROLLER: { + r.mState = STATE_INVALID; + break; + } + + default: { + r.mState = STATE_INVALID; + break; + } + } + + if (result != WPAD_ERR_BUSY) { + r.NotifyCallback(chan, result); + } + + r.mCommandBusyFlag = false; +} + +void RemoteSpeaker::SpeakerOffCallback(s32 chan, s32 result) { + RemoteSpeaker& r = + detail::RemoteSpeakerManager::GetInstance().GetRemoteSpeaker(chan); + + switch (result) { + case WPAD_ERR_OK: { + r.mState = STATE_SPEAKER_OFF; + break; + } + + case WPAD_ERR_BUSY: { + r.mInternalCommand = COMMAND_SPEAKER_OFF; + break; + } + + case WPAD_ERR_TRANSFER: { + r.mState = STATE_INVALID; + break; + } + + case WPAD_ERR_NO_CONTROLLER: { + r.mState = STATE_INVALID; + break; + } + + default: { + r.mState = STATE_INVALID; + break; + } + } + + if (result != WPAD_ERR_BUSY) { + r.NotifyCallback(chan, result); + } + + r.mCommandBusyFlag = false; +} + +void RemoteSpeaker::NotifyCallback(s32 chan, s32 result) { + if (mValidCallbackFlag && mWpadCallback != NULL) { + mWpadCallback(chan, result); + mWpadCallback = NULL; + } +} + +void RemoteSpeaker::ContinueAlarmHandler(OSAlarm* pAlarm, OSContext* pCtx) { +#pragma unused(pCtx) + + ut::AutoInterruptLock lock; + RemoteSpeaker* p = static_cast(OSGetAlarmUserData(pAlarm)); + + // p->mForceResumeFlag = true; + p->mContinueFlag = false; +} + +void RemoteSpeaker::IntervalAlarmHandler(OSAlarm* pAlarm, OSContext* pCtx) { +#pragma unused(pCtx) + + ut::AutoInterruptLock lock; + RemoteSpeaker* p = static_cast(OSGetAlarmUserData(pAlarm)); + + if (p->mIntervalFlag) { + OSCancelAlarm(&p->mContinueAlarm); + // p->mForceResumeFlag = false; + p->mContinueFlag = false; + } + + p->mIntervalFlag = false; +} + +} // namespace snd +} // namespace nw4r diff --git a/src/nw4r/snd/snd_RemoteSpeakerManager.cpp b/src/nw4r/snd/snd_RemoteSpeakerManager.cpp index 4a82dfa5..cbfd9d2a 100644 --- a/src/nw4r/snd/snd_RemoteSpeakerManager.cpp +++ b/src/nw4r/snd/snd_RemoteSpeakerManager.cpp @@ -1 +1,75 @@ #include "nw4r/snd/snd_RemoteSpeakerManager.h" +#include "rvl/AX/AXOut.h" + + +namespace nw4r { +namespace snd { +namespace detail { + +RemoteSpeakerManager& RemoteSpeakerManager::GetInstance() { + static RemoteSpeakerManager instance; + return instance; +} + +RemoteSpeakerManager::RemoteSpeakerManager() : mInitialized(false) { + for (int i = 0; i < WPAD_MAX_CONTROLLERS; i++) { + mSpeaker[i].SetChannelIndex(i); + } +} + +RemoteSpeaker& RemoteSpeakerManager::GetRemoteSpeaker(int idx) { + return mSpeaker[idx]; +} + +void RemoteSpeakerManager::Setup() { + if (mInitialized) { + return; + } + + OSCreateAlarm(&mRemoteSpeakerAlarm); + + OSSetPeriodicAlarm(&mRemoteSpeakerAlarm, OSGetTime(), + OS_NSEC_TO_TICKS(SPEAKER_ALARM_PERIOD_NSEC), + RemoteSpeakerAlarmProc); + + mInitialized = true; +} + +void RemoteSpeakerManager::Shutdown() { + if (!mInitialized) { + return; + } + + OSCancelAlarm(&mRemoteSpeakerAlarm); + mInitialized = false; +} + +void RemoteSpeakerManager::RemoteSpeakerAlarmProc(OSAlarm* pAlarm, + OSContext* pCtx) { +#pragma unused(pAlarm) +#pragma unused(pCtx) + + RemoteSpeakerManager& r = GetInstance(); + + s16 samples[RemoteSpeaker::SAMPLES_PER_AUDIO_PACKET]; + if (AXRmtGetSamplesLeft() < RemoteSpeaker::SAMPLES_PER_AUDIO_PACKET) { + return; + } + + for (int i = 0; i < WPAD_MAX_CONTROLLERS; i++) { + if (r.mSpeaker[i].IsAvailable()) { + AXRmtGetSamples(i, samples, + RemoteSpeaker::SAMPLES_PER_AUDIO_PACKET); + + r.mSpeaker[i].UpdateStreamData(samples); + } + + r.mSpeaker[i].Update(); + } + + AXRmtAdvancePtr(RemoteSpeaker::SAMPLES_PER_AUDIO_PACKET); +} + +} // namespace detail +} // namespace snd +} // namespace nw4r diff --git a/src/nw4r/snd/snd_SeqPlayer.cpp b/src/nw4r/snd/snd_SeqPlayer.cpp index d9195d5c..10264746 100644 --- a/src/nw4r/snd/snd_SeqPlayer.cpp +++ b/src/nw4r/snd/snd_SeqPlayer.cpp @@ -241,6 +241,10 @@ void SeqPlayer::Skip(OffsetType offsetType, int offset) // SeqPlayer::SetTempoRatio ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13781) DECOMP_FORCE(NW4RAssert_String(tempoRatio >= 0.0f)); +void SeqPlayer::SetTempoRatio(f32 tempo) { + mTempoRatio = tempo; +} + void SeqPlayer::SetChannelPriority(int priority) { // specifically not the source variant diff --git a/src/nw4r/snd/snd_SeqSound.cpp b/src/nw4r/snd/snd_SeqSound.cpp index c398e9ee..9b68e1f5 100644 --- a/src/nw4r/snd/snd_SeqSound.cpp +++ b/src/nw4r/snd/snd_SeqSound.cpp @@ -169,6 +169,10 @@ void SeqSound::Shutdown() // SeqSound::SetTempoRatio ([R89JEL]:/bin/RVL/Debug/mainD.MAP:13849) DECOMP_FORCE(NW4RAssert_String(tempoRatio >= 0.0f)); +void SeqSound::SetTempoRatio(f32 tempo) { + mSeqPlayer.SetTempoRatio(tempo); +} + void SeqSound::SetChannelPriority(int priority) { // specifically not the source variant diff --git a/src/nw4r/snd/snd_Sound3DManager.cpp b/src/nw4r/snd/snd_Sound3DManager.cpp index 5c410f11..8a75b573 100644 --- a/src/nw4r/snd/snd_Sound3DManager.cpp +++ b/src/nw4r/snd/snd_Sound3DManager.cpp @@ -1 +1,294 @@ #include "nw4r/snd/snd_Sound3DManager.h" +#include "nw4r/snd/snd_AxManager.h" +#include "nw4r/snd/snd_Sound3DListener.h" +#include "nw4r/snd/snd_SoundHandle.h" + +#include "nw4r/math.h" + +#include + +namespace nw4r { +namespace snd { +namespace { + +/** + * Solves the linear equation: + * a*x + b*y + c = d + * + * Where: + * a = dFactor - cFactor + * b = dAngle - cAngle + * c = cAngle * cFactor + * d = dAngle * dFactor + */ +inline f32 SolveLinerFunction(f32 x, f32 dAngle, f32 cAngle, f32 dFactor, + f32 cFactor) { // @typo + if (dAngle == cAngle) { + return (dFactor + cFactor) / 2; + } + + f32 b = dAngle - cAngle; + + return x * (cFactor - dFactor) / b + + (dAngle * dFactor - cAngle * cFactor) / b; +} + +} // namespace + +Sound3DManager::Sound3DManager() + : mMaxPriorityReduction(32), + mSpeakerAngleStereo(NW4R_MATH_PI / 4), + mFrontSpeakerAngleDpl2(NW4R_MATH_PI / 6), + mRearSpeakerAngleDpl2(2 * NW4R_MATH_PI / 3), + mInitPan(0.0f), + mPanRange(0.9f) {} + +u32 Sound3DManager::GetRequiredMemSize(const SoundArchive* pArchive) { + u32 numParam = 0; + + SoundArchive::SoundArchivePlayerInfo playerInfo; + if (pArchive->ReadSoundArchivePlayerInfo(&playerInfo)) { + numParam += playerInfo.seqSoundCount; + numParam += playerInfo.strmSoundCount; + numParam += playerInfo.waveSoundCount; + } + + return numParam * sizeof(Sound3DActorParam); +} + +bool Sound3DManager::Setup(const SoundArchive* pArchive, void* pBuffer, + u32 size) { +#pragma unused(pArchive) + + mParamPool.Create(pBuffer, size); + return true; +} + +void Sound3DManager::detail_Update(SoundParam* pParam, u32 id, + detail::BasicSound* pSound, const void* pArg, + u32 flags) { + SoundHandle handle; + if (pSound != NULL) { + handle.detail_AttachSoundAsTempHandle(pSound); + } + + Update(pParam, id, pSound != NULL ? &handle : NULL, pArg, flags); +} + +void Sound3DManager::Update(SoundParam* pParam, u32 id, SoundHandle* pHandle, + const void* pArg, u32 flags) { +#pragma unused(id) +#pragma unused(pHandle) + + f32 angle; + f32 panLR, panFR; + f32 pan, surroundPan; + + const Sound3DActorParam* pActorParam = + static_cast(pArg); + + Sound3DListener* pListener = GetListener(); + if (pListener == NULL) { + pParam->volume = 0.0f; + return; + } + + math::VEC3 relativePos; + VEC3Transform(&relativePos, &pListener->GetMatrix(), + &pActorParam->position); + + f32 distance = VEC3Len(&relativePos); + f32 volume = 1.0f; + + if ((flags & (PARAM_UPDATE_VOLUME | PARAM_UPDATE_PRIORITY)) && + distance > pListener->GetMaxVolumeDistance()) { + + switch (pActorParam->soundParam.decayCurve) { + case DECAY_CURVE_LOGARITHMIC: { + f32 units = (distance - pListener->GetMaxVolumeDistance()) / + pListener->GetUnitDistance(); + + volume = + std::pow(pActorParam->soundParam.decayRatio / 256.0f, units); + break; + } + + case DECAY_CURVE_LINEAR: { + f32 units = (distance - pListener->GetMaxVolumeDistance()) / + pListener->GetUnitDistance() * + pActorParam->soundParam.decayRatio; + + units = units / 256.0f; + volume = ut::Max(0.0f, 1.0f - units); + break; + } + } + } + + if ((flags & PARAM_UPDATE_VOLUME) && + !(pActorParam->soundParam.flags & PARAM_UPDATE_VOLUME)) { + + pParam->volume = volume; + } + + if (flags & (PARAM_UPDATE_PAN | PARAM_UPDATE_SURROUND_PAN)) { + math::VEC3 adjustedPos; + + if (0.0f == distance) { + adjustedPos.z = 0.0f; + adjustedPos.y = 0.0f; + adjustedPos.x = 0.0f; + } else { + math::VEC3 relativeXZ(relativePos.x, 0.0f, relativePos.z); + + f32 distanceXZ = VEC3Len(&relativeXZ); + if (distanceXZ > pListener->GetInteriorSize()) { + relativeXZ.x *= pListener->GetInteriorSize() / distanceXZ; + relativeXZ.z *= pListener->GetInteriorSize() / distanceXZ; + } + + f32 ratioXZ = VEC3Len(&relativeXZ); + adjustedPos.x = relativePos.x * ratioXZ / distance; + adjustedPos.y = 0.0f; + adjustedPos.z = relativePos.z * ratioXZ / distance; + } + + angle = atan2(adjustedPos.x, -adjustedPos.z); + f32 distanceNrm = VEC3Len(&adjustedPos) / pListener->GetInteriorSize(); + + switch (detail::AxManager::GetInstance().GetOutputMode()) { + case OUTPUT_MODE_SURROUND: + case OUTPUT_MODE_DPL2: { + static f32 angleRearLeft = -mRearSpeakerAngleDpl2; + static f32 angleFrontLeft = -mFrontSpeakerAngleDpl2; + static f32 angleFrontRight = mFrontSpeakerAngleDpl2; + static f32 angleRearRight = mRearSpeakerAngleDpl2; + + // clang-format off + if (angle < angleRearLeft) { + panLR = SolveLinerFunction(angle, -NW4R_MATH_PI, angleRearLeft, -1.0f, 0.0f); + panFR = 1.0f; + } else if (angle < -NW4R_MATH_PI / 2) { + panLR = -1.0f; + panFR = SolveLinerFunction(angle, angleRearLeft, -NW4R_MATH_PI / 2, 0.0f, 1.0f); + } else if (angle < angleFrontLeft) { + panLR = -1.0f; + panFR = SolveLinerFunction(angle, -NW4R_MATH_PI / 2, angleFrontLeft, -1.0f, 0.0f); + } else if (angle < angleFrontRight) { + panLR = SolveLinerFunction(angle, angleFrontLeft, angleFrontRight, 1.0f, -1.0f); + panFR = -1.0f; + } else if (angle < NW4R_MATH_PI / 2) { + panLR = 1.0f; + panFR = SolveLinerFunction(angle, angleFrontRight, NW4R_MATH_PI / 2, 0.0f, -1.0f); + } else if (angle < angleRearRight) { + panLR = 1.0f; + panFR = SolveLinerFunction(angle, NW4R_MATH_PI / 2, angleRearRight, 1.0f, 0.0f); + } else { + panLR = SolveLinerFunction(angle, angleRearRight, NW4R_MATH_PI, 0.0f, 1.0f); + panFR = 1.0f; + } + // clang-format on + + f32 cosAvg = (std::cosf(mFrontSpeakerAngleDpl2) + + std::cosf(mRearSpeakerAngleDpl2)) / + 2.0f; + + f32 rearFactor = + cosAvg / (cosAvg + -std::cosf(mRearSpeakerAngleDpl2)); + + panLR *= mPanRange; + panFR *= mPanRange; + + pan = panLR * distanceNrm; + + surroundPan = + mInitPan + (1.0f + (panFR * distanceNrm + + rearFactor * (1.0f - distanceNrm))); + break; + } + + case OUTPUT_MODE_STEREO: { + static f32 angleRearLeft = -NW4R_MATH_PI + mSpeakerAngleStereo; + static f32 angleFrontLeft = -mSpeakerAngleStereo; + static f32 angleFrontRight = mSpeakerAngleStereo; + static f32 angleRearRight = NW4R_MATH_PI - mSpeakerAngleStereo; + + // clang-format off + if (angle < angleRearLeft) { + panLR = SolveLinerFunction(angle, -NW4R_MATH_PI, angleRearLeft, -1.0f, 0.0f); + panFR = 1.0f; + } else if (angle < angleFrontLeft) { + panLR = -1.0f; + panFR = SolveLinerFunction(angle, angleRearLeft, angleFrontLeft, -1.0f, 1.0f); + } else if (angle < angleFrontRight) { + panLR = SolveLinerFunction(angle, angleFrontLeft, angleFrontRight, 1.0f, -1.0f); + panFR = -1.0f; + } else if (angle < angleRearRight) { + panLR = 1.0f; + panFR = SolveLinerFunction(angle, angleFrontRight, angleRearRight, 1.0f, -1.0f); + } else { + panLR = SolveLinerFunction(angle, angleRearRight, NW4R_MATH_PI, 0.0f, 1.0f); + panFR = 1.0f; + } + // clang-format on + + panLR *= mPanRange; + panFR *= mPanRange; + + surroundPan = 1.0f + panFR * distanceNrm; + pan = panLR * distanceNrm; + break; + } + + case OUTPUT_MODE_MONO: + default: { + pan = 0.0f; + surroundPan = 0.0f; + break; + } + } + } + + if ((flags & PARAM_UPDATE_PAN) && + !(pActorParam->soundParam.flags & PARAM_UPDATE_PAN)) { + + pParam->pan = pan; + } + + if ((flags & PARAM_UPDATE_SURROUND_PAN) && + !(pActorParam->soundParam.flags & PARAM_UPDATE_SURROUND_PAN)) { + + pParam->surroundPan = surroundPan; + } + + if ((flags & PARAM_UPDATE_PRIORITY) && + !(pActorParam->soundParam.flags & PARAM_UPDATE_PRIORITY)) { + + pParam->priority = + -static_cast((1.0f - volume) * GetMaxPriorityReduction()); + } +} + +void* Sound3DManager::detail_AllocAmbientArg(u32 size) { + if (size != sizeof(Sound3DActorParam)) { + return NULL; + } + + return mParamPool.Alloc(); +} + +void Sound3DManager::detail_FreeAmbientArg(void* pArg, + const detail::BasicSound* pSound) { +#pragma unused(pSound) + + mParamPool.Free(static_cast(pArg)); +} + +Sound3DManager::Sound3DActorParam::Sound3DActorParam() : userParam(-1) { + soundParam.flags = 0; + soundParam.decayCurve = DECAY_CURVE_LOGARITHMIC; + soundParam.decayRatio = 128; +} + +} // namespace snd +} // namespace nw4r diff --git a/src/nw4r/snd/snd_SoundArchive.cpp b/src/nw4r/snd/snd_SoundArchive.cpp index d8f98a2c..e00a0f08 100644 --- a/src/nw4r/snd/snd_SoundArchive.cpp +++ b/src/nw4r/snd/snd_SoundArchive.cpp @@ -72,11 +72,27 @@ u32 SoundArchive::GetGroupCount() const return mFileReader->GetGroupCount(); } +const char* SoundArchive::GetSoundLabelString(u32 id) const { + return mFileReader->GetSoundLabelString(id); +} + u32 SoundArchive::ConvertLabelStringToSoundId(char const *label) const { return mFileReader->ConvertLabelStringToSoundId(label); } +u32 SoundArchive::ConvertLabelStringToPlayerId(const char* pLabel) const { + return mFileReader->ConvertLabelStringToPlayerId(pLabel); +} + +u32 SoundArchive::ConvertLabelStringToGroupId(const char* pLabel) const { + return mFileReader->ConvertLabelStringToGroupId(pLabel); +} + +u32 SoundArchive::GetSoundUserParam(u32 id) const { + return mFileReader->GetSoundUserParam(id); +} + SoundArchive::SoundType SoundArchive::GetSoundType(u32 soundId) const { return mFileReader->GetSoundType(soundId); @@ -226,4 +242,52 @@ ut::FileStream *SoundArchive::OpenExtStreamImpl(void *buffer, int size, return OpenExtStream(buffer, size, fullPath, begin, length); } +ut::FileStream* SoundArchive::detail_OpenGroupStream(u32 id, void* pBuffer, + int bufferSize) const { + GroupInfo groupInfo; + if (!detail_ReadGroupInfo(id, &groupInfo)) { + return NULL; + } + + if (groupInfo.extFilePath != NULL) { + return OpenExtStreamImpl(pBuffer, bufferSize, groupInfo.extFilePath, + groupInfo.offset, groupInfo.size); + } + + return OpenStream(pBuffer, bufferSize, groupInfo.offset, groupInfo.size); +} + +ut::FileStream* +SoundArchive::detail_OpenGroupWaveDataStream(u32 id, void* pBuffer, + int bufferSize) const { + GroupInfo groupInfo; + if (!detail_ReadGroupInfo(id, &groupInfo)) { + return NULL; + } + + if (groupInfo.extFilePath != NULL) { + return OpenExtStreamImpl(pBuffer, bufferSize, groupInfo.extFilePath, + groupInfo.waveDataOffset, + groupInfo.waveDataSize); + } + + return OpenStream(pBuffer, bufferSize, groupInfo.waveDataOffset, + groupInfo.waveDataSize); +} + +void SoundArchive::SetExternalFileRoot(const char* pExtFileRoot) { + u32 len = std::strlen(pExtFileRoot); + u32 nullPos = len; + + if (pExtFileRoot[len - 1] != '/') { + mExtFileRoot[len] = '/'; + nullPos++; + } + + mExtFileRoot[nullPos] = '\0'; + + // @bug Long path can overflow mExtFileRoot buffer + std::strncpy(mExtFileRoot, pExtFileRoot, len); +} + }} // namespace nw4r::snd diff --git a/src/nw4r/snd/snd_SoundArchiveLoader.cpp b/src/nw4r/snd/snd_SoundArchiveLoader.cpp index 175076b5..57c8b893 100644 --- a/src/nw4r/snd/snd_SoundArchiveLoader.cpp +++ b/src/nw4r/snd/snd_SoundArchiveLoader.cpp @@ -1 +1,139 @@ #include "nw4r/snd/snd_SoundArchiveLoader.h" + +#include "nw4r/snd/snd_SoundArchive.h" +#include "nw4r/snd/snd_SoundMemoryAllocatable.h" + +namespace nw4r { +namespace snd { +namespace detail { + +SoundArchiveLoader::SoundArchiveLoader(const SoundArchive& rArchive) + : mArc(rArchive), mStream(NULL) { + OSInitMutex(&mMutex); +} + +SoundArchiveLoader::~SoundArchiveLoader() {} + +void* SoundArchiveLoader::LoadGroup(u32 id, + SoundMemoryAllocatable* pAllocatable, + void** ppWaveBuffer, u32 blockSize) { + ut::detail::AutoLock lock(mMutex); + + FileStreamHandle groupHandle( + mArc.detail_OpenGroupStream(id, mStreamArea, sizeof(mStreamArea))); + + if (!groupHandle) { + return NULL; + } + + if (!groupHandle->CanSeek() || !groupHandle->CanRead()) { + return NULL; + } + + void* pGroupBuffer = pAllocatable->Alloc(groupHandle->GetSize()); + if (pGroupBuffer == NULL) { + return NULL; + } + + mStream = groupHandle.GetFileStream(); + + if (blockSize == 0) { + s32 bytesRead = groupHandle->Read(pGroupBuffer, groupHandle->GetSize()); + + if (bytesRead < 0) { + mStream = NULL; + return NULL; + } + } else { + u8* pReadPtr = static_cast(pGroupBuffer); + u32 bytesLeft = groupHandle->GetSize(); + + while (bytesLeft) { + s32 bytesRead = + groupHandle->Read(pReadPtr, ut::Min(blockSize, bytesLeft)); + + if (bytesRead < 0) { + mStream = NULL; + return NULL; + } + + if (bytesLeft > bytesRead) { + bytesLeft -= bytesRead; + pReadPtr += bytesRead; + } else { + bytesLeft = 0; + } + } + } + + mStream = NULL; + + SoundArchive::GroupInfo groupInfo; + if (!mArc.detail_ReadGroupInfo(id, &groupInfo)) { + return NULL; + } + + if (groupInfo.waveDataSize != 0) { + FileStreamHandle waveHandle(mArc.detail_OpenGroupWaveDataStream( + id, mStreamArea, sizeof(mStreamArea))); + + if (!waveHandle) { + return NULL; + } + + if (!waveHandle->CanSeek() || !waveHandle->CanRead()) { + return NULL; + } + + void* pWaveBuffer = pAllocatable->Alloc(waveHandle->GetSize()); + if (pWaveBuffer == NULL) { + return NULL; + } + + mStream = waveHandle.GetFileStream(); + + if (blockSize == 0) { + s32 bytesRead = + waveHandle->Read(pWaveBuffer, waveHandle->GetSize()); + + if (bytesRead < 0) { + mStream = NULL; + return NULL; + } + } else { + u8* pReadPtr = static_cast(pWaveBuffer); + u32 bytesLeft = waveHandle->GetSize(); + + while (bytesLeft) { + s32 bytesRead = + waveHandle->Read(pReadPtr, ut::Min(blockSize, bytesLeft)); + + if (bytesRead < 0) { + mStream = NULL; + return NULL; + } + + if (bytesLeft > bytesRead) { + bytesLeft -= bytesRead; + pReadPtr += bytesRead; + } else { + bytesLeft = 0; + } + } + } + + mStream = NULL; + + if (ppWaveBuffer != NULL) { + *ppWaveBuffer = pWaveBuffer; + } + } else if (ppWaveBuffer != NULL) { + *ppWaveBuffer = NULL; + } + + return pGroupBuffer; +} + +} // namespace detail +} // namespace snd +} // namespace nw4r diff --git a/src/nw4r/snd/snd_SoundArchivePlayer.cpp b/src/nw4r/snd/snd_SoundArchivePlayer.cpp index 44eeed9e..8e9019d6 100644 --- a/src/nw4r/snd/snd_SoundArchivePlayer.cpp +++ b/src/nw4r/snd/snd_SoundArchivePlayer.cpp @@ -16,6 +16,7 @@ #include "nw4r/snd/snd_Bank.h" #include "nw4r/snd/snd_BasicSound.h" +#include "nw4r/snd/snd_SoundArchiveLoader.h" #include "nw4r/snd/snd_debug.h" #include "nw4r/snd/snd_DisposeCallbackManager.h" #include "nw4r/snd/snd_ExternalSoundPlayer.h" @@ -1327,6 +1328,51 @@ void SoundArchivePlayer::UpdateCommonSoundParam( */ DECOMP_FORCE(NW4RAssertAligned_String(loadBlockSize, 32)); +bool SoundArchivePlayer::LoadGroup(u32 id, SoundMemoryAllocatable* pAllocatable, + u32 blockSize) { + if (!IsAvailable()) { + return false; + } + + if (id >= mSoundArchive->GetGroupCount()) { + return false; + } + + if (GetGroupAddress(id) != NULL) { + return true; + } + + if (pAllocatable == NULL) { + return false; + } + + detail::SoundArchiveLoader loader(*mSoundArchive); + + void* pWaveBuffer; + const void* pGroup = + loader.LoadGroup(id, pAllocatable, &pWaveBuffer, blockSize); + + if (pGroup == NULL) { + return NULL; + } + + SetGroupAddress(id, pGroup); + SetGroupWaveDataAddress(id, pWaveBuffer); + + return true; +} + +bool SoundArchivePlayer::LoadGroup(const char* pLabel, + SoundMemoryAllocatable* pAllocatable, + u32 blockSize) { + u32 id = mSoundArchive->ConvertLabelStringToGroupId(pLabel); + if (id == SoundArchive::INVALID_ID) { + return false; + } + + return LoadGroup(id, pAllocatable, blockSize); +} + void SoundArchivePlayer::InvalidateData(void const *start, void const *end) { if (mFileTable) diff --git a/src/nw4r/snd/snd_SoundHandle.cpp b/src/nw4r/snd/snd_SoundHandle.cpp index 249815da..bc785840 100644 --- a/src/nw4r/snd/snd_SoundHandle.cpp +++ b/src/nw4r/snd/snd_SoundHandle.cpp @@ -21,6 +21,20 @@ namespace nw4r { namespace snd { +void SoundHandle::detail_AttachSoundAsTempHandle(detail::BasicSound* pSound) { + mSound = pSound; + + if (pSound->IsAttachedTempGeneralHandle()) { + mSound->DetachTempGeneralHandle(); + } + + if (mSound->IsAttachedTempSpecialHandle()) { + mSound->DetachTempSpecialHandle(); + } + + mSound->mTempGeneralHandle = this; +} + void SoundHandle::detail_AttachSound(detail::BasicSound *sound) { NW4RAssertPointerNonnull_Line(81, sound); diff --git a/src/nw4r/snd/snd_SoundHeap.cpp b/src/nw4r/snd/snd_SoundHeap.cpp index 1e021805..ace487a5 100644 --- a/src/nw4r/snd/snd_SoundHeap.cpp +++ b/src/nw4r/snd/snd_SoundHeap.cpp @@ -1 +1,65 @@ #include "nw4r/snd/snd_SoundHeap.h" + +#include "nw4r/snd/snd_DisposeCallbackManager.h" +#include "nw4r/snd/snd_SoundThread.h" + +namespace nw4r { +namespace snd { + +SoundHeap::SoundHeap() { + OSInitMutex(&mMutex); +} + +SoundHeap::~SoundHeap() { + mFrameHeap.Destroy(); +} + +bool SoundHeap::Create(void* pBase, u32 size) { + return mFrameHeap.Create(pBase, size); +} + +void SoundHeap::Destroy() { + mFrameHeap.Destroy(); +} + +void* SoundHeap::Alloc(u32 size) { + ut::detail::AutoLock lock(mMutex); + return mFrameHeap.Alloc(size, DisposeCallbackFunc, NULL); +} + +void* SoundHeap::Alloc(u32 size, detail::FrameHeap::FreeCallback pCallback, + void* pCallbackArg) { + ut::detail::AutoLock lock(mMutex); + return mFrameHeap.Alloc(size, pCallback, pCallbackArg); +} + +void SoundHeap::Clear() { + ut::detail::AutoLock lockHeap(mMutex); + detail::SoundThread::AutoLock lockThread; + + mFrameHeap.Clear(); +} + +int SoundHeap::SaveState() { + ut::detail::AutoLock lock(mMutex); + return mFrameHeap.SaveState(); +} + +void SoundHeap::LoadState(int id) { + ut::detail::AutoLock lockHeap(mMutex); + detail::SoundThread::AutoLock lockThread; + + mFrameHeap.LoadState(id); +} + +void SoundHeap::DisposeCallbackFunc(void* pBuffer, u32 size, + void* pCallbackArg) { + detail::DisposeCallbackManager::GetInstance().Dispose(pBuffer, size, + pCallbackArg); + + detail::DisposeCallbackManager::GetInstance().DisposeWave(pBuffer, size, + pCallbackArg); +} + +} // namespace snd +} // namespace nw4r diff --git a/src/nw4r/snd/snd_SoundSystem.cpp b/src/nw4r/snd/snd_SoundSystem.cpp index 01afc6d4..ef27f5a5 100644 --- a/src/nw4r/snd/snd_SoundSystem.cpp +++ b/src/nw4r/snd/snd_SoundSystem.cpp @@ -78,6 +78,27 @@ namespace nw4r { namespace snd namespace nw4r { namespace snd { +void SoundSystem::InitSoundSystem(s32 soundThreadPriority, + s32 dvdThreadPriority) { + const int defaultWorkSize = DEFAULT_SOUND_THREAD_STACK_SIZE + + DEFAULT_DVD_THREAD_STACK_SIZE + + detail::AxVoiceManager::WORK_SIZE_MAX + + detail::VoiceManager::WORK_SIZE_MAX + + detail::ChannelManager::WORK_SIZE_MAX; + + static u8 defaultSoundSystemWork[defaultWorkSize] ALIGN_DECL(32); + + // OSRegisterVersion(NW4R_SND_Version_); + + SoundSystemParam param; + param.soundThreadPriority = soundThreadPriority; + param.dvdThreadPriority = dvdThreadPriority; + + // @bug This function ignores the specified buffer size + InitSoundSystem(param, defaultSoundSystemWork, + sizeof(defaultSoundSystemWork)); +} + u32 SoundSystem::GetRequiredMemSize(SoundSystemParam const ¶m) { // could have just used align assert? idk @@ -210,6 +231,20 @@ bool SoundSystem::IsInitializedSoundSystem() return sInitialized; } + +void SoundSystem::WaitForResetReady() { + if (!sInitialized) { + return; + } + + u32 start = OSGetTick(); + + while (!detail::AxManager::GetInstance().IsResetReady()) { + if (OS_TICKS_TO_SEC(OSGetTick() - start) > 0) { + break; + } + } +} // SoundSystem::WaitForResetReady ([R89JEL]:/bin/RVL/Debug/mainD.MAP:14493) DECOMP_FORCE("SoundSystem::WaitForResetReady is TIME OUT.\n");