sm64pc/src/audio/heap.c

942 lines
31 KiB
C
Raw Permalink Normal View History

2019-08-25 04:46:40 +00:00
#include <ultra64.h>
#include <macros.h>
2020-03-02 03:42:52 +00:00
#include "heap.h"
2019-08-25 04:46:40 +00:00
#include "data.h"
#include "load.h"
#include "synthesis.h"
#include "seqplayer.h"
#include "effects.h"
#define ALIGN16(val) (((val) + 0xF) & ~0xF)
2019-11-03 19:36:27 +00:00
struct PoolSplit {
2019-08-25 04:46:40 +00:00
u32 wantSeq;
u32 wantBank;
u32 wantUnused;
u32 wantCustom;
}; // size = 0x10
2019-11-03 19:36:27 +00:00
struct PoolSplit2 {
2019-08-25 04:46:40 +00:00
u32 wantPersistent;
u32 wantTemporary;
}; // size = 0x8
2020-02-03 05:51:26 +00:00
#ifndef VERSION_EU
2019-11-03 19:36:27 +00:00
s16 gVolume;
s8 gReverbDownsampleRate;
u8 sReverbDownsampleRateLog; // never read
2020-02-03 05:51:26 +00:00
#endif
2019-08-25 04:46:40 +00:00
2019-11-03 19:36:27 +00:00
struct SoundAllocPool gAudioSessionPool;
struct SoundAllocPool gAudioInitPool;
struct SoundAllocPool gNotesAndBuffersPool;
2020-03-02 03:42:52 +00:00
u8 sAudioHeapPad[0x20]; // probably two unused pools
2019-11-03 19:36:27 +00:00
struct SoundAllocPool gSeqAndBankPool;
struct SoundAllocPool gPersistentCommonPool;
struct SoundAllocPool gTemporaryCommonPool;
2020-02-03 05:51:26 +00:00
2019-08-25 04:46:40 +00:00
struct SoundMultiPool gSeqLoadedPool;
struct SoundMultiPool gBankLoadedPool;
struct SoundMultiPool gUnusedLoadedPool;
2019-11-03 19:36:27 +00:00
struct PoolSplit sSessionPoolSplit;
struct PoolSplit2 sSeqAndBankPoolSplit;
struct PoolSplit sPersistentCommonPoolSplit;
struct PoolSplit sTemporaryCommonPoolSplit;
2019-08-25 04:46:40 +00:00
u8 gBankLoadStatus[0x40];
u8 gSeqLoadStatus[0x100];
2020-02-03 05:51:26 +00:00
#ifdef VERSION_EU
volatile u8 gAudioResetStatus;
u8 gAudioResetPresetIdToLoad;
s32 gAudioResetFadeOutFramesLeft;
#endif
2019-08-25 04:46:40 +00:00
u8 gAudioUnusedBuffer[0x1000];
2019-12-02 02:52:53 +00:00
extern s32 gMaxAudioCmds;
2019-08-25 04:46:40 +00:00
2020-02-03 05:51:26 +00:00
#ifdef 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) {
deriv *= x;
}
if (degree & 2) {
deriv *= deg2;
}
if (degree & 4) {
deriv *= deg4;
}
if (degree & 8) {
deriv *= deg8;
}
fx = deriv * x - d;
deriv = k * deriv;
return x - fx / deriv;
}
/**
* Assuming 'k' in [9, 24],
* Computes d ^ (1/k)
*
* @return the root, or 1.0 if d is 0
*/
2020-03-02 03:42:52 +00:00
f64 kth_root(f64 d, s32 k) {
2020-02-03 05:51:26 +00:00
f64 root = 1.5;
f64 next;
f64 diff;
s32 i;
if (d == 0.0) {
root = 1.0;
} else {
for (i = 0; i < 64; i++) {
if (1) {
}
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;
}
#endif
#ifdef VERSION_EU
2020-03-02 03:42:52 +00:00
void build_vol_rampings_table(s32 UNUSED unused, s32 len) {
2020-02-03 05:51:26 +00:00
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
2019-08-25 04:46:40 +00:00
void reset_bank_and_seq_load_status(void) {
s32 i;
for (i = 0; i < 64; i++) {
gBankLoadStatus[i] = SOUND_LOAD_STATUS_NOT_LOADED;
}
for (i = 0; i < 256; i++) {
gSeqLoadStatus[i] = SOUND_LOAD_STATUS_NOT_LOADED;
}
}
void discard_bank(s32 bankId) {
s32 i;
for (i = 0; i < gMaxSimultaneousNotes; i++) {
struct Note *note = &gNotes[i];
2020-02-03 05:51:26 +00:00
#ifdef VERSION_EU
if (note->noteSubEu.bankId == bankId) {
#else
2019-08-25 04:46:40 +00:00
if (note->bankId == bankId) {
2020-02-03 05:51:26 +00:00
#endif
2019-08-25 04:46:40 +00:00
if (note->priority >= NOTE_PRIORITY_MIN) {
note->parentLayer->enabled = FALSE;
note->parentLayer->finished = TRUE;
}
note_disable(note);
audio_list_remove(&note->listItem);
audio_list_push_back(&gNoteFreeLists.disabled, &note->listItem);
}
}
}
void discard_sequence(s32 seqId) {
s32 i;
for (i = 0; i < SEQUENCE_PLAYERS; i++) {
2019-09-01 19:50:50 +00:00
if (gSequencePlayers[i].enabled && gSequencePlayers[i].seqId == seqId) {
2020-02-03 05:51:26 +00:00
#ifdef VERSION_EU
sequence_player_disable(&gSequencePlayers[i]);
#else
2019-08-25 04:46:40 +00:00
sequence_player_disable(gSequencePlayers + i);
2020-02-03 05:51:26 +00:00
#endif
2019-09-01 19:50:50 +00:00
}
2019-08-25 04:46:40 +00:00
}
}
void *soundAlloc(struct SoundAllocPool *pool, u32 size) {
2020-02-03 05:51:26 +00:00
#ifdef VERSION_EU
u8 *start;
u8 *pos;
u32 alignedSize = ALIGN16(size);
start = pool->cur;
if (start + alignedSize <= pool->start + pool->size) {
pool->cur += alignedSize;
for (pos = start; pos < pool->cur; pos++) {
*pos = 0;
}
} else {
return NULL;
}
return start;
#else
u8 *start;
2019-08-25 04:46:40 +00:00
s32 last;
s32 i;
if ((pool->cur + ALIGN16(size) <= pool->size + pool->start)) {
start = pool->cur;
pool->cur += ALIGN16(size);
last = pool->cur - start - 1;
2019-09-01 19:50:50 +00:00
for (i = 0; i <= last; i++) {
2019-08-25 04:46:40 +00:00
start[i] = 0;
2019-09-01 19:50:50 +00:00
}
2019-08-25 04:46:40 +00:00
} else {
return NULL;
}
return start;
2020-02-03 05:51:26 +00:00
#endif
2019-08-25 04:46:40 +00:00
}
2019-11-03 19:36:27 +00:00
void sound_alloc_pool_init(struct SoundAllocPool *pool, void *memAddr, u32 size) {
2019-12-02 02:52:53 +00:00
pool->cur = pool->start = (u8 *) ALIGN16((uintptr_t) memAddr);
2019-11-03 19:36:27 +00:00
pool->size = size;
2019-08-25 04:46:40 +00:00
pool->unused = 0;
}
2019-11-03 19:36:27 +00:00
void persistent_pool_clear(struct PersistentPool *persistent) {
2019-08-25 04:46:40 +00:00
persistent->pool.unused = 0;
persistent->pool.cur = persistent->pool.start;
persistent->numEntries = 0;
}
2019-11-03 19:36:27 +00:00
void temporary_pool_clear(struct TemporaryPool *temporary) {
2019-08-25 04:46:40 +00:00
temporary->pool.unused = 0;
temporary->pool.cur = temporary->pool.start;
temporary->nextSide = 0;
temporary->entries[0].ptr = temporary->pool.start;
2020-02-03 05:51:26 +00:00
#ifdef VERSION_EU
temporary->entries[1].ptr = temporary->pool.start + temporary->pool.size;
#else
2019-08-25 04:46:40 +00:00
temporary->entries[1].ptr = temporary->pool.size + temporary->pool.start;
2020-02-03 05:51:26 +00:00
#endif
2019-08-25 04:46:40 +00:00
temporary->entries[0].id = -1;
temporary->entries[1].id = -1;
}
void unused_803160F8(struct SoundAllocPool *pool) {
pool->unused = 0;
pool->cur = pool->start;
}
2019-11-03 19:36:27 +00:00
void sound_init_main_pools(s32 sizeForAudioInitPool) {
sound_alloc_pool_init(&gAudioInitPool, gAudioHeap, sizeForAudioInitPool);
sound_alloc_pool_init(&gAudioSessionPool, gAudioHeap + sizeForAudioInitPool, gAudioHeapSize - sizeForAudioInitPool);
2019-08-25 04:46:40 +00:00
}
2019-11-03 19:36:27 +00:00
void session_pools_init(struct PoolSplit *a) {
gAudioSessionPool.cur = gAudioSessionPool.start;
sound_alloc_pool_init(&gNotesAndBuffersPool, soundAlloc(&gAudioSessionPool, a->wantSeq), a->wantSeq);
sound_alloc_pool_init(&gSeqAndBankPool, soundAlloc(&gAudioSessionPool, a->wantCustom), a->wantCustom);
2019-08-25 04:46:40 +00:00
}
2019-11-03 19:36:27 +00:00
void seq_and_bank_pool_init(struct PoolSplit2 *a) {
gSeqAndBankPool.cur = gSeqAndBankPool.start;
sound_alloc_pool_init(&gPersistentCommonPool, soundAlloc(&gSeqAndBankPool, a->wantPersistent), a->wantPersistent);
sound_alloc_pool_init(&gTemporaryCommonPool, soundAlloc(&gSeqAndBankPool, a->wantTemporary), a->wantTemporary);
2019-08-25 04:46:40 +00:00
}
2019-11-03 19:36:27 +00:00
void persistent_pools_init(struct PoolSplit *a) {
gPersistentCommonPool.cur = gPersistentCommonPool.start;
sound_alloc_pool_init(&gSeqLoadedPool.persistent.pool, soundAlloc(&gPersistentCommonPool, a->wantSeq), a->wantSeq);
sound_alloc_pool_init(&gBankLoadedPool.persistent.pool, soundAlloc(&gPersistentCommonPool, a->wantBank), a->wantBank);
sound_alloc_pool_init(&gUnusedLoadedPool.persistent.pool, soundAlloc(&gPersistentCommonPool, a->wantUnused),
2019-08-25 04:46:40 +00:00
a->wantUnused);
2019-11-03 19:36:27 +00:00
persistent_pool_clear(&gSeqLoadedPool.persistent);
persistent_pool_clear(&gBankLoadedPool.persistent);
persistent_pool_clear(&gUnusedLoadedPool.persistent);
2019-08-25 04:46:40 +00:00
}
2019-11-03 19:36:27 +00:00
void temporary_pools_init(struct PoolSplit *a) {
gTemporaryCommonPool.cur = gTemporaryCommonPool.start;
sound_alloc_pool_init(&gSeqLoadedPool.temporary.pool, soundAlloc(&gTemporaryCommonPool, a->wantSeq), a->wantSeq);
sound_alloc_pool_init(&gBankLoadedPool.temporary.pool, soundAlloc(&gTemporaryCommonPool, a->wantBank), a->wantBank);
sound_alloc_pool_init(&gUnusedLoadedPool.temporary.pool, soundAlloc(&gTemporaryCommonPool, a->wantUnused),
2019-08-25 04:46:40 +00:00
a->wantUnused);
2019-11-03 19:36:27 +00:00
temporary_pool_clear(&gSeqLoadedPool.temporary);
temporary_pool_clear(&gBankLoadedPool.temporary);
temporary_pool_clear(&gUnusedLoadedPool.temporary);
2019-08-25 04:46:40 +00:00
}
2020-02-03 05:51:26 +00:00
#ifndef VERSION_EU
2019-08-25 04:46:40 +00:00
static void unused_803163D4() {
}
2020-02-03 05:51:26 +00:00
#endif
2019-08-25 04:46:40 +00:00
void *alloc_bank_or_seq(struct SoundMultiPool *arg0, s32 arg1, s32 size, s32 arg3, s32 id) {
// arg3 = 0, 1 or 2?
2020-03-02 03:42:52 +00:00
struct TemporaryPool *tp; // sp30
struct PersistentPool *persistent = &arg0->persistent;
2019-08-25 04:46:40 +00:00
struct SoundAllocPool *pool;
void *ret;
2020-03-02 03:42:52 +00:00
#ifndef VERSION_EU
u16 UNUSED _firstVal;
u16 UNUSED _secondVal;
#else
u16 firstVal;
u16 secondVal;
#endif
u32 nullID = -1;
u8 *table;
u8 isSound;
#ifndef VERSION_EU
u16 firstVal;
u16 secondVal;
2019-08-25 04:46:40 +00:00
u32 bothDiscardable;
u32 leftDiscardable, rightDiscardable;
u32 leftNotLoaded, rightNotLoaded;
u32 leftAvail, rightAvail;
2020-03-02 03:42:52 +00:00
#endif
2019-08-25 04:46:40 +00:00
if (arg3 == 0) {
2020-03-02 03:42:52 +00:00
tp = &arg0->temporary;
2019-08-25 04:46:40 +00:00
if (arg0 == &gSeqLoadedPool) {
table = gSeqLoadStatus;
isSound = FALSE;
} else if (arg0 == &gBankLoadedPool) {
table = gBankLoadStatus;
isSound = TRUE;
}
2020-03-02 03:42:52 +00:00
firstVal = (tp->entries[0].id == (s8)nullID ? SOUND_LOAD_STATUS_NOT_LOADED : table[tp->entries[0].id]); // a3, a2
secondVal = (tp->entries[1].id == (s8)nullID ? SOUND_LOAD_STATUS_NOT_LOADED : table[tp->entries[1].id]); // a1
#ifndef VERSION_EU
2019-08-25 04:46:40 +00:00
leftNotLoaded = (firstVal == SOUND_LOAD_STATUS_NOT_LOADED);
leftDiscardable = (firstVal == SOUND_LOAD_STATUS_DISCARDABLE); // t0
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); // a0
if (leftNotLoaded) {
2020-03-02 03:42:52 +00:00
tp->nextSide = 0;
2019-08-25 04:46:40 +00:00
} else if (rightNotLoaded) {
2020-03-02 03:42:52 +00:00
tp->nextSide = 1;
2019-08-25 04:46:40 +00:00
} else if (bothDiscardable) {
// Use the opposite side from last time.
2020-03-02 03:42:52 +00:00
} else if (firstVal == SOUND_LOAD_STATUS_DISCARDABLE) { //??!
tp->nextSide = 0;
2019-08-25 04:46:40 +00:00
} else if (rightDiscardable) {
2020-03-02 03:42:52 +00:00
tp->nextSide = 1;
2019-08-25 04:46:40 +00:00
} else if (leftAvail) {
2020-03-02 03:42:52 +00:00
tp->nextSide = 0;
2019-08-25 04:46:40 +00:00
} else if (rightAvail) {
2020-03-02 03:42:52 +00:00
tp->nextSide = 1;
} else {
// Both left and right sides are being loaded into.
return NULL;
}
#else
if (firstVal == SOUND_LOAD_STATUS_NOT_LOADED) {
tp->nextSide = 0;
} else if (secondVal == SOUND_LOAD_STATUS_NOT_LOADED) {
tp->nextSide = 1;
} else 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 if (firstVal != SOUND_LOAD_STATUS_IN_PROGRESS) {
tp->nextSide = 0;
} else if (secondVal != SOUND_LOAD_STATUS_IN_PROGRESS) {
tp->nextSide = 1;
2019-08-25 04:46:40 +00:00
} else {
// Both left and right sides are being loaded into.
return NULL;
}
2020-03-02 03:42:52 +00:00
#endif
2019-08-25 04:46:40 +00:00
2020-03-02 03:42:52 +00:00
pool = &arg0->temporary.pool; // a1
if (tp->entries[tp->nextSide].id != (s8)nullID) {
table[tp->entries[tp->nextSide].id] = SOUND_LOAD_STATUS_NOT_LOADED;
2019-08-25 04:46:40 +00:00
if (isSound == TRUE) {
2020-03-02 03:42:52 +00:00
discard_bank(tp->entries[tp->nextSide].id);
2019-08-25 04:46:40 +00:00
}
}
2020-03-02 03:42:52 +00:00
switch (tp->nextSide) {
2019-08-25 04:46:40 +00:00
case 0:
2020-03-02 03:42:52 +00:00
tp->entries[0].ptr = pool->start;
tp->entries[0].id = id;
tp->entries[0].size = size;
2019-08-25 04:46:40 +00:00
pool->cur = pool->start + size;
2020-03-02 03:42:52 +00:00
if (tp->entries[1].ptr < pool->cur) {
2019-08-25 04:46:40 +00:00
// Throw out the entry on the other side if it doesn't fit.
// (possible @bug: what if it's currently being loaded?)
2020-03-02 03:42:52 +00:00
table[tp->entries[1].id] = SOUND_LOAD_STATUS_NOT_LOADED;
2019-08-25 04:46:40 +00:00
switch (isSound) {
case FALSE:
2020-03-02 03:42:52 +00:00
discard_sequence(tp->entries[1].id);
2019-08-25 04:46:40 +00:00
break;
case TRUE:
2020-03-02 03:42:52 +00:00
discard_bank(tp->entries[1].id);
2019-08-25 04:46:40 +00:00
break;
}
2020-03-02 03:42:52 +00:00
tp->entries[1].id = (s32)nullID;
#ifdef VERSION_EU
tp->entries[1].ptr = pool->start + pool->size;
#else
tp->entries[1].ptr = pool->size + pool->start;
#endif
2019-08-25 04:46:40 +00:00
}
2020-03-02 03:42:52 +00:00
ret = tp->entries[0].ptr;
2019-08-25 04:46:40 +00:00
break;
case 1:
2020-03-02 03:42:52 +00:00
#ifdef VERSION_EU
tp->entries[1].ptr = pool->start + pool->size - size - 0x10;
#else
tp->entries[1].ptr = pool->size + pool->start - size - 0x10;
#endif
tp->entries[1].id = id;
tp->entries[1].size = size;
2019-08-25 04:46:40 +00:00
2020-03-02 03:42:52 +00:00
if (tp->entries[1].ptr < pool->cur) {
table[tp->entries[0].id] = SOUND_LOAD_STATUS_NOT_LOADED;
2019-08-25 04:46:40 +00:00
switch (isSound) {
case FALSE:
2020-03-02 03:42:52 +00:00
discard_sequence(tp->entries[0].id);
2019-08-25 04:46:40 +00:00
break;
case TRUE:
2020-03-02 03:42:52 +00:00
discard_bank(tp->entries[0].id);
2019-08-25 04:46:40 +00:00
break;
}
2020-03-02 03:42:52 +00:00
tp->entries[0].id = (s32)nullID;
2019-08-25 04:46:40 +00:00
pool->cur = pool->start;
}
2020-03-02 03:42:52 +00:00
ret = tp->entries[1].ptr;
2019-08-25 04:46:40 +00:00
break;
default:
return NULL;
}
// Switch sides for next time in case both entries are
// SOUND_LOAD_STATUS_DISCARDABLE.
2020-03-02 03:42:52 +00:00
tp->nextSide ^= 1;
2019-08-25 04:46:40 +00:00
return ret;
}
2020-03-02 03:42:52 +00:00
#ifdef VERSION_EU
ret = soundAlloc(&arg0->persistent.pool, arg1 * size);
arg0->persistent.entries[arg0->persistent.numEntries].ptr = ret;
if (ret == NULL)
#else
2019-08-25 04:46:40 +00:00
persistent->entries[persistent->numEntries].ptr = soundAlloc(&persistent->pool, arg1 * size);
2020-03-02 03:42:52 +00:00
if (persistent->entries[persistent->numEntries].ptr == NULL)
#endif
{
2019-08-25 04:46:40 +00:00
switch (arg3) {
case 2:
2020-03-02 03:42:52 +00:00
#ifdef VERSION_EU
return alloc_bank_or_seq(arg0, arg1, size, 0, id);
#else
2019-08-25 04:46:40 +00:00
// Prevent tail call optimization.
ret = alloc_bank_or_seq(arg0, arg1, size, 0, id);
return ret;
2020-03-02 03:42:52 +00:00
#endif
2019-08-25 04:46:40 +00:00
case 1:
return NULL;
}
}
// TODO: why is this guaranteed to write <= 32 entries...?
// Because the buffer is small enough that more don't fit?
persistent->entries[persistent->numEntries].id = id;
persistent->entries[persistent->numEntries].size = size;
2020-03-02 03:42:52 +00:00
#ifdef VERSION_EU
return persistent->entries[persistent->numEntries++].ptr;
2019-08-25 04:46:40 +00:00
#else
2020-03-02 03:42:52 +00:00
persistent->numEntries++; return persistent->entries[persistent->numEntries - 1].ptr;
2020-02-03 05:51:26 +00:00
#endif
2020-03-02 03:42:52 +00:00
}
2019-08-25 04:46:40 +00:00
void *get_bank_or_seq(struct SoundMultiPool *arg0, s32 arg1, s32 id) {
u32 i;
2020-02-03 05:51:26 +00:00
UNUSED void *ret;
2019-08-25 04:46:40 +00:00
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;
}
return NULL;
} else {
struct PersistentPool *persistent = &arg0->persistent;
for (i = 0; i < persistent->numEntries; i++) {
if (id == persistent->entries[i].id) {
return persistent->entries[i].ptr;
}
}
if (arg1 == 2) {
2020-02-03 05:51:26 +00:00
#ifdef VERSION_EU
return get_bank_or_seq(arg0, 0, id);
#else
2019-08-25 04:46:40 +00:00
// Prevent tail call optimization by using a temporary.
// (Did they compile with -Wo,-notail?)
ret = get_bank_or_seq(arg0, 0, id);
return ret;
2020-02-03 05:51:26 +00:00
#endif
2019-08-25 04:46:40 +00:00
}
return NULL;
}
}
2020-02-03 05:51:26 +00:00
#ifdef VERSION_EU
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];
}
for (i = 0; i < 8; i++) {
}
for (i = 8; i < 16; i += 4) {
}
}
#endif
#ifdef VERSION_EU
void decrease_reverb_gain(void) {
s32 i;
for (i = 0; i < gNumSynthesisReverbs; i++) {
gSynthesisReverbs[i].reverbGain -= gSynthesisReverbs[i].reverbGain / 8;
}
}
#else
2019-11-03 19:36:27 +00:00
void decrease_reverb_gain(void) {
gSynthesisReverb.reverbGain -= gSynthesisReverb.reverbGain / 4;
2019-08-25 04:46:40 +00:00
}
2020-02-03 05:51:26 +00:00
#endif
2019-08-25 04:46:40 +00:00
2020-02-03 05:51:26 +00:00
#ifdef VERSION_EU
2020-03-02 03:42:52 +00:00
s32 audio_shut_down_and_reset_step(void) {
2020-02-03 05:51:26 +00:00
s32 i;
s32 j;
switch (gAudioResetStatus) {
case 5:
for (i = 0; i < SEQUENCE_PLAYERS; i++) {
sequence_player_disable(&gSequencePlayers[i]);
}
gAudioResetFadeOutFramesLeft = 4;
gAudioResetStatus--;
break;
case 4:
if (gAudioResetFadeOutFramesLeft != 0) {
gAudioResetFadeOutFramesLeft--;
decrease_reverb_gain();
} else {
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 = 16;
gAudioResetStatus--;
}
break;
case 3:
if (gAudioResetFadeOutFramesLeft != 0) {
gAudioResetFadeOutFramesLeft--;
decrease_reverb_gain();
} else {
for (i = 0; i < NUMAIBUFFERS; i++) {
for (j = 0; j < (s32) (AIBUFFER_LEN / sizeof(s16)); j++) {
gAiBuffers[i][j] = 0;
}
}
gAudioResetFadeOutFramesLeft = 4;
gAudioResetStatus--;
}
break;
case 2:
if (gAudioResetFadeOutFramesLeft != 0) {
gAudioResetFadeOutFramesLeft--;
} else {
gAudioResetStatus--;
}
break;
case 1:
audio_reset_session();
gAudioResetStatus = 0;
}
if (gAudioResetStatus < 3) {
return 0;
}
return 1;
}
#else
2019-08-25 04:46:40 +00:00
/**
* Waits until a specified number of audio frames have been created
*/
void wait_for_audio_frames(s32 frames) {
2019-12-02 02:52:53 +00:00
gAudioFrameCount = 0;
2020-05-07 18:21:22 +00:00
#ifdef TARGET_N64
2019-12-02 02:52:53 +00:00
// Sound thread will update gAudioFrameCount
while (gAudioFrameCount < frames) {
2019-10-05 19:08:05 +00:00
// spin
2019-08-25 04:46:40 +00:00
}
2020-05-07 18:21:22 +00:00
#endif
2019-08-25 04:46:40 +00:00
}
2020-02-03 05:51:26 +00:00
#endif
2019-08-25 04:46:40 +00:00
2020-02-03 05:51:26 +00:00
#ifndef VERSION_EU
2019-11-03 19:36:27 +00:00
void audio_reset_session(struct AudioSessionSettings *preset) {
2020-02-03 05:51:26 +00:00
#else
void audio_reset_session(void) {
struct AudioSessionSettingsEU *preset = &gAudioSessionPresets[gAudioResetPresetIdToLoad];
struct ReverbSettingsEU *reverbSettings;
#endif
2019-08-25 04:46:40 +00:00
s16 *mem;
2020-02-03 05:51:26 +00:00
#ifndef VERSION_EU
s8 updatesPerFrame;
2019-11-03 19:36:27 +00:00
s32 reverbWindowSize;
2020-02-03 05:51:26 +00:00
s32 k;
#endif
2019-10-05 19:08:05 +00:00
s32 i;
2019-08-25 04:46:40 +00:00
s32 j;
s32 persistentMem;
s32 temporaryMem;
s32 totalMem;
s32 wantMisc;
2020-02-03 05:51:26 +00:00
#ifndef VERSION_EU
2019-10-05 19:08:05 +00:00
s32 frames;
s32 remainingDmas;
2020-02-03 05:51:26 +00:00
#endif
#ifdef VERSION_EU
struct SynthesisReverb *reverb;
#endif
2019-08-25 04:46:40 +00:00
2020-02-03 05:51:26 +00:00
#ifndef VERSION_EU
2019-08-25 04:46:40 +00:00
if (gAudioLoadLock != AUDIO_LOCK_UNINITIALIZED) {
2019-11-03 19:36:27 +00:00
decrease_reverb_gain();
2019-08-25 04:46:40 +00:00
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++;
2019-10-05 19:08:05 +00:00
if (frames > 4 * 60) {
2019-08-25 04:46:40 +00:00
// Break after 4 seconds
break;
}
for (i = 0; i < gMaxSimultaneousNotes; i++) {
if (gNotes[i].enabled)
break;
}
if (i == gMaxSimultaneousNotes) {
// All zero, break early
break;
}
}
2019-12-02 02:52:53 +00:00
// Wait for the reverb to finish as well
2019-11-03 19:36:27 +00:00
decrease_reverb_gain();
2019-08-25 04:46:40 +00:00
wait_for_audio_frames(3);
2019-12-02 02:52:53 +00:00
// 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.
2019-08-25 04:46:40 +00:00
gAudioLoadLock = AUDIO_LOCK_LOADING;
wait_for_audio_frames(3);
2019-10-05 19:08:05 +00:00
remainingDmas = gCurrAudioFrameDmaCount;
while (remainingDmas > 0) {
2019-08-25 04:46:40 +00:00
for (i = 0; i < gCurrAudioFrameDmaCount; i++) {
if (osRecvMesg(&gCurrAudioFrameDmaQueue, NULL, OS_MESG_NOBLOCK) == 0)
2019-10-05 19:08:05 +00:00
remainingDmas--;
2019-08-25 04:46:40 +00:00
}
}
gCurrAudioFrameDmaCount = 0;
for (j = 0; j < NUMAIBUFFERS; j++) {
2020-02-03 05:51:26 +00:00
for (k = 0; k < (s32) (AIBUFFER_LEN / sizeof(s16)); k++) {
2019-08-25 04:46:40 +00:00
gAiBuffers[j][k] = 0;
}
}
}
2020-02-03 05:51:26 +00:00
#endif
2019-08-25 04:46:40 +00:00
gSampleDmaNumListItems = 0;
2020-02-03 05:51:26 +00:00
#ifdef VERSION_EU
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;
gAudioBufferParameters.updatesPerFrame = (gAudioBufferParameters.samplesPerFrameTarget + 0x10) / 160 + 1;
gAudioBufferParameters.samplesPerUpdate = (gAudioBufferParameters.samplesPerFrameTarget / gAudioBufferParameters.updatesPerFrame) & 0xfff8;
gAudioBufferParameters.samplesPerUpdateMax = gAudioBufferParameters.samplesPerUpdate + 8;
gAudioBufferParameters.samplesPerUpdateMin = gAudioBufferParameters.samplesPerUpdate - 8;
gAudioBufferParameters.resampleRate = 32000.0f / FLOAT_CAST(gAudioBufferParameters.frequency);
gAudioBufferParameters.unkUpdatesPerFrameScaled = (3.0f / 1280.0f) / gAudioBufferParameters.updatesPerFrame;
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;
gMaxAudioCmds = gMaxSimultaneousNotes * 0x10 * gAudioBufferParameters.updatesPerFrame + preset->numReverbs * 0x20 + 0x300;
#else
2019-11-03 19:36:27 +00:00
reverbWindowSize = preset->reverbWindowSize;
gAiFrequency = osAiSetFrequency(preset->frequency);
gMaxSimultaneousNotes = preset->maxSimultaneousNotes;
2019-12-02 02:52:53 +00:00
gSamplesPerFrameTarget = ALIGN16(gAiFrequency / 60);
2019-11-03 19:36:27 +00:00
gReverbDownsampleRate = preset->reverbDownsampleRate;
2019-08-25 04:46:40 +00:00
2019-11-03 19:36:27 +00:00
switch (gReverbDownsampleRate) {
2019-08-25 04:46:40 +00:00
case 1:
2019-11-03 19:36:27 +00:00
sReverbDownsampleRateLog = 0;
2019-08-25 04:46:40 +00:00
break;
case 2:
2019-11-03 19:36:27 +00:00
sReverbDownsampleRateLog = 1;
2019-08-25 04:46:40 +00:00
break;
case 4:
2019-11-03 19:36:27 +00:00
sReverbDownsampleRateLog = 2;
2019-08-25 04:46:40 +00:00
break;
case 8:
2019-11-03 19:36:27 +00:00
sReverbDownsampleRateLog = 3;
2019-08-25 04:46:40 +00:00
break;
case 16:
2019-11-03 19:36:27 +00:00
sReverbDownsampleRateLog = 4;
2019-08-25 04:46:40 +00:00
break;
default:
2019-11-03 19:36:27 +00:00
sReverbDownsampleRateLog = 0;
2019-08-25 04:46:40 +00:00
}
2019-11-03 19:36:27 +00:00
gReverbDownsampleRate = preset->reverbDownsampleRate;
gVolume = preset->volume;
2019-12-02 02:52:53 +00:00
gMinAiBufferLength = gSamplesPerFrameTarget - 0x10;
updatesPerFrame = gSamplesPerFrameTarget / 160 + 1;
gAudioUpdatesPerFrame = gSamplesPerFrameTarget / 160 + 1;
2019-10-05 19:08:05 +00:00
2019-08-25 04:46:40 +00:00
// 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
2019-10-05 19:08:05 +00:00
gTempoInternalToExternal = updatesPerFrame * 3600 / gTatumsPerBeat;
2019-08-25 04:46:40 +00:00
#else
2019-10-05 19:08:05 +00:00
gTempoInternalToExternal = (u32)(updatesPerFrame * 2880000.0f / gTatumsPerBeat / 16.713f);
2019-08-25 04:46:40 +00:00
#endif
2019-12-02 02:52:53 +00:00
gMaxAudioCmds = gMaxSimultaneousNotes * 20 * updatesPerFrame + 320;
2020-02-03 05:51:26 +00:00
#endif
#ifdef VERSION_EU
persistentMem = DOUBLE_SIZE_ON_64_BIT(preset->persistentSeqMem + preset->persistentBankMem);
temporaryMem = DOUBLE_SIZE_ON_64_BIT(preset->temporarySeqMem + preset->temporaryBankMem);
#else
2019-11-03 19:36:27 +00:00
persistentMem = DOUBLE_SIZE_ON_64_BIT(preset->persistentBankMem + preset->persistentSeqMem);
temporaryMem = DOUBLE_SIZE_ON_64_BIT(preset->temporaryBankMem + preset->temporarySeqMem);
2020-02-03 05:51:26 +00:00
#endif
2019-08-25 04:46:40 +00:00
totalMem = persistentMem + temporaryMem;
2019-11-03 19:36:27 +00:00
wantMisc = gAudioSessionPool.size - totalMem - 0x100;
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(preset->persistentSeqMem);
sPersistentCommonPoolSplit.wantBank = DOUBLE_SIZE_ON_64_BIT(preset->persistentBankMem);
sPersistentCommonPoolSplit.wantUnused = 0;
persistent_pools_init(&sPersistentCommonPoolSplit);
sTemporaryCommonPoolSplit.wantSeq = DOUBLE_SIZE_ON_64_BIT(preset->temporarySeqMem);
sTemporaryCommonPoolSplit.wantBank = DOUBLE_SIZE_ON_64_BIT(preset->temporaryBankMem);
sTemporaryCommonPoolSplit.wantUnused = 0;
temporary_pools_init(&sTemporaryCommonPoolSplit);
2019-08-25 04:46:40 +00:00
reset_bank_and_seq_load_status();
2020-02-03 05:51:26 +00:00
#ifndef VERSION_EU
2019-08-25 04:46:40 +00:00
for (j = 0; j < 2; j++) {
2019-12-02 02:52:53 +00:00
gAudioCmdBuffers[j] = soundAlloc(&gNotesAndBuffersPool, gMaxAudioCmds * sizeof(u64));
2019-08-25 04:46:40 +00:00
}
2020-02-03 05:51:26 +00:00
#endif
2019-08-25 04:46:40 +00:00
2019-11-03 19:36:27 +00:00
gNotes = soundAlloc(&gNotesAndBuffersPool, gMaxSimultaneousNotes * sizeof(struct Note));
2019-08-25 04:46:40 +00:00
note_init_all();
init_note_free_list();
2020-02-03 05:51:26 +00:00
#ifdef VERSION_EU
gNoteSubsEu = soundAlloc(&gNotesAndBuffersPool, (gAudioBufferParameters.updatesPerFrame * gMaxSimultaneousNotes) * sizeof(struct NoteSubEu));
for (j = 0; j != 2; j++) {
gAudioCmdBuffers[j] = soundAlloc(&gNotesAndBuffersPool, gMaxAudioCmds * sizeof(u64));
}
for (j = 0; j < 4; j++) {
gSynthesisReverbs[j].useReverb = 0;
}
gNumSynthesisReverbs = preset->numReverbs;
for (j = 0; j < gNumSynthesisReverbs; j++) {
reverb = &gSynthesisReverbs[j];
reverbSettings = &preset->reverbSettings[j];
reverb->windowSize = reverbSettings->windowSize * 64;
reverb->downsampleRate = reverbSettings->downsampleRate;
reverb->reverbGain = reverbSettings->gain;
reverb->useReverb = 8;
reverb->ringBuffer.left = soundAlloc(&gNotesAndBuffersPool, reverb->windowSize * 2);
reverb->ringBuffer.right = soundAlloc(&gNotesAndBuffersPool, reverb->windowSize * 2);
reverb->nextRingBufferPos = 0;
reverb->unkC = 0;
reverb->curFrame = 0;
reverb->bufSizePerChannel = reverb->windowSize;
reverb->framesLeftToIgnore = 2;
if (reverb->downsampleRate != 1) {
reverb->resampleFlags = A_INIT;
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);
}
}
}
#else
2019-11-03 19:36:27 +00:00
if (reverbWindowSize == 0) {
gSynthesisReverb.useReverb = 0;
2019-08-25 04:46:40 +00:00
} else {
2019-11-03 19:36:27 +00:00
gSynthesisReverb.useReverb = 8;
gSynthesisReverb.ringBuffer.left = soundAlloc(&gNotesAndBuffersPool, reverbWindowSize * 2);
gSynthesisReverb.ringBuffer.right = soundAlloc(&gNotesAndBuffersPool, reverbWindowSize * 2);
gSynthesisReverb.nextRingBufferPos = 0;
gSynthesisReverb.unkC = 0;
gSynthesisReverb.curFrame = 0;
gSynthesisReverb.bufSizePerChannel = reverbWindowSize;
gSynthesisReverb.reverbGain = preset->reverbGain;
gSynthesisReverb.framesLeftToIgnore = 2;
if (gReverbDownsampleRate != 1) {
gSynthesisReverb.resampleFlags = A_INIT;
gSynthesisReverb.resampleRate = 0x8000 / gReverbDownsampleRate;
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));
2019-08-25 04:46:40 +00:00
for (i = 0; i < gAudioUpdatesPerFrame; i++) {
2019-12-02 02:52:53 +00:00
mem = soundAlloc(&gNotesAndBuffersPool, DEFAULT_LEN_2CH);
2019-11-03 19:36:27 +00:00
gSynthesisReverb.items[0][i].toDownsampleLeft = mem;
2020-02-03 05:51:26 +00:00
gSynthesisReverb.items[0][i].toDownsampleRight = mem + DEFAULT_LEN_1CH / sizeof(s16);
2019-12-02 02:52:53 +00:00
mem = soundAlloc(&gNotesAndBuffersPool, DEFAULT_LEN_2CH);
2019-11-03 19:36:27 +00:00
gSynthesisReverb.items[1][i].toDownsampleLeft = mem;
2020-02-03 05:51:26 +00:00
gSynthesisReverb.items[1][i].toDownsampleRight = mem + DEFAULT_LEN_1CH / sizeof(s16);
2019-08-25 04:46:40 +00:00
}
}
}
2020-02-03 05:51:26 +00:00
#endif
2019-08-25 04:46:40 +00:00
2019-12-02 02:52:53 +00:00
init_sample_dma_buffers(gMaxSimultaneousNotes);
2020-02-03 05:51:26 +00:00
#ifdef VERSION_EU
2020-03-02 03:42:52 +00:00
build_vol_rampings_table(0, gAudioBufferParameters.samplesPerUpdate);
2020-02-03 05:51:26 +00:00
#endif
2019-08-25 04:46:40 +00:00
osWritebackDCacheAll();
2020-02-03 05:51:26 +00:00
#ifndef VERSION_EU
2019-08-25 04:46:40 +00:00
if (gAudioLoadLock != AUDIO_LOCK_UNINITIALIZED) {
gAudioLoadLock = AUDIO_LOCK_NOT_LOADING;
}
2020-02-03 05:51:26 +00:00
#endif
2019-08-25 04:46:40 +00:00
}