From 8f1fd58f22518857a1131582af0d41adfde2dad9 Mon Sep 17 00:00:00 2001 From: engineer124 <47598039+engineer124@users.noreply.github.com> Date: Mon, 30 May 2022 04:31:43 +1000 Subject: [PATCH] Audio Docs: Adsr Decay Rate (#1238) * Init adsr decay docs * cleanup * cleanup * Revert some docs * Cleaner docs * count hex to dec * scaled updates per frame * Consistency * Oops, fix meaning * Avoid `decayRate` conflict with reverb `decayRate` * PR suggestion --- include/z64audio.h | 18 ++++++++-------- src/code/audio_effects.c | 23 ++++++++++---------- src/code/audio_heap.c | 43 ++++++++++++++++++++++++-------------- src/code/audio_playback.c | 8 +++---- src/code/audio_seqplayer.c | 14 ++++++------- 5 files changed, 59 insertions(+), 47 deletions(-) diff --git a/include/z64audio.h b/include/z64audio.h index 7fe2de390e..4a25b9f72d 100644 --- a/include/z64audio.h +++ b/include/z64audio.h @@ -193,7 +193,7 @@ typedef struct { /* 0x00 */ u8 loaded; /* 0x01 */ u8 normalRangeLo; /* 0x02 */ u8 normalRangeHi; - /* 0x03 */ u8 releaseRate; + /* 0x03 */ u8 adsrDecayIndex; // index used to obtain adsr decay rate from adsrDecayTable /* 0x04 */ AdsrEnvelope* envelope; /* 0x08 */ SoundFontSound lowNotesSound; /* 0x10 */ SoundFontSound normalNotesSound; @@ -201,7 +201,7 @@ typedef struct { } Instrument; // size = 0x20 typedef struct { - /* 0x00 */ u8 releaseRate; + /* 0x00 */ u8 adsrDecayIndex; // index used to obtain adsr decay rate from adsrDecayTable /* 0x01 */ u8 pan; /* 0x02 */ u8 loaded; /* 0x04 */ SoundFontSound sound; @@ -271,7 +271,7 @@ typedef struct { } SequencePlayer; // size = 0x160 typedef struct { - /* 0x0 */ u8 releaseRate; + /* 0x0 */ u8 decayIndex; // index used to obtain adsr decay rate from adsrDecayTable /* 0x1 */ u8 sustain; /* 0x4 */ AdsrEnvelope* envelope; } AdsrSettings; // size = 0x8 @@ -581,16 +581,16 @@ typedef struct { /* 0x06 */ s16 samplesPerFrameTarget; /* 0x08 */ s16 maxAiBufferLength; /* 0x0A */ s16 minAiBufferLength; - /* 0x0C */ s16 updatesPerFrame; + /* 0x0C */ s16 updatesPerFrame; // for each frame of the audio thread (default 60 fps), number of updates to process audio /* 0x0E */ s16 samplesPerUpdate; /* 0x10 */ s16 samplesPerUpdateMax; /* 0x12 */ s16 samplesPerUpdateMin; /* 0x14 */ s16 numSequencePlayers; /* 0x18 */ f32 resampleRate; - /* 0x1C */ f32 updatesPerFrameInv; - /* 0x20 */ f32 unkUpdatesPerFrameScaled; - /* 0x24 */ f32 unk_24; -} AudioBufferParameters; + /* 0x1C */ f32 updatesPerFrameInv; // inverse (reciprocal) of updatesPerFrame + /* 0x20 */ f32 updatesPerFrameInvScaled; // updatesPerFrameInv scaled down by a factor of 256 + /* 0x24 */ f32 updatesPerFrameScaled; // updatesPerFrame scaled down by a factor of 4 +} AudioBufferParameters; // size = 0x28 typedef struct { /* 0x0 */ u8* start; @@ -856,7 +856,7 @@ typedef struct { /* 0x3518 */ volatile u8 resetStatus; /* 0x3519 */ u8 audioResetSpecIdToLoad; /* 0x351C */ s32 audioResetFadeOutFramesLeft; - /* 0x3520 */ f32* unk_3520; + /* 0x3520 */ f32* adsrDecayTable; // A table on the audio heap that stores decay rates used for adsr /* 0x3524 */ u8* audioHeap; /* 0x3528 */ u32 audioHeapSize; /* 0x352C */ Note* notes; diff --git a/src/code/audio_effects.c b/src/code/audio_effects.c index b871867193..13156e8831 100644 --- a/src/code/audio_effects.c +++ b/src/code/audio_effects.c @@ -223,23 +223,21 @@ void Audio_AdsrInit(AdsrState* adsr, AdsrEnvelope* envelope, s16* volOut) { f32 Audio_AdsrUpdate(AdsrState* adsr) { u8 state = adsr->action.s.state; + switch (state) { case ADSR_STATE_DISABLED: return 0.0f; - case ADSR_STATE_INITIAL: { + case ADSR_STATE_INITIAL: if (adsr->action.s.hang) { adsr->action.s.state = ADSR_STATE_HANG; break; } // fallthrough - } - case ADSR_STATE_START_LOOP: adsr->envIndex = 0; adsr->action.s.state = ADSR_STATE_LOOP; // fallthrough - retry: case ADSR_STATE_LOOP: adsr->delay = adsr->envelope[adsr->envIndex].delay; @@ -247,18 +245,21 @@ f32 Audio_AdsrUpdate(AdsrState* adsr) { case ADSR_DISABLE: adsr->action.s.state = ADSR_STATE_DISABLED; break; + case ADSR_HANG: adsr->action.s.state = ADSR_STATE_HANG; break; + case ADSR_GOTO: adsr->envIndex = adsr->envelope[adsr->envIndex].arg; goto retry; + case ADSR_RESTART: adsr->action.s.state = ADSR_STATE_INITIAL; break; default: - adsr->delay *= gAudioContext.audioBufferParameters.unk_24; + adsr->delay *= gAudioContext.audioBufferParameters.updatesPerFrameScaled; if (adsr->delay == 0) { adsr->delay = 1; } @@ -273,19 +274,18 @@ f32 Audio_AdsrUpdate(AdsrState* adsr) { break; } // fallthrough - case ADSR_STATE_FADE: adsr->current += adsr->velocity; - if (--adsr->delay <= 0) { + adsr->delay--; + if (adsr->delay <= 0) { adsr->action.s.state = ADSR_STATE_LOOP; } // fallthrough - case ADSR_STATE_HANG: break; case ADSR_STATE_DECAY: - case ADSR_STATE_RELEASE: { + case ADSR_STATE_RELEASE: adsr->current -= adsr->fadeOutVel; if (adsr->sustain != 0.0f && state == ADSR_STATE_DECAY) { if (adsr->current < adsr->sustain) { @@ -301,10 +301,9 @@ f32 Audio_AdsrUpdate(AdsrState* adsr) { adsr->action.s.state = ADSR_STATE_DISABLED; } break; - } case ADSR_STATE_SUSTAIN: - adsr->delay -= 1; + adsr->delay--; if (adsr->delay == 0) { adsr->action.s.state = ADSR_STATE_RELEASE; } @@ -324,8 +323,10 @@ f32 Audio_AdsrUpdate(AdsrState* adsr) { if (adsr->current < 0.0f) { return 0.0f; } + if (adsr->current > 1.0f) { return 1.0f; } + return adsr->current; } diff --git a/src/code/audio_heap.c b/src/code/audio_heap.c index 3dc51979c5..82d1a2e951 100644 --- a/src/code/audio_heap.c +++ b/src/code/audio_heap.c @@ -10,32 +10,40 @@ void AudioHeap_DiscardSampleCaches(void); void AudioHeap_DiscardSampleBank(s32 sampleBankId); void AudioHeap_DiscardSampleBanks(void); -f32 func_800DDE20(f32 arg0) { - return 256.0f * gAudioContext.audioBufferParameters.unkUpdatesPerFrameScaled / arg0; +/** + * Effectively scales `updatesPerFrameInv` by the reciprocal of `scaleInv` + * `updatesPerFrameInvScaled` is just `updatesPerFrameInv` scaled down by a factor of 256.0f + * i.e. (256.0f * `updatesPerFrameInvScaled`) is just `updatesPerFrameInv` + */ +f32 AudioHeap_CalculateAdsrDecay(f32 scaleInv) { + return (256.0f * gAudioContext.audioBufferParameters.updatesPerFrameInvScaled) / scaleInv; } -void func_800DDE3C(void) { +/** + * Initialize the decay rate table used for decaying notes as part of adsr + */ +void AudioHeap_InitAdsrDecayTable(void) { s32 i; - gAudioContext.unk_3520[255] = func_800DDE20(0.25f); - gAudioContext.unk_3520[254] = func_800DDE20(0.33f); - gAudioContext.unk_3520[253] = func_800DDE20(0.5f); - gAudioContext.unk_3520[252] = func_800DDE20(0.66f); - gAudioContext.unk_3520[251] = func_800DDE20(0.75f); + gAudioContext.adsrDecayTable[255] = AudioHeap_CalculateAdsrDecay(0.25f); + gAudioContext.adsrDecayTable[254] = AudioHeap_CalculateAdsrDecay(0.33f); + gAudioContext.adsrDecayTable[253] = AudioHeap_CalculateAdsrDecay(0.5f); + gAudioContext.adsrDecayTable[252] = AudioHeap_CalculateAdsrDecay(0.66f); + gAudioContext.adsrDecayTable[251] = AudioHeap_CalculateAdsrDecay(0.75f); for (i = 128; i < 251; i++) { - gAudioContext.unk_3520[i] = func_800DDE20(251 - i); + gAudioContext.adsrDecayTable[i] = AudioHeap_CalculateAdsrDecay(251 - i); } for (i = 16; i < 128; i++) { - gAudioContext.unk_3520[i] = func_800DDE20(4 * (143 - i)); + gAudioContext.adsrDecayTable[i] = AudioHeap_CalculateAdsrDecay(4 * (143 - i)); } for (i = 1; i < 16; i++) { - gAudioContext.unk_3520[i] = func_800DDE20(60 * (23 - i)); + gAudioContext.adsrDecayTable[i] = AudioHeap_CalculateAdsrDecay(60 * (23 - i)); } - gAudioContext.unk_3520[0] = 0.0f; + gAudioContext.adsrDecayTable[0] = 0.0f; } void AudioHeap_ResetLoadStatus(void) { @@ -826,9 +834,10 @@ void AudioHeap_Init(void) { gAudioContext.audioBufferParameters.samplesPerUpdateMax = gAudioContext.audioBufferParameters.samplesPerUpdate + 8; gAudioContext.audioBufferParameters.samplesPerUpdateMin = gAudioContext.audioBufferParameters.samplesPerUpdate - 8; gAudioContext.audioBufferParameters.resampleRate = 32000.0f / (s32)gAudioContext.audioBufferParameters.frequency; - gAudioContext.audioBufferParameters.unkUpdatesPerFrameScaled = + gAudioContext.audioBufferParameters.updatesPerFrameInvScaled = (1.0f / 256.0f) / gAudioContext.audioBufferParameters.updatesPerFrame; - gAudioContext.audioBufferParameters.unk_24 = gAudioContext.audioBufferParameters.updatesPerFrame * 0.25f; + gAudioContext.audioBufferParameters.updatesPerFrameScaled = + gAudioContext.audioBufferParameters.updatesPerFrame / 4.0f; gAudioContext.audioBufferParameters.updatesPerFrameInv = 1.0f / gAudioContext.audioBufferParameters.updatesPerFrame; gAudioContext.sampleDmaBufSize1 = spec->sampleDmaBufSize1; gAudioContext.sampleDmaBufSize2 = spec->sampleDmaBufSize2; @@ -898,8 +907,10 @@ void AudioHeap_Init(void) { gAudioContext.maxAudioCmds * sizeof(u64)); } - gAudioContext.unk_3520 = AudioHeap_Alloc(&gAudioContext.notesAndBuffersPool, 0x100 * sizeof(f32)); - func_800DDE3C(); + // Initialize the decay rate table for adsr + gAudioContext.adsrDecayTable = AudioHeap_Alloc(&gAudioContext.notesAndBuffersPool, 256 * sizeof(f32)); + AudioHeap_InitAdsrDecayTable(); + for (i = 0; i < 4; i++) { gAudioContext.synthesisReverbs[i].useReverb = 0; } diff --git a/src/code/audio_playback.c b/src/code/audio_playback.c index 4edc18db38..7c9a29d7f2 100644 --- a/src/code/audio_playback.c +++ b/src/code/audio_playback.c @@ -125,7 +125,7 @@ void Audio_NoteSetResamplingRate(NoteSubEu* noteSubEu, f32 resamplingRateInput) } void Audio_NoteInit(Note* note) { - if (note->playbackState.parentLayer->adsr.releaseRate == 0) { + if (note->playbackState.parentLayer->adsr.decayIndex == 0) { Audio_AdsrInit(¬e->playbackState.adsr, note->playbackState.parentLayer->channel->adsr.envelope, ¬e->playbackState.adsrVolScaleUnused); } else { @@ -506,10 +506,10 @@ void Audio_SeqLayerDecayRelease(SequenceLayer* layer, s32 target) { } else { note->playbackState.unk_04 = 1; note->playbackState.adsr.action.s.decay = true; - if (layer->adsr.releaseRate == 0) { - note->playbackState.adsr.fadeOutVel = gAudioContext.unk_3520[layer->channel->adsr.releaseRate]; + if (layer->adsr.decayIndex == 0) { + note->playbackState.adsr.fadeOutVel = gAudioContext.adsrDecayTable[layer->channel->adsr.decayIndex]; } else { - note->playbackState.adsr.fadeOutVel = gAudioContext.unk_3520[layer->adsr.releaseRate]; + note->playbackState.adsr.fadeOutVel = gAudioContext.adsrDecayTable[layer->adsr.decayIndex]; } note->playbackState.adsr.sustain = ((f32)(s32)(layer->channel->adsr.sustain) * note->playbackState.adsr.current) / 256.0f; diff --git a/src/code/audio_seqplayer.c b/src/code/audio_seqplayer.c index 3f4b159782..413e8c8e25 100644 --- a/src/code/audio_seqplayer.c +++ b/src/code/audio_seqplayer.c @@ -139,7 +139,7 @@ void AudioSeq_InitSequenceChannel(SequenceChannel* channel) { channel->someOtherPriority = 1; channel->delay = 0; channel->adsr.envelope = gDefaultEnvelope; - channel->adsr.releaseRate = 0xF0; + channel->adsr.decayIndex = 0xF0; channel->adsr.sustain = 0; channel->vibratoRateTarget = 0x800; channel->vibratoRateStart = 0x800; @@ -181,7 +181,7 @@ s32 AudioSeq_SeqChannelSetLayer(SequenceChannel* channel, s32 layerIdx) { layer = channel->layers[layerIdx]; layer->channel = channel; layer->adsr = channel->adsr; - layer->adsr.releaseRate = 0; + layer->adsr.decayIndex = 0; layer->enabled = true; layer->finished = false; layer->stopSomething = false; @@ -543,7 +543,7 @@ s32 AudioSeq_SeqLayerProcessScriptStep2(SequenceLayer* layer) { } if (cmd == 0xFF) { - layer->adsr.releaseRate = 0; + layer->adsr.decayIndex = 0; } break; @@ -588,7 +588,7 @@ s32 AudioSeq_SeqLayerProcessScriptStep2(SequenceLayer* layer) { // fallthrough case 0xCF: - layer->adsr.releaseRate = AudioSeq_ScriptReadU8(state); + layer->adsr.decayIndex = AudioSeq_ScriptReadU8(state); break; case 0xCC: @@ -664,7 +664,7 @@ s32 AudioSeq_SeqLayerProcessScriptStep4(SequenceLayer* layer, s32 cmd) { } sound = &drum->sound; layer->adsr.envelope = (AdsrEnvelope*)drum->envelope; - layer->adsr.releaseRate = (u8)drum->releaseRate; + layer->adsr.decayIndex = (u8)drum->adsrDecayIndex; if (!layer->ignoreDrumPan) { layer->pan = drum->pan; } @@ -937,7 +937,7 @@ u8 AudioSeq_GetInstrument(SequenceChannel* channel, u8 instId, Instrument** inst return 0; } adsr->envelope = inst->envelope; - adsr->releaseRate = inst->releaseRate; + adsr->decayIndex = inst->adsrDecayIndex; *instOut = inst; instId += 2; return instId; @@ -1118,7 +1118,7 @@ void AudioSeq_SequenceChannelProcessScript(SequenceChannel* channel) { break; case 0xD9: command = (u8)parameters[0]; - channel->adsr.releaseRate = command; + channel->adsr.decayIndex = command; break; case 0xD8: command = (u8)parameters[0];