diff --git a/src/Cafe/OS/libs/snd_core/ax.h b/src/Cafe/OS/libs/snd_core/ax.h index 0861e6a7..144e0c9a 100644 --- a/src/Cafe/OS/libs/snd_core/ax.h +++ b/src/Cafe/OS/libs/snd_core/ax.h @@ -78,6 +78,7 @@ namespace snd_core const int AX_FILTER_LOWPASS_16K = 0x2; bool isInitialized(); + bool IsSndCore2(); void reset(); // AX VPB diff --git a/src/Cafe/OS/libs/snd_core/ax_exports.cpp b/src/Cafe/OS/libs/snd_core/ax_exports.cpp index 44eaa391..a1ba69c9 100644 --- a/src/Cafe/OS/libs/snd_core/ax_exports.cpp +++ b/src/Cafe/OS/libs/snd_core/ax_exports.cpp @@ -379,144 +379,23 @@ namespace snd_core return 0; } - // void loadExportsSndCore1() - // { - // cafeExportRegisterFunc(sndcore1_AXInit, "snd_core", "AXInit", LogType::SoundAPI); - // cafeExportRegisterFunc(sndcore1_AXInitEx, "snd_core", "AXInitEx", LogType::SoundAPI); - // cafeExportRegister("snd_core", AXIsInit, LogType::SoundAPI); - // cafeExportRegister("snd_core", AXQuit, LogType::SoundAPI); - // - // cafeExportRegister("snd_core", AXGetMaxVoices, LogType::SoundAPI); - // cafeExportRegister("snd_core", AXGetInputSamplesPerFrame, LogType::SoundAPI); - // cafeExportRegister("snd_core", AXGetInputSamplesPerSec, LogType::SoundAPI); - // cafeExportRegister("snd_core", AXSetDefaultMixerSelect, LogType::SoundAPI); - // cafeExportRegister("snd_core", AXGetDefaultMixerSelect, LogType::SoundAPI); - // - // osLib_addFunction("snd_core", "AXGetDeviceFinalMixCallback", export_AXGetDeviceFinalMixCallback); - // osLib_addFunction("snd_core", "AXRegisterDeviceFinalMixCallback", export_AXRegisterDeviceFinalMixCallback); - // - // osLib_addFunction("snd_core", "AXRegisterAppFrameCallback", export_AXRegisterAppFrameCallback); - // osLib_addFunction("snd_core", "AXDeregisterAppFrameCallback", export_AXDeregisterAppFrameCallback); - // - // osLib_addFunction("snd_core", "AXRegisterFrameCallback", export_AXRegisterFrameCallback); - // osLib_addFunction("snd_core", "AXRegisterCallback", export_AXRegisterCallback); - // - // osLib_addFunction("snd_core", "AXRegisterAuxCallback", export_AXRegisterAuxCallback); - // osLib_addFunction("snd_core", "AXGetAuxCallback", export_AXGetAuxCallback); - // - // osLib_addFunction("snd_core", "AXSetAuxReturnVolume", export_AXSetAuxReturnVolume); - // - // osLib_addFunction("snd_core", "AXGetDeviceMode", export_AXGetDeviceMode); - // - // osLib_addFunction("snd_core", "AXSetDeviceUpsampleStage", export_AXSetDeviceUpsampleStage); - // osLib_addFunction("snd_core", "AXGetDeviceUpsampleStage", export_AXGetDeviceUpsampleStage); - // - // osLib_addFunction("snd_core", "AXAcquireVoiceEx", export_AXAcquireVoiceEx); - // osLib_addFunction("snd_core", "AXAcquireVoice", export_AXAcquireVoice); - // osLib_addFunction("snd_core", "AXFreeVoice", export_AXFreeVoice); - // - // osLib_addFunction("snd_core", "AXUserIsProtected", export_AXUserIsProtected); - // osLib_addFunction("snd_core", "AXUserBegin", export_AXUserBegin); - // osLib_addFunction("snd_core", "AXUserEnd", export_AXUserEnd); - // osLib_addFunction("snd_core", "AXVoiceBegin", export_AXVoiceBegin); - // osLib_addFunction("snd_core", "AXVoiceEnd", export_AXVoiceEnd); - // osLib_addFunction("snd_core", "AXVoiceIsProtected", export_AXVoiceIsProtected); - // - // osLib_addFunction("snd_core", "AXCheckVoiceOffsets", export_AXCheckVoiceOffsets); - // - // osLib_addFunction("snd_core", "AXSetDeviceRemixMatrix", export_AXSetDeviceRemixMatrix); - // osLib_addFunction("snd_core", "AXGetDeviceRemixMatrix", export_AXGetDeviceRemixMatrix); - // - // cafeExportRegister("snd_core", AXGetDeviceFinalOutput, LogType::SoundAPI); - // } - // - // void loadExportsSndCore2() - // { - // cafeExportRegisterFunc(sndcore2_AXInitWithParams, "sndcore2", "AXInitWithParams", LogType::SoundAPI); - // cafeExportRegisterFunc(sndcore2_AXInit, "sndcore2", "AXInit", LogType::SoundAPI); - // cafeExportRegisterFunc(sndcore2_AXInitEx, "sndcore2", "AXInitEx", LogType::SoundAPI); - // cafeExportRegister("sndcore2", AXIsInit, LogType::SoundAPI); - // cafeExportRegister("sndcore2", AXQuit, LogType::SoundAPI); - // - // cafeExportRegister("sndcore2", AXGetMaxVoices, LogType::SoundAPI); - // cafeExportRegister("sndcore2", AXGetInputSamplesPerFrame, LogType::SoundAPI); - // cafeExportRegister("sndcore2", AXGetInputSamplesPerSec, LogType::SoundAPI); - // cafeExportRegister("sndcore2", AXSetDefaultMixerSelect, LogType::SoundAPI); - // cafeExportRegister("sndcore2", AXGetDefaultMixerSelect, LogType::SoundAPI); - // - // osLib_addFunction("sndcore2", "AXGetDeviceFinalMixCallback", export_AXGetDeviceFinalMixCallback); - // osLib_addFunction("sndcore2", "AXRegisterDeviceFinalMixCallback", export_AXRegisterDeviceFinalMixCallback); - // - // osLib_addFunction("sndcore2", "AXRegisterAppFrameCallback", export_AXRegisterAppFrameCallback); - // osLib_addFunction("sndcore2", "AXDeregisterAppFrameCallback", export_AXDeregisterAppFrameCallback); - // - // osLib_addFunction("sndcore2", "AXRegisterFrameCallback", export_AXRegisterFrameCallback); - // osLib_addFunction("sndcore2", "AXRegisterCallback", export_AXRegisterCallback); - // - // osLib_addFunction("sndcore2", "AXRegisterAuxCallback", export_AXRegisterAuxCallback); - // osLib_addFunction("sndcore2", "AXGetAuxCallback", export_AXGetAuxCallback); - // - // osLib_addFunction("sndcore2", "AXSetAuxReturnVolume", export_AXSetAuxReturnVolume); - // - // osLib_addFunction("sndcore2", "AXGetDeviceMode", export_AXGetDeviceMode); - // - // osLib_addFunction("sndcore2", "AXSetDeviceUpsampleStage", export_AXSetDeviceUpsampleStage); - // osLib_addFunction("sndcore2", "AXGetDeviceUpsampleStage", export_AXGetDeviceUpsampleStage); - // - // osLib_addFunction("sndcore2", "AXAcquireVoiceEx", export_AXAcquireVoiceEx); - // osLib_addFunction("sndcore2", "AXAcquireVoice", export_AXAcquireVoice); - // osLib_addFunction("sndcore2", "AXFreeVoice", export_AXFreeVoice); - // - // osLib_addFunction("sndcore2", "AXUserIsProtected", export_AXUserIsProtected); - // osLib_addFunction("sndcore2", "AXUserBegin", export_AXUserBegin); - // osLib_addFunction("sndcore2", "AXUserEnd", export_AXUserEnd); - // osLib_addFunction("sndcore2", "AXVoiceBegin", export_AXVoiceBegin); - // osLib_addFunction("sndcore2", "AXVoiceEnd", export_AXVoiceEnd); - // - // osLib_addFunction("sndcore2", "AXVoiceIsProtected", export_AXVoiceIsProtected); - // - // osLib_addFunction("sndcore2", "AXCheckVoiceOffsets", export_AXCheckVoiceOffsets); - // - // osLib_addFunction("sndcore2", "AXSetDeviceRemixMatrix", export_AXSetDeviceRemixMatrix); - // osLib_addFunction("sndcore2", "AXGetDeviceRemixMatrix", export_AXGetDeviceRemixMatrix); - // - // cafeExportRegister("sndcore2", AXGetDeviceFinalOutput, LogType::SoundAPI); - // - // // multi voice - // cafeExportRegister("sndcore2", AXAcquireMultiVoice, LogType::SoundAPI); - // cafeExportRegister("sndcore2", AXFreeMultiVoice, LogType::SoundAPI); - // cafeExportRegister("sndcore2", AXGetMultiVoiceReformatBufferSize, LogType::SoundAPI); - // cafeExportRegister("sndcore2", AXSetMultiVoiceType, LogType::SoundAPI); - // cafeExportRegister("sndcore2", AXSetMultiVoiceAdpcm, LogType::SoundAPI); - // cafeExportRegister("sndcore2", AXSetMultiVoiceSrcType, LogType::SoundAPI); - // cafeExportRegister("sndcore2", AXSetMultiVoiceOffsets, LogType::SoundAPI); - // cafeExportRegister("sndcore2", AXSetMultiVoiceVe, LogType::SoundAPI); - // cafeExportRegister("sndcore2", AXSetMultiVoiceSrcRatio, LogType::SoundAPI); - // cafeExportRegister("sndcore2", AXSetMultiVoiceSrc, LogType::SoundAPI); - // cafeExportRegister("sndcore2", AXSetMultiVoiceLoop, LogType::SoundAPI); - // cafeExportRegister("sndcore2", AXSetMultiVoiceState, LogType::SoundAPI); - // cafeExportRegister("sndcore2", AXSetMultiVoiceAdpcmLoop, LogType::SoundAPI); - // cafeExportRegister("sndcore2", AXIsMultiVoiceRunning, LogType::SoundAPI); - // } - - // void loadExports() - // { - // AXResetToDefaultState(); - // - // loadExportsSndCore1(); - // loadExportsSndCore2(); - // } - bool isInitialized() { return sndGeneric.isInitialized; } + bool IsSndCore2() + { + cemu_assert_debug(sndGeneric.isInitialized); + return sndGeneric.isSoundCore2; + } + void reset() { AXOut_reset(); AXResetToDefaultState(); sndGeneric.isInitialized = false; + sndGeneric.isSoundCore2 = false; } void RegisterVoiceFunctions() diff --git a/src/Cafe/OS/libs/snd_core/ax_mix.cpp b/src/Cafe/OS/libs/snd_core/ax_mix.cpp index 31a24a44..59d1552e 100644 --- a/src/Cafe/OS/libs/snd_core/ax_mix.cpp +++ b/src/Cafe/OS/libs/snd_core/ax_mix.cpp @@ -8,7 +8,6 @@ void mic_updateOnAXFrame(); namespace snd_core { uint32 __AXDefaultMixerSelect = AX_MIXER_SELECT_BOTH; - uint16 __AXTVAuxReturnVolume[AX_AUX_BUS_COUNT]; void AXSetDefaultMixerSelect(uint32 mixerSelect) @@ -226,7 +225,6 @@ namespace snd_core uint32 newInternalCurrentOffset = internalCurrentOffset + (playbackNibbleOffset - playbackNibbleOffsetStart); *(uint32*)&internalShadowCopy->internalOffsets.currentOffsetPtrHigh = _swapEndianU32(newInternalCurrentOffset); - } void AX_DecodeSamplesADPCM_NoSrc(AXVPBInternal_t* internalShadowCopy, float* output, sint32 sampleCount) @@ -459,8 +457,6 @@ namespace snd_core uint16* endOffsetAddr = (uint16*)(memory_base + ((endOffsetPtr * 2) | (ptrHighExtension << 29))); uint16* currentOffsetAddr = (uint16*)(memory_base + ((currentOffsetPtr * 2) | (ptrHighExtension << 29))); - uint16* loopOffsetAddrDebug = (uint16*)(memory_base + ((loopOffsetPtr * 2) | (ptrHighExtension << 29))); - sint16 historySamples[4]; historySamples[0] = _swapEndianS16(internalShadowCopy->src.historySamples[0]); historySamples[1] = _swapEndianS16(internalShadowCopy->src.historySamples[1]); @@ -469,17 +465,19 @@ namespace snd_core sint32 historyIndex = 0; + sint32 s = 0; for (sint32 i = 0; i < sampleCount; i++) { currentFracPos += ratio; while (currentFracPos >= 0x10000) { // read next sample + sint32 prevIndex = historyIndex; historyIndex = (historyIndex + 1) & 3; if (internalShadowCopy->playbackState) { - sint32 s = _swapEndianS16(*currentOffsetAddr); - historySamples[historyIndex] = s; + s = _swapEndianS16(*currentOffsetAddr); + historySamples[prevIndex] = s; if (currentOffsetAddr == endOffsetAddr) { if (internalShadowCopy->internalOffsets.loopFlag) @@ -501,15 +499,15 @@ namespace snd_core else { // voice not playing -> sample is silent - historySamples[historyIndex] = 0; + historySamples[historyIndex] = s; } currentFracPos -= 0x10000; } // interpolate sample - sint32 previousSample = historySamples[(historyIndex + 3) & 3]; - sint32 nextSample = historySamples[historyIndex]; + sint32 curSample = historySamples[historyIndex]; + sint32 nextSample = historySamples[(historyIndex + 1) & 3]; - sint32 p0 = (sint32)previousSample * (sint32)(0x10000 - currentFracPos); + sint32 p0 = (sint32)curSample * (sint32)(0x10000 - currentFracPos); sint32 p1 = (sint32)nextSample * (sint32)(currentFracPos); p0 >>= 7; p1 >>= 7; @@ -659,7 +657,7 @@ namespace snd_core float __AXMixBufferTV[AX_SAMPLES_MAX * AX_TV_CHANNEL_COUNT * AX_BUS_COUNT]; float __AXMixBufferDRC[2 * AX_SAMPLES_MAX * AX_DRC_CHANNEL_COUNT * AX_BUS_COUNT]; - void AXVoiceMix_ApplyADSR(AXVPBInternal_t* internalShadowCopy, float* sampleData, sint32 sampleCount) + void AXVoiceMix_ApplyADSR_NoClamp(AXVPBInternal_t* internalShadowCopy, float* sampleData, sint32 sampleCount) { uint16 volume = internalShadowCopy->veVolume; sint16 volumeDelta = (sint16)internalShadowCopy->veDelta; @@ -669,23 +667,73 @@ namespace snd_core if (volumeDelta == 0) { // without delta - for (sint32 i = 0; i < sampleCount; i++) - sampleData[i] *= volumeScaler; + for (sint32 i = 0; i < sampleCount; i += 4) + { + sampleData[i+0] *= volumeScaler; + sampleData[i+1] *= volumeScaler; + sampleData[i+2] *= volumeScaler; + sampleData[i+3] *= volumeScaler; + } return; } // with delta - double volumeScalerDelta = (double)volumeDelta / 32768.0; - volumeScalerDelta = volumeScalerDelta + volumeScalerDelta; + float volumeScalerDelta = volumeDelta / 32768.0f; for (sint32 i = 0; i < sampleCount; i++) { volumeScaler += (float)volumeScalerDelta; sampleData[i] *= volumeScaler; } - if (volumeDelta != 0) + internalShadowCopy->veVolume = (uint16)((sint32)internalShadowCopy->veVolume + volumeDelta * sampleCount); + } + + FORCE_INLINE float ClampADSRSample(float x) + { + if (x > 8388352.0f) + return 8388352.0f; + if (x < -8388608.0f) + return -8388608.0f; + return x; + } + + void AXVoiceMix_ApplyADSR_Sndcore2(AXVPBInternal_t* internalShadowCopy, float* sampleData, sint32 sampleCount) + { + // sndcore2 clamps samples to -8388608,8388352 range (16bit signed range << 8) + uint16 volume = internalShadowCopy->veVolume; + sint16 volumeDelta = (sint16)internalShadowCopy->veDelta; + uint16 finalVolume = (uint16)((sint32)volume + volumeDelta * sampleCount); + if (volume == 0x8000 && volumeDelta == 0) + return; + if (volume <= 0x8000 && finalVolume <= 0x8000) // if volume scaler doesn't exceed 1.0 then we don't need to clamp. We assume that volume never wraps around + return AXVoiceMix_ApplyADSR_NoClamp(internalShadowCopy, sampleData, sampleCount); + float volumeScaler = (float)volume / 32768.0f; + if (volumeDelta == 0) { - volume = (uint16)(volumeScaler * 32768.0); - internalShadowCopy->veVolume = volume; + // without delta + for (sint32 i = 0; i < sampleCount; i += 4) + { + sampleData[i+0] = ClampADSRSample(sampleData[i+0] * volumeScaler); + sampleData[i+1] = ClampADSRSample(sampleData[i+1] * volumeScaler); + sampleData[i+2] = ClampADSRSample(sampleData[i+2] * volumeScaler); + sampleData[i+3] = ClampADSRSample(sampleData[i+3] * volumeScaler); + } + return; } + // with delta + float volumeScalerDelta = volumeDelta / 32768.0f; + for (sint32 i = 0; i < sampleCount; i++) + { + volumeScaler += volumeScalerDelta; + sampleData[i] = ClampADSRSample(sampleData[i] * volumeScaler); + } + internalShadowCopy->veVolume = finalVolume; + } + + void AXVoiceMix_ApplyADSR(AXVPBInternal_t* internalShadowCopy, float* sampleData, sint32 sampleCount) + { + if (sndGeneric.isSoundCore2) + AXVoiceMix_ApplyADSR_Sndcore2(internalShadowCopy, sampleData, sampleCount); + else + AXVoiceMix_ApplyADSR_NoClamp(internalShadowCopy, sampleData, sampleCount); } void AXVoiceMix_ApplyBiquad(AXVPBInternal_t* internalShadowCopy, float* sampleData, sint32 sampleCount)