diff options
Diffstat (limited to 'src/code')
| -rw-r--r-- | src/code/audio/audio_data.c | 95 | ||||
| -rw-r--r-- | src/code/audio/audio_heap.c | 268 | ||||
| -rw-r--r-- | src/code/audio/audio_init_params.c | 182 | ||||
| -rw-r--r-- | src/code/audio/audio_load.c | 6 | ||||
| -rw-r--r-- | src/code/audio/audio_playback.c | 202 | ||||
| -rw-r--r-- | src/code/audio/audio_seqplayer.c | 49 | ||||
| -rw-r--r-- | src/code/audio/audio_synthesis.c | 1807 | ||||
| -rw-r--r-- | src/code/audio/code_8019AF00.c | 2 |
8 files changed, 2223 insertions, 388 deletions
diff --git a/src/code/audio/audio_data.c b/src/code/audio/audio_data.c index 7238a4bfa..eb931262b 100644 --- a/src/code/audio/audio_data.c +++ b/src/code/audio/audio_data.c @@ -857,22 +857,99 @@ EnvelopePoint gDefaultEnvelope[] = { { ADSR_DISABLE, 0 }, }; -NoteSubEu gZeroNoteSub = { 0 }; - -NoteSubEu gDefaultNoteSub = { - { 1, 1, 0, 0, 0, 0, 0, 0 }, { 0 }, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +NoteSampleState gZeroedSampleState = { 0 }; + +NoteSampleState gDefaultSampleState = { + { true, true, false, false, false, false, false, false }, + { 0 }, + 0, // gain + 0, // haasEffectLeftDelaySize + 0, // haasEffectRightDelaySize + 0, // targetReverbVol + 0, // harmonicIndexCurAndPrev + 0, // combFilterSize + 0, // targetVolLeft + 0, // targetVolRight + 0, // frequencyFixedPoint + 0, // combFilterGain + NULL, // tunedSample + NULL, // filter + 0, // unk_18 + 0, // surroundEffectIndex + 0, // unk_1A }; -u16 gHeadsetPanQuantization[64] = { - 60, 58, 56, 54, 52, 50, 48, 46, 44, 42, 40, 38, 36, 34, 32, 30, 28, 26, 24, 22, 20, 18, - 16, 14, 12, 10, 8, 6, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +u16 gHaasEffectDelaySize[64] = { + 30 * SAMPLE_SIZE, + 29 * SAMPLE_SIZE, + 28 * SAMPLE_SIZE, + 27 * SAMPLE_SIZE, + 26 * SAMPLE_SIZE, + 25 * SAMPLE_SIZE, + 24 * SAMPLE_SIZE, + 23 * SAMPLE_SIZE, + 22 * SAMPLE_SIZE, + 21 * SAMPLE_SIZE, + 20 * SAMPLE_SIZE, + 19 * SAMPLE_SIZE, + 18 * SAMPLE_SIZE, + 17 * SAMPLE_SIZE, + 16 * SAMPLE_SIZE, + 15 * SAMPLE_SIZE, + 14 * SAMPLE_SIZE, + 13 * SAMPLE_SIZE, + 12 * SAMPLE_SIZE, + 11 * SAMPLE_SIZE, + 10 * SAMPLE_SIZE, + 9 * SAMPLE_SIZE, + 8 * SAMPLE_SIZE, + 7 * SAMPLE_SIZE, + 6 * SAMPLE_SIZE, + 5 * SAMPLE_SIZE, + 4 * SAMPLE_SIZE, + 3 * SAMPLE_SIZE, + 2 * SAMPLE_SIZE, + 1 * SAMPLE_SIZE, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, }; s32 D_801D58A4 = 0; // clang-format off -s16 D_801D58A8[] = { +s16 gInvalidAdpcmCodeBook[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, diff --git a/src/code/audio/audio_heap.c b/src/code/audio/audio_heap.c index 667cd1c40..8412220a3 100644 --- a/src/code/audio/audio_heap.c +++ b/src/code/audio/audio_heap.c @@ -10,7 +10,7 @@ void AudioHeap_DiscardSampleCaches(void); void AudioHeap_DiscardSampleBank(s32 sampleBankId); void AudioHeap_ApplySampleBankCacheInternal(s32 apply, s32 sampleBankId); void AudioHeap_DiscardSampleBanks(void); -void AudioHeap_InitReverb(s32 reverbIndex, ReverbSettings* settings, s32 flags); +void AudioHeap_InitReverb(s32 reverbIndex, ReverbSettings* settings, s32 isFirstInit); #define gTatumsPerBeat (gAudioTatumInit[1]) @@ -421,7 +421,7 @@ void* AudioHeap_AllocCached(s32 tableType, size_t size, s32 cache, s32 id) { if (loadStatusEntry0 == LOAD_STATUS_MAYBE_DISCARDABLE) { for (i = 0; i < gAudioContext.numNotes; i++) { if ((gAudioContext.notes[i].playbackState.fontId == temporaryCache->entries[0].id) && - gAudioContext.notes[i].noteSubEu.bitField0.enabled) { + gAudioContext.notes[i].sampleState.bitField0.enabled) { break; } } @@ -435,7 +435,7 @@ void* AudioHeap_AllocCached(s32 tableType, size_t size, s32 cache, s32 id) { if (loadStatusEntry1 == LOAD_STATUS_MAYBE_DISCARDABLE) { for (i = 0; i < gAudioContext.numNotes; i++) { if ((gAudioContext.notes[i].playbackState.fontId == temporaryCache->entries[1].id) && - gAudioContext.notes[i].noteSubEu.bitField0.enabled) { + gAudioContext.notes[i].sampleState.bitField0.enabled) { break; } } @@ -491,7 +491,7 @@ void* AudioHeap_AllocCached(s32 tableType, size_t size, s32 cache, s32 id) { if (loadStatusEntry0 == LOAD_STATUS_COMPLETE) { for (i = 0; i < gAudioContext.numNotes; i++) { if ((gAudioContext.notes[i].playbackState.fontId == temporaryCache->entries[0].id) && - gAudioContext.notes[i].noteSubEu.bitField0.enabled) { + gAudioContext.notes[i].sampleState.bitField0.enabled) { break; } } @@ -504,7 +504,7 @@ void* AudioHeap_AllocCached(s32 tableType, size_t size, s32 cache, s32 id) { if (loadStatusEntry1 == LOAD_STATUS_COMPLETE) { for (i = 0; i < gAudioContext.numNotes; i++) { if ((gAudioContext.notes[i].playbackState.fontId == temporaryCache->entries[1].id) && - gAudioContext.notes[i].noteSubEu.bitField0.enabled) { + gAudioContext.notes[i].sampleState.bitField0.enabled) { break; } } @@ -814,7 +814,7 @@ void AudioHeap_UpdateReverb(SynthesisReverb* reverb) { void AudioHeap_UpdateReverbs(void) { s32 count; - s32 i; + s32 reverbIndex; s32 j; if (gAudioContext.audioBufferParameters.specUnk4 == 2) { @@ -823,9 +823,9 @@ void AudioHeap_UpdateReverbs(void) { count = 1; } - for (i = 0; i < gAudioContext.numSynthesisReverbs; i++) { + for (reverbIndex = 0; reverbIndex < gAudioContext.numSynthesisReverbs; reverbIndex++) { for (j = 0; j < count; j++) { - AudioHeap_UpdateReverb(&gAudioContext.synthesisReverbs[i]); + AudioHeap_UpdateReverb(&gAudioContext.synthesisReverbs[reverbIndex]); } } } @@ -837,7 +837,7 @@ void AudioHeap_ClearAiBuffers(void) { s32 curAiBufferIndex = gAudioContext.curAiBufferIndex; s32 i; - gAudioContext.aiBufNumSamples[curAiBufferIndex] = gAudioContext.audioBufferParameters.minAiBufNumSamples; + gAudioContext.aiBufNumSamples[curAiBufferIndex] = gAudioContext.audioBufferParameters.numSamplesPerFrameMin; for (i = 0; i < AIBUF_LEN; i++) { gAudioContext.aiBuffers[curAiBufferIndex][i] = 0; @@ -870,7 +870,7 @@ s32 AudioHeap_ResetStep(void) { AudioHeap_UpdateReverbs(); } else { for (i = 0; i < gAudioContext.numNotes; i++) { - if (gAudioContext.notes[i].noteSubEu.bitField0.enabled && + if (gAudioContext.notes[i].sampleState.bitField0.enabled && gAudioContext.notes[i].playbackState.adsr.action.s.state != ADSR_STATE_DISABLED) { gAudioContext.notes[i].playbackState.adsr.fadeOutVel = gAudioContext.audioBufferParameters.updatesPerFrameInv; @@ -907,7 +907,7 @@ s32 AudioHeap_ResetStep(void) { AudioHeap_Init(); gAudioContext.resetStatus = 0; for (i = 0; i < ARRAY_COUNT(gAudioContext.aiBufNumSamples); i++) { - gAudioContext.aiBufNumSamples[i] = gAudioContext.audioBufferParameters.maxAiBufNumSamples; + gAudioContext.aiBufNumSamples[i] = gAudioContext.audioBufferParameters.numSamplesPerFrameMax; for (j = 0; j < AIBUF_LEN; j++) { gAudioContext.aiBuffers[i][j] = 0; } @@ -930,8 +930,8 @@ void AudioHeap_Init(void) { size_t cachePoolSize; size_t miscPoolSize; u32 intMask; + s32 reverbIndex; s32 i; - s32 j; s32 pad2; AudioSpec* spec = &gAudioSpecs[gAudioContext.audioResetSpecIdToLoad]; // Audio Specifications @@ -942,19 +942,22 @@ void AudioHeap_Init(void) { gAudioContext.audioBufferParameters.aiSamplingFreq = osAiSetFrequency(gAudioContext.audioBufferParameters.samplingFreq); - gAudioContext.audioBufferParameters.samplesPerFrameTarget = + gAudioContext.audioBufferParameters.numSamplesPerFrameTarget = ALIGN16(gAudioContext.audioBufferParameters.samplingFreq / gAudioContext.refreshRate); - gAudioContext.audioBufferParameters.minAiBufNumSamples = - gAudioContext.audioBufferParameters.samplesPerFrameTarget - 0x10; - gAudioContext.audioBufferParameters.maxAiBufNumSamples = - gAudioContext.audioBufferParameters.samplesPerFrameTarget + 0x10; + gAudioContext.audioBufferParameters.numSamplesPerFrameMin = + gAudioContext.audioBufferParameters.numSamplesPerFrameTarget - 0x10; + gAudioContext.audioBufferParameters.numSamplesPerFrameMax = + gAudioContext.audioBufferParameters.numSamplesPerFrameTarget + 0x10; gAudioContext.audioBufferParameters.updatesPerFrame = - ((gAudioContext.audioBufferParameters.samplesPerFrameTarget + 0x10) / 0xD0) + 1; - gAudioContext.audioBufferParameters.samplesPerUpdate = (gAudioContext.audioBufferParameters.samplesPerFrameTarget / - gAudioContext.audioBufferParameters.updatesPerFrame) & - ~7; - gAudioContext.audioBufferParameters.samplesPerUpdateMax = gAudioContext.audioBufferParameters.samplesPerUpdate + 8; - gAudioContext.audioBufferParameters.samplesPerUpdateMin = gAudioContext.audioBufferParameters.samplesPerUpdate - 8; + ((gAudioContext.audioBufferParameters.numSamplesPerFrameTarget + 0x10) / 0xD0) + 1; + gAudioContext.audioBufferParameters.numSamplesPerUpdate = + (gAudioContext.audioBufferParameters.numSamplesPerFrameTarget / + gAudioContext.audioBufferParameters.updatesPerFrame) & + ~7; + gAudioContext.audioBufferParameters.numSamplesPerUpdateMax = + gAudioContext.audioBufferParameters.numSamplesPerUpdate + 8; + gAudioContext.audioBufferParameters.numSamplesPerUpdateMin = + gAudioContext.audioBufferParameters.numSamplesPerUpdate - 8; gAudioContext.audioBufferParameters.resampleRate = 32000.0f / (s32)gAudioContext.audioBufferParameters.samplingFreq; gAudioContext.audioBufferParameters.updatesPerFrameInvScaled = (1.0f / 256.0f) / gAudioContext.audioBufferParameters.updatesPerFrame; @@ -984,13 +987,13 @@ void AudioHeap_Init(void) { gAudioContext.unk_2870 /= gAudioContext.tempoInternalToExternal; gAudioContext.audioBufferParameters.specUnk4 = spec->unk_04; - gAudioContext.audioBufferParameters.samplesPerFrameTarget *= gAudioContext.audioBufferParameters.specUnk4; - gAudioContext.audioBufferParameters.maxAiBufNumSamples *= gAudioContext.audioBufferParameters.specUnk4; - gAudioContext.audioBufferParameters.minAiBufNumSamples *= gAudioContext.audioBufferParameters.specUnk4; + gAudioContext.audioBufferParameters.numSamplesPerFrameTarget *= gAudioContext.audioBufferParameters.specUnk4; + gAudioContext.audioBufferParameters.numSamplesPerFrameMax *= gAudioContext.audioBufferParameters.specUnk4; + gAudioContext.audioBufferParameters.numSamplesPerFrameMin *= gAudioContext.audioBufferParameters.specUnk4; gAudioContext.audioBufferParameters.updatesPerFrame *= gAudioContext.audioBufferParameters.specUnk4; if (gAudioContext.audioBufferParameters.specUnk4 >= 2) { - gAudioContext.audioBufferParameters.maxAiBufNumSamples -= 0x10; + gAudioContext.audioBufferParameters.numSamplesPerFrameMax -= 0x10; } // Determine the maximum allowable number of audio command list entries for the rsp microcode @@ -1037,13 +1040,13 @@ void AudioHeap_Init(void) { gAudioContext.notes = AudioHeap_AllocZeroed(&gAudioContext.miscPool, gAudioContext.numNotes * sizeof(Note)); AudioPlayback_NoteInitAll(); AudioPlayback_InitNoteFreeList(); - gAudioContext.noteSubsEu = + gAudioContext.sampleStateList = AudioHeap_AllocZeroed(&gAudioContext.miscPool, gAudioContext.audioBufferParameters.updatesPerFrame * - gAudioContext.numNotes * sizeof(NoteSubEu)); + gAudioContext.numNotes * sizeof(NoteSampleState)); // Initialize audio binary interface command list buffer - for (j = 0; j < ARRAY_COUNT(gAudioContext.abiCmdBufs); j++) { - gAudioContext.abiCmdBufs[j] = + for (i = 0; i < ARRAY_COUNT(gAudioContext.abiCmdBufs); i++) { + gAudioContext.abiCmdBufs[i] = AudioHeap_AllocDmaMemoryZeroed(&gAudioContext.miscPool, gAudioContext.maxAudioCmds * sizeof(Acmd)); } @@ -1052,20 +1055,20 @@ void AudioHeap_Init(void) { AudioHeap_InitAdsrDecayTable(); // Initialize reverbs - for (i = 0; i < ARRAY_COUNT(gAudioContext.synthesisReverbs); i++) { - gAudioContext.synthesisReverbs[i].useReverb = 0; + for (reverbIndex = 0; reverbIndex < ARRAY_COUNT(gAudioContext.synthesisReverbs); reverbIndex++) { + gAudioContext.synthesisReverbs[reverbIndex].useReverb = 0; } gAudioContext.numSynthesisReverbs = spec->numReverbs; - for (i = 0; i < gAudioContext.numSynthesisReverbs; i++) { - AudioHeap_InitReverb(i, &spec->reverbSettings[i], 1); + for (reverbIndex = 0; reverbIndex < gAudioContext.numSynthesisReverbs; reverbIndex++) { + AudioHeap_InitReverb(reverbIndex, &spec->reverbSettings[reverbIndex], true); } // Initialize sequence players AudioSeq_InitSequencePlayers(); - for (j = 0; j < gAudioContext.audioBufferParameters.numSequencePlayers; j++) { - AudioSeq_InitSequencePlayerChannels(j); - AudioSeq_ResetSequencePlayer(&gAudioContext.seqPlayers[j]); + for (i = 0; i < gAudioContext.audioBufferParameters.numSequencePlayers; i++) { + AudioSeq_InitSequencePlayerChannels(i); + AudioSeq_ResetSequencePlayer(&gAudioContext.seqPlayers[i]); } // Initialize two additional caches on the audio heap to store individual audio samples @@ -1529,149 +1532,165 @@ void AudioHeap_DiscardSampleBanks(void) { } } -void AudioHeap_SetReverbData(s32 reverbIndex, u32 dataType, s32 data, s32 flags) { - s32 windowSize; +void AudioHeap_SetReverbData(s32 reverbIndex, u32 dataType, s32 data, s32 isFirstInit) { + s32 delayNumSamples; SynthesisReverb* reverb = &gAudioContext.synthesisReverbs[reverbIndex]; switch (dataType) { - case 0: - AudioHeap_InitReverb(reverbIndex, (ReverbSettings*)data, 0); + case REVERB_DATA_TYPE_SETTINGS: + AudioHeap_InitReverb(reverbIndex, (ReverbSettings*)data, false); break; - case 1: + + case REVERB_DATA_TYPE_DELAY: if (data < 4) { data = 4; } - windowSize = data * 64; - if (windowSize < 0x100) { - windowSize = 0x100; + delayNumSamples = data * 64; + if (delayNumSamples < (16 * SAMPLES_PER_FRAME)) { + delayNumSamples = 16 * SAMPLES_PER_FRAME; } - windowSize /= reverb->downsampleRate; + delayNumSamples /= reverb->downsampleRate; - if (flags == 0) { - if (reverb->unk_1E >= (data / reverb->downsampleRate)) { - if ((reverb->nextRingBufPos >= windowSize) || (reverb->unk_24 >= windowSize)) { - reverb->nextRingBufPos = 0; - reverb->unk_24 = 0; - } - } else { + if (!isFirstInit) { + if (reverb->delayNumSamplesAfterDownsampling < (data / reverb->downsampleRate)) { break; } + if ((reverb->nextReverbBufPos >= delayNumSamples) || (reverb->delayNumSamplesUnk >= delayNumSamples)) { + reverb->nextReverbBufPos = 0; + reverb->delayNumSamplesUnk = 0; + } } - reverb->windowSize = windowSize; - - if ((reverb->downsampleRate != 1) || reverb->unk_18) { - reverb->unk_0E = 0x8000 / reverb->downsampleRate; - if (reverb->unk_30 == NULL) { - reverb->unk_30 = AudioHeap_AllocZeroed(&gAudioContext.miscPool, 0x20); - reverb->unk_34 = AudioHeap_AllocZeroed(&gAudioContext.miscPool, 0x20); - reverb->unk_38 = AudioHeap_AllocZeroed(&gAudioContext.miscPool, 0x20); - reverb->unk_3C = AudioHeap_AllocZeroed(&gAudioContext.miscPool, 0x20); - if (reverb->unk_3C == NULL) { + reverb->delayNumSamples = delayNumSamples; + + if ((reverb->downsampleRate != 1) || reverb->resampleEffectOn) { + reverb->downsamplePitch = 0x8000 / reverb->downsampleRate; + if (reverb->leftLoadResampleBuf == NULL) { + reverb->leftLoadResampleBuf = + AudioHeap_AllocZeroed(&gAudioContext.miscPool, sizeof(RESAMPLE_STATE)); + reverb->rightLoadResampleBuf = + AudioHeap_AllocZeroed(&gAudioContext.miscPool, sizeof(RESAMPLE_STATE)); + reverb->leftSaveResampleBuf = + AudioHeap_AllocZeroed(&gAudioContext.miscPool, sizeof(RESAMPLE_STATE)); + reverb->rightSaveResampleBuf = + AudioHeap_AllocZeroed(&gAudioContext.miscPool, sizeof(RESAMPLE_STATE)); + if (reverb->rightSaveResampleBuf == NULL) { reverb->downsampleRate = 1; } } } break; - case 2: - gAudioContext.synthesisReverbs[reverbIndex].unk_0C = data; + + case REVERB_DATA_TYPE_DECAY: + gAudioContext.synthesisReverbs[reverbIndex].decayRatio = data; break; - case 3: - gAudioContext.synthesisReverbs[reverbIndex].unk_16 = data; + + case REVERB_DATA_TYPE_SUB_VOLUME: + gAudioContext.synthesisReverbs[reverbIndex].subVolume = data; break; - case 4: - gAudioContext.synthesisReverbs[reverbIndex].unk_0A = data; + + case REVERB_DATA_TYPE_VOLUME: + gAudioContext.synthesisReverbs[reverbIndex].volume = data; break; - case 5: + + case REVERB_DATA_TYPE_LEAK_RIGHT: gAudioContext.synthesisReverbs[reverbIndex].leakRtl = data; break; - case 6: + + case REVERB_DATA_TYPE_LEAK_LEFT: gAudioContext.synthesisReverbs[reverbIndex].leakLtr = data; break; - case 7: + + case REVERB_DATA_TYPE_FILTER_LEFT: if (data != 0) { - if ((flags != 0) || (reverb->unk_278 == 0)) { - reverb->filterLeftState = AudioHeap_AllocDmaMemoryZeroed(&gAudioContext.miscPool, 0x40); - reverb->unk_278 = AudioHeap_AllocDmaMemory(&gAudioContext.miscPool, 0x10); + if (isFirstInit || (reverb->filterLeftInit == NULL)) { + reverb->filterLeftState = AudioHeap_AllocDmaMemoryZeroed(&gAudioContext.miscPool, + 2 * (FILTER_BUF_PART1 + FILTER_BUF_PART2)); + reverb->filterLeftInit = AudioHeap_AllocDmaMemory(&gAudioContext.miscPool, FILTER_SIZE); } - reverb->filterLeft = reverb->unk_278; - if (reverb->filterLeft != 0) { + reverb->filterLeft = reverb->filterLeftInit; + if (reverb->filterLeft != NULL) { AudioHeap_LoadLowPassFilter(reverb->filterLeft, data); } } else { - reverb->filterLeft = 0; + reverb->filterLeft = NULL; - if (flags != 0) { - reverb->unk_278 = 0; + if (isFirstInit) { + reverb->filterLeftInit = NULL; } } - break; - case 8: + + case REVERB_DATA_TYPE_FILTER_RIGHT: if (data != 0) { - if ((flags != 0) || (reverb->unk_27C == 0)) { - reverb->filterRightState = AudioHeap_AllocDmaMemoryZeroed(&gAudioContext.miscPool, 0x40); - reverb->unk_27C = AudioHeap_AllocDmaMemory(&gAudioContext.miscPool, 0x10); + if (isFirstInit || (reverb->filterRightInit == NULL)) { + reverb->filterRightState = AudioHeap_AllocDmaMemoryZeroed( + &gAudioContext.miscPool, 2 * (FILTER_BUF_PART1 + FILTER_BUF_PART2)); + reverb->filterRightInit = AudioHeap_AllocDmaMemory(&gAudioContext.miscPool, FILTER_SIZE); } - reverb->filterRight = reverb->unk_27C; - if (reverb->unk_27C != 0) { - AudioHeap_LoadLowPassFilter(reverb->unk_27C, data); + reverb->filterRight = reverb->filterRightInit; + if (reverb->filterRight != NULL) { + AudioHeap_LoadLowPassFilter(reverb->filterRight, data); } } else { - reverb->filterRight = 0; - if (flags != 0) { - reverb->unk_27C = 0; + reverb->filterRight = NULL; + if (isFirstInit) { + reverb->filterRightInit = NULL; } } break; - case 9: - reverb->unk_19 = data; + + case REVERB_DATA_TYPE_9: + reverb->resampleEffectExtraSamples = data; if (data == 0) { - reverb->unk_18 = false; + reverb->resampleEffectOn = false; } else { - reverb->unk_18 = true; + reverb->resampleEffectOn = true; } break; + default: break; } } -void AudioHeap_InitReverb(s32 reverbIndex, ReverbSettings* settings, s32 flags) { +void AudioHeap_InitReverb(s32 reverbIndex, ReverbSettings* settings, s32 isFirstInit) { SynthesisReverb* reverb = &gAudioContext.synthesisReverbs[reverbIndex]; - if (flags != 0) { - reverb->unk_1E = settings->windowSize / settings->downsampleRate; - reverb->unk_30 = 0; - } else if (reverb->unk_1E < (settings->windowSize / settings->downsampleRate)) { + if (isFirstInit) { + reverb->delayNumSamplesAfterDownsampling = settings->delayNumSamples / settings->downsampleRate; + reverb->leftLoadResampleBuf = NULL; + } else if (reverb->delayNumSamplesAfterDownsampling < (settings->delayNumSamples / settings->downsampleRate)) { return; } reverb->downsampleRate = settings->downsampleRate; - reverb->unk_18 = false; - reverb->unk_19 = 0; - reverb->unk_1A = 0; - reverb->unk_1C = 0; - AudioHeap_SetReverbData(reverbIndex, 1, settings->windowSize, flags); - reverb->unk_0C = settings->unk_4; - reverb->unk_0A = settings->unk_A; - reverb->unk_14 = settings->unk_6 << 6; - reverb->unk_16 = settings->unk_8; + reverb->resampleEffectOn = false; + reverb->resampleEffectExtraSamples = 0; + reverb->resampleEffectLoadUnk = 0; + reverb->resampleEffectSaveUnk = 0; + AudioHeap_SetReverbData(reverbIndex, REVERB_DATA_TYPE_DELAY, settings->delayNumSamples, isFirstInit); + reverb->decayRatio = settings->decayRatio; + reverb->volume = settings->volume; + reverb->subDelay = settings->subDelay * 64; + reverb->subVolume = settings->subVolume; reverb->leakRtl = settings->leakRtl; reverb->leakLtr = settings->leakLtr; - reverb->unk_05 = settings->unk_10; - reverb->unk_08 = settings->unk_12; - reverb->useReverb = 8; - - if (flags != 0) { - reverb->leftRingBuf = AudioHeap_AllocZeroedAttemptExternal(&gAudioContext.miscPool, reverb->windowSize * 2); - reverb->rightRingBuf = AudioHeap_AllocZeroedAttemptExternal(&gAudioContext.miscPool, reverb->windowSize * 2); + reverb->mixReverbIndex = settings->mixReverbIndex; + reverb->mixReverbStrength = settings->mixReverbStrength; + reverb->useReverb = 8; // used as a boolean + + if (isFirstInit) { + reverb->leftReverbBuf = + AudioHeap_AllocZeroedAttemptExternal(&gAudioContext.miscPool, reverb->delayNumSamples * 2); + reverb->rightReverbBuf = + AudioHeap_AllocZeroedAttemptExternal(&gAudioContext.miscPool, reverb->delayNumSamples * 2); reverb->resampleFlags = 1; - reverb->nextRingBufPos = 0; - reverb->unk_24 = 0; + reverb->nextReverbBufPos = 0; + reverb->delayNumSamplesUnk = 0; reverb->curFrame = 0; reverb->framesToIgnore = 2; } @@ -1681,12 +1700,13 @@ void AudioHeap_InitReverb(s32 reverbIndex, ReverbSettings* settings, s32 flags) reverb->tunedSample.tuning = 1.0f; reverb->sample.codec = CODEC_REVERB; reverb->sample.medium = MEDIUM_RAM; - reverb->sample.size = reverb->windowSize * 2; - reverb->sample.sampleAddr = (u8*)reverb->leftRingBuf; + reverb->sample.size = reverb->delayNumSamples * SAMPLE_SIZE; + reverb->sample.sampleAddr = (u8*)reverb->leftReverbBuf; reverb->loop.start = 0; reverb->loop.count = 1; - reverb->loop.end = reverb->windowSize; + reverb->loop.loopEnd = reverb->delayNumSamples; - AudioHeap_SetReverbData(reverbIndex, 7, settings->lowPassFilterCutoffLeft, flags); - AudioHeap_SetReverbData(reverbIndex, 8, settings->lowPassFilterCutoffRight, flags); + AudioHeap_SetReverbData(reverbIndex, REVERB_DATA_TYPE_FILTER_LEFT, settings->lowPassFilterCutoffLeft, isFirstInit); + AudioHeap_SetReverbData(reverbIndex, REVERB_DATA_TYPE_FILTER_RIGHT, settings->lowPassFilterCutoffRight, + isFirstInit); } diff --git a/src/code/audio/audio_init_params.c b/src/code/audio/audio_init_params.c index 81c49b30c..59c6658ac 100644 --- a/src/code/audio/audio_init_params.c +++ b/src/code/audio/audio_init_params.c @@ -1,12 +1,184 @@ #include "global.h" const s16 gAudioTatumInit[] = { - 0x1C00, // unused - 0x30, // gTatumsPerBeat + 0x1C00, // unused + TATUMS_PER_BEAT, // gTatumsPerBeat }; +// TODO: Extract from table? +#define NUM_SOUNDFONTS 41 +#define SFX_SEQ_SIZE 0xC6A0 +#define AMBIENCE_SEQ_SIZE 0xFC0 +#define SOUNDFONT_0_SIZE 0x81C0 +#define SOUNDFONT_1_SIZE 0x36D0 +#define SOUNDFONT_2_SIZE 0xCE0 + +// Sizes of everything on the init pool +#define AI_BUFFERS_SIZE (AIBUF_SIZE * ARRAY_COUNT(gAudioContext.aiBuffers)) +#define SOUNDFONT_LIST_SIZE (NUM_SOUNDFONTS * sizeof(SoundFont)) + +// 0x19BD0 +#define PERMANENT_POOL_SIZE \ + (SFX_SEQ_SIZE + AMBIENCE_SEQ_SIZE + SOUNDFONT_0_SIZE + SOUNDFONT_1_SIZE + SOUNDFONT_2_SIZE + 0x430) + const AudioHeapInitSizes gAudioHeapInitSizes = { - 0x137F00, // heapSize - 0x1C480, // initPoolSize - 0x1A000, // permanentPoolSize + ALIGN16(sizeof(gAudioHeap) - 0x100), // audio heap size + ALIGN16(PERMANENT_POOL_SIZE + AI_BUFFERS_SIZE + SOUNDFONT_LIST_SIZE + 0x40), // init pool size + ALIGN16(PERMANENT_POOL_SIZE), // permanent pool size +}; + +#define REVERB_INDEX_0_SETTINGS \ + { 1, 0x30, 0x3000, 0, 0, 0x7FFF, 0x0000, 0x0000, REVERB_INDEX_NONE, 0x3000, 0, 0 } + +ReverbSettings reverbSettings0[3] = { + REVERB_INDEX_0_SETTINGS, + { 1, 0x20, 0x0800, 0, 0, 0x7FFF, 0x0000, 0x0000, REVERB_INDEX_NONE, 0x0000, 0, 0 }, +}; + +ReverbSettings reverbSettings1[3] = { + REVERB_INDEX_0_SETTINGS, + { 1, 0x30, 0x1800, 0, 0, 0x7FFF, 0x0000, 0x0000, REVERB_INDEX_NONE, 0x0000, 11, 11 }, +}; + +ReverbSettings reverbSettings2[3] = { + REVERB_INDEX_0_SETTINGS, + { 1, 0x38, 0x2800, 0, 0, 0x7FFF, 0x0000, 0x0000, REVERB_INDEX_NONE, 0x0000, 7, 7 }, +}; + +ReverbSettings reverbSettings3[3] = { + REVERB_INDEX_0_SETTINGS, + { 1, 0x30, 0x6800, 0, 0, 0x7FFF, 0x1400, 0x1400, REVERB_INDEX_NONE, 0x3000, 6, 6 }, + { 2, 0x50, 0x6000, 0, 0, 0x7FFF, 0xD000, 0x3000, REVERB_INDEX_NONE, 0x3000, 0, 0 }, +}; + +ReverbSettings reverbSettings4[3] = { + REVERB_INDEX_0_SETTINGS, + { 1, 0x40, 0x5000, 0, 0, 0x7FFF, 0x1800, 0x1800, REVERB_INDEX_NONE, 0x3000, 7, 7 }, +}; + +ReverbSettings reverbSettings5[3] = { + REVERB_INDEX_0_SETTINGS, + { 1, 0x40, 0x5C00, 0, 0, 0x7FFF, 0x2000, 0x2000, REVERB_INDEX_NONE, 0x3000, 4, 4 }, +}; + +ReverbSettings reverbSettings6[3] = { + REVERB_INDEX_0_SETTINGS, + { 1, 0x30, 0x6000, 0, 0, 0x7FFF, 0x1000, 0x1000, REVERB_INDEX_NONE, 0x3000, 10, 10 }, +}; + +ReverbSettings reverbSettings7[3] = { + REVERB_INDEX_0_SETTINGS, + { 1, 0x30, 0x6800, 0, 0, 0x7FFF, 0x1400, 0x1400, REVERB_INDEX_NONE, 0x3000, 6, 6 }, +}; + +ReverbSettings reverbSettings8[2] = { + REVERB_INDEX_0_SETTINGS, + { 1, 0x50, 0x5000, 0, 0, 0x7FFF, 0xD000, 0x3000, REVERB_INDEX_NONE, 0x3000, 0, 0 }, +}; + +ReverbSettings reverbSettings9[3] = { + REVERB_INDEX_0_SETTINGS, + { 1, 0x20, 0x0000, 0, 0, 0x7FFF, 0x0000, 0x0000, REVERB_INDEX_NONE, 0x0000, 0, 0 }, +}; + +ReverbSettings reverbSettingsA[3] = { + REVERB_INDEX_0_SETTINGS, + { 1, 0x30, 0x1800, 0, 0, 0x7FFF, 0x0000, 0x0000, REVERB_INDEX_NONE, 0x0000, 11, 11 }, +}; + +ReverbSettings reverbSettingsB[3] = { + REVERB_INDEX_0_SETTINGS, +}; + +ReverbSettings reverbSettingsC[3] = { + REVERB_INDEX_0_SETTINGS, + { 1, 0x40, 0x5000, 0, 0, 0x7FFF, 0x0000, 0x0000, REVERB_INDEX_NONE, 0x3000, 0, 0 }, +}; + +ReverbSettings reverbSettingsD[3] = { + REVERB_INDEX_0_SETTINGS, + { 1, 0x30, 0x6800, 0, 0, 0x7FFF, 0x1400, 0x1400, REVERB_INDEX_NONE, 0x3000, 6, 6 }, + { 2, 0x50, 0x6000, 0, 0, 0x7FFF, 0xD000, 0x3000, REVERB_INDEX_NONE, 0x3000, 0, 0 }, +}; + +ReverbSettings reverbSettingsE[3] = { + REVERB_INDEX_0_SETTINGS, + { 1, 0x30, 0x1800, 0, 0, 0x7FFF, 0x0000, 0x0000, REVERB_INDEX_NONE, 0x0000, 11, 11 }, + { 1, 0x40, 0x5000, 0, 0, 0x7FFF, 0x1800, 0x1800, REVERB_INDEX_NONE, 0x3000, 7, 7 }, +}; + +ReverbSettings reverbSettingsF[2] = { + REVERB_INDEX_0_SETTINGS, + { 1, 0x50, 0x1800, 0, 0, 0x7FFF, 0x0000, 0x0000, REVERB_INDEX_NONE, 0x0000, 11, 11 }, +}; + +ReverbSettings* gReverbSettingsTable[] = { + reverbSettings0, reverbSettings1, reverbSettings2, reverbSettings4, reverbSettings5, + reverbSettings6, reverbSettings7, reverbSettings8, reverbSettings9, reverbSettings3, +}; + +AudioSpec gAudioSpecs[21] = { + /* 0x0 */ + { 32000, 1, 24, 5, 0, 0, 2, reverbSettingsF, 0x500, 0x200, 0x7FFF, 0xAF0, 0x2D80, 0, 0x4100, 0x2D00, 0, 0, + 0xDC800 }, + /* 0x1 */ + { 32000, 1, 24, 5, 0, 0, 2, reverbSettingsF, 0x500, 0x200, 0x7FFF, 0xAF0, 0x2D80, 0, 0x4100, 0x2D00, 0, 0, + 0xDC800 }, + /* 0x2 */ + { 32000, 1, 24, 5, 0, 0, 2, reverbSettingsF, 0x500, 0x200, 0x7FFF, 0xAF0, 0x2D80, 0, 0x4100, 0x2D00, 0, 0, + 0xDC800 }, + /* 0x3 */ + { 32000, 1, 24, 5, 0, 0, 2, reverbSettingsF, 0x500, 0x200, 0x7FFF, 0xAF0, 0x2D80, 0, 0x4100, 0x2D00, 0, 0, + 0xDC800 }, + /* 0x4 */ + { 32000, 1, 24, 5, 0, 0, 2, reverbSettingsF, 0x500, 0x200, 0x7FFF, 0xAF0, 0x2D80, 0, 0x4100, 0x2D00, 0, 0, + 0xDC800 }, + /* 0x5 */ + { 32000, 1, 24, 5, 0, 0, 2, reverbSettingsF, 0x500, 0x200, 0x7FFF, 0xAF0, 0x2D80, 0, 0x4100, 0x2D00, 0, 0, + 0xDC800 }, + /* 0x6 */ + { 32000, 1, 24, 5, 0, 0, 2, reverbSettingsF, 0x500, 0x200, 0x7FFF, 0xAF0, 0x2D80, 0, 0x4100, 0x2D00, 0, 0, + 0xDC800 }, + /* 0x7 */ + { 32000, 1, 24, 5, 0, 0, 2, reverbSettingsF, 0x500, 0x200, 0x7FFF, 0xAF0, 0x2D80, 0, 0x4100, 0x2D00, 0, 0, + 0xDC800 }, + /* 0x8 */ + { 32000, 1, 24, 5, 0, 0, 2, reverbSettingsF, 0x500, 0x200, 0x7FFF, 0xAF0, 0x2D80, 0, 0x4100, 0x2D00, 0, 0, + 0xDC800 }, + /* 0x9 */ + { 32000, 1, 24, 5, 0, 0, 2, reverbSettingsF, 0x500, 0x200, 0x7FFF, 0xAF0, 0x2D80, 0, 0x4100, 0x2D00, 0, 0, + 0xDC800 }, + /* 0xA */ + { 32000, 1, 28, 3, 0, 0, 2, reverbSettingsA, 0x500, 0x200, 0x7FFF, 0xAF0, 0x2D80, 0, 0x2800, 0x2D00, 0, 0, + 0xDC800 }, + /* 0xB */ + { 32000, 1, 28, 3, 0, 0, 2, reverbSettingsA, 0x500, 0x200, 0x7FFF, 0xAF0, 0x2D80, 0, 0x4100, 0x2D00, 0, 0, + 0xDC800 }, + /* 0xC */ + { 32000, 1, 28, 5, 0, 0, 2, reverbSettingsA, 0x500, 0x200, 0x7FFF, 0xAF0, 0x2D80, 0, 0x4100, 0x2D00, 0, 0, + 0xCC800 }, + /* 0xD */ + { 32000, 1, 24, 5, 0, 0, 3, reverbSettingsD, 0x500, 0x200, 0x7FFF, 0xAF0, 0x2D80, 0, 0x4100, 0x2D00, 0, 0, + 0xDC800 }, + /* 0xE */ + { 32000, 1, 24, 5, 0, 0, 3, reverbSettingsE, 0x500, 0x200, 0x7FFF, 0xAF0, 0x2D80, 0, 0x4100, 0x2D00, 0, 0, + 0xDC800 }, + /* 0xF */ + { 32000, 1, 24, 5, 0, 0, 2, reverbSettingsF, 0x500, 0x200, 0x7FFF, 0xAF0, 0x2D80, 0, 0x4000, 0x2D00, 0, 0, + 0xDC800 }, + /* 0x10 */ + { 32000, 1, 22, 5, 0, 0, 2, reverbSettings0, 0x500, 0x200, 0x7FFF, 0xAF0, 0x2D80, 0, 0x4100, 0x2D00, 0, 0, + 0xDC800 }, + /* 0x11 */ + { 32000, 1, 22, 5, 0, 0, 2, reverbSettings8, 0x500, 0x200, 0x7FFF, 0xAF0, 0x2D80, 0, 0x4100, 0x2D00, 0, 0, + 0xDC800 }, + /* 0x12 */ + { 32000, 1, 16, 5, 0, 0, 2, reverbSettings0, 0x500, 0x200, 0x7FFF, 0xAF0, 0x2D80, 0, 0x4100, 0x2D00, 0, 0, + 0xDC800 }, + /* 0x13 */ + { 22050, 1, 24, 5, 0, 0, 2, reverbSettings0, 0x500, 0x200, 0x7FFF, 0xAF0, 0x2D80, 0, 0x4100, 0x2D00, 0, 0, + 0xDC800 }, + /* 0x14 */ + { 32000, 1, 24, 5, 0, 0, 2, reverbSettings2, 0x500, 0x200, 0x7FFF, 0xAF0, 0x2D80, 0, 0x3600, 0x2600, 0, 0, + 0xDC800 }, }; diff --git a/src/code/audio/audio_load.c b/src/code/audio/audio_load.c index c8e5ac5d1..ccdc7488d 100644 --- a/src/code/audio/audio_load.c +++ b/src/code/audio/audio_load.c @@ -1218,9 +1218,9 @@ void AudioLoad_Init(void* heap, size_t heapSize) { s32 i; s32 j; - D_80208E68 = NULL; - D_80208E70 = NULL; - D_80208E74 = NULL; + gCustomAudioUpdateFunction = NULL; + gCustomAudioReverbFunction = NULL; + gCustomAudioSynthFunction = NULL; for (i = 0; i < ARRAY_COUNT(gAudioContext.unk_29A8); i++) { gAudioContext.unk_29A8[i] = NULL; diff --git a/src/code/audio/audio_playback.c b/src/code/audio/audio_playback.c index e1a42b1ed..a7cc9a588 100644 --- a/src/code/audio/audio_playback.c +++ b/src/code/audio/audio_playback.c @@ -1,10 +1,10 @@ #include "global.h" -void AudioPlayback_NoteSetResamplingRate(NoteSubEu* noteSubEu, f32 resamplingRateInput); +void AudioPlayback_NoteSetResamplingRate(NoteSampleState* sampleState, f32 resamplingRateInput); void AudioPlayback_AudioListPushFront(AudioListItem* list, AudioListItem* item); void AudioPlayback_NoteInitForLayer(Note* note, SequenceLayer* layer); -void AudioPlayback_InitNoteSub(Note* note, NoteSubEu* noteSubEu, NoteSubAttributes* subAttrs) { +void AudioPlayback_InitSampleState(Note* note, NoteSampleState* sampleState, NoteSubAttributes* subAttrs) { f32 volLeft; f32 volRight; s32 halfPanIndex; @@ -13,45 +13,45 @@ void AudioPlayback_InitNoteSub(Note* note, NoteSubEu* noteSubEu, NoteSubAttribut u8 strongRight; f32 vel; u8 pan; - u8 reverbVol; + u8 targetReverbVol; StereoData stereoData; s32 stereoHeadsetEffects = note->playbackState.stereoHeadsetEffects; vel = subAttrs->velocity; pan = subAttrs->pan; - reverbVol = subAttrs->reverbVol; - stereoData = subAttrs->stereo.s; + targetReverbVol = subAttrs->targetReverbVol; + stereoData = subAttrs->stereoData; - noteSubEu->bitField0 = note->noteSubEu.bitField0; - noteSubEu->bitField1 = note->noteSubEu.bitField1; - noteSubEu->waveSampleAddr = note->noteSubEu.waveSampleAddr; - noteSubEu->harmonicIndexCurAndPrev = note->noteSubEu.harmonicIndexCurAndPrev; + sampleState->bitField0 = note->sampleState.bitField0; + sampleState->bitField1 = note->sampleState.bitField1; + sampleState->waveSampleAddr = note->sampleState.waveSampleAddr; + sampleState->harmonicIndexCurAndPrev = note->sampleState.harmonicIndexCurAndPrev; - AudioPlayback_NoteSetResamplingRate(noteSubEu, subAttrs->frequency); + AudioPlayback_NoteSetResamplingRate(sampleState, subAttrs->frequency); pan &= 0x7F; - noteSubEu->bitField0.stereoStrongRight = false; - noteSubEu->bitField0.stereoStrongLeft = false; - noteSubEu->bitField0.stereoHeadsetEffects = stereoData.stereoHeadsetEffects; - noteSubEu->bitField0.usesHeadsetPanEffects = stereoData.usesHeadsetPanEffects; + sampleState->bitField0.strongRight = false; + sampleState->bitField0.strongLeft = false; + sampleState->bitField0.strongReverbRight = stereoData.strongReverbRight; + sampleState->bitField0.strongReverbLeft = stereoData.strongReverbLeft; if (stereoHeadsetEffects && (gAudioContext.soundMode == SOUNDMODE_HEADSET)) { halfPanIndex = pan >> 1; if (halfPanIndex > 0x3F) { halfPanIndex = 0x3F; } - noteSubEu->headsetPanLeft = gHeadsetPanQuantization[halfPanIndex]; - noteSubEu->headsetPanRight = gHeadsetPanQuantization[0x3F - halfPanIndex]; - noteSubEu->bitField1.usesHeadsetPanEffects2 = true; + sampleState->haasEffectRightDelaySize = gHaasEffectDelaySize[halfPanIndex]; + sampleState->haasEffectLeftDelaySize = gHaasEffectDelaySize[0x3F - halfPanIndex]; + sampleState->bitField1.useHaasEffect = true; volLeft = gHeadsetPanVolume[pan]; volRight = gHeadsetPanVolume[0x7F - pan]; } else if (stereoHeadsetEffects && (gAudioContext.soundMode == SOUNDMODE_STEREO)) { strongLeft = strongRight = false; - noteSubEu->headsetPanRight = 0; - noteSubEu->headsetPanLeft = 0; - noteSubEu->bitField1.usesHeadsetPanEffects2 = false; + sampleState->haasEffectLeftDelaySize = 0; + sampleState->haasEffectRightDelaySize = 0; + sampleState->bitField1.useHaasEffect = false; volLeft = gStereoPanVolume[pan]; volRight = gStereoPanVolume[0x7F - pan]; @@ -62,37 +62,37 @@ void AudioPlayback_InitNoteSub(Note* note, NoteSubEu* noteSubEu, NoteSubAttribut } // case 0: - noteSubEu->bitField0.stereoStrongRight = strongRight; - noteSubEu->bitField0.stereoStrongLeft = strongLeft; + sampleState->bitField0.strongRight = strongRight; + sampleState->bitField0.strongLeft = strongLeft; - switch (stereoData.bit2) { + switch (stereoData.type) { case 0: break; case 1: - noteSubEu->bitField0.stereoStrongRight = stereoData.strongRight; - noteSubEu->bitField0.stereoStrongLeft = stereoData.strongLeft; + sampleState->bitField0.strongRight = stereoData.strongRight; + sampleState->bitField0.strongLeft = stereoData.strongLeft; break; case 2: - noteSubEu->bitField0.stereoStrongRight = stereoData.strongRight | strongRight; - noteSubEu->bitField0.stereoStrongLeft = stereoData.strongLeft | strongLeft; + sampleState->bitField0.strongRight = stereoData.strongRight | strongRight; + sampleState->bitField0.strongLeft = stereoData.strongLeft | strongLeft; break; case 3: - noteSubEu->bitField0.stereoStrongRight = stereoData.strongRight ^ strongRight; - noteSubEu->bitField0.stereoStrongLeft = stereoData.strongLeft ^ strongLeft; + sampleState->bitField0.strongRight = stereoData.strongRight ^ strongRight; + sampleState->bitField0.strongLeft = stereoData.strongLeft ^ strongLeft; break; } } else if (gAudioContext.soundMode == SOUNDMODE_MONO) { - noteSubEu->bitField0.stereoHeadsetEffects = false; - noteSubEu->bitField0.usesHeadsetPanEffects = false; + sampleState->bitField0.strongReverbRight = false; + sampleState->bitField0.strongReverbLeft = false; volLeft = 0.707f; // approx 1/sqrt(2) volRight = 0.707f; } else { - noteSubEu->bitField0.stereoStrongRight = stereoData.strongRight; - noteSubEu->bitField0.stereoStrongLeft = stereoData.strongLeft; + sampleState->bitField0.strongRight = stereoData.strongRight; + sampleState->bitField0.strongLeft = stereoData.strongLeft; volLeft = gDefaultPanVolume[pan]; volRight = gDefaultPanVolume[0x7F - pan]; } @@ -100,33 +100,33 @@ void AudioPlayback_InitNoteSub(Note* note, NoteSubEu* noteSubEu, NoteSubAttribut vel = 0.0f > vel ? 0.0f : vel; vel = 1.0f < vel ? 1.0f : vel; - noteSubEu->targetVolLeft = (s32)((vel * volLeft) * (0x1000 - 0.001f)); - noteSubEu->targetVolRight = (s32)((vel * volRight) * (0x1000 - 0.001f)); + sampleState->targetVolLeft = (s32)((vel * volLeft) * (0x1000 - 0.001f)); + sampleState->targetVolRight = (s32)((vel * volRight) * (0x1000 - 0.001f)); - noteSubEu->gain = subAttrs->gain; - noteSubEu->filter = subAttrs->filter; - noteSubEu->unk_07 = subAttrs->unk_14; - noteSubEu->unk_0E = subAttrs->unk_16; - noteSubEu->reverbVol = reverbVol; - noteSubEu->unk_19 = subAttrs->unk_3; + sampleState->gain = subAttrs->gain; + sampleState->filter = subAttrs->filter; + sampleState->combFilterSize = subAttrs->combFilterSize; + sampleState->combFilterGain = subAttrs->combFilterGain; + sampleState->targetReverbVol = targetReverbVol; + sampleState->surroundEffectIndex = subAttrs->surroundEffectIndex; } -void AudioPlayback_NoteSetResamplingRate(NoteSubEu* noteSubEu, f32 resamplingRateInput) { +void AudioPlayback_NoteSetResamplingRate(NoteSampleState* sampleState, f32 resamplingRateInput) { f32 resamplingRate = 0.0f; if (resamplingRateInput < 2.0f) { - noteSubEu->bitField1.hasTwoParts = false; + sampleState->bitField1.hasTwoParts = false; resamplingRate = CLAMP_MAX(resamplingRateInput, 1.99998f); } else { - noteSubEu->bitField1.hasTwoParts = true; + sampleState->bitField1.hasTwoParts = true; if (resamplingRateInput > 3.99996f) { resamplingRate = 1.99998f; } else { resamplingRate = resamplingRateInput * 0.5f; } } - noteSubEu->resamplingRateFixedPoint = (s32)(resamplingRate * 32768.0f); + sampleState->frequencyFixedPoint = (s32)(resamplingRate * 32768.0f); } void AudioPlayback_NoteInit(Note* note) { @@ -140,17 +140,17 @@ void AudioPlayback_NoteInit(Note* note) { note->playbackState.status = PLAYBACK_STATUS_0; note->playbackState.adsr.action.s.state = ADSR_STATE_INITIAL; - note->noteSubEu = gDefaultNoteSub; + note->sampleState = gDefaultSampleState; } void AudioPlayback_NoteDisable(Note* note) { - if (note->noteSubEu.bitField0.needsInit == true) { - note->noteSubEu.bitField0.needsInit = false; + if (note->sampleState.bitField0.needsInit == true) { + note->sampleState.bitField0.needsInit = false; } note->playbackState.priority = 0; - note->noteSubEu.bitField0.enabled = false; + note->sampleState.bitField0.enabled = false; note->playbackState.status = PLAYBACK_STATUS_0; - note->noteSubEu.bitField0.finished = false; + note->sampleState.bitField0.finished = false; note->playbackState.parentLayer = NO_LAYER; note->playbackState.prevParentLayer = NO_LAYER; note->playbackState.adsr.action.s.state = ADSR_STATE_DISABLED; @@ -161,8 +161,8 @@ void AudioPlayback_ProcessNotes(void) { s32 pad; s32 playbackStatus; NoteAttributes* attrs; - NoteSubEu* noteSubEu2; - NoteSubEu* noteSubEu; + NoteSampleState* sampleState; + NoteSampleState* noteSampleState; Note* note; NotePlaybackState* playbackState; NoteSubAttributes subAttrs; @@ -172,7 +172,7 @@ void AudioPlayback_ProcessNotes(void) { for (i = 0; i < gAudioContext.numNotes; i++) { note = &gAudioContext.notes[i]; - noteSubEu2 = &gAudioContext.noteSubsEu[gAudioContext.noteSubEuOffset + i]; + sampleState = &gAudioContext.sampleStateList[gAudioContext.sampleStateOffset + i]; playbackState = ¬e->playbackState; if (playbackState->parentLayer != NO_LAYER) { if ((u32)playbackState->parentLayer < 0x7FFFFFFF) { @@ -211,10 +211,12 @@ void AudioPlayback_ProcessNotes(void) { out: if (playbackState->priority != 0) { + //! FAKE: if (1) {} - noteSubEu = ¬e->noteSubEu; - if ((playbackState->status >= 1) || noteSubEu->bitField0.finished) { - if ((playbackState->adsr.action.s.state == ADSR_STATE_DISABLED) || noteSubEu->bitField0.finished) { + noteSampleState = ¬e->sampleState; + if ((playbackState->status >= 1) || noteSampleState->bitField0.finished) { + if ((playbackState->adsr.action.s.state == ADSR_STATE_DISABLED) || + noteSampleState->bitField0.finished) { if (playbackState->wantedParentLayer != NO_LAYER) { AudioPlayback_NoteDisable(note); if (playbackState->wantedParentLayer->channel != NULL) { @@ -260,14 +262,14 @@ void AudioPlayback_ProcessNotes(void) { subAttrs.frequency = attrs->freqScale; subAttrs.velocity = attrs->velocity; subAttrs.pan = attrs->pan; - subAttrs.reverbVol = attrs->reverb; - subAttrs.stereo = attrs->stereo; + subAttrs.targetReverbVol = attrs->targetReverbVol; + subAttrs.stereoData = attrs->stereoData; subAttrs.gain = attrs->gain; subAttrs.filter = attrs->filter; - subAttrs.unk_14 = attrs->unk_4; - subAttrs.unk_16 = attrs->unk_6; - subAttrs.unk_3 = attrs->unk_3; - bookOffset = noteSubEu->bitField1.bookOffset; + subAttrs.combFilterSize = attrs->combFilterSize; + subAttrs.combFilterGain = attrs->combFilterGain; + subAttrs.surroundEffectIndex = attrs->surroundEffectIndex; + bookOffset = noteSampleState->bitField1.bookOffset; } else { SequenceLayer* layer = playbackState->parentLayer; SequenceChannel* channel = playbackState->parentLayer->channel; @@ -276,34 +278,35 @@ void AudioPlayback_ProcessNotes(void) { subAttrs.velocity = layer->noteVelocity; subAttrs.pan = layer->notePan; - if (layer->unk_08 == 0x80) { - subAttrs.unk_3 = channel->unk_10; + if (layer->surroundEffectIndex == 0x80) { + subAttrs.surroundEffectIndex = channel->surroundEffectIndex; } else { - subAttrs.unk_3 = layer->unk_08; + subAttrs.surroundEffectIndex = layer->surroundEffectIndex; } - if (layer->stereo.s.bit2 == 0) { - subAttrs.stereo = channel->stereo; + if (layer->stereoData.type == 0) { + subAttrs.stereoData = channel->stereoData; } else { - subAttrs.stereo = layer->stereo; + subAttrs.stereoData = layer->stereoData; } if (layer->unk_0A.s.bit_2 == 1) { - subAttrs.reverbVol = channel->reverb; + subAttrs.targetReverbVol = channel->targetReverbVol; } else { - subAttrs.reverbVol = layer->unk_09; + subAttrs.targetReverbVol = layer->targetReverbVol; } if (layer->unk_0A.s.bit_9 == 1) { subAttrs.gain = channel->gain; } else { subAttrs.gain = 0; + //! FAKE: if (1) {} } subAttrs.filter = channel->filter; - subAttrs.unk_14 = channel->unk_0F; - subAttrs.unk_16 = channel->unk_20; + subAttrs.combFilterSize = channel->combFilterSize; + subAttrs.combFilterGain = channel->combFilterGain; bookOffset = channel->bookOffset & 0x7; if (channel->seqPlayer->muted && (channel->muteFlags & MUTE_FLAGS_3)) { @@ -315,8 +318,8 @@ void AudioPlayback_ProcessNotes(void) { subAttrs.frequency *= playbackState->vibratoFreqScale * playbackState->portamentoFreqScale; subAttrs.frequency *= gAudioContext.audioBufferParameters.resampleRate; subAttrs.velocity *= scale; - AudioPlayback_InitNoteSub(note, noteSubEu2, &subAttrs); - noteSubEu->bitField1.bookOffset = bookOffset; + AudioPlayback_InitSampleState(note, sampleState, &subAttrs); + noteSampleState->bitField1.bookOffset = bookOffset; skip:; } } @@ -500,15 +503,15 @@ void AudioPlayback_SeqLayerDecayRelease(SequenceLayer* layer, s32 target) { channel = layer->channel; if (layer->unk_0A.s.bit_2 == 1) { - attrs->reverb = channel->reverb; + attrs->targetReverbVol = channel->targetReverbVol; } else { - attrs->reverb = layer->unk_09; + attrs->targetReverbVol = layer->targetReverbVol; } - if (layer->unk_08 == 0x80) { - attrs->unk_3 = channel->unk_10; + if (layer->surroundEffectIndex == 0x80) { + attrs->surroundEffectIndex = channel->surroundEffectIndex; } else { - attrs->unk_3 = layer->unk_08; + attrs->surroundEffectIndex = layer->surroundEffectIndex; } if (layer->unk_0A.s.bit_9 == 1) { @@ -526,20 +529,20 @@ void AudioPlayback_SeqLayerDecayRelease(SequenceLayer* layer, s32 target) { attrs->filter = attrs->filterBuf; } - attrs->unk_6 = channel->unk_20; - attrs->unk_4 = channel->unk_0F; + attrs->combFilterGain = channel->combFilterGain; + attrs->combFilterSize = channel->combFilterSize; if (channel->seqPlayer->muted && (channel->muteFlags & MUTE_FLAGS_3)) { - note->noteSubEu.bitField0.finished = true; + note->sampleState.bitField0.finished = true; } - if (layer->stereo.asByte == 0) { - attrs->stereo = channel->stereo; + if (layer->stereoData.asByte == 0) { + attrs->stereoData = channel->stereoData; } else { - attrs->stereo = layer->stereo; + attrs->stereoData = layer->stereoData; } note->playbackState.priority = channel->someOtherPriority; } else { - attrs->stereo = layer->stereo; + attrs->stereoData = layer->stereoData; note->playbackState.priority = 1; } @@ -620,7 +623,7 @@ s32 AudioPlayback_BuildSyntheticWave(Note* note, SequenceLayer* layer, s32 waveI // Save the pointer to the synthethic wave // waveId index starts at 128, there are WAVE_SAMPLE_COUNT samples to read from - note->noteSubEu.waveSampleAddr = &gWaveSamples[waveId - 128][harmonicIndex * WAVE_SAMPLE_COUNT]; + note->sampleState.waveSampleAddr = &gWaveSamples[waveId - 128][harmonicIndex * WAVE_SAMPLE_COUNT]; return harmonicIndex; } @@ -638,7 +641,7 @@ void AudioPlayback_InitSyntheticWave(Note* note, SequenceLayer* layer) { curHarmonicIndex = AudioPlayback_BuildSyntheticWave(note, layer, waveId); if (curHarmonicIndex != prevHarmonicIndex) { - note->noteSubEu.harmonicIndexCurAndPrev = (curHarmonicIndex << 2) + prevHarmonicIndex; + note->sampleState.harmonicIndexCurAndPrev = (curHarmonicIndex << 2) + prevHarmonicIndex; } } @@ -808,7 +811,7 @@ void AudioPlayback_NoteInitForLayer(Note* note, SequenceLayer* layer) { s16 instId; SequenceChannel* channel = layer->channel; NotePlaybackState* playbackState = ¬e->playbackState; - NoteSubEu* noteSubEu = ¬e->noteSubEu; + NoteSampleState* noteSampleState = ¬e->sampleState; playbackState->prevParentLayer = NO_LAYER; playbackState->parentLayer = layer; @@ -825,28 +828,28 @@ void AudioPlayback_NoteInitForLayer(Note* note, SequenceLayer* layer) { if (instId == 0xFF) { instId = channel->instOrWave; } - noteSubEu->tunedSample = layer->tunedSample; + noteSampleState->tunedSample = layer->tunedSample; if (instId >= 0x80 && instId < 0xC0) { - noteSubEu->bitField1.isSyntheticWave = true; + noteSampleState->bitField1.isSyntheticWave = true; } else { - noteSubEu->bitField1.isSyntheticWave = false; + noteSampleState->bitField1.isSyntheticWave = false; } - if (noteSubEu->bitField1.isSyntheticWave) { + if (noteSampleState->bitField1.isSyntheticWave) { AudioPlayback_BuildSyntheticWave(note, layer, instId); } else if (channel->startSamplePos == 1) { - playbackState->startSamplePos = noteSubEu->tunedSample->sample->loop->start; + playbackState->startSamplePos = noteSampleState->tunedSample->sample->loop->start; } else { playbackState->startSamplePos = channel->startSamplePos; - if (playbackState->startSamplePos >= noteSubEu->tunedSample->sample->loop->end) { + if (playbackState->startSamplePos >= noteSampleState->tunedSample->sample->loop->loopEnd) { playbackState->startSamplePos = 0; } } playbackState->fontId = channel->fontId; playbackState->stereoHeadsetEffects = channel->stereoHeadsetEffects; - noteSubEu->bitField1.reverbIndex = channel->reverbIndex & 3; + noteSampleState->bitField1.reverbIndex = channel->reverbIndex & 3; } void func_801963E8(Note* note, SequenceLayer* layer) { @@ -987,7 +990,7 @@ void AudioPlayback_NoteInitAll(void) { for (i = 0; i < gAudioContext.numNotes; i++) { note = &gAudioContext.notes[i]; - note->noteSubEu = gZeroNoteSub; + note->sampleState = gZeroedSampleState; note->playbackState.priority = 0; note->playbackState.status = PLAYBACK_STATUS_0; note->playbackState.parentLayer = NO_LAYER; @@ -1002,7 +1005,8 @@ void AudioPlayback_NoteInitAll(void) { note->playbackState.portamento.speed = 0; note->playbackState.stereoHeadsetEffects = false; note->playbackState.startSamplePos = 0; - note->synthesisState.synthesisBuffers = AudioHeap_AllocDmaMemory(&gAudioContext.miscPool, 0x2E0); - note->playbackState.attributes.filterBuf = AudioHeap_AllocDmaMemory(&gAudioContext.miscPool, 0x10); + note->synthesisState.synthesisBuffers = + AudioHeap_AllocDmaMemory(&gAudioContext.miscPool, sizeof(NoteSynthesisBuffers)); + note->playbackState.attributes.filterBuf = AudioHeap_AllocDmaMemory(&gAudioContext.miscPool, FILTER_SIZE); } } diff --git a/src/code/audio/audio_seqplayer.c b/src/code/audio/audio_seqplayer.c index a8573e33d..bc5115f8f 100644 --- a/src/code/audio/audio_seqplayer.c +++ b/src/code/audio/audio_seqplayer.c @@ -282,17 +282,17 @@ void AudioSeq_InitSequenceChannel(SequenceChannel* channel) { channel->transposition = 0; channel->largeNotes = false; channel->bookOffset = 0; - channel->stereo.asByte = 0; + channel->stereoData.asByte = 0; channel->changes.asByte = 0xFF; channel->scriptState.depth = 0; channel->newPan = 0x40; channel->panChannelWeight = 0x80; - channel->unk_10 = 0xFF; + channel->surroundEffectIndex = 0xFF; channel->velocityRandomVariance = 0; channel->gateTimeRandomVariance = 0; channel->noteUnused = NULL; channel->reverbIndex = 0; - channel->reverb = 0; + channel->targetReverbVol = 0; channel->gain = 0; channel->notePriority = 3; channel->someOtherPriority = 1; @@ -308,8 +308,8 @@ void AudioSeq_InitSequenceChannel(SequenceChannel* channel) { channel->vibrato.vibratoExtentChangeDelay = 0; channel->vibrato.vibratoDelay = 0; channel->filter = NULL; - channel->unk_20 = 0; - channel->unk_0F = 0; + channel->combFilterGain = 0; + channel->combFilterSize = 0; channel->volume = 1.0f; channel->volumeScale = 1.0f; channel->freqScale = 1.0f; @@ -345,7 +345,7 @@ s32 AudioSeq_SeqChannelSetLayer(SequenceChannel* channel, s32 layerIndex) { layer->channel = channel; layer->adsr = channel->adsr; layer->adsr.decayIndex = 0; - layer->unk_09 = channel->reverb; + layer->targetReverbVol = channel->targetReverbVol; layer->enabled = true; layer->finished = false; layer->stopSomething = false; @@ -355,8 +355,8 @@ s32 AudioSeq_SeqChannelSetLayer(SequenceChannel* channel, s32 layerIndex) { layer->bit1 = false; layer->notePropertiesNeedInit = false; layer->gateTime = 0x80; - layer->unk_08 = 0x80; - layer->stereo.asByte = 0; + layer->surroundEffectIndex = 0x80; + layer->stereoData.asByte = 0; layer->portamento.mode = PORTAMENTO_MODE_OFF; layer->scriptState.depth = 0; layer->pan = 0x40; @@ -782,7 +782,7 @@ s32 AudioSeq_SeqLayerProcessScriptStep2(SequenceLayer* layer) { break; case 0xCD: // layer: stereo effects - layer->stereo.asByte = AudioSeq_ScriptReadU8(state); + layer->stereoData.asByte = AudioSeq_ScriptReadU8(state); break; case 0xCE: // layer: bend pitch @@ -796,7 +796,7 @@ s32 AudioSeq_SeqLayerProcessScriptStep2(SequenceLayer* layer) { break; case 0xF1: // layer: - layer->unk_08 = AudioSeq_ScriptReadU8(state); + layer->surroundEffectIndex = AudioSeq_ScriptReadU8(state); break; default: @@ -991,13 +991,14 @@ s32 AudioSeq_SeqLayerProcessScriptStep4(SequenceLayer* layer, s32 cmd) { if (layer->delay == 0) { if (layer->tunedSample != NULL) { - time = layer->tunedSample->sample->loop->end; + time = layer->tunedSample->sample->loop->loopEnd; } else { time = 0.0f; } time *= seqPlayer->tempo; time *= gAudioContext.unk_2870; time /= layer->freqScale; + //! FAKE: if (1) {} if (time > 0x7FFE) { time = 0x7FFE; @@ -1416,9 +1417,9 @@ void AudioSeq_SequenceChannelProcessScript(SequenceChannel* channel) { channel->vibrato.vibratoDelay = cmd * 16; break; - case 0xD4: // channel: set reverb + case 0xD4: // channel: set reverb volume cmd = (u8)cmdArgs[0]; - channel->reverb = cmd; + channel->targetReverbVol = cmd; break; case 0xC6: // channel: set soundFont @@ -1491,7 +1492,7 @@ void AudioSeq_SequenceChannelProcessScript(SequenceChannel* channel) { } else { channel->stereoHeadsetEffects = false; } - channel->stereo.asByte = cmd & 0x7F; + channel->stereoData.asByte = cmd & 0x7F; break; case 0xD1: // channel: set note allocation policy @@ -1537,7 +1538,7 @@ void AudioSeq_SequenceChannelProcessScript(SequenceChannel* channel) { data += 4; channel->newPan = data[-3]; channel->panChannelWeight = data[-2]; - channel->reverb = data[-1]; + channel->targetReverbVol = data[-1]; channel->reverbIndex = data[0]; //! @bug: Not marking reverb state as changed channel->changes.s.pan = true; @@ -1551,7 +1552,7 @@ void AudioSeq_SequenceChannelProcessScript(SequenceChannel* channel) { channel->transposition = (s8)AudioSeq_ScriptReadU8(scriptState); channel->newPan = AudioSeq_ScriptReadU8(scriptState); channel->panChannelWeight = AudioSeq_ScriptReadU8(scriptState); - channel->reverb = AudioSeq_ScriptReadU8(scriptState); + channel->targetReverbVol = AudioSeq_ScriptReadU8(scriptState); channel->reverbIndex = AudioSeq_ScriptReadU8(scriptState); //! @bug: Not marking reverb state as changed channel->changes.s.pan = true; @@ -1569,8 +1570,8 @@ void AudioSeq_SequenceChannelProcessScript(SequenceChannel* channel) { channel->adsr.sustain = 0; channel->velocityRandomVariance = 0; channel->gateTimeRandomVariance = 0; - channel->unk_0F = 0; - channel->unk_20 = 0; + channel->combFilterSize = 0; + channel->combFilterGain = 0; channel->bookOffset = 0; channel->startSamplePos = 0; channel->unk_E0 = 0; @@ -1651,8 +1652,8 @@ void AudioSeq_SequenceChannelProcessScript(SequenceChannel* channel) { break; case 0xBB: // channel: - channel->unk_0F = cmdArgs[0]; - channel->unk_20 = cmdArgs[1]; + channel->combFilterSize = cmdArgs[0]; + channel->combFilterGain = cmdArgs[1]; break; case 0xBC: // channel: add large @@ -1667,8 +1668,8 @@ void AudioSeq_SequenceChannelProcessScript(SequenceChannel* channel) { if (cmdArgs[0] < 5) { if (1) {} if (gAudioContext.unk_29A8[cmdArgs[0]] != NULL) { - D_80208E6C = gAudioContext.unk_29A8[cmdArgs[0]]; - scriptState->value = D_80208E6C(scriptState->value, channel); + gCustomAudioSeqFunction = gAudioContext.unk_29A8[cmdArgs[0]]; + scriptState->value = gCustomAudioSeqFunction(scriptState->value, channel); } } break; @@ -1693,7 +1694,7 @@ void AudioSeq_SequenceChannelProcessScript(SequenceChannel* channel) { break; case 0xA4: // channel: - channel->unk_10 = cmdArgs[0]; + channel->surroundEffectIndex = cmdArgs[0]; break; case 0xA5: // channel: @@ -2197,7 +2198,7 @@ void AudioSeq_ProcessSequences(s32 arg0) { SequencePlayer* seqPlayer; u32 i; - gAudioContext.noteSubEuOffset = + gAudioContext.sampleStateOffset = (gAudioContext.audioBufferParameters.updatesPerFrame - arg0 - 1) * gAudioContext.numNotes; for (i = 0; i < (u32)gAudioContext.audioBufferParameters.numSequencePlayers; i++) { diff --git a/src/code/audio/audio_synthesis.c b/src/code/audio/audio_synthesis.c index 51842a33f..17b3ff4be 100644 --- a/src/code/audio/audio_synthesis.c +++ b/src/code/audio/audio_synthesis.c @@ -1,137 +1,1698 @@ #include "global.h" -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_801877D0.s") +// DMEM Addresses for the RSP +#define DMEM_TEMP 0x3B0 +#define DMEM_TEMP2 0x3C0 +#define DMEM_SURROUND_TEMP 0x4B0 +#define DMEM_UNCOMPRESSED_NOTE 0x570 +#define DMEM_HAAS_TEMP 0x5B0 +#define DMEM_COMB_TEMP 0x750 // = DMEM_TEMP + DMEM_2CH_SIZE + a bit more +#define DMEM_COMPRESSED_ADPCM_DATA 0x930 // = DMEM_LEFT_CH +#define DMEM_LEFT_CH 0x930 +#define DMEM_RIGHT_CH 0xAD0 +#define DMEM_WET_TEMP 0x3D0 +#define DMEM_WET_SCRATCH 0x710 // = DMEM_WET_TEMP + DMEM_2CH_SIZE +#define DMEM_WET_LEFT_CH 0xC70 +#define DMEM_WET_RIGHT_CH 0xE10 // = 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_SaveResampledReverbSamplesImpl(Acmd* cmd, u16 dmem, u16 arg2, uintptr_t arg3); +Acmd* AudioSynth_LoadReverbSamplesImpl(Acmd* cmd, u16 dmem, u16 startPos, s32 size, SynthesisReverb* reverb); +Acmd* AudioSynth_SaveReverbSamplesImpl(Acmd* cmd, u16 dmem, u16 startPos, s32 size, SynthesisReverb* reverb); +Acmd* AudioSynth_ProcessSamples(s16* aiBuf, s32 numSamplesPerUpdate, Acmd* cmd, s32 updateIndex); +Acmd* AudioSynth_ProcessSample(s32 noteIndex, NoteSampleState* sampleState, NoteSynthesisState* synthState, s16* aiBuf, + s32 numSamplesPerUpdate, Acmd* cmd, s32 updateIndex); +Acmd* AudioSynth_ApplySurroundEffect(Acmd* cmd, NoteSampleState* sampleState, NoteSynthesisState* synthState, s32 size, + s32 dmem, s32 flags); +Acmd* AudioSynth_FinalResample(Acmd* cmd, NoteSynthesisState* synthState, s32 size, u16 pitch, u16 inpDmem, + s32 resampleFlags); +Acmd* AudioSynth_ProcessEnvelope(Acmd* cmd, NoteSampleState* sampleState, NoteSynthesisState* synthState, + s32 numSamplesPerUpdate, u16 dmemSrc, s32 haasEffectDelaySide, s32 flags); +Acmd* AudioSynth_LoadWaveSamples(Acmd* cmd, NoteSampleState* sampleState, NoteSynthesisState* synthState, + s32 numSamplesToLoad); +Acmd* AudioSynth_ApplyHaasEffect(Acmd* cmd, NoteSampleState* sampleState, NoteSynthesisState* synthState, s32 size, + s32 flags, s32 haasEffectDelaySide); + +s32 D_801D5FB0 = 0; + +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 = + AUDIO_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 = + AUDIO_MK_CMD(DMEM_LEFT_CH >> 4, DMEM_HAAS_TEMP >> 4, DMEM_WET_LEFT_CH >> 4, DMEM_WET_RIGHT_CH >> 4); + +u32 sEnvMixerDefaultDmemDests = + AUDIO_MK_CMD(DMEM_LEFT_CH >> 4, DMEM_RIGHT_CH >> 4, DMEM_WET_LEFT_CH >> 4, DMEM_WET_RIGHT_CH >> 4); + +// Unused Data +u16 D_801D5FC4[] = { + 0x7FFF, 0xD001, 0x3FFF, 0xF001, 0x5FFF, 0x9001, 0x7FFF, 0x8001, +}; + +u8 sNumSamplesPerWavePeriod[] = { + WAVE_SAMPLE_COUNT / 1, // 1st harmonic + WAVE_SAMPLE_COUNT / 2, // 2nd harmonic + WAVE_SAMPLE_COUNT / 4, // 4th harmonic + WAVE_SAMPLE_COUNT / 8, // 8th harmonic +}; + +/** + * Add a collection of s16-samples as a single entry to the reverb buffer + */ +void AudioSynth_AddReverbBufferEntry(s32 numSamples, s32 updateIndex, s32 reverbIndex) { + SynthesisReverb* reverb; + ReverbBufferEntry* entry; + s32 extraSamples; + s32 numSamplesAfterDownsampling; + s32 reverbBufPos; + s32 temp_t2; + s32 temp_t4; + s32 count1; + s32 count2; + s32 nextReverbSubBufPos; + + reverb = &gAudioContext.synthesisReverbs[reverbIndex]; + entry = &reverb->bufEntry[reverb->curFrame][updateIndex]; + + numSamplesAfterDownsampling = numSamples / gAudioContext.synthesisReverbs[reverbIndex].downsampleRate; + + // Apply resampling effect + if (gAudioContext.synthesisReverbs[reverbIndex].resampleEffectOn) { + if (reverb->downsampleRate == 1) { + count1 = 0; + count2 = 0; + + numSamplesAfterDownsampling += reverb->resampleEffectExtraSamples; + + entry->saveResampleNumSamples = numSamplesAfterDownsampling; + entry->loadResamplePitch = ((u16)numSamplesAfterDownsampling << 0xF) / numSamples; + entry->saveResamplePitch = (numSamples << 0xF) / (u16)numSamplesAfterDownsampling; + + while (true) { + temp_t2 = (entry->loadResamplePitch * numSamples * 2) + reverb->resampleEffectLoadUnk; + temp_t4 = temp_t2 >> 0x10; + + if ((temp_t4 != numSamplesAfterDownsampling) && (count1 == 0)) { + entry->loadResamplePitch = + ((numSamplesAfterDownsampling << 0x10) - reverb->resampleEffectLoadUnk) / (numSamples * 2); + count1++; + } else { + count1++; + if (temp_t4 > numSamplesAfterDownsampling) { + entry->loadResamplePitch--; + } else if (temp_t4 < numSamplesAfterDownsampling) { + entry->loadResamplePitch++; + } else { + break; + } + } + } + + reverb->resampleEffectLoadUnk = temp_t2 & 0xFFFF; + + while (true) { + temp_t2 = (entry->saveResamplePitch * numSamplesAfterDownsampling * 2) + reverb->resampleEffectSaveUnk; + temp_t4 = temp_t2 >> 0x10; + + if ((temp_t4 != numSamples) && (count2 == 0)) { + entry->saveResamplePitch = + ((numSamples << 0x10) - reverb->resampleEffectSaveUnk) / (numSamplesAfterDownsampling * 2); + count2++; + } else { + count2++; + if (temp_t4 > numSamples) { + entry->saveResamplePitch--; + } else if (temp_t4 < numSamples) { + entry->saveResamplePitch++; + } else { + break; + } + } + } + + reverb->resampleEffectSaveUnk = temp_t2 & 0xFFFF; + } + } + + extraSamples = (reverb->nextReverbBufPos + numSamplesAfterDownsampling) - reverb->delayNumSamples; + reverbBufPos = reverb->nextReverbBufPos; + + // Add a reverb entry + if (extraSamples < 0) { + entry->size = numSamplesAfterDownsampling * SAMPLE_SIZE; + entry->wrappedSize = 0; + entry->startPos = reverb->nextReverbBufPos; + reverb->nextReverbBufPos += numSamplesAfterDownsampling; + } else { + // End of the buffer is reached. Loop back around + entry->size = (numSamplesAfterDownsampling - extraSamples) * SAMPLE_SIZE; + entry->wrappedSize = extraSamples * SAMPLE_SIZE; + entry->startPos = reverb->nextReverbBufPos; + reverb->nextReverbBufPos = extraSamples; + } + + entry->numSamplesAfterDownsampling = numSamplesAfterDownsampling; + entry->numSamples = numSamples; + + // Add a sub-reverb entry + if (reverb->subDelay != 0) { + nextReverbSubBufPos = reverb->subDelay + reverbBufPos; + if (nextReverbSubBufPos >= reverb->delayNumSamples) { + nextReverbSubBufPos -= reverb->delayNumSamples; + } + + entry = &reverb->subBufEntry[reverb->curFrame][updateIndex]; + numSamplesAfterDownsampling = numSamples / reverb->downsampleRate; + extraSamples = (nextReverbSubBufPos + numSamplesAfterDownsampling) - reverb->delayNumSamples; + + if (extraSamples < 0) { + entry->size = numSamplesAfterDownsampling * SAMPLE_SIZE; + entry->wrappedSize = 0; + entry->startPos = nextReverbSubBufPos; + } else { + // End of the buffer is reached. Loop back around + entry->size = (numSamplesAfterDownsampling - extraSamples) * SAMPLE_SIZE; + entry->wrappedSize = extraSamples * SAMPLE_SIZE; + entry->startPos = nextReverbSubBufPos; + } + + entry->numSamplesAfterDownsampling = numSamplesAfterDownsampling; + entry->numSamples = numSamples; + } +} + +/** + * Sync the sample states between the notes and the list + */ +void AudioSynth_SyncSampleStates(s32 updateIndex) { + NoteSampleState* noteSampleState; + NoteSampleState* sampleState; + s32 sampleStateBaseIndex; + s32 i; + + sampleStateBaseIndex = gAudioContext.numNotes * updateIndex; + for (i = 0; i < gAudioContext.numNotes; i++) { + noteSampleState = &gAudioContext.notes[i].sampleState; + sampleState = &gAudioContext.sampleStateList[sampleStateBaseIndex + i]; + if (noteSampleState->bitField0.enabled) { + noteSampleState->bitField0.needsInit = false; + } else { + sampleState->bitField0.enabled = false; + } + + noteSampleState->harmonicIndexCurAndPrev = 0; + } +} + +Acmd* AudioSynth_Update(Acmd* abiCmdStart, s32* numAbiCmds, s16* aiBufStart, s32 numSamplesPerFrame) { + s32 numSamplesPerUpdate; + s16* curAiBufPos; + Acmd* curCmd = abiCmdStart; + s32 reverseUpdateIndex; + s32 reverbIndex; + SynthesisReverb* reverb; + + for (reverseUpdateIndex = gAudioContext.audioBufferParameters.updatesPerFrame; reverseUpdateIndex > 0; + reverseUpdateIndex--) { + AudioSeq_ProcessSequences(reverseUpdateIndex - 1); + AudioSynth_SyncSampleStates(gAudioContext.audioBufferParameters.updatesPerFrame - reverseUpdateIndex); + } + + curAiBufPos = aiBufStart; + gAudioContext.adpcmCodeBook = NULL; + + // Process/Update all samples multiple times in a single frame + for (reverseUpdateIndex = gAudioContext.audioBufferParameters.updatesPerFrame; reverseUpdateIndex > 0; + reverseUpdateIndex--) { + if (reverseUpdateIndex == 1) { + // Final Update + numSamplesPerUpdate = numSamplesPerFrame; + } else if ((numSamplesPerFrame / reverseUpdateIndex) >= + gAudioContext.audioBufferParameters.numSamplesPerUpdateMax) { + numSamplesPerUpdate = gAudioContext.audioBufferParameters.numSamplesPerUpdateMax; + } else if ((numSamplesPerFrame / reverseUpdateIndex) <= + gAudioContext.audioBufferParameters.numSamplesPerUpdateMin) { + numSamplesPerUpdate = gAudioContext.audioBufferParameters.numSamplesPerUpdateMin; + } else { + numSamplesPerUpdate = gAudioContext.audioBufferParameters.numSamplesPerUpdate; + } + + for (reverbIndex = 0; reverbIndex < gAudioContext.numSynthesisReverbs; reverbIndex++) { + if (gAudioContext.synthesisReverbs[reverbIndex].useReverb) { + AudioSynth_AddReverbBufferEntry( + numSamplesPerUpdate, gAudioContext.audioBufferParameters.updatesPerFrame - reverseUpdateIndex, + reverbIndex); + } + } + + curCmd = AudioSynth_ProcessSamples(curAiBufPos, numSamplesPerUpdate, curCmd, + gAudioContext.audioBufferParameters.updatesPerFrame - reverseUpdateIndex); + numSamplesPerFrame -= numSamplesPerUpdate; + curAiBufPos += numSamplesPerUpdate * SAMPLE_SIZE; + } + + // Update reverb frame info + for (reverbIndex = 0; reverbIndex < gAudioContext.numSynthesisReverbs; reverbIndex++) { + if (gAudioContext.synthesisReverbs[reverbIndex].framesToIgnore != 0) { + gAudioContext.synthesisReverbs[reverbIndex].framesToIgnore--; + } + gAudioContext.synthesisReverbs[reverbIndex].curFrame ^= 1; + } + + *numAbiCmds = curCmd - abiCmdStart; + return curCmd; +} + +void AudioSynth_DisableSampleStates(s32 updateIndex, s32 noteIndex) { + NoteSampleState* sampleState; + s32 i; + + for (i = updateIndex + 1; i < gAudioContext.audioBufferParameters.updatesPerFrame; i++) { + sampleState = &gAudioContext.sampleStateList[(gAudioContext.numNotes * i) + noteIndex]; + if (sampleState->bitField0.needsInit) { + break; + } + sampleState->bitField0.enabled = false; + } +} + +/** + * Load reverb samples from a different reverb index + */ +Acmd* AudioSynth_LoadMixedReverbSamples(Acmd* cmd, SynthesisReverb* reverb, s16 updateIndex) { + ReverbBufferEntry* entry = &reverb->bufEntry[reverb->curFrame][updateIndex]; + + cmd = AudioSynth_LoadReverbSamplesImpl(cmd, DMEM_WET_TEMP, entry->startPos, entry->size, reverb); + if (entry->wrappedSize != 0) { + // Ring buffer wrapped + cmd = AudioSynth_LoadReverbSamplesImpl(cmd, DMEM_WET_TEMP + entry->size, 0, entry->wrappedSize, reverb); + } + return cmd; +} + +/** + * Save reverb samples from a different reverb index + */ +Acmd* AudioSynth_SaveMixedReverbSamples(Acmd* cmd, SynthesisReverb* reverb, s16 updateIndex) { + ReverbBufferEntry* entry = &reverb->bufEntry[reverb->curFrame][updateIndex]; + + cmd = AudioSynth_SaveReverbSamplesImpl(cmd, DMEM_WET_TEMP, entry->startPos, entry->size, reverb); + if (entry->wrappedSize != 0) { + // Ring buffer wrapped + cmd = AudioSynth_SaveReverbSamplesImpl(cmd, DMEM_WET_TEMP + entry->size, 0, entry->wrappedSize, reverb); + } + return cmd; +} + +void AudioSynth_Noop1(void) { +} + +void AudioSynth_ClearBuffer(Acmd* cmd, s32 dmem, s32 size) { + aClearBuffer(cmd, dmem, size); +} + +void AudioSynth_Noop2(void) { +} + +void AudioSynth_Noop3(void) { +} + +void AudioSynth_Noop4(void) { +} + +void AudioSynth_Mix(Acmd* cmd, size_t size, s32 gain, s32 dmemIn, s32 dmemOut) { + aMix(cmd, size, gain, dmemIn, dmemOut); +} + +void AudioSynth_Noop5(void) { +} + +void AudioSynth_Noop6(void) { +} + +void AudioSynth_Noop7(void) { +} + +void AudioSynth_SetBuffer(Acmd* cmd, s32 flags, s32 dmemIn, s32 dmemOut, size_t size) { + aSetBuffer(cmd, flags, dmemIn, dmemOut, size); +} + +void AudioSynth_Noop8(void) { +} + +void AudioSynth_Noop9(void) { +} + +void AudioSynth_DMemMove(Acmd* cmd, s32 dmemIn, s32 dmemOut, size_t size) { + // aDMEMMove(cmd, dmemIn, dmemOut, 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 AudioSynth_Noop10(void) { +} + +void AudioSynth_Noop11(void) { +} + +void AudioSynth_Noop12(void) { +} + +void AudioSynth_Noop13(void) { +} + +void AudioSynth_InterL(Acmd* cmd, s32 dmemIn, s32 dmemOut, s32 numSamples) { + // aInterl(cmd, dmemIn, dmemOut, 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 reverbVol, s32 rampReverb, s32 rampLeft, s32 rampRight) { + aEnvSetup1(cmd, reverbVol, rampReverb, rampLeft, rampRight); +} + +void AudioSynth_Noop14(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); +} -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80187B64.s") +void AudioSynth_EnvSetup2(Acmd* cmd, s32 volLeft, s32 volRight) { + // aEnvSetup2(cmd, volLeft, volRight); + cmd->words.w0 = _SHIFTL(A_ENVSETUP2, 24, 8); + cmd->words.w1 = _SHIFTL(volLeft, 16, 16) | _SHIFTL(volRight, 0, 16); +} -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80187BEC.s") +void AudioSynth_Noop15(void) { +} -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80187DE8.s") +void AudioSynth_Noop16(void) { +} -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80187E58.s") +void AudioSynth_Noop17(void) { +} -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80187F00.s") +void AudioSynth_S8Dec(Acmd* cmd, s32 flags, s16* state) { + aS8Dec(cmd, flags, state); +} -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80187FA8.s") +void AudioSynth_HiLoGain(Acmd* cmd, s32 gain, s32 dmemIn, s32 dmemOut, s32 size) { + // aHiLoGain(cmd, gain, size, dmemIn, dmemOut); + 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); +} -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80187FB0.s") +// Remnant of OoT +void AudioSynth_UnkCmd19(Acmd* cmd, s32 dmem1, s32 dmem2, s32 size, s32 arg4) { + cmd->words.w0 = _SHIFTL(A_SPNOOP, 24, 8) | _SHIFTL(arg4, 16, 8) | _SHIFTL(size, 0, 16); + cmd->words.w1 = _SHIFTL(dmem1, 16, 16) | _SHIFTL(dmem2, 0, 16); +} -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80187FD0.s") +void AudioSynth_Noop18(void) { +} -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80187FD8.s") +void AudioSynth_Noop19(void) { +} -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80187FE0.s") +void AudioSynth_Noop20(void) { +} -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80187FE8.s") +void AudioSynth_Noop21(void) { +} -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_8018801C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80188024.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_8018802C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80188034.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80188068.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80188070.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80188078.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_801880A4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_801880AC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_801880B4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_801880BC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_801880C4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_801880E8.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_8018811C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80188124.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_8018814C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80188174.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80188190.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80188198.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_801881A0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_801881A8.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_801881C4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_801881F8.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80188224.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_8018822C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80188234.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_8018823C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80188244.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_8018824C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80188254.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_8018825C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80188264.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80188288.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_801882A0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80188304.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_801884A0.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80188698.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_8018883C.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_801888E4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_801889A4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80188A50.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80188AFC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80188C48.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80188CB4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80188D20.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80188D28.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80188D68.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80188DDC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80188FBC.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80189064.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_80189620.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_8018A4B4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_8018A768.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_8018A808.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_8018ACC4.s") - -#pragma GLOBAL_ASM("asm/non_matchings/code/audio_synthesis/func_8018AE34.s") +void AudioSynth_Noop22(void) { +} + +void AudioSynth_Noop23(void) { +} + +void AudioSynth_Noop24(void) { +} + +void AudioSynth_Noop25(void) { +} + +void AudioSynth_LoadFilterBuffer(Acmd* cmd, s32 flags, s32 buf, s16* addr) { + aFilter(cmd, flags, buf, addr); +} + +void AudioSynth_LoadFilterSize(Acmd* cmd, size_t size, s16* addr) { + aFilter(cmd, 2, size, addr); +} + +/** + * 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* AudioSynth_LoadDownsampledReverbSamples(Acmd* cmd, s32 numSamplesPerUpdate, SynthesisReverb* reverb, + s16 updateIndex) { + ReverbBufferEntry* entry = &reverb->bufEntry[reverb->curFrame][updateIndex]; + s16 offsetSize = (entry->startPos & 7) * SAMPLE_SIZE; + s16 wrappedOffsetSize = ALIGN16(offsetSize + entry->size); + + cmd = AudioSynth_LoadReverbSamplesImpl(cmd, DMEM_WET_TEMP, entry->startPos - (offsetSize / (s32)SAMPLE_SIZE), + DMEM_1CH_SIZE, reverb); + + if (entry->wrappedSize != 0) { + // Ring buffer wrapped + cmd = AudioSynth_LoadReverbSamplesImpl(cmd, DMEM_WET_TEMP + wrappedOffsetSize, 0, + DMEM_1CH_SIZE - wrappedOffsetSize, reverb); + } + + aSetBuffer(cmd++, 0, DMEM_WET_TEMP + offsetSize, DMEM_WET_LEFT_CH, numSamplesPerUpdate * SAMPLE_SIZE); + aResample(cmd++, reverb->resampleFlags, reverb->downsamplePitch, reverb->leftLoadResampleBuf); + aSetBuffer(cmd++, 0, DMEM_WET_TEMP + DMEM_1CH_SIZE + offsetSize, DMEM_WET_RIGHT_CH, + numSamplesPerUpdate * SAMPLE_SIZE); + aResample(cmd++, reverb->resampleFlags, reverb->downsamplePitch, reverb->rightLoadResampleBuf); + + return cmd; +} + +Acmd* AudioSynth_SaveResampledReverbSamples(Acmd* cmd, SynthesisReverb* reverb, s16 updateIndex) { + ReverbBufferEntry* entry = &reverb->bufEntry[reverb->curFrame][updateIndex]; + s16 numSamples = entry->numSamples; + u32 size = numSamples * SAMPLE_SIZE; + + // Left Resample + aDMEMMove(cmd++, DMEM_WET_LEFT_CH, DMEM_WET_TEMP, size); + aSetBuffer(cmd++, 0, DMEM_WET_TEMP, DMEM_WET_SCRATCH, entry->saveResampleNumSamples * SAMPLE_SIZE); + aResample(cmd++, reverb->resampleFlags, entry->saveResamplePitch, reverb->leftSaveResampleBuf); + + cmd = AudioSynth_SaveResampledReverbSamplesImpl(cmd, DMEM_WET_SCRATCH, entry->size, + &reverb->leftReverbBuf[entry->startPos]); + + if (entry->wrappedSize != 0) { + // Ring buffer wrapped + cmd = AudioSynth_SaveResampledReverbSamplesImpl(cmd, entry->size + DMEM_WET_SCRATCH, entry->wrappedSize, + reverb->leftReverbBuf); + } + + // Right Resample + aDMEMMove(cmd++, DMEM_WET_RIGHT_CH, DMEM_WET_TEMP, size); + aSetBuffer(cmd++, 0, DMEM_WET_TEMP, DMEM_WET_SCRATCH, entry->saveResampleNumSamples * SAMPLE_SIZE); + aResample(cmd++, reverb->resampleFlags, entry->saveResamplePitch, reverb->rightSaveResampleBuf); + + cmd = AudioSynth_SaveResampledReverbSamplesImpl(cmd, DMEM_WET_SCRATCH, entry->size, + &reverb->rightReverbBuf[entry->startPos]); + + if (entry->wrappedSize != 0) { + // Ring buffer wrapped + cmd = AudioSynth_SaveResampledReverbSamplesImpl(cmd, entry->size + DMEM_WET_SCRATCH, entry->wrappedSize, + reverb->rightReverbBuf); + } + + return cmd; +} + +Acmd* AudioSynth_LoadResampledReverbSamples(Acmd* cmd, s32 numSamplesPerUpdate, SynthesisReverb* reverb, + s16 updateIndex) { + ReverbBufferEntry* entry = &reverb->bufEntry[reverb->curFrame][updateIndex]; + s16 offsetSize = (entry->startPos & 7) * SAMPLE_SIZE; + s16 wrappedOffsetSize = ALIGN16(offsetSize + entry->size); + + cmd = AudioSynth_LoadReverbSamplesImpl(cmd, DMEM_WET_TEMP, entry->startPos - (offsetSize / (s32)SAMPLE_SIZE), + DMEM_1CH_SIZE, reverb); + + if (entry->wrappedSize != 0) { + // Ring buffer wrapped + cmd = AudioSynth_LoadReverbSamplesImpl(cmd, wrappedOffsetSize + DMEM_WET_TEMP, 0, + DMEM_1CH_SIZE - wrappedOffsetSize, reverb); + } + + aSetBuffer(cmd++, 0, DMEM_WET_TEMP + offsetSize, DMEM_WET_LEFT_CH, numSamplesPerUpdate * SAMPLE_SIZE); + aResample(cmd++, reverb->resampleFlags, entry->loadResamplePitch, reverb->leftLoadResampleBuf); + aSetBuffer(cmd++, 0, DMEM_WET_TEMP + DMEM_1CH_SIZE + offsetSize, DMEM_WET_RIGHT_CH, + numSamplesPerUpdate * SAMPLE_SIZE); + aResample(cmd++, reverb->resampleFlags, entry->loadResamplePitch, reverb->rightLoadResampleBuf); + + 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; +} + +/** + * Mix in reverb from a different reverb index + */ +Acmd* AudioSynth_MixOtherReverbIndex(Acmd* cmd, SynthesisReverb* reverb, s32 updateIndex) { + SynthesisReverb* mixReverb; + + if (reverb->mixReverbIndex >= gAudioContext.numSynthesisReverbs) { + return cmd; + } + + mixReverb = &gAudioContext.synthesisReverbs[reverb->mixReverbIndex]; + if (mixReverb->downsampleRate == 1) { + cmd = AudioSynth_LoadMixedReverbSamples(cmd, mixReverb, updateIndex); + aMix(cmd++, DMEM_2CH_SIZE >> 4, reverb->mixReverbStrength, DMEM_WET_LEFT_CH, DMEM_WET_TEMP); + cmd = AudioSynth_SaveMixedReverbSamples(cmd, mixReverb, updateIndex); + } + + return cmd; +} + +Acmd* AudioSynth_LoadDefaultReverbSamples(Acmd* cmd, s32 numSamplesPerUpdate, SynthesisReverb* reverb, + s16 updateIndex) { + ReverbBufferEntry* entry = &reverb->bufEntry[reverb->curFrame][updateIndex]; + + cmd = AudioSynth_LoadReverbSamplesImpl(cmd, DMEM_WET_LEFT_CH, entry->startPos, entry->size, reverb); + if (entry->wrappedSize != 0) { + // Ring buffer wrapped + cmd = AudioSynth_LoadReverbSamplesImpl(cmd, DMEM_WET_LEFT_CH + entry->size, 0, entry->wrappedSize, reverb); + } + + return cmd; +} + +Acmd* AudioSynth_LoadSubReverbSamples(Acmd* cmd, s32 numSamplesPerUpdate, SynthesisReverb* reverb, s16 updateIndex) { + ReverbBufferEntry* subEntry = &reverb->subBufEntry[reverb->curFrame][updateIndex]; + + cmd = AudioSynth_LoadReverbSamplesImpl(cmd, DMEM_WET_LEFT_CH, subEntry->startPos, subEntry->size, reverb); + if (subEntry->wrappedSize != 0) { + // Ring buffer wrapped + cmd = + AudioSynth_LoadReverbSamplesImpl(cmd, DMEM_WET_LEFT_CH + subEntry->size, 0, subEntry->wrappedSize, reverb); + } + + return cmd; +} + +Acmd* AudioSynth_SaveResampledReverbSamplesImpl(Acmd* cmd, u16 dmem, u16 size, uintptr_t startAddr) { + s32 startAddrAlignDropped; + u32 endAddr; + s32 endAddrAlignDropped; + + endAddr = startAddr + size; + + endAddrAlignDropped = endAddr & 0xF; + if (endAddrAlignDropped != 0) { + aLoadBuffer(cmd++, (endAddr - endAddrAlignDropped), DMEM_TEMP, 0x10); + aDMEMMove(cmd++, dmem, DMEM_TEMP2, size); + aDMEMMove(cmd++, DMEM_TEMP + endAddrAlignDropped, size + DMEM_TEMP2, 0x10 - endAddrAlignDropped); + + size += (0x10 - endAddrAlignDropped); + dmem = DMEM_TEMP2; + } + + startAddrAlignDropped = startAddr & 0xF; + if (startAddrAlignDropped != 0) { + aLoadBuffer(cmd++, startAddr - startAddrAlignDropped, DMEM_TEMP, 0x10); + aDMEMMove(cmd++, dmem, startAddrAlignDropped + DMEM_TEMP, size); + + size += startAddrAlignDropped; + dmem = DMEM_TEMP; + } + + aSaveBuffer(cmd++, dmem, startAddr - startAddrAlignDropped, size); + + return cmd; +} + +Acmd* AudioSynth_LoadReverbSamplesImpl(Acmd* cmd, u16 dmem, u16 startPos, s32 size, SynthesisReverb* reverb) { + aLoadBuffer(cmd++, &reverb->leftReverbBuf[startPos], dmem, size); + aLoadBuffer(cmd++, &reverb->rightReverbBuf[startPos], dmem + DMEM_1CH_SIZE, size); + + return cmd; +} + +Acmd* AudioSynth_SaveReverbSamplesImpl(Acmd* cmd, u16 dmem, u16 startPos, s32 size, SynthesisReverb* reverb) { + aSaveBuffer(cmd++, dmem, &reverb->leftReverbBuf[startPos], size); + aSaveBuffer(cmd++, dmem + DMEM_1CH_SIZE, &reverb->rightReverbBuf[startPos], size); + + return cmd; +} + +void AudioSynth_Noop26(void) { +} + +Acmd* AudioSynth_LoadSubReverbSamplesWithoutDownsample(Acmd* cmd, s32 numSamplesPerUpdate, SynthesisReverb* reverb, + s16 updateIndex) { + if (reverb->downsampleRate == 1) { + cmd = AudioSynth_LoadSubReverbSamples(cmd, numSamplesPerUpdate, reverb, updateIndex); + } + + return cmd; +} + +Acmd* AudioSynth_LoadReverbSamples(Acmd* cmd, s32 numSamplesPerUpdate, SynthesisReverb* reverb, s16 updateIndex) { + if (reverb->downsampleRate == 1) { + if (reverb->resampleEffectOn) { + cmd = AudioSynth_LoadResampledReverbSamples(cmd, numSamplesPerUpdate, reverb, updateIndex); + } else { + cmd = AudioSynth_LoadDefaultReverbSamples(cmd, numSamplesPerUpdate, reverb, updateIndex); + } + } else { + cmd = AudioSynth_LoadDownsampledReverbSamples(cmd, numSamplesPerUpdate, reverb, updateIndex); + } + + return cmd; +} + +Acmd* AudioSynth_SaveReverbSamples(Acmd* cmd, SynthesisReverb* reverb, s16 updateIndex) { + ReverbBufferEntry* entry = &reverb->bufEntry[reverb->curFrame][updateIndex]; + s32 downsampleRate; + s32 numSamples; + + if (reverb->downsampleRate == 1) { + if (reverb->resampleEffectOn) { + cmd = AudioSynth_SaveResampledReverbSamples(cmd, reverb, updateIndex); + } else { + // Put the oldest samples in the ring buffer into the wet channels + cmd = AudioSynth_SaveReverbSamplesImpl(cmd, DMEM_WET_LEFT_CH, entry->startPos, entry->size, reverb); + if (entry->wrappedSize != 0) { + // Ring buffer wrapped + cmd = AudioSynth_SaveReverbSamplesImpl(cmd, DMEM_WET_LEFT_CH + entry->size, 0, entry->wrappedSize, + reverb); + } + } + } else { + //! FAKE: + if (1) {} + + downsampleRate = reverb->downsampleRate; + numSamples = 13 * SAMPLES_PER_FRAME; + + while (downsampleRate >= 2) { + aInterl(cmd++, DMEM_WET_LEFT_CH, DMEM_WET_LEFT_CH, numSamples); + aInterl(cmd++, DMEM_WET_RIGHT_CH, DMEM_WET_RIGHT_CH, numSamples); + downsampleRate >>= 1; + numSamples >>= 1; + } + + if (entry->size != 0) { + cmd = AudioSynth_SaveResampledReverbSamplesImpl(cmd, DMEM_WET_LEFT_CH, entry->size, + &reverb->leftReverbBuf[entry->startPos]); + cmd = AudioSynth_SaveResampledReverbSamplesImpl(cmd, DMEM_WET_RIGHT_CH, entry->size, + &reverb->rightReverbBuf[entry->startPos]); + } + + if (entry->wrappedSize != 0) { + cmd = AudioSynth_SaveResampledReverbSamplesImpl(cmd, entry->size + DMEM_WET_LEFT_CH, entry->wrappedSize, + reverb->leftReverbBuf); + cmd = AudioSynth_SaveResampledReverbSamplesImpl(cmd, entry->size + DMEM_WET_RIGHT_CH, entry->wrappedSize, + reverb->rightReverbBuf); + } + } + + reverb->resampleFlags = 0; + + return cmd; +} + +Acmd* AudioSynth_SaveSubReverbSamples(Acmd* cmd, SynthesisReverb* reverb, s16 updateIndex) { + ReverbBufferEntry* subEntry = &reverb->subBufEntry[reverb->curFrame][updateIndex]; + + cmd = AudioSynth_SaveReverbSamplesImpl(cmd, DMEM_WET_LEFT_CH, subEntry->startPos, subEntry->size, reverb); + if (subEntry->wrappedSize != 0) { + // Ring buffer wrapped + cmd = + AudioSynth_SaveReverbSamplesImpl(cmd, DMEM_WET_LEFT_CH + subEntry->size, 0, subEntry->wrappedSize, reverb); + } + + return cmd; +} + +/** + * Process all samples embedded in a note. Every sample has numSamplesPerUpdate processed, + * and each of those are mixed together into both DMEM_LEFT_CH and DMEM_RIGHT_CH + */ +Acmd* AudioSynth_ProcessSamples(s16* aiBuf, s32 numSamplesPerUpdate, Acmd* cmd, s32 updateIndex) { + s32 size; + u8 noteIndices[0x58]; + s16 noteCount = 0; + s16 reverbIndex; + SynthesisReverb* reverb; + s32 useReverb; + s32 sampleStateOffset = gAudioContext.numNotes * updateIndex; + s32 i; + + if (gAudioContext.numSynthesisReverbs == 0) { + for (i = 0; i < gAudioContext.numNotes; i++) { + if (gAudioContext.sampleStateList[sampleStateOffset + i].bitField0.enabled) { + noteIndices[noteCount++] = i; + } + } + } else { + NoteSampleState* sampleState; + + for (reverbIndex = 0; reverbIndex < gAudioContext.numSynthesisReverbs; reverbIndex++) { + for (i = 0; i < gAudioContext.numNotes; i++) { + sampleState = &gAudioContext.sampleStateList[sampleStateOffset + i]; + if (sampleState->bitField0.enabled && (sampleState->bitField1.reverbIndex == reverbIndex)) { + noteIndices[noteCount++] = i; + } + } + } + + for (i = 0; i < gAudioContext.numNotes; i++) { + sampleState = &gAudioContext.sampleStateList[sampleStateOffset + i]; + if (sampleState->bitField0.enabled && + (sampleState->bitField1.reverbIndex >= gAudioContext.numSynthesisReverbs)) { + noteIndices[noteCount++] = i; + } + } + } + + aClearBuffer(cmd++, DMEM_LEFT_CH, DMEM_2CH_SIZE); + + i = 0; + for (reverbIndex = 0; reverbIndex < gAudioContext.numSynthesisReverbs; reverbIndex++) { + s32 subDelay; + NoteSampleState* sampleState; + + reverb = &gAudioContext.synthesisReverbs[reverbIndex]; + useReverb = reverb->useReverb; + if (useReverb) { + + // Loads reverb samples from DRAM (ringBuffer) into DMEM (DMEM_WET_LEFT_CH) + cmd = AudioSynth_LoadReverbSamples(cmd, numSamplesPerUpdate, 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 reverbs. + // So 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); + + subDelay = reverb->subDelay; + if (subDelay != 0) { + 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); + + if (((reverb->leakRtl != 0) || (reverb->leakLtr != 0)) && (gAudioContext.soundMode != SOUNDMODE_MONO)) { + cmd = AudioSynth_LeakReverb(cmd, reverb); + } + + if (subDelay != 0) { + if (reverb->mixReverbIndex != REVERB_INDEX_NONE) { + cmd = AudioSynth_MixOtherReverbIndex(cmd, reverb, updateIndex); + } + cmd = AudioSynth_SaveReverbSamples(cmd, reverb, updateIndex); + cmd = AudioSynth_LoadSubReverbSamplesWithoutDownsample(cmd, numSamplesPerUpdate, reverb, updateIndex); + aMix(cmd++, DMEM_2CH_SIZE >> 4, reverb->subVolume, DMEM_WET_TEMP, DMEM_WET_LEFT_CH); + } + } + + while (i < noteCount) { + sampleState = &gAudioContext.sampleStateList[sampleStateOffset + noteIndices[i]]; + if (sampleState->bitField1.reverbIndex != reverbIndex) { + break; + } + cmd = AudioSynth_ProcessSample(noteIndices[i], sampleState, + &gAudioContext.notes[noteIndices[i]].synthesisState, aiBuf, + numSamplesPerUpdate, cmd, updateIndex); + i++; + } + + if (useReverb) { + if ((reverb->filterLeft != NULL) || (reverb->filterRight != NULL)) { + cmd = AudioSynth_FilterReverb(cmd, numSamplesPerUpdate * SAMPLE_SIZE, reverb); + } + + // Saves the wet channel sample from DMEM (DMEM_WET_LEFT_CH) into (ringBuffer) DRAM for future use + if (subDelay != 0) { + cmd = AudioSynth_SaveSubReverbSamples(cmd, reverb, updateIndex); + } else { + if (reverb->mixReverbIndex != REVERB_INDEX_NONE) { + cmd = AudioSynth_MixOtherReverbIndex(cmd, reverb, updateIndex); + } + cmd = AudioSynth_SaveReverbSamples(cmd, reverb, updateIndex); + } + } + } + + while (i < noteCount) { + cmd = AudioSynth_ProcessSample( + noteIndices[i], &gAudioContext.sampleStateList[sampleStateOffset + noteIndices[i]], + &gAudioContext.notes[noteIndices[i]].synthesisState, aiBuf, numSamplesPerUpdate, cmd, updateIndex); + i++; + } + + size = numSamplesPerUpdate * SAMPLE_SIZE; + aInterleave(cmd++, DMEM_TEMP, DMEM_LEFT_CH, DMEM_RIGHT_CH, size); + + if (gCustomAudioSynthFunction != NULL) { + cmd = gCustomAudioSynthFunction(cmd, 2 * size, updateIndex); + } + aSaveBuffer(cmd++, DMEM_TEMP, aiBuf, 2 * size); + + return cmd; +} + +Acmd* AudioSynth_ProcessSample(s32 noteIndex, NoteSampleState* sampleState, NoteSynthesisState* synthState, s16* aiBuf, + s32 numSamplesPerUpdate, Acmd* cmd, s32 updateIndex) { + s32 pad1[2]; + void* reverbAddrSrc; + Sample* sample; + AdpcmLoop* loopInfo; + s32 numSamplesUntilEnd; + s32 numSamplesInThisIteration; + s32 sampleFinished; + s32 loopToPoint; + s32 flags; + u16 frequencyFixedPoint; + s32 gain; + s32 frameIndex; + s32 skipBytes; + void* combFilterState; + s32 numSamplesToDecode; + s32 numFirstFrameSamplesToIgnore; + u8* sampleAddr; + u32 numSamplesToLoadFixedPoint; + s32 numSamplesToLoadAdj; + s32 numSamplesProcessed; + s32 sampleEndPos; + s32 numSamplesToProcess; + s32 dmemUncompressedAddrOffset2; + s32 pad2[3]; + s32 numSamplesInFirstFrame; + s32 numTrailingSamplesToIgnore; + s32 pad3[3]; + s32 frameSize; + s32 numFramesToDecode; + s32 skipInitialSamples; + s32 zeroOffset; + u8* samplesToLoadAddr; + s32 numParts; + s32 curPart; + s32 sampleDataChunkAlignPad; + s32 haasEffectDelaySide; + s32 numSamplesToLoadFirstPart; + u16 sampleDmemBeforeResampling; + s32 sampleAddrOffset; + s32 combFilterDmem; + s32 dmemUncompressedAddrOffset1; + Note* note; + u32 numSamplesToLoad; + u16 combFilterSize; + u16 combFilterGain; + s16* filter; + s32 bookOffset = sampleState->bitField1.bookOffset; + s32 finished = sampleState->bitField0.finished; + s32 sampleDataChunkSize; + s16 sampleDataDmemAddr; + + note = &gAudioContext.notes[noteIndex]; + flags = A_CONTINUE; + + // Initialize the synthesis state + if (sampleState->bitField0.needsInit == true) { + flags = A_INIT; + synthState->atLoopPoint = false; + synthState->stopLoop = false; + synthState->samplePosInt = note->playbackState.startSamplePos; + synthState->samplePosFrac = 0; + synthState->curVolLeft = 0; + synthState->curVolRight = 0; + synthState->prevHaasEffectLeftDelaySize = 0; + synthState->prevHaasEffectRightDelaySize = 0; + synthState->curReverbVol = sampleState->targetReverbVol; + synthState->numParts = 0; + synthState->combFilterNeedsInit = true; + note->sampleState.bitField0.finished = false; + synthState->unk_1F = note->playbackState.unk_80; // Never set, never used + finished = false; + } + + // Process the sample in either one or two parts + numParts = sampleState->bitField1.hasTwoParts + 1; + + // Determine number of samples to load based on numSamplesPerUpdate and relative frequency + frequencyFixedPoint = sampleState->frequencyFixedPoint; + numSamplesToLoadFixedPoint = (frequencyFixedPoint * numSamplesPerUpdate * 2) + synthState->samplePosFrac; + numSamplesToLoad = numSamplesToLoadFixedPoint >> 16; + + if (numSamplesToLoad == 0) { + skipBytes = false; + } + + synthState->samplePosFrac = numSamplesToLoadFixedPoint & 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) && (numParts == 2)) { + } else if ((synthState->numParts == 2) && (numParts == 1)) { + } else { + } + + synthState->numParts = numParts; + + if (sampleState->bitField1.isSyntheticWave) { + cmd = AudioSynth_LoadWaveSamples(cmd, sampleState, synthState, numSamplesToLoad); + sampleDmemBeforeResampling = DMEM_UNCOMPRESSED_NOTE + (synthState->samplePosInt * 2); + synthState->samplePosInt += numSamplesToLoad; + } else { + sample = sampleState->tunedSample->sample; + loopInfo = sample->loop; + + if (note->playbackState.status != PLAYBACK_STATUS_0) { + synthState->stopLoop = true; + } + + if ((loopInfo->count == 2) && synthState->stopLoop) { + sampleEndPos = loopInfo->sampleEnd; + } else { + sampleEndPos = loopInfo->loopEnd; + } + + sampleAddr = sample->sampleAddr; + numSamplesToLoadFirstPart = 0; + + // If the frequency requested is more than double that of the raw sample, + // then the sample processing is split into two parts. + for (curPart = 0; curPart < numParts; curPart++) { + numSamplesProcessed = 0; + dmemUncompressedAddrOffset1 = 0; + + // Adjust the number of samples to load only if there are two parts and an odd number of samples + if (numParts == 1) { + numSamplesToLoadAdj = numSamplesToLoad; + } else if (numSamplesToLoad & 1) { + // round down for the first part + // round up for the second part + numSamplesToLoadAdj = (numSamplesToLoad & ~1) + (curPart * 2); + } else { + numSamplesToLoadAdj = numSamplesToLoad; + } + + // Load the ADPCM codeBook + if ((sample->codec == CODEC_ADPCM) || (sample->codec == CODEC_SMALL_ADPCM)) { + if (gAudioContext.adpcmCodeBook != sample->book->codeBook) { + u32 numEntries; + + switch (bookOffset) { + case 1: + gAudioContext.adpcmCodeBook = &gInvalidAdpcmCodeBook[1]; + break; + + case 2: + case 3: + default: + gAudioContext.adpcmCodeBook = sample->book->codeBook; + break; + } + + numEntries = SAMPLES_PER_FRAME * sample->book->order * sample->book->numPredictors; + aLoadADPCM(cmd++, numEntries, gAudioContext.adpcmCodeBook); + } + } + + // Continue processing samples until the number of samples needed to load is reached + while (numSamplesProcessed != numSamplesToLoadAdj) { + sampleFinished = false; + loopToPoint = false; + dmemUncompressedAddrOffset2 = 0; + + numFirstFrameSamplesToIgnore = synthState->samplePosInt & 0xF; + numSamplesUntilEnd = sampleEndPos - synthState->samplePosInt; + + // Calculate number of samples to process this loop + numSamplesToProcess = numSamplesToLoadAdj - numSamplesProcessed; + + if ((numFirstFrameSamplesToIgnore == 0) && !synthState->atLoopPoint) { + numFirstFrameSamplesToIgnore = SAMPLES_PER_FRAME; + } + numSamplesInFirstFrame = SAMPLES_PER_FRAME - numFirstFrameSamplesToIgnore; + + // Determine the number of samples to decode based on whether the end will be reached or not. + if (numSamplesToProcess < numSamplesUntilEnd) { + // The end will not be reached. + numFramesToDecode = + (s32)(numSamplesToProcess - numSamplesInFirstFrame + SAMPLES_PER_FRAME - 1) / SAMPLES_PER_FRAME; + numSamplesToDecode = numFramesToDecode * SAMPLES_PER_FRAME; + numTrailingSamplesToIgnore = numSamplesInFirstFrame + numSamplesToDecode - numSamplesToProcess; + } else { + // The end will be reached. + numSamplesToDecode = numSamplesUntilEnd - numSamplesInFirstFrame; + numTrailingSamplesToIgnore = 0; + if (numSamplesToDecode <= 0) { + numSamplesToDecode = 0; + numSamplesInFirstFrame = numSamplesUntilEnd; + } + numFramesToDecode = (numSamplesToDecode + SAMPLES_PER_FRAME - 1) / SAMPLES_PER_FRAME; + if (loopInfo->count != 0) { + if ((loopInfo->count == 2) && synthState->stopLoop) { + sampleFinished = true; + } else { + // Loop around and restart + loopToPoint = true; + } + } else { + sampleFinished = true; + } + } + + // Set parameters based on compression type + 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; + zeroOffset = 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; + zeroOffset = 0; + break; + + case CODEC_UNK7: + // 2 2-byte samples (4 bytes) processed without decompression + frameSize = 4; + skipInitialSamples = SAMPLES_PER_FRAME; + zeroOffset = 0; + break; + + case CODEC_S8: + // 16 2-byte samples (32 bytes) compressed into 8-bit samples (16 bytes) + frameSize = 16; + skipInitialSamples = SAMPLES_PER_FRAME; + zeroOffset = 0; + break; + + case CODEC_REVERB: + reverbAddrSrc = (void*)0xFFFFFFFF; + if (gCustomAudioReverbFunction != NULL) { + reverbAddrSrc = gCustomAudioReverbFunction(sample, numSamplesToLoadAdj, flags, noteIndex); + } + + if (reverbAddrSrc == (void*)0xFFFFFFFF) { + sampleFinished = true; + } else if (reverbAddrSrc == NULL) { + return cmd; + } else { + AudioSynth_LoadBuffer(cmd++, DMEM_UNCOMPRESSED_NOTE, + (numSamplesToLoadAdj + SAMPLES_PER_FRAME) * SAMPLE_SIZE, + reverbAddrSrc); + flags = A_CONTINUE; + skipBytes = 0; + numSamplesProcessed = numSamplesToLoadAdj; + dmemUncompressedAddrOffset1 = numSamplesToLoadAdj; + } + goto skip; + + case CODEC_S16_INMEMORY: + case CODEC_UNK6: + AudioSynth_ClearBuffer(cmd++, DMEM_UNCOMPRESSED_NOTE, + (numSamplesToLoadAdj + SAMPLES_PER_FRAME) * SAMPLE_SIZE); + flags = A_CONTINUE; + skipBytes = 0; + numSamplesProcessed = numSamplesToLoadAdj; + dmemUncompressedAddrOffset1 = numSamplesToLoadAdj; + goto skip; + + case CODEC_S16: + AudioSynth_ClearBuffer(cmd++, DMEM_UNCOMPRESSED_NOTE, + (numSamplesToLoadAdj + SAMPLES_PER_FRAME) * SAMPLE_SIZE); + flags = A_CONTINUE; + skipBytes = 0; + numSamplesProcessed = numSamplesToLoadAdj; + dmemUncompressedAddrOffset1 = numSamplesToLoadAdj; + goto skip; + + default: + break; + } + + // Move the compressed raw sample data from ram into the rsp (DMEM) + if (numFramesToDecode != 0) { + // Get the offset from the start of the sample to where the sample is currently playing from + frameIndex = (synthState->samplePosInt + skipInitialSamples - numFirstFrameSamplesToIgnore) / + SAMPLES_PER_FRAME; + sampleAddrOffset = frameIndex * frameSize; + + // Get the ram address of the requested sample chunk + if (sample->medium == MEDIUM_RAM) { + // Sample is already loaded into ram + samplesToLoadAddr = sampleAddr + (zeroOffset + sampleAddrOffset); + } else if (gAudioContext.unk_29B8) { // always false + return cmd; + } else if (sample->medium == MEDIUM_UNK) { + // This medium is unsupported so terminate processing this note + return cmd; + } else { + // This medium is not in ram, so dma the requested sample into ram + samplesToLoadAddr = + AudioLoad_DmaSampleData(sampleAddr + (zeroOffset + sampleAddrOffset), + ALIGN16((numFramesToDecode * frameSize) + SAMPLES_PER_FRAME), flags, + &synthState->sampleDmaIndex, sample->medium); + } + + if (samplesToLoadAddr == NULL) { + // The ram address was unsuccessfully allocated + return cmd; + } + + // Move the raw sample chunk from ram to the rsp + // DMEM at the addresses before DMEM_COMPRESSED_ADPCM_DATA + sampleDataChunkAlignPad = (u32)samplesToLoadAddr & 0xF; + sampleDataChunkSize = ALIGN16((numFramesToDecode * frameSize) + SAMPLES_PER_FRAME); + sampleDataDmemAddr = DMEM_COMPRESSED_ADPCM_DATA - sampleDataChunkSize; + aLoadBuffer(cmd++, samplesToLoadAddr - sampleDataChunkAlignPad, sampleDataDmemAddr, + sampleDataChunkSize); + } else { + numSamplesToDecode = 0; + sampleDataChunkAlignPad = 0; + } + + if (synthState->atLoopPoint) { + aSetLoop(cmd++, sample->loop->predictorState); + flags = A_LOOP; + synthState->atLoopPoint = false; + } + + numSamplesInThisIteration = numSamplesToDecode + numSamplesInFirstFrame - numTrailingSamplesToIgnore; + + if (numSamplesProcessed == 0) { + //! FAKE: + if (1) {} + skipBytes = numFirstFrameSamplesToIgnore * SAMPLE_SIZE; + } else { + dmemUncompressedAddrOffset2 = ALIGN16(dmemUncompressedAddrOffset1 + 8 * SAMPLE_SIZE); + } + + // Decompress the raw sample chunks in the rsp + // Goes from adpcm (compressed) sample data to pcm (uncompressed) sample data + switch (sample->codec) { + case CODEC_ADPCM: + sampleDataChunkSize = ALIGN16((numFramesToDecode * frameSize) + SAMPLES_PER_FRAME); + sampleDataDmemAddr = DMEM_COMPRESSED_ADPCM_DATA - sampleDataChunkSize; + aSetBuffer(cmd++, 0, sampleDataDmemAddr + sampleDataChunkAlignPad, + DMEM_UNCOMPRESSED_NOTE + dmemUncompressedAddrOffset2, + numSamplesToDecode * SAMPLE_SIZE); + aADPCMdec(cmd++, flags, synthState->synthesisBuffers->adpcmState); + break; + + case CODEC_SMALL_ADPCM: + sampleDataChunkSize = ALIGN16((numFramesToDecode * frameSize) + SAMPLES_PER_FRAME); + sampleDataDmemAddr = DMEM_COMPRESSED_ADPCM_DATA - sampleDataChunkSize; + aSetBuffer(cmd++, 0, sampleDataDmemAddr + sampleDataChunkAlignPad, + DMEM_UNCOMPRESSED_NOTE + dmemUncompressedAddrOffset2, + numSamplesToDecode * SAMPLE_SIZE); + aADPCMdec(cmd++, flags | A_ADPCM_SHORT, synthState->synthesisBuffers->adpcmState); + break; + + case CODEC_S8: + sampleDataChunkSize = ALIGN16((numFramesToDecode * frameSize) + SAMPLES_PER_FRAME); + sampleDataDmemAddr = DMEM_COMPRESSED_ADPCM_DATA - sampleDataChunkSize; + AudioSynth_SetBuffer(cmd++, 0, sampleDataDmemAddr + sampleDataChunkAlignPad, + DMEM_UNCOMPRESSED_NOTE + dmemUncompressedAddrOffset2, + numSamplesToDecode * SAMPLE_SIZE); + AudioSynth_S8Dec(cmd++, flags, synthState->synthesisBuffers->adpcmState); + break; + + case CODEC_UNK7: + default: + // No decompression + break; + } + + if (numSamplesProcessed != 0) { + aDMEMMove(cmd++, + DMEM_UNCOMPRESSED_NOTE + dmemUncompressedAddrOffset2 + + (numFirstFrameSamplesToIgnore * SAMPLE_SIZE), + DMEM_UNCOMPRESSED_NOTE + dmemUncompressedAddrOffset1, + numSamplesInThisIteration * SAMPLE_SIZE); + } + + numSamplesProcessed += numSamplesInThisIteration; + + switch (flags) { + case A_INIT: + skipBytes = SAMPLES_PER_FRAME * SAMPLE_SIZE; + dmemUncompressedAddrOffset1 = (numSamplesToDecode + SAMPLES_PER_FRAME) * SAMPLE_SIZE; + break; + + case A_LOOP: + dmemUncompressedAddrOffset1 = + numSamplesInThisIteration * SAMPLE_SIZE + dmemUncompressedAddrOffset1; + break; + + default: + if (dmemUncompressedAddrOffset1 != 0) { + dmemUncompressedAddrOffset1 = + numSamplesInThisIteration * SAMPLE_SIZE + dmemUncompressedAddrOffset1; + } else { + dmemUncompressedAddrOffset1 = + (numFirstFrameSamplesToIgnore + numSamplesInThisIteration) * SAMPLE_SIZE; + } + break; + } + + flags = A_CONTINUE; + + skip: + + // Update what to do with the samples next + if (sampleFinished) { + if ((numSamplesToLoadAdj - numSamplesProcessed) != 0) { + AudioSynth_ClearBuffer(cmd++, DMEM_UNCOMPRESSED_NOTE + dmemUncompressedAddrOffset1, + (numSamplesToLoadAdj - numSamplesProcessed) * SAMPLE_SIZE); + } + finished = true; + note->sampleState.bitField0.finished = true; + AudioSynth_DisableSampleStates(updateIndex, noteIndex); + break; // break out of the for-loop + } else if (loopToPoint) { + synthState->atLoopPoint = true; + synthState->samplePosInt = loopInfo->start; + } else { + synthState->samplePosInt += numSamplesToProcess; + } + } + + switch (numParts) { + 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(numSamplesToLoadAdj / 2)); + numSamplesToLoadFirstPart = numSamplesToLoadAdj; + sampleDmemBeforeResampling = DMEM_TEMP + (SAMPLES_PER_FRAME * SAMPLE_SIZE); + if (finished) { + AudioSynth_ClearBuffer(cmd++, sampleDmemBeforeResampling + numSamplesToLoadFirstPart, + numSamplesToLoadAdj + SAMPLES_PER_FRAME); + } + break; + + case 1: + AudioSynth_InterL(cmd++, DMEM_UNCOMPRESSED_NOTE + skipBytes, + DMEM_TEMP + (SAMPLES_PER_FRAME * SAMPLE_SIZE) + numSamplesToLoadFirstPart, + ALIGN8(numSamplesToLoadAdj / 2)); + break; + + default: + break; + } + break; + + default: + break; + } + if (finished) { + break; + } + } + } + + // Update the flags for the signal processing below + flags = A_CONTINUE; + if (sampleState->bitField0.needsInit == true) { + sampleState->bitField0.needsInit = false; + flags = A_INIT; + } + + // Resample the decompressed mono-signal to the correct pitch + cmd = AudioSynth_FinalResample(cmd, synthState, numSamplesPerUpdate * SAMPLE_SIZE, frequencyFixedPoint, + sampleDmemBeforeResampling, flags); + + // UnkCmd19 was removed from the audio microcode + // This block performs no operation + if (bookOffset == 3) { + AudioSynth_UnkCmd19(cmd++, DMEM_TEMP, DMEM_TEMP, numSamplesPerUpdate * (s32)SAMPLE_SIZE, 0); + } + + // Apply the gain to the mono-signal to adjust the volume + gain = sampleState->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, (numSamplesPerUpdate + SAMPLES_PER_FRAME) * SAMPLE_SIZE); + } + + // Apply the filter to the mono-signal + filter = sampleState->filter; + if (filter != 0) { + AudioSynth_LoadFilterSize(cmd++, numSamplesPerUpdate * SAMPLE_SIZE, filter); + AudioSynth_LoadFilterBuffer(cmd++, flags, DMEM_TEMP, synthState->synthesisBuffers->filterState); + } + + // Apply the comb filter to the mono-signal by taking the signal with a small temporal offset, + // and adding it back to itself + combFilterSize = sampleState->combFilterSize; + combFilterGain = sampleState->combFilterGain; + combFilterState = synthState->synthesisBuffers->combFilterState; + if ((combFilterSize != 0) && (sampleState->combFilterGain != 0)) { + AudioSynth_DMemMove(cmd++, DMEM_TEMP, DMEM_COMB_TEMP, numSamplesPerUpdate * SAMPLE_SIZE); + combFilterDmem = DMEM_COMB_TEMP - combFilterSize; + if (synthState->combFilterNeedsInit) { + AudioSynth_ClearBuffer(cmd++, combFilterDmem, combFilterSize); + synthState->combFilterNeedsInit = false; + } else { + AudioSynth_LoadBuffer(cmd++, combFilterDmem, combFilterSize, combFilterState); + } + AudioSynth_SaveBuffer(cmd++, DMEM_TEMP + (numSamplesPerUpdate * SAMPLE_SIZE) - combFilterSize, combFilterSize, + combFilterState); + AudioSynth_Mix(cmd++, (numSamplesPerUpdate * (s32)SAMPLE_SIZE) >> 4, combFilterGain, DMEM_COMB_TEMP, + combFilterDmem); + AudioSynth_DMemMove(cmd++, combFilterDmem, DMEM_TEMP, numSamplesPerUpdate * SAMPLE_SIZE); + } else { + synthState->combFilterNeedsInit = true; + } + + // Determine the behavior of the audio processing that leads to the haas effect + if ((sampleState->haasEffectLeftDelaySize != 0) || (synthState->prevHaasEffectLeftDelaySize != 0)) { + haasEffectDelaySide = HAAS_EFFECT_DELAY_LEFT; + } else if ((sampleState->haasEffectRightDelaySize != 0) || (synthState->prevHaasEffectRightDelaySize != 0)) { + haasEffectDelaySide = HAAS_EFFECT_DELAY_RIGHT; + } else { + haasEffectDelaySide = HAAS_EFFECT_DELAY_NONE; + } + + // Apply an unknown effect based on the surround sound-mode + if (gAudioContext.soundMode == SOUNDMODE_SURROUND) { + sampleState->targetVolLeft = sampleState->targetVolLeft >> 1; + sampleState->targetVolRight = sampleState->targetVolRight >> 1; + if (sampleState->surroundEffectIndex != 0xFF) { + cmd = AudioSynth_ApplySurroundEffect(cmd, sampleState, synthState, numSamplesPerUpdate, DMEM_TEMP, flags); + } + } + + // Split the mono-signal into left and right channels: + // Both for dry signal (to go to the speakers now) + // and for wet signal (to go to a reverb buffer to be stored, and brought back later to produce an echo) + cmd = AudioSynth_ProcessEnvelope(cmd, sampleState, synthState, numSamplesPerUpdate, DMEM_TEMP, haasEffectDelaySide, + flags); + + // Apply the haas effect by delaying either the left or the right channel by a small amount + if (sampleState->bitField1.useHaasEffect) { + if (!(flags & A_INIT)) { + flags = A_CONTINUE; + } + cmd = AudioSynth_ApplyHaasEffect(cmd, sampleState, synthState, numSamplesPerUpdate * (s32)SAMPLE_SIZE, flags, + haasEffectDelaySide); + } + + return cmd; +} + +Acmd* AudioSynth_ApplySurroundEffect(Acmd* cmd, NoteSampleState* sampleState, NoteSynthesisState* synthState, + s32 numSamplesPerUpdate, s32 haasDmem, s32 flags) { + s32 wetGain; + u16 dryGain; + s64 dmem = DMEM_SURROUND_TEMP; + f32 decayGain; + + AudioSynth_DMemMove(cmd++, haasDmem, DMEM_HAAS_TEMP, numSamplesPerUpdate * SAMPLE_SIZE); + dryGain = synthState->surroundEffectGain; + + if (flags == A_INIT) { + aClearBuffer(cmd++, dmem, sizeof(synthState->synthesisBuffers->surroundEffectState)); + synthState->surroundEffectGain = 0; + } else { + aLoadBuffer(cmd++, synthState->synthesisBuffers->surroundEffectState, dmem, + sizeof(synthState->synthesisBuffers->surroundEffectState)); + aMix(cmd++, (numSamplesPerUpdate * (s32)SAMPLE_SIZE) >> 4, dryGain, dmem, DMEM_LEFT_CH); + aMix(cmd++, (numSamplesPerUpdate * (s32)SAMPLE_SIZE) >> 4, (dryGain ^ 0xFFFF), dmem, DMEM_RIGHT_CH); + + wetGain = (dryGain * synthState->curReverbVol) >> 7; + + aMix(cmd++, (numSamplesPerUpdate * (s32)SAMPLE_SIZE) >> 4, wetGain, dmem, DMEM_WET_LEFT_CH); + aMix(cmd++, (numSamplesPerUpdate * (s32)SAMPLE_SIZE) >> 4, (wetGain ^ 0xFFFF), dmem, DMEM_WET_RIGHT_CH); + } + + aSaveBuffer(cmd++, DMEM_SURROUND_TEMP + (numSamplesPerUpdate * SAMPLE_SIZE), + synthState->synthesisBuffers->surroundEffectState, + sizeof(synthState->synthesisBuffers->surroundEffectState)); + + decayGain = (sampleState->targetVolLeft + sampleState->targetVolRight) * (1.0f / 0x2000); + + if (decayGain > 1.0f) { + decayGain = 1.0f; + } + + decayGain = decayGain * gDefaultPanVolume[127 - sampleState->surroundEffectIndex]; + synthState->surroundEffectGain = ((decayGain * 0x7FFF) + synthState->surroundEffectGain) / 2; + + AudioSynth_DMemMove(cmd++, DMEM_HAAS_TEMP, haasDmem, numSamplesPerUpdate * SAMPLE_SIZE); + + 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, NoteSampleState* sampleState, NoteSynthesisState* synthState, + s32 numSamplesPerUpdate, u16 dmemSrc, s32 haasEffectDelaySide, s32 flags) { + u32 dmemDests; + u16 curVolLeft; + u16 targetVolLeft; + s32 curReverbVol; + u16 curVolRight; + s16 targetReverbVol; + s16 rampLeft; + s16 rampRight; + s16 rampReverb; + s16 curReverbVolAndFlags; + u16 targetVolRight; + f32 defaultPanVolume; + s32 pad; + + targetReverbVol = sampleState->targetReverbVol; + + curVolLeft = synthState->curVolLeft; + curVolRight = synthState->curVolRight; + + targetVolLeft = sampleState->targetVolLeft; + targetVolLeft <<= 4; + targetVolRight = sampleState->targetVolRight; + targetVolRight <<= 4; + + if ((gAudioContext.soundMode == SOUNDMODE_SURROUND) && (sampleState->surroundEffectIndex != 0xFF)) { + defaultPanVolume = gDefaultPanVolume[sampleState->surroundEffectIndex]; + targetVolLeft *= defaultPanVolume; + targetVolRight *= defaultPanVolume; + } + + if (targetVolLeft != curVolLeft) { + rampLeft = (targetVolLeft - curVolLeft) / (numSamplesPerUpdate >> 3); + } else { + rampLeft = 0; + } + + if (targetVolRight != curVolRight) { + rampRight = (targetVolRight - curVolRight) / (numSamplesPerUpdate >> 3); + } else { + rampRight = 0; + } + + curReverbVolAndFlags = synthState->curReverbVol; + curReverbVol = curReverbVolAndFlags & 0x7F; + + if (curReverbVolAndFlags != targetReverbVol) { + rampReverb = (((targetReverbVol & 0x7F) - curReverbVol) << 9) / (numSamplesPerUpdate >> 3); + synthState->curReverbVol = targetReverbVol; + } else { + rampReverb = 0; + } + + synthState->curVolLeft = curVolLeft + (rampLeft * (numSamplesPerUpdate >> 3)); + synthState->curVolRight = curVolRight + (rampRight * (numSamplesPerUpdate >> 3)); + + if (sampleState->bitField1.useHaasEffect) { + AudioSynth_ClearBuffer(cmd++, DMEM_HAAS_TEMP, DMEM_1CH_SIZE); + AudioSynth_EnvSetup1(cmd++, curReverbVol * 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++, curReverbVol * 2, rampReverb, rampLeft, rampRight); + aEnvSetup2(cmd++, curVolLeft, curVolRight); + dmemDests = sEnvMixerDefaultDmemDests; + } + + aEnvMixer(cmd++, dmemSrc, numSamplesPerUpdate, (curReverbVolAndFlags & 0x80) >> 7, + sampleState->bitField0.strongReverbRight, sampleState->bitField0.strongReverbLeft, + sampleState->bitField0.strongRight, sampleState->bitField0.strongLeft, dmemDests, sEnvMixerOp); + + return cmd; +} + +Acmd* AudioSynth_LoadWaveSamples(Acmd* cmd, NoteSampleState* sampleState, NoteSynthesisState* synthState, + s32 numSamplesToLoad) { + s32 numSamplesAvailable; + s32 harmonicIndexCurAndPrev = sampleState->harmonicIndexCurAndPrev; + s32 samplePosInt = synthState->samplePosInt; + s32 numDuplicates; + + if (sampleState->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++, sampleState->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 + numSamplesAvailable = WAVE_SAMPLE_COUNT - samplePosInt; + + // Require duplicates if there are more samples to load than available + if (numSamplesToLoad > numSamplesAvailable) { + // Duplicate (copy) the WAVE_SAMPLE_COUNT samples as many times as needed to reach numSamplesToLoad. + // (numSamplesToLoad - numSamplesAvailable) is the number of samples missing. + // Divide by WAVE_SAMPLE_COUNT, rounding up, to get the amount of duplicates + numDuplicates = ((numSamplesToLoad - numSamplesAvailable + 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, NoteSampleState* sampleState, 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 = sampleState->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 = sampleState->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); + if (haasEffectDelaySize) { // != 0 + aClearBuffer(cmd++, DMEM_HAAS_TEMP, haasEffectDelaySize); + } + aDMEMMove(cmd++, DMEM_TEMP, DMEM_HAAS_TEMP + haasEffectDelaySize, size); + } + + if (haasEffectDelaySize) { // != 0 + // Save excessive samples for next iteration + aSaveBuffer(cmd++, DMEM_HAAS_TEMP + size, synthState->synthesisBuffers->haasEffectDelayState, + ALIGN16(haasEffectDelaySize)); + } + + aAddMixer(cmd++, ALIGN64(size), DMEM_HAAS_TEMP, dmemDest, 0x7FFF); + + return cmd; +} diff --git a/src/code/audio/code_8019AF00.c b/src/code/audio/code_8019AF00.c index 007d7ca85..98be41397 100644 --- a/src/code/audio/code_8019AF00.c +++ b/src/code/audio/code_8019AF00.c @@ -4078,7 +4078,7 @@ void AudioSfx_SetProperties(u8 bankId, u8 entryIndex, u8 channelIndex) { } u32 AudioSfx_SetFreqAndStereoBits(u8 seqScriptValIn, SequenceChannel* channel) { - channel->stereo.asByte = sSfxChannelState[seqScriptValIn].stereoBits; + channel->stereoData.asByte = sSfxChannelState[seqScriptValIn].stereoBits; channel->freqScale = sSfxChannelState[seqScriptValIn].freqScale; channel->changes.s.freqScale = true; |
