Add 6-channel audio support.

This commit is contained in:
Tortuga veloz 2025-03-12 14:20:15 +01:00 committed by Alejandro Asenjo Nitti
parent 94553db6c1
commit 798f6fbe1f
13 changed files with 433 additions and 326 deletions

View file

@ -52,10 +52,14 @@ typedef void (*AudioCustomUpdateFunction)(void);
// Samples are processed in groups of 16 called a "frame" // Samples are processed in groups of 16 called a "frame"
#define SAMPLES_PER_FRAME ADPCMFSIZE #define SAMPLES_PER_FRAME ADPCMFSIZE
// The length of one left/right channel is 12 frames #define MAX_NUM_AUDIO_CHANNELS 6
// The length of one channel is 12 frames
#define DMEM_1CH_SIZE (12 * SAMPLES_PER_FRAME * SAMPLE_SIZE) #define DMEM_1CH_SIZE (12 * SAMPLES_PER_FRAME * SAMPLE_SIZE)
// Both left and right channels // Both left and right channels
#define DMEM_2CH_SIZE (2 * DMEM_1CH_SIZE) #define DMEM_2CH_SIZE (2 * DMEM_1CH_SIZE)
// 6 channels
#define DMEM_6CH_SIZE (6 * DMEM_1CH_SIZE)
#define AIBUF_LEN (170 * SAMPLES_PER_FRAME) // number of samples #define AIBUF_LEN (170 * SAMPLES_PER_FRAME) // number of samples
#define AIBUF_SIZE (AIBUF_LEN * SAMPLE_SIZE) // number of bytes #define AIBUF_SIZE (AIBUF_LEN * SAMPLE_SIZE) // number of bytes
@ -399,7 +403,8 @@ typedef struct {
typedef struct { typedef struct {
/* 0x00 */ u8 strongLeft : 1; /* 0x00 */ u8 strongLeft : 1;
/* 0x00 */ u8 strongRight : 1; /* 0x00 */ u8 strongRight : 1;
/* 0x00 */ u8 bit2 : 2; /* 0x00 */ u8 is_voice : 1;
/* 0x00 */ u8 is_sfx : 1;
/* 0x00 */ u8 unused : 2; /* 0x00 */ u8 unused : 2;
/* 0x00 */ u8 usesHeadsetPanEffects : 1; /* 0x00 */ u8 usesHeadsetPanEffects : 1;
/* 0x00 */ u8 stereoHeadsetEffects : 1; /* 0x00 */ u8 stereoHeadsetEffects : 1;
@ -428,7 +433,8 @@ typedef struct SequenceChannel {
/* 0x00 */ u8 hasInstrument : 1; /* 0x00 */ u8 hasInstrument : 1;
/* 0x00 */ u8 stereoHeadsetEffects : 1; /* 0x00 */ u8 stereoHeadsetEffects : 1;
/* 0x00 */ u8 largeNotes : 1; // notes specify duration and velocity /* 0x00 */ u8 largeNotes : 1; // notes specify duration and velocity
/* 0x00 */ u8 unused : 1; /* 0x00 */ u8 is_voice : 1;
/* 0x00 */ u8 is_sfx : 1;
union { union {
struct { struct {
/* 0x4 */ char pad_4 : 1; /* 0x4 */ char pad_4 : 1;
@ -551,8 +557,12 @@ typedef struct {
/* 0x0C */ NoteSynthesisBuffers* synthesisBuffers; /* 0x0C */ NoteSynthesisBuffers* synthesisBuffers;
/* 0x10 */ s16 curVolLeft; /* 0x10 */ s16 curVolLeft;
/* 0x12 */ s16 curVolRight; /* 0x12 */ s16 curVolRight;
/* 0x14 */ char unk_14[0xC]; /* 0x14 */ s16 curVolCenter;
} NoteSynthesisState; // size = 0x20 /* 0x16 */ s16 curVolLfe;
/* 0x18 */ s16 curVolRLeft;
/* 0x1A */ s16 curVolRRight;
/* 0x1C */ char unk_14[0xC];
} NoteSynthesisState; // size = 0x1E
typedef struct { typedef struct {
/* 0x00 */ struct SequenceChannel* channel; /* 0x00 */ struct SequenceChannel* channel;
@ -609,9 +619,13 @@ typedef struct {
/* 0x05 */ u8 reverb; /* 0x05 */ u8 reverb;
/* 0x06 */ u16 panVolLeft; /* 0x06 */ u16 panVolLeft;
/* 0x08 */ u16 panVolRight; /* 0x08 */ u16 panVolRight;
/* 0x0A */ u16 resampleRate; /* 0x0A */ u16 panVolCenter;
/* 0x0C */ Sample** waveSampleAddr; /* 0x0C */ u16 panVolLfe;
} NoteSubEu; // size = 0x10 /* 0x0E */ u16 panVolRLeft;
/* 0x10 */ u16 panVolRRight;
/* 0x12 */ u16 resampleRate;
/* 0x14 */ Sample** waveSampleAddr;
} NoteSubEu; // size = 0x16
typedef struct Note { typedef struct Note {
/* 0x00 */ AudioListItem listItem; /* 0x00 */ AudioListItem listItem;

@ -1 +1 @@
Subproject commit c6bd64c5da0e55a1c00e969bc01046f9335cda43 Subproject commit e1db49367bb3743a304d763be34830b4c1999873

View file

@ -454,26 +454,37 @@ s8 Audio_GetSfxReverb(u8 bankId, u8 entryIndex, u8 channelId) {
s8 Audio_GetSfxPan(f32 xPos, f32 zPos, u8 mode) { s8 Audio_GetSfxPan(f32 xPos, f32 zPos, u8 mode) {
if (sSfxChannelLayout != SFXCHAN_3) { if (sSfxChannelLayout != SFXCHAN_3) {
f32 absx = ABSF(xPos); float absx = ABSF(xPos);
f32 absz = ABSF(zPos); float absz = ABSF(zPos);
f32 pan;
if ((absx < 1.0f) && (absz < 1.0f)) { // [0, 0] would be a degenerate value
// Consider these to be close to zero and put in center
if (absx < 1.f && absz < 1.f) {
return 64; return 64;
} }
absx = MIN(1200.0f, absx);
absz = MIN(1200.0f, absz);
if ((xPos == 0) && (zPos == 0)) { if (GetNumAudioChannels() == 2) {
pan = 0.5f; float pan;
} else if ((xPos >= 0.f) && (absz <= absx)) { absx = MIN(1200.0f, absx);
pan = 1.0f - ((2400.0f - absx) / (10.0f * (2400.0f - absz))); absz = MIN(1200.0f, absz);
} else if ((xPos < 0.0f) && (absz <= absx)) {
pan = (2400.0f - absx) / (10.0f * (2400.0f - absz)); if ((xPos == 0) && (zPos == 0)) {
pan = 0.5f;
} else if ((xPos >= 0.f) && (absz <= absx)) {
pan = 1.0f - ((2400.0f - absx) / (10.0f * (2400.0f - absz)));
} else if ((xPos < 0.0f) && (absz <= absx)) {
pan = (2400.0f - absx) / (10.0f * (2400.0f - absz));
} else {
pan = (xPos / (2.5f * absz)) + 0.5f;
}
return ROUND(pan * 127.0f);
} else { } else {
pan = (xPos / (2.5f * absz)) + 0.5f; // Calculate the angle in radians
float angle = atan2f(xPos, -zPos);
float normalized_angle = (angle / (2 * M_PI)) + 0.5f;
s8 pan = (s8) (normalized_angle * 127);
return pan;
} }
return ROUND(pan * 127.0f);
} else if (mode != 4) { } else if (mode != 4) {
return ((mode & 1) * 127); return ((mode & 1) * 127);
} }
@ -507,7 +518,6 @@ void Audio_SetSfxProperties(u8 bankId, u8 entryIndex, u8 channelId) {
f32 freqMod = 1.0f; f32 freqMod = 1.0f;
s8 pan = 64; s8 pan = 64;
SfxBankEntry* entry = &sSfxBanks[bankId][entryIndex]; SfxBankEntry* entry = &sSfxBanks[bankId][entryIndex];
switch (bankId) { switch (bankId) {
case SFX_BANK_PLAYER: case SFX_BANK_PLAYER:
case SFX_BANK_1: case SFX_BANK_1:

View file

@ -67,8 +67,7 @@ int testBits(void) {
void Audio_InitNoteSub(Note* note, NoteAttributes* noteAttr) { void Audio_InitNoteSub(Note* note, NoteAttributes* noteAttr) {
NoteSubEu* noteSub; NoteSubEu* noteSub;
f32 panVolumeLeft; f32 panVolumeLeft = 0, panVolumeRight = 0, panVolumeRearLeft = 0, panVolumeRearRight = 0, panVolumeCenter = 0;
f32 pamVolumeRight;
f32 velocity; f32 velocity;
s32 temp_t0; s32 temp_t0;
s32 var_a0; s32 var_a0;
@ -86,59 +85,95 @@ void Audio_InitNoteSub(Note* note, NoteAttributes* noteAttr) {
pan = noteAttr->pan; pan = noteAttr->pan;
reverb = noteAttr->reverb; reverb = noteAttr->reverb;
stereo = noteAttr->stereo; stereo = noteAttr->stereo;
pan %= ARRAY_COUNTU(gHeadsetPanVolume);
if ((noteSub->bitField0.stereoHeadsetEffects) && (gAudioSoundMode == SOUNDMODE_HEADSET)) {
var_a0 = pan >> 1;
if (var_a0 >= ARRAY_COUNT(gHaasEffectDelaySizes)) {
var_a0 = ARRAY_COUNT(gHaasEffectDelaySizes) - 1;
}
noteSub->rightDelaySize = gHaasEffectDelaySizes[var_a0];
noteSub->leftDelaySize = gHaasEffectDelaySizes[ARRAY_COUNT(gHaasEffectDelaySizes) - 1 - var_a0];
noteSub->bitField0.stereoStrongRight = false;
noteSub->bitField0.stereoStrongLeft = false;
noteSub->bitField0.usesHeadsetPanEffects = true;
panVolumeLeft = gHeadsetPanVolume[pan]; if (GetNumAudioChannels() == 2) {
pamVolumeRight = gHeadsetPanVolume[ARRAY_COUNT(gHeadsetPanVolume) - 1 - pan]; pan %= ARRAY_COUNTU(gHeadsetPanVolume);
} else if (noteSub->bitField0.stereoHeadsetEffects && (gAudioSoundMode == SOUNDMODE_STEREO)) { if ((noteSub->bitField0.stereoHeadsetEffects) && (gAudioSoundMode == SOUNDMODE_HEADSET)) {
noteSub->leftDelaySize = 0; var_a0 = pan >> 1;
noteSub->rightDelaySize = 0; if (var_a0 >= ARRAY_COUNT(gHaasEffectDelaySizes)) {
noteSub->bitField0.usesHeadsetPanEffects = false; var_a0 = ARRAY_COUNT(gHaasEffectDelaySizes) - 1;
}
panVolumeLeft = gStereoPanVolume[pan]; noteSub->rightDelaySize = gHaasEffectDelaySizes[var_a0];
pamVolumeRight = gStereoPanVolume[ARRAY_COUNT(gStereoPanVolume) - 1 - pan]; noteSub->leftDelaySize = gHaasEffectDelaySizes[ARRAY_COUNT(gHaasEffectDelaySizes) - 1 - var_a0];
strongRight = false; noteSub->bitField0.stereoStrongRight = false;
strongLeft = false; noteSub->bitField0.stereoStrongLeft = false;
if (pan < 32) { noteSub->bitField0.usesHeadsetPanEffects = true;
strongLeft = true; panVolumeLeft = gHeadsetPanVolume[pan];
} else if (pan > 96) { panVolumeRight = gHeadsetPanVolume[ARRAY_COUNT(gHeadsetPanVolume) - 1 - pan];
strongRight = true; } else if (noteSub->bitField0.stereoHeadsetEffects && (gAudioSoundMode == SOUNDMODE_STEREO)) {
noteSub->leftDelaySize = 0;
noteSub->rightDelaySize = 0;
noteSub->bitField0.usesHeadsetPanEffects = false;
panVolumeLeft = gStereoPanVolume[pan];
panVolumeRight = gStereoPanVolume[ARRAY_COUNT(gStereoPanVolume) - 1 - pan];
strongRight = false;
strongLeft = false;
if (pan < 32) {
strongLeft = true;
} else if (pan > 96) {
strongRight = true;
}
noteSub->bitField0.stereoStrongRight = strongRight;
noteSub->bitField0.stereoStrongLeft = strongLeft;
switch (stereo.s.bit2) {
case 0:
noteSub->bitField0.stereoStrongRight = stereo.s.strongRight;
noteSub->bitField0.stereoStrongLeft = stereo.s.strongLeft;
break;
case 1:
break;
case 2:
noteSub->bitField0.stereoStrongRight = stereo.s.strongRight | strongRight;
noteSub->bitField0.stereoStrongLeft = stereo.s.strongLeft | strongLeft;
break;
case 3:
noteSub->bitField0.stereoStrongRight = stereo.s.strongRight ^ strongRight;
noteSub->bitField0.stereoStrongLeft = stereo.s.strongLeft ^ strongLeft;
break;
}
} else if (gAudioSoundMode == SOUNDMODE_MONO) {
panVolumeLeft = 0.707f;
panVolumeRight = 0.707f;
} else {
panVolumeLeft = gDefaultPanVolume[pan];
panVolumeRight = gDefaultPanVolume[ARRAY_COUNT(gDefaultPanVolume) - 1 - pan];
} }
noteSub->bitField0.stereoStrongRight = strongRight;
noteSub->bitField0.stereoStrongLeft = strongLeft;
switch (stereo.s.bit2) {
case 0:
noteSub->bitField0.stereoStrongRight = stereo.s.strongRight;
noteSub->bitField0.stereoStrongLeft = stereo.s.strongLeft;
break;
case 1:
break;
case 2:
noteSub->bitField0.stereoStrongRight = stereo.s.strongRight | strongRight;
noteSub->bitField0.stereoStrongLeft = stereo.s.strongLeft | strongLeft;
break;
case 3:
noteSub->bitField0.stereoStrongRight = stereo.s.strongRight ^ strongRight;
noteSub->bitField0.stereoStrongLeft = stereo.s.strongLeft ^ strongLeft;
break;
}
} else if (gAudioSoundMode == SOUNDMODE_MONO) {
panVolumeLeft = 0.707f;
pamVolumeRight = 0.707f;
} else { } else {
panVolumeLeft = gDefaultPanVolume[pan]; // Surround 5.1
pamVolumeRight = gDefaultPanVolume[ARRAY_COUNT(gDefaultPanVolume) - 1 - pan]; const float vol_voice = 0.8f;
const float vol_music = 0.707f;
if (stereo.s.is_voice) { // VOICE
panVolumeCenter = vol_voice;
}
else if (stereo.s.is_sfx) { // SFX
float pan_angle = (float)(pan + 64) / 128 * 2 * M_PI;
// Speaker angles in radians
const float front_left = -0.5236;
const float front_right = 0.5236;
const float rear_left = -1.92;
const float rear_right = 1.92;
// Normalize pan_angle to [0, 2π]
pan_angle = fmodf(pan_angle, 2 * M_PI);
if (pan_angle < 0) pan_angle += 2 * M_PI;
// Calculate volumes using cosine panning law
panVolumeLeft = fmaxf(0, cosf(pan_angle - front_left)); // Front Left
panVolumeRight = fmaxf(0, cosf(pan_angle - front_right)); // Front Right
panVolumeRearLeft = fmaxf(0, cosf(pan_angle - rear_left)); // Rear Left
panVolumeRearRight = fmaxf(0, cosf(pan_angle - rear_right)); // Rear Right
// printf("pan: %d, pan_angle: %f, left: %f, right: %f, rleft: %f, rright: %f\n", pan, pan_angle, panVolumeLeft, panVolumeRight, panVolumeRearLeft, panVolumeRearRight);
} else { // MUSIC
panVolumeLeft = vol_music;
panVolumeRight = vol_music;
panVolumeRearLeft = vol_music;
panVolumeRearRight = vol_music;
}
} }
if (velocity < 0.0f) { if (velocity < 0.0f) {
velocity = 0.0f; velocity = 0.0f;
} }
@ -148,16 +183,15 @@ void Audio_InitNoteSub(Note* note, NoteAttributes* noteAttr) {
float master_vol = CVarGetFloat("gGameMasterVolume", 1.0f); float master_vol = CVarGetFloat("gGameMasterVolume", 1.0f);
noteSub->panVolLeft = (s32) (velocity * panVolumeLeft * 4095.999f) * master_vol; noteSub->panVolLeft = (s32) (velocity * panVolumeLeft * 4095.999f) * master_vol;
noteSub->panVolRight = (s32) (velocity * pamVolumeRight * 4095.999f) * master_vol; noteSub->panVolRight = (s32) (velocity * panVolumeRight * 4095.999f) * master_vol;
noteSub->panVolRLeft = (s32) (velocity * panVolumeRearLeft * 4095.999f) * master_vol;
noteSub->panVolRRight = (s32) (velocity * panVolumeRearRight * 4095.999f) * master_vol;
noteSub->panVolCenter = (s32) (velocity * panVolumeCenter * 4095.999f) * master_vol;
noteSub->panVolLfe = (s32) (velocity * 4095.999f) * master_vol;
noteSub->gain = noteAttr->gain; noteSub->gain = noteAttr->gain;
if (noteSub->reverb != reverb) { if (noteSub->reverb != reverb) {
noteSub->reverb = reverb; noteSub->reverb = reverb;
noteSub->bitField0.unused = true;
} else if (noteSub->bitField0.needsInit) {
noteSub->bitField0.unused = true;
} else {
noteSub->bitField0.unused = false;
} }
} }
@ -367,6 +401,8 @@ void Audio_ProcessNotes(void) {
sp70.velocity = playbackState->parentLayer->noteVelocity; sp70.velocity = playbackState->parentLayer->noteVelocity;
sp70.pan = playbackState->parentLayer->notePan; sp70.pan = playbackState->parentLayer->notePan;
sp70.stereo = playbackState->parentLayer->stereo; sp70.stereo = playbackState->parentLayer->stereo;
sp70.stereo.s.is_voice = playbackState->parentLayer->channel->is_voice;
sp70.stereo.s.is_sfx = playbackState->parentLayer->channel->is_sfx;
sp70.reverb = playbackState->parentLayer->channel->targetReverbVol; sp70.reverb = playbackState->parentLayer->channel->targetReverbVol;
sp70.gain = playbackState->parentLayer->channel->reverbIndex; sp70.gain = playbackState->parentLayer->channel->reverbIndex;

View file

@ -89,7 +89,8 @@ void AudioSeq_InitSequenceChannel(SequenceChannel* channel) {
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++) {
channel->seqScriptIO[i] = -1; channel->seqScriptIO[i] = -1;
} }
channel->unused = 0; channel->is_sfx = 0;
channel->is_voice = 0;
Audio_InitNoteLists(&channel->notePool); Audio_InitNoteLists(&channel->notePool);
} }

View file

@ -5,14 +5,23 @@
#include "port/Engine.h" #include "port/Engine.h"
#define DMEM_WET_SCRATCH 0x470 #define DMEM_WET_SCRATCH 0x470
#define DMEM_COMPRESSED_ADPCM_DATA 0x990 #define DMEM_COMPRESSED_ADPCM_DATA 0xD50
#define DMEM_LEFT_CH 0x990 #define DMEM_LEFT_CH 0xD50
#define DMEM_RIGHT_CH 0xB10 #define DMEM_RIGHT_CH (DMEM_LEFT_CH + DMEM_1CH_SIZE)
#define DMEM_CENTER_CH (DMEM_LEFT_CH + 2 * DMEM_1CH_SIZE)
#define DMEM_SUBWOOFER_CH (DMEM_LEFT_CH + 3 * DMEM_1CH_SIZE)
#define DMEM_REAR_LEFT_CH (DMEM_LEFT_CH + 4 * DMEM_1CH_SIZE)
#define DMEM_REAR_RIGHT_CH (DMEM_LEFT_CH + 5 * DMEM_1CH_SIZE)
#define DMEM_HAAS_TEMP 0x650 #define DMEM_HAAS_TEMP 0x650
#define DMEM_TEMP 0x450 #define DMEM_TEMP 0x450
#define DMEM_UNCOMPRESSED_NOTE 0x5F0 #define DMEM_UNCOMPRESSED_NOTE 0x5F0
#define DMEM_WET_LEFT_CH 0xC90 #define DMEM_WET_LEFT_CH (DMEM_LEFT_CH + 6 * DMEM_1CH_SIZE)
#define DMEM_WET_RIGHT_CH 0xE10 // = DMEM_WET_LEFT_CH + DMEM_1CH_SIZE #define DMEM_WET_RIGHT_CH (DMEM_WET_LEFT_CH + DMEM_1CH_SIZE)
#define DMEM_WET_CENTER_CH (DMEM_WET_LEFT_CH + 2 * DMEM_1CH_SIZE)
#define DMEM_WET_SUBWOOFER_CH (DMEM_WET_LEFT_CH + 3 * DMEM_1CH_SIZE)
#define DMEM_WET_REAR_LEFT_CH (DMEM_WET_LEFT_CH + 4 * DMEM_1CH_SIZE)
#define DMEM_WET_REAR_RIGHT_CH (DMEM_WET_LEFT_CH + 5 * DMEM_1CH_SIZE)
#define SAMPLE_SIZE sizeof(s16) #define SAMPLE_SIZE sizeof(s16)
typedef enum { typedef enum {
@ -77,7 +86,7 @@ void AudioSynth_InitNextRingBuf(s32 sampleCount, s32 itemIndex, s32 reverbIndex)
if ((reverb->downsampleRate != 1) && (reverb->framesToIgnore == 0)) { if ((reverb->downsampleRate != 1) && (reverb->framesToIgnore == 0)) {
ringItem = &reverb->items[reverb->curFrame][itemIndex]; ringItem = &reverb->items[reverb->curFrame][itemIndex];
osInvalDCache(ringItem->toDownsampleLeft, 0x300); osInvalDCache(ringItem->toDownsampleLeft, DMEM_1CH_SIZE * MAX_NUM_AUDIO_CHANNELS);
j = 0; j = 0;
for (i = 0; i < ringItem->lengthA / 2; i++, j += reverb->downsampleRate) { for (i = 0; i < ringItem->lengthA / 2; i++, j += reverb->downsampleRate) {
reverb->leftRingBuf[ringItem->startPos + i] = ringItem->toDownsampleLeft[j]; reverb->leftRingBuf[ringItem->startPos + i] = ringItem->toDownsampleLeft[j];
@ -675,6 +684,7 @@ Acmd* AudioSynth_Update(Acmd* aList, s32* cmdCount, s16* aiBufStart, s32 aiBufLe
} }
aiBufPtr = aiBufStart; aiBufPtr = aiBufStart;
for (i = gAudioBufferParams.ticksPerUpdate; i > 0; i--) { for (i = gAudioBufferParams.ticksPerUpdate; i > 0; i--) {
if (i == 1) { if (i == 1) {
chunkLen = aiBufLen; chunkLen = aiBufLen;
@ -693,9 +703,11 @@ Acmd* AudioSynth_Update(Acmd* aList, s32* cmdCount, s16* aiBufStart, s32 aiBufLe
} }
aCmdPtr = aCmdPtr =
AudioSynth_DoOneAudioUpdate((s16*) aiBufPtr, chunkLen, aCmdPtr, gAudioBufferParams.ticksPerUpdate - i); AudioSynth_DoOneAudioUpdate(aiBufPtr, chunkLen, aCmdPtr, gAudioBufferParams.ticksPerUpdate - i);
aiBufLen -= chunkLen; aiBufLen -= chunkLen;
aiBufPtr += chunkLen * 2;
int num_audio_channels = GetNumAudioChannels();
aiBufPtr += chunkLen * num_audio_channels;
} }
for (j = 0; j < gNumSynthReverbs; j++) { for (j = 0; j < gNumSynthReverbs; j++) {
@ -723,7 +735,7 @@ Acmd* AudioSynth_LoadReverbSamples(Acmd* aList, s32 aiBufLen, s16 reverbIndex, s
aList = aList =
AudioSynth_LoadRingBufferPart(aList, sp64->lengthA + DMEM_WET_LEFT_CH, 0, sp64->lengthB, reverbIndex); AudioSynth_LoadRingBufferPart(aList, sp64->lengthA + DMEM_WET_LEFT_CH, 0, sp64->lengthB, reverbIndex);
} }
aAddMixer(aList++, 0x300, DMEM_WET_LEFT_CH, DMEM_LEFT_CH); aAddMixer(aList++, DMEM_2CH_SIZE, DMEM_WET_LEFT_CH, DMEM_LEFT_CH);
aMix(aList++, 0x30, gSynthReverbs[reverbIndex].decayRatio + 0x8000, DMEM_WET_LEFT_CH, DMEM_WET_LEFT_CH); aMix(aList++, 0x30, gSynthReverbs[reverbIndex].decayRatio + 0x8000, DMEM_WET_LEFT_CH, DMEM_WET_LEFT_CH);
} else { } else {
sp62 = (sp64->startPos & 7) * 2; sp62 = (sp64->startPos & 7) * 2;
@ -739,7 +751,7 @@ Acmd* AudioSynth_LoadReverbSamples(Acmd* aList, s32 aiBufLen, s16 reverbIndex, s
aSetBuffer(aList++, 0, sp62 + DMEM_UNCOMPRESSED_NOTE, DMEM_WET_RIGHT_CH, aiBufLen * 2); aSetBuffer(aList++, 0, sp62 + DMEM_UNCOMPRESSED_NOTE, DMEM_WET_RIGHT_CH, aiBufLen * 2);
aResample(aList++, gSynthReverbs[reverbIndex].resampleFlags, gSynthReverbs[reverbIndex].unk_0A, aResample(aList++, gSynthReverbs[reverbIndex].resampleFlags, gSynthReverbs[reverbIndex].unk_0A,
OS_K0_TO_PHYSICAL(gSynthReverbs[reverbIndex].unk_34)); OS_K0_TO_PHYSICAL(gSynthReverbs[reverbIndex].unk_34));
aAddMixer(aList++, 0x300, DMEM_WET_LEFT_CH, DMEM_LEFT_CH); aAddMixer(aList++, DMEM_2CH_SIZE, DMEM_WET_LEFT_CH, DMEM_LEFT_CH);
aMix(aList++, 0x30, gSynthReverbs[reverbIndex].decayRatio + 0x8000, DMEM_WET_LEFT_CH, DMEM_WET_LEFT_CH); aMix(aList++, 0x30, gSynthReverbs[reverbIndex].decayRatio + 0x8000, DMEM_WET_LEFT_CH, DMEM_WET_LEFT_CH);
} }
@ -768,7 +780,7 @@ Acmd* AudioSynth_SaveReverbSamples(Acmd* aList, s16 reverbIndex, s16 updateIndex
OS_K0_TO_PHYSICAL(gSynthReverbs[reverbIndex] OS_K0_TO_PHYSICAL(gSynthReverbs[reverbIndex]
.items[gSynthReverbs[reverbIndex].curFrame][updateIndex] .items[gSynthReverbs[reverbIndex].curFrame][updateIndex]
.toDownsampleLeft), .toDownsampleLeft),
0x300); DMEM_2CH_SIZE);
gSynthReverbs[reverbIndex].resampleFlags = 0; gSynthReverbs[reverbIndex].resampleFlags = 0;
break; break;
} }
@ -807,7 +819,7 @@ Acmd* AudioSynth_DoOneAudioUpdate(s16* aiBuf, s32 aiBufLen, Acmd* aList, s32 upd
} }
} }
aClearBuffer(aList++, DMEM_LEFT_CH, DMEM_2CH_SIZE); aClearBuffer(aList++, DMEM_LEFT_CH, DMEM_6CH_SIZE);
j = 0; j = 0;
for (i = 0; i < gNumSynthReverbs; i++) { for (i = 0; i < gNumSynthReverbs; i++) {
@ -834,10 +846,13 @@ Acmd* AudioSynth_DoOneAudioUpdate(s16* aiBuf, s32 aiBufLen, Acmd* aList, s32 upd
j++; j++;
} }
j = aiBufLen * 2; int num_audio_channels = GetNumAudioChannels();
j = aiBufLen * num_audio_channels * sizeof(s16);
// Set rsp output buffer to DMEM_TEMP with size j
aSetBuffer(aList++, 0, 0, DMEM_TEMP, j); aSetBuffer(aList++, 0, 0, DMEM_TEMP, j);
aInterleave(aList++, DMEM_LEFT_CH, DMEM_RIGHT_CH); aInterleave(aList++, DMEM_LEFT_CH, DMEM_RIGHT_CH, DMEM_CENTER_CH, DMEM_SUBWOOFER_CH, DMEM_REAR_LEFT_CH, DMEM_REAR_RIGHT_CH, num_audio_channels);
aSaveBuffer(aList++, DMEM_TEMP, OS_K0_TO_PHYSICAL(aiBuf), j * 2); // Copy j bytes from DMEM_TEMP to aiBuf
aSaveBuffer(aList++, DMEM_TEMP, OS_K0_TO_PHYSICAL(aiBuf), j);
return aList; return aList;
} }
@ -908,6 +923,10 @@ Acmd* AudioSynth_ProcessNote(s32 noteIndex, NoteSubEu* noteSub, NoteSynthesisSta
synthState->samplePosFrac = 0; synthState->samplePosFrac = 0;
synthState->curVolLeft = 0; synthState->curVolLeft = 0;
synthState->curVolRight = 0; synthState->curVolRight = 0;
synthState->curVolCenter = 0;
synthState->curVolLfe = 0;
synthState->curVolRLeft = 0;
synthState->curVolRRight = 0;
synthState->numParts = synthState->prevHaasEffectRightDelaySize = synthState->prevHaasEffectLeftDelaySize = 0; synthState->numParts = synthState->prevHaasEffectRightDelaySize = synthState->prevHaasEffectLeftDelaySize = 0;
note->noteSubEu.bitField0.finished = 0; note->noteSubEu.bitField0.finished = 0;
} }
@ -1288,74 +1307,90 @@ Acmd* AudioSynth_FinalResample(Acmd* aList, NoteSynthesisState* synthState, s32
Acmd* AudioSynth_ProcessEnvelope(Acmd* aList, NoteSubEu* noteSub, NoteSynthesisState* synthState, s32 aiBufLen, Acmd* AudioSynth_ProcessEnvelope(Acmd* aList, NoteSubEu* noteSub, NoteSynthesisState* synthState, s32 aiBufLen,
u16 dmemSrc, s32 delaySide, s32 flags) { u16 dmemSrc, s32 delaySide, s32 flags) {
s16 rampReverb; s16 rampReverb;
s16 rampRight; s16 rampRight = 0, rampLeft = 0, rampCenter = 0, rampLfe = 0, rampRLeft = 0, rampRRight = 0;
s16 rampLeft; u16 panVolLeft, panVolRight, panVolCenter, panVolLfe, panVolRLeft, panVolRRight;
u16 panVolLeft; u16 curVolLeft, curVolRight, curVolCenter, curVolLfe, curVolRLeft, curVolRRight;
u16 panVolRight;
u16 curVolLeft;
u16 curVolRight;
s32 sourceReverbVol; s32 sourceReverbVol;
s32 temp = 0; s32 temp = 0;
curVolLeft = synthState->curVolLeft; curVolLeft = synthState->curVolLeft;
curVolRight = synthState->curVolRight; curVolRight = synthState->curVolRight;
curVolCenter = synthState->curVolCenter;
curVolLfe = synthState->curVolLfe;
curVolRLeft = synthState->curVolRLeft;
curVolRRight = synthState->curVolRRight;
panVolLeft = noteSub->panVolLeft; panVolLeft = 16 * noteSub->panVolLeft;
panVolRight = noteSub->panVolRight; panVolRight = 16 * noteSub->panVolRight;
panVolCenter = 16 * noteSub->panVolCenter;
panVolLeft <<= 4; panVolLfe = 16 * noteSub->panVolLfe;
panVolRight <<= 4; panVolRLeft = 16 * noteSub->panVolRLeft;
panVolRRight = 16 * noteSub->panVolRRight;
s32 aiBufLenSmall = aiBufLen >> 3;
if (panVolLeft != curVolLeft) { if (panVolLeft != curVolLeft) {
rampLeft = (panVolLeft - curVolLeft) / (aiBufLen >> 3); rampLeft = (panVolLeft - curVolLeft) / aiBufLenSmall;
} else {
rampLeft = 0;
} }
if (panVolRight != curVolRight) { if (panVolRight != curVolRight) {
rampRight = (panVolRight - curVolRight) / (aiBufLen >> 3); rampRight = (panVolRight - curVolRight) / aiBufLenSmall;
} else { }
rampRight = 0; if (panVolRLeft != curVolRLeft) {
rampRLeft = (panVolRLeft - curVolRLeft) / aiBufLenSmall;
}
if (panVolRRight != curVolRRight) {
rampRRight = (panVolRRight - curVolRRight) / aiBufLenSmall;
}
if (panVolCenter != curVolCenter) {
rampCenter = (panVolCenter - curVolCenter) / aiBufLenSmall;
}
if (panVolLfe != curVolLfe) {
rampLfe = (panVolLfe - curVolLfe) / aiBufLenSmall;
} }
sourceReverbVol = synthState->reverbVol; sourceReverbVol = synthState->reverbVol;
if (noteSub->reverb != sourceReverbVol) { if (noteSub->reverb != sourceReverbVol) {
temp = (((noteSub->reverb & 0x7F) - (sourceReverbVol & 0x7F)) << 8); temp = (((noteSub->reverb & 0x7F) - (sourceReverbVol & 0x7F)) << 8);
rampReverb = temp / (aiBufLen >> 3); rampReverb = temp / aiBufLenSmall;
synthState->reverbVol = noteSub->reverb; synthState->reverbVol = noteSub->reverb;
} else { } else {
rampReverb = 0; rampReverb = 0;
} }
synthState->curVolLeft = curVolLeft + (rampLeft * (aiBufLen >> 3)); synthState->curVolLeft = curVolLeft + (rampLeft * aiBufLenSmall);
synthState->curVolRight = curVolRight + (rampRight * (aiBufLen >> 3)); synthState->curVolRight = curVolRight + (rampRight * aiBufLenSmall);
synthState->curVolCenter = curVolCenter + (rampCenter * aiBufLenSmall);
synthState->curVolLfe = curVolLfe + (rampLfe * aiBufLenSmall);
synthState->curVolRLeft = curVolRLeft + (rampRLeft * aiBufLenSmall);
synthState->curVolRRight = curVolRRight + (rampRRight * aiBufLenSmall);
if (noteSub->bitField0.usesHeadsetPanEffects) { if (noteSub->bitField0.usesHeadsetPanEffects) {
int32_t num_audio_channels = 2;
aClearBuffer(aList++, DMEM_HAAS_TEMP, DMEM_1CH_SIZE); aClearBuffer(aList++, DMEM_HAAS_TEMP, DMEM_1CH_SIZE);
aEnvSetup1(aList++, (sourceReverbVol & 0x7F), rampReverb, rampLeft, rampRight); aEnvSetup1(aList++, (sourceReverbVol & 0x7F), rampReverb, rampLeft, rampRight, rampCenter, rampLfe, rampRLeft, rampRRight);
aEnvSetup2(aList++, curVolLeft, curVolRight); aEnvSetup2(aList++, curVolLeft, curVolRight, curVolCenter, curVolLfe, curVolRLeft, curVolRRight);
switch (delaySide) { switch (delaySide) {
case HAAS_EFFECT_DELAY_LEFT: case HAAS_EFFECT_DELAY_LEFT:
aEnvMixer(aList++, dmemSrc, aiBufLen, 0, 0, ((sourceReverbVol & 0x80) >> 7), aEnvMixer(aList++, dmemSrc, aiBufLen, ((sourceReverbVol & 0x80) >> 7),
noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, 0x65B1C9E1, 0); noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, (DMEM_WET_LEFT_CH << 16) | DMEM_LEFT_CH, DMEM_HAAS_TEMP << 16, num_audio_channels);
break; break;
case HAAS_EFFECT_DELAY_RIGHT: case HAAS_EFFECT_DELAY_RIGHT:
aEnvMixer(aList++, dmemSrc, aiBufLen, 0, 0, ((sourceReverbVol & 0x80) >> 7), aEnvMixer(aList++, dmemSrc, aiBufLen, ((sourceReverbVol & 0x80) >> 7),
noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, 0x9965C9E1, 0); noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, (DMEM_WET_LEFT_CH << 16) | DMEM_LEFT_CH, DMEM_HAAS_TEMP, num_audio_channels);
break; break;
default: // HAAS_EFFECT_DELAY_NONE default: // HAAS_EFFECT_DELAY_NONE
aEnvMixer(aList++, dmemSrc, aiBufLen, 0, 0, ((sourceReverbVol & 0x80) >> 7), aEnvMixer(aList++, dmemSrc, aiBufLen, ((sourceReverbVol & 0x80) >> 7),
noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, 0x99B1C9E1, 0); noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, (DMEM_WET_LEFT_CH << 16) | DMEM_LEFT_CH, 0, num_audio_channels);
break; break;
} }
} else { } else {
aEnvSetup1(aList++, (sourceReverbVol & 0x7F), rampReverb, rampLeft, rampRight); aEnvSetup1(aList++, (sourceReverbVol & 0x7F), rampReverb, rampLeft, rampRight, rampCenter, rampLfe, rampRLeft, rampRRight);
aEnvSetup2(aList++, curVolLeft, curVolRight); aEnvSetup2(aList++, curVolLeft, curVolRight, curVolCenter, curVolLfe, curVolRLeft, curVolRRight);
aEnvMixer(aList++, dmemSrc, aiBufLen, 0, 0, ((sourceReverbVol & 0x80) >> 7), aEnvMixer(aList++, dmemSrc, aiBufLen, ((sourceReverbVol & 0x80) >> 7),
noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, 0x99B1C9E1, 0); noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, (DMEM_WET_LEFT_CH << 16) | DMEM_LEFT_CH, 0, GetNumAudioChannels());
} }
return aList; return aList;

View file

@ -82,7 +82,7 @@ void AudioThread_CreateNextAudioBuffer(s16* samples, u32 num_samples) {
AudioSynth_Update(gCurAbiCmdBuffer, &abiCmdCount, samples, num_samples); AudioSynth_Update(gCurAbiCmdBuffer, &abiCmdCount, samples, num_samples);
// Spectrum Analyzer fix // Spectrum Analyzer fix
memcpy(gAiBuffers[gCurAiBuffIndex], samples, num_samples); memcpy(gAiBuffers[gCurAiBuffIndex], samples, num_samples * sizeof(s16));
gAudioRandom = osGetCount() * (gAudioRandom + gAudioTaskCountQ); gAudioRandom = osGetCount() * (gAudioRandom + gAudioTaskCountQ);
} }
@ -186,7 +186,7 @@ SPTask* AudioThread_CreateTask() {
task->output_buff = NULL; task->output_buff = NULL;
task->output_buff_size = NULL; task->output_buff_size = NULL;
if (1) {}
task->data_ptr = (u64*) gAbiCmdBuffs[aiBuffIndex]; task->data_ptr = (u64*) gAbiCmdBuffs[aiBuffIndex];
task->data_size = abiCmdCount * sizeof(Acmd); task->data_size = abiCmdCount * sizeof(Acmd);
@ -438,6 +438,14 @@ void AudioThread_ProcessCmds(u32 msg) {
case AUDIOCMD_OP_CHANNEL_SET_IO: case AUDIOCMD_OP_CHANNEL_SET_IO:
if (cmd->arg2 < 8) { if (cmd->arg2 < 8) {
channel->seqScriptIO[cmd->arg2] = cmd->asSbyte; channel->seqScriptIO[cmd->arg2] = cmd->asSbyte;
// Mark the audio as voice, sfx or bgm
channel->is_voice = 0;
channel->is_sfx = 0;
if (cmd->arg0 == SEQ_PLAYER_VOICE) {
channel->is_voice = 1;
} else if (cmd->arg0 == SEQ_PLAYER_SFX) {
channel->is_sfx = 1;
}
} }
break; break;
case AUDIOCMD_OP_CHANNEL_SET_MUTE: case AUDIOCMD_OP_CHANNEL_SET_MUTE:

View file

@ -2,6 +2,7 @@
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <math.h>
#include <macros.h> #include <macros.h>
@ -71,18 +72,20 @@ static __m128i m256i_clamp_to_m128i(m256i a) {
#define ROUND_UP_8(v) (((v) + 7) & ~7) #define ROUND_UP_8(v) (((v) + 7) & ~7)
#define ROUND_DOWN_16(v) ((v) & ~0xf) #define ROUND_DOWN_16(v) ((v) & ~0xf)
#define DMEM_BUF_SIZE (0x1000) #define DMEM_BUF_SIZE (0x1B90) // 7056 B
// #define DMEM_BUF_SIZE 0xC90 #define BUF_U8(a) (rspa.buf + ((a)-0x450))
#define BUF_U8(a) (rspa.buf.as_u8 + ((a)-0x450)) #define BUF_S16(a) (int16_t*) BUF_U8(a)
#define BUF_S16(a) (rspa.buf.as_s16 + ((a)-0x450) / sizeof(int16_t))
#define SAMPLE_RATE 32000 // Adjusted to match the actual sample rate of 32 kHz
#define CUTOFF_FREQ_LFE 80 // Cutoff frequency of 80 Hz
static struct { static struct {
uint16_t in; uint16_t in;
uint16_t out; uint16_t out;
uint16_t nbytes; uint16_t nbytes;
uint16_t vol[2]; uint16_t vol[6];
uint16_t rate[2]; uint16_t rate[6];
uint16_t vol_wet; uint16_t vol_wet;
uint16_t rate_wet; uint16_t rate_wet;
@ -93,10 +96,7 @@ static struct {
uint16_t filter_count; uint16_t filter_count;
int16_t filter[8]; int16_t filter[8];
union { uint8_t buf[DMEM_BUF_SIZE];
int16_t as_s16[DMEM_BUF_SIZE / sizeof(int16_t)];
uint8_t as_u8[DMEM_BUF_SIZE];
} buf;
} rspa; } rspa;
static int16_t resample_table[64][4] = { static int16_t resample_table[64][4] = {
@ -183,87 +183,38 @@ void aSetBufferImpl(uint8_t flags, uint16_t in, uint16_t out, uint16_t nbytes) {
rspa.nbytes = nbytes; rspa.nbytes = nbytes;
} }
#if 1 void aInterleaveImpl(uint16_t left, uint16_t right, uint16_t center, uint16_t lfe, uint16_t surround_left, uint16_t surround_right, uint16_t num_channels) {
// old abi impl if (rspa.nbytes == 0) {
void aInterleaveImpl(uint16_t left, uint16_t right) {
if(rspa.nbytes == 0) { // Added
return; return;
} }
int count = ROUND_UP_16(rspa.nbytes) >> 3; int count = rspa.nbytes / (num_channels * sizeof(int16_t));
int16_t *l = BUF_S16(left);
int16_t *r = BUF_S16(right);
int16_t *d = BUF_S16(rspa.out);
while (count > 0) {
int16_t l0 = *l++;
int16_t l1 = *l++;
int16_t l2 = *l++;
int16_t l3 = *l++;
int16_t l4 = *l++;
int16_t l5 = *l++;
int16_t l6 = *l++;
int16_t l7 = *l++;
int16_t r0 = *r++;
int16_t r1 = *r++;
int16_t r2 = *r++;
int16_t r3 = *r++;
int16_t r4 = *r++;
int16_t r5 = *r++;
int16_t r6 = *r++;
int16_t r7 = *r++;
*d++ = l0;
*d++ = r0;
*d++ = l1;
*d++ = r1;
*d++ = l2;
*d++ = r2;
*d++ = l3;
*d++ = r3;
*d++ = l4;
*d++ = r4;
*d++ = l5;
*d++ = r5;
*d++ = l6;
*d++ = r6;
*d++ = l7;
*d++ = r7;
--count;
}
}
#else
// new abi
void aInterleaveImpl(uint16_t dest, uint16_t left, uint16_t right, uint16_t c) {
if(rspa.nbytes == 0){
return;
}
int count = ROUND_UP_16(rspa.nbytes) >> 3;
int16_t *l = BUF_S16(left); int16_t *l = BUF_S16(left);
int16_t *r = BUF_S16(right); int16_t *r = BUF_S16(right);
int16_t *d = BUF_S16(rspa.out); int16_t *d = BUF_S16(rspa.out);
while (count > 0) { if (num_channels == 2) {
int16_t l0 = *l++; for (int i = 0; i < count; i++) {
int16_t l1 = *l++; *d++ = *l++;
int16_t l2 = *l++; *d++ = *r++;
int16_t l3 = *l++; }
int16_t r0 = *r++; } else {
int16_t r1 = *r++; int16_t *c = BUF_S16(center);
int16_t r2 = *r++; int16_t *lf = BUF_S16(lfe);
int16_t r3 = *r++; int16_t *sl = BUF_S16(surround_left);
*d++ = l0; int16_t *sr = BUF_S16(surround_right);
*d++ = r0;
*d++ = l1; for (int i = 0; i < count; i++) {
*d++ = r1; *d++ = *l++;
*d++ = l2; *d++ = *r++;
*d++ = r2; *d++ = *c++;
*d++ = l3; *d++ = *lf++;
*d++ = r3; *d++ = *sl++;
--count; *d++ = *sr++;
}
} }
} }
#endif
void aDMEMMoveImpl(uint16_t in_addr, uint16_t out_addr, int nbytes) { void aDMEMMoveImpl(uint16_t in_addr, uint16_t out_addr, int nbytes) {
nbytes = ROUND_UP_16(nbytes); nbytes = ROUND_UP_16(nbytes);
@ -300,19 +251,19 @@ void aADPCMdecImpl(uint8_t flags, ADPCM_STATE state) {
int16_t prev1 = out[-1]; int16_t prev1 = out[-1];
int16_t prev2 = out[-2]; int16_t prev2 = out[-2];
int j, k; int j, k;
if (flags & 4) { if (flags & 4) {
for (j = 0; j < 2; j++) { for (j = 0; j < 2; j++) {
ins[j * 4] = (((*in >> 6) << 30) >> 30) << shift; ins[j * 4] = (((*in >> 6) << 30) >> 30) << shift;
ins[j * 4 + 1] = ((((*in >> 4) & 0x3) << 30) >> 30) << shift; ins[j * 4 + 1] = ((((*in >> 4) & 0x3) << 30) >> 30) << shift;
ins[j * 4 + 2] = ((((*in >> 2) & 0x3) << 30) >> 30) << shift; ins[j * 4 + 2] = ((((*in >> 2) & 0x3) << 30) >> 30) << shift;
ins[j * 4 + 3] = (((*in++ & 0x3) << 30) >> 30) << shift; ins[j * 4 + 3] = (((*in++ & 0x3) << 30) >> 30) << shift;
} }
} else { } else {
for (j = 0; j < 4; j++) { for (j = 0; j < 4; j++) {
ins[j * 2] = (((*in >> 4) << 28) >> 28) << shift; ins[j * 2] = (((*in >> 4) << 28) >> 28) << shift;
ins[j * 2 + 1] = (((*in++ & 0xf) << 28) >> 28) << shift; ins[j * 2 + 1] = (((*in++ & 0xf) << 28) >> 28) << shift;
} }
} }
for (j = 0; j < 8; j++) { for (j = 0; j < 8; j++) {
int32_t acc = tbl[0][j] * prev2 + tbl[1][j] * prev1 + (ins[j] << 11); int32_t acc = tbl[0][j] * prev2 + tbl[1][j] * prev1 + (ins[j] << 11);
for (k = 0; k < j; k++) { for (k = 0; k < j; k++) {
@ -670,111 +621,152 @@ void aResampleImpl(uint8_t flags, uint16_t pitch, RESAMPLE_STATE state) {
#endif #endif
void aEnvSetup1Impl(uint8_t initial_vol_wet, uint16_t rate_wet, uint16_t rate_left, uint16_t rate_right) { void aEnvSetup1Impl(uint8_t initial_vol_wet, uint16_t rate_wet, uint16_t rate_left, uint16_t rate_right,
uint16_t rate_center, uint16_t rate_lfe, uint16_t rate_rear_left, uint16_t rate_rear_right) {
rspa.vol_wet = (uint16_t)(initial_vol_wet << 8); rspa.vol_wet = (uint16_t)(initial_vol_wet << 8);
rspa.rate_wet = rate_wet; rspa.rate_wet = rate_wet;
rspa.rate[0] = rate_left; rspa.rate[0] = rate_left;
rspa.rate[1] = rate_right; rspa.rate[1] = rate_right;
rspa.rate[2] = rate_center;
rspa.rate[3] = rate_lfe;
rspa.rate[4] = rate_rear_left;
rspa.rate[5] = rate_rear_right;
} }
void aEnvSetup2Impl(uint16_t initial_vol_left, uint16_t initial_vol_right) { void aEnvSetup2Impl(uint16_t initial_vol_left, uint16_t initial_vol_right, int16_t initial_vol_center,
int16_t initial_vol_lfe, int16_t initial_vol_rear_left, int16_t initial_vol_rear_right) {
rspa.vol[0] = initial_vol_left; rspa.vol[0] = initial_vol_left;
rspa.vol[1] = initial_vol_right; rspa.vol[1] = initial_vol_right;
rspa.vol[2] = initial_vol_center;
rspa.vol[3] = initial_vol_lfe;
rspa.vol[4] = initial_vol_rear_left;
rspa.vol[5] = initial_vol_rear_right;
} }
#ifndef SSE2_AVAILABLE
void aEnvMixerImpl(uint16_t in_addr, uint16_t n_samples, bool swap_reverb, void aEnvMixerImpl(uint16_t in_addr, uint16_t n_samples, bool swap_reverb,
bool neg_3, bool neg_2,
bool neg_left, bool neg_right, bool neg_left, bool neg_right,
int32_t wet_dry_addr, uint32_t unk) uint32_t wet_dry_addr, uint32_t haas_temp_addr, uint32_t num_channels)
{ {
// Note: max number of samples is 192 (192 * 2 = 384 bytes = 0x180)
int max_num_samples = 192;
int16_t *in = BUF_S16(in_addr); int16_t *in = BUF_S16(in_addr);
int16_t *dry[2] = {BUF_S16(((wet_dry_addr >> 24) & 0xFF) << 4), BUF_S16(((wet_dry_addr >> 16) & 0xFF) << 4)}; int n = ROUND_UP_16(n_samples);
int16_t *wet[2] = {BUF_S16(((wet_dry_addr >> 8) & 0xFF) << 4), BUF_S16(((wet_dry_addr) & 0xFF) << 4)}; if (n > max_num_samples) {
int16_t negs[4] = {neg_left ? -1 : 0, neg_right ? -1 : 0, neg_3 ? -4 : 0, neg_2 ? -2 : 0}; printf("Warning: n_samples is too large: %d\n", n_samples);
}
uint16_t rate_wet = rspa.rate_wet;
uint16_t vol_wet = rspa.vol_wet;
// All speakers
int dry_addr_start = wet_dry_addr & 0xFFFF;
int wet_addr_start = wet_dry_addr >> 16;
int16_t *dry[6];
int16_t *wet[6];
for (int i = 0; i < 6; i++) {
dry[i] = BUF_S16(dry_addr_start + max_num_samples * i * sizeof(int16_t));
wet[i] = BUF_S16(wet_addr_start + max_num_samples * i * sizeof(int16_t));
}
uint16_t vols[6] = {rspa.vol[0], rspa.vol[1], rspa.vol[2], rspa.vol[3], rspa.vol[4], rspa.vol[5]};
int swapped[2] = {swap_reverb ? 1 : 0, swap_reverb ? 0 : 1}; int swapped[2] = {swap_reverb ? 1 : 0, swap_reverb ? 0 : 1};
int n = ROUND_UP_16(n_samples);
uint16_t vols[2] = {rspa.vol[0], rspa.vol[1]}; if (num_channels == 6) {
uint16_t rates[2] = {rspa.rate[0], rspa.rate[1]}; // Calculate the filter coefficient
uint16_t vol_wet = rspa.vol_wet; float RC = 1.f / (2 * M_PI * CUTOFF_FREQ_LFE);
uint16_t rate_wet = rspa.rate_wet; float dt = 1.f / SAMPLE_RATE;
float alpha = dt / (RC + dt);
// Low-pass filter state for the subwoofer channel
static float prev_lfe_sample = 0.0f;
for (int i = 0; i < n / 8; i++) {
for (int k = 0; k < 8; k++) {
int16_t samples[6] = {0};
samples[0] = *in;
samples[1] = *in;
samples[2] = *in;
samples[3] = *in; // LFE channel
samples[4] = *in;
samples[5] = *in;
in++;
// Apply volume
for (int j = 0; j < 6; j++) {
samples[j] = samples[j] * vols[j] >> 16;
}
// Apply low-pass filter to the LFE channel (index 3)
float lfe_sample = samples[3];
lfe_sample = alpha * lfe_sample + (1.0f - alpha) * prev_lfe_sample;
prev_lfe_sample = lfe_sample;
samples[3] = (int16_t)lfe_sample;
// Mix dry and wet signals
for (int j = 0; j < 6; j++) {
*dry[j] = clamp16(*dry[j] + samples[j]);
dry[j]++;
if (j >= 4) {
// Apply reverb only to the rear channels (4 and 5)
*wet[j] = clamp16(*wet[j] + (samples[swapped[j % 2]] * vol_wet >> 16));
wet[j]++;
}
}
}
for (int j = 0; j < 6; j++) {
vols[j] += rspa.rate[j];
}
vol_wet += rate_wet;
}
} else {
// Account for haas effect
int haas_addr_left = haas_temp_addr >> 16;
int haas_addr_right = haas_temp_addr & 0xFFFF;
if (haas_addr_left) {
dry[0] = BUF_S16(haas_addr_left);
} else if (haas_addr_right) {
dry[1] = BUF_S16(haas_addr_right);
}
int16_t negs[2] = {neg_left ? 0 : 0xFFFF, neg_right ? 0 : 0xFFFF};
for (int i = 0; i < n / 8; i++) {
for (int k = 0; k < 8; k++) {
int16_t samples[2] = {0};
samples[0] = *in;
samples[1] = *in;
in++;
// Apply volume
for (int j = 0; j < 2; j++) {
samples[j] = (samples[j] * vols[j] >> 16) & negs[j];
}
// Mix dry and wet signals
for (int j = 0; j < 2; j++) {
*dry[j] = clamp16(*dry[j] + samples[j]);
dry[j]++;
// Apply reverb
*wet[j] = clamp16(*wet[j] + (samples[swapped[j]] * vol_wet >> 16));
wet[j]++;
}
}
do {
for (int i = 0; i < 8; i++) {
int16_t samples[2] = {*in, *in}; in++;
for (int j = 0; j < 2; j++) { for (int j = 0; j < 2; j++) {
samples[j] = (samples[j] * vols[j] >> 16) ^ negs[j]; vols[j] += rspa.rate[j];
}
for (int j = 0; j < 2; j++) {
*dry[j] = clamp16(*dry[j] + samples[j]); dry[j]++;
*wet[j] = clamp16(*wet[j] + ((samples[swapped[j]] * vol_wet >> 16) ^ negs[2 + j])); wet[j]++;
} }
vol_wet += rate_wet;
} }
vols[0] += rates[0];
vols[1] += rates[1];
vol_wet += rate_wet;
n -= 8;
} while (n > 0);
}
#else
// SSE2 optimized version of algorithm
void aEnvMixerImpl(uint16_t in_addr, uint16_t n_samples, bool swap_reverb,
bool neg_3, bool neg_2,
bool neg_left, bool neg_right,
int32_t wet_dry_addr, u32 unk)
{
int16_t *in = BUF_S16(in_addr);
int16_t *dry[2] = {BUF_S16(((wet_dry_addr >> 24) & 0xFF) << 4), BUF_S16(((wet_dry_addr >> 16) & 0xFF) << 4)};
int16_t *wet[2] = {BUF_S16(((wet_dry_addr >> 8) & 0xFF) << 4), BUF_S16(((wet_dry_addr) & 0xFF) << 4)};
int16_t negs[4] = {neg_left ? -1 : 0, neg_right ? -1 : 0, neg_3 ? -4 : 0, neg_2 ? -2 : 0};
int n = ROUND_UP_16(n_samples);
const int n_aligned = n - (n % 8);
uint16_t vols[2] = {rspa.vol[0], rspa.vol[1]};
uint16_t rates[2] = {rspa.rate[0], rspa.rate[1]};
uint16_t vol_wet = rspa.vol_wet;
uint16_t rate_wet = rspa.rate_wet;
const __m128i* in_ptr = (__m128i*)in;
const __m128i* d_ptr[2] = { (__m128i*) dry[0], (__m128i*) dry[1] };
const __m128i* w_ptr[2] = { (__m128i*) wet[0], (__m128i*) wet[1] };
// Aligned loop
for (int N = 0; N < n_aligned; N+=8) {
// Init vectors
const __m128i in_channels = _mm_loadu_si128(in_ptr++);
__m128i d[2] = { _mm_loadu_si128(d_ptr[0]), _mm_loadu_si128(d_ptr[1]) };
__m128i w[2] = { _mm_loadu_si128(w_ptr[0]), _mm_loadu_si128(w_ptr[1]) };
// Compute base samples
// sample = ((in * vols) >> 16) ^ negs
__m128i s[2] = {
_mm_xor_si128(_mm_mulhi_epi16(in_channels, _mm_set1_epi16(vols[0])), _mm_set1_epi16(negs[0])),
_mm_xor_si128(_mm_mulhi_epi16(in_channels, _mm_set1_epi16(vols[1])), _mm_set1_epi16(negs[1]))
};
// Compute left swapped samples
// (sample * vol_wet) >> 16) ^ negs
__m128i ss[2] = {
_mm_xor_si128(_mm_mulhi_epi16(s[swap_reverb], _mm_set1_epi16(vol_wet)), _mm_set1_epi16(negs[2])),
_mm_xor_si128(_mm_mulhi_epi16(s[!swap_reverb], _mm_set1_epi16(vol_wet)), _mm_set1_epi16(negs[3]))
};
// Store values to buffers
for (int j = 0; j < 2; j++) {
_mm_storeu_si128((__m128i*) d_ptr[j]++, _mm_adds_epi16(s[j], d[j]));
_mm_storeu_si128((__m128i*) w_ptr[j]++, _mm_adds_epi16(ss[j], w[j]));
vols[j] += rates[j];
}
vol_wet += rate_wet;
} }
} }
#endif
#ifndef SSE2_AVAILABLE #ifndef SSE2_AVAILABLE

View file

@ -37,15 +37,17 @@ void aLoadBufferImpl(const void* source_addr, uint16_t dest_addr, uint16_t nbyte
void aSaveBufferImpl(uint16_t source_addr, int16_t* dest_addr, uint16_t nbytes); void aSaveBufferImpl(uint16_t source_addr, int16_t* dest_addr, uint16_t nbytes);
void aLoadADPCMImpl(int num_entries_times_16, const int16_t* book_source_addr); void aLoadADPCMImpl(int num_entries_times_16, const int16_t* book_source_addr);
void aSetBufferImpl(uint8_t flags, uint16_t in, uint16_t out, uint16_t nbytes); void aSetBufferImpl(uint8_t flags, uint16_t in, uint16_t out, uint16_t nbytes);
void aInterleaveImpl(uint16_t left, uint16_t right); void aInterleaveImpl(uint16_t left, uint16_t right, uint16_t center, uint16_t lfe, uint16_t surround_left, uint16_t surround_right, uint16_t num_audio_channels);
void aDMEMMoveImpl(uint16_t in_addr, uint16_t out_addr, int nbytes); void aDMEMMoveImpl(uint16_t in_addr, uint16_t out_addr, int nbytes);
void aSetLoopImpl(ADPCM_STATE* adpcm_loop_state); void aSetLoopImpl(ADPCM_STATE* adpcm_loop_state);
void aADPCMdecImpl(uint8_t flags, ADPCM_STATE state); void aADPCMdecImpl(uint8_t flags, ADPCM_STATE state);
void aResampleImpl(uint8_t flags, uint16_t pitch, RESAMPLE_STATE state); void aResampleImpl(uint8_t flags, uint16_t pitch, RESAMPLE_STATE state);
void aEnvSetup1Impl(uint8_t initial_vol_wet, uint16_t rate_wet, uint16_t rate_left, uint16_t rate_right); void aEnvSetup1Impl(uint8_t initial_vol_wet, uint16_t rate_wet, uint16_t rate_left, uint16_t rate_right,
void aEnvSetup2Impl(uint16_t initial_vol_left, uint16_t initial_vol_right); uint16_t rate_center, uint16_t rate_lfe, uint16_t rate_rear_left, uint16_t rate_rear_right);
void aEnvMixerImpl(uint16_t in_addr, uint16_t n_samples, bool swap_reverb, bool neg_3, bool neg_2, bool neg_left, void aEnvSetup2Impl(uint16_t initial_vol_left, uint16_t initial_vol_right, int16_t initial_vol_center,
bool neg_right, int32_t wet_dry_addr, u32 unk); int16_t initial_vol_lfe, int16_t initial_vol_rear_left, int16_t initial_vol_rear_right);
void aEnvMixerImpl(uint16_t in_addr, uint16_t n_samples, bool swap_reverb, bool neg_left,
bool neg_right, uint32_t wet_dry_addr, uint32_t haas_temp_addr, uint32_t num_channels);
void aMixImpl(uint16_t count, int16_t gain, uint16_t in_addr, uint16_t out_addr); void aMixImpl(uint16_t count, int16_t gain, uint16_t in_addr, uint16_t out_addr);
void aS8DecImpl(uint8_t flags, ADPCM_STATE state); void aS8DecImpl(uint8_t flags, ADPCM_STATE state);
void aAddMixerImpl(uint16_t count, uint16_t in_addr, uint16_t out_addr); void aAddMixerImpl(uint16_t count, uint16_t in_addr, uint16_t out_addr);
@ -65,16 +67,17 @@ void aUnkCmd19Impl(uint8_t f, uint16_t count, uint16_t out_addr, uint16_t in_add
#define aSaveBuffer(pkt, s, d, c) aSaveBufferImpl(s, d, c) #define aSaveBuffer(pkt, s, d, c) aSaveBufferImpl(s, d, c)
#define aLoadADPCM(pkt, c, d) aLoadADPCMImpl(c, d) #define aLoadADPCM(pkt, c, d) aLoadADPCMImpl(c, d)
#define aSetBuffer(pkt, f, i, o, c) aSetBufferImpl(f, i, o, c) #define aSetBuffer(pkt, f, i, o, c) aSetBufferImpl(f, i, o, c)
#define aInterleave(pkt, l, r) aInterleaveImpl(l, r) #define aInterleave(pkt, l, r, c, lfe, sl, sr, num_channels) aInterleaveImpl(l, r, c, lfe, sl, sr, num_channels)
#define aDMEMMove(pkt, i, o, c) aDMEMMoveImpl(i, o, c) #define aDMEMMove(pkt, i, o, c) aDMEMMoveImpl(i, o, c)
#define aSetLoop(pkt, a) aSetLoopImpl(a) #define aSetLoop(pkt, a) aSetLoopImpl(a)
#define aADPCMdec(pkt, f, s) aADPCMdecImpl(f, s) #define aADPCMdec(pkt, f, s) aADPCMdecImpl(f, s)
#define aResample(pkt, f, p, s) aResampleImpl(f, p, s) #define aResample(pkt, f, p, s) aResampleImpl(f, p, s)
#define aEnvSetup1(pkt, initialVolReverb, rampReverb, rampLeft, rampRight) \ #define aEnvSetup1(pkt, initialVolReverb, rampReverb, rampLeft, rampRight, rampCenter, rampLfe, rampRLeft, rampRRight) \
aEnvSetup1Impl(initialVolReverb, rampReverb, rampLeft, rampRight) aEnvSetup1Impl(initialVolReverb, rampReverb, rampLeft, rampRight, rampCenter, rampLfe, rampRLeft, rampRRight)
#define aEnvSetup2(pkt, initialVolLeft, initialVolRight) aEnvSetup2Impl(initialVolLeft, initialVolRight) #define aEnvSetup2(pkt, initialVolLeft, initialVolRight, initialVolCenter, initialVolLfe, initialVolRLeft, initialVolRRight) \
#define aEnvMixer(pkt, inBuf, nSamples, swapReverb, negLeft, negRight, dryLeft, dryRight, wetLeft, wetRight) \ aEnvSetup2Impl(initialVolLeft, initialVolRight, initialVolCenter, initialVolLfe, initialVolRLeft, initialVolRRight)
aEnvMixerImpl(inBuf, nSamples, swapReverb, negLeft, negRight, dryLeft, dryRight, wetLeft, wetRight) #define aEnvMixer(pkt, inAddr, nSamples, swapReverb, negLeft, negRight, wetDryAddr, haasTempAddr, numChannels) \
aEnvMixerImpl(inAddr, nSamples, swapReverb, negLeft, negRight, wetDryAddr, haasTempAddr, numChannels)
#define aMix(pkt, c, g, i, o) aMixImpl(c, g, i, o) #define aMix(pkt, c, g, i, o) aMixImpl(c, g, i, o)
#define aS8Dec(pkt, f, s) aS8DecImpl(f, s) #define aS8Dec(pkt, f, s) aS8DecImpl(f, s)
#define aAddMixer(pkt, s, d, c) aAddMixerImpl(s, d, c) #define aAddMixer(pkt, s, d, c) aAddMixerImpl(s, d, c)

View file

@ -166,7 +166,8 @@ GameEngine::GameEngine() {
auto window = std::make_shared<Fast::Fast3dWindow>(std::vector<std::shared_ptr<Ship::GuiWindow>>({})); auto window = std::make_shared<Fast::Fast3dWindow>(std::vector<std::shared_ptr<Ship::GuiWindow>>({}));
this->context->Init(archiveFiles, {}, 3, { 32000, 1024, 1680 }, window, controlDeck); AudioSurroundSetting surroundSetting = Ship::Context::GetInstance()->GetConfig()->GetCurrentAudioSurround();
this->context->Init(archiveFiles, {}, 3, { 32000, 1024, 1680, surroundSetting }, window, controlDeck);
#ifndef __SWITCH__ #ifndef __SWITCH__
Ship::Context::GetInstance()->GetLogger()->set_level( Ship::Context::GetInstance()->GetLogger()->set_level(
@ -341,7 +342,7 @@ void GameEngine::StartFrame() const {
#endif #endif
#define NUM_AUDIO_CHANNELS 2 #define MAX_NUM_AUDIO_CHANNELS 6
extern "C" u16 audBuffer = 0; extern "C" u16 audBuffer = 0;
#include <sf64audio_provisional.h> #include <sf64audio_provisional.h>
@ -371,6 +372,7 @@ void GameEngine::HandleAudioThread() {
// gVIsPerFrame = 2; // gVIsPerFrame = 2;
#define AUDIO_FRAMES_PER_UPDATE (gVIsPerFrame > 0 ? gVIsPerFrame : 1) #define AUDIO_FRAMES_PER_UPDATE (gVIsPerFrame > 0 ? gVIsPerFrame : 1)
#define MAX_AUDIO_FRAMES_PER_UPDATE 3 // Compile-time constant with max value of gVIsPerFrame
std::unique_lock<std::mutex> Lock(audio.mutex); std::unique_lock<std::mutex> Lock(audio.mutex);
int samples_left = AudioPlayerBuffered(); int samples_left = AudioPlayerBuffered();
@ -382,19 +384,22 @@ void GameEngine::HandleAudioThread() {
countermin++; countermin++;
} }
s16 audio_buffer[SAMPLES_HIGH * NUM_AUDIO_CHANNELS * 3] = { 0 }; const int32_t num_audio_channels = GetNumAudioChannels();
s16 audio_buffer[SAMPLES_HIGH * MAX_NUM_AUDIO_CHANNELS * MAX_AUDIO_FRAMES_PER_UPDATE] = { 0 };
for (int i = 0; i < AUDIO_FRAMES_PER_UPDATE; i++) { for (int i = 0; i < AUDIO_FRAMES_PER_UPDATE; i++) {
AudioThread_CreateNextAudioBuffer(audio_buffer + i * (num_audio_samples * NUM_AUDIO_CHANNELS), AudioThread_CreateNextAudioBuffer(audio_buffer + i * (num_audio_samples * num_audio_channels),
num_audio_samples); num_audio_samples);
} }
#ifdef PIPE_DEBUG #ifdef PIPE_DEBUG
if (outfile.is_open()) { if (outfile.is_open()) {
outfile.write(reinterpret_cast<char*>(audio_buffer), outfile.write(reinterpret_cast<char*>(audio_buffer),
num_audio_samples * (sizeof(int16_t) * NUM_AUDIO_CHANNELS * AUDIO_FRAMES_PER_UPDATE)); num_audio_samples * (sizeof(int16_t) * num_audio_channels * AUDIO_FRAMES_PER_UPDATE));
} }
#endif #endif
AudioPlayerPlayFrame((u8*) audio_buffer, AudioPlayerPlayFrame((u8*) audio_buffer,
num_audio_samples * (sizeof(int16_t) * NUM_AUDIO_CHANNELS * AUDIO_FRAMES_PER_UPDATE)); num_audio_samples * (sizeof(int16_t) * num_audio_channels * AUDIO_FRAMES_PER_UPDATE));
audio.processing = false; audio.processing = false;
audio.cv_from_thread.notify_one(); audio.cv_from_thread.notify_one();
} }

View file

@ -171,6 +171,8 @@ void DrawSettingsMenu(){
if (Ship::Context::GetInstance()->GetAudio()->GetAvailableAudioBackends()->size() <= 1) { if (Ship::Context::GetInstance()->GetAudio()->GetAvailableAudioBackends()->size() <= 1) {
UIWidgets::ReEnableComponent(""); UIWidgets::ReEnableComponent("");
} }
UIWidgets::PaddedEnhancementCheckbox("Surround 5.1 (needs reload)", "gSurroundAudio", true, false);
ImGui::EndMenu(); ImGui::EndMenu();
} }

1
tools/SDL3 Submodule

@ -0,0 +1 @@
Subproject commit 69d28027adb70daf962621bb5e8ab00a069cbe30

@ -1 +1 @@
Subproject commit 228a17c68282092b6c3937c46b943229388931fe Subproject commit db4cb9a493e0dfee8e8fa6dc529390bb3321a771