#include #include "heap.h" #include "data.h" #include "load.h" #include "synthesis.h" #include "seqplayer.h" #include "effects.h" #include "game/game_init.h" #include "game/puppyprint.h" #include "game/vc_check.h" #include "game/debug.h" #include "string.h" struct PoolSplit { u32 wantSeq; u32 wantBank; u32 wantUnused; u32 wantCustom; }; // size = 0x10 struct PoolSplit2 { u32 wantPersistent; u32 wantTemporary; }; // size = 0x8 #if defined(VERSION_JP) || defined(VERSION_US) s16 gVolume; s8 gReverbDownsampleRate; #endif struct SoundAllocPool gAudioSessionPool; struct SoundAllocPool gAudioInitPool; struct SoundAllocPool gNotesAndBuffersPool; u8 sAudioHeapPad[0x20]; // probably two unused pools #ifdef BETTER_REVERB struct SoundAllocPool gBetterReverbPool; #endif struct SoundAllocPool gSeqAndBankPool; struct SoundAllocPool gPersistentCommonPool; struct SoundAllocPool gTemporaryCommonPool; struct SoundMultiPool gSeqLoadedPool; struct SoundMultiPool gBankLoadedPool; #ifdef VERSION_SH struct SoundMultiPool gUnusedLoadedPool; struct Unk1Pool gUnkPool1; struct UnkPool gUnkPool2; struct UnkPool gUnkPool3; #endif struct PoolSplit sSessionPoolSplit; struct PoolSplit2 sSeqAndBankPoolSplit; struct PoolSplit sPersistentCommonPoolSplit; struct PoolSplit sTemporaryCommonPoolSplit; #ifdef VERSION_SH u8 gUnkLoadStatus[MAX_NUM_SOUNDBANKS]; #endif u8 gBankLoadStatus[MAX_NUM_SOUNDBANKS]; u8 gSeqLoadStatus[0x100]; #if defined(VERSION_EU) || defined(VERSION_SH) volatile u8 gAudioResetStatus; u8 gAudioResetPresetIdToLoad; s32 gAudioResetFadeOutFramesLeft; #endif extern s32 gMaxAudioCmds; #ifdef VERSION_SH void *get_bank_or_seq_inner(s32 poolIdx, s32 arg1, s32 bankId); struct UnkEntry *func_sh_802f1ec4(u32 size); void func_sh_802f2158(struct UnkEntry *entry); struct UnkEntry *unk_pool2_alloc(u32 size); void func_sh_802F2320(struct UnkEntry *entry, struct AudioBankSample *sample); void func_sh_802f23ec(void); void unk_pools_init(u32 size1, u32 size2); #endif #if defined(VERSION_EU) /** * Assuming 'k' in [9, 24], * Computes a newton's method step for f(x) = x^k - d */ f64 root_newton_step(f64 x, s32 k, f64 d) { f64 deg2 = x * x; f64 deg4 = deg2 * deg2; f64 deg8 = deg4 * deg4; s32 degree = k - 9; f64 fx; f64 deriv = deg8; if (degree & (1 << 0)) { deriv *= x; } if (degree & (1 << 1)) { deriv *= deg2; } if (degree & (1 << 2)) { deriv *= deg4; } if (degree & (1 << 3)) { deriv *= deg8; } fx = deriv * x - d; deriv *= k; return x - fx / deriv; } /** * Assuming 'k' in [9, 24], * Computes d ^ (1/k) * * @return the root, or 1.0 if d is 0 */ f64 kth_root(f64 d, s32 k) { f64 root = 1.5; f64 next; f64 diff; s32 i; if (d == 0.0) { root = 1.0; } else { for (i = 0; i < 64; i++) { next = root_newton_step(root, k, d); diff = next - root; if (diff < 0) { diff = -diff; } if (diff < 1e-07) { root = next; break; } else { root = next; } } } return root; } void build_vol_rampings_table(s32 UNUSED unused, s32 len) { s32 i; s32 step; s32 d; s32 k = len / 8; for (step = 0, i = 0; i < 0x400; step += 32, i++) { d = step; if (step == 0) { d = 1; } gLeftVolRampings[0][i] = kth_root( d, k - 1); gRightVolRampings[0][i] = kth_root(1.0 / d, k - 1) * 65536.0; gLeftVolRampings[1][i] = kth_root( d, k); gRightVolRampings[1][i] = kth_root(1.0 / d, k) * 65536.0; gLeftVolRampings[2][i] = kth_root( d, k + 1); gRightVolRampings[2][i] = kth_root(1.0 / d, k + 1) * 65536.0; } } #endif void reset_bank_and_seq_load_status(void) { #ifdef VERSION_SH bzero(&gBankLoadStatus, sizeof(gBankLoadStatus)); bzero(&gUnkLoadStatus, sizeof(gUnkLoadStatus)); bzero(&gSeqLoadStatus, sizeof(gSeqLoadStatus)); #else bzero(&gBankLoadStatus, sizeof(gBankLoadStatus)); // Setting this array to zero is equivilent to SOUND_LOAD_STATUS_NOT_LOADED bzero(&gSeqLoadStatus, sizeof(gSeqLoadStatus)); // Same dealio #endif } void discard_bank(s32 bankId) { s32 i; for (i = 0; i < gMaxSimultaneousNotes; i++) { struct Note *note = &gNotes[i]; #if defined(VERSION_EU) if (note->noteSubEu.bankId == bankId) { #else if (note->bankId == bankId) { #endif // (These prints are unclear. Arguments are picked semi-randomly.) eu_stubbed_printf_1("Warning:Kill Note %x \n", i); #ifdef VERSION_SH if (note->unkSH34 == NOTE_PRIORITY_DISABLED && note->priority) { #else if (note->priority >= NOTE_PRIORITY_MIN) { #endif #if defined(VERSION_EU) eu_stubbed_printf_3("Kill Voice %d (ID %d) %d\n", note->waveId, bankId, note->priority); #endif eu_stubbed_printf_0("Warning: Running Sequence's data disappear!\n"); note->parentLayer->enabled = FALSE; // is 0x48, should be 0x44 note->parentLayer->finished = TRUE; } note_disable(note); audio_list_remove(¬e->listItem); audio_list_push_back(&gNoteFreeLists.disabled, ¬e->listItem); } } } void discard_sequence(s32 seqId) { s32 i; for (i = 0; i < SEQUENCE_PLAYERS; i++) { if (gSequencePlayers[i].enabled && gSequencePlayers[i].seqId == seqId) { #if defined(VERSION_EU) || defined(VERSION_SH) sequence_player_disable(&gSequencePlayers[i]); #else sequence_player_disable(gSequencePlayers + i); #endif } } } void *soundAlloc(struct SoundAllocPool *pool, u32 size) { #if defined(VERSION_EU) || defined(VERSION_SH) u8 *start; u32 alignedSize = ALIGN16(size); start = pool->cur; if (start + alignedSize <= pool->start + pool->size) { bzero(start, alignedSize); pool->cur += alignedSize; } else { eu_stubbed_printf_1("Heap OverFlow : Not Allocate %d!\n", size); return NULL; } pool->numAllocatedEntries++; return start; #else u32 alignedSize = ALIGN16(size); u8 *start = pool->cur; if ((start + alignedSize <= pool->size + pool->start)) { bzero(start, alignedSize); pool->cur += alignedSize; } else { return NULL; } return start; #endif } #ifdef VERSION_SH void *sound_alloc_uninitialized(struct SoundAllocPool *pool, u32 size) { u8 *start; u32 alignedSize = ALIGN16(size); start = pool->cur; if (start + alignedSize <= pool->start + pool->size) { pool->cur += alignedSize; } else { return NULL; } pool->numAllocatedEntries++; return start; } #endif void sound_alloc_pool_init(struct SoundAllocPool *pool, void *memAddr, u32 size) { pool->cur = pool->start = (u8 *) ALIGN16((uintptr_t) memAddr); #ifdef VERSION_SH pool->size = size - ((uintptr_t) memAddr & 0xf); #else pool->size = ALIGN16(size); #endif pool->numAllocatedEntries = 0; } void persistent_pool_clear(struct PersistentPool *persistent) { persistent->pool.numAllocatedEntries = 0; persistent->pool.cur = persistent->pool.start; persistent->numEntries = 0; } void temporary_pool_clear(struct TemporaryPool *temporary) { temporary->pool.numAllocatedEntries = 0; temporary->pool.cur = temporary->pool.start; temporary->nextSide = 0; temporary->entries[0].ptr = temporary->pool.start; temporary->entries[1].ptr = temporary->pool.start + temporary->pool.size; temporary->entries[0].id = -1; // should be at 1e not 1c temporary->entries[1].id = -1; } void sound_init_main_pools(s32 sizeForAudioInitPool) { sound_alloc_pool_init(&gAudioInitPool, gAudioHeap, sizeForAudioInitPool); sound_alloc_pool_init(&gAudioSessionPool, (gAudioHeap + sizeForAudioInitPool), (gAudioHeapSize - sizeForAudioInitPool)); } #ifdef PUPPYPRINT_DEBUG void puppyprint_get_allocated_pools(s32 *audioPoolList) { u32 i, j; const struct SoundAllocPool *pools[NUM_AUDIO_POOLS] = { &gAudioInitPool, &gNotesAndBuffersPool, &gSeqLoadedPool.persistent.pool, &gBankLoadedPool.persistent.pool, &gSeqLoadedPool.temporary.pool, &gBankLoadedPool.temporary.pool, #ifdef BETTER_REVERB &gBetterReverbPool, #endif }; for (i = 0, j = 0; j < NUM_AUDIO_POOLS; i += 2, j++) { audioPoolList[i ] = (s32) pools[j]->size; audioPoolList[i + 1] = (s32) (pools[j]->cur - pools[j]->start); } } #endif #ifdef VERSION_SH #define SOUND_ALLOC_FUNC sound_alloc_uninitialized #else #define SOUND_ALLOC_FUNC soundAlloc #endif void session_pools_init(struct PoolSplit *a) { gAudioSessionPool.cur = gAudioSessionPool.start; sound_alloc_pool_init(&gNotesAndBuffersPool, SOUND_ALLOC_FUNC(&gAudioSessionPool, a->wantSeq ), a->wantSeq ); sound_alloc_pool_init(&gSeqAndBankPool, SOUND_ALLOC_FUNC(&gAudioSessionPool, a->wantCustom ), a->wantCustom ); #ifdef BETTER_REVERB sound_alloc_pool_init(&gBetterReverbPool, SOUND_ALLOC_FUNC(&gAudioSessionPool, BETTER_REVERB_SIZE), BETTER_REVERB_SIZE); #endif } void seq_and_bank_pool_init(struct PoolSplit2 *a) { gSeqAndBankPool.cur = gSeqAndBankPool.start; sound_alloc_pool_init(&gPersistentCommonPool, SOUND_ALLOC_FUNC(&gSeqAndBankPool, a->wantPersistent), a->wantPersistent); sound_alloc_pool_init (&gTemporaryCommonPool, SOUND_ALLOC_FUNC(&gSeqAndBankPool, a->wantTemporary ), a->wantTemporary ); } void persistent_pools_init(struct PoolSplit *a) { gPersistentCommonPool.cur = gPersistentCommonPool.start; sound_alloc_pool_init( &gSeqLoadedPool.persistent.pool, SOUND_ALLOC_FUNC(&gPersistentCommonPool, a->wantSeq ), a->wantSeq ); sound_alloc_pool_init(&gBankLoadedPool.persistent.pool, SOUND_ALLOC_FUNC(&gPersistentCommonPool, a->wantBank), a->wantBank); #ifdef VERSION_SH sound_alloc_pool_init(&gUnusedLoadedPool.persistent.pool, SOUND_ALLOC_FUNC(&gPersistentCommonPool, a->wantUnused), a->wantUnused); #endif persistent_pool_clear(&gSeqLoadedPool.persistent); persistent_pool_clear(&gBankLoadedPool.persistent); #ifdef VERSION_SH persistent_pool_clear(&gUnusedLoadedPool.persistent); #endif } void temporary_pools_init(struct PoolSplit *a) { gTemporaryCommonPool.cur = gTemporaryCommonPool.start; sound_alloc_pool_init( &gSeqLoadedPool.temporary.pool, SOUND_ALLOC_FUNC(&gTemporaryCommonPool, a->wantSeq ), a->wantSeq ); sound_alloc_pool_init(&gBankLoadedPool.temporary.pool, SOUND_ALLOC_FUNC(&gTemporaryCommonPool, a->wantBank), a->wantBank); #ifdef VERSION_SH sound_alloc_pool_init(&gUnusedLoadedPool.temporary.pool, SOUND_ALLOC_FUNC(&gTemporaryCommonPool, a->wantUnused), a->wantUnused); #endif temporary_pool_clear(&gSeqLoadedPool.temporary); temporary_pool_clear(&gBankLoadedPool.temporary); #ifdef VERSION_SH temporary_pool_clear(&gUnusedLoadedPool.temporary); #endif } #undef SOUND_ALLOC_FUNC #ifdef VERSION_SH void *alloc_bank_or_seq(s32 poolIdx, s32 size, s32 arg3, s32 id) { #else void *alloc_bank_or_seq(struct SoundMultiPool *arg0, s32 arg1, s32 size, s32 arg3, s32 id) { #endif // arg3 = 0, 1 or 2? #ifdef VERSION_SH struct SoundMultiPool *arg0 = NULL; #define isSound poolIdx #endif struct TemporaryPool *tp; struct SoundAllocPool *pool; void *ret; #if defined(VERSION_JP) || defined(VERSION_US) u16 UNUSED _firstVal; u16 UNUSED _secondVal; #else u16 firstVal; u16 secondVal; #endif u32 nullID = -1; UNUSED s32 i; u8 *table = NULL; #ifndef VERSION_SH u8 isSound = FALSE; #endif #if defined(VERSION_JP) || defined(VERSION_US) u16 firstVal; u16 secondVal; u32 bothDiscardable; u32 leftDiscardable, rightDiscardable; u32 leftNotLoaded, rightNotLoaded; u32 leftAvail, rightAvail; #endif size = ALIGN16(size); #ifdef VERSION_SH switch (poolIdx) { case 0: arg0 = &gSeqLoadedPool; table = gSeqLoadStatus; break; case 1: arg0 = &gBankLoadedPool; table = gBankLoadStatus; break; case 2: arg0 = &gUnusedLoadedPool; table = gUnkLoadStatus; break; } #endif if (arg3 == 0) { tp = &arg0->temporary; #ifndef VERSION_SH if (arg0 == &gSeqLoadedPool) { table = gSeqLoadStatus; isSound = FALSE; } else if (arg0 == &gBankLoadedPool) { table = gBankLoadStatus; isSound = TRUE; } #endif #ifdef VERSION_SH if (tp->entries[0].id == (s8)nullID) { firstVal = SOUND_LOAD_STATUS_NOT_LOADED; } else { firstVal = table[tp->entries[0].id]; } if (tp->entries[1].id == (s8)nullID) { secondVal = SOUND_LOAD_STATUS_NOT_LOADED; } else { secondVal = table[tp->entries[1].id]; } #else firstVal = (tp->entries[0].id == (s8)nullID ? SOUND_LOAD_STATUS_NOT_LOADED : table[tp->entries[0].id]); secondVal = (tp->entries[1].id == (s8)nullID ? SOUND_LOAD_STATUS_NOT_LOADED : table[tp->entries[1].id]); #endif #if defined(VERSION_JP) || defined(VERSION_US) leftNotLoaded = (firstVal == SOUND_LOAD_STATUS_NOT_LOADED ); leftDiscardable = (firstVal == SOUND_LOAD_STATUS_DISCARDABLE); leftAvail = (firstVal != SOUND_LOAD_STATUS_IN_PROGRESS); rightNotLoaded = (secondVal == SOUND_LOAD_STATUS_NOT_LOADED ); rightDiscardable = (secondVal == SOUND_LOAD_STATUS_DISCARDABLE); rightAvail = (secondVal != SOUND_LOAD_STATUS_IN_PROGRESS); bothDiscardable = (leftDiscardable && rightDiscardable); if (leftNotLoaded) { tp->nextSide = 0; } else if (rightNotLoaded) { tp->nextSide = 1; } else if (bothDiscardable) { // Use the opposite side from last time. } else if (firstVal == SOUND_LOAD_STATUS_DISCARDABLE) { // ??! (I blame copt) tp->nextSide = 0; } else if (rightDiscardable) { tp->nextSide = 1; } else if (leftAvail) { tp->nextSide = 0; } else if (rightAvail) { tp->nextSide = 1; } else { // Both left and right sides are being loaded into. return NULL; } #else #ifdef VERSION_SH if (poolIdx == 1) { if (firstVal == SOUND_LOAD_STATUS_4) { for (i = 0; i < gMaxSimultaneousNotes; i++) { if (gNotes[i].bankId == tp->entries[0].id && gNotes[i].noteSubEu.enabled) { break; } } if (i == gMaxSimultaneousNotes) { if (gBankLoadStatus[tp->entries[0].id] != SOUND_LOAD_STATUS_5) { gBankLoadStatus[tp->entries[0].id] = SOUND_LOAD_STATUS_DISCARDABLE; } firstVal = SOUND_LOAD_STATUS_DISCARDABLE; } } if (secondVal == SOUND_LOAD_STATUS_4) { for (i = 0; i < gMaxSimultaneousNotes; i++) { if (gNotes[i].bankId == tp->entries[1].id && gNotes[i].noteSubEu.enabled) { break; } } if (i == gMaxSimultaneousNotes) { if (gBankLoadStatus[tp->entries[1].id] != SOUND_LOAD_STATUS_5) { gBankLoadStatus[tp->entries[1].id] = SOUND_LOAD_STATUS_DISCARDABLE; } secondVal = SOUND_LOAD_STATUS_DISCARDABLE; } } } #endif if (firstVal == SOUND_LOAD_STATUS_NOT_LOADED) { tp->nextSide = 0; } else if (secondVal == SOUND_LOAD_STATUS_NOT_LOADED) { tp->nextSide = 1; } else { eu_stubbed_printf_0("WARNING: NO FREE AUTOSEQ AREA.\n"); if ((firstVal == SOUND_LOAD_STATUS_DISCARDABLE) && (secondVal == SOUND_LOAD_STATUS_DISCARDABLE)) { // Use the opposite side from last time. } else if (firstVal == SOUND_LOAD_STATUS_DISCARDABLE) { tp->nextSide = 0; } else if (secondVal == SOUND_LOAD_STATUS_DISCARDABLE) { tp->nextSide = 1; } else { #ifdef VERSION_EU eu_stubbed_printf_0("WARNING: NO STOP AUTO AREA.\n"); eu_stubbed_printf_0(" AND TRY FORCE TO STOP SIDE \n"); if (firstVal != SOUND_LOAD_STATUS_IN_PROGRESS) { tp->nextSide = 0; } else if (secondVal != SOUND_LOAD_STATUS_IN_PROGRESS) { tp->nextSide = 1; } else { // Both left and right sides are being loaded into. eu_stubbed_printf_0("TWO SIDES ARE LOADING... ALLOC CANCELED.\n"); return NULL; } #else if (poolIdx == 0) { if (firstVal == SOUND_LOAD_STATUS_COMPLETE) { for (i = 0; i < SEQUENCE_PLAYERS; i++) { if (gSequencePlayers[i].enabled && gSequencePlayers[i].seqId == tp->entries[0].id) { break; } } if (i == SEQUENCE_PLAYERS) { tp->nextSide = 0; goto out; } } if (secondVal == SOUND_LOAD_STATUS_COMPLETE) { for (i = 0; i < SEQUENCE_PLAYERS; i++) { if (gSequencePlayers[i].enabled && gSequencePlayers[i].seqId == tp->entries[1].id) { break; } } if (i == SEQUENCE_PLAYERS) { tp->nextSide = 1; goto out; } } } else if (poolIdx == 1) { if (firstVal == SOUND_LOAD_STATUS_COMPLETE) { for (i = 0; i < gMaxSimultaneousNotes; i++) { if (gNotes[i].bankId == tp->entries[0].id && gNotes[i].noteSubEu.enabled) { break; } } if (i == gMaxSimultaneousNotes) { tp->nextSide = 0; goto out; } } if (secondVal == SOUND_LOAD_STATUS_COMPLETE) { for (i = 0; i < gMaxSimultaneousNotes; i++) { if (gNotes[i].bankId == tp->entries[1].id && gNotes[i].noteSubEu.enabled) { break; } } if (i == gMaxSimultaneousNotes) { tp->nextSide = 1; goto out; } } } if (tp->nextSide == 0) { if (firstVal == SOUND_LOAD_STATUS_IN_PROGRESS) { if (secondVal != SOUND_LOAD_STATUS_IN_PROGRESS) { tp->nextSide = 1; goto out; } } else { goto out; } } else { if (secondVal == SOUND_LOAD_STATUS_IN_PROGRESS) { if (firstVal != SOUND_LOAD_STATUS_IN_PROGRESS) { tp->nextSide = 0; goto out; } } else { goto out; } } return NULL; out:; #endif } } #endif pool = &arg0->temporary.pool; if (tp->entries[tp->nextSide].id != (s8)nullID) { table[tp->entries[tp->nextSide].id] = SOUND_LOAD_STATUS_NOT_LOADED; if (isSound == TRUE) { discard_bank(tp->entries[tp->nextSide].id); } } switch (tp->nextSide) { case 0: tp->entries[0].ptr = pool->start; tp->entries[0].id = id; tp->entries[0].size = size; pool->cur = pool->start + size; #ifdef VERSION_SH if (tp->entries[1].id != (s32)nullID) #endif if (tp->entries[1].ptr < pool->cur) { eu_stubbed_printf_0("WARNING: Before Area Overlaid After."); // Throw out the entry on the other side if it doesn't fit. // (possible @bug: what if it's currently being loaded?) table[tp->entries[1].id] = SOUND_LOAD_STATUS_NOT_LOADED; if (isSound) { discard_bank(tp->entries[1].id); } else { discard_sequence(tp->entries[1].id); } tp->entries[1].id = (s32)nullID; tp->entries[1].ptr = pool->start + pool->size; } ret = tp->entries[0].ptr; break; case 1: #if defined(VERSION_SH) tp->entries[1].ptr = (u8 *) ((uintptr_t) (pool->start + pool->size - size) & ~0x0f); #else tp->entries[1].ptr = pool->start + pool->size - size; #endif tp->entries[1].id = id; tp->entries[1].size = size; #ifdef VERSION_SH if (tp->entries[0].id != (s32)nullID) #endif if (tp->entries[1].ptr < pool->cur) { eu_stubbed_printf_0("WARNING: After Area Overlaid Before."); table[tp->entries[0].id] = SOUND_LOAD_STATUS_NOT_LOADED; if (isSound) { discard_bank(tp->entries[0].id); } else { discard_sequence(tp->entries[0].id); } tp->entries[0].id = (s32)nullID; pool->cur = pool->start; } ret = tp->entries[1].ptr; break; default: eu_stubbed_printf_1("MEMORY:SzHeapAlloc ERROR: sza->side %d\n", tp->nextSide); return NULL; } // Switch sides for next time in case both entries are // SOUND_LOAD_STATUS_DISCARDABLE. tp->nextSide ^= 1; return ret; } #if defined(VERSION_EU) || defined(VERSION_SH) #ifdef VERSION_SH ret = sound_alloc_uninitialized(&arg0->persistent.pool, size); #else ret = soundAlloc(&arg0->persistent.pool, arg1 * size); #endif arg0->persistent.entries[arg0->persistent.numEntries].ptr = ret; if (ret == NULL) { #else arg0->persistent.entries[arg0->persistent.numEntries].ptr = soundAlloc(&arg0->persistent.pool, (arg1 * size)); if (arg0->persistent.entries[arg0->persistent.numEntries].ptr == NULL) { #endif switch (arg3) { case 2: #if defined(VERSION_EU) eu_stubbed_printf_0("MEMORY:StayHeap OVERFLOW."); return alloc_bank_or_seq(arg0, arg1, size, 0, id); #elif defined(VERSION_SH) return alloc_bank_or_seq(poolIdx, size, 0, id); #else // Prevent tail call optimization. ret = alloc_bank_or_seq(arg0, arg1, size, 0, id); return ret; #endif case 1: #ifdef VERSION_SH case 0: #endif #ifdef VERSION_EU eu_stubbed_printf_1("MEMORY:StayHeap OVERFLOW (REQ:%d)", arg1 * size); #endif return NULL; } } // TODO: why is this guaranteed to write <= 32 entries...? // Because the buffer is small enough that more don't fit? arg0->persistent.entries[arg0->persistent.numEntries].id = id; arg0->persistent.entries[arg0->persistent.numEntries].size = size; #if defined(VERSION_EU) || defined(VERSION_SH) return arg0->persistent.entries[arg0->persistent.numEntries++].ptr; #else arg0->persistent.numEntries++; return arg0->persistent.entries[arg0->persistent.numEntries - 1].ptr; #endif #ifdef VERSION_SH #undef isSound #endif } #ifdef VERSION_SH void *get_bank_or_seq(s32 poolIdx, s32 arg1, s32 id) { void *ret = unk_pool1_lookup(poolIdx, id); if (ret != NULL) { return ret; } if (arg1 == 3) { return NULL; } return get_bank_or_seq_inner(poolIdx, arg1, id); } void *get_bank_or_seq_inner(s32 poolIdx, s32 arg1, s32 bankId) { u32 i; struct SoundMultiPool* loadedPool = NULL; struct TemporaryPool* temporary; struct PersistentPool* persistent; switch (poolIdx) { case 0: loadedPool = &gSeqLoadedPool; break; case 1: loadedPool = &gBankLoadedPool; break; case 2: loadedPool = &gUnusedLoadedPool; break; } temporary = &loadedPool->temporary; if (arg1 == 0) { if (temporary->entries[0].id == bankId) { temporary->nextSide = 1; return temporary->entries[0].ptr; } else if (temporary->entries[1].id == bankId) { temporary->nextSide = 0; return temporary->entries[1].ptr; } else { return NULL; } } persistent = &loadedPool->persistent; for (i = 0; i < persistent->numEntries; i++) { if (persistent->entries[i].id == bankId) { return persistent->entries[i].ptr; } } if (arg1 == 2) { return get_bank_or_seq(poolIdx, 0, bankId); } return NULL; } #endif #ifndef VERSION_SH void *get_bank_or_seq(struct SoundMultiPool *arg0, s32 arg1, s32 id) { u32 i; struct TemporaryPool *temporary = &arg0->temporary; if (arg1 == 0) { // Try not to overwrite sound that we have just accessed, by setting nextSide appropriately. if (temporary->entries[0].id == id) { temporary->nextSide = 1; return temporary->entries[0].ptr; } else if (temporary->entries[1].id == id) { temporary->nextSide = 0; return temporary->entries[1].ptr; } eu_stubbed_printf_1("Auto Heap Unhit for ID %d\n", id); return NULL; } else { struct PersistentPool *persistent = &arg0->persistent; for (i = 0; i < persistent->numEntries; i++) { if (id == persistent->entries[i].id) { //eu_stubbed_printf_2("Cache hit %d at stay %d\n", id, i); return persistent->entries[i].ptr; } } if (arg1 == 2) { return get_bank_or_seq(arg0, 0, id); } return NULL; } } #endif #if defined(VERSION_EU) || defined(VERSION_SH) UNUSED void func_eu_802e27e4_unused(f32 arg0, f32 arg1, u16 *arg2) { s32 i; f32 tmp[16]; tmp[0] = (f32) (arg1 * 262159.0f); tmp[8] = (f32) (arg0 * 262159.0f); tmp[1] = (f32) ((arg1 * arg0) * 262159.0f); tmp[9] = (f32) (((arg0 * arg0) + arg1) * 262159.0f); for (i = 2; i < 8; i++) { //! @bug they probably meant to store the value to tmp[i] and tmp[8 + i] arg2[i] = arg1 * tmp[i - 2] + arg0 * tmp[i - 1]; arg2[8 + i] = arg1 * tmp[6 + i] + arg0 * tmp[7 + i]; } for (i = 0; i < 16; i++) { arg2[i] = tmp[i]; } #ifdef VERSION_EU for (i = 0; i < 8; i++) { eu_stubbed_printf_1("%d ", arg2[i]); } eu_stubbed_printf_0("\n"); for (i = 8; i < 16; i++) { eu_stubbed_printf_1("%d ", arg2[i]); } eu_stubbed_printf_0("\n"); #endif } #endif #ifdef VERSION_SH void fill_zero_filter(s16 filter[]) { s32 i; for (i = 0; i < 8; i++) { filter[i] = 0; } } extern s16 unk_sh_data_3[15 * 8]; extern s16 unk_sh_data_4[15 * 8]; void func_sh_802F0DE8(s16 filter[8], s32 arg1) { s32 i; s16 *ptr = &unk_sh_data_3[8 * (arg1 - 1)]; for (i = 0; i < 8; i++) { filter[i] = ptr[i]; } } void func_sh_802F0E40(s16 filter[8], s32 arg1) { // Unused s32 i; s16 *ptr = &unk_sh_data_4[8 * (arg1 - 1)]; for (i = 0; i < 8; i++) { filter[i] = ptr[i]; } } void fill_filter(s16 filter[8], s32 arg1, s32 arg2) { s32 i; s16 *ptr; if (arg1 != 0) { func_sh_802F0DE8(filter, arg1); } else { fill_zero_filter(filter); } if (arg2 != 0) { ptr = &unk_sh_data_4[8 * (arg2 - 1)]; for (i = 0; i < 8; i++) { filter[i] += ptr[i]; } } } #endif void decrease_reverb_gain(void) { #if defined(VERSION_EU) s32 i; for (i = 0; i < gNumSynthesisReverbs; i++) { gSynthesisReverbs[i].reverbGain -= (gSynthesisReverbs[i].reverbGain / 8); } #elif defined(VERSION_JP) || defined(VERSION_US) gSynthesisReverb.reverbGain -= (gSynthesisReverb.reverbGain / 4); #else s32 i, j; s32 v0 = ((gAudioBufferParameters.presetUnk4 == 2) ? 2 : 1); for (i = 0; i < gNumSynthesisReverbs; i++) { for (j = 0; j < v0; j++) { gSynthesisReverbs[i].reverbGain -= (gSynthesisReverbs[i].reverbGain / 3); } } #endif } #if defined(VERSION_EU) || defined(VERSION_SH) s32 audio_shut_down_and_reset_step(void) { s32 i; switch (gAudioResetStatus) { case 5: for (i = 0; i < SEQUENCE_PLAYERS; i++) { sequence_player_disable(&gSequencePlayers[i]); } gAudioResetStatus--; break; case 4: for (i = 0; i < gMaxSimultaneousNotes; i++) { if (gNotes[i].noteSubEu.enabled && gNotes[i].adsr.state != ADSR_STATE_DISABLED) { gNotes[i].adsr.fadeOutVel = gAudioBufferParameters.updatesPerFrameInv; gNotes[i].adsr.action |= ADSR_ACTION_RELEASE; } } gAudioResetFadeOutFramesLeft = 0; gAudioResetStatus--; break; case 3: gAudioResetStatus--; break; case 2: bzero(&gAiBuffers[0][0], (AIBUFFER_LEN * NUMAIBUFFERS)); gAudioResetStatus--; #ifdef VERSION_SH func_sh_802f23ec(); #endif break; case 1: audio_reset_session(); bzero(&gAiBuffers[0][0], (AIBUFFER_LEN * NUMAIBUFFERS)); gAudioResetStatus = 0; } return (gAudioResetStatus < 3); } #endif u8 sAudioIsInitialized = FALSE; // Separate the reverb settings into their own func. Bit unstable currently, so still only runs at boot. #if defined(VERSION_EU) || defined(VERSION_SH) void init_reverb_eu(void) { s16 *mem; struct AudioSessionSettingsEU *preset = &gAudioSessionPresets[0]; struct SynthesisReverb *reverb; struct ReverbSettingsEU *reverbSettings; s32 i, j; // This is called 4 times for numReverbs to work at higher values. This does eat up some memory though. for (j = 0; j < 4; j++) { gSynthesisReverbs[j].useReverb = FALSE; // Both left and right channels are allocated/cleared together, then separated based on the reverb window size if (!sAudioIsInitialized) { gSynthesisReverbs[j].ringBuffer.left = soundAlloc(&gNotesAndBuffersPool, REVERB_WINDOW_SIZE_MAX * 4); } } gNumSynthesisReverbs = preset->numReverbs; for (j = 0; j < gNumSynthesisReverbs; j++) { reverb = &gSynthesisReverbs[j]; reverbSettings = &sReverbSettings[MIN((u32)(gAudioResetPresetIdToLoad + j), (sizeof(sReverbSettings) / sizeof(struct ReverbSettingsEU)) - 1)]; reverb->windowSize = (reverbSettings->windowSize * 0x40); reverb->downsampleRate = reverbSettings->downsampleRate; reverb->reverbGain = reverbSettings->gain; reverb->useReverb = TRUE; if (reverb->windowSize > REVERB_WINDOW_SIZE_MAX) { reverb->windowSize = REVERB_WINDOW_SIZE_MAX; } if (sAudioIsInitialized) { bzero(reverb->ringBuffer.left, (REVERB_WINDOW_SIZE_MAX * 4)); } else { reverb->resampleRate = (0x8000 / reverb->downsampleRate); reverb->resampleStateLeft = soundAlloc(&gNotesAndBuffersPool, (16 * sizeof(s16))); reverb->resampleStateRight = soundAlloc(&gNotesAndBuffersPool, (16 * sizeof(s16))); reverb->unk24 = soundAlloc(&gNotesAndBuffersPool, (16 * sizeof(s16))); reverb->unk28 = soundAlloc(&gNotesAndBuffersPool, (16 * sizeof(s16))); for (i = 0; i < gAudioBufferParameters.updatesPerFrame; i++) { mem = soundAlloc(&gNotesAndBuffersPool, DEFAULT_LEN_2CH); reverb->items[0][i].toDownsampleLeft = mem; reverb->items[0][i].toDownsampleRight = (mem + (DEFAULT_LEN_1CH / sizeof(s16))); mem = soundAlloc(&gNotesAndBuffersPool, DEFAULT_LEN_2CH); reverb->items[1][i].toDownsampleLeft = mem; reverb->items[1][i].toDownsampleRight = (mem + (DEFAULT_LEN_1CH / sizeof(s16))); } } reverb->ringBuffer.right = &reverb->ringBuffer.left[reverb->windowSize]; reverb->nextRingBufferPos = 0; reverb->unkC = 0; reverb->curFrame = 0; reverb->bufSizePerChannel = reverb->windowSize; reverb->framesLeftToIgnore = 2; if (reverb->downsampleRate != 1) { reverb->resampleRate = (0x8000 / reverb->downsampleRate); if (sAudioIsInitialized) { bzero(reverb->resampleStateLeft, (16 * sizeof(s16))); bzero(reverb->resampleStateRight, (16 * sizeof(s16))); bzero(reverb->unk24, (16 * sizeof(s16))); bzero(reverb->unk28, (16 * sizeof(s16))); // All reverb downsample buffers are adjacent in memory, so clear them all in a single call bzero(reverb->items[0][0].toDownsampleLeft, (DEFAULT_LEN_1CH * 4 * gAudioBufferParameters.updatesPerFrame)); } } } } #else void init_reverb_us(s32 presetId) { s16 *mem; s32 i; s32 reverbWindowSize = gReverbSettings[presetId].windowSize; gReverbDownsampleRate = gReverbSettings[presetId].downsampleRate; #ifdef BETTER_REVERB // This will likely crash if given an invalid preset value. Adding a safety check here isn't worth the usability interference. struct BetterReverbSettings *betterReverbPreset = &gBetterReverbSettings[gBetterReverbPreset]; betterReverbLightweight = betterReverbPreset->useLightweightSettings; betterReverbDownsampleRate = betterReverbPreset->downsampleRate; monoReverb = betterReverbPreset->isMono; reverbFilterCount = betterReverbPreset->filterCount; betterReverbWindowsSize = betterReverbPreset->windowSize; betterReverbRevIndex = betterReverbPreset->reverbIndex; betterReverbGainIndex = betterReverbPreset->gainIndex; gReverbMults[SYNTH_CHANNEL_LEFT] = betterReverbPreset->reverbMultsL; gReverbMults[SYNTH_CHANNEL_RIGHT] = betterReverbPreset->reverbMultsR; if (betterReverbDownsampleRate <= 0) { toggleBetterReverb = FALSE; if (betterReverbWindowsSize >= 0) reverbWindowSize = betterReverbWindowsSize; } else { toggleBetterReverb = TRUE; gReverbDownsampleRate = (1 << (betterReverbDownsampleRate - 1)); if (betterReverbWindowsSize >= 0) { reverbWindowSize = betterReverbWindowsSize; reverbWindowSize /= gReverbDownsampleRate; if (reverbWindowSize < DEFAULT_LEN_2CH && betterReverbWindowsSize != 0) // Minimum window size to not overflow reverbWindowSize = DEFAULT_LEN_2CH; } } reverbFilterCount -= reverbFilterCount % 3; if (reverbFilterCount > NUM_ALLPASS) { reverbFilterCount = NUM_ALLPASS; } else if (reverbFilterCount < 3) { reverbFilterCount = 3; } #endif if (reverbWindowSize > 0) gSynthesisReverb.useReverb = TRUE; else gSynthesisReverb.useReverb = FALSE; if (reverbWindowSize > REVERB_WINDOW_SIZE_MAX) { reverbWindowSize = REVERB_WINDOW_SIZE_MAX; } // Both left and right channels are allocated/cleared together, then separated based on the reverb window size if (!sAudioIsInitialized) { gSynthesisReverb.ringBuffer.left = soundAlloc(&gNotesAndBuffersPool, REVERB_WINDOW_SIZE_MAX * 2 * sizeof(s16)); gSynthesisReverb.resampleStateLeft = soundAlloc(&gNotesAndBuffersPool, (16 * sizeof(s16))); gSynthesisReverb.resampleStateRight = soundAlloc(&gNotesAndBuffersPool, (16 * sizeof(s16))); gSynthesisReverb.unk24 = soundAlloc(&gNotesAndBuffersPool, (16 * sizeof(s16))); gSynthesisReverb.unk28 = soundAlloc(&gNotesAndBuffersPool, (16 * sizeof(s16))); for (i = 0; i < gAudioUpdatesPerFrame; i++) { mem = soundAlloc(&gNotesAndBuffersPool, DEFAULT_LEN_2CH); gSynthesisReverb.items[0][i].toDownsampleLeft = mem; gSynthesisReverb.items[0][i].toDownsampleRight = (mem + (DEFAULT_LEN_1CH / sizeof(s16))); mem = soundAlloc(&gNotesAndBuffersPool, DEFAULT_LEN_2CH); gSynthesisReverb.items[1][i].toDownsampleLeft = mem; gSynthesisReverb.items[1][i].toDownsampleRight = (mem + (DEFAULT_LEN_1CH / sizeof(s16))); } } else { bzero(gSynthesisReverb.ringBuffer.left, (REVERB_WINDOW_SIZE_MAX * 2 * sizeof(s16))); } gSynthesisReverb.ringBuffer.right = &gSynthesisReverb.ringBuffer.left[reverbWindowSize]; gSynthesisReverb.nextRingBufferPos = 0; gSynthesisReverb.unkC = 0; gSynthesisReverb.curFrame = 0; gSynthesisReverb.bufSizePerChannel = reverbWindowSize; gSynthesisReverb.reverbGain = gReverbSettings[presetId].gain; gSynthesisReverb.framesLeftToIgnore = 2; if (gReverbDownsampleRate != 1) { gSynthesisReverb.resampleFlags = A_INIT; gSynthesisReverb.resampleRate = (0x8000 / gReverbDownsampleRate); if (sAudioIsInitialized) { bzero(gSynthesisReverb.resampleStateLeft, (16 * sizeof(s16))); bzero(gSynthesisReverb.resampleStateRight, (16 * sizeof(s16))); bzero(gSynthesisReverb.unk24, (16 * sizeof(s16))); bzero(gSynthesisReverb.unk28, (16 * sizeof(s16))); // All reverb downsample buffers are adjacent in memory, so clear them all in a single call bzero(gSynthesisReverb.items[0][0].toDownsampleLeft, (DEFAULT_LEN_1CH * 4 * gAudioUpdatesPerFrame)); } } #ifdef BETTER_REVERB if (!gSynthesisReverb.useReverb) toggleBetterReverb = FALSE; if (betterReverbPreset->gain > 0) gSynthesisReverb.reverbGain = (u16) betterReverbPreset->gain; if (!sAudioIsInitialized) initialize_better_reverb_buffers(); // This does not have to be reset after being initialized for the first time, which would help speed up load times. // However, resetting this allows for proper clearing of the reverb buffers, as well as dynamic customization of the delays array. set_better_reverb_buffers(betterReverbPreset->delaysL, betterReverbPreset->delaysR); #endif } #endif #if defined(VERSION_JP) || defined(VERSION_US) void audio_reset_session(s32 reverbPresetId) { if (sAudioIsInitialized) { if (gAudioLoadLock != AUDIO_LOCK_UNINITIALIZED) { gAudioLoadLock = AUDIO_LOCK_LOADING; if (!gIsVC) { gAudioFrameCount = 0; while (gAudioFrameCount < 1) { // spin } } for (s32 i = 0; i < gMaxSimultaneousNotes; i++) { gNotes[i].enabled = FALSE; } persistent_pool_clear(&gSeqLoadedPool.persistent); persistent_pool_clear(&gBankLoadedPool.persistent); temporary_pool_clear( &gSeqLoadedPool.temporary); temporary_pool_clear( &gBankLoadedPool.temporary); reset_bank_and_seq_load_status(); init_reverb_us(reverbPresetId); bzero(&gAiBuffers[0][0], (AIBUFFER_LEN * NUMAIBUFFERS)); if (gAudioLoadLock != AUDIO_LOCK_UNINITIALIZED) { gAudioLoadLock = AUDIO_LOCK_NOT_LOADING; } } return; } #else void audio_reset_session(void) { if (sAudioIsInitialized) { persistent_pool_clear(&gSeqLoadedPool.persistent); persistent_pool_clear(&gBankLoadedPool.persistent); temporary_pool_clear(&gSeqLoadedPool.temporary); temporary_pool_clear(&gBankLoadedPool.temporary); #ifdef VERSION_SH persistent_pool_clear(&gUnusedLoadedPool.persistent); temporary_pool_clear(&gUnusedLoadedPool.temporary); #endif reset_bank_and_seq_load_status(); init_reverb_eu(); return; } struct AudioSessionSettingsEU *preset = &gAudioSessionPresets[0]; #endif #if defined(VERSION_JP) || defined(VERSION_US) s8 updatesPerFrame; #endif #ifdef PUPPYPRINT_DEBUG OSTime first = osGetTime(); #endif s32 j; s32 persistentMem; s32 temporaryMem; s32 totalMem; s32 wantMisc; /* #if defined(VERSION_JP) || defined(VERSION_US) s32 frames; s32 remainingDmas; s32 i; #endif */ #ifdef VERSION_EU eu_stubbed_printf_1("Heap Reconstruct Start %x\n", gAudioResetPresetIdToLoad); #endif /* #if defined(VERSION_JP) || defined(VERSION_US) if (gAudioLoadLock != AUDIO_LOCK_UNINITIALIZED) { decrease_reverb_gain(); for (i = 0; i < gMaxSimultaneousNotes; i++) { if (gNotes[i].enabled && gNotes[i].adsr.state != ADSR_STATE_DISABLED) { gNotes[i].adsr.fadeOutVel = 0x8000 / gAudioUpdatesPerFrame; gNotes[i].adsr.action |= ADSR_ACTION_RELEASE; } } // Wait for all notes to stop playing frames = 0; for (;;) { wait_for_audio_frames(1); frames++; if (frames > 4 * 60) { // Break after 4 seconds break; } for (i = 0; i < gMaxSimultaneousNotes; i++) { if (gNotes[i].enabled) break; } if (i == gMaxSimultaneousNotes) { // All zero, break early break; } } // Wait for the reverb to finish as well decrease_reverb_gain(); wait_for_audio_frames(3); // The audio interface is double buffered; thus, we have to take the // load lock for 2 frames for the buffers to free up before we can // repurpose memory. Make that 3 frames, just in case. gAudioLoadLock = AUDIO_LOCK_LOADING; wait_for_audio_frames(3); remainingDmas = gCurrAudioFrameDmaCount; while (remainingDmas > 0) { for (i = 0; i < gCurrAudioFrameDmaCount; i++) { if (osRecvMesg(&gCurrAudioFrameDmaQueue, NULL, OS_MESG_NOBLOCK) == 0) remainingDmas--; } } gCurrAudioFrameDmaCount = 0; bzero(&gAiBuffers[0][0], (AIBUFFER_LEN * NUMAIBUFFERS)); } #endif */ gSampleDmaNumListItems = 0; #if defined(VERSION_EU) || defined(VERSION_SH) gAudioBufferParameters.frequency = preset->frequency; gAudioBufferParameters.aiFrequency = osAiSetFrequency(gAudioBufferParameters.frequency); gAudioBufferParameters.samplesPerFrameTarget = ALIGN16(gAudioBufferParameters.frequency / gRefreshRate); gAudioBufferParameters.minAiBufferLength = gAudioBufferParameters.samplesPerFrameTarget - 0x10; gAudioBufferParameters.maxAiBufferLength = gAudioBufferParameters.samplesPerFrameTarget + 0x10; #ifdef VERSION_SH gAudioBufferParameters.updatesPerFrame = (gAudioBufferParameters.samplesPerFrameTarget + 0x10) / 192 + 1; gAudioBufferParameters.samplesPerUpdate = (gAudioBufferParameters.samplesPerFrameTarget / gAudioBufferParameters.updatesPerFrame) & -8; #else gAudioBufferParameters.updatesPerFrame = (gAudioBufferParameters.samplesPerFrameTarget + 0x10) / 160 + 1; gAudioBufferParameters.samplesPerUpdate = (gAudioBufferParameters.samplesPerFrameTarget / gAudioBufferParameters.updatesPerFrame) & 0xfff8; #endif gAudioBufferParameters.samplesPerUpdateMax = gAudioBufferParameters.samplesPerUpdate + 8; gAudioBufferParameters.samplesPerUpdateMin = gAudioBufferParameters.samplesPerUpdate - 8; gAudioBufferParameters.resampleRate = 32000.0f / FLOAT_CAST(gAudioBufferParameters.frequency); #ifdef VERSION_SH gAudioBufferParameters.unkUpdatesPerFrameScaled = (1.0f / 256.0f) / gAudioBufferParameters.updatesPerFrame; #else gAudioBufferParameters.unkUpdatesPerFrameScaled = (3.0f / 1280.0f) / gAudioBufferParameters.updatesPerFrame; #endif gAudioBufferParameters.updatesPerFrameInv = 1.0f / gAudioBufferParameters.updatesPerFrame; gMaxSimultaneousNotes = preset->maxSimultaneousNotes; gVolume = preset->volume; gTempoInternalToExternal = (u32) (gAudioBufferParameters.updatesPerFrame * 2880000.0f / gTatumsPerBeat / D_EU_802298D0); gAudioBufferParameters.presetUnk4 = preset->unk1; gAudioBufferParameters.samplesPerFrameTarget *= gAudioBufferParameters.presetUnk4; gAudioBufferParameters.maxAiBufferLength *= gAudioBufferParameters.presetUnk4; gAudioBufferParameters.minAiBufferLength *= gAudioBufferParameters.presetUnk4; gAudioBufferParameters.updatesPerFrame *= gAudioBufferParameters.presetUnk4; if (gIsConsole) gMaxSimultaneousNotes = MAX_SIMULTANEOUS_NOTES_CONSOLE; else gMaxSimultaneousNotes = MAX_SIMULTANEOUS_NOTES_EMULATOR; if (gMaxSimultaneousNotes > MAX_SIMULTANEOUS_NOTES) gMaxSimultaneousNotes = MAX_SIMULTANEOUS_NOTES; else if (gMaxSimultaneousNotes < 0) gMaxSimultaneousNotes = 0; #ifdef VERSION_SH if (gAudioBufferParameters.presetUnk4 >= 2) { gAudioBufferParameters.maxAiBufferLength -= 0x10; } gMaxAudioCmds = gMaxSimultaneousNotes * 0x14 * gAudioBufferParameters.updatesPerFrame + preset->numReverbs * 0x20 + 0x1E0; #else gMaxAudioCmds = gMaxSimultaneousNotes * 0x10 * gAudioBufferParameters.updatesPerFrame + preset->numReverbs * 0x20 + 0x300; #endif #else gAiFrequency = osAiSetFrequency(gAudioSessionSettings.frequency); gMaxSimultaneousNotes = gAudioSessionSettings.maxSimultaneousNotes; gSamplesPerFrameTarget = ALIGN16(gAiFrequency / 60); gVolume = gAudioSessionSettings.volume; gMinAiBufferLength = gSamplesPerFrameTarget - 0x10; gAudioUpdatesPerFrame = updatesPerFrame = gSamplesPerFrameTarget / 160 + 1; if (gIsConsole) gMaxSimultaneousNotes = MAX_SIMULTANEOUS_NOTES_CONSOLE; else gMaxSimultaneousNotes = MAX_SIMULTANEOUS_NOTES_EMULATOR; if (gMaxSimultaneousNotes > MAX_SIMULTANEOUS_NOTES) gMaxSimultaneousNotes = MAX_SIMULTANEOUS_NOTES; else if (gMaxSimultaneousNotes < 0) gMaxSimultaneousNotes = 0; // Compute conversion ratio from the internal unit tatums/tick to the // external beats/minute (JP) or tatums/minute (US). In practice this is // 300 on JP and 14360 on US. #ifdef VERSION_JP gTempoInternalToExternal = updatesPerFrame * 3600 / gTatumsPerBeat; #else gTempoInternalToExternal = (u32)(updatesPerFrame * 2880000.0f / gTatumsPerBeat / 16.713f); #endif gMaxAudioCmds = gMaxSimultaneousNotes * 20 * updatesPerFrame + 320; #endif #if defined(VERSION_SH) persistentMem = DOUBLE_SIZE_ON_64_BIT(preset->persistentSeqMem + preset->persistentBankMem + preset->unk18 + preset->unkMem28 + 0x10); temporaryMem = DOUBLE_SIZE_ON_64_BIT(preset->temporarySeqMem + preset->temporaryBankMem + preset->unk24 + preset->unkMem2C + 0x10); #else persistentMem = DOUBLE_SIZE_ON_64_BIT(gAudioSessionSettings.persistentSeqMem + gAudioSessionSettings.persistentBankMem); temporaryMem = DOUBLE_SIZE_ON_64_BIT(gAudioSessionSettings.temporarySeqMem + gAudioSessionSettings.temporaryBankMem); #endif totalMem = persistentMem + temporaryMem; wantMisc = gAudioSessionPool.size - totalMem - BETTER_REVERB_SIZE; sSessionPoolSplit.wantSeq = wantMisc; sSessionPoolSplit.wantCustom = totalMem; session_pools_init(&sSessionPoolSplit); sSeqAndBankPoolSplit.wantPersistent = persistentMem; sSeqAndBankPoolSplit.wantTemporary = temporaryMem; seq_and_bank_pool_init(&sSeqAndBankPoolSplit); sPersistentCommonPoolSplit.wantSeq = DOUBLE_SIZE_ON_64_BIT(gAudioSessionSettings.persistentSeqMem); sPersistentCommonPoolSplit.wantBank = DOUBLE_SIZE_ON_64_BIT(gAudioSessionSettings.persistentBankMem); #ifdef VERSION_SH sPersistentCommonPoolSplit.wantUnused = preset->unk18; #else sPersistentCommonPoolSplit.wantUnused = 0; #endif persistent_pools_init(&sPersistentCommonPoolSplit); sTemporaryCommonPoolSplit.wantSeq = DOUBLE_SIZE_ON_64_BIT(gAudioSessionSettings.temporarySeqMem); sTemporaryCommonPoolSplit.wantBank = DOUBLE_SIZE_ON_64_BIT(gAudioSessionSettings.temporaryBankMem); #ifdef VERSION_SH sTemporaryCommonPoolSplit.wantUnused = preset->unk24; #else sTemporaryCommonPoolSplit.wantUnused = 0; #endif temporary_pools_init(&sTemporaryCommonPoolSplit); #ifdef VERSION_SH unk_pools_init(preset->unkMem28, preset->unkMem2C); #endif reset_bank_and_seq_load_status(); #if defined(VERSION_JP) || defined(VERSION_US) for (j = 0; j < 2; j++) { gAudioCmdBuffers[j] = soundAlloc(&gNotesAndBuffersPool, ALIGN16(gMaxAudioCmds * sizeof(u64))); } #endif gNotes = soundAlloc(&gNotesAndBuffersPool, ALIGN16(gMaxSimultaneousNotes * sizeof(struct Note))); note_init_all(); init_note_free_list(); #if defined(VERSION_EU) || defined(VERSION_SH) // NOTE: Cannot be computed automatically in the audio heap define; approximation to be used instead gNoteSubsEu = soundAlloc(&gNotesAndBuffersPool, ALIGN16((gAudioBufferParameters.updatesPerFrame * gMaxSimultaneousNotes) * sizeof(struct NoteSubEu))); for (j = 0; j != 2; j++) { gAudioCmdBuffers[j] = soundAlloc(&gNotesAndBuffersPool, ALIGN16(gMaxAudioCmds * sizeof(u64))); } init_reverb_eu(); #else init_reverb_us(reverbPresetId); #endif init_sample_dma_buffers(gMaxSimultaneousNotes); #if defined(VERSION_EU) build_vol_rampings_table(0, gAudioBufferParameters.samplesPerUpdate); #endif #ifdef VERSION_SH D_SH_8034F68C = 0; D_SH_803479B4 = 4096; #endif osWritebackDCacheAll(); #if defined(VERSION_JP) || defined(VERSION_US) if (gAudioLoadLock != AUDIO_LOCK_UNINITIALIZED) { gAudioLoadLock = AUDIO_LOCK_NOT_LOADING; } #endif append_puppyprint_log("Audio Initialised in %d" PP_CYCLE_STRING ".", (s32)PP_CYCLE_CONV(osGetTime() - first)); sAudioIsInitialized = TRUE; } #ifdef VERSION_SH void *unk_pool1_lookup(s32 poolIdx, s32 id) { s32 i; for (i = 0; i < gUnkPool1.pool.numAllocatedEntries; i++) { if (gUnkPool1.entries[i].poolIndex == poolIdx && gUnkPool1.entries[i].id == id) { return gUnkPool1.entries[i].ptr; } } return NULL; } void *unk_pool1_alloc(s32 poolIndex, s32 arg1, u32 size) { s32 pos = gUnkPool1.pool.numAllocatedEntries; void *ret = sound_alloc_uninitialized(&gUnkPool1.pool, size); gUnkPool1.entries[pos].ptr = ret; if (ret == NULL) { return NULL; } gUnkPool1.entries[pos].poolIndex = poolIndex; gUnkPool1.entries[pos].id = arg1; gUnkPool1.entries[pos].size = size; return ret; } u8 *func_sh_802f1d40(u32 size, s32 bank, u8 *arg2, s8 medium) { struct UnkEntry *ret = func_sh_802f1ec4(size); if (ret != NULL) { ret->bankId = bank; ret->dstAddr = arg2; ret->medium = medium; return ret->srcAddr; } return NULL; } u8 *func_sh_802f1d90(u32 size, s32 bank, u8 *arg2, s8 medium) { struct UnkEntry *ret = unk_pool2_alloc(size); if (ret != NULL) { ret->bankId = bank; ret->dstAddr = arg2; ret->medium = medium; return ret->srcAddr; } return NULL; } void unk_pools_init(u32 size1, u32 size2) { void *mem = sound_alloc_uninitialized(&gPersistentCommonPool, size1); if (mem == NULL) { gUnkPool2.pool.size = 0; } else { sound_alloc_pool_init(&gUnkPool2.pool, mem, size1); } mem = sound_alloc_uninitialized(&gTemporaryCommonPool, size2); if (mem == NULL) { gUnkPool3.pool.size = 0; } else { sound_alloc_pool_init(&gUnkPool3.pool, mem, size2); } gUnkPool2.numEntries = 0; gUnkPool3.numEntries = 0; } struct UnkEntry *func_sh_802f1ec4(u32 size) { u8 *cur; s32 i; struct UnkStructSH8034EC88 *unkStruct; struct UnkPool *pool = &gUnkPool3; u8 *itemStart; u8 *itemEnd; u8 *phi_s3 = pool->pool.cur; u8 *memLocation = sound_alloc_uninitialized(&pool->pool, size); if (memLocation == NULL) { cur = pool->pool.cur; pool->pool.cur = pool->pool.start; memLocation = sound_alloc_uninitialized(&pool->pool, size); if (memLocation == NULL) { pool->pool.cur = cur; return NULL; } phi_s3 = pool->pool.start; } u8 *temp_s2 = pool->pool.cur; s32 chosenIndex = -1; for (i = 0; i < D_SH_8034F68C; i++) { unkStruct = &D_SH_8034EC88[i]; if (unkStruct->isFree == FALSE) { itemStart = unkStruct->ramAddr; itemEnd = unkStruct->ramAddr + unkStruct->sample->size - 1; if (itemEnd < phi_s3 && itemStart < phi_s3) { continue; } if (itemEnd >= temp_s2 && itemStart >= temp_s2) { continue; } unkStruct->isFree = TRUE; } } for (i = 0; i < pool->numEntries; i++) { if (pool->entries[i].used == FALSE) { continue; } itemStart = pool->entries[i].srcAddr; itemEnd = ((itemStart + pool->entries[i].size) - 1); if (itemEnd < phi_s3 && itemStart < phi_s3) { continue; } if (itemEnd >= temp_s2 && itemStart >= temp_s2) { continue; } func_sh_802f2158(&pool->entries[i]); if (chosenIndex == -1) { chosenIndex = i; } } if (chosenIndex == -1) { chosenIndex = pool->numEntries++; } pool->entries[chosenIndex].used = TRUE; pool->entries[chosenIndex].srcAddr = memLocation; pool->entries[chosenIndex].size = size; return &pool->entries[chosenIndex]; } void func_sh_802f2158(struct UnkEntry *entry) { s32 idx; s32 seqCount; s32 bankId1, bankId2; s32 instId, drumId; struct Drum *drum; struct Instrument *inst; seqCount = gAlCtlHeader->seqCount; for (idx = 0; idx < seqCount; idx++) { bankId1 = gCtlEntries[idx].bankId1; bankId2 = gCtlEntries[idx].bankId2; if ((bankId1 != 0xff && entry->bankId == bankId1) || (bankId2 != 0xff && entry->bankId == bankId2) || entry->bankId == 0) { if (get_bank_or_seq(1, 2, idx) != NULL) { if (IS_BANK_LOAD_COMPLETE(idx)) { for (instId = 0; instId < gCtlEntries[idx].numInstruments; instId++) { inst = get_instrument_inner(idx, instId); if (inst != NULL) { if (inst->normalRangeLo != 0) { func_sh_802F2320(entry, inst->lowNotesSound.sample); } if (inst->normalRangeHi != 127) { func_sh_802F2320(entry, inst->highNotesSound.sample); } func_sh_802F2320(entry, inst->normalNotesSound.sample); } } for (drumId = 0; drumId < gCtlEntries[idx].numDrums; drumId++) { drum = get_drum(idx, drumId); if (drum != NULL) { func_sh_802F2320(entry, drum->sound.sample); } } } } } } } void func_sh_802F2320(struct UnkEntry *entry, struct AudioBankSample *sample) { if (sample != NULL && sample->sampleAddr == entry->srcAddr) { sample->sampleAddr = entry->dstAddr; sample->medium = entry->medium; } } struct UnkEntry *unk_pool2_alloc(u32 size) { void *data; struct UnkEntry *ret; s32 *numEntries = &gUnkPool2.numEntries; data = sound_alloc_uninitialized(&gUnkPool2.pool, size); if (data == NULL) { return NULL; } ret = &gUnkPool2.entries[*numEntries]; ret->used = TRUE; ret->srcAddr = data; ret->size = size; (*numEntries)++; return ret; } void func_sh_802f23ec(void) { s32 i, idx; s32 seqCount; s32 bankId1, bankId2; s32 instId, drumId; struct Drum *drum; struct Instrument *inst; struct UnkEntry *entry = NULL; //! @bug: not initialized but nevertheless used seqCount = gAlCtlHeader->seqCount; for (idx = 0; idx < seqCount; idx++) { bankId1 = gCtlEntries[idx].bankId1; bankId2 = gCtlEntries[idx].bankId2; if ((bankId1 != 0xffu && entry->bankId == bankId1) || (bankId2 != 0xff && entry->bankId == bankId2) || entry->bankId == 0) { if (get_bank_or_seq(1, 3, idx) != NULL) { if (IS_BANK_LOAD_COMPLETE(idx)) { for (i = 0; i < gUnkPool2.numEntries; i++) { entry = &gUnkPool2.entries[i]; for (instId = 0; instId < gCtlEntries[idx].numInstruments; instId++) { inst = get_instrument_inner(idx, instId); if (inst != NULL) { if (inst->normalRangeLo != 0) { func_sh_802F2320(entry, inst->lowNotesSound.sample); } if (inst->normalRangeHi != 127) { func_sh_802F2320(entry, inst->highNotesSound.sample); } func_sh_802F2320(entry, inst->normalNotesSound.sample); } } for (drumId = 0; drumId < gCtlEntries[idx].numDrums; drumId++) { drum = get_drum(idx, drumId); if (drum != NULL) { func_sh_802F2320(entry, drum->sound.sample); } } } } } } } } #endif