mirror of
https://github.com/izzy2lost/Diddy-Kong-Racing.git
synced 2026-06-19 01:16:26 -07:00
* Migrade rodata for thread3_main, fix game.c not having a defined rodata in the splat yaml files, and run the formatter. * func_80066CDC NON_MATCHING * Minor stuff * Small docs * Fix some audio stuff * Undo some var moving.
1056 lines
31 KiB
C
1056 lines
31 KiB
C
#include "audio.h"
|
|
#include "memory.h"
|
|
|
|
#include "types.h"
|
|
#include "macros.h"
|
|
#include "asset_enums.h"
|
|
#include "asset_loading.h"
|
|
#include "audio_internal.h"
|
|
#include "audiomgr.h"
|
|
#include "audiosfx.h"
|
|
#include "sched.h"
|
|
#include "libultra/src/audio/seqchannel.h"
|
|
|
|
/************ .data ************/
|
|
|
|
ALCSPlayer *gMusicPlayer = NULL;
|
|
ALCSPlayer *gJinglePlayer = NULL;
|
|
u8 gMusicBaseVolume = 127;
|
|
u8 sfxRelativeVolume = 127;
|
|
u8 gCanPlayMusic = TRUE;
|
|
u8 gCanPlayJingle = FALSE;
|
|
s32 gBlockMusicChange = FALSE;
|
|
s32 audioPrevCount = 0;
|
|
f32 sMusicFadeVolume = 1.0f;
|
|
s32 gMusicSliderVolume = 256;
|
|
s32 gDelayedSoundsCount = 0;
|
|
u8 gMusicNextSeqID = SEQUENCE_NONE;
|
|
u8 gJingleNextSeqID = SEQUENCE_NONE;
|
|
UNUSED s32 D_800DC664 = 0;
|
|
UNUSED s32 D_800DC668 = 0;
|
|
s32 gGlobalMusicVolume = 256; // This is never not 256...
|
|
u8 gBlockVoiceLimitChange = FALSE;
|
|
|
|
/*******************************/
|
|
|
|
/************ .bss ************/
|
|
|
|
// The audio heap is located at the start of the BSS section.
|
|
u8 gAudioHeapStack[AUDIO_HEAP_SIZE];
|
|
|
|
ALHeap gALHeap;
|
|
ALSeqFile *gSequenceTable;
|
|
void *gMusicSequenceData;
|
|
void *gJingleSequenceData;
|
|
u8 gCurrentSequenceID;
|
|
u8 gCurrentJingleID;
|
|
s32 gMusicTempo;
|
|
u32 *gSeqLengthTable;
|
|
ALBankFile *gSequenceBank;
|
|
ALBankFile *gSoundBank; // Official Name: sfxBankPtr
|
|
SoundData *gSoundTable;
|
|
MusicData *gSeqSoundTable;
|
|
s32 gSoundCount;
|
|
s32 gSeqSoundCount;
|
|
u32 gSoundTableSize;
|
|
u32 gSeqSoundTableSize;
|
|
s16 sMusicTempo;
|
|
f32 gMusicAnimationTick;
|
|
s32 sMusicDelayTimer;
|
|
s32 sMusicDelayLength;
|
|
u8 gMusicPlaying;
|
|
u8 gJinglePlaying;
|
|
DelayedSound gDelayedSounds[8];
|
|
ALCSeq gMusicSequence;
|
|
ALCSeq gJingleSequence;
|
|
u8 gSkipResetChannels; // Stored and used by a single function, but redundant.
|
|
u8 gAudioVolumeSetting;
|
|
u32 gDynamicMusicChannelMask;
|
|
SoundMask *gGlobalSoundMask;
|
|
SoundMask *gSpatialSoundMask;
|
|
SoundMask *gRacerSoundMask;
|
|
|
|
/******************************/
|
|
|
|
/**
|
|
* Allocate memory for all of the audio systems, including sequence data, sound data and heaps.
|
|
* Afterwards, set up the audio thread and start it.
|
|
*/
|
|
void audio_init(OSSched *sc) {
|
|
s32 iCnt;
|
|
ALSynConfig synth_config;
|
|
s32 *addrPtr;
|
|
u32 seqfSize;
|
|
u32 seqLength;
|
|
UNUSED u32 pad;
|
|
audioMgrConfig audConfig;
|
|
|
|
seqLength = 0;
|
|
alHeapInit(&gALHeap, gAudioHeapStack, sizeof(gAudioHeapStack));
|
|
|
|
addrPtr = (s32 *) load_asset_section_from_rom(ASSET_AUDIO_TABLE);
|
|
gSoundBank = (ALBankFile *) mempool_alloc_safe(addrPtr[ASSET_AUDIO_2] - addrPtr[ASSET_AUDIO_1], COLOUR_TAG_CYAN);
|
|
load_asset_to_address(ASSET_AUDIO, (u32) gSoundBank, addrPtr[ASSET_AUDIO_1],
|
|
addrPtr[ASSET_AUDIO_2] - addrPtr[ASSET_AUDIO_1]);
|
|
alBnkfNew(gSoundBank, get_rom_offset_of_asset(ASSET_AUDIO, addrPtr[ASSET_AUDIO_2]));
|
|
|
|
gSoundTableSize = addrPtr[ASSET_AUDIO_7] - addrPtr[ASSET_AUDIO_6];
|
|
gSoundTable = (SoundData *) mempool_alloc_safe(gSoundTableSize, COLOUR_TAG_CYAN);
|
|
load_asset_to_address(ASSET_AUDIO, (u32) gSoundTable, addrPtr[ASSET_AUDIO_6], gSoundTableSize);
|
|
gSoundCount = gSoundTableSize / sizeof(SoundData);
|
|
|
|
gSeqSoundTableSize = addrPtr[ASSET_AUDIO_6] - addrPtr[ASSET_AUDIO_5];
|
|
gSeqSoundTable = (MusicData *) mempool_alloc_safe(gSeqSoundTableSize, COLOUR_TAG_CYAN);
|
|
load_asset_to_address(ASSET_AUDIO, (u32) gSeqSoundTable, addrPtr[ASSET_AUDIO_5], gSeqSoundTableSize);
|
|
gSeqSoundCount = gSeqSoundTableSize / sizeof(MusicData);
|
|
|
|
gSequenceBank = (ALBankFile *) mempool_alloc_safe(addrPtr[ASSET_AUDIO_0], COLOUR_TAG_CYAN);
|
|
load_asset_to_address(ASSET_AUDIO, (u32) gSequenceBank, 0, addrPtr[ASSET_AUDIO_0]);
|
|
alBnkfNew(gSequenceBank, get_rom_offset_of_asset(ASSET_AUDIO, addrPtr[ASSET_AUDIO_0]));
|
|
gSequenceTable = (ALSeqFile *) alHeapAlloc(&gALHeap, 1, 4);
|
|
load_asset_to_address(ASSET_AUDIO, (u32) gSequenceTable, addrPtr[ASSET_AUDIO_4], 4);
|
|
|
|
seqfSize = (gSequenceTable->seqCount) * 8 + 4;
|
|
gSequenceTable = mempool_alloc_safe(seqfSize, COLOUR_TAG_CYAN);
|
|
load_asset_to_address(ASSET_AUDIO, (u32) gSequenceTable, addrPtr[ASSET_AUDIO_4], seqfSize);
|
|
alSeqFileNew(gSequenceTable, get_rom_offset_of_asset(ASSET_AUDIO, addrPtr[ASSET_AUDIO_4]));
|
|
gSeqLengthTable = (u32 *) mempool_alloc_safe((gSequenceTable->seqCount) * 4, COLOUR_TAG_CYAN);
|
|
|
|
for (iCnt = 0; iCnt < gSequenceTable->seqCount; iCnt++) {
|
|
pad = (u32) (gSequenceTable + 8 + iCnt * 8); // Fakematch
|
|
gSeqLengthTable[iCnt] = gSequenceTable->seqArray[iCnt].len;
|
|
if (gSeqLengthTable[iCnt] & 1) {
|
|
gSeqLengthTable[iCnt]++;
|
|
}
|
|
if (seqLength < gSeqLengthTable[iCnt]) {
|
|
seqLength = gSeqLengthTable[iCnt];
|
|
}
|
|
}
|
|
|
|
synth_config.maxVVoices = 40;
|
|
synth_config.maxPVoices = 40;
|
|
synth_config.maxUpdates = 96;
|
|
synth_config.dmaproc = NULL;
|
|
synth_config.fxType[0] = 6;
|
|
synth_config.fxType[1] = 2;
|
|
synth_config.outputRate = 0;
|
|
synth_config.heap = &gALHeap;
|
|
amCreateAudioMgr(&synth_config, 12, sc);
|
|
gMusicPlayer = sound_seqplayer_init(24, 120);
|
|
set_voice_limit(gMusicPlayer, 18);
|
|
gJinglePlayer = sound_seqplayer_init(16, 50);
|
|
gMusicSequenceData = mempool_alloc_safe(seqLength, COLOUR_TAG_CYAN);
|
|
gJingleSequenceData = mempool_alloc_safe(seqLength, COLOUR_TAG_CYAN);
|
|
audConfig.unk04 = 150;
|
|
audConfig.unk00 = 32;
|
|
audConfig.maxChannels = AUDIO_CHANNELS;
|
|
audConfig.unk10 = 1;
|
|
audConfig.hp = &gALHeap;
|
|
alSndPNew(&audConfig);
|
|
audioStartThread();
|
|
sound_volume_change(VOLUME_NORMAL);
|
|
mempool_free(addrPtr);
|
|
set_sound_channel_count(10);
|
|
gBlockMusicChange = FALSE;
|
|
gMusicPlaying = FALSE;
|
|
gJinglePlaying = FALSE;
|
|
gDelayedSoundsCount = 0;
|
|
gSkipResetChannels = FALSE;
|
|
gAudioVolumeSetting = VOLUME_NORMAL;
|
|
#ifdef AVOID_UB
|
|
gMusicAnimationTick = 1.0f; // Prevents a denorm crash on the character select screen.
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Depending on whether or not the audio volume is set to normal and the argument is false, reset sound effect channel
|
|
* volumes.
|
|
*/
|
|
void sound_volume_reset(u8 skipReset) {
|
|
if (gAudioVolumeSetting == VOLUME_NORMAL) {
|
|
gSkipResetChannels = skipReset;
|
|
if (gSkipResetChannels == FALSE) {
|
|
gGlobalMusicVolume = 256;
|
|
music_volume_set(gMusicBaseVolume);
|
|
set_sound_channel_volume(0, gGlobalMusicVolume * 128 - 1);
|
|
set_sound_channel_volume(1, gGlobalMusicVolume * 128 - 1);
|
|
set_sound_channel_volume(2, gGlobalMusicVolume * 128 - 1);
|
|
set_sound_channel_volume(4, gGlobalMusicVolume * 128 - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Changes the volume of each sound channel depending on what value is passed through.
|
|
*/
|
|
void sound_volume_change(s32 behaviour) {
|
|
switch (behaviour) {
|
|
case VOLUME_LOWER: // Mute most sound effects and half the volume of music.
|
|
set_sound_channel_volume(0, 0);
|
|
set_sound_channel_volume(1, 32767);
|
|
set_sound_channel_volume(2, 0);
|
|
set_sound_channel_volume(4, 0);
|
|
alCSPSetVol(gMusicPlayer, (s16) (gMusicBaseVolume * gMusicSliderVolume >> 2));
|
|
alCSPSetVol(gJinglePlayer, 0);
|
|
break;
|
|
case VOLUME_LOWER_AMBIENT: // Mute the ambient channel, making course elements stop making noise.
|
|
set_sound_channel_volume(0, 0);
|
|
set_sound_channel_volume(1, 32767);
|
|
set_sound_channel_volume(2, 32767);
|
|
set_sound_channel_volume(4, 32767);
|
|
break;
|
|
case VOLUME_UNK03:
|
|
set_sound_channel_volume(0, 0);
|
|
set_sound_channel_volume(1, 32767);
|
|
set_sound_channel_volume(2, 0);
|
|
set_sound_channel_volume(4, 0);
|
|
break;
|
|
default: // Restore sound back to normal.
|
|
set_sound_channel_volume(0, 32767);
|
|
set_sound_channel_volume(1, 32767);
|
|
set_sound_channel_volume(2, 32767);
|
|
set_sound_channel_volume(4, 32767);
|
|
alCSPSetVol(gMusicPlayer, (s16) (gMusicBaseVolume * gMusicSliderVolume));
|
|
alCSPSetVol(gJinglePlayer, (s16) (get_sfx_volume_slider() * sfxRelativeVolume));
|
|
break;
|
|
}
|
|
gAudioVolumeSetting = behaviour;
|
|
}
|
|
|
|
/**
|
|
* Prevents changing of background music.
|
|
*/
|
|
void music_change_off(void) {
|
|
gBlockMusicChange = TRUE;
|
|
}
|
|
|
|
/**
|
|
* Allows changing of background music.
|
|
*/
|
|
void music_change_on(void) {
|
|
gBlockMusicChange = FALSE;
|
|
}
|
|
|
|
/**
|
|
* Queue a new music sequence to play if not blocked.
|
|
* Stops any playing existing music beforehand.
|
|
*/
|
|
void music_play(u8 seqID) {
|
|
if (gBlockMusicChange == FALSE && gMusicSliderVolume != 0) {
|
|
gCurrentSequenceID = seqID;
|
|
gMusicBaseVolume = 127;
|
|
if (gCanPlayMusic) {
|
|
music_sequence_start(gCurrentSequenceID, gMusicPlayer);
|
|
}
|
|
gMusicTempo = alCSPGetTempo(gMusicPlayer);
|
|
audioPrevCount = osGetCount();
|
|
gMusicPlaying = TRUE;
|
|
gDynamicMusicChannelMask = MUSIC_CHAN_MASK_NONE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update the background music voice limit if not prevented from doing so.
|
|
*/
|
|
void music_voicelimit_set(u8 voiceLimit) {
|
|
if (gBlockVoiceLimitChange == FALSE) {
|
|
set_voice_limit(gMusicPlayer, voiceLimit);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Prevent the background music voice limit from being changed.
|
|
*/
|
|
void music_voicelimit_change_off(void) {
|
|
gBlockVoiceLimitChange = TRUE;
|
|
}
|
|
|
|
/**
|
|
* Allow the background music voice limit to be changed.
|
|
*/
|
|
void music_voicelimit_change_on(void) {
|
|
gBlockVoiceLimitChange = FALSE;
|
|
}
|
|
|
|
/**
|
|
* Update the jingle players voice limit.
|
|
*/
|
|
void music_jingle_voicelimit_set(u8 voiceLimit) {
|
|
set_voice_limit(gJinglePlayer, voiceLimit);
|
|
}
|
|
|
|
UNUSED void func_80000C68(u8 arg0) {
|
|
func_80063A90(gMusicPlayer, arg0);
|
|
}
|
|
|
|
/**
|
|
* Enables the timer to start fading in or out the background music.
|
|
* Give it a positive number to fade in, otherwise, give it a negative one to fade out.
|
|
*/
|
|
void music_fade(s32 time) {
|
|
sMusicDelayTimer = 0;
|
|
sMusicDelayLength = (time * 60) >> 8;
|
|
}
|
|
|
|
/**
|
|
* Sets the background music volume back to normal.
|
|
*/
|
|
void music_volume_reset(void) {
|
|
sMusicDelayTimer = 0;
|
|
sMusicDelayLength = 0;
|
|
sMusicFadeVolume = 1.0f;
|
|
music_volume_set(gMusicBaseVolume);
|
|
}
|
|
|
|
/**
|
|
* Run every frame, this handles the transitions in and out of music sequences.
|
|
* If there's something in the queue, then begin to play that.
|
|
* Additionally, it also handles the delayed audio queue, counting down and playing any sounds.
|
|
*/
|
|
void sound_update_queue(u8 updateRate) {
|
|
s32 i;
|
|
s32 j;
|
|
|
|
if (sMusicDelayLength > 0) {
|
|
sMusicDelayTimer += updateRate;
|
|
sMusicFadeVolume = ((f32) sMusicDelayTimer) / ((f32) sMusicDelayLength);
|
|
if (sMusicFadeVolume > 1.0) {
|
|
sMusicDelayTimer = 0;
|
|
sMusicDelayLength = 0;
|
|
sMusicFadeVolume = 1.0f;
|
|
}
|
|
music_volume_set(gMusicBaseVolume);
|
|
} else if (sMusicDelayLength < 0) {
|
|
sMusicDelayTimer -= updateRate;
|
|
sMusicFadeVolume = 1.0f - ((f32) sMusicDelayTimer) / ((f32) sMusicDelayLength);
|
|
if (sMusicFadeVolume < 0.0) {
|
|
sMusicDelayTimer = 0;
|
|
sMusicDelayLength = 0;
|
|
sMusicFadeVolume = 0.0f;
|
|
}
|
|
music_volume_set(gMusicBaseVolume);
|
|
}
|
|
|
|
if (gDelayedSoundsCount > 0) {
|
|
for (i = 0; i < gDelayedSoundsCount;) {
|
|
gDelayedSounds[i].timer -= updateRate;
|
|
if (gDelayedSounds[i].timer <= 0) {
|
|
j = i;
|
|
sound_play(gDelayedSounds[i].soundId, (s32 *) gDelayedSounds[i].soundMask);
|
|
|
|
gDelayedSoundsCount -= 1;
|
|
while (j < gDelayedSoundsCount) {
|
|
gDelayedSounds[i].soundId = gDelayedSounds[i + 1].soundId;
|
|
gDelayedSounds[i].timer = gDelayedSounds[i + 1].timer;
|
|
gDelayedSounds[i].soundMask = gDelayedSounds[i + 1].soundMask;
|
|
j++;
|
|
}
|
|
j++;
|
|
} else {
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
music_sequence_init(gMusicPlayer, gMusicSequenceData, &gMusicNextSeqID, &gMusicSequence);
|
|
music_sequence_init(gJinglePlayer, gJingleSequenceData, &gJingleNextSeqID, &gJingleSequence);
|
|
if (sMusicTempo == -1 && gMusicPlayer->target) {
|
|
sMusicTempo = 60000000 / alCSPGetTempo(gMusicPlayer);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add a sound to a queue to play after a set time has passed.
|
|
* Delay time is in seconds. (1.0f = 1 second)
|
|
*/
|
|
void sound_play_delayed(u16 soundId, SoundMask *soundMask, f32 delayTime) {
|
|
if (gDelayedSoundsCount < 8) {
|
|
gDelayedSounds[gDelayedSoundsCount].soundId = soundId;
|
|
gDelayedSounds[gDelayedSoundsCount].soundMask = soundMask;
|
|
gDelayedSounds[gDelayedSoundsCount].timer = delayTime * 60.0f;
|
|
gDelayedSoundsCount++;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clear the delayed sound queue.
|
|
*/
|
|
void sound_clear_delayed(void) {
|
|
gDelayedSoundsCount = 0;
|
|
}
|
|
|
|
/**
|
|
* Return the channel mask of the music player.
|
|
* Official name: musicGetChanMask
|
|
*/
|
|
u16 music_channel_get_mask(void) {
|
|
return gMusicPlayer->chanMask;
|
|
}
|
|
|
|
/**
|
|
* Sets the channels in the sequence on or off based on the channel mask given.
|
|
*/
|
|
void music_dynamic_set(u16 channelMask) {
|
|
u32 i;
|
|
if (gMusicNextSeqID) {
|
|
gDynamicMusicChannelMask = channelMask;
|
|
} else {
|
|
gMusicPlayer->chanMask = channelMask;
|
|
for (i = 0; i != AUDIO_CHANNELS; i++) {
|
|
if (channelMask & (1 << i)) {
|
|
music_channel_on(i);
|
|
} else {
|
|
music_channel_off(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Mute the sequence channel, preventing it from playing any sound.
|
|
*/
|
|
void music_channel_off(u8 channel) {
|
|
if (channel < AUDIO_CHANNELS) {
|
|
alSeqChOff(gMusicPlayer, channel);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return true if the given channel is currently active.
|
|
* Official name: musicGetChnlActive
|
|
*/
|
|
s32 music_channel_active(s32 channel) {
|
|
return (gMusicPlayer->chanMask & (1 << channel)) == 0;
|
|
}
|
|
|
|
/**
|
|
* Unmute the sequence channel so it can play sound.
|
|
*/
|
|
void music_channel_on(u8 channel) {
|
|
if (channel < AUDIO_CHANNELS) {
|
|
alSeqChOn(gMusicPlayer, channel);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the panning level of the given channel for the music player.
|
|
* Official name: musicSetChlPan
|
|
*/
|
|
void music_channel_pan_set(u8 channel, ALPan pan) {
|
|
if (channel < AUDIO_CHANNELS) {
|
|
alCSPSetChlPan(gMusicPlayer, channel, pan);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the volume of the given channel for the music player.
|
|
* Official name musicSetChlVol
|
|
*/
|
|
void music_channel_volume_set(u8 channel, u8 volume) {
|
|
if (channel < AUDIO_CHANNELS) {
|
|
alCSPSetChlVol(gMusicPlayer, channel, volume);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the volume of the given channel in the music player.
|
|
* Official name: musicGetChlVol
|
|
*/
|
|
UNUSED u8 music_channel_volume(u8 channel) {
|
|
if (channel >= AUDIO_CHANNELS) {
|
|
return 0;
|
|
} else {
|
|
return alCSPGetChlVol(gMusicPlayer, channel);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set this channel to fade in over time.
|
|
*/
|
|
void music_channel_fade_set(u8 channel, ALPan fade) {
|
|
if (channel < AUDIO_CHANNELS) {
|
|
alCSPSetFadeIn(gMusicPlayer, channel, fade);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the fade volume of the given channel.
|
|
*/
|
|
u8 music_channel_fade(u8 channel) {
|
|
if (channel >= AUDIO_CHANNELS) {
|
|
return 0;
|
|
}
|
|
return alCSPGetFadeIn(gMusicPlayer, channel);
|
|
}
|
|
|
|
/**
|
|
* Resets all audio channels for the music player to the default state.
|
|
* This is being enabled, centre panning and at normal volume.
|
|
*/
|
|
void music_channel_reset_all(void) {
|
|
u32 channel;
|
|
if (gBlockMusicChange == FALSE) {
|
|
for (channel = 0; channel < AUDIO_CHANNELS; channel++) {
|
|
music_channel_on(channel);
|
|
music_channel_fade_set(channel, 127);
|
|
music_channel_volume_set(channel, 127);
|
|
}
|
|
}
|
|
}
|
|
|
|
UNUSED u8 func_80001358(u8 arg0, u8 arg1, s32 arg2) {
|
|
u8 val_1f;
|
|
u8 val_1e;
|
|
s32 updatedVol;
|
|
|
|
// u8 fadeIn_chan = arg0;
|
|
if (!(arg0 == 100)) {
|
|
val_1f = arg2 + alCSPGetChlVol(gMusicPlayer, arg0);
|
|
if (val_1f > 127) {
|
|
val_1f = 127;
|
|
}
|
|
alCSPSetChlVol(gMusicPlayer, arg0, val_1f);
|
|
}
|
|
|
|
if (arg1 != 100) {
|
|
updatedVol = alCSPGetChlVol(gMusicPlayer, arg1);
|
|
val_1e = (updatedVol > arg2) ? updatedVol - arg2 : 0;
|
|
alCSPSetChlVol(gMusicPlayer, arg1, val_1e);
|
|
return val_1e;
|
|
} else {
|
|
return 127 - val_1f;
|
|
}
|
|
}
|
|
|
|
UNUSED void func_80001440(u8 *arg0) {
|
|
s32 s0 = 0;
|
|
if (gMusicPlayer->maxChannels > 0) {
|
|
do {
|
|
arg0[s0] = alSeqpGetChlFXMix((ALSeqPlayer *) gMusicPlayer, s0);
|
|
s0++;
|
|
} while (s0 < gMusicPlayer->maxChannels);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Multiplies the current tempo of the background music.
|
|
* Since it calls music_tempo and multiplies it by the result, calling this repeatedly can recursively change the
|
|
* music's speed.
|
|
*/
|
|
void music_tempo_set_relative(f32 tempo) {
|
|
music_tempo_set((s32) ((f32) (u32) (music_tempo() & 0xFF) * tempo));
|
|
}
|
|
|
|
/**
|
|
* Set the tempo of the current playing background music.
|
|
* Official name: musicSetTempo
|
|
*/
|
|
void music_tempo_set(s32 tempo) {
|
|
if (tempo != 0) {
|
|
f32 inv_tempo = (1.0f / tempo);
|
|
alCSPSetTempo(gMusicPlayer, (s32) (inv_tempo * 60000000.0f));
|
|
sMusicTempo = tempo;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the tempo of the current playing background music.
|
|
* Official name: musicGetTempo
|
|
*/
|
|
s16 music_tempo(void) {
|
|
return sMusicTempo;
|
|
}
|
|
|
|
/**
|
|
* Returns true if background music is currently playing.
|
|
*/
|
|
u8 music_is_playing(void) {
|
|
return (alCSPGetState(gMusicPlayer) == AL_PLAYING);
|
|
}
|
|
|
|
/**
|
|
* Counts up using the internal timer.
|
|
* Loops itself round, so the final result will return 0.0f - 1.0f.
|
|
*/
|
|
f32 music_animation_fraction(void) {
|
|
f32 tmp;
|
|
u32 cnt = osGetCount();
|
|
if ((u32) audioPrevCount < cnt) {
|
|
gMusicAnimationTick += (f32) (cnt - audioPrevCount) / 46875.0f;
|
|
} else {
|
|
gMusicAnimationTick += (f32) ((cnt - audioPrevCount) - 1) / 46875.0f;
|
|
}
|
|
if (gMusicPlaying == FALSE) {
|
|
sMusicTempo = 182;
|
|
}
|
|
for (tmp = 120000.0f / (f32) sMusicTempo; tmp < gMusicAnimationTick; gMusicAnimationTick -= tmp) {
|
|
;
|
|
}
|
|
audioPrevCount = (s32) cnt;
|
|
return gMusicAnimationTick / tmp;
|
|
}
|
|
|
|
/**
|
|
* Writes the music and sound tempo, as well as the volume to the arguments.
|
|
*/
|
|
UNUSED void sound_get_properties(u8 poolID, u8 *tempo, u8 *volume, u8 *reverb) {
|
|
*tempo = gSeqSoundTable[poolID].tempo;
|
|
*volume = gSeqSoundTable[poolID].volume;
|
|
*reverb = gSeqSoundTable[poolID].reverb;
|
|
}
|
|
|
|
/**
|
|
* Play a jingle, but only if there isn't one playing already.
|
|
*/
|
|
void music_jingle_play_safe(u8 jingleID) {
|
|
if (music_jingle_playing() == SEQUENCE_NONE) {
|
|
music_sequence_start(gCurrentJingleID = jingleID, gJinglePlayer);
|
|
gJinglePlaying = TRUE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the tempo for the jingle player.
|
|
*/
|
|
void sound_jingle_tempo_set(s32 tempo) {
|
|
f32 inv_tempo = (1.0f / tempo);
|
|
alCSPSetTempo(gJinglePlayer, (s32) (inv_tempo * 60000000.0f));
|
|
}
|
|
|
|
/**
|
|
* Stops the background music.
|
|
*/
|
|
void music_stop(void) {
|
|
if (gBlockMusicChange == FALSE) {
|
|
music_sequence_stop(gMusicPlayer);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set background music to play or not.
|
|
* If the setting changed, then either stop or start music.
|
|
*/
|
|
UNUSED void music_enabled_set(u8 setting) {
|
|
if (setting != gCanPlayMusic) {
|
|
gCanPlayMusic = setting;
|
|
if (setting) {
|
|
music_play(gCurrentSequenceID);
|
|
} else {
|
|
music_stop();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return whether background music can be played.
|
|
*/
|
|
u8 music_can_play(void) {
|
|
return gCanPlayMusic;
|
|
}
|
|
|
|
/**
|
|
* Stops the currently playing jingle.
|
|
*/
|
|
void music_jingle_stop(void) {
|
|
if (music_jingle_playing() == SEQUENCE_NONE) {
|
|
gCurrentJingleID = SEQUENCE_NONE;
|
|
music_sequence_stop(gJinglePlayer);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the currently playing music.
|
|
*/
|
|
u8 music_current_sequence(void) {
|
|
if (gCurrentSequenceID != SEQUENCE_NONE && gMusicPlayer->state == AL_PLAYING) {
|
|
return gCurrentSequenceID;
|
|
} else {
|
|
return SEQUENCE_NONE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the next music sequence to be played if there is one.
|
|
* Otherwise, return what's currently playing.
|
|
*/
|
|
UNUSED u8 music_next(void) {
|
|
if (gMusicNextSeqID) {
|
|
return gMusicNextSeqID;
|
|
} else {
|
|
return gCurrentSequenceID;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the currently playing jingle.
|
|
*/
|
|
u8 music_jingle_current(void) {
|
|
return gCurrentJingleID;
|
|
}
|
|
|
|
/**
|
|
* Set the volume of the music.
|
|
* Update music volume with this new setting.
|
|
*/
|
|
void music_volume_set(u8 volume) {
|
|
f32 normalized_vol;
|
|
|
|
gMusicBaseVolume = volume;
|
|
normalized_vol = gMusicSliderVolume * gMusicBaseVolume * sMusicFadeVolume;
|
|
alCSPSetVol(gMusicPlayer, (s16) ((s32) (gGlobalMusicVolume * normalized_vol) >> 8));
|
|
}
|
|
|
|
/**
|
|
* Set the user configured music volume.
|
|
* Update music volume with this new setting.
|
|
*/
|
|
void music_volume_config_set(u32 slider_val) {
|
|
f32 normalized_vol;
|
|
|
|
slider_val = (slider_val <= 256) ? slider_val : 256;
|
|
gMusicSliderVolume = slider_val;
|
|
normalized_vol = gMusicSliderVolume * gMusicBaseVolume * sMusicFadeVolume;
|
|
alCSPSetVol(gMusicPlayer, (s16) ((s32) (gGlobalMusicVolume * normalized_vol) >> 8));
|
|
}
|
|
|
|
/**
|
|
* Return the baseline music volume, unaffected by user config.
|
|
*/
|
|
u8 music_volume(void) {
|
|
return gMusicBaseVolume;
|
|
}
|
|
|
|
/**
|
|
* Return the music volume set by the player.
|
|
*/
|
|
s32 music_volume_config(void) {
|
|
return gMusicSliderVolume;
|
|
}
|
|
|
|
/**
|
|
* Set the volume for the jingle player.
|
|
* The jingle player scales with sfx volume rather than music volume.
|
|
*/
|
|
void music_jingle_volume_set(u8 arg0) {
|
|
sfxRelativeVolume = arg0;
|
|
alCSPSetVol(gJinglePlayer, (s16) (get_sfx_volume_slider() * sfxRelativeVolume));
|
|
}
|
|
|
|
/**
|
|
* Set the panning level for every channel in the jingle player.
|
|
*/
|
|
void music_jingle_pan_set(ALPan pan) {
|
|
u32 iChan;
|
|
for (iChan = 0; iChan < AUDIO_CHANNELS; iChan++) {
|
|
alCSPSetChlPan(gJinglePlayer, iChan, pan);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Plays a sequence just once, allowing it to coexist with the music if necessary.
|
|
* Examples include getting silver coins, challenge keys, or getting the locked message.
|
|
*/
|
|
void music_jingle_play(u8 seqID) {
|
|
gCanPlayJingle = TRUE;
|
|
music_sequence_start(gCurrentJingleID = seqID, gJinglePlayer);
|
|
}
|
|
|
|
/**
|
|
* If there's a jingle playing, return that, otherwise, return 0.
|
|
*/
|
|
u32 music_jingle_playing(void) {
|
|
if (gCurrentJingleID && gCanPlayJingle && (gJinglePlayer->state == AL_PLAYING)) {
|
|
return gCurrentJingleID;
|
|
}
|
|
gCanPlayJingle = FALSE;
|
|
return SEQUENCE_NONE;
|
|
}
|
|
|
|
/**
|
|
* Sets the volume for every sound channel.
|
|
* Tries to set up to 64, regardless of if there are 64 sound channels or not.
|
|
* !@bug: This can cause an out of bounds array index.
|
|
*/
|
|
UNUSED void sound_channel_volume_all(u16 volume) {
|
|
u32 i;
|
|
for (i = 0; i < 64; i++) {
|
|
set_sound_channel_volume(i, volume << 8);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the audible distance of the sound effect.
|
|
*/
|
|
u16 sound_distance(u16 soundId) {
|
|
if (soundId > gSoundCount) {
|
|
return 0;
|
|
}
|
|
return gSoundTable[soundId].distance;
|
|
}
|
|
|
|
/**
|
|
* Add the requested sound to the queue and update the mask to show that this sound is playing at that source.
|
|
* If no soundmask is provided, then instead use the global mask.
|
|
*/
|
|
void sound_play(u16 soundID, s32 *soundMask) {
|
|
f32 pitch;
|
|
s32 soundBite;
|
|
|
|
if (soundID > gSoundCount) {
|
|
if (soundMask != NULL) {
|
|
*soundMask = NULL;
|
|
}
|
|
stubbed_printf("amSndPlay: Illegal sound effects table index\n");
|
|
return;
|
|
}
|
|
soundBite = gSoundTable[soundID].soundBite;
|
|
if (soundBite == NULL) {
|
|
if (soundMask != NULL) {
|
|
*soundMask = NULL;
|
|
}
|
|
return;
|
|
}
|
|
pitch = gSoundTable[soundID].pitch / 100.0f;
|
|
if (soundMask != NULL) {
|
|
func_80004668(gSoundBank->bankArray[0], soundBite, gSoundTable[soundID].unk8, (SoundMask *) soundMask);
|
|
if (*soundMask != NULL) {
|
|
sound_event_update(*soundMask, AL_SNDP_VOL_EVT, gSoundTable[soundID].volume * 256);
|
|
sound_event_update(*soundMask, AL_SNDP_PITCH_EVT, *((u32 *) &pitch));
|
|
}
|
|
} else {
|
|
soundMask = (s32 *) &gGlobalSoundMask;
|
|
func_80004668(gSoundBank->bankArray[0], soundBite, gSoundTable[soundID].unk8, (SoundMask *) &gGlobalSoundMask);
|
|
if (*soundMask != NULL) {
|
|
sound_event_update(*soundMask, AL_SNDP_VOL_EVT, gSoundTable[soundID].volume * 256);
|
|
sound_event_update(*soundMask, AL_SNDP_PITCH_EVT, *((u32 *) &pitch));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates a spatial audio reference, then plays a sound.
|
|
* This then makes the audio pan around in 3D space.
|
|
* If it is not given a mask, then it will use the global mask.
|
|
*/
|
|
void sound_play_spatial(u16 soundID, f32 x, f32 y, f32 z, s32 **soundMask) {
|
|
if (soundMask == NULL) {
|
|
soundMask = (s32 **) &gSpatialSoundMask;
|
|
}
|
|
|
|
sound_play(soundID, (s32 *) soundMask);
|
|
|
|
if (*soundMask != NULL) {
|
|
func_80009B7C(*soundMask, x, y, z);
|
|
}
|
|
}
|
|
|
|
void func_80001F14(u16 soundID, s32 *soundMask) {
|
|
if (soundID <= 0 || sound_count() < soundID) {
|
|
stubbed_printf("amSndPlayDirect: Somebody tried to play illegal sound %d\n", soundID);
|
|
if (soundMask) {
|
|
*soundMask = NULL;
|
|
}
|
|
return;
|
|
}
|
|
if (soundMask) {
|
|
func_80004638(gSoundBank->bankArray[0], (s16) soundID, (SoundMask *) soundMask);
|
|
} else {
|
|
func_80004638(gSoundBank->bankArray[0], (s16) soundID, (SoundMask *) &gRacerSoundMask);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the volume of the sound relative to the baseline volume of the sound ID.
|
|
*/
|
|
void sound_volume_set_relative(u16 soundID, void *soundState, u8 volume) {
|
|
s32 newVolume = ((s32) (gSoundTable[soundID].volume * (volume / 127.0f))) * 256;
|
|
if (soundState) {
|
|
sound_event_update((s32) soundState, AL_SNDP_VOL_EVT, newVolume);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates the volume of the given sound mask.
|
|
*/
|
|
UNUSED void sound_volume_set(SoundMask *soundMask, u8 arg1) {
|
|
if (soundMask != NULL) {
|
|
sound_event_update((s32) soundMask, AL_SNDP_VOL_EVT, arg1 * 256);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates the pitch of the given sound mask.
|
|
* Official name: amSndSetPitchDirect
|
|
*/
|
|
UNUSED void sound_pitch_set(SoundMask *soundMask, u32 pitch) {
|
|
u32 *pitchAddr = &pitch;
|
|
if (soundMask != NULL) {
|
|
sound_event_update((s32) soundMask, AL_SNDP_PITCH_EVT, *pitchAddr);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the number of playable sounds in the audio table.
|
|
* Official name: amGetSfxCount
|
|
*/
|
|
u16 sound_count(void) {
|
|
return gSoundBank->bankArray[0]->instArray[0]->soundCount;
|
|
}
|
|
|
|
/**
|
|
* Return mumber of playable sequences in the table.
|
|
*/
|
|
u8 music_sequence_count(void) {
|
|
return gSequenceTable->seqCount;
|
|
}
|
|
|
|
/**
|
|
* Writes the sound table address, size and element count into the arguments.
|
|
*/
|
|
void sound_table_properties(SoundData **table, s32 *size, s32 *count) {
|
|
if (table != NULL) {
|
|
*table = gSoundTable;
|
|
}
|
|
if (size != NULL) {
|
|
*size = gSoundTableSize;
|
|
}
|
|
if (count != NULL) {
|
|
*count = gSoundCount;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Writes the music sound table address, size and element count into the arguments.
|
|
*/
|
|
UNUSED void music_table_properties(MusicData **table, s32 *size, s32 *count) {
|
|
if (table != NULL) {
|
|
*table = gSeqSoundTable;
|
|
}
|
|
if (size != NULL) {
|
|
*size = gSeqSoundTableSize;
|
|
}
|
|
if (count != NULL) {
|
|
*count = gSeqSoundCount;
|
|
}
|
|
}
|
|
|
|
u8 gSoundBank_GetSoundDecayTime(u16 soundID) {
|
|
if (soundID <= 0 || gSoundBank->bankArray[0]->instArray[0]->soundCount < soundID) {
|
|
return 0;
|
|
}
|
|
return ((u32) (1 + gSoundBank->bankArray[0]->instArray[0]->soundArray[soundID - 1]->envelope->decayTime) == 0);
|
|
}
|
|
|
|
/**
|
|
* Allocate space, then initialise a sequence player for audio playback.
|
|
*/
|
|
ALCSPlayer *sound_seqplayer_init(s32 maxVoices, s32 maxEvents) {
|
|
ALCSPlayer *cseqp;
|
|
ALSeqpConfig config;
|
|
|
|
config.maxVoices = maxVoices;
|
|
config.maxEvents = maxEvents;
|
|
config.voiceLimit = maxVoices; // this member doesn't exist in other versions of ALSeqpConfig
|
|
config.maxChannels = AUDIO_CHANNELS;
|
|
config.heap = &gALHeap;
|
|
config.initOsc = NULL;
|
|
config.updateOsc = NULL;
|
|
config.stopOsc = NULL;
|
|
|
|
cseqp = (ALCSPlayer *) alHeapAlloc(&gALHeap, 1, sizeof(ALCSPlayer));
|
|
alCSPNew(cseqp, &config);
|
|
alCSPSetBank(cseqp, gSequenceBank->bankArray[0]);
|
|
cseqp->unk36 = 127;
|
|
|
|
return cseqp;
|
|
}
|
|
|
|
/**
|
|
* Stop the current sequence then set the parameters for the next sequence.
|
|
*/
|
|
void music_sequence_start(u8 seqID, ALCSPlayer *seqPlayer) {
|
|
music_sequence_stop(seqPlayer);
|
|
if (seqID < gSequenceTable->seqCount) {
|
|
if (seqPlayer == gMusicPlayer) {
|
|
gMusicNextSeqID = seqID;
|
|
} else {
|
|
gJingleNextSeqID = seqID;
|
|
}
|
|
} else {
|
|
stubbed_printf("Invalid midi sequence index\n");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If the sequence player is currently inactive, start a new sequence with the current properties.
|
|
*/
|
|
void music_sequence_init(ALCSPlayer *seqp, void *sequence, u8 *seqID, ALCSeq *seq) {
|
|
s32 i;
|
|
|
|
if ((alCSPGetState(seqp) == AL_STOPPED) && (*seqID != 0)) {
|
|
load_asset_to_address(ASSET_AUDIO, (u32) sequence,
|
|
gSequenceTable->seqArray[*seqID].offset - get_rom_offset_of_asset(ASSET_AUDIO, 0),
|
|
(s32) gSeqLengthTable[*seqID]);
|
|
alCSeqNew(seq, sequence);
|
|
alCSPSetSeq(seqp, seq);
|
|
alCSPPlay(seqp);
|
|
if (seqp == gMusicPlayer) {
|
|
music_volume_set(gSeqSoundTable[*seqID].volume);
|
|
if (gSeqSoundTable[*seqID].tempo != 0) {
|
|
music_tempo_set(gSeqSoundTable[*seqID].tempo);
|
|
} else {
|
|
sMusicTempo = -1;
|
|
}
|
|
sound_reverb_set(gSeqSoundTable[*seqID].reverb);
|
|
gCurrentSequenceID = *seqID;
|
|
if (gDynamicMusicChannelMask != MUSIC_CHAN_MASK_NONE) {
|
|
for (i = 0; i < AUDIO_CHANNELS; i++) {
|
|
if ((1 << i) & gDynamicMusicChannelMask) {
|
|
music_channel_on(i);
|
|
} else {
|
|
music_channel_off(i);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
music_jingle_volume_set(gSeqSoundTable[*seqID].volume);
|
|
if (gSeqSoundTable[*seqID].tempo != 0) {
|
|
sound_jingle_tempo_set(gSeqSoundTable[*seqID].tempo);
|
|
}
|
|
gCurrentJingleID = *seqID;
|
|
}
|
|
*seqID = SEQUENCE_NONE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stops the current playing sequence for the given sequence player.
|
|
*/
|
|
void music_sequence_stop(ALCSPlayer *seqPlayer) {
|
|
if (gMusicPlayer == seqPlayer && gMusicPlaying) {
|
|
alCSPStop(seqPlayer);
|
|
gMusicPlaying = FALSE;
|
|
gCurrentSequenceID = SEQUENCE_NONE;
|
|
gMusicNextSeqID = SEQUENCE_NONE;
|
|
} else if (gJinglePlayer == seqPlayer && gJinglePlaying) {
|
|
alCSPStop(seqPlayer);
|
|
gJinglePlaying = FALSE;
|
|
gJingleNextSeqID = SEQUENCE_NONE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Enable or disable special audio effects.
|
|
* This includes reverb and echo.
|
|
*/
|
|
void sound_reverb_set(u8 setting) {
|
|
alFxReverbSet(setting);
|
|
}
|
|
|
|
/**
|
|
* Returns whether or not reverb is currently enabled.
|
|
*/
|
|
UNUSED u8 sound_reverb_enabled(void) {
|
|
return gSeqSoundTable[gCurrentSequenceID].reverb;
|
|
}
|