diff --git a/include/config/config_audio.h b/include/config/config_audio.h index 24e5b3eb..79dca44a 100644 --- a/include/config/config_audio.h +++ b/include/config/config_audio.h @@ -38,6 +38,6 @@ /** * Uses a much better implementation of reverb over vanilla's fake echo reverb. Great for caves or eerie levels, as well as just a better audio experience in general. - * Reverb parameters can be configured in audio/synthesis.c to meet desired aesthetic/performance needs. Currently US/JP only. Hurts emulator and console performance. + * Reverb presets can be configured in audio/data.c to meet desired aesthetic/performance needs. More detailed usage info can also be found on the HackerSM64 Wiki page. */ // #define BETTER_REVERB diff --git a/include/level_commands.h b/include/level_commands.h index 7ae45dfd..d85a1ceb 100644 --- a/include/level_commands.h +++ b/include/level_commands.h @@ -420,12 +420,30 @@ enum GoddardScene { #define GAMMA(enabled) \ CMD_BBBB(LEVEL_CMD_SET_GAMMA, 0x04, enabled, 0x00) -#define SET_BACKGROUND_MUSIC(settingsPreset, seq) \ +#ifdef BETTER_REVERB +#define SET_BACKGROUND_MUSIC_WITH_REVERB(settingsPreset, seq, reverbPresetConsole, reverbPresetEmulator) \ CMD_BBH(LEVEL_CMD_SET_MUSIC, 0x08, settingsPreset), \ - CMD_HH(seq, 0x0000) + CMD_BBH(reverbPresetConsole, reverbPresetEmulator, seq) + +#define SET_MENU_MUSIC_WITH_REVERB(seq, reverbPresetConsole, reverbPresetEmulator) \ + CMD_BBH(LEVEL_CMD_SET_MENU_MUSIC, 0x08, seq), \ + CMD_BBH(reverbPresetConsole, reverbPresetEmulator, 0x0000) +#else +// Functionally identical to calling SET_BACKGROUND_MUSIC if BETTER_REVERB is disabled +#define SET_BACKGROUND_MUSIC_WITH_REVERB(settingsPreset, seq, reverbPresetConsole, reverbPresetEmulator) \ + CMD_BBH(LEVEL_CMD_SET_MUSIC, 0x08, settingsPreset), \ + CMD_HH(0x0000, seq) + +// Functionally identical to calling SET_MENU_MUSIC if BETTER_REVERB is disabled +#define SET_MENU_MUSIC_WITH_REVERB(seq, reverbPresetConsole, reverbPresetEmulator) \ + CMD_BBH(LEVEL_CMD_SET_MENU_MUSIC, 0x04, seq) +#endif + +#define SET_BACKGROUND_MUSIC(settingsPreset, seq) \ + SET_BACKGROUND_MUSIC_WITH_REVERB(settingsPreset, seq, 0x00, 0x00) #define SET_MENU_MUSIC(seq) \ - CMD_BBH(LEVEL_CMD_SET_MENU_MUSIC, 0x04, seq) + SET_MENU_MUSIC_WITH_REVERB(seq, 0x00, 0x00) #define STOP_MUSIC(fadeOutTime) \ CMD_BBH(LEVEL_CMD_FADEOUT_MUSIC, 0x04, fadeOutTime) diff --git a/levels/ending/script.c b/levels/ending/script.c index 073cf6bf..d4b3f9f0 100644 --- a/levels/ending/script.c +++ b/levels/ending/script.c @@ -16,22 +16,26 @@ #include "make_const_nonconst.h" #include "levels/ending/header.h" -const LevelScript level_ending_entry[] = { - /*0*/ INIT_LEVEL(), - /*1*/ LOAD_LEVEL_DATA(ending), - /*4*/ ALLOC_LEVEL_POOL(), - - /*5*/ AREA(/*index*/ 1, ending_geo_area_1), - /*7*/ END_AREA(), - - /*8*/ FREE_LEVEL_POOL(), - /*9*/ SLEEP(/*frames*/ 60), - /*10*/ BLACKOUT(/*active*/ FALSE), - /*11*/ LOAD_AREA(/*area*/ 1), - /*12*/ TRANSITION(/*transType*/ WARP_TRANSITION_FADE_FROM_COLOR, /*time*/ 75, /*color*/ 0x00, 0x00, 0x00), - /*14*/ SLEEP(/*frames*/ 120), - /*15*/ CALL(/*arg*/ 0, /*func*/ lvl_play_the_end_screen_sound), - // L1: - /*17*/ SLEEP(/*frames*/ 1), - /*18*/ JUMP(level_ending_entry + 17), // goto L1 (loop sleep 1 forever) +const LevelScript level_ending_entry_loop[] = { + SLEEP(/*frames*/ 1), + JUMP(level_ending_entry_loop), // (loop sleep 1 forever) +}; + +const LevelScript level_ending_entry[] = { + INIT_LEVEL(), + LOAD_LEVEL_DATA(ending), + ALLOC_LEVEL_POOL(), + + AREA(/*index*/ 1, ending_geo_area_1), + END_AREA(), + + FREE_LEVEL_POOL(), + SLEEP(/*frames*/ 60), + BLACKOUT(/*active*/ FALSE), + LOAD_AREA(/*area*/ 1), + TRANSITION(/*transType*/ WARP_TRANSITION_FADE_FROM_COLOR, /*time*/ 75, /*color*/ 0x00, 0x00, 0x00), + SLEEP(/*frames*/ 120), + CALL(/*arg*/ 0, /*func*/ lvl_play_the_end_screen_sound), + + JUMP(level_ending_entry_loop), // (loop sleep 1 forever) }; diff --git a/levels/intro/script.c b/levels/intro/script.c index 7f459911..a2a161b8 100644 --- a/levels/intro/script.c +++ b/levels/intro/script.c @@ -26,10 +26,10 @@ #include "game/object_list_processor.h" const LevelScript level_intro_splash_screen[] = { - INIT_LEVEL(), #ifdef SKIP_TITLE_SCREEN EXIT_AND_EXECUTE_WITH_CODE(/*seg*/ SEGMENT_MENU_INTRO, _introSegmentRomStart, _introSegmentRomEnd, level_intro_mario_head_regular, _introSegmentBssStart, _introSegmentBssEnd), #endif + INIT_LEVEL(), LOAD_GODDARD(), LOAD_BEHAVIOR_DATA(), LOAD_LEVEL_DATA(intro), @@ -48,6 +48,7 @@ const LevelScript level_intro_splash_screen[] = { // Start animation LOAD_AREA(/*area*/ 1), + SET_MENU_MUSIC(/*seq*/ SEQ_SOUND_PLAYER), CALL(/*arg*/ LVL_INTRO_PLAY_ITS_A_ME_MARIO, /*func*/ lvl_intro_update), CALL(/*arg*/ 0, /*func*/ load_mario_area), @@ -70,6 +71,7 @@ const LevelScript level_intro_splash_screen[] = { // Start animation LOAD_AREA(/*area*/ 1), + SET_MENU_MUSIC(/*seq*/ SEQ_SOUND_PLAYER), CALL(/*arg*/ LVL_INTRO_PLAY_ITS_A_ME_MARIO, /*func*/ lvl_intro_update), SLEEP(/*frames*/ 75), TRANSITION(/*transType*/ WARP_TRANSITION_FADE_INTO_COLOR, /*time*/ 16, /*color*/ 0x00, 0x00, 0x00), diff --git a/levels/menu/script.c b/levels/menu/script.c index d8819885..a4b032e2 100644 --- a/levels/menu/script.c +++ b/levels/menu/script.c @@ -57,40 +57,44 @@ const LevelScript level_main_menu_entry_file_select[] = { EXIT_AND_EXECUTE(/*seg*/ SEGMENT_GLOBAL_LEVEL_SCRIPT, _scriptsSegmentRomStart, _scriptsSegmentRomEnd, level_main_scripts_entry), }; +const LevelScript level_main_menu_entry_act_select_exit[] = { + EXIT(), +}; + const LevelScript level_main_menu_entry_act_select[] = { - /* 0*/ CALL(/*arg*/ 0, /*func*/ lvl_set_current_level), - /* 2*/ JUMP_IF(/*op*/ OP_EQ, /*arg*/ FALSE, (level_main_menu_entry_act_select + 42)), // goto L1 (exit) - /* 5*/ INIT_LEVEL(), - /* 6*/ LOAD_GODDARD(), - /*10*/ LOAD_LEVEL_DATA(menu), - /*13*/ ALLOC_LEVEL_POOL(), + CALL(/*arg*/ 0, /*func*/ lvl_set_current_level), + JUMP_IF(/*op*/ OP_EQ, /*arg*/ FALSE, (level_main_menu_entry_act_select_exit)), + INIT_LEVEL(), + LOAD_GODDARD(), + LOAD_LEVEL_DATA(menu), + ALLOC_LEVEL_POOL(), - /*14*/ AREA(/*index*/ 2, geo_menu_act_selector_strings), - /*16*/ OBJECT(/*model*/ MODEL_NONE, /*pos*/ 0, -100, 0, /*angle*/ 0, 0, 0, /*behParam*/ BP(0x04, 0x00, 0x00, 0x00), /*beh*/ bhvActSelector), - /*22*/ TERRAIN(/*terrainData*/ main_menu_seg7_collision), - /*24*/ END_AREA(), + AREA(/*index*/ 2, geo_menu_act_selector_strings), + OBJECT(/*model*/ MODEL_NONE, /*pos*/ 0, -100, 0, /*angle*/ 0, 0, 0, /*behParam*/ BP(0x04, 0x00, 0x00, 0x00), /*beh*/ bhvActSelector), + TERRAIN(/*terrainData*/ main_menu_seg7_collision), + END_AREA(), - /*25*/ FREE_LEVEL_POOL(), - /*26*/ LOAD_AREA(/*area*/ 2), + FREE_LEVEL_POOL(), + LOAD_AREA(/*area*/ 2), #ifdef NO_SEGMENTED_MEMORY // sVisibleStars is set to 0 during FIXED_LOAD above on N64, but not when NO_SEGMENTED_MEMORY is used. // lvl_init_act_selector_values_and_stars must be called here otherwise the previous // value is retained and causes incorrect drawing during the 16 transition frames. CALL(/*arg*/ 0, /*func*/ lvl_init_act_selector_values_and_stars), #endif - /*27*/ TRANSITION(/*transType*/ WARP_TRANSITION_FADE_FROM_COLOR, /*time*/ 16, /*color*/ 0xFF, 0xFF, 0xFF), - /*29*/ SLEEP(/*frames*/ 16), - /*30*/ SET_MENU_MUSIC(/*seq*/ 0x000D), + TRANSITION(/*transType*/ WARP_TRANSITION_FADE_FROM_COLOR, /*time*/ 16, /*color*/ 0xFF, 0xFF, 0xFF), + SLEEP(/*frames*/ 16), + SET_MENU_MUSIC(/*seq*/ 0x000D), #ifndef NO_SEGMENTED_MEMORY - /*31*/ CALL( /*arg*/ 0, /*func*/ lvl_init_act_selector_values_and_stars), + CALL( /*arg*/ 0, /*func*/ lvl_init_act_selector_values_and_stars), #endif - /*33*/ CALL_LOOP(/*arg*/ 0, /*func*/ lvl_update_obj_and_load_act_button_actions), - /*35*/ GET_OR_SET(/*op*/ OP_SET, /*var*/ VAR_CURR_ACT_NUM), - /*36*/ STOP_MUSIC(/*fadeOutTime*/ 0x00BE), - /*37*/ TRANSITION(/*transType*/ WARP_TRANSITION_FADE_INTO_COLOR, /*time*/ 16, /*color*/ 0xFF, 0xFF, 0xFF), - /*39*/ SLEEP(/*frames*/ 16), - /*40*/ CLEAR_LEVEL(), - /*41*/ SLEEP_BEFORE_EXIT(/*frames*/ 1), - // L1: - /*42*/ EXIT(), + CALL_LOOP(/*arg*/ 0, /*func*/ lvl_update_obj_and_load_act_button_actions), + GET_OR_SET(/*op*/ OP_SET, /*var*/ VAR_CURR_ACT_NUM), + STOP_MUSIC(/*fadeOutTime*/ 0x00BE), + TRANSITION(/*transType*/ WARP_TRANSITION_FADE_INTO_COLOR, /*time*/ 16, /*color*/ 0xFF, 0xFF, 0xFF), + SLEEP(/*frames*/ 16), + CLEAR_LEVEL(), + SLEEP_BEFORE_EXIT(/*frames*/ 1), + + EXIT(), }; diff --git a/src/audio/data.c b/src/audio/data.c index bfe942ce..b235130d 100644 --- a/src/audio/data.c +++ b/src/audio/data.c @@ -41,6 +41,71 @@ struct AudioSessionSettingsEU gAudioSessionPresets[] = { }; #endif +#ifdef BETTER_REVERB +// Each entry represents an array of variable audio buffer sizes / delays for each respective filter. +u32 delaysArr[][NUM_ALLPASS] = { + { /* 0 */ + 4, 4, 4, + 4, 4, 4, + 4, 4, 4, + 4, 4, 4 + }, + { /* 1 */ + 1080, 1352, 1200, + 1200, 1232, 1432, + 1384, 1048, 1352, + 928, 1504, 1512 + }, + { /* 2 */ + 1384, 1352, 1048, + 928, 1512, 1504, + 1080, 1200, 1352, + 1200, 1432, 1232 + }, +}; + +// Each entry represents an array of multipliers applied to the final output of each group of 3 filters. +// These values are u8s in spirit, but are set as s32 values to slightly increase performance during calculations. +s32 reverbMultsArr[][NUM_ALLPASS / 3] = { + /* 0 */ {0x00, 0x00, 0x00, 0x00}, + /* 1 */ {0xD7, 0x6F, 0x36, 0x22}, + /* 2 */ {0xCF, 0x73, 0x38, 0x1F}, +}; + +/** + * Format: + * - downsampleRate (Higher values exponentially reduce the number of input samples to process, improving perfomance at cost of quality) + * - isMono (Only process reverb on the left channel and share it with the right channel, improving performance at cost of quality) + * - filterCount (Number of filters to process data with; in general, more filters means higher quality at the cost of performance demand) + * - windowSize (Size of circular reverb buffer; higher values work better for a more open soundscape, lower is better for a more compact sound) + * - gain (Amount of audio retransmitted into the circular reverb buffer, emulating decay; higher values represent a lengthier decay period) + * - gainIndex (Advanced parameter used to tune the outputs of every first two of three filters) + * - reverbIndex (Advanced parameter used to tune the incoming output of every third filter) + * + * - *delaysL (Array of variable audio buffer sizes / delays for each respective filter [left channel]) + * - *delaysR (Array of variable audio buffer sizes / delays for each respective filter [right channel]) + * - *reverbMultsL (Array of multipliers applied to the final output of each group of 3 filters [left channel]) + * - *reverbMultsR (Array of multipliers applied to the final output of each group of 3 filters [right channel]) + * + * NOTE: First entry will always be used by default when not using the level commands to specify a preset. + * Please reference the HackerSM64 Wiki for more descriptive documentation of these parameters and usage of BETTER_REVERB in general. + */ +struct BetterReverbSettings gBetterReverbSettings[] = { + { /* 0 */ + -1, FALSE, NUM_ALLPASS, -1, -1, 0x00, 0x00, // Vanilla Reverb + delaysArr[0], delaysArr[0], reverbMultsArr[0], reverbMultsArr[0] + }, + { /* 1 */ + 2, FALSE, (NUM_ALLPASS - 9), 0xE00, 0x43FF, 0xA0, 0x30, // Default Console + delaysArr[1], delaysArr[2], reverbMultsArr[1], reverbMultsArr[2] + }, + { /* 2 */ + 1, FALSE, NUM_ALLPASS, 0xE00, 0x28FF, 0xA0, 0x60, // Default Emulator (RCVI Hack only) + delaysArr[1], delaysArr[2], reverbMultsArr[1], reverbMultsArr[2] + }, +}; +#endif + // Format: // - frequency // - max number of simultaneous notes @@ -76,10 +141,9 @@ struct ReverbSettingsUS gReverbSettings[18] = { { 1, 0x0800, 0x2FFF }, }; -struct AudioSessionSettings gAudioSessionPresets[1] = { - { 32000, MAX_SIMULTANEOUS_NOTES, 1, 0x1000, 0x2FFF, 0x7FFF, PERSISTENT_SEQ_MEM, PERSISTENT_BANK_MEM, TEMPORARY_SEQ_MEM, TEMPORARY_BANK_MEM }, -}; +struct AudioSessionSettings gAudioSessionSettings = { 32000, MAX_SIMULTANEOUS_NOTES, 0x7FFF, PERSISTENT_SEQ_MEM, PERSISTENT_BANK_MEM, TEMPORARY_SEQ_MEM, TEMPORARY_BANK_MEM }; #endif + // gAudioCosineTable[k] = round((2**15 - 1) * cos(pi/2 * k / 127)). Unused. #if defined(VERSION_JP) || defined(VERSION_US) u16 gAudioCosineTable[128] = { diff --git a/src/audio/data.h b/src/audio/data.h index e4351788..8a38590b 100644 --- a/src/audio/data.h +++ b/src/audio/data.h @@ -44,10 +44,12 @@ extern struct AudioSessionSettingsEU gAudioSessionPresets[]; extern struct ReverbSettingsEU sReverbSettings[8]; #else -extern struct AudioSessionSettings gAudioSessionPresets[1]; +extern struct AudioSessionSettings gAudioSessionSettings; extern struct ReverbSettingsUS gReverbSettings[18]; #endif -extern u16 D_80332388[128]; // unused +#ifdef BETTER_REVERB +extern struct BetterReverbSettings gBetterReverbSettings[]; +#endif #if defined(VERSION_EU) || defined(VERSION_SH) extern f32 gPitchBendFrequencyScale[256]; diff --git a/src/audio/external.c b/src/audio/external.c index bd189ee9..4b8d9760 100644 --- a/src/audio/external.c +++ b/src/audio/external.c @@ -326,7 +326,6 @@ u8 sMaxChannelsForSoundBank[SOUND_BANK_COUNT] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } f32 gGlobalSoundSource[3] = { 0.0f, 0.0f, 0.0f }; u8 sSoundBankDisabled[16] = { 0 }; -u8 D_80332108 = 0; u8 sHasStartedFadeOut = FALSE; u16 sSoundBanksThatLowerBackgroundMusic = 0; u8 sBackgroundMusicMaxTargetVolume = TARGET_VOLUME_UNSET; @@ -2505,9 +2504,9 @@ void play_toads_jingle(void) { /** * Called from threads: thread5_game_loop */ -void sound_reset(u8 presetId) { - if (presetId >= 8) { - presetId = 0; +void sound_reset(u8 reverbPresetId) { + if (reverbPresetId >= ARRAY_COUNT(gReverbSettings)) { + reverbPresetId = 0; } sGameLoopTicked = 0; disable_all_sequence_players(); @@ -2516,26 +2515,16 @@ void sound_reset(u8 presetId) { func_802ad74c(0xF2000000, 0); #endif #if defined(VERSION_JP) || defined(VERSION_US) - audio_reset_session(&gAudioSessionPresets[0], presetId); + audio_reset_session(reverbPresetId); #else - audio_reset_session_eu(presetId); + audio_reset_session_eu(reverbPresetId); #endif osWritebackDCacheAll(); - if (presetId != 7) { + if (reverbPresetId != 7) { preload_sequence(SEQ_EVENT_SOLVE_PUZZLE, PRELOAD_BANKS | PRELOAD_SEQUENCE); preload_sequence(SEQ_EVENT_PEACH_MESSAGE, PRELOAD_BANKS | PRELOAD_SEQUENCE); preload_sequence(SEQ_EVENT_CUTSCENE_STAR_SPAWN, PRELOAD_BANKS | PRELOAD_SEQUENCE); } seq_player_play_sequence(SEQ_PLAYER_SFX, SEQ_SOUND_PLAYER, 0); - D_80332108 = (D_80332108 & 0xf0) + presetId; - gSoundMode = D_80332108 >> 4; sHasStartedFadeOut = FALSE; } - -/** - * Called from threads: thread5_game_loop - */ -void audio_set_sound_mode(u8 soundMode) { - D_80332108 = (D_80332108 & 0xf) + (soundMode << 4); - gSoundMode = soundMode; -} diff --git a/src/audio/external.h b/src/audio/external.h index 6079f439..706d073c 100644 --- a/src/audio/external.h +++ b/src/audio/external.h @@ -66,8 +66,7 @@ void play_star_fanfare(void); void play_power_star_jingle(void); void play_race_fanfare(void); void play_toads_jingle(void); -void sound_reset(u8 presetId); -void audio_set_sound_mode(u8 soundMode); +void sound_reset(u8 reverbPresetId); void audio_init(void); // in load.c diff --git a/src/audio/heap.c b/src/audio/heap.c index b7a924b4..141a583b 100644 --- a/src/audio/heap.c +++ b/src/audio/heap.c @@ -9,6 +9,7 @@ #include "game/game_init.h" #include "game/puppyprint.h" #include "game/vc_check.h" +#include "game/debug.h" #include "string.h" struct PoolSplit { @@ -983,7 +984,7 @@ void init_reverb_eu(void) { // This is called 4 times for numReverbs to work at higher values. This does eat up some memory though. for (j = 0; j < 4; j++) { - gSynthesisReverbs[j].useReverb = 0; + gSynthesisReverbs[j].useReverb = FALSE; // Both left and right channels are allocated/cleared together, then separated based on the reverb window size if (!sAudioIsInitialized) { @@ -997,7 +998,7 @@ void init_reverb_eu(void) { reverb->windowSize = (reverbSettings->windowSize * 0x40); reverb->downsampleRate = reverbSettings->downsampleRate; reverb->reverbGain = reverbSettings->gain; - reverb->useReverb = 8; + reverb->useReverb = TRUE; if (reverb->windowSize > REVERB_WINDOW_SIZE_MAX) { reverb->windowSize = REVERB_WINDOW_SIZE_MAX; } @@ -1043,105 +1044,115 @@ void init_reverb_eu(void) { void init_reverb_us(s32 presetId) { s16 *mem; s32 i; -#ifdef BETTER_REVERB - s8 reverbConsole; -#endif s32 reverbWindowSize = gReverbSettings[presetId].windowSize; gReverbDownsampleRate = gReverbSettings[presetId].downsampleRate; #ifdef BETTER_REVERB - if (gIsConsole) { - reverbConsole = betterReverbDownsampleConsole; // Console! - } else { - reverbConsole = betterReverbDownsampleEmulator; // Setting this to 1 is REALLY slow, please use sparingly! - } - if (reverbConsole <= 0) { - reverbConsole = 1; + // This will likely crash if given an invalid preset value. Adding a safety check here isn't worth the usability interference. + struct BetterReverbSettings *betterReverbPreset = &gBetterReverbSettings[gBetterReverbPreset]; + + betterReverbDownsampleRate = betterReverbPreset->downsampleRate; + monoReverb = betterReverbPreset->isMono; + reverbFilterCount = betterReverbPreset->filterCount; + betterReverbWindowsSize = betterReverbPreset->windowSize; + betterReverbRevIndex = betterReverbPreset->reverbIndex; + betterReverbGainIndex = betterReverbPreset->gainIndex; + gReverbMultsL = betterReverbPreset->reverbMultsL; + gReverbMultsR = betterReverbPreset->reverbMultsR; + + if (betterReverbDownsampleRate <= 0) { toggleBetterReverb = FALSE; + if (betterReverbWindowsSize >= 0) + reverbWindowSize = betterReverbWindowsSize; } else { toggleBetterReverb = TRUE; + gReverbDownsampleRate = (1 << (betterReverbDownsampleRate - 1)); + + if (betterReverbWindowsSize >= 0) { + reverbWindowSize = betterReverbWindowsSize; + reverbWindowSize /= gReverbDownsampleRate; + if (reverbWindowSize < DEFAULT_LEN_2CH && betterReverbWindowsSize != 0) // Minimum window size to not overflow + reverbWindowSize = DEFAULT_LEN_2CH; + } } - if (toggleBetterReverb && betterReverbWindowsSize >= 0) { - reverbWindowSize = betterReverbWindowsSize; - } - if (gReverbDownsampleRate < (1 << (reverbConsole - 1))) { - gReverbDownsampleRate = (1 << (reverbConsole - 1)); - } - reverbWindowSize /= gReverbDownsampleRate; - if (reverbWindowSize < DEFAULT_LEN_2CH) { // Minimum window size to not overflow - reverbWindowSize = DEFAULT_LEN_2CH; + reverbFilterCount -= reverbFilterCount % 3; + + if (reverbFilterCount > NUM_ALLPASS) { + reverbFilterCount = NUM_ALLPASS; + } else if (reverbFilterCount < 3) { + reverbFilterCount = 3; } #endif - if (reverbWindowSize == 0) { - gSynthesisReverb.useReverb = 0; + if (reverbWindowSize > 0) + gSynthesisReverb.useReverb = TRUE; + else + gSynthesisReverb.useReverb = FALSE; + + if (reverbWindowSize > REVERB_WINDOW_SIZE_MAX) { + reverbWindowSize = REVERB_WINDOW_SIZE_MAX; + } + // Both left and right channels are allocated/cleared together, then separated based on the reverb window size + if (!sAudioIsInitialized) { + gSynthesisReverb.ringBuffer.left = soundAlloc(&gNotesAndBuffersPool, REVERB_WINDOW_SIZE_MAX * 2 * sizeof(s16)); + + gSynthesisReverb.resampleStateLeft = soundAlloc(&gNotesAndBuffersPool, (16 * sizeof(s16))); + gSynthesisReverb.resampleStateRight = soundAlloc(&gNotesAndBuffersPool, (16 * sizeof(s16))); + gSynthesisReverb.unk24 = soundAlloc(&gNotesAndBuffersPool, (16 * sizeof(s16))); + gSynthesisReverb.unk28 = soundAlloc(&gNotesAndBuffersPool, (16 * sizeof(s16))); + for (i = 0; i < gAudioUpdatesPerFrame; i++) { + mem = soundAlloc(&gNotesAndBuffersPool, DEFAULT_LEN_2CH); + gSynthesisReverb.items[0][i].toDownsampleLeft = mem; + gSynthesisReverb.items[0][i].toDownsampleRight = (mem + (DEFAULT_LEN_1CH / sizeof(s16))); + mem = soundAlloc(&gNotesAndBuffersPool, DEFAULT_LEN_2CH); + gSynthesisReverb.items[1][i].toDownsampleLeft = mem; + gSynthesisReverb.items[1][i].toDownsampleRight = (mem + (DEFAULT_LEN_1CH / sizeof(s16))); + } } else { - gSynthesisReverb.useReverb = 8; - if (reverbWindowSize > REVERB_WINDOW_SIZE_MAX) { - reverbWindowSize = REVERB_WINDOW_SIZE_MAX; - } - // Both left and right channels are allocated/cleared together, then separated based on the reverb window size - if (!sAudioIsInitialized) { - gSynthesisReverb.ringBuffer.left = soundAlloc(&gNotesAndBuffersPool, REVERB_WINDOW_SIZE_MAX * 2 * sizeof(s16)); - - gSynthesisReverb.resampleStateLeft = soundAlloc(&gNotesAndBuffersPool, (16 * sizeof(s16))); - gSynthesisReverb.resampleStateRight = soundAlloc(&gNotesAndBuffersPool, (16 * sizeof(s16))); - gSynthesisReverb.unk24 = soundAlloc(&gNotesAndBuffersPool, (16 * sizeof(s16))); - gSynthesisReverb.unk28 = soundAlloc(&gNotesAndBuffersPool, (16 * sizeof(s16))); - for (i = 0; i < gAudioUpdatesPerFrame; i++) { - mem = soundAlloc(&gNotesAndBuffersPool, DEFAULT_LEN_2CH); - gSynthesisReverb.items[0][i].toDownsampleLeft = mem; - gSynthesisReverb.items[0][i].toDownsampleRight = (mem + (DEFAULT_LEN_1CH / sizeof(s16))); - mem = soundAlloc(&gNotesAndBuffersPool, DEFAULT_LEN_2CH); - gSynthesisReverb.items[1][i].toDownsampleLeft = mem; - gSynthesisReverb.items[1][i].toDownsampleRight = (mem + (DEFAULT_LEN_1CH / sizeof(s16))); - } -#ifdef BETTER_REVERB - initialize_better_reverb_buffers(); -#endif - } else { - bzero(gSynthesisReverb.ringBuffer.left, (REVERB_WINDOW_SIZE_MAX * 2 * sizeof(s16))); - } - - gSynthesisReverb.ringBuffer.right = &gSynthesisReverb.ringBuffer.left[reverbWindowSize]; - gSynthesisReverb.nextRingBufferPos = 0; - gSynthesisReverb.unkC = 0; - gSynthesisReverb.curFrame = 0; - gSynthesisReverb.bufSizePerChannel = reverbWindowSize; - gSynthesisReverb.reverbGain = gReverbSettings[presetId].gain; - gSynthesisReverb.framesLeftToIgnore = 2; - if (gReverbDownsampleRate != 1) { - gSynthesisReverb.resampleFlags = A_INIT; - gSynthesisReverb.resampleRate = (0x8000 / gReverbDownsampleRate); - if (sAudioIsInitialized) { - bzero(gSynthesisReverb.resampleStateLeft, (16 * sizeof(s16))); - bzero(gSynthesisReverb.resampleStateRight, (16 * sizeof(s16))); - bzero(gSynthesisReverb.unk24, (16 * sizeof(s16))); - bzero(gSynthesisReverb.unk28, (16 * sizeof(s16))); - - // All reverb downsample buffers are adjacent in memory, so clear them all in a single call - bzero(gSynthesisReverb.items[0][0].toDownsampleLeft, (DEFAULT_LEN_1CH * 4 * gAudioUpdatesPerFrame)); - } - } - - // This does not have to be reset after being initialized for the first time, which would speed up load times dramatically. - // However, reseting this allows for proper clearing of the reverb buffers, as well as dynamic customization of the delays array. -#ifdef BETTER_REVERB - if (toggleBetterReverb) { - if (sAudioIsInitialized) { - clear_better_reverb_buffers(); - } - - set_better_reverb_buffers(); - } -#endif + bzero(gSynthesisReverb.ringBuffer.left, (REVERB_WINDOW_SIZE_MAX * 2 * sizeof(s16))); } + + gSynthesisReverb.ringBuffer.right = &gSynthesisReverb.ringBuffer.left[reverbWindowSize]; + gSynthesisReverb.nextRingBufferPos = 0; + gSynthesisReverb.unkC = 0; + gSynthesisReverb.curFrame = 0; + gSynthesisReverb.bufSizePerChannel = reverbWindowSize; + gSynthesisReverb.reverbGain = gReverbSettings[presetId].gain; + gSynthesisReverb.framesLeftToIgnore = 2; + if (gReverbDownsampleRate != 1) { + gSynthesisReverb.resampleFlags = A_INIT; + gSynthesisReverb.resampleRate = (0x8000 / gReverbDownsampleRate); + if (sAudioIsInitialized) { + bzero(gSynthesisReverb.resampleStateLeft, (16 * sizeof(s16))); + bzero(gSynthesisReverb.resampleStateRight, (16 * sizeof(s16))); + bzero(gSynthesisReverb.unk24, (16 * sizeof(s16))); + bzero(gSynthesisReverb.unk28, (16 * sizeof(s16))); + + // All reverb downsample buffers are adjacent in memory, so clear them all in a single call + bzero(gSynthesisReverb.items[0][0].toDownsampleLeft, (DEFAULT_LEN_1CH * 4 * gAudioUpdatesPerFrame)); + } + } + +#ifdef BETTER_REVERB + if (!gSynthesisReverb.useReverb) + toggleBetterReverb = FALSE; + + if (betterReverbPreset->gain > 0) + gSynthesisReverb.reverbGain = (u16) betterReverbPreset->gain; + + if (!sAudioIsInitialized) + initialize_better_reverb_buffers(); + + // This does not have to be reset after being initialized for the first time, which would help speed up load times. + // However, resetting this allows for proper clearing of the reverb buffers, as well as dynamic customization of the delays array. + set_better_reverb_buffers(betterReverbPreset->delaysL, betterReverbPreset->delaysR); +#endif } #endif #if defined(VERSION_JP) || defined(VERSION_US) -void audio_reset_session(struct AudioSessionSettings *preset, s32 presetId) { +void audio_reset_session(s32 reverbPresetId) { if (sAudioIsInitialized) { if (gAudioLoadLock != AUDIO_LOCK_UNINITIALIZED) { gAudioLoadLock = AUDIO_LOCK_LOADING; @@ -1163,7 +1174,7 @@ void audio_reset_session(struct AudioSessionSettings *preset, s32 presetId) { temporary_pool_clear( &gBankLoadedPool.temporary); reset_bank_and_seq_load_status(); - init_reverb_us(presetId); + init_reverb_us(reverbPresetId); bzero(&gAiBuffers[0][0], (AIBUFFER_LEN * NUMAIBUFFERS)); if (gAudioLoadLock != AUDIO_LOCK_UNINITIALIZED) { @@ -1319,11 +1330,11 @@ void audio_reset_session(void) { gMaxAudioCmds = gMaxSimultaneousNotes * 0x10 * gAudioBufferParameters.updatesPerFrame + preset->numReverbs * 0x20 + 0x300; #endif #else - gAiFrequency = osAiSetFrequency(preset->frequency); - gMaxSimultaneousNotes = preset->maxSimultaneousNotes; + gAiFrequency = osAiSetFrequency(gAudioSessionSettings.frequency); + gMaxSimultaneousNotes = gAudioSessionSettings.maxSimultaneousNotes; gSamplesPerFrameTarget = ALIGN16(gAiFrequency / 60); - gVolume = preset->volume; + gVolume = gAudioSessionSettings.volume; gMinAiBufferLength = gSamplesPerFrameTarget - 0x10; gAudioUpdatesPerFrame = updatesPerFrame = gSamplesPerFrameTarget / 160 + 1; @@ -1352,8 +1363,8 @@ void audio_reset_session(void) { persistentMem = DOUBLE_SIZE_ON_64_BIT(preset->persistentSeqMem + preset->persistentBankMem + preset->unk18 + preset->unkMem28 + 0x10); temporaryMem = DOUBLE_SIZE_ON_64_BIT(preset->temporarySeqMem + preset->temporaryBankMem + preset->unk24 + preset->unkMem2C + 0x10); #else - persistentMem = DOUBLE_SIZE_ON_64_BIT(preset->persistentSeqMem + preset->persistentBankMem); - temporaryMem = DOUBLE_SIZE_ON_64_BIT(preset->temporarySeqMem + preset->temporaryBankMem); + persistentMem = DOUBLE_SIZE_ON_64_BIT(gAudioSessionSettings.persistentSeqMem + gAudioSessionSettings.persistentBankMem); + temporaryMem = DOUBLE_SIZE_ON_64_BIT(gAudioSessionSettings.temporarySeqMem + gAudioSessionSettings.temporaryBankMem); #endif totalMem = persistentMem + temporaryMem; wantMisc = gAudioSessionPool.size - totalMem - BETTER_REVERB_SIZE; @@ -1363,16 +1374,16 @@ void audio_reset_session(void) { sSeqAndBankPoolSplit.wantPersistent = persistentMem; sSeqAndBankPoolSplit.wantTemporary = temporaryMem; seq_and_bank_pool_init(&sSeqAndBankPoolSplit); - sPersistentCommonPoolSplit.wantSeq = DOUBLE_SIZE_ON_64_BIT(preset->persistentSeqMem); - sPersistentCommonPoolSplit.wantBank = DOUBLE_SIZE_ON_64_BIT(preset->persistentBankMem); + sPersistentCommonPoolSplit.wantSeq = DOUBLE_SIZE_ON_64_BIT(gAudioSessionSettings.persistentSeqMem); + sPersistentCommonPoolSplit.wantBank = DOUBLE_SIZE_ON_64_BIT(gAudioSessionSettings.persistentBankMem); #ifdef VERSION_SH sPersistentCommonPoolSplit.wantUnused = preset->unk18; #else sPersistentCommonPoolSplit.wantUnused = 0; #endif persistent_pools_init(&sPersistentCommonPoolSplit); - sTemporaryCommonPoolSplit.wantSeq = DOUBLE_SIZE_ON_64_BIT(preset->temporarySeqMem); - sTemporaryCommonPoolSplit.wantBank = DOUBLE_SIZE_ON_64_BIT(preset->temporaryBankMem); + sTemporaryCommonPoolSplit.wantSeq = DOUBLE_SIZE_ON_64_BIT(gAudioSessionSettings.temporarySeqMem); + sTemporaryCommonPoolSplit.wantBank = DOUBLE_SIZE_ON_64_BIT(gAudioSessionSettings.temporaryBankMem); #ifdef VERSION_SH sTemporaryCommonPoolSplit.wantUnused = preset->unk24; #else @@ -1404,7 +1415,7 @@ void audio_reset_session(void) { init_reverb_eu(); #else - init_reverb_us(presetId); + init_reverb_us(reverbPresetId); #endif init_sample_dma_buffers(gMaxSimultaneousNotes); diff --git a/src/audio/heap.h b/src/audio/heap.h index 0d4d3f6e..748e4471 100644 --- a/src/audio/heap.h +++ b/src/audio/heap.h @@ -142,7 +142,7 @@ void *get_bank_or_seq(struct SoundMultiPool *arg0, s32 arg1, s32 id); s32 audio_shut_down_and_reset_step(void); void audio_reset_session(void); #else -void audio_reset_session(struct AudioSessionSettings *preset, s32 presetId); +void audio_reset_session(s32 reverbPresetId); #endif void discard_bank(s32 bankId); diff --git a/src/audio/internal.h b/src/audio/internal.h index cb90045d..bae4ff6d 100644 --- a/src/audio/internal.h +++ b/src/audio/internal.h @@ -730,6 +730,22 @@ struct NoteSynthesisBuffers { #endif }; +#ifdef BETTER_REVERB +struct BetterReverbSettings { + s8 downsampleRate; + u8 isMono; + u8 filterCount; + s16 windowSize; + s16 gain; + u8 gainIndex; + u8 reverbIndex; + u32 *delaysL; + u32 *delaysR; + s32 *reverbMultsL; + s32 *reverbMultsR; +}; +#endif + #ifdef VERSION_EU struct ReverbSettingsEU { u8 downsampleRate; @@ -781,15 +797,12 @@ struct AudioSessionSettingsEU { struct AudioSessionSettings { /*0x00*/ u32 frequency; /*0x04*/ u8 maxSimultaneousNotes; - /*0x05*/ u8 reverbDownsampleRate; // always 1 - /*0x06*/ u16 reverbWindowSize; - /*0x08*/ u16 reverbGain; - /*0x0A*/ u16 volume; - /*0x0C*/ u32 persistentSeqMem; - /*0x10*/ u32 persistentBankMem; - /*0x14*/ u32 temporarySeqMem; - /*0x18*/ u32 temporaryBankMem; -}; // size = 0x1C + /*0x06*/ u16 volume; + /*0x08*/ u32 persistentSeqMem; + /*0x0C*/ u32 persistentBankMem; + /*0x10*/ u32 temporarySeqMem; + /*0x14*/ u32 temporaryBankMem; +}; // size = 0x18 struct AudioBufferParametersEU { /*0x00*/ s16 presetUnk4; // audio frames per vsync? diff --git a/src/audio/load.c b/src/audio/load.c index e4cb423d..deeaf33d 100644 --- a/src/audio/load.c +++ b/src/audio/load.c @@ -879,7 +879,7 @@ void audio_init() { gAudioResetStatus = 1; audio_shut_down_and_reset_step(); #else - audio_reset_session(&gAudioSessionPresets[0], 0); + audio_reset_session(0); #endif // Not sure about these prints diff --git a/src/audio/port_eu.c b/src/audio/port_eu.c index b49fa141..462d38a4 100644 --- a/src/audio/port_eu.c +++ b/src/audio/port_eu.c @@ -153,7 +153,7 @@ void eu_process_audio_cmd(struct EuAudioCmd *cmd) { break; case 0xf0: - gSoundMode = cmd->u2.as_s32; + // gSoundMode = cmd->u2.as_s32; // Commenting this out, as the way to reset this has been removed. break; case 0xf1: diff --git a/src/audio/port_sh.c b/src/audio/port_sh.c index 8b19c9d6..517a628a 100644 --- a/src/audio/port_sh.c +++ b/src/audio/port_sh.c @@ -176,7 +176,7 @@ void eu_process_audio_cmd(struct EuAudioCmd *cmd) { break; case 0xf0: - gSoundMode = cmd->u2.as_s32; + // gSoundMode = cmd->u2.as_s32; // Commenting this out, as the way to reset this has been removed. break; case 0xf1: diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index 3923a80b..5e4b8ad0 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -9,6 +9,7 @@ #include "internal.h" #include "external.h" #include "game/game_init.h" +#include "game/debug.h" #include "engine/math_util.h" @@ -40,122 +41,26 @@ #define AUDIO_ALIGN(val, amnt) (((val) + (1 << amnt) - 1) & ~((1 << amnt) - 1)) #ifdef BETTER_REVERB -/* ----------------------------------------------------------------------BEGIN REVERB PARAMETERS---------------------------------------------------------------------- */ - - -/** - * This reverb is a much more natural, ambient implementation over vanilla's, though at the cost of some memory and performance. - * These parameters are here to provide maximum control over the usage of the reverb effect, as well as with game performance. - * - * To take advantage of the reverb effect, you can change the echo parameters set in levels/level_defines.h to tailor the reverb to each specific level area. - * To adjust reverb presence with individual sound effects, apply the .set_reverb command within sound/sequences/00_sound_player.s (see examples of other sounds that use it). - * To use with M64 sequences, set the Effect parameter for each channel accordingly (CC 91 for MIDI files). - * - * Most parameter configuration is to be done here, though BETTER_REVERB_SIZE can be adjusted in audio/synthesis.h. - * - * If after changing the parameters, you hear increasing noise followed by a sudden disappearance of reverb and/or scratchy audio, this indicates an s16 overflow. - * If this happens, stop immediately and reduce the parameters at fault. This becomes a ticking time bomb, and may eventually result in very loud noise if it reaches the point of s32 overflow. - * Depending on the violating parameters chosen, you probably won't ever experience s32 overflow, but s16 overflow still isn't a pleasant experience. - * Checks to prevent this have not been implemented to maximize performance potential, so choose your parameters wisely. The current defaults are unlikely to have this problem. - * Generally speaking, a sound that doesn't seem to be fading at a natural rate is a parameter red flag (also known as feedback). - * - * This is also known to cause severe lag on emulators that have counter factor set to 2 or greater. - * If this is an issue, it is recommended you enable RCVI hack in the config files. Alternatively, you can reduce the reverb parameters here to compensate. - */ - - -// Larger values downsample the reverb exponentially by multiples of 2. A value of 1 doesn't downsample at all. This can substantially increase performance at the cost of resolution. -// Setting this to 4 corrupts the game, so set this value to -1 to use vanilla reverb if this is too slow, or if it just doesn't fit the desired aesthetic of a level. -// You can change this value before audio_reset_session gets called if different levels can tolerate the demand better than others or just have different reverb goals. -// A higher downsample value hits the game's frequency limit sooner, which can cause the reverb sometimes to be off pitch. This is a vanilla quirk (and also counter intuitive). -// Higher downsample values also result in slightly shorter reverb decay times, so keep this in mind if balancing reverb presence with emulator, or maybe consider adjusting REVERB_REV_INDEX with a console check. -s8 betterReverbDownsampleConsole = 2; - -// Larger values downsample the reverb exponentially by multiples of 2. A value of 1 doesn't downsample at all. This can substantially increase performance at the cost of resolution. -// Most emulators can handle a default value of 2, but 3 may be advisable as a failsafe against a counter factor of 2 (if RCVI hack is disabled). Setting this to -1 also uses vanilla reverb. -// Using a value of 1 is not recommended on emulator unless RCVI hack is enabled or other parameters are reduced to compensate. If you do decide to use 1 here, you must adjust BETTER_REVERB_SIZE appropriately. -// You can change this value before audio_reset_session gets called if different levels can tolerate the demand better than others or just have different reverb goals. -// A higher downsample value hits the game's frequency limit sooner, which can cause the reverb sometimes to be off pitch. This is a vanilla quirk (and also counter intuitive). -// Higher downsample values also result in slightly shorter reverb decay times, so keep this in mind if balancing reverb presence with console, or maybe consider adjusting REVERB_REV_INDEX with a console check. -s8 betterReverbDownsampleEmulator = 2; - -// This value represents the number of filters to use with the reverb. This can be decreased to improve performance, but at the cost of a lesser presence of reverb in the final audio. -// Filter count should always be a multiple of 3. Never ever set this value to be greater than NUM_ALLPASS. -// This value cannot be less than 3. Setting it to anything lower will act as if it was set to 3. -// This can be changed at any time, but is best set immediately before calling audio_reset_session. -s32 reverbFilterCountConsole = (NUM_ALLPASS - 9); - -// This value represents the number of filters to use with the reverb. This can be decreased to improve performance, but at the cost of a lesser presence of reverb in the final audio. -// Filter count should always be a multiple of 3. Never ever set this value to be greater than NUM_ALLPASS. -// This value cannot be less than 3. Setting it to anything lower will act as if it was set to 3. -// This can be changed at any time, but is best set immediately before calling audio_reset_session. -s32 reverbFilterCountEmulator = NUM_ALLPASS; - -// Set this to TRUE to use mono over stereo for reverb. This should increase performance, but at the cost of a less fulfilling reverb experience. -// If performance is desirable, it is recommended to change reverbFilterCountConsole or betterReverbDownsampleConsole first. -// This can be changed at any time, but is best set immediately before calling audio_reset_session. -u8 monoReverbConsole = FALSE; - -// Set this to TRUE to use mono over stereo for reverb. This should increase performance, but at the cost of a less fulfilling reverb experience. -// If performance is desirable, it is recommended to change reverbFilterCountEmulator or betterReverbDownsampleEmulator first. -// This can be changed at any time, but is best set immediately before calling audio_reset_session. -u8 monoReverbEmulator = FALSE; - -// This value controls the size of the reverb buffer. It affects the global reverb delay time. This variable is one of the easiest to control. -// Setting the value lower than the downsample buffer size will destroy the game audio. This is taken into account automatically, but also means the value set here isn't what always gets used. -// Similarly, this value maxes out at (REVERB_WINDOW_SIZE_MAX * 2^(downsample factor - 1)). -// If this value is changed, it will go into effect the next time audio_reset_session is called. -// Set to -1 to use a default preset instead. Higher values represent more audio delay (usually better for echoey spaces). -s32 betterReverbWindowsSize = -1; - -// These are set to defines rather than variables to increase performance. Change these to s32 if you want them to be configurable in-game. (Maybe extern them in synthesis.h) -// Setting these to values larger than 0xFF (255) or less than 0 may cause issues and is not recommended. -#define REVERB_REV_INDEX 0x60 // Affects decay time mostly (large values can cause terrible feedback!); can be messed with at any time -#define REVERB_GAIN_INDEX 0xA0 // Affects signal immediately retransmitted back into buffers (mid-high values yield the strongest effect); can be messed with at any time -#define REVERB_WET_SIGNAL 0xE0 // Amount of reverb specific output in final signal (also affects decay); can be messed with at any time, also very easy to control - -// #define REVERB_DRY_SIGNAL = 0x00; // Amount of original input in final signal (large values can cause terrible feedback!); declaration and uses commented out by default to improve compiler optimization - - -/* ---------------------------------------------------------------------ADVANCED REVERB PARAMETERS-------------------------------------------------------------------- */ - - -// These values affect filter delays. Bigger values will result in fatter echo (and more memory); must be cumulatively smaller than BETTER_REVERB_SIZE/2. -// If setting a reverb downsample value to 1, these must be cumulatively smaller than BETTER_REVERB_SIZE/4. -// None of the delay values should ever be smaller than 1; these are s32s purely to avoid typecasts. -// These values are applied any time audio_reset_session is called, and as such can be changed at any time without issues. -s32 delaysBaselineL[NUM_ALLPASS] = { - 1080, 1352, 1200, - 1200, 1232, 1432, - 1384, 1048, 1352, - 928, 1504, 1512 -}; -s32 delaysBaselineR[NUM_ALLPASS] = { - 1384, 1352, 1048, - 928, 1512, 1504, - 1080, 1200, 1352, - 1200, 1432, 1232 -}; - -// These values affect reverb decay depending on the filter index; can be messed with at any time, and will have effects updated in real time -s32 gReverbMultsL[NUM_ALLPASS / 3] = {0xD7, 0x6F, 0x36, 0x22}; -s32 gReverbMultsR[NUM_ALLPASS / 3] = {0xCF, 0x73, 0x38, 0x1F}; - - -/* -----------------------------------------------------------------------END REVERB PARAMETERS----------------------------------------------------------------------- */ - // Do not touch these values manually, unless you want potential for problems. -u8 toggleBetterReverb = TRUE; -static u8 monoReverb = FALSE; -static s32 reverbFilterCount = NUM_ALLPASS; +u8 gBetterReverbPreset = 0; +u8 toggleBetterReverb = FALSE; +u8 monoReverb; +s8 betterReverbDownsampleRate; +static u8 reverbMultsL[NUM_ALLPASS / 3] = {0}; +static u8 reverbMultsR[NUM_ALLPASS / 3] = {0}; static s32 allpassIdxL[NUM_ALLPASS] = {0}; static s32 allpassIdxR[NUM_ALLPASS] = {0}; static s32 delaysL[NUM_ALLPASS] = {0}; static s32 delaysR[NUM_ALLPASS] = {0}; -static u8 reverbMultsL[NUM_ALLPASS / 3] = {0}; -static u8 reverbMultsR[NUM_ALLPASS / 3] = {0}; static s32 **delayBufsL; static s32 **delayBufsR; +s32 reverbLastFilterIndex; +s32 reverbFilterCount; +s32 betterReverbWindowsSize; +s32 betterReverbRevIndex; // This one is okay to adjust whenever +s32 betterReverbGainIndex; // This one is okay to adjust whenever +s32 *gReverbMultsL; +s32 *gReverbMultsR; #endif @@ -203,10 +108,10 @@ static void reverb_samples(s16 *outSampleL, s16 *outSampleR, s32 inSampleL, s32 s32 k = 0; s32 outTmpL = 0; s32 outTmpR = 0; - s32 tmpCarryoverL = (((delayBufsL[reverbFilterCount][allpassIdxL[reverbFilterCount]] * REVERB_REV_INDEX) >> 8) + inSampleL); - s32 tmpCarryoverR = (((delayBufsR[reverbFilterCount][allpassIdxR[reverbFilterCount]] * REVERB_REV_INDEX) >> 8) + inSampleR); + s32 tmpCarryoverL = ((delayBufsL[reverbLastFilterIndex][allpassIdxL[reverbLastFilterIndex]] * betterReverbRevIndex) >> 8) + inSampleL; + s32 tmpCarryoverR = ((delayBufsR[reverbLastFilterIndex][allpassIdxR[reverbLastFilterIndex]] * betterReverbRevIndex) >> 8) + inSampleR; - for (; i <= reverbFilterCount; ++i, ++j) { + for (; i <= reverbLastFilterIndex; ++i, ++j) { curDelaySampleL = &delayBufsL[i][allpassIdxL[i]]; curDelaySampleR = &delayBufsR[i][allpassIdxR[i]]; historySampleL = *curDelaySampleL; @@ -214,29 +119,27 @@ static void reverb_samples(s16 *outSampleL, s16 *outSampleR, s32 inSampleL, s32 if (j == 2) { j = -1; - outTmpL += ((historySampleL * reverbMultsL[k ]) >> 8); - outTmpR += ((historySampleR * reverbMultsR[k++]) >> 8); + outTmpL += (historySampleL * reverbMultsL[k ]) >> 8; + outTmpR += (historySampleR * reverbMultsR[k++]) >> 8; *curDelaySampleL = tmpCarryoverL; *curDelaySampleR = tmpCarryoverR; - if (i != reverbFilterCount) { - tmpCarryoverL = ((historySampleL * REVERB_REV_INDEX) >> 8); - tmpCarryoverR = ((historySampleR * REVERB_REV_INDEX) >> 8); + if (i != reverbLastFilterIndex) { + tmpCarryoverL = ((historySampleL * betterReverbRevIndex) >> 8)/* + inSampleL*/; + tmpCarryoverR = ((historySampleR * betterReverbRevIndex) >> 8)/* + inSampleR*/; } } else { - *curDelaySampleL = (((historySampleL * (-REVERB_GAIN_INDEX)) >> 8) + tmpCarryoverL); - *curDelaySampleR = (((historySampleR * (-REVERB_GAIN_INDEX)) >> 8) + tmpCarryoverR); - tmpCarryoverL = (((*curDelaySampleL * REVERB_GAIN_INDEX) >> 8) + historySampleL); - tmpCarryoverR = (((*curDelaySampleR * REVERB_GAIN_INDEX) >> 8) + historySampleR); + *curDelaySampleL = ((historySampleL * (-betterReverbGainIndex)) >> 8) + tmpCarryoverL; + *curDelaySampleR = ((historySampleR * (-betterReverbGainIndex)) >> 8) + tmpCarryoverR; + tmpCarryoverL = ((*curDelaySampleL * betterReverbGainIndex) >> 8) + historySampleL; + tmpCarryoverR = ((*curDelaySampleR * betterReverbGainIndex) >> 8) + historySampleR; } if (++allpassIdxL[i] == delaysL[i]) allpassIdxL[i] = 0; if (++allpassIdxR[i] == delaysR[i]) allpassIdxR[i] = 0; } - s32 outUnclamped = ((outTmpL * REVERB_WET_SIGNAL/* + inSampleL * REVERB_DRY_SIGNAL*/) >> 8); - *outSampleL = CLAMP_S16(outUnclamped); - outUnclamped = ((outTmpR * REVERB_WET_SIGNAL/* + inSampleL * REVERB_DRY_SIGNAL*/) >> 8); - *outSampleR = CLAMP_S16(outUnclamped); + *outSampleL = CLAMP_S16(outTmpL); + *outSampleR = CLAMP_S16(outTmpR); } static void reverb_mono_sample(s16 *outSample, s32 inSample) { @@ -246,9 +149,9 @@ static void reverb_mono_sample(s16 *outSample, s32 inSample) { s32 j = 0; s32 k = 0; s32 outTmp = 0; - s32 tmpCarryover = (((delayBufsL[reverbFilterCount][allpassIdxL[reverbFilterCount]] * REVERB_REV_INDEX) >> 8) + inSample); + s32 tmpCarryover = ((delayBufsL[reverbLastFilterIndex][allpassIdxL[reverbLastFilterIndex]] * betterReverbRevIndex) >> 8) + inSample; - for (; i <= reverbFilterCount; ++i, ++j) { + for (; i <= reverbLastFilterIndex; ++i, ++j) { curDelaySample = &delayBufsL[i][allpassIdxL[i]]; historySample = *curDelaySample; @@ -256,45 +159,49 @@ static void reverb_mono_sample(s16 *outSample, s32 inSample) { j = -1; outTmp += ((historySample * reverbMultsL[k++]) >> 8); *curDelaySample = tmpCarryover; - if (i != reverbFilterCount) - tmpCarryover = ((historySample * REVERB_REV_INDEX) >> 8); + if (i != reverbLastFilterIndex) + tmpCarryover = ((historySample * betterReverbRevIndex) >> 8)/* + inSample*/; } else { - *curDelaySample = (((historySample * (-REVERB_GAIN_INDEX)) >> 8) + tmpCarryover); - tmpCarryover = (((*curDelaySample * REVERB_GAIN_INDEX) >> 8) + historySample); + *curDelaySample = ((historySample * (-betterReverbGainIndex)) >> 8) + tmpCarryover; + tmpCarryover = ((*curDelaySample * betterReverbGainIndex) >> 8) + historySample; } if (++allpassIdxL[i] == delaysL[i]) allpassIdxL[i] = 0; } - s32 outUnclamped = ((outTmp * REVERB_WET_SIGNAL/* + inSample * REVERB_DRY_SIGNAL*/) >> 8); - *outSample = CLAMP_S16(outUnclamped); + *outSample = CLAMP_S16(outTmp); } void initialize_better_reverb_buffers(void) { delayBufsL = (s32**) soundAlloc(&gBetterReverbPool, BETTER_REVERB_PTR_SIZE); delayBufsR = &delayBufsL[NUM_ALLPASS]; - delayBufsL[0] = (s32*) soundAlloc(&gBetterReverbPool, BETTER_REVERB_SIZE - BETTER_REVERB_PTR_SIZE); } -void clear_better_reverb_buffers(void) { - bzero(delayBufsL[0], (BETTER_REVERB_SIZE - BETTER_REVERB_PTR_SIZE)); - - bzero(allpassIdxL, sizeof(allpassIdxL)); - bzero(allpassIdxR, sizeof(allpassIdxR)); -} - -void set_better_reverb_buffers(void) { +void set_better_reverb_buffers(u32 *inputDelaysL, u32 *inputDelaysR) { s32 bufOffset = 0; s32 i; - for (i = 0; i < NUM_ALLPASS; ++i) { - delaysL[i] = (delaysBaselineL[i] / gReverbDownsampleRate); - delaysR[i] = (delaysBaselineR[i] / gReverbDownsampleRate); - delayBufsL[i] = (s32*) &delayBufsL[0][bufOffset]; + gBetterReverbPool.cur = gBetterReverbPool.start + ALIGN16(BETTER_REVERB_PTR_SIZE); // Reset reverb data pool + + // Don't bother setting any buffers if BETTER_REVERB is disabled + if (!toggleBetterReverb) + return; + + // NOTE: Using reverbFilterCount over NUM_ALLPASS will report less memory usage with fewer filters, but poses an additional + // risk to anybody testing on console with performance compromises, as emulator can be easily overlooked. + for (i = 0; i < reverbFilterCount; ++i) { + delaysL[i] = (s32) (inputDelaysL[i] / gReverbDownsampleRate); + delaysR[i] = (s32) (inputDelaysR[i] / gReverbDownsampleRate); + delayBufsL[i] = soundAlloc(&gBetterReverbPool, delaysL[i] * sizeof(s32)); bufOffset += delaysL[i]; - delayBufsR[i] = (s32*) &delayBufsL[0][bufOffset]; // L and R buffers are interleaved adjacently in memory; not a bug + delayBufsR[i] = soundAlloc(&gBetterReverbPool, delaysR[i] * sizeof(s32)); bufOffset += delaysR[i]; } + + aggress(bufOffset * sizeof(s32) <= BETTER_REVERB_SIZE - ALIGN16(BETTER_REVERB_PTR_SIZE), "BETTER_REVERB_SIZE is too small for this preset!"); + + bzero(allpassIdxL, sizeof(allpassIdxL)); + bzero(allpassIdxR, sizeof(allpassIdxR)); } #endif @@ -372,12 +279,12 @@ void prepare_reverb_ring_buffer(s32 chunkLen, u32 updateIndex) { s32 nSamples; s32 excessiveSamples; + if (gSynthesisReverb.framesLeftToIgnore == 0) { #ifdef BETTER_REVERB - if (!toggleBetterReverb && gReverbDownsampleRate != 1) { + if (!toggleBetterReverb && gReverbDownsampleRate != 1) { #else - if (gReverbDownsampleRate != 1) { + if (gReverbDownsampleRate != 1) { #endif - if (gSynthesisReverb.framesLeftToIgnore == 0) { // Now that the RSP has finished, downsample the samples produced two frames ago by skipping // samples. item = &gSynthesisReverb.items[gSynthesisReverb.curFrame][updateIndex]; @@ -395,49 +302,47 @@ void prepare_reverb_ring_buffer(s32 chunkLen, u32 updateIndex) { gSynthesisReverb.ringBuffer.right[dstPos] = item->toDownsampleRight[srcPos]; } } - } #ifdef BETTER_REVERB - else if (toggleBetterReverb) { - reverbFilterCount--; // Temporarily lower filter count for optimized bulk processing - item = &gSynthesisReverb.items[gSynthesisReverb.curFrame][updateIndex]; - if (gSoundMode == SOUND_MODE_MONO || monoReverb) { - if (gReverbDownsampleRate != 1) { - osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH); - for (srcPos = 0, dstPos = item->startPos; dstPos < ((item->lengthA / 2) + item->startPos); srcPos += gReverbDownsampleRate, dstPos++) { - reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) item->toDownsampleLeft[srcPos] + (s32) item->toDownsampleRight[srcPos]) / 2); - gSynthesisReverb.ringBuffer.right[dstPos] = gSynthesisReverb.ringBuffer.left[dstPos]; - } - for (dstPos = 0; dstPos < (item->lengthB / 2); srcPos += gReverbDownsampleRate, dstPos++) { - reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) item->toDownsampleLeft[srcPos] + (s32) item->toDownsampleRight[srcPos]) / 2); - gSynthesisReverb.ringBuffer.right[dstPos] = gSynthesisReverb.ringBuffer.left[dstPos]; + else if (toggleBetterReverb) { + item = &gSynthesisReverb.items[gSynthesisReverb.curFrame][updateIndex]; + if (gSoundMode == SOUND_MODE_MONO || monoReverb) { + if (gReverbDownsampleRate != 1) { + osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH); + for (srcPos = 0, dstPos = item->startPos; dstPos < ((item->lengthA / 2) + item->startPos); srcPos += gReverbDownsampleRate, dstPos++) { + reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) item->toDownsampleLeft[srcPos] + (s32) item->toDownsampleRight[srcPos]) / 2); + gSynthesisReverb.ringBuffer.right[dstPos] = gSynthesisReverb.ringBuffer.left[dstPos]; + } + for (dstPos = 0; dstPos < (item->lengthB / 2); srcPos += gReverbDownsampleRate, dstPos++) { + reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) item->toDownsampleLeft[srcPos] + (s32) item->toDownsampleRight[srcPos]) / 2); + gSynthesisReverb.ringBuffer.right[dstPos] = gSynthesisReverb.ringBuffer.left[dstPos]; + } + } else { + for (dstPos = item->startPos; dstPos < ((item->lengthA / 2) + item->startPos); dstPos++) { + reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) gSynthesisReverb.ringBuffer.left[dstPos] + (s32) gSynthesisReverb.ringBuffer.right[dstPos]) / 2); + gSynthesisReverb.ringBuffer.right[dstPos] = gSynthesisReverb.ringBuffer.left[dstPos]; + } + for (dstPos = 0; dstPos < (item->lengthB / 2); dstPos++) { + reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) gSynthesisReverb.ringBuffer.left[dstPos] + (s32) gSynthesisReverb.ringBuffer.right[dstPos]) / 2); + gSynthesisReverb.ringBuffer.right[dstPos] = gSynthesisReverb.ringBuffer.left[dstPos]; + } } } else { - for (dstPos = item->startPos; dstPos < ((item->lengthA / 2) + item->startPos); dstPos++) { - reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) gSynthesisReverb.ringBuffer.left[dstPos] + (s32) gSynthesisReverb.ringBuffer.right[dstPos]) / 2); - gSynthesisReverb.ringBuffer.right[dstPos] = gSynthesisReverb.ringBuffer.left[dstPos]; + if (gReverbDownsampleRate != 1) { + osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH); + for (srcPos = 0, dstPos = item->startPos; dstPos < ((item->lengthA / 2) + item->startPos); srcPos += gReverbDownsampleRate, dstPos++) + reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], item->toDownsampleLeft[srcPos], item->toDownsampleRight[srcPos]); + for (dstPos = 0; dstPos < (item->lengthB / 2); srcPos += gReverbDownsampleRate, dstPos++) + reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], item->toDownsampleLeft[srcPos], item->toDownsampleRight[srcPos]); + } else { + for (dstPos = item->startPos; dstPos < ((item->lengthA / 2) + item->startPos); dstPos++) + reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], gSynthesisReverb.ringBuffer.left[dstPos], gSynthesisReverb.ringBuffer.right[dstPos]); + for (dstPos = 0; dstPos < (item->lengthB / 2); dstPos++) + reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], gSynthesisReverb.ringBuffer.left[dstPos], gSynthesisReverb.ringBuffer.right[dstPos]); } - for (dstPos = 0; dstPos < (item->lengthB / 2); dstPos++) { - reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) gSynthesisReverb.ringBuffer.left[dstPos] + (s32) gSynthesisReverb.ringBuffer.right[dstPos]) / 2); - gSynthesisReverb.ringBuffer.right[dstPos] = gSynthesisReverb.ringBuffer.left[dstPos]; - } - } - } else { - if (gReverbDownsampleRate != 1) { - osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH); - for (srcPos = 0, dstPos = item->startPos; dstPos < ((item->lengthA / 2) + item->startPos); srcPos += gReverbDownsampleRate, dstPos++) - reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], item->toDownsampleLeft[srcPos], item->toDownsampleRight[srcPos]); - for (dstPos = 0; dstPos < (item->lengthB / 2); srcPos += gReverbDownsampleRate, dstPos++) - reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], item->toDownsampleLeft[srcPos], item->toDownsampleRight[srcPos]); - } else { - for (dstPos = item->startPos; dstPos < ((item->lengthA / 2) + item->startPos); dstPos++) - reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], gSynthesisReverb.ringBuffer.left[dstPos], gSynthesisReverb.ringBuffer.right[dstPos]); - for (dstPos = 0; dstPos < (item->lengthB / 2); dstPos++) - reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], gSynthesisReverb.ringBuffer.left[dstPos], gSynthesisReverb.ringBuffer.right[dstPos]); } } - reverbFilterCount++; // Reset filter count to accurate numbers - } #endif + } item = &gSynthesisReverb.items[gSynthesisReverb.curFrame][updateIndex]; s32 numSamplesAfterDownsampling = chunkLen / gReverbDownsampleRate; @@ -564,7 +469,7 @@ u64 *synthesis_execute(u64 *cmdBuf, s32 *writtenCmds, s16 *aiBuf, s32 bufLen) { gCurrentLeftVolRamping = leftVolRamp; gCurrentRightVolRamping = rightVolRamp; for (j = 0; j < gNumSynthesisReverbs; j++) { - if (gSynthesisReverbs[j].useReverb != 0) { + if (gSynthesisReverbs[j].useReverb) { prepare_reverb_ring_buffer(chunkLen, gAudioBufferParameters.updatesPerFrame - i, j); } } @@ -594,26 +499,23 @@ u64 *synthesis_execute(u64 *cmdBuf, s32 *writtenCmds, s16 *aiBuf, s32 bufLen) { aSegment(cmdBuf, 0, 0); #ifdef BETTER_REVERB - if (gIsConsole) { - reverbFilterCount = reverbFilterCountConsole; - monoReverb = monoReverbConsole; - } else { - reverbFilterCount = reverbFilterCountEmulator; - monoReverb = monoReverbEmulator; - } + s32 filterCountDiv3 = reverbFilterCount / 3; + reverbFilterCount = filterCountDiv3 * 3; // reverbFilterCount should always be a multiple of 3. + if (reverbFilterCount > NUM_ALLPASS) { reverbFilterCount = NUM_ALLPASS; } else if (reverbFilterCount < 3) { reverbFilterCount = 3; } - s32 filterCountDiv3 = reverbFilterCount / 3; - reverbFilterCount = filterCountDiv3 * 3; // reverbFilterCount should always be a multiple of 3. + reverbLastFilterIndex = reverbFilterCount - 1; // Update reverbMultsL every audio frame just in case gReverbMults is ever to change. - for (i = 0; i < filterCountDiv3; ++i) { - reverbMultsL[i] = gReverbMultsL[i]; - reverbMultsR[i] = gReverbMultsR[i]; + if (gReverbMultsL != NULL && gReverbMultsR != NULL) { + for (i = 0; i < filterCountDiv3; ++i) { + reverbMultsL[i] = gReverbMultsL[i]; + reverbMultsR[i] = gReverbMultsR[i]; + } } // If there's only one reverb multiplier set, adjust these to match so one channel doesn't end up potentially overpowering the other. @@ -637,7 +539,7 @@ u64 *synthesis_execute(u64 *cmdBuf, s32 *writtenCmds, s16 *aiBuf, s32 bufLen) { } } process_sequences(i - 1); - if (gSynthesisReverb.useReverb != 0) { + if (gSynthesisReverb.useReverb) { prepare_reverb_ring_buffer(chunkLen, gAudioUpdatesPerFrame - i); } cmd = synthesis_do_one_audio_update((s16 *) aiBufPtr, chunkLen, cmd, gAudioUpdatesPerFrame - i); @@ -697,7 +599,7 @@ u64 *synthesis_save_reverb_samples(u64 *cmd, s16 reverbIndex, s16 updateIndex) { struct ReverbRingBufferItem *item; item = &gSynthesisReverbs[reverbIndex].items[gSynthesisReverbs[reverbIndex].curFrame][updateIndex]; - if (gSynthesisReverbs[reverbIndex].useReverb != 0) { + if (gSynthesisReverbs[reverbIndex].useReverb) { switch (gSynthesisReverbs[reverbIndex].downsampleRate) { case 1: // Put the oldest samples in the ring buffer into the wet channels @@ -757,7 +659,7 @@ u64 *synthesis_do_one_audio_update(s16 *aiBuf, s32 bufLen, u64 *cmd, s32 updateI i = 0; for (j = 0; j < gNumSynthesisReverbs; j++) { gUseReverb = gSynthesisReverbs[j].useReverb; - if (gUseReverb != 0) { + if (gUseReverb) { cmd = synthesis_resample_and_mix_reverb(cmd, bufLen, j, updateIndex); } for (; i < notePos; i++) { @@ -772,7 +674,7 @@ u64 *synthesis_do_one_audio_update(s16 *aiBuf, s32 bufLen, u64 *cmd, s32 updateI break; } } - if (gSynthesisReverbs[j].useReverb != 0) { + if (gSynthesisReverbs[j].useReverb) { cmd = synthesis_save_reverb_samples(cmd, j, updateIndex); } } @@ -803,7 +705,7 @@ u64 *synthesis_do_one_audio_update(s16 *aiBuf, s32 bufLen, u64 *cmd, s32 updateI v1 = &gSynthesisReverb.items[gSynthesisReverb.curFrame][updateIndex]; - if (gSynthesisReverb.useReverb == 0) { + if (!gSynthesisReverb.useReverb) { aClearBuffer(cmd++, DMEM_ADDR_LEFT_CH, DEFAULT_LEN_2CH); cmd = synthesis_process_notes(aiBuf, bufLen, cmd); } else { diff --git a/src/audio/synthesis.h b/src/audio/synthesis.h index 95236321..41326309 100644 --- a/src/audio/synthesis.h +++ b/src/audio/synthesis.h @@ -21,28 +21,22 @@ #ifdef BETTER_REVERB -#define NUM_ALLPASS 12 // Number of delay filters to use with better reverb; do not change this value if you don't know what you're doing. +#define NUM_ALLPASS 12 // Maximum number of delay filters to use with better reverb; do not change this value if you don't know what you're doing. #define BETTER_REVERB_PTR_SIZE ALIGN16(NUM_ALLPASS * sizeof(s32*) * 2) // Allocation space consumed by dynamically allocated pointers - // Size determined by (all delaysBaselineL/R values * 8) / (2 ^ Minimum Downsample Factor). - // The default value can be increased or decreased in conjunction with the values in delaysBaselineL/R -#define BETTER_REVERB_SIZE ALIGN16(0xF200 + BETTER_REVERB_PTR_SIZE) +// Size determined by (all delaysL/R values * 8) / (2 ^ Minimum Downsample Factor). +// The default value can be increased or decreased in conjunction with the values in delaysL/R +#define BETTER_REVERB_SIZE ALIGN16(0x1E000 + BETTER_REVERB_PTR_SIZE) // This can be significantly decreased if a downsample rate of 1 is not being used. -// #define BETTER_REVERB_SIZE (0x7A00 + BETTER_REVERB_PTR_SIZE) // Default for use only with a downsampling value of 3 (i.e. double the emulator default) -// #define BETTER_REVERB_SIZE (0x1E200 + BETTER_REVERB_PTR_SIZE) // Default for use with a downsampling value of 1 (i.e. no downsampling at all) - -extern s8 betterReverbDownsampleConsole; -extern s8 betterReverbDownsampleEmulator; -extern u8 monoReverbConsole; -extern u8 monoReverbEmulator; -extern s32 reverbFilterCountConsole; -extern s32 reverbFilterCountEmulator; +extern u8 gBetterReverbPreset; +extern s8 betterReverbDownsampleRate; +extern u8 monoReverb; +extern s32 reverbFilterCount; extern s32 betterReverbWindowsSize; - -extern s32 delaysBaselineL[NUM_ALLPASS]; -extern s32 delaysBaselineR[NUM_ALLPASS]; -extern s32 gReverbMultsL[NUM_ALLPASS / 3]; -extern s32 gReverbMultsR[NUM_ALLPASS / 3]; +extern s32 betterReverbRevIndex; +extern s32 betterReverbGainIndex; +extern s32 *gReverbMultsL; +extern s32 *gReverbMultsR; extern u8 toggleBetterReverb; #define REVERB_WINDOW_SIZE_MAX 0x2000 @@ -147,8 +141,7 @@ extern s16 D_SH_803479B4; #ifdef BETTER_REVERB void initialize_better_reverb_buffers(void); -void clear_better_reverb_buffers(void); -void set_better_reverb_buffers(void); +void set_better_reverb_buffers(u32 *inputDelaysL, u32 *inputDelaysR); #endif u64 *synthesis_execute(u64 *cmdBuf, s32 *writtenCmds, s16 *aiBuf, s32 bufLen); diff --git a/src/audio/synthesis_sh.c b/src/audio/synthesis_sh.c index a95d1d19..b1d9a3cf 100644 --- a/src/audio/synthesis_sh.c +++ b/src/audio/synthesis_sh.c @@ -177,7 +177,7 @@ u64 *synthesis_execute(u64 *cmdBuf, s32 *writtenCmds, s16 *aiBuf, s32 bufLen) { } } for (j = 0; j < gNumSynthesisReverbs; j++) { - if (gSynthesisReverbs[j].useReverb != 0) { + if (gSynthesisReverbs[j].useReverb) { prepare_reverb_ring_buffer(chunkLen, gAudioBufferParameters.updatesPerFrame - i, j); } } @@ -319,7 +319,7 @@ u64 *synthesis_do_one_audio_update(s16 *aiBuf, s32 bufLen, u64 *cmd, s32 updateI i = 0; for (j = 0; j < gNumSynthesisReverbs; j++) { gUseReverb = gSynthesisReverbs[j].useReverb; - if (gUseReverb != 0) { + if (gUseReverb) { cmd = synthesis_resample_and_mix_reverb(cmd, bufLen, j, updateIndex); } for (; i < notePos; i++) { @@ -334,7 +334,7 @@ u64 *synthesis_do_one_audio_update(s16 *aiBuf, s32 bufLen, u64 *cmd, s32 updateI break; } } - if (gSynthesisReverbs[j].useReverb != 0) { + if (gSynthesisReverbs[j].useReverb) { if (gSynthesisReverbs[j].unk100 != NULL) { aFilter(cmd++, 0x02, bufLen * 2, gSynthesisReverbs[j].unk100); aFilter(cmd++, gSynthesisReverbs[j].resampleFlags, DMEM_ADDR_WET_LEFT_CH, gSynthesisReverbs[j].unk108); diff --git a/src/engine/level_script.c b/src/engine/level_script.c index 9af7c9bb..1de2a462 100644 --- a/src/engine/level_script.c +++ b/src/engine/level_script.c @@ -5,6 +5,7 @@ #include "sm64.h" #include "audio/external.h" +#include "audio/synthesis.h" #include "buffers/framebuffers.h" #include "buffers/zbuffer.h" #include "game/area.h" @@ -735,18 +736,43 @@ static void level_cmd_show_dialog(void) { static void level_cmd_set_music(void) { if (sCurrAreaIndex != -1) { gAreas[sCurrAreaIndex].musicParam = CMD_GET(s16, 2); - gAreas[sCurrAreaIndex].musicParam2 = CMD_GET(s16, 4); +#ifdef BETTER_REVERB + if (gIsConsole) + gAreas[sCurrAreaIndex].betterReverbPreset = CMD_GET(u8, 4); + else + gAreas[sCurrAreaIndex].betterReverbPreset = CMD_GET(u8, 5); +#endif + gAreas[sCurrAreaIndex].musicParam2 = CMD_GET(s16, 6); } sCurrentCmd = CMD_NEXT; } static void level_cmd_set_menu_music(void) { +#ifdef BETTER_REVERB + // Must come before set_background_music() + if (gIsConsole) + gBetterReverbPreset = CMD_GET(u8, 4); + else + gBetterReverbPreset = CMD_GET(u8, 5); +#endif set_background_music(0, CMD_GET(s16, 2), 0); sCurrentCmd = CMD_NEXT; } static void level_cmd_fadeout_music(void) { - fadeout_music(CMD_GET(s16, 2)); + s16 dur = CMD_GET(s16, 2); + if (sCurrAreaIndex != -1 && dur == 0) { + // Allow persistent block overrides for SET_BACKGROUND_MUSIC_WITH_REVERB + gAreas[sCurrAreaIndex].musicParam = 0x00; + gAreas[sCurrAreaIndex].musicParam2 = 0x00; +#ifdef BETTER_REVERB + gAreas[sCurrAreaIndex].betterReverbPreset = 0x00; +#endif + } else { + if (dur < 0) + dur = 0; + fadeout_music(dur); + } sCurrentCmd = CMD_NEXT; } diff --git a/src/game/area.c b/src/game/area.c index d573cb13..3b5b851f 100644 --- a/src/game/area.c +++ b/src/game/area.c @@ -205,6 +205,9 @@ void clear_areas(void) { gAreaData[i].dialog[1] = DIALOG_NONE; gAreaData[i].musicParam = 0; gAreaData[i].musicParam2 = 0; +#ifdef BETTER_REVERB + gAreaData[i].betterReverbPreset = 0; +#endif } } diff --git a/src/game/area.h b/src/game/area.h index c9d09e72..ff544e9b 100644 --- a/src/game/area.h +++ b/src/game/area.h @@ -73,6 +73,9 @@ struct Area { /*0x34*/ u8 dialog[2]; // Level start dialog number (set by level script cmd 0x30) /*0x36*/ u16 musicParam; /*0x38*/ u16 musicParam2; +#ifdef BETTER_REVERB + /*0x3A*/ u8 betterReverbPreset; +#endif }; // All the transition data to be used in screen_transition.c diff --git a/src/game/level_update.c b/src/game/level_update.c index e80e62ff..b22407d6 100644 --- a/src/game/level_update.c +++ b/src/game/level_update.c @@ -4,6 +4,7 @@ #include "seq_ids.h" #include "dialog_ids.h" #include "audio/external.h" +#include "audio/synthesis.h" #include "level_update.h" #include "game_init.h" #include "level_update.h" @@ -395,6 +396,9 @@ void init_mario_after_warp(void) { } if (gCurrDemoInput == NULL) { +#ifdef BETTER_REVERB + gBetterReverbPreset = gCurrentArea->betterReverbPreset; +#endif set_background_music(gCurrentArea->musicParam, gCurrentArea->musicParam2, 0); if (gMarioState->flags & MARIO_METAL_CAP) { @@ -501,6 +505,9 @@ void warp_credits(void) { play_transition(WARP_TRANSITION_FADE_FROM_COLOR, 0x14, 0x00, 0x00, 0x00); if (gCurrCreditsEntry == NULL || gCurrCreditsEntry == sCreditsSequence) { +#ifdef BETTER_REVERB + gBetterReverbPreset = gCurrentArea->betterReverbPreset; +#endif set_background_music(gCurrentArea->musicParam, gCurrentArea->musicParam2, 0); } } @@ -572,9 +579,14 @@ s16 music_unchanged_through_warp(s16 arg) { #endif u16 destParam1 = gAreas[destArea].musicParam; u16 destParam2 = gAreas[destArea].musicParam2; - +#ifdef BETTER_REVERB + u16 destParam3 = gAreas[destArea].betterReverbPreset; + unchanged = levelNum == gCurrLevelNum && destParam1 == gCurrentArea->musicParam + && destParam2 == gCurrentArea->musicParam2 && destParam3 == gCurrentArea->betterReverbPreset; +#else unchanged = levelNum == gCurrLevelNum && destParam1 == gCurrentArea->musicParam && destParam2 == gCurrentArea->musicParam2; +#endif if (get_current_background_music() != destParam2) { unchanged = FALSE; @@ -1231,6 +1243,9 @@ s32 init_level(void) { } if (gCurrDemoInput == NULL) { +#ifdef BETTER_REVERB + gBetterReverbPreset = gCurrentArea->betterReverbPreset; +#endif set_background_music(gCurrentArea->musicParam, gCurrentArea->musicParam2, 0); } } diff --git a/src/game/sound_init.c b/src/game/sound_init.c index ea124bdc..3e5c41eb 100644 --- a/src/game/sound_init.c +++ b/src/game/sound_init.c @@ -2,6 +2,7 @@ #include "area.h" #include "audio/external.h" +#include "audio/load.h" #include "engine/graph_node.h" #include "engine/math_util.h" #include "level_table.h" @@ -35,7 +36,7 @@ static u16 sCurrentCapMusic = MUSIC_NONE; #ifdef ENABLE_VANILLA_LEVEL_SPECIFIC_CHECKS static u8 sPlayingInfiniteStairs = FALSE; #endif -static s16 sSoundMenuModeToSoundMode[] = { SOUND_MODE_STEREO, SOUND_MODE_MONO, SOUND_MODE_HEADSET }; +static s16 sSoundMenuModeToSoundMode[3] = { SOUND_MODE_STEREO, SOUND_MODE_MONO, SOUND_MODE_HEADSET }; // Only the 20th array element is used. static u32 sMenuSoundsExtra[] = { SOUND_MOVING_TERRAIN_SLIDE + (0 << 16), @@ -142,8 +143,8 @@ void enable_background_sound(void) { * Called from threads: thread5_game_loop */ void set_sound_mode(u16 soundMode) { - if (soundMode < 3) { - audio_set_sound_mode(sSoundMenuModeToSoundMode[soundMode]); + if (soundMode < ARRAY_COUNT(sSoundMenuModeToSoundMode)) { + gSoundMode = sSoundMenuModeToSoundMode[soundMode]; } } diff --git a/src/hvqm/hvqm.c b/src/hvqm/hvqm.c index 4ad2c17c..3719606b 100644 --- a/src/hvqm/hvqm.c +++ b/src/hvqm/hvqm.c @@ -223,7 +223,7 @@ void hvqm_main_proc() { --video_remain; //if (1) { - //osAiSetFrequency(gAudioSessionPresets[0].frequency); + //osAiSetFrequency(gAudioSessionPresets.frequency); //osSendMesg(&gDmaMesgQueue, 0, OS_MESG_BLOCK); //osDestroyThread(&hvqmMesgQ); //} diff --git a/src/menu/title_screen.c b/src/menu/title_screen.c index dee7d009..67628845 100644 --- a/src/menu/title_screen.c +++ b/src/menu/title_screen.c @@ -246,7 +246,6 @@ s32 intro_game_over(void) { * Plays the casual "It's a me mario" when the game stars. */ s32 intro_play_its_a_me_mario(void) { - set_background_music(0, SEQ_SOUND_PLAYER, 0); play_sound(SOUND_MENU_COIN_ITS_A_ME_MARIO, gGlobalSoundSource); return LEVEL_NONE + 1; }