From b37fa6a05bcaa967f6e8587ec0429a3d5d6cb545 Mon Sep 17 00:00:00 2001 From: z64a Date: Mon, 9 Jun 2025 19:26:39 -0400 Subject: [PATCH] bgm update units --- include/common_structs.h | 2 +- include/script_api/common.h | 6 ++ src/audio.h | 45 +++++++++++--- src/audio/bgm_player.c | 62 +++++++++++-------- src/audio/core/engine.c | 4 +- src/audio/private.h | 2 +- src/world/area_mac/mac_00/music_mix.c | 4 +- src/world/area_mac/mac_01/music_mix.c | 6 +- src/world/area_mac/mac_02/music_mix.c | 4 +- src/world/area_mac/mac_04/music_mix.c | 2 +- .../atomic/MonitorMusicProximityTrigger.inc.c | 2 +- 11 files changed, 90 insertions(+), 49 deletions(-) diff --git a/include/common_structs.h b/include/common_structs.h index 5adea3739e..9fab4c96d4 100644 --- a/include/common_structs.h +++ b/include/common_structs.h @@ -648,7 +648,7 @@ typedef struct MusicProximityTrigger { /* 0x00 */ VecXZf pos; /* 0x08 */ f32 innerDist; /* 0x0C */ f32 outerDist; - /* 0x10 */ s32 unk; + /* 0x10 */ s32 mix; /// which branch value to switch to /* 0x14 */ s32 manualActivationFlag; } MusicProximityTrigger; // size = 0x18 diff --git a/include/script_api/common.h b/include/script_api/common.h index e321cbb408..7e150633c9 100644 --- a/include/script_api/common.h +++ b/include/script_api/common.h @@ -1028,6 +1028,12 @@ API_CALLABLE(RegisterMusicEvents); API_CALLABLE(FadeOutMusic); /// @evtapi +/// Start playing a song. +/// +/// @param playerID MusicPlayer to use, should almost always be 0 (MUSIC_PLAYER_MAIN). +/// @param song Song from SongIDs. +/// @param variation +/// @param volumeLevel Perceptual loudness from VOL_LEVEL_MUTE to VOL_LEVEL_FULL. API_CALLABLE(SetMusic); /// @evtapi diff --git a/src/audio.h b/src/audio.h index 6bb3f947b4..f119506edc 100644 --- a/src/audio.h +++ b/src/audio.h @@ -66,13 +66,35 @@ typedef u8* WaveData; #define AU_PAN_MID 64 #define AU_PAN_MAX 127 -// only valid for vol != 0 +// converts an 8-bit volume to a 16-bit volume. only valid for vol != 0. #define AU_VOL_8_TO_16(vol) (((vol) << 8) | 0xFF) -// only valid for vol != 0 +// converts an 8-bit volume to a 32-bit volume. only valid for vol != 0. #define AU_VOL_8_TO_32(vol) (((vol) << 0x18) | 0xFFFFFF) -#define BGM_SAMPLE_RATE 156250 -#define BGM_DEFAULT_TEMPO 15600 +#define BGM_DEFAULT_TICKS_PER_BEAT 48 + +// fixed scale for update counter to avoid precision loss of fractional frames; 1 unit = 0.001 audio frame +// (ie, prevent truncation when updating tick every 1.5 frames) +#define BGM_UPDATE_SCALE (1000) +// 1 unit = 0.01 BPM +#define BGM_TEMPO_SCALE (100) + +// number of audio frames per minute x BGM_UPDATE_SCALE --> units = milli-frames per minute +#define BGM_MFRAMES_PER_MINUTE (BGM_UPDATE_SCALE * (60 * HARDWARE_OUTPUT_RATE) / AUDIO_SAMPLES) + +// default BPM for BGMPlayer +#define BGM_DEFAULT_BPM 156 +// default masterTempo for BGMPlayer, derived from BGM_DEFAULT_BPM +#define BGM_DEFAULT_TEMPO (BGM_DEFAULT_BPM * BGM_TEMPO_SCALE) + +// just over 100x the default tempo, seemingly chosen for to make each tick = 256 audio samples +// solves (AUDIO_SAMPLES * BGM_MFRAMES_PER_MINUTE) / (BGM_DEFAULT_TICKS_PER_BEAT * x) = 256 +// --> (184 * 10434782) / (48 * x) = 256 +// --> x = (184 * 10434782) / (48 * 256) +#define BGM_DEFAULT_UPDATE_STEP 156250 + +// converts a 'tempo' value from BGM command to units suitable for the player update counter +#define BGM_TEMPO_TO_UPDATE_UNITS(tempo) (BGM_UPDATE_SCALE * (u32)(tempo) / BGM_TEMPO_SCALE) #define SND_MIN_DURATION 250 #define SND_MAX_DURATION 10000 @@ -135,6 +157,11 @@ typedef enum AuEffectType { AU_FX_OTHER_BIGROOM = 10, } AuEffectType; +typedef enum MusicPlayer { + MUSIC_PLAYER_MAIN = 0, + MUSIC_PLAYER_AUX = 1, +} MusicPlayer; + typedef enum MusicState { MUSIC_STATE_IDLE = 0, MUSIC_STATE_STOP_CURRENT = 1, @@ -192,7 +219,7 @@ typedef enum BGMSpecialSubops { BGM_SPECIAL_WRITE_CUSTOM_ENV = 3, // write custom envelope data BGM_SPECIAL_USE_CUSTOM_ENV = 4, // select which custom envelope to use BGM_SPECIAL_TRIGGER_SOUND = 5, - BGM_SPECIAL_PROX_MIX_OVERRIDE = 6, + BGM_SPECIAL_PROX_MIX_OVERRIDE = 6, } BGMSpecialSubops; typedef enum FxBus { @@ -1103,10 +1130,10 @@ typedef struct SeqNote { typedef struct BGMPlayer { /* 0x000 */ AuGlobals* globals; /* 0x004 */ SoundManager* soundManager; - /* 0x008 */ s32 nextUpdateStep; - /* 0x00C */ s32 nextUpdateInterval; - /* 0x010 */ s32 nextUpdateCounter; - /* 0x014 */ s32 updateCounter; + /* 0x008 */ s32 nextUpdateStep; /// update counter amount to add per audio frame + /* 0x00C */ s32 tickUpdateInterval; /// update counter threshold for a single tick + /* 0x010 */ s32 nextUpdateCounter; /// current update counter value + /* 0x014 */ s32 frameCounter; /// video frames (60 fps) /* 0x018 */ s32 songPlayingCounter; /* 0x01C */ s32 songName; /* 0x020 */ s32 pushSongName; diff --git a/src/audio/bgm_player.c b/src/audio/bgm_player.c index 6dbaad28fe..0f1bce1332 100644 --- a/src/audio/bgm_player.c +++ b/src/audio/bgm_player.c @@ -34,7 +34,7 @@ void au_bgm_begin_video_frame(BGMPlayer* player) { compID = 0; unkType = -1; - player->updateCounter++; + player->frameCounter++; if (player->cmdBufPending != 0) { if (player->cmdBufPending < ARRAY_COUNT(player->cmdBufData)) { @@ -68,7 +68,7 @@ void au_bgm_begin_video_frame(BGMPlayer* player) { player->unk_58 = unkType & 0xFF; player->unk_5A = unkType & 0xFF; player->masterState = BGM_PLAY_STATE_INIT; - player->nextUpdateStep = BGM_SAMPLE_RATE; + player->nextUpdateStep = BGM_DEFAULT_UPDATE_STEP; if (unkType == 2) { bgmFile = player->globals->dataBGM[1]; } else { @@ -76,7 +76,7 @@ void au_bgm_begin_video_frame(BGMPlayer* player) { } player->bgmFile = bgmFile; bgmData = &bgmFile->info; - au_bgm_set_tick_resolution(player, BGM_SAMPLE_RATE, BgmTicksRates[*(player->tickRatePtr) & 7]); + au_bgm_set_tick_resolution(player, BGM_DEFAULT_UPDATE_STEP, BgmTicksRates[*(player->tickRatePtr) & 7]); compOffset = bgmData->compositions[compID]; if (compOffset == 0) { @@ -179,7 +179,7 @@ AuResult au_bgm_process_init_song(SongStartRequest* request) { au_fade_init(&player->fadeInfo, duration, volume0, volume1); player->fadeInfo.envelopeTarget = AU_MAX_VOLUME_16; player->fadeInfo.envelopeTicks = 1; - au_bgm_set_tick_resolution(player, BGM_SAMPLE_RATE, BgmTicksRates[fileInfo->timingPreset & 7]); + au_bgm_set_tick_resolution(player, BGM_DEFAULT_UPDATE_STEP, BgmTicksRates[fileInfo->timingPreset & 7]); if (variation < BGM_VARIATION_0 || variation > BGM_VARIATION_3 || fileInfo->compositions[variation] == 0) { variation = BGM_VARIATION_0; @@ -524,11 +524,11 @@ void au_bgm_player_init(BGMPlayer* player, s32 priority, s32 busID, AuGlobals* g s16 i; player->globals = globals; - au_bgm_set_tick_resolution(player, BGM_SAMPLE_RATE, 48); + au_bgm_set_tick_resolution(player, BGM_DEFAULT_UPDATE_STEP, BGM_DEFAULT_TICKS_PER_BEAT); player->busVolume = AU_MAX_BUS_VOLUME; player->masterTempo = BGM_DEFAULT_TEMPO; player->masterVolume = AU_MAX_VOLUME_8 << 24; - player->updateCounter = 0; + player->frameCounter = 0; player->songPlayingCounter = 0; player->songName = 0; player->pushSongName = 0; @@ -663,8 +663,8 @@ s32 au_bgm_player_audio_frame_update(BGMPlayer* player) { s32 retVal = FALSE; // update pseudorandom numbers with fast 'good enough' method - player->randomValue1 = (player->randomValue1 & 0xFFFF) + (player->songPlayingCounter & 0xFFFF) + (player->updateCounter & 0xFFFF); - player->randomValue2 = (player->randomValue2 & 0xFFFF) + ((player->songPlayingCounter << 4) & 0xFFFF) + ((player->updateCounter >> 4) & 0xFFFF); + player->randomValue1 = (player->randomValue1 & 0xFFFF) + (player->songPlayingCounter & 0xFFFF) + (player->frameCounter & 0xFFFF); + player->randomValue2 = (player->randomValue2 & 0xFFFF) + ((player->songPlayingCounter << 4) & 0xFFFF) + ((player->frameCounter >> 4) & 0xFFFF); do { switch (player->masterState) { case BGM_PLAY_STATE_IDLE: @@ -757,7 +757,7 @@ void au_bgm_player_initialize(BGMPlayer* player) { au_bgm_reset_all_voices(player); player->playbackRate = 128.0f; // set to 1.0 later om... player->masterTempo = BGM_DEFAULT_TEMPO; - player->masterTempoBPM = BGM_DEFAULT_TEMPO / 100; + player->masterTempoBPM = player->masterTempo / 100; player->unused_21E = 0x80; player->masterVolume = AU_MAX_VOLUME_8 << 24; player->pushSongName = 0; @@ -824,27 +824,35 @@ void au_bgm_clear_custom_note_press(BGMPlayer* player, s32 index) { } } -void au_bgm_set_tick_resolution(BGMPlayer* player, s32 sampleRate, s32 resolution) { - u32 samplesPerTick; - - samplesPerTick = 10434782 / (u32)resolution; +void au_bgm_set_tick_resolution(BGMPlayer* player, s32 mBeatsPerMinute, u32 ticksPerBeat) { + // compute how many audio frames before the next tick + u32 mFramesPerTick = BGM_MFRAMES_PER_MINUTE / ticksPerBeat; // Clamp samples per tick to stay in a valid range - if (samplesPerTick > 500000) { - samplesPerTick = 500000; - } else if (samplesPerTick < 80000) { - samplesPerTick = 80000; + if (mFramesPerTick > 500000) { + mFramesPerTick = 500000; + } else if (mFramesPerTick < 80000) { + mFramesPerTick = 80000; } // Clamp to sample rate - if (samplesPerTick < sampleRate) { - sampleRate = samplesPerTick; + if (mFramesPerTick < mBeatsPerMinute) { + mBeatsPerMinute = mFramesPerTick; } - player->nextUpdateStep = sampleRate; - player->nextUpdateInterval = samplesPerTick; - player->nextUpdateCounter = samplesPerTick; - player->maxTempo = samplesPerTick / 1000; + // breakdown of units: + // + // tickUpdateInterval / nextUpdateStep = framesPerTick + // + // 1000 x frames beat min frames + // ------------- x ------ x --------------- = ------ + // min tick 1000 x beat tick + + player->nextUpdateStep = mBeatsPerMinute; + player->tickUpdateInterval = mFramesPerTick; + player->nextUpdateCounter = mFramesPerTick; + + player->maxTempo = mFramesPerTick / BGM_UPDATE_SCALE; } // runs whenever a new composition begins playing @@ -1004,7 +1012,7 @@ void au_bgm_player_update_stop(BGMPlayer* player) { } au_bgm_reset_all_voices(player); player->masterState = BGM_PLAY_STATE_IDLE; - player->nextUpdateStep = BGM_SAMPLE_RATE; + player->nextUpdateStep = BGM_DEFAULT_UPDATE_STEP; } #define POST_BGM_READ() \ @@ -1048,7 +1056,7 @@ void au_bgm_player_update_playing(BGMPlayer *player) { } else { player->masterTempo += player->masterTempoStep; } - player->nextUpdateStep = player->masterTempo * 10; + player->nextUpdateStep = BGM_TEMPO_TO_UPDATE_UNITS(player->masterTempo); } if (player->masterVolumeTicks != 0) { player->masterVolumeTicks--; @@ -1504,7 +1512,7 @@ void au_BGMCmd_E0_MasterTempo(BGMPlayer* player, BGMPlayerTrack* track) { player->masterTempoBPM = bpm; tempo = au_bgm_bpm_to_tempo(player, bpm); player->masterTempo = tempo; - player->nextUpdateStep = tempo * 10; + player->nextUpdateStep = BGM_TEMPO_TO_UPDATE_UNITS(tempo); player->masterTempoTicks = 0; player->masterTempoTarget = 0; player->masterTempoStep = 0; @@ -1999,7 +2007,7 @@ void au_bgm_set_playback_rate(BGMPlayer* player, f32 rate) { player->playbackRate = rate; player->masterTempo = au_bgm_bpm_to_tempo(player, player->masterTempoBPM); - player->nextUpdateStep = player->masterTempo * 10; + player->nextUpdateStep = BGM_TEMPO_TO_UPDATE_UNITS(player->masterTempo); player->masterTempoTicks = 0; player->masterTempoTarget = 0; player->masterTempoStep = 0; diff --git a/src/audio/core/engine.c b/src/audio/core/engine.c index 741bd3132c..abb3376ee4 100644 --- a/src/audio/core/engine.c +++ b/src/audio/core/engine.c @@ -247,7 +247,7 @@ void au_update_clients_for_audio_frame(void) { bgmPlayer->nextUpdateCounter -= bgmPlayer->nextUpdateStep; if (bgmPlayer->nextUpdateCounter <= 0) { - bgmPlayer->nextUpdateCounter += bgmPlayer->nextUpdateInterval; + bgmPlayer->nextUpdateCounter += bgmPlayer->tickUpdateInterval; bgmPlayer->prevUpdateResult = au_bgm_player_audio_frame_update(bgmPlayer); } } @@ -274,7 +274,7 @@ void au_update_clients_for_audio_frame(void) { bgmPlayer->nextUpdateCounter -= bgmPlayer->nextUpdateStep; if (bgmPlayer->nextUpdateCounter <= 0) { - bgmPlayer->nextUpdateCounter += bgmPlayer->nextUpdateInterval; + bgmPlayer->nextUpdateCounter += bgmPlayer->tickUpdateInterval; bgmPlayer->prevUpdateResult = au_bgm_player_audio_frame_update(bgmPlayer); } } diff --git a/src/audio/private.h b/src/audio/private.h index 91ae20305c..063bad0eb8 100644 --- a/src/audio/private.h +++ b/src/audio/private.h @@ -66,7 +66,7 @@ void au_bgm_update_bus_volumes(BGMPlayer* arg0); s32 au_bgm_player_audio_frame_update(BGMPlayer* player); void au_bgm_player_initialize(BGMPlayer* player); void au_bgm_clear_custom_note_press(BGMPlayer* player, s32 arg1); -void au_bgm_set_tick_resolution(BGMPlayer* player, s32 sampleRate, s32 divisor); +void au_bgm_set_tick_resolution(BGMPlayer* player, s32 sampleRate, u32 resolution); void au_bgm_player_read_composition(BGMPlayer* player); void au_bgm_end_composition_loop(BGMPlayer* player, u32 cmd); void au_bgm_load_phrase(BGMPlayer* player, u32 cmd); diff --git a/src/world/area_mac/mac_00/music_mix.c b/src/world/area_mac/mac_00/music_mix.c index 6bd17b96bf..43b89c8d4f 100644 --- a/src/world/area_mac/mac_00/music_mix.c +++ b/src/world/area_mac/mac_00/music_mix.c @@ -6,7 +6,7 @@ MusicProximityTrigger N(MusicMixTrigger1) = { .pos = { 15.0f, -400.0f }, .innerDist = 90.0f, .outerDist = 120.0f, - .unk = 7, + .mix = 7, .manualActivationFlag = MF_MusicMixTrigger1, }; @@ -14,7 +14,7 @@ MusicProximityTrigger N(MusicMixTrigger2) = { .pos = { 445.0f, -307.0f }, .innerDist = 90.0f, .outerDist = 110.0f, - .unk = 3, + .mix = 3, .manualActivationFlag = MF_MusicMixTrigger2, }; diff --git a/src/world/area_mac/mac_01/music_mix.c b/src/world/area_mac/mac_01/music_mix.c index 8c86c7b8f5..adc1b87945 100644 --- a/src/world/area_mac/mac_01/music_mix.c +++ b/src/world/area_mac/mac_01/music_mix.c @@ -6,7 +6,7 @@ MusicProximityTrigger N(MusicMixTrigger1) = { .pos = { -190.0f, -210.0f }, .innerDist = 100.0f, .outerDist = 120.0f, - .unk = 2, + .mix = 2, .manualActivationFlag = MF_MusicMixTrigger1, }; @@ -14,7 +14,7 @@ MusicProximityTrigger N(MusicMixTrigger2) = { .pos = { -150.0f, 330.0f }, .innerDist = 110.0f, .outerDist = 130.0f, - .unk = 8, + .mix = 8, .manualActivationFlag = MF_MusicMixTrigger2, }; @@ -22,7 +22,7 @@ MusicProximityTrigger N(MusicMixTrigger3) = { .pos = { 266.0f, 370.0f }, .innerDist = 200.0f, .outerDist = 220.0f, - .unk = 5, + .mix = 5, .manualActivationFlag = MF_MusicMixTrigger3, }; diff --git a/src/world/area_mac/mac_02/music_mix.c b/src/world/area_mac/mac_02/music_mix.c index dcbf69e3ba..9757029f00 100644 --- a/src/world/area_mac/mac_02/music_mix.c +++ b/src/world/area_mac/mac_02/music_mix.c @@ -6,7 +6,7 @@ MusicProximityTrigger N(MusicMixTrigger1) = { .pos = { -150.0f, -205.0f }, .innerDist = 120.0f, .outerDist = 150.0f, - .unk = 1, + .mix = 1, .manualActivationFlag = MF_MusicMixTrigger1, }; @@ -14,7 +14,7 @@ MusicProximityTrigger N(MusicMixTrigger2) = { .pos = { -400.0f, 250.0f }, .innerDist = 110.0f, .outerDist = 130.0f, - .unk = 4, + .mix = 4, .manualActivationFlag = MF_MusicMixTrigger2, }; diff --git a/src/world/area_mac/mac_04/music_mix.c b/src/world/area_mac/mac_04/music_mix.c index 30dd9fbaa3..b3272a9347 100644 --- a/src/world/area_mac/mac_04/music_mix.c +++ b/src/world/area_mac/mac_04/music_mix.c @@ -6,7 +6,7 @@ MusicProximityTrigger N(MusicMixTrigger) = { .pos = { -480.0f, 220.0f }, .innerDist = 100.0f, .outerDist = 120.0f, - .unk = 9, + .mix = 9, .manualActivationFlag = MF_MusicMixTrigger, }; diff --git a/src/world/common/atomic/MonitorMusicProximityTrigger.inc.c b/src/world/common/atomic/MonitorMusicProximityTrigger.inc.c index 83cfbe0065..15c616e079 100644 --- a/src/world/common/atomic/MonitorMusicProximityTrigger.inc.c +++ b/src/world/common/atomic/MonitorMusicProximityTrigger.inc.c @@ -51,7 +51,7 @@ API_CALLABLE(N(MonitorMusicProximityTrigger)) { } if (cond) { - bgm_adjust_proximity(0, trigger->unk, script->functionTemp[1]); + bgm_adjust_proximity(0, trigger->mix, script->functionTemp[1]); } return ApiStatus_BLOCK;