#include "nw4r/snd/snd_Voice.h" /* Original source: * kiwi515/ogws * src/nw4r/snd/snd_Voice.cpp */ /******************************************************************************* * headers */ #include #include "common.h" #include "nw4r/snd/snd_AxManager.h" #include "nw4r/snd/snd_AxVoiceManager.h" #include "nw4r/snd/snd_AxVoice.h" #include "nw4r/snd/snd_adpcm.h" #include "nw4r/snd/snd_global.h" #include "nw4r/snd/snd_Util.h" #include "nw4r/snd/snd_VoiceManager.h" #include "nw4r/snd/snd_WaveFile.h" #include "nw4r/ut/ut_algorithm.h" #include "nw4r/ut/ut_Lock.h" #include // AX_MAX_VOLUME #include "nw4r/NW4RAssert.hpp" /******************************************************************************* * local function declarations */ namespace nw4r { namespace snd { namespace detail { inline u16 CalcMixVolume(f32 volume) { if (volume <= 0.0f) return 0; return ut::Min(65535, AX_MAX_VOLUME * volume); } }}} // namespace nw4r::snd::detail /******************************************************************************* * variables */ namespace nw4r { namespace snd { namespace detail { #if defined(BETTER_OBJDIFF_DIFF) # define SEND_MAX 1.0f # define SEND_MIN 0.0f # define BIQUAD_VALUE_MAX 1.0f # define BIQUAD_VALUE_MIN 0.0f # define PAN_CENTER 0.0f # define PAN_RIGHT 1.0f # define PAN_LEFT -1.0f # define VOLUME_MAX 1.0f # define VOLUME_MIN 0.0f #else f32 const Voice::SEND_MAX = 1.0f; f32 const Voice::SEND_MIN = 0.0f; f32 const Voice::BIQUAD_VALUE_MAX = 1.0f; f32 const Voice::BIQUAD_VALUE_MIN = 0.0f; f32 const Voice::PAN_CENTER = 0.0f; f32 const Voice::PAN_RIGHT = 1.0f; f32 const Voice::PAN_LEFT = -1.0f; f32 const Voice::VOLUME_MAX = 1.0f; f32 const Voice::VOLUME_MIN = 0.0f; #endif }}} // namespace nw4r::snd::detail /******************************************************************************* * functions */ namespace nw4r { namespace snd { namespace detail { Voice::Voice() : mCallback (nullptr), mActiveFlag (false), mStartFlag (false), mStartedFlag (false), mPauseFlag (false), mSyncFlag (0) { for (int channelIndex = 0; channelIndex < CHANNEL_MAX; channelIndex++) { for (int voiceOutIndex = 0; voiceOutIndex < 4; voiceOutIndex++) mAxVoice[channelIndex][voiceOutIndex] = nullptr; } } Voice::~Voice() { for (int channelIndex = 0; channelIndex < CHANNEL_MAX; channelIndex++) { for (int voiceOutIndex = 0; voiceOutIndex < 4; voiceOutIndex++) { if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) AxVoiceManager::GetInstance().FreeAxVoice(axVoice); } } } void Voice::InitParam(int channelCount, int voiceOutCount, Callback *callback, void *callbackData) { // specifically not the source variants NW4RAssertHeaderClampedLRValue_Line(128, channelCount, 1, CHANNEL_MAX); NW4RAssertHeaderClampedLRValue_Line(129, voiceOutCount, 1, 4); mChannelCount = channelCount; mVoiceOutCount = voiceOutCount; mCallback = callback; mCallbackData = callbackData; mSyncFlag = 0; mPauseFlag = false; mPausingFlag = false; mStartedFlag = false; mVoiceOutParamPitchDisableFlag = false; mVolume = 1.0f; mVeInitVolume = 0.0f; mVeTargetVolume = 1.0f; mLpfFreq = 1.0f; mBiquadType = 0; mBiquadValue = 0.0f; mPan = 0.0f; mSurroundPan = 0.0f; mOutputLineFlag = 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 < 4; 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) (*mCallback)(this, CALLBACK_STATUS_FINISH_WAVE, mCallbackData); mStartedFlag = false; mStartFlag = false; } } void Voice::Calc() { if (!mStartFlag) return; if (mSyncFlag & UPDATE_SRC) { CalcAxSrc(false); mSyncFlag &= ~UPDATE_SRC; } if (mSyncFlag & UPDATE_VE) { CalcAxVe(); mSyncFlag &= ~UPDATE_VE; } if (mSyncFlag & UPDATE_MIX) { bool nextUpdateFlag = CalcAxMix(); if (!nextUpdateFlag) mSyncFlag &= ~UPDATE_MIX; } if (mSyncFlag & UPDATE_LPF) { CalcAxLpf(); mSyncFlag &= ~UPDATE_LPF; } if (mSyncFlag & UPDATE_BIQUAD) { CalcAxBiquadFilter(); mSyncFlag &= ~UPDATE_BIQUAD; } if (mSyncFlag & UPDATE_REMOTE_FILTER) { CalcAxRemoteFilter(); mSyncFlag &= ~UPDATE_REMOTE_FILTER; } } void Voice::Update() { ut::AutoInterruptLock lock; if (!mActiveFlag) return; enum { NONE, RUN, STOP, } runFlag = NONE; if (mSyncFlag & UPDATE_START && mStartFlag && !mStartedFlag) { CalcAxSrc(true); runFlag = RUN; mStartedFlag = true; mSyncFlag &= ~UPDATE_START; mSyncFlag &= ~UPDATE_SRC; } if (mStartedFlag) { if (mSyncFlag & UPDATE_PAUSE && mStartFlag) { if (mPauseFlag) { mPausingFlag = true; runFlag = STOP; } else { mPausingFlag = false; runFlag = RUN; } mSyncFlag &= ~UPDATE_PAUSE; } SyncAxVoice(); } switch (runFlag) { case RUN: RunAllAxVoice(); break; case STOP: StopAllAxVoice(); break; } } bool Voice::Acquire(int channelCount, int voiceOutCount, int priority, Callback *callback, void *callbackData) { NW4RAssertHeaderClampedLRValue_Line(336, channelCount, 1, CHANNEL_MAX); channelCount = ut::Clamp(channelCount, 1, CHANNEL_MAX); NW4RAssertHeaderClampedLRValue_Line(339, voiceOutCount, 1, 4); voiceOutCount = ut::Clamp(voiceOutCount, 1, 4); ut::AutoInterruptLock lock; u32 axPriority = priority == PRIORITY_MAX ? VOICE_PRIORITY_MAX : 16; NW4RAssert_Line(346, ! mActiveFlag); int requiredVoiceCount = channelCount * voiceOutCount; AxVoice *voiceTable[CHANNEL_MAX * 4]; for (int i = 0; i < requiredVoiceCount; i++) { AxVoice *axVoice = nullptr; axVoice = AxVoiceManager::GetInstance().AcquireAxVoice( axPriority, &AxVoiceCallbackFunc, this); if (!axVoice) { int restAXVPBCount = requiredVoiceCount - i; Voice::LinkList const &voiceList = VoiceManager::GetInstance().GetVoiceList(); NW4R_RANGE_FOR(itr, voiceList) { if (priority < itr->GetPriority()) break; restAXVPBCount -= itr->GetPhysicalVoiceCount(); if (restAXVPBCount <= 0) break; } if (restAXVPBCount > 0) { for (int j = 0; j < i; j++) AxVoiceManager::GetInstance().FreeAxVoice(voiceTable[j]); return false; } u32 allocPriority = axPriority == VOICE_PRIORITY_MAX ? VOICE_PRIORITY_MAX : 17; axVoice = AxVoiceManager::GetInstance().AcquireAxVoice( allocPriority, &AxVoiceCallbackFunc, this); } NW4RAssertPointerNonnull_Line(399, axVoice); if (!axVoice) { for (int j = 0; j < i; j++) AxVoiceManager::GetInstance().FreeAxVoice(voiceTable[j]); return false; } voiceTable[i] = axVoice; } int axVoiceIndex = 0; for (int channelIndex = 0; channelIndex < channelCount; channelIndex++) { for (int voiceOutIndex = 0; voiceOutIndex < voiceOutCount; voiceOutIndex++) { voiceTable[axVoiceIndex]->SetPriority(axPriority); mAxVoice[channelIndex][voiceOutIndex] = voiceTable[axVoiceIndex]; axVoiceIndex++; } } InitParam(channelCount, voiceOutCount, callback, callbackData); mActiveFlag = true; return true; } void Voice::Free() { ut::AutoInterruptLock lock; if (!mActiveFlag) return; for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) { for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) { if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) { AxVoiceManager::GetInstance().FreeAxVoice(axVoice); mAxVoice[channelIndex][voiceOutIndex] = nullptr; } } } mChannelCount = 0; VoiceManager::GetInstance().FreeVoice(this); mActiveFlag = false; } void Voice::Setup(WaveInfo const &waveParam, u32 startOffset) { int sampleRate = waveParam.sampleRate; for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) { if (!mAxVoice[channelIndex][0]) continue; NW4RAssertPointerNonnull_Line( 477, waveParam.channelParam[channelIndex].dataAddr); void *dataAddr = waveParam.channelParam[channelIndex].dataAddr; AdpcmParam adpcmParam; if (waveParam.sampleFormat == SAMPLE_FORMAT_DSP_ADPCM) { adpcmParam = waveParam.channelParam[channelIndex].adpcmParam; AxVoice::CalcOffsetAdpcmParam(&adpcmParam.pred_scale, &adpcmParam.yn1, &adpcmParam.yn2, startOffset, dataAddr, adpcmParam); } for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) { AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]; if (!axVoice) continue; axVoice->Setup(waveParam.channelParam[channelIndex].dataAddr, waveParam.sampleFormat, sampleRate); axVoice->SetAddr(waveParam.loopFlag, dataAddr, startOffset, waveParam.loopStart, waveParam.loopEnd); if (waveParam.sampleFormat == SAMPLE_FORMAT_DSP_ADPCM) { axVoice->SetAdpcm(&adpcmParam); axVoice->SetAdpcmLoop( &waveParam.channelParam[channelIndex].adpcmLoopParam); } axVoice->SetSrcType(AxManager::GetInstance().GetSrcType(), mPitch); axVoice->SetVoiceType(AxVoice::VOICE_TYPE_NORMAL); } } for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) { mVoiceOutParam[voiceOutIndex].volume = 1.0f; mVoiceOutParam[voiceOutIndex].pitch = 1.0f; mVoiceOutParam[voiceOutIndex].pan = 0.0f; mVoiceOutParam[voiceOutIndex].surroundPan = 0.0f; mVoiceOutParam[voiceOutIndex].fxSend = 0.0f; mVoiceOutParam[voiceOutIndex].lpf = 0.0f; } mPauseFlag = false; mPausingFlag = false; mStartFlag = false; mStartedFlag = false; mSyncFlag |= UPDATE_MIX; mSyncFlag |= UPDATE_VE; mSyncFlag |= UPDATE_LPF; } void Voice::Start() { mStartFlag = true; mPauseFlag = false; mSyncFlag |= UPDATE_START; } void Voice::Stop() { if (mStartedFlag) { StopAllAxVoice(); mStartedFlag = false; } mPausingFlag = false; mPauseFlag = false; mStartFlag = false; } void Voice::Pause(bool flag) { if (mPauseFlag != flag) { mPauseFlag = flag; mSyncFlag |= UPDATE_PAUSE; } } SampleFormat Voice::GetFormat() const { NW4RAssert_Line(583, IsActive()); if (IsActive()) return mAxVoice[0][0]->GetFormat(); return SAMPLE_FORMAT_PCM_S16; } void Voice::SetVolume(f32 volume) { if (volume < 0.0f) volume = 0.0f; if (volume != mVolume) { mVolume = volume; mSyncFlag |= UPDATE_VE; } } void Voice::SetVeVolume(f32 targetVolume, f32 initVolume) { if (targetVolume < 0.0f) targetVolume = 0.0f; if (initVolume < 0.0f) initVolume = 0.0f; if (initVolume < 0.0f) { // NOTE: unreachable (initVolume was clamped) if (targetVolume == mVeTargetVolume) return; mVeTargetVolume = targetVolume; mSyncFlag |= UPDATE_VE; return; } if (initVolume != mVeInitVolume || targetVolume != mVeTargetVolume) { mVeInitVolume = initVolume; mVeTargetVolume = targetVolume; mSyncFlag |= UPDATE_VE; } } void Voice::SetPitch(f32 pitch) { if (pitch != mPitch) { mPitch = pitch; mSyncFlag |= UPDATE_SRC; } } void Voice::SetPanMode(PanMode panMode) { if (panMode != mPanMode) { mPanMode = panMode; mSyncFlag |= UPDATE_MIX; } } void Voice::SetPanCurve(PanCurve panCurve) { if (panCurve != mPanCurve) { mPanCurve = panCurve; mSyncFlag |= UPDATE_MIX; } } void Voice::SetPan(f32 pan) { if (pan != mPan) { mPan = pan; mSyncFlag |= UPDATE_MIX; } } void Voice::SetSurroundPan(f32 pan) { if (pan != mSurroundPan) { mSurroundPan = pan; mSyncFlag |= UPDATE_MIX; } } void Voice::SetLpfFreq(f32 freq) { if (freq != mLpfFreq) { mLpfFreq = freq; mSyncFlag |= UPDATE_LPF; } } void Voice::SetBiquadFilter(int type, f32 value) { // specifically not the source variant NW4RAssertHeaderClampedLRValue_Line(680, type, 0, 127); value = ut::Clamp(value, BIQUAD_VALUE_MIN, BIQUAD_VALUE_MAX); bool isUpdate = false; if (type != mBiquadType) { mBiquadType = type; isUpdate = true; } if (value != mBiquadValue) { mBiquadValue = value; isUpdate = true; } if (isUpdate) mSyncFlag |= UPDATE_BIQUAD; } void Voice::SetRemoteFilter(int filter) { filter = ut::Clamp(filter, REMOTE_FILTER_MIN, REMOTE_FILTER_MAX); if (filter != mRemoteFilter) { mRemoteFilter = filter; mSyncFlag |= UPDATE_REMOTE_FILTER; } } void Voice::SetOutputLine(int lineFlag) { if (lineFlag != mOutputLineFlag) { mOutputLineFlag = lineFlag; mSyncFlag |= UPDATE_MIX; } } void Voice::SetMainOutVolume(f32 volume) { if (volume < 0.0f) volume = 0.0f; if (volume != mMainOutVolume) { mMainOutVolume = volume; mSyncFlag |= UPDATE_MIX; } } void Voice::SetMainSend(f32 send) { send += 1.0f; if (send < SEND_MIN) send = SEND_MIN; if (send != mMainSend) { mMainSend = send; mSyncFlag |= UPDATE_MIX; } } void Voice::SetFxSend(AuxBus bus, f32 send) { // specifically not the source variant NW4RAssertHeaderClampedLValue_Line(748, bus, 0, AUX_BUS_NUM); if (send < SEND_MIN) send = SEND_MIN; if (send != mFxSend[bus]) { mFxSend[bus] = send; mSyncFlag |= UPDATE_MIX; } } void Voice::SetRemoteOutVolume(int remote, f32 volume) { if (volume < 0.0f) volume = 0.0f; if (mRemoteOutVolume[remote] == volume) return; mRemoteOutVolume[remote] = volume; mSyncFlag |= UPDATE_MIX; } void Voice::SetVoiceOutParam(int voiceOutIndex, VoiceOutParam const &voiceOutParam) { // specifically not the source variant NW4RAssertHeaderClampedLRValue_Line(820, voiceOutIndex, 0, 4); mVoiceOutParam[voiceOutIndex] = voiceOutParam; mSyncFlag |= UPDATE_SRC | UPDATE_VE | UPDATE_MIX | UPDATE_LPF; } void Voice::SetPriority(int priority) { // specifically not the source variant NW4RAssertHeaderClampedLRValue_Line(828, priority, PRIORITY_MIN, PRIORITY_MAX); mPriority = priority; VoiceManager::GetInstance().ChangeVoicePriority(this); if (mPriority != AX_PRIORITY_MIN) return; for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) { for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) { if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) axVoice->SetPriority(15); } } } void Voice::UpdateVoicesPriority() { if (mPriority == AX_PRIORITY_MIN) return; for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) { for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) { if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) axVoice->SetPriority(16); } } } #if 0 // Voice::GetAxVoice ([R89JEL]:/bin/RVL/Debug/mainD.MAP:14824) DECOMP_FORCE(NW4RAssert_String(channelIndex < CHANNEL_MAX)); #endif void Voice::SetAdpcmLoop(int channelIndex, AdpcmLoopParam const *param) { for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) { if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) axVoice->SetAdpcmLoop(param); } } u32 Voice::GetCurrentPlayingSample() const { if (IsActive()) return mAxVoice[0][0]->GetCurrentPlayingSample(); return 0; } void Voice::SetLoopStart(int channelIndex, void const *baseAddress, u32 samples) { for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) { if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) axVoice->SetLoopStart(baseAddress, samples); } } void Voice::SetLoopEnd(int channelIndex, void const *baseAddress, u32 samples) { for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) { if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) axVoice->SetLoopEnd(baseAddress, samples); } } void Voice::SetLoopFlag(bool loopFlag) { for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) { for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) { if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) axVoice->SetLoopFlag(loopFlag); } } } void Voice::StopAtPoint(int channelIndex, void const *baseAddress, u32 samples) { for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) { if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) axVoice->StopAtPoint(baseAddress, samples); } } void Voice::SetVoiceType(AxVoice::VoiceType type) { for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) { for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) { if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) axVoice->SetVoiceType(type); } } } void Voice::CalcAxSrc(bool initialUpdate) { for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) { f32 ratio = mPitch; if (!mVoiceOutParamPitchDisableFlag) ratio *= mVoiceOutParam[voiceOutIndex].pitch; for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) { if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) axVoice->SetSrc(ratio, initialUpdate); } } } void Voice::CalcAxVe() { f32 baseVolume = 1.0f; baseVolume *= mVolume; baseVolume *= AxManager::GetInstance().GetOutputVolume(); for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) { f32 volume = baseVolume * mVoiceOutParam[voiceOutIndex].volume; f32 targetVolume = volume * mVeTargetVolume; f32 initVolume = volume * mVeInitVolume; for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) { if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) axVoice->SetVe(targetVolume, initVolume); } } } bool Voice::CalcAxMix() { bool nextUpdateFlag = false; AxVoice::MixParam mix; /* The address is taken and the members are set, but the members aren't used * after that */ AxVoice::RemoteMixParam rmtmix; for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) { for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) { if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) { CalcMixParam(channelIndex, voiceOutIndex, &mix, &rmtmix); nextUpdateFlag |= axVoice->SetMix(mix); if (mOutputLineFlag == 0 || mOutputLineFlag == OUTPUT_LINE_MAIN) { axVoice->EnableRemote(false); } else { axVoice->EnableRemote(true); axVoice->SetRmtMix(rmtmix); } } } } return nextUpdateFlag; } void Voice::CalcAxLpf() { for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) { u16 freq = Util::CalcLpfFreq(mLpfFreq + mVoiceOutParam[voiceOutIndex].lpf); for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) { if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) axVoice->SetLpf(freq); } } } void Voice::CalcAxBiquadFilter() { for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) { for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) { if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) axVoice->SetBiquad(mBiquadType, mBiquadValue); } } } void Voice::CalcAxRemoteFilter() { for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) { for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) { if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) axVoice->SetRemoteFilter(mRemoteFilter); } } } void Voice::SyncAxVoice() { for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) { for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) { // What if (mAxVoice[channelIndex][voiceOutIndex]) mAxVoice[channelIndex][voiceOutIndex]->Sync(); } } } void Voice::ResetDelta() { for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) { for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) { if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) axVoice->ResetDelta(); } } } void Voice::AxVoiceCallbackFunc(AxVoice *dropVoice, AxVoice::AxVoiceCallbackStatus status, void *callbackData) { Voice *voice = static_cast(callbackData); NW4RAssertPointerNonnull_Line(1165, voice); VoiceCallbackStatus voiceStatus; bool freeDropVoice = false; switch (status) { case AxVoice::CALLBACK_STATUS_CANCEL: voiceStatus = CALLBACK_STATUS_CANCEL; break; case AxVoice::CALLBACK_STATUS_DROP_DSP: voiceStatus = CALLBACK_STATUS_DROP_DSP; freeDropVoice = true; break; } for (int channelIndex = 0; channelIndex < voice->mChannelCount; channelIndex++) { for (int voiceOutIndex = 0; voiceOutIndex < voice->mVoiceOutCount; voiceOutIndex++) { AxVoice *axVoice = voice->mAxVoice[channelIndex][voiceOutIndex]; if (!axVoice) continue; if (axVoice == dropVoice) { if (!freeDropVoice) AxVoiceManager::GetInstance().FreeAxVoice(axVoice); } else { axVoice->Stop(); AxVoiceManager::GetInstance().FreeAxVoice(axVoice); } voice->mAxVoice[channelIndex][voiceOutIndex] = nullptr; } } voice->mPauseFlag = false; voice->mStartFlag = false; voice->mChannelCount = 0; if (freeDropVoice) voice->Free(); if (voice->mCallback) (*voice->mCallback)(voice, voiceStatus, voice->mCallbackData); } void Voice::TransformDpl2Pan(f32 *outPan, f32 *outSurroundPan, f32 inPan, f32 inSurroundPan) { inSurroundPan -= 1.0f; if (ut::Abs(inPan) <= ut::Abs(inSurroundPan)) { if (inSurroundPan <= 0.0f) { *outPan = inPan; *outSurroundPan = -0.12f + 0.88f * inSurroundPan; } else { *outPan = 0.5f * inPan; *outSurroundPan = -0.12f + 1.12f * inSurroundPan; } } else if (inPan >= 0.0f) { /* NOTE: do not constant-fold 1.0f - 0.85f or 1.0f - 0.65f; they differ * by 1 digit */ if (inSurroundPan <= 0.0f) { *outPan = (0.85f + (1.0f - 0.85f) * (-inSurroundPan / inPan)) * ut::Abs(inPan); *outSurroundPan = -0.12f + (2.0f * inSurroundPan + 0.88f * inPan); } else { *outPan = (0.85f + (1.0f - 0.65f) * (-inSurroundPan / inPan)) * ut::Abs(inPan); *outSurroundPan = -0.12f + 1.12f * inPan; } } else { if (inSurroundPan <= 0.0f) { *outPan = ((1.0f - 0.85f) * (-inSurroundPan / inPan) - 0.85f) * ut::Abs(inPan); *outSurroundPan = -0.12f + (2.0f * inSurroundPan - 1.12f * inPan); } else { *outPan = ((1.0f - 0.65f) * (-inSurroundPan / inPan) - 0.85f) * ut::Abs(inPan); *outSurroundPan = -0.12f + 1.12f * -inPan; } } *outSurroundPan += 1.0f; } void Voice::CalcMixParam(int channelIndex, int voiceOutIndex, AxVoice::MixParam *mix, AxVoice::RemoteMixParam *rmtmix) { NW4RAssertPointerNonnull_Line(1284, mix); f32 mainVolume = 0.0f; f32 mainSend = 0.0f; f32 fxSendA = 0.0f; f32 fxSendB = 0.0f; f32 fxSendC = 0.0f; if (mOutputLineFlag & OUTPUT_LINE_MAIN) { mainVolume = mMainOutVolume; mainSend = mMainSend; fxSendA = mFxSend[AUX_A] + mVoiceOutParam[voiceOutIndex].fxSend; if (fxSendA < SEND_MIN) { fxSendA = SEND_MIN; } fxSendB = mFxSend[AUX_B]; fxSendC = mFxSend[AUX_C]; } f32 main = mainVolume * nw4r::ut::Clamp(mainSend, 0.0f, 1.0f); f32 fx_a = mainVolume * nw4r::ut::Clamp(fxSendA, 0.0f, 1.0f); f32 fx_b = mainVolume * nw4r::ut::Clamp(fxSendB, 0.0f, 1.0f); f32 fx_c = mainVolume * nw4r::ut::Clamp(fxSendC, 0.0f, 1.0f); f32 remoteOutVolumeOrig[4]; for (int i = 0; i < 4; i++) { if (mOutputLineFlag & (2 << i)) { remoteOutVolumeOrig[i] = mRemoteOutVolume[i]; } else { remoteOutVolumeOrig[i] = 0.0f; } } f32 left, right, surround, lrMixed; f32 front, rear; Util::PanInfo panInfo; panInfo.curve = Util::PAN_CURVE_SQRT; panInfo.centerZeroFlag = false; panInfo.zeroClampFlag = false; switch (mPanCurve) { case PAN_CURVE_SQRT: panInfo.curve = Util::PAN_CURVE_SQRT; break; case PAN_CURVE_SQRT_0DB: panInfo.curve = Util::PAN_CURVE_SQRT; panInfo.centerZeroFlag = true; break; case PAN_CURVE_SQRT_0DB_CLAMP: panInfo.curve = Util::PAN_CURVE_SQRT; panInfo.centerZeroFlag = true; panInfo.zeroClampFlag = true; break; case PAN_CURVE_SINCOS: panInfo.curve = Util::PAN_CURVE_SINCOS; break; case PAN_CURVE_SINCOS_0DB: panInfo.curve = Util::PAN_CURVE_SINCOS; panInfo.centerZeroFlag = true; break; case PAN_CURVE_SINCOS_0DB_CLAMP: panInfo.curve = Util::PAN_CURVE_SINCOS; panInfo.centerZeroFlag = true; panInfo.zeroClampFlag = true; break; case PAN_CURVE_LINEAR: panInfo.curve = Util::PAN_CURVE_LINEAR; break; case PAN_CURVE_LINEAR_0DB: panInfo.curve = Util::PAN_CURVE_LINEAR; panInfo.centerZeroFlag = true; break; case PAN_CURVE_LINEAR_0DB_CLAMP: panInfo.curve = Util::PAN_CURVE_LINEAR; panInfo.centerZeroFlag = true; panInfo.zeroClampFlag = true; break; default: panInfo.curve = Util::PAN_CURVE_SQRT; break; } if (mChannelCount > 1 && mPanMode == PAN_MODE_BALANCE) { f32 pan = mPan + mVoiceOutParam[voiceOutIndex].pan; f32 surroundPan = mSurroundPan + mVoiceOutParam[voiceOutIndex].surroundPan; if (channelIndex == 0) { left = Util::CalcPanRatio(pan, panInfo); right = 0.0f; } else if (channelIndex == 1) { left = 0.0f; right = Util::CalcPanRatio(-pan, panInfo); } /* ERRATUM: left and right are used uninitialized if channelIndex is * neither 0 nor 1 */ front = Util::CalcSurroundPanRatio(surroundPan, panInfo); rear = Util::CalcSurroundPanRatio(2.0f - surroundPan, panInfo); } else { f32 voicePan = PAN_CENTER; f32 pan, surroundPan; if (mChannelCount == 2) { if (channelIndex == 0) voicePan = PAN_LEFT; if (channelIndex == 1) voicePan = PAN_RIGHT; } switch (AxManager::GetInstance().GetOutputMode()) { case OUTPUT_MODE_DPL2: TransformDpl2Pan( &pan, &surroundPan, mPan + voicePan + mVoiceOutParam[voiceOutIndex].pan, mSurroundPan + mVoiceOutParam[voiceOutIndex].surroundPan); break; case OUTPUT_MODE_STEREO: case OUTPUT_MODE_SURROUND: case OUTPUT_MODE_MONO: default: pan = mPan + voicePan + mVoiceOutParam[voiceOutIndex].pan; surroundPan = mSurroundPan + mVoiceOutParam[voiceOutIndex].surroundPan; break; } left = Util::CalcPanRatio(pan, panInfo); right = Util::CalcPanRatio(-pan, panInfo); front = Util::CalcSurroundPanRatio(surroundPan, panInfo); rear = Util::CalcSurroundPanRatio(2.0f - surroundPan, panInfo); } surround = Util::CalcVolumeRatio(-3.0f); lrMixed = 0.5f * (left + right); f32 m_l; f32 m_r; f32 m_s; f32 a_l; f32 a_r; f32 a_s; f32 b_l; f32 b_r; f32 b_s; f32 c_l; f32 c_r; f32 c_s; f32 &m_sl = m_s; f32 &m_sr = c_l; f32 &a_sl = a_s; f32 &a_sr = c_r; f32 &b_sl = b_s; f32 &b_sr = c_s; switch (AxManager::GetInstance().GetOutputMode()) { case OUTPUT_MODE_STEREO: m_l = main * left; m_r = main * right; m_s = 0.0f; a_l = fx_a * left; a_r = fx_a * right; a_s = 0.0f; b_l = fx_b * left; b_r = fx_b * right; b_s = 0.0f; c_l = fx_c * left; c_r = fx_c * right; c_s = 0.0f; break; case OUTPUT_MODE_MONO: m_l = main * lrMixed; m_r = main * lrMixed; m_s = 0.0f; a_l = fx_a * lrMixed; a_r = fx_a * lrMixed; a_s = 0.0f; b_l = fx_b * lrMixed; b_r = fx_b * lrMixed; b_s = 0.0f; c_l = fx_c * lrMixed; c_r = fx_c * lrMixed; c_s = 0.0f; break; case OUTPUT_MODE_SURROUND: { f32 fl = left * front; f32 fr = right * front; f32 rs = surround * rear; m_l = main * fl; m_r = main * fr; m_s = main * rs; a_l = fx_a * fl; a_r = fx_a * fr; a_s = fx_a * rs; b_l = fx_b * fl; b_r = fx_b * fr; b_s = fx_b * rs; c_l = fx_c * fl; c_r = fx_c * fr; c_s = fx_c * rs; } break; case OUTPUT_MODE_DPL2: { f32 fl = left * front; f32 fr = right * front; f32 rl = left * rear; f32 rr = right * rear; m_l = main * fl; m_r = main * fr; m_sl = main * rl; m_sr = main * rr; a_l = fx_a * fl; a_r = fx_a * fr; a_sl = fx_a * rl; a_sr = fx_a * rr; b_l = fx_b * fl; b_r = fx_b * fr; b_sl = fx_b * rl; b_sr = fx_b * rr; } break; } f32 remoteOutVolume[4]; for (int i = 0; i < 4; i++) remoteOutVolume[i] = lrMixed * remoteOutVolumeOrig[i]; mix->vL = CalcMixVolume(m_l); mix->vR = CalcMixVolume(m_r); mix->vS = CalcMixVolume(m_s); mix->vAuxAL = CalcMixVolume(a_l); mix->vAuxAR = CalcMixVolume(a_r); mix->vAuxAS = CalcMixVolume(a_s); mix->vAuxBL = CalcMixVolume(b_l); mix->vAuxBR = CalcMixVolume(b_r); mix->vAuxBS = CalcMixVolume(b_s); mix->vAuxCL = CalcMixVolume(c_l); mix->vAuxCR = CalcMixVolume(c_r); mix->vAuxCS = CalcMixVolume(c_s); rmtmix->vMain0 = CalcMixVolume(remoteOutVolume[0]); rmtmix->vAux0 = 0; rmtmix->vMain1 = CalcMixVolume(remoteOutVolume[1]); rmtmix->vAux1 = 0; rmtmix->vMain2 = CalcMixVolume(remoteOutVolume[2]); rmtmix->vAux2 = 0; rmtmix->vMain3 = CalcMixVolume(remoteOutVolume[3]); rmtmix->vAux3 = 0; } void Voice::RunAllAxVoice() { for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) { for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) { if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) axVoice->Run(); } } } void Voice::StopAllAxVoice() { for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) { for (int voiceOutIndex = 0; voiceOutIndex < mVoiceOutCount; voiceOutIndex++) { if (AxVoice *axVoice = mAxVoice[channelIndex][voiceOutIndex]) axVoice->Stop(); } } } void Voice::InvalidateWaveData(void const *start, void const *end) { bool disposeFlag = false; for (int channelIndex = 0; channelIndex < mChannelCount; channelIndex++) { AxVoice *axVoice = mAxVoice[channelIndex][0]; if (axVoice && axVoice->IsDataAddressCoverd(start, end)) { disposeFlag = true; break; } } if (disposeFlag) { Stop(); if (mCallback) (*mCallback)(this, CALLBACK_STATUS_CANCEL, mCallbackData); } } }}} // namespace nw4r::snd::detail