mirror of
https://github.com/Mr-Wiseguy/Zelda64Recomp.git
synced 2024-11-18 03:39:16 +01:00
364 lines
18 KiB
C
364 lines
18 KiB
C
|
#include "patches.h"
|
||
|
#include "overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope.h"
|
||
|
#include "sound.h"
|
||
|
|
||
|
void AudioSeq_ProcessSeqCmd(u32 cmd);
|
||
|
void AudioThread_QueueCmd(u32 opArgs, void** data);
|
||
|
|
||
|
// Direct audio command (skips the queueing system)
|
||
|
#define SEQCMD_SET_SEQPLAYER_VOLUME_NOW(seqPlayerIndex, duration, volume) \
|
||
|
AudioSeq_ProcessSeqCmd((SEQCMD_OP_SET_SEQPLAYER_VOLUME << 28) | ((u8)(seqPlayerIndex) << 24) | \
|
||
|
((u8)(duration) << 16) | ((u8)((volume)*127.0f)));
|
||
|
|
||
|
bool is_bgm_player(u8 player_index) {
|
||
|
return player_index == SEQ_PLAYER_BGM_MAIN || player_index == SEQ_PLAYER_BGM_SUB;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Update different commands and requests for active sequences
|
||
|
*/
|
||
|
void AudioSeq_UpdateActiveSequences(void) {
|
||
|
u32 tempoCmd;
|
||
|
u16 tempoPrev;
|
||
|
u16 seqId;
|
||
|
u16 channelMask;
|
||
|
u16 tempoTarget;
|
||
|
u8 setupOp;
|
||
|
u8 targetSeqPlayerIndex;
|
||
|
u8 setupVal2;
|
||
|
u8 setupVal1;
|
||
|
u8 tempoOp;
|
||
|
s32 pad[2];
|
||
|
u32 retMsg;
|
||
|
f32 volume;
|
||
|
u8 tempoTimer;
|
||
|
u8 seqPlayerIndex;
|
||
|
u8 j;
|
||
|
u8 channelIndex;
|
||
|
|
||
|
for (seqPlayerIndex = 0; seqPlayerIndex < SEQ_PLAYER_MAX; seqPlayerIndex++) {
|
||
|
|
||
|
// The seqPlayer has finished initializing and is currently playing the active sequences
|
||
|
if (gActiveSeqs[seqPlayerIndex].isSeqPlayerInit && gAudioCtx.seqPlayers[seqPlayerIndex].enabled) {
|
||
|
gActiveSeqs[seqPlayerIndex].isSeqPlayerInit = false;
|
||
|
}
|
||
|
|
||
|
// The seqPlayer is no longer playing the active sequences
|
||
|
if ((AudioSeq_GetActiveSeqId(seqPlayerIndex) != NA_BGM_DISABLED) &&
|
||
|
!gAudioCtx.seqPlayers[seqPlayerIndex].enabled && (!gActiveSeqs[seqPlayerIndex].isSeqPlayerInit)) {
|
||
|
gActiveSeqs[seqPlayerIndex].seqId = NA_BGM_DISABLED;
|
||
|
}
|
||
|
|
||
|
// Check if the requested sequences is waiting for fonts to load
|
||
|
if (gActiveSeqs[seqPlayerIndex].isWaitingForFonts) {
|
||
|
switch ((s32)AudioThread_GetExternalLoadQueueMsg(&retMsg)) {
|
||
|
case SEQ_PLAYER_BGM_MAIN + 1:
|
||
|
case SEQ_PLAYER_FANFARE + 1:
|
||
|
case SEQ_PLAYER_SFX + 1:
|
||
|
case SEQ_PLAYER_BGM_SUB + 1:
|
||
|
case SEQ_PLAYER_AMBIENCE + 1:
|
||
|
// The fonts have been loaded successfully.
|
||
|
gActiveSeqs[seqPlayerIndex].isWaitingForFonts = false;
|
||
|
// Queue the same command that was stored previously, but without the 0x8000
|
||
|
AudioSeq_ProcessSeqCmd(gActiveSeqs[seqPlayerIndex].startAsyncSeqCmd);
|
||
|
break;
|
||
|
case 0xFF:
|
||
|
// There was an error in loading the fonts
|
||
|
gActiveSeqs[seqPlayerIndex].isWaitingForFonts = false;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Update global volume
|
||
|
if (gActiveSeqs[seqPlayerIndex].fadeVolUpdate) {
|
||
|
volume = 1.0f;
|
||
|
for (j = 0; j < VOL_SCALE_INDEX_MAX; j++) {
|
||
|
volume *= (gActiveSeqs[seqPlayerIndex].volScales[j] / 127.0f);
|
||
|
}
|
||
|
|
||
|
SEQCMD_SET_SEQPLAYER_VOLUME((u8)(seqPlayerIndex + (SEQCMD_ASYNC_ACTIVE >> 24)),
|
||
|
gActiveSeqs[seqPlayerIndex].volFadeTimer, (u8)(volume * 127.0f));
|
||
|
gActiveSeqs[seqPlayerIndex].fadeVolUpdate = false;
|
||
|
}
|
||
|
|
||
|
if (gActiveSeqs[seqPlayerIndex].volTimer != 0) {
|
||
|
gActiveSeqs[seqPlayerIndex].volTimer--;
|
||
|
|
||
|
if (gActiveSeqs[seqPlayerIndex].volTimer != 0) {
|
||
|
gActiveSeqs[seqPlayerIndex].volCur -= gActiveSeqs[seqPlayerIndex].volStep;
|
||
|
} else {
|
||
|
gActiveSeqs[seqPlayerIndex].volCur = gActiveSeqs[seqPlayerIndex].volTarget;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
// @recomp Send a volume scale command regardless of whether volTimer is active and scale it for background music players.
|
||
|
f32 cur_volume = gActiveSeqs[seqPlayerIndex].volCur;
|
||
|
if (is_bgm_player(seqPlayerIndex)) {
|
||
|
cur_volume *= recomp_get_bgm_volume();
|
||
|
}
|
||
|
AUDIOCMD_SEQPLAYER_FADE_VOLUME_SCALE(seqPlayerIndex, cur_volume);
|
||
|
|
||
|
// Process tempo
|
||
|
if (gActiveSeqs[seqPlayerIndex].tempoCmd != 0) {
|
||
|
tempoCmd = gActiveSeqs[seqPlayerIndex].tempoCmd;
|
||
|
tempoTimer = (tempoCmd & 0xFF0000) >> 15;
|
||
|
tempoTarget = tempoCmd & 0xFFF;
|
||
|
if (tempoTimer == 0) {
|
||
|
tempoTimer++;
|
||
|
}
|
||
|
|
||
|
// Process tempo commands
|
||
|
if (gAudioCtx.seqPlayers[seqPlayerIndex].enabled) {
|
||
|
tempoPrev = gAudioCtx.seqPlayers[seqPlayerIndex].tempo / TATUMS_PER_BEAT;
|
||
|
tempoOp = (tempoCmd & 0xF000) >> 12;
|
||
|
switch (tempoOp) {
|
||
|
case SEQCMD_SUB_OP_TEMPO_SPEED_UP:
|
||
|
// Speed up tempo by `tempoTarget` amount
|
||
|
tempoTarget += tempoPrev;
|
||
|
break;
|
||
|
|
||
|
case SEQCMD_SUB_OP_TEMPO_SLOW_DOWN:
|
||
|
// Slow down tempo by `tempoTarget` amount
|
||
|
if (tempoTarget < tempoPrev) {
|
||
|
tempoTarget = tempoPrev - tempoTarget;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case SEQCMD_SUB_OP_TEMPO_SCALE:
|
||
|
// Scale tempo by a multiplicative factor
|
||
|
tempoTarget = tempoPrev * (tempoTarget / 100.0f);
|
||
|
break;
|
||
|
|
||
|
case SEQCMD_SUB_OP_TEMPO_RESET:
|
||
|
// Reset tempo to original tempo
|
||
|
tempoTarget = (gActiveSeqs[seqPlayerIndex].tempoOriginal != 0)
|
||
|
? gActiveSeqs[seqPlayerIndex].tempoOriginal
|
||
|
: tempoPrev;
|
||
|
break;
|
||
|
|
||
|
default: // `SEQCMD_SUB_OP_TEMPO_SET`
|
||
|
// `tempoTarget` is the new tempo
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (gActiveSeqs[seqPlayerIndex].tempoOriginal == 0) {
|
||
|
gActiveSeqs[seqPlayerIndex].tempoOriginal = tempoPrev;
|
||
|
}
|
||
|
|
||
|
gActiveSeqs[seqPlayerIndex].tempoTarget = tempoTarget;
|
||
|
gActiveSeqs[seqPlayerIndex].tempoCur = gAudioCtx.seqPlayers[seqPlayerIndex].tempo / 0x30;
|
||
|
gActiveSeqs[seqPlayerIndex].tempoStep =
|
||
|
(gActiveSeqs[seqPlayerIndex].tempoCur - gActiveSeqs[seqPlayerIndex].tempoTarget) / tempoTimer;
|
||
|
gActiveSeqs[seqPlayerIndex].tempoTimer = tempoTimer;
|
||
|
gActiveSeqs[seqPlayerIndex].tempoCmd = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Step tempo to target
|
||
|
if (gActiveSeqs[seqPlayerIndex].tempoTimer != 0) {
|
||
|
gActiveSeqs[seqPlayerIndex].tempoTimer--;
|
||
|
if (gActiveSeqs[seqPlayerIndex].tempoTimer != 0) {
|
||
|
gActiveSeqs[seqPlayerIndex].tempoCur -= gActiveSeqs[seqPlayerIndex].tempoStep;
|
||
|
} else {
|
||
|
gActiveSeqs[seqPlayerIndex].tempoCur = gActiveSeqs[seqPlayerIndex].tempoTarget;
|
||
|
}
|
||
|
|
||
|
AUDIOCMD_SEQPLAYER_SET_TEMPO(seqPlayerIndex, gActiveSeqs[seqPlayerIndex].tempoCur);
|
||
|
}
|
||
|
|
||
|
// Update channel volumes
|
||
|
if (gActiveSeqs[seqPlayerIndex].volChannelFlags != 0) {
|
||
|
for (channelIndex = 0; channelIndex < SEQ_NUM_CHANNELS; channelIndex++) {
|
||
|
if (gActiveSeqs[seqPlayerIndex].channelData[channelIndex].volTimer != 0) {
|
||
|
gActiveSeqs[seqPlayerIndex].channelData[channelIndex].volTimer--;
|
||
|
if (gActiveSeqs[seqPlayerIndex].channelData[channelIndex].volTimer != 0) {
|
||
|
gActiveSeqs[seqPlayerIndex].channelData[channelIndex].volCur -=
|
||
|
gActiveSeqs[seqPlayerIndex].channelData[channelIndex].volStep;
|
||
|
} else {
|
||
|
gActiveSeqs[seqPlayerIndex].channelData[channelIndex].volCur =
|
||
|
gActiveSeqs[seqPlayerIndex].channelData[channelIndex].volTarget;
|
||
|
gActiveSeqs[seqPlayerIndex].volChannelFlags ^= (1 << channelIndex);
|
||
|
}
|
||
|
|
||
|
AUDIOCMD_CHANNEL_SET_VOL_SCALE(seqPlayerIndex, channelIndex,
|
||
|
gActiveSeqs[seqPlayerIndex].channelData[channelIndex].volCur);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Update frequencies
|
||
|
if (gActiveSeqs[seqPlayerIndex].freqScaleChannelFlags != 0) {
|
||
|
for (channelIndex = 0; channelIndex < SEQ_NUM_CHANNELS; channelIndex++) {
|
||
|
if (gActiveSeqs[seqPlayerIndex].channelData[channelIndex].freqScaleTimer != 0) {
|
||
|
gActiveSeqs[seqPlayerIndex].channelData[channelIndex].freqScaleTimer--;
|
||
|
if (gActiveSeqs[seqPlayerIndex].channelData[channelIndex].freqScaleTimer != 0) {
|
||
|
gActiveSeqs[seqPlayerIndex].channelData[channelIndex].freqScaleCur -=
|
||
|
gActiveSeqs[seqPlayerIndex].channelData[channelIndex].freqScaleStep;
|
||
|
} else {
|
||
|
gActiveSeqs[seqPlayerIndex].channelData[channelIndex].freqScaleCur =
|
||
|
gActiveSeqs[seqPlayerIndex].channelData[channelIndex].freqScaleTarget;
|
||
|
gActiveSeqs[seqPlayerIndex].freqScaleChannelFlags ^= (1 << channelIndex);
|
||
|
}
|
||
|
|
||
|
AUDIOCMD_CHANNEL_SET_FREQ_SCALE(seqPlayerIndex, channelIndex,
|
||
|
gActiveSeqs[seqPlayerIndex].channelData[channelIndex].freqScaleCur);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Process setup commands
|
||
|
if (gActiveSeqs[seqPlayerIndex].setupCmdNum != 0) {
|
||
|
// If there is a SeqCmd to reset the audio heap queued, then drop all setup commands
|
||
|
if (!AudioSeq_IsSeqCmdNotQueued(SEQCMD_OP_RESET_AUDIO_HEAP << 28, SEQCMD_OP_MASK)) {
|
||
|
gActiveSeqs[seqPlayerIndex].setupCmdNum = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Only process setup commands once the timer reaches zero
|
||
|
if (gActiveSeqs[seqPlayerIndex].setupCmdTimer != 0) {
|
||
|
gActiveSeqs[seqPlayerIndex].setupCmdTimer--;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Only process setup commands if `seqPlayerIndex` if no longer playing
|
||
|
// i.e. the `seqPlayer` is no longer enabled
|
||
|
if (gAudioCtx.seqPlayers[seqPlayerIndex].enabled) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
for (j = 0; j < gActiveSeqs[seqPlayerIndex].setupCmdNum; j++) {
|
||
|
setupOp = (gActiveSeqs[seqPlayerIndex].setupCmd[j] & 0xF00000) >> 20;
|
||
|
targetSeqPlayerIndex = (gActiveSeqs[seqPlayerIndex].setupCmd[j] & 0xF0000) >> 16;
|
||
|
setupVal2 = (gActiveSeqs[seqPlayerIndex].setupCmd[j] & 0xFF00) >> 8;
|
||
|
setupVal1 = gActiveSeqs[seqPlayerIndex].setupCmd[j] & 0xFF;
|
||
|
|
||
|
switch (setupOp) {
|
||
|
case SEQCMD_SUB_OP_SETUP_RESTORE_SEQPLAYER_VOLUME:
|
||
|
// Restore `targetSeqPlayerIndex` volume back to normal levels
|
||
|
AudioSeq_SetVolumeScale(targetSeqPlayerIndex, VOL_SCALE_INDEX_FANFARE, 0x7F, setupVal1);
|
||
|
break;
|
||
|
|
||
|
case SEQCMD_SUB_OP_SETUP_RESTORE_SEQPLAYER_VOLUME_IF_QUEUED:
|
||
|
// Restore `targetSeqPlayerIndex` volume back to normal levels,
|
||
|
// but only if the number of sequence queue requests from `sSeqRequests`
|
||
|
// exactly matches the argument to the command
|
||
|
if (setupVal1 == sNumSeqRequests[seqPlayerIndex]) {
|
||
|
AudioSeq_SetVolumeScale(targetSeqPlayerIndex, VOL_SCALE_INDEX_FANFARE, 0x7F, setupVal2);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case SEQCMD_SUB_OP_SETUP_SEQ_UNQUEUE:
|
||
|
// Unqueue `seqPlayerIndex` from sSeqRequests
|
||
|
//! @bug this command does not work as intended as unqueueing
|
||
|
//! the sequence relies on `gActiveSeqs[seqPlayerIndex].seqId`
|
||
|
//! However, `gActiveSeqs[seqPlayerIndex].seqId` is reset before the sequence on
|
||
|
//! `seqPlayerIndex` is requested to stop, i.e. before the sequence is disabled and setup
|
||
|
//! commands (including this command) can run. A simple fix would have been to unqueue based on
|
||
|
//! `gActiveSeqs[seqPlayerIndex].prevSeqId` instead
|
||
|
SEQCMD_UNQUEUE_SEQUENCE((u8)(seqPlayerIndex + (SEQCMD_ASYNC_ACTIVE >> 24)), 0,
|
||
|
gActiveSeqs[seqPlayerIndex].seqId);
|
||
|
break;
|
||
|
|
||
|
case SEQCMD_SUB_OP_SETUP_RESTART_SEQ:
|
||
|
// Restart the currently active sequence on `targetSeqPlayerIndex` with full volume.
|
||
|
// Sequence on `targetSeqPlayerIndex` must still be active to play (can be muted)
|
||
|
SEQCMD_PLAY_SEQUENCE((u8)(targetSeqPlayerIndex + (SEQCMD_ASYNC_ACTIVE >> 24)), 1,
|
||
|
gActiveSeqs[targetSeqPlayerIndex].seqId);
|
||
|
gActiveSeqs[targetSeqPlayerIndex].fadeVolUpdate = true;
|
||
|
gActiveSeqs[targetSeqPlayerIndex].volScales[1] = 0x7F;
|
||
|
break;
|
||
|
|
||
|
case SEQCMD_SUB_OP_SETUP_TEMPO_SCALE:
|
||
|
// Scale tempo by a multiplicative factor
|
||
|
SEQCMD_SCALE_TEMPO((u8)(targetSeqPlayerIndex + (SEQCMD_ASYNC_ACTIVE >> 24)), setupVal2,
|
||
|
setupVal1);
|
||
|
break;
|
||
|
|
||
|
case SEQCMD_SUB_OP_SETUP_TEMPO_RESET:
|
||
|
// Reset tempo to previous tempo
|
||
|
SEQCMD_RESET_TEMPO((u8)(targetSeqPlayerIndex + (SEQCMD_ASYNC_ACTIVE >> 24)), setupVal1);
|
||
|
break;
|
||
|
|
||
|
case SEQCMD_SUB_OP_SETUP_PLAY_SEQ:
|
||
|
// Play the requested sequence
|
||
|
// Uses the fade timer set by `SEQCMD_SUB_OP_SETUP_SET_FADE_TIMER`
|
||
|
seqId = gActiveSeqs[seqPlayerIndex].setupCmd[j] & 0xFFFF;
|
||
|
SEQCMD_PLAY_SEQUENCE((u8)(targetSeqPlayerIndex + (SEQCMD_ASYNC_ACTIVE >> 24)),
|
||
|
gActiveSeqs[targetSeqPlayerIndex].setupFadeTimer, seqId);
|
||
|
AudioSeq_SetVolumeScale(targetSeqPlayerIndex, VOL_SCALE_INDEX_FANFARE, 0x7F, 0);
|
||
|
gActiveSeqs[targetSeqPlayerIndex].setupFadeTimer = 0;
|
||
|
break;
|
||
|
|
||
|
case SEQCMD_SUB_OP_SETUP_SET_FADE_TIMER:
|
||
|
// A command specifically to support `SEQCMD_SUB_OP_SETUP_PLAY_SEQ`
|
||
|
// Sets the fade timer for the sequence requested in `SEQCMD_SUB_OP_SETUP_PLAY_SEQ`
|
||
|
gActiveSeqs[seqPlayerIndex].setupFadeTimer = setupVal2;
|
||
|
break;
|
||
|
|
||
|
case SEQCMD_SUB_OP_SETUP_RESTORE_SEQPLAYER_VOLUME_WITH_SCALE_INDEX:
|
||
|
// Restore the volume back to default levels
|
||
|
// Allows a `scaleIndex` to be specified.
|
||
|
AudioSeq_SetVolumeScale(targetSeqPlayerIndex, setupVal2, 0x7F, setupVal1);
|
||
|
break;
|
||
|
|
||
|
case SEQCMD_SUB_OP_SETUP_POP_PERSISTENT_CACHE:
|
||
|
// Discard audio data by popping one more audio caches from the audio heap
|
||
|
if (setupVal1 & (1 << SEQUENCE_TABLE)) {
|
||
|
AUDIOCMD_GLOBAL_POP_PERSISTENT_CACHE(SEQUENCE_TABLE);
|
||
|
}
|
||
|
if (setupVal1 & (1 << FONT_TABLE)) {
|
||
|
AUDIOCMD_GLOBAL_POP_PERSISTENT_CACHE(FONT_TABLE);
|
||
|
}
|
||
|
if (setupVal1 & (1 << SAMPLE_TABLE)) {
|
||
|
AUDIOCMD_GLOBAL_POP_PERSISTENT_CACHE(SAMPLE_TABLE);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case SEQCMD_SUB_OP_SETUP_SET_CHANNEL_DISABLE_MASK:
|
||
|
// Disable (or reenable) specific channels of `targetSeqPlayerIndex`
|
||
|
channelMask = gActiveSeqs[seqPlayerIndex].setupCmd[j] & 0xFFFF;
|
||
|
SEQCMD_SET_CHANNEL_DISABLE_MASK((u8)(targetSeqPlayerIndex + (SEQCMD_ASYNC_ACTIVE >> 24)),
|
||
|
channelMask);
|
||
|
break;
|
||
|
|
||
|
case SEQCMD_SUB_OP_SETUP_SET_SEQPLAYER_FREQ:
|
||
|
// Scale all channels of `targetSeqPlayerIndex`
|
||
|
SEQCMD_SET_SEQPLAYER_FREQ((u8)(targetSeqPlayerIndex + (SEQCMD_ASYNC_ACTIVE >> 24)), setupVal2,
|
||
|
setupVal1 * 10);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
gActiveSeqs[seqPlayerIndex].setupCmdNum = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// @recomp Patched to add the ability to turn off low health beeps.
|
||
|
void LifeMeter_UpdateSizeAndBeep(PlayState* play) {
|
||
|
InterfaceContext* interfaceCtx = &play->interfaceCtx;
|
||
|
|
||
|
if (interfaceCtx->lifeSizeChangeDirection != 0) {
|
||
|
interfaceCtx->lifeSizeChange--;
|
||
|
if (interfaceCtx->lifeSizeChange <= 0) {
|
||
|
interfaceCtx->lifeSizeChange = 0;
|
||
|
interfaceCtx->lifeSizeChangeDirection = 0;
|
||
|
// @recomp Additional check for whether low health beeps are enabled.
|
||
|
if (recomp_get_low_health_beeps_enabled() && !Player_InCsMode(play) && (play->pauseCtx.state == PAUSE_STATE_OFF) &&
|
||
|
(play->pauseCtx.debugEditor == DEBUG_EDITOR_NONE) && LifeMeter_IsCritical() && !Play_InCsMode(play)) {
|
||
|
Audio_PlaySfx(NA_SE_SY_HITPOINT_ALARM);
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
interfaceCtx->lifeSizeChange++;
|
||
|
if ((s32)interfaceCtx->lifeSizeChange >= 10) {
|
||
|
interfaceCtx->lifeSizeChange = 10;
|
||
|
interfaceCtx->lifeSizeChangeDirection = 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|