1
0
Fork 0
mirror of https://github.com/zeldaret/oot.git synced 2024-12-02 15:55:59 +00:00
oot/src/code/audio_synthesis.c

1339 lines
52 KiB
C
Raw Normal View History

#include "ultra64.h"
#include "global.h"
// DMEM Addresses for the RSP
#define DMEM_TEMP 0x3C0
#define DMEM_UNCOMPRESSED_NOTE 0x580
#define DMEM_HAAS_TEMP 0x5C0
#define DMEM_SCRATCH2 0x760 // = DMEM_TEMP + DMEM_2CH_SIZE + a bit more
#define DMEM_COMPRESSED_ADPCM_DATA 0x940 // = DMEM_LEFT_CH
#define DMEM_LEFT_CH 0x940
#define DMEM_RIGHT_CH 0xAE0
#define DMEM_WET_TEMP 0x3E0
#define DMEM_WET_SCRATCH 0x720 // = DMEM_WET_TEMP + DMEM_2CH_SIZE
#define DMEM_WET_LEFT_CH 0xC80
#define DMEM_WET_RIGHT_CH 0xE20 // = DMEM_WET_LEFT_CH + DMEM_1CH_SIZE
typedef enum {
/* 0 */ HAAS_EFFECT_DELAY_NONE,
/* 1 */ HAAS_EFFECT_DELAY_LEFT, // Delay left channel so that right channel is heard first
/* 2 */ HAAS_EFFECT_DELAY_RIGHT // Delay right channel so that left channel is heard first
} HaasEffectDelaySide;
Acmd* AudioSynth_LoadRingBufferPart(Acmd* cmd, u16 dmem, u16 startPos, s32 size, SynthesisReverb* reverb);
Acmd* AudioSynth_SaveBufferOffset(Acmd* cmd, u16 dmem, u16 offset, s32 size, s16* buf);
Acmd* AudioSynth_SaveRingBufferPart(Acmd* cmd, u16 dmem, u16 startPos, s32 size, SynthesisReverb* reverb);
Acmd* AudioSynth_DoOneAudioUpdate(s16* aiBuf, s32 aiBufLen, Acmd* cmd, s32 updateIndex);
Acmd* AudioSynth_ProcessNote(s32 noteIndex, NoteSubEu* noteSubEu, NoteSynthesisState* synthState, s16* aiBuf,
s32 aiBufLen, Acmd* cmd, s32 updateIndex);
Acmd* AudioSynth_LoadWaveSamples(Acmd* cmd, NoteSubEu* noteSubEu, NoteSynthesisState* synthState, s32 numSamplesToLoad);
Acmd* AudioSynth_ApplyHaasEffect(Acmd* cmd, NoteSubEu* noteSubEu, NoteSynthesisState* synthState, s32 size, s32 flags,
s32 haasEffectDelaySide);
Acmd* AudioSynth_ProcessEnvelope(Acmd* cmd, NoteSubEu* noteSubEu, NoteSynthesisState* synthState, s32 aiBufLen,
u16 dmemSrc, s32 haasEffectDelaySide, s32 flags);
Acmd* AudioSynth_FinalResample(Acmd* cmd, NoteSynthesisState* synthState, s32 size, u16 pitch, u16 inpDmem,
s32 resampleFlags);
u32 sEnvMixerOp = _SHIFTL(A_ENVMIXER, 24, 8);
// Store the left dry channel in a temp space to be delayed to produce the haas effect
u32 sEnvMixerLeftHaasDmemDests =
MK_CMD(DMEM_HAAS_TEMP >> 4, DMEM_RIGHT_CH >> 4, DMEM_WET_LEFT_CH >> 4, DMEM_WET_RIGHT_CH >> 4);
// Store the right dry channel in a temp space to be delayed to produce the haas effect
u32 sEnvMixerRightHaasDmemDests =
MK_CMD(DMEM_LEFT_CH >> 4, DMEM_HAAS_TEMP >> 4, DMEM_WET_LEFT_CH >> 4, DMEM_WET_RIGHT_CH >> 4);
u32 sEnvMixerDefaultDmemDests =
MK_CMD(DMEM_LEFT_CH >> 4, DMEM_RIGHT_CH >> 4, DMEM_WET_LEFT_CH >> 4, DMEM_WET_RIGHT_CH >> 4);
u16 D_801304B0[] = {
0x7FFF, 0xD001, 0x3FFF, 0xF001, 0x5FFF, 0x9001, 0x7FFF, 0x8001,
};
u8 sNumSamplesPerWavePeriod[] = {
WAVE_SAMPLE_COUNT, // 1st harmonic
WAVE_SAMPLE_COUNT / 2, // 2nd harmonic
WAVE_SAMPLE_COUNT / 4, // 4th harmonic
WAVE_SAMPLE_COUNT / 8, // 8th harmonic
};
void AudioSynth_InitNextRingBuf(s32 chunkLen, s32 updateIndex, s32 reverbIndex) {
ReverbRingBufferItem* bufItem;
s32 pad[3];
2022-10-07 17:43:17 +00:00
SynthesisReverb* reverb = &gAudioCtx.synthesisReverbs[reverbIndex];
s32 temp_a0_2;
s32 temp_a0_4;
s32 numSamples;
s32 extraSamples;
s32 i;
s32 j;
if (reverb->downsampleRate >= 2) {
if (reverb->framesToIgnore == 0) {
bufItem = &reverb->items[reverb->curFrame][updateIndex];
Audio_InvalDCache(bufItem->toDownsampleLeft, DMEM_2CH_SIZE);
for (j = 0, i = 0; i < bufItem->lengthA / (s32)SAMPLE_SIZE; j += reverb->downsampleRate, i++) {
reverb->leftRingBuf[bufItem->startPos + i] = bufItem->toDownsampleLeft[j];
reverb->rightRingBuf[bufItem->startPos + i] = bufItem->toDownsampleRight[j];
}
for (i = 0; i < bufItem->lengthB / (s32)SAMPLE_SIZE; j += reverb->downsampleRate, i++) {
reverb->leftRingBuf[i] = bufItem->toDownsampleLeft[j];
reverb->rightRingBuf[i] = bufItem->toDownsampleRight[j];
}
}
}
bufItem = &reverb->items[reverb->curFrame][updateIndex];
numSamples = chunkLen / reverb->downsampleRate;
extraSamples = (numSamples + reverb->nextRingBufPos) - reverb->bufSizePerChan;
temp_a0_2 = reverb->nextRingBufPos;
if (extraSamples < 0) {
bufItem->lengthA = numSamples * SAMPLE_SIZE;
bufItem->lengthB = 0;
bufItem->startPos = reverb->nextRingBufPos;
reverb->nextRingBufPos += numSamples;
} else {
// End of the buffer is reached. Loop back around
bufItem->lengthA = (numSamples - extraSamples) * SAMPLE_SIZE;
bufItem->lengthB = extraSamples * SAMPLE_SIZE;
bufItem->startPos = reverb->nextRingBufPos;
reverb->nextRingBufPos = extraSamples;
}
bufItem->numSamplesAfterDownsampling = numSamples;
bufItem->chunkLen = chunkLen;
if (reverb->unk_14 != 0) {
temp_a0_4 = reverb->unk_14 + temp_a0_2;
if (temp_a0_4 >= reverb->bufSizePerChan) {
temp_a0_4 -= reverb->bufSizePerChan;
}
bufItem = &reverb->items2[reverb->curFrame][updateIndex];
numSamples = chunkLen / reverb->downsampleRate;
extraSamples = (temp_a0_4 + numSamples) - reverb->bufSizePerChan;
if (extraSamples < 0) {
bufItem->lengthA = numSamples * SAMPLE_SIZE;
bufItem->lengthB = 0;
bufItem->startPos = temp_a0_4;
} else {
// End of the buffer is reached. Loop back around
bufItem->lengthA = (numSamples - extraSamples) * SAMPLE_SIZE;
bufItem->lengthB = extraSamples * SAMPLE_SIZE;
bufItem->startPos = temp_a0_4;
}
bufItem->numSamplesAfterDownsampling = numSamples;
bufItem->chunkLen = chunkLen;
}
}
void func_800DB03C(s32 updateIndex) {
NoteSubEu* subEu;
NoteSubEu* subEu2;
s32 baseIndex;
s32 i;
2022-10-07 17:43:17 +00:00
baseIndex = gAudioCtx.numNotes * updateIndex;
for (i = 0; i < gAudioCtx.numNotes; i++) {
subEu = &gAudioCtx.notes[i].noteSubEu;
subEu2 = &gAudioCtx.noteSubsEu[baseIndex + i];
if (subEu->bitField0.enabled) {
subEu->bitField0.needsInit = false;
} else {
subEu2->bitField0.enabled = false;
}
subEu->harmonicIndexCurAndPrev = 0;
}
}
Acmd* AudioSynth_Update(Acmd* cmdStart, s32* cmdCnt, s16* aiStart, s32 aiBufLen) {
s32 chunkLen;
s16* aiBufP;
Acmd* cmdP;
s32 i;
s32 j;
SynthesisReverb* reverb;
cmdP = cmdStart;
2022-10-07 17:43:17 +00:00
for (i = gAudioCtx.audioBufferParameters.updatesPerFrame; i > 0; i--) {
AudioSeq_ProcessSequences(i - 1);
2022-10-07 17:43:17 +00:00
func_800DB03C(gAudioCtx.audioBufferParameters.updatesPerFrame - i);
}
aiBufP = aiStart;
2022-10-07 17:43:17 +00:00
gAudioCtx.curLoadedBook = NULL;
2022-10-07 17:43:17 +00:00
for (i = gAudioCtx.audioBufferParameters.updatesPerFrame; i > 0; i--) {
if (i == 1) {
chunkLen = aiBufLen;
2022-10-07 17:43:17 +00:00
} else if ((aiBufLen / i) >= gAudioCtx.audioBufferParameters.samplesPerUpdateMax) {
chunkLen = gAudioCtx.audioBufferParameters.samplesPerUpdateMax;
} else if (gAudioCtx.audioBufferParameters.samplesPerUpdateMin >= (aiBufLen / i)) {
chunkLen = gAudioCtx.audioBufferParameters.samplesPerUpdateMin;
} else {
2022-10-07 17:43:17 +00:00
chunkLen = gAudioCtx.audioBufferParameters.samplesPerUpdate;
}
2022-10-07 17:43:17 +00:00
for (j = 0; j < gAudioCtx.numSynthesisReverbs; j++) {
if (gAudioCtx.synthesisReverbs[j].useReverb) {
AudioSynth_InitNextRingBuf(chunkLen, gAudioCtx.audioBufferParameters.updatesPerFrame - i, j);
}
}
2022-10-07 17:43:17 +00:00
cmdP = AudioSynth_DoOneAudioUpdate(aiBufP, chunkLen, cmdP, gAudioCtx.audioBufferParameters.updatesPerFrame - i);
aiBufLen -= chunkLen;
aiBufP += 2 * chunkLen;
}
2022-10-07 17:43:17 +00:00
for (j = 0; j < gAudioCtx.numSynthesisReverbs; j++) {
if (gAudioCtx.synthesisReverbs[j].framesToIgnore != 0) {
gAudioCtx.synthesisReverbs[j].framesToIgnore--;
}
2022-10-07 17:43:17 +00:00
gAudioCtx.synthesisReverbs[j].curFrame ^= 1;
}
*cmdCnt = cmdP - cmdStart;
return cmdP;
}
void func_800DB2C0(s32 updateIndex, s32 noteIndex) {
NoteSubEu* noteSubEu;
s32 i;
2022-10-07 17:43:17 +00:00
for (i = updateIndex + 1; i < gAudioCtx.audioBufferParameters.updatesPerFrame; i++) {
noteSubEu = &gAudioCtx.noteSubsEu[(gAudioCtx.numNotes * i) + noteIndex];
if (!noteSubEu->bitField0.needsInit) {
noteSubEu->bitField0.enabled = false;
} else {
break;
}
}
}
Acmd* AudioSynth_LoadRingBuffer1AtTemp(Acmd* cmd, SynthesisReverb* reverb, s16 updateIndex) {
ReverbRingBufferItem* bufItem = &reverb->items[reverb->curFrame][updateIndex];
cmd = AudioSynth_LoadRingBufferPart(cmd, DMEM_WET_TEMP, bufItem->startPos, bufItem->lengthA, reverb);
if (bufItem->lengthB != 0) {
// Ring buffer wrapped
cmd = AudioSynth_LoadRingBufferPart(cmd, DMEM_WET_TEMP + bufItem->lengthA, 0, bufItem->lengthB, reverb);
}
return cmd;
}
Acmd* AudioSynth_SaveRingBuffer1AtTemp(Acmd* cmd, SynthesisReverb* reverb, s16 updateIndex) {
ReverbRingBufferItem* bufItem = &reverb->items[reverb->curFrame][updateIndex];
cmd = AudioSynth_SaveRingBufferPart(cmd, DMEM_WET_TEMP, bufItem->startPos, bufItem->lengthA, reverb);
if (bufItem->lengthB != 0) {
// Ring buffer wrapped
cmd = AudioSynth_SaveRingBufferPart(cmd, DMEM_WET_TEMP + bufItem->lengthA, 0, bufItem->lengthB, reverb);
}
return cmd;
}
/**
* Leak some audio from the left reverb channel into the right reverb channel and vice versa (pan)
*/
Acmd* AudioSynth_LeakReverb(Acmd* cmd, SynthesisReverb* reverb) {
aDMEMMove(cmd++, DMEM_WET_LEFT_CH, DMEM_WET_SCRATCH, DMEM_1CH_SIZE);
aMix(cmd++, DMEM_1CH_SIZE >> 4, reverb->leakRtl, DMEM_WET_RIGHT_CH, DMEM_WET_LEFT_CH);
aMix(cmd++, DMEM_1CH_SIZE >> 4, reverb->leakLtr, DMEM_WET_SCRATCH, DMEM_WET_RIGHT_CH);
return cmd;
}
Acmd* func_800DB4E4(Acmd* cmd, s32 aiBufLen, SynthesisReverb* reverb, s16 updateIndex) {
ReverbRingBufferItem* item = &reverb->items[reverb->curFrame][updateIndex];
s16 offsetA;
s16 offsetB;
offsetA = (item->startPos & 7) * SAMPLE_SIZE;
offsetB = ALIGN16(offsetA + item->lengthA);
cmd = AudioSynth_LoadRingBufferPart(cmd, DMEM_WET_TEMP, item->startPos - (offsetA / (s32)SAMPLE_SIZE),
DMEM_1CH_SIZE, reverb);
if (item->lengthB != 0) {
// Ring buffer wrapped
cmd = AudioSynth_LoadRingBufferPart(cmd, DMEM_WET_TEMP + offsetB, 0, DMEM_1CH_SIZE - offsetB, reverb);
}
aSetBuffer(cmd++, 0, DMEM_WET_TEMP + offsetA, DMEM_WET_LEFT_CH, aiBufLen * SAMPLE_SIZE);
aResample(cmd++, reverb->resampleFlags, reverb->unk_0E, reverb->unk_30);
aSetBuffer(cmd++, 0, DMEM_WET_TEMP + DMEM_1CH_SIZE + offsetA, DMEM_WET_RIGHT_CH, aiBufLen * SAMPLE_SIZE);
aResample(cmd++, reverb->resampleFlags, reverb->unk_0E, reverb->unk_34);
return cmd;
}
Acmd* func_800DB680(Acmd* cmd, SynthesisReverb* reverb, s16 updateIndex) {
ReverbRingBufferItem* bufItem = &reverb->items[reverb->curFrame][updateIndex];
aSetBuffer(cmd++, 0, DMEM_WET_LEFT_CH, DMEM_WET_SCRATCH, bufItem->unk_18 * SAMPLE_SIZE);
aResample(cmd++, reverb->resampleFlags, bufItem->unk_16, reverb->unk_38);
cmd = AudioSynth_SaveBufferOffset(cmd, DMEM_WET_SCRATCH, bufItem->startPos, bufItem->lengthA, reverb->leftRingBuf);
if (bufItem->lengthB != 0) {
// Ring buffer wrapped
cmd = AudioSynth_SaveBufferOffset(cmd, DMEM_WET_SCRATCH + bufItem->lengthA, 0, bufItem->lengthB,
reverb->leftRingBuf);
}
aSetBuffer(cmd++, 0, DMEM_WET_RIGHT_CH, DMEM_WET_SCRATCH, bufItem->unk_18 * SAMPLE_SIZE);
aResample(cmd++, reverb->resampleFlags, bufItem->unk_16, reverb->unk_3C);
cmd = AudioSynth_SaveBufferOffset(cmd, DMEM_WET_SCRATCH, bufItem->startPos, bufItem->lengthA, reverb->rightRingBuf);
if (bufItem->lengthB != 0) {
// Ring buffer wrapped
cmd = AudioSynth_SaveBufferOffset(cmd, DMEM_WET_SCRATCH + bufItem->lengthA, 0, bufItem->lengthB,
reverb->rightRingBuf);
}
return cmd;
}
Acmd* func_800DB828(Acmd* cmd, s32 aiBufLen, SynthesisReverb* reverb, s16 updateIndex) {
ReverbRingBufferItem* item = &reverb->items[reverb->curFrame][updateIndex];
s16 offsetA;
s16 offsetB;
item->unk_14 = (item->unk_18 << 0xF) / aiBufLen;
offsetA = (item->startPos & 7) * SAMPLE_SIZE;
item->unk_16 = (aiBufLen << 0xF) / item->unk_18;
offsetB = ALIGN16(offsetA + item->lengthA);
cmd = AudioSynth_LoadRingBufferPart(cmd, DMEM_WET_TEMP, item->startPos - (offsetA / (s32)SAMPLE_SIZE),
DMEM_1CH_SIZE, reverb);
if (item->lengthB != 0) {
// Ring buffer wrapped
cmd = AudioSynth_LoadRingBufferPart(cmd, DMEM_WET_TEMP + offsetB, 0, DMEM_1CH_SIZE - offsetB, reverb);
}
aSetBuffer(cmd++, 0, DMEM_WET_TEMP + offsetA, DMEM_WET_LEFT_CH, aiBufLen * SAMPLE_SIZE);
aResample(cmd++, reverb->resampleFlags, item->unk_14, reverb->unk_30);
aSetBuffer(cmd++, 0, DMEM_WET_TEMP + DMEM_1CH_SIZE + offsetA, DMEM_WET_RIGHT_CH, aiBufLen * SAMPLE_SIZE);
aResample(cmd++, reverb->resampleFlags, item->unk_14, reverb->unk_34);
return cmd;
}
/**
* Apply a filter (convolution) to each reverb channel.
*/
Acmd* AudioSynth_FilterReverb(Acmd* cmd, s32 size, SynthesisReverb* reverb) {
if (reverb->filterLeft != NULL) {
aFilter(cmd++, 2, size, reverb->filterLeft);
aFilter(cmd++, reverb->resampleFlags, DMEM_WET_LEFT_CH, reverb->filterLeftState);
}
if (reverb->filterRight != NULL) {
aFilter(cmd++, 2, size, reverb->filterRight);
aFilter(cmd++, reverb->resampleFlags, DMEM_WET_RIGHT_CH, reverb->filterRightState);
}
return cmd;
}
Acmd* AudioSynth_MaybeMixRingBuffer1(Acmd* cmd, SynthesisReverb* reverb, s32 updateIndex) {
SynthesisReverb* temp_a3;
2022-10-07 17:43:17 +00:00
temp_a3 = &gAudioCtx.synthesisReverbs[reverb->unk_05];
if (temp_a3->downsampleRate == 1) {
cmd = AudioSynth_LoadRingBuffer1AtTemp(cmd, temp_a3, updateIndex);
aMix(cmd++, DMEM_2CH_SIZE >> 4, reverb->unk_08, DMEM_WET_LEFT_CH, DMEM_WET_TEMP);
cmd = AudioSynth_SaveRingBuffer1AtTemp(cmd, temp_a3, updateIndex);
}
return cmd;
}
void func_800DBB94(void) {
}
void AudioSynth_ClearBuffer(Acmd* cmd, s32 dmem, s32 size) {
aClearBuffer(cmd, dmem, size);
}
void func_800DBBBC(void) {
}
void func_800DBBC4(void) {
}
void func_800DBBCC(void) {
}
void AudioSynth_Mix(Acmd* cmd, s32 arg1, s32 arg2, s32 arg3, s32 arg4) {
aMix(cmd, arg1, arg2, arg3, arg4);
}
void func_800DBC08(void) {
}
void func_800DBC10(void) {
}
void func_800DBC18(void) {
}
void AudioSynth_SetBuffer(Acmd* cmd, s32 flags, s32 dmemIn, s32 dmemOut, u32 size) {
aSetBuffer(cmd, flags, dmemIn, dmemOut, size);
}
void func_800DBC54(void) {
}
void func_800DBC5C(void) {
}
// possible fake match?
void AudioSynth_DMemMove(Acmd* cmd, s32 dmemIn, s32 dmemOut, u32 size) {
cmd->words.w0 = _SHIFTL(A_DMEMMOVE, 24, 8) | _SHIFTL(dmemIn, 0, 24);
cmd->words.w1 = _SHIFTL(dmemOut, 16, 16) | _SHIFTL(size, 0, 16);
}
void func_800DBC90(void) {
}
void func_800DBC98(void) {
}
void func_800DBCA0(void) {
}
void func_800DBCA8(void) {
}
void AudioSynth_InterL(Acmd* cmd, s32 dmemIn, s32 dmemOut, s32 numSamples) {
cmd->words.w0 = _SHIFTL(A_INTERL, 24, 8) | _SHIFTL(numSamples, 0, 16);
cmd->words.w1 = _SHIFTL(dmemIn, 16, 16) | _SHIFTL(dmemOut, 0, 16);
}
void AudioSynth_EnvSetup1(Acmd* cmd, s32 arg1, s32 arg2, s32 arg3, s32 arg4) {
aEnvSetup1(cmd, arg1, arg2, arg3, arg4);
}
void func_800DBD08(void) {
}
void AudioSynth_LoadBuffer(Acmd* cmd, s32 dmemDest, s32 size, void* addrSrc) {
aLoadBuffer(cmd, addrSrc, dmemDest, size);
}
void AudioSynth_SaveBuffer(Acmd* cmd, s32 dmemSrc, s32 size, void* addrDest) {
aSaveBuffer(cmd, dmemSrc, addrDest, size);
}
void AudioSynth_EnvSetup2(Acmd* cmd, s32 volLeft, s32 volRight) {
cmd->words.w0 = _SHIFTL(A_ENVSETUP2, 24, 8);
cmd->words.w1 = _SHIFTL(volLeft, 16, 16) | _SHIFTL(volRight, 0, 16);
}
void func_800DBD7C(void) {
}
void func_800DBD84(void) {
}
void func_800DBD8C(void) {
}
void AudioSynth_S8Dec(Acmd* cmd, s32 flags, s16* state) {
aS8Dec(cmd, flags, state);
}
void AudioSynth_HiLoGain(Acmd* cmd, s32 gain, s32 dmemIn, s32 dmemOut, s32 size) {
cmd->words.w0 = _SHIFTL(A_HILOGAIN, 24, 8) | _SHIFTL(gain, 16, 8) | _SHIFTL(size, 0, 16);
cmd->words.w1 = _SHIFTL(dmemIn, 16, 16) | _SHIFTL(dmemOut, 0, 16);
}
void AudioSynth_UnkCmd19(Acmd* cmd, s32 arg1, s32 arg2, s32 size, s32 arg4) {
cmd->words.w0 = _SHIFTL(A_UNK19, 24, 8) | _SHIFTL(arg4, 16, 8) | _SHIFTL(size, 0, 16);
cmd->words.w1 = _SHIFTL(arg1, 16, 16) | _SHIFTL(arg2, 0, 16);
}
void func_800DBE18(void) {
}
void func_800DBE20(void) {
}
void func_800DBE28(void) {
}
void func_800DBE30(void) {
}
void AudioSynth_UnkCmd3(Acmd* cmd, s32 arg1, s32 arg2, s32 size) {
cmd->words.w0 = _SHIFTL(A_UNK3, 24, 8) | _SHIFTL(size, 0, 16);
cmd->words.w1 = _SHIFTL(arg1, 16, 16) | _SHIFTL(arg2, 0, 16);
}
void func_800DBE5C(void) {
}
void func_800DBE64(void) {
}
void func_800DBE6C(void) {
}
void AudioSynth_LoadFilterBuffer(Acmd* cmd, s32 flags, s32 buf, void* addr) {
aFilter(cmd, flags, buf, addr);
}
void AudioSynth_LoadFilterSize(Acmd* cmd, s32 size, void* addr) {
aFilter(cmd, 2, size, addr);
}
Acmd* AudioSynth_LoadRingBuffer1(Acmd* cmd, s32 aiBufLen, SynthesisReverb* reverb, s16 updateIndex) {
ReverbRingBufferItem* ringBufferItem = &reverb->items[reverb->curFrame][updateIndex];
cmd =
AudioSynth_LoadRingBufferPart(cmd, DMEM_WET_LEFT_CH, ringBufferItem->startPos, ringBufferItem->lengthA, reverb);
if (ringBufferItem->lengthB != 0) {
// Ring buffer wrapped
cmd = AudioSynth_LoadRingBufferPart(cmd, DMEM_WET_LEFT_CH + ringBufferItem->lengthA, 0, ringBufferItem->lengthB,
reverb);
}
return cmd;
}
Acmd* AudioSynth_LoadRingBuffer2(Acmd* cmd, s32 aiBufLen, SynthesisReverb* reverb, s16 updateIndex) {
ReverbRingBufferItem* bufItem = &reverb->items2[reverb->curFrame][updateIndex];
cmd = AudioSynth_LoadRingBufferPart(cmd, DMEM_WET_LEFT_CH, bufItem->startPos, bufItem->lengthA, reverb);
if (bufItem->lengthB != 0) {
// Ring buffer wrapped
cmd = AudioSynth_LoadRingBufferPart(cmd, DMEM_WET_LEFT_CH + bufItem->lengthA, 0, bufItem->lengthB, reverb);
}
return cmd;
}
Acmd* AudioSynth_LoadRingBufferPart(Acmd* cmd, u16 dmem, u16 startPos, s32 size, SynthesisReverb* reverb) {
aLoadBuffer(cmd++, &reverb->leftRingBuf[startPos], dmem, size);
aLoadBuffer(cmd++, &reverb->rightRingBuf[startPos], dmem + DMEM_1CH_SIZE, size);
return cmd;
}
Acmd* AudioSynth_SaveRingBufferPart(Acmd* cmd, u16 dmem, u16 startPos, s32 size, SynthesisReverb* reverb) {
aSaveBuffer(cmd++, dmem, &reverb->leftRingBuf[startPos], size);
aSaveBuffer(cmd++, dmem + DMEM_1CH_SIZE, &reverb->rightRingBuf[startPos], size);
return cmd;
}
Acmd* AudioSynth_SaveBufferOffset(Acmd* cmd, u16 dmem, u16 offset, s32 size, s16* buf) {
aSaveBuffer(cmd++, dmem, &buf[offset], size);
return cmd;
}
Acmd* AudioSynth_MaybeLoadRingBuffer2(Acmd* cmd, s32 aiBufLen, SynthesisReverb* reverb, s16 updateIndex) {
if (reverb->downsampleRate == 1) {
cmd = AudioSynth_LoadRingBuffer2(cmd, aiBufLen, reverb, updateIndex);
}
return cmd;
}
Acmd* AudioSynth_LoadReverbSamples(Acmd* cmd, s32 aiBufLen, SynthesisReverb* reverb, s16 updateIndex) {
// Sets DMEM_WET_{LEFT,RIGHT}_CH, clobbers DMEM_TEMP
if (reverb->downsampleRate == 1) {
if (reverb->unk_18 != 0) {
cmd = func_800DB828(cmd, aiBufLen, reverb, updateIndex);
} else {
cmd = AudioSynth_LoadRingBuffer1(cmd, aiBufLen, reverb, updateIndex);
}
} else {
cmd = func_800DB4E4(cmd, aiBufLen, reverb, updateIndex);
}
return cmd;
}
Acmd* AudioSynth_SaveReverbSamples(Acmd* cmd, SynthesisReverb* reverb, s16 updateIndex) {
ReverbRingBufferItem* bufItem = &reverb->items[reverb->curFrame][updateIndex];
if (reverb->downsampleRate == 1) {
if (reverb->unk_18 != 0) {
cmd = func_800DB680(cmd, reverb, updateIndex);
} else {
// Put the oldest samples in the ring buffer into the wet channels
cmd = AudioSynth_SaveRingBufferPart(cmd, DMEM_WET_LEFT_CH, bufItem->startPos, bufItem->lengthA, reverb);
if (bufItem->lengthB != 0) {
// Ring buffer wrapped
cmd = AudioSynth_SaveRingBufferPart(cmd, DMEM_WET_LEFT_CH + bufItem->lengthA, 0, bufItem->lengthB,
reverb);
}
}
} else {
// Downsampling is done later by CPU when RSP is done, therefore we need to have
// double buffering. Left and right buffers are adjacent in memory.
AudioSynth_SaveBuffer(cmd++, DMEM_WET_LEFT_CH, DMEM_2CH_SIZE,
reverb->items[reverb->curFrame][updateIndex].toDownsampleLeft);
}
reverb->resampleFlags = 0;
return cmd;
}
Acmd* AudioSynth_SaveRingBuffer2(Acmd* cmd, SynthesisReverb* reverb, s16 updateIndex) {
ReverbRingBufferItem* bufItem = &reverb->items2[reverb->curFrame][updateIndex];
cmd = AudioSynth_SaveRingBufferPart(cmd, DMEM_WET_LEFT_CH, bufItem->startPos, bufItem->lengthA, reverb);
if (bufItem->lengthB != 0) {
// Ring buffer wrapped
cmd = AudioSynth_SaveRingBufferPart(cmd, DMEM_WET_LEFT_CH + bufItem->lengthA, 0, bufItem->lengthB, reverb);
}
return cmd;
}
Acmd* AudioSynth_DoOneAudioUpdate(s16* aiBuf, s32 aiBufLen, Acmd* cmd, s32 updateIndex) {
u8 noteIndices[0x5C];
s16 count;
s16 reverbIndex;
SynthesisReverb* reverb;
s32 useReverb;
s32 t;
s32 i;
NoteSubEu* noteSubEu;
NoteSubEu* noteSubEu2;
s32 unk14;
2022-10-07 17:43:17 +00:00
t = gAudioCtx.numNotes * updateIndex;
count = 0;
2022-10-07 17:43:17 +00:00
if (gAudioCtx.numSynthesisReverbs == 0) {
for (i = 0; i < gAudioCtx.numNotes; i++) {
if (gAudioCtx.noteSubsEu[t + i].bitField0.enabled) {
noteIndices[count++] = i;
}
}
} else {
2022-10-07 17:43:17 +00:00
for (reverbIndex = 0; reverbIndex < gAudioCtx.numSynthesisReverbs; reverbIndex++) {
for (i = 0; i < gAudioCtx.numNotes; i++) {
noteSubEu = &gAudioCtx.noteSubsEu[t + i];
if (noteSubEu->bitField0.enabled && noteSubEu->bitField1.reverbIndex == reverbIndex) {
noteIndices[count++] = i;
}
}
}
2022-10-07 17:43:17 +00:00
for (i = 0; i < gAudioCtx.numNotes; i++) {
noteSubEu = &gAudioCtx.noteSubsEu[t + i];
if (noteSubEu->bitField0.enabled && noteSubEu->bitField1.reverbIndex >= gAudioCtx.numSynthesisReverbs) {
noteIndices[count++] = i;
}
}
}
aClearBuffer(cmd++, DMEM_LEFT_CH, DMEM_2CH_SIZE);
i = 0;
2022-10-07 17:43:17 +00:00
for (reverbIndex = 0; reverbIndex < gAudioCtx.numSynthesisReverbs; reverbIndex++) {
reverb = &gAudioCtx.synthesisReverbs[reverbIndex];
useReverb = reverb->useReverb;
if (useReverb) {
// Loads reverb samples from RDRAM (ringBuffer) into DMEM (DMEM_WET_LEFT_CH)
cmd = AudioSynth_LoadReverbSamples(cmd, aiBufLen, reverb, updateIndex);
// Mixes reverb sample into the main dry channel
// reverb->volume is always set to 0x7FFF (audio spec), and DMEM_LEFT_CH is cleared before the loop.
// So for the first iteration, this is essentially a DMEMmove from DMEM_WET_LEFT_CH to DMEM_LEFT_CH
aMix(cmd++, DMEM_2CH_SIZE >> 4, reverb->volume, DMEM_WET_LEFT_CH, DMEM_LEFT_CH);
unk14 = reverb->unk_14;
if (unk14) {
aDMEMMove(cmd++, DMEM_WET_LEFT_CH, DMEM_WET_TEMP, DMEM_2CH_SIZE);
}
// Decays reverb over time. The (+ 0x8000) here is -100%
aMix(cmd++, DMEM_2CH_SIZE >> 4, reverb->decayRatio + 0x8000, DMEM_WET_LEFT_CH, DMEM_WET_LEFT_CH);
// Leak reverb between the left and right channels
if (reverb->leakRtl != 0 || reverb->leakLtr != 0) {
cmd = AudioSynth_LeakReverb(cmd, reverb);
}
if (unk14) {
// Saves the wet channel sample from DMEM (DMEM_WET_LEFT_CH) into RDRAM (ringBuffer) for future use
cmd = AudioSynth_SaveReverbSamples(cmd, reverb, updateIndex);
if (reverb->unk_05 != -1) {
cmd = AudioSynth_MaybeMixRingBuffer1(cmd, reverb, updateIndex);
}
cmd = AudioSynth_MaybeLoadRingBuffer2(cmd, aiBufLen, reverb, updateIndex);
aMix(cmd++, DMEM_2CH_SIZE >> 4, reverb->unk_16, DMEM_WET_TEMP, DMEM_WET_LEFT_CH);
}
}
while (i < count) {
2022-10-07 17:43:17 +00:00
noteSubEu2 = &gAudioCtx.noteSubsEu[noteIndices[i] + t];
if (noteSubEu2->bitField1.reverbIndex == reverbIndex) {
2022-10-07 17:43:17 +00:00
cmd =
AudioSynth_ProcessNote(noteIndices[i], noteSubEu2, &gAudioCtx.notes[noteIndices[i]].synthesisState,
aiBuf, aiBufLen, cmd, updateIndex);
} else {
break;
}
i++;
}
if (useReverb) {
if (reverb->filterLeft != NULL || reverb->filterRight != NULL) {
cmd = AudioSynth_FilterReverb(cmd, aiBufLen * SAMPLE_SIZE, reverb);
}
// Saves the wet channel sample from DMEM (DMEM_WET_LEFT_CH) into RDRAM (ringBuffer) for future use
if (unk14) {
cmd = AudioSynth_SaveRingBuffer2(cmd, reverb, updateIndex);
} else {
cmd = AudioSynth_SaveReverbSamples(cmd, reverb, updateIndex);
if (reverb->unk_05 != -1) {
cmd = AudioSynth_MaybeMixRingBuffer1(cmd, reverb, updateIndex);
}
}
}
}
while (i < count) {
2022-10-07 17:43:17 +00:00
cmd =
AudioSynth_ProcessNote(noteIndices[i], &gAudioCtx.noteSubsEu[t + noteIndices[i]],
&gAudioCtx.notes[noteIndices[i]].synthesisState, aiBuf, aiBufLen, cmd, updateIndex);
i++;
}
aInterleave(cmd++, DMEM_TEMP, DMEM_LEFT_CH, DMEM_RIGHT_CH, 2 * aiBufLen);
aSaveBuffer(cmd++, DMEM_TEMP, aiBuf, 2 * (aiBufLen * (s32)SAMPLE_SIZE));
return cmd;
}
Acmd* AudioSynth_ProcessNote(s32 noteIndex, NoteSubEu* noteSubEu, NoteSynthesisState* synthState, s16* aiBuf,
s32 aiBufLen, Acmd* cmd, s32 updateIndex) {
s32 pad1[3];
Sample* sample;
AdpcmLoop* loopInfo;
s32 nSamplesUntilLoopEnd;
s32 nSamplesInThisIteration;
s32 noteFinished;
s32 restart;
s32 flags;
u16 resamplingRateFixedPoint;
s32 nSamplesInFirstFrame;
s32 nTrailingSamplesToIgnore;
s32 gain;
s32 frameIndex;
s32 skipBytes;
s32 temp_v1_6;
void* buf;
s32 nSamplesToDecode;
u32 sampleAddr;
u32 samplesLenFixedPoint;
s32 samplesLenAdjusted; // seems to be used as both bytes and numSamples?
s32 nSamplesProcessed;
s32 loopEndPos;
s32 nSamplesToProcess;
s32 phi_s4;
s32 nFirstFrameSamplesToIgnore;
s32 pad2[7];
s32 frameSize;
s32 nFramesToDecode;
s32 skipInitialSamples;
s32 sampleDataStart;
u8* sampleData;
s32 nParts;
s32 curPart;
s32 sampleDataStartPad;
s32 haasEffectDelaySide;
s32 resampledTempLen;
u16 sampleDmemBeforeResampling;
s32 sampleDataOffset;
s32 thing;
s32 s5;
Note* note;
u32 numSamplesToLoad;
u16 unk7;
u16 unkE;
s16* filter;
s32 bookOffset;
s32 finished;
s32 aligned;
s16 addr;
u16 unused;
bookOffset = noteSubEu->bitField1.bookOffset;
finished = noteSubEu->bitField0.finished;
2022-10-07 17:43:17 +00:00
note = &gAudioCtx.notes[noteIndex];
flags = A_CONTINUE;
if (noteSubEu->bitField0.needsInit == true) {
flags = A_INIT;
synthState->restart = 0;
synthState->samplePosInt = note->startSamplePos;
synthState->samplePosFrac = 0;
synthState->curVolLeft = 0;
synthState->curVolRight = 0;
synthState->prevHaasEffectLeftDelaySize = 0;
synthState->prevHaasEffectRightDelaySize = 0;
synthState->reverbVol = noteSubEu->reverbVol;
synthState->numParts = 0;
synthState->unk_1A = 1;
note->noteSubEu.bitField0.finished = false;
finished = false;
}
resamplingRateFixedPoint = noteSubEu->resamplingRateFixedPoint;
nParts = noteSubEu->bitField1.hasTwoParts + 1;
samplesLenFixedPoint = (resamplingRateFixedPoint * aiBufLen * 2) + synthState->samplePosFrac;
numSamplesToLoad = samplesLenFixedPoint >> 16;
synthState->samplePosFrac = samplesLenFixedPoint & 0xFFFF;
// Partially-optimized out no-op ifs required for matching. SM64 decomp
// makes it clear that this is how it should look.
if (synthState->numParts == 1 && nParts == 2) {
} else if (synthState->numParts == 2 && nParts == 1) {
} else {
}
synthState->numParts = nParts;
if (noteSubEu->bitField1.isSyntheticWave) {
cmd = AudioSynth_LoadWaveSamples(cmd, noteSubEu, synthState, numSamplesToLoad);
sampleDmemBeforeResampling = DMEM_UNCOMPRESSED_NOTE + (synthState->samplePosInt * (s32)SAMPLE_SIZE);
synthState->samplePosInt += numSamplesToLoad;
} else {
sample = noteSubEu->tunedSample->sample;
loopInfo = sample->loop;
loopEndPos = loopInfo->end;
sampleAddr = (u32)sample->sampleAddr;
resampledTempLen = 0;
for (curPart = 0; curPart < nParts; curPart++) {
nSamplesProcessed = 0;
s5 = 0;
if (nParts == 1) {
samplesLenAdjusted = numSamplesToLoad;
} else if (numSamplesToLoad & 1) {
samplesLenAdjusted = (numSamplesToLoad & ~1) + (curPart * 2);
} else {
samplesLenAdjusted = numSamplesToLoad;
}
if (sample->codec == CODEC_ADPCM || sample->codec == CODEC_SMALL_ADPCM) {
2022-10-07 17:43:17 +00:00
if (gAudioCtx.curLoadedBook != sample->book->book) {
u32 nEntries;
switch (bookOffset) {
case 1:
2022-10-07 17:43:17 +00:00
gAudioCtx.curLoadedBook = &D_8012FBA8[1];
break;
case 2:
case 3:
default:
2022-10-07 17:43:17 +00:00
gAudioCtx.curLoadedBook = sample->book->book;
break;
}
if (1) {}
if (1) {}
if (1) {}
nEntries = SAMPLES_PER_FRAME * sample->book->order * sample->book->numPredictors;
2022-10-07 17:43:17 +00:00
aLoadADPCM(cmd++, nEntries, gAudioCtx.curLoadedBook);
}
}
while (nSamplesProcessed != samplesLenAdjusted) {
noteFinished = false;
restart = false;
phi_s4 = 0;
nFirstFrameSamplesToIgnore = synthState->samplePosInt & 0xF;
nSamplesUntilLoopEnd = loopEndPos - synthState->samplePosInt;
nSamplesToProcess = samplesLenAdjusted - nSamplesProcessed;
if (nFirstFrameSamplesToIgnore == 0 && !synthState->restart) {
nFirstFrameSamplesToIgnore = SAMPLES_PER_FRAME;
}
nSamplesInFirstFrame = SAMPLES_PER_FRAME - nFirstFrameSamplesToIgnore;
if (nSamplesToProcess < nSamplesUntilLoopEnd) {
nFramesToDecode =
(s32)(nSamplesToProcess - nSamplesInFirstFrame + SAMPLES_PER_FRAME - 1) / SAMPLES_PER_FRAME;
nSamplesToDecode = nFramesToDecode * SAMPLES_PER_FRAME;
nTrailingSamplesToIgnore = nSamplesInFirstFrame + nSamplesToDecode - nSamplesToProcess;
} else {
nSamplesToDecode = nSamplesUntilLoopEnd - nSamplesInFirstFrame;
nTrailingSamplesToIgnore = 0;
if (nSamplesToDecode <= 0) {
nSamplesToDecode = 0;
nSamplesInFirstFrame = nSamplesUntilLoopEnd;
}
nFramesToDecode = (nSamplesToDecode + SAMPLES_PER_FRAME - 1) / SAMPLES_PER_FRAME;
if (loopInfo->count != 0) {
// Loop around and restart
restart = true;
} else {
noteFinished = true;
}
}
switch (sample->codec) {
case CODEC_ADPCM:
// 16 2-byte samples (32 bytes) compressed into 4-bit samples (8 bytes) + 1 header byte
frameSize = 9;
skipInitialSamples = SAMPLES_PER_FRAME;
sampleDataStart = 0;
break;
case CODEC_SMALL_ADPCM:
// 16 2-byte samples (32 bytes) compressed into 2-bit samples (4 bytes) + 1 header byte
frameSize = 5;
skipInitialSamples = SAMPLES_PER_FRAME;
sampleDataStart = 0;
break;
case CODEC_S8:
// 16 2-byte samples (32 bytes) compressed into 8-bit samples (16 bytes)
frameSize = 16;
skipInitialSamples = SAMPLES_PER_FRAME;
sampleDataStart = 0;
break;
case CODEC_S16_INMEMORY:
AudioSynth_ClearBuffer(cmd++, DMEM_UNCOMPRESSED_NOTE,
(samplesLenAdjusted + SAMPLES_PER_FRAME) * SAMPLE_SIZE);
flags = A_CONTINUE;
skipBytes = 0;
nSamplesProcessed = samplesLenAdjusted;
s5 = samplesLenAdjusted;
goto skip;
case CODEC_S16:
AudioSynth_ClearBuffer(cmd++, DMEM_UNCOMPRESSED_NOTE,
(samplesLenAdjusted + SAMPLES_PER_FRAME) * SAMPLE_SIZE);
flags = A_CONTINUE;
skipBytes = 0;
nSamplesProcessed = samplesLenAdjusted;
s5 = samplesLenAdjusted;
goto skip;
case CODEC_REVERB:
break;
}
if (nFramesToDecode != 0) {
frameIndex = (synthState->samplePosInt + skipInitialSamples - nFirstFrameSamplesToIgnore) /
SAMPLES_PER_FRAME;
sampleDataOffset = frameIndex * frameSize;
if (sample->medium == MEDIUM_RAM) {
sampleData = (u8*)(sampleDataStart + sampleDataOffset + sampleAddr);
} else if (sample->medium == MEDIUM_UNK) {
return cmd;
} else {
sampleData = AudioLoad_DmaSampleData(sampleDataStart + sampleDataOffset + sampleAddr,
ALIGN16((nFramesToDecode * frameSize) + SAMPLES_PER_FRAME),
flags, &synthState->sampleDmaIndex, sample->medium);
}
if (sampleData == NULL) {
return cmd;
}
sampleDataStartPad = (u32)sampleData & 0xF;
aligned = ALIGN16((nFramesToDecode * frameSize) + SAMPLES_PER_FRAME);
addr = DMEM_COMPRESSED_ADPCM_DATA - aligned;
aLoadBuffer(cmd++, sampleData - sampleDataStartPad, addr, aligned);
} else {
nSamplesToDecode = 0;
sampleDataStartPad = 0;
}
if (synthState->restart) {
aSetLoop(cmd++, sample->loop->predictorState);
flags = A_LOOP;
synthState->restart = false;
}
nSamplesInThisIteration = nSamplesToDecode + nSamplesInFirstFrame - nTrailingSamplesToIgnore;
if (nSamplesProcessed == 0) {
if (1) {}
skipBytes = nFirstFrameSamplesToIgnore * SAMPLE_SIZE;
} else {
phi_s4 = ALIGN16(s5 + 8 * SAMPLE_SIZE);
}
switch (sample->codec) {
case CODEC_ADPCM:
aligned = ALIGN16((nFramesToDecode * frameSize) + SAMPLES_PER_FRAME);
addr = DMEM_COMPRESSED_ADPCM_DATA - aligned;
aSetBuffer(cmd++, 0, addr + sampleDataStartPad, DMEM_UNCOMPRESSED_NOTE + phi_s4,
nSamplesToDecode * SAMPLE_SIZE);
aADPCMdec(cmd++, flags, synthState->synthesisBuffers->adpcmdecState);
break;
case CODEC_SMALL_ADPCM:
aligned = ALIGN16((nFramesToDecode * frameSize) + SAMPLES_PER_FRAME);
addr = DMEM_COMPRESSED_ADPCM_DATA - aligned;
aSetBuffer(cmd++, 0, addr + sampleDataStartPad, DMEM_UNCOMPRESSED_NOTE + phi_s4,
nSamplesToDecode * SAMPLE_SIZE);
aADPCMdec(cmd++, flags | 4, synthState->synthesisBuffers->adpcmdecState);
break;
case CODEC_S8:
aligned = ALIGN16((nFramesToDecode * frameSize) + SAMPLES_PER_FRAME);
addr = DMEM_COMPRESSED_ADPCM_DATA - aligned;
AudioSynth_SetBuffer(cmd++, 0, addr + sampleDataStartPad, DMEM_UNCOMPRESSED_NOTE + phi_s4,
nSamplesToDecode * SAMPLE_SIZE);
AudioSynth_S8Dec(cmd++, flags, synthState->synthesisBuffers->adpcmdecState);
break;
}
if (nSamplesProcessed != 0) {
aDMEMMove(cmd++, DMEM_UNCOMPRESSED_NOTE + phi_s4 + (nFirstFrameSamplesToIgnore * SAMPLE_SIZE),
DMEM_UNCOMPRESSED_NOTE + s5, nSamplesInThisIteration * SAMPLE_SIZE);
}
nSamplesProcessed += nSamplesInThisIteration;
switch (flags) {
case A_INIT:
skipBytes = SAMPLES_PER_FRAME * SAMPLE_SIZE;
s5 = (nSamplesToDecode + SAMPLES_PER_FRAME) * SAMPLE_SIZE;
break;
case A_LOOP:
s5 = nSamplesInThisIteration * SAMPLE_SIZE + s5;
break;
default:
if (s5 != 0) {
s5 = nSamplesInThisIteration * SAMPLE_SIZE + s5;
} else {
s5 = (nFirstFrameSamplesToIgnore + nSamplesInThisIteration) * SAMPLE_SIZE;
}
break;
}
flags = A_CONTINUE;
skip:
if (noteFinished) {
AudioSynth_ClearBuffer(cmd++, DMEM_UNCOMPRESSED_NOTE + s5,
(samplesLenAdjusted - nSamplesProcessed) * SAMPLE_SIZE);
finished = true;
note->noteSubEu.bitField0.finished = true;
func_800DB2C0(updateIndex, noteIndex);
break;
} else {
if (restart) {
synthState->restart = true;
synthState->samplePosInt = loopInfo->start;
} else {
synthState->samplePosInt += nSamplesToProcess;
}
}
}
switch (nParts) {
case 1:
sampleDmemBeforeResampling = DMEM_UNCOMPRESSED_NOTE + skipBytes;
break;
case 2:
switch (curPart) {
case 0:
AudioSynth_InterL(cmd++, DMEM_UNCOMPRESSED_NOTE + skipBytes,
DMEM_TEMP + (SAMPLES_PER_FRAME * SAMPLE_SIZE),
ALIGN8(samplesLenAdjusted / 2));
resampledTempLen = samplesLenAdjusted;
sampleDmemBeforeResampling = DMEM_TEMP + (SAMPLES_PER_FRAME * SAMPLE_SIZE);
if (finished) {
AudioSynth_ClearBuffer(cmd++, sampleDmemBeforeResampling + resampledTempLen,
samplesLenAdjusted + SAMPLES_PER_FRAME);
}
break;
case 1:
AudioSynth_InterL(cmd++, DMEM_UNCOMPRESSED_NOTE + skipBytes,
DMEM_TEMP + (SAMPLES_PER_FRAME * SAMPLE_SIZE) + resampledTempLen,
ALIGN8(samplesLenAdjusted / 2));
break;
}
}
if (finished) {
break;
}
}
}
flags = A_CONTINUE;
if (noteSubEu->bitField0.needsInit == true) {
noteSubEu->bitField0.needsInit = false;
flags = A_INIT;
}
cmd = AudioSynth_FinalResample(cmd, synthState, aiBufLen * (s32)SAMPLE_SIZE, resamplingRateFixedPoint,
sampleDmemBeforeResampling, flags);
if (bookOffset == 3) {
AudioSynth_UnkCmd19(cmd++, DMEM_TEMP, DMEM_TEMP, aiBufLen * SAMPLE_SIZE, 0);
}
if (bookOffset == 2) {
AudioSynth_UnkCmd3(cmd++, DMEM_TEMP, DMEM_TEMP, aiBufLen * SAMPLE_SIZE);
}
gain = noteSubEu->gain;
if (gain != 0) {
// A gain of 0x10 (a UQ4.4 number) is equivalent to 1.0 and represents no volume change
if (gain < 0x10) {
gain = 0x10;
}
AudioSynth_HiLoGain(cmd++, gain, DMEM_TEMP, 0, (aiBufLen + SAMPLES_PER_FRAME) * SAMPLE_SIZE);
}
filter = noteSubEu->filter;
if (filter != NULL) {
AudioSynth_LoadFilterSize(cmd++, aiBufLen * SAMPLE_SIZE, filter);
AudioSynth_LoadFilterBuffer(cmd++, flags, DMEM_TEMP, synthState->synthesisBuffers->mixEnvelopeState);
}
unk7 = noteSubEu->unk_07;
unkE = noteSubEu->unk_0E;
buf = synthState->synthesisBuffers->unkState;
if (unk7 != 0 && noteSubEu->unk_0E != 0) {
AudioSynth_DMemMove(cmd++, DMEM_TEMP, DMEM_SCRATCH2, aiBufLen * SAMPLE_SIZE);
thing = DMEM_SCRATCH2 - unk7;
if (synthState->unk_1A != 0) {
AudioSynth_ClearBuffer(cmd++, thing, unk7);
synthState->unk_1A = 0;
} else {
AudioSynth_LoadBuffer(cmd++, thing, unk7, buf);
}
AudioSynth_SaveBuffer(cmd++, DMEM_TEMP + (aiBufLen * SAMPLE_SIZE) - unk7, unk7, buf);
AudioSynth_Mix(cmd++, (aiBufLen * (s32)SAMPLE_SIZE) >> 4, unkE, DMEM_SCRATCH2, thing);
AudioSynth_DMemMove(cmd++, thing, DMEM_TEMP, aiBufLen * SAMPLE_SIZE);
} else {
synthState->unk_1A = 1;
}
if ((noteSubEu->haasEffectLeftDelaySize != 0) || (synthState->prevHaasEffectLeftDelaySize != 0)) {
haasEffectDelaySide = HAAS_EFFECT_DELAY_LEFT;
} else if ((noteSubEu->haasEffectRightDelaySize != 0) || (synthState->prevHaasEffectRightDelaySize != 0)) {
haasEffectDelaySide = HAAS_EFFECT_DELAY_RIGHT;
} else {
haasEffectDelaySide = HAAS_EFFECT_DELAY_NONE;
}
cmd = AudioSynth_ProcessEnvelope(cmd, noteSubEu, synthState, aiBufLen, DMEM_TEMP, haasEffectDelaySide, flags);
if (noteSubEu->bitField1.useHaasEffect) {
if (!(flags & A_INIT)) {
flags = A_CONTINUE;
}
cmd = AudioSynth_ApplyHaasEffect(cmd, noteSubEu, synthState, aiBufLen * (s32)SAMPLE_SIZE, flags,
haasEffectDelaySide);
}
return cmd;
}
Acmd* AudioSynth_FinalResample(Acmd* cmd, NoteSynthesisState* synthState, s32 size, u16 pitch, u16 inpDmem,
s32 resampleFlags) {
if (pitch == 0) {
AudioSynth_ClearBuffer(cmd++, DMEM_TEMP, size);
} else {
aSetBuffer(cmd++, 0, inpDmem, DMEM_TEMP, size);
aResample(cmd++, resampleFlags, pitch, synthState->synthesisBuffers->finalResampleState);
}
return cmd;
}
Acmd* AudioSynth_ProcessEnvelope(Acmd* cmd, NoteSubEu* noteSubEu, NoteSynthesisState* synthState, s32 aiBufLen,
u16 dmemSrc, s32 haasEffectDelaySide, s32 flags) {
u32 dmemDests;
u16 curVolLeft;
u16 targetVolLeft;
s32 phi_t1;
s16 reverbVol;
u16 curVolRight;
s16 rampLeft;
s16 rampRight;
s16 rampReverb;
s16 sourceReverbVol;
u16 targetVolRight;
s32 pad;
curVolLeft = synthState->curVolLeft;
targetVolLeft = noteSubEu->targetVolLeft;
targetVolLeft <<= 4;
reverbVol = noteSubEu->reverbVol;
curVolRight = synthState->curVolRight;
targetVolRight = noteSubEu->targetVolRight;
targetVolRight <<= 4;
if (targetVolLeft != curVolLeft) {
rampLeft = (targetVolLeft - curVolLeft) / (aiBufLen >> 3);
} else {
rampLeft = 0;
}
if (targetVolRight != curVolRight) {
rampRight = (targetVolRight - curVolRight) / (aiBufLen >> 3);
} else {
rampRight = 0;
}
sourceReverbVol = synthState->reverbVol;
phi_t1 = sourceReverbVol & 0x7F;
if (sourceReverbVol != reverbVol) {
rampReverb = (((reverbVol & 0x7F) - phi_t1) << 9) / (aiBufLen >> 3);
synthState->reverbVol = reverbVol;
} else {
rampReverb = 0;
}
synthState->curVolLeft = curVolLeft + (rampLeft * (aiBufLen >> 3));
synthState->curVolRight = curVolRight + (rampRight * (aiBufLen >> 3));
if (noteSubEu->bitField1.useHaasEffect) {
AudioSynth_ClearBuffer(cmd++, DMEM_HAAS_TEMP, DMEM_1CH_SIZE);
AudioSynth_EnvSetup1(cmd++, phi_t1 * 2, rampReverb, rampLeft, rampRight);
AudioSynth_EnvSetup2(cmd++, curVolLeft, curVolRight);
switch (haasEffectDelaySide) {
case HAAS_EFFECT_DELAY_LEFT:
// Store the left dry channel in a temp space to be delayed to produce the haas effect
dmemDests = sEnvMixerLeftHaasDmemDests;
break;
case HAAS_EFFECT_DELAY_RIGHT:
// Store the right dry channel in a temp space to be delayed to produce the haas effect
dmemDests = sEnvMixerRightHaasDmemDests;
break;
default: // HAAS_EFFECT_DELAY_NONE
dmemDests = sEnvMixerDefaultDmemDests;
break;
}
} else {
aEnvSetup1(cmd++, phi_t1 * 2, rampReverb, rampLeft, rampRight);
aEnvSetup2(cmd++, curVolLeft, curVolRight);
dmemDests = sEnvMixerDefaultDmemDests;
}
aEnvMixer(cmd++, dmemSrc, aiBufLen, (sourceReverbVol & 0x80) >> 7, noteSubEu->bitField0.stereoHeadsetEffects,
noteSubEu->bitField0.usesHeadsetPanEffects, noteSubEu->bitField0.stereoStrongRight,
noteSubEu->bitField0.stereoStrongLeft, dmemDests, sEnvMixerOp);
return cmd;
}
Acmd* AudioSynth_LoadWaveSamples(Acmd* cmd, NoteSubEu* noteSubEu, NoteSynthesisState* synthState,
s32 numSamplesToLoad) {
s32 numSamplesAvail;
s32 harmonicIndexCurAndPrev = noteSubEu->harmonicIndexCurAndPrev;
s32 samplePosInt = synthState->samplePosInt;
s32 numDuplicates;
if (noteSubEu->bitField1.bookOffset != 0) {
// Move the noise wave (that reads compiled assembly as samples) from ram to dmem
AudioSynth_LoadBuffer(cmd++, DMEM_UNCOMPRESSED_NOTE, ALIGN16(numSamplesToLoad * SAMPLE_SIZE), gWaveSamples[8]);
// Offset the address for the samples read by gWaveSamples[8] to the next set of samples
gWaveSamples[8] += numSamplesToLoad * SAMPLE_SIZE;
return cmd;
} else {
// Move the synthetic wave from ram to dmem
aLoadBuffer(cmd++, noteSubEu->waveSampleAddr, DMEM_UNCOMPRESSED_NOTE, WAVE_SAMPLE_COUNT * SAMPLE_SIZE);
// If the harmonic changes, map the offset in the wave from one harmonic to another for continuity
if (harmonicIndexCurAndPrev != 0) {
samplePosInt = samplePosInt * sNumSamplesPerWavePeriod[harmonicIndexCurAndPrev >> 2] /
sNumSamplesPerWavePeriod[harmonicIndexCurAndPrev & 3];
}
// Offset in the WAVE_SAMPLE_COUNT samples of gWaveSamples to start processing the wave for continuity
samplePosInt = (u32)samplePosInt % WAVE_SAMPLE_COUNT;
// Number of samples in the initial WAVE_SAMPLE_COUNT samples available to be used to process
numSamplesAvail = WAVE_SAMPLE_COUNT - samplePosInt;
// Require duplicates if there are more samples to load than available
if (numSamplesToLoad > numSamplesAvail) {
// Duplicate (copy) the WAVE_SAMPLE_COUNT samples as many times as needed to reach numSamplesToLoad.
// (numSamplesToLoad - numSamplesAvail) is the number of samples missing.
// Divide by WAVE_SAMPLE_COUNT, rounding up, to get the amount of duplicates
numDuplicates = ((numSamplesToLoad - numSamplesAvail + WAVE_SAMPLE_COUNT - 1) / WAVE_SAMPLE_COUNT);
if (numDuplicates != 0) {
aDuplicate(cmd++, numDuplicates, DMEM_UNCOMPRESSED_NOTE,
DMEM_UNCOMPRESSED_NOTE + (WAVE_SAMPLE_COUNT * SAMPLE_SIZE));
}
}
synthState->samplePosInt = samplePosInt;
}
return cmd;
}
/**
* The Haas Effect gives directionality to sound by applying a small (< 35ms) delay to either the left or right channel.
* The delay is small enough that the sound is still perceived as one sound, but the channel that is not delayed will
* reach our ear first and give a sense of directionality. The sound is directed towards the opposite side of the delay.
*/
Acmd* AudioSynth_ApplyHaasEffect(Acmd* cmd, NoteSubEu* noteSubEu, NoteSynthesisState* synthState, s32 size, s32 flags,
s32 haasEffectDelaySide) {
u16 dmemDest;
u16 pitch;
u8 prevHaasEffectDelaySize;
u8 haasEffectDelaySize;
switch (haasEffectDelaySide) {
case HAAS_EFFECT_DELAY_LEFT:
// Delay the sample on the left channel
// This allows the right channel to be heard first
dmemDest = DMEM_LEFT_CH;
haasEffectDelaySize = noteSubEu->haasEffectLeftDelaySize;
prevHaasEffectDelaySize = synthState->prevHaasEffectLeftDelaySize;
synthState->prevHaasEffectRightDelaySize = 0;
synthState->prevHaasEffectLeftDelaySize = haasEffectDelaySize;
break;
case HAAS_EFFECT_DELAY_RIGHT:
// Delay the sample on the right channel
// This allows the left channel to be heard first
dmemDest = DMEM_RIGHT_CH;
haasEffectDelaySize = noteSubEu->haasEffectRightDelaySize;
prevHaasEffectDelaySize = synthState->prevHaasEffectRightDelaySize;
synthState->prevHaasEffectRightDelaySize = haasEffectDelaySize;
synthState->prevHaasEffectLeftDelaySize = 0;
break;
default: // HAAS_EFFECT_DELAY_NONE
return cmd;
}
if (flags != A_INIT) {
// Slightly adjust the sample rate in order to fit a change in sample delay
if (haasEffectDelaySize != prevHaasEffectDelaySize) {
pitch = (((size << 0xF) / 2) - 1) / ((size + haasEffectDelaySize - prevHaasEffectDelaySize - 2) / 2);
aSetBuffer(cmd++, 0, DMEM_HAAS_TEMP, DMEM_TEMP, size + haasEffectDelaySize - prevHaasEffectDelaySize);
aResampleZoh(cmd++, pitch, 0);
} else {
aDMEMMove(cmd++, DMEM_HAAS_TEMP, DMEM_TEMP, size);
}
if (prevHaasEffectDelaySize != 0) {
aLoadBuffer(cmd++, synthState->synthesisBuffers->haasEffectDelayState, DMEM_HAAS_TEMP,
ALIGN16(prevHaasEffectDelaySize));
aDMEMMove(cmd++, DMEM_TEMP, DMEM_HAAS_TEMP + prevHaasEffectDelaySize,
size + haasEffectDelaySize - prevHaasEffectDelaySize);
} else {
aDMEMMove(cmd++, DMEM_TEMP, DMEM_HAAS_TEMP, size + haasEffectDelaySize);
}
} else {
// Just apply a delay directly
aDMEMMove(cmd++, DMEM_HAAS_TEMP, DMEM_TEMP, size);
aClearBuffer(cmd++, DMEM_HAAS_TEMP, haasEffectDelaySize);
aDMEMMove(cmd++, DMEM_TEMP, DMEM_HAAS_TEMP + haasEffectDelaySize, size);
}
if (haasEffectDelaySize) { // != 0
// Save excessive samples for next iteration
aSaveBuffer(cmd++, DMEM_HAAS_TEMP + size, synthState->synthesisBuffers->haasEffectDelayState,
ALIGN16(haasEffectDelaySize));
}
aAddMixer(cmd++, ALIGN64(size), DMEM_HAAS_TEMP, dmemDest, 0x7FFF);
return cmd;
}