diff --git a/config/SOUE01/splits.txt b/config/SOUE01/splits.txt index fc5106f6..254165a8 100644 --- a/config/SOUE01/splits.txt +++ b/config/SOUE01/splits.txt @@ -1673,6 +1673,8 @@ nw4r/snd/snd_Voice.cpp: nw4r/snd/snd_VoiceManager.cpp: .text start:0x80484040 end:0x804846F4 + .sbss start:0x805766E0 end:0x805766E8 + .bss start:0x80673A80 end:0x80673AA8 nw4r/snd/snd_Util.cpp: .text start:0x80484700 end:0x80484BD4 diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt index b02a42c6..8b4c365d 100644 --- a/config/SOUE01/symbols.txt +++ b/config/SOUE01/symbols.txt @@ -40962,7 +40962,7 @@ sMaxVoices__Q34nw4r3snd11SoundSystem = .sbss:0x805766BC; // type:object size:0x4 lbl_805766C8 = .sbss:0x805766C8; // type:object size:0x8 data:byte lbl_805766D0 = .sbss:0x805766D0; // type:object size:0x8 data:4byte @GUARD@GetInstance__Q44nw4r3snd6detail11TaskManagerFv@instance = .sbss:0x805766D8; // type:object size:0x8 data:byte -lbl_805766E0 = .sbss:0x805766E0; // type:object size:0x8 data:byte +@GUARD@GetInstance__Q44nw4r3snd6detail12VoiceManagerFv@instance = .sbss:0x805766E0; // type:object size:0x1 data:byte lbl_805766E8 = .sbss:0x805766E8; // type:object size:0x8 data:4byte typeInfo__Q34nw4r3lyt4Pane = .sbss:0x805766F0; // type:object size:0x4 data:4byte mspAllocator__Q34nw4r3lyt6Layout = .sbss:0x805766F8; // type:object size:0x4 data:4byte @@ -49444,7 +49444,7 @@ lbl_80673A20 = .bss:0x80673A20; // type:object size:0x18 lbl_80673A38 = .bss:0x80673A38; // type:object size:0xC @LOCAL@GetInstance__Q44nw4r3snd6detail11TaskManagerFv@instance = .bss:0x80673A44; // type:object size:0x3C lbl_80673A80 = .bss:0x80673A80; // type:object size:0xC -lbl_80673A8C = .bss:0x80673A8C; // type:object size:0x1C data:byte +@LOCAL@GetInstance__Q44nw4r3snd6detail12VoiceManagerFv@instance = .bss:0x80673A8C; // type:object size:0x1C data:byte @LOCAL@SetSize__Q44nw4r3lyt6detail11TexCoordAryFUc@texCoords = .bss:0x80673AA8; // type:object size:0x20 scope:local data:float sArchiveList__Q23EGG7Archive = .bss:0x80673AC8; // type:object size:0xC sDvdList__Q23EGG7DvdFile = .bss:0x80673AD8; // type:object size:0xC diff --git a/configure.py b/configure.py index 330562f5..4e3e69e5 100644 --- a/configure.py +++ b/configure.py @@ -764,7 +764,7 @@ def nw4rLib(lib_name, objects, extra_cflags=[]): Object(Matching, "nw4r/snd/snd_TaskManager.cpp"), Object(Matching, "nw4r/snd/snd_TaskThread.cpp"), Object(NonMatching, "nw4r/snd/snd_Voice.cpp"), - Object(NonMatching, "nw4r/snd/snd_VoiceManager.cpp"), + Object(Matching, "nw4r/snd/snd_VoiceManager.cpp"), Object(Matching, "nw4r/snd/snd_Util.cpp"), Object(NonMatching, "nw4r/snd/snd_WaveArchive.cpp"), Object(NonMatching, "nw4r/snd/snd_WaveFile.cpp"), diff --git a/include/nw4r/snd/snd_AxVoice.h b/include/nw4r/snd/snd_AxVoice.h index bd743f9d..ff0b54d3 100644 --- a/include/nw4r/snd/snd_AxVoice.h +++ b/include/nw4r/snd/snd_AxVoice.h @@ -131,8 +131,8 @@ class AxVoice { enum Format { FORMAT_ADPCM = 0, - FORMAT_PCM16 = 10, - FORMAT_PCM8 = 25, + FORMAT_PCM16 = 1, + FORMAT_PCM8 = 2, }; enum VoiceType { diff --git a/include/nw4r/snd/snd_Voice.h b/include/nw4r/snd/snd_Voice.h index fb5c759a..aee299a8 100644 --- a/include/nw4r/snd/snd_Voice.h +++ b/include/nw4r/snd/snd_Voice.h @@ -6,7 +6,6 @@ #include "nw4r/ut.h" // IWYU pragma: export #include "rvl/WPAD.h" // IWYU pragma: export - namespace nw4r { namespace snd { @@ -117,8 +116,6 @@ class Voice : public DisposeCallback { void SetFxSend(AuxBus bus, f32 send); void SetRemoteOutVolume(int remote, f32 volume); - void SetRemoteSend(int remote, f32 send); - void SetRemoteFxSend(int remote, f32 send); void SetPriority(int priority); void UpdateVoicesPriority(); @@ -156,42 +153,42 @@ class Voice : public DisposeCallback { private: AxVoice *mAxVoice[CHANNEL_MAX][VOICES_MAX]; // at 0xC - SoundParam mVoiceOutParam[VOICES_MAX]; // at 0x2C - int mChannelCount; // at 0x9C - int mVoiceOutCount; // at 0xA0 - - VoiceCallback mCallback; // at 0xA4 - void *mCallbackArg; // at 0xA8 - - bool mIsActive; // at 0xAC - bool mIsStarting; // at 0xAD - bool mIsStarted; // at 0xAE - bool mIsPause; // at 0xAF - bool mIsPausing; // at 0xB0 - - u8 mSyncFlag; // at 0xB1 - u8 mRemoteFilter; // at 0xB2 - u8 mBiquadType; // at 0xB3 - int mPriority; // at 0xB4 - f32 mPan; // at 0xB8 - f32 mSurroundPan; // at 0xBC - f32 mLpfFreq; // at 0xC0 - int mOutputLineFlag; // at 0xC4 - f32 mMainOutVolume; // at 0xC8 - f32 mMainSend; // at 0xCC - f32 mFxSend[AUX_BUS_NUM]; // at 0xD0 - f32 mRemoteOutVolume[WPAD_MAX_CONTROLLERS]; // at 0xDC - f32 mRemoteSend[WPAD_MAX_CONTROLLERS]; // at 0xEC - f32 mRemoteFxSend[WPAD_MAX_CONTROLLERS]; // at 0xFC - f32 mPitch; // at 0x10C - f32 mVolume; // at 0x110 - f32 mVeInitVolume; // at 0x114 - f32 mVeTargetVolume; // at 0x118 - PanMode mPanMode; // at 0x11C - PanCurve mPanCurve; // at 0x120 + VoiceOutParam mVoiceOutParam[VOICES_MAX]; // at 0x2C + int mChannelCount; // at 0x8C + int mVoiceOutCount; // at 0x90 + + VoiceCallback mCallback; // at 0x94 + void *mCallbackData; // at 0x98 + + bool mActiveFlag; // at 0x9C + bool mStartFlag; // at 0x9D + bool mStartedFlag; // at 0x9E + bool mPauseFlag; // at 0x9F + bool mPausingFlag; // at 0xA0 + + u8 mVoiceOutParamPitchDisableFlag; // at 0xA1 + u16 mSyncFlag; // at 0xA2 + u8 mRemoteFilter; // at 0xA4 + u8 mBiquadType; // at 0xA5 + int mPriority; // at 0xA8 + f32 mPan; // at 0xAC + f32 mSurroundPan; // at 0xB0 + f32 mLpfFreq; // at 0xB4 + f32 mBiquadValue; // at 0xB8 + int mOutputLineFlag; // at 0xBC + f32 mMainOutVolume; // at 0xC0 + f32 mMainSend; // at 0xC4 + f32 mFxSend[AUX_BUS_NUM]; // at 0xC8 + f32 mRemoteOutVolume[WPAD_MAX_CONTROLLERS]; // at 0xD4 + f32 mPitch; // at 0xE4 + f32 mVolume; // at 0xE8 + f32 mVeInitVolume; // at 0xEC + f32 mVeTargetVolume; // at 0xF0 + PanMode mPanMode; // at 0xF4 + PanCurve mPanCurve; // at 0xF8 public: - NW4R_UT_LIST_NODE_DECL(); // at 0x124 + NW4R_UT_LIST_NODE_DECL(); // at 0xFC }; NW4R_UT_LIST_TYPEDEF_DECL(Voice); diff --git a/include/nw4r/snd/snd_VoiceManager.h b/include/nw4r/snd/snd_VoiceManager.h index d29e6982..b9d946a0 100644 --- a/include/nw4r/snd/snd_VoiceManager.h +++ b/include/nw4r/snd/snd_VoiceManager.h @@ -3,7 +3,6 @@ #include "nw4r/snd/snd_Voice.h" #include "nw4r/types_nw4r.h" - namespace nw4r { namespace snd { namespace detail { diff --git a/src/nw4r/snd/snd_Voice.cpp b/src/nw4r/snd/snd_Voice.cpp index ef9e27ef..8db6f6ee 100644 --- a/src/nw4r/snd/snd_Voice.cpp +++ b/src/nw4r/snd/snd_Voice.cpp @@ -1 +1,1163 @@ #include "nw4r/snd/snd_Voice.h" +#include "nw4r/snd/snd_AxManager.h" +#include "nw4r/snd/snd_AxVoiceManager.h" +#include "nw4r/snd/snd_VoiceManager.h" +#include "nw4r/snd/snd_WaveFile.h" +#include "nw4r/snd/snd_Util.h" + +namespace nw4r { +namespace snd { +namespace detail { + +Voice::Voice() + : mCallback(NULL), + mActiveFlag(false), + mStartFlag(false), + mStartedFlag(false), + mPauseFlag(false), + mSyncFlag(0) { + + for (int i = 0; i < CHANNEL_MAX; i++) { + for (int j = 0; j < VOICES_MAX; j++) { + mAxVoice[i][j] = NULL; + } + } +} + +Voice::~Voice() { + for (int i = 0; i < CHANNEL_MAX; i++) { + for (int j = 0; j < VOICES_MAX; j++) { + AxVoice* pVoice = mAxVoice[i][j]; + + if (pVoice != NULL) { + AxVoiceManager::GetInstance().FreeAxVoice(pVoice); + } + } + } +} + +void Voice::InitParam(int channels, int voices, VoiceCallback pCallback, + void* pCallbackArg) { + mChannelCount = channels; + mVoiceOutCount = voices; + mCallback = pCallback; + mCallbackData = pCallbackArg; + + mSyncFlag = 0; + mPauseFlag = false; + mPausingFlag = false; + mStartedFlag = false; + + mVoiceOutParamPitchDisableFlag = 0; + + mVolume = 1.0f; + mVeInitVolume = 0.0f; + mVeTargetVolume = 1.0f; + mLpfFreq = 1.0f; + mBiquadType = 0; + mBiquadValue = 0.0f; + mPan = 0.0f; + mSurroundPan = 0.0f; + mOutputLineFlag = OUTPUT_LINE_MAIN; + mMainOutVolume = 1.0f; + mMainSend = 1.0f; + + for (int i = 0; i < AUX_BUS_NUM; i++) { + mFxSend[i] = 0.0f; + } + + for (int i = 0; i < WPAD_MAX_CONTROLLERS; i++) { + mRemoteOutVolume[i] = 1.0f; + } + + mPitch = 1.0f; + mRemoteFilter = 0; + mPanMode = PAN_MODE_DUAL; + mPanCurve = PAN_CURVE_SQRT; +} + +void Voice::StopFinished() { + if (mActiveFlag && mStartedFlag && IsPlayFinished()) { + if (mCallback != NULL) { + mCallback(this, CALLBACK_STATUS_FINISH_WAVE, mCallbackData); + } + + mStartedFlag = false; + mStartFlag = false; + } +} + +void Voice::Calc() { + if (!mStartFlag) { + return; + } + + if (mSyncFlag & SYNC_AX_SRC) { + CalcAxSrc(false); + mSyncFlag &= ~SYNC_AX_SRC; + } + + if (mSyncFlag & SYNC_AX_VE) { + CalcAxVe(); + mSyncFlag &= ~SYNC_AX_VE; + } + + if (mSyncFlag & SYNC_AX_MIX) { + if (!CalcAxMix()) { + mSyncFlag &= ~SYNC_AX_MIX; + } + } + + if (mSyncFlag & SYNC_AX_LPF) { + CalcAxLpf(); + mSyncFlag &= ~SYNC_AX_LPF; + } + + if (mSyncFlag & SYNC_AX_REMOTE) { + CalcAxRemoteFilter(); + mSyncFlag &= ~SYNC_AX_REMOTE; + } +} + +void Voice::Update() { + ut::AutoInterruptLock lock; + + if (!mActiveFlag) { + return; + } + + if ((mSyncFlag & SYNC_AX_SRC_INITIAL) && mStartFlag && !mStartedFlag) { + CalcAxSrc(true); + RunAllAxVoice(); + + mStartedFlag = true; + mSyncFlag &= ~SYNC_AX_SRC_INITIAL; + mSyncFlag &= ~SYNC_AX_SRC; + } + + if (mStartedFlag) { + if ((mSyncFlag & SYNC_AX_VOICE) && mStartFlag) { + if (mPauseFlag || AxManager::GetInstance().IsDiskError()) { + StopAllAxVoice(); + mPausingFlag = true; + } else { + RunAllAxVoice(); + mPausingFlag = false; + } + + mSyncFlag &= ~SYNC_AX_VOICE; + } + + SyncAxVoice(); + } +} + +bool Voice::Acquire(int channels, int voices, int priority, + VoiceCallback pCallback, void* pCallbackArg) { + channels = ut::Clamp(channels, CHANNEL_MIN, CHANNEL_MAX); + voices = ut::Clamp(voices, VOICES_MIN, VOICES_MAX); + + ut::AutoInterruptLock lock; + + u32 axPrio; + if (priority == PRIORITY_MAX) { + axPrio = AX_PRIORITY_MAX; + } else { + axPrio = (AX_PRIORITY_MAX / 2) + 1; + } + + int required = channels * voices; + AxVoice* voiceTable[CHANNEL_MAX * VOICES_MAX]; + + for (int i = 0; required > i; i++) { + AxVoice* pAxVoice = AxVoiceManager::GetInstance().AcquireAxVoice( + axPrio, AxVoiceCallbackFunc, this); + + if (pAxVoice == NULL) { + int rest = required - i; + + const VoiceList& rVoiceList = + VoiceManager::GetInstance().GetVoiceList(); + + for (VoiceList::ConstIterator it = rVoiceList.GetBeginIter(); + it != rVoiceList.GetEndIter(); ++it) { + + if (priority < it->GetPriority()) { + break; + } + + rest -= it->GetAxVoiceCount(); + if (rest <= 0) { + break; + } + } + + if (rest > 0) { + for (int j = 0; j < i; j++) { + AxVoiceManager::GetInstance().FreeAxVoice(voiceTable[j]); + } + + return false; + } + + u32 allocPrio; + if (axPrio == AX_PRIORITY_MAX) { + allocPrio = AX_PRIORITY_MAX; + } else { + allocPrio = (AX_PRIORITY_MAX / 2) + 2; + } + + pAxVoice = AxVoiceManager::GetInstance().AcquireAxVoice( + allocPrio, AxVoiceCallbackFunc, this); + } + + if (pAxVoice == NULL) { + for (int j = 0; j < i; j++) { + AxVoiceManager::GetInstance().FreeAxVoice(voiceTable[j]); + } + + return false; + } + + voiceTable[i] = pAxVoice; + } + + int idx = 0; + for (int i = 0; i < channels; i++) { + for (int j = 0; j < voices; j++) { + voiceTable[idx]->SetPriority(axPrio); + mAxVoice[i][j] = voiceTable[idx]; + idx++; + } + } + + InitParam(channels, voices, pCallback, pCallbackArg); + mActiveFlag = true; + return true; +} + +void Voice::Free() { + ut::AutoInterruptLock lock; + + if (!mActiveFlag) { + return; + } + + for (int i = 0; i < mChannelCount; i++) { + for (int j = 0; j < mVoiceOutCount; j++) { + AxVoice* pAxVoice = mAxVoice[i][j]; + + if (pAxVoice != NULL) { + AxVoiceManager::GetInstance().FreeAxVoice(pAxVoice); + mAxVoice[i][j] = NULL; + } + } + } + + mChannelCount = 0; + VoiceManager::GetInstance().FreeVoice(this); + mActiveFlag = false; +} + +void Voice::Setup(const WaveInfo& rData, u32 offset) { + AxVoice::Format format = WaveFormatToAxFormat(rData.sampleFormat); + int sampleRate = rData.sampleRate; + + for (int i = 0; i < mChannelCount; i++) { + if (mAxVoice[i][0] == NULL) { + continue; + } + + void* pAddr = rData.channelParam[i].dataAddr; + const ChannelParam& rParam = rData.channelParam[i]; + const AdpcmInfo& rInfo = rData.channelParam[i].adpcmInfo; + + AdpcmParam param; + if (format == AxVoice::FORMAT_ADPCM) { + param = rInfo.param; + AxVoice::CalcOffsetAdpcmParam(¶m.pred_scale, ¶m.yn1, + ¶m.yn2, offset, pAddr, param); + } + + for (int j = 0; j < mVoiceOutCount; j++) { + AxVoice* pAxVoice = mAxVoice[i][j]; + if (pAxVoice == NULL) { + continue; + } + + pAxVoice->Setup(rData.channelParam[i].dataAddr, format, sampleRate); + pAxVoice->SetAddr(rData.loopFlag, pAddr, offset, rData.loopStart, + rData.loopEnd); + + if (format == AxVoice::FORMAT_ADPCM) { + pAxVoice->SetAdpcm(¶m); + pAxVoice->SetAdpcmLoop(&rInfo.loopParam); + } + + pAxVoice->SetSrcType(AxVoice::SRC_4TAP_AUTO, mPitch); + pAxVoice->SetVoiceType(AxVoice::VOICE_TYPE_NORMAL); + } + } + + for (int i = 0; i < mVoiceOutCount; i++) { + mVoiceOutParam[i].volume = 1.0f; + mVoiceOutParam[i].pitch = 1.0f; + mVoiceOutParam[i].pan = 0.0f; + mVoiceOutParam[i].surroundPan = 0.0f; + mVoiceOutParam[i].fxSend = 0.0f; + mVoiceOutParam[i].lpf = 0.0f; + // mVoiceOutParam[i].priority = 0; + } + + mPauseFlag = false; + mPausingFlag = false; + + mStartFlag = false; + mStartedFlag = false; + + mSyncFlag |= (SYNC_AX_LPF | SYNC_AX_MIX | SYNC_AX_VE); +} + +void Voice::Start() { + mStartFlag = true; + mPauseFlag = false; + mSyncFlag |= SYNC_AX_SRC_INITIAL; +} + +void Voice::Stop() { + if (mStartedFlag) { + StopAllAxVoice(); + mStartedFlag = false; + } + + mPausingFlag = false; + mPauseFlag = false; + mStartFlag = false; +} + +void Voice::Pause(bool flag) { + if (mPauseFlag == flag) { + return; + } + + mPauseFlag = flag; + mSyncFlag |= SYNC_AX_VOICE; +} + +AxVoice::Format Voice::GetFormat() const { + if (IsActive()) { + return mAxVoice[0][0]->GetFormat(); + } + + return AxVoice::FORMAT_PCM16; +} + +void Voice::SetVolume(f32 volume) { + if (volume < 0.0f) { + volume = 0.0f; + } + + if (volume != mVolume) { + mVolume = volume; + mSyncFlag |= SYNC_AX_VE; + } +} + +void Voice::SetVeVolume(f32 target, f32 init) { + if (target < 0.0f) { + target = 0.0f; + } + if (init < 0.0f) { + init = 0.0f; + } + + // @bug Unreachable code + if (init < 0.0f) { + if (target == mVeTargetVolume) { + return; + } + + mVeTargetVolume = target; + mSyncFlag |= SYNC_AX_VE; + return; + } + + if (init == mVeInitVolume && target == mVeTargetVolume) { + return; + } + + mVeInitVolume = init; + mVeTargetVolume = target; + mSyncFlag |= SYNC_AX_VE; +} + +void Voice::SetPitch(f32 pitch) { + if (pitch == mPitch) { + return; + } + + mPitch = pitch; + mSyncFlag |= SYNC_AX_SRC; +} + +void Voice::SetPanMode(PanMode mode) { + if (mode == mPanMode) { + return; + } + + mPanMode = mode; + mSyncFlag |= SYNC_AX_MIX; +} + +void Voice::SetPanCurve(PanCurve curve) { + if (curve == mPanCurve) { + return; + } + + mPanCurve = curve; + mSyncFlag |= SYNC_AX_MIX; +} + +void Voice::SetPan(f32 pan) { + if (pan == mPan) { + return; + } + + mPan = pan; + mSyncFlag |= SYNC_AX_MIX; +} + +void Voice::SetSurroundPan(f32 pan) { + if (pan == mSurroundPan) { + return; + } + + mSurroundPan = pan; + mSyncFlag |= SYNC_AX_MIX; +} + +void Voice::SetLpfFreq(f32 freq) { + if (freq == mLpfFreq) { + return; + } + + mLpfFreq = freq; + mSyncFlag |= SYNC_AX_LPF; +} + +void Voice::SetRemoteFilter(int filter) { + filter = ut::Clamp(filter, 0, REMOTE_FILTER_MAX); + + if (filter == mRemoteFilter) { + return; + } + + mRemoteFilter = filter; + mSyncFlag |= SYNC_AX_REMOTE; +} + +void Voice::SetOutputLine(int flag) { + if (flag == mOutputLineFlag) { + return; + } + + mOutputLineFlag = flag; + mSyncFlag |= SYNC_AX_MIX; +} + +void Voice::SetMainOutVolume(f32 volume) { + if (volume < 0.0f) { + volume = 0.0f; + } + + if (volume == mMainOutVolume) { + return; + } + + mMainOutVolume = volume; + mSyncFlag |= SYNC_AX_MIX; +} + +void Voice::SetMainSend(f32 send) { + send += 1.0f; + if (send < 0.0f) { + send = 0.0f; + } + + if (send == mMainSend) { + return; + } + + mMainSend = send; + mSyncFlag |= SYNC_AX_MIX; +} + +void Voice::SetFxSend(AuxBus bus, f32 send) { + if (send < 0.0f) { + send = 0.0f; + } + + if (send == mFxSend[bus]) { + return; + } + + mFxSend[bus] = send; + mSyncFlag |= SYNC_AX_MIX; +} + +void Voice::SetRemoteOutVolume(int remote, f32 volume) { + if (volume < 0.0f) { + volume = 0.0f; + } + + if (volume == mRemoteOutVolume[remote]) { + return; + } + + mRemoteOutVolume[remote] = volume; + mSyncFlag |= SYNC_AX_MIX; +} + +void Voice::SetPriority(int priority) { + mPriority = priority; + VoiceManager::GetInstance().ChangeVoicePriority(this); + + if (mPriority != 1) { + return; + } + + for (int i = 0; i < mChannelCount; i++) { + for (int j = 0; j < mVoiceOutCount; j++) { + AxVoice* pAxVoice = mAxVoice[i][j]; + + if (pAxVoice != NULL) { + pAxVoice->SetPriority(AX_PRIORITY_MAX / 2); + } + } + } +} + +void Voice::UpdateVoicesPriority() { + if (mPriority == 1) { + return; + } + + for (int i = 0; i < mChannelCount; i++) { + for (int j = 0; j < mVoiceOutCount; j++) { + AxVoice* pAxVoice = mAxVoice[i][j]; + + if (pAxVoice != NULL) { + pAxVoice->SetPriority((AX_PRIORITY_MAX / 2) + 1); + } + } + } +} + +void Voice::SetAdpcmLoop(int channel, const AdpcmLoopParam* pParam) { + for (int i = 0; i < mVoiceOutCount; i++) { + AxVoice* pAxVoice = mAxVoice[channel][i]; + + if (pAxVoice != NULL) { + pAxVoice->SetAdpcmLoop(pParam); + } + } +} + +u32 Voice::GetCurrentPlayingSample() const { + if (IsActive()) { + return mAxVoice[0][0]->GetCurrentPlayingSample(); + } + + return 0; +} + +void Voice::SetLoopStart(int channel, const void* pBase, u32 samples) { + for (int i = 0; i < mVoiceOutCount; i++) { + AxVoice* pAxVoice = mAxVoice[channel][i]; + + if (pAxVoice != NULL) { + pAxVoice->SetLoopStart(pBase, samples); + } + } +} + +void Voice::SetLoopEnd(int channel, const void* pBase, u32 samples) { + for (int i = 0; i < mVoiceOutCount; i++) { + AxVoice* pAxVoice = mAxVoice[channel][i]; + + if (pAxVoice != NULL) { + pAxVoice->SetLoopEnd(pBase, samples); + } + } +} + +void Voice::SetLoopFlag(bool loop) { + for (int i = 0; i < mChannelCount; i++) { + for (int j = 0; j < mVoiceOutCount; j++) { + AxVoice* pAxVoice = mAxVoice[i][j]; + + if (pAxVoice != NULL) { + pAxVoice->SetLoopFlag(loop); + } + } + } +} + +void Voice::StopAtPoint(int channel, const void* pBase, u32 samples) { + for (int i = 0; i < mVoiceOutCount; i++) { + AxVoice* pAxVoice = mAxVoice[channel][i]; + + if (pAxVoice != NULL) { + pAxVoice->StopAtPoint(pBase, samples); + } + } +} + +void Voice::SetVoiceType(AxVoice::VoiceType type) { + for (int i = 0; i < mChannelCount; i++) { + for (int j = 0; j < mVoiceOutCount; j++) { + AxVoice* pAxVoice = mAxVoice[i][j]; + + if (pAxVoice != NULL) { + pAxVoice->SetVoiceType(type); + } + } + } +} + +void Voice::CalcAxSrc(bool initial) { + for (int i = 0; i < mVoiceOutCount; i++) { + f32 ratio = ut::Clamp(mVoiceOutParam[i].pitch, 0.0f, 1.0f); + ratio = mPitch * ratio; + + for (int j = 0; j < mChannelCount; j++) { + AxVoice* pAxVoice = mAxVoice[j][i]; + + if (pAxVoice != NULL) { + pAxVoice->SetSrc(ratio, initial); + } + } + } +} + +void Voice::CalcAxVe() { + f32 baseVolume = 1.0f; + baseVolume *= mVolume; + baseVolume *= AxManager::GetInstance().GetOutputVolume(); + + for (int i = 0; i < mVoiceOutCount; i++) { + const VoiceOutParam& rParam = mVoiceOutParam[i]; + f32 volume = baseVolume * rParam.volume; + f32 target = volume * mVeTargetVolume; + f32 init = volume * mVeInitVolume; + + for (int j = 0; j < mChannelCount; j++) { + AxVoice* pAxVoice = mAxVoice[j][i]; + + if (pAxVoice != NULL) { + pAxVoice->SetVe(target, init); + } + } + } +} + +bool Voice::CalcAxMix() { + AxVoice::MixParam param; + AxVoice::RemoteMixParam rmtParam; + + bool nextUpdate = false; + + for (int i = 0; i < mChannelCount; i++) { + for (int j = 0; j < mVoiceOutCount; j++) { + AxVoice* pAxVoice = mAxVoice[i][j]; + if (pAxVoice == NULL) { + continue; + } + + CalcMixParam(i, j, ¶m, &rmtParam); + nextUpdate |= pAxVoice->SetMix(param); + + if (mOutputLineFlag == 0 || mOutputLineFlag == OUTPUT_LINE_MAIN) { + pAxVoice->EnableRemote(false); + } else { + pAxVoice->EnableRemote(true); + pAxVoice->SetRmtMix(rmtParam); + } + } + } + + return nextUpdate; +} + +void Voice::CalcAxLpf() { + for (int i = 0; i < mVoiceOutCount; i++) { + int freq = Util::CalcLpfFreq(mLpfFreq + mVoiceOutParam[i].lpf); + + for (int j = 0; j < mChannelCount; j++) { + AxVoice* pAxVoice = mAxVoice[j][i]; + + if (pAxVoice != NULL) { + pAxVoice->SetLpf(freq); + } + } + } +} + +void Voice::CalcAxRemoteFilter() { + for (int i = 0; i < mVoiceOutCount; i++) { + for (int j = 0; j < mChannelCount; j++) { + AxVoice* pAxVoice = mAxVoice[j][i]; + + if (pAxVoice != NULL) { + pAxVoice->SetRemoteFilter(mRemoteFilter); + } + } + } +} + +void Voice::SyncAxVoice() { + for (int i = 0; i < mChannelCount; i++) { + for (int j = 0; j < mVoiceOutCount; j++) { + AxVoice* pAxVoice = mAxVoice[i][j]; + + if (pAxVoice != NULL) { + pAxVoice->Sync(); + } + } + } +} + +void Voice::ResetDelta() { + for (int i = 0; i < mVoiceOutCount; i++) { + for (int j = 0; j < mChannelCount; j++) { + AxVoice* pAxVoice = mAxVoice[j][i]; + + if (pAxVoice != NULL) { + pAxVoice->ResetDelta(); + } + } + } +} + +void Voice::AxVoiceCallbackFunc(AxVoice* pDropVoice, + AxVoice::AxVoiceCallbackStatus status, + void* pCallbackArg) { + Voice* p = static_cast(pCallbackArg); + + VoiceCallbackStatus voiceStatus; + bool freeDropVoice = false; + + switch (status) { + case AxVoice::CALLBACK_STATUS_CANCEL: { + voiceStatus = CALLBACK_STATUS_CANCEL; + break; + } + + case AxVoice::CALLBACK_STATUS_DROP_DSP: { + voiceStatus = CALLBACK_STATUS_DROP_DSP; + freeDropVoice = true; + break; + } + } + + for (int i = 0; i < p->mChannelCount; i++) { + for (int j = 0; j < p->mVoiceOutCount; j++) { + AxVoice* pAxVoice = p->mAxVoice[i][j]; + + if (pAxVoice != NULL) { + if (pAxVoice == pDropVoice) { + if (!freeDropVoice) { + AxVoiceManager::GetInstance().FreeAxVoice(pAxVoice); + } + } else { + pAxVoice->Stop(); + AxVoiceManager::GetInstance().FreeAxVoice(pAxVoice); + } + + p->mAxVoice[i][j] = NULL; + } + } + } + + p->mPauseFlag = false; + p->mStartFlag = false; + p->mChannelCount = 0; + + if (freeDropVoice) { + p->Free(); + } + + if (p->mCallback != NULL) { + p->mCallback(p, voiceStatus, p->mCallbackData); + } +} + +void Voice::TransformDpl2Pan(f32* pPan, f32* pSurroundPan, f32 pan, + f32 surroundPan) { + surroundPan -= 1.0f; + + if (ut::Abs(pan) <= ut::Abs(surroundPan)) { + if (surroundPan <= 0.0f) { + *pPan = pan; + *pSurroundPan = -0.12f + 0.88f * surroundPan; + } else { + *pPan = 0.5f * pan; + *pSurroundPan = -0.12f + 1.12f * surroundPan; + } + } else if (pan >= 0.0f) { + if (surroundPan <= 0.0f) { + *pPan = + (0.85f + (1.0f - 0.85f) * (-surroundPan / pan)) * ut::Abs(pan); + *pSurroundPan = -0.12f + (2.0f * surroundPan + 0.88f * pan); + } else { + *pPan = + (0.85f + (1.0f - 0.65f) * (-surroundPan / pan)) * ut::Abs(pan); + *pSurroundPan = -0.12f + 1.12f * pan; + } + } else if (surroundPan <= 0.0f) { + *pPan = ((1.0f - 0.85f) * (-surroundPan / pan) - 0.85f) * ut::Abs(pan); + *pSurroundPan = -0.12f + (2.0f * surroundPan - 1.12f * pan); + } else { + *pPan = ((1.0f - 0.65f) * (-surroundPan / pan) - 0.85f) * ut::Abs(pan); + *pSurroundPan = -0.12f + 1.12f * -pan; + } + + *pSurroundPan += 1.0f; +} + +void Voice::CalcMixParam(int channel, int voice, AxVoice::MixParam* pMix, + AxVoice::RemoteMixParam* pRmtMix) { + f32 mainVolume = 0.0f; + f32 mainSend = 0.0f; + + f32 fxSendA = 0.0f; + f32 fxSendB = 0.0f; + f32 fxSendC = 0.0f; + + if (mOutputLineFlag & OUTPUT_LINE_MAIN) { + mainVolume = mMainOutVolume; + mainSend = mMainSend; + + fxSendA = ut::Clamp(mFxSend[AUX_A] + mVoiceOutParam[voice].fxSend, 0.0f, + 1.0f); + fxSendB = mFxSend[AUX_B]; + fxSendC = mFxSend[AUX_C]; + } + + f32 main = mainVolume * mainSend; + f32 fx_a = mainVolume * fxSendA; + f32 fx_b = mainVolume * fxSendB; + f32 fx_c = mainVolume * fxSendC; + + f32 remote[WPAD_MAX_CONTROLLERS]; + f32 remoteFx[WPAD_MAX_CONTROLLERS]; + for (int i = 0; i < WPAD_MAX_CONTROLLERS; i++) { + f32 remoteVolume = 0.0f; + f32 remoteSend = 0.0f; + f32 remoteFxSend = 0.0f; + + if (mOutputLineFlag & (OUTPUT_LINE_REMOTE_N << i)) { + remoteVolume = mRemoteOutVolume[i]; + } + + remote[i] = remoteVolume * remoteSend; + remoteFx[i] = remoteVolume * remoteFxSend; + } + + f32 left, right, surround, lrMixed; + f32 front, rear; + + Util::PanInfo panInfo; + + switch (mPanCurve) { + case PAN_CURVE_SQRT: { + panInfo.curve = Util::PAN_CURVE_SQRT; + break; + } + case PAN_CURVE_SQRT_0DB: { + panInfo.curve = Util::PAN_CURVE_SQRT; + panInfo.centerZero = true; + break; + } + case PAN_CURVE_SQRT_0DB_CLAMP: { + panInfo.curve = Util::PAN_CURVE_SQRT; + panInfo.centerZero = true; + panInfo.zeroClamp = true; + break; + } + + case PAN_CURVE_SINCOS: { + panInfo.curve = Util::PAN_CURVE_SINCOS; + break; + } + case PAN_CURVE_SINCOS_0DB: { + panInfo.curve = Util::PAN_CURVE_SINCOS; + panInfo.centerZero = true; + break; + } + case PAN_CURVE_SINCOS_0DB_CLAMP: { + panInfo.curve = Util::PAN_CURVE_SINCOS; + panInfo.centerZero = true; + panInfo.zeroClamp = true; + break; + } + + case PAN_CURVE_LINEAR: { + panInfo.curve = Util::PAN_CURVE_LINEAR; + break; + } + case PAN_CURVE_LINEAR_0DB: { + panInfo.curve = Util::PAN_CURVE_LINEAR; + panInfo.centerZero = true; + break; + } + case PAN_CURVE_LINEAR_0DB_CLAMP: { + panInfo.curve = Util::PAN_CURVE_LINEAR; + panInfo.centerZero = true; + panInfo.zeroClamp = true; + break; + } + + default: { + panInfo.curve = Util::PAN_CURVE_SQRT; + } + } + + if (mChannelCount > 1 && mPanMode == PAN_MODE_BALANCE) { + f32 pan = mPan + mVoiceOutParam[voice].pan; + f32 surroundPan = mSurroundPan + mVoiceOutParam[voice].surroundPan; + + if (channel == 0) { + left = Util::CalcPanRatio(pan, panInfo); + right = 0.0f; + } else if (channel == 1) { + left = 0.0f; + right = Util::CalcPanRatio(-pan, panInfo); + } + + front = Util::CalcSurroundPanRatio(surroundPan, panInfo); + rear = Util::CalcSurroundPanRatio(2.0f - surroundPan, panInfo); + } else { + f32 voicePan = 0.0f; + f32 pan, surroundPan; + + if (mChannelCount == 2) { + if (channel == 0) { + voicePan = -1.0f; + } + if (channel == 1) { + voicePan = 1.0f; + } + } + + switch (AxManager::GetInstance().GetOutputMode()) { + case OUTPUT_MODE_DPL2: { + TransformDpl2Pan(&pan, &surroundPan, + mPan + voicePan + mVoiceOutParam[voice].pan, + mSurroundPan + mVoiceOutParam[voice].surroundPan); + break; + } + + case OUTPUT_MODE_STEREO: + case OUTPUT_MODE_SURROUND: + case OUTPUT_MODE_MONO: + default: { + pan = mPan + voicePan + mVoiceOutParam[voice].pan; + surroundPan = mSurroundPan + mVoiceOutParam[voice].surroundPan; + break; + } + } + + left = Util::CalcPanRatio(pan, panInfo); + right = Util::CalcPanRatio(-pan, panInfo); + front = Util::CalcSurroundPanRatio(surroundPan, panInfo); + rear = Util::CalcSurroundPanRatio(2.0f - surroundPan, panInfo); + } + + surround = Util::CalcVolumeRatio(-3.0f); + lrMixed = 0.5f * (left + right); + + f32 m_l, m_r, m_s; + f32 a_l, a_r, a_s; + f32 b_l, b_r, b_s; + f32 c_l, c_r, c_s; + + f32& m_sl = m_s; + f32& m_sr = c_l; + + f32& a_sl = a_s; + f32& a_sr = c_r; + + f32& b_sl = b_s; + f32& b_sr = c_s; + + switch (AxManager::GetInstance().GetOutputMode()) { + case OUTPUT_MODE_STEREO: { + m_l = main * left; + m_r = main * right; + m_s = 0.0f; + + a_l = fx_a * left; + a_r = fx_a * right; + a_s = 0.0f; + + b_l = fx_b * left; + b_r = fx_b * right; + b_s = 0.0f; + + c_l = fx_c * left; + c_r = fx_c * right; + c_s = 0.0f; + break; + } + + case OUTPUT_MODE_MONO: { + m_l = main * lrMixed; + m_r = main * lrMixed; + m_s = 0.0f; + + a_l = fx_a * lrMixed; + a_r = fx_a * lrMixed; + a_s = 0.0f; + + b_l = fx_b * lrMixed; + b_r = fx_b * lrMixed; + b_s = 0.0f; + + c_l = fx_c * lrMixed; + c_r = fx_c * lrMixed; + c_s = 0.0f; + break; + } + + case OUTPUT_MODE_SURROUND: { + f32 fl = left * front; + f32 fr = right * front; + f32 rs = surround * rear; + + m_l = main * fl; + m_r = main * fr; + m_s = main * rs; + + a_l = fx_a * fl; + a_r = fx_a * fr; + a_s = fx_a * rs; + + b_l = fx_b * fl; + b_r = fx_b * fr; + b_s = fx_b * rs; + + c_l = fx_c * fl; + c_r = fx_c * fr; + c_s = fx_c * rs; + break; + } + + case OUTPUT_MODE_DPL2: { + f32 fl = left * front; + f32 fr = right * front; + f32 rl = left * rear; + f32 rr = right * rear; + + m_l = main * fl; + m_r = main * fr; + m_sl = main * rl; + m_sr = main * rr; + + a_l = fx_a * fl; + a_r = fx_a * fr; + a_sl = fx_a * rl; + a_sr = fx_a * rr; + + b_l = fx_b * fl; + b_r = fx_b * fr; + b_sl = fx_b * rl; + b_sr = fx_b * rr; + break; + } + + default: { + break; + } + } + + f32 rmt[WPAD_MAX_CONTROLLERS]; + f32 rmtFx[WPAD_MAX_CONTROLLERS]; + for (int i = 0; i < WPAD_MAX_CONTROLLERS; i++) { + rmt[i] = lrMixed * remote[i]; + rmtFx[i] = lrMixed * remoteFx[i]; + } + + pMix->vL = CalcMixVolume(m_l); + pMix->vR = CalcMixVolume(m_r); + pMix->vS = CalcMixVolume(m_s); + + pMix->vAuxAL = CalcMixVolume(a_l); + pMix->vAuxAR = CalcMixVolume(a_r); + pMix->vAuxAS = CalcMixVolume(a_s); + + pMix->vAuxBL = CalcMixVolume(b_l); + pMix->vAuxBR = CalcMixVolume(b_r); + pMix->vAuxBS = CalcMixVolume(b_s); + + pMix->vAuxCL = CalcMixVolume(c_l); + pMix->vAuxCR = CalcMixVolume(c_r); + pMix->vAuxCS = CalcMixVolume(c_s); + + pRmtMix->vMain0 = CalcMixVolume(rmt[0]); + pRmtMix->vAux0 = 0; + + pRmtMix->vMain1 = CalcMixVolume(rmt[1]); + pRmtMix->vAux1 = 0; + + pRmtMix->vMain2 = CalcMixVolume(rmt[2]); + pRmtMix->vAux2 = 0; + + pRmtMix->vMain3 = CalcMixVolume(rmt[3]); + pRmtMix->vAux3 = 0; +} + +void Voice::RunAllAxVoice() { + for (int i = 0; i < mChannelCount; i++) { + for (int j = 0; j < mVoiceOutCount; j++) { + if (mAxVoice[i][j] != NULL) { + mAxVoice[i][j]->Run(); + } + } + } +} + +void Voice::StopAllAxVoice() { + for (int i = 0; i < mChannelCount; i++) { + for (int j = 0; j < mVoiceOutCount; j++) { + if (mAxVoice[i][j] != NULL) { + mAxVoice[i][j]->Stop(); + } + } + } +} + +void Voice::InvalidateWaveData(const void* pStart, const void* pEnd) { + bool dispose = false; + + for (int i = 0; i < mChannelCount; i++) { + AxVoice* pAxVoice = mAxVoice[i][0]; + + if (pAxVoice != NULL && pAxVoice->IsDataAddressCoverd(pStart, pEnd)) { + dispose = true; + break; + } + } + + if (dispose) { + Stop(); + + if (mCallback != NULL) { + mCallback(this, CALLBACK_STATUS_CANCEL, mCallbackData); + } + } +} + +} // namespace detail +} // namespace snd +} // namespace nw4r diff --git a/src/nw4r/snd/snd_VoiceManager.cpp b/src/nw4r/snd/snd_VoiceManager.cpp index df1bd462..2ff8511c 100644 --- a/src/nw4r/snd/snd_VoiceManager.cpp +++ b/src/nw4r/snd/snd_VoiceManager.cpp @@ -1 +1,207 @@ #include "nw4r/snd/snd_VoiceManager.h" +#include "nw4r/snd/snd_DisposeCallbackManager.h" +#include "new.h" + +namespace nw4r { +namespace snd { +namespace detail { + +VoiceManager& VoiceManager::GetInstance() { + static VoiceManager instance; + return instance; +} + +VoiceManager::VoiceManager() : mInitialized(false) {} + +u32 VoiceManager::GetRequiredMemSize(int numVoices) { + return numVoices * sizeof(Voice); +} + +void VoiceManager::Setup(void* pBuffer, u32 size) { + if (mInitialized) { + return; + } + + u32 voices = size / sizeof(Voice); + u8* pPtr = static_cast(pBuffer); + + for (u32 i = 0; i < voices; i++) { + Voice* pVoice = new (pPtr) Voice(); + mFreeVoiceList.PushBack(pVoice); + pPtr += sizeof(Voice); + } + + mInitialized = true; +} + +void VoiceManager::Shutdown() { + if (!mInitialized) { + return; + } + + StopAllVoices(); + + while (!mFreeVoiceList.IsEmpty()) { + Voice& rVoice = mFreeVoiceList.GetFront(); + mFreeVoiceList.PopFront(); + rVoice.~Voice(); + } + + mInitialized = false; +} + +void VoiceManager::StopAllVoices() { + ut::AutoInterruptLock lock; + + while (!mPrioVoiceList.IsEmpty()) { + Voice& rVoice = mPrioVoiceList.GetFront(); + + rVoice.Stop(); + + if (rVoice.mCallback != NULL) { + rVoice.mCallback(&rVoice, Voice::CALLBACK_STATUS_CANCEL, + rVoice.mCallbackData); + } + + rVoice.Free(); + } +} + +Voice* VoiceManager::AllocVoice(int channels, int voices, int priority, + Voice::VoiceCallback pCallback, + void* pCallbackArg) { + ut::AutoInterruptLock lock; + + if (mFreeVoiceList.IsEmpty() && DropLowestPriorityVoice(priority) == 0) { + return NULL; + } + + Voice& rVoice = mFreeVoiceList.GetFront(); + if (!rVoice.Acquire(channels, voices, priority, pCallback, pCallbackArg)) { + return NULL; + } + + rVoice.mPriority = priority & Voice::PRIORITY_MAX; + AppendVoiceList(&rVoice); + UpdateEachVoicePriority(mPrioVoiceList.GetIteratorFromPointer(&rVoice), + mPrioVoiceList.GetEndIter()); + DisposeCallbackManager::GetInstance().RegisterDisposeCallback(&rVoice); + + return &rVoice; +} + +void VoiceManager::FreeVoice(Voice* pVoice) { + ut::AutoInterruptLock lock; + + DisposeCallbackManager::GetInstance().UnregisterDisposeCallback(pVoice); + RemoveVoiceList(pVoice); +} + +void VoiceManager::UpdateAllVoices() { + NW4R_UT_LIST_SAFE_FOREACH(mPrioVoiceList, + it->StopFinished(); + ); + + NW4R_UT_LIST_SAFE_FOREACH(mPrioVoiceList, + it->Calc(); + ); + + ut::AutoInterruptLock lock; + + NW4R_UT_LIST_SAFE_FOREACH(mPrioVoiceList, + it->Update(); + ); +} + +void VoiceManager::NotifyVoiceUpdate() { + ut::AutoInterruptLock lock; + + NW4R_UT_LIST_SAFE_FOREACH(mPrioVoiceList, + it->ResetDelta(); + ); +} + +void VoiceManager::AppendVoiceList(Voice* pVoice) { + ut::AutoInterruptLock lock; + + mFreeVoiceList.Erase(pVoice); + + VoiceList::RevIterator it = mPrioVoiceList.GetEndReverseIter(); + for (; it != mPrioVoiceList.GetBeginReverseIter(); ++it) { + if (it->GetPriority() <= pVoice->GetPriority()) { + break; + } + } + + mPrioVoiceList.Insert(it.GetBase(), pVoice); +} + +void VoiceManager::RemoveVoiceList(Voice* pVoice) { + ut::AutoInterruptLock lock; + + mPrioVoiceList.Erase(pVoice); + mFreeVoiceList.PushBack(pVoice); +} + +void VoiceManager::ChangeVoicePriority(Voice* pVoice) { + ut::AutoInterruptLock lock; + + RemoveVoiceList(pVoice); + AppendVoiceList(pVoice); + + UpdateEachVoicePriority(mPrioVoiceList.GetIteratorFromPointer(pVoice), + mPrioVoiceList.GetEndIter()); +} + +void VoiceManager::UpdateEachVoicePriority(const VoiceList::Iterator& rBegin, + const VoiceList::Iterator& rEnd) { + for (VoiceList::Iterator it = rBegin; it != rEnd; ++it) { + if (it->GetPriority() <= 1) { + return; + } + + if (it->GetPriority() != Voice::PRIORITY_MAX) { + it->UpdateVoicesPriority(); + } + } +} + +void VoiceManager::UpdateAllVoicesSync(u32 syncFlag) { + ut::AutoInterruptLock lock; + + // clang-format off + NW4R_UT_LIST_SAFE_FOREACH(mPrioVoiceList, + if (it->mActiveFlag) { + it->mSyncFlag |= syncFlag; + } + ); + // clang-format on +} + +int VoiceManager::DropLowestPriorityVoice(int priority) { + int dropped = 0; + + if (mFreeVoiceList.IsEmpty()) { + Voice& rVoice = mPrioVoiceList.GetFront(); + + if (rVoice.GetPriority() > priority) { + return 0; + } + + dropped = rVoice.GetAxVoiceCount(); + + rVoice.Stop(); + rVoice.Free(); + + if (rVoice.mCallback != NULL) { + rVoice.mCallback(&rVoice, Voice::CALLBACK_STATUS_DROP_VOICE, + rVoice.mCallbackData); + } + } + + return dropped; +} + +} // namespace detail +} // namespace snd +} // namespace nw4r