bgm update units

This commit is contained in:
z64a 2025-06-09 19:26:39 -04:00
parent c3416b5cb2
commit b37fa6a05b
11 changed files with 90 additions and 49 deletions

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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,
};

View File

@ -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,
};

View File

@ -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,
};

View File

@ -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,
};

View File

@ -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;