mirror of https://github.com/pmret/papermario.git
1288 lines
43 KiB
C
1288 lines
43 KiB
C
#include "audio.h"
|
|
#include "audio/core.h"
|
|
#include "ld_addrs.h"
|
|
|
|
AuCallback BeginSoundUpdateCallback;
|
|
BGMPlayer* gBGMPlayerA;
|
|
BGMPlayer* gBGMPlayerB;
|
|
BGMPlayer* gBGMPlayerC;
|
|
SoundManager* gSoundManager;
|
|
AuGlobals* gSoundGlobals;
|
|
AmbienceManager* gAuAmbienceManager;
|
|
|
|
// data
|
|
extern u16 PerceptualVolumeLevels[9];
|
|
extern u8 EnvelopePressDefault[];
|
|
extern u8 EnvelopeReleaseDefault[];
|
|
extern f32 AlTuneScaling[];
|
|
|
|
#ifdef SHIFT
|
|
#define SBN_ROM_OFFSET (s32) audio_ROM_START
|
|
#elif VERSION_JP
|
|
#define SBN_ROM_OFFSET 0xFC0000
|
|
#elif VERSION_PAL
|
|
#define SBN_ROM_OFFSET 0x13A0000
|
|
#else
|
|
#define SBN_ROM_OFFSET 0xF00000
|
|
#endif
|
|
|
|
void au_release_voice(u8 index) {
|
|
AuVoice* voice = &gSoundGlobals->voices[index];
|
|
|
|
voice->cmdPtr = NULL;
|
|
voice->priority = AU_PRIORITY_FREE;
|
|
}
|
|
|
|
void au_engine_init(s32 outputRate) {
|
|
AuGlobals* globals;
|
|
ALHeap* alHeap;
|
|
SBNFileEntry fileEntry;
|
|
s32* dummyTrackData;
|
|
u8 effects[4];
|
|
u32 i;
|
|
|
|
alHeap = gSynDriverPtr->heap;
|
|
gSoundGlobals = alHeapAlloc(alHeap, 1, sizeof(*gSoundGlobals));
|
|
|
|
gBGMPlayerA = alHeapAlloc(alHeap, 1, sizeof(*gBGMPlayerA));
|
|
gBGMPlayerB = alHeapAlloc(alHeap, 1, sizeof(*gBGMPlayerB));
|
|
gBGMPlayerC = alHeapAlloc(alHeap, 1, sizeof(*gBGMPlayerC));
|
|
gSoundManager = alHeapAlloc(alHeap, 1, sizeof(*gSoundManager));
|
|
gAuAmbienceManager = alHeapAlloc(alHeap, 1, sizeof(*gAuAmbienceManager));
|
|
gBGMPlayerA->soundManager = gSoundManager;
|
|
gAuAmbienceManager->globals = gSoundGlobals;
|
|
|
|
globals = gSoundGlobals;
|
|
dummyTrackData = alHeapAlloc(alHeap, 1, 0x8000);
|
|
globals->dataBGM[0] = (BGMHeader*) &dummyTrackData[0];
|
|
globals->dataBGM[1] = (BGMHeader*) &dummyTrackData[0x1400];
|
|
globals->dataMSEQ[0] = (MSEQHeader*) &dummyTrackData[0x1C00];
|
|
globals->dataMSEQ[1] = (MSEQHeader*) &dummyTrackData[0x1400];
|
|
|
|
for (i = 0; i < ARRAY_COUNT(globals->snapshots); i++) {
|
|
globals->snapshots[i].bgmPlayer = alHeapAlloc(alHeap, 1, sizeof(BGMPlayer));
|
|
}
|
|
|
|
globals->dataSEF = alHeapAlloc(alHeap, 1, 0x5200);
|
|
globals->defaultInstrument = alHeapAlloc(alHeap, 1, sizeof(Instrument));
|
|
globals->dataPER = alHeapAlloc(alHeap, 1, 6 * sizeof(PEREntry));
|
|
globals->dataPRG = alHeapAlloc(alHeap, 1, PRG_MAX_COUNT * sizeof(BGMInstrumentInfo));
|
|
globals->musicEventQueue = alHeapAlloc(alHeap, 1, MUS_QUEUE_SIZE * sizeof(MusicEventTrigger));
|
|
globals->outputRate = outputRate;
|
|
au_reset_instrument(globals->defaultInstrument);
|
|
au_reset_drum_entry(&globals->defaultDrumEntry);
|
|
au_reset_instrument_entry(&globals->defaultPRGEntry);
|
|
snd_song_clear_music_events();
|
|
|
|
globals->audioThreadCallbacks[0] = NULL;
|
|
globals->audioThreadCallbacks[1] = NULL;
|
|
|
|
for (i = 0; i < ARRAY_COUNT(globals->snapshots); i++) {
|
|
globals->snapshots[i].assigned = 0;
|
|
globals->snapshots[i].priority = 0;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_COUNT(globals->effectChanges); i++) {
|
|
globals->effectChanges[i].type = AU_FX_NONE;
|
|
globals->effectChanges[i].changed = FALSE;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_COUNT(globals->voices); i++) {
|
|
AuVoice* voice;
|
|
au_pvoice_set_bus(i, FX_BUS_BGMA_MAIN);
|
|
au_syn_set_wavetable(i, globals->defaultInstrument);
|
|
voice = &globals->voices[i];
|
|
voice->instrument = NULL;
|
|
voice->pitchRatio = 0;
|
|
voice->volume = -1;
|
|
voice->pan = 0xFF;
|
|
voice->reverb = 0xFF;
|
|
voice->busID = 0;
|
|
voice->donePending = FALSE;
|
|
voice->syncFlags = 0;
|
|
voice->clientPriority = AU_PRIORITY_FREE;
|
|
voice->priority = AU_PRIORITY_FREE;
|
|
}
|
|
|
|
au_load_INIT(globals, SBN_ROM_OFFSET, alHeap);
|
|
|
|
for (i = 0; i < ARRAY_COUNT(globals->auxBanks); i++) {
|
|
globals->auxBanks[i] = alHeapAlloc(alHeap, 1, sizeof(BKFileBuffer));
|
|
}
|
|
|
|
au_bgm_player_init(gBGMPlayerA, AU_PRIORITY_BGM_PLAYER_MAIN, FX_BUS_BGMA_MAIN, globals);
|
|
effects[0] = FX_BUS_BGMA_MAIN;
|
|
effects[1] = FX_BUS_BGMA_AUX;
|
|
effects[2] = -1;
|
|
effects[3] = -1;
|
|
au_bgm_set_effect_indices(gBGMPlayerA, effects);
|
|
|
|
au_bgm_player_init(gBGMPlayerB, AU_PRIORITY_BGM_PLAYER_AUX, FX_BUS_BGMB, globals);
|
|
effects[0] = FX_BUS_BGMB;
|
|
effects[1] = -1;
|
|
effects[2] = -1;
|
|
effects[3] = -1;
|
|
au_bgm_set_effect_indices(gBGMPlayerB, effects);
|
|
|
|
au_sfx_init(gSoundManager, AU_PRIORITY_SFX_MANAGER, FX_BUS_SOUND, globals, 16);
|
|
au_mseq_manager_init(gAuAmbienceManager, AU_PRIORITY_MSEQ_MANAGER, FX_BUS_SOUND, globals);
|
|
au_init_voices(globals);
|
|
au_load_BK_headers(globals, alHeap);
|
|
if (au_fetch_SBN_file(globals->extraFileList[0], AU_FMT_SEF, &fileEntry) == AU_RESULT_OK) {
|
|
au_read_rom(fileEntry.offset, globals->dataSEF, fileEntry.data & 0xFFFFFF);
|
|
}
|
|
au_sfx_load_groups_from_SEF(gSoundManager);
|
|
if (au_fetch_SBN_file(globals->extraFileList[1], AU_FMT_PER, &fileEntry) == AU_RESULT_OK) {
|
|
au_load_PER(globals, fileEntry.offset);
|
|
}
|
|
if (au_fetch_SBN_file(globals->extraFileList[2], AU_FMT_PRG, &fileEntry) == AU_RESULT_OK) {
|
|
au_load_PRG(globals, fileEntry.offset);
|
|
}
|
|
|
|
globals->bankSets[BANK_SET_IDX_0] = globals->auxBankSet;
|
|
globals->bankSets[BANK_SET_IDX_1] = globals->bankSet2;
|
|
globals->bankSets[BANK_SET_IDX_2] = globals->defaultBankSet;
|
|
globals->bankSets[BANK_SET_IDX_3] = globals->musicBankSet;
|
|
globals->bankSets[BANK_SET_IDX_4] = globals->bankSet4;
|
|
globals->bankSets[BANK_SET_IDX_5] = globals->bankSet5;
|
|
globals->bankSets[BANK_SET_IDX_6] = globals->bankSet6;
|
|
globals->bankSets[BANK_SET_IDX_7] = globals->auxBankSet;
|
|
|
|
globals->channelDelaySide = AU_DELAY_CHANNEL_NONE;
|
|
globals->channelDelayTime = 0;
|
|
globals->channelDelayBusID = 0;
|
|
globals->channelDelayPending = FALSE;
|
|
|
|
au_init_delay_channel(0);
|
|
snd_notify_engine_ready(alHeap);
|
|
}
|
|
|
|
/// used to initialize the default Instrument
|
|
static void au_reset_instrument(Instrument* instrument) {
|
|
instrument->wavData = DummyInstrumentWavData;
|
|
instrument->wavDataLength = sizeof(DummyInstrumentWavData);
|
|
instrument->predictor = DummyInstrumentCodebook;
|
|
instrument->codebookSize = sizeof(DummyInstrumentCodebook);
|
|
instrument->keyBase = DEFAULT_KEYBASE;
|
|
instrument->loopState = NULL;
|
|
instrument->loopStart = 0;
|
|
instrument->loopEnd = 0;
|
|
instrument->loopCount = 0;
|
|
instrument->type = 0;
|
|
instrument->useDma = FALSE;
|
|
instrument->envelopes = &DummyInstrumentEnvelope;
|
|
instrument->unused_26 = 0;
|
|
instrument->unused_27 = 0;
|
|
instrument->unused_28 = 0;
|
|
instrument->unused_29 = 0;
|
|
instrument->unused_2A = 0;
|
|
instrument->unused_2B = 0;
|
|
instrument->pitchRatio = 0.5f;
|
|
}
|
|
|
|
/// used to initialize the default BGMDrumInfo
|
|
static void au_reset_drum_entry(BGMDrumInfo* info) {
|
|
// @bug index 0x10 will overflow defaultBankSet and choose first instrument from musicBankSet instead?
|
|
info->bankPatch = (BANK_SET_IDX_2 << 12) | 0x10;
|
|
info->keyBase = DEFAULT_KEYBASE;
|
|
info->volume = AU_MAX_VOLUME_8;
|
|
info->pan = 64;
|
|
info->reverb = 0;
|
|
info->randTune = 0;
|
|
info->randVolume = 0;
|
|
info->randPan = 0;
|
|
info->randReverb = 0;
|
|
}
|
|
|
|
/// used to initialize the default BGMInstrumentInfo
|
|
static void au_reset_instrument_entry(BGMInstrumentInfo* info) {
|
|
// @bug index 0x10 will overflow defaultBankSet and choose first instrument from musicBankSet instead?
|
|
info->bankPatch = (BANK_SET_IDX_2 << 12) | 0x10;
|
|
info->volume = AU_MAX_VOLUME_8;
|
|
info->pan = 64;
|
|
info->reverb = 0;
|
|
info->coarseTune = 0;
|
|
info->fineTune = 0;
|
|
}
|
|
|
|
/// Called exactly once per audio frame (every 5.75ms at 32kHz).
|
|
/// Updates MSEQ, SFX, and BGM players for the current audio frame.
|
|
void au_update_clients_for_audio_frame(void) {
|
|
AuGlobals* globals = gSoundGlobals;
|
|
SoundManager* sfxManager = gSoundManager;
|
|
AmbienceManager* ambManager = gAuAmbienceManager;
|
|
BGMPlayer* bgmPlayer;
|
|
|
|
au_syn_begin_audio_frame(globals);
|
|
|
|
// Update ambience manager every other frame
|
|
ambManager->nextUpdateCounter -= ambManager->nextUpdateStep;
|
|
if (ambManager->nextUpdateCounter <= 0) {
|
|
ambManager->nextUpdateCounter += ambManager->nextUpdateInterval;
|
|
au_mseq_manager_audio_frame_update(ambManager);
|
|
}
|
|
|
|
// Update volume fade for SFX bus
|
|
if (sfxManager->fadeInfo.baseTicks != 0) {
|
|
au_fade_update(&sfxManager->fadeInfo);
|
|
au_fade_set_volume(sfxManager->busID, sfxManager->fadeInfo.baseVolume >> 16, sfxManager->busVolume);
|
|
}
|
|
|
|
// Periodic SFX manager update
|
|
sfxManager->nextUpdateCounter -= sfxManager->nextUpdateStep;
|
|
if (sfxManager->nextUpdateCounter <= 0) {
|
|
sfxManager->nextUpdateCounter += sfxManager->nextUpdateInterval;
|
|
sfxManager->prevUpdateResult = au_sfx_manager_audio_frame_update(sfxManager);
|
|
}
|
|
|
|
// Update gBGMPlayerB
|
|
if (!PreventBGMPlayerUpdate) {
|
|
bgmPlayer = gBGMPlayerB;
|
|
if (bgmPlayer->fadeInfo.baseTicks != 0) {
|
|
au_bgm_update_fade(bgmPlayer);
|
|
}
|
|
if (bgmPlayer->songName != 0) {
|
|
bgmPlayer->songPlayingCounter++;
|
|
}
|
|
|
|
bgmPlayer->nextUpdateCounter -= bgmPlayer->nextUpdateStep;
|
|
if (bgmPlayer->nextUpdateCounter <= 0) {
|
|
bgmPlayer->nextUpdateCounter += bgmPlayer->tickUpdateInterval;
|
|
bgmPlayer->prevUpdateResult = au_bgm_player_audio_frame_update(bgmPlayer);
|
|
}
|
|
}
|
|
|
|
// Update gBGMPlayerA
|
|
if (!PreventBGMPlayerUpdate) {
|
|
if (globals->resumeRequested) {
|
|
au_bgm_restore_copied_player(globals);
|
|
}
|
|
bgmPlayer = gBGMPlayerA;
|
|
if (bgmPlayer->fadeInfo.envelopeTicks != 0) {
|
|
au_fade_update_envelope(&bgmPlayer->fadeInfo);
|
|
if (bgmPlayer->fadeInfo.baseTicks == 0) {
|
|
au_bgm_update_bus_volumes(bgmPlayer);
|
|
} else {
|
|
au_bgm_update_fade(bgmPlayer);
|
|
}
|
|
} else if (bgmPlayer->fadeInfo.baseTicks != 0) {
|
|
au_bgm_update_fade(bgmPlayer);
|
|
}
|
|
if (bgmPlayer->songName != 0) {
|
|
bgmPlayer->songPlayingCounter++;
|
|
}
|
|
|
|
bgmPlayer->nextUpdateCounter -= bgmPlayer->nextUpdateStep;
|
|
if (bgmPlayer->nextUpdateCounter <= 0) {
|
|
bgmPlayer->nextUpdateCounter += bgmPlayer->tickUpdateInterval;
|
|
bgmPlayer->prevUpdateResult = au_bgm_player_audio_frame_update(bgmPlayer);
|
|
}
|
|
}
|
|
|
|
// With all clients updated, now update all voices
|
|
au_update_voices(globals);
|
|
}
|
|
|
|
void au_update_clients_for_video_frame(void) {
|
|
AuGlobals* globals = gSoundGlobals;
|
|
BGMPlayer* player = gBGMPlayerA;
|
|
SoundManager* manager = gSoundManager;
|
|
|
|
if (globals->flushMusicEventQueue) {
|
|
snd_song_clear_music_events();
|
|
}
|
|
|
|
BeginSoundUpdateCallback = globals->audioThreadCallbacks[0];
|
|
if (BeginSoundUpdateCallback != NULL) {
|
|
BeginSoundUpdateCallback();
|
|
}
|
|
|
|
au_bgm_begin_video_frame(player);
|
|
|
|
player = gBGMPlayerB;
|
|
au_bgm_begin_video_frame(player);
|
|
|
|
au_sfx_begin_video_frame(manager);
|
|
}
|
|
|
|
void au_syn_begin_audio_frame(AuGlobals* globals) {
|
|
u32 i;
|
|
|
|
if (globals->channelDelayState == AU_DELAY_STATE_REQUEST_OFF) {
|
|
globals->channelDelayState = AU_DELAY_STATE_OFF;
|
|
au_disable_channel_delay();
|
|
}
|
|
|
|
if (globals->channelDelayPending && (globals->channelDelayState == AU_DELAY_STATE_ON)) {
|
|
switch (globals->channelDelaySide) {
|
|
case AU_DELAY_CHANNEL_LEFT:
|
|
au_set_delay_time(globals->channelDelayTime);
|
|
au_delay_left_channel(globals->channelDelayBusID);
|
|
globals->channelDelayPending = FALSE;
|
|
break;
|
|
case AU_DELAY_CHANNEL_RIGHT:
|
|
au_set_delay_time(globals->channelDelayTime);
|
|
au_delay_right_channel(globals->channelDelayBusID);
|
|
globals->channelDelayPending = FALSE;
|
|
break;
|
|
default:
|
|
au_disable_channel_delay();
|
|
globals->channelDelayPending = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// handle effect bus changes
|
|
if (globals->effectChanges[FX_BUS_BGMA_MAIN].changed) {
|
|
au_bus_set_effect(FX_BUS_BGMA_MAIN, globals->effectChanges[FX_BUS_BGMA_MAIN].type);
|
|
globals->effectChanges[FX_BUS_BGMA_MAIN].changed = FALSE;
|
|
}
|
|
if (globals->effectChanges[FX_BUS_SOUND].changed) {
|
|
au_bus_set_effect(FX_BUS_SOUND, globals->effectChanges[FX_BUS_SOUND].type);
|
|
globals->effectChanges[FX_BUS_SOUND].changed = FALSE;
|
|
|
|
} if (globals->effectChanges[FX_BUS_BGMB].changed) {
|
|
au_bus_set_effect(FX_BUS_BGMB, globals->effectChanges[FX_BUS_BGMB].type);
|
|
globals->effectChanges[FX_BUS_BGMB].changed = FALSE;
|
|
}
|
|
if (globals->effectChanges[FX_BUS_BGMA_AUX].changed) {
|
|
au_bus_set_effect(FX_BUS_BGMA_AUX, globals->effectChanges[FX_BUS_BGMA_AUX].type);
|
|
globals->effectChanges[FX_BUS_BGMA_AUX].changed = FALSE;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_COUNT(globals->voices); i++) {
|
|
AuVoice* voice = &globals->voices[i];
|
|
u8 voiceUpdateFlags = voice->syncFlags;
|
|
|
|
if (voice->donePending) {
|
|
au_syn_stop_voice(i);
|
|
voice->donePending = FALSE;
|
|
voice->cmdPtr = NULL;
|
|
voice->priority = AU_PRIORITY_FREE;
|
|
}
|
|
|
|
if (voiceUpdateFlags & AU_VOICE_SYNC_FLAG_ALL) {
|
|
au_voice_start(voice, &voice->envelope);
|
|
au_syn_start_voice_params(i, voice->busID, voice->instrument, voice->pitchRatio, voice->volume, voice->pan, voice->reverb, voice->delta);
|
|
// priority may be AU_PRIORITY_FREE if this voice was stolen and reset
|
|
voice->priority = voice->clientPriority;
|
|
} else {
|
|
if (voiceUpdateFlags & AU_VOICE_SYNC_FLAG_PITCH) {
|
|
au_syn_set_pitch(i, voice->pitchRatio);
|
|
}
|
|
|
|
if (voiceUpdateFlags & AU_VOICE_SYNC_FLAG_PARAMS) {
|
|
au_syn_set_mixer_params(i, voice->volume, voice->delta, voice->pan, voice->reverb);
|
|
} else if (voiceUpdateFlags & AU_VOICE_SYNC_FLAG_PAN_FXMIX) {
|
|
au_syn_set_pan_fxmix(i, voice->pan, voice->reverb);
|
|
}
|
|
}
|
|
voice->syncFlags = 0;
|
|
}
|
|
}
|
|
|
|
void au_reset_nonfree_voice(AuVoice* voice, u8 index) {
|
|
if (voice->priority != AU_PRIORITY_FREE) {
|
|
voice->cmdPtr = NULL;
|
|
voice->donePending = TRUE;
|
|
voice->syncFlags = 0;
|
|
au_syn_set_volume_delta(index, 0, AUDIO_SAMPLES);
|
|
}
|
|
}
|
|
|
|
void au_reset_voice(AuVoice* voice, u8 voiceIdx) {
|
|
voice->cmdPtr = NULL;
|
|
voice->donePending = TRUE;
|
|
voice->syncFlags = 0;
|
|
au_syn_set_volume_delta(voiceIdx, 0, AUDIO_SAMPLES);
|
|
}
|
|
|
|
// array offsets into AlTuneScaling
|
|
#define TUNE_SCALING_ARR_AMPLIFY_FINE 0
|
|
#define TUNE_SCALING_ARR_AMPLIFY_COARSE 128
|
|
#define TUNE_SCALING_ARR_ATTENUATE_FINE 160
|
|
#define TUNE_SCALING_ARR_ATTENUATE_COARSE 288
|
|
|
|
f32 au_compute_pitch_ratio(s32 tuning) {
|
|
if (tuning >= 0) {
|
|
return AlTuneScaling[(tuning & 0x7F) + TUNE_SCALING_ARR_AMPLIFY_FINE]
|
|
* AlTuneScaling[((tuning & 0xF80) >> 7) + TUNE_SCALING_ARR_AMPLIFY_COARSE];
|
|
} else {
|
|
tuning = -tuning;
|
|
return AlTuneScaling[(tuning & 0x7F) + TUNE_SCALING_ARR_ATTENUATE_FINE]
|
|
* AlTuneScaling[((tuning & 0x3F80) >> 7) + TUNE_SCALING_ARR_ATTENUATE_COARSE];
|
|
}
|
|
}
|
|
|
|
void au_fade_init(Fade* fade, s32 time, s32 startValue, s32 endValue) {
|
|
fade->baseVolume = startValue << 16;
|
|
fade->baseTarget = endValue;
|
|
|
|
if (time != 0) {
|
|
fade->baseTicks = (time * 1000) / AU_FRAME_USEC;
|
|
fade->baseStep = ((endValue << 16) - fade->baseVolume) / fade->baseTicks;
|
|
} else {
|
|
fade->baseTicks = 1;
|
|
fade->baseStep = 0;
|
|
}
|
|
|
|
fade->onCompleteCallback = NULL;
|
|
}
|
|
|
|
void au_fade_clear(Fade* fade) {
|
|
fade->baseTicks = 0;
|
|
fade->baseStep = 0;
|
|
fade->onCompleteCallback = NULL;
|
|
}
|
|
|
|
void au_fade_update(Fade* fade) {
|
|
fade->baseTicks--;
|
|
|
|
if ((fade->baseTicks << 16) != 0) {
|
|
fade->baseVolume += fade->baseStep;
|
|
} else {
|
|
fade->baseVolume = fade->baseTarget << 16;
|
|
if (fade->onCompleteCallback != NULL) {
|
|
fade->onCompleteCallback();
|
|
fade->baseStep = 0;
|
|
fade->onCompleteCallback = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
void au_fade_set_volume(u8 busID, u16 volume, s32 busVolume) {
|
|
au_bus_set_volume(busID, (u32)(volume * busVolume) / AU_MAX_BUS_VOLUME);
|
|
}
|
|
|
|
void au_fade_flush(Fade* fade) {
|
|
if (fade->baseTicks == 0) {
|
|
fade->baseTicks = 1;
|
|
fade->baseStep = 0;
|
|
fade->baseTarget = ((u32)fade->baseVolume >> 16);
|
|
}
|
|
}
|
|
|
|
void au_fade_set_envelope(Fade* fade, s16 value) {
|
|
fade->envelopeVolume = value << 16;
|
|
fade->envelopeTarget = value;
|
|
fade->envelopeTicks = 0;
|
|
fade->envelopeStep = 0;
|
|
}
|
|
|
|
void au_fade_calc_envelope(Fade* fade, u32 duration, s32 target) {
|
|
s16 ticks;
|
|
s32 delta;
|
|
|
|
if (duration >= 250 && duration <= 100000) {
|
|
ticks = (s32)(duration * 1000) / AU_FRAME_USEC;
|
|
delta = (target << 16) - fade->envelopeVolume;
|
|
|
|
fade->envelopeTarget = target;
|
|
fade->envelopeTicks = ticks;
|
|
fade->envelopeStep = delta / ticks;
|
|
} else {
|
|
fade->envelopeTicks = 0;
|
|
fade->envelopeStep = 0;
|
|
}
|
|
}
|
|
|
|
void au_fade_update_envelope(Fade* fade) {
|
|
fade->envelopeTicks--;
|
|
|
|
if (fade->envelopeTicks != 0) {
|
|
fade->envelopeVolume += fade->envelopeStep;
|
|
} else {
|
|
fade->envelopeStep = 0;
|
|
fade->envelopeVolume = fade->envelopeTarget << 16;
|
|
}
|
|
}
|
|
|
|
/// Note that bank is supplied as BankSetIndex and not BankSet, which means it will be used to perform a raw
|
|
/// access into AuGlobals::bankSets. This does not affect values above 3, but 1 and 2 differ.
|
|
Instrument* au_get_instrument(AuGlobals* globals, BankSetIndex bank, s32 patch, EnvelopeData* envData) {
|
|
// note that patch here can be up to 255, selecting from a maximum of 16 instruments and 16 banks per group
|
|
Instrument* instrument = (*globals->bankSets[(bank & 0x70) >> 4])[patch];
|
|
EnvelopePreset* envelope = instrument->envelopes;
|
|
u32 envelopeIdx = bank & 3;
|
|
|
|
if (envelopeIdx < envelope->count) {
|
|
envData->cmdListPress = AU_FILE_RELATIVE(envelope, envelope->offsets[envelopeIdx].offsetPress);
|
|
envData->cmdListRelease = AU_FILE_RELATIVE(envelope, envelope->offsets[envelopeIdx].offsetRelease);
|
|
} else {
|
|
envData->cmdListPress = EnvelopePressDefault;
|
|
envData->cmdListRelease = &EnvelopePressDefault[4]; //EnvelopeReleaseDefault;
|
|
}
|
|
return instrument;
|
|
}
|
|
|
|
void au_get_bgm_player_and_file(u32 playerIndex, BGMHeader** outFile, BGMPlayer** outPlayer) {
|
|
AuGlobals* globals = gSoundGlobals;
|
|
|
|
switch (playerIndex) {
|
|
case 0:
|
|
*outFile = globals->dataBGM[0];
|
|
*outPlayer = gBGMPlayerA;
|
|
break;
|
|
case 1:
|
|
*outFile = globals->dataBGM[1];
|
|
*outPlayer = gBGMPlayerB;
|
|
break;
|
|
case 2:
|
|
*outFile = globals->dataBGM[0];
|
|
*outPlayer = gBGMPlayerA;
|
|
break;
|
|
default:
|
|
*outFile = NULL;
|
|
*outPlayer = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void au_get_bgm_player(u32 playerIndex, BGMPlayer** outPlayer) {
|
|
switch (playerIndex) {
|
|
case 0:
|
|
*outPlayer = gBGMPlayerA;
|
|
break;
|
|
case 1:
|
|
*outPlayer = gBGMPlayerB;
|
|
break;
|
|
case 2:
|
|
*outPlayer = gBGMPlayerA;
|
|
break;
|
|
default:
|
|
*outPlayer = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
AuResult au_load_song_files(u32 songID, BGMHeader* bgmFile, BGMPlayer* player) {
|
|
AuResult status;
|
|
SBNFileEntry fileEntry;
|
|
SBNFileEntry fileEntry2;
|
|
SBNFileEntry* bkFileEntry;
|
|
AuGlobals* globals = gSoundGlobals;
|
|
InitSongEntry* songInfo;
|
|
s32 i;
|
|
u16 bkFileIndex;
|
|
s32 bgmFileIndex;
|
|
u32 data;
|
|
u32 offset;
|
|
BGMPlayer* playerCopy;
|
|
BGMHeader* fileCopy;
|
|
s32 cond;
|
|
|
|
// needed to match
|
|
cond = songID < globals->songListLength;
|
|
playerCopy = player;
|
|
fileCopy = bgmFile;
|
|
|
|
if (cond) {
|
|
songInfo = &globals->songList[songID];
|
|
status = au_fetch_SBN_file(songInfo->bgmFileIndex, AU_FMT_BGM, &fileEntry);
|
|
if (status != AU_RESULT_OK) {
|
|
return status;
|
|
}
|
|
|
|
if (au_bgm_player_is_active(playerCopy)) {
|
|
return AU_ERROR_201;
|
|
}
|
|
|
|
au_read_rom(fileEntry.offset, fileCopy, fileEntry.data & 0xFFFFFF);
|
|
|
|
for (i = 0; i < ARRAY_COUNT(songInfo->bkFileIndex); i++) {
|
|
bkFileIndex = songInfo->bkFileIndex[i];
|
|
if (bkFileIndex != 0) {
|
|
bkFileEntry = &globals->sbnFileList[bkFileIndex];
|
|
|
|
offset = (bkFileEntry->offset & 0xFFFFFF) + globals->baseRomOffset;
|
|
fileEntry2.offset = offset;
|
|
|
|
data = bkFileEntry->data;
|
|
fileEntry2.data = data;
|
|
|
|
if ((data >> 0x18) == AU_FMT_BK) {
|
|
au_load_aux_bank(offset, i);
|
|
}
|
|
}
|
|
}
|
|
bgmFileIndex = songInfo->bgmFileIndex;
|
|
playerCopy->songID = songID;
|
|
playerCopy->bgmFile = bgmFile;
|
|
playerCopy->bgmFileIndex = bgmFileIndex;
|
|
return fileCopy->name;
|
|
} else {
|
|
return AU_ERROR_151;
|
|
}
|
|
}
|
|
|
|
AuResult au_reload_song_files(s32 songID, BGMHeader* bgmFile) {
|
|
AuResult status;
|
|
SBNFileEntry fileEntry;
|
|
SBNFileEntry sbnEntry;
|
|
SBNFileEntry* bkFileEntry;
|
|
AuGlobals* globals;
|
|
InitSongEntry* songInfo;
|
|
s32 i;
|
|
u16 bkFileIndex;
|
|
|
|
globals = gSoundGlobals;
|
|
songInfo = &globals->songList[songID];
|
|
status = au_fetch_SBN_file(songInfo->bgmFileIndex, AU_FMT_BGM, &sbnEntry);
|
|
if (status == AU_RESULT_OK) {
|
|
// load BGM file
|
|
au_read_rom(sbnEntry.offset, bgmFile, sbnEntry.data & 0xFFFFFF);
|
|
|
|
// load any auxiliary banks required by this BGM
|
|
for (i = 0; i < ARRAY_COUNT(songInfo->bkFileIndex); i++) {
|
|
bkFileIndex = songInfo->bkFileIndex[i];
|
|
if (bkFileIndex != 0) {
|
|
bkFileEntry = &globals->sbnFileList[bkFileIndex];
|
|
|
|
fileEntry.offset = (bkFileEntry->offset & 0xFFFFFF) + globals->baseRomOffset;
|
|
fileEntry.data = bkFileEntry->data;
|
|
|
|
if ((fileEntry.data >> 0x18) == AU_FMT_BK) {
|
|
au_load_aux_bank(fileEntry.offset, i);
|
|
} else {
|
|
status = AU_ERROR_SBN_FORMAT_MISMATCH;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
BGMPlayer* au_get_snapshot_by_index(s32 index) {
|
|
if (index == BGM_SNAPSHOT_0) {
|
|
return gSoundGlobals->snapshots[BGM_SNAPSHOT_0].bgmPlayer;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
#define SBN_EXTRA_LOOKUP(i,fmt,e) (au_fetch_SBN_file(globals->extraFileList[AmbientSoundIDtoMSEQFileIndex[i]], fmt, &e))
|
|
|
|
AuResult au_ambient_load(u32 ambSoundID) {
|
|
AmbienceManager* manager;
|
|
SBNFileEntry fileEntry;
|
|
AuGlobals* globals;
|
|
MSEQHeader* mseqFile;
|
|
u32 i;
|
|
|
|
globals = gSoundGlobals;
|
|
manager = gAuAmbienceManager;
|
|
if (ambSoundID < AMBIENT_RADIO) {
|
|
if (manager->players[0].mseqName == 0) {
|
|
if (SBN_EXTRA_LOOKUP(ambSoundID, AU_FMT_MSEQ, fileEntry) == AU_RESULT_OK) {
|
|
au_read_rom(fileEntry.offset, globals->dataMSEQ[0], fileEntry.data & 0xFFFFFF);
|
|
manager->mseqFiles[0] = globals->dataMSEQ[0];
|
|
for (i = 1; i < ARRAY_COUNT(manager->mseqFiles); i++) {
|
|
manager->mseqFiles[i] = NULL;
|
|
}
|
|
manager->numActivePlayers = 1;
|
|
}
|
|
}
|
|
} else if (ambSoundID == AMBIENT_RADIO
|
|
&& manager->players[0].mseqName == 0
|
|
&& manager->players[1].mseqName == 0
|
|
&& manager->players[2].mseqName == 0
|
|
) {
|
|
manager->numActivePlayers = 0;
|
|
for (i = 0; i < ARRAY_COUNT(manager->mseqFiles); i++) {
|
|
manager->mseqFiles[i] = NULL;
|
|
}
|
|
|
|
mseqFile = globals->dataMSEQ[1];
|
|
if (SBN_EXTRA_LOOKUP(ambSoundID, AU_FMT_MSEQ, fileEntry) == AU_RESULT_OK) {
|
|
au_read_rom(fileEntry.offset, mseqFile, fileEntry.data & 0xFFFFFF);
|
|
manager->mseqFiles[0] = mseqFile;
|
|
|
|
mseqFile = AU_FILE_RELATIVE(mseqFile, (fileEntry.data + 0x40) & 0xFFFFFF);
|
|
if (SBN_EXTRA_LOOKUP(ambSoundID + 1, AU_FMT_MSEQ, fileEntry) == AU_RESULT_OK) {
|
|
au_read_rom(fileEntry.offset, mseqFile, fileEntry.data & 0xFFFFFF);
|
|
manager->mseqFiles[1] = mseqFile;
|
|
|
|
mseqFile = AU_FILE_RELATIVE(mseqFile, (fileEntry.data + 0x40) & 0xFFFFFF);
|
|
if (SBN_EXTRA_LOOKUP(ambSoundID + 2, AU_FMT_MSEQ, fileEntry) == AU_RESULT_OK) {
|
|
au_read_rom(fileEntry.offset, mseqFile, fileEntry.data & 0xFFFFFF);
|
|
manager->mseqFiles[2] = mseqFile;
|
|
|
|
mseqFile = AU_FILE_RELATIVE(mseqFile, (fileEntry.data + 0x40) & 0xFFFFFF);
|
|
if (SBN_EXTRA_LOOKUP(ambSoundID + 3, AU_FMT_MSEQ, fileEntry) == AU_RESULT_OK) {
|
|
au_read_rom(fileEntry.offset, mseqFile, fileEntry.data & 0xFFFFFF);
|
|
manager->mseqFiles[3] = mseqFile;
|
|
|
|
manager->numActivePlayers = 4;
|
|
if (SBN_EXTRA_LOOKUP(ambSoundID + 4, AU_FMT_BK, fileEntry) == AU_RESULT_OK) {
|
|
// @bug perhaps meant to be 3?
|
|
// the index here corresponds to an entry in gSoundGlobals->banks
|
|
// 0-2 are used for the extra banks which may be loaded for BGM files
|
|
// there exists an unused 4th entry this could plausibly be intended for
|
|
au_load_aux_bank(fileEntry.offset, 2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return AU_RESULT_OK;
|
|
}
|
|
|
|
BGMPlayer* au_get_client_by_priority(u8 priority) {
|
|
switch (priority) {
|
|
case AU_PRIORITY_BGM_PLAYER_MAIN:
|
|
return gBGMPlayerA;
|
|
case AU_PRIORITY_BGM_PLAYER_AUX:
|
|
return gBGMPlayerB;
|
|
case AU_PRIORITY_SFX_MANAGER:
|
|
return (BGMPlayer*)gSoundManager; // TODO: why return pointer to SoundManager?
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
void au_load_INIT(AuGlobals* globals, s32 romAddr, ALHeap* heap) {
|
|
SBNHeader sbnHeader;
|
|
INITHeader initHeader;
|
|
SBNFileEntry* entry;
|
|
s32 fileListSize, initBase, size;
|
|
s32 songListOffset, mseqListOffset;
|
|
s32* data;
|
|
s32 numEntries;
|
|
s32* romPtr = &globals->baseRomOffset;
|
|
|
|
au_read_rom(romAddr, &sbnHeader, sizeof(sbnHeader));
|
|
numEntries = sbnHeader.numEntries;
|
|
fileListSize = numEntries * sizeof(SBNFileEntry);
|
|
globals->baseRomOffset = romAddr;
|
|
globals->fileListLength = sbnHeader.numEntries;
|
|
globals->sbnFileList = alHeapAlloc(heap, 1, fileListSize);
|
|
au_read_rom(globals->baseRomOffset + sbnHeader.fileListOffset, globals->sbnFileList, fileListSize);
|
|
|
|
entry = globals->sbnFileList;
|
|
while (sbnHeader.numEntries--) {
|
|
if ((entry->offset & 0xFFFFFF) == 0) {
|
|
break;
|
|
}
|
|
|
|
// 16-byte align size
|
|
size = entry->data;
|
|
entry->data = (entry->data + 0xF) & ~0xF;
|
|
entry++;
|
|
}
|
|
|
|
if (sbnHeader.INIToffset != 0) {
|
|
initBase = *romPtr + sbnHeader.INIToffset;
|
|
au_read_rom(initBase, &initHeader, sizeof(initHeader));
|
|
|
|
songListOffset = initBase + initHeader.songListOffset;
|
|
size = ALIGN16_(initHeader.songListSize);
|
|
globals->songList = alHeapAlloc(heap, 1, size);
|
|
au_read_rom(songListOffset, globals->songList, size);
|
|
|
|
mseqListOffset = initBase + initHeader.mseqListOffset;
|
|
size = ALIGN16_(initHeader.mseqListSize);
|
|
globals->extraFileList = alHeapAlloc(heap, 1, size);
|
|
au_read_rom(mseqListOffset, globals->extraFileList, size);
|
|
|
|
globals->bkFileListOffset = initBase + initHeader.bankListOffset;
|
|
globals->bkListLength = ALIGN16_(initHeader.bankListSize);
|
|
|
|
globals->songListLength = initHeader.songListSize / sizeof(InitSongEntry) - 1;
|
|
}
|
|
}
|
|
|
|
AuResult au_fetch_SBN_file(u32 fileIdx, AuFileFormat format, SBNFileEntry* outEntry) {
|
|
SBNFileEntry fileEntry;
|
|
s32 status = AU_RESULT_OK;
|
|
|
|
if (fileIdx < gSoundGlobals->fileListLength) {
|
|
SBNFileEntry* entry = &gSoundGlobals->sbnFileList[fileIdx];
|
|
s32 offset = (entry->offset & 0xFFFFFF) + gSoundGlobals->baseRomOffset;
|
|
|
|
fileEntry.offset = offset;
|
|
fileEntry.data = entry->data;
|
|
if ((fileEntry.data >> 0x18) == format) {
|
|
outEntry->offset = offset;
|
|
outEntry->data = fileEntry.data;
|
|
} else {
|
|
status = AU_ERROR_SBN_FORMAT_MISMATCH;
|
|
}
|
|
} else {
|
|
status = AU_ERROR_SBN_INDEX_OUT_OF_RANGE;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
void au_load_PER(AuGlobals* globals, s32 romAddr) {
|
|
PERHeader header;
|
|
u32 size;
|
|
s32 numItemsLeft;
|
|
s32 numItems;
|
|
void* end;
|
|
|
|
au_read_rom(romAddr, &header, sizeof(header));
|
|
size = header.mdata.size - sizeof(header);
|
|
au_read_rom(romAddr + sizeof(header), globals->dataPER, size);
|
|
numItems = size / sizeof(PEREntry);
|
|
numItemsLeft = 6 - numItems;
|
|
if (numItemsLeft > 0) {
|
|
end = &globals->dataPER[numItems];
|
|
au_copy_words(&globals->defaultDrumEntry, end, sizeof(BGMDrumInfo));
|
|
au_copy_words(end, end + sizeof(BGMDrumInfo), numItemsLeft * sizeof(PEREntry) - sizeof(BGMDrumInfo));
|
|
}
|
|
}
|
|
|
|
void au_load_PRG(AuGlobals* globals, s32 romAddr) {
|
|
PERHeader header;
|
|
u32 size;
|
|
s32 numItemsLeft;
|
|
s32 numItems;
|
|
s32 dataRomAddr;
|
|
void* end;
|
|
|
|
au_read_rom(romAddr, &header, sizeof(header));
|
|
dataRomAddr = romAddr + sizeof(header);
|
|
size = header.mdata.size - sizeof(header);
|
|
if (size > PRG_MAX_COUNT * sizeof(BGMInstrumentInfo)) {
|
|
size = PRG_MAX_COUNT * sizeof(BGMInstrumentInfo);
|
|
}
|
|
au_read_rom(dataRomAddr, globals->dataPRG, size);
|
|
numItems = size / sizeof(BGMInstrumentInfo);
|
|
numItemsLeft = PRG_MAX_COUNT - numItems;
|
|
if (numItemsLeft > 0) {
|
|
end = &globals->dataPRG[numItems];
|
|
au_copy_words(&globals->defaultPRGEntry, end, sizeof(BGMInstrumentInfo));
|
|
au_copy_words(end, end + sizeof(BGMInstrumentInfo), numItemsLeft * sizeof(BGMInstrumentInfo) - sizeof(BGMInstrumentInfo));
|
|
}
|
|
}
|
|
|
|
s32 au_load_BGM(s32 arg0) {
|
|
AuGlobals* globals = gSoundGlobals;
|
|
InitSongEntry* song = globals->songList;
|
|
s32 ret = AU_RESULT_OK;
|
|
s32 i;
|
|
|
|
while (TRUE) {
|
|
if (song->bgmFileIndex == 0xFFFF) {
|
|
return ret;
|
|
}
|
|
|
|
if (song->bgmFileIndex == arg0) {
|
|
for (i = 0; i < ARRAY_COUNT(song->bkFileIndex); i++) {
|
|
u16 bkFileIndex = song->bkFileIndex[i];
|
|
if (bkFileIndex != 0) {
|
|
SBNFileEntry* bkFileEntry = &globals->sbnFileList[bkFileIndex];
|
|
SBNFileEntry fileEntry;
|
|
|
|
fileEntry.offset = (bkFileEntry->offset & 0xFFFFFF) + globals->baseRomOffset;
|
|
fileEntry.data = bkFileEntry->data;
|
|
if ((fileEntry.data >> 0x18) == AU_FMT_BK) {
|
|
au_load_aux_bank(fileEntry.offset, i);
|
|
} else {
|
|
ret = AU_ERROR_SBN_FORMAT_MISMATCH;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
song++;
|
|
}
|
|
}
|
|
|
|
InstrumentBank* au_get_BK_instruments(BankSet bankSet, u32 bankIndex) {
|
|
InstrumentBank* ret = NULL;
|
|
AuGlobals* globals = gSoundGlobals;
|
|
|
|
// TODO fake match - this multiplying the bankIndex by 16 and then dividing it right after is dumb
|
|
bankIndex *= 16;
|
|
|
|
switch (bankSet) {
|
|
case BANK_SET_AUX:
|
|
ret = &globals->auxBankSet[bankIndex / 16];
|
|
break;
|
|
case BANK_SET_2:
|
|
ret = &globals->bankSet2[bankIndex / 16];
|
|
break;
|
|
case BANK_SET_4:
|
|
ret = &globals->bankSet4[bankIndex / 16];
|
|
break;
|
|
case BANK_SET_5:
|
|
ret = &globals->bankSet5[bankIndex / 16];
|
|
break;
|
|
case BANK_SET_6:
|
|
ret = &globals->bankSet6[bankIndex / 16];
|
|
break;
|
|
case BANK_SET_MUSIC:
|
|
ret = &globals->musicBankSet[bankIndex / 16];
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
enum BKParseState {
|
|
BK_READ_DONE = 0,
|
|
BK_READ_FETCH_HEADER = 11,
|
|
BK_READ_FETCH_DATA = 21,
|
|
BK_READ_SWIZZLE = 31,
|
|
BK_READ_PROCESS_CR = 101,
|
|
BK_READ_SWIZZLE_CR = 111,
|
|
BK_READ_PROCESS_DR = 201,
|
|
BK_READ_UNK_DR = 211,
|
|
BK_READ_PROCESS_SR = 301,
|
|
BK_READ_UNK_SR = 311,
|
|
};
|
|
|
|
#define AL_HEADER_SIG_BK 0x424B
|
|
#define AL_HEADER_SIG_CR 0x4352
|
|
#define AL_HEADER_SIG_DR 0x4452
|
|
#define AL_HEADER_SIG_SR 0x5352
|
|
|
|
/// Loads an instrument bank file from ROM, allocates memory if needed, and sets up instrument pointers.
|
|
/// Instruments in the bank will be configured to use DMA streaming for sample/codebook data.
|
|
/// This is the standard loader for streamed instrument banks.
|
|
BKFileBuffer* au_load_BK_to_bank(s32 bkFileOffset, BKFileBuffer* bkFile, s32 bankIndex, BankSet bankSet) {
|
|
ALHeap* heap = gSynDriverPtr->heap;
|
|
BKHeader localHeader;
|
|
BKHeader* header = &localHeader;
|
|
InstrumentBank* group;
|
|
Instrument** inst;
|
|
s32 instrumentCount;
|
|
u16 keepReading;
|
|
u16 readState;
|
|
s32 size;
|
|
u32 i;
|
|
|
|
au_read_rom(bkFileOffset, header, sizeof(*header));
|
|
readState = BK_READ_FETCH_HEADER;
|
|
keepReading = TRUE;
|
|
|
|
while (keepReading) {
|
|
switch (readState) {
|
|
case BK_READ_DONE:
|
|
keepReading = FALSE;
|
|
break;
|
|
case BK_READ_FETCH_HEADER:
|
|
if (header->signature != AL_HEADER_SIG_BK) {
|
|
keepReading = FALSE;
|
|
} else if (header->size == 0) {
|
|
keepReading = FALSE;
|
|
} else {
|
|
readState = BK_READ_FETCH_DATA;
|
|
}
|
|
break;
|
|
case BK_READ_FETCH_DATA:
|
|
if (header->format == AL_HEADER_SIG_CR) {
|
|
readState = BK_READ_PROCESS_CR;
|
|
} else if (header->format == AL_HEADER_SIG_DR) {
|
|
readState = BK_READ_PROCESS_DR;
|
|
} else if (header->format == AL_HEADER_SIG_SR) {
|
|
readState = BK_READ_PROCESS_SR;
|
|
} else {
|
|
keepReading = FALSE;
|
|
}
|
|
break;
|
|
|
|
case BK_READ_PROCESS_CR:
|
|
size = ALIGN16_(header->instrumetsLength)
|
|
+ ALIGN16_(header->loopStatesLength)
|
|
+ ALIGN16_(header->predictorsLength)
|
|
+ ALIGN16_(header->envelopesLength)
|
|
+ sizeof(*header);
|
|
if (bkFile == NULL) {
|
|
bkFile = alHeapAlloc(heap, 1, size);
|
|
}
|
|
au_read_rom(bkFileOffset, bkFile, size);
|
|
|
|
group = au_get_BK_instruments(bankSet, bankIndex);
|
|
inst = (*group);
|
|
instrumentCount = 0;
|
|
|
|
for (i = 0; i < ARRAY_COUNT(header->instruments); inst++, i++) {
|
|
u16 instOffset = header->instruments[i];
|
|
if (instOffset != 0) {
|
|
instrumentCount++;
|
|
*inst = AU_FILE_RELATIVE(bkFile, instOffset);
|
|
} else {
|
|
*inst = NULL;
|
|
}
|
|
}
|
|
|
|
if (instrumentCount != 0) {
|
|
readState = BK_READ_SWIZZLE_CR;
|
|
} else {
|
|
keepReading = FALSE;
|
|
}
|
|
break;
|
|
case BK_READ_SWIZZLE_CR:
|
|
au_swizzle_BK_instruments(bkFileOffset, bkFile, *group, 16, TRUE);
|
|
readState = BK_READ_DONE;
|
|
break;
|
|
|
|
// inferred states
|
|
case BK_READ_PROCESS_DR:
|
|
case BK_READ_UNK_DR:
|
|
case BK_READ_PROCESS_SR:
|
|
case BK_READ_UNK_SR:
|
|
default:
|
|
keepReading = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return bkFile;
|
|
}
|
|
|
|
/// Fixes up (swizzles) instrument pointers in a loaded bank, converting file-relative offsets to valid RAM pointers.
|
|
/// Sets whether each instrument uses DMA streaming or not, and updates pitch ratios to match output rate.
|
|
/// Replaces NULL instruments with a default instrument to ensure all loaded patches point to valid data.
|
|
void au_swizzle_BK_instruments(s32 bkFileOffset, BKFileBuffer* file, InstrumentBank instruments, u32 instrumentCount, u8 useDma) {
|
|
Instrument* defaultInstrument = gSoundGlobals->defaultInstrument;
|
|
BKHeader* header = &file->header;
|
|
f32 outputRate = gSoundGlobals->outputRate;
|
|
s32 i;
|
|
|
|
if (!header->swizzled) {
|
|
for (i = 0; i < instrumentCount; i++) {
|
|
Instrument* instrument = instruments[i];
|
|
|
|
if (instrument != NULL) {
|
|
if (instrument->wavData != 0) {
|
|
instrument->wavData += bkFileOffset;
|
|
}
|
|
if (instrument->loopState != NULL) {
|
|
instrument->loopState = AU_FILE_RELATIVE(file, instrument->loopState);
|
|
}
|
|
if (instrument->predictor != NULL) {
|
|
instrument->predictor = AU_FILE_RELATIVE(file, instrument->predictor);
|
|
}
|
|
if (instrument->envelopes != NULL) {
|
|
instrument->envelopes = AU_FILE_RELATIVE(file, instrument->envelopes);
|
|
}
|
|
instrument->useDma = useDma;
|
|
instrument->pitchRatio = instrument->sampleRate / outputRate;
|
|
} else {
|
|
instruments[i] = defaultInstrument;
|
|
}
|
|
}
|
|
header->swizzled = TRUE;
|
|
}
|
|
}
|
|
|
|
/// UNUSED
|
|
/// Loads an instrument bank file from ROM to a given buffer (allocates if needed), and sets up instrument pointers.
|
|
/// Instruments are configured to always bypass DMA: sample and codebook data is assumed to be already present in RAM.
|
|
/// Use this only for banks whose sample data is guaranteed to be preloaded, not for standard streaming.
|
|
BKFileBuffer* au_load_static_BK_to_bank(s32* inAddr, void* outAddr, s32 bankIndex, BankSet bankSet) {
|
|
ALHeap* heap = gSynDriverPtr->heap;
|
|
BKFileBuffer* bkFile = outAddr;
|
|
BKHeader localHeader;
|
|
BKHeader* header = &localHeader;
|
|
InstrumentBank* group;
|
|
Instrument* instruments;
|
|
Instrument** inst;
|
|
s32 instrumentCount;
|
|
u32 keepReading;
|
|
u32 readState;
|
|
u32 i;
|
|
s32 useDma = FALSE;
|
|
|
|
readState = BK_READ_FETCH_HEADER;
|
|
keepReading = TRUE;
|
|
|
|
while (keepReading) {
|
|
switch (readState) {
|
|
case BK_READ_DONE:
|
|
keepReading = FALSE;
|
|
break;
|
|
case BK_READ_FETCH_HEADER:
|
|
au_read_rom(*inAddr, &localHeader, sizeof(localHeader));
|
|
if (header->signature != AL_HEADER_SIG_BK) {
|
|
keepReading = FALSE;
|
|
} else if (header->size == 0) {
|
|
keepReading = FALSE;
|
|
} else if (header->format != AL_HEADER_SIG_CR) {
|
|
keepReading = FALSE;
|
|
} else {
|
|
readState = BK_READ_FETCH_DATA;
|
|
}
|
|
break;
|
|
case BK_READ_FETCH_DATA:
|
|
if (bkFile == NULL) {
|
|
bkFile = alHeapAlloc(heap, 1, header->size);
|
|
}
|
|
au_read_rom(*inAddr, bkFile, header->size);
|
|
|
|
instrumentCount = 0;
|
|
group = au_get_BK_instruments(bankSet, bankIndex);
|
|
inst = (*group);
|
|
for (i = 0; i < ARRAY_COUNT(header->instruments); inst++, i++) {
|
|
u16 instOffset = header->instruments[i];
|
|
if (instOffset != 0) {
|
|
instrumentCount++;
|
|
*inst = AU_FILE_RELATIVE(bkFile, instOffset);
|
|
} else {
|
|
*inst = NULL;
|
|
}
|
|
}
|
|
|
|
if (instrumentCount != 0) {
|
|
readState = BK_READ_SWIZZLE;
|
|
} else {
|
|
keepReading = FALSE;
|
|
}
|
|
break;
|
|
case BK_READ_SWIZZLE:
|
|
au_swizzle_BK_instruments((s32)bkFile, bkFile, *group, 16, useDma);
|
|
readState = BK_READ_DONE;
|
|
break;
|
|
default:
|
|
keepReading = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
return bkFile;
|
|
}
|
|
|
|
s32 au_load_aux_bank(s32 bkFileOffset, s32 bankIndex) {
|
|
au_load_BK_to_bank(bkFileOffset, gSoundGlobals->auxBanks[bankIndex], bankIndex, BANK_SET_AUX);
|
|
return AU_RESULT_OK;
|
|
}
|
|
|
|
/// unused. resets all instruments in (bankIndex, bankSet) to default
|
|
void au_clear_instrument_group(s32 bankIndex, BankSet bankSet) {
|
|
Instrument* instrument = gSoundGlobals->defaultInstrument;
|
|
InstrumentBank* group = au_get_BK_instruments(bankSet, bankIndex);
|
|
Instrument** ptr = *group;
|
|
u32 i;
|
|
|
|
if (group != NULL) {
|
|
for (i = 0; i < ARRAY_COUNT(*group); i++) {
|
|
*ptr++ = instrument;
|
|
}
|
|
}
|
|
}
|
|
|
|
void au_set_bus_volume_level(s32 soundTypeFlags, u32 volPreset) {
|
|
if (volPreset < ARRAY_COUNT(PerceptualVolumeLevels)) {
|
|
s32 vol = PerceptualVolumeLevels[volPreset];
|
|
if (soundTypeFlags & AUDIO_TYPE_BGM) {
|
|
gBGMPlayerA->busVolume = vol;
|
|
au_fade_flush(&gBGMPlayerA->fadeInfo);
|
|
gBGMPlayerB->busVolume = vol;
|
|
au_fade_flush(&gBGMPlayerB->fadeInfo);
|
|
}
|
|
if (soundTypeFlags & AUDIO_TYPE_SFX) {
|
|
gSoundManager->busVolume = vol;
|
|
au_fade_flush(&gSoundManager->fadeInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
s32 au_set_reverb_type(s32 soundTypeFlags, s32 reverbType) {
|
|
if (soundTypeFlags & AUDIO_TYPE_SFX) {
|
|
return au_sfx_set_reverb_type(gSoundManager, reverbType);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void au_sync_channel_delay_enabled(u32 bMonoSound) {
|
|
if (bMonoSound % 2 == 1) {
|
|
// mono sound
|
|
if (gSoundGlobals->channelDelayState == AU_DELAY_STATE_ON) {
|
|
gSoundGlobals->channelDelayState = AU_DELAY_STATE_REQUEST_OFF;
|
|
}
|
|
} else {
|
|
// stereo sound
|
|
if (gSoundGlobals->channelDelayState != AU_DELAY_STATE_ON) {
|
|
gSoundGlobals->channelDelayPending = TRUE;
|
|
gSoundGlobals->channelDelayState = AU_DELAY_STATE_ON;
|
|
}
|
|
}
|
|
}
|
|
|
|
// probable split
|
|
|
|
void au_read_rom(s32 romAddr, void* buffer, u32 size) {
|
|
s32 nchunks = size / 0x2000;
|
|
s32 offset = 0;
|
|
|
|
if (nchunks != 0) {
|
|
while (nchunks--) {
|
|
nuPiReadRom(romAddr + offset, buffer + offset, 0x2000);
|
|
offset += 0x2000;
|
|
}
|
|
}
|
|
|
|
size %= 0x2000;
|
|
if (size != 0) {
|
|
nuPiReadRom(romAddr + offset, buffer + offset, size);
|
|
}
|
|
}
|
|
|
|
void au_memset(void* dst, s32 size, u8 value) {
|
|
s32 count;
|
|
s32 intValue;
|
|
|
|
if (size == 0) {
|
|
return;
|
|
}
|
|
|
|
if (size < 1024) {
|
|
while (size--) {
|
|
*(u8*)dst++ = value;
|
|
}
|
|
} else {
|
|
count = (u32)dst & 0x3;
|
|
if (count != 0) {
|
|
count = 4 - count;
|
|
size -= count;
|
|
while (count--) {
|
|
*(u8*)dst++ = value;
|
|
}
|
|
}
|
|
|
|
count = size >> 2;
|
|
intValue = (value << 8) + value;
|
|
intValue = (intValue << 16) + intValue;
|
|
while (count--) {
|
|
*(u32*)dst = intValue;
|
|
dst += 4;
|
|
}
|
|
|
|
count = size & 3;
|
|
if (count != 0) {
|
|
while (count--) {
|
|
*(u8*)dst++ = value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void au_copy_bytes(s8* src, s8* dest, s32 size) {
|
|
if (size > 0) {
|
|
while (size-- != 0) {
|
|
*dest++ = *src++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void au_copy_words(void* src, void* dst, s32 size) {
|
|
size /= 4;
|
|
|
|
if (size > 0) {
|
|
if (!(((s32) src | (s32) dst) & 3)) {
|
|
s32* srcIt = src;
|
|
s32* dstIt = dst;
|
|
|
|
size--;
|
|
do {
|
|
*dstIt++ = *srcIt++;
|
|
} while (size-- != 0);
|
|
}
|
|
}
|
|
}
|