summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorrobojumper <robojumper@gmail.com>2025-05-25 16:29:42 +0200
committerrobojumper <robojumper@gmail.com>2025-05-25 16:32:47 +0200
commitf7884242c7e31b516d56ba4d9f4ac5ce2155f13d (patch)
treec17d3ce6204353d27d1d9e152cf9a1fca035dc47 /src
parentb5c3d85ea29678b73ac5140015f16ecc542bb060 (diff)
Import of more nw4r::snd from https://github.com/kiwi515/ogws
Co-authored-by: kiwi515 <49212064+kiwi515@users.noreply.github.com>
Diffstat (limited to 'src')
-rw-r--r--src/PowerPC_EABI_Support/MSL/MSL_C/MSL_Common/Include/cstring.h3
-rw-r--r--src/nw4r/snd/snd_AxManager.cpp148
-rw-r--r--src/nw4r/snd/snd_AxfxImpl.cpp56
-rw-r--r--src/nw4r/snd/snd_DvdSoundArchive.cpp233
-rw-r--r--src/nw4r/snd/snd_ExternalSoundPlayer.cpp20
-rw-r--r--src/nw4r/snd/snd_FrameHeap.cpp135
-rw-r--r--src/nw4r/snd/snd_NandSoundArchive.cpp263
-rw-r--r--src/nw4r/snd/snd_RemoteSpeaker.cpp378
-rw-r--r--src/nw4r/snd/snd_RemoteSpeakerManager.cpp74
-rw-r--r--src/nw4r/snd/snd_SeqPlayer.cpp4
-rw-r--r--src/nw4r/snd/snd_SeqSound.cpp4
-rw-r--r--src/nw4r/snd/snd_Sound3DManager.cpp293
-rw-r--r--src/nw4r/snd/snd_SoundArchive.cpp64
-rw-r--r--src/nw4r/snd/snd_SoundArchiveLoader.cpp138
-rw-r--r--src/nw4r/snd/snd_SoundArchivePlayer.cpp46
-rw-r--r--src/nw4r/snd/snd_SoundHandle.cpp14
-rw-r--r--src/nw4r/snd/snd_SoundHeap.cpp64
-rw-r--r--src/nw4r/snd/snd_SoundSystem.cpp35
18 files changed, 1970 insertions, 2 deletions
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<void*>(bus));
+ break;
+ }
+
+ case AUX_B: {
+ AXRegisterAuxBCallback(AuxCallbackFunc,
+ reinterpret_cast<void*>(bus));
+ break;
+ }
+
+ case AUX_C: {
+ AXRegisterAuxCCallback(AuxCallbackFunc,
+ reinterpret_cast<void*>(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<void**>(pChans);
+ AuxBus bus = static_cast<AuxBus>(reinterpret_cast<u32>(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 <cstring>
+#include <new>
-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<u8>(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 <new>
+
+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<u8*>(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<int>(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 <cstring>
+#include <new>
+
+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<u8>(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<s32*>(&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<s32*>(&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 <cstring>
+
+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<const u32*>(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<RemoteSpeaker*>(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<RemoteSpeaker*>(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 <cmath>
+
+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<const Sound3DActorParam*>(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<int>((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<Sound3DActorParam*>(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<OSMutex> 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<u8*>(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<u8*>(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<OSMutex> lock(mMutex);
+ return mFrameHeap.Alloc(size, DisposeCallbackFunc, NULL);
+}
+
+void* SoundHeap::Alloc(u32 size, detail::FrameHeap::FreeCallback pCallback,
+ void* pCallbackArg) {
+ ut::detail::AutoLock<OSMutex> lock(mMutex);
+ return mFrameHeap.Alloc(size, pCallback, pCallbackArg);
+}
+
+void SoundHeap::Clear() {
+ ut::detail::AutoLock<OSMutex> lockHeap(mMutex);
+ detail::SoundThread::AutoLock lockThread;
+
+ mFrameHeap.Clear();
+}
+
+int SoundHeap::SaveState() {
+ ut::detail::AutoLock<OSMutex> lock(mMutex);
+ return mFrameHeap.SaveState();
+}
+
+void SoundHeap::LoadState(int id) {
+ ut::detail::AutoLock<OSMutex> 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 &param)
{
// 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");