mirror of
https://github.com/cemu-project/Cemu.git
synced 2026-07-11 01:25:15 +02:00
sndcore: Improve PCM16 linear sampling + correctly clamp sndcore2 ADSR
This commit is contained in:
@@ -78,6 +78,7 @@ namespace snd_core
|
||||
const int AX_FILTER_LOWPASS_16K = 0x2;
|
||||
|
||||
bool isInitialized();
|
||||
bool IsSndCore2();
|
||||
void reset();
|
||||
|
||||
// AX VPB
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user