#include "nw4r/snd/snd_AxVoice.h" /* Original source: * kiwi515/ogws * src/nw4r/snd/snd_AxVoice.cpp */ /******************************************************************************* * headers */ #include #include #include "common.h" #include "nw4r/snd/snd_adpcm.h" // DecodeDspAdpcm #include "nw4r/snd/snd_AxManager.h" #include "nw4r/snd/snd_AxVoiceManager.h" #include "nw4r/snd/snd_BiquadFilterCallback.h" #include "nw4r/snd/snd_global.h" #include "nw4r/snd/snd_Util.h" // Util::GetRemoteFilterCoefs #include "nw4r/ut/ut_algorithm.h" #include "nw4r/ut/ut_Lock.h" // ut::AutoInterruptLock #include #include #include #include #include "nw4r/NW4RAssert.hpp" /******************************************************************************* * local function declarations */ namespace nw4r { namespace snd { namespace detail { inline int CalcAxvpbDelta(u16 initValue, u16 targetValue) { if (initValue == targetValue) return 0; return (targetValue - initValue) / AX_SAMPLES_PER_FRAME; } }}} // namespace nw4r::snd::detail /******************************************************************************* * functions */ namespace nw4r { namespace snd { namespace detail { AxVoice::AxVoice() : mWaveData (nullptr), mFirstMixUpdateFlag (false), mReserveForFreeFlag (false), mCallback (nullptr), mCallbackData (nullptr) { } AxVoice::~AxVoice() {} void AxVoice::Setup(void const *waveAddr, SampleFormat format, int sampleRate) { ut::AutoInterruptLock lock; mWaveData = waveAddr; mFormat = format; mSampleRate = sampleRate; std::memset(&mMixPrev, 0, sizeof mMixPrev); mFirstMixUpdateFlag = true; } bool AxVoice::IsPlayFinished() const { ut::AutoInterruptLock lock; if (!mWaveData) return false; u32 dspAddr = GetCurrentPlayingDspAddress(); void const *zeroBuffer = AxManager::GetInstance().GetZeroBufferAddress(); u32 beginPos = GetDspAddressBySample(zeroBuffer, 0, mFormat); u32 endPos = beginPos; switch (mFormat) { case SAMPLE_FORMAT_DSP_ADPCM: endPos += 0x200; break; case SAMPLE_FORMAT_PCM_S8: endPos += 0x100; break; case SAMPLE_FORMAT_PCM_S16: endPos += 0x80; break; default: NW4RPanicMessage_Line(103, "Invalid format\n"); return false; } if (beginPos <= dspAddr && dspAddr < endPos) return true; else return false; } void AxVoice::SetLoopStart(void const *baseAddress, u32 samples) { ut::AutoInterruptLock lock; if (!mVpb.IsAvailable()) return; u32 dspAddress = GetDspAddressBySample(baseAddress, samples, mFormat); mVpb.SetVoiceLoopAddr(dspAddress); } void AxVoice::SetLoopEnd(void const *baseAddress, u32 samples) { ut::AutoInterruptLock lock; if (!mVpb.IsAvailable()) return; u32 dspAddress = GetDspAddressBySample(baseAddress, samples - 1, mFormat); mVpb.SetVoiceEndAddr(dspAddress); } void AxVoice::SetLoopFlag(bool loopFlag) { ut::AutoInterruptLock lock; if (!mVpb.IsAvailable()) return; mVpb.SetVoiceLoop(loopFlag); } void AxVoice::StopAtPoint(void const *baseAddress, u32 samples) { ut::AutoInterruptLock lock; if (!mVpb.IsAvailable()) return; void const *zeroBuffer = AxManager::GetInstance().GetZeroBufferAddress(); u32 beginPos = GetDspAddressBySample(zeroBuffer, 0, mFormat); u32 endPos = GetDspAddressBySample(baseAddress, samples - 1, mFormat); mVpb.SetVoiceLoopAddr(beginPos); mVpb.SetVoiceEndAddr(endPos); mVpb.SetVoiceLoop(false); } bool AxVoice::IsDataAddressCoverd(void const *beginAddress, void const *endAddress) const { ut::AutoInterruptLock lock; if (!mVpb.IsAvailable()) return false; // NOTE: parentheses necessary, do not remove return mWaveData && (beginAddress <= mWaveData && mWaveData <= endAddress); } u32 AxVoice::GetCurrentPlayingSample() const { ut::AutoInterruptLock lock; if (!mVpb.IsAvailable()) return 0; if (!mWaveData) return 0; if (IsPlayFinished()) { u32 samples = GetSampleByDspAddress(mWaveData, GetLoopEndDspAddress(), mFormat); return samples + 1; } else { u32 samples = GetSampleByDspAddress( mWaveData, GetCurrentPlayingDspAddress(), mFormat); return samples; } } u32 AxVoice::GetCurrentPlayingDspAddress() const { u32 dspAddr = mVpb.GetCurrentAddress(); return dspAddr; } u32 AxVoice::GetLoopEndDspAddress() const { u32 dspAddr = mVpb.GetEndAddress(); return dspAddr; } void AxVoice::VoiceCallback(void *callbackData) { ut::AutoInterruptLock lock; AXVPB *dropVpb = static_cast(callbackData); AxVoice *voice = reinterpret_cast(dropVpb->userContext); NW4RAssertPointerNonnull_Line(276, voice); voice->mVpb.Clear(); AxVoiceManager::GetInstance().ReserveForFreeAxVoice(voice); } u32 AxVoice::GetDspAddressBySample(void const *baseAddress, u32 samples, SampleFormat format) { if (baseAddress) baseAddress = OSCachedToPhysical(const_cast(baseAddress)); u32 addr = 0; switch (format) { case SAMPLE_FORMAT_DSP_ADPCM: addr = (samples / AX_ADPCM_SAMPLES_PER_FRAME * AX_ADPCM_NIBBLES_PER_FRAME) + (samples % AX_ADPCM_SAMPLES_PER_FRAME) + (reinterpret_cast(baseAddress) * sizeof(u16)) + sizeof(u16); break; case SAMPLE_FORMAT_PCM_S8: addr = reinterpret_cast(baseAddress) + samples; break; case SAMPLE_FORMAT_PCM_S16: addr = reinterpret_cast(baseAddress) / sizeof(u16) + samples; break; default: NW4RPanicMessage_Line(318, "Invalid format\n"); break; } return addr; } u32 AxVoice::GetSampleByDspAddress(void const *baseAddress, u32 addr, SampleFormat format) { if (baseAddress) baseAddress = OSCachedToPhysical(const_cast(baseAddress)); u32 samples = 0; switch (format) { case SAMPLE_FORMAT_DSP_ADPCM: samples = addr - reinterpret_cast(baseAddress) * sizeof(u16); samples = (samples % AX_ADPCM_NIBBLES_PER_FRAME) + (samples / AX_ADPCM_NIBBLES_PER_FRAME * AX_ADPCM_SAMPLES_PER_FRAME) - sizeof(u16); break; case SAMPLE_FORMAT_PCM_S8: samples = addr - reinterpret_cast(baseAddress); break; case SAMPLE_FORMAT_PCM_S16: samples = addr - reinterpret_cast(baseAddress) / sizeof(u16); break; default: NW4RPanicMessage_Line(350, "Invalid format\n"); break; } return samples; } void AxVoice::SetPriority(u32 priority) { mVpb.SetVoicePriority(priority); } void AxVoice::SetVoiceType(VoiceType type) { mVpb.SetVoiceType(type); } void AxVoice::EnableRemote(bool enable) { mVpb.SetVoiceRmtOn(enable); } void AxVoice::ResetDelta() { ut::AutoInterruptLock lock; if (!mVpb.IsAvailable()) return; mVpb.UpdateDelta(); AXPBMIX mix; mix.vL = mMixPrev.vL; mix.vDeltaL = 0; mix.vR = mMixPrev.vR; mix.vDeltaR = 0; mix.vAuxAL = mMixPrev.vAuxAL; mix.vDeltaAuxAL = 0; mix.vAuxAR = mMixPrev.vAuxAR; mix.vDeltaAuxAR = 0; mix.vAuxBL = mMixPrev.vAuxBL; mix.vDeltaAuxBL = 0; mix.vAuxBR = mMixPrev.vAuxBR; mix.vDeltaAuxBR = 0; mix.vAuxCL = mMixPrev.vAuxCL; mix.vDeltaAuxCL = 0; mix.vAuxCR = mMixPrev.vAuxCR; mix.vDeltaAuxCR = 0; mix.vS = mMixPrev.vS; mix.vDeltaS = 0; mix.vAuxAS = mMixPrev.vAuxAS; mix.vDeltaAuxAS = 0; mix.vAuxBS = mMixPrev.vAuxBS; mix.vDeltaAuxBS = 0; mix.vAuxCS = mMixPrev.vAuxCS; mix.vDeltaAuxCS = 0; mVpb.SetVoiceMix(mix, true); } void AxVoice::SetAddr(bool loopFlag, void const *waveAddr, u32 startOffset, u32 loopStart, u32 loopEnd) { ut::AutoInterruptLock lock; if (!mVpb.IsAvailable()) return; u32 startPos; u32 endPos; u32 loopPos; if (startOffset > loopEnd) { void const *zeroBuffer = AxManager::GetInstance().GetZeroBufferAddress(); loopFlag = false; startPos = GetDspAddressBySample(zeroBuffer, 0, mFormat); loopPos = GetDspAddressBySample(zeroBuffer, 0, mFormat); endPos = GetDspAddressBySample(zeroBuffer, 1, mFormat); } else { if (loopFlag) { loopPos = GetDspAddressBySample(waveAddr, loopStart, mFormat); } else { void const *zeroBuffer = AxManager::GetInstance().GetZeroBufferAddress(); loopPos = GetDspAddressBySample(zeroBuffer, 0, mFormat); } startPos = GetDspAddressBySample(waveAddr, startOffset, mFormat); endPos = GetDspAddressBySample(waveAddr, loopEnd - 1, mFormat); } AXPBADDR addr; addr.loopFlag = loopFlag; addr.format = GetAxFormatFromSampleFormat(mFormat); addr.loopAddressHi = loopPos >> 16; addr.loopAddressLo = loopPos & 0xffff; addr.endAddressHi = endPos >> 16; addr.endAddressLo = endPos & 0xffff; addr.currentAddressHi = startPos >> 16; addr.currentAddressLo = startPos & 0xffff; mVpb.SetVoiceAddr(addr); } u16 AxVoice::GetAxFormatFromSampleFormat(SampleFormat sampleFormat) { switch (sampleFormat) { case SAMPLE_FORMAT_DSP_ADPCM: return 0; case SAMPLE_FORMAT_PCM_S8: return 25; case SAMPLE_FORMAT_PCM_S16: return 10; default: NW4RPanicMessage_Line(503, "Invalid format\n"); return 0; } } void AxVoice::SetSrcType(SrcType type, f32 pitch) { ut::AutoInterruptLock lock; if (!mVpb.IsAvailable()) return; if (type == SRC_4TAP_AUTO) { f32 ratio = GetDspRatio(pitch); if (ratio > 4.0f / 3.0f) type = SRC_4TAP_8K; else if (ratio > 1.0f) type = SRC_4TAP_12K; else type = SRC_4TAP_16K; } mVpb.SetVoiceSrcType(type); } void AxVoice::SetAdpcm(AdpcmParam const *param) { ut::AutoInterruptLock lock; if (!mVpb.IsAvailable()) return; AXPBADPCM adpcm; switch (mFormat) { case SAMPLE_FORMAT_DSP_ADPCM: NW4RAssertPointerNonnull_Line(533, param); std::memcpy(adpcm.a, param->coef, sizeof adpcm.a); adpcm.gain = param->gain; adpcm.pred_scale = param->pred_scale; adpcm.yn1 = param->yn1; adpcm.yn2 = param->yn2; break; case SAMPLE_FORMAT_PCM_S16: std::memset(adpcm.a, 0, sizeof adpcm.a); adpcm.gain = 0x800; adpcm.pred_scale = 0; adpcm.yn1 = 0; adpcm.yn2 = 0; break; case SAMPLE_FORMAT_PCM_S8: std::memset(adpcm.a, 0, sizeof adpcm.a); adpcm.gain = 0x100; adpcm.pred_scale = 0; adpcm.yn1 = 0; adpcm.yn2 = 0; break; default: NW4RPanicMessage_Line(555, "Invalid format\n"); break; } mVpb.SetVoiceAdpcm(adpcm); } bool AxVoice::IsNeedNextUpdate(MixParam const ¶m) const { if (mMixPrev.vL != param.vL) return true; if (mMixPrev.vR != param.vR) return true; if (mMixPrev.vS != param.vS) return true; if (mMixPrev.vAuxAL != param.vAuxAL) return true; if (mMixPrev.vAuxAR != param.vAuxAR) return true; if (mMixPrev.vAuxAS != param.vAuxAS) return true; if (mMixPrev.vAuxBL != param.vAuxBL) return true; if (mMixPrev.vAuxBR != param.vAuxBR) return true; if (mMixPrev.vAuxBS != param.vAuxBS) return true; if (mMixPrev.vAuxCL != param.vAuxCL) return true; if (mMixPrev.vAuxCR != param.vAuxCR) return true; if (mMixPrev.vAuxCS != param.vAuxCS) return true; return false; } void AxVoice::SetAdpcmLoop(AdpcmLoopParam const *param) { ut::AutoInterruptLock lock; if (!mVpb.IsAvailable()) return; AXPBADPCMLOOP adpcmloop; if (mFormat == SAMPLE_FORMAT_DSP_ADPCM) { NW4RAssertPointerNonnull_Line(587, param); adpcmloop.loop_pred_scale = param->loop_pred_scale; adpcmloop.loop_yn1 = param->loop_yn1; adpcmloop.loop_yn2 = param->loop_yn2; } else { adpcmloop.loop_pred_scale = 0; adpcmloop.loop_yn1 = 0; adpcmloop.loop_yn2 = 0; } mVpb.SetVoiceAdpcmLoop(adpcmloop); } bool AxVoice::SetMix(MixParam const ¶m) { ut::AutoInterruptLock lock; if (!mVpb.IsAvailable()) return false; AXPBMIX mix; if (mFirstMixUpdateFlag || !IsRun()) { mMixPrev = param; mFirstMixUpdateFlag = false; } bool needUpdateFlag = IsNeedNextUpdate(param); mix.vL = mMixPrev.vL; mix.vR = mMixPrev.vR; mix.vS = mMixPrev.vS; mix.vAuxAL = mMixPrev.vAuxAL; mix.vAuxAR = mMixPrev.vAuxAR; mix.vAuxAS = mMixPrev.vAuxAS; mix.vAuxBL = mMixPrev.vAuxBL; mix.vAuxBR = mMixPrev.vAuxBR; mix.vAuxBS = mMixPrev.vAuxBS; mix.vAuxCL = mMixPrev.vAuxCL; mix.vAuxCR = mMixPrev.vAuxCR; mix.vAuxCS = mMixPrev.vAuxCS; int vDeltaL = CalcAxvpbDelta(mMixPrev.vL, param.vL); int vDeltaR = CalcAxvpbDelta(mMixPrev.vR, param.vR); int vDeltaS = CalcAxvpbDelta(mMixPrev.vS, param.vS); int vDeltaAuxAL = CalcAxvpbDelta(mMixPrev.vAuxAL, param.vAuxAL); int vDeltaAuxAR = CalcAxvpbDelta(mMixPrev.vAuxAR, param.vAuxAR); int vDeltaAuxAS = CalcAxvpbDelta(mMixPrev.vAuxAS, param.vAuxAS); int vDeltaAuxBL = CalcAxvpbDelta(mMixPrev.vAuxBL, param.vAuxBL); int vDeltaAuxBR = CalcAxvpbDelta(mMixPrev.vAuxBR, param.vAuxBR); int vDeltaAuxBS = CalcAxvpbDelta(mMixPrev.vAuxBS, param.vAuxBS); int vDeltaAuxCL = CalcAxvpbDelta(mMixPrev.vAuxCL, param.vAuxCL); int vDeltaAuxCR = CalcAxvpbDelta(mMixPrev.vAuxCR, param.vAuxCR); int vDeltaAuxCS = CalcAxvpbDelta(mMixPrev.vAuxCS, param.vAuxCS); mix.vDeltaL = vDeltaL; mix.vDeltaR = vDeltaR; mix.vDeltaS = vDeltaS; mix.vDeltaAuxAL = vDeltaAuxAL; mix.vDeltaAuxAR = vDeltaAuxAR; mix.vDeltaAuxAS = vDeltaAuxAS; mix.vDeltaAuxBL = vDeltaAuxBL; mix.vDeltaAuxBR = vDeltaAuxBR; mix.vDeltaAuxBS = vDeltaAuxBS; mix.vDeltaAuxCL = vDeltaAuxCL; mix.vDeltaAuxCR = vDeltaAuxCR; mix.vDeltaAuxCS = vDeltaAuxCS; mVpb.SetVoiceMix(mix, false); if (param.vL == 0 || vDeltaL == 0) mMixPrev.vL = param.vL; else mMixPrev.vL += vDeltaL * AX_SAMPLES_PER_FRAME; if (param.vR == 0 || vDeltaR == 0) mMixPrev.vR = param.vR; else mMixPrev.vR += vDeltaR * AX_SAMPLES_PER_FRAME; if (param.vS == 0 || vDeltaS == 0) mMixPrev.vS = param.vS; else mMixPrev.vS += vDeltaS * AX_SAMPLES_PER_FRAME; if (param.vAuxAL == 0 || vDeltaAuxAL == 0) mMixPrev.vAuxAL = param.vAuxAL; else mMixPrev.vAuxAL += vDeltaAuxAL * AX_SAMPLES_PER_FRAME; if (param.vAuxAR == 0 || vDeltaAuxAR == 0) mMixPrev.vAuxAR = param.vAuxAR; else mMixPrev.vAuxAR += vDeltaAuxAR * AX_SAMPLES_PER_FRAME; if (param.vAuxAS == 0 || vDeltaAuxAS == 0) mMixPrev.vAuxAS = param.vAuxAS; else mMixPrev.vAuxAS += vDeltaAuxAS * AX_SAMPLES_PER_FRAME; if (param.vAuxBL == 0 || vDeltaAuxBL == 0) mMixPrev.vAuxBL = param.vAuxBL; else mMixPrev.vAuxBL += vDeltaAuxBL * AX_SAMPLES_PER_FRAME; if (param.vAuxBR == 0 || vDeltaAuxBR == 0) mMixPrev.vAuxBR = param.vAuxBR; else mMixPrev.vAuxBR += vDeltaAuxBR * AX_SAMPLES_PER_FRAME; if (param.vAuxBS == 0 || vDeltaAuxBS == 0) mMixPrev.vAuxBS = param.vAuxBS; else mMixPrev.vAuxBS += vDeltaAuxBS * AX_SAMPLES_PER_FRAME; if (param.vAuxCL == 0 || vDeltaAuxCL == 0) mMixPrev.vAuxCL = param.vAuxCL; else mMixPrev.vAuxCL += vDeltaAuxCL * AX_SAMPLES_PER_FRAME; if (param.vAuxCR == 0 || vDeltaAuxCR == 0) mMixPrev.vAuxCR = param.vAuxCR; else mMixPrev.vAuxCR += vDeltaAuxCR * AX_SAMPLES_PER_FRAME; if (param.vAuxCS == 0 || vDeltaAuxCS == 0) mMixPrev.vAuxCS = param.vAuxCS; else mMixPrev.vAuxCS += vDeltaAuxCS * AX_SAMPLES_PER_FRAME; return needUpdateFlag; } void AxVoice::SetRmtMix(const RemoteMixParam& rParam) { AXPBRMTMIX mix; mix.vMain0 = rParam.vMain0; mix.vDeltaMain0 = 0; mix.vAux0 = rParam.vAux0; mix.vDeltaAux0 = 0; mix.vMain1 = rParam.vMain1; mix.vDeltaMain1 = 0; mix.vAux1 = rParam.vAux1; mix.vDeltaAux1 = 0; mix.vMain2 = rParam.vMain2; mix.vDeltaMain2 = 0; mix.vAux2 = rParam.vAux2; mix.vDeltaAux2 = 0; mix.vMain3 = rParam.vMain3; mix.vDeltaMain3 = 0; mix.vAux3 = rParam.vAux3; mix.vDeltaAux3 = 0; mVpb.SetVoiceRmtMix(mix); } void AxVoice::SetSrc(f32 ratio, bool initialUpdate) { ut::AutoInterruptLock lock; if (!mVpb.IsAvailable()) return; if (initialUpdate) { ratio = ut::Clamp(GetDspRatio(ratio), 0.0f, 65535.0f); u32 srcBits = 65536 * ratio; AXPBSRC src; src.ratioHi = srcBits >> 16; src.ratioLo = srcBits & 0xffff; src.currentAddressFrac = 0; src.last_samples[0] = 0; src.last_samples[1] = 0; src.last_samples[2] = 0; src.last_samples[3] = 0; mVpb.SetVoiceSrc(src); } else { ratio = GetDspRatio(ratio); mVpb.SetVoiceSrcRatio(ratio); } } void AxVoice::SetVe(f32 volume, f32 initVolume) { ut::AutoInterruptLock lock; if (!mVpb.IsAvailable()) return; int v = volume * (AX_MAX_VOLUME - 1); int iv = initVolume * (AX_MAX_VOLUME - 1); v = ut::Clamp(v, 0, 0xFFFF); iv = ut::Clamp(iv, 0, 0xFFFF); mVpb.SetVoiceVe(v, iv); } void AxVoice::SetLpf(u16 freq) { ut::AutoInterruptLock lock; if (!mVpb.IsAvailable()) return; if (freq >= 16000) { AXPBLPF lpf; lpf.on = AX_PB_OFF; lpf.yn1 = 0; mVpb.SetVoiceLpf(lpf); } else if (mVpb.IsLpfEnable()) { u16 a0, b0; AXGetLpfCoefs(freq, &a0, &b0); mVpb.SetVoiceLpfCoefs(a0, b0); } else { AXPBLPF lpf; lpf.on = AX_PB_LPF_ON; lpf.yn1 = 0; AXGetLpfCoefs(freq, &lpf.a0, &lpf.b0); mVpb.SetVoiceLpf(lpf); } } void AxVoice::SetBiquad(u8 filterType, f32 value) { ut::AutoInterruptLock lock; if (!mVpb.IsAvailable()) return; BiquadFilterCallback const *biquadFilter = AxManager::GetInstance().GetBiquadFilterCallback(filterType); bool filterEnable = true; if (filterType == 0) filterEnable = false; if (!biquadFilter) filterEnable = false; if (!filterEnable) { AXPBBIQUAD biquad; biquad.on = AX_PB_OFF; biquad.xn1 = 0; biquad.xn2 = 0; biquad.yn1 = 0; biquad.yn2 = 0; mVpb.SetVoiceBiquad(biquad); } else { BiquadFilterCallback::BiquadCoef coef; biquadFilter->GetCoef(filterType, value, &coef); if (mVpb.IsBiquadEnable()) { mVpb.SetVoiceBiquadCoefs(coef.b0, coef.b1, coef.b2, coef.a1, coef.a2); } else { AXPBBIQUAD biquad; biquad.on = AX_PB_BIQUAD_ON; biquad.xn1 = 0; biquad.xn2 = 0; biquad.yn1 = 0; biquad.yn2 = 0; biquad.b0 = coef.b0; biquad.b1 = coef.b1; biquad.b2 = coef.b2; biquad.a1 = coef.a1; biquad.a2 = coef.a2; mVpb.SetVoiceBiquad(biquad); } } } void AxVoice::SetRemoteFilter(u8 filter) { ut::AutoInterruptLock lock; if (!mVpb.IsAvailable()) return; if (filter == 0) { __AXPBRMTIIR iir; iir.lpf.on = AX_PB_OFF; mVpb.SetVoiceRmtIIR(iir); } else if (mVpb.IsRmtIirEnable()) { u16 b0, b1, b2, a1, a2; Util::GetRemoteFilterCoefs(filter, &b0, &b1, &b2, &a1, &a2); mVpb.SetVoiceRmtIIRCoefs(AX_PB_BIQUAD_ON, b0, b1, b2, a1, a2); } else { __AXPBRMTIIR iir; iir.biquad.on = AX_PB_BIQUAD_ON; iir.biquad.xn1 = 0; iir.biquad.xn2 = 0; iir.biquad.yn1 = 0; iir.biquad.yn2 = 0; Util::GetRemoteFilterCoefs(filter, &iir.biquad.b0, &iir.biquad.b1, &iir.biquad.b2, &iir.biquad.a1, &iir.biquad.a2); mVpb.SetVoiceRmtIIR(iir); } } void AxVoice::CalcOffsetAdpcmParam(u16 *outPredScale, u16 *outYn1, u16 *outYn2, u32 offset, const void *dataAddr, AdpcmParam const &adpcmParam) { AXPBADPCM adpcm; std::memcpy(adpcm.a, adpcmParam.coef, sizeof adpcm.a); adpcm.gain = adpcmParam.gain; adpcm.pred_scale = adpcmParam.pred_scale; adpcm.yn1 = adpcmParam.yn1; adpcm.yn2 = adpcmParam.yn2; u32 currentPos = GetDspAddressBySample(dataAddr, 0, SAMPLE_FORMAT_DSP_ADPCM); u32 endPos = GetDspAddressBySample(dataAddr, offset, SAMPLE_FORMAT_DSP_ADPCM); while (currentPos < endPos) { if (currentPos % AX_ADPCM_NIBBLES_PER_FRAME == 0) { byte_t byte = *static_cast(OSPhysicalToCached( currentPos / sizeof(u16))); adpcm.pred_scale = byte; currentPos += sizeof(u16); } byte_t byte = *static_cast(OSPhysicalToCached( currentPos / sizeof(u16))); u8 nibble; if (currentPos % sizeof(u16) != 0) nibble = byte & 0x0f; else nibble = byte >> 4; DecodeDspAdpcm(&adpcm, nibble); currentPos++; } *outPredScale = adpcm.pred_scale; *outYn1 = adpcm.yn1; *outYn2 = adpcm.yn2; } void AxVoiceParamBlock::Sync() { ut::AutoInterruptLock lock; if (!IsAvailable()) return; mVpb->pb.ve.currentVolume = mPrevVeSetting.currentVolume; s16 deltaIn = (mVolume - mPrevVeSetting.currentVolume) / AX_SAMPLES_PER_FRAME; s16 deltaOut = deltaIn + BOOLIFY_TERNARY(deltaIn > 0) ? 1 : -1; int predIn = mPrevVeSetting.currentVolume + deltaIn * AX_SAMPLES_PER_FRAME; int predOut = mPrevVeSetting.currentVolume + deltaOut * AX_SAMPLES_PER_FRAME; if (ut::Abs(mVolume - predIn) < ut::Abs(mVolume - predOut)) mVpb->pb.ve.currentDelta = deltaIn; else mVpb->pb.ve.currentDelta = deltaOut; int nextVolume = mPrevVeSetting.currentVolume + mVpb->pb.ve.currentDelta * AX_SAMPLES_PER_FRAME; if (nextVolume < 0) { mVpb->pb.ve.currentDelta = -mPrevVeSetting.currentVolume / AX_SAMPLES_PER_FRAME; } else if (nextVolume > 65535) { mVpb->pb.ve.currentDelta = (65535 - mPrevVeSetting.currentVolume) / AX_SAMPLES_PER_FRAME; } if (mVpb->pb.ve.currentDelta == 0 && mPrevVeSetting.currentDelta == 0) mVpb->pb.ve.currentVolume = mVolume; mSync &= ~AX_VPB_SYNC_FLAG_VE_DELTA; mSync |= AX_VPB_SYNC_FLAG_VE; mPrevVeSetting.currentVolume = mVpb->pb.ve.currentVolume; mPrevVeSetting.currentDelta = mVpb->pb.ve.currentDelta; mVpb->sync |= mSync; mSync = 0; } bool AxVoiceParamBlock::IsRmtIirEnable() const { return IsAvailable() && mVpb->pb.rmtIIR.biquad.on == AX_PB_BIQUAD_ON; } AxVoiceParamBlock::AxVoiceParamBlock() : mVpb (nullptr), mSync (), mFirstVeUpdateFlag (false), mVolume (DEFAULT_VOLUME) { mPrevVeSetting.currentVolume = DEFAULT_VOLUME; mPrevVeSetting.currentDelta = 0; } void AxVoiceParamBlock::Set(AXVPB *vpb) { NW4RAssertPointerNonnull_Line(1044, vpb); mVpb = vpb; mSync = 0; mFirstVeUpdateFlag = true; mVolume = DEFAULT_VOLUME; mPrevVeSetting.currentVolume = DEFAULT_VOLUME; mPrevVeSetting.currentDelta = 0; } void AxVoiceParamBlock::Clear() { mVpb = nullptr; mSync = 0; } void AxVoiceParamBlock::SetVoiceType(u16 type) { ut::AutoInterruptLock lock; if (!IsAvailable()) return; mVpb->pb.type = type; mSync |= AX_VPB_SYNC_FLAG_TYPE; } void AxVoiceParamBlock::SetVoiceVe(u16 volume, u16 initVolume) { ut::AutoInterruptLock lock; if (!IsAvailable()) return; if (mFirstVeUpdateFlag) { mPrevVeSetting.currentVolume = initVolume; mFirstVeUpdateFlag = false; } mVolume = volume; } void AxVoiceParamBlock::SetVoiceMix(AXPBMIX const &mix, bool immediatelySync) { ut::AutoInterruptLock lock; if (!IsAvailable()) return; u16 *src = const_cast(reinterpret_cast(&mix)); u16 *dst = reinterpret_cast(&mVpb->pb.mix); u32 mixerCtrl = 0; if ((*dst++ = *src++)) // vL mixerCtrl |= AX_MIXER_CTRL_L; if ((*dst++ = *src++)) // vDeltaL mixerCtrl |= AX_MIXER_CTRL_L | AX_MIXER_CTRL_DELTA; if ((*dst++ = *src++)) // vR mixerCtrl |= AX_MIXER_CTRL_R; if ((*dst++ = *src++)) // vDeltaR mixerCtrl |= AX_MIXER_CTRL_R | AX_MIXER_CTRL_DELTA; if ((*dst++ = *src++)) // vAuxAL mixerCtrl |= AX_MIXER_CTRL_A_L; if ((*dst++ = *src++)) // vDeltaAuxAL mixerCtrl |= AX_MIXER_CTRL_A_L | AX_MIXER_CTRL_A_DELTA; if ((*dst++ = *src++)) // vAuxAR mixerCtrl |= AX_MIXER_CTRL_A_R; if ((*dst++ = *src++)) // vDeltaAuxAR mixerCtrl |= AX_MIXER_CTRL_A_R | AX_MIXER_CTRL_A_DELTA; if ((*dst++ = *src++)) // vAuxBL mixerCtrl |= AX_MIXER_CTRL_B_L; if ((*dst++ = *src++)) // vDeltaAuxBL mixerCtrl |= AX_MIXER_CTRL_B_L | AX_MIXER_CTRL_B_DELTA; if ((*dst++ = *src++)) // vAuxBR mixerCtrl |= AX_MIXER_CTRL_B_R; if ((*dst++ = *src++)) // vDeltaAuxBR mixerCtrl |= AX_MIXER_CTRL_B_R | AX_MIXER_CTRL_B_DELTA; if ((*dst++ = *src++)) // vAuxCL mixerCtrl |= AX_MIXER_CTRL_C_L; if ((*dst++ = *src++)) // vDeltaAuxCL mixerCtrl |= AX_MIXER_CTRL_C_L | AX_MIXER_CTRL_C_DELTA; if ((*dst++ = *src++)) // vAuxCR mixerCtrl |= AX_MIXER_CTRL_C_R; if ((*dst++ = *src++)) // vDeltaAuxCR mixerCtrl |= AX_MIXER_CTRL_C_R | AX_MIXER_CTRL_C_DELTA; if ((*dst++ = *src++)) // vS mixerCtrl |= AX_MIXER_CTRL_S; if ((*dst++ = *src++)) // vDeltaS mixerCtrl |= AX_MIXER_CTRL_S | AX_MIXER_CTRL_DELTA_S; if ((*dst++ = *src++)) // vAuxAS mixerCtrl |= AX_MIXER_CTRL_A_S; if ((*dst++ = *src++)) // vDeltaAuxAS mixerCtrl |= AX_MIXER_CTRL_A_S | AX_MIXER_CTRL_A_DELTA_S; if ((*dst++ = *src++)) // vAuxBS mixerCtrl |= AX_MIXER_CTRL_B_S; if ((*dst++ = *src++)) // vDeltaAuxBS mixerCtrl |= AX_MIXER_CTRL_B_S | AX_MIXER_CTRL_B_DELTA_S; if ((*dst++ = *src++)) // vAuxCS mixerCtrl |= AX_MIXER_CTRL_C_S; if ((*dst++ = *src++)) // vDeltaAuxCS mixerCtrl |= AX_MIXER_CTRL_C_S | AX_MIXER_CTRL_C_DELTA_S; mVpb->pb.mixerCtrl = mixerCtrl; if (immediatelySync) mVpb->sync |= AX_VPB_SYNC_FLAG_MIX | AX_VPB_SYNC_FLAG_MIXER_CTRL; else mSync |= AX_VPB_SYNC_FLAG_MIX | AX_VPB_SYNC_FLAG_MIXER_CTRL; } void AxVoiceParamBlock::SetVoiceLoop(u16 loop) { ut::AutoInterruptLock lock; if (!IsAvailable()) return; mVpb->pb.addr.loopFlag = loop; if (!(mVpb->sync & AX_VPB_SYNC_FLAG_ADDR)) mVpb->sync |= AX_VPB_SYNC_FLAG_ADDR_LOOP_FLAG; } void AxVoiceParamBlock::SetVoiceLoopAddr(u32 addr) { ut::AutoInterruptLock lock; if (!IsAvailable()) return; mVpb->pb.addr.loopAddressHi = addr >> 16; mVpb->pb.addr.loopAddressLo = addr & 0xffff; if (!(mVpb->sync & AX_VPB_SYNC_FLAG_ADDR)) mVpb->sync |= AX_VPB_SYNC_FLAG_ADDR_LOOP_ADDR; } void AxVoiceParamBlock::SetVoiceEndAddr(u32 addr) { ut::AutoInterruptLock lock; if (!IsAvailable()) return; mVpb->pb.addr.endAddressHi = addr >> 16; mVpb->pb.addr.endAddressLo = addr & 0xffff; if (!(mVpb->sync & AX_VPB_SYNC_FLAG_ADDR)) mVpb->sync |= AX_VPB_SYNC_FLAG_ADDR_END_ADDR; } void AxVoiceParamBlock::SetVoiceAdpcm(AXPBADPCM const &adpcm) { ut::AutoInterruptLock lock; if (!IsAvailable()) return; std::memcpy(&mVpb->pb.adpcm, &adpcm, sizeof mVpb->pb.adpcm); mSync |= AX_VPB_SYNC_FLAG_ADPCM; } void AxVoiceParamBlock::SetVoiceSrcType(u32 type) { ut::AutoInterruptLock lock; if (!IsAvailable()) return; switch (type) { case AxVoice::SRC_NONE: mVpb->pb.srcSelect = 2; break; case AxVoice::SRC_LINEAR: mVpb->pb.srcSelect = 1; break; case AxVoice::SRC_4TAP_8K: mVpb->pb.srcSelect = 0; mVpb->pb.coefSelect = 0; break; case AxVoice::SRC_4TAP_12K: mVpb->pb.srcSelect = 0; mVpb->pb.coefSelect = 1; break; case AxVoice::SRC_4TAP_16K: mVpb->pb.srcSelect = 0; mVpb->pb.coefSelect = 2; break; } mSync |= AX_VPB_SYNC_FLAG_SRC_TYPE; } void AxVoiceParamBlock::SetVoiceSrc(AXPBSRC const &src) { ut::AutoInterruptLock lock; if (!IsAvailable()) return; std::memcpy(&mVpb->pb.src, &src, sizeof mVpb->pb.src); mSync &= ~AX_VPB_SYNC_FLAG_SRC_RATIO; mSync |= AX_VPB_SYNC_FLAG_SRC; } void AxVoiceParamBlock::SetVoiceSrcRatio(f32 ratio) { ut::AutoInterruptLock lock; if (!IsAvailable()) return; u32 r = 65536 * ratio; mVpb->pb.src.ratioHi = r >> 16; mVpb->pb.src.ratioLo = r & 0xffff; if (!(mSync & AX_VPB_SYNC_FLAG_SRC)) mSync |= AX_VPB_SYNC_FLAG_SRC_RATIO; } void AxVoiceParamBlock::SetVoiceAdpcmLoop(AXPBADPCMLOOP const &adpcmloop) { ut::AutoInterruptLock lock; if (!IsAvailable()) return; std::memcpy(&mVpb->pb.adpcmLoop, &adpcmloop, sizeof mVpb->pb.adpcmLoop); mSync |= AX_VPB_SYNC_FLAG_ADPCM_LOOP; } void AxVoiceParamBlock::SetVoiceLpf(AXPBLPF const &lpf) { ut::AutoInterruptLock lock; if (!IsAvailable()) return; std::memcpy(&mVpb->pb.lpf, &lpf, sizeof mVpb->pb.lpf); mSync |= AX_VPB_SYNC_FLAG_LPF; } void AxVoiceParamBlock::SetVoiceLpfCoefs(u16 a0, u16 b0) { ut::AutoInterruptLock lock; if (!IsAvailable()) return; mVpb->pb.lpf.a0 = a0; mVpb->pb.lpf.b0 = b0; mSync |= AX_VPB_SYNC_FLAG_LPF_COEFS; } void AxVoiceParamBlock::SetVoiceBiquad(AXPBBIQUAD const &biquad) { ut::AutoInterruptLock lock; if (!IsAvailable()) return; std::memcpy(&mVpb->pb.biquad, &biquad, sizeof mVpb->pb.biquad); mSync |= AX_VPB_SYNC_FLAG_BIQUAD; } void AxVoiceParamBlock::SetVoiceBiquadCoefs(u16 b0, u16 b1, u16 b2, u16 a1, u16 a2) { ut::AutoInterruptLock lock; if (!IsAvailable()) return; mVpb->pb.biquad.b0 = b0; mVpb->pb.biquad.b1 = b1; mVpb->pb.biquad.b2 = b2; mVpb->pb.biquad.a1 = a1; mVpb->pb.biquad.a2 = a2; mSync |= AX_VPB_SYNC_FLAG_BIQUAD_COEFS; } void AxVoiceParamBlock::SetVoiceRmtOn(u16 on) { ut::AutoInterruptLock lock; if (!IsAvailable()) { return; } mVpb->pb.remote = on; mSync |= AX_VPB_SYNC_FLAG_RMT_ON; } void AxVoiceParamBlock::SetVoiceRmtMix(_AXPBRMTMIX const &rMix) { ut::AutoInterruptLock lock; if (!IsAvailable()) { return; } u16* pDst = reinterpret_cast(&mVpb->pb.rmtMix); const u16* pSrc = reinterpret_cast(&rMix); u16 ctrl = 0; // vMain0 if ((*pDst++ = *pSrc++) != 0) { ctrl |= AX_MIXER_CTRL_RMT_M0; } // vDeltaMain0 if ((*pDst++ = *pSrc++) != 0) { ctrl |= AX_MIXER_CTRL_RMT_DELTA_M0; } // vAux0 if ((*pDst++ = *pSrc++) != 0) { ctrl |= AX_MIXER_CTRL_RMT_A0; } // vDeltaAux0 if ((*pDst++ = *pSrc++) != 0) { ctrl |= AX_MIXER_CTRL_RMT_DELTA_A0; } // vMain1 if ((*pDst++ = *pSrc++) != 0) { ctrl |= AX_MIXER_CTRL_RMT_M1; } // vDeltaMain1 if ((*pDst++ = *pSrc++) != 0) { ctrl |= AX_MIXER_CTRL_RMT_DELTA_M1; } // vAux1 if ((*pDst++ = *pSrc++) != 0) { ctrl |= AX_MIXER_CTRL_RMT_A1; } // vDeltaAux1 if ((*pDst++ = *pSrc++) != 0) { ctrl |= AX_MIXER_CTRL_RMT_DELTA_A1; } // vMain2 if ((*pDst++ = *pSrc++) != 0) { ctrl |= AX_MIXER_CTRL_RMT_M2; } // vDeltaMain2 if ((*pDst++ = *pSrc++) != 0) { ctrl |= AX_MIXER_CTRL_RMT_DELTA_M2; } // vAux2 if ((*pDst++ = *pSrc++) != 0) { ctrl |= AX_MIXER_CTRL_RMT_A2; } // vDeltaAux2 if ((*pDst++ = *pSrc++) != 0) { ctrl |= AX_MIXER_CTRL_RMT_DELTA_A2; } // vMain3 if ((*pDst++ = *pSrc++) != 0) { ctrl |= AX_MIXER_CTRL_RMT_M3; } // vDeltaMain3 if ((*pDst++ = *pSrc++) != 0) { ctrl |= AX_MIXER_CTRL_RMT_DELTA_M3; } // vAux3 if ((*pDst++ = *pSrc++) != 0) { ctrl |= AX_MIXER_CTRL_RMT_A3; } // vDeltaAux3 if ((*pDst++ = *pSrc++) != 0) { ctrl |= AX_MIXER_CTRL_RMT_DELTA_A3; } mVpb->pb.rmtMixerCtrl = ctrl; mSync |= AX_VPB_SYNC_FLAG_RMT_MIXER_CTRL | AX_VPB_SYNC_FLAG_RMT_MIX; } void AxVoiceParamBlock::SetVoiceRmtIIR(__AXPBRMTIIR const &iir) { ut::AutoInterruptLock lock; if (!IsAvailable()) return; std::memcpy(&mVpb->pb.rmtIIR, &iir, sizeof mVpb->pb.rmtIIR); mSync |= AX_VPB_SYNC_FLAG_RMT_IIR; } void AxVoiceParamBlock::SetVoiceRmtIIRCoefs(u16 type, ...) { ut::AutoInterruptLock lock; if (!IsAvailable()) return; std::va_list argp; s32 ii; s32 num; if (type == AX_PB_LPF_ON) num = 2; else if (type == AX_PB_BIQUAD_ON) num = 5; else return; u16 coefs[5]; va_start(argp, type); for (ii = 0; ii < num; ii++) coefs[ii] = va_arg(argp, unsigned long); va_end(argp); if (type == AX_PB_LPF_ON) { mVpb->pb.rmtIIR.lpf.a0 = coefs[0]; mVpb->pb.rmtIIR.lpf.b0 = coefs[1]; mSync |= AX_VPB_SYNC_FLAG_RMT_IIR_LPF_COEFS; } else { mVpb->pb.rmtIIR.biquad.b0 = coefs[0]; mVpb->pb.rmtIIR.biquad.b1 = coefs[1]; mVpb->pb.rmtIIR.biquad.b2 = coefs[2]; mVpb->pb.rmtIIR.biquad.a1 = coefs[3]; mVpb->pb.rmtIIR.biquad.a2 = coefs[4]; mSync |= AX_VPB_SYNC_FLAG_RMT_IIR_BIQUAD_COEFS; } } void AxVoiceParamBlock::UpdateDelta() { ut::AutoInterruptLock lock; if (!IsAvailable()) return; mPrevVeSetting.currentVolume += mVpb->pb.ve.currentDelta * AX_SAMPLES_PER_FRAME; mVpb->pb.ve.currentVolume = mPrevVeSetting.currentVolume; mVpb->pb.ve.currentDelta = 0; mVpb->sync |= AX_VPB_SYNC_FLAG_VE; } }}} // namespace nw4r::snd::detail