diff --git a/include/sf64audio_provisional.h b/include/sf64audio_provisional.h index 952af4dc..4ca35ea6 100644 --- a/include/sf64audio_provisional.h +++ b/include/sf64audio_provisional.h @@ -52,10 +52,14 @@ typedef void (*AudioCustomUpdateFunction)(void); // Samples are processed in groups of 16 called a "frame" #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) // Both left and right channels #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_SIZE (AIBUF_LEN * SAMPLE_SIZE) // number of bytes @@ -399,7 +403,8 @@ typedef struct { typedef struct { /* 0x00 */ u8 strongLeft : 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 usesHeadsetPanEffects : 1; /* 0x00 */ u8 stereoHeadsetEffects : 1; @@ -428,7 +433,8 @@ typedef struct SequenceChannel { /* 0x00 */ u8 hasInstrument : 1; /* 0x00 */ u8 stereoHeadsetEffects : 1; /* 0x00 */ u8 largeNotes : 1; // notes specify duration and velocity - /* 0x00 */ u8 unused : 1; + /* 0x00 */ u8 is_voice : 1; + /* 0x00 */ u8 is_sfx : 1; union { struct { /* 0x4 */ char pad_4 : 1; @@ -551,8 +557,12 @@ typedef struct { /* 0x0C */ NoteSynthesisBuffers* synthesisBuffers; /* 0x10 */ s16 curVolLeft; /* 0x12 */ s16 curVolRight; - /* 0x14 */ char unk_14[0xC]; -} NoteSynthesisState; // size = 0x20 + /* 0x14 */ s16 curVolCenter; + /* 0x16 */ s16 curVolLfe; + /* 0x18 */ s16 curVolRLeft; + /* 0x1A */ s16 curVolRRight; + /* 0x1C */ char unk_14[0xC]; +} NoteSynthesisState; // size = 0x1E typedef struct { /* 0x00 */ struct SequenceChannel* channel; @@ -609,9 +619,13 @@ typedef struct { /* 0x05 */ u8 reverb; /* 0x06 */ u16 panVolLeft; /* 0x08 */ u16 panVolRight; - /* 0x0A */ u16 resampleRate; - /* 0x0C */ Sample** waveSampleAddr; -} NoteSubEu; // size = 0x10 + /* 0x0A */ u16 panVolCenter; + /* 0x0C */ u16 panVolLfe; + /* 0x0E */ u16 panVolRLeft; + /* 0x10 */ u16 panVolRRight; + /* 0x12 */ u16 resampleRate; + /* 0x14 */ Sample** waveSampleAddr; +} NoteSubEu; // size = 0x16 typedef struct Note { /* 0x00 */ AudioListItem listItem; diff --git a/libultraship b/libultraship index c6bd64c5..e1db4936 160000 --- a/libultraship +++ b/libultraship @@ -1 +1 @@ -Subproject commit c6bd64c5da0e55a1c00e969bc01046f9335cda43 +Subproject commit e1db49367bb3743a304d763be34830b4c1999873 diff --git a/src/audio/audio_general.c b/src/audio/audio_general.c index 315fbe97..c54407a5 100644 --- a/src/audio/audio_general.c +++ b/src/audio/audio_general.c @@ -454,26 +454,37 @@ s8 Audio_GetSfxReverb(u8 bankId, u8 entryIndex, u8 channelId) { s8 Audio_GetSfxPan(f32 xPos, f32 zPos, u8 mode) { if (sSfxChannelLayout != SFXCHAN_3) { - f32 absx = ABSF(xPos); - f32 absz = ABSF(zPos); - f32 pan; + float absx = ABSF(xPos); + float absz = ABSF(zPos); - 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; } - absx = MIN(1200.0f, absx); - absz = MIN(1200.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)); + if (GetNumAudioChannels() == 2) { + float pan; + absx = MIN(1200.0f, absx); + absz = MIN(1200.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 { - 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) { return ((mode & 1) * 127); } @@ -507,7 +518,6 @@ void Audio_SetSfxProperties(u8 bankId, u8 entryIndex, u8 channelId) { f32 freqMod = 1.0f; s8 pan = 64; SfxBankEntry* entry = &sSfxBanks[bankId][entryIndex]; - switch (bankId) { case SFX_BANK_PLAYER: case SFX_BANK_1: diff --git a/src/audio/audio_playback.c b/src/audio/audio_playback.c index 9e250ae3..b1c75dca 100644 --- a/src/audio/audio_playback.c +++ b/src/audio/audio_playback.c @@ -67,8 +67,7 @@ int testBits(void) { void Audio_InitNoteSub(Note* note, NoteAttributes* noteAttr) { NoteSubEu* noteSub; - f32 panVolumeLeft; - f32 pamVolumeRight; + f32 panVolumeLeft = 0, panVolumeRight = 0, panVolumeRearLeft = 0, panVolumeRearRight = 0, panVolumeCenter = 0; f32 velocity; s32 temp_t0; s32 var_a0; @@ -86,59 +85,95 @@ void Audio_InitNoteSub(Note* note, NoteAttributes* noteAttr) { pan = noteAttr->pan; reverb = noteAttr->reverb; 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]; - pamVolumeRight = gHeadsetPanVolume[ARRAY_COUNT(gHeadsetPanVolume) - 1 - pan]; - } else if (noteSub->bitField0.stereoHeadsetEffects && (gAudioSoundMode == SOUNDMODE_STEREO)) { - noteSub->leftDelaySize = 0; - noteSub->rightDelaySize = 0; - noteSub->bitField0.usesHeadsetPanEffects = false; - - panVolumeLeft = gStereoPanVolume[pan]; - pamVolumeRight = gStereoPanVolume[ARRAY_COUNT(gStereoPanVolume) - 1 - pan]; - strongRight = false; - strongLeft = false; - if (pan < 32) { - strongLeft = true; - } else if (pan > 96) { - strongRight = true; + if (GetNumAudioChannels() == 2) { + 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]; + panVolumeRight = gHeadsetPanVolume[ARRAY_COUNT(gHeadsetPanVolume) - 1 - pan]; + } 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 { - panVolumeLeft = gDefaultPanVolume[pan]; - pamVolumeRight = gDefaultPanVolume[ARRAY_COUNT(gDefaultPanVolume) - 1 - pan]; + // Surround 5.1 + 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) { velocity = 0.0f; } @@ -148,16 +183,15 @@ void Audio_InitNoteSub(Note* note, NoteAttributes* noteAttr) { float master_vol = CVarGetFloat("gGameMasterVolume", 1.0f); 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; if (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.pan = playbackState->parentLayer->notePan; 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.gain = playbackState->parentLayer->channel->reverbIndex; diff --git a/src/audio/audio_seqplayer.c b/src/audio/audio_seqplayer.c index 8a941c46..b6467dd8 100644 --- a/src/audio/audio_seqplayer.c +++ b/src/audio/audio_seqplayer.c @@ -89,7 +89,8 @@ void AudioSeq_InitSequenceChannel(SequenceChannel* channel) { for (i = 0; i < 8; i++) { channel->seqScriptIO[i] = -1; } - channel->unused = 0; + channel->is_sfx = 0; + channel->is_voice = 0; Audio_InitNoteLists(&channel->notePool); } diff --git a/src/audio/audio_synthesis.c b/src/audio/audio_synthesis.c index 7be193d7..5ad68b49 100644 --- a/src/audio/audio_synthesis.c +++ b/src/audio/audio_synthesis.c @@ -5,14 +5,23 @@ #include "port/Engine.h" #define DMEM_WET_SCRATCH 0x470 -#define DMEM_COMPRESSED_ADPCM_DATA 0x990 -#define DMEM_LEFT_CH 0x990 -#define DMEM_RIGHT_CH 0xB10 +#define DMEM_COMPRESSED_ADPCM_DATA 0xD50 +#define DMEM_LEFT_CH 0xD50 +#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_TEMP 0x450 #define DMEM_UNCOMPRESSED_NOTE 0x5F0 -#define DMEM_WET_LEFT_CH 0xC90 -#define DMEM_WET_RIGHT_CH 0xE10 // = DMEM_WET_LEFT_CH + DMEM_1CH_SIZE +#define DMEM_WET_LEFT_CH (DMEM_LEFT_CH + 6 * 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) typedef enum { @@ -77,7 +86,7 @@ void AudioSynth_InitNextRingBuf(s32 sampleCount, s32 itemIndex, s32 reverbIndex) if ((reverb->downsampleRate != 1) && (reverb->framesToIgnore == 0)) { ringItem = &reverb->items[reverb->curFrame][itemIndex]; - osInvalDCache(ringItem->toDownsampleLeft, 0x300); + osInvalDCache(ringItem->toDownsampleLeft, DMEM_1CH_SIZE * MAX_NUM_AUDIO_CHANNELS); j = 0; for (i = 0; i < ringItem->lengthA / 2; i++, j += reverb->downsampleRate) { 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; + for (i = gAudioBufferParams.ticksPerUpdate; i > 0; i--) { if (i == 1) { chunkLen = aiBufLen; @@ -693,9 +703,11 @@ Acmd* AudioSynth_Update(Acmd* aList, s32* cmdCount, s16* aiBufStart, s32 aiBufLe } aCmdPtr = - AudioSynth_DoOneAudioUpdate((s16*) aiBufPtr, chunkLen, aCmdPtr, gAudioBufferParams.ticksPerUpdate - i); + AudioSynth_DoOneAudioUpdate(aiBufPtr, chunkLen, aCmdPtr, gAudioBufferParams.ticksPerUpdate - i); aiBufLen -= chunkLen; - aiBufPtr += chunkLen * 2; + + int num_audio_channels = GetNumAudioChannels(); + aiBufPtr += chunkLen * num_audio_channels; } for (j = 0; j < gNumSynthReverbs; j++) { @@ -723,7 +735,7 @@ Acmd* AudioSynth_LoadReverbSamples(Acmd* aList, s32 aiBufLen, s16 reverbIndex, s aList = 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); } else { 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); aResample(aList++, gSynthReverbs[reverbIndex].resampleFlags, gSynthReverbs[reverbIndex].unk_0A, 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); } @@ -768,7 +780,7 @@ Acmd* AudioSynth_SaveReverbSamples(Acmd* aList, s16 reverbIndex, s16 updateIndex OS_K0_TO_PHYSICAL(gSynthReverbs[reverbIndex] .items[gSynthReverbs[reverbIndex].curFrame][updateIndex] .toDownsampleLeft), - 0x300); + DMEM_2CH_SIZE); gSynthReverbs[reverbIndex].resampleFlags = 0; 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; for (i = 0; i < gNumSynthReverbs; i++) { @@ -834,10 +846,13 @@ Acmd* AudioSynth_DoOneAudioUpdate(s16* aiBuf, s32 aiBufLen, Acmd* aList, s32 upd 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); - aInterleave(aList++, DMEM_LEFT_CH, DMEM_RIGHT_CH); - aSaveBuffer(aList++, DMEM_TEMP, OS_K0_TO_PHYSICAL(aiBuf), j * 2); + 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); + // Copy j bytes from DMEM_TEMP to aiBuf + aSaveBuffer(aList++, DMEM_TEMP, OS_K0_TO_PHYSICAL(aiBuf), j); return aList; } @@ -908,6 +923,10 @@ Acmd* AudioSynth_ProcessNote(s32 noteIndex, NoteSubEu* noteSub, NoteSynthesisSta synthState->samplePosFrac = 0; synthState->curVolLeft = 0; synthState->curVolRight = 0; + synthState->curVolCenter = 0; + synthState->curVolLfe = 0; + synthState->curVolRLeft = 0; + synthState->curVolRRight = 0; synthState->numParts = synthState->prevHaasEffectRightDelaySize = synthState->prevHaasEffectLeftDelaySize = 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, u16 dmemSrc, s32 delaySide, s32 flags) { s16 rampReverb; - s16 rampRight; - s16 rampLeft; - u16 panVolLeft; - u16 panVolRight; - u16 curVolLeft; - u16 curVolRight; + s16 rampRight = 0, rampLeft = 0, rampCenter = 0, rampLfe = 0, rampRLeft = 0, rampRRight = 0; + u16 panVolLeft, panVolRight, panVolCenter, panVolLfe, panVolRLeft, panVolRRight; + u16 curVolLeft, curVolRight, curVolCenter, curVolLfe, curVolRLeft, curVolRRight; s32 sourceReverbVol; s32 temp = 0; curVolLeft = synthState->curVolLeft; curVolRight = synthState->curVolRight; + curVolCenter = synthState->curVolCenter; + curVolLfe = synthState->curVolLfe; + curVolRLeft = synthState->curVolRLeft; + curVolRRight = synthState->curVolRRight; - panVolLeft = noteSub->panVolLeft; - panVolRight = noteSub->panVolRight; - - panVolLeft <<= 4; - panVolRight <<= 4; + panVolLeft = 16 * noteSub->panVolLeft; + panVolRight = 16 * noteSub->panVolRight; + panVolCenter = 16 * noteSub->panVolCenter; + panVolLfe = 16 * noteSub->panVolLfe; + panVolRLeft = 16 * noteSub->panVolRLeft; + panVolRRight = 16 * noteSub->panVolRRight; + s32 aiBufLenSmall = aiBufLen >> 3; if (panVolLeft != curVolLeft) { - rampLeft = (panVolLeft - curVolLeft) / (aiBufLen >> 3); - } else { - rampLeft = 0; + rampLeft = (panVolLeft - curVolLeft) / aiBufLenSmall; } if (panVolRight != curVolRight) { - rampRight = (panVolRight - curVolRight) / (aiBufLen >> 3); - } else { - rampRight = 0; + rampRight = (panVolRight - curVolRight) / aiBufLenSmall; + } + 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; if (noteSub->reverb != sourceReverbVol) { temp = (((noteSub->reverb & 0x7F) - (sourceReverbVol & 0x7F)) << 8); - rampReverb = temp / (aiBufLen >> 3); + rampReverb = temp / aiBufLenSmall; synthState->reverbVol = noteSub->reverb; } else { rampReverb = 0; } - synthState->curVolLeft = curVolLeft + (rampLeft * (aiBufLen >> 3)); - synthState->curVolRight = curVolRight + (rampRight * (aiBufLen >> 3)); + synthState->curVolLeft = curVolLeft + (rampLeft * aiBufLenSmall); + 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) { + int32_t num_audio_channels = 2; aClearBuffer(aList++, DMEM_HAAS_TEMP, DMEM_1CH_SIZE); - aEnvSetup1(aList++, (sourceReverbVol & 0x7F), rampReverb, rampLeft, rampRight); - aEnvSetup2(aList++, curVolLeft, curVolRight); + aEnvSetup1(aList++, (sourceReverbVol & 0x7F), rampReverb, rampLeft, rampRight, rampCenter, rampLfe, rampRLeft, rampRRight); + aEnvSetup2(aList++, curVolLeft, curVolRight, curVolCenter, curVolLfe, curVolRLeft, curVolRRight); switch (delaySide) { case HAAS_EFFECT_DELAY_LEFT: - aEnvMixer(aList++, dmemSrc, aiBufLen, 0, 0, ((sourceReverbVol & 0x80) >> 7), - noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, 0x65B1C9E1, 0); + aEnvMixer(aList++, dmemSrc, aiBufLen, ((sourceReverbVol & 0x80) >> 7), + noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, (DMEM_WET_LEFT_CH << 16) | DMEM_LEFT_CH, DMEM_HAAS_TEMP << 16, num_audio_channels); break; case HAAS_EFFECT_DELAY_RIGHT: - aEnvMixer(aList++, dmemSrc, aiBufLen, 0, 0, ((sourceReverbVol & 0x80) >> 7), - noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, 0x9965C9E1, 0); + aEnvMixer(aList++, dmemSrc, aiBufLen, ((sourceReverbVol & 0x80) >> 7), + noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, (DMEM_WET_LEFT_CH << 16) | DMEM_LEFT_CH, DMEM_HAAS_TEMP, num_audio_channels); break; default: // HAAS_EFFECT_DELAY_NONE - aEnvMixer(aList++, dmemSrc, aiBufLen, 0, 0, ((sourceReverbVol & 0x80) >> 7), - noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, 0x99B1C9E1, 0); + aEnvMixer(aList++, dmemSrc, aiBufLen, ((sourceReverbVol & 0x80) >> 7), + noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, (DMEM_WET_LEFT_CH << 16) | DMEM_LEFT_CH, 0, num_audio_channels); break; } } else { - aEnvSetup1(aList++, (sourceReverbVol & 0x7F), rampReverb, rampLeft, rampRight); - aEnvSetup2(aList++, curVolLeft, curVolRight); - aEnvMixer(aList++, dmemSrc, aiBufLen, 0, 0, ((sourceReverbVol & 0x80) >> 7), - noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, 0x99B1C9E1, 0); + aEnvSetup1(aList++, (sourceReverbVol & 0x7F), rampReverb, rampLeft, rampRight, rampCenter, rampLfe, rampRLeft, rampRRight); + aEnvSetup2(aList++, curVolLeft, curVolRight, curVolCenter, curVolLfe, curVolRLeft, curVolRRight); + aEnvMixer(aList++, dmemSrc, aiBufLen, ((sourceReverbVol & 0x80) >> 7), + noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, (DMEM_WET_LEFT_CH << 16) | DMEM_LEFT_CH, 0, GetNumAudioChannels()); } return aList; diff --git a/src/audio/audio_thread.c b/src/audio/audio_thread.c index 193e43e7..4853158b 100644 --- a/src/audio/audio_thread.c +++ b/src/audio/audio_thread.c @@ -82,7 +82,7 @@ void AudioThread_CreateNextAudioBuffer(s16* samples, u32 num_samples) { AudioSynth_Update(gCurAbiCmdBuffer, &abiCmdCount, samples, num_samples); // Spectrum Analyzer fix - memcpy(gAiBuffers[gCurAiBuffIndex], samples, num_samples); + memcpy(gAiBuffers[gCurAiBuffIndex], samples, num_samples * sizeof(s16)); gAudioRandom = osGetCount() * (gAudioRandom + gAudioTaskCountQ); } @@ -186,7 +186,7 @@ SPTask* AudioThread_CreateTask() { task->output_buff = NULL; task->output_buff_size = NULL; - if (1) {} + task->data_ptr = (u64*) gAbiCmdBuffs[aiBuffIndex]; task->data_size = abiCmdCount * sizeof(Acmd); @@ -438,6 +438,14 @@ void AudioThread_ProcessCmds(u32 msg) { case AUDIOCMD_OP_CHANNEL_SET_IO: if (cmd->arg2 < 8) { 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; case AUDIOCMD_OP_CHANNEL_SET_MUTE: diff --git a/src/audio/mixer.c b/src/audio/mixer.c index 73254973..a2b1798b 100644 --- a/src/audio/mixer.c +++ b/src/audio/mixer.c @@ -2,6 +2,7 @@ #include #include #include +#include #include @@ -71,18 +72,20 @@ static __m128i m256i_clamp_to_m128i(m256i a) { #define ROUND_UP_8(v) (((v) + 7) & ~7) #define ROUND_DOWN_16(v) ((v) & ~0xf) -#define DMEM_BUF_SIZE (0x1000) -// #define DMEM_BUF_SIZE 0xC90 -#define BUF_U8(a) (rspa.buf.as_u8 + ((a)-0x450)) -#define BUF_S16(a) (rspa.buf.as_s16 + ((a)-0x450) / sizeof(int16_t)) +#define DMEM_BUF_SIZE (0x1B90) // 7056 B +#define BUF_U8(a) (rspa.buf + ((a)-0x450)) +#define BUF_S16(a) (int16_t*) BUF_U8(a) + +#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 { uint16_t in; uint16_t out; uint16_t nbytes; - uint16_t vol[2]; - uint16_t rate[2]; + uint16_t vol[6]; + uint16_t rate[6]; uint16_t vol_wet; uint16_t rate_wet; @@ -93,10 +96,7 @@ static struct { uint16_t filter_count; int16_t filter[8]; - union { - int16_t as_s16[DMEM_BUF_SIZE / sizeof(int16_t)]; - uint8_t as_u8[DMEM_BUF_SIZE]; - } buf; + uint8_t buf[DMEM_BUF_SIZE]; } rspa; 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; } -#if 1 -// old abi impl -void aInterleaveImpl(uint16_t left, uint16_t right) { - if(rspa.nbytes == 0) { // Added +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) { + if (rspa.nbytes == 0) { return; } - int count = ROUND_UP_16(rspa.nbytes) >> 3; - 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; + 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 r0 = *r++; - int16_t r1 = *r++; - int16_t r2 = *r++; - int16_t r3 = *r++; - *d++ = l0; - *d++ = r0; - *d++ = l1; - *d++ = r1; - *d++ = l2; - *d++ = r2; - *d++ = l3; - *d++ = r3; - --count; + if (num_channels == 2) { + for (int i = 0; i < count; i++) { + *d++ = *l++; + *d++ = *r++; + } + } else { + int16_t *c = BUF_S16(center); + int16_t *lf = BUF_S16(lfe); + int16_t *sl = BUF_S16(surround_left); + int16_t *sr = BUF_S16(surround_right); + + for (int i = 0; i < count; i++) { + *d++ = *l++; + *d++ = *r++; + *d++ = *c++; + *d++ = *lf++; + *d++ = *sl++; + *d++ = *sr++; + } } } -#endif void aDMEMMoveImpl(uint16_t in_addr, uint16_t out_addr, int 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 prev2 = out[-2]; int j, k; - if (flags & 4) { - for (j = 0; j < 2; j++) { - ins[j * 4] = (((*in >> 6) << 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 + 3] = (((*in++ & 0x3) << 30) >> 30) << shift; - } - } else { - for (j = 0; j < 4; j++) { - ins[j * 2] = (((*in >> 4) << 28) >> 28) << shift; - ins[j * 2 + 1] = (((*in++ & 0xf) << 28) >> 28) << shift; - } - } + if (flags & 4) { + for (j = 0; j < 2; j++) { + ins[j * 4] = (((*in >> 6) << 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 + 3] = (((*in++ & 0x3) << 30) >> 30) << shift; + } + } else { + for (j = 0; j < 4; j++) { + ins[j * 2] = (((*in >> 4) << 28) >> 28) << shift; + ins[j * 2 + 1] = (((*in++ & 0xf) << 28) >> 28) << shift; + } + } for (j = 0; j < 8; j++) { int32_t acc = tbl[0][j] * prev2 + tbl[1][j] * prev1 + (ins[j] << 11); for (k = 0; k < j; k++) { @@ -670,111 +621,152 @@ void aResampleImpl(uint8_t flags, uint16_t pitch, RESAMPLE_STATE state) { #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.rate_wet = rate_wet; rspa.rate[0] = rate_left; 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[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, - bool neg_3, bool neg_2, 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 *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); + if (n > max_num_samples) { + 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 n = ROUND_UP_16(n_samples); - 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; + if (num_channels == 6) { + // Calculate the filter coefficient + float RC = 1.f / (2 * M_PI * CUTOFF_FREQ_LFE); + 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++) { - samples[j] = (samples[j] * vols[j] >> 16) ^ negs[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]++; + vols[j] += rspa.rate[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 diff --git a/src/audio/mixer.h b/src/audio/mixer.h index b9ec250c..64c2221c 100644 --- a/src/audio/mixer.h +++ b/src/audio/mixer.h @@ -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 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 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 aSetLoopImpl(ADPCM_STATE* adpcm_loop_state); void aADPCMdecImpl(uint8_t flags, ADPCM_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 aEnvSetup2Impl(uint16_t initial_vol_left, uint16_t initial_vol_right); -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); +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); +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); +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 aS8DecImpl(uint8_t flags, ADPCM_STATE state); 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 aLoadADPCM(pkt, c, d) aLoadADPCMImpl(c, d) #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 aSetLoop(pkt, a) aSetLoopImpl(a) #define aADPCMdec(pkt, f, s) aADPCMdecImpl(f, s) #define aResample(pkt, f, p, s) aResampleImpl(f, p, s) -#define aEnvSetup1(pkt, initialVolReverb, rampReverb, rampLeft, rampRight) \ - aEnvSetup1Impl(initialVolReverb, rampReverb, rampLeft, rampRight) -#define aEnvSetup2(pkt, initialVolLeft, initialVolRight) aEnvSetup2Impl(initialVolLeft, initialVolRight) -#define aEnvMixer(pkt, inBuf, nSamples, swapReverb, negLeft, negRight, dryLeft, dryRight, wetLeft, wetRight) \ - aEnvMixerImpl(inBuf, nSamples, swapReverb, negLeft, negRight, dryLeft, dryRight, wetLeft, wetRight) +#define aEnvSetup1(pkt, initialVolReverb, rampReverb, rampLeft, rampRight, rampCenter, rampLfe, rampRLeft, rampRRight) \ + aEnvSetup1Impl(initialVolReverb, rampReverb, rampLeft, rampRight, rampCenter, rampLfe, rampRLeft, rampRRight) +#define aEnvSetup2(pkt, initialVolLeft, initialVolRight, initialVolCenter, initialVolLfe, initialVolRLeft, initialVolRRight) \ + aEnvSetup2Impl(initialVolLeft, initialVolRight, initialVolCenter, initialVolLfe, initialVolRLeft, initialVolRRight) +#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 aS8Dec(pkt, f, s) aS8DecImpl(f, s) #define aAddMixer(pkt, s, d, c) aAddMixerImpl(s, d, c) diff --git a/src/port/Engine.cpp b/src/port/Engine.cpp index 87cd9dd9..592660f9 100644 --- a/src/port/Engine.cpp +++ b/src/port/Engine.cpp @@ -166,7 +166,8 @@ GameEngine::GameEngine() { auto window = std::make_shared(std::vector>({})); - 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__ Ship::Context::GetInstance()->GetLogger()->set_level( @@ -341,7 +342,7 @@ void GameEngine::StartFrame() const { #endif -#define NUM_AUDIO_CHANNELS 2 +#define MAX_NUM_AUDIO_CHANNELS 6 extern "C" u16 audBuffer = 0; #include @@ -371,6 +372,7 @@ void GameEngine::HandleAudioThread() { // gVIsPerFrame = 2; #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 Lock(audio.mutex); int samples_left = AudioPlayerBuffered(); @@ -382,19 +384,22 @@ void GameEngine::HandleAudioThread() { 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++) { - 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); } #ifdef PIPE_DEBUG if (outfile.is_open()) { outfile.write(reinterpret_cast(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 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.cv_from_thread.notify_one(); } diff --git a/src/port/ui/ImguiUI.cpp b/src/port/ui/ImguiUI.cpp index addf058a..32dc2f41 100644 --- a/src/port/ui/ImguiUI.cpp +++ b/src/port/ui/ImguiUI.cpp @@ -171,6 +171,8 @@ void DrawSettingsMenu(){ if (Ship::Context::GetInstance()->GetAudio()->GetAvailableAudioBackends()->size() <= 1) { UIWidgets::ReEnableComponent(""); } + + UIWidgets::PaddedEnhancementCheckbox("Surround 5.1 (needs reload)", "gSurroundAudio", true, false); ImGui::EndMenu(); } diff --git a/tools/SDL3 b/tools/SDL3 new file mode 160000 index 00000000..69d28027 --- /dev/null +++ b/tools/SDL3 @@ -0,0 +1 @@ +Subproject commit 69d28027adb70daf962621bb5e8ab00a069cbe30 diff --git a/tools/Torch b/tools/Torch index 228a17c6..db4cb9a4 160000 --- a/tools/Torch +++ b/tools/Torch @@ -1 +1 @@ -Subproject commit 228a17c68282092b6c3937c46b943229388931fe +Subproject commit db4cb9a493e0dfee8e8fa6dc529390bb3321a771