From ce56cfc0bc3a13150706357a09bf7559b81603fe Mon Sep 17 00:00:00 2001 From: Cuyler36 Date: Thu, 23 Oct 2025 04:00:49 -0400 Subject: [PATCH] jaudio_NES/driver.c 98% --- include/jaudio_NES/audiocommon.h | 30 ++++ include/jaudio_NES/audiostruct.h | 2 +- include/jaudio_NES/driver.h | 2 +- src/static/jaudio_NES/internal/driver.c | 228 +++++++++++++++++++++++- 4 files changed, 255 insertions(+), 7 deletions(-) diff --git a/include/jaudio_NES/audiocommon.h b/include/jaudio_NES/audiocommon.h index 0dfa9e9a..7c5d2d36 100644 --- a/include/jaudio_NES/audiocommon.h +++ b/include/jaudio_NES/audiocommon.h @@ -168,6 +168,36 @@ extern "C" { #define aFirLoadTable(pkt, size, addr) aFirFilter(pkt, 2, size, addr) +#define aEnvMixer2(pkt, dmemi, count, swapLR, x0, x1, x2, x3, m, bits) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = (bits | _SHIFTL(dmemi >> 4, 16, 8) | \ + _SHIFTL(count, 8, 8) | _SHIFTL(swapLR, 4, 1) | \ + _SHIFTL(x0, 3, 1) | _SHIFTL(x1, 2, 1) | \ + _SHIFTL(x2, 1, 1) | _SHIFTL(x3, 0, 1)); \ + _a->words.w1 = (unsigned int)(m); \ +} + +#define aAddMixer(pkt, count, dmemi, dmemo, a4) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_CMD_ADDMIXER, 24, 8) | \ + _SHIFTL(count >> 4, 16, 8) | _SHIFTL(a4, 0, 16)); \ + _a->words.w1 = _SHIFTL(dmemi, 16, 16) | _SHIFTL(dmemo, 0, 16); \ +} + +// from MM +#define aResampleZoh(pkt, pitch, pitchAccu) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_CMD_RESAMPLE_ZOH, 24, 8) | \ + _SHIFTL(pitch, 0, 16)); \ + _a->words.w1 = _SHIFTL(pitchAccu, 0, 16); \ +} + #define NA_MAKE_COMMAND(a0, a1, a2, a3) \ (u32)((((a0) & 0xFF) << 24) | (((a1) & 0xFF) << 16) | (((a2) & 0xFF) << 8) | (((a3) & 0xFF) << 0)) diff --git a/include/jaudio_NES/audiostruct.h b/include/jaudio_NES/audiostruct.h index d8a5d7ca..a8de0d20 100644 --- a/include/jaudio_NES/audiostruct.h +++ b/include/jaudio_NES/audiostruct.h @@ -278,7 +278,7 @@ typedef struct driverch_ { /* 0x06 */ u8 comb_filter_needs_init; /* 0x07 */ u8 vel_conv_table_idx; /* 0x08 */ u16 sample_pos_fractional_part; - /* 0x0A */ u16 sound_effect_gain; + /* 0x0A */ u16 surround_effect_gain; /* 0x0C */ s32 sample_pos_integer_part; /* 0x10 */ synthparams* synth_params; /* 0x14 */ s16 current_volume_left; diff --git a/include/jaudio_NES/driver.h b/include/jaudio_NES/driver.h index 95171e8f..b09a5292 100644 --- a/include/jaudio_NES/driver.h +++ b/include/jaudio_NES/driver.h @@ -19,7 +19,7 @@ extern Acmd* Nas_smzAudioFrame(Acmd* cmds, s32* processed_cmds, s16* pSamples, s32 nSamples); extern Acmd* Nas_DriveRsp(s16* aiBuf, s32 aiBufLen, Acmd* cmd, s32 updateIndex); extern Acmd* Nas_SynthMain(s32 chan_id, commonch* common, driverch* driver, s16* samples, s32 samples_per_update, Acmd* cmd, s32 update_idx); -extern Acmd* Nas_Synth_Resample(Acmd* cmd, const driverch* driver, s32 size, u16 frequencyFixedPoint, u16 sampleDmemBeforeResampling, s32 flags); +extern Acmd* Nas_Synth_Resample(Acmd* cmd, const driverch* driver, s32 size, u16 pitch, u16 sampleDmemBeforeResampling, s32 flags); extern Acmd* Nas_DolbySurround(Acmd* cmd, commonch* common, driverch* driver, s32 samples_per_update, s32 dmem, s32 flags); extern Acmd* Nas_Synth_Envelope(Acmd* cmd, commonch* common, driverch* driver, s32 samples_per_update, u16 dmem, s32 haasEffectDelaySide, s32 flags); extern Acmd* Nas_Synth_Delay(Acmd* cmd, commonch* common, driverch* driver, s32 size, s32 flags, s32 haasEffectDelaySide); diff --git a/src/static/jaudio_NES/internal/driver.c b/src/static/jaudio_NES/internal/driver.c index 4c497d43..16dec490 100644 --- a/src/static/jaudio_NES/internal/driver.c +++ b/src/static/jaudio_NES/internal/driver.c @@ -1,5 +1,6 @@ #include "jaudio_NES/driver.h" +#include "PR/abi.h" #include "jaudio_NES/audiocommon.h" #include "jaudio_NES/audiostruct.h" #include "jaudio_NES/astest.h" @@ -7,6 +8,7 @@ #include "jaudio_NES/track.h" #include "jaudio_NES/system.h" #include "os/OSCache.h" +#include "types.h" #define DMEM_TEMP 0x380 #define DMEM_WET_TEMP 0x3A0 @@ -14,8 +16,11 @@ #define DMEM_LEFT_CH 0x900 #define DMEM_RIGHT_CH 0xAA0 #define DMEM_WET_LEFT_CH 0xC40 +#define DMEM_WET_RIGHT_CH 0xDE0 #define DMEM_UNCOMPRESSED_NOTE 0x540 #define DMEM_COMPRESSED_ADPCM_DATA 0x900 +#define DMEM_HAAS_TEMP 0x580 +#define DMEM_SURROUND_TEMP 0x480 typedef enum { /* 0 */ HAAS_EFFECT_DELAY_NONE, @@ -23,14 +28,14 @@ typedef enum { /* 2 */ HAAS_EFFECT_DELAY_RIGHT // Delay right channel so that left channel is heard first } HaasEffectDelaySide; -static u16 NOISE_TABLE[] = { 0, 1, 2, 4, 8, 12, 16, 20, 24, 32, 36, 40, 46, 52, 56, 53 }; +static u16 NOISE_TABLE[] = { 0, 1, 2, 4, 8, 12, 16, 20, 24, 32, 36, 40, 46, 52, 56, 64 }; static dspch_ DSPCH[64]; static s32 STOP_VELOCONV = 0; -static u32 Env_DataH = (A_ENVMIXER << 24) | (0x00 << 16) | (0x00 << 8) | (0x00); -static u32 Env_DataL1 = 0x58AAC4DE; -static u32 Env_DataL2 = 0x9058C4DE; -static u32 Env_DataL3 = 0x9058C4DE; +static u32 Env_DataH = (A_CMD_ENVMIXER << 24) | (0x00 << 16) | (0x00 << 8) | (0x00); +static u32 Env_Data_L1 = 0x58AAC4DE; +static u32 Env_Data_L2 = 0x9058C4DE; +static u32 Env_Data_L3 = 0x90AAC4DE; static Acmd* __LoadAuxBuf(Acmd* cmd, u16 ofs, u16 startPos, s32 size, delay* del_p); static Acmd* __SaveAuxBuf(Acmd* cmd, u16 ofs, u16 startPos, s32 size, delay* del_p); @@ -1240,3 +1245,216 @@ codec_continue_and_skip: return cmd; } + +Acmd* Nas_DolbySurround(Acmd* cmd, commonch* common, driverch* driver, s32 num_samples_per_update, s32 haas_dmem, s32 flags) { + s32 size; + s32 wetGain; + u16 dryGain; + s64 dmem = DMEM_SURROUND_TEMP; + f32 decayGain; + + size = num_samples_per_update * SAMPLE_SIZE; + Nas_DMEMMove(cmd++, haas_dmem, DMEM_HAAS_TEMP, size); + dryGain = driver->surround_effect_gain; + + if (flags == A_INIT) { + aClearBuffer(cmd++, dmem, sizeof(driver->synth_params->surround_effect_state)); + driver->surround_effect_gain = 0; + } else { + wetGain = (driver->surround_effect_gain * driver->cur_reverb_vol) >> 7; + + aLoadBuffer2(cmd++, driver->synth_params->surround_effect_state, dmem, sizeof(driver->synth_params->surround_effect_state)); + + aMix(cmd++, size >> 4, dryGain, dmem, DMEM_LEFT_CH); + aMix(cmd++, size >> 4, (dryGain ^ 0xFFFF), dmem, DMEM_RIGHT_CH); + + aMix(cmd++, size >> 4, wetGain, dmem, DMEM_WET_LEFT_CH); + aMix(cmd++, size >> 4, (wetGain ^ 0xFFFF), dmem, DMEM_WET_RIGHT_CH); + } + + aSaveBuffer2(cmd++, DMEM_SURROUND_TEMP + size, driver->synth_params->surround_effect_state, sizeof(driver->synth_params->surround_effect_state)); + + decayGain = (common->target_volume_left + common->target_volume_right) / 8192.0f; // 1.0f / 0x2000 + + if (decayGain > 1.0f) { + decayGain = 1.0f; + } + + decayGain = decayGain * StereoLeft[127 - common->surround_effect_idx]; + driver->surround_effect_gain = ((decayGain * 0x7FFF) + driver->surround_effect_gain) / 2; + + Nas_DMEMMove(cmd++, DMEM_HAAS_TEMP, haas_dmem, size); + + return cmd; +} + +extern Acmd* Nas_Synth_Resample(Acmd* cmd, const driverch* driver, s32 size, u16 pitch, u16 sampleDmemBeforeResampling, s32 flags) { + if (pitch == 0) { + Nas_ClearBuffer(cmd++, DMEM_TEMP, size); + } else { + aSetBuffer(cmd++, 0, sampleDmemBeforeResampling, DMEM_TEMP, size); + aResample(cmd++, flags, pitch, driver->synth_params->final_resample_state); + } + + return cmd; +} + +extern Acmd* Nas_Synth_Envelope(Acmd* cmd, commonch* common, driverch* driver, s32 samples_per_update, u16 dmem, s32 haasEffectDelaySide, s32 flags) { + u16 targetVolRight; + u16 targetVolLeft; + u32 dmemDests; + u16 curVolLeft; + u16 curVolRight; + s32 curReverbVolAndFlags; + u16 curReverbVol; + s32 targetReverbVol; + s16 rampLeft; + s16 rampRight; + s16 rampReverb; + f32 defaultPanVolume; + + + targetReverbVol = common->target_reverb_volume; + curVolLeft = driver->current_volume_left; + curVolRight = driver->current_volume_right; + + targetVolLeft = common->target_volume_left << 4; + targetVolRight = common->target_volume_right << 4; + + if ((AG.sound_mode == SOUND_OUTPUT_DOLBY_SURROUND)) { + u8 idx = common->surround_effect_idx; + + if (idx != 0xFF) { + defaultPanVolume = StereoLeft[idx]; + targetVolLeft *= defaultPanVolume; + targetVolRight *= defaultPanVolume; + } + } + + if (targetVolLeft != curVolLeft) { + rampLeft = (targetVolLeft - curVolLeft) / (samples_per_update >> 3); + } else { + rampLeft = 0; + } + + if (targetVolRight != curVolRight) { + rampRight = (targetVolRight - curVolRight) / (samples_per_update >> 3); + } else { + rampRight = 0; + } + + curReverbVolAndFlags = (s16)driver->cur_reverb_vol; + if (targetReverbVol != curReverbVolAndFlags) { + curReverbVol = curReverbVolAndFlags & 0x7F; + rampReverb = (((targetReverbVol & 0x7F) - (curReverbVol)) << 9) / (samples_per_update >> 3); + driver->cur_reverb_vol = targetReverbVol; + } else { + rampReverb = 0; + } + + driver->current_volume_left = curVolLeft + (rampLeft * (samples_per_update >> 3)); + driver->current_volume_right = curVolRight + (rampRight * (samples_per_update >> 3)); + + if (common->use_haas_effect) { + Nas_ClearBuffer(cmd++, DMEM_HAAS_TEMP, DMEM_1CH_SIZE); + curReverbVol = curReverbVolAndFlags & 0x7F; + Nas_SetEnvParam(cmd++, curReverbVol * 2, rampReverb, rampLeft, rampRight); + Nas_SetEnvParam2(cmd++, curVolLeft, curVolRight); + + switch (haasEffectDelaySide) { + case HAAS_EFFECT_DELAY_LEFT: + // Store the left dry channel in a temp space to be delayed to produce the haas effect + dmemDests = Env_Data_L1; + break; + + case HAAS_EFFECT_DELAY_RIGHT: + // Store the right dry channel in a temp space to be delayed to produce the haas effect + dmemDests = Env_Data_L2; + break; + + default: // HAAS_EFFECT_DELAY_NONE + dmemDests = Env_Data_L3; + break; + } + } else { + curReverbVol = curReverbVolAndFlags & 0x7F; + aSetEnvParam(cmd++, curReverbVol * 2, rampReverb, rampLeft, rampRight); + aSetEnvParam2(cmd++, curVolLeft, curVolRight); + dmemDests = Env_Data_L3; + } + + aEnvMixer2(cmd++, dmem, samples_per_update, (curReverbVolAndFlags & 0x80) >> 7, + common->strong_reverb_right, common->strong_reverb_left, + common->strong_right, common->strong_left, dmemDests, Env_DataH); + + return cmd; +} + +extern Acmd* Nas_Synth_Delay(Acmd* cmd, commonch* common, driverch* driver, s32 size, s32 flags, s32 haasEffectDelaySide) { + u16 dmemDest; + u16 pitch; + u16 prevHaasEffectDelaySize; + u16 haasEffectDelaySize; + + switch (haasEffectDelaySide) { + case HAAS_EFFECT_DELAY_LEFT: + // Delay the sample on the left channel + // This allows the right channel to be heard first + dmemDest = DMEM_LEFT_CH; + haasEffectDelaySize = common->haas_effect_left_delay_size; + prevHaasEffectDelaySize = driver->prev_haas_effect_left_delay_size; + driver->prev_haas_effect_left_delay_size = haasEffectDelaySize; + driver->prev_haas_effect_right_delay_size = 0; + break; + + case HAAS_EFFECT_DELAY_RIGHT: + // Delay the sample on the right channel + // This allows the left channel to be heard first + dmemDest = DMEM_RIGHT_CH; + haasEffectDelaySize = common->haas_effect_right_delay_size; + prevHaasEffectDelaySize = driver->prev_haas_effect_right_delay_size; + driver->prev_haas_effect_right_delay_size = haasEffectDelaySize; + driver->prev_haas_effect_left_delay_size = 0; + break; + + default: // HAAS_EFFECT_DELAY_NONE + return cmd; + } + + if (flags != A_INIT) { + // Slightly adjust the sample rate in order to fit a change in sample delay + if (haasEffectDelaySize != prevHaasEffectDelaySize) { + pitch = (((size << 0xF) / 2) - 1) / ((size + haasEffectDelaySize - prevHaasEffectDelaySize - 2) / 2); + aSetBuffer(cmd++, 0, DMEM_HAAS_TEMP, DMEM_TEMP, size + haasEffectDelaySize - prevHaasEffectDelaySize); + aResampleZoh(cmd++, pitch, 0); + } else { + aDMEMMove(cmd++, DMEM_HAAS_TEMP, DMEM_TEMP, size); + } + + if (prevHaasEffectDelaySize != 0) { + aLoadBuffer2(cmd++, driver->synth_params->haas_effect_delay_state, DMEM_HAAS_TEMP, + ALIGN_NEXT(prevHaasEffectDelaySize, 16)); + aDMEMMove(cmd++, DMEM_TEMP, DMEM_HAAS_TEMP + prevHaasEffectDelaySize, + size + haasEffectDelaySize - prevHaasEffectDelaySize); + } else { + aDMEMMove(cmd++, DMEM_TEMP, DMEM_HAAS_TEMP, size + haasEffectDelaySize); + } + } else { + // Just apply a delay directly + aDMEMMove(cmd++, DMEM_HAAS_TEMP, DMEM_TEMP, size); + if (haasEffectDelaySize) { // != 0 + aClearBuffer(cmd++, DMEM_HAAS_TEMP, haasEffectDelaySize); + } + aDMEMMove(cmd++, DMEM_TEMP, DMEM_HAAS_TEMP + haasEffectDelaySize, size); + } + + if (haasEffectDelaySize) { // != 0 + // Save excessive samples for next iteration + aSaveBuffer2(cmd++, DMEM_HAAS_TEMP + size, driver->synth_params->haas_effect_delay_state, + ALIGN_NEXT(haasEffectDelaySize, 16)); + } + + aAddMixer(cmd++, ALIGN_NEXT(size, 64), DMEM_HAAS_TEMP, dmemDest, 0x7FFF); + + return cmd; +}