diff --git a/Makefile b/Makefile index b3fc266..59df3ee 100644 --- a/Makefile +++ b/Makefile @@ -7,9 +7,9 @@ else CC := cc LDFLAGS := -lm -shared endif -CFLAGS := -g -Wall -fPIC -DSM64_LIB_EXPORT -DGBI_FLOATS +CFLAGS := -g -Wall -fPIC -DSM64_LIB_EXPORT -DGBI_FLOATS -DVERSION_US -DNO_SEGMENTED_MEMORY -SRC_DIRS := src src/decomp src/decomp/engine src/decomp/game src/decomp/mario src/decomp/tools +SRC_DIRS := src src/decomp src/decomp/engine src/decomp/include/PR src/decomp/game src/decomp/pc src/decomp/pc/audio src/decomp/mario src/decomp/tools src/decomp/audio BUILD_DIR := build DIST_DIR := dist ALL_DIRS := $(addprefix $(BUILD_DIR)/,$(SRC_DIRS)) @@ -42,7 +42,7 @@ src/decomp/mario/geo.inc.c: ./import-mario-geo.py $(BUILD_DIR)/%.o: %.c $(IMPORTED) @$(CC) $(CFLAGS) -MM -MP -MT $@ -MF $(BUILD_DIR)/$*.d $< - $(CC) -c $(CFLAGS) -o $@ $< + $(CC) -c $(CFLAGS) -I src/decomp/include -o $@ $< $(LIB_FILE): $(O_FILES) $(CC) $(LDFLAGS) -o $@ $^ diff --git a/src/decomp/audio/copt/seq_channel_layer_process_script_copt.inc.c b/src/decomp/audio/copt/seq_channel_layer_process_script_copt.inc.c new file mode 100644 index 0000000..7b6b777 --- /dev/null +++ b/src/decomp/audio/copt/seq_channel_layer_process_script_copt.inc.c @@ -0,0 +1,500 @@ +//! Copt inlining for US/JP. Here be dragons +// This version is basically identical to EU + +#include +#include + +#include "../heap.h" +#include "../data.h" +#include "../load.h" +#include "../seqplayer.h" +#include "../external.h" +#include "../effects.h" + +#define PORTAMENTO_IS_SPECIAL(x) ((x).mode & 0x80) +#define PORTAMENTO_MODE(x) ((x).mode & ~0x80) +#define PORTAMENTO_MODE_1 1 +#define PORTAMENTO_MODE_2 2 +#define PORTAMENTO_MODE_3 3 +#define PORTAMENTO_MODE_4 4 +#define PORTAMENTO_MODE_5 5 + +#define COPT 0 +#if COPT +#define M64_READ_U8(state, dst) \ + dst = m64_read_u8(state); +#else +#define M64_READ_U8(state, dst) \ +{ \ + u8 * _ptr_pc; \ + u8 _pc; \ + _ptr_pc = (*state).pc; \ + ((*state).pc)++; \ + _pc = *_ptr_pc; \ + dst = _pc; \ +} +#endif + + +#if COPT +#define M64_READ_S16(state, dst) \ + dst = m64_read_s16(state); +#else +#define M64_READ_S16(state, dst) \ +{ \ + s16 _ret; \ + _ret = *(*state).pc << 8; \ + ((*state).pc)++; \ + _ret = *(*state).pc | _ret; \ + ((*state).pc)++; \ + dst = _ret; \ +} +#endif +#if COPT +#define M64_READ_COMPRESSED_U16(state, dst) \ + dst = m64_read_compressed_u16(state); +#else +#define M64_READ_COMPRESSED_U16(state, dst) \ +{ \ + u16 ret = *(state->pc++); \ + if (ret & 0x80) { \ + ret = (ret << 8) & 0x7f00; \ + ret = *(state->pc++) | ret; \ + } \ + dst = ret; \ +} +#endif + +#if COPT +#define GET_INSTRUMENT(seqChannel, instId, _instOut, _adsr, dst, l) \ + dst = get_instrument(seqChannel, instId, _instOut, _adsr); +#else +#define GET_INSTRUMENT(seqChannel, instId, _instOut, _adsr, dst, l) \ +{ \ +struct AdsrSettings *adsr = _adsr; \ +struct Instrument **instOut = _instOut;\ + u8 _instId = instId; \ + struct Instrument *inst; \ + UNUSED u32 pad; \ + /* copt inlines instId here */ \ + if (instId >= gCtlEntries[(*seqChannel).bankId].numInstruments) { \ + _instId = gCtlEntries[(*seqChannel).bankId].numInstruments; \ + if (_instId == 0) { \ + dst = 0; \ + goto ret ## l; \ + } \ + _instId--; \ + } \ + inst = gCtlEntries[(*seqChannel).bankId].instruments[_instId]; \ + if (inst == NULL) { \ + while (_instId != 0xff) { \ + inst = gCtlEntries[(*seqChannel).bankId].instruments[_instId]; \ + if (inst != NULL) { \ + goto gi ## l; \ + } \ + _instId--; \ + } \ + gi ## l:; \ + } \ + if (((uintptr_t) gBankLoadedPool.persistent.pool.start <= (uintptr_t) inst \ + && (uintptr_t) inst <= (uintptr_t)(gBankLoadedPool.persistent.pool.start \ + + gBankLoadedPool.persistent.pool.size)) \ + || ((uintptr_t) gBankLoadedPool.temporary.pool.start <= (uintptr_t) inst \ + && (uintptr_t) inst <= (uintptr_t)(gBankLoadedPool.temporary.pool.start \ + + gBankLoadedPool.temporary.pool.size))) { \ + (*adsr).envelope = (*inst).envelope; \ + (*adsr).releaseRate = (*inst).releaseRate; \ + *instOut = inst; \ + _instId++; \ + goto ret ## l; \ + } \ + gAudioErrorFlags = _instId + 0x20000; \ + *instOut = NULL; \ + ret ## l: ; \ +} +#endif + +void seq_channel_layer_process_script(struct SequenceChannelLayer *layer) { + struct SequencePlayer *seqPlayer; // sp5C, t4 + struct SequenceChannel *seqChannel; // sp58, t5 + struct M64ScriptState *state; + struct Portamento *portamento; + struct AudioBankSound *sound; + struct Instrument *instrument; + struct Drum *drum; + s32 temp_a0_5; + u8 sameSound; + u8 cmd; // a0 sp3E, EU s2 + u8 cmdSemitone; // sp3D, t0 + u16 sp3A; // t2, a0, a1 + f32 tuning; // f0 + s32 vel; // sp30, t3 + s32 usedSemitone; // a1 + f32 freqScale; // sp28, f0 + f32 sp24; + f32 temp_f12; + f32 temp_f2; + +//! Copt: manually inline these functions in the scope of this routine +#ifdef __sgi +#pragma inline routine(m64_read_u8) +#pragma inline routine(m64_read_compressed_u16) +#pragma inline routine(m64_read_s16) +#pragma inline routine(get_instrument) +#endif + + sameSound = TRUE; + if ((*layer).enabled == FALSE) { + return; + } + + if ((*layer).delay > 1) { + (*layer).delay--; + if (!layer->stopSomething && layer->delay <= layer->duration) { + seq_channel_layer_note_decay(layer); + layer->stopSomething = TRUE; + } + return; + } + + if (!layer->continuousNotes) { + seq_channel_layer_note_decay(layer); + } + + if (PORTAMENTO_MODE(layer->portamento) == PORTAMENTO_MODE_1 || + PORTAMENTO_MODE(layer->portamento) == PORTAMENTO_MODE_2) { + layer->portamento.mode = 0; + } + + seqChannel = (*layer).seqChannel; + seqPlayer = (*seqChannel).seqPlayer; + for (;;) { + state = &layer->scriptState; + //M64_READ_U8(state, cmd); + { + u8 *_ptr_pc; + _ptr_pc = (*state).pc++; + cmd = *_ptr_pc; + } + + if (cmd <= 0xc0) { + break; + } + + switch (cmd) { + case 0xff: // layer_end; function return or end of script + if (state->depth == 0) { + // N.B. this function call is *not* inlined even though it's + // within the same file, unlike in the rest of this function. + seq_channel_layer_disable(layer); + return; + } + state->depth--, state->pc = state->stack[state->depth]; + break; + + case 0xfc: // layer_call + M64_READ_S16(state, sp3A); + state->depth++, state->stack[state->depth - 1] = state->pc; + state->pc = seqPlayer->seqData + sp3A; + break; + + case 0xf8: // layer_loop; loop start, N iterations (or 256 if N = 0) + M64_READ_U8(state, state->remLoopIters[state->depth]); + state->depth++, state->stack[state->depth - 1] = state->pc; + break; + + case 0xf7: // layer_loopend + if (--state->remLoopIters[state->depth - 1] != 0) { + state->pc = state->stack[state->depth - 1]; + } else { + state->depth--; + } + break; + + case 0xfb: // layer_jump + M64_READ_S16(state, sp3A); + state->pc = seqPlayer->seqData + sp3A; + break; + + case 0xc1: // layer_setshortnotevelocity + case 0xca: // layer_setpan + temp_a0_5 = *(state->pc++); + if (cmd == 0xc1) { + layer->velocitySquare = (f32)(temp_a0_5 * temp_a0_5); + } else { + layer->pan = (f32) temp_a0_5 / US_FLOAT(128.0); + } + break; + + case 0xc2: // layer_transpose; set transposition in semitones + case 0xc9: // layer_setshortnoteduration + temp_a0_5 = *(state->pc++); + if (cmd == 0xc9) { + layer->noteDuration = temp_a0_5; + } else { + layer->transposition = temp_a0_5; + } + break; + + case 0xc4: // layer_somethingon + case 0xc5: // layer_somethingoff + //! copt needs a ternary: + //layer->continuousNotes = (cmd == 0xc4) ? TRUE : FALSE; + { + u8 setting; + if (cmd == 0xc4) { + setting = TRUE; + } else { + setting = FALSE; + } + layer->continuousNotes = setting; + seq_channel_layer_note_decay(layer); + } + break; + + case 0xc3: // layer_setshortnotedefaultplaypercentage + M64_READ_COMPRESSED_U16(state, sp3A); + layer->shortNoteDefaultPlayPercentage = sp3A; + break; + + case 0xc6: // layer_setinstr + M64_READ_U8(state, cmdSemitone); + + if (cmdSemitone < 127) { + GET_INSTRUMENT(seqChannel, cmdSemitone, &(*layer).instrument, &(*layer).adsr, cmdSemitone, 1); + } + break; + + case 0xc7: // layer_portamento + M64_READ_U8(state, (*layer).portamento.mode); + M64_READ_U8(state, cmdSemitone); + + cmdSemitone = cmdSemitone + (*seqChannel).transposition; + cmdSemitone += (*layer).transposition; + cmdSemitone += (*seqPlayer).transposition; + + if (cmdSemitone >= 0x80) { + cmdSemitone = 0; + } + layer->portamentoTargetNote = cmdSemitone; + + // If special, the next param is u8 instead of var + if (PORTAMENTO_IS_SPECIAL((*layer).portamento)) { + layer->portamentoTime = *((state)->pc++); + break; + } + + M64_READ_COMPRESSED_U16(state, sp3A); + layer->portamentoTime = sp3A; + break; + + case 0xc8: // layer_disableportamento + layer->portamento.mode = 0; + break; + + default: + switch (cmd & 0xf0) { + case 0xd0: // layer_setshortnotevelocityfromtable + sp3A = seqPlayer->shortNoteVelocityTable[cmd & 0xf]; + (*layer).velocitySquare = (f32)(sp3A * sp3A); + break; + case 0xe0: // layer_setshortnotedurationfromtable + (*layer).noteDuration = seqPlayer->shortNoteDurationTable[cmd & 0xf]; + break; + } + } + } + + if (cmd == 0xc0) { // layer_delay + M64_READ_COMPRESSED_U16(state, layer->delay); + layer->stopSomething = TRUE; + } else { + layer->stopSomething = FALSE; + + if (seqChannel->largeNotes == TRUE) { + switch (cmd & 0xc0) { + case 0x00: // layer_note0 (play percentage, velocity, duration) + M64_READ_COMPRESSED_U16(state, sp3A); + vel = *((*state).pc++); + layer->noteDuration = *((*state).pc++); + layer->playPercentage = sp3A; + goto l1090; + + case 0x40: // layer_note1 (play percentage, velocity) + M64_READ_COMPRESSED_U16(state, sp3A); + vel = *((*state).pc++); + layer->noteDuration = 0; + layer->playPercentage = sp3A; + goto l1090; + + case 0x80: // layer_note2 (velocity, duration; uses last play percentage) + sp3A = layer->playPercentage; + vel = *((*state).pc++); + layer->noteDuration = *((*state).pc++); + goto l1090; + } +l1090: + cmdSemitone = cmd - (cmd & 0xc0); + layer->velocitySquare = vel * vel; + } else { + switch (cmd & 0xc0) { + case 0x00: // play note, type 0 (play percentage) + M64_READ_COMPRESSED_U16(state, sp3A); + layer->playPercentage = sp3A; + goto l1138; + + case 0x40: // play note, type 1 (uses default play percentage) + sp3A = layer->shortNoteDefaultPlayPercentage; + goto l1138; + + case 0x80: // play note, type 2 (uses last play percentage) + sp3A = layer->playPercentage; + goto l1138; + } +l1138: + + cmdSemitone = cmd - (cmd & 0xc0); + } + + layer->delay = sp3A; + layer->duration = layer->noteDuration * sp3A / 256; + if ((seqPlayer->muted && (seqChannel->muteBehavior & MUTE_BEHAVIOR_STOP_NOTES) != 0) + || seqChannel->stopSomething2 + || !seqChannel->hasInstrument + ) { + layer->stopSomething = TRUE; + } else { + if (seqChannel->instOrWave == 0) { // drum + cmdSemitone += (*seqChannel).transposition + (*layer).transposition; + if (cmdSemitone >= gCtlEntries[seqChannel->bankId].numDrums) { + cmdSemitone = gCtlEntries[seqChannel->bankId].numDrums; + if (cmdSemitone == 0) { + // this goto looks a bit like a function return... + layer->stopSomething = TRUE; + goto skip; + } + + cmdSemitone--; + } + + drum = gCtlEntries[seqChannel->bankId].drums[cmdSemitone]; + if (drum == NULL) { + layer->stopSomething = TRUE; + } else { + layer->adsr.envelope = drum->envelope; + layer->adsr.releaseRate = drum->releaseRate; + layer->pan = FLOAT_CAST(drum->pan) / US_FLOAT(128.0); + layer->sound = &drum->sound; + layer->freqScale = layer->sound->tuning; + } + + skip:; + } else { // instrument + cmdSemitone += (*seqPlayer).transposition + (*seqChannel).transposition + (*layer).transposition; + if (cmdSemitone >= 0x80) { + layer->stopSomething = TRUE; + } else { + instrument = layer->instrument; + if (instrument == NULL) { + instrument = seqChannel->instrument; + } + + if (layer->portamento.mode != 0) { + //! copt needs a ternary: + //usedSemitone = (layer->portamentoTargetNote < cmdSemitone) ? cmdSemitone : layer->portamentoTargetNote; + if (layer->portamentoTargetNote < cmdSemitone) { + usedSemitone = cmdSemitone; + } else { + usedSemitone = layer->portamentoTargetNote; + } + + if (instrument != NULL) { + sound = (u8) usedSemitone < instrument->normalRangeLo ? &instrument->lowNotesSound + : (u8) usedSemitone <= instrument->normalRangeHi ? + &instrument->normalNotesSound : &instrument->highNotesSound; + + sameSound = (sound == (*layer).sound); + layer->sound = sound; + tuning = (*sound).tuning; + } else { + layer->sound = NULL; + tuning = 1.0f; + } + + temp_f2 = gNoteFrequencies[cmdSemitone] * tuning; + temp_f12 = gNoteFrequencies[layer->portamentoTargetNote] * tuning; + + portamento = &layer->portamento; + switch (PORTAMENTO_MODE(layer->portamento)) { + case PORTAMENTO_MODE_1: + case PORTAMENTO_MODE_3: + case PORTAMENTO_MODE_5: + sp24 = temp_f2; + freqScale = temp_f12; + goto l13cc; + + case PORTAMENTO_MODE_2: + case PORTAMENTO_MODE_4: + freqScale = temp_f2; + sp24 = temp_f12; + goto l13cc; + } +l13cc: + portamento->extent = sp24 / freqScale - US_FLOAT(1.0); + if (PORTAMENTO_IS_SPECIAL((*layer).portamento)) { + portamento->speed = US_FLOAT(32512.0) * FLOAT_CAST((*seqPlayer).tempo) + / ((f32)(*layer).delay * (f32) gTempoInternalToExternal + * FLOAT_CAST((*layer).portamentoTime)); + } else { + portamento->speed = US_FLOAT(127.0) / FLOAT_CAST((*layer).portamentoTime); + } + portamento->cur = 0.0f; + layer->freqScale = freqScale; + if (PORTAMENTO_MODE((*layer).portamento) == PORTAMENTO_MODE_5) { + layer->portamentoTargetNote = cmdSemitone; + } + } else if (instrument != NULL) { + sound = cmdSemitone < instrument->normalRangeLo ? + &instrument->lowNotesSound : cmdSemitone <= instrument->normalRangeHi ? + &instrument->normalNotesSound : &instrument->highNotesSound; + + sameSound = (sound == (*layer).sound); + layer->sound = sound; + layer->freqScale = gNoteFrequencies[cmdSemitone] * (*sound).tuning; + } else { + layer->sound = NULL; + layer->freqScale = gNoteFrequencies[cmdSemitone]; + } + } + } + layer->delayUnused = layer->delay; + } + } + + if (layer->stopSomething == TRUE) { + if (layer->note != NULL || layer->continuousNotes) { + seq_channel_layer_note_decay(layer); + } + return; + } + + cmdSemitone = FALSE; + if (!layer->continuousNotes) { + cmdSemitone = TRUE; + } else if (layer->note == NULL || layer->status == SOUND_LOAD_STATUS_NOT_LOADED) { + cmdSemitone = TRUE; + } else if (sameSound == FALSE) { + seq_channel_layer_note_decay(layer); + cmdSemitone = TRUE; + } else if (layer->sound == NULL) { + init_synthetic_wave(layer->note, layer); + } + + if (cmdSemitone != FALSE) { + (*layer).note = alloc_note(layer); + } + + if (layer->note != NULL && layer->note->parentLayer == layer) { + note_vibrato_init(layer->note); + } +} diff --git a/src/decomp/audio/data.c b/src/decomp/audio/data.c new file mode 100644 index 0000000..f8495bb --- /dev/null +++ b/src/decomp/audio/data.c @@ -0,0 +1,961 @@ +#include + +#include "data.h" +#include "effects.h" + +extern struct OSMesgQueue OSMesgQueue0; +extern struct OSMesgQueue OSMesgQueue1; +extern struct OSMesgQueue OSMesgQueue2; +extern struct OSMesgQueue OSMesgQueue3; + +#ifdef VERSION_EU +struct ReverbSettingsEU sReverbSettings[] = { + { 0x04, 0x0c, 0x2fff }, + { 0x04, 0x0a, 0x47ff }, + { 0x04, 0x10, 0x2fff }, + { 0x04, 0x0e, 0x3fff }, + { 0x04, 0x0c, 0x4fff }, + { 0x04, 0x0a, 0x37ff } +}; +struct AudioSessionSettingsEU gAudioSessionPresets[] = { + { 0x00007d00, 0x01, 0x10, 0x01, 0x00, &sReverbSettings[0], 0x7fff, 0x0000, 0x00003a40, 0x00006d00, + 0x00004400, 0x00002a00 }, + { 0x00007d00, 0x01, 0x10, 0x01, 0x00, &sReverbSettings[1], 0x7fff, 0x0000, 0x00003a40, 0x00006d00, + 0x00004400, 0x00002a00 }, + { 0x00007d00, 0x01, 0x10, 0x01, 0x00, &sReverbSettings[2], 0x7fff, 0x0000, 0x00003a40, 0x00006d00, + 0x00004400, 0x00002a00 }, + { 0x00007d00, 0x01, 0x10, 0x01, 0x00, &sReverbSettings[3], 0x7fff, 0x0000, 0x00003a40, 0x00006d00, + 0x00004400, 0x00002a00 }, + { 0x00007d00, 0x01, 0x10, 0x01, 0x00, &sReverbSettings[4], 0x7fff, 0x0000, 0x00003a40, 0x00006d00, + 0x00004400, 0x00002a00 }, + { 0x00007d00, 0x01, 0x10, 0x01, 0x00, &sReverbSettings[0], 0x7fff, 0x0000, 0x00004000, 0x00006e00, + 0x00003f00, 0x00002a00 }, + { 0x00007d00, 0x01, 0x10, 0x01, 0x00, &sReverbSettings[1], 0x7fff, 0x0000, 0x00004100, 0x00006e00, + 0x00004400, 0x00002a80 }, + { 0x00007d00, 0x01, 0x14, 0x01, 0x00, &sReverbSettings[5], 0x7fff, 0x0000, 0x00003500, 0x00006280, + 0x00004000, 0x00001b00 } +}; +#endif + +// Format: +// - frequency +// - max number of simultaneous notes +// - reverb downsample rate (makes the ring buffer be downsampled to save memory) +// - reverb window size (ring buffer size, length affects reverb delay) +// - reverb gain (0 = min reverb, 32767 = max reverb, 32769 to 65535 = louder and louder...) +// - volume +// - memory used for persistent sequences +// - memory used for persistent banks +// - memory used for temporary sequences +// - memory used for temporary banks +#if defined(VERSION_JP) || defined(VERSION_US) +struct AudioSessionSettings gAudioSessionPresets[18] = { +#ifdef VERSION_JP + { 32000, 16, 1, 0x0800, 0x2FFF, 0x7FFF, 0x3900, 0x6000, 0x4400, 0x2A00 }, + { 32000, 16, 1, 0x0A00, 0x47FF, 0x7FFF, 0x3900, 0x6000, 0x4400, 0x2A00 }, + { 32000, 16, 1, 0x1000, 0x2FFF, 0x7FFF, 0x3900, 0x6000, 0x4400, 0x2A00 }, + { 32000, 16, 1, 0x0E00, 0x3FFF, 0x7FFF, 0x3900, 0x6000, 0x4400, 0x2A00 }, + { 32000, 16, 1, 0x0C00, 0x4FFF, 0x7FFF, 0x3900, 0x6000, 0x4400, 0x2A00 }, + { 32000, 16, 1, 0x0800, 0x2FFF, 0x7FFF, 0x3E00, 0x6200, 0x3F00, 0x2A00 }, + { 32000, 16, 1, 0x0A00, 0x47FF, 0x7FFF, 0x3F00, 0x6200, 0x4400, 0x2A80 }, + { 32000, 20, 1, 0x0800, 0x37FF, 0x7FFF, 0x3300, 0x5500, 0x4000, 0x1B00 }, +#else + { 32000, 16, 1, 0x0C00, 0x2FFF, 0x7FFF, 0x3A00, 0x6D00, 0x4400, 0x2A00 }, + { 32000, 16, 1, 0x0A00, 0x47FF, 0x7FFF, 0x3A00, 0x6D00, 0x4400, 0x2A00 }, + { 32000, 16, 1, 0x1000, 0x2FFF, 0x7FFF, 0x3A00, 0x6D00, 0x4400, 0x2A00 }, + { 32000, 16, 1, 0x0E00, 0x3FFF, 0x7FFF, 0x3A00, 0x6D00, 0x4400, 0x2A00 }, + { 32000, 16, 1, 0x0C00, 0x4FFF, 0x7FFF, 0x3A00, 0x6D00, 0x4400, 0x2A00 }, + { 32000, 16, 1, 0x0C00, 0x2FFF, 0x7FFF, 0x4000, 0x6E00, 0x3F00, 0x2A00 }, + { 32000, 16, 1, 0x0A00, 0x47FF, 0x7FFF, 0x4100, 0x6E00, 0x4400, 0x2A80 }, + { 32000, 20, 1, 0x0800, 0x37FF, 0x7FFF, 0x34C0, 0x6280, 0x4000, 0x1B00 }, +#endif + { 27000, 16, 1, 0x0800, 0x2FFF, 0x7FFF, 0x2500, 0x5500, 0x7400, 0x2400 }, + { 27000, 16, 1, 0x0800, 0x3FFF, 0x7FFF, 0x2500, 0x5500, 0x7400, 0x2400 }, + { 27000, 16, 1, 0x1000, 0x2FFF, 0x7FFF, 0x2500, 0x5500, 0x7400, 0x2400 }, + { 27000, 16, 1, 0x1000, 0x3FFF, 0x7FFF, 0x2500, 0x5500, 0x7400, 0x2400 }, + { 27000, 16, 1, 0x0C00, 0x4FFF, 0x7FFF, 0x2500, 0x5500, 0x7400, 0x2400 }, + { 32000, 14, 1, 0x0800, 0x2FFF, 0x7FFF, 0x2500, 0x5500, 0x7400, 0x2400 }, + { 32000, 12, 1, 0x0800, 0x2FFF, 0x7FFF, 0x2500, 0x5500, 0x7400, 0x2400 }, + { 32000, 10, 1, 0x0800, 0x2FFF, 0x7FFF, 0x2500, 0x5500, 0x7400, 0x2400 }, + { 32000, 8, 1, 0x0800, 0x2FFF, 0x7FFF, 0x2500, 0x5500, 0x7400, 0x2400 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } +}; +#endif +// gAudioCosineTable[k] = round((2**15 - 1) * cos(pi/2 * k / 127)). Unused. +#if defined(VERSION_JP) || defined(VERSION_US) +u16 gAudioCosineTable[128] = { + 0x7FFF, 32764, 32757, 32744, 32727, 32704, 32677, 32644, 32607, 32564, 32517, 32464, 32407, + 32344, 32277, 32205, 32127, 32045, 31958, 31866, 31770, 31668, 31561, 31450, 31334, 31213, + 31087, 30957, 30822, 30682, 30537, 30388, 30234, 30075, 29912, 29744, 29572, 29395, 29214, + 29028, 28838, 28643, 28444, 28241, 28033, 27821, 27605, 27385, 27160, 26931, 26698, 26461, + 26220, 25975, 25726, 25473, 25216, 24956, 24691, 24423, 24151, 23875, 23596, 23313, 23026, + 22736, 22442, 22145, 21845, 21541, 21234, 20924, 20610, 20294, 19974, 19651, 19325, 18997, + 18665, 18331, 17993, 17653, 17310, 16965, 16617, 16266, 15913, 15558, 15200, 14840, 14477, + 14113, 13746, 13377, 13006, 12633, 12258, 11881, 11503, 11122, 10740, 10357, 9971, 9584, + 9196, 8806, 8415, 8023, 7630, 7235, 6839, 6442, 6044, 5646, 5246, 4845, 4444, + 4042, 3640, 3237, 2833, 2429, 2025, 1620, 1216, 810, 405, 0, +}; +#endif + +// Transforms a pitch scale factor in -127..127 into a frequency scale factor +// between -1 and +1 octave. +// gPitchBendFrequencyScale[k] = 0.5 * 2^(k/127) +#ifndef VERSION_SH +#if defined(VERSION_EU) +f32 gPitchBendFrequencyScale[256] = { + 0.5f, +#else +f32 gPitchBendFrequencyScale[255] = { +#endif + 0.5f, 0.502736f, 0.505488f, 0.508254f, 0.511036f, 0.513833f, 0.516645f, 0.519472f, 0.522315f, + 0.525174f, 0.528048f, 0.530938f, 0.533843f, 0.536765f, 0.539702f, 0.542656f, 0.545626f, 0.548612f, + 0.551614f, 0.554633f, 0.557669f, 0.560721f, 0.563789f, 0.566875f, 0.569977f, 0.573097f, 0.576233f, + 0.579387f, 0.582558f, 0.585746f, 0.588951f, 0.592175f, 0.595415f, 0.598674f, 0.601950f, 0.605245f, + 0.608557f, 0.611888f, 0.615236f, 0.618603f, 0.621989f, 0.625393f, 0.628815f, 0.632257f, 0.635717f, + 0.639196f, 0.642694f, 0.646212f, 0.649748f, 0.653304f, 0.656880f, 0.660475f, 0.664089f, 0.667724f, + 0.671378f, 0.675052f, 0.678747f, 0.682461f, 0.686196f, 0.689952f, 0.693727f, 0.697524f, 0.701341f, + 0.705180f, 0.709039f, 0.712919f, 0.716821f, 0.720744f, 0.724689f, 0.728655f, 0.732642f, 0.736652f, + 0.740684f, 0.744737f, 0.748813f, 0.752911f, 0.757031f, 0.761175f, 0.765340f, 0.769529f, 0.773740f, + 0.777975f, 0.782232f, 0.786513f, 0.790818f, 0.795146f, 0.799497f, 0.803873f, 0.808272f, 0.812696f, + 0.817144f, 0.821616f, 0.826112f, 0.830633f, 0.835179f, 0.839750f, 0.844346f, 0.848966f, 0.853613f, + 0.858284f, 0.862982f, 0.867704f, 0.872453f, 0.877228f, 0.882029f, 0.886856f, 0.891709f, 0.896590f, + 0.901496f, 0.906430f, 0.911391f, 0.916379f, 0.921394f, 0.926436f, 0.931507f, 0.936604f, 0.941730f, + 0.946884f, 0.952066f, 0.957277f, 0.962516f, 0.967783f, 0.973080f, 0.978405f, 0.983760f, 0.989144f, + 0.994557f, 1.0f, 1.005473f, 1.010975f, 1.016508f, 1.022071f, 1.027665f, 1.033289f, 1.038944f, + 1.044630f, 1.050347f, 1.056095f, 1.061875f, 1.067687f, 1.073530f, 1.079405f, 1.085312f, 1.091252f, + 1.097224f, 1.103229f, 1.109267f, 1.115337f, 1.121441f, 1.127579f, 1.133750f, 1.139955f, 1.146193f, + 1.152466f, 1.158773f, 1.165115f, 1.171491f, 1.177903f, 1.184349f, 1.190831f, 1.197348f, 1.203901f, + 1.210489f, 1.217114f, 1.223775f, 1.230473f, 1.237207f, 1.243978f, 1.250786f, 1.257631f, 1.264514f, + 1.271434f, 1.278392f, 1.285389f, 1.292423f, 1.299497f, 1.306608f, 1.313759f, 1.320949f, 1.328178f, + 1.335447f, 1.342756f, 1.350104f, 1.357493f, 1.364922f, 1.372392f, 1.379903f, 1.387455f, 1.395048f, + 1.402683f, 1.410360f, 1.418078f, 1.425839f, 1.433642f, 1.441488f, 1.449377f, 1.457309f, 1.465285f, + 1.473304f, 1.481367f, 1.489474f, 1.497626f, 1.505822f, 1.514063f, 1.522349f, 1.530681f, 1.539058f, + 1.547481f, 1.555950f, 1.564465f, 1.573027f, 1.581636f, 1.590292f, 1.598995f, 1.607746f, 1.616545f, + 1.625392f, 1.634287f, 1.643231f, 1.652224f, 1.661266f, 1.670358f, 1.679500f, 1.688691f, 1.697933f, + 1.707225f, 1.716569f, 1.725963f, 1.735409f, 1.744906f, 1.754456f, 1.764058f, 1.773712f, 1.783419f, + 1.793179f, 1.802993f, 1.812860f, 1.822782f, 1.832757f, 1.842788f, 1.852873f, 1.863013f, 1.873209f, + 1.883461f, 1.893768f, 1.904132f, 1.914553f, 1.925031f, 1.935567f, 1.946159f, 1.956810f, 1.967520f, + 1.978287f, 1.989114f, 2.0f +}; + +// Frequencies for notes using the standard twelve-tone equal temperament scale. +// For indices 0..116, gNoteFrequencies[k] = 2^((k-39)/12). +// For indices 117..128, gNoteFrequencies[k] = 0.5 * 2^((k-39)/12). +// The 39 in the formula refers to piano key 40 (middle C, at 256 Hz) being +// the reference frequency, which is assigned value 1. +// clang-format off +f32 gNoteFrequencies[128] = { + 0.105112f, 0.111362f, 0.117984f, 0.125f, 0.132433f, 0.140308f, 0.148651f, 0.15749f, 0.166855f, 0.176777f, 0.187288f, 0.198425f, + 0.210224f, 0.222725f, 0.235969f, 0.25f, 0.264866f, 0.280616f, 0.297302f, 0.31498f, 0.33371f, 0.353553f, 0.374577f, 0.39685f, + 0.420448f, 0.445449f, 0.471937f, 0.5f, 0.529732f, 0.561231f, 0.594604f, 0.629961f, 0.66742f, 0.707107f, 0.749154f, 0.793701f, + 0.840897f, 0.890899f, 0.943875f, 1.0f, 1.059463f, 1.122462f, 1.189207f, 1.259921f, 1.33484f, 1.414214f, 1.498307f, 1.587401f, + 1.681793f, 1.781798f, 1.887749f, 2.0f, 2.118926f, 2.244924f, 2.378414f, 2.519842f, 2.66968f, 2.828428f, 2.996615f, 3.174803f, + 3.363586f, 3.563596f, 3.775498f, 4.0f, 4.237853f, 4.489849f, 4.756829f, 5.039685f, 5.33936f, 5.656855f, 5.993229f, 6.349606f, + 6.727173f, 7.127192f, 7.550996f, 8.0f, 8.475705f, 8.979697f, 9.513658f, 10.07937f, 10.67872f, 11.31371f, 11.986459f, 12.699211f, + 13.454346f, 14.254383f, 15.101993f, 16.0f, 16.95141f, 17.959394f, 19.027315f, 20.15874f, 21.35744f, 22.62742f, 23.972918f, 25.398422f, + 26.908691f, 28.508766f, 30.203985f, 32.0f, 33.90282f, 35.91879f, 38.05463f, 40.31748f, 42.71488f, 45.25484f, 47.945835f, 50.796844f, + 53.817383f, 57.017532f, 60.40797f, 64.0f, 67.80564f, 71.83758f, 76.10926f, 80.63496f, 85.42976f, 45.25484f, 47.945835f, 50.796844f, + 53.817383f, 57.017532f, 60.40797f, 64.0f, 67.80564f, 71.83758f, 76.10926f, 80.63496f +}; +// clang-format on + +// goes up by ~12 at each step for the first 4 values (starting from 0), then by ~6 +u8 gDefaultShortNoteVelocityTable[16] = { + 12, 25, 38, 51, 57, 64, 71, 76, 83, 89, 96, 102, 109, 115, 121, 127, +}; + +// goes down by 26 at each step for the first 4 values (starting from 255), then by ~12 +u8 gDefaultShortNoteDurationTable[16] = { + 229, 203, 177, 151, 139, 126, 113, 100, 87, 74, 61, 48, 36, 23, 10, 0, +}; + +#if defined(VERSION_JP) || defined(VERSION_US) +// gVibratoCurve[k] = k*8 +s8 gVibratoCurve[16] = { 0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120 }; +#endif + +struct AdsrEnvelope gDefaultEnvelope[] = { + { BSWAP16(4), BSWAP16(32000) }, // go from 0 to 32000 over the course of 16ms + { BSWAP16(1000), BSWAP16(32000) }, // stay there for 4.16 seconds + { BSWAP16(ADSR_HANG), 0 } // then continue staying there +}; +#endif + +#ifdef VERSION_EU +struct NoteSubEu gZeroNoteSub = { 0 }; +struct NoteSubEu gDefaultNoteSub = { 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, { NULL } }; +#endif + +#if defined(VERSION_EU) || defined(VERSION_SH) +s16 sSawtoothWaves[256] = { + 0, 1023, 2047, 3071, 4095, 5119, 6143, 7167, 8191, 9215, 10239, + 11263, 0x2FFF, 13311, 0x37FF, 15359, 0x3FFF, 17407, 0x47FF, 19455, 0x4FFF, 21503, + 22527, 23551, 24575, 25599, 26623, 27647, 28671, 29695, 30719, 31743, -0x7FFF, + -31743, -30719, -29695, -28671, -27647, -26623, -25599, -24575, -23551, -22527, -21503, + -0x4FFF, -19455, -0x47FF, -17407, -0x3FFF, -15359, -0x37FF, -13311, -0x2FFF, -11263, -10239, + -9215, -8191, -7167, -6143, -5119, -4095, -3071, -2047, -1023, + 0, 0x7FF, 0xFFF, 0x17FF, 0x1FFF, 0x27FF, 0x2FFF, 0x37FF, 0x3FFF, 0x47FF, 0x4FFF, + 0x57FF, 0x5FFF, 0x67FF, 0x6FFF, 0x77FF, 0x8001, 0x8801, 0x9001, 0x9801, 0xa001, 0xa801, + 0xb001, 0xb801, 0xc001, 0xc801, 0xd001, 0xd801, 0xe001, 0xe801, 0xf001, 0xf801, 0x0000, + 0x07ff, 0x0fff, 0x17ff, 0x1fff, 0x27ff, 0x2fff, 0x37ff, 0x3fff, 0x47ff, 0x4fff, 0x57ff, + 0x5fff, 0x67ff, 0x6fff, 0x77ff, 0x8001, 0x8801, 0x9001, 0x9801, 0xa001, 0xa801, 0xb001, + 0xb801, 0xc001, 0xc801, 0xd001, 0xd801, 0xe001, 0xe801, 0xf001, 0xf801, + 0x0000, 0x0fff, 0x1fff, 0x2fff, 0x3fff, 0x4fff, 0x5fff, 0x6fff, + 0x8001, 0x9001, 0xa001, 0xb001, 0xc001, 0xd001, 0xe001, 0xf001, + 0x0000, 0x0fff, 0x1fff, 0x2fff, 0x3fff, 0x4fff, 0x5fff, 0x6fff, + 0x8001, 0x9001, 0xa001, 0xb001, 0xc001, 0xd001, 0xe001, 0xf001, + 0x0000, 0x0fff, 0x1fff, 0x2fff, 0x3fff, 0x4fff, 0x5fff, 0x6fff, + 0x8001, 0x9001, 0xa001, 0xb001, 0xc001, 0xd001, 0xe001, 0xf001, + 0x0000, 0x0fff, 0x1fff, 0x2fff, 0x3fff, 0x4fff, 0x5fff, 0x6fff, + 0x8001, 0x9001, 0xa001, 0xb001, 0xc001, 0xd001, 0xe001, 0xf001, + 0x0000, 0x1fff, 0x3fff, 0x5fff, 0x8001, 0xa001, 0xc001, 0xe001, + 0x0000, 0x1fff, 0x3fff, 0x5fff, 0x8001, 0xa001, 0xc001, 0xe001, + 0x0000, 0x1fff, 0x3fff, 0x5fff, 0x8001, 0xa001, 0xc001, 0xe001, + 0x0000, 0x1fff, 0x3fff, 0x5fff, 0x8001, 0xa001, 0xc001, 0xe001, + 0x0000, 0x1fff, 0x3fff, 0x5fff, 0x8001, 0xa001, 0xc001, 0xe001, + 0x0000, 0x1fff, 0x3fff, 0x5fff, 0x8001, 0xa001, 0xc001, 0xe001, + 0x0000, 0x1fff, 0x3fff, 0x5fff, 0x8001, 0xa001, 0xc001, 0xe001, + 0x0000, 0x1fff, 0x3fff, 0x5fff, 0x8001, 0xa001, 0xc001, 0xe001 +}; +s16 sTriangleWaves[256] = { + 0x0000, 0x07ff, 0x0fff, 0x17ff, 0x1fff, 0x27ff, 0x2fff, 0x37ff, 0x3fff, 0x47ff, 0x4fff, 0x57ff, + 0x5fff, 0x67ff, 0x6fff, 0x77ff, 0x7fff, 0x77ff, 0x6fff, 0x67ff, 0x5fff, 0x57ff, 0x4fff, 0x47ff, + 0x3fff, 0x37ff, 0x2fff, 0x27ff, 0x1fff, 0x17ff, 0x0fff, 0x07ff, 0x0000, 0xf801, 0xf001, 0xe801, + 0xe001, 0xd801, 0xd001, 0xc801, 0xc001, 0xb801, 0xb001, 0xa801, 0xa001, 0x9801, 0x9001, 0x8801, + 0x8001, 0x8801, 0x9001, 0x9801, 0xa001, 0xa801, 0xb001, 0xb801, 0xc001, 0xc801, 0xd001, 0xd801, + 0xe001, 0xe801, 0xf001, 0xf801, 0x0000, 0x0fff, 0x1fff, 0x2fff, 0x3fff, 0x4fff, 0x5fff, 0x6fff, + 0x7fff, 0x6fff, 0x5fff, 0x4fff, 0x3fff, 0x2fff, 0x1fff, 0x0fff, 0x0000, 0xf001, 0xe001, 0xd001, + 0xc001, 0xb001, 0xa001, 0x9001, 0x8001, 0x9001, 0xa001, 0xb001, 0xc001, 0xd001, 0xe001, 0xf001, + 0x0000, 0x0fff, 0x1fff, 0x2fff, 0x3fff, 0x4fff, 0x5fff, 0x6fff, 0x7fff, 0x6fff, 0x5fff, 0x4fff, + 0x3fff, 0x2fff, 0x1fff, 0x0fff, 0x0000, 0xf001, 0xe001, 0xd001, 0xc001, 0xb001, 0xa001, 0x9001, + 0x8001, 0x9001, 0xa001, 0xb001, 0xc001, 0xd001, 0xe001, 0xf001, 0x0000, 0x1fff, 0x3fff, 0x5fff, + 0x7fff, 0x5fff, 0x3fff, 0x1fff, 0x0000, 0xe001, 0xc001, 0xa001, 0x8001, 0xa001, 0xc001, 0xe001, + 0x0000, 0x1fff, 0x3fff, 0x5fff, 0x7fff, 0x5fff, 0x3fff, 0x1fff, 0x0000, 0xe001, 0xc001, 0xa001, + 0x8001, 0xa001, 0xc001, 0xe001, 0x0000, 0x1fff, 0x3fff, 0x5fff, 0x7fff, 0x5fff, 0x3fff, 0x1fff, + 0x0000, 0xe001, 0xc001, 0xa001, 0x8001, 0xa001, 0xc001, 0xe001, 0x0000, 0x1fff, 0x3fff, 0x5fff, + 0x7fff, 0x5fff, 0x3fff, 0x1fff, 0x0000, 0xe001, 0xc001, 0xa001, 0x8001, 0xa001, 0xc001, 0xe001, + 0x0000, 0x3fff, 0x7fff, 0x3fff, 0x0000, 0xc001, 0x8001, 0xc001, 0x0000, 0x3fff, 0x7fff, 0x3fff, + 0x0000, 0xc001, 0x8001, 0xc001, 0x0000, 0x3fff, 0x7fff, 0x3fff, 0x0000, 0xc001, 0x8001, 0xc001, + 0x0000, 0x3fff, 0x7fff, 0x3fff, 0x0000, 0xc001, 0x8001, 0xc001, 0x0000, 0x3fff, 0x7fff, 0x3fff, + 0x0000, 0xc001, 0x8001, 0xc001, 0x0000, 0x3fff, 0x7fff, 0x3fff, 0x0000, 0xc001, 0x8001, 0xc001, + 0x0000, 0x3fff, 0x7fff, 0x3fff, 0x0000, 0xc001, 0x8001, 0xc001, 0x0000, 0x3fff, 0x7fff, 0x3fff, + 0x0000, 0xc001, 0x8001, 0xc001, +}; +s16 sSineWaves[256] = { + 0x0000, 0x0c8b, 0x18f8, 0x2527, 0x30fb, 0x3c56, 0x471c, 0x5133, 0x5a81, 0x62f1, 0x6a6c, 0x70e1, + 0x7640, 0x7a7c, 0x7d89, 0x7f61, 0x7fff, 0x7f61, 0x7d89, 0x7a7c, 0x7640, 0x70e1, 0x6a6c, 0x62f1, + 0x5a81, 0x5133, 0x471c, 0x3c56, 0x30fb, 0x2527, 0x18f8, 0x0c8b, 0x0000, 0xf375, 0xe708, 0xdad9, + 0xcf05, 0xc3aa, 0xb8e4, 0xaecd, 0xa57f, 0x9d0f, 0x9594, 0x8f1f, 0x89c0, 0x8584, 0x8277, 0x809f, + 0x8001, 0x809f, 0x8277, 0x8584, 0x89c0, 0x8f1f, 0x9594, 0x9d0f, 0xa57f, 0xaecd, 0xb8e4, 0xc3aa, + 0xcf05, 0xdad9, 0xe708, 0xf375, 0x0000, 0x18f8, 0x30fb, 0x471c, 0x5a81, 0x6a6c, 0x7640, 0x7d89, + 0x7fff, 0x7d89, 0x7640, 0x6a6c, 0x5a81, 0x471c, 0x30fb, 0x18f8, 0x0000, 0xe708, 0xcf05, 0xb8e4, + 0xa57f, 0x9594, 0x89c0, 0x8277, 0x8001, 0x8277, 0x89c0, 0x9594, 0xa57f, 0xb8e4, 0xcf05, 0xe708, + 0x0000, 0x18f8, 0x30fb, 0x471c, 0x5a81, 0x6a6c, 0x7640, 0x7d89, 0x7fff, 0x7d89, 0x7640, 0x6a6c, + 0x5a81, 0x471c, 0x30fb, 0x18f8, 0x0000, 0xe708, 0xcf05, 0xb8e4, 0xa57f, 0x9594, 0x89c0, 0x8277, + 0x8001, 0x8277, 0x89c0, 0x9594, 0xa57f, 0xb8e4, 0xcf05, 0xe708, 0x0000, 0x30fb, 0x5a81, 0x7640, + 0x7fff, 0x7640, 0x5a81, 0x30fb, 0x0000, 0xcf05, 0xa57f, 0x89c0, 0x8001, 0x89c0, 0xa57f, 0xcf05, + 0x0000, 0x30fb, 0x5a81, 0x7640, 0x7fff, 0x7640, 0x5a81, 0x30fb, 0x0000, 0xcf05, 0xa57f, 0x89c0, + 0x8001, 0x89c0, 0xa57f, 0xcf05, 0x0000, 0x30fb, 0x5a81, 0x7640, 0x7fff, 0x7640, 0x5a81, 0x30fb, + 0x0000, 0xcf05, 0xa57f, 0x89c0, 0x8001, 0x89c0, 0xa57f, 0xcf05, 0x0000, 0x30fb, 0x5a81, 0x7640, + 0x7fff, 0x7640, 0x5a81, 0x30fb, 0x0000, 0xcf05, 0xa57f, 0x89c0, 0x8001, 0x89c0, 0xa57f, 0xcf05, + 0x0000, 0x5a81, 0x7fff, 0x5a81, 0x0000, 0xa57f, 0x8001, 0xa57f, 0x0000, 0x5a81, 0x7fff, 0x5a81, + 0x0000, 0xa57f, 0x8001, 0xa57f, 0x0000, 0x5a81, 0x7fff, 0x5a81, 0x0000, 0xa57f, 0x8001, 0xa57f, + 0x0000, 0x5a81, 0x7fff, 0x5a81, 0x0000, 0xa57f, 0x8001, 0xa57f, 0x0000, 0x5a81, 0x7fff, 0x5a81, + 0x0000, 0xa57f, 0x8001, 0xa57f, 0x0000, 0x5a81, 0x7fff, 0x5a81, 0x0000, 0xa57f, 0x8001, 0xa57f, + 0x0000, 0x5a81, 0x7fff, 0x5a81, 0x0000, 0xa57f, 0x8001, 0xa57f, 0x0000, 0x5a81, 0x7fff, 0x5a81, + 0x0000, 0xa57f, 0x8001, 0xa57f, +}; +s16 sSquareWaves[256] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, + 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, + 0x8001, 0x8001, 0x8001, 0x8001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7fff, 0x7fff, 0x7fff, 0x7fff, + 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x0000, 0x0000, 0x0000, 0x0000, + 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x0000, 0x0000, 0x0000, 0x0000, 0x8001, 0x8001, 0x8001, 0x8001, + 0x0000, 0x0000, 0x0000, 0x0000, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x0000, 0x0000, 0x0000, 0x0000, + 0x8001, 0x8001, 0x8001, 0x8001, 0x0000, 0x0000, 0x0000, 0x0000, 0x7fff, 0x7fff, 0x7fff, 0x7fff, + 0x0000, 0x0000, 0x0000, 0x0000, 0x8001, 0x8001, 0x8001, 0x8001, 0x0000, 0x0000, 0x0000, 0x0000, + 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x0000, 0x0000, 0x0000, 0x0000, 0x8001, 0x8001, 0x8001, 0x8001, + 0x0000, 0x0000, 0x7fff, 0x7fff, 0x0000, 0x0000, 0x8001, 0x8001, 0x0000, 0x0000, 0x7fff, 0x7fff, + 0x0000, 0x0000, 0x8001, 0x8001, 0x0000, 0x0000, 0x7fff, 0x7fff, 0x0000, 0x0000, 0x8001, 0x8001, + 0x0000, 0x0000, 0x7fff, 0x7fff, 0x0000, 0x0000, 0x8001, 0x8001, 0x0000, 0x0000, 0x7fff, 0x7fff, + 0x0000, 0x0000, 0x8001, 0x8001, 0x0000, 0x0000, 0x7fff, 0x7fff, 0x0000, 0x0000, 0x8001, 0x8001, + 0x0000, 0x0000, 0x7fff, 0x7fff, 0x0000, 0x0000, 0x8001, 0x8001, 0x0000, 0x0000, 0x7fff, 0x7fff, + 0x0000, 0x0000, 0x8001, 0x8001, +}; +s16 sEuUnknownWave6[256] = { + 0x0000, 0x9ba7, 0x9b41, 0x6c9b, 0x9450, 0xadda, 0x569e, 0x189a, 0x69bf, 0xb79d, 0x6fe9, 0x08ec, + 0x0d34, 0x1aea, 0xce76, 0xad86, 0x2710, 0xa038, 0x7e28, 0x2fd8, 0x3af8, 0x3bfa, 0xd10b, 0x84c7, + 0xcd7f, 0x18f4, 0xd4c8, 0x76f8, 0x8994, 0xaa11, 0x73fb, 0x6c01, 0x0000, 0x93ff, 0x8c05, 0x55ef, + 0x766c, 0x8907, 0x2b38, 0xe70d, 0x3281, 0x7b38, 0x2ef5, 0xc407, 0xc508, 0xd027, 0x81d8, 0x5fc9, + 0xd8f0, 0x5279, 0x318a, 0xe517, 0xf2cc, 0xf713, 0x9017, 0x4864, 0x9641, 0xe765, 0xa962, 0x5227, + 0x6bb0, 0x9364, 0x64bf, 0x645a, 0x0000, 0x9b41, 0x9450, 0x569e, 0x69bf, 0x6fe9, 0x0d34, 0xce76, + 0x2710, 0x7e28, 0x3af8, 0xd10b, 0xcd7f, 0xd4c8, 0x8994, 0x73fb, 0x0000, 0x8c05, 0x766c, 0x2b38, + 0x3281, 0x2ef5, 0xc508, 0x81d8, 0xd8f0, 0x318a, 0xf2cc, 0x9017, 0x9641, 0xa962, 0x6bb0, 0x64bf, + 0x0000, 0x9b41, 0x9450, 0x569e, 0x69bf, 0x6fe9, 0x0d34, 0xce76, 0x2710, 0x7e28, 0x3af8, 0xd10b, + 0xcd7f, 0xd4c8, 0x8994, 0x73fb, 0x0000, 0x8c05, 0x766c, 0x2b38, 0x3281, 0x2ef5, 0xc508, 0x81d8, + 0xd8f0, 0x318a, 0xf2cc, 0x9017, 0x9641, 0xa962, 0x6bb0, 0x64bf, 0x0000, 0x9450, 0x69bf, 0x0d34, + 0x2710, 0x3af8, 0xcd7f, 0x8994, 0x0000, 0x766c, 0x3281, 0xc508, 0xd8f0, 0xf2cc, 0x9641, 0x6bb0, + 0x0000, 0x9450, 0x69bf, 0x0d34, 0x2710, 0x3af8, 0xcd7f, 0x8994, 0x0000, 0x766c, 0x3281, 0xc508, + 0xd8f0, 0xf2cc, 0x9641, 0x6bb0, 0x0000, 0x9450, 0x69bf, 0x0d34, 0x2710, 0x3af8, 0xcd7f, 0x8994, + 0x0000, 0x766c, 0x3281, 0xc508, 0xd8f0, 0xf2cc, 0x9641, 0x6bb0, 0x0000, 0x9450, 0x69bf, 0x0d34, + 0x2710, 0x3af8, 0xcd7f, 0x8994, 0x0000, 0x766c, 0x3281, 0xc508, 0xd8f0, 0xf2cc, 0x9641, 0x6bb0, + 0x0000, 0x69bf, 0x2710, 0xcd7f, 0x0000, 0x3281, 0xd8f0, 0x9641, 0x0000, 0x69bf, 0x2710, 0xcd7f, + 0x0000, 0x3281, 0xd8f0, 0x9641, 0x0000, 0x69bf, 0x2710, 0xcd7f, 0x0000, 0x3281, 0xd8f0, 0x9641, + 0x0000, 0x69bf, 0x2710, 0xcd7f, 0x0000, 0x3281, 0xd8f0, 0x9641, 0x0000, 0x69bf, 0x2710, 0xcd7f, + 0x0000, 0x3281, 0xd8f0, 0x9641, 0x0000, 0x69bf, 0x2710, 0xcd7f, 0x0000, 0x3281, 0xd8f0, 0x9641, + 0x0000, 0x69bf, 0x2710, 0xcd7f, 0x0000, 0x3281, 0xd8f0, 0x9641, 0x0000, 0x69bf, 0x2710, 0xcd7f, + 0x0000, 0x3281, 0xd8f0, 0x9641, +}; +s16 gEuUnknownWave7[256] = { + 0x0000, 0x3fbc, 0x4eb4, 0x4f21, 0x6a49, 0x806f, 0x7250, 0x6a7b, 0x8d2e, 0xac0a, 0x98d6, 0x7832, + 0x7551, 0x71ca, 0x4eee, 0x3731, 0x4e20, 0x644d, 0x4a50, 0x23ba, 0x1b09, 0x119a, 0xe914, 0xccbe, + 0xe14e, 0xf8a3, 0xe47e, 0xc937, 0xd181, 0xde39, 0xcfc6, 0xcf94, 0x0000, 0x306c, 0x303a, 0x21c7, + 0x2e7f, 0x36c8, 0x1b82, 0x075e, 0x1eb2, 0x3341, 0x16ec, 0xee67, 0xe4f7, 0xdc45, 0xb5b0, 0x9bb4, + 0xb1e0, 0xc8ce, 0xb112, 0x8e37, 0x8aaf, 0x87cd, 0x672a, 0x53f7, 0x72d2, 0x9584, 0x8db0, 0x7f92, + 0x95b7, 0xb0de, 0xb14c, 0xc045, 0x0000, 0x4eb4, 0x6a49, 0x7250, 0x8d2e, 0x98d6, 0x7551, 0x4eee, + 0x4e20, 0x4a50, 0x1b09, 0xe914, 0xe14e, 0xe47e, 0xd181, 0xcfc6, 0x0000, 0x303a, 0x2e7f, 0x1b82, + 0x1eb2, 0x16ec, 0xe4f7, 0xb5b0, 0xb1e0, 0xb112, 0x8aaf, 0x672a, 0x72d2, 0x8db0, 0x95b7, 0xb14c, + 0x0000, 0x4eb4, 0x6a49, 0x7250, 0x8d2e, 0x98d6, 0x7551, 0x4eee, 0x4e20, 0x4a50, 0x1b09, 0xe914, + 0xe14e, 0xe47e, 0xd181, 0xcfc6, 0x0000, 0x303a, 0x2e7f, 0x1b82, 0x1eb2, 0x16ec, 0xe4f7, 0xb5b0, + 0xb1e0, 0xb112, 0x8aaf, 0x672a, 0x72d2, 0x8db0, 0x95b7, 0xb14c, 0x0000, 0x6a49, 0x8d2e, 0x7551, + 0x4e20, 0x1b09, 0xe14e, 0xd181, 0x0000, 0x2e7f, 0x1eb2, 0xe4f7, 0xb1e0, 0x8aaf, 0x72d2, 0x95b7, + 0x0000, 0x6a49, 0x8d2e, 0x7551, 0x4e20, 0x1b09, 0xe14e, 0xd181, 0x0000, 0x2e7f, 0x1eb2, 0xe4f7, + 0xb1e0, 0x8aaf, 0x72d2, 0x95b7, 0x0000, 0x6a49, 0x8d2e, 0x7551, 0x4e20, 0x1b09, 0xe14e, 0xd181, + 0x0000, 0x2e7f, 0x1eb2, 0xe4f7, 0xb1e0, 0x8aaf, 0x72d2, 0x95b7, 0x0000, 0x6a49, 0x8d2e, 0x7551, + 0x4e20, 0x1b09, 0xe14e, 0xd181, 0x0000, 0x2e7f, 0x1eb2, 0xe4f7, 0xb1e0, 0x8aaf, 0x72d2, 0x95b7, + 0x0000, 0x8d2e, 0x4e20, 0xe14e, 0x0000, 0x1eb2, 0xb1e0, 0x72d2, 0x0000, 0x8d2e, 0x4e20, 0xe14e, + 0x0000, 0x1eb2, 0xb1e0, 0x72d2, 0x0000, 0x8d2e, 0x4e20, 0xe14e, 0x0000, 0x1eb2, 0xb1e0, 0x72d2, + 0x0000, 0x8d2e, 0x4e20, 0xe14e, 0x0000, 0x1eb2, 0xb1e0, 0x72d2, 0x0000, 0x8d2e, 0x4e20, 0xe14e, + 0x0000, 0x1eb2, 0xb1e0, 0x72d2, 0x0000, 0x8d2e, 0x4e20, 0xe14e, 0x0000, 0x1eb2, 0xb1e0, 0x72d2, + 0x0000, 0x8d2e, 0x4e20, 0xe14e, 0x0000, 0x1eb2, 0xb1e0, 0x72d2, 0x0000, 0x8d2e, 0x4e20, 0xe14e, + 0x0000, 0x1eb2, 0xb1e0, 0x72d2, +}; +s16 *gWaveSamples[6] = { sSawtoothWaves, sTriangleWaves, sSineWaves, sSquareWaves, sEuUnknownWave6, gEuUnknownWave7 }; + +#else +// !VERSION_EU + +s16 sSineWave[0x40] = { + 0, 3211, 6392, 9511, 12539, 15446, 18204, 20787, 23169, 25329, 27244, + 28897, 30272, 31356, 32137, 32609, 0x7FFF, 32609, 32137, 31356, 30272, 28897, + 27244, 25329, 23169, 20787, 18204, 15446, 12539, 9511, 6392, 3211, 0, + -3211, -6392, -9511, -12539, -15446, -18204, -20787, -23169, -25329, -27244, -28897, + -30272, -31356, -32137, -32609, -0x7FFF, -32609, -32137, -31356, -30272, -28897, -27244, + -25329, -23169, -20787, -18204, -15446, -12539, -9511, -6392, -3211, +}; + +s16 sSquareWave[0x40] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, + 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, + -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, +}; +s16 sTriangleWave[0x40] = { + 0, 0x7FF, 0xFFF, 0x17FF, 0x1FFF, 0x27FF, 0x2FFF, 0x37FF, 0x3FFF, 0x47FF, 0x4FFF, + 0x57FF, 0x5FFF, 0x67FF, 0x6FFF, 0x77FF, 0x7FFF, 0x77FF, 0x6FFF, 0x67FF, 0x5FFF, 0x57FF, + 0x4FFF, 0x47FF, 0x3FFF, 0x37FF, 0x2FFF, 0x27FF, 0x1FFF, 0x17FF, 0xFFF, 0x7FF, 0, + -0x7FF, -0xFFF, -0x17FF, -0x1FFF, -10239, -0x2FFF, -0x37FF, -0x3FFF, -0x47FF, -0x4FFF, -22527, + -24575, -26623, -28671, -30719, -0x7FFF, -30719, -28671, -26623, -24575, -22527, -0x4FFF, + -0x47FF, -0x3FFF, -0x37FF, -0x2FFF, -0x27FF, -0x1FFF, -0x17FF, -0xFFF, -0x7FF, +}; + +s16 sSawtoothWave[0x40] = { + 0, 1023, 2047, 3071, 4095, 5119, 6143, 7167, 8191, 9215, 10239, + 11263, 0x2FFF, 13311, 0x37FF, 15359, 0x3FFF, 17407, 0x47FF, 19455, 0x4FFF, 21503, + 22527, 23551, 24575, 25599, 26623, 27647, 28671, 29695, 30719, 31743, -0x7FFF, + -31743, -30719, -29695, -28671, -27647, -26623, -25599, -24575, -23551, -22527, -21503, + -0x4FFF, -19455, -0x47FF, -17407, -0x3FFF, -15359, -0x37FF, -13311, -0x2FFF, -11263, -10239, + -9215, -8191, -7167, -6143, -5119, -4095, -3071, -2047, -1023, +}; +s16 *gWaveSamples[4] = { sSawtoothWave, sTriangleWave, sSineWave, sSquareWave }; +#endif + +#ifdef VERSION_SH +s32 unk_sh_data_0[2] = {0, 0}; +f32 gPitchBendFrequencyScale[256] = { + 0.5f, 0.5f, 0.502736f, 0.505488f, 0.508254f, 0.511036f, 0.513833f, 0.516645f, 0.519472f, + 0.522315f, 0.525174f, 0.528048f, 0.530938f, 0.533843f, 0.536765f, 0.539702f, 0.542656f, 0.545626f, + 0.548612f, 0.551614f, 0.554633f, 0.557669f, 0.560721f, 0.563789f, 0.566875f, 0.569977f, 0.573097f, + 0.576233f, 0.579387f, 0.582558f, 0.585746f, 0.588951f, 0.592175f, 0.595415f, 0.598674f, 0.601950f, + 0.605245f, 0.608557f, 0.611888f, 0.615236f, 0.618603f, 0.621989f, 0.625393f, 0.628815f, 0.632257f, + 0.635717f, 0.639196f, 0.642694f, 0.646212f, 0.649748f, 0.653304f, 0.656880f, 0.660475f, 0.664089f, + 0.667724f, 0.671378f, 0.675052f, 0.678747f, 0.682461f, 0.686196f, 0.689952f, 0.693727f, 0.697524f, + 0.701341f, 0.705180f, 0.709039f, 0.712919f, 0.716821f, 0.720744f, 0.724689f, 0.728655f, 0.732642f, + 0.736652f, 0.740684f, 0.744737f, 0.748813f, 0.752911f, 0.757031f, 0.761175f, 0.765340f, 0.769529f, + 0.773740f, 0.777975f, 0.782232f, 0.786513f, 0.790818f, 0.795146f, 0.799497f, 0.803873f, 0.808272f, + 0.812696f, 0.817144f, 0.821616f, 0.826112f, 0.830633f, 0.835179f, 0.839750f, 0.844346f, 0.848966f, + 0.853613f, 0.858284f, 0.862982f, 0.867704f, 0.872453f, 0.877228f, 0.882029f, 0.886856f, 0.891709f, + 0.896590f, 0.901496f, 0.906430f, 0.911391f, 0.916379f, 0.921394f, 0.926436f, 0.931507f, 0.936604f, + 0.941730f, 0.946884f, 0.952066f, 0.957277f, 0.962516f, 0.967783f, 0.973080f, 0.978405f, 0.983760f, + 0.989144f, 0.994557f, 1.0f, 1.005473f, 1.010975f, 1.016508f, 1.022071f, 1.027665f, 1.033289f, + 1.038944f, 1.044630f, 1.050347f, 1.056095f, 1.061875f, 1.067687f, 1.073530f, 1.079405f, 1.085312f, + 1.091252f, 1.097224f, 1.103229f, 1.109267f, 1.115337f, 1.121441f, 1.127579f, 1.133750f, 1.139955f, + 1.146193f, 1.152466f, 1.158773f, 1.165115f, 1.171491f, 1.177903f, 1.184349f, 1.190831f, 1.197348f, + 1.203901f, 1.210489f, 1.217114f, 1.223775f, 1.230473f, 1.237207f, 1.243978f, 1.250786f, 1.257631f, + 1.264514f, 1.271434f, 1.278392f, 1.285389f, 1.292423f, 1.299497f, 1.306608f, 1.313759f, 1.320949f, + 1.328178f, 1.335447f, 1.342756f, 1.350104f, 1.357493f, 1.364922f, 1.372392f, 1.379903f, 1.387455f, + 1.395048f, 1.402683f, 1.410360f, 1.418078f, 1.425839f, 1.433642f, 1.441488f, 1.449377f, 1.457309f, + 1.465285f, 1.473304f, 1.481367f, 1.489474f, 1.497626f, 1.505822f, 1.514063f, 1.522349f, 1.530681f, + 1.539058f, 1.547481f, 1.555950f, 1.564465f, 1.573027f, 1.581636f, 1.590292f, 1.598995f, 1.607746f, + 1.616545f, 1.625392f, 1.634287f, 1.643231f, 1.652224f, 1.661266f, 1.670358f, 1.679500f, 1.688691f, + 1.697933f, 1.707225f, 1.716569f, 1.725963f, 1.735409f, 1.744906f, 1.754456f, 1.764058f, 1.773712f, + 1.783419f, 1.793179f, 1.802993f, 1.812860f, 1.822782f, 1.832757f, 1.842788f, 1.852873f, 1.863013f, + 1.873209f, 1.883461f, 1.893768f, 1.904132f, 1.914553f, 1.925031f, 1.935567f, 1.946159f, 1.956810f, + 1.967520f, 1.978287f, 1.989114f, 2.0f +}; +#endif + +#ifdef VERSION_SH +f32 unk_sh_data_1[] = { + 0.890899f, 0.890899f, 0.89171f, 0.892521f, 0.893333f, 0.894146f, 0.89496f, 0.895774f, + 0.89659f, 0.897406f, 0.898222f, 0.89904f, 0.899858f, 0.900677f, 0.901496f, 0.902317f, + 0.903138f, 0.90396f, 0.904783f, 0.905606f, 0.90643f, 0.907255f, 0.908081f, 0.908907f, + 0.909734f, 0.910562f, 0.911391f, 0.91222f, 0.91305f, 0.913881f, 0.914713f, 0.915545f, + 0.916379f, 0.917213f, 0.918047f, 0.918883f, 0.919719f, 0.920556f, 0.921394f, 0.922232f, + 0.923072f, 0.923912f, 0.924752f, 0.925594f, 0.926436f, 0.927279f, 0.928123f, 0.928968f, + 0.929813f, 0.93066f, 0.931507f, 0.932354f, 0.933203f, 0.934052f, 0.934902f, 0.935753f, + 0.936604f, 0.937457f, 0.93831f, 0.939164f, 0.940019f, 0.940874f, 0.94173f, 0.942587f, + 0.943445f, 0.944304f, 0.945163f, 0.946023f, 0.946884f, 0.947746f, 0.948608f, 0.949472f, + 0.950336f, 0.951201f, 0.952066f, 0.952933f, 0.9538f, 0.954668f, 0.955537f, 0.956406f, + 0.957277f, 0.958148f, 0.95902f, 0.959893f, 0.960766f, 0.961641f, 0.962516f, 0.963392f, + 0.964268f, 0.965146f, 0.966024f, 0.966903f, 0.967783f, 0.968664f, 0.969546f, 0.970428f, + 0.971311f, 0.972195f, 0.97308f, 0.973965f, 0.974852f, 0.975739f, 0.976627f, 0.977516f, + 0.978405f, 0.979296f, 0.980187f, 0.981079f, 0.981972f, 0.982865f, 0.98376f, 0.984655f, + 0.985551f, 0.986448f, 0.987346f, 0.988244f, 0.989144f, 0.990044f, 0.990945f, 0.991847f, + 0.992749f, 0.993653f, 0.994557f, 0.995462f, 0.996368f, 0.997275f, 0.998182f, 0.999091f, + 1.0f, 1.00091f, 1.001821f, 1.002733f, 1.003645f, 1.004559f, 1.005473f, 1.006388f, + 1.007304f, 1.00822f, 1.009138f, 1.010056f, 1.010975f, 1.011896f, 1.012816f, 1.013738f, + 1.014661f, 1.015584f, 1.016508f, 1.017433f, 1.018359f, 1.019286f, 1.020214f, 1.021142f, + 1.022071f, 1.023002f, 1.023933f, 1.024864f, 1.025797f, 1.026731f, 1.027665f, 1.0286f, + 1.029536f, 1.030473f, 1.031411f, 1.03235f, 1.033289f, 1.03423f, 1.035171f, 1.036113f, + 1.037056f, 1.038f, 1.038944f, 1.03989f, 1.040836f, 1.041783f, 1.042731f, 1.04368f, + 1.04463f, 1.045581f, 1.046532f, 1.047485f, 1.048438f, 1.049392f, 1.050347f, 1.051303f, + 1.05226f, 1.053217f, 1.054176f, 1.055135f, 1.056095f, 1.057056f, 1.058018f, 1.058981f, + 1.059945f, 1.06091f, 1.061875f, 1.062842f, 1.063809f, 1.064777f, 1.065746f, 1.066716f, + 1.067687f, 1.068658f, 1.069631f, 1.070604f, 1.071578f, 1.072554f, 1.07353f, 1.074507f, + 1.075485f, 1.076463f, 1.077443f, 1.078424f, 1.079405f, 1.080387f, 1.08137f, 1.082355f, + 1.08334f, 1.084325f, 1.085312f, 1.0863f, 1.087289f, 1.088278f, 1.089268f, 1.09026f, + 1.091252f, 1.092245f, 1.093239f, 1.094234f, 1.09523f, 1.096226f, 1.097224f, 1.098223f, + 1.099222f, 1.100222f, 1.101224f, 1.102226f, 1.103229f, 1.104233f, 1.105238f, 1.106244f, + 1.10725f, 1.108258f, 1.109267f, 1.110276f, 1.111287f, 1.112298f, 1.11331f, 1.114323f, + 1.115337f, 1.116352f, 1.117368f, 1.118385f, 1.119403f, 1.120422f, 1.121441f, 1.122462f, +}; + +// Shindou moved these variables down here. :/ +// clang-format off +f32 gNoteFrequencies[128] = { + 0.105112f, 0.111362f, 0.117984f, 0.125f, 0.132433f, 0.140308f, 0.148651f, 0.15749f, 0.166855f, 0.176777f, 0.187288f, 0.198425f, + 0.210224f, 0.222725f, 0.235969f, 0.25f, 0.264866f, 0.280616f, 0.297302f, 0.31498f, 0.33371f, 0.353553f, 0.374577f, 0.39685f, + 0.420448f, 0.445449f, 0.471937f, 0.5f, 0.529732f, 0.561231f, 0.594604f, 0.629961f, 0.66742f, 0.707107f, 0.749154f, 0.793701f, + 0.840897f, 0.890899f, 0.943875f, 1.0f, 1.059463f, 1.122462f, 1.189207f, 1.259921f, 1.33484f, 1.414214f, 1.498307f, 1.587401f, + 1.681793f, 1.781798f, 1.887749f, 2.0f, 2.118926f, 2.244924f, 2.378414f, 2.519842f, 2.66968f, 2.828428f, 2.996615f, 3.174803f, + 3.363586f, 3.563596f, 3.775498f, 4.0f, 4.237853f, 4.489849f, 4.756829f, 5.039685f, 5.33936f, 5.656855f, 5.993229f, 6.349606f, + 6.727173f, 7.127192f, 7.550996f, 8.0f, 8.475705f, 8.979697f, 9.513658f, 10.07937f, 10.67872f, 11.31371f, 11.986459f, 12.699211f, + 13.454346f, 14.254383f, 15.101993f, 16.0f, 16.95141f, 17.959394f, 19.027315f, 20.15874f, 21.35744f, 22.62742f, 23.972918f, 25.398422f, + 26.908691f, 28.508766f, 30.203985f, 32.0f, 33.90282f, 35.91879f, 38.05463f, 40.31748f, 42.71488f, 45.25484f, 47.945835f, 50.796844f, + 53.817383f, 57.017532f, 60.40797f, 64.0f, 67.80564f, 71.83758f, 76.10926f, 80.63496f, 85.42976f, 45.25484f, 47.945835f, 50.796844f, + 53.817383f, 57.017532f, 60.40797f, 64.0f, 67.80564f, 71.83758f, 76.10926f, 80.63496f +}; +// clang-format on + +u8 gDefaultShortNoteVelocityTable[16] = { + 12, 25, 38, 51, 57, 64, 71, 76, 83, 89, 96, 102, 109, 115, 121, 127, +}; + +u8 gDefaultShortNoteDurationTable[16] = { + 229, 203, 177, 151, 139, 126, 113, 100, 87, 74, 61, 48, 36, 23, 10, 0, +}; + +struct AdsrEnvelope gDefaultEnvelope[] = { + { BSWAP16(4), BSWAP16(32000) }, // go from 0 to 32000 over the course of 16ms + { BSWAP16(1000), BSWAP16(32000) }, // stay there for 4.16 seconds + { BSWAP16(ADSR_HANG), 0 } // then continue staying there +}; + +u8 unk_sh_data2[4] = { 0, 0, 0, 0 }; + +struct NoteSubEu gZeroNoteSub = { 0 }; +struct NoteSubEu gDefaultNoteSub = { + 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, { NULL }, +#ifdef VERSION_SH + 0 +#endif +}; + +u16 gHeadsetPanQuantization[0x40] = { +0x3C, 0x3A, 0x38, 0x36, 0x34, 0x32, 0x30, 0x2E, +0x2C, 0x2A, 0x28, 0x26, 0x24, 0x22, 0x20, 0x1E, +0x1C, 0x1A, 0x18, 0x16, 0x14, 0x12, 0x10, 0x0E, +0x0C, 0x0A, 0x08, 0x06, 0x04, 0x02, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; +#endif + +#ifdef VERSION_EU +u8 euUnknownData_8030194c[4] = { 0x40, 0x20, 0x10, 0x08 }; +u16 gHeadsetPanQuantization[0x10] = { + 0x40, 0x40, 0x30, 0x30, 0x20, 0x20, 0x10, 0, 0, 0, +}; +#elif !defined(VERSION_SH) +u16 gHeadsetPanQuantization[10] = { 0x40, 0x30, 0x20, 0x10, 0, 0, 0, 0, 0, 0 }; +#endif + +#if defined(VERSION_EU) || defined(VERSION_SH) +s16 euUnknownData_80301950[64] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 500, 0, 0, 0, 0, 0, 0, 0, 500, 0, 0, 0, 0, 0, 0, 0, 500, 0, 0, 0, 0, 0, 0, 0, 500, 0, 0, 0, 0, +}; +#endif + +// Linearly interpolated between +// f(0/2 * 127) = 1 +// f(1/2 * 127) = 1/sqrt(2) +// f(2/2 * 127) = 0 +f32 gHeadsetPanVolume[128] = { + 1.0f, 0.995386f, 0.990772f, 0.986157f, 0.981543f, 0.976929f, 0.972315f, 0.967701f, 0.963087f, + 0.958472f, 0.953858f, 0.949244f, 0.94463f, 0.940016f, 0.935402f, 0.930787f, 0.926173f, 0.921559f, + 0.916945f, 0.912331f, 0.907717f, 0.903102f, 0.898488f, 0.893874f, 0.88926f, 0.884646f, 0.880031f, + 0.875417f, 0.870803f, 0.866189f, 0.861575f, 0.856961f, 0.852346f, 0.847732f, 0.843118f, 0.838504f, + 0.83389f, 0.829276f, 0.824661f, 0.820047f, 0.815433f, 0.810819f, 0.806205f, 0.801591f, 0.796976f, + 0.792362f, 0.787748f, 0.783134f, 0.77852f, 0.773906f, 0.769291f, 0.764677f, 0.760063f, 0.755449f, + 0.750835f, 0.74622f, 0.741606f, 0.736992f, 0.732378f, 0.727764f, 0.72315f, 0.718535f, 0.713921f, + 0.709307f, 0.70537f, 0.70211f, 0.69885f, 0.695591f, 0.692331f, 0.689071f, 0.685811f, 0.682551f, + 0.679291f, 0.676031f, 0.672772f, 0.669512f, 0.666252f, 0.662992f, 0.659732f, 0.656472f, 0.653213f, + 0.649953f, 0.646693f, 0.643433f, 0.640173f, 0.636913f, 0.633654f, 0.630394f, 0.627134f, 0.623874f, + 0.620614f, 0.617354f, 0.614094f, 0.610835f, 0.607575f, 0.604315f, 0.601055f, 0.597795f, 0.594535f, + 0.591276f, 0.588016f, 0.584756f, 0.581496f, 0.578236f, 0.574976f, 0.571717f, 0.568457f, 0.565197f, + 0.561937f, 0.558677f, 0.555417f, 0.552157f, 0.548898f, 0.545638f, 0.542378f, 0.539118f, 0.535858f, + 0.532598f, 0.529339f, 0.526079f, 0.522819f, 0.519559f, 0.516299f, 0.513039f, 0.50978f, 0.50652f, + 0.50326f, 0.5f +}; + +// Linearly interpolated between +// f(0/4 * 127) = 1/sqrt(2) +// f(1/4 * 127) = 1 +// f(2/4 * 127) = 1/sqrt(2) +// f(3/4 * 127) = 0 +// f(4/4 * 127) = 1/sqrt(8) +f32 gStereoPanVolume[128] = { + 0.707f, 0.716228f, 0.725457f, 0.734685f, 0.743913f, 0.753142f, 0.76237f, 0.771598f, 0.780827f, + 0.790055f, 0.799283f, 0.808512f, 0.81774f, 0.826968f, 0.836197f, 0.845425f, 0.854654f, 0.863882f, + 0.87311f, 0.882339f, 0.891567f, 0.900795f, 0.910024f, 0.919252f, 0.92848f, 0.937709f, 0.946937f, + 0.956165f, 0.965394f, 0.974622f, 0.98385f, 0.993079f, 0.997693f, 0.988465f, 0.979236f, 0.970008f, + 0.960779f, 0.951551f, 0.942323f, 0.933095f, 0.923866f, 0.914638f, 0.905409f, 0.896181f, 0.886953f, + 0.877724f, 0.868496f, 0.859268f, 0.850039f, 0.840811f, 0.831583f, 0.822354f, 0.813126f, 0.803898f, + 0.794669f, 0.785441f, 0.776213f, 0.766984f, 0.757756f, 0.748528f, 0.739299f, 0.730071f, 0.720843f, + 0.711614f, 0.695866f, 0.673598f, 0.651331f, 0.629063f, 0.606795f, 0.584528f, 0.56226f, 0.539992f, + 0.517724f, 0.495457f, 0.473189f, 0.450921f, 0.428654f, 0.406386f, 0.384118f, 0.36185f, 0.339583f, + 0.317315f, 0.295047f, 0.27278f, 0.250512f, 0.228244f, 0.205976f, 0.183709f, 0.161441f, 0.139173f, + 0.116905f, 0.094638f, 0.07237f, 0.050102f, 0.027835f, 0.005567f, 0.00835f, 0.019484f, 0.030618f, + 0.041752f, 0.052886f, 0.06402f, 0.075154f, 0.086287f, 0.097421f, 0.108555f, 0.119689f, 0.130823f, + 0.141957f, 0.153091f, 0.164224f, 0.175358f, 0.186492f, 0.197626f, 0.20876f, 0.219894f, 0.231028f, + 0.242161f, 0.253295f, 0.264429f, 0.275563f, 0.286697f, 0.297831f, 0.308965f, 0.320098f, 0.331232f, + 0.342366f, 0.3535f +}; + +// gDefaultVolume[k] = cos(pi/2 * k / 127) +f32 gDefaultPanVolume[128] = { + 1.0f, 0.999924f, 0.999694f, 0.999312f, 0.998776f, 0.998088f, 0.997248f, 0.996254f, 0.995109f, + 0.993811f, 0.992361f, 0.990759f, 0.989006f, 0.987101f, 0.985045f, 0.982839f, 0.980482f, 0.977976f, + 0.97532f, 0.972514f, 0.96956f, 0.966457f, 0.963207f, 0.959809f, 0.956265f, 0.952574f, 0.948737f, + 0.944755f, 0.940629f, 0.936359f, 0.931946f, 0.92739f, 0.922692f, 0.917853f, 0.912873f, 0.907754f, + 0.902497f, 0.897101f, 0.891567f, 0.885898f, 0.880093f, 0.874153f, 0.868079f, 0.861873f, 0.855535f, + 0.849066f, 0.842467f, 0.835739f, 0.828884f, 0.821901f, 0.814793f, 0.807561f, 0.800204f, 0.792725f, + 0.785125f, 0.777405f, 0.769566f, 0.76161f, 0.753536f, 0.745348f, 0.737045f, 0.72863f, 0.720103f, + 0.711466f, 0.70272f, 0.693867f, 0.684908f, 0.675843f, 0.666676f, 0.657406f, 0.648036f, 0.638567f, + 0.629f, 0.619337f, 0.609579f, 0.599728f, 0.589785f, 0.579752f, 0.56963f, 0.559421f, 0.549126f, + 0.538748f, 0.528287f, 0.517745f, 0.507124f, 0.496425f, 0.485651f, 0.474802f, 0.46388f, 0.452888f, + 0.441826f, 0.430697f, 0.419502f, 0.408243f, 0.396921f, 0.385538f, 0.374097f, 0.362598f, 0.351044f, + 0.339436f, 0.327776f, 0.316066f, 0.304308f, 0.292503f, 0.280653f, 0.268761f, 0.256827f, 0.244854f, + 0.232844f, 0.220798f, 0.208718f, 0.196606f, 0.184465f, 0.172295f, 0.160098f, 0.147877f, 0.135634f, + 0.12337f, 0.111087f, 0.098786f, 0.086471f, 0.074143f, 0.061803f, 0.049454f, 0.037097f, 0.024734f, + 0.012368f, 0.0f +}; + +#if defined(VERSION_JP) || defined(VERSION_US) +// gVolRampingLhs136[k] = 2^16 * max(1, (256*k)^(1/17) +f32 gVolRampingLhs136[128] = { + 65536.0f, 90811.555f, 94590.766f, 96873.96f, 98527.26f, 99829.06f, 100905.47f, + 101824.61f, 102627.57f, 103341.086f, 103983.55f, 104568.164f, 105104.75f, 105600.8f, + 106062.14f, 106493.46f, 106898.52f, 107280.414f, 107641.73f, 107984.62f, 108310.93f, + 108622.23f, 108919.875f, 109205.055f, 109478.8f, 109742.0f, 109995.48f, 110239.94f, + 110476.02f, 110704.305f, 110925.3f, 111139.45f, 111347.21f, 111548.945f, 111745.0f, + 111935.7f, 112121.35f, 112302.2f, 112478.51f, 112650.51f, 112818.4f, 112982.38f, + 113142.66f, 113299.37f, 113452.69f, 113602.766f, 113749.734f, 113893.73f, 114034.87f, + 114173.26f, 114309.02f, 114442.26f, 114573.055f, 114701.5f, 114827.69f, 114951.695f, + 115073.6f, 115193.47f, 115311.375f, 115427.39f, 115541.56f, 115653.96f, 115764.63f, + 115873.64f, 115981.04f, 116086.86f, 116191.164f, 116293.99f, 116395.38f, 116495.38f, + 116594.02f, 116691.34f, 116787.39f, 116882.19f, 116975.77f, 117068.17f, 117159.414f, + 117249.54f, 117338.57f, 117426.53f, 117513.45f, 117599.35f, 117684.266f, 117768.2f, + 117851.195f, 117933.266f, 118014.44f, 118094.72f, 118174.14f, 118252.71f, 118330.46f, + 118407.4f, 118483.55f, 118558.914f, 118633.53f, 118707.4f, 118780.54f, 118852.97f, + 118924.695f, 118995.74f, 119066.11f, 119135.82f, 119204.88f, 119273.31f, 119341.125f, + 119408.32f, 119474.92f, 119540.93f, 119606.36f, 119671.22f, 119735.52f, 119799.28f, + 119862.5f, 119925.195f, 119987.36f, 120049.02f, 120110.18f, 120170.84f, 120231.016f, + 120290.71f, 120349.945f, 120408.7f, 120467.016f, 120524.875f, 120582.3f, 120639.28f, + 120695.84f, 120751.984f +}; + +// gVolRampingRhs136[k] = 1 / max(1, (256*k)^(1/17)) +f32 gVolRampingRhs136[128] = { + 1.0f, 0.72167f, 0.692837f, 0.676508f, 0.665156f, 0.656482f, 0.649479f, 0.643616f, 0.638581f, + 0.634172f, 0.630254f, 0.62673f, 0.62353f, 0.620601f, 0.617902f, 0.615399f, 0.613067f, 0.610885f, + 0.608835f, 0.606901f, 0.605073f, 0.603339f, 0.60169f, 0.600119f, 0.598618f, 0.597183f, 0.595806f, + 0.594485f, 0.593215f, 0.591991f, 0.590812f, 0.589674f, 0.588573f, 0.587509f, 0.586478f, 0.585479f, + 0.58451f, 0.583568f, 0.582654f, 0.581764f, 0.580898f, 0.580055f, 0.579233f, 0.578432f, 0.57765f, + 0.576887f, 0.576142f, 0.575414f, 0.574701f, 0.574005f, 0.573323f, 0.572656f, 0.572002f, 0.571361f, + 0.570733f, 0.570118f, 0.569514f, 0.568921f, 0.568339f, 0.567768f, 0.567207f, 0.566656f, 0.566114f, + 0.565582f, 0.565058f, 0.564543f, 0.564036f, 0.563537f, 0.563046f, 0.562563f, 0.562087f, 0.561618f, + 0.561156f, 0.560701f, 0.560253f, 0.559811f, 0.559375f, 0.558945f, 0.558521f, 0.558102f, 0.557689f, + 0.557282f, 0.55688f, 0.556483f, 0.556091f, 0.555704f, 0.555322f, 0.554944f, 0.554571f, 0.554203f, + 0.553839f, 0.553479f, 0.553123f, 0.552772f, 0.552424f, 0.55208f, 0.55174f, 0.551404f, 0.551071f, + 0.550742f, 0.550417f, 0.550095f, 0.549776f, 0.549461f, 0.549148f, 0.548839f, 0.548534f, 0.548231f, + 0.547931f, 0.547634f, 0.54734f, 0.547048f, 0.54676f, 0.546474f, 0.546191f, 0.54591f, 0.545632f, + 0.545357f, 0.545084f, 0.544813f, 0.544545f, 0.54428f, 0.544016f, 0.543755f, 0.543496f, 0.543239f, + 0.542985f, 0.542732f +}; + +// gVolRampingLhs144[k] = 2^16 * max(1, (256*k)^(1/18)) +f32 gVolRampingLhs144[128] = { + 65536.0f, 89180.734f, 92681.9f, 94793.33f, 96320.52f, 97522.02f, 98514.84f, + 99362.14f, 100101.99f, 100759.16f, 101350.664f, 101888.74f, 102382.46f, 102838.75f, + 103263.016f, 103659.58f, 104031.914f, 104382.89f, 104714.88f, 105029.89f, 105329.61f, + 105615.5f, 105888.81f, 106150.63f, 106401.914f, 106643.49f, 106876.12f, 107100.44f, + 107317.05f, 107526.47f, 107729.17f, 107925.6f, 108116.125f, 108301.12f, 108480.88f, + 108655.72f, 108825.91f, 108991.68f, 109153.28f, 109310.914f, 109464.77f, 109615.04f, + 109761.88f, 109905.46f, 110045.92f, 110183.41f, 110318.02f, 110449.91f, 110579.17f, + 110705.914f, 110830.234f, 110952.234f, 111071.99f, 111189.59f, 111305.12f, 111418.64f, + 111530.23f, 111639.95f, 111747.875f, 111854.05f, 111958.54f, 112061.4f, 112162.67f, + 112262.42f, 112360.68f, 112457.51f, 112552.93f, 112647.0f, 112739.76f, 112831.23f, + 112921.46f, 113010.484f, 113098.33f, 113185.02f, 113270.61f, 113355.11f, 113438.555f, + 113520.97f, 113602.375f, 113682.805f, 113762.27f, 113840.81f, 113918.44f, 113995.18f, + 114071.055f, 114146.08f, 114220.266f, 114293.65f, 114366.24f, 114438.06f, 114509.12f, + 114579.44f, 114649.02f, 114717.91f, 114786.086f, 114853.586f, 114920.42f, 114986.6f, + 115052.14f, 115117.055f, 115181.34f, 115245.04f, 115308.13f, 115370.65f, 115432.59f, + 115493.98f, 115554.81f, 115615.11f, 115674.875f, 115734.12f, 115792.85f, 115851.08f, + 115908.82f, 115966.07f, 116022.85f, 116079.16f, 116135.01f, 116190.4f, 116245.35f, + 116299.87f, 116353.945f, 116407.6f, 116460.84f, 116513.67f, 116566.09f, 116618.125f, + 116669.76f, 116721.01f +}; + +// gVolRampingRhs144[k] = 1 / max(1, (256*k)^(1/18)) +f32 gVolRampingRhs144[128] = { + 1.0f, 0.734867f, 0.707107f, 0.691357f, 0.680395f, 0.672012f, 0.66524f, 0.659567f, 0.654692f, + 0.650422f, 0.646626f, 0.643211f, 0.64011f, 0.637269f, 0.634651f, 0.632223f, 0.629961f, 0.627842f, + 0.625852f, 0.623975f, 0.622199f, 0.620515f, 0.618913f, 0.617387f, 0.615929f, 0.614533f, 0.613196f, + 0.611912f, 0.610677f, 0.609487f, 0.60834f, 0.607233f, 0.606163f, 0.605128f, 0.604125f, 0.603153f, + 0.60221f, 0.601294f, 0.600403f, 0.599538f, 0.598695f, 0.597874f, 0.597074f, 0.596294f, 0.595533f, + 0.59479f, 0.594064f, 0.593355f, 0.592661f, 0.591983f, 0.591319f, 0.590669f, 0.590032f, 0.589408f, + 0.588796f, 0.588196f, 0.587608f, 0.58703f, 0.586463f, 0.585906f, 0.58536f, 0.584822f, 0.584294f, + 0.583775f, 0.583265f, 0.582762f, 0.582268f, 0.581782f, 0.581303f, 0.580832f, 0.580368f, 0.579911f, + 0.57946f, 0.579017f, 0.578579f, 0.578148f, 0.577722f, 0.577303f, 0.576889f, 0.576481f, 0.576078f, + 0.575681f, 0.575289f, 0.574902f, 0.574519f, 0.574142f, 0.573769f, 0.5734f, 0.573036f, 0.572677f, + 0.572321f, 0.57197f, 0.571623f, 0.57128f, 0.57094f, 0.570605f, 0.570273f, 0.569945f, 0.56962f, + 0.569299f, 0.568981f, 0.568667f, 0.568355f, 0.568047f, 0.567743f, 0.567441f, 0.567142f, 0.566846f, + 0.566553f, 0.566263f, 0.565976f, 0.565692f, 0.56541f, 0.565131f, 0.564854f, 0.56458f, 0.564309f, + 0.56404f, 0.563773f, 0.563509f, 0.563247f, 0.562987f, 0.56273f, 0.562475f, 0.562222f, 0.561971f, + 0.561722f, 0.561476f +}; + +// gVolRampingLhs128[k] = 2^16 * max(1, (256*k)^(1/16)) +f32 gVolRampingLhs128[128] = { + 65536.0f, 92681.9f, 96785.28f, 99269.31f, 101070.33f, 102489.78f, 103664.336f, + 104667.914f, 105545.09f, 106324.92f, 107027.39f, 107666.84f, 108253.95f, 108796.87f, + 109301.95f, 109774.29f, 110217.98f, 110636.39f, 111032.33f, 111408.164f, 111765.9f, + 112107.234f, 112433.66f, 112746.46f, 113046.766f, 113335.555f, 113613.72f, 113882.02f, + 114141.164f, 114391.77f, 114634.414f, 114869.58f, 115097.74f, 115319.31f, 115534.68f, + 115744.19f, 115948.16f, 116146.875f, 116340.625f, 116529.66f, 116714.195f, 116894.46f, + 117070.64f, 117242.945f, 117411.52f, 117576.55f, 117738.17f, 117896.54f, 118051.77f, + 118204.0f, 118353.35f, 118499.92f, 118643.83f, 118785.16f, 118924.01f, 119060.47f, + 119194.625f, 119326.555f, 119456.336f, 119584.03f, 119709.71f, 119833.445f, 119955.29f, + 120075.31f, 120193.555f, 120310.08f, 120424.94f, 120538.17f, 120649.836f, 120759.97f, + 120868.62f, 120975.82f, 121081.62f, 121186.05f, 121289.14f, 121390.94f, 121491.47f, + 121590.766f, 121688.87f, 121785.79f, 121881.57f, 121976.24f, 122069.82f, 122162.33f, + 122253.805f, 122344.266f, 122433.73f, 122522.23f, 122609.77f, 122696.4f, 122782.11f, + 122866.93f, 122950.89f, 123033.99f, 123116.26f, 123197.72f, 123278.37f, 123358.24f, + 123437.34f, 123515.69f, 123593.3f, 123670.19f, 123746.36f, 123821.84f, 123896.63f, + 123970.76f, 124044.23f, 124117.04f, 124189.23f, 124260.78f, 124331.73f, 124402.07f, + 124471.83f, 124540.99f, 124609.59f, 124677.63f, 124745.12f, 124812.055f, 124878.47f, + 124944.34f, 125009.71f, 125074.57f, 125138.92f, 125202.79f, 125266.164f, 125329.06f, + 125391.5f, 125453.47f +}; + +// gVolRampingRhs128[k] = 1 / max(1, (256*k)^(1/16)) +f32 gVolRampingRhs128[128] = { + 1.0f, 0.707107f, 0.677128f, 0.660184f, 0.64842f, 0.639439f, 0.632194f, 0.626133f, 0.620929f, + 0.616375f, 0.612329f, 0.608693f, 0.605391f, 0.60237f, 0.599587f, 0.597007f, 0.594604f, 0.592355f, + 0.590243f, 0.588251f, 0.586369f, 0.584583f, 0.582886f, 0.581269f, 0.579725f, 0.578247f, 0.576832f, + 0.575473f, 0.574166f, 0.572908f, 0.571696f, 0.570525f, 0.569394f, 0.5683f, 0.567241f, 0.566214f, + 0.565218f, 0.564251f, 0.563311f, 0.562398f, 0.561508f, 0.560642f, 0.559799f, 0.558976f, 0.558173f, + 0.55739f, 0.556625f, 0.555877f, 0.555146f, 0.554431f, 0.553732f, 0.553047f, 0.552376f, 0.551719f, + 0.551075f, 0.550443f, 0.549823f, 0.549216f, 0.548619f, 0.548033f, 0.547458f, 0.546892f, 0.546337f, + 0.545791f, 0.545254f, 0.544726f, 0.544206f, 0.543695f, 0.543192f, 0.542696f, 0.542209f, 0.541728f, + 0.541255f, 0.540788f, 0.540329f, 0.539876f, 0.539429f, 0.538988f, 0.538554f, 0.538125f, 0.537702f, + 0.537285f, 0.536873f, 0.536467f, 0.536065f, 0.535669f, 0.535277f, 0.534891f, 0.534509f, 0.534131f, + 0.533759f, 0.53339f, 0.533026f, 0.532666f, 0.53231f, 0.531958f, 0.53161f, 0.531266f, 0.530925f, + 0.530588f, 0.530255f, 0.529926f, 0.529599f, 0.529277f, 0.528957f, 0.528641f, 0.528328f, 0.528018f, + 0.527711f, 0.527407f, 0.527106f, 0.526808f, 0.526513f, 0.52622f, 0.525931f, 0.525644f, 0.525359f, + 0.525077f, 0.524798f, 0.524522f, 0.524247f, 0.523975f, 0.523706f, 0.523439f, 0.523174f, 0.522911f, + 0.522651f, 0.522393f +}; +#endif + +#ifdef VERSION_SH +u16 unk_sh_data_3[] = { + // 30 entries + // pattern: + // A B + // C D + // C B + // A E + 0x1000, 0x1000, + 0x1000, 0x1000, + 0x1000, 0x1000, + 0x1000, 0x1000, + + 0x0E6F, 0x1091, + 0x11EF, 0x1267, + 0x11EF, 0x1091, + 0x0E6F, 0x0BB8, + + 0x0C1D, 0x111D, + 0x1494, 0x15D2, + 0x1494, 0x111D, + 0x0C1D, 0x068E, + + 0x0838, 0x116E, + 0x18A6, 0x1B61, + 0x18A6, 0x116E, + 0x0838, 0x0001, + + 0x0227, 0x0F42, + 0x1B75, 0x206B, + 0x1B75, 0x0F42, + 0x0227, 0xFA2B, + + 0xFC28, 0x0AAE, + 0x1BE7, 0x2394, + 0x1BE7, 0x0AAE, + 0xFC28, 0xF874, + + 0xF809, 0x0582, + 0x1C36, 0x2788, + 0x1C36, 0x0582, + 0xF809, 0xFAEB, + + 0xF5F0, 0x0001, + 0x1E34, 0x2F71, + 0x1E34, 0x0001, + 0xF5F0, 0x0000, + + 0xF8AD, 0xFAF3, + 0x19ED, 0x2EB6, + 0x19ED, 0xFAF3, + 0xF8AD, 0x04AC, + + 0xFCC0, 0xF6FF, + 0x178B, 0x3207, + 0x178B, 0xF6FF, + 0xFCC0, 0x065F, + + 0x01A5, 0xF44E, + 0x1510, 0x36B4, + 0x1510, 0xF44E, + 0x01A5, 0x047B, + + 0x05C1, 0xF3CC, + 0x1145, 0x3988, + 0x1145, 0xF3CC, + 0x05C1, 0x0001, + + 0x07B9, 0xF517, + 0x0D20, 0x3C4C, + 0x0D20, 0xF517, + 0x07B9, 0xFBD4, + + 0x07C0, 0xF71C, + 0x09A1, 0x4528, + 0x09A1, 0xF71C, + 0x07C0, 0xF9B7, + + 0x058F, 0xFA43, + 0x05DC, 0x585F, + 0x05DC, 0xFA43, + 0x058F, 0xFAB3, +}; + +u16 unk_sh_data_4[] = { + 0xFA73, 0xFA42, + 0xFA27, 0x5866, + 0xFA27, 0xFA42, + 0xFA73, 0xFAB2, + + 0xF842, 0xF71B, + 0xF661, 0x452B, + 0xF661, 0xF71B, + 0xF842, 0xF9B5, + + 0xF848, 0xF516, + 0xF2E1, 0x3C4D, + 0xF2E1, 0xF516, + 0xF848, 0xFBD2, + + 0xFA3F, 0xF3CA, + 0xEEBD, 0x3989, + 0xEEBD, 0xF3CA, + 0xFA3F, 0xFFFF, + + 0xFE5B, 0xF44C, + 0xEAF2, 0x36B5, + 0xEAF2, 0xF44C, + 0xFE5B, 0x0479, + + 0x0340, 0xF6FD, + 0xE877, 0x3207, + 0xE877, 0xF6FD, + 0x0340, 0x065E, + + 0x0753, 0xFAF1, + 0xE615, 0x2EB5, + 0xE615, 0xFAF1, + 0x0753, 0x04AB, + + 0x0A12, 0xFFFF, + 0xE1CD, 0x2F71, + 0xE1CD, 0xFFFF, + 0x0A12, 0x0000, + + 0x07FA, 0x057F, + 0xE3CA, 0x2789, + 0xE3CA, 0x057F, + 0x07FA, 0xFAEA, + + 0x03DB, 0x0AAC, + 0xE41A, 0x2394, + 0xE41A, 0x0AAC, + 0x03DB, 0xF873, + + 0xFDDC, 0x0F41, + 0xE489, 0x206E, + 0xE489, 0x0F41, + 0xFDDC, 0xFA28, + + 0xF7CA, 0x116E, + 0xE758, 0x1B63, + 0xE758, 0x116E, + 0xF7CA, 0xFFFF, + + 0xF3E4, 0x111D, + 0xEB6A, 0x15D4, + 0xEB6A, 0x111D, + 0xF3E4, 0x068B, + + 0xF192, 0x1092, + 0xEE11, 0x1268, + 0xEE11, 0x1092, + 0xF192, 0x0BB6, + + 0xF05F, 0x1026, + 0xEF89, 0x1093, + 0xEF89, 0x1026, + 0xF05F, 0x0EEB, + + 0x0000, 0x0000, + 0x0000, 0x0000, + 0x7FFF, 0xD001, + 0x3FFF, 0xF001, + 0x5FFF, 0x9001, + 0x7FFF, 0x8001 +}; +#endif + +#ifndef VERSION_SH +s16 gTatumsPerBeat = TATUMS_PER_BEAT; +s8 gUnusedCount80333EE8 = UNUSED_COUNT_80333EE8; +s32 gAudioHeapSize = DOUBLE_SIZE_ON_64_BIT(AUDIO_HEAP_SIZE); +s32 gAudioInitPoolSize = DOUBLE_SIZE_ON_64_BIT(AUDIO_INIT_POOL_SIZE); +volatile s32 gAudioLoadLock = AUDIO_LOCK_UNINITIALIZED; +#endif + +#if defined(VERSION_EU) +u8 bufferDelete2[12] = { 0 }; +u8 D_EU_80302010 = 0; +u8 D_EU_80302014 = 0; + +struct OSMesgQueue *OSMesgQueues[4] = { &OSMesgQueue0, &OSMesgQueue1, &OSMesgQueue2, &OSMesgQueue3 }; +#elif defined(VERSION_JP) || defined(VERSION_US) +s8 sUnused8033EF8 = 24; +#endif + +// .bss + +volatile s32 gAudioFrameCount; + +#if defined(VERSION_EU) || defined(VERSION_SH) +s32 gCurrAudioFrameDmaCount; +#else +volatile s32 gCurrAudioFrameDmaCount; +#endif + +s32 gAudioTaskIndex; +s32 gCurrAiBufferIndex; + +u64 *gAudioCmdBuffers[2]; +u64 *gAudioCmd; + +struct SPTask *gAudioTask; +struct SPTask gAudioTasks[2]; + +#if defined(VERSION_EU) || defined(VERSION_SH) +f32 D_EU_802298D0; +s32 gRefreshRate; +#endif + +s16 *gAiBuffers[NUMAIBUFFERS]; +s16 gAiBufferLengths[NUMAIBUFFERS]; + +#if defined(VERSION_JP) || defined(VERSION_US) +u32 gUnused80226E58[0x10]; +u16 gUnused80226E98[0x10]; +#endif + +u32 gAudioRandom; + +#if defined(VERSION_EU) || defined(VERSION_SH) +s32 gAudioErrorFlags; +#endif + +#ifdef VERSION_SH +volatile u32 gAudioLoadLockSH; +struct EuAudioCmd sAudioCmd[0x100]; +u8 D_SH_80350F18; +u8 D_SH_80350F19; + +OSMesg D_SH_80350F1C[1]; +OSMesgQueue D_SH_80350F20; // address written to D_SH_80350F38 +OSMesgQueue *D_SH_80350F38; + +OSMesg D_SH_80350F40[4]; +OSMesgQueue D_SH_80350F50; // address written to D_SH_80350F68 +OSMesgQueue *D_SH_80350F68; + +OSMesg D_SH_80350F6C[1]; +OSMesgQueue D_SH_80350F70; // address written to D_SH_80350F88 +OSMesgQueue *D_SH_80350F88; + +OSMesg D_SH_80350F8C[1]; +OSMesgQueue D_SH_80350F90; // address written to D_SH_80350F90 +OSMesgQueue *D_SH_80350FA8; +#endif + +u64 gAudioGlobalsEndMarker; diff --git a/src/decomp/audio/data.h b/src/decomp/audio/data.h new file mode 100644 index 0000000..3deda31 --- /dev/null +++ b/src/decomp/audio/data.h @@ -0,0 +1,154 @@ +#ifndef AUDIO_DATA_H +#define AUDIO_DATA_H + +#include + +#include "internal.h" +#include + +#define AUDIO_LOCK_UNINITIALIZED 0 +#define AUDIO_LOCK_NOT_LOADING 0x76557364 +#define AUDIO_LOCK_LOADING 0x19710515 + +#define NUMAIBUFFERS 3 + +// constant .data +#if defined(VERSION_EU) || defined(VERSION_SH) +extern struct AudioSessionSettingsEU gAudioSessionPresets[]; +#else +extern struct AudioSessionSettings gAudioSessionPresets[18]; +#endif +extern u16 D_80332388[128]; // unused + +#if defined(VERSION_EU) || defined(VERSION_SH) +extern f32 gPitchBendFrequencyScale[256]; +#else +extern f32 gPitchBendFrequencyScale[255]; +#endif +extern f32 gNoteFrequencies[128]; + +extern u8 gDefaultShortNoteVelocityTable[16]; +extern u8 gDefaultShortNoteDurationTable[16]; +extern s8 gVibratoCurve[16]; +extern struct AdsrEnvelope gDefaultEnvelope[3]; + +#if defined(VERSION_EU) || defined(VERSION_SH) +extern s16 gEuUnknownWave7[256]; +extern s16 *gWaveSamples[6]; +#else +extern s16 *gWaveSamples[4]; +#endif + +#if defined(VERSION_EU) || defined(VERSION_SH) +extern u8 euUnknownData_8030194c[4]; +#ifdef VERSION_EU +extern u16 gHeadsetPanQuantization[0x10]; +#else +extern u16 gHeadsetPanQuantization[0x40]; +#endif +extern s16 euUnknownData_80301950[64]; +extern struct NoteSubEu gZeroNoteSub; +extern struct NoteSubEu gDefaultNoteSub; +#else +extern u16 gHeadsetPanQuantization[10]; +#endif +extern f32 gHeadsetPanVolume[128]; +extern f32 gStereoPanVolume[128]; +extern f32 gDefaultPanVolume[128]; + +extern f32 gVolRampingLhs136[128]; +extern f32 gVolRampingRhs136[128]; +extern f32 gVolRampingLhs144[128]; +extern f32 gVolRampingRhs144[128]; +extern f32 gVolRampingLhs128[128]; +extern f32 gVolRampingRhs128[128]; + +// non-constant .data +extern s16 gTatumsPerBeat; +extern s8 gUnusedCount80333EE8; +extern s32 gAudioHeapSize; // AUDIO_HEAP_SIZE +extern s32 gAudioInitPoolSize; // AUDIO_INIT_POOL_SIZE +extern volatile s32 gAudioLoadLock; + +// .bss +extern volatile s32 gAudioFrameCount; + +// number of DMAs performed during this frame +#if defined(VERSION_EU) || defined(VERSION_SH) +extern s32 gCurrAudioFrameDmaCount; +#else +extern volatile s32 gCurrAudioFrameDmaCount; +#endif + +extern s32 gAudioTaskIndex; +extern s32 gCurrAiBufferIndex; + +extern u64 *gAudioCmdBuffers[2]; +extern u64 *gAudioCmd; + +extern struct SPTask *gAudioTask; +extern struct SPTask gAudioTasks[2]; + +#if defined(VERSION_EU) || defined(VERSION_SH) +extern f32 D_EU_802298D0; +extern s32 gRefreshRate; +#endif + +extern s16 *gAiBuffers[NUMAIBUFFERS]; +extern s16 gAiBufferLengths[NUMAIBUFFERS]; +#if defined(VERSION_SH) +#define AIBUFFER_LEN 0xb00 +#elif defined(VERSION_EU) +#define AIBUFFER_LEN (0xa0 * 17) +#else +#define AIBUFFER_LEN (0xa0 * 16) +#endif + +extern u32 gUnused80226E58[0x10]; +extern u16 gUnused80226E98[0x10]; + +extern u32 gAudioRandom; + +#ifdef VERSION_SH +extern f32 unk_sh_data_1[]; + +extern volatile u32 gAudioLoadLockSH; + +extern u8 D_SH_80350F18; +extern u8 D_SH_80350F19; + +extern OSMesg D_SH_80350F1C[1]; +extern OSMesgQueue D_SH_80350F20; // address written to D_SH_80350F38 +extern OSMesgQueue *D_SH_80350F38; + +extern OSMesg D_SH_80350F40[4]; +extern OSMesgQueue D_SH_80350F50; // address written to D_SH_80350F68 +extern OSMesgQueue *D_SH_80350F68; + +extern OSMesg D_SH_80350F6C[1]; +extern OSMesgQueue D_SH_80350F70; // address written to D_SH_80350F88 +extern OSMesgQueue *D_SH_80350F88; + +extern OSMesg D_SH_80350F8C[1]; +extern OSMesgQueue D_SH_80350F90; // address written to D_SH_80350F90 +extern OSMesgQueue *D_SH_80350FA8; +#endif + +#if defined(VERSION_EU) || defined(VERSION_SH) +#define UNUSED_COUNT_80333EE8 24 +#define AUDIO_HEAP_SIZE 0x2c500 +#define AUDIO_INIT_POOL_SIZE 0x2c00 +#else +#define UNUSED_COUNT_80333EE8 16 +#define AUDIO_HEAP_SIZE 0x31150 +#define AUDIO_INIT_POOL_SIZE 0x2500 +#endif + +#ifdef VERSION_SH +extern u32 D_SH_80315EF0; +extern u16 D_SH_80315EF4; +extern u16 D_SH_80315EF8; +extern u16 D_SH_80315EFC; +#endif + +#endif // AUDIO_DATA_H diff --git a/src/decomp/audio/effects.c b/src/decomp/audio/effects.c new file mode 100644 index 0000000..76bb584 --- /dev/null +++ b/src/decomp/audio/effects.c @@ -0,0 +1,545 @@ +#include + +#include "effects.h" +#include "load.h" +#include "data.h" +#include "seqplayer.h" + +#ifdef VERSION_JP +#define US_FLOAT2(x) x##.0 +#else +#define US_FLOAT2(x) x +#endif + +f32 gTrackVolume = 1.0f; + +#if defined(VERSION_EU) || defined(VERSION_SH) +void sequence_channel_process_sound(struct SequenceChannel *seqChannel, s32 recalculateVolume) { + f32 channelVolume; + s32 i; + + if (seqChannel->changes.as_bitfields.volume || recalculateVolume) { + channelVolume = seqChannel->volume * seqChannel->volumeScale * seqChannel->seqPlayer->appliedFadeVolume; + if (seqChannel->seqPlayer->muted && (seqChannel->muteBehavior & MUTE_BEHAVIOR_SOFTEN) != 0) { + channelVolume = seqChannel->seqPlayer->muteVolumeScale * channelVolume; + } +#ifdef VERSION_SH + seqChannel->appliedVolume = channelVolume * channelVolume; +#else + seqChannel->appliedVolume = channelVolume; +#endif + } + + if (seqChannel->changes.as_bitfields.pan) { + seqChannel->pan = seqChannel->newPan * seqChannel->panChannelWeight; + } + + for (i = 0; i < 4; ++i) { + struct SequenceChannelLayer *layer = seqChannel->layers[i]; + if (layer != NULL && layer->enabled && layer->note != NULL) { + if (layer->notePropertiesNeedInit) { + layer->noteFreqScale = layer->freqScale * seqChannel->freqScale; + layer->noteVelocity = layer->velocitySquare * seqChannel->appliedVolume; + layer->notePan = (seqChannel->pan + layer->pan * (0x80 - seqChannel->panChannelWeight)) >> 7; + layer->notePropertiesNeedInit = FALSE; + } else { + if (seqChannel->changes.as_bitfields.freqScale) { + layer->noteFreqScale = layer->freqScale * seqChannel->freqScale; + } + if (seqChannel->changes.as_bitfields.volume || recalculateVolume) { + layer->noteVelocity = layer->velocitySquare * seqChannel->appliedVolume; + } + if (seqChannel->changes.as_bitfields.pan) { + layer->notePan = (seqChannel->pan + layer->pan * (0x80 - seqChannel->panChannelWeight)) >> 7; + } + } + } + } + seqChannel->changes.as_u8 = 0; +} +#else +static void sequence_channel_process_sound(struct SequenceChannel *seqChannel) { + f32 channelVolume; + f32 panLayerWeight; + f32 panFromChannel; + s32 i; + + channelVolume = seqChannel->volume * seqChannel->volumeScale * seqChannel->seqPlayer->fadeVolume; + if (seqChannel->seqPlayer->muted && (seqChannel->muteBehavior & MUTE_BEHAVIOR_SOFTEN) != 0) { + channelVolume *= seqChannel->seqPlayer->muteVolumeScale; + } + + panFromChannel = seqChannel->pan * seqChannel->panChannelWeight; + panLayerWeight = US_FLOAT(1.0) - seqChannel->panChannelWeight; + + for (i = 0; i < 4; i++) { + struct SequenceChannelLayer *layer = seqChannel->layers[i]; + if (layer != NULL && layer->enabled && layer->note != NULL) { + layer->noteFreqScale = layer->freqScale * seqChannel->freqScale; + layer->noteVelocity = layer->velocitySquare * (channelVolume * gTrackVolume); + layer->notePan = (layer->pan * panLayerWeight) + panFromChannel; + } + } +} +#endif + +void sequence_player_process_sound(struct SequencePlayer *seqPlayer) { + s32 i; + + if (seqPlayer->fadeRemainingFrames != 0) { + seqPlayer->fadeVolume += seqPlayer->fadeVelocity; +#if defined(VERSION_EU) || defined(VERSION_SH) + seqPlayer->recalculateVolume = TRUE; +#endif + + if (seqPlayer->fadeVolume > US_FLOAT2(1)) { + seqPlayer->fadeVolume = US_FLOAT2(1); + } + if (seqPlayer->fadeVolume < 0) { + seqPlayer->fadeVolume = 0; + } + + if (--seqPlayer->fadeRemainingFrames == 0) { +#if defined(VERSION_EU) || defined(VERSION_SH) + if (seqPlayer->state == 2) { + sequence_player_disable(seqPlayer); + return; + } +#else + switch (seqPlayer->state) { + case SEQUENCE_PLAYER_STATE_FADE_OUT: + sequence_player_disable(seqPlayer); + return; + + case SEQUENCE_PLAYER_STATE_2: + case SEQUENCE_PLAYER_STATE_3: + seqPlayer->state = SEQUENCE_PLAYER_STATE_0; + break; + + case SEQUENCE_PLAYER_STATE_4: + break; + } +#endif + } + } + +#if defined(VERSION_EU) || defined(VERSION_SH) + if (seqPlayer->recalculateVolume) { + seqPlayer->appliedFadeVolume = seqPlayer->fadeVolume * seqPlayer->fadeVolumeScale; + } +#endif + + // Process channels + for (i = 0; i < CHANNELS_MAX; i++) { + if (IS_SEQUENCE_CHANNEL_VALID(seqPlayer->channels[i]) == TRUE + && seqPlayer->channels[i]->enabled == TRUE) { +#if defined(VERSION_EU) || defined(VERSION_SH) + sequence_channel_process_sound(seqPlayer->channels[i], seqPlayer->recalculateVolume); +#else + sequence_channel_process_sound(seqPlayer->channels[i]); +#endif + } + } + +#if defined(VERSION_EU) || defined(VERSION_SH) + seqPlayer->recalculateVolume = FALSE; +#endif +} + +f32 get_portamento_freq_scale(struct Portamento *p) { + u32 v0; + f32 result; +#if defined(VERSION_JP) || defined(VERSION_US) + if (p->mode == 0) { + return 1.0f; + } +#endif + + p->cur += p->speed; + v0 = (u32) p->cur; + +#if defined(VERSION_EU) || defined(VERSION_SH) + if (v0 > 127) +#else + if (v0 >= 127) +#endif + { + v0 = 127; + } + +#if defined(VERSION_EU) || defined(VERSION_SH) + result = US_FLOAT(1.0) + p->extent * (gPitchBendFrequencyScale[v0 + 128] - US_FLOAT(1.0)); +#else + result = US_FLOAT(1.0) + p->extent * (gPitchBendFrequencyScale[v0 + 127] - US_FLOAT(1.0)); +#endif + return result; +} + +#if defined(VERSION_EU) || defined(VERSION_SH) +s16 get_vibrato_pitch_change(struct VibratoState *vib) { + s32 index; + vib->time += (s32) vib->rate; + index = (vib->time >> 10) & 0x3F; + return vib->curve[index] >> 8; +} +#else +s8 get_vibrato_pitch_change(struct VibratoState *vib) { + s32 index; + vib->time += vib->rate; + + index = (vib->time >> 10) & 0x3F; + + switch (index & 0x30) { + case 0x10: + index = 31 - index; + + case 0x00: + return vib->curve[index]; + + case 0x20: + index -= 0x20; + break; + + case 0x30: + index = 63 - index; + break; + } + + return -vib->curve[index]; +} +#endif + +f32 get_vibrato_freq_scale(struct VibratoState *vib) { + s32 pitchChange; + f32 extent; + f32 result; + + if (vib->delay != 0) { + vib->delay--; + return 1; + } + + if (vib->extentChangeTimer) { + if (vib->extentChangeTimer == 1) { + vib->extent = (s32) vib->seqChannel->vibratoExtentTarget; + } else { + vib->extent += + ((s32) vib->seqChannel->vibratoExtentTarget - vib->extent) / (s32) vib->extentChangeTimer; + } + + vib->extentChangeTimer--; + } else if (vib->seqChannel->vibratoExtentTarget != (s32) vib->extent) { + if ((vib->extentChangeTimer = vib->seqChannel->vibratoExtentChangeDelay) == 0) { + vib->extent = (s32) vib->seqChannel->vibratoExtentTarget; + } + } + + if (vib->rateChangeTimer) { + if (vib->rateChangeTimer == 1) { + vib->rate = (s32) vib->seqChannel->vibratoRateTarget; + } else { + vib->rate += ((s32) vib->seqChannel->vibratoRateTarget - vib->rate) / (s32) vib->rateChangeTimer; + } + + vib->rateChangeTimer--; + } else if (vib->seqChannel->vibratoRateTarget != (s32) vib->rate) { + if ((vib->rateChangeTimer = vib->seqChannel->vibratoRateChangeDelay) == 0) { + vib->rate = (s32) vib->seqChannel->vibratoRateTarget; + } + } + + if (vib->extent == 0) { + return 1.0f; + } + + pitchChange = get_vibrato_pitch_change(vib); + extent = (f32) vib->extent / US_FLOAT(4096.0); + +#if defined(VERSION_EU) || defined(VERSION_SH) + result = US_FLOAT(1.0) + extent * (gPitchBendFrequencyScale[pitchChange + 128] - US_FLOAT(1.0)); +#else + result = US_FLOAT(1.0) + extent * (gPitchBendFrequencyScale[pitchChange + 127] - US_FLOAT(1.0)); +#endif + return result; +} + +void note_vibrato_update(struct Note *note) { +#if defined(VERSION_EU) || defined(VERSION_SH) + if (note->portamento.mode != 0) { + note->portamentoFreqScale = get_portamento_freq_scale(¬e->portamento); + } + if (note->vibratoState.active && note->parentLayer != NO_LAYER) { + note->vibratoFreqScale = get_vibrato_freq_scale(¬e->vibratoState); + } +#else + if (note->vibratoState.active) { + note->portamentoFreqScale = get_portamento_freq_scale(¬e->portamento); + if (note->parentLayer != NO_LAYER) { + note->vibratoFreqScale = get_vibrato_freq_scale(¬e->vibratoState); + } + } +#endif +} + +void note_vibrato_init(struct Note *note) { + struct VibratoState *vib; + UNUSED struct SequenceChannel *seqChannel; +#if defined(VERSION_EU) || defined(VERSION_SH) + struct NotePlaybackState *seqPlayerState = (struct NotePlaybackState *) ¬e->priority; +#endif + + note->vibratoFreqScale = 1.0f; + note->portamentoFreqScale = 1.0f; + + vib = ¬e->vibratoState; + +#if defined(VERSION_JP) || defined(VERSION_US) + if (note->parentLayer->seqChannel->vibratoExtentStart == 0 + && note->parentLayer->seqChannel->vibratoExtentTarget == 0 + && note->parentLayer->portamento.mode == 0) { + vib->active = FALSE; + return; + } +#endif + + vib->active = TRUE; + vib->time = 0; + +#if defined(VERSION_EU) || defined(VERSION_SH) + vib->curve = gWaveSamples[2]; + vib->seqChannel = note->parentLayer->seqChannel; + if ((vib->extentChangeTimer = vib->seqChannel->vibratoExtentChangeDelay) == 0) { + vib->extent = FLOAT_CAST(vib->seqChannel->vibratoExtentTarget); + } else { + vib->extent = FLOAT_CAST(vib->seqChannel->vibratoExtentStart); + } + + if ((vib->rateChangeTimer = vib->seqChannel->vibratoRateChangeDelay) == 0) { + vib->rate = FLOAT_CAST(vib->seqChannel->vibratoRateTarget); + } else { + vib->rate = FLOAT_CAST(vib->seqChannel->vibratoRateStart); + } + vib->delay = vib->seqChannel->vibratoDelay; + + seqPlayerState->portamento = seqPlayerState->parentLayer->portamento; +#else + vib->curve = gVibratoCurve; + vib->seqChannel = note->parentLayer->seqChannel; + seqChannel = vib->seqChannel; + + if ((vib->extentChangeTimer = seqChannel->vibratoExtentChangeDelay) == 0) { + vib->extent = seqChannel->vibratoExtentTarget; + } else { + vib->extent = seqChannel->vibratoExtentStart; + } + + if ((vib->rateChangeTimer = seqChannel->vibratoRateChangeDelay) == 0) { + vib->rate = seqChannel->vibratoRateTarget; + } else { + vib->rate = seqChannel->vibratoRateStart; + } + vib->delay = seqChannel->vibratoDelay; + + note->portamento = note->parentLayer->portamento; +#endif +} + +void adsr_init(struct AdsrState *adsr, struct AdsrEnvelope *envelope, UNUSED s16 *volOut) { + adsr->action = 0; + adsr->state = ADSR_STATE_DISABLED; +#if defined(VERSION_EU) || defined(VERSION_SH) + adsr->delay = 0; + adsr->envelope = envelope; +#ifdef VERSION_SH + adsr->sustain = 0.0f; +#endif + adsr->current = 0.0f; +#else + adsr->initial = 0; + adsr->delay = 0; + adsr->velocity = 0; + adsr->envelope = envelope; + adsr->volOut = volOut; +#endif +} + +#if defined(VERSION_EU) || defined(VERSION_SH) +f32 adsr_update(struct AdsrState *adsr) { +#else +s32 adsr_update(struct AdsrState *adsr) { +#endif + u8 action = adsr->action; +#if defined(VERSION_EU) || defined(VERSION_SH) + u8 state = adsr->state; + switch (state) { +#else + switch (adsr->state) { +#endif + case ADSR_STATE_DISABLED: + return 0; + + case ADSR_STATE_INITIAL: { +#if defined(VERSION_JP) || defined(VERSION_US) + adsr->current = adsr->initial; + adsr->target = adsr->initial; +#endif + if (action & ADSR_ACTION_HANG) { + adsr->state = ADSR_STATE_HANG; + break; + } + // fallthrough + } + + case ADSR_STATE_START_LOOP: + adsr->envIndex = 0; +#if defined(VERSION_JP) || defined(VERSION_US) + adsr->currentHiRes = adsr->current << 0x10; +#endif + adsr->state = ADSR_STATE_LOOP; + // fallthrough + +#ifdef VERSION_SH + restart: +#endif + case ADSR_STATE_LOOP: + adsr->delay = BSWAP16(adsr->envelope[adsr->envIndex].delay); + switch (adsr->delay) { + case ADSR_DISABLE: + adsr->state = ADSR_STATE_DISABLED; + break; + case ADSR_HANG: + adsr->state = ADSR_STATE_HANG; + break; + case ADSR_GOTO: + adsr->envIndex = BSWAP16(adsr->envelope[adsr->envIndex].arg); +#ifdef VERSION_SH + goto restart; +#else + break; +#endif + case ADSR_RESTART: + adsr->state = ADSR_STATE_INITIAL; + break; + + default: +#if defined(VERSION_EU) || defined(VERSION_SH) + if (adsr->delay >= 4) { + adsr->delay = adsr->delay * gAudioBufferParameters.updatesPerFrame +#ifdef VERSION_SH + / gAudioBufferParameters.presetUnk4 +#endif + / 4; + } +#if defined(VERSION_SH) + if (adsr->delay == 0) { + adsr->delay = 1; + } + adsr->target = (f32) BSWAP16(adsr->envelope[adsr->envIndex].arg) / 32767.0f; +#elif defined(VERSION_EU) + adsr->target = (f32) BSWAP16(adsr->envelope[adsr->envIndex].arg) / 32767.0; +#endif + adsr->target = adsr->target * adsr->target; + adsr->velocity = (adsr->target - adsr->current) / adsr->delay; +#else + adsr->target = BSWAP16(adsr->envelope[adsr->envIndex].arg); + adsr->velocity = ((adsr->target - adsr->current) << 0x10) / adsr->delay; +#endif + adsr->state = ADSR_STATE_FADE; + adsr->envIndex++; + break; + } + if (adsr->state != ADSR_STATE_FADE) { + break; + } + // fallthrough + + case ADSR_STATE_FADE: +#if defined(VERSION_EU) || defined(VERSION_SH) + adsr->current += adsr->velocity; +#else + adsr->currentHiRes += adsr->velocity; + adsr->current = adsr->currentHiRes >> 0x10; +#endif + if (--adsr->delay <= 0) { + adsr->state = ADSR_STATE_LOOP; + } + // fallthrough + + case ADSR_STATE_HANG: + break; + + case ADSR_STATE_DECAY: + case ADSR_STATE_RELEASE: { + adsr->current -= adsr->fadeOutVel; +#if defined(VERSION_EU) || defined(VERSION_SH) + if (adsr->sustain != 0.0f && state == ADSR_STATE_DECAY) { +#else + if (adsr->sustain != 0 && adsr->state == ADSR_STATE_DECAY) { +#endif + if (adsr->current < adsr->sustain) { + adsr->current = adsr->sustain; +#if defined(VERSION_EU) || defined(VERSION_SH) + adsr->delay = 128; +#else + adsr->delay = adsr->sustain / 16; +#endif + adsr->state = ADSR_STATE_SUSTAIN; + } + break; + } + +#if defined(VERSION_SH) + if (adsr->current < 0.00001f) { + adsr->current = 0.0f; + adsr->state = ADSR_STATE_DISABLED; + } +#elif defined(VERSION_EU) + if (adsr->current < 0) { + adsr->current = 0.0f; + adsr->state = ADSR_STATE_DISABLED; + } +#else + if (adsr->current < 100) { + adsr->current = 0; + adsr->state = ADSR_STATE_DISABLED; + } +#endif + break; + } + + case ADSR_STATE_SUSTAIN: + adsr->delay -= 1; + if (adsr->delay == 0) { + adsr->state = ADSR_STATE_RELEASE; + } + break; + } + + if ((action & ADSR_ACTION_DECAY)) { + adsr->state = ADSR_STATE_DECAY; + adsr->action = action & ~ADSR_ACTION_DECAY; + } + + if ((action & ADSR_ACTION_RELEASE)) { + adsr->state = ADSR_STATE_RELEASE; +#if defined(VERSION_EU) || defined(VERSION_SH) + adsr->action = action & ~ADSR_ACTION_RELEASE; +#else + adsr->action = action & ~(ADSR_ACTION_RELEASE | ADSR_ACTION_DECAY); +#endif + } + +#if defined(VERSION_EU) || defined(VERSION_SH) + if (adsr->current < 0.0f) { + return 0.0f; + } + if (adsr->current > 1.0f) { + eu_stubbed_printf_1("Audio:Envp: overflow %f\n", adsr->current); + return 1.0f; + } + return adsr->current; +#else + *adsr->volOut = adsr->current; + return 0; +#endif +} diff --git a/src/decomp/audio/effects.h b/src/decomp/audio/effects.h new file mode 100644 index 0000000..56709e8 --- /dev/null +++ b/src/decomp/audio/effects.h @@ -0,0 +1,48 @@ +#ifndef AUDIO_EFFECTS_H +#define AUDIO_EFFECTS_H + +#include + +#include "internal.h" +#include + +#define ADSR_STATE_DISABLED 0 +#define ADSR_STATE_INITIAL 1 +#define ADSR_STATE_START_LOOP 2 +#define ADSR_STATE_LOOP 3 +#define ADSR_STATE_FADE 4 +#define ADSR_STATE_HANG 5 +#define ADSR_STATE_DECAY 6 +#define ADSR_STATE_RELEASE 7 +#define ADSR_STATE_SUSTAIN 8 + +#define ADSR_ACTION_RELEASE 0x10 +#define ADSR_ACTION_DECAY 0x20 +#define ADSR_ACTION_HANG 0x40 + +#define ADSR_DISABLE 0 +#define ADSR_HANG -1 +#define ADSR_GOTO -2 +#define ADSR_RESTART -3 + +// Envelopes are always stored as big endian, to match sequence files which are +// byte blobs and can embed envelopes. Hence this byteswapping macro. +#if IS_BIG_ENDIAN +#define BSWAP16(x) (x) +#else +#define BSWAP16(x) (((x) & 0xff) << 8 | (((x) >> 8) & 0xff)) +#endif + +extern f32 gTrackVolume; + +void sequence_player_process_sound(struct SequencePlayer *seqPlayer); +void note_vibrato_update(struct Note *note); +void note_vibrato_init(struct Note *note); +void adsr_init(struct AdsrState *adsr, struct AdsrEnvelope *envelope, s16 *volOut); +#if defined(VERSION_EU) || defined(VERSION_SH) +f32 adsr_update(struct AdsrState *adsr); +#else +s32 adsr_update(struct AdsrState *adsr); +#endif + +#endif // AUDIO_EFFECTS_H diff --git a/src/decomp/audio/external.c b/src/decomp/audio/external.c new file mode 100644 index 0000000..0d525cd --- /dev/null +++ b/src/decomp/audio/external.c @@ -0,0 +1,2668 @@ +#include +#include +#include "heap.h" +#include "load.h" +#include "data.h" +#include "seqplayer.h" +#include "external.h" +#include "playback.h" +#include "synthesis.h" +#include "../game/level_update.h" +#include "../game/camera.h" +#include + +#define SOUND_BANK_COUNT 10 +#define gCurrLevelNum 0 +#define gCurrAreaIndex 1 +#define gMarioCurrentRoom 0 +#define LEVEL_MAX 0 +#define SEQ_BASE_ID 0x7f + +#if defined(VERSION_EU) || defined(VERSION_SH) +#define EU_FLOAT(x) x##f +#else +#define EU_FLOAT(x) x +#endif +#include "../../debug_print.h" + +// N.B. sound banks are different from the audio banks referred to in other +// files. We should really fix our naming to be less ambiguous... +#define MAX_BACKGROUND_MUSIC_QUEUE_SIZE 6 +#define MAX_CHANNELS_PER_SOUND_BANK 1 + +#define SEQUENCE_NONE 0xFF + +#define SAMPLES_TO_OVERPRODUCE 0x10 +#define EXTRA_BUFFERED_AI_SAMPLES_TARGET 0x40 + +struct ChannelVolumeScaleFade { + f32 velocity; + u8 target; + f32 current; + u16 remainingFrames; +}; // size = 0x10 + +struct SoundCharacteristics { + f32 *x; + f32 *y; + f32 *z; + f32 distance; + u32 priority; + u32 soundBits; // packed bits, same as first arg to play_sound + u8 soundStatus; + u8 freshness; + u8 prev; + u8 next; +}; // size = 0x1C + +// Also the number of frames a discrete sound can be in the WAITING state before being deleted +#define SOUND_MAX_FRESHNESS 10 + +struct SequenceQueueItem { + u8 seqId; + u8 priority; +}; // size = 0x2 + +// data +#if defined(VERSION_EU) || defined(VERSION_SH) +// moved to bss in data.c +s32 gAudioErrorFlags2 = 0; +#else +s32 gAudioErrorFlags = 0; +#endif +s32 sGameLoopTicked = 0; + +f32 gAudioVolume = 1.0f; +u8 gAudioReverb = 0.0f; + +// Dialog sounds +// The US difference is the sound for DIALOG_037 ("I win! You lose! Ha ha ha ha! +// You're no slouch, but I'm a better sledder! Better luck next time!"), spoken +// by Koopa instead of the penguin in JP. + +#define UKIKI 0 +#define TUXIE 1 +#define BOWS1 2 // Bowser Intro / Doors Laugh +#define KOOPA 3 +#define KBOMB 4 +#define BOO 5 +#define BOMB 6 +#define BOWS2 7 // Bowser Battle Laugh +#define GRUNT 8 +#define WIGLR 9 +#define YOSHI 10 +#define _ 0xFF + +#ifdef VERSION_JP +#define DIFF KOOPA +#else +#define DIFF TUXIE +#endif + +u8 sDialogSpeaker[] = { + // 0 1 2 3 4 5 6 7 8 9 + /* 0*/ _, BOMB, BOMB, BOMB, BOMB, KOOPA, KOOPA, KOOPA, _, KOOPA, + /* 1*/ _, _, _, _, _, _, _, KBOMB, _, _, + /* 2*/ _, BOWS1, BOWS1, BOWS1, BOWS1, BOWS1, BOWS1, BOWS1, BOWS1, BOWS1, + /* 3*/ _, _, _, _, _, _, _, DIFF, _, _, + /* 4*/ _, KOOPA, _, _, _, _, _, BOMB, _, _, + /* 5*/ _, _, _, _, _, TUXIE, TUXIE, TUXIE, TUXIE, TUXIE, + /* 6*/ _, _, _, _, _, _, _, BOWS2, _, _, + /* 7*/ _, _, _, _, _, _, _, _, _, UKIKI, + /* 8*/ UKIKI, _, _, _, _, BOO, _, _, _, _, + /* 9*/ BOWS2, _, BOWS2, BOWS2, _, _, _, _, BOO, BOO, + /*10*/ UKIKI, UKIKI, _, _, _, BOMB, BOMB, BOO, BOO, _, + /*11*/ _, _, _, _, GRUNT, GRUNT, KBOMB, GRUNT, GRUNT, _, + /*12*/ _, _, _, _, _, _, _, _, KBOMB, _, + /*13*/ _, _, TUXIE, _, _, _, _, _, _, _, + /*14*/ _, _, _, _, _, _, _, _, _, _, + /*15*/ WIGLR, WIGLR, WIGLR, _, _, _, _, _, _, _, + /*16*/ _, YOSHI, _, _, _, _, _, _, WIGLR, _ +}; +#undef _ + +s32 sDialogSpeakerVoice[] = { + SOUND_OBJ_UKIKI_CHATTER_LONG, + SOUND_OBJ_BIG_PENGUIN_YELL, + SOUND_OBJ_BOWSER_INTRO_LAUGH, + SOUND_OBJ_KOOPA_TALK, + SOUND_OBJ_KING_BOBOMB_TALK, + SOUND_OBJ_BOO_LAUGH_LONG, + SOUND_OBJ_BOBOMB_BUDDY_TALK, + SOUND_OBJ_BOWSER_LAUGH, + SOUND_OBJ2_BOSS_DIALOG_GRUNT, + SOUND_OBJ_WIGGLER_TALK, + SOUND_GENERAL_YOSHI_TALK, +#if defined(VERSION_JP) || defined(VERSION_US) + NO_SOUND, + NO_SOUND, + NO_SOUND, + NO_SOUND, +#endif +}; + +u8 sNumProcessedSoundRequests = 0; +u8 sSoundRequestCount = 0; + +// Music dynamic tables. A dynamic describes which volumes to apply to which +// channels of a sequence (I think?), and different parts of a level can have +// different dynamics. Each table below specifies first the sequence to apply +// the dynamics to, then a bunch of conditions for when each dynamic applies +// (e.g. "only if Mario's X position is between 100 and 300"), and finally a +// fallback dynamic. Due to the encoding of the tables, the conditions must +// come in the same order as the macros. +// TODO: dynamic isn't a great term for this, it means other things in music... + +#define MARIO_X_GE 0 +#define MARIO_Y_GE 1 +#define MARIO_Z_GE 2 +#define MARIO_X_LT 3 +#define MARIO_Y_LT 4 +#define MARIO_Z_LT 5 +#define MARIO_IS_IN_AREA 6 +#define MARIO_IS_IN_ROOM 7 + +#define DYN1(cond1, val1, res) (s16)(1 << (15 - cond1) | res), val1 +#define DYN2(cond1, val1, cond2, val2, res) \ + (s16)(1 << (15 - cond1) | 1 << (15 - cond2) | res), val1, val2 +#define DYN3(cond1, val1, cond2, val2, cond3, val3, res) \ + (s16)(1 << (15 - cond1) | 1 << (15 - cond2) | 1 << (15 - cond3) | res), val1, val2, val3 + +s16 sDynHmc[] = { + SEQ_LEVEL_UNDERGROUND, DYN2(MARIO_X_GE, 0, MARIO_Y_LT, -203, 4), + DYN2(MARIO_X_LT, 0, MARIO_Y_LT, -2400, 4), 3, +}; +s16 sDynUnk38[] = { + SEQ_LEVEL_UNDERGROUND, + DYN1(MARIO_IS_IN_AREA, 1, 3), + DYN1(MARIO_IS_IN_AREA, 2, 4), + DYN1(MARIO_IS_IN_AREA, 3, 7), + 0, +}; +s16 sDynNone[] = { SEQ_SOUND_PLAYER, 0 }; + +u8 sCurrentMusicDynamic = 0xff; +u8 sBackgroundMusicForDynamics = SEQUENCE_NONE; + +#define STUB_LEVEL(_0, _1, _2, _3, _4, _5, _6, leveldyn, _8) leveldyn, +#define DEFINE_LEVEL(_0, _1, _2, _3, _4, _5, _6, _7, _8, leveldyn, _10) leveldyn, +#define LEVEL_COUNT 1 +#define _ sDynNone +s16 *sLevelDynamics[LEVEL_COUNT] = { + _, // LEVEL_NONE +}; +#undef _ +#undef STUB_LEVEL +#undef DEFINE_LEVEL + +struct MusicDynamic { + /*0x0*/ s16 bits1; + /*0x2*/ u16 volScale1; + /*0x4*/ s16 dur1; + /*0x6*/ s16 bits2; + /*0x8*/ u16 volScale2; + /*0xA*/ s16 dur2; +}; // size = 0xC + +struct MusicDynamic sMusicDynamics[8] = { + { 0x0000, 127, 100, 0x0e43, 0, 100 }, // SEQ_LEVEL_WATER + { 0x0003, 127, 100, 0x0e40, 0, 100 }, // SEQ_LEVEL_WATER + { 0x0e43, 127, 200, 0x0000, 0, 200 }, // SEQ_LEVEL_WATER + { 0x02ff, 127, 100, 0x0100, 0, 100 }, // SEQ_LEVEL_UNDERGROUND + { 0x03f7, 127, 100, 0x0008, 0, 100 }, // SEQ_LEVEL_UNDERGROUND + { 0x0070, 127, 10, 0x0000, 0, 100 }, // SEQ_LEVEL_SPOOKY + { 0x0000, 127, 100, 0x0070, 0, 10 }, // SEQ_LEVEL_SPOOKY + { 0xffff, 127, 100, 0x0000, 0, 100 }, // any (unused) +}; + +#define STUB_LEVEL(_0, _1, _2, _3, echo1, echo2, echo3, _7, _8) { echo1, echo2, echo3 }, +#define DEFINE_LEVEL(_0, _1, _2, _3, _4, _5, echo1, echo2, echo3, _9, _10) { echo1, echo2, echo3 }, + +u8 sLevelAreaReverbs[LEVEL_COUNT][3] = { + { 0x00, 0x00, 0x00 }, // LEVEL_NONE +}; +#undef STUB_LEVEL +#undef DEFINE_LEVEL + +#define STUB_LEVEL(_0, _1, _2, volume, _4, _5, _6, _7, _8) volume, +#define DEFINE_LEVEL(_0, _1, _2, _3, _4, volume, _6, _7, _8, _9, _10) volume, + +u16 sLevelAcousticReaches[LEVEL_COUNT] = { + 20000, // LEVEL_NONE +}; + +#undef STUB_LEVEL +#undef DEFINE_LEVEL + +#define AUDIO_MAX_DISTANCE US_FLOAT(22000.0) + +#ifdef VERSION_JP +#define LOW_VOLUME_REVERB 48.0 +#else +#define LOW_VOLUME_REVERB 40.0f +#endif + +#ifdef VERSION_JP +#define VOLUME_RANGE_UNK1 0.8f +#define VOLUME_RANGE_UNK2 1.0f +#else +#define VOLUME_RANGE_UNK1 0.9f +#define VOLUME_RANGE_UNK2 0.8f +#endif + +// Default volume for background music sequences (playing on player 0). +u8 sBackgroundMusicDefaultVolume[] = { + 127, // SEQ_SOUND_PLAYER + 80, // SEQ_EVENT_CUTSCENE_COLLECT_STAR + 80, // SEQ_MENU_TITLE_SCREEN + 75, // SEQ_LEVEL_GRASS + 70, // SEQ_LEVEL_INSIDE_CASTLE + 75, // SEQ_LEVEL_WATER + 75, // SEQ_LEVEL_HOT + 75, // SEQ_LEVEL_BOSS_KOOPA + 70, // SEQ_LEVEL_SNOW + 65, // SEQ_LEVEL_SLIDE + 80, // SEQ_LEVEL_SPOOKY + 65, // SEQ_EVENT_PIRANHA_PLANT + 85, // SEQ_LEVEL_UNDERGROUND + 75, // SEQ_MENU_STAR_SELECT + 65, // SEQ_EVENT_POWERUP + 70, // SEQ_EVENT_METAL_CAP + 65, // SEQ_EVENT_KOOPA_MESSAGE + 70, // SEQ_LEVEL_KOOPA_ROAD + 70, // SEQ_EVENT_HIGH_SCORE + 65, // SEQ_EVENT_MERRY_GO_ROUND + 80, // SEQ_EVENT_RACE + 70, // SEQ_EVENT_CUTSCENE_STAR_SPAWN + 85, // SEQ_EVENT_BOSS + 75, // SEQ_EVENT_CUTSCENE_COLLECT_KEY + 75, // SEQ_EVENT_ENDLESS_STAIRS + 85, // SEQ_LEVEL_BOSS_KOOPA_FINAL + 70, // SEQ_EVENT_CUTSCENE_CREDITS + 80, // SEQ_EVENT_SOLVE_PUZZLE + 80, // SEQ_EVENT_TOAD_MESSAGE + 70, // SEQ_EVENT_PEACH_MESSAGE + 75, // SEQ_EVENT_CUTSCENE_INTRO + 80, // SEQ_EVENT_CUTSCENE_VICTORY + 70, // SEQ_EVENT_CUTSCENE_ENDING + 65, // SEQ_MENU_FILE_SELECT + 0, // SEQ_EVENT_CUTSCENE_LAKITU (not in JP) +}; + +STATIC_ASSERT(ARRAY_COUNT(sBackgroundMusicDefaultVolume) == SEQ_COUNT, + "change this array if you are adding sequences"); + +u8 sCurrentBackgroundMusicSeqId = SEQUENCE_NONE; +u8 sMusicDynamicDelay = 0; +u8 sSoundBankUsedListBack[SOUND_BANK_COUNT] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +u8 sSoundBankFreeListFront[SOUND_BANK_COUNT] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; +u8 sNumSoundsInBank[SOUND_BANK_COUNT] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // only used for debugging +u8 sMaxChannelsForSoundBank[SOUND_BANK_COUNT] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + +// Banks 2 and 7 both grew from 0x30 sounds to 0x40 in size in US. +#ifdef VERSION_JP +#define BANK27_SIZE 0x30 +#else +#define BANK27_SIZE 0x40 +#endif +u8 sNumSoundsPerBank[SOUND_BANK_COUNT] = { + 0x70, 0x30, BANK27_SIZE, 0x80, 0x20, 0x80, 0x20, BANK27_SIZE, 0x80, 0x80, +}; +#undef BANK27_SIZE + +// sBackgroundMusicMaxTargetVolume and sBackgroundMusicTargetVolume use the 0x80 +// bit to indicate that they are set, and the rest of the bits for the actual value +#define TARGET_VOLUME_IS_PRESENT_FLAG 0x80 +#define TARGET_VOLUME_VALUE_MASK 0x7f +#define TARGET_VOLUME_UNSET 0x00 + +f32 gGlobalSoundSource[3] = { 0.0f, 0.0f, 0.0f }; +f32 sUnusedSoundArgs[3] = { 1.0f, 1.0f, 1.0f }; +u8 sSoundBankDisabled[16] = { 0 }; +u8 D_80332108 = 0; +u8 sHasStartedFadeOut = FALSE; +u16 sSoundBanksThatLowerBackgroundMusic = 0; +u8 sUnused80332114 = 0; // never read, set to 0 +u16 sUnused80332118 = 0; // never read, set to 0 +u8 sBackgroundMusicMaxTargetVolume = TARGET_VOLUME_UNSET; +u8 D_80332120 = 0; +u8 D_80332124 = 0; + +#if defined(VERSION_EU) || defined(VERSION_SH) +u8 D_EU_80300558 = 0; +#endif + +u8 sBackgroundMusicQueueSize = 0; + +#ifndef VERSION_JP +u8 sUnused8033323C = 0; // never read, set to 0 +#endif + + +// bss +#if defined(VERSION_JP) || defined(VERSION_US) +s16 *gCurrAiBuffer; +#endif +#ifdef VERSION_SH +s8 D_SH_80343CD0_pad[0x20]; +s32 D_SH_80343CF0; +s8 D_SH_80343CF8_pad[0x8]; +struct UnkStruct80343D00 D_SH_80343D00; +s8 D_SH_8034DC8_pad[0x8]; +OSPiHandle DriveRomHandle; // used in osDriveRomInit.c. Why here? +s8 D_SH_80343E48_pad[0x8]; +#endif + +struct Sound sSoundRequests[0x100]; +// Curiously, this has size 3, despite SEQUENCE_PLAYERS == 4 on EU +struct ChannelVolumeScaleFade D_80360928[3][CHANNELS_MAX]; +u8 sUsedChannelsForSoundBank[SOUND_BANK_COUNT]; +u8 sCurrentSound[SOUND_BANK_COUNT][MAX_CHANNELS_PER_SOUND_BANK]; // index into sSoundBanks + +/** + * For each sound bank, a pool containing the currently active sounds for that bank. + * The free and used slots in these pools are linked lists. The element sSoundBanks[bank][0] + * is used as a list header for the used list, and never holds an actual sound. See also + * sSoundBankUsedListBack and sSoundBankFreeListFront. + */ +struct SoundCharacteristics sSoundBanks[SOUND_BANK_COUNT][40]; + +u8 sSoundMovingSpeed[SOUND_BANK_COUNT]; +u8 sBackgroundMusicTargetVolume; +static u8 sLowerBackgroundMusicVolume; +struct SequenceQueueItem sBackgroundMusicQueue[MAX_BACKGROUND_MUSIC_QUEUE_SIZE]; + +#if defined(VERSION_EU) || defined(VERSION_SH) +s32 unk_sh_8034754C; +#endif + +#ifdef VERSION_EU +OSMesgQueue OSMesgQueue0; +OSMesgQueue OSMesgQueue1; +OSMesgQueue OSMesgQueue2; +OSMesgQueue OSMesgQueue3; +extern OSMesgQueue *OSMesgQueues[]; + +struct EuAudioCmd sAudioCmd[0x100]; + +OSMesg OSMesg0; +s32 pad1; // why is there 1 s32 here +OSMesg OSMesg1; +s32 pad2[2]; // it's not just that the struct is bigger than we think, because there are 2 here +OSMesg OSMesg2; // and none here. wth nintendo +OSMesg OSMesg3; +#else // VERSION_SH +extern OSMesgQueue *D_SH_80350F88; +extern OSMesgQueue *D_SH_80350FA8; +#endif + +#ifdef VERSION_JP +typedef u16 FadeT; +#else +typedef s32 FadeT; +#endif + +// some sort of main thread -> sound thread dispatchers +extern void func_802ad728(u32 bits, f32 arg); +extern void func_802ad74c(u32 bits, u32 arg); +extern void func_802ad770(u32 bits, s8 arg); + +static void update_background_music_after_sound(u8 bank, u8 soundIndex); +void update_game_sound(void); +static void fade_channel_volume_scale(u8 player, u8 channelId, u8 targetScale, u16 fadeTimer); +void process_level_music_dynamics(void); +static u8 begin_background_music_fade(u16 fadeDuration); +void func_80320ED8(void); + +#ifndef VERSION_JP +void unused_8031E4F0(void) { + // This is a debug function which is almost entirely optimized away, + // except for loops, string literals, and a read of a volatile variable. + // The string literals have allowed it to be partially reconstructed. + s32 i; + + stubbed_printf("AUTOSEQ "); + stubbed_printf("%2x %2x <%5x : %5x / %5x>\n", gSeqLoadedPool.temporary.entries[0].id, + gSeqLoadedPool.temporary.entries[1].id, gSeqLoadedPool.temporary.entries[0].size, + gSeqLoadedPool.temporary.entries[1].size, gSeqLoadedPool.temporary.pool.size); + + stubbed_printf("AUTOBNK "); + stubbed_printf("%2x %3x <%5x : %5x / %5x>\n", gBankLoadedPool.temporary.entries[0].id, + gBankLoadedPool.temporary.entries[1].id, gBankLoadedPool.temporary.entries[0].size, + gBankLoadedPool.temporary.entries[1].size, gBankLoadedPool.temporary.pool.size); + + stubbed_printf("STAYSEQ "); + stubbed_printf("[%2x] <%5x / %5x>\n", gSeqLoadedPool.persistent.numEntries, + gSeqLoadedPool.persistent.pool.cur - gSeqLoadedPool.persistent.pool.start, + gSeqLoadedPool.persistent.pool.size); + for (i = 0; (u32) i < gSeqLoadedPool.persistent.numEntries; i++) { + stubbed_printf("%2x ", gSeqLoadedPool.persistent.entries[i].id); + } + stubbed_printf("\n"); + + stubbed_printf("STAYBNK "); + stubbed_printf("[%2x] <%5x / %5x>\n", gBankLoadedPool.persistent.numEntries, + gBankLoadedPool.persistent.pool.cur - gBankLoadedPool.persistent.pool.start, + gBankLoadedPool.persistent.pool.size); + for (i = 0; (u32) i < gBankLoadedPool.persistent.numEntries; i++) { + stubbed_printf("%2x ", gBankLoadedPool.persistent.entries[i].id); + } + stubbed_printf("\n\n"); + + stubbed_printf(" 0123456789ABCDEF0123456789ABCDEF01234567\n"); + stubbed_printf("--------------------------------------------\n"); + + // gSeqLoadStatus/gBankLoadStatus, 4 entries at a time? + stubbed_printf("SEQ "); + for (i = 0; i < 40; i++) { + stubbed_printf("%1x", 0); + } + stubbed_printf("\n"); + + stubbed_printf("BNK "); + for (i = 0; i < 40; i += 4) { + stubbed_printf("%1x", 0); + } + stubbed_printf("\n"); + + stubbed_printf("FIXHEAP "); + stubbed_printf("%4x / %4x\n", 0, 0); + stubbed_printf("DRVHEAP "); + stubbed_printf("%5x / %5x\n", 0, 0); + stubbed_printf("DMACACHE %4d Blocks\n", 0); + stubbed_printf("CHANNELS %2d / MAX %3d \n", 0, 0); + + stubbed_printf("TEMPOMAX %d\n", gTempoInternalToExternal / TEMPO_SCALE); + stubbed_printf("TEMPO G0 %d\n", gSequencePlayers[SEQ_PLAYER_LEVEL].tempo / TEMPO_SCALE); + stubbed_printf("TEMPO G1 %d\n", gSequencePlayers[SEQ_PLAYER_ENV].tempo / TEMPO_SCALE); + stubbed_printf("TEMPO G2 %d\n", gSequencePlayers[SEQ_PLAYER_SFX].tempo / TEMPO_SCALE); + stubbed_printf("DEBUGFLAG %8x\n", gAudioErrorFlags); +} + +void unused_8031E568(void) { + stubbed_printf("COUNT %8d\n", gAudioFrameCount); +} +#endif + +#if defined(VERSION_EU) || defined(VERSION_SH) +const char unusedErrorStr1[] = "Error : Queue is not empty ( %x ) \n"; +const char unusedErrorStr2[] = "specchg error\n"; +#endif + +/** + * Fade out a sequence player + * Called from threads: thread5_game_loop + */ +#if defined(VERSION_EU) || defined(VERSION_SH) +void audio_reset_session_eu(s32 presetId) { + OSMesg mesg; +#ifdef VERSION_SH + osRecvMesg(D_SH_80350FA8, &mesg, OS_MESG_NOBLOCK); + osSendMesg(D_SH_80350F88, (OSMesg) presetId, OS_MESG_NOBLOCK); + osRecvMesg(D_SH_80350FA8, &mesg, OS_MESG_BLOCK); + if ((s32) mesg != presetId) { + osRecvMesg(D_SH_80350FA8, &mesg, OS_MESG_BLOCK); + } + +#else + osRecvMesg(OSMesgQueues[3], &mesg, OS_MESG_NOBLOCK); + osSendMesg(OSMesgQueues[2], (OSMesg) presetId, OS_MESG_NOBLOCK); + osRecvMesg(OSMesgQueues[3], &mesg, OS_MESG_BLOCK); + if ((s32) mesg != presetId) { + osRecvMesg(OSMesgQueues[3], &mesg, OS_MESG_BLOCK); + } +#endif +} +#endif + +#if defined(VERSION_JP) || defined(VERSION_US) +/** + * Called from threads: thread3_main, thread5_game_loop + */ +static void seq_player_fade_to_zero_volume(s32 player, FadeT fadeDuration) { + struct SequencePlayer *seqPlayer = &gSequencePlayers[player]; + +#ifndef VERSION_JP + // fadeDuration is never 0 in practice + if (fadeDuration == 0) { + fadeDuration++; + } +#endif + + seqPlayer->fadeVelocity = -(seqPlayer->fadeVolume / fadeDuration); + seqPlayer->state = SEQUENCE_PLAYER_STATE_FADE_OUT; + seqPlayer->fadeRemainingFrames = fadeDuration; +} + +/** + * Called from threads: thread4_sound, thread5_game_loop + */ +static void func_8031D690(s32 player, FadeT fadeInTime) { + struct SequencePlayer *seqPlayer = &gSequencePlayers[player]; + + if (fadeInTime == 0 || seqPlayer->state == SEQUENCE_PLAYER_STATE_FADE_OUT) { + return; + } + + seqPlayer->state = SEQUENCE_PLAYER_STATE_2; + seqPlayer->fadeRemainingFrames = fadeInTime; + seqPlayer->fadeVolume = 0.0f; + seqPlayer->fadeVelocity = 0.0f; +} +#endif + +/** + * Called from threads: thread5_game_loop + */ +static void seq_player_fade_to_percentage_of_volume(s32 player, FadeT fadeDuration, u8 percentage) { + struct SequencePlayer *seqPlayer = &gSequencePlayers[player]; + f32 targetVolume; + +#if defined(VERSION_EU) || defined(VERSION_SH) + if (seqPlayer->state == 2) { + return; + } +#else + if (seqPlayer->state == SEQUENCE_PLAYER_STATE_FADE_OUT) { + return; + } +#endif + + targetVolume = (FLOAT_CAST(percentage) / EU_FLOAT(100.0)) * seqPlayer->fadeVolume; + seqPlayer->volume = seqPlayer->fadeVolume; + + seqPlayer->fadeRemainingFrames = 0; + if (fadeDuration == 0) { + seqPlayer->fadeVolume = targetVolume; + return; + } + seqPlayer->fadeVelocity = (targetVolume - seqPlayer->fadeVolume) / fadeDuration; +#if defined(VERSION_EU) || defined(VERSION_SH) + seqPlayer->state = 0; +#else + seqPlayer->state = SEQUENCE_PLAYER_STATE_4; +#endif + + seqPlayer->fadeRemainingFrames = fadeDuration; +} + +/** + * Called from threads: thread3_main, thread4_sound, thread5_game_loop + */ +static void seq_player_fade_to_normal_volume(s32 player, FadeT fadeDuration) { + struct SequencePlayer *seqPlayer = &gSequencePlayers[player]; + +#if defined(VERSION_EU) || defined(VERSION_SH) + if (seqPlayer->state == 2) { + return; + } +#else + if (seqPlayer->state == SEQUENCE_PLAYER_STATE_FADE_OUT) { + return; + } +#endif + + seqPlayer->fadeRemainingFrames = 0; + if (fadeDuration == 0) { + seqPlayer->fadeVolume = seqPlayer->volume; + return; + } + seqPlayer->fadeVelocity = (seqPlayer->volume - seqPlayer->fadeVolume) / fadeDuration; +#if defined(VERSION_EU) || defined(VERSION_SH) + seqPlayer->state = 0; +#else + seqPlayer->state = SEQUENCE_PLAYER_STATE_2; +#endif + + seqPlayer->fadeRemainingFrames = fadeDuration; +} + +/** + * Called from threads: thread3_main, thread4_sound, thread5_game_loop + */ +static void seq_player_fade_to_target_volume(s32 player, FadeT fadeDuration, u8 targetVolume) { + struct SequencePlayer *seqPlayer = &gSequencePlayers[player]; + +#if defined(VERSION_JP) || defined(VERSION_US) + if (seqPlayer->state == SEQUENCE_PLAYER_STATE_FADE_OUT) { + return; + } +#endif + + seqPlayer->fadeRemainingFrames = 0; + if (fadeDuration == 0) { + seqPlayer->fadeVolume = (FLOAT_CAST(targetVolume) / EU_FLOAT(127.0)); + return; + } + + seqPlayer->fadeVelocity = + (((f32)(FLOAT_CAST(targetVolume) / EU_FLOAT(127.0)) - seqPlayer->fadeVolume) + / (f32) fadeDuration); +#if defined(VERSION_EU) || defined(VERSION_SH) + seqPlayer->state = 0; +#else + seqPlayer->state = SEQUENCE_PLAYER_STATE_4; +#endif + + seqPlayer->fadeRemainingFrames = fadeDuration; +} + +#if defined(VERSION_EU) || defined(VERSION_SH) +#ifdef VERSION_EU +extern void func_802ad7a0(void); +#else +extern void func_sh_802F64C8(void); +#endif + +/** + * Called from threads: thread5_game_loop + */ +void maybe_tick_game_sound(void) { + if (sGameLoopTicked != 0) { + update_game_sound(); + sGameLoopTicked = 0; + } +#ifdef VERSION_EU + func_802ad7a0(); +#else + func_sh_802F64C8(); // moved in SH +#endif +} + +void func_eu_802e9bec(s32 player, s32 channel, s32 arg2) { + // EU verson of unused_803209D8 + // chan->stopSomething2 = arg2? + func_802ad770(0x08000000 | (player & 0xff) << 16 | (channel & 0xff) << 8, (s8) arg2); +} + +#else + +#ifdef TARGET_N64 +/** + * Called from threads: thread4_sound + */ +struct SPTask *create_next_audio_frame_task(void) { + u32 samplesRemainingInAI; + s32 writtenCmds; + s32 index; + OSTask_t *task; + s32 oldDmaCount; + s32 flags; + + gAudioFrameCount++; + if (gAudioLoadLock != AUDIO_LOCK_NOT_LOADING) { + stubbed_printf("DAC:Lost 1 Frame.\n"); + return NULL; + } + + gAudioTaskIndex ^= 1; + gCurrAiBufferIndex++; + gCurrAiBufferIndex %= NUMAIBUFFERS; + index = (gCurrAiBufferIndex - 2 + NUMAIBUFFERS) % NUMAIBUFFERS; + samplesRemainingInAI = osAiGetLength() / 4; + + // Audio is triple buffered; the audio interface reads from two buffers + // while the third is being written by the RSP. More precisely, the + // lifecycle is: + // - this function computes an audio command list + // - wait for vblank + // - the command list is sent to the RSP (we could have sent it to the + // RSP before the vblank, but that gives the RSP less time to finish) + // - wait for vblank + // - the RSP is now expected to be finished, and we can send its output + // on to the AI + // Here we thus send to the AI the sound that was generated two frames ago. + if (gAiBufferLengths[index] != 0) { + osAiSetNextBuffer(gAiBuffers[index], gAiBufferLengths[index] * 4); + } + + oldDmaCount = gCurrAudioFrameDmaCount; + // There has to be some sort of no-op if here, but it's not exactly clear + // how it should look... It's also very unclear why gCurrAudioFrameDmaQueue + // isn't read from here, despite gCurrAudioFrameDmaCount being reset. + if (oldDmaCount > AUDIO_FRAME_DMA_QUEUE_SIZE) { + stubbed_printf("DMA: Request queue over.( %d )\n", oldDmaCount); + } + gCurrAudioFrameDmaCount = 0; + + gAudioTask = &gAudioTasks[gAudioTaskIndex]; + gAudioCmd = gAudioCmdBuffers[gAudioTaskIndex]; + + index = gCurrAiBufferIndex; + gCurrAiBuffer = gAiBuffers[index]; + gAiBufferLengths[index] = + ((gSamplesPerFrameTarget - samplesRemainingInAI + EXTRA_BUFFERED_AI_SAMPLES_TARGET) & ~0xf) + + SAMPLES_TO_OVERPRODUCE; + if (gAiBufferLengths[index] < gMinAiBufferLength) { + gAiBufferLengths[index] = gMinAiBufferLength; + } + if (gAiBufferLengths[index] > gSamplesPerFrameTarget + SAMPLES_TO_OVERPRODUCE) { + gAiBufferLengths[index] = gSamplesPerFrameTarget + SAMPLES_TO_OVERPRODUCE; + } + + if (sGameLoopTicked != 0) { + update_game_sound(); + sGameLoopTicked = 0; + } + + // For the function to match we have to preserve some arbitrary variable + // across this function call. + flags = 0; + gAudioCmd = synthesis_execute(gAudioCmd, &writtenCmds, gCurrAiBuffer, gAiBufferLengths[index]); + gAudioRandom = ((gAudioRandom + gAudioFrameCount) * gAudioFrameCount); + + index = gAudioTaskIndex; + gAudioTask->msgqueue = NULL; + gAudioTask->msg = NULL; + + task = &gAudioTask->task.t; + task->type = M_AUDTASK; + task->flags = flags; + task->ucode_boot = rspF3DBootStart; + task->ucode_boot_size = (u8 *) rspF3DBootEnd - (u8 *) rspF3DBootStart; + task->ucode = rspAspMainStart; + task->ucode_size = 0x800; // (this size is ignored) + task->ucode_data = rspAspMainDataStart; + task->ucode_data_size = (rspAspMainDataEnd - rspAspMainDataStart) * sizeof(u64); + task->dram_stack = NULL; + task->dram_stack_size = 0; + task->output_buff = NULL; + task->output_buff_size = NULL; + task->data_ptr = gAudioCmdBuffers[index]; + task->data_size = writtenCmds * sizeof(u64); + +// The audio task never yields, so having a yield buffer is pointless. +// This wastefulness was fixed in US. +#ifdef VERSION_JP + task->yield_data_ptr = (u64 *) gAudioSPTaskYieldBuffer; + task->yield_data_size = OS_YIELD_AUDIO_SIZE; +#else + task->yield_data_ptr = NULL; + task->yield_data_size = 0; +#endif + + decrease_sample_dma_ttls(); + return gAudioTask; +} +#else +struct SPTask *create_next_audio_frame_task(void) { + return NULL; +} +void create_next_audio_buffer(s16 *samples, u32 num_samples) { + gAudioFrameCount++; + if (sGameLoopTicked != 0) { + update_game_sound(); + sGameLoopTicked = 0; + } + s32 writtenCmds; + synthesis_execute(gAudioCmdBuffers[0], &writtenCmds, samples, num_samples); + gAudioRandom = ((gAudioRandom + gAudioFrameCount) * gAudioFrameCount); + decrease_sample_dma_ttls(); +} +#endif +#endif + +/** + * Called from threads: thread4_sound, thread5_game_loop (EU only) + */ +static void process_sound_request(u32 bits, f32 *pos) { + u8 bank; + u8 soundIndex; + u8 counter = 0; + u8 soundId; + f32 dist; + const f32 one = 1.0f; + + bank = (bits & SOUNDARGS_MASK_BANK) >> SOUNDARGS_SHIFT_BANK; + soundId = (bits & SOUNDARGS_MASK_SOUNDID) >> SOUNDARGS_SHIFT_SOUNDID; + + if (soundId >= sNumSoundsPerBank[bank] || sSoundBankDisabled[bank]) { + //DEBUG_PRINT("process_sound_request: invalid soundId %d\n", soundId); + return; + } + + soundIndex = sSoundBanks[bank][0].next; + while (soundIndex != 0xff && soundIndex != 0) { + //DEBUG_PRINT("process_sound_request: soundIndex %d\n", soundIndex); + // If an existing sound from the same source exists in the bank, then we should either + // interrupt that sound and replace it with the new sound, or we should drop the new sound. + if (sSoundBanks[bank][soundIndex].x == pos) { + // If the existing sound has lower or equal priority, then we should replace it. + // Otherwise the new sound will be dropped. + if ((sSoundBanks[bank][soundIndex].soundBits & SOUNDARGS_MASK_PRIORITY) + <= (bits & SOUNDARGS_MASK_PRIORITY)) { + + // If the existing sound is discrete or is a different continuous sound, then + // interrupt it and play the new sound instead. + // Otherwise the new sound is continuous and equals the existing sound, so we just + // need to update the sound's freshness. + if ((sSoundBanks[bank][soundIndex].soundBits & SOUND_DISCRETE) != 0 + || (bits & SOUNDARGS_MASK_SOUNDID) + != (sSoundBanks[bank][soundIndex].soundBits & SOUNDARGS_MASK_SOUNDID)) { + update_background_music_after_sound(bank, soundIndex); + sSoundBanks[bank][soundIndex].soundBits = bits; + // In practice, the starting status is always WAITING + sSoundBanks[bank][soundIndex].soundStatus = bits & SOUNDARGS_MASK_STATUS; + } + + // Reset freshness: + // - For discrete sounds, this gives the sound SOUND_MAX_FRESHNESS frames to play + // before it gets deleted for being stale + // - For continuous sounds, this gives it another 2 frames before play_sound must + // be called again to keep it playing + sSoundBanks[bank][soundIndex].freshness = SOUND_MAX_FRESHNESS; + } + + // Prevent allocating a new node - if the existing sound had higher piority, then the + // new sound will be dropped + soundIndex = 0; + } else { + soundIndex = sSoundBanks[bank][soundIndex].next; + } + counter++; + } + + if (counter == 0) { + sSoundMovingSpeed[bank] = 32; + } + + // If free list has more than one element remaining + if (sSoundBanks[bank][sSoundBankFreeListFront[bank]].next != 0xff && soundIndex != 0) { + DEBUG_PRINT("process_sound_request2: soundIndex %d\n", soundIndex); + // Allocate from free list + soundIndex = sSoundBankFreeListFront[bank]; + + dist = sqrtf(pos[0] * pos[0] + pos[1] * pos[1] + pos[2] * pos[2]) * one; + sSoundBanks[bank][soundIndex].x = &pos[0]; + sSoundBanks[bank][soundIndex].y = &pos[1]; + sSoundBanks[bank][soundIndex].z = &pos[2]; + sSoundBanks[bank][soundIndex].distance = dist; + sSoundBanks[bank][soundIndex].soundBits = bits; + // In practice, the starting status is always WAITING + sSoundBanks[bank][soundIndex].soundStatus = bits & SOUNDARGS_MASK_STATUS; + sSoundBanks[bank][soundIndex].freshness = SOUND_MAX_FRESHNESS; + + // Append to end of used list and pop from front of free list + sSoundBanks[bank][soundIndex].prev = sSoundBankUsedListBack[bank]; + sSoundBanks[bank][sSoundBankUsedListBack[bank]].next = sSoundBankFreeListFront[bank]; + sSoundBankUsedListBack[bank] = sSoundBankFreeListFront[bank]; + sSoundBankFreeListFront[bank] = sSoundBanks[bank][sSoundBankFreeListFront[bank]].next; + sSoundBanks[bank][sSoundBankFreeListFront[bank]].prev = 0xff; + sSoundBanks[bank][soundIndex].next = 0xff; + } +} + +/** + * Processes all sound requests + * + * Called from threads: thread4_sound, thread5_game_loop (EU only) + */ +static void process_all_sound_requests(void) { + struct Sound *sound; + + while (sSoundRequestCount != sNumProcessedSoundRequests) { + sound = &sSoundRequests[sNumProcessedSoundRequests]; + process_sound_request(sound->soundBits, sound->position); + sNumProcessedSoundRequests++; + //DEBUG_PRINT("processed sounds: %d\n", sNumProcessedSoundRequests); + } +} + +/** + * Called from threads: thread4_sound, thread5_game_loop (EU only) + */ +static void delete_sound_from_bank(u8 bank, u8 soundIndex) { + if (sSoundBankUsedListBack[bank] == soundIndex) { + // Remove from end of used list + sSoundBankUsedListBack[bank] = sSoundBanks[bank][soundIndex].prev; + } else { + // Set sound.next.prev to sound.prev + sSoundBanks[bank][sSoundBanks[bank][soundIndex].next].prev = sSoundBanks[bank][soundIndex].prev; + } + + // Set sound.prev.next to sound.next + sSoundBanks[bank][sSoundBanks[bank][soundIndex].prev].next = sSoundBanks[bank][soundIndex].next; + + // Push to front of free list + sSoundBanks[bank][soundIndex].next = sSoundBankFreeListFront[bank]; + sSoundBanks[bank][soundIndex].prev = 0xff; + sSoundBanks[bank][sSoundBankFreeListFront[bank]].prev = soundIndex; + sSoundBankFreeListFront[bank] = soundIndex; +} + +/** + * Called from threads: thread3_main, thread4_sound, thread5_game_loop + */ +static void update_background_music_after_sound(u8 bank, u8 soundIndex) { + if (sSoundBanks[bank][soundIndex].soundBits & SOUND_LOWER_BACKGROUND_MUSIC) { + sSoundBanksThatLowerBackgroundMusic &= (1 << bank) ^ 0xffff; + begin_background_music_fade(50); + } +} + +/** + * Called from threads: thread4_sound, thread5_game_loop (EU only) + */ +static void select_current_sounds(u8 bank) { + u32 isDiscreteAndStatus; + u8 latestSoundIndex; + u8 i; + u8 j; + u8 soundIndex; + u32 liveSoundPriorities[16] = { 0x10000000, 0x10000000, 0x10000000, 0x10000000, + 0x10000000, 0x10000000, 0x10000000, 0x10000000, + 0x10000000, 0x10000000, 0x10000000, 0x10000000, + 0x10000000, 0x10000000, 0x10000000, 0x10000000 }; + u8 liveSoundIndices[16] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + u8 liveSoundStatuses[16] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + u8 numSoundsInBank = 0; + u8 requestedPriority; + + // + // Delete stale sounds and prioritize remaining sounds into the liveSound arrays + // + soundIndex = sSoundBanks[bank][0].next; + while (soundIndex != 0xff) { + latestSoundIndex = soundIndex; + + // If a discrete sound goes 10 frames without being played (because it is too low + // priority), then mark it for deletion + if ((sSoundBanks[bank][soundIndex].soundBits & (SOUND_DISCRETE | SOUNDARGS_MASK_STATUS)) + == (SOUND_DISCRETE | SOUND_STATUS_WAITING)) { + if (sSoundBanks[bank][soundIndex].freshness-- == 0) { + sSoundBanks[bank][soundIndex].soundBits = NO_SOUND; + } + } + // If a continuous sound goes 2 frames without play_sound being called, then mark it for + // deletion + else if ((sSoundBanks[bank][soundIndex].soundBits & SOUND_DISCRETE) == 0) { + if (sSoundBanks[bank][soundIndex].freshness-- == SOUND_MAX_FRESHNESS - 2) { + update_background_music_after_sound(bank, soundIndex); + sSoundBanks[bank][soundIndex].soundBits = NO_SOUND; + } + } + + // If a sound was marked for deletion and hasn't started playing yet, delete it now + if (sSoundBanks[bank][soundIndex].soundBits == NO_SOUND + && sSoundBanks[bank][soundIndex].soundStatus == SOUND_STATUS_WAITING) { + // Since the current sound will be deleted, the next iteration should process + // sound.prev.next + latestSoundIndex = sSoundBanks[bank][soundIndex].prev; + sSoundBanks[bank][soundIndex].soundStatus = SOUND_STATUS_STOPPED; + delete_sound_from_bank(bank, soundIndex); + } + + // If the current sound was not just deleted, consider it as a candidate for the currently + // playing sound + if (sSoundBanks[bank][soundIndex].soundStatus != SOUND_STATUS_STOPPED + && soundIndex == latestSoundIndex) { + + // Recompute distance each frame since the sound's position may have changed + sSoundBanks[bank][soundIndex].distance = + sqrtf((*sSoundBanks[bank][soundIndex].x * *sSoundBanks[bank][soundIndex].x) + + (*sSoundBanks[bank][soundIndex].y * *sSoundBanks[bank][soundIndex].y) + + (*sSoundBanks[bank][soundIndex].z * *sSoundBanks[bank][soundIndex].z)) + * 1; + + requestedPriority = (sSoundBanks[bank][soundIndex].soundBits & SOUNDARGS_MASK_PRIORITY) + >> SOUNDARGS_SHIFT_PRIORITY; + + // Recompute priority, possibly based on the sound's source position relative to the + // camera. + // (Note that the sound's priority is the opposite of requestedPriority; lower is + // more important) + if (sSoundBanks[bank][soundIndex].soundBits & SOUND_NO_PRIORITY_LOSS) { + sSoundBanks[bank][soundIndex].priority = 0x4c * (0xff - requestedPriority); + } else if (*sSoundBanks[bank][soundIndex].z > 0.0f) { + sSoundBanks[bank][soundIndex].priority = + (u32) sSoundBanks[bank][soundIndex].distance + + (u32)(*sSoundBanks[bank][soundIndex].z / US_FLOAT(6.0)) + + 0x4c * (0xff - requestedPriority); + } else { + sSoundBanks[bank][soundIndex].priority = + (u32) sSoundBanks[bank][soundIndex].distance + 0x4c * (0xff - requestedPriority); + } + + // Insert the sound into the liveSound arrays, keeping the arrays sorted by priority. + // If more than sMaxChannelsForSoundBank[bank] sounds are live, then the + // sound with lowest priority will be removed from the arrays. + // In practice sMaxChannelsForSoundBank is always 1, so this code is overly general. + for (i = 0; i < sMaxChannelsForSoundBank[bank]; i++) { + // If the correct position is found + if (liveSoundPriorities[i] >= sSoundBanks[bank][soundIndex].priority) { + // Shift remaining sounds to the right + for (j = sMaxChannelsForSoundBank[bank] - 1; j > i; j--) { + liveSoundPriorities[j] = liveSoundPriorities[j - 1]; + liveSoundIndices[j] = liveSoundIndices[j - 1]; + liveSoundStatuses[j] = liveSoundStatuses[j - 1]; + } + // Insert the sound at index i + liveSoundPriorities[i] = sSoundBanks[bank][soundIndex].priority; + liveSoundIndices[i] = soundIndex; + liveSoundStatuses[i] = sSoundBanks[bank][soundIndex].soundStatus; // unused + // Break + i = sMaxChannelsForSoundBank[bank]; + } + } + + numSoundsInBank++; + } + + soundIndex = sSoundBanks[bank][latestSoundIndex].next; + } + + sNumSoundsInBank[bank] = numSoundsInBank; + sUsedChannelsForSoundBank[bank] = sMaxChannelsForSoundBank[bank]; + + // + // Remove any sounds from liveSoundIndices that are already playing. + // Stop any currently playing sounds that are not in liveSoundIndices. + // + for (i = 0; i < sUsedChannelsForSoundBank[bank]; i++) { + // Check if sCurrentSound[bank][i] is present in the liveSound arrays. + for (soundIndex = 0; soundIndex < sUsedChannelsForSoundBank[bank]; soundIndex++) { + if (liveSoundIndices[soundIndex] != 0xff + && sCurrentSound[bank][i] == liveSoundIndices[soundIndex]) { + // If found, remove it from liveSoundIndices + liveSoundIndices[soundIndex] = 0xff; + soundIndex = 0xfe; // Break. Afterward soundIndex will be 0xff + } + } + + // If it is not present in the liveSound arrays, then stop playing it + if (soundIndex != 0xff) { + if (sCurrentSound[bank][i] != 0xff) { + // If the sound was marked for deletion and is playing, delete it + if (sSoundBanks[bank][sCurrentSound[bank][i]].soundBits == NO_SOUND) { + if (sSoundBanks[bank][sCurrentSound[bank][i]].soundStatus == SOUND_STATUS_PLAYING) { + sSoundBanks[bank][sCurrentSound[bank][i]].soundStatus = SOUND_STATUS_STOPPED; + delete_sound_from_bank(bank, sCurrentSound[bank][i]); + } + } + + // If the sound is discrete and is playing, then delete it + isDiscreteAndStatus = sSoundBanks[bank][sCurrentSound[bank][i]].soundBits + & (SOUND_DISCRETE | SOUNDARGS_MASK_STATUS); + if (isDiscreteAndStatus >= (SOUND_DISCRETE | SOUND_STATUS_PLAYING) + && sSoundBanks[bank][sCurrentSound[bank][i]].soundStatus != SOUND_STATUS_STOPPED) { +//! @bug On JP, if a discrete sound that lowers the background music is +// interrupted in this way, it will keep the background music low afterward. +// There are only a few of these sounds, and it probably isn't possible to do +// it in practice without using a time stop glitch like triple star spawn. +#ifndef VERSION_JP + update_background_music_after_sound(bank, sCurrentSound[bank][i]); +#endif + + sSoundBanks[bank][sCurrentSound[bank][i]].soundBits = NO_SOUND; + sSoundBanks[bank][sCurrentSound[bank][i]].soundStatus = SOUND_STATUS_STOPPED; + delete_sound_from_bank(bank, sCurrentSound[bank][i]); + } + // If the sound is continuous and is playing, then stop playing it but don't delete + // it. (A continuous sound shouldn't be deleted until it stops being requested) + else { + if (isDiscreteAndStatus == SOUND_STATUS_PLAYING + && sSoundBanks[bank][sCurrentSound[bank][i]].soundStatus + != SOUND_STATUS_STOPPED) { + sSoundBanks[bank][sCurrentSound[bank][i]].soundStatus = SOUND_STATUS_WAITING; + } + } + } + sCurrentSound[bank][i] = 0xff; + } + } + + // + // Start playing the remaining sounds from liveSoundIndices. + // + for (soundIndex = 0; soundIndex < sUsedChannelsForSoundBank[bank]; soundIndex++) { + if (liveSoundIndices[soundIndex] != 0xff) { + for (i = 0; i < sUsedChannelsForSoundBank[bank]; i++) { + if (sCurrentSound[bank][i] == 0xff) { + sCurrentSound[bank][i] = liveSoundIndices[soundIndex]; + + // Set (soundBits & status) to WAITING (soundStatus will be updated + // shortly after in update_game_sound) + sSoundBanks[bank][liveSoundIndices[soundIndex]].soundBits = + (sSoundBanks[bank][liveSoundIndices[soundIndex]].soundBits + & ~SOUNDARGS_MASK_STATUS) + + SOUND_STATUS_WAITING; + + liveSoundIndices[i] = 0xff; // doesn't do anything + i = 0xfe; // break + } + } + } + } +} + +/** + * Given x and z coordinates, return the pan. This is a value nominally between + * 0 and 1 that represents the audio direction. + * + * Pan: + * 0.0 - fully left + * 0.5 - center pan + * 1.0 - fully right + * + * Called from threads: thread4_sound, thread5_game_loop (EU only) + */ +static f32 get_sound_pan(f32 x, f32 z) { + f32 absX; + f32 absZ; + f32 pan; + + absX = (x < 0 ? -x : x); + if (absX > AUDIO_MAX_DISTANCE) { + absX = AUDIO_MAX_DISTANCE; + } + + absZ = (z < 0 ? -z : z); + if (absZ > AUDIO_MAX_DISTANCE) { + absZ = AUDIO_MAX_DISTANCE; + } + + // There are 4 panning equations (12-hr clock used for angles) + // 1. (0,0) fully-centered pan + // 2. far right pan: between 1:30 and 4:30 + // 3. far left pan: between 7:30 and 10:30 + // 4. center pan: between 4:30 and 7:30 or between 10:30 and 1:30 + if (x == US_FLOAT(0.0) && z == US_FLOAT(0.0)) { + // x and z being 0 results in a center pan + pan = US_FLOAT(0.5); + } else if (x >= US_FLOAT(0.0) && absX >= absZ) { + // far right pan + pan = US_FLOAT(1.0) - (2 * AUDIO_MAX_DISTANCE - absX) / (US_FLOAT(3.0) * (2 * AUDIO_MAX_DISTANCE - absZ)); + } else if (x < 0 && absX > absZ) { + // far left pan + pan = (2 * AUDIO_MAX_DISTANCE - absX) / (US_FLOAT(3.0) * (2 * AUDIO_MAX_DISTANCE - absZ)); + } else { + // center pan + //! @bug (JP PU sound glitch) If |x|, |z| > AUDIO_MAX_DISTANCE, we'll + // end up in this case, and pan may be set to something outside of [0,1] + // since x is not clamped. On JP, this can lead to an out-of-bounds + // float read in note_set_vel_pan_reverb when x is highly negative, + // causing console crashes when that float is a nan or denormal. + pan = 0.5 + x / (US_FLOAT(6.0) * absZ); + } + + return pan; +} + +/** + * Called from threads: thread4_sound, thread5_game_loop (EU only) + */ +static f32 get_sound_volume(u8 bank, u8 soundIndex, f32 volumeRange) { + f32 maxSoundDistance; + f32 intensity; +#ifndef VERSION_JP + s32 div = bank < 3 ? 2 : 3; +#endif + + if (!(sSoundBanks[bank][soundIndex].soundBits & SOUND_NO_VOLUME_LOSS)) { +#ifdef VERSION_JP + // Intensity linearly lowers from 1 at the camera to 0 at maxSoundDistance + maxSoundDistance = sLevelAcousticReaches[gCurrLevelNum]; + if (maxSoundDistance < sSoundBanks[bank][soundIndex].distance) { + intensity = 0.0f; + } else { + intensity = 1.0 - sSoundBanks[bank][soundIndex].distance / maxSoundDistance; + } +#else + // Intensity linearly lowers from 1 at the camera to 1 - volumeRange at maxSoundDistance, + // then it goes from 1 - volumeRange at maxSoundDistance to 0 at AUDIO_MAX_DISTANCE + if (sSoundBanks[bank][soundIndex].distance > AUDIO_MAX_DISTANCE) { + intensity = 0.0f; + } else { + maxSoundDistance = sLevelAcousticReaches[gCurrLevelNum] / div; + if (maxSoundDistance < sSoundBanks[bank][soundIndex].distance) { + intensity = ((AUDIO_MAX_DISTANCE - sSoundBanks[bank][soundIndex].distance) + / (AUDIO_MAX_DISTANCE - maxSoundDistance)) + * (1.0f - volumeRange); + } else { + intensity = + 1.0f - sSoundBanks[bank][soundIndex].distance / maxSoundDistance * volumeRange; + } + } +#endif + + if (sSoundBanks[bank][soundIndex].soundBits & SOUND_VIBRATO) { +#ifdef VERSION_JP + //! @bug Intensity is 0 when the sound is far away. Due to the subtraction below, it is possible to end up with a negative intensity. + // When it is, objects with a volumeRange of 1 can still occasionally be lightly heard. + if (intensity != 0.0) +#else + if (intensity >= 0.08f) +#endif + { + intensity -= (f32)(gAudioRandom & 0xf) / US_FLOAT(192.0); + } + } + } else { + intensity = 1.0f; + } + + // Rise quadratically from 1 - volumeRange to 1 + return (volumeRange * intensity * intensity + 1.0f - volumeRange) * gAudioVolume; +} + +/** + * Called from threads: thread4_sound, thread5_game_loop (EU only) + */ +static f32 get_sound_freq_scale(u8 bank, u8 item) { + f32 amount; + + if (!(sSoundBanks[bank][item].soundBits & SOUND_CONSTANT_FREQUENCY)) { + amount = sSoundBanks[bank][item].distance / AUDIO_MAX_DISTANCE; + if (sSoundBanks[bank][item].soundBits & SOUND_VIBRATO) { + amount += (f32)(gAudioRandom & 0xff) / US_FLOAT(64.0); + } + } else { + amount = 0.0f; + } + + // Goes from 1 at the camera to 1 + 1/15 at AUDIO_MAX_DISTANCE (and continues rising + // farther than that) + return amount / US_FLOAT(15.0) + US_FLOAT(1.0); +} + +/** + * Called from threads: thread4_sound, thread5_game_loop (EU only) + */ +static u8 get_sound_reverb(UNUSED u8 bank, UNUSED u8 soundIndex, u8 channelIndex) { + u8 area; + u8 level; + u8 reverb; + +#ifndef VERSION_JP + // Disable level reverb if NO_ECHO is set + if (sSoundBanks[bank][soundIndex].soundBits & SOUND_NO_ECHO) { + level = 0; + area = 0; + } else { +#endif + level = (gCurrLevelNum > LEVEL_MAX ? LEVEL_MAX : gCurrLevelNum); + area = gCurrAreaIndex - 1; + if (area > 2) { + area = 2; + } +#ifndef VERSION_JP + } +#endif + + // reverb = reverb adjustment + level reverb + a volume-dependent value + // The volume-dependent value is 0 when volume is at maximum, and raises to + // LOW_VOLUME_REVERB when the volume is 0 + /*reverb = (u8)((u8) gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->soundScriptIO[5] + + sLevelAreaReverbs[level][area] + + (US_FLOAT(1.0) - gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->volume) + * LOW_VOLUME_REVERB);*/ + + reverb = gAudioReverb; + + if (reverb > 0x7f) { + reverb = 0x7f; + } + return reverb; +} + +static void noop_8031EEC8(void) { +} + +/** + * Called from the game loop thread to inform the audio thread that a new game + * frame has started. + * + * Called from threads: thread5_game_loop + */ +void audio_signal_game_loop_tick(void) { + sGameLoopTicked = 1; +#if defined(VERSION_EU) || defined(VERSION_SH) + maybe_tick_game_sound(); +#endif + noop_8031EEC8(); +} + +/** + * Called from threads: thread4_sound, thread5_game_loop (EU and SH only) + */ +void update_game_sound(void) { + u8 soundStatus; + u8 i; + u8 soundId; + u8 bank; + u8 channelIndex = 0; + u8 soundIndex; +#if defined(VERSION_JP) || defined(VERSION_US) + f32 value; +#endif + + process_all_sound_requests(); + process_level_music_dynamics(); + + if (gSequencePlayers[SEQ_PLAYER_SFX].channels[0] == &gSequenceChannelNone) { + return; + } + + for (bank = 0; bank < SOUND_BANK_COUNT; bank++) { + select_current_sounds(bank); + + for (i = 0; i < MAX_CHANNELS_PER_SOUND_BANK; i++) { + soundIndex = sCurrentSound[bank][i]; + + if (soundIndex < 0xff + && sSoundBanks[bank][soundIndex].soundStatus != SOUND_STATUS_STOPPED) { + soundStatus = sSoundBanks[bank][soundIndex].soundBits & SOUNDARGS_MASK_STATUS; + soundId = (sSoundBanks[bank][soundIndex].soundBits >> SOUNDARGS_SHIFT_SOUNDID); + + sSoundBanks[bank][soundIndex].soundStatus = soundStatus; + + if (soundStatus == SOUND_STATUS_WAITING) { + if (sSoundBanks[bank][soundIndex].soundBits & SOUND_LOWER_BACKGROUND_MUSIC) { + sSoundBanksThatLowerBackgroundMusic |= 1 << bank; + begin_background_music_fade(50); + } + + // Set sound status to PLAYING + sSoundBanks[bank][soundIndex].soundBits++; + sSoundBanks[bank][soundIndex].soundStatus = SOUND_STATUS_PLAYING; + + // Begin playing the sound + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->soundScriptIO[4] = soundId; + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->soundScriptIO[0] = 1; + + switch (bank) { + case SOUND_BANK_MOVING: + if (!(sSoundBanks[bank][soundIndex].soundBits & SOUND_CONSTANT_FREQUENCY)) { + if (sSoundMovingSpeed[bank] > 8) { +#if defined(VERSION_EU) || defined(VERSION_SH) + func_802ad728( + 0x02020000 | ((channelIndex & 0xff) << 8), + get_sound_volume(bank, soundIndex, VOLUME_RANGE_UNK1)); +#else + value = get_sound_volume(bank, soundIndex, VOLUME_RANGE_UNK1); + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->volume = + value; +#endif + } else { +#if defined(VERSION_EU) || defined(VERSION_SH) + func_802ad728(0x02020000 | ((channelIndex & 0xff) << 8), + get_sound_volume(bank, soundIndex, VOLUME_RANGE_UNK1) + * ((sSoundMovingSpeed[bank] + 8.0f) / 16)); +#else + value = get_sound_volume(bank, soundIndex, VOLUME_RANGE_UNK1); + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->volume = + (sSoundMovingSpeed[bank] + 8.0f) / 16 * value; +#endif + } +#if defined(VERSION_EU) || defined(VERSION_SH) + func_802ad770(0x03020000 | ((channelIndex & 0xff) << 8), + get_sound_pan(*sSoundBanks[bank][soundIndex].x, + *sSoundBanks[bank][soundIndex].z)); +#else + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->pan = + get_sound_pan(*sSoundBanks[bank][soundIndex].x, + *sSoundBanks[bank][soundIndex].z); +#endif + + if ((sSoundBanks[bank][soundIndex].soundBits & SOUNDARGS_MASK_SOUNDID) + == (SOUND_MOVING_FLYING & SOUNDARGS_MASK_SOUNDID)) { +#if defined(VERSION_EU) || defined(VERSION_SH) + func_802ad728( + 0x04020000 | ((channelIndex & 0xff) << 8), + get_sound_freq_scale(bank, soundIndex) + + ((f32) sSoundMovingSpeed[bank] / US_FLOAT(80.0))); +#else + value = get_sound_freq_scale(bank, soundIndex); + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->freqScale = + ((f32) sSoundMovingSpeed[bank] / US_FLOAT(80.0)) + value; +#endif + } else { +#if defined(VERSION_EU) || defined(VERSION_SH) + func_802ad728( + 0x04020000 | ((channelIndex & 0xff) << 8), + get_sound_freq_scale(bank, soundIndex) + + ((f32) sSoundMovingSpeed[bank] / US_FLOAT(400.0))); +#else + value = get_sound_freq_scale(bank, soundIndex); + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->freqScale = + ((f32) sSoundMovingSpeed[bank] / US_FLOAT(400.0)) + value; +#endif + } +#if defined(VERSION_EU) || defined(VERSION_SH) + func_802ad770(0x05020000 | ((channelIndex & 0xff) << 8), + get_sound_reverb(bank, soundIndex, channelIndex)); +#else + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->reverbVol = + get_sound_reverb(bank, soundIndex, channelIndex); +#endif + + break; + } + // fallthrough + case SOUND_BANK_MENU: +#if defined(VERSION_EU) || defined(VERSION_SH) + func_802ad728(0x02020000 | ((channelIndex & 0xff) << 8), 1); + func_802ad770(0x03020000 | ((channelIndex & 0xff) << 8), 64); + func_802ad728(0x04020000 | ((channelIndex & 0xff) << 8), + get_sound_freq_scale(bank, soundIndex)); +#else + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->volume = 1.0f; + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->pan = 0.5f; + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->freqScale = 1.0f; +#endif + break; + case SOUND_BANK_ACTION: + case SOUND_BANK_VOICE: +#if defined(VERSION_EU) || defined(VERSION_SH) + func_802ad770(0x05020000 | ((channelIndex & 0xff) << 8), + get_sound_reverb(bank, soundIndex, channelIndex)); + func_802ad728(0x02020000 | ((channelIndex & 0xff) << 8), + get_sound_volume(bank, soundIndex, VOLUME_RANGE_UNK1)); + func_802ad770(0x03020000 | ((channelIndex & 0xff) << 8), + get_sound_pan(*sSoundBanks[bank][soundIndex].x, + *sSoundBanks[bank][soundIndex].z) + * 127.0f + + 0.5f); + func_802ad728(0x04020000 | ((channelIndex & 0xff) << 8), + get_sound_freq_scale(bank, soundIndex)); +#else + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->volume = + get_sound_volume(bank, soundIndex, VOLUME_RANGE_UNK1); + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->pan = + get_sound_pan(*sSoundBanks[bank][soundIndex].x, + *sSoundBanks[bank][soundIndex].z); + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->freqScale = + get_sound_freq_scale(bank, soundIndex); + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->reverbVol = + get_sound_reverb(bank, soundIndex, channelIndex); +#endif + break; + case SOUND_BANK_GENERAL: + case SOUND_BANK_ENV: + case SOUND_BANK_OBJ: + case SOUND_BANK_AIR: + case SOUND_BANK_GENERAL2: + case SOUND_BANK_OBJ2: +#if defined(VERSION_EU) || defined(VERSION_SH) + func_802ad770(0x05020000 | ((channelIndex & 0xff) << 8), + get_sound_reverb(bank, soundIndex, channelIndex)); + func_802ad728(0x02020000 | ((channelIndex & 0xff) << 8), + get_sound_volume(bank, soundIndex, VOLUME_RANGE_UNK2)); + func_802ad770(0x03020000 | ((channelIndex & 0xff) << 8), + get_sound_pan(*sSoundBanks[bank][soundIndex].x, + *sSoundBanks[bank][soundIndex].z) + * 127.0f + + 0.5f); + func_802ad728(0x04020000 | ((channelIndex & 0xff) << 8), + get_sound_freq_scale(bank, soundIndex)); +#else + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->reverbVol = + get_sound_reverb(bank, soundIndex, channelIndex); + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->volume = + get_sound_volume(bank, soundIndex, VOLUME_RANGE_UNK2); + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->pan = + get_sound_pan(*sSoundBanks[bank][soundIndex].x, + *sSoundBanks[bank][soundIndex].z); + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->freqScale = + get_sound_freq_scale(bank, soundIndex); +#endif + break; + } + } +#ifdef VERSION_JP + // If the sound was marked for deletion (bits set to NO_SOUND), then stop playing it + // and delete it + // @bug (JP double red coin sound) If the sound finished within the same frame as + // being marked for deletion, the signal to stop playing will be interpreted as a + // signal to *start* playing, as .main_loop_023589 in 00_sound_player does not check + // for soundScriptIO[0] being zero. This happens most commonly for red coin sounds + // whose sound spawners deactivate 30 frames after the sound starts to play, while + // the sound itself runs for 1.20 seconds. With enough lag these may coincide. + // Fixed on US by checking that layer0->finished is FALSE. + else if (soundStatus == SOUND_STATUS_STOPPED) { + update_background_music_after_sound(bank, soundIndex); + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->soundScriptIO[0] = 0; + delete_sound_from_bank(bank, soundIndex); + } +#else + else if (gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->layers[0] == NULL) { + update_background_music_after_sound(bank, soundIndex); + sSoundBanks[bank][soundIndex].soundStatus = SOUND_STATUS_STOPPED; + delete_sound_from_bank(bank, soundIndex); + } else if (soundStatus == SOUND_STATUS_STOPPED + && gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex] + ->layers[0]->finished == FALSE) { + update_background_music_after_sound(bank, soundIndex); + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->soundScriptIO[0] = 0; + delete_sound_from_bank(bank, soundIndex); + } +#endif + // If sound has finished playing, then delete it + // @bug (JP sound glitch) On JP, ...->layers[0] has not been checked for null, + // so this access can crash if an earlier layer allocation failed due to too + // many sounds playing at once. This crash is comparatively common; RTA + // speedrunners even have a setup for avoiding it within the SSL pyramid: + // https://www.youtube.com/watch?v=QetyTgbQxcw + else if (gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->layers[0]->enabled + == FALSE) { + update_background_music_after_sound(bank, soundIndex); + sSoundBanks[bank][soundIndex].soundStatus = SOUND_STATUS_STOPPED; + delete_sound_from_bank(bank, soundIndex); + } else { + // Exactly the same code as before. Unfortunately we can't + // make a macro out of this, because then everything ends up + // on the same line after preprocessing, and the compiler, + // somehow caring about line numbers, makes it not match (it + // computes function arguments in the wrong order). + switch (bank) { + case SOUND_BANK_MOVING: + if (!(sSoundBanks[bank][soundIndex].soundBits & SOUND_CONSTANT_FREQUENCY)) { + if (sSoundMovingSpeed[bank] > 8) { +#if defined(VERSION_EU) || defined(VERSION_SH) + func_802ad728( + 0x02020000 | ((channelIndex & 0xff) << 8), + get_sound_volume(bank, soundIndex, VOLUME_RANGE_UNK1)); +#else + value = get_sound_volume(bank, soundIndex, VOLUME_RANGE_UNK1); + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->volume = + value; +#endif + } else { +#if defined(VERSION_EU) || defined(VERSION_SH) + func_802ad728(0x02020000 | ((channelIndex & 0xff) << 8), + get_sound_volume(bank, soundIndex, VOLUME_RANGE_UNK1) + * ((sSoundMovingSpeed[bank] + 8.0f) / 16)); +#else + value = get_sound_volume(bank, soundIndex, VOLUME_RANGE_UNK1); + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->volume = + (sSoundMovingSpeed[bank] + 8.0f) / 16 * value; +#endif + } +#if defined(VERSION_EU) || defined(VERSION_SH) + func_802ad770(0x03020000 | ((channelIndex & 0xff) << 8), + get_sound_pan(*sSoundBanks[bank][soundIndex].x, + *sSoundBanks[bank][soundIndex].z)); +#else + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->pan = + get_sound_pan(*sSoundBanks[bank][soundIndex].x, + *sSoundBanks[bank][soundIndex].z); +#endif + + if ((sSoundBanks[bank][soundIndex].soundBits & SOUNDARGS_MASK_SOUNDID) + == (SOUND_MOVING_FLYING & SOUNDARGS_MASK_SOUNDID)) { +#if defined(VERSION_EU) || defined(VERSION_SH) + func_802ad728( + 0x04020000 | ((channelIndex & 0xff) << 8), + get_sound_freq_scale(bank, soundIndex) + + ((f32) sSoundMovingSpeed[bank] / US_FLOAT(80.0))); +#else + value = get_sound_freq_scale(bank, soundIndex); + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->freqScale = + ((f32) sSoundMovingSpeed[bank] / US_FLOAT(80.0)) + value; +#endif + } else { +#if defined(VERSION_EU) || defined(VERSION_SH) + func_802ad728( + 0x04020000 | ((channelIndex & 0xff) << 8), + get_sound_freq_scale(bank, soundIndex) + + ((f32) sSoundMovingSpeed[bank] / US_FLOAT(400.0))); +#else + value = get_sound_freq_scale(bank, soundIndex); + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->freqScale = + ((f32) sSoundMovingSpeed[bank] / US_FLOAT(400.0)) + value; +#endif + } +#if defined(VERSION_EU) || defined(VERSION_SH) + func_802ad770(0x05020000 | ((channelIndex & 0xff) << 8), + get_sound_reverb(bank, soundIndex, channelIndex)); +#else + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->reverbVol = + get_sound_reverb(bank, soundIndex, channelIndex); +#endif + + break; + } + // fallthrough + case SOUND_BANK_MENU: +#if defined(VERSION_EU) || defined(VERSION_SH) + func_802ad728(0x02020000 | ((channelIndex & 0xff) << 8), 1); + func_802ad770(0x03020000 | ((channelIndex & 0xff) << 8), 64); + func_802ad728(0x04020000 | ((channelIndex & 0xff) << 8), + get_sound_freq_scale(bank, soundIndex)); +#else + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->volume = 1.0f; + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->pan = 0.5f; + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->freqScale = 1.0f; +#endif + break; + case SOUND_BANK_ACTION: + case SOUND_BANK_VOICE: +#if defined(VERSION_EU) || defined(VERSION_SH) + func_802ad770(0x05020000 | ((channelIndex & 0xff) << 8), + get_sound_reverb(bank, soundIndex, channelIndex)); + func_802ad728(0x02020000 | ((channelIndex & 0xff) << 8), + get_sound_volume(bank, soundIndex, VOLUME_RANGE_UNK1)); + func_802ad770(0x03020000 | ((channelIndex & 0xff) << 8), + get_sound_pan(*sSoundBanks[bank][soundIndex].x, + *sSoundBanks[bank][soundIndex].z) + * 127.0f + + 0.5f); + func_802ad728(0x04020000 | ((channelIndex & 0xff) << 8), + get_sound_freq_scale(bank, soundIndex)); +#else + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->volume = + get_sound_volume(bank, soundIndex, VOLUME_RANGE_UNK1); + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->pan = + get_sound_pan(*sSoundBanks[bank][soundIndex].x, + *sSoundBanks[bank][soundIndex].z); + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->freqScale = + get_sound_freq_scale(bank, soundIndex); + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->reverbVol = + get_sound_reverb(bank, soundIndex, channelIndex); +#endif + break; + case SOUND_BANK_GENERAL: + case SOUND_BANK_ENV: + case SOUND_BANK_OBJ: + case SOUND_BANK_AIR: + case SOUND_BANK_GENERAL2: + case SOUND_BANK_OBJ2: +#if defined(VERSION_EU) || defined(VERSION_SH) + func_802ad770(0x05020000 | ((channelIndex & 0xff) << 8), + get_sound_reverb(bank, soundIndex, channelIndex)); + func_802ad728(0x02020000 | ((channelIndex & 0xff) << 8), + get_sound_volume(bank, soundIndex, VOLUME_RANGE_UNK2)); + func_802ad770(0x03020000 | ((channelIndex & 0xff) << 8), + get_sound_pan(*sSoundBanks[bank][soundIndex].x, + *sSoundBanks[bank][soundIndex].z) + * 127.0f + + 0.5f); + func_802ad728(0x04020000 | ((channelIndex & 0xff) << 8), + get_sound_freq_scale(bank, soundIndex)); +#else + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->reverbVol = + get_sound_reverb(bank, soundIndex, channelIndex); + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->volume = + get_sound_volume(bank, soundIndex, VOLUME_RANGE_UNK2); + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->pan = + get_sound_pan(*sSoundBanks[bank][soundIndex].x, + *sSoundBanks[bank][soundIndex].z); + gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->freqScale = + get_sound_freq_scale(bank, soundIndex); +#endif + break; + } + } + } + + // Increment to the next channel that this bank owns + channelIndex++; + } + + // Increment to the first channel index of the next bank + // (In practice sUsedChannelsForSoundBank[i] = sMaxChannelsForSoundBank[i] = 1, so this + // doesn't do anything) + channelIndex += sMaxChannelsForSoundBank[bank] - sUsedChannelsForSoundBank[bank]; + } +} + +/** + * Called from threads: thread4_sound, thread5_game_loop + */ +void seq_player_play_sequence(u8 player, u8 seqId, u16 arg2) { + u8 targetVolume; + u8 i; + + if (player == SEQ_PLAYER_LEVEL) { + sCurrentBackgroundMusicSeqId = seqId & SEQ_BASE_ID; + sBackgroundMusicForDynamics = SEQUENCE_NONE; + sCurrentMusicDynamic = 0xff; + sMusicDynamicDelay = 2; + } + + for (i = 0; i < CHANNELS_MAX; i++) { + D_80360928[player][i].remainingFrames = 0; + } + +#if defined(VERSION_EU) || defined(VERSION_SH) + func_802ad770(0x46000000 | ((u8)(u32) player) << 16, seqId & SEQ_VARIATION); + func_802ad74c(0x82000000 | ((u8)(u32) player) << 16 | ((u8)(seqId & SEQ_BASE_ID)) << 8, arg2); + + if (player == SEQ_PLAYER_LEVEL) { + targetVolume = begin_background_music_fade(0); + if (targetVolume != 0xff) { + gSequencePlayers[SEQ_PLAYER_LEVEL].fadeVolumeScale = (f32) targetVolume / US_FLOAT(127.0); + } + } +#else + + gSequencePlayers[player].seqVariation = seqId & SEQ_VARIATION; + load_sequence(player, seqId & SEQ_BASE_ID, 0); + + if (player == SEQ_PLAYER_LEVEL) { + targetVolume = begin_background_music_fade(0); + if (targetVolume != 0xff) { + gSequencePlayers[SEQ_PLAYER_LEVEL].state = SEQUENCE_PLAYER_STATE_4; + gSequencePlayers[SEQ_PLAYER_LEVEL].fadeVolume = (f32) targetVolume / US_FLOAT(127.0); + } + } else { + func_8031D690(player, arg2); + } +#endif +} + +/** + * Called from threads: thread5_game_loop + */ +void seq_player_fade_out(u8 player, u16 fadeDuration) { +#if defined(VERSION_EU) || defined(VERSION_SH) +#ifdef VERSION_EU + u32 fd = fadeDuration; +#else + s32 fd = fadeDuration; // will also match if we change function signature func_802ad74c to use s32 as arg1 +#endif + if (!player) { + sCurrentBackgroundMusicSeqId = SEQUENCE_NONE; + } + func_802ad74c(0x83000000 | (player & 0xff) << 16, fd); +#else + if (player == SEQ_PLAYER_LEVEL) { + sCurrentBackgroundMusicSeqId = SEQUENCE_NONE; + } + seq_player_fade_to_zero_volume(player, fadeDuration); +#endif +} + +/** + * Called from threads: thread5_game_loop + */ +void fade_volume_scale(u8 player, u8 targetScale, u16 fadeDuration) { + u8 i; + for (i = 0; i < CHANNELS_MAX; i++) { + fade_channel_volume_scale(player, i, targetScale, fadeDuration); + } +} + +/** + * Called from threads: thread3_main, thread4_sound, thread5_game_loop + */ +static void fade_channel_volume_scale(u8 player, u8 channelIndex, u8 targetScale, u16 fadeDuration) { + struct ChannelVolumeScaleFade *temp; + + if (gSequencePlayers[player].channels[channelIndex] != &gSequenceChannelNone) { + temp = &D_80360928[player][channelIndex]; + temp->remainingFrames = fadeDuration; + temp->velocity = ((f32)(targetScale / US_FLOAT(127.0)) + - gSequencePlayers[player].channels[channelIndex]->volumeScale) + / fadeDuration; + temp->target = targetScale; + temp->current = gSequencePlayers[player].channels[channelIndex]->volumeScale; + } +} + +/** + * Called from threads: thread4_sound, thread5_game_loop (EU only) + */ +static void func_8031F96C(u8 player) { + u8 i; + + // Loop over channels + for (i = 0; i < CHANNELS_MAX; i++) { + if (gSequencePlayers[player].channels[i] != &gSequenceChannelNone + && D_80360928[player][i].remainingFrames != 0) { + D_80360928[player][i].current += D_80360928[player][i].velocity; +#if defined(VERSION_EU) || defined(VERSION_SH) + func_802ad728(0x01000000 | (player & 0xff) << 16 | (i & 0xff) << 8, + D_80360928[player][i].current); +#else + gSequencePlayers[player].channels[i]->volumeScale = D_80360928[player][i].current; +#endif + D_80360928[player][i].remainingFrames--; + if (D_80360928[player][i].remainingFrames == 0) { +#if defined(VERSION_EU) + func_802ad728(0x01000000 | (player & 0xff) << 16 | (i & 0xff) << 8, + FLOAT_CAST(D_80360928[player][i].target) / 127.0); +#elif defined(VERSION_SH) + func_802ad728(0x01000000 | (player & 0xff) << 16 | (i & 0xff) << 8, + FLOAT_CAST(D_80360928[player][i].target) / 127.0f); +#else + gSequencePlayers[player].channels[i]->volumeScale = + D_80360928[player][i].target / 127.0f; +#endif + } + } + } +} + +/** + * Called from threads: thread4_sound, thread5_game_loop (EU only) + */ +void process_level_music_dynamics(void) { + u32 conditionBits; + u16 tempBits; + UNUSED u16 pad; + u8 musicDynIndex; + u8 condIndex; + u8 i; + u8 j; + s16 conditionValues[8]; + u8 conditionTypes[8]; + s16 dur1; + s16 dur2; + u16 bit; + + func_8031F96C(0); + func_8031F96C(2); + func_80320ED8(); + if (sMusicDynamicDelay != 0) { + sMusicDynamicDelay--; + } else { + sBackgroundMusicForDynamics = sCurrentBackgroundMusicSeqId; + } + + if (sBackgroundMusicForDynamics != sLevelDynamics[gCurrLevelNum][0]) { + return; + } + + conditionBits = sLevelDynamics[gCurrLevelNum][1] & 0xff00; + musicDynIndex = (u8) sLevelDynamics[gCurrLevelNum][1] & 0xff; + i = 2; + while (conditionBits & 0xff00) { + j = 0; + condIndex = 0; + bit = 0x8000; + while (j < 8) { + if (conditionBits & bit) { + conditionValues[condIndex] = sLevelDynamics[gCurrLevelNum][i++]; + conditionTypes[condIndex] = j; + condIndex++; + } + + j++; + bit = bit >> 1; + } + + for (j = 0; j < condIndex; j++) { + switch (conditionTypes[j]) { + case MARIO_IS_IN_AREA: { + if (gCurrAreaIndex != conditionValues[j]) { + j = condIndex + 1; + } + break; + } + case MARIO_IS_IN_ROOM: { + if (gMarioCurrentRoom != conditionValues[j]) { + j = condIndex + 1; + } + break; + } + } + } + + if (j == condIndex) { + // The area matches. Break out of the loop. + tempBits = 0; + } else { + tempBits = sLevelDynamics[gCurrLevelNum][i] & 0xff00; + musicDynIndex = sLevelDynamics[gCurrLevelNum][i] & 0xff; + i++; + } + + conditionBits = tempBits; + } + + if (sCurrentMusicDynamic != musicDynIndex) { + tempBits = 1; + if (sCurrentMusicDynamic == 0xff) { + dur1 = 1; + dur2 = 1; + } else { + dur1 = sMusicDynamics[musicDynIndex].dur1; + dur2 = sMusicDynamics[musicDynIndex].dur2; + } + + for (i = 0; i < CHANNELS_MAX; i++) { + conditionBits = tempBits; + tempBits = 0; + if (sMusicDynamics[musicDynIndex].bits1 & conditionBits) { + fade_channel_volume_scale(SEQ_PLAYER_LEVEL, i, sMusicDynamics[musicDynIndex].volScale1, + dur1); + } + if (sMusicDynamics[musicDynIndex].bits2 & conditionBits) { + fade_channel_volume_scale(SEQ_PLAYER_LEVEL, i, sMusicDynamics[musicDynIndex].volScale2, + dur2); + } + tempBits = conditionBits << 1; + } + + sCurrentMusicDynamic = musicDynIndex; + } +} + +void unused_8031FED0(u8 player, u32 bits, s8 arg2) { + u8 i; + + if (arg2 < 0) { + arg2 = -arg2; + } + + for (i = 0; i < CHANNELS_MAX; i++) { + if (gSequencePlayers[player].channels[i] != &gSequenceChannelNone) { + if ((bits & 3) == 0) { + gSequencePlayers[player].channels[i]->volumeScale = 1.0f; + } else if ((bits & 1) != 0) { + gSequencePlayers[player].channels[i]->volumeScale = (f32) arg2 / US_FLOAT(127.0); + } else { + gSequencePlayers[player].channels[i]->volumeScale = + US_FLOAT(1.0) - (f32) arg2 / US_FLOAT(127.0); + } + } + bits >>= 2; + } +} + +/** + * Lower a sequence player's volume over fadeDuration frames. + * If player is SEQ_PLAYER_LEVEL (background music), the given percentage is ignored + * and a max target volume of 40 is used. + * + * Called from threads: thread5_game_loop + */ +void seq_player_lower_volume(u8 player, u16 fadeDuration, u8 percentage) { + if (player == SEQ_PLAYER_LEVEL) { + sLowerBackgroundMusicVolume = TRUE; + begin_background_music_fade(fadeDuration); + } else if (gSequencePlayers[player].enabled == TRUE) { + seq_player_fade_to_percentage_of_volume(player, fadeDuration, percentage); + } +} + +/** + * Remove the lowered volume constraint set by seq_player_lower_volume. + * If player is SEQ_PLAYER_LEVEL (background music), the music won't necessarily + * raise back to normal volume if other constraints have been set, e.g. + * sBackgroundMusicTargetVolume. + * + * Called from threads: thread5_game_loop + */ +void seq_player_unlower_volume(u8 player, u16 fadeDuration) { + sLowerBackgroundMusicVolume = FALSE; + if (player == SEQ_PLAYER_LEVEL) { + if (gSequencePlayers[player].state != SEQUENCE_PLAYER_STATE_FADE_OUT) { + begin_background_music_fade(fadeDuration); + } + } else { + if (gSequencePlayers[player].enabled == TRUE) { + seq_player_fade_to_normal_volume(player, fadeDuration); + } + } +} + +/** + * Begin a volume fade to adjust the background music to the correct volume. + * The target volume is determined by global variables like sBackgroundMusicTargetVolume + * and sLowerBackgroundMusicVolume. + * If none of the relevant global variables are set, then the default background music + * volume for the sequence is used. + * + * Called from threads: thread3_main, thread4_sound, thread5_game_loop + */ +static u8 begin_background_music_fade(u16 fadeDuration) { + u8 targetVolume = 0xff; + + if (sCurrentBackgroundMusicSeqId == SEQUENCE_NONE + || sCurrentBackgroundMusicSeqId == SEQ_EVENT_CUTSCENE_CREDITS) { + return 0xff; + } + + if (gSequencePlayers[SEQ_PLAYER_LEVEL].volume == 0.0f && fadeDuration) { + gSequencePlayers[SEQ_PLAYER_LEVEL].volume = gSequencePlayers[SEQ_PLAYER_LEVEL].fadeVolume; + } + + if (sBackgroundMusicTargetVolume != TARGET_VOLUME_UNSET) { + targetVolume = (sBackgroundMusicTargetVolume & TARGET_VOLUME_VALUE_MASK); + } + + if (sBackgroundMusicMaxTargetVolume != TARGET_VOLUME_UNSET) { + u8 maxTargetVolume = (sBackgroundMusicMaxTargetVolume & TARGET_VOLUME_VALUE_MASK); + if (targetVolume > maxTargetVolume) { + targetVolume = maxTargetVolume; + } + } + + if (sLowerBackgroundMusicVolume && targetVolume > 40) { + targetVolume = 40; + } + + if (sSoundBanksThatLowerBackgroundMusic != 0 && targetVolume > 20) { + targetVolume = 20; + } + + if (gSequencePlayers[SEQ_PLAYER_LEVEL].enabled == TRUE) { + if (targetVolume != 0xff) { + seq_player_fade_to_target_volume(SEQ_PLAYER_LEVEL, fadeDuration, targetVolume); + } else { +#if defined(VERSION_JP) || defined(VERSION_US) + gSequencePlayers[SEQ_PLAYER_LEVEL].volume = + sBackgroundMusicDefaultVolume[sCurrentBackgroundMusicSeqId] / 127.0f; +#endif + seq_player_fade_to_normal_volume(SEQ_PLAYER_LEVEL, fadeDuration); + } + } + + return targetVolume; +} + +/** + * Called from threads: thread5_game_loop + */ +void set_audio_muted(u8 muted) { + u8 i; + + for (i = 0; i < SEQUENCE_PLAYERS; i++) { +#if defined(VERSION_EU) || defined(VERSION_SH) + if (muted) + func_802ad74c(0xf1000000, 0); + else + func_802ad74c(0xf2000000, 0); +#else + gSequencePlayers[i].muted = muted; +#endif + } +} + +/** + * Called from threads: thread4_sound + */ +void sound_init(void) { + u8 i; + u8 j; + + for (i = 0; i < SOUND_BANK_COUNT; i++) { + // Set each sound in the bank to STOPPED + for (j = 0; j < 40; j++) { + sSoundBanks[i][j].soundStatus = SOUND_STATUS_STOPPED; + } + + // Remove current sounds + for (j = 0; j < MAX_CHANNELS_PER_SOUND_BANK; j++) { + sCurrentSound[i][j] = 0xff; + } + + sSoundBankUsedListBack[i] = 0; + sSoundBankFreeListFront[i] = 1; + sNumSoundsInBank[i] = 0; + } + + for (i = 0; i < SOUND_BANK_COUNT; i++) { + // Set used list to empty + sSoundBanks[i][0].prev = 0xff; + sSoundBanks[i][0].next = 0xff; + + // Set free list to contain every sound slot + for (j = 1; j < 40 - 1; j++) { + sSoundBanks[i][j].prev = j - 1; + sSoundBanks[i][j].next = j + 1; + } + sSoundBanks[i][j].prev = j - 1; + sSoundBanks[i][j].next = 0xff; + } + + for (j = 0; j < 3; j++) { + for (i = 0; i < CHANNELS_MAX; i++) { + D_80360928[j][i].remainingFrames = 0; + } + } + + for (i = 0; i < MAX_BACKGROUND_MUSIC_QUEUE_SIZE; i++) { + sBackgroundMusicQueue[i].priority = 0; + } + + sound_banks_enable(SEQ_PLAYER_SFX, SOUND_BANKS_ALL_BITS); + + sUnused80332118 = 0; + sBackgroundMusicTargetVolume = TARGET_VOLUME_UNSET; + sLowerBackgroundMusicVolume = FALSE; + sSoundBanksThatLowerBackgroundMusic = 0; + sUnused80332114 = 0; + sCurrentBackgroundMusicSeqId = 0xff; + gSoundMode = SOUND_MODE_STEREO; + sBackgroundMusicQueueSize = 0; + sBackgroundMusicMaxTargetVolume = TARGET_VOLUME_UNSET; + D_80332120 = 0; + D_80332124 = 0; + sNumProcessedSoundRequests = 0; + sSoundRequestCount = 0; +} + +// (unused) +void get_currently_playing_sound(u8 bank, u8 *numPlayingSounds, u8 *numSoundsInBank, u8 *soundId) { + u8 i; + u8 count = 0; + + for (i = 0; i < sMaxChannelsForSoundBank[bank]; i++) { + if (sCurrentSound[bank][i] != 0xff) { + count++; + } + } + *numPlayingSounds = count; + + *numSoundsInBank = sNumSoundsInBank[bank]; + + if (sCurrentSound[bank][0] != 0xff) { + *soundId = (u8)(sSoundBanks[bank][sCurrentSound[bank][0]].soundBits >> SOUNDARGS_SHIFT_SOUNDID); + } else { + *soundId = 0xff; + } +} + +/** + * Called from threads: thread5_game_loop + */ +void stop_sound(u32 soundBits, f32 *pos) { + u8 bank = (soundBits & SOUNDARGS_MASK_BANK) >> SOUNDARGS_SHIFT_BANK; + u8 soundIndex = sSoundBanks[bank][0].next; + + while (soundIndex != 0xff) { + // If sound has same id and source position pointer + if ((u16)(soundBits >> SOUNDARGS_SHIFT_SOUNDID) + == (u16)(sSoundBanks[bank][soundIndex].soundBits >> SOUNDARGS_SHIFT_SOUNDID) + && sSoundBanks[bank][soundIndex].x == pos) { + + // Mark sound for deletion + update_background_music_after_sound(bank, soundIndex); + sSoundBanks[bank][soundIndex].soundBits = NO_SOUND; + soundIndex = 0xff; // break + } else { + soundIndex = sSoundBanks[bank][soundIndex].next; + } + } +} + +/** + * Called from threads: thread5_game_loop + */ +void stop_sounds_from_source(f32 *pos) { + u8 bank; + u8 soundIndex; + + for (bank = 0; bank < SOUND_BANK_COUNT; bank++) { + soundIndex = sSoundBanks[bank][0].next; + while (soundIndex != 0xff) { + if (sSoundBanks[bank][soundIndex].x == pos) { + update_background_music_after_sound(bank, soundIndex); + sSoundBanks[bank][soundIndex].soundBits = NO_SOUND; + } + soundIndex = sSoundBanks[bank][soundIndex].next; + } + } +} + +/** + * Called from threads: thread3_main, thread5_game_loop + */ +static void stop_sounds_in_bank(u8 bank) { + u8 soundIndex = sSoundBanks[bank][0].next; + + while (soundIndex != 0xff) { + update_background_music_after_sound(bank, soundIndex); + sSoundBanks[bank][soundIndex].soundBits = NO_SOUND; + soundIndex = sSoundBanks[bank][soundIndex].next; + } +} + +/** + * Stops sounds in all of the sound banks that predominantly consist of continuous + * sounds. Misses some specific continuous sounds in other banks like bird chirping + * and the ticking sound after pressing a switch. + * + * Called from threads: thread3_main, thread5_game_loop + */ +void stop_sounds_in_continuous_banks(void) { + stop_sounds_in_bank(SOUND_BANK_MOVING); + stop_sounds_in_bank(SOUND_BANK_ENV); + stop_sounds_in_bank(SOUND_BANK_AIR); +} + +/** + * Called from threads: thread3_main, thread5_game_loop + */ +void sound_banks_disable(UNUSED u8 player, u16 bankMask) { + u8 i; + + for (i = 0; i < SOUND_BANK_COUNT; i++) { + if (bankMask & 1) { + sSoundBankDisabled[i] = TRUE; + } + bankMask = bankMask >> 1; + } +} + +/** + * Called from threads: thread5_game_loop + */ +static void disable_all_sequence_players(void) { + u8 i; + + for (i = 0; i < SEQUENCE_PLAYERS; i++) { + sequence_player_disable(&gSequencePlayers[i]); + } +} + +/** + * Called from threads: thread5_game_loop + */ +void sound_banks_enable(UNUSED u8 player, u16 bankMask) { + u8 i; + + for (i = 0; i < SOUND_BANK_COUNT; i++) { + if (bankMask & 1) { + sSoundBankDisabled[i] = FALSE; + } + bankMask = bankMask >> 1; + } +} + +u8 unused_803209D8(u8 player, u8 channelIndex, u8 arg2) { + u8 ret = 0; + if (gSequencePlayers[player].channels[channelIndex] != &gSequenceChannelNone) { + gSequencePlayers[player].channels[channelIndex]->stopSomething2 = arg2; + ret = arg2; + } + return ret; +} +void func_80320A4C(u8 bankIndex, u8 arg1) { + sSoundMovingSpeed[bankIndex] = arg1; +} + +/** + * Set the moving speed for a sound bank, which may affect the volume and pitch + * of the sound. + * + * Called from threads: thread5_game_loop + */ +void set_sound_moving_speed(u8 bank, u8 speed) { + sSoundMovingSpeed[bank] = speed; +} + +/** + * Called from threads: thread5_game_loop + */ +void play_music(u8 player, u16 seqArgs, u16 fadeTimer) { + u8 seqId = seqArgs & 0xff; + u8 priority = seqArgs >> 8; + u8 i; + u8 foundIndex = 0; + + // Except for the background music player, we don't support queued + // sequences. Just play them immediately, stopping any old sequence. + if (player != SEQ_PLAYER_LEVEL) { + seq_player_play_sequence(player, seqId, fadeTimer); + return; + } + + // Abort if the queue is already full. + if (sBackgroundMusicQueueSize == MAX_BACKGROUND_MUSIC_QUEUE_SIZE) { + return; + } + + // If already in the queue, abort, after first restarting the sequence if + // it is first, and handling disabled music somehow. + // (That handling probably ought to occur even when the queue is full...) + for (i = 0; i < sBackgroundMusicQueueSize; i++) { + if (sBackgroundMusicQueue[i].seqId == seqId) { + if (i == 0) { + seq_player_play_sequence(SEQ_PLAYER_LEVEL, seqId, fadeTimer); + } else if (!gSequencePlayers[SEQ_PLAYER_LEVEL].enabled) { + stop_background_music(sBackgroundMusicQueue[0].seqId); + } + return; + } + } + + // Find the next sequence slot by priority. + for (i = 0; i < sBackgroundMusicQueueSize; i++) { + if (sBackgroundMusicQueue[i].priority <= priority) { + foundIndex = i; + i = sBackgroundMusicQueueSize; // break + } + } + + // If the sequence ends up first in the queue, start it, and make space for + // one more entry in the queue. + if (foundIndex == 0) { + seq_player_play_sequence(SEQ_PLAYER_LEVEL, seqId, fadeTimer); + sBackgroundMusicQueueSize++; + } + + // Move all items up in queue, throwing away the last one if we didn't put + // the new sequence first. + for (i = sBackgroundMusicQueueSize - 1; i > foundIndex; i--) { + sBackgroundMusicQueue[i].priority = sBackgroundMusicQueue[i - 1].priority; + sBackgroundMusicQueue[i].seqId = sBackgroundMusicQueue[i - 1].seqId; + } + + // Insert item into queue. + sBackgroundMusicQueue[foundIndex].priority = priority; + sBackgroundMusicQueue[foundIndex].seqId = seqId; +} + +/** + * Called from threads: thread5_game_loop + */ +void stop_background_music(u16 seqId) { + u8 foundIndex; + u8 i; + + if (sBackgroundMusicQueueSize == 0) { + return; + } + + // If sequence is not found, remove an empty queue item (the next empty + // queue slot). + foundIndex = sBackgroundMusicQueueSize; + + // Search for the sequence. + for (i = 0; i < sBackgroundMusicQueueSize; i++) { + if (sBackgroundMusicQueue[i].seqId == (u8)(seqId & 0xff)) { + // Remove sequence from queue. If it was first, play the next one, + // or fade out the music. + sBackgroundMusicQueueSize--; + if (i == 0) { + if (sBackgroundMusicQueueSize != 0) { + seq_player_play_sequence(SEQ_PLAYER_LEVEL, sBackgroundMusicQueue[1].seqId, 0); + } else { + seq_player_fade_out(SEQ_PLAYER_LEVEL, 20); + } + } + foundIndex = i; + i = sBackgroundMusicQueueSize; // "break;" + } + } + + // Move later slots down. + for (i = foundIndex; i < sBackgroundMusicQueueSize; i++) { + sBackgroundMusicQueue[i].priority = sBackgroundMusicQueue[i + 1].priority; + sBackgroundMusicQueue[i].seqId = sBackgroundMusicQueue[i + 1].seqId; + } + + // @bug? If the sequence queue is full and we attempt to stop a sequence + // that isn't in the queue, this writes out of bounds. Can that happen? + sBackgroundMusicQueue[i].priority = 0; +} + +/** + * Called from threads: thread5_game_loop + */ +void fadeout_background_music(u16 seqId, u16 fadeOut) { + if (sBackgroundMusicQueueSize != 0 && sBackgroundMusicQueue[0].seqId == (u8)(seqId & 0xff)) { + seq_player_fade_out(SEQ_PLAYER_LEVEL, fadeOut); + } +} + +/** + * Called from threads: thread5_game_loop + */ +void drop_queued_background_music(void) { + if (sBackgroundMusicQueueSize != 0) { + sBackgroundMusicQueueSize = 1; + } +} + +/** + * Called from threads: thread5_game_loop + */ +u16 get_current_background_music(void) { + if (sBackgroundMusicQueueSize != 0) { + return (sBackgroundMusicQueue[0].priority << 8) + sBackgroundMusicQueue[0].seqId; + } + return -1; +} + +/** + * Called from threads: thread4_sound, thread5_game_loop (EU only) + */ +void func_80320ED8(void) { +#if defined(VERSION_EU) || defined(VERSION_SH) + if (D_EU_80300558 != 0) { + D_EU_80300558--; + } + + if (gSequencePlayers[SEQ_PLAYER_ENV].enabled + || sBackgroundMusicMaxTargetVolume == TARGET_VOLUME_UNSET || D_EU_80300558 != 0) { +#else + if (gSequencePlayers[SEQ_PLAYER_ENV].enabled + || sBackgroundMusicMaxTargetVolume == TARGET_VOLUME_UNSET) { +#endif + return; + } + + sBackgroundMusicMaxTargetVolume = TARGET_VOLUME_UNSET; + begin_background_music_fade(50); + + if (sBackgroundMusicTargetVolume != TARGET_VOLUME_UNSET + && (D_80332120 == SEQ_EVENT_MERRY_GO_ROUND || D_80332120 == SEQ_EVENT_PIRANHA_PLANT)) { + seq_player_play_sequence(SEQ_PLAYER_ENV, D_80332120, 1); + if (D_80332124 != 0xff) { + seq_player_fade_to_target_volume(SEQ_PLAYER_ENV, 1, D_80332124); + } + } +} + +/** + * Called from threads: thread5_game_loop + */ +void play_secondary_music(u8 seqId, u8 bgMusicVolume, u8 volume, u16 fadeTimer) { + UNUSED u32 dummy; + + sUnused80332118 = 0; + if (sCurrentBackgroundMusicSeqId == 0xff || sCurrentBackgroundMusicSeqId == SEQ_MENU_TITLE_SCREEN) { + return; + } + + if (sBackgroundMusicTargetVolume == TARGET_VOLUME_UNSET) { + sBackgroundMusicTargetVolume = bgMusicVolume + TARGET_VOLUME_IS_PRESENT_FLAG; + begin_background_music_fade(fadeTimer); + seq_player_play_sequence(SEQ_PLAYER_ENV, seqId, fadeTimer >> 1); + if (volume < 0x80) { + seq_player_fade_to_target_volume(SEQ_PLAYER_ENV, fadeTimer, volume); + } + D_80332124 = volume; + D_80332120 = seqId; + } else if (volume != 0xff) { + sBackgroundMusicTargetVolume = bgMusicVolume + TARGET_VOLUME_IS_PRESENT_FLAG; + begin_background_music_fade(fadeTimer); + seq_player_fade_to_target_volume(SEQ_PLAYER_ENV, fadeTimer, volume); + D_80332124 = volume; + } +} + +/** + * Called from threads: thread5_game_loop + */ +void func_80321080(u16 fadeTimer) { + if (sBackgroundMusicTargetVolume != TARGET_VOLUME_UNSET) { + sBackgroundMusicTargetVolume = TARGET_VOLUME_UNSET; + D_80332120 = 0; + D_80332124 = 0; + begin_background_music_fade(fadeTimer); + seq_player_fade_out(SEQ_PLAYER_ENV, fadeTimer); + } +} + +/** + * Called from threads: thread3_main, thread5_game_loop + */ +void func_803210D4(u16 fadeDuration) { + u8 i; + + if (sHasStartedFadeOut) { + return; + } + + if (gSequencePlayers[SEQ_PLAYER_LEVEL].enabled == TRUE) { +#if defined(VERSION_EU) || defined(VERSION_SH) + func_802ad74c(0x83000000, fadeDuration); +#else + seq_player_fade_to_zero_volume(SEQ_PLAYER_LEVEL, fadeDuration); +#endif + } + + if (gSequencePlayers[SEQ_PLAYER_ENV].enabled == TRUE) { +#if defined(VERSION_EU) || defined(VERSION_SH) + func_802ad74c(0x83010000, fadeDuration); +#else + seq_player_fade_to_zero_volume(SEQ_PLAYER_ENV, fadeDuration); +#endif + } + + for (i = 0; i < SOUND_BANK_COUNT; i++) { + if (i != SOUND_BANK_MENU) { + fade_channel_volume_scale(SEQ_PLAYER_SFX, i, 0, fadeDuration / 16); + } + } + + sHasStartedFadeOut = TRUE; +} + +/** + * Called from threads: thread5_game_loop + */ +void play_course_clear(void) { + seq_player_play_sequence(SEQ_PLAYER_ENV, SEQ_EVENT_CUTSCENE_COLLECT_STAR, 0); + sBackgroundMusicMaxTargetVolume = TARGET_VOLUME_IS_PRESENT_FLAG | 0; +#if defined(VERSION_EU) || defined(VERSION_SH) + D_EU_80300558 = 2; +#endif + begin_background_music_fade(50); +} + +/** + * Called from threads: thread5_game_loop + */ +void play_peachs_jingle(void) { + seq_player_play_sequence(SEQ_PLAYER_ENV, SEQ_EVENT_PEACH_MESSAGE, 0); + sBackgroundMusicMaxTargetVolume = TARGET_VOLUME_IS_PRESENT_FLAG | 0; +#if defined(VERSION_EU) || defined(VERSION_SH) + D_EU_80300558 = 2; +#endif + begin_background_music_fade(50); +} + +/** + * Plays the puzzle jingle. Plays the dadada dadada *dadada* jingle + * that usually plays when you solve a "puzzle", like chests, talking to + * yoshi, releasing chain chomp, opening the pyramid top, etc. + * + * Called from threads: thread5_game_loop + */ +void play_puzzle_jingle(void) { + seq_player_play_sequence(SEQ_PLAYER_ENV, SEQ_EVENT_SOLVE_PUZZLE, 0); + sBackgroundMusicMaxTargetVolume = TARGET_VOLUME_IS_PRESENT_FLAG | 20; +#if defined(VERSION_EU) || defined(VERSION_SH) + D_EU_80300558 = 2; +#endif + begin_background_music_fade(50); +} + +/** + * Called from threads: thread5_game_loop + */ +void play_star_fanfare(void) { + seq_player_play_sequence(SEQ_PLAYER_ENV, SEQ_EVENT_HIGH_SCORE, 0); + sBackgroundMusicMaxTargetVolume = TARGET_VOLUME_IS_PRESENT_FLAG | 20; +#if defined(VERSION_EU) || defined(VERSION_SH) + D_EU_80300558 = 2; +#endif + begin_background_music_fade(50); +} + +/** + * Called from threads: thread5_game_loop + */ +void play_power_star_jingle(u8 arg0) { + if (!arg0) { + sBackgroundMusicTargetVolume = 0; + } + seq_player_play_sequence(SEQ_PLAYER_ENV, SEQ_EVENT_CUTSCENE_STAR_SPAWN, 0); + sBackgroundMusicMaxTargetVolume = TARGET_VOLUME_IS_PRESENT_FLAG | 20; +#if defined(VERSION_EU) || defined(VERSION_SH) + D_EU_80300558 = 2; +#endif + begin_background_music_fade(50); +} + +/** + * Called from threads: thread5_game_loop + */ +void play_race_fanfare(void) { + seq_player_play_sequence(SEQ_PLAYER_ENV, SEQ_EVENT_RACE, 0); + sBackgroundMusicMaxTargetVolume = TARGET_VOLUME_IS_PRESENT_FLAG | 20; +#if defined(VERSION_EU) || defined(VERSION_SH) + D_EU_80300558 = 2; +#endif + begin_background_music_fade(50); +} + +/** + * Called from threads: thread5_game_loop + */ +void play_toads_jingle(void) { + seq_player_play_sequence(SEQ_PLAYER_ENV, SEQ_EVENT_TOAD_MESSAGE, 0); + sBackgroundMusicMaxTargetVolume = TARGET_VOLUME_IS_PRESENT_FLAG | 20; +#if defined(VERSION_EU) || defined(VERSION_SH) + D_EU_80300558 = 2; +#endif + begin_background_music_fade(50); +} + +/** + * Called from threads: thread5_game_loop + */ +void sound_reset(u8 presetId) { +#ifndef VERSION_JP + if (presetId >= 8) { + presetId = 0; + sUnused8033323C = 0; + } +#endif + sGameLoopTicked = 0; + disable_all_sequence_players(); + sound_init(); +#ifdef VERSION_SH + func_802ad74c(0xF2000000, 0); +#endif +#if defined(VERSION_JP) || defined(VERSION_US) + audio_reset_session(&gAudioSessionPresets[presetId]); +#else + audio_reset_session_eu(presetId); +#endif + osWritebackDCacheAll(); + if (presetId != 7) { + preload_sequence(SEQ_EVENT_SOLVE_PUZZLE, PRELOAD_BANKS | PRELOAD_SEQUENCE); + preload_sequence(SEQ_EVENT_PEACH_MESSAGE, PRELOAD_BANKS | PRELOAD_SEQUENCE); + preload_sequence(SEQ_EVENT_CUTSCENE_STAR_SPAWN, PRELOAD_BANKS | PRELOAD_SEQUENCE); + } + seq_player_play_sequence(SEQ_PLAYER_SFX, SEQ_SOUND_PLAYER, 0); + D_80332108 = (D_80332108 & 0xf0) + presetId; + gSoundMode = D_80332108 >> 4; + sHasStartedFadeOut = FALSE; +} + +/** + * Called from threads: thread5_game_loop + */ +void audio_set_sound_mode(u8 soundMode) { + D_80332108 = (D_80332108 & 0xf) + (soundMode << 4); + gSoundMode = soundMode; +} + +#if defined(VERSION_JP) || defined(VERSION_US) +void unused_80321460(UNUSED s32 arg0, UNUSED s32 arg1, UNUSED s32 arg2, UNUSED s32 arg3) { +} + +void unused_80321474(UNUSED s32 arg0) { +} +#endif diff --git a/src/decomp/audio/external.h b/src/decomp/audio/external.h new file mode 100644 index 0000000..00c6d39 --- /dev/null +++ b/src/decomp/audio/external.h @@ -0,0 +1,81 @@ +#ifndef AUDIO_EXTERNAL_H +#define AUDIO_EXTERNAL_H + +#include + +#include + +// Sequence arguments, passed to seq_player_play_sequence. seqId may be bit-OR'ed with +// SEQ_VARIATION; this will load the same sequence, but set a variation +// bit which may be read by the sequence script. +#define SEQUENCE_ARGS(priority, seqId) ((priority << 8) | seqId) + +#define SOUND_MODE_STEREO 0 +#define SOUND_MODE_MONO 3 +#define SOUND_MODE_HEADSET 1 + +#define SEQ_PLAYER_LEVEL 0 // Level background music +#define SEQ_PLAYER_ENV 1 // Misc music like the puzzle jingle +#define SEQ_PLAYER_SFX 2 // Sound effects + +extern s32 gAudioErrorFlags; +extern f32 gGlobalSoundSource[3]; +extern u32 gAudioRandom; +extern f32 gAudioVolume; +extern u8 gAudioReverb; + +struct Sound { + s32 soundBits; + f32 *position; +}; // size = 0x8 + +extern struct Sound sSoundRequests[0x100]; +extern u8 sSoundRequestCount; + +extern u8 gAudioSPTaskYieldBuffer[]; // ucode yield data ptr; only used in JP + +struct SPTask *create_next_audio_frame_task(void); +void create_next_audio_buffer(s16 *samples, u32 num_samples); +void audio_signal_game_loop_tick(void); +void update_game_sound(void); +void seq_player_fade_out(u8 player, u16 fadeDuration); +void fade_volume_scale(u8 player, u8 targetScale, u16 fadeDuration); +void seq_player_lower_volume(u8 player, u16 fadeDuration, u8 percentage); +void seq_player_unlower_volume(u8 player, u16 fadeDuration); +void set_audio_muted(u8 muted); +void sound_init(void); +void get_currently_playing_sound(u8 bank, u8 *numPlayingSounds, u8 *numSoundsInBank, u8 *soundId); +void stop_sound(u32 soundBits, f32 *pos); +void stop_sounds_from_source(f32 *pos); +void stop_sounds_in_continuous_banks(void); +void sound_banks_disable(u8 player, u16 bankMask); +void sound_banks_enable(u8 player, u16 bankMask); +void set_sound_moving_speed(u8 bank, u8 speed); +void play_dialog_sound(u8 dialogID); +void play_music(u8 player, u16 seqArgs, u16 fadeTimer); +void stop_background_music(u16 seqId); +void fadeout_background_music(u16 arg0, u16 fadeOut); +void drop_queued_background_music(void); +u16 get_current_background_music(void); +void play_secondary_music(u8 seqId, u8 bgMusicVolume, u8 volume, u16 fadeTimer); +void func_80321080(u16 fadeTimer); +void func_803210D4(u16 fadeOutTime); +void play_course_clear(void); +void play_peachs_jingle(void); +void play_puzzle_jingle(void); +void play_star_fanfare(void); +void play_power_star_jingle(u8 arg0); +void play_race_fanfare(void); +void play_toads_jingle(void); +void sound_reset(u8 presetId); +void audio_set_sound_mode(u8 arg0); + +void audio_init(void); // in load.c +void seq_player_play_sequence(u8 player, u8 seqId, u16 arg2); + +#if defined(VERSION_EU) || defined(VERSION_SH) +struct SPTask *unused_80321460(); +struct SPTask *unused_80321460(void); +#endif + +#endif // AUDIO_EXTERNAL_H diff --git a/src/decomp/audio/globals_start.c b/src/decomp/audio/globals_start.c new file mode 100644 index 0000000..01498f2 --- /dev/null +++ b/src/decomp/audio/globals_start.c @@ -0,0 +1,3 @@ +#include + +u64 gAudioGlobalsStartMarker; diff --git a/src/decomp/audio/heap.c b/src/decomp/audio/heap.c new file mode 100644 index 0000000..c11c24c --- /dev/null +++ b/src/decomp/audio/heap.c @@ -0,0 +1,1726 @@ +#include + +#include "heap.h" +#include "data.h" +#include "load.h" +#include "synthesis.h" +#include "seqplayer.h" +#include "effects.h" + +#define ALIGN16(val) (((val) + 0xF) & ~0xF) + +#if defined(VERSION_EU) +ALIGNED16 u8 gAudioHeap[DOUBLE_SIZE_ON_64_BIT(0x31200) - 0x3800]; +#elif defined(VERSION_SH) +ALIGNED16 u8 gAudioHeap[DOUBLE_SIZE_ON_64_BIT(0x31200) - 0x4800]; +#else +ALIGNED16 u8 gAudioHeap[DOUBLE_SIZE_ON_64_BIT(0x31200)]; +#endif + +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; +u8 sReverbDownsampleRateLog; // never read +#endif + +struct SoundAllocPool gAudioSessionPool; +struct SoundAllocPool gAudioInitPool; +struct SoundAllocPool gNotesAndBuffersPool; +u8 sAudioHeapPad[0x20]; // probably two unused pools +struct SoundAllocPool gSeqAndBankPool; +struct SoundAllocPool gPersistentCommonPool; +struct SoundAllocPool gTemporaryCommonPool; + +struct SoundMultiPool gSeqLoadedPool; +struct SoundMultiPool gBankLoadedPool; +struct SoundMultiPool gUnusedLoadedPool; + +#ifdef VERSION_SH +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[0x40]; +#endif +u8 gBankLoadStatus[0x40]; +u8 gSeqLoadStatus[0x100]; + +#if defined(VERSION_EU) || defined(VERSION_SH) +volatile u8 gAudioResetStatus; +u8 gAudioResetPresetIdToLoad; +s32 gAudioResetFadeOutFramesLeft; +#endif + +u8 gAudioUnusedBuffer[0x1000]; + +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) { + 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 + */ +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++) { + 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; +} + +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) { + s32 i; + +#ifdef VERSION_SH + for (i = 0; i < 64; i++) { + if (gBankLoadStatus[i] != SOUND_LOAD_STATUS_5) { + gBankLoadStatus[i] = SOUND_LOAD_STATUS_NOT_LOADED; + } + } + + for (i = 0; i < 64; i++) { + if (gUnkLoadStatus[i] != SOUND_LOAD_STATUS_5) { + gUnkLoadStatus[i] = SOUND_LOAD_STATUS_NOT_LOADED; + } + } + + for (i = 0; i < 256; i++) { + if (gSeqLoadStatus[i] != SOUND_LOAD_STATUS_5) { + gSeqLoadStatus[i] = SOUND_LOAD_STATUS_NOT_LOADED; + } + } +#else + 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; + } +#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 + eu_stubbed_printf_3("Kill Voice %d (ID %d) %d\n", note->waveId, + bankId, note->priority); + 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) { + u8 *start; + 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; + for (i = 0; i <= last; i++) { + start[i] = 0; + } + } else { + return NULL; + } + return start; +} + +#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 = 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; +#if defined(VERSION_EU) || defined(VERSION_SH) + temporary->entries[1].ptr = temporary->pool.start + temporary->pool.size; +#else + temporary->entries[1].ptr = temporary->pool.size + temporary->pool.start; +#endif + temporary->entries[0].id = -1; // should be at 1e not 1c + temporary->entries[1].id = -1; +} + +void unused_803160F8(struct SoundAllocPool *pool) { + pool->numAllocatedEntries = 0; + pool->cur = pool->start; +} + +extern s32 D_SH_80315EE8; +void sound_init_main_pools(s32 sizeForAudioInitPool) { + sound_alloc_pool_init(&gAudioInitPool, gAudioHeap, sizeForAudioInitPool); + sound_alloc_pool_init(&gAudioSessionPool, gAudioHeap + sizeForAudioInitPool, gAudioHeapSize - sizeForAudioInitPool); +} + +#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); +} + +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); + sound_alloc_pool_init(&gUnusedLoadedPool.persistent.pool, SOUND_ALLOC_FUNC(&gPersistentCommonPool, a->wantUnused), + a->wantUnused); + persistent_pool_clear(&gSeqLoadedPool.persistent); + persistent_pool_clear(&gBankLoadedPool.persistent); + persistent_pool_clear(&gUnusedLoadedPool.persistent); +} + +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); + sound_alloc_pool_init(&gUnusedLoadedPool.temporary.pool, SOUND_ALLOC_FUNC(&gTemporaryCommonPool, a->wantUnused), + a->wantUnused); + temporary_pool_clear(&gSeqLoadedPool.temporary); + temporary_pool_clear(&gBankLoadedPool.temporary); + temporary_pool_clear(&gUnusedLoadedPool.temporary); +} +#undef SOUND_ALLOC_FUNC + +#if defined(VERSION_JP) || defined(VERSION_US) +UNUSED static void unused_803163D4(void) { +} +#endif + +#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; +#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; +#ifndef VERSION_SH + u8 isSound; +#endif +#if defined(VERSION_JP) || defined(VERSION_US) + u16 firstVal; + u16 secondVal; + u32 bothDiscardable; + u32 leftDiscardable, rightDiscardable; + u32 leftNotLoaded, rightNotLoaded; + u32 leftAvail, rightAvail; +#endif + +#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_EU + if (0) { + // It's unclear where these string literals go. + eu_stubbed_printf_0("DataHeap Not Allocate \n"); + eu_stubbed_printf_1("StayHeap Not Allocate %d\n", 0); + eu_stubbed_printf_1("AutoHeap Not Allocate %d\n", 0); + } +#endif + +#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; + + switch (isSound) { + case FALSE: + discard_sequence(tp->entries[1].id); + break; + case TRUE: + discard_bank(tp->entries[1].id); + break; + } + + tp->entries[1].id = (s32)nullID; +#if defined(VERSION_EU) || defined(VERSION_SH) + tp->entries[1].ptr = pool->start + pool->size; +#else + tp->entries[1].ptr = pool->size + pool->start; +#endif + } + + 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); +#elif defined(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; + +#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; + + switch (isSound) { + case FALSE: + discard_sequence(tp->entries[0].id); + break; + case TRUE: + discard_bank(tp->entries[0].id); + break; + } + + 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 + eu_stubbed_printf_1("MEMORY:StayHeap OVERFLOW (REQ:%d)", arg1 * size); + 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; + + 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; + 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; + UNUSED void *ret; + 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) { +#if defined(VERSION_EU) || defined(VERSION_SH) + return get_bank_or_seq(arg0, 0, id); +#else + // Prevent tail call optimization by using a temporary. + // Either copt or -Wo,-notail. + ret = get_bank_or_seq(arg0, 0, id); + return ret; +#endif + } + return NULL; + } +} +#endif + +#if defined(VERSION_EU) || defined(VERSION_SH) +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_SH) +void clear_curr_ai_buffer(void) { + s32 currIndex = gCurrAiBufferIndex; + s32 i; + gAiBufferLengths[currIndex] = gAudioBufferParameters.minAiBufferLength; + for (i = 0; i < (s32) (AIBUFFER_LEN / sizeof(s16)); i++) { + gAiBuffers[currIndex][i] = 0; + } +} +#endif + + +#if defined(VERSION_EU) || defined(VERSION_SH) +s32 audio_shut_down_and_reset_step(void) { + s32 i; + s32 j; +#ifdef VERSION_SH + s32 num = gAudioBufferParameters.presetUnk4 == 2 ? 2 : 1; +#endif + + switch (gAudioResetStatus) { + case 5: + for (i = 0; i < SEQUENCE_PLAYERS; i++) { + sequence_player_disable(&gSequencePlayers[i]); + } +#ifdef VERSION_SH + gAudioResetFadeOutFramesLeft = 4 / num; +#else + gAudioResetFadeOutFramesLeft = 4; +#endif + 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; + } + } +#ifdef VERSION_SH + gAudioResetFadeOutFramesLeft = 16 / num; +#else + gAudioResetFadeOutFramesLeft = 16; +#endif + gAudioResetStatus--; + } + break; + case 3: + if (gAudioResetFadeOutFramesLeft != 0) { + gAudioResetFadeOutFramesLeft--; +#ifdef VERSION_SH + if (1) { + } +#endif + decrease_reverb_gain(); + } else { + for (i = 0; i < NUMAIBUFFERS; i++) { + for (j = 0; j < (s32) (AIBUFFER_LEN / sizeof(s16)); j++) { + gAiBuffers[i][j] = 0; + } + } +#ifdef VERSION_SH + gAudioResetFadeOutFramesLeft = 4 / num; +#else + gAudioResetFadeOutFramesLeft = 4; +#endif + gAudioResetStatus--; + } + break; + case 2: +#ifdef VERSION_SH + clear_curr_ai_buffer(); +#endif + if (gAudioResetFadeOutFramesLeft != 0) { + gAudioResetFadeOutFramesLeft--; + } else { + gAudioResetStatus--; +#ifdef VERSION_SH + func_sh_802f23ec(); +#endif + } + break; + case 1: + audio_reset_session(); + gAudioResetStatus = 0; +#ifdef VERSION_SH + for (i = 0; i < NUMAIBUFFERS; i++) { + gAiBufferLengths[i] = gAudioBufferParameters.maxAiBufferLength; + for (j = 0; j < (s32) (AIBUFFER_LEN / sizeof(s16)); j++) { + gAiBuffers[i][j] = 0; + } + } +#endif + } +#ifdef VERSION_SH + if (gAudioResetFadeOutFramesLeft) { + } +#endif + if (gAudioResetStatus < 3) { + return 0; + } + return 1; +} +#else +/** + * Waits until a specified number of audio frames have been created + */ +void wait_for_audio_frames(UNUSED s32 frames) { + gAudioFrameCount = 0; +#ifdef TARGET_N64 + // Sound thread will update gAudioFrameCount + while (gAudioFrameCount < frames) { + // spin + } +#endif +} +#endif + +#if defined(VERSION_JP) || defined(VERSION_US) +void audio_reset_session(struct AudioSessionSettings *preset) { +#else +void audio_reset_session(void) { + struct AudioSessionSettingsEU *preset = &gAudioSessionPresets[gAudioResetPresetIdToLoad]; + struct ReverbSettingsEU *reverbSettings; +#endif + s16 *mem; +#if defined(VERSION_JP) || defined(VERSION_US) + s8 updatesPerFrame; + s32 reverbWindowSize; + s32 k; +#endif + s32 i; + s32 j; + s32 persistentMem; + s32 temporaryMem; + s32 totalMem; + s32 wantMisc; +#if defined(VERSION_JP) || defined(VERSION_US) + s32 frames; + s32 remainingDmas; +#else + struct SynthesisReverb *reverb; +#endif + eu_stubbed_printf_1("Heap Reconstruct Start %x\n", gAudioResetPresetIdToLoad); + +#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; + + for (j = 0; j < NUMAIBUFFERS; j++) { + for (k = 0; k < (s32) (AIBUFFER_LEN / sizeof(s16)); k++) { + gAiBuffers[j][k] = 0; + } + } + } +#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; + +#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 + reverbWindowSize = preset->reverbWindowSize; + gAiFrequency = osAiSetFrequency(preset->frequency); + gMaxSimultaneousNotes = preset->maxSimultaneousNotes; + gSamplesPerFrameTarget = ALIGN16(gAiFrequency / 60); + gReverbDownsampleRate = preset->reverbDownsampleRate; + + switch (gReverbDownsampleRate) { + case 1: + sReverbDownsampleRateLog = 0; + break; + case 2: + sReverbDownsampleRateLog = 1; + break; + case 4: + sReverbDownsampleRateLog = 2; + break; + case 8: + sReverbDownsampleRateLog = 3; + break; + case 16: + sReverbDownsampleRateLog = 4; + break; + default: + sReverbDownsampleRateLog = 0; + } + + gReverbDownsampleRate = preset->reverbDownsampleRate; + gVolume = preset->volume; + gMinAiBufferLength = gSamplesPerFrameTarget - 0x10; + updatesPerFrame = gSamplesPerFrameTarget / 160 + 1; + gAudioUpdatesPerFrame = gSamplesPerFrameTarget / 160 + 1; + + // 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); +#elif defined(VERSION_EU) + persistentMem = DOUBLE_SIZE_ON_64_BIT(preset->persistentSeqMem + preset->persistentBankMem); + temporaryMem = DOUBLE_SIZE_ON_64_BIT(preset->temporarySeqMem + preset->temporaryBankMem); +#else + persistentMem = DOUBLE_SIZE_ON_64_BIT(preset->persistentBankMem + preset->persistentSeqMem); + temporaryMem = DOUBLE_SIZE_ON_64_BIT(preset->temporaryBankMem + preset->temporarySeqMem); +#endif + totalMem = persistentMem + temporaryMem; + 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); +#ifdef VERSION_SH + sPersistentCommonPoolSplit.wantUnused = preset->unk18; +#else + sPersistentCommonPoolSplit.wantUnused = 0; +#endif + persistent_pools_init(&sPersistentCommonPoolSplit); + sTemporaryCommonPoolSplit.wantSeq = DOUBLE_SIZE_ON_64_BIT(preset->temporarySeqMem); + sTemporaryCommonPoolSplit.wantBank = DOUBLE_SIZE_ON_64_BIT(preset->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, gMaxAudioCmds * sizeof(u64)); + } +#endif + + gNotes = soundAlloc(&gNotesAndBuffersPool, gMaxSimultaneousNotes * sizeof(struct Note)); + note_init_all(); + init_note_free_list(); + +#if defined(VERSION_EU) || defined(VERSION_SH) + 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]; +#ifdef VERSION_SH + reverb->downsampleRate = reverbSettings->downsampleRate; + reverb->windowSize = reverbSettings->windowSize * 64; + reverb->windowSize /= reverb->downsampleRate; +#else + reverb->windowSize = reverbSettings->windowSize * 64; + reverb->downsampleRate = reverbSettings->downsampleRate; +#endif + reverb->reverbGain = reverbSettings->gain; +#ifdef VERSION_SH + reverb->panRight = reverbSettings->unk4; + reverb->panLeft = reverbSettings->unk6; + reverb->unk5 = reverbSettings->unk8; + reverb->unk08 = reverbSettings->unkA; +#endif + 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; +#ifdef VERSION_SH + reverb->resampleFlags = A_INIT; +#endif + if (reverb->downsampleRate != 1) { +#ifndef VERSION_SH + reverb->resampleFlags = A_INIT; +#endif + 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); + } + } +#ifdef VERSION_SH + if (reverbSettings->unkC != 0) { + reverb->unk108 = sound_alloc_uninitialized(&gNotesAndBuffersPool, 16 * sizeof(s16)); + reverb->unk100 = sound_alloc_uninitialized(&gNotesAndBuffersPool, 8 * sizeof(s16)); + func_sh_802F0DE8(reverb->unk100, reverbSettings->unkC); + } else { + reverb->unk100 = NULL; + } + if (reverbSettings->unkE != 0) { + reverb->unk10C = sound_alloc_uninitialized(&gNotesAndBuffersPool, 16 * sizeof(s16)); + reverb->unk104 = sound_alloc_uninitialized(&gNotesAndBuffersPool, 8 * sizeof(s16)); + func_sh_802F0DE8(reverb->unk104, reverbSettings->unkE); + } else { + reverb->unk104 = NULL; + } +#endif + } + +#else + if (reverbWindowSize == 0) { + gSynthesisReverb.useReverb = 0; + } else { + 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)); + 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); + } + } + } +#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 +} + +#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) { + void *ret; + s32 pos; + + pos = gUnkPool1.pool.numAllocatedEntries; + 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; + +#ifdef AVOID_UB + //! @bug UB: missing return. "ret" is in v0 at this point, but doing an + // explicit return uses an additional register. + return ret; +#endif +} + +u8 *func_sh_802f1d40(u32 size, s32 bank, u8 *arg2, s8 medium) { + struct UnkEntry *ret; + + 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; + + ret = unk_pool2_alloc(size); + if (ret != NULL) { + ret->bankId = bank; + ret->dstAddr = arg2; + ret->medium = medium; + return ret->srcAddr; + } + return NULL; +} +u8 *func_sh_802f1de0(u32 size, s32 bank, u8 *arg2, s8 medium) { // duplicated function? + struct UnkEntry *ret; + + 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; + + 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 *temp_s2; + u8 *phi_s3; + u8 *memLocation; + u8 *cur; + + s32 i; + s32 chosenIndex; + + struct UnkStructSH8034EC88 *unkStruct; + struct UnkPool *pool = &gUnkPool3; + + u8 *itemStart; + u8 *itemEnd; + + phi_s3 = pool->pool.cur; + 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; + } + temp_s2 = pool->pool.cur; + + 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; + s32 bankId2; + s32 instId; + s32 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) != FALSE) { + 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; + s32 idx; + s32 seqCount; + s32 bankId1; + s32 bankId2; + s32 instId; + s32 drumId; + struct Drum *drum; + struct Instrument *inst; + UNUSED s32 pad; + struct UnkEntry *entry; //! @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) != FALSE) { + 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 + +#ifdef VERSION_EU +u8 audioString22[] = "SFrame Sample %d %d %d\n"; +u8 audioString23[] = "AHPBASE %x\n"; +u8 audioString24[] = "AHPCUR %x\n"; +u8 audioString25[] = "HeapTop %x\n"; +u8 audioString26[] = "SynoutRate %d / %d \n"; +u8 audioString27[] = "FXSIZE %d\n"; +u8 audioString28[] = "FXCOMP %d\n"; +u8 audioString29[] = "FXDOWN %d\n"; +u8 audioString30[] = "WaveCacheLen: %d\n"; +u8 audioString31[] = "SpecChange Finished\n"; +#endif diff --git a/src/decomp/audio/heap.h b/src/decomp/audio/heap.h new file mode 100644 index 0000000..4bc73bd --- /dev/null +++ b/src/decomp/audio/heap.h @@ -0,0 +1,144 @@ +#ifndef AUDIO_HEAP_H +#define AUDIO_HEAP_H + +#include + +#include "internal.h" + +#define SOUND_LOAD_STATUS_NOT_LOADED 0 +#define SOUND_LOAD_STATUS_IN_PROGRESS 1 +#define SOUND_LOAD_STATUS_COMPLETE 2 +#define SOUND_LOAD_STATUS_DISCARDABLE 3 +#define SOUND_LOAD_STATUS_4 4 +#define SOUND_LOAD_STATUS_5 5 + +#define IS_BANK_LOAD_COMPLETE(bankId) (gBankLoadStatus[bankId] >= SOUND_LOAD_STATUS_COMPLETE) +#define IS_SEQ_LOAD_COMPLETE(seqId) (gSeqLoadStatus[seqId] >= SOUND_LOAD_STATUS_COMPLETE) + +struct SoundAllocPool +{ + u8 *start; + u8 *cur; + u32 size; + s32 numAllocatedEntries; +}; // size = 0x10 + +struct SeqOrBankEntry { + u8 *ptr; + u32 size; +#ifdef VERSION_SH + s16 poolIndex; + s16 id; +#else + s32 id; // seqId or bankId +#endif +}; // size = 0xC + +struct PersistentPool +{ + /*0x00*/ u32 numEntries; + /*0x04*/ struct SoundAllocPool pool; + /*0x14*/ struct SeqOrBankEntry entries[32]; +}; // size = 0x194 + +struct TemporaryPool +{ + /*EU, SH*/ + /*0x00, 0x00*/ u32 nextSide; + /*0x04, */ struct SoundAllocPool pool; + /*0x04, pool.start */ + /*0x08, pool.cur */ + /*0x0C, 0x0C pool.size */ + /*0x10, 0x10 pool.numAllocatedEntries */ + /*0x14, */ struct SeqOrBankEntry entries[2]; + /*0x14, 0x14 entries[0].ptr */ + /*0x18, entries[0].size*/ + /*0x1C, 0x1E entries[0].id */ + /*0x20, 0x20 entries[1].ptr */ + /*0x24, entries[1].size*/ + /*0x28, 0x2A entries[1].id */ +}; // size = 0x2C + +struct SoundMultiPool +{ + /*0x000*/ struct PersistentPool persistent; + /*0x194*/ struct TemporaryPool temporary; + /* */ u32 pad2[4]; +}; // size = 0x1D0 + +struct Unk1Pool +{ + struct SoundAllocPool pool; + struct SeqOrBankEntry entries[32]; +}; + +struct UnkEntry +{ + s8 used; + s8 medium; + s8 bankId; + u32 pad; + u8 *srcAddr; + u8 *dstAddr; + u32 size; +}; + +struct UnkPool +{ + /*0x00*/ struct SoundAllocPool pool; + /*0x10*/ struct UnkEntry entries[64]; + /*0x510*/ s32 numEntries; + /*0x514*/ u32 unk514; +}; + +extern u8 gAudioHeap[]; +extern s16 gVolume; +extern s8 gReverbDownsampleRate; +extern struct SoundAllocPool gAudioInitPool; +extern struct SoundAllocPool gNotesAndBuffersPool; +extern struct SoundAllocPool gPersistentCommonPool; +extern struct SoundAllocPool gTemporaryCommonPool; +extern struct SoundMultiPool gSeqLoadedPool; +extern struct SoundMultiPool gBankLoadedPool; +#ifdef VERSION_SH +extern struct Unk1Pool gUnkPool1; +extern struct UnkPool gUnkPool2; +extern struct UnkPool gUnkPool3; +#endif +extern u8 gBankLoadStatus[64]; +extern u8 gSeqLoadStatus[256]; +extern volatile u8 gAudioResetStatus; +extern u8 gAudioResetPresetIdToLoad; + +#if defined(VERSION_EU) || defined(VERSION_SH) +extern volatile u8 gAudioResetStatus; +#endif + +void *soundAlloc(struct SoundAllocPool *pool, u32 size); +void *sound_alloc_uninitialized(struct SoundAllocPool *pool, u32 size); +void sound_init_main_pools(s32 sizeForAudioInitPool); +void sound_alloc_pool_init(struct SoundAllocPool *pool, void *memAddr, u32 size); +#ifdef VERSION_SH +void *alloc_bank_or_seq(s32 poolIdx, s32 size, s32 arg3, s32 id); +void *get_bank_or_seq(s32 poolIdx, s32 arg1, s32 id); +#else +void *alloc_bank_or_seq(struct SoundMultiPool *arg0, s32 arg1, s32 size, s32 arg3, s32 id); +void *get_bank_or_seq(struct SoundMultiPool *arg0, s32 arg1, s32 id); +#endif +#if defined(VERSION_EU) || defined(VERSION_SH) +s32 audio_shut_down_and_reset_step(void); +void audio_reset_session(void); +#else +void audio_reset_session(struct AudioSessionSettings *preset); +#endif +void discard_bank(s32 bankId); + +#ifdef VERSION_SH +void fill_filter(s16 filter[8], s32 arg1, s32 arg2); +u8 *func_sh_802f1d40(u32 size, s32 bank, u8 *arg2, s8 medium); +u8 *func_sh_802f1d90(u32 size, s32 bank, u8 *arg2, s8 medium); +void *unk_pool1_lookup(s32 poolIdx, s32 id); +void *unk_pool1_alloc(s32 poolIndex, s32 arg1, u32 size); +#endif + +#endif // AUDIO_HEAP_H diff --git a/src/decomp/audio/internal.h b/src/decomp/audio/internal.h new file mode 100644 index 0000000..a3fc8f5 --- /dev/null +++ b/src/decomp/audio/internal.h @@ -0,0 +1,886 @@ +#ifndef AUDIO_INTERNAL_H +#define AUDIO_INTERNAL_H + +#include + +#include + +#if defined(VERSION_EU) || defined(VERSION_SH) +#define SEQUENCE_PLAYERS 4 +#define SEQUENCE_CHANNELS 48 +#define SEQUENCE_LAYERS 64 +#else +#define SEQUENCE_PLAYERS 3 +#define SEQUENCE_CHANNELS 32 +#ifdef VERSION_JP +#define SEQUENCE_LAYERS 48 +#else +#define SEQUENCE_LAYERS 52 +#endif +#endif + +#define LAYERS_MAX 4 +#define CHANNELS_MAX 16 + +#define NO_LAYER ((struct SequenceChannelLayer *)(-1)) + +#define MUTE_BEHAVIOR_STOP_SCRIPT 0x80 // stop processing sequence/channel scripts +#define MUTE_BEHAVIOR_STOP_NOTES 0x40 // prevent further notes from playing +#define MUTE_BEHAVIOR_SOFTEN 0x20 // lower volume, by default to half + +#define SEQUENCE_PLAYER_STATE_0 0 +#define SEQUENCE_PLAYER_STATE_FADE_OUT 1 +#define SEQUENCE_PLAYER_STATE_2 2 +#define SEQUENCE_PLAYER_STATE_3 3 +#define SEQUENCE_PLAYER_STATE_4 4 + +#define NOTE_PRIORITY_DISABLED 0 +#define NOTE_PRIORITY_STOPPING 1 +#define NOTE_PRIORITY_MIN 2 +#define NOTE_PRIORITY_DEFAULT 3 + +#define TATUMS_PER_BEAT 48 + +// abi.h contains more details about the ADPCM and S8 codecs, "skip" skips codec processing +#define CODEC_ADPCM 0 +#define CODEC_S8 1 +#define CODEC_SKIP 2 + +#ifdef VERSION_JP +#define TEMPO_SCALE 1 +#else +#define TEMPO_SCALE TATUMS_PER_BEAT +#endif + +// TODO: US_FLOAT should probably be renamed to JP_DOUBLE since eu seems to use floats too +#ifdef VERSION_JP +#define US_FLOAT(x) x +#else +#define US_FLOAT(x) x ## f +#endif + +// Convert u8 or u16 to f32. On JP, this uses a u32->f32 conversion, +// resulting in more bloated codegen, while on US it goes through s32. +// Since u8 and u16 fit losslessly in both, behavior is the same. +#ifdef VERSION_JP +#define FLOAT_CAST(x) (f32) (x) +#else +#define FLOAT_CAST(x) (f32) (s32) (x) +#endif + +// No-op printf macro which leaves string literals in rodata in IDO. IDO +// doesn't support variadic macros, so instead we let the parameter list +// expand to a no-op comma expression. Another possibility is that it might +// have expanded to something with "if (0)". See also goddard/gd_main.h. +// On US/JP, -sopt optimizes away these except for external.c. +#ifdef __sgi +#define stubbed_printf +#else +#define stubbed_printf(...) +#endif + +#ifdef VERSION_EU +#define eu_stubbed_printf_0(msg) stubbed_printf(msg) +#define eu_stubbed_printf_1(msg, a) stubbed_printf(msg, a) +#define eu_stubbed_printf_2(msg, a, b) stubbed_printf(msg, a, b) +#define eu_stubbed_printf_3(msg, a, b, c) stubbed_printf(msg, a, b, c) +#else +#define eu_stubbed_printf_0(msg) +#define eu_stubbed_printf_1(msg, a) +#define eu_stubbed_printf_2(msg, a, b) +#define eu_stubbed_printf_3(msg, a, b, c) +#endif + +struct NotePool; + +struct AudioListItem +{ + // A node in a circularly linked list. Each node is either a head or an item: + // - Items can be either detached (prev = NULL), or attached to a list. + // 'value' points to something of interest. + // - List heads are always attached; if a list is empty, its head points + // to itself. 'count' contains the size of the list. + // If the list holds notes, 'pool' points back to the pool where it lives. + // Otherwise, that member is NULL. + struct AudioListItem *prev; + struct AudioListItem *next; + union { + void *value; // either Note* or SequenceChannelLayer* + s32 count; + } u; + struct NotePool *pool; +}; // size = 0x10 + +struct NotePool +{ + struct AudioListItem disabled; + struct AudioListItem decaying; + struct AudioListItem releasing; + struct AudioListItem active; +}; + +struct VibratoState { + /*0x00, 0x00*/ struct SequenceChannel *seqChannel; + /*0x04, 0x04*/ u32 time; +#if defined(VERSION_EU) || defined(VERSION_SH) + /* , 0x08*/ s16 *curve; + /* , 0x0C*/ f32 extent; + /* , 0x10*/ f32 rate; + /* , 0x14*/ u8 active; +#else + /*0x08, */ s8 *curve; + /*0x0C, */ u8 active; + /*0x0E, */ u16 rate; + /*0x10, */ u16 extent; +#endif + /*0x12, 0x16*/ u16 rateChangeTimer; + /*0x14, 0x18*/ u16 extentChangeTimer; + /*0x16, 0x1A*/ u16 delay; +}; // size = 0x18, 0x1C on EU + +// Pitch sliding by up to one octave in the positive direction. Negative +// direction is "supported" by setting extent to be negative. The code +// extrapolates exponentially in the wrong direction in that case, but that +// doesn't prevent seqplayer from doing it, AFAICT. +struct Portamento { + u8 mode; // bit 0x80 denotes something; the rest are an index 0-5 + f32 cur; + f32 speed; + f32 extent; +}; // size = 0x10 + +struct AdsrEnvelope { + s16 delay; + s16 arg; +}; // size = 0x4 + +struct AdpcmLoop +{ + u32 start; + u32 end; + u32 count; + u32 pad; + s16 state[16]; // only exists if count != 0. 8-byte aligned +}; + +struct AdpcmBook +{ + s32 order; + s32 npredictors; + s16 book[1]; // size 8 * order * npredictors. 8-byte aligned +}; + +struct AudioBankSample +{ +#ifdef VERSION_SH +#if !IS_BIG_ENDIAN + u32 size : 24; +#endif + /* 0x00 */ u32 codec : 4; + /* 0x00 */ u32 medium : 2; + /* 0x00 */ u32 bit1 : 1; + /* 0x00 */ u32 isPatched : 1; +#if IS_BIG_ENDIAN + /* 0x01 */ u32 size : 24; +#endif +#else + u8 unused; + u8 loaded; +#endif + u8 *sampleAddr; + struct AdpcmLoop *loop; + struct AdpcmBook *book; +#ifndef VERSION_SH + u32 sampleSize; // never read. either 0 or 1 mod 9, depending on padding +#endif +}; + +struct AudioBankSound +{ + struct AudioBankSample *sample; + f32 tuning; // frequency scale factor +}; // size = 0x8 + +struct Instrument +{ + /*0x00*/ u8 loaded; + /*0x01*/ u8 normalRangeLo; + /*0x02*/ u8 normalRangeHi; + /*0x03*/ u8 releaseRate; + /*0x04*/ struct AdsrEnvelope *envelope; + /*0x08*/ struct AudioBankSound lowNotesSound; + /*0x10*/ struct AudioBankSound normalNotesSound; + /*0x18*/ struct AudioBankSound highNotesSound; +}; // size = 0x20 + +struct Drum +{ + u8 releaseRate; + u8 pan; + u8 loaded; + struct AudioBankSound sound; + struct AdsrEnvelope *envelope; +}; + +struct AudioBank +{ + struct Drum **drums; + struct Instrument *instruments[1]; +}; // dynamic size + +struct CtlEntry +{ +#ifndef VERSION_SH + u8 unused; +#endif + u8 numInstruments; + u8 numDrums; +#ifdef VERSION_SH + u8 bankId1; + u8 bankId2; +#endif + struct Instrument **instruments; + struct Drum **drums; +}; // size = 0xC + +struct M64ScriptState { + u8 *pc; + u8 *stack[4]; + u8 remLoopIters[4]; + u8 depth; +}; // size = 0x1C + +// Also known as a Group, according to debug strings. +struct SequencePlayer +{ + /*US/JP, EU, SH */ +#if defined(VERSION_EU) || defined(VERSION_SH) + /*0x000, 0x000, 0x000*/ u8 enabled : 1; +#else + /*0x000, 0x000*/ volatile u8 enabled : 1; +#endif + /*0x000, 0x000*/ u8 finished : 1; // never read + /*0x000, 0x000*/ u8 muted : 1; + /*0x000, 0x000*/ u8 seqDmaInProgress : 1; + /*0x000, 0x000*/ u8 bankDmaInProgress : 1; +#if defined(VERSION_EU) || defined(VERSION_SH) + /* 0x000*/ u8 recalculateVolume : 1; +#endif +#ifdef VERSION_SH + /* 0x000*/ u8 unkSh: 1; +#endif +#if defined(VERSION_JP) || defined(VERSION_US) + /*0x001 */ s8 seqVariation; +#endif + /*0x002, 0x001, 0x001*/ u8 state; + /*0x003, 0x002*/ u8 noteAllocPolicy; + /*0x004, 0x003*/ u8 muteBehavior; + /*0x005, 0x004*/ u8 seqId; + /*0x006, 0x005*/ u8 defaultBank[1]; // must be an array to get a comparison + // to match; other u8's might also be part of that array + /*0x007, 0x006*/ u8 loadingBankId; +#if defined(VERSION_JP) || defined(VERSION_US) + /*0x008, ?????*/ u8 loadingBankNumInstruments; + /*0x009, ?????*/ u8 loadingBankNumDrums; +#endif +#if defined(VERSION_EU) || defined(VERSION_SH) + /* , 0x007, 0x007*/ s8 seqVariationEu[1]; +#endif + /*0x00A, 0x008*/ u16 tempo; // beats per minute in JP, tatums per minute in US/EU + /*0x00C, 0x00A*/ u16 tempoAcc; +#if defined(VERSION_JP) || defined(VERSION_US) + /*0x00E, 0x010*/ u16 fadeRemainingFrames; +#endif +#ifdef VERSION_SH + /* 0x00C*/ s16 tempoAdd; +#endif + /*0x010, 0x00C, 0x00E*/ s16 transposition; + /*0x012, 0x00E, 0x010*/ u16 delay; +#if defined(VERSION_EU) || defined(VERSION_SH) + /*0x00E, 0x010, 0x012*/ u16 fadeRemainingFrames; + /* , 0x012, 0x014*/ u16 fadeTimerUnkEu; +#endif + /*0x014, 0x014*/ u8 *seqData; // buffer of some sort + /*0x018, 0x018, 0x1C*/ f32 fadeVolume; // set to 1.0f + /*0x01C, 0x01C*/ f32 fadeVelocity; // set to 0.0f + /*0x020, 0x020, 0x024*/ f32 volume; // set to 0.0f + /*0x024, 0x024*/ f32 muteVolumeScale; // set to 0.5f +#if defined(VERSION_EU) || defined(VERSION_SH) + /* , 0x028, 0x02C*/ f32 fadeVolumeScale; + /* , 0x02C*/ f32 appliedFadeVolume; +#else + /* */ u8 pad2[4]; +#endif + /*0x02C, 0x030, 0x034*/ struct SequenceChannel *channels[CHANNELS_MAX]; + /*0x06C, 0x070*/ struct M64ScriptState scriptState; + /*0x088, 0x08C*/ u8 *shortNoteVelocityTable; + /*0x08C, 0x090*/ u8 *shortNoteDurationTable; + /*0x090, 0x094*/ struct NotePool notePool; + /*0x0D0, 0x0D4*/ OSMesgQueue seqDmaMesgQueue; + /*0x0E8, 0x0EC*/ OSMesg seqDmaMesg; + /*0x0EC, 0x0F0*/ OSIoMesg seqDmaIoMesg; + /*0x100, 0x108*/ OSMesgQueue bankDmaMesgQueue; + /*0x118, 0x120*/ OSMesg bankDmaMesg; + /*0x11C, 0x124*/ OSIoMesg bankDmaIoMesg; + /*0x130, 0x13C*/ u8 *bankDmaCurrMemAddr; +#if defined(VERSION_JP) || defined(VERSION_US) + /*0x134, ?????*/ struct AudioBank *loadingBank; +#endif + /*0x138, 0x140*/ uintptr_t bankDmaCurrDevAddr; + /*0x13C, 0x144*/ ssize_t bankDmaRemaining; +}; // size = 0x140, 0x148 on EU, 0x14C on SH + +struct AdsrSettings +{ + u8 releaseRate; +#if defined(VERSION_EU) || defined(VERSION_SH) + u8 sustain; +#else + u16 sustain; // sustain level, 2^16 = max +#endif + struct AdsrEnvelope *envelope; +}; // size = 0x8 + +struct AdsrState { + /*0x00, 0x00*/ u8 action; + /*0x01, 0x01*/ u8 state; +#if defined(VERSION_JP) || defined(VERSION_US) + /*0x02, */ s16 initial; // always 0 + /*0x04, */ s16 target; + /*0x06, */ s16 current; +#endif + /*0x08, 0x02*/ s16 envIndex; + /*0x0A, 0x04*/ s16 delay; +#if defined(VERSION_EU) || defined(VERSION_SH) + /* , 0x08*/ f32 sustain; + /* , 0x0C*/ f32 velocity; + /* , 0x10*/ f32 fadeOutVel; + /* , 0x14*/ f32 current; + /* , 0x18*/ f32 target; + s32 pad1C; +#else + /*0x0C, */ s16 sustain; + /*0x0E, */ s16 fadeOutVel; + /*0x10, */ s32 velocity; + /*0x14, */ s32 currentHiRes; + /*0x18, */ s16 *volOut; +#endif + /*0x1C, 0x20*/ struct AdsrEnvelope *envelope; +}; // size = 0x20, 0x24 in EU + +struct ReverbBitsData { + /* 0x00 */ u8 bit0 : 1; + /* 0x00 */ u8 bit1 : 1; + /* 0x00 */ u8 bit2 : 1; + /* 0x00 */ u8 usesHeadsetPanEffects : 1; + /* 0x00 */ u8 stereoHeadsetEffects : 2; + /* 0x00 */ u8 strongRight : 1; + /* 0x00 */ u8 strongLeft : 1; +}; + +union ReverbBits { + /* 0x00 */ struct ReverbBitsData s; + /* 0x00 */ u8 asByte; +}; +struct ReverbInfo { + u8 reverbVol; + u8 synthesisVolume; // UQ4.4, although 0 <= x < 1 is rounded up to 1 + u8 pan; + union ReverbBits reverbBits; + f32 freqScale; + f32 velocity; + s32 unused; + s16 *filter; +}; + +struct NoteAttributes +{ + u8 reverbVol; +#ifdef VERSION_SH + u8 synthesisVolume; // UQ4.4, although 0 <= x < 1 is rounded up to 1 +#endif +#if defined(VERSION_EU) || defined(VERSION_SH) + u8 pan; +#endif +#ifdef VERSION_SH + union ReverbBits reverbBits; +#endif + f32 freqScale; + f32 velocity; +#if defined(VERSION_JP) || defined(VERSION_US) + f32 pan; +#endif +#ifdef VERSION_SH + s16 *filter; +#endif +}; // size = 0x10 + +// Also known as a SubTrack, according to debug strings. +// Confusingly, a SubTrack is a container of Tracks. +struct SequenceChannel +{ + /* U/J, EU, SH */ + /*0x00, 0x00*/ u8 enabled : 1; + /*0x00, 0x00*/ u8 finished : 1; + /*0x00, 0x00*/ u8 stopScript : 1; + /*0x00, 0x00*/ u8 stopSomething2 : 1; // sets SequenceChannelLayer.stopSomething + /*0x00, 0x00*/ u8 hasInstrument : 1; + /*0x00, 0x00*/ u8 stereoHeadsetEffects : 1; + /*0x00, ????*/ u8 largeNotes : 1; // notes specify duration and velocity + /*0x00, ????*/ u8 unused : 1; // never read, set to 0 +#if defined(VERSION_EU) || defined(VERSION_SH) + /* , 0x01*/ union { + struct { + u8 freqScale : 1; + u8 volume : 1; + u8 pan : 1; + } as_bitfields; + u8 as_u8; + } changes; +#endif + /*0x01, 0x02*/ u8 noteAllocPolicy; + /*0x02, 0x03, 0x03*/ u8 muteBehavior; + /*0x03, 0x04, 0x04*/ u8 reverbVol; // until EU: Q1.7, after EU: UQ0.8 + /*0x04, ????*/ u8 notePriority; // 0-3 +#ifdef VERSION_SH + u8 unkSH06; // some priority +#endif + /*0x05, 0x06*/ u8 bankId; +#if defined(VERSION_EU) || defined(VERSION_SH) + /* , 0x07*/ u8 reverbIndex; + /* , 0x08, 0x09*/ u8 bookOffset; + /* , 0x09*/ u8 newPan; + /* , 0x0A*/ u8 panChannelWeight; // proportion of pan that comes from the channel (0..128) +#else + /*0x06, */ u8 updatesPerFrameUnused; +#endif +#ifdef VERSION_SH + /* 0x0C*/ u8 synthesisVolume; // UQ4.4, although 0 <= x < 1 is rounded up to 1 +#endif + /*0x08, 0x0C, 0x0E*/ u16 vibratoRateStart; // initially 0x800 + /*0x0A, 0x0E, 0x10*/ u16 vibratoExtentStart; + /*0x0C, 0x10, 0x12*/ u16 vibratoRateTarget; // initially 0x800 + /*0x0E, 0x12, 0x14*/ u16 vibratoExtentTarget; + /*0x10, 0x14, 0x16*/ u16 vibratoRateChangeDelay; + /*0x12, 0x16, 0x18*/ u16 vibratoExtentChangeDelay; + /*0x14, 0x18, 0x1A*/ u16 vibratoDelay; + /*0x16, 0x1A, 0x1C*/ u16 delay; + /*0x18, 0x1C, 0x1E*/ s16 instOrWave; // either 0 (none), instrument index + 1, or + // 0x80..0x83 for sawtooth/triangle/sine/square waves. + /*0x1A, 0x1E, 0x20*/ s16 transposition; + /*0x1C, 0x20, 0x24*/ f32 volumeScale; + /*0x20, 0x24, 0x28*/ f32 volume; +#if defined(VERSION_JP) || defined(VERSION_US) + /*0x24, */ f32 pan; + /*0x28, */ f32 panChannelWeight; // proportion of pan that comes from the channel (0..1) +#else + /* , 0x28*/ s32 pan; + /* , 0x2C*/ f32 appliedVolume; +#endif + /*0x2C, 0x30*/ f32 freqScale; + /*0x30, 0x34*/ u8 (*dynTable)[][2]; + /*0x34, ????*/ struct Note *noteUnused; // never read + /*0x38, ????*/ struct SequenceChannelLayer *layerUnused; // never read + /*0x3C, 0x40*/ struct Instrument *instrument; + /*0x40, 0x44*/ struct SequencePlayer *seqPlayer; + /*0x44, 0x48*/ struct SequenceChannelLayer *layers[LAYERS_MAX]; +#ifndef VERSION_SH + /*0x54, 0x58 */ s8 soundScriptIO[8]; // bridge between sound script and audio lib. For player 2, + // [0] contains enabled, [4] contains sound ID, [5] contains reverb adjustment +#endif + /*0x5C, 0x60*/ struct M64ScriptState scriptState; + /*0x78, 0x7C*/ struct AdsrSettings adsr; + /*0x80, 0x84*/ struct NotePool notePool; +#ifdef VERSION_SH + /* 0xC0*/ s8 soundScriptIO[8]; // bridge between sound script and audio lib. For player 2, + // [0] contains enabled, [4] contains sound ID, [5] contains reverb adjustment + /* 0xC8*/ u16 unkC8; + /* 0xCC*/ s16 *filter; +#endif +}; // size = 0xC0, 0xC4 in EU, 0xD0 in SH + +// Also known as a Track, according to debug strings. +struct SequenceChannelLayer +{ + /* U/J, EU, SH */ + /*0x00, 0x00*/ u8 enabled : 1; + /*0x00, 0x00*/ u8 finished : 1; + /*0x00, 0x00*/ u8 stopSomething : 1; // ? + /*0x00, 0x00*/ u8 continuousNotes : 1; // keep the same note for consecutive notes with the same sound +#if defined(VERSION_EU) || defined(VERSION_SH) + /* , 0x00*/ u8 unusedEu0b8 : 1; + /* , 0x00*/ u8 notePropertiesNeedInit : 1; + /* , 0x00*/ u8 ignoreDrumPan : 1; +#ifdef VERSION_SH + /* , , 0x01 */ union ReverbBits reverbBits; +#endif + /* , 0x01, 0x02*/ u8 instOrWave; +#endif + /*0x01, 0x02, 0x03*/ u8 status; // 0x03 in SH + /*0x02, 0x03*/ u8 noteDuration; // set to 0x80 + /*0x03, 0x04*/ u8 portamentoTargetNote; +#if defined(VERSION_EU) || defined(VERSION_SH) + /* , 0x05*/ u8 pan; // 0..128 + /* , 0x06, 0x07*/ u8 notePan; +#endif + /*0x04, 0x08*/ struct Portamento portamento; + /*0x14, 0x18*/ struct AdsrSettings adsr; + /*0x1C, 0x20*/ u16 portamentoTime; + /*0x1E, 0x22*/ s16 transposition; // #semitones added to play commands + // (m64 instruction encoding only allows referring to the limited range + // 0..0x3f; this makes 0x40..0x7f accessible as well) + /*0x20, 0x24, 0x24*/ f32 freqScale; +#ifdef VERSION_SH + /* 0x28*/ f32 freqScaleMultiplier; +#endif + /*0x24, 0x28, 0x2C*/ f32 velocitySquare; +#if defined(VERSION_JP) || defined(VERSION_US) + /*0x28, */ f32 pan; // 0..1 +#endif + /*0x2C, 0x2C, 0x30*/ f32 noteVelocity; +#if defined(VERSION_JP) || defined(VERSION_US) + /*0x30*/ f32 notePan; +#endif + /*0x34, 0x30, 0x34*/ f32 noteFreqScale; + /*0x38, 0x34*/ s16 shortNoteDefaultPlayPercentage; + /*0x3A, 0x36*/ s16 playPercentage; // it's not really a percentage... + /*0x3C, 0x38*/ s16 delay; + /*0x3E, 0x3A*/ s16 duration; + /*0x40, 0x3C*/ s16 delayUnused; // set to 'delay', never read + /*0x44, 0x40, 0x44*/ struct Note *note; + /*0x48, 0x44*/ struct Instrument *instrument; + /*0x4C, 0x48*/ struct AudioBankSound *sound; + /*0x50, 0x4C, 0x50*/ struct SequenceChannel *seqChannel; + /*0x54, 0x50*/ struct M64ScriptState scriptState; + /*0x70, 0x6C*/ struct AudioListItem listItem; +#if defined(VERSION_EU) + u8 pad2[4]; +#endif +}; // size = 0x80 + +#if defined(VERSION_EU) || defined(VERSION_SH) +struct NoteSynthesisState +{ + /*0x00*/ u8 restart; + /*0x01*/ u8 sampleDmaIndex; + /*0x02*/ u8 prevHeadsetPanRight; + /*0x03*/ u8 prevHeadsetPanLeft; +#ifdef VERSION_SH + /* 0x04*/ u8 reverbVol; + /* 0x05*/ u8 unk5; +#endif + /*0x04, 0x06*/ u16 samplePosFrac; + /*0x08*/ s32 samplePosInt; + /*0x0C*/ struct NoteSynthesisBuffers *synthesisBuffers; + /*0x10*/ s16 curVolLeft; // UQ0.16 (EU Q1.15) + /*0x12*/ s16 curVolRight; // UQ0.16 (EU Q1.15) +}; +struct NotePlaybackState +{ + /* U/J, EU, SH */ + /*0x04, 0x00, 0x00*/ u8 priority; + /* 0x01, 0x01*/ u8 waveId; + /* 0x02, 0x02*/ u8 sampleCountIndex; +#ifdef VERSION_SH + /* 0x03*/ u8 bankId; + /* 0x04*/ u8 unkSH34; +#endif + /*0x08, 0x04, 0x06*/ s16 adsrVolScale; + /*0x18, 0x08, 0x08*/ f32 portamentoFreqScale; + /*0x1C, 0x0C, 0x0C*/ f32 vibratoFreqScale; + /*0x28, 0x10, */ struct SequenceChannelLayer *prevParentLayer; + /*0x2C, 0x14, 0x14*/ struct SequenceChannelLayer *parentLayer; + /*0x30, 0x18, 0x18*/ struct SequenceChannelLayer *wantedParentLayer; + /* , 0x1C, 0x1C*/ struct NoteAttributes attributes; + /*0x54, 0x28, 0x2C*/ struct AdsrState adsr; + /*0x74, 0x4C, */ struct Portamento portamento; + /*0x84, 0x5C, */ struct VibratoState vibratoState; +}; +struct NoteSubEu +{ + /*0x00*/ volatile u8 enabled : 1; + /*0x00*/ u8 needsInit : 1; + /*0x00*/ u8 finished : 1; + /*0x00*/ u8 envMixerNeedsInit : 1; + /*0x00*/ u8 stereoStrongRight : 1; + /*0x00*/ u8 stereoStrongLeft : 1; + /*0x00*/ u8 stereoHeadsetEffects : 1; + /*0x00*/ u8 usesHeadsetPanEffects : 1; + /*0x01*/ u8 reverbIndex : 3; + /*0x01*/ u8 bookOffset : 3; + /*0x01*/ u8 isSyntheticWave : 1; + /*0x01*/ u8 hasTwoAdpcmParts : 1; +#ifdef VERSION_EU + /*0x02*/ u8 bankId; +#else + /*0x02*/ u8 synthesisVolume; // UQ4.4, although 0 <= x < 1 is rounded up to 1 +#endif + /*0x03*/ u8 headsetPanRight; + /*0x04*/ u8 headsetPanLeft; + /*0x05*/ u8 reverbVol; // UQ0.7 (EU Q1.7) + /*0x06*/ u16 targetVolLeft; // UQ0.12 (EU UQ0.10) + /*0x08*/ u16 targetVolRight; // UQ0.12 (EU UQ0.10) + /*0x0A*/ u16 resamplingRateFixedPoint; // stored as signed but loaded as u16 + /*0x0C*/ union { + s16 *samples; + struct AudioBankSound *audioBankSound; + } sound; +#ifdef VERSION_SH + /*0x10*/ s16 *filter; +#endif +}; +struct Note +{ + /* U/J, EU, SH */ + /*0xA4, 0x00, 0x00*/ struct AudioListItem listItem; + /* 0x10, 0x10*/ struct NoteSynthesisState synthesisState; + // The next members are actually part of a struct (NotePlaybackState), but + // that results in messy US/EU ifdefs. Instead we cast to a struct pointer + // when needed... This breaks alignment on non-N64 platforms, which we hack + // around by skipping the padding in that case. + // TODO: use macros or something instead. +#ifdef TARGET_N64 + u8 pad0[12]; +#endif + + /*0x04, 0x30, 0x30*/ u8 priority; + /* 0x31, 0x31*/ u8 waveId; + /* 0x32, 0x32*/ u8 sampleCountIndex; +#ifdef VERSION_SH + /* 0x33*/ u8 bankId; + /* 0x34*/ u8 unkSH34; +#endif + /*0x08, 0x34, 0x36*/ s16 adsrVolScale; + /*0x18, 0x38, */ f32 portamentoFreqScale; + /*0x1C, 0x3C, */ f32 vibratoFreqScale; + /*0x28, 0x40, */ struct SequenceChannelLayer *prevParentLayer; + /*0x2C, 0x44, 0x44*/ struct SequenceChannelLayer *parentLayer; + /*0x30, 0x48, 0x48*/ struct SequenceChannelLayer *wantedParentLayer; + /* , 0x4C, 0x4C*/ struct NoteAttributes attributes; + /*0x54, 0x58, 0x5C*/ struct AdsrState adsr; + /*0x74, 0x7C*/ struct Portamento portamento; + /*0x84, 0x8C*/ struct VibratoState vibratoState; + u8 pad3[8]; + /* , 0xB0, 0xB4*/ struct NoteSubEu noteSubEu; +}; // size = 0xC0, known to be 0xC8 on SH +#else +// volatile Note, needed in synthesis_process_notes +struct vNote +{ + /* U/J, EU */ + /*0x00*/ volatile u8 enabled : 1; + long long int force_structure_alignment; +}; // size = 0xC0 +struct Note +{ + /* U/J, EU */ + /*0x00*/ u8 enabled : 1; + /*0x00*/ u8 needsInit : 1; + /*0x00*/ u8 restart : 1; + /*0x00*/ u8 finished : 1; + /*0x00*/ u8 envMixerNeedsInit : 1; + /*0x00*/ u8 stereoStrongRight : 1; + /*0x00*/ u8 stereoStrongLeft : 1; + /*0x00*/ u8 stereoHeadsetEffects : 1; + /*0x01*/ u8 usesHeadsetPanEffects; + /*0x02*/ u8 unk2; + /*0x03*/ u8 sampleDmaIndex; + /*0x04, 0x30*/ u8 priority; + /*0x05*/ u8 sampleCount; // 0, 8, 16, 32 or 64 + /*0x06*/ u8 instOrWave; + /*0x07*/ u8 bankId; // in NoteSubEu on EU + /*0x08*/ s16 adsrVolScale; + /* */ u8 pad1[2]; + /*0x0C, 0xB3*/ u16 headsetPanRight; + /*0x0E, 0xB4*/ u16 headsetPanLeft; + /*0x10*/ u16 prevHeadsetPanRight; + /*0x12*/ u16 prevHeadsetPanLeft; + /*0x14*/ s32 samplePosInt; + /*0x18, 0x38*/ f32 portamentoFreqScale; + /*0x1C, 0x3C*/ f32 vibratoFreqScale; + /*0x20*/ u16 samplePosFrac; + /*0x24*/ struct AudioBankSound *sound; + /*0x28, 0x40*/ struct SequenceChannelLayer *prevParentLayer; + /*0x2C, 0x44*/ struct SequenceChannelLayer *parentLayer; + /*0x30, 0x48*/ struct SequenceChannelLayer *wantedParentLayer; + /*0x34*/ struct NoteSynthesisBuffers *synthesisBuffers; + /*0x38*/ f32 frequency; + /*0x3C*/ u16 targetVolLeft; // Q1.15, but will always be non-negative + /*0x3E*/ u16 targetVolRight; // Q1.15, but will always be non-negative + /*0x40*/ u8 reverbVol; // Q1.7 + /*0x41*/ u8 unused1; // never read, set to 0x3f + /*0x44*/ struct NoteAttributes attributes; + /*0x54, 0x58*/ struct AdsrState adsr; + /*0x74, 0x7C*/ struct Portamento portamento; + /*0x84, 0x8C*/ struct VibratoState vibratoState; + /*0x9C*/ s16 curVolLeft; // Q1.15, but will always be non-negative + /*0x9E*/ s16 curVolRight; // Q1.15, but will always be non-negative + /*0xA0*/ s16 reverbVolShifted; // Q1.15 + /*0xA2*/ s16 unused2; // never read, set to 0 + /*0xA4, 0x00*/ struct AudioListItem listItem; + /* */ u8 pad2[0xc]; +}; // size = 0xC0 +#endif + +struct NoteSynthesisBuffers +{ + s16 adpcmdecState[0x10]; + s16 finalResampleState[0x10]; +#ifdef VERSION_SH + s16 unk[0x10]; + s16 filterBuffer[0x20]; + s16 panSamplesBuffer[0x20]; +#else + s16 mixEnvelopeState[0x28]; + s16 panResampleState[0x10]; + s16 panSamplesBuffer[0x20]; + s16 dummyResampleState[0x10]; +#if defined(VERSION_JP) || defined(VERSION_US) + s16 samples[0x40]; +#endif +#endif +}; + +#ifdef VERSION_EU +struct ReverbSettingsEU +{ + u8 downsampleRate; + u8 windowSize; // To be multiplied by 64 + u16 gain; +}; +#else +struct ReverbSettingsEU +{ + u8 downsampleRate; // always 1 + u8 windowSize; // To be multiplied by 16 + u16 gain; + u16 unk4; // always zero + u16 unk6; // always zero + s8 unk8; // always -1 + u16 unkA; // always 0x3000 + s16 unkC; // always zero + s16 unkE; // always zero +}; +#endif + +struct AudioSessionSettingsEU +{ + /* 0x00 */ u32 frequency; + /* 0x04 */ u8 unk1; // always 1 + /* 0x05 */ u8 maxSimultaneousNotes; + /* 0x06 */ u8 numReverbs; // always 1 + /* 0x07 */ u8 unk2; // always 0 + /* 0x08 */ struct ReverbSettingsEU *reverbSettings; + /* 0x0C */ u16 volume; + /* 0x0E */ u16 unk3; // always 0 + /* 0x10 */ u32 persistentSeqMem; + /* 0x14 */ u32 persistentBankMem; +#ifdef VERSION_SH + /* 0x18 */ u32 unk18; // always 0 +#endif + /* 0x18, 0x1C */ u32 temporarySeqMem; + /* 0x1C, 0x20 */ u32 temporaryBankMem; +#ifdef VERSION_SH + /* 0x24 */ u32 unk24; // always 0 + /* 0x28 */ u32 unkMem28; // always 0 + /* 0x2C */ u32 unkMem2C; // always 0 +#endif +}; // 0x30 on shindou + +struct AudioSessionSettings +{ + /*0x00*/ u32 frequency; + /*0x04*/ u8 maxSimultaneousNotes; + /*0x05*/ u8 reverbDownsampleRate; // always 1 + /*0x06*/ u16 reverbWindowSize; + /*0x08*/ u16 reverbGain; + /*0x0A*/ u16 volume; + /*0x0C*/ u32 persistentSeqMem; + /*0x10*/ u32 persistentBankMem; + /*0x14*/ u32 temporarySeqMem; + /*0x18*/ u32 temporaryBankMem; +}; // size = 0x1C + +struct AudioBufferParametersEU { + /*0x00*/ s16 presetUnk4; // audio frames per vsync? + /*0x02*/ u16 frequency; + /*0x04*/ u16 aiFrequency; // ?16 + /*0x06*/ s16 samplesPerFrameTarget; + /*0x08*/ s16 maxAiBufferLength; + /*0x0A*/ s16 minAiBufferLength; + /*0x0C*/ s16 updatesPerFrame; + /*0x0E*/ s16 samplesPerUpdate; + /*0x10*/ s16 samplesPerUpdateMax; + /*0x12*/ s16 samplesPerUpdateMin; + /*0x14*/ f32 resampleRate; // contains 32000.0f / frequency + /*0x18*/ f32 updatesPerFrameInv; // 1.0f / updatesPerFrame + /*0x1C*/ f32 unkUpdatesPerFrameScaled; // 3.0f / (1280.0f * updatesPerFrame) +}; + +struct EuAudioCmd { + union { +#if IS_BIG_ENDIAN + struct { + u8 op; + u8 arg1; + u8 arg2; + u8 arg3; + } s; +#else + struct { + u8 arg3; + u8 arg2; + u8 arg1; + u8 op; + } s; +#endif + s32 first; + } u; + union { + s32 as_s32; + u32 as_u32; + f32 as_f32; +#if IS_BIG_ENDIAN + u8 as_u8; + s8 as_s8; +#else + struct { + u8 pad0[3]; + u8 as_u8; + }; + struct { + u8 pad1[3]; + s8 as_s8; + }; +#endif + } u2; +}; + +#ifdef VERSION_SH +struct PendingDmaSample { + u8 medium; + u8 bankId; + u8 idx; + uintptr_t devAddr; + void *vAddr; + u8 *resultSampleAddr; + s32 state; + s32 remaining; + s8 *io; + /*0x1C*/ struct AudioBankSample sample; + /*0x2C*/ OSMesgQueue queue; + /*0x44*/ OSMesg mesgs[1]; + /*0x48*/ OSIoMesg ioMesg; +}; + +struct UnkStruct80343D00 { + u32 someIndex; // array into one of the two slots below + struct PendingDmaSample arr[2]; +}; + +// in external.c +extern s32 D_SH_80343CF0; +extern struct UnkStruct80343D00 D_SH_80343D00; +#endif + +#endif // AUDIO_INTERNAL_H diff --git a/src/decomp/audio/load.c b/src/decomp/audio/load.c new file mode 100644 index 0000000..9dfe84c --- /dev/null +++ b/src/decomp/audio/load.c @@ -0,0 +1,1065 @@ +#ifndef VERSION_SH +#include + +#include "../../debug_print.h" +#include "data.h" +#include "external.h" +#include "heap.h" +#include "load.h" +#include "seqplayer.h" +#include "load_dat.h" + +#define ALIGN16(val) (((val) + 0xF) & ~0xF) + +struct SharedDma { + /*0x0*/ u8 *buffer; // target, points to pre-allocated buffer + /*0x4*/ uintptr_t source; // device address + /*0x8*/ u16 sizeUnused; // set to bufSize, never read + /*0xA*/ u16 bufSize; // size of buffer + /*0xC*/ u8 unused2; // set to 0, never read + /*0xD*/ u8 reuseIndex; // position in sSampleDmaReuseQueue1/2, if ttl == 0 + /*0xE*/ u8 ttl; // duration after which the DMA can be discarded +}; // size = 0x10 + +// EU only +void port_eu_init(void); + +struct Note *gNotes; + +#if defined(VERSION_EU) +UNUSED static u8 pad[4]; +#endif + +struct SequencePlayer gSequencePlayers[SEQUENCE_PLAYERS]; +struct SequenceChannel gSequenceChannels[SEQUENCE_CHANNELS]; +struct SequenceChannelLayer gSequenceLayers[SEQUENCE_LAYERS]; + +struct SequenceChannel gSequenceChannelNone; +struct AudioListItem gLayerFreeList; +struct NotePool gNoteFreeLists; + +OSMesgQueue gCurrAudioFrameDmaQueue; +OSMesg gCurrAudioFrameDmaMesgBufs[AUDIO_FRAME_DMA_QUEUE_SIZE]; +OSIoMesg gCurrAudioFrameDmaIoMesgBufs[AUDIO_FRAME_DMA_QUEUE_SIZE]; + +OSMesgQueue gAudioDmaMesgQueue; +OSMesg gAudioDmaMesg; +OSIoMesg gAudioDmaIoMesg; + +struct SharedDma sSampleDmas[0x60]; +u32 gSampleDmaNumListItems; // sh: 0x803503D4 +u32 sSampleDmaListSize1; // sh: 0x803503D8 +u32 sUnused80226B40; // set to 0, never read, sh: 0x803503DC + +// Circular buffer of DMAs with ttl = 0. tail <= head, wrapping around mod 256. +u8 sSampleDmaReuseQueue1[256]; +u8 sSampleDmaReuseQueue2[256]; +u8 sSampleDmaReuseQueueTail1; +u8 sSampleDmaReuseQueueTail2; +u8 sSampleDmaReuseQueueHead1; // sh: 0x803505E2 +u8 sSampleDmaReuseQueueHead2; // sh: 0x803505E3 + +// bss correct up to here + +ALSeqFile *gSeqFileHeader; +ALSeqFile *gAlCtlHeader; +ALSeqFile *gAlTbl; +u8 *gAlBankSets; +u16 gSequenceCount; + +struct CtlEntry *gCtlEntries; // sh: 0x803505F8 + +#if defined(VERSION_EU) +u32 padEuBss1; +struct AudioBufferParametersEU gAudioBufferParameters; +#elif defined(VERSION_US) || defined(VERSION_JP) +s32 gAiFrequency; +#endif + +u32 sDmaBufSize; +s32 gMaxAudioCmds; +s32 gMaxSimultaneousNotes; + +#if defined(VERSION_EU) +s16 gTempoInternalToExternal; +#else +s32 gSamplesPerFrameTarget; +s32 gMinAiBufferLength; + +s16 gTempoInternalToExternal; + +s8 gAudioUpdatesPerFrame; +#endif + +s8 gSoundMode; + +#if defined(VERSION_EU) +s8 gAudioUpdatesPerFrame; +#endif + +extern u64 gAudioGlobalsStartMarker; +extern u64 gAudioGlobalsEndMarker; + +extern struct seqFile *gSoundDataADSR; // sound_data.ctl +extern struct seqFile *gSoundDataRaw; // sound_data.tbl +extern struct seqFile *gMusicData; // sequences.s +extern u8* gBankSetsData; // bank_sets.s + +ALSeqFile *get_audio_file_header(s32 arg0); + +/** + * Performs an immediate DMA copy + */ +void audio_dma_copy_immediate(uintptr_t devAddr, void *vAddr, size_t nbytes) { + DEBUG_PRINT("audio_dma_copy_immediate()"); + DEBUG_PRINT("- dev addr: %x", devAddr); + DEBUG_PRINT("- vAddr: %x", vAddr); + DEBUG_PRINT("- # bytes: %d", nbytes); + eu_stubbed_printf_3("Romcopy %x -> %x ,size %x\n", devAddr, vAddr, nbytes); + DEBUG_PRINT("- invalidate d cache"); + osInvalDCache(vAddr, nbytes); + DEBUG_PRINT("- start dma"); + osPiStartDma(&gAudioDmaIoMesg, OS_MESG_PRI_HIGH, OS_READ, devAddr, vAddr, nbytes, + &gAudioDmaMesgQueue); + DEBUG_PRINT("- recv message"); + osRecvMesg(&gAudioDmaMesgQueue, NULL, OS_MESG_BLOCK); + eu_stubbed_printf_0("Romcopyend\n"); +} + +#ifdef VERSION_EU +u8 audioString34[] = "CAUTION:WAVE CACHE FULL %d"; +u8 audioString35[] = "BASE %x %x\n"; +u8 audioString36[] = "LOAD %x %x %x\n"; +u8 audioString37[] = "INSTTOP %x\n"; +u8 audioString38[] = "INSTMAP[0] %x\n"; +u8 audioString39[] = "already flags %d\n"; +u8 audioString40[] = "already flags %d\n"; +u8 audioString41[] = "ERR:SLOW BANK DMA BUSY\n"; +u8 audioString42[] = "ERR:SLOW DMA BUSY\n"; +u8 audioString43[] = "Check %d bank %d\n"; +u8 audioString44[] = "Cache Check\n"; +u8 audioString45[] = "NO BANK ERROR\n"; +u8 audioString46[] = "BANK %d LOADING START\n"; +u8 audioString47[] = "BANK %d LOAD MISS (NO MEMORY)!\n"; +u8 audioString48[] = "BANK %d ALREADY CACHED\n"; +u8 audioString49[] = "BANK LOAD MISS! FOR %d\n"; +#endif + +/** + * Performs an asynchronus (normal priority) DMA copy + */ +void audio_dma_copy_async(uintptr_t devAddr, void *vAddr, size_t nbytes, OSMesgQueue *queue, OSIoMesg *mesg) { + osInvalDCache(vAddr, nbytes); + osPiStartDma(mesg, OS_MESG_PRI_NORMAL, OS_READ, devAddr, vAddr, nbytes, queue); +} + +/** + * Performs a partial asynchronous (normal priority) DMA copy. This is limited + * to 0x1000 bytes transfer at once. + */ +void audio_dma_partial_copy_async(uintptr_t *devAddr, u8 **vAddr, ssize_t *remaining, OSMesgQueue *queue, OSIoMesg *mesg) { +#if defined(VERSION_EU) + ssize_t transfer = (*remaining >= 0x1000 ? 0x1000 : *remaining); +#else + ssize_t transfer = (*remaining < 0x1000 ? *remaining : 0x1000); +#endif + *remaining -= transfer; + osInvalDCache(*vAddr, transfer); + osPiStartDma(mesg, OS_MESG_PRI_NORMAL, OS_READ, *devAddr, *vAddr, transfer, queue); + *devAddr += transfer; + *vAddr += transfer; +} + +void decrease_sample_dma_ttls() { + u32 i; + + for (i = 0; i < sSampleDmaListSize1; i++) { +#if defined(VERSION_EU) + struct SharedDma *temp = &sSampleDmas[i]; +#else + struct SharedDma *temp = sSampleDmas + i; +#endif + if (temp->ttl != 0) { + temp->ttl--; + if (temp->ttl == 0) { + temp->reuseIndex = sSampleDmaReuseQueueHead1; + sSampleDmaReuseQueue1[sSampleDmaReuseQueueHead1++] = (u8) i; + } + } + } + + for (i = sSampleDmaListSize1; i < gSampleDmaNumListItems; i++) { +#if defined(VERSION_EU) + struct SharedDma *temp = &sSampleDmas[i]; +#else + struct SharedDma *temp = sSampleDmas + i; +#endif + if (temp->ttl != 0) { + temp->ttl--; + if (temp->ttl == 0) { + temp->reuseIndex = sSampleDmaReuseQueueHead2; + sSampleDmaReuseQueue2[sSampleDmaReuseQueueHead2++] = (u8) i; + } + } + } + + sUnused80226B40 = 0; +} + +void *dma_sample_data(uintptr_t devAddr, u32 size, s32 arg2, u8 *dmaIndexRef) { + s32 hasDma = FALSE; + struct SharedDma *dma; + uintptr_t dmaDevAddr; + u32 transfer; + u32 i; + u32 dmaIndex; + ssize_t bufferPos; + UNUSED u32 pad; + + if (arg2 != 0 || *dmaIndexRef >= sSampleDmaListSize1) { + for (i = sSampleDmaListSize1; i < gSampleDmaNumListItems; i++) { +#if defined(VERSION_EU) + dma = &sSampleDmas[i]; +#else + dma = sSampleDmas + i; +#endif + bufferPos = devAddr - dma->source; + if (0 <= bufferPos && (size_t) bufferPos <= dma->bufSize - size) { + // We already have a DMA request for this memory range. + if (dma->ttl == 0 && sSampleDmaReuseQueueTail2 != sSampleDmaReuseQueueHead2) { + // Move the DMA out of the reuse queue, by swapping it with the + // tail, and then incrementing the tail. + if (dma->reuseIndex != sSampleDmaReuseQueueTail2) { + sSampleDmaReuseQueue2[dma->reuseIndex] = + sSampleDmaReuseQueue2[sSampleDmaReuseQueueTail2]; + sSampleDmas[sSampleDmaReuseQueue2[sSampleDmaReuseQueueTail2]].reuseIndex = + dma->reuseIndex; + } + sSampleDmaReuseQueueTail2++; + } + dma->ttl = 60; + *dmaIndexRef = (u8) i; +#if defined(VERSION_EU) + return &dma->buffer[(devAddr - dma->source)]; +#else + return (devAddr - dma->source) + dma->buffer; +#endif + } + } + + if (sSampleDmaReuseQueueTail2 != sSampleDmaReuseQueueHead2 && arg2 != 0) { + // Allocate a DMA from reuse queue 2. This queue can be empty, since + // TTL 60 is pretty large. + dmaIndex = sSampleDmaReuseQueue2[sSampleDmaReuseQueueTail2]; + sSampleDmaReuseQueueTail2++; + dma = sSampleDmas + dmaIndex; + hasDma = TRUE; + } + } else { +#if defined(VERSION_EU) + dma = sSampleDmas; + dma += *dmaIndexRef; +#else + dma = sSampleDmas + *dmaIndexRef; +#endif + bufferPos = devAddr - dma->source; + if (0 <= bufferPos && (size_t) bufferPos <= dma->bufSize - size) { + // We already have DMA for this memory range. + if (dma->ttl == 0) { + // Move the DMA out of the reuse queue, by swapping it with the + // tail, and then incrementing the tail. + if (dma->reuseIndex != sSampleDmaReuseQueueTail1) { +#if defined(VERSION_EU) + if (1) { + } +#endif + sSampleDmaReuseQueue1[dma->reuseIndex] = + sSampleDmaReuseQueue1[sSampleDmaReuseQueueTail1]; + sSampleDmas[sSampleDmaReuseQueue1[sSampleDmaReuseQueueTail1]].reuseIndex = + dma->reuseIndex; + } + sSampleDmaReuseQueueTail1++; + } + dma->ttl = 2; +#if defined(VERSION_EU) + return dma->buffer + (devAddr - dma->source); +#else + return (devAddr - dma->source) + dma->buffer; +#endif + } + } + + if (!hasDma) { + // Allocate a DMA from reuse queue 1. This queue will hopefully never + // be empty, since TTL 2 is so small. + dmaIndex = sSampleDmaReuseQueue1[sSampleDmaReuseQueueTail1++]; + dma = sSampleDmas + dmaIndex; + hasDma = TRUE; + } + + transfer = dma->bufSize; + dmaDevAddr = devAddr & ~0xF; + dma->ttl = 2; + dma->source = dmaDevAddr; + dma->sizeUnused = transfer; +#ifdef VERSION_US + osInvalDCache(dma->buffer, transfer); +#endif +#if defined(VERSION_EU) + osPiStartDma(&gCurrAudioFrameDmaIoMesgBufs[gCurrAudioFrameDmaCount++], OS_MESG_PRI_NORMAL, + OS_READ, dmaDevAddr, dma->buffer, transfer, &gCurrAudioFrameDmaQueue); + *dmaIndexRef = dmaIndex; + return (devAddr - dmaDevAddr) + dma->buffer; +#else + gCurrAudioFrameDmaCount++; + osPiStartDma(&gCurrAudioFrameDmaIoMesgBufs[gCurrAudioFrameDmaCount - 1], OS_MESG_PRI_NORMAL, + OS_READ, dmaDevAddr, dma->buffer, transfer, &gCurrAudioFrameDmaQueue); + *dmaIndexRef = dmaIndex; + return dma->buffer + (devAddr - dmaDevAddr); +#endif +} + + +void init_sample_dma_buffers(UNUSED s32 arg0) { + s32 i; +#if defined(VERSION_EU) +#define j i +#else + s32 j; +#endif + +#if defined(VERSION_EU) + sDmaBufSize = 0x400; +#else + sDmaBufSize = 144 * 9; +#endif + +#if defined(VERSION_EU) + for (i = 0; i < gMaxSimultaneousNotes * 3 * gAudioBufferParameters.presetUnk4; i++) +#else + for (i = 0; i < gMaxSimultaneousNotes * 3; i++) +#endif + { + sSampleDmas[gSampleDmaNumListItems].buffer = soundAlloc(&gNotesAndBuffersPool, sDmaBufSize); + if (sSampleDmas[gSampleDmaNumListItems].buffer == NULL) { +#if defined(VERSION_EU) + break; +#else + goto out1; +#endif + } + sSampleDmas[gSampleDmaNumListItems].bufSize = sDmaBufSize; + sSampleDmas[gSampleDmaNumListItems].source = 0; + sSampleDmas[gSampleDmaNumListItems].sizeUnused = 0; + sSampleDmas[gSampleDmaNumListItems].unused2 = 0; + sSampleDmas[gSampleDmaNumListItems].ttl = 0; + gSampleDmaNumListItems++; + } +#if defined(VERSION_JP) || defined(VERSION_US) +out1: +#endif + + for (i = 0; (u32) i < gSampleDmaNumListItems; i++) { + sSampleDmaReuseQueue1[i] = (u8) i; + sSampleDmas[i].reuseIndex = (u8) i; + } + + for (j = gSampleDmaNumListItems; j < 0x100; j++) { + sSampleDmaReuseQueue1[j] = 0; + } + + sSampleDmaReuseQueueTail1 = 0; + sSampleDmaReuseQueueHead1 = (u8) gSampleDmaNumListItems; + sSampleDmaListSize1 = gSampleDmaNumListItems; + +#if defined(VERSION_EU) + sDmaBufSize = 0x200; +#else + sDmaBufSize = 160 * 9; +#endif + for (i = 0; i < gMaxSimultaneousNotes; i++) { + sSampleDmas[gSampleDmaNumListItems].buffer = soundAlloc(&gNotesAndBuffersPool, sDmaBufSize); + if (sSampleDmas[gSampleDmaNumListItems].buffer == NULL) { +#if defined(VERSION_EU) + break; +#else + goto out2; +#endif + } + sSampleDmas[gSampleDmaNumListItems].bufSize = sDmaBufSize; + sSampleDmas[gSampleDmaNumListItems].source = 0; + sSampleDmas[gSampleDmaNumListItems].sizeUnused = 0; + sSampleDmas[gSampleDmaNumListItems].unused2 = 0; + sSampleDmas[gSampleDmaNumListItems].ttl = 0; + gSampleDmaNumListItems++; + } +#if defined(VERSION_JP) || defined(VERSION_US) +out2: +#endif + + for (i = sSampleDmaListSize1; (u32) i < gSampleDmaNumListItems; i++) { + sSampleDmaReuseQueue2[i - sSampleDmaListSize1] = (u8) i; + sSampleDmas[i].reuseIndex = (u8)(i - sSampleDmaListSize1); + } + + // This probably meant to touch the range size1..size2 as well... but it + // doesn't matter, since these values are never read anyway. + for (j = gSampleDmaNumListItems; j < 0x100; j++) { + sSampleDmaReuseQueue2[j] = sSampleDmaListSize1; + } + + sSampleDmaReuseQueueTail2 = 0; + sSampleDmaReuseQueueHead2 = gSampleDmaNumListItems - sSampleDmaListSize1; +#if defined(VERSION_EU) +#undef j +#endif +} + +#if defined(VERSION_JP) || defined(VERSION_US) +// This function gets optimized out on US due to being static and never called +UNUSED static +#endif +void patch_sound(UNUSED struct AudioBankSound *sound, UNUSED u8 *memBase, UNUSED u8 *offsetBase) { + struct AudioBankSample *sample; + void *patched; + UNUSED u8 *mem; // unused on US + +#define PATCH(x, base) (patched = (void *)((uintptr_t) (x) + (uintptr_t) base)) + + if (sound->sample != NULL) { + sample = sound->sample = PATCH(sound->sample, memBase); + if (sample->loaded == 0) { + sample->sampleAddr = PATCH(sample->sampleAddr, offsetBase); + sample->loop = PATCH(sample->loop, memBase); + sample->book = PATCH(sample->book, memBase); + sample->loaded = 1; + } +#if defined(VERSION_EU) + else if (sample->loaded == 0x80) { + PATCH(sample->sampleAddr, offsetBase); + mem = soundAlloc(&gNotesAndBuffersPool, sample->sampleSize); + if (mem == NULL) { + sample->sampleAddr = patched; + sample->loaded = 1; + } else { + audio_dma_copy_immediate((uintptr_t) patched, mem, sample->sampleSize); + sample->loaded = 0x81; + sample->sampleAddr = mem; + } + sample->loop = PATCH(sample->loop, memBase); + sample->book = PATCH(sample->book, memBase); + } +#endif + } + +#undef PATCH +} + +#ifdef VERSION_EU +#define PATCH_SOUND patch_sound +#else +// copt inline of the above +#define PATCH_SOUND(_sound, mem, offset) \ +{ \ + struct AudioBankSound *sound = _sound; \ + struct AudioBankSample *sample; \ + void *patched; \ + if ((*sound).sample != (void *) 0) \ + { \ + patched = (void *)(((uintptr_t)(*sound).sample) + ((uintptr_t)((u8 *) mem))); \ + (*sound).sample = patched; \ + sample = (*sound).sample; \ + if ((*sample).loaded == 0) \ + { \ + patched = (void *)(((uintptr_t)(*sample).sampleAddr) + ((uintptr_t) offset)); \ + (*sample).sampleAddr = patched; \ + patched = (void *)(((uintptr_t)(*sample).loop) + ((uintptr_t)((u8 *) mem))); \ + (*sample).loop = patched; \ + patched = (void *)(((uintptr_t)(*sample).book) + ((uintptr_t)((u8 *) mem))); \ + (*sample).book = patched; \ + (*sample).loaded = 1; \ + } \ + } \ +} +#endif + +// on US/JP this inlines patch_sound, using some -sopt compiler flag +void patch_audio_bank(struct AudioBank *mem, u8 *offset, u32 numInstruments, u32 numDrums) { + struct Instrument *instrument; + struct Instrument **itInstrs; + struct Instrument **end; + struct AudioBank *temp; + u32 i; + void *patched; + struct Drum *drum; + struct Drum **drums; +#if defined(VERSION_EU) + u32 numDrums2; +#endif + +#define BASE_OFFSET_REAL(x, base) (void *)((uintptr_t) (x) + (uintptr_t) base) +#define PATCH(x, base) (patched = BASE_OFFSET_REAL(x, base)) +#define PATCH_MEM(x) x = PATCH(x, mem) + +#if defined(VERSION_JP) || defined(VERSION_US) +#define BASE_OFFSET(x, base) BASE_OFFSET_REAL(x, base) +#else +#define BASE_OFFSET(x, base) BASE_OFFSET_REAL(base, x) +#endif + + drums = mem->drums; +#if defined(VERSION_JP) || defined(VERSION_US) + if (drums != NULL && numDrums > 0) { + mem->drums = (void *)((uintptr_t) drums + (uintptr_t) mem); + if (numDrums > 0) //! unneeded when -sopt is enabled + for (i = 0; i < numDrums; i++) { +#else + numDrums2 = numDrums; + if (drums != NULL && numDrums2 > 0) { + mem->drums = PATCH(drums, mem); + for (i = 0; i < numDrums2; i++) { +#endif + patched = mem->drums[i]; + if (patched != NULL) { + drum = PATCH(patched, mem); + mem->drums[i] = drum; + if (drum->loaded == 0) { +#if defined(VERSION_JP) || defined(VERSION_US) + //! copt replaces drum with 'patched' for these two lines + PATCH_SOUND(&(*(struct Drum *)patched).sound, mem, offset); + patched = (*(struct Drum *)patched).envelope; +#else + patch_sound(&drum->sound, (u8 *) mem, offset); + patched = drum->envelope; +#endif + drum->envelope = BASE_OFFSET(mem, patched); + drum->loaded = 1; + } + + } + } + } + + //! Doesn't affect EU, but required for US/JP + temp = &*mem; +#if defined(VERSION_JP) || defined(VERSION_US) + if (numInstruments >= 1) +#endif + if (numInstruments > 0) { + //! Doesn't affect EU, but required for US/JP + struct Instrument **tempInst; + itInstrs = temp->instruments; + tempInst = temp->instruments; + end = numInstruments + tempInst; + +#if defined(VERSION_JP) || defined(VERSION_US) +l2: +#else + do { +#endif + if (*itInstrs != NULL) { + *itInstrs = BASE_OFFSET(*itInstrs, mem); + instrument = *itInstrs; + + if (instrument->loaded == 0) { + PATCH_SOUND(&instrument->lowNotesSound, (u8 *) mem, offset); + PATCH_SOUND(&instrument->normalNotesSound, (u8 *) mem, offset); + PATCH_SOUND(&instrument->highNotesSound, (u8 *) mem, offset); + patched = instrument->envelope; + instrument->envelope = BASE_OFFSET(mem, patched); + instrument->loaded = 1; + } + } + itInstrs++; +#if defined(VERSION_JP) || defined(VERSION_US) + //! goto generated by copt, required to match US/JP + if (end != itInstrs) { + goto l2; + } +#else + } while (end != itInstrs); +#endif + } +#undef PATCH_MEM +#undef PATCH +#undef BASE_OFFSET_REAL +#undef BASE_OFFSET +#undef PATCH_SOUND +} + +struct AudioBank *bank_load_immediate(s32 bankId, s32 arg1) { + DEBUG_PRINT("bank_load_immediate()"); + + UNUSED u32 pad1[4]; + u32 buf[4]; + u32 numInstruments, numDrums; + struct AudioBank *ret; + u8 *ctlData; + s32 alloc; + + // (This is broken if the length is 1 (mod 16), but that never happens -- + // it's always divisible by 4.) + DEBUG_PRINT("- getting alloc"); + alloc = gAlCtlHeader->seqArray[bankId].len + 0xf; + DEBUG_PRINT("- aligning"); + alloc = ALIGN16(alloc); + alloc -= 0x10; + DEBUG_PRINT("- getting ctl data for bank %d", bankId); + ctlData = gAlCtlHeader->seqArray[bankId].offset; + DEBUG_PRINT("- alloc bank or seq"); + ret = alloc_bank_or_seq(&gBankLoadedPool, 1, alloc, arg1, bankId); + if (ret == NULL) { + return NULL; + } + + DEBUG_PRINT("- copying dma immediate 1"); + DEBUG_PRINT("- ctlData: %x", ctlData); + audio_dma_copy_immediate((uintptr_t) ctlData, buf, 0x10); + DEBUG_PRINT("- getting nums"); + numInstruments = buf[0]; + numDrums = buf[1]; + DEBUG_PRINT("- copying dma immediate 2"); + audio_dma_copy_immediate((uintptr_t)(ctlData + 0x10), ret, alloc); + DEBUG_PRINT("- patching bank"); + patch_audio_bank(ret, gAlTbl->seqArray[bankId].offset, numInstruments, numDrums); + DEBUG_PRINT("- setting ctl entries"); + gCtlEntries[bankId].numInstruments = (u8) numInstruments; + gCtlEntries[bankId].numDrums = (u8) numDrums; + gCtlEntries[bankId].instruments = ret->instruments; + gCtlEntries[bankId].drums = ret->drums; + DEBUG_PRINT("- setting load status"); + gBankLoadStatus[bankId] = SOUND_LOAD_STATUS_COMPLETE; + return ret; +} + +struct AudioBank *bank_load_async(s32 bankId, s32 arg1, struct SequencePlayer *seqPlayer) { + u32 numInstruments, numDrums; + UNUSED u32 pad1[2]; + u32 buf[4]; + UNUSED u32 pad2; + size_t alloc; + struct AudioBank *ret; + u8 *ctlData; + OSMesgQueue *mesgQueue; +#if defined(VERSION_EU) + UNUSED u32 pad3; +#endif + alloc = gAlCtlHeader->seqArray[bankId].len + 0xf; + alloc = ALIGN16(alloc); + alloc -= 0x10; + ctlData = gAlCtlHeader->seqArray[bankId].offset; + ret = alloc_bank_or_seq(&gBankLoadedPool, 1, alloc, arg1, bankId); + if (ret == NULL) { + return NULL; + } + + audio_dma_copy_immediate((uintptr_t) ctlData, buf, 0x10); + numInstruments = buf[0]; + numDrums = buf[1]; + seqPlayer->loadingBankId = (u8) bankId; +#if defined(VERSION_EU) + gCtlEntries[bankId].numInstruments = numInstruments; + gCtlEntries[bankId].numDrums = numDrums; + gCtlEntries[bankId].instruments = ret->instruments; + gCtlEntries[bankId].drums = 0; + seqPlayer->bankDmaCurrMemAddr = (u8 *) ret; + seqPlayer->bankDmaCurrDevAddr = (uintptr_t)(ctlData + 0x10); + seqPlayer->bankDmaRemaining = alloc; + if (1) { + } +#else + seqPlayer->loadingBankNumInstruments = numInstruments; + seqPlayer->loadingBankNumDrums = numDrums; + seqPlayer->bankDmaCurrMemAddr = (u8 *) ret; + seqPlayer->loadingBank = ret; + seqPlayer->bankDmaCurrDevAddr = (uintptr_t)(ctlData + 0x10); + seqPlayer->bankDmaRemaining = alloc; +#endif + mesgQueue = &seqPlayer->bankDmaMesgQueue; + osCreateMesgQueue(mesgQueue, &seqPlayer->bankDmaMesg, 1); +#if defined(VERSION_JP) || defined(VERSION_US) + seqPlayer->bankDmaMesg = NULL; +#endif + seqPlayer->bankDmaInProgress = TRUE; + audio_dma_partial_copy_async(&seqPlayer->bankDmaCurrDevAddr, &seqPlayer->bankDmaCurrMemAddr, + &seqPlayer->bankDmaRemaining, mesgQueue, &seqPlayer->bankDmaIoMesg); + gBankLoadStatus[bankId] = SOUND_LOAD_STATUS_IN_PROGRESS; + return ret; +} + +void *sequence_dma_immediate(s32 seqId, s32 arg1) { + s32 seqLength; + void *ptr; + u8 *seqData; + + seqLength = gSeqFileHeader->seqArray[seqId].len + 0xf; + seqLength = ALIGN16(seqLength); + seqData = gSeqFileHeader->seqArray[seqId].offset; + ptr = alloc_bank_or_seq(&gSeqLoadedPool, 1, seqLength, arg1, seqId); + if (ptr == NULL) { + return NULL; + } + + audio_dma_copy_immediate((uintptr_t) seqData, ptr, seqLength); + gSeqLoadStatus[seqId] = SOUND_LOAD_STATUS_COMPLETE; + return ptr; +} + +void *sequence_dma_async(s32 seqId, s32 arg1, struct SequencePlayer *seqPlayer) { + s32 seqLength; + void *ptr; + u8 *seqData; + OSMesgQueue *mesgQueue; + + eu_stubbed_printf_1("Seq %d Loading Start\n", seqId); + seqLength = gSeqFileHeader->seqArray[seqId].len + 0xf; + seqLength = ALIGN16(seqLength); + seqData = gSeqFileHeader->seqArray[seqId].offset; + ptr = alloc_bank_or_seq(&gSeqLoadedPool, 1, seqLength, arg1, seqId); + if (ptr == NULL) { + eu_stubbed_printf_0("Heap Overflow Error\n"); + return NULL; + } + + if (seqLength <= 0x40) { + // Immediately load short sequenece + audio_dma_copy_immediate((uintptr_t) seqData, ptr, seqLength); + if (1) { + } + gSeqLoadStatus[seqId] = SOUND_LOAD_STATUS_COMPLETE; + } else { + audio_dma_copy_immediate((uintptr_t) seqData, ptr, 0x40); + mesgQueue = &seqPlayer->seqDmaMesgQueue; + osCreateMesgQueue(mesgQueue, &seqPlayer->seqDmaMesg, 1); +#if defined(VERSION_JP) || defined(VERSION_US) + seqPlayer->seqDmaMesg = NULL; +#endif + seqPlayer->seqDmaInProgress = TRUE; + audio_dma_copy_async((uintptr_t)(seqData + 0x40), (u8 *) ptr + 0x40, seqLength - 0x40, mesgQueue, + &seqPlayer->seqDmaIoMesg); + gSeqLoadStatus[seqId] = SOUND_LOAD_STATUS_IN_PROGRESS; + } + return ptr; +} + +u8 get_missing_bank(u32 seqId, s32 *nonNullCount, s32 *nullCount) { + void *temp; + u32 bankId; + u16 offset; + u8 i; + u8 ret; + + *nullCount = 0; + *nonNullCount = 0; +#if defined(VERSION_EU) + offset = ((u16 *) gAlBankSets)[seqId]; + for (i = gAlBankSets[offset++], ret = 0; i != 0; i--) { + bankId = gAlBankSets[offset++]; +#else + offset = ((u16 *) gAlBankSets)[seqId] + 1; + for (i = gAlBankSets[offset - 1], ret = 0; i != 0; i--) { + offset++; + bankId = gAlBankSets[offset - 1]; +#endif + + if (IS_BANK_LOAD_COMPLETE(bankId) == TRUE) { +#if defined(VERSION_EU) + temp = get_bank_or_seq(&gBankLoadedPool, 2, bankId); +#else + temp = get_bank_or_seq(&gBankLoadedPool, 2, gAlBankSets[offset - 1]); +#endif + } else { + temp = NULL; + } + + if (temp == NULL) { + (*nullCount)++; + ret = bankId; + } else { + (*nonNullCount)++; + } + } + + return ret; +} + +struct AudioBank *load_banks_immediate(s32 seqId, u8 *outDefaultBank) { + DEBUG_PRINT("load_banks_immediate()"); + void *ret; + u32 bankId; + u16 offset; + u8 i; + + DEBUG_PRINT("- getting offset"); + offset = ((u16 *) gAlBankSets)[seqId]; +#ifdef VERSION_EU + for (i = gAlBankSets[offset++]; i != 0; i--) { + bankId = gAlBankSets[offset++]; +#else + offset++; + DEBUG_PRINT("- looping through bank sets"); + for (i = gAlBankSets[offset - 1]; i != 0; i--) { + offset++; + DEBUG_PRINT("- getting bank id"); + bankId = gAlBankSets[offset - 1]; +#endif + + DEBUG_PRINT("- checking if bank load is complete"); + if (IS_BANK_LOAD_COMPLETE(bankId) == TRUE) { +#ifdef VERSION_EU + ret = get_bank_or_seq(&gBankLoadedPool, 2, bankId); +#else + DEBUG_PRINT("- getting bank or seq"); + ret = get_bank_or_seq(&gBankLoadedPool, 2, gAlBankSets[offset - 1]); +#endif + } else { + ret = NULL; + } + + if (ret == NULL) { + DEBUG_PRINT("- bank load immediate"); + ret = bank_load_immediate(bankId, 2); + } + } + *outDefaultBank = bankId; + return ret; +} + +void preload_sequence(u32 seqId, u8 preloadMask) { + DEBUG_PRINT("preload_sequence()"); + void *sequenceData; + u8 temp; + + if (seqId >= gSequenceCount) { + return; + } + + gAudioLoadLock = AUDIO_LOCK_LOADING; + if (preloadMask & PRELOAD_BANKS) { + DEBUG_PRINT("- load banks immediate"); + load_banks_immediate(seqId, &temp); + } + + if (preloadMask & PRELOAD_SEQUENCE) { + // @bug should be IS_SEQ_LOAD_COMPLETE + DEBUG_PRINT("- checking if bank load immediate"); + if (IS_BANK_LOAD_COMPLETE(seqId) == TRUE) { + eu_stubbed_printf_1("SEQ %d ALREADY CACHED\n", seqId); + DEBUG_PRINT("- getting bank or seq"); + sequenceData = get_bank_or_seq(&gSeqLoadedPool, 2, seqId); + } else { + sequenceData = NULL; + } + DEBUG_PRINT("- checking dma immediate"); + if (sequenceData == NULL && sequence_dma_immediate(seqId, 2) == NULL) { + gAudioLoadLock = AUDIO_LOCK_NOT_LOADING; + return; + } + } + + gAudioLoadLock = AUDIO_LOCK_NOT_LOADING; +} + +void load_sequence_internal(u32 player, u32 seqId, s32 loadAsync); + +void load_sequence(u32 player, u32 seqId, s32 loadAsync) { + if (!loadAsync) { + gAudioLoadLock = AUDIO_LOCK_LOADING; + } + load_sequence_internal(player, seqId, loadAsync); + if (!loadAsync) { + gAudioLoadLock = AUDIO_LOCK_NOT_LOADING; + } +} + +void load_sequence_internal(u32 player, u32 seqId, s32 loadAsync) { + void *sequenceData; + struct SequencePlayer *seqPlayer = &gSequencePlayers[player]; + UNUSED u32 padding[2]; + + if (seqId >= gSequenceCount) { + return; + } + + sequence_player_disable(seqPlayer); + if (loadAsync) { + s32 numMissingBanks = 0; + s32 dummy = 0; + s32 bankId = get_missing_bank(seqId, &dummy, &numMissingBanks); + if (numMissingBanks == 1) { + eu_stubbed_printf_0("Ok,one bank slow load Start \n"); + if (bank_load_async(bankId, 2, seqPlayer) == NULL) { + return; + } + // @bug This should set the last bank (i.e. the first in the JSON) + // as default, not the missing one. This code path never gets + // taken, though -- all sequence loading is synchronous. + seqPlayer->defaultBank[0] = bankId; + } else { + eu_stubbed_printf_1("Sorry,too many %d bank is none.fast load Start \n", numMissingBanks); + if (load_banks_immediate(seqId, &seqPlayer->defaultBank[0]) == NULL) { + return; + } + } + } else if (load_banks_immediate(seqId, &seqPlayer->defaultBank[0]) == NULL) { + return; + } + + eu_stubbed_printf_2("Seq %d:Default Load Id is %d\n", seqId, seqPlayer->defaultBank[0]); + eu_stubbed_printf_0("Seq Loading Start\n"); + + seqPlayer->seqId = seqId; + sequenceData = get_bank_or_seq(&gSeqLoadedPool, 2, seqId); + if (sequenceData == NULL) { + if (seqPlayer->seqDmaInProgress) { + eu_stubbed_printf_0("Error:Before Sequence-SlowDma remain.\n"); + eu_stubbed_printf_0(" Cancel Seq Start.\n"); + return; + } + if (loadAsync) { + sequenceData = sequence_dma_async(seqId, 2, seqPlayer); + } else { + sequenceData = sequence_dma_immediate(seqId, 2); + } + + if (sequenceData == NULL) { + return; + } + } + + eu_stubbed_printf_1("SEQ %d ALREADY CACHED\n", seqId); + init_sequence_player(player); + seqPlayer->scriptState.depth = 0; + seqPlayer->delay = 0; + seqPlayer->enabled = TRUE; + seqPlayer->seqData = sequenceData; + seqPlayer->scriptState.pc = sequenceData; +} + +// (void) must be omitted from parameters to fix stack with -framepointer +void audio_init() { + DEBUG_PRINT("audio_init()"); + UNUSED s8 pad[32]; + u8 buf[0x10]; + s32 i, j, UNUSED k; + UNUSED s32 lim1; // lim1 unused in EU + s32 lim2, UNUSED lim3; + UNUSED u32 size; + UNUSED u64 *ptr64; + void *data; + UNUSED s32 pad2; + + gAudioLoadLock = AUDIO_LOCK_UNINITIALIZED; + + DEBUG_PRINT("- setting values in unused"); + lim1 = gUnusedCount80333EE8; + for (i = 0; i < lim1; i++) { + gUnused80226E58[i] = 0; + gUnused80226E98[i] = 0; + } + + DEBUG_PRINT("- clearing audio heap"); + lim2 = gAudioHeapSize; + for (i = 0; i <= lim2 / 8 - 1; i++) { + ((u64 *) gAudioHeap)[i] = 0; + } + + + eu_stubbed_printf_1("AudioHeap is %x\n", gAudioHeapSize); + + for (i = 0; i < NUMAIBUFFERS; i++) { + gAiBufferLengths[i] = 0xa0; + } + + gAudioFrameCount = 0; + gAudioTaskIndex = 0; + gCurrAiBufferIndex = 0; + gSoundMode = 0; + gAudioTask = NULL; + gAudioTasks[0].task.t.data_size = 0; + gAudioTasks[1].task.t.data_size = 0; + osCreateMesgQueue(&gAudioDmaMesgQueue, &gAudioDmaMesg, 1); + osCreateMesgQueue(&gCurrAudioFrameDmaQueue, gCurrAudioFrameDmaMesgBufs, + ARRAY_COUNT(gCurrAudioFrameDmaMesgBufs)); + gCurrAudioFrameDmaCount = 0; + gSampleDmaNumListItems = 0; + + sound_init_main_pools(gAudioInitPoolSize); + + for (i = 0; i < NUMAIBUFFERS; i++) { + gAiBuffers[i] = soundAlloc(&gAudioInitPool, AIBUFFER_LEN); + + for (j = 0; j < (s32) (AIBUFFER_LEN / sizeof(s16)); j++) { + gAiBuffers[i][j] = 0; + } + } + + audio_reset_session(&gAudioSessionPresets[0]); + + // Not sure about these prints + eu_stubbed_printf_1("Heap reset.Synth Change %x \n", 0); + eu_stubbed_printf_3("Heap %x %x %x\n", 0, 0, 0); + eu_stubbed_printf_0("Main Heap Initialize.\n"); + + // Load headers for sounds and sequences + gSeqFileHeader = (ALSeqFile *) buf; + data = gMusicData; + audio_dma_copy_immediate((uintptr_t) data, gSeqFileHeader, 0x10); + gSequenceCount = gSeqFileHeader->seqCount; + size = ALIGN16(gSequenceCount * sizeof(ALSeqData) + 4); + gSeqFileHeader = soundAlloc(&gAudioInitPool, size); + audio_dma_copy_immediate((uintptr_t) data, gSeqFileHeader, size); + alSeqFileNew(gSeqFileHeader, data); + + // Load header for CTL (instrument metadata) + DEBUG_PRINT("- loading ctl header"); + gAlCtlHeader = (ALSeqFile *) buf; + data = gSoundDataADSR; + DEBUG_PRINT("- copying dma immediate"); + audio_dma_copy_immediate((uintptr_t) data, gAlCtlHeader, 0x10); + size = gAlCtlHeader->seqCount * sizeof(ALSeqData) + 4; + DEBUG_PRINT("- seq count: %d", gAlCtlHeader->seqCount); + DEBUG_PRINT("- size after read: %d", size); + size = ALIGN16(size); + DEBUG_PRINT("- size after align: %d", size); + gCtlEntries = soundAlloc(&gAudioInitPool, gAlCtlHeader->seqCount * sizeof(struct CtlEntry)); + gAlCtlHeader = soundAlloc(&gAudioInitPool, size); + DEBUG_PRINT("@ copying data from sound data adsr to ctl header"); + DEBUG_PRINT("- data: %x", data); + DEBUG_PRINT("- ctl header: %x", gAlCtlHeader); + DEBUG_PRINT("- size: %d", size); + audio_dma_copy_immediate((uintptr_t) data, gAlCtlHeader, size); + DEBUG_PRINT("- creating new seq file for ctl"); + alSeqFileNew(gAlCtlHeader, data); + + // Load header for TBL (raw sound data) + DEBUG_PRINT("- loading tbl"); + gAlTbl = (ALSeqFile *) buf; + DEBUG_PRINT("- copying dma"); + audio_dma_copy_immediate((uintptr_t) data, gAlTbl, 0x10); + DEBUG_PRINT("- tbl seq count: %d", gAlTbl->seqCount); + size = gAlTbl->seqCount * sizeof(ALSeqData) + 4; + DEBUG_PRINT("- size: %d", size); + size = ALIGN16(size); + DEBUG_PRINT("- size after align: %d", size); + gAlTbl = soundAlloc(&gAudioInitPool, size); + DEBUG_PRINT("- tbl alloc at %x", gAlTbl); + DEBUG_PRINT("- gSoundDataRaw at %x", gSoundDataRaw); + audio_dma_copy_immediate((uintptr_t) gSoundDataRaw, gAlTbl, size); + alSeqFileNew(gAlTbl, gSoundDataRaw); + + // Load bank sets for each sequence + gAlBankSets = soundAlloc(&gAudioInitPool, 0x100); + audio_dma_copy_immediate((uintptr_t) gBankSetsData, gAlBankSets, 0x100); + + init_sequence_players(); + gAudioLoadLock = AUDIO_LOCK_NOT_LOADING; + // Should probably contain the sizes of the data banks, but those aren't + // easily accessible from here. + eu_stubbed_printf_0("---------- Init Completed. ------------\n"); + eu_stubbed_printf_1(" Syndrv :[%6d]\n", 0); // gSoundDataADSR + eu_stubbed_printf_1(" Seqdrv :[%6d]\n", 0); // gMusicData + eu_stubbed_printf_1(" audiodata :[%6d]\n", 0); // gSoundDataRaw + eu_stubbed_printf_0("---------------------------------------\n"); +} +#endif diff --git a/src/decomp/audio/load.h b/src/decomp/audio/load.h new file mode 100644 index 0000000..86f8fa8 --- /dev/null +++ b/src/decomp/audio/load.h @@ -0,0 +1,116 @@ +#ifndef AUDIO_LOAD_H +#define AUDIO_LOAD_H + +#include + +#include "internal.h" + +#define AUDIO_FRAME_DMA_QUEUE_SIZE 0x40 + +#define PRELOAD_BANKS 2 +#define PRELOAD_SEQUENCE 1 + +#define IS_SEQUENCE_CHANNEL_VALID(ptr) ((uintptr_t)(ptr) != (uintptr_t)&gSequenceChannelNone) + +extern struct Note *gNotes; + +// Music in SM64 is played using 3 players: +// gSequencePlayers[0] is level background music +// gSequencePlayers[1] is misc music, like the puzzle jingle +// gSequencePlayers[2] is sound +extern struct SequencePlayer gSequencePlayers[SEQUENCE_PLAYERS]; + +extern struct SequenceChannel gSequenceChannels[SEQUENCE_CHANNELS]; +extern struct SequenceChannelLayer gSequenceLayers[SEQUENCE_LAYERS]; + +extern struct SequenceChannel gSequenceChannelNone; + +extern struct AudioListItem gLayerFreeList; +extern struct NotePool gNoteFreeLists; + +extern OSMesgQueue gCurrAudioFrameDmaQueue; +extern u32 gSampleDmaNumListItems; +extern ALSeqFile *gAlCtlHeader; +extern ALSeqFile *gAlTbl; +extern ALSeqFile *gSeqFileHeader; +extern u8 *gAlBankSets; + +extern struct CtlEntry *gCtlEntries; +#if defined(VERSION_EU) || defined(VERSION_SH) +extern struct AudioBufferParametersEU gAudioBufferParameters; +#endif +extern s32 gAiFrequency; +#ifdef VERSION_SH +extern s16 gCurrAiBufferLength; +extern s32 D_SH_8034F68C; +#endif +extern s32 gMaxAudioCmds; + +extern s32 gMaxSimultaneousNotes; +extern s32 gSamplesPerFrameTarget; +extern s32 gMinAiBufferLength; +extern s16 gTempoInternalToExternal; +extern s8 gAudioUpdatesPerFrame; // = 4 +extern s8 gSoundMode; + +#ifdef VERSION_SH +extern OSMesgQueue gUnkQueue1; + +struct UnkStructSH8034EC88 { + u8 *endAndMediumIdentification; + struct AudioBankSample *sample; + u8 *ramAddr; + u32 encodedInfo; + s32 isFree; +}; + +struct PatchStruct { + s32 bankId1; + s32 bankId2; + void *baseAddr1; + void *baseAddr2; + s32 medium1; + s32 medium2; +}; + +extern struct UnkStructSH8034EC88 D_SH_8034EC88[0x80]; +#endif + +struct AudioBank *bank_load_immediate(s32 bankId, s32 arg1); + +void audio_dma_partial_copy_async(uintptr_t *devAddr, u8 **vAddr, ssize_t *remaining, OSMesgQueue *queue, OSIoMesg *mesg); +void decrease_sample_dma_ttls(void); +#ifdef VERSION_SH +void *dma_sample_data(uintptr_t devAddr, u32 size, s32 arg2, u8 *dmaIndexRef, s32 medium); +#else +void *dma_sample_data(uintptr_t devAddr, u32 size, s32 arg2, u8 *dmaIndexRef); +#endif +void init_sample_dma_buffers(s32 arg0); +#if defined(VERSION_SH) +void patch_audio_bank(s32 bankId, struct AudioBank *mem, struct PatchStruct *patchInfo); +#else +void patch_audio_bank(struct AudioBank *mem, u8 *offset, u32 numInstruments, u32 numDrums); +#endif +#ifdef VERSION_SH +void preload_sequence(u32 seqId, s32 preloadMask); +#else +void preload_sequence(u32 seqId, u8 preloadMask); +#endif +void load_sequence(u32 player, u32 seqId, s32 loadAsync); + +#ifdef VERSION_SH +void func_sh_802f3158(s32 seqId, s32 arg1, s32 arg2, OSMesgQueue *retQueue); +u8 *func_sh_802f3220(u32 seqId, u32 *a1); +struct AudioBankSample *func_sh_802f4978(s32 bankId, s32 idx); +s32 func_sh_802f47c8(s32 bankId, u8 idx, s8 *io); +void *func_sh_802f3f08(s32 poolIdx, s32 arg1, s32 arg2, s32 arg3, OSMesgQueue *retQueue); +void func_sh_802f41e4(s32 audioResetStatus); +BAD_RETURN(s32) func_sh_802f3368(s32 bankId); +void *func_sh_802f3764(s32 arg0, s32 idx, s32 *arg2); +s32 func_sh_802f3024(s32 bankId, s32 instId, s32 arg2); +void func_sh_802f30f4(s32 arg0, s32 arg1, s32 arg2, OSMesgQueue *arg3); +void func_sh_802f3288(s32 idx); + +#endif + +#endif // AUDIO_LOAD_H diff --git a/src/decomp/audio/load_dat.c b/src/decomp/audio/load_dat.c new file mode 100644 index 0000000..9c99214 --- /dev/null +++ b/src/decomp/audio/load_dat.c @@ -0,0 +1,11 @@ +#include "load_dat.h" + +struct seqFile *gSoundDataADSR; + +struct seqFile *gSoundDataRaw; + +struct seqFile *gMusicData; + +#ifndef VERSION_SH +unsigned char* gBankSetsData; +#endif \ No newline at end of file diff --git a/src/decomp/audio/load_dat.h b/src/decomp/audio/load_dat.h new file mode 100644 index 0000000..e563902 --- /dev/null +++ b/src/decomp/audio/load_dat.h @@ -0,0 +1,14 @@ +#pragma once + +#include "../tools/convTypes.h" +#include + +extern struct seqFile *gSoundDataADSR; + +extern struct seqFile *gSoundDataRaw; + +extern struct seqFile *gMusicData; + +#ifndef VERSION_SH +extern unsigned char *gBankSetsData; +#endif \ No newline at end of file diff --git a/src/decomp/audio/load_sh.c b/src/decomp/audio/load_sh.c new file mode 100644 index 0000000..c57e722 --- /dev/null +++ b/src/decomp/audio/load_sh.c @@ -0,0 +1,1639 @@ +#ifdef VERSION_SH +#include + +#include "data.h" +#include "external.h" +#include "heap.h" +#include "load.h" +#include "seqplayer.h" + +#define ALIGN16(val) (((val) + 0xF) & ~0xF) + +struct SharedDma { + /*0x0*/ u8 *buffer; // target, points to pre-allocated buffer + /*0x4*/ uintptr_t source; // device address + /*0x8*/ u16 sizeUnused; // set to bufSize, never read + /*0xA*/ u16 bufSize; // size of buffer + /*0xC*/ u8 unused2; // set to 0, never read + /*0xD*/ u8 reuseIndex; // position in sSampleDmaReuseQueue1/2, if ttl == 0 + /*0xE*/ u8 ttl; // duration after which the DMA can be discarded +}; // size = 0x10 + +void port_eu_init(void); +void patch_sound(struct AudioBankSound *sound, struct AudioBank *memBase, struct PatchStruct *patchInfo); +void *func_802f3f08(s32 poolIdx, s32 idx, s32 numChunks, s32 arg3, OSMesgQueue *retQueue); + +struct Note *gNotes; + +UNUSED static u32 pad; + +struct SequencePlayer gSequencePlayers[SEQUENCE_PLAYERS]; +struct SequenceChannel gSequenceChannels[SEQUENCE_CHANNELS]; +struct SequenceChannelLayer gSequenceLayers[SEQUENCE_LAYERS]; + +struct SequenceChannel gSequenceChannelNone; +struct AudioListItem gLayerFreeList; +struct NotePool gNoteFreeLists; + +struct AudioBankSample *D_SH_8034EA88[0x80]; +struct UnkStructSH8034EC88 D_SH_8034EC88[0x80]; +s32 D_SH_8034F688; // index into D_SH_8034EA88 +s32 D_SH_8034F68C; // index or size for D_SH_8034EC88 + +struct PendingDmaAudioBank { + s8 inProgress; + s8 timer; + s8 medium; + struct AudioBank *audioBank; + uintptr_t devAddr; + void *vAddr; + u32 remaining; + u32 transferSize; + u32 encodedInfo; + OSMesgQueue *retQueue; + OSMesgQueue dmaRetQueue; + OSMesg mesgs[1]; + OSIoMesg ioMesg; +}; +struct PendingDmaAudioBank sPendingDmaAudioBanks[16]; + +OSMesgQueue gUnkQueue1; +OSMesg gUnkMesgBufs1[0x10]; +OSMesgQueue gUnkQueue2; +OSMesg gUnkMesgBufs2[0x10]; + +OSMesgQueue gCurrAudioFrameDmaQueue; +OSMesg gCurrAudioFrameDmaMesgBufs[AUDIO_FRAME_DMA_QUEUE_SIZE]; +OSIoMesg gCurrAudioFrameDmaIoMesgBufs[AUDIO_FRAME_DMA_QUEUE_SIZE]; + +OSMesgQueue gAudioDmaMesgQueue; +OSMesg gAudioDmaMesg; +OSIoMesg gAudioDmaIoMesg; + +struct SharedDma *sSampleDmas; +u32 gSampleDmaNumListItems; +u32 sSampleDmaListSize1; +u32 sUnused80226B40; // set to 0, never read + +// Circular buffer of DMAs with ttl = 0. tail <= head, wrapping around mod 256. +u8 sSampleDmaReuseQueue1[256]; +u8 sSampleDmaReuseQueue2[256]; +u8 sSampleDmaReuseQueueTail1; +u8 sSampleDmaReuseQueueTail2; +u8 sSampleDmaReuseQueueHead1; +u8 sSampleDmaReuseQueueHead2; + +ALSeqFile *gSeqFileHeader; +ALSeqFile *gAlCtlHeader; +ALSeqFile *gAlTbl; +u8 *gAlBankSets; +u16 gSequenceCount; + +struct CtlEntry *gCtlEntries; + +struct AudioBufferParametersEU gAudioBufferParameters; +u32 sDmaBufSize; +s32 gMaxAudioCmds; +s32 gMaxSimultaneousNotes; + +s16 gTempoInternalToExternal; + +s8 gSoundMode; + +s8 gAudioUpdatesPerFrame; + +extern u64 gAudioGlobalsStartMarker; +extern u64 gAudioGlobalsEndMarker; + +extern u8 gSoundDataADSR[]; // ctl +extern u8 gSoundDataRaw[]; // tbl +extern u8 gMusicData[]; // sequences + +ALSeqFile *get_audio_file_header(s32 arg0); + +void *func_sh_802f3688(s32 bankId); +void *get_bank_or_seq_wrapper(s32 arg0, s32 arg1); +void func_sh_802f3d78(uintptr_t devAddr, void *vAddr, size_t nbytes, s32 arg3); +void func_sh_802f3c38(uintptr_t devAddr, void *vAddr, size_t nbytes, s32 medium); +s32 func_sh_802f3dd0(OSIoMesg *m, s32 pri, s32 direction, uintptr_t devAddr, + void *dramAddr, s32 size, OSMesgQueue *retQueue, s32 medium, UNUSED const char *reason); +void func_sh_802f4a4c(s32 audioResetStatus); +void func_sh_802f4bd8(struct PendingDmaSample *arg0, s32 len); +void func_sh_802f4c5c(uintptr_t devAddr, void *vAddr, size_t nbytes, s32 arg3); +struct PendingDmaAudioBank *func_sh_802f4cb4(uintptr_t devAddr, void *vAddr, s32 size, + s32 medium, s32 numChunks, OSMesgQueue *retQueue, s32 encodedInfo); +void func_sh_802f4dcc(s32 audioResetStatus); +void func_sh_802f4e50(struct PendingDmaAudioBank *audioBank, s32 audioResetStatus); +void func_sh_802f50ec(struct PendingDmaAudioBank *arg0, size_t len); +void func_sh_802f517c(uintptr_t devAddr, void *vAddr, size_t nbytes, s32 arg3); +BAD_RETURN(s32) func_sh_802f5310(s32 bankId, struct AudioBank *mem, struct PatchStruct *patchInfo, s32 arg3); +s32 func_sh_802f573c(s32 audioResetStatus); +void *func_sh_802f3564(s32 seqId); +s32 func_sh_802f3ec4(s32 arg0, uintptr_t *arg1); +void func_sh_802f3ed4(UNUSED s32 arg0, UNUSED s32 arg1, UNUSED void *vAddr, UNUSED size_t nbytes); + +s32 canonicalize_index(s32 poolIdx, s32 idx); + +void decrease_sample_dma_ttls() { + u32 i; + + for (i = 0; i < sSampleDmaListSize1; i++) { + struct SharedDma *temp = &sSampleDmas[i]; + if (temp->ttl != 0) { + temp->ttl--; + if (temp->ttl == 0) { + temp->reuseIndex = sSampleDmaReuseQueueHead1; + sSampleDmaReuseQueue1[sSampleDmaReuseQueueHead1++] = (u8) i; + } + } + } + + for (i = sSampleDmaListSize1; i < gSampleDmaNumListItems; i++) { + struct SharedDma *temp = &sSampleDmas[i]; + if (temp->ttl != 0) { + temp->ttl--; + if (temp->ttl == 0) { + temp->reuseIndex = sSampleDmaReuseQueueHead2; + sSampleDmaReuseQueue2[sSampleDmaReuseQueueHead2++] = (u8) i; + } + } + } + + sUnused80226B40 = 0; +} + +extern char shindouDebugPrint62[]; // "SUPERDMA" +void *dma_sample_data(uintptr_t devAddr, u32 size, s32 arg2, u8 *dmaIndexRef, s32 medium) { + UNUSED s32 sp60; + struct SharedDma *dma; + s32 hasDma = FALSE; + uintptr_t dmaDevAddr; + UNUSED u32 pad; + u32 dmaIndex; + u32 transfer; + ssize_t bufferPos; + u32 i; + + if (arg2 != 0 || *dmaIndexRef >= sSampleDmaListSize1) { + for (i = sSampleDmaListSize1; i < gSampleDmaNumListItems; i++) { + dma = &sSampleDmas[i]; + bufferPos = devAddr - dma->source; + if (0 <= bufferPos && (size_t) bufferPos <= dma->bufSize - size) { + // We already have a DMA request for this memory range. + if (dma->ttl == 0 && sSampleDmaReuseQueueTail2 != sSampleDmaReuseQueueHead2) { + // Move the DMA out of the reuse queue, by swapping it with the + // tail, and then incrementing the tail. + if (dma->reuseIndex != sSampleDmaReuseQueueTail2) { + sSampleDmaReuseQueue2[dma->reuseIndex] = + sSampleDmaReuseQueue2[sSampleDmaReuseQueueTail2]; + sSampleDmas[sSampleDmaReuseQueue2[sSampleDmaReuseQueueTail2]].reuseIndex = + dma->reuseIndex; + } + sSampleDmaReuseQueueTail2++; + } + dma->ttl = 60; + *dmaIndexRef = (u8) i; + return &dma->buffer[(devAddr - dma->source)]; + } + } + + if (sSampleDmaReuseQueueTail2 != sSampleDmaReuseQueueHead2 && arg2 != 0) { + // Allocate a DMA from reuse queue 2. This queue can be empty, since + // TTL 60 is pretty large. + dmaIndex = sSampleDmaReuseQueue2[sSampleDmaReuseQueueTail2]; + sSampleDmaReuseQueueTail2++; + dma = sSampleDmas + dmaIndex; + hasDma = TRUE; + } + } else { + dma = sSampleDmas + *dmaIndexRef; + bufferPos = devAddr - dma->source; + if (0 <= bufferPos && (size_t) bufferPos <= dma->bufSize - size) { + // We already have DMA for this memory range. + if (dma->ttl == 0) { + // Move the DMA out of the reuse queue, by swapping it with the + // tail, and then incrementing the tail. + if (dma->reuseIndex != sSampleDmaReuseQueueTail1) { + sSampleDmaReuseQueue1[dma->reuseIndex] = + sSampleDmaReuseQueue1[sSampleDmaReuseQueueTail1]; + sSampleDmas[sSampleDmaReuseQueue1[sSampleDmaReuseQueueTail1]].reuseIndex = + dma->reuseIndex; + } + sSampleDmaReuseQueueTail1++; + } + dma->ttl = 2; + return dma->buffer + (devAddr - dma->source); + } + } + + if (!hasDma) { + if (1) {} + // Allocate a DMA from reuse queue 1. This queue will hopefully never + // be empty, since TTL 2 is so small. + dmaIndex = sSampleDmaReuseQueue1[sSampleDmaReuseQueueTail1++]; + dma = sSampleDmas + dmaIndex; + hasDma = TRUE; + } + + transfer = dma->bufSize; + dmaDevAddr = devAddr & ~0xF; + dma->ttl = 2; + dma->source = dmaDevAddr; + dma->sizeUnused = transfer; + func_sh_802f3dd0(&gCurrAudioFrameDmaIoMesgBufs[gCurrAudioFrameDmaCount++], OS_MESG_PRI_NORMAL, OS_READ, + dmaDevAddr, dma->buffer, transfer, &gCurrAudioFrameDmaQueue, medium, shindouDebugPrint62); + *dmaIndexRef = dmaIndex; + return (devAddr - dmaDevAddr) + dma->buffer; +} + +void init_sample_dma_buffers(UNUSED s32 arg0) { + s32 i; + + sDmaBufSize = 0x2D0; + sSampleDmas = sound_alloc_uninitialized(&gNotesAndBuffersPool, + gMaxSimultaneousNotes * 4 * sizeof(struct SharedDma) * gAudioBufferParameters.presetUnk4); + + for (i = 0; i < gMaxSimultaneousNotes * 3 * gAudioBufferParameters.presetUnk4; i++) + { + if ((sSampleDmas[gSampleDmaNumListItems].buffer = sound_alloc_uninitialized(&gNotesAndBuffersPool, sDmaBufSize)) == NULL) { + break; + } + sSampleDmas[gSampleDmaNumListItems].bufSize = sDmaBufSize; + sSampleDmas[gSampleDmaNumListItems].source = 0; + sSampleDmas[gSampleDmaNumListItems].sizeUnused = 0; + sSampleDmas[gSampleDmaNumListItems].unused2 = 0; + sSampleDmas[gSampleDmaNumListItems].ttl = 0; + gSampleDmaNumListItems++; + } + + for (i = 0; (u32) i < gSampleDmaNumListItems; i++) { + sSampleDmaReuseQueue1[i] = (u8) i; + sSampleDmas[i].reuseIndex = (u8) i; + } + + for (i = gSampleDmaNumListItems; i < 0x100; i++) { + sSampleDmaReuseQueue1[i] = 0; + } + + sSampleDmaReuseQueueTail1 = 0; + sSampleDmaReuseQueueHead1 = (u8) gSampleDmaNumListItems; + sSampleDmaListSize1 = gSampleDmaNumListItems; + + sDmaBufSize = 0x2D0; + for (i = 0; i < gMaxSimultaneousNotes; i++) { + if ((sSampleDmas[gSampleDmaNumListItems].buffer = sound_alloc_uninitialized(&gNotesAndBuffersPool, sDmaBufSize)) == NULL) { + break; + } + sSampleDmas[gSampleDmaNumListItems].bufSize = sDmaBufSize; + sSampleDmas[gSampleDmaNumListItems].source = 0; + sSampleDmas[gSampleDmaNumListItems].sizeUnused = 0; + sSampleDmas[gSampleDmaNumListItems].unused2 = 0; + sSampleDmas[gSampleDmaNumListItems].ttl = 0; + gSampleDmaNumListItems++; + } + + for (i = sSampleDmaListSize1; (u32) i < gSampleDmaNumListItems; i++) { + sSampleDmaReuseQueue2[i - sSampleDmaListSize1] = (u8) i; + sSampleDmas[i].reuseIndex = (u8)(i - sSampleDmaListSize1); + } + + // This probably meant to touch the range size1..size2 as well... but it + // doesn't matter, since these values are never read anyway. + for (i = gSampleDmaNumListItems; i < 0x100; i++) { + sSampleDmaReuseQueue2[i] = sSampleDmaListSize1; + } + + sSampleDmaReuseQueueTail2 = 0; + sSampleDmaReuseQueueHead2 = gSampleDmaNumListItems - sSampleDmaListSize1; +} + +void patch_seq_file(ALSeqFile *seqFile, u8 *data, u16 arg2) { + s32 i; + + seqFile->unk2 = arg2; + seqFile->data = data; + for (i = 0; i < seqFile->seqCount; i++) { + if (seqFile->seqArray[i].len != 0 && seqFile->seqArray[i].medium == 2) { + seqFile->seqArray[i].offset += (uintptr_t)data; + } + } +} + +struct AudioBank *load_banks_immediate(s32 seqId, s32 *outDefaultBank) { + u8 bank; + s32 offset; + s32 i; + void *ret; + + offset = ((u16 *)gAlBankSets)[canonicalize_index(0, seqId)]; + bank = 0xFF; + for (i = gAlBankSets[offset++]; i > 0; i--) { + bank = gAlBankSets[offset++]; + ret = func_sh_802f3688(bank); + } + *outDefaultBank = bank; + return ret; +} + +void preload_sequence(u32 seqId, s32 preloadMask) { + UNUSED s32 pad; + s32 temp; + + seqId = canonicalize_index(0, seqId); + if (preloadMask & PRELOAD_BANKS) { + load_banks_immediate(seqId, &temp); + } + if (preloadMask & PRELOAD_SEQUENCE) { + func_sh_802f3564(seqId); + } +} + +s32 func_sh_802f2f38(struct AudioBankSample *sample, s32 bankId) { + u8 *sp24; + + if (sample->isPatched == TRUE && sample->medium != 0) { + sp24 = func_sh_802f1d90(sample->size, bankId, sample->sampleAddr, sample->medium); + if (sp24 == NULL) { + return -1; + } + if (sample->medium == 1) { + func_sh_802f3d78((uintptr_t) sample->sampleAddr, sp24, sample->size, gAlTbl->unk2); + } else { + func_sh_802f3c38((uintptr_t) sample->sampleAddr, sp24, sample->size, sample->medium); + } + sample->medium = 0; + sample->sampleAddr = sp24; + } +#ifdef AVOID_UB + return 0; +#endif +} + +s32 func_sh_802f3024(s32 bankId, s32 instId, s32 arg2) { + struct Instrument *instr; + struct Drum *drum; + + if (instId < 0x7F) { + instr = get_instrument_inner(bankId, instId); + if (instr == NULL) { + return -1; + } + if (instr->normalRangeLo != 0) { + func_sh_802f2f38(instr->lowNotesSound.sample, bankId); + } + func_sh_802f2f38(instr->normalNotesSound.sample, bankId); + if (instr->normalRangeHi != 0x7F) { + func_sh_802f2f38(instr->highNotesSound.sample, bankId); + } + //! @bug missing return + } else if (instId == 0x7F) { + drum = get_drum(bankId, arg2); + if (drum == NULL) { + return -1; + } + func_sh_802f2f38(drum->sound.sample, bankId); + return 0; + } +#ifdef AVOID_UB + return 0; +#endif +} + +void func_sh_802f30f4(s32 arg0, s32 arg1, s32 arg2, OSMesgQueue *arg3) { + if (func_802f3f08(2, canonicalize_index(2, arg0), arg1, arg2, arg3) == 0) { + osSendMesg(arg3, 0, 0); + } +} + +void func_sh_802f3158(s32 seqId, s32 numChunks, s32 arg2, OSMesgQueue *retQueue) { + s32 val; + s32 v; + + val = ((u16 *) gAlBankSets)[canonicalize_index(0, seqId)]; + v = gAlBankSets[val++]; + + while (v > 0) { + func_802f3f08(1, canonicalize_index(1, gAlBankSets[val++]), numChunks, arg2, retQueue); + v--; + } +} + +u8 *func_sh_802f3220(u32 seqId, u32 *a1) { + s32 val; + + val = ((u16 *) gAlBankSets)[canonicalize_index(0, seqId)]; + *a1 = gAlBankSets[val++]; + if (*a1 == 0) { + return NULL; + } + return &gAlBankSets[val]; +} + +void func_sh_802f3288(s32 idx) { + s32 bankId; + s32 s2; + + idx = ((u16*)gAlBankSets)[canonicalize_index(0, idx)]; + s2 = gAlBankSets[idx++]; + while (s2 > 0) { + s2--; + bankId = canonicalize_index(1, gAlBankSets[idx++]); + + if (unk_pool1_lookup(1, bankId) == NULL) { + func_sh_802f3368(bankId); + if (gBankLoadStatus[bankId] != SOUND_LOAD_STATUS_5) { + gBankLoadStatus[bankId] = SOUND_LOAD_STATUS_NOT_LOADED; + } + + continue; + } + + } +} + +BAD_RETURN(s32) func_sh_802f3368(s32 bankId) { + struct SoundMultiPool *pool = &gBankLoadedPool; + struct TemporaryPool *temporary = &pool->temporary; + struct PersistentPool *persistent; + u32 i; + + if (temporary->entries[0].id == bankId) { + temporary->entries[0].id = -1; + } else if (temporary->entries[1].id == bankId) { + temporary->entries[1].id = -1; + } + + persistent = &pool->persistent; + for (i = 0; i < persistent->numEntries; i++) { + if (persistent->entries[i].id == bankId) { + persistent->entries[i].id = -1; + } + + } + + discard_bank(bankId); +} + + +void load_sequence_internal(s32 player, s32 seqId, s32 loadAsync); +void load_sequence(u32 player, u32 seqId, s32 loadAsync) { + load_sequence_internal(player, seqId, loadAsync); +} + +void load_sequence_internal(s32 player, s32 seqId, UNUSED s32 loadAsync) { + struct SequencePlayer *seqPlayer; + u8 *sequenceData; + u32 s0; + s32 count; + u8 bank; + s32 i; + + seqPlayer = &gSequencePlayers[player]; + + seqId = canonicalize_index(0, seqId); + sequence_player_disable(seqPlayer); + + s0 = ((u16 *) gAlBankSets)[seqId]; + count = gAlBankSets[s0++]; + bank = 0xff; + + while (count > 0) { + bank = gAlBankSets[s0++]; + func_sh_802f3688(bank); + count--; + } + + sequenceData = func_sh_802f3564(seqId); + init_sequence_player(player); + seqPlayer->seqId = seqId; + seqPlayer->defaultBank[0] = bank; + seqPlayer->enabled = 1; + seqPlayer->seqData = sequenceData; + seqPlayer->scriptState.pc = sequenceData; + seqPlayer->scriptState.depth = 0; + seqPlayer->delay = 0; + seqPlayer->finished = 0; + + for (i = 0; i < 0x10; i++) { + } +} + +void *func_sh_802f3564(s32 seqId) { + s32 seqId2 = canonicalize_index(0, seqId); + s32 temp; + return func_sh_802f3764(0, seqId2, &temp); +} + +extern u8 gUnkLoadStatus[0x40]; + +void *func_sh_802f3598(s32 idx, s32 *medium) { + void *ret; + ALSeqFile *f; + s32 temp; + s32 sp28; + + f = get_audio_file_header(2); + idx = canonicalize_index(2, idx); + ret = get_bank_or_seq_wrapper(2, idx); + if (ret != NULL) { + if (gUnkLoadStatus[idx] != SOUND_LOAD_STATUS_5) { + gUnkLoadStatus[idx] = SOUND_LOAD_STATUS_COMPLETE; + } + + *medium = 0; + return ret; + } + + temp = f->seqArray[idx].magic; + if (temp == 4) { + *medium = f->seqArray[idx].medium; + return f->seqArray[idx].offset; + } else { + ret = func_sh_802f3764(2, idx, &sp28); + if (ret != 0) { + *medium = 0; + return ret; + } + + *medium = f->seqArray[idx].medium; + } + return f->seqArray[idx].offset; + +} + +void *func_sh_802f3688(s32 bankId) { + void *ret; + s32 bankId1; + s32 bankId2; + s32 sp38; + struct PatchStruct patchInfo; + + bankId = canonicalize_index(1, bankId); + bankId1 = gCtlEntries[bankId].bankId1; + bankId2 = gCtlEntries[bankId].bankId2; + + patchInfo.bankId1 = bankId1; + patchInfo.bankId2 = bankId2; + + if (patchInfo.bankId1 != 0xFF) { + patchInfo.baseAddr1 = func_sh_802f3598(patchInfo.bankId1, &patchInfo.medium1); + } else { + patchInfo.baseAddr1 = NULL; + } + + if (bankId2 != 0xFF) { + patchInfo.baseAddr2 = func_sh_802f3598(bankId2, &patchInfo.medium2); + } else { + patchInfo.baseAddr2 = NULL; + } + + if ((ret = func_sh_802f3764(1, bankId, &sp38)) == NULL) { + return NULL; + } + + if (sp38 == 1) { + func_sh_802f5310(bankId, ret, &patchInfo, 0); + } + + return ret; +} + +void *func_sh_802f3764(s32 poolIdx, s32 idx, s32 *arg2) { + s32 size; + ALSeqFile *f; + void *vAddr; + s32 medium; + UNUSED u32 pad2; + u8 *devAddr; + s8 loadStatus; + s32 sp18; + + vAddr = get_bank_or_seq_wrapper(poolIdx, idx); + if (vAddr != NULL) { + *arg2 = 0; + loadStatus = SOUND_LOAD_STATUS_COMPLETE; + } else { + f = get_audio_file_header(poolIdx); + size = f->seqArray[idx].len; + size = ALIGN16(size); + medium = f->seqArray[idx].medium; + sp18 = f->seqArray[idx].magic; + devAddr = f->seqArray[idx].offset; + + + switch (sp18) + { + case 0: + vAddr = unk_pool1_alloc(poolIdx, idx, size); + if (vAddr == NULL) { + return vAddr; + } + break; + case 1: + vAddr = alloc_bank_or_seq(poolIdx, size, 1, idx); + if (vAddr == NULL) { + return vAddr; + } + break; + case 2: + vAddr = alloc_bank_or_seq(poolIdx, size, 0, idx); + if (vAddr == NULL) { + return vAddr; + } + break; + + case 3: + case 4: + vAddr = alloc_bank_or_seq(poolIdx, size, 2, idx); + if (vAddr == NULL) { + return vAddr; + } + break; + } + + *arg2 = 1; + if (medium == 1) { + func_sh_802f3d78((uintptr_t) devAddr, vAddr, size, f->unk2); + } else { + func_sh_802f3c38((uintptr_t) devAddr, vAddr, size, medium); + } + + switch (sp18) { + case 0: + loadStatus = SOUND_LOAD_STATUS_5; + break; + + default: + loadStatus = SOUND_LOAD_STATUS_COMPLETE; + break; + } + } + + switch (poolIdx) { + case 0: + if (gSeqLoadStatus[idx] != SOUND_LOAD_STATUS_5) { + gSeqLoadStatus[idx] = loadStatus; + } + break; + + case 1: + if (gBankLoadStatus[idx] != SOUND_LOAD_STATUS_5) { + gBankLoadStatus[idx] = loadStatus; + } + break; + + case 2: + if (gUnkLoadStatus[idx] != SOUND_LOAD_STATUS_5) { + gUnkLoadStatus[idx] = loadStatus; + } + break; + } + + return vAddr; +} + +s32 canonicalize_index(s32 poolIdx, s32 idx) { + ALSeqFile *f; + + f = get_audio_file_header(poolIdx); + if (f->seqArray[idx].len == 0) { + idx = (s32) (uintptr_t) f->seqArray[idx].offset; + } + return idx; +} + +void *get_bank_or_seq_wrapper(s32 poolIdx, s32 id) { + void *ret; + + ret = unk_pool1_lookup(poolIdx, id); + if (ret != NULL) { + return ret; + } + ret = get_bank_or_seq(poolIdx, 2, id); + if (ret != 0) { + return ret; + } + return NULL; +} + +ALSeqFile *get_audio_file_header(s32 poolIdx) { + ALSeqFile *ret; + switch (poolIdx) { + default: + ret = NULL; + break; + case 0: + ret = gSeqFileHeader; + break; + case 1: + ret = gAlCtlHeader; + break; + case 2: + ret = gAlTbl; + break; + } + return ret; +} + +void patch_audio_bank(s32 bankId, struct AudioBank *mem, struct PatchStruct *patchInfo) { + struct Instrument *instrument; + void **itInstrs; + struct Instrument **end; + s32 i; + void *patched; + struct Drum *drum; + s32 numDrums; + s32 numInstruments; + +#define BASE_OFFSET(x, base) (void *)((uintptr_t) (x) + (uintptr_t) base) +#define PATCH(x, base) (patched = BASE_OFFSET(x, base)) +#define PATCH_MEM(x) x = PATCH(x, mem) + + numDrums = gCtlEntries[bankId].numDrums; + numInstruments = gCtlEntries[bankId].numInstruments; + itInstrs = (void **) mem->drums; + if (mem->drums) { + } + if (itInstrs != NULL && numDrums != 0) { + if (1) { + mem->drums = PATCH(itInstrs, mem); + } + for (i = 0; i < numDrums; i++) { + patched = mem->drums[i]; + if (patched != NULL) { + drum = PATCH(patched, mem); + mem->drums[i] = drum; + if (drum->loaded == 0) { + patch_sound(&drum->sound, mem, patchInfo); + patched = drum->envelope; + drum->envelope = BASE_OFFSET(patched, mem); + drum->loaded = 1; + } + + } + } + } + + if (numInstruments > 0) { + itInstrs = (void **) mem->instruments; + end = numInstruments + (struct Instrument **) itInstrs; + + do { + if (*itInstrs != NULL) { + *itInstrs = BASE_OFFSET(*itInstrs, mem); + instrument = *itInstrs; + + if (instrument->loaded == 0) { + if (instrument->normalRangeLo != 0) { + patch_sound(&instrument->lowNotesSound, mem, patchInfo); + } + patch_sound(&instrument->normalNotesSound, mem, patchInfo); + if (instrument->normalRangeHi != 0x7F) { + patch_sound(&instrument->highNotesSound, mem, patchInfo); + } + patched = instrument->envelope; + + instrument->envelope = BASE_OFFSET(patched, mem); + instrument->loaded = 1; + } + } + itInstrs = (void **) ((struct Instrument **) itInstrs) + 1; + } while ((struct Instrument **) itInstrs != ((void) 0, end)); //! This is definitely fake + } + gCtlEntries[bankId].drums = mem->drums; + gCtlEntries[bankId].instruments = mem->instruments; +#undef PATCH_MEM +#undef PATCH +#undef BASE_OFFSET +} + +extern char shindouDebugPrint81[]; // "FastCopy" +extern char shindouDebugPrint82[]; // "FastCopy" +void func_sh_802f3c38(uintptr_t devAddr, void *vAddr, size_t nbytes, s32 medium) { + nbytes = ALIGN16(nbytes); + osInvalDCache(vAddr, nbytes); + +again: + if (gAudioLoadLockSH != 0) { + goto again; + } + + if (nbytes >= 0x400U) { + func_sh_802f3dd0(&gAudioDmaIoMesg, 1, 0, devAddr, vAddr, 0x400, &gAudioDmaMesgQueue, medium, shindouDebugPrint81); + osRecvMesg(&gAudioDmaMesgQueue, NULL, 1); + nbytes = nbytes - 0x400; + devAddr = devAddr + 0x400; + vAddr = (u8*)vAddr + 0x400; + goto again; + } + + if (nbytes != 0) { + func_sh_802f3dd0(&gAudioDmaIoMesg, 1, 0, devAddr, vAddr, nbytes, &gAudioDmaMesgQueue, medium, shindouDebugPrint82); + osRecvMesg(&gAudioDmaMesgQueue, NULL, 1); + } +} + +void func_sh_802f3d78(uintptr_t devAddr, void *vAddr, size_t nbytes, s32 arg3) { + uintptr_t sp1C; + + sp1C = devAddr; + osInvalDCache(vAddr, nbytes); + func_sh_802f3ed4(func_sh_802f3ec4(arg3, &sp1C), sp1C, vAddr, nbytes); +} + +s32 func_sh_802f3dd0(OSIoMesg *m, s32 pri, s32 direction, uintptr_t devAddr, void *dramAddr, s32 size, OSMesgQueue *retQueue, s32 medium, UNUSED const char *reason) { + OSPiHandle *handle; + if (gAudioLoadLockSH >= 0x11U) { + return -1; + } + switch (medium) { + case 2: + handle = osCartRomInit(); + break; + case 3: + handle = osDriveRomInit(); + break; + default: + return 0; + } + if ((size & 0xf) != 0) { + size = ALIGN16(size); + } + m->hdr.pri = pri; + m->hdr.retQueue = retQueue; + m->dramAddr = dramAddr; + m->devAddr = devAddr; + m->size = size; + handle->transferInfo.cmdType = 2; + osEPiStartDma(handle, m, direction); + return 0; +} + +s32 func_sh_802f3ec4(UNUSED s32 arg0, UNUSED uintptr_t *arg1) { + return 0; +} + +void func_sh_802f3ed4(UNUSED s32 arg0, UNUSED s32 arg1, UNUSED void *vAddr, UNUSED size_t nbytes) { +} + +void *func_sh_802f3ee8(s32 poolIdx, s32 idx) { + s32 temp; + return func_sh_802f3764(poolIdx, idx, &temp); +} + +void *func_802f3f08(s32 poolIdx, s32 idx, s32 numChunks, s32 arg3, OSMesgQueue *retQueue) { + s32 size; + ALSeqFile *f; + void *vAddr; + s32 medium; + s32 sp18; + uintptr_t devAddr; + s32 loadStatus; + + switch (poolIdx) { + case 0: + if (gSeqLoadStatus[idx] == SOUND_LOAD_STATUS_IN_PROGRESS) { + return 0; + } + break; + case 1: + if (gBankLoadStatus[idx] == SOUND_LOAD_STATUS_IN_PROGRESS) { + return 0; + } + break; + case 2: + if (gUnkLoadStatus[idx] == SOUND_LOAD_STATUS_IN_PROGRESS) { + return 0; + } + break; + + } + vAddr = get_bank_or_seq_wrapper(poolIdx, idx); + if (vAddr != NULL) { + loadStatus = 2; + osSendMesg(retQueue, (OSMesg) (arg3 << 0x18), 0); + } else { + f = get_audio_file_header(poolIdx); + size = f->seqArray[idx].len; + size = ALIGN16(size); + medium = f->seqArray[idx].medium; + sp18 = f->seqArray[idx].magic; + devAddr = (uintptr_t) f->seqArray[idx].offset; + loadStatus = 2; + + switch (sp18) { + case 0: + vAddr = unk_pool1_alloc(poolIdx, idx, size); + if (vAddr == NULL) { + return vAddr; + } + loadStatus = SOUND_LOAD_STATUS_5; + break; + case 1: + vAddr = alloc_bank_or_seq(poolIdx, size, 1, idx); + if (vAddr == NULL) { + return vAddr; + } + break; + case 2: + vAddr = alloc_bank_or_seq(poolIdx, size, 0, idx); + if (vAddr == NULL) { + return vAddr; + } + break; + + case 4: + case 3: + vAddr = alloc_bank_or_seq(poolIdx, size, 2, idx); + if (vAddr == NULL) { + return vAddr; + } + break; + } + + func_sh_802f4cb4(devAddr, vAddr, size, medium, numChunks, retQueue, (arg3 << 0x18) | (poolIdx << 0x10) | (idx << 8) | loadStatus); + loadStatus = SOUND_LOAD_STATUS_IN_PROGRESS; + } + + switch (poolIdx) { + case 0: + if (gSeqLoadStatus[idx] != SOUND_LOAD_STATUS_5) { + gSeqLoadStatus[idx] = loadStatus; + } + break; + + case 1: + if (gBankLoadStatus[idx] != SOUND_LOAD_STATUS_5) { + gBankLoadStatus[idx] = loadStatus; + } + break; + + case 2: + if (gUnkLoadStatus[idx] != SOUND_LOAD_STATUS_5) { + gUnkLoadStatus[idx] = loadStatus; + } + break; + } + + return vAddr; +} + +void func_sh_802f41e4(s32 audioResetStatus) { + func_sh_802f4a4c(audioResetStatus); + func_sh_802f573c(audioResetStatus); + func_sh_802f4dcc(audioResetStatus); +} + +#if defined(VERSION_SH) +u8 gShindouSoundBanksHeader[] = { +#include "sound/ctl_header.inc.c" +}; + +u8 gBankSetsData[] = { +#include "sound/bank_sets.inc.c" +}; + +u8 gShindouSampleBanksHeader[] = { +#include "sound/tbl_header.inc.c" +}; + +u8 gShindouSequencesHeader[] = { +#include "sound/sequences_header.inc.c" +}; +#endif + +// (void) must be omitted from parameters +void audio_init() { + UNUSED s8 pad[0x34]; + s32 i, j, k; + s32 lim; + u64 *ptr64; + void *data; + UNUSED u8 pad2[4]; + s32 seqCount; + + gAudioLoadLockSH = 0; + + for (i = 0; i < gAudioHeapSize / 8; i++) { + ((u64 *) gAudioHeap)[i] = 0; + } + +#ifdef TARGET_N64 + // It seems boot.s doesn't clear the .bss area for audio, so do it here. + lim = ((uintptr_t) &gAudioGlobalsEndMarker - (uintptr_t) &gAudioGlobalsStartMarker) / 8; + ptr64 = &gAudioGlobalsStartMarker; + for (k = lim; k >= 0; k--) { + *ptr64++ = 0; + } +#endif + + D_EU_802298D0 = 16.713f; + gRefreshRate = 60; + port_eu_init(); + +#ifdef TARGET_N64 + eu_stubbed_printf_3("Clear Workarea %x -%x size %x \n", + (uintptr_t) &gAudioGlobalsStartMarker, + (uintptr_t) &gAudioGlobalsEndMarker, + (uintptr_t) &gAudioGlobalsEndMarker - (uintptr_t) &gAudioGlobalsStartMarker + ); +#endif + + eu_stubbed_printf_1("AudioHeap is %x\n", gAudioHeapSize); + + for (i = 0; i < NUMAIBUFFERS; i++) { + gAiBufferLengths[i] = 0xa0; + } + + gAudioFrameCount = 0; + gAudioTaskIndex = 0; + gCurrAiBufferIndex = 0; + gSoundMode = 0; + gAudioTask = NULL; + gAudioTasks[0].task.t.data_size = 0; + gAudioTasks[1].task.t.data_size = 0; + osCreateMesgQueue(&gAudioDmaMesgQueue, &gAudioDmaMesg, 1); + osCreateMesgQueue(&gCurrAudioFrameDmaQueue, gCurrAudioFrameDmaMesgBufs, + ARRAY_COUNT(gCurrAudioFrameDmaMesgBufs)); + osCreateMesgQueue(&gUnkQueue1, gUnkMesgBufs1, 0x10); + osCreateMesgQueue(&gUnkQueue2, gUnkMesgBufs2, 0x10); + gCurrAudioFrameDmaCount = 0; + gSampleDmaNumListItems = 0; + + sound_init_main_pools(gAudioInitPoolSize); + + for (i = 0; i < NUMAIBUFFERS; i++) { + gAiBuffers[i] = sound_alloc_uninitialized(&gAudioInitPool, AIBUFFER_LEN); + + for (j = 0; j < (s32) (AIBUFFER_LEN / sizeof(s16)); j++) { + gAiBuffers[i][j] = 0; + } + } + + gAudioResetPresetIdToLoad = 0; + gAudioResetStatus = 1; + audio_shut_down_and_reset_step(); + + // Not sure about these prints + eu_stubbed_printf_1("Heap reset.Synth Change %x \n", 0); + eu_stubbed_printf_3("Heap %x %x %x\n", 0, 0, 0); + eu_stubbed_printf_0("Main Heap Initialize.\n"); + + // Load headers for sounds and sequences + gSeqFileHeader = (ALSeqFile *) gShindouSequencesHeader; + gAlCtlHeader = (ALSeqFile *) gShindouSoundBanksHeader; + gAlTbl = (ALSeqFile *) gShindouSampleBanksHeader; + gAlBankSets = gBankSetsData; + gSequenceCount = (s16) gSeqFileHeader->seqCount; + patch_seq_file(gSeqFileHeader, gMusicData, D_SH_80315EF4); + patch_seq_file(gAlCtlHeader, gSoundDataADSR, D_SH_80315EF8); + patch_seq_file(gAlTbl, gSoundDataRaw, D_SH_80315EFC); + seqCount = gAlCtlHeader->seqCount; + gCtlEntries = sound_alloc_uninitialized(&gAudioInitPool, seqCount * sizeof(struct CtlEntry)); + for (i = 0; i < seqCount; i++) { + gCtlEntries[i].bankId1 = (u8) ((gAlCtlHeader->seqArray[i].ctl.as_s16.bankAndFf >> 8) & 0xff); + gCtlEntries[i].bankId2 = (u8) (gAlCtlHeader->seqArray[i].ctl.as_s16.bankAndFf & 0xff); + gCtlEntries[i].numInstruments = (u8) ((gAlCtlHeader->seqArray[i].ctl.as_s16.numInstrumentsAndDrums >> 8) & 0xff); + gCtlEntries[i].numDrums = (u8) (gAlCtlHeader->seqArray[i].ctl.as_s16.numInstrumentsAndDrums & 0xff); + } + data = sound_alloc_uninitialized(&gAudioInitPool, D_SH_80315EF0); + if (data == NULL) { + D_SH_80315EF0 = 0; + } + sound_alloc_pool_init(&gUnkPool1.pool, data, D_SH_80315EF0); + init_sequence_players(); +} + +s32 func_sh_802f47c8(s32 bankId, u8 idx, s8 *io) { + struct AudioBankSample *sample = func_sh_802f4978(bankId, idx); + struct PendingDmaSample *temp; + if (sample == NULL) { + *io = 0; + return -1; + } + if (sample->medium == 0) { + *io = 2; + return 0; + } + temp = &D_SH_80343D00.arr[D_SH_80343D00.someIndex]; + if (temp->state == 3) { + temp->state = 0; + } + temp->sample = *sample; + temp->io = io; + temp->vAddr = func_sh_802f1d40(sample->size, bankId, sample->sampleAddr, sample->medium); + if (temp->vAddr == NULL) { + if (sample->medium == 1 || sample->codec == CODEC_SKIP) { + *io = 0; + return -1; + } else { + *io = 3; + return -1; + } + } + temp->state = 1; + temp->remaining = ALIGN16(sample->size); + temp->resultSampleAddr = (u8 *) temp->vAddr; + temp->devAddr = (uintptr_t) sample->sampleAddr; + temp->medium = sample->medium; + temp->bankId = bankId; + temp->idx = idx; + D_SH_80343D00.someIndex ^= 1; + return 0; +} + +struct AudioBankSample *func_sh_802f4978(s32 bankId, s32 idx) { + struct Drum *drum; + struct Instrument *inst; + struct AudioBankSample *ret; + + if (idx < 128) { + inst = get_instrument_inner(bankId, idx); + if (inst == 0) { + return NULL; + } + ret = inst->normalNotesSound.sample; + } else { + drum = get_drum(bankId, idx - 128); + if (drum == 0) { + return NULL; + } + ret = drum->sound.sample; + } + return ret; +} + +void stub_sh_802f49dc(void) { +} + +void func_sh_802f49e4(struct PendingDmaSample *arg0) { + struct AudioBankSample *sample = func_sh_802f4978(arg0->bankId, arg0->idx); + if (sample != NULL) { + arg0->sample = *sample; + sample->sampleAddr = arg0->resultSampleAddr; + sample->medium = 0; + } +} + +void func_sh_802f4a4c(s32 audioResetStatus) { + ALSeqFile *alTbl; + struct PendingDmaSample *entry; + + s32 i; + + alTbl = gAlTbl; + + for (i = 0; i < 2; i++) { + entry = &D_SH_80343D00.arr[i]; + switch (entry->state) { + case 2: + osRecvMesg(&entry->queue, NULL, 1); + if (audioResetStatus != 0) { + entry->state = 3; + break; + } + // fallthrough + case 1: + entry->state = 2; + if (entry->remaining == 0) { + func_sh_802f49e4(entry); + entry->state = 3; + *entry->io = 1; + } else if (entry->remaining < 0x1000) { + if (entry->medium == 1) { + func_sh_802f4c5c(entry->devAddr, entry->vAddr, entry->remaining, alTbl->unk2); + } else { + func_sh_802f4bd8(entry, entry->remaining); + } + entry->remaining = 0; + } else { + if (entry->medium == 1) { + func_sh_802f4c5c(entry->devAddr, entry->vAddr, 0x1000, alTbl->unk2); + } else { + func_sh_802f4bd8(entry, 0x1000); + } + entry->remaining = (s32) (entry->remaining - 0x1000); + entry->vAddr = (u8 *) entry->vAddr + 0x1000; + entry->devAddr = entry->devAddr + 0x1000; + } + break; + } + } +} + +extern char shindouDebugPrint102[]; // "SLOWCOPY" +void func_sh_802f4bd8(struct PendingDmaSample *arg0, s32 len) { // len must be signed + osInvalDCache(arg0->vAddr, len); + osCreateMesgQueue(&arg0->queue, arg0->mesgs, 1); + func_sh_802f3dd0(&arg0->ioMesg, 0, 0, arg0->devAddr, arg0->vAddr, len, &arg0->queue, arg0->medium, shindouDebugPrint102); +} + +void func_sh_802f4c5c(uintptr_t devAddr, void *vAddr, size_t nbytes, s32 arg3) { + uintptr_t sp1C; + + sp1C = devAddr; + osInvalDCache(vAddr, nbytes); + func_sh_802f3ed4(func_sh_802f3ec4(arg3, &sp1C), sp1C, vAddr, nbytes); +} + +struct PendingDmaAudioBank *func_sh_802f4cb4(uintptr_t devAddr, void *vAddr, s32 size, s32 medium, s32 numChunks, OSMesgQueue *retQueue, s32 encodedInfo) { + struct PendingDmaAudioBank *item; + s32 i; + + for (i = 0; i < ARRAY_COUNT(sPendingDmaAudioBanks); i++) { + if (sPendingDmaAudioBanks[i].inProgress == 0) { + item = &sPendingDmaAudioBanks[i]; + break; + } + } + if (i == ARRAY_COUNT(sPendingDmaAudioBanks)) { + return NULL; + } + + item->inProgress = 1; + item->devAddr = devAddr; + item->audioBank = vAddr; + item->vAddr = vAddr; + item->remaining = size; + if (numChunks == 0) { + item->transferSize = 0x1000; + } else { + item->transferSize = ((size / numChunks) + 0xFF) & ~0xFF; + if (item->transferSize < 0x100) { + item->transferSize = 0x100; + } + } + item->retQueue = retQueue; + item->timer = 3; + item->medium = medium; + item->encodedInfo = encodedInfo; + osCreateMesgQueue(&item->dmaRetQueue, item->mesgs, 1); + return item; +} + +void func_sh_802f4dcc(s32 audioResetStatus) { + s32 i; + + if (gAudioLoadLockSH != 1) { + for (i = 0; i < ARRAY_COUNT(sPendingDmaAudioBanks); i++) { + if (sPendingDmaAudioBanks[i].inProgress == 1) { + func_sh_802f4e50(&sPendingDmaAudioBanks[i], audioResetStatus); + } + } + } +} + +void func_sh_802f4e50(struct PendingDmaAudioBank *audioBank, s32 audioResetStatus) { + ALSeqFile *alSeqFile; + u32 *encodedInfo; + OSMesg mesg; + u32 temp; + u32 bankId; + s32 bankId1; + s32 bankId2; + struct PatchStruct patchStruct; + alSeqFile = gAlTbl; + if (audioBank->timer >= 2) { + audioBank->timer--; + return; + } + if (audioBank->timer == 1) { + audioBank->timer = 0; + } else { + if (audioResetStatus != 0) { + osRecvMesg(&audioBank->dmaRetQueue, NULL, OS_MESG_BLOCK); + audioBank->inProgress = 0; + return; + } + if (osRecvMesg(&audioBank->dmaRetQueue, NULL, OS_MESG_NOBLOCK) == -1) { + return; + } + } + + encodedInfo = &audioBank->encodedInfo; + if (audioBank->remaining == 0) { + mesg = (OSMesg) audioBank->encodedInfo; +#pragma GCC diagnostic push +#if defined(__clang__) +#pragma GCC diagnostic ignored "-Wself-assign" +#endif + mesg = mesg; //! needs an extra read from mesg here to match... +#pragma GCC diagnostic pop + temp = *encodedInfo; + bankId = (temp >> 8) & 0xFF; + switch ((u8) (temp >> 0x10)) { + case 0: + if (gSeqLoadStatus[bankId] != SOUND_LOAD_STATUS_5) { + gSeqLoadStatus[bankId] = (u8) (temp & 0xFF); + } + break; + case 2: + if (gUnkLoadStatus[bankId] != SOUND_LOAD_STATUS_5) { + gUnkLoadStatus[bankId] = (u8) (temp & 0xFF); + } + break; + case 1: + if (gBankLoadStatus[bankId] != SOUND_LOAD_STATUS_5) { + gBankLoadStatus[bankId] = (u8) (temp & 0xFF); + } + bankId1 = gCtlEntries[bankId].bankId1; + bankId2 = gCtlEntries[bankId].bankId2; + patchStruct.bankId1 = bankId1; + patchStruct.bankId2 = bankId2; + if (bankId1 != 0xFF) { + patchStruct.baseAddr1 = func_sh_802f3598(bankId1, &patchStruct.medium1); + } else { + patchStruct.baseAddr1 = NULL; + } + if (bankId2 != 0xFF) { + patchStruct.baseAddr2 = func_sh_802f3598(bankId2, &patchStruct.medium2); + } else { + patchStruct.baseAddr2 = NULL; + } + + func_sh_802f5310(bankId, audioBank->audioBank, &patchStruct, 1); + break; + } + mesg = (OSMesg) audioBank->encodedInfo; + audioBank->inProgress = 0; + osSendMesg(audioBank->retQueue, mesg, OS_MESG_NOBLOCK); + } else if (audioBank->remaining < audioBank->transferSize) { + if (audioBank->medium == 1) { + func_sh_802f517c(audioBank->devAddr, audioBank->vAddr, audioBank->remaining, alSeqFile->unk2); + } else { + func_sh_802f50ec(audioBank, audioBank->remaining); + } + + audioBank->remaining = 0; + } else { + if (audioBank->medium == 1) { + func_sh_802f517c(audioBank->devAddr, audioBank->vAddr, audioBank->transferSize, alSeqFile->unk2); + } else { + func_sh_802f50ec(audioBank, audioBank->transferSize); + } + + audioBank->remaining -= audioBank->transferSize; + audioBank->devAddr += audioBank->transferSize; + audioBank->vAddr = ((u8 *) audioBank->vAddr) + audioBank->transferSize; + } +} + +extern char shindouDebugPrint110[]; // "BGCOPY" +void func_sh_802f50ec(struct PendingDmaAudioBank *arg0, size_t len) { + len += 0xf; + len &= ~0xf; + osInvalDCache(arg0->vAddr, len); + osCreateMesgQueue(&arg0->dmaRetQueue, arg0->mesgs, 1); + func_sh_802f3dd0(&arg0->ioMesg, 0, 0, arg0->devAddr, arg0->vAddr, len, &arg0->dmaRetQueue, arg0->medium, shindouDebugPrint110); +} + + +void func_sh_802f517c(uintptr_t devAddr, void *vAddr, size_t nbytes, s32 arg3) { + uintptr_t sp1C; + + sp1C = devAddr; + osInvalDCache(vAddr, nbytes); + func_sh_802f3ed4(func_sh_802f3ec4(arg3, &sp1C), sp1C, vAddr, nbytes); +} + +void patch_sound(struct AudioBankSound *sound, struct AudioBank *memBase, struct PatchStruct *patchInfo) { + struct AudioBankSample *sample; + void *patched; + +#define PATCH(x, base) (patched = (void *)((uintptr_t) (x) + (uintptr_t) base)) + + if ((uintptr_t) sound->sample <= 0x80000000) { + sample = sound->sample = PATCH(sound->sample, memBase); + if (sample->size != 0 && sample->isPatched != TRUE) { + sample->loop = PATCH(sample->loop, memBase); + sample->book = PATCH(sample->book, memBase); + switch (sample->medium) { + case 0: + sample->sampleAddr = PATCH(sample->sampleAddr, patchInfo->baseAddr1); + sample->medium = patchInfo->medium1; + break; + case 1: + sample->sampleAddr = PATCH(sample->sampleAddr, patchInfo->baseAddr2); + sample->medium = patchInfo->medium2; + break; + + case 2: + case 3: + break; + } + sample->isPatched = TRUE; + if (sample->bit1 && sample->medium != 0) { + D_SH_8034EA88[D_SH_8034F688++] = sample; + } + } + } +#undef PATCH +} + +BAD_RETURN(s32) func_sh_802f5310(s32 bankId, struct AudioBank *mem, struct PatchStruct *patchInfo, s32 arg3) { + UNUSED u32 pad[2]; + u8 *addr; + UNUSED u32 pad1[3]; + s32 sp4C; + struct AudioBankSample *temp_s0; + s32 i; + uintptr_t count; + s32 temp; + + sp4C = 0; + if (D_SH_8034F68C != 0) { + sp4C = 1; + } else { + D_SH_80343CF0 = 0; + } + D_SH_8034F688 = 0; + patch_audio_bank(bankId, mem, patchInfo); + + count = 0; + for (i = 0; i < D_SH_8034F688; i++) { + count += ALIGN16(D_SH_8034EA88[i]->size); + } + + for (i = 0; i < D_SH_8034F688; i++) { + if (D_SH_8034F68C != 0x78) { + temp_s0 = D_SH_8034EA88[i]; + switch (arg3) { + case 0: + temp = temp_s0->medium = patchInfo->medium1; + if (temp != 0) { + if (temp_s0->size) { + } + addr = func_sh_802f1d90(temp_s0->size, patchInfo->bankId1, temp_s0->sampleAddr, temp_s0->medium); + } else { + temp = temp_s0->medium = patchInfo->medium2; + if (temp != 0) { + addr = func_sh_802f1d90(temp_s0->size, patchInfo->bankId2, temp_s0->sampleAddr, temp_s0->medium); + } + } + break; + + case 1: + temp = temp_s0->medium = patchInfo->medium1; + if (temp != 0) { + addr = func_sh_802f1d40(temp_s0->size, patchInfo->bankId1, temp_s0->sampleAddr, temp_s0->medium); + } else { + temp = temp_s0->medium = patchInfo->medium2; + if (temp != 0) { + addr = func_sh_802f1d40(temp_s0->size, patchInfo->bankId2, temp_s0->sampleAddr, temp_s0->medium); + } + } + break; + } + switch ((uintptr_t) addr) { + case 0: + break; + default: + switch (arg3) { + case 0: + if (temp_s0->medium == 1) { + func_sh_802f3d78((uintptr_t) temp_s0->sampleAddr, addr, temp_s0->size, gAlTbl->unk2); + temp_s0->sampleAddr = addr; + temp_s0->medium = 0; + } else { + func_sh_802f3c38((uintptr_t) temp_s0->sampleAddr, addr, temp_s0->size, temp_s0->medium); + temp_s0->sampleAddr = addr; + temp_s0->medium = 0; + } + break; + + case 1: + D_SH_8034EC88[D_SH_8034F68C].sample = temp_s0; + D_SH_8034EC88[D_SH_8034F68C].ramAddr = addr; + D_SH_8034EC88[D_SH_8034F68C].encodedInfo = (D_SH_8034F68C << 24) | 0xffffff; + D_SH_8034EC88[D_SH_8034F68C].isFree = FALSE; + D_SH_8034EC88[D_SH_8034F68C].endAndMediumIdentification = temp_s0->sampleAddr + temp_s0->size + temp_s0->medium; + D_SH_8034F68C++; + break; + } + } + continue; + } + break; + } + + D_SH_8034F688 = 0; + if (D_SH_8034F68C != 0 && sp4C == 0) { + temp_s0 = D_SH_8034EC88[D_SH_8034F68C - 1].sample; + temp = (temp_s0->size >> 12); + temp += 1; + count = (uintptr_t) temp_s0->sampleAddr; + func_sh_802f4cb4( + count, + D_SH_8034EC88[D_SH_8034F68C - 1].ramAddr, + temp_s0->size, + temp_s0->medium, + temp, + &gUnkQueue2, + D_SH_8034EC88[D_SH_8034F68C - 1].encodedInfo); + } +} + +s32 func_sh_802f573c(s32 audioResetStatus) { + struct AudioBankSample *sample; + u32 idx; + u8 *sampleAddr; + u32 size; + s32 unk; + u8 *added; + + if (D_SH_8034F68C > 0) { + if (audioResetStatus != 0) { + if (osRecvMesg(&gUnkQueue2, (OSMesg *) &idx, OS_MESG_NOBLOCK)){ + } + D_SH_8034F68C = 0; + return 0; + } + if (osRecvMesg(&gUnkQueue2, (OSMesg *) &idx, OS_MESG_NOBLOCK) == -1) { + return 0; + } + idx >>= 0x18; + if (D_SH_8034EC88[idx].isFree == FALSE) { + sample = D_SH_8034EC88[idx].sample; + added = (sample->sampleAddr + sample->size + sample->medium); + if (added == D_SH_8034EC88[idx].endAndMediumIdentification) { + sample->sampleAddr = D_SH_8034EC88[idx].ramAddr; + sample->medium = 0; + } + D_SH_8034EC88[idx].isFree = TRUE; + } + +next: + if (D_SH_8034F68C > 0) { + if (D_SH_8034EC88[D_SH_8034F68C - 1].isFree == TRUE) { + D_SH_8034F68C--; + goto next; + } + sample = D_SH_8034EC88[D_SH_8034F68C - 1].sample; + sampleAddr = sample->sampleAddr; + size = sample->size; + unk = size >> 0xC; + unk += 1; + added = ((sampleAddr + size) + sample->medium); + if (added != D_SH_8034EC88[D_SH_8034F68C - 1].endAndMediumIdentification) { + D_SH_8034EC88[D_SH_8034F68C - 1].isFree = TRUE; + D_SH_8034F68C--; + goto next; + } + size = sample->size; + func_sh_802f4cb4((uintptr_t) sampleAddr, D_SH_8034EC88[D_SH_8034F68C - 1].ramAddr, size, sample->medium, + unk, &gUnkQueue2, D_SH_8034EC88[D_SH_8034F68C - 1].encodedInfo); + } + } + return 1; +} + +s32 func_sh_802f5900(struct AudioBankSample *sample, s32 numLoaded, struct AudioBankSample *arg2[]) { + s32 i; + + for (i = 0; i < numLoaded; i++) { + if (sample->sampleAddr == arg2[i]->sampleAddr) { + break; + } + } + if (i == numLoaded) { + arg2[numLoaded++] = sample; + } + return numLoaded; +} + +s32 func_sh_802f5948(s32 bankId, struct AudioBankSample *list[]) { + s32 i; + struct Drum *drum; + struct Instrument *inst; + s32 numLoaded; + s32 numDrums; + s32 numInstruments; + + numLoaded = 0; + numDrums = gCtlEntries[bankId].numDrums; + numInstruments = gCtlEntries[bankId].numInstruments; + + for (i = 0; i < numDrums; i++) { + drum = get_drum(bankId, i); + if (drum == NULL) { + continue; + } + numLoaded = func_sh_802f5900(drum->sound.sample, numLoaded, list); + } + for (i = 0; i < numInstruments; i++) { + inst = get_instrument_inner(bankId, i); + if (inst == NULL) { + continue; + + } + if (inst->normalRangeLo != 0) { + numLoaded = func_sh_802f5900(inst->lowNotesSound.sample, numLoaded, list); + } + if (inst->normalRangeHi != 127) { + numLoaded = func_sh_802f5900(inst->highNotesSound.sample, numLoaded, list); + } + numLoaded = func_sh_802f5900(inst->normalNotesSound.sample, numLoaded, list); + } + return numLoaded; +} +#endif diff --git a/src/decomp/audio/playback.c b/src/decomp/audio/playback.c new file mode 100644 index 0000000..ff42e76 --- /dev/null +++ b/src/decomp/audio/playback.c @@ -0,0 +1,1472 @@ +#include + +#include "heap.h" +#include "data.h" +#include "load.h" +#include "seqplayer.h" +#include "playback.h" +#include "synthesis.h" +#include "effects.h" +#include "external.h" + +void note_set_resampling_rate(struct Note *note, f32 resamplingRateInput); + +#if defined(VERSION_EU) || defined(VERSION_SH) +#ifdef VERSION_SH +void note_set_vel_pan_reverb(struct Note *note, struct ReverbInfo *reverbInfo) +#else +void note_set_vel_pan_reverb(struct Note *note, f32 velocity, u8 pan, u8 reverbVol) +#endif +{ + struct NoteSubEu *sub = ¬e->noteSubEu; + f32 volRight, volLeft; + u8 strongRight; + u8 strongLeft; + s32 smallPanIndex; +#ifdef VERSION_EU + u16 unkMask = ~0x80; +#else + UNUSED u32 pad; + UNUSED u32 pad1; + f32 velocity; + u8 pan; + u8 reverbVol; + struct ReverbBitsData reverbBits; +#endif + +#ifdef VERSION_SH + note_set_resampling_rate(note, reverbInfo->freqScale); + velocity = reverbInfo->velocity; + pan = reverbInfo->pan; + reverbVol = reverbInfo->reverbVol; + reverbBits = reverbInfo->reverbBits.s; + pan &= 0x7f; +#else + pan &= unkMask; +#endif + + if (note->noteSubEu.stereoHeadsetEffects && gSoundMode == SOUND_MODE_HEADSET) { +#ifdef VERSION_SH + smallPanIndex = pan >> 1; +#else + smallPanIndex = pan >> 3; +#endif + if (smallPanIndex >= ARRAY_COUNT(gHeadsetPanQuantization)) { + smallPanIndex = ARRAY_COUNT(gHeadsetPanQuantization) - 1; + } + + sub->headsetPanLeft = gHeadsetPanQuantization[smallPanIndex]; + sub->headsetPanRight = gHeadsetPanQuantization[ARRAY_COUNT(gHeadsetPanQuantization) - 1 - smallPanIndex]; + sub->stereoStrongRight = FALSE; + sub->stereoStrongLeft = FALSE; + sub->usesHeadsetPanEffects = TRUE; + + volLeft = gHeadsetPanVolume[pan]; + volRight = gHeadsetPanVolume[127 - pan]; + } else if (sub->stereoHeadsetEffects && gSoundMode == SOUND_MODE_STEREO) { +#ifdef VERSION_SH + strongRight = FALSE; + strongLeft = FALSE; + sub->headsetPanRight = 0; + sub->headsetPanLeft = 0; +#else + strongLeft = FALSE; + strongRight = FALSE; + sub->headsetPanLeft = 0; + sub->headsetPanRight = 0; +#endif + + sub->usesHeadsetPanEffects = FALSE; + + volLeft = gStereoPanVolume[pan]; + volRight = gStereoPanVolume[127 - pan]; + if (pan < 0x20) { + strongLeft = TRUE; + } else if (pan > 0x60) { + strongRight = TRUE; + } + + sub->stereoStrongRight = strongRight; + sub->stereoStrongLeft = strongLeft; + +#ifdef VERSION_SH + switch (reverbBits.stereoHeadsetEffects) { + case 0: + sub->stereoStrongRight = reverbBits.strongRight; + sub->stereoStrongLeft = reverbBits.strongLeft; + break; + + case 1: + break; + + case 2: + sub->stereoStrongRight = reverbBits.strongRight | strongRight; + sub->stereoStrongLeft = reverbBits.strongLeft | strongLeft; + break; + + case 3: + sub->stereoStrongRight = reverbBits.strongRight ^ strongRight; + sub->stereoStrongLeft = reverbBits.strongLeft ^ strongLeft; + break; + } +#endif + } else if (gSoundMode == SOUND_MODE_MONO) { + volLeft = 0.707f; + volRight = 0.707f; + } else { + volLeft = gDefaultPanVolume[pan]; + volRight = gDefaultPanVolume[127 - pan]; + } + +#ifdef VERSION_SH + if (velocity < 0.0f) { + velocity = 0.0f; + } + if (velocity > 1.0f) { + velocity = 1.0f; + } + + sub->targetVolLeft = ((s32) (velocity * volLeft * 4095.999f)); + sub->targetVolRight = ((s32) (velocity * volRight * 4095.999f)); + sub->synthesisVolume = reverbInfo->synthesisVolume; + sub->filter = reverbInfo->filter; +#else + if (velocity < 0.0f) { + stubbed_printf("Audio: setvol: volume minus %f\n", velocity); + velocity = 0.0f; + } + if (velocity > 32767.f) { + stubbed_printf("Audio: setvol: volume overflow %f\n", velocity); + velocity = 32767.f; + } + + sub->targetVolLeft = ((s32) (velocity * volLeft) & 0xffff) >> 5; + sub->targetVolRight = ((s32) (velocity * volRight) & 0xffff) >> 5; +#endif + + //! @bug for the change to UQ0.7, the if statement should also have been changed accordingly + if (sub->reverbVol != reverbVol) { +#ifdef VERSION_SH + sub->reverbVol = reverbVol >> 1; +#else + sub->reverbVol = reverbVol; +#endif + sub->envMixerNeedsInit = TRUE; + return; + } + + if (sub->needsInit) { + sub->envMixerNeedsInit = TRUE; + } else { + sub->envMixerNeedsInit = FALSE; + } +} + +#ifdef VERSION_SH +#define MIN_RESAMPLING_RATE 1.99998f +#else +#define MIN_RESAMPLING_RATE 1.99996f +#endif + +void note_set_resampling_rate(struct Note *note, f32 resamplingRateInput) { + f32 resamplingRate = 0.0f; + struct NoteSubEu *tempSub = ¬e->noteSubEu; + +#ifdef VERSION_EU + if (resamplingRateInput < 0.0f) { + stubbed_printf("Audio: setpitch: pitch minus %f\n", resamplingRateInput); + resamplingRateInput = 0.0f; + } +#endif + if (resamplingRateInput < 2.0f) { + tempSub->hasTwoAdpcmParts = 0; + + if (MIN_RESAMPLING_RATE < resamplingRateInput) { + resamplingRate = MIN_RESAMPLING_RATE; + } else { + resamplingRate = resamplingRateInput; + } + + } else { + tempSub->hasTwoAdpcmParts = 1; + if (2 * MIN_RESAMPLING_RATE < resamplingRateInput) { + resamplingRate = MIN_RESAMPLING_RATE; + } else { + resamplingRate = resamplingRateInput * 0.5f; + } + } + note->noteSubEu.resamplingRateFixedPoint = (s32) (resamplingRate * 32768.0f); +} + +#ifdef VERSION_EU +struct AudioBankSound *instrument_get_audio_bank_sound(struct Instrument *instrument, s32 semitone) { + struct AudioBankSound *sound; + if (semitone < instrument->normalRangeLo) { + sound = &instrument->lowNotesSound; + } else if (semitone <= instrument->normalRangeHi) { + sound = &instrument->normalNotesSound; + } else { + sound = &instrument->highNotesSound; + } + return sound; +} + +struct Instrument *get_instrument_inner(s32 bankId, s32 instId) { + struct Instrument *inst; + + if (IS_BANK_LOAD_COMPLETE(bankId) == FALSE) { + stubbed_printf("Audio: voiceman: No bank error %d\n", bankId); + gAudioErrorFlags = bankId + 0x10000000; + return NULL; + } + + if (instId >= gCtlEntries[bankId].numInstruments) { + stubbed_printf("Audio: voiceman: progNo. overflow %d,%d\n", + instId, gCtlEntries[bankId].numInstruments); + gAudioErrorFlags = ((bankId << 8) + instId) + 0x3000000; + return NULL; + } + + inst = gCtlEntries[bankId].instruments[instId]; + if (inst == NULL) { + stubbed_printf("Audio: voiceman: progNo. undefined %d,%d\n", bankId, instId); + gAudioErrorFlags = ((bankId << 8) + instId) + 0x1000000; + return inst; + } + +#ifdef VERSION_EU + if (((uintptr_t) gBankLoadedPool.persistent.pool.start <= (uintptr_t) inst + && (uintptr_t) inst <= (uintptr_t)(gBankLoadedPool.persistent.pool.start + + gBankLoadedPool.persistent.pool.size)) + || ((uintptr_t) gBankLoadedPool.temporary.pool.start <= (uintptr_t) inst + && (uintptr_t) inst <= (uintptr_t)(gBankLoadedPool.temporary.pool.start + + gBankLoadedPool.temporary.pool.size))) { + return inst; + } + + stubbed_printf("Audio: voiceman: BAD Voicepointer %x,%d,%d\n", inst, bankId, instId); + gAudioErrorFlags = ((bankId << 8) + instId) + 0x2000000; + return NULL; +#else + return inst; +#endif +} + +struct Drum *get_drum(s32 bankId, s32 drumId) { + struct Drum *drum; + +#ifdef VERSION_SH + if (IS_BANK_LOAD_COMPLETE(bankId) == FALSE) { + stubbed_printf("Audio: voiceman: No bank error %d\n", bankId); + gAudioErrorFlags = bankId + 0x10000000; + return NULL; + } +#endif + + if (drumId >= gCtlEntries[bankId].numDrums) { + stubbed_printf("Audio: voiceman: Percussion Overflow %d,%d\n", + drumId, gCtlEntries[bankId].numDrums); + gAudioErrorFlags = ((bankId << 8) + drumId) + 0x4000000; + return NULL; + } + +#ifndef NO_SEGMENTED_MEMORY + if ((uintptr_t) gCtlEntries[bankId].drums < 0x80000000U) { + stubbed_printf("Percussion Pointer Error\n"); + return NULL; + } +#endif + + drum = gCtlEntries[bankId].drums[drumId]; + if (drum == NULL) { + stubbed_printf("Audio: voiceman: Percpointer NULL %d,%d\n", bankId, drumId); + gAudioErrorFlags = ((bankId << 8) + drumId) + 0x5000000; + } + return drum; +} +#endif +#endif // VERSION_EU + +#if defined(VERSION_EU) || defined(VERSION_SH) +void note_init_for_layer(struct Note *note, struct SequenceChannelLayer *seqLayer); +#else +s32 note_init_for_layer(struct Note *note, struct SequenceChannelLayer *seqLayer); +#endif + +void note_init(struct Note *note) { + if (note->parentLayer->adsr.releaseRate == 0) { + adsr_init(¬e->adsr, note->parentLayer->seqChannel->adsr.envelope, ¬e->adsrVolScale); + } else { + adsr_init(¬e->adsr, note->parentLayer->adsr.envelope, ¬e->adsrVolScale); + } +#ifdef VERSION_SH + note->unkSH34 = 0; +#endif + note->adsr.state = ADSR_STATE_INITIAL; +#if defined(VERSION_EU) || defined(VERSION_SH) + note->noteSubEu = gDefaultNoteSub; +#else + note_init_volume(note); + note_enable(note); +#endif +} + +#if defined(VERSION_EU) || defined(VERSION_SH) +#define note_disable2 note_disable +void note_disable(struct Note *note) { + if (note->noteSubEu.needsInit == TRUE) { + note->noteSubEu.needsInit = FALSE; + } +#ifdef VERSION_EU + else { + note_set_vel_pan_reverb(note, 0, 0x40, 0); + } +#endif + note->priority = NOTE_PRIORITY_DISABLED; +#ifdef VERSION_SH + note->unkSH34 = 0; +#endif + note->parentLayer = NO_LAYER; + note->prevParentLayer = NO_LAYER; + note->noteSubEu.enabled = FALSE; + note->noteSubEu.finished = FALSE; +#ifdef VERSION_SH + note->adsr.state = ADSR_STATE_DISABLED; + note->adsr.current = 0; +#endif +} +#else +void note_disable2(struct Note *note) { + note_disable(note); +} +#endif // VERSION_EU || VERSION_SH + +void process_notes(void) { + f32 scale; +#ifndef VERSION_SH + f32 frequency; +#if defined(VERSION_JP) || defined(VERSION_US) + u8 reverbVol; +#endif + f32 velocity; +#if defined(VERSION_JP) || defined(VERSION_US) + f32 pan; + f32 cap; +#endif +#endif + struct Note *note; +#if defined(VERSION_EU) || defined(VERSION_SH) + struct NotePlaybackState *playbackState; + struct NoteSubEu *noteSubEu; +#ifndef VERSION_SH + UNUSED u8 pad[12]; + u8 reverbVol; + UNUSED u8 pad3; + u8 pan; +#else + UNUSED u8 pad[8]; + struct ReverbInfo reverbInfo; +#endif + u8 bookOffset; +#endif + struct NoteAttributes *attributes; +#if defined(VERSION_JP) || defined(VERSION_US) + struct AudioListItem *it; +#endif + s32 i; + + // Macro versions of audio_list_push_front and audio_list_remove. + // Should ideally be changed to use copt. +#define PREPEND(item, head_arg) \ + ((it = (item), it->prev != NULL) \ + ? it \ + : (it->prev = (head_arg), it->next = (head_arg)->next, (head_arg)->next->prev = it, \ + (head_arg)->next = it, (head_arg)->u.count++, it->pool = (head_arg)->pool, it)) +#define POP(item) \ + ((it = (item), it->prev == NULL) \ + ? it \ + : (it->prev->next = it->next, it->next->prev = it->prev, it->prev = NULL, it)) + + for (i = 0; i < gMaxSimultaneousNotes; i++) { + note = &gNotes[i]; +#if defined(VERSION_EU) || defined(VERSION_SH) + playbackState = (struct NotePlaybackState *) ¬e->priority; + if (note->parentLayer != NO_LAYER) { +#ifndef NO_SEGMENTED_MEMORY + if ((uintptr_t) playbackState->parentLayer < 0x7fffffffU) { + continue; + } +#endif +#ifdef VERSION_SH + if (note != playbackState->parentLayer->note && playbackState->unkSH34 == 0) { + playbackState->adsr.action |= ADSR_ACTION_RELEASE; + playbackState->adsr.fadeOutVel = gAudioBufferParameters.updatesPerFrameInv; + playbackState->priority = 1; + playbackState->unkSH34 = 2; + goto d; + } else if (!playbackState->parentLayer->enabled && playbackState->unkSH34 == 0 && + playbackState->priority >= 1) { + // do nothing + } else if (playbackState->parentLayer->seqChannel->seqPlayer == NULL) { + sequence_channel_disable(playbackState->parentLayer->seqChannel); + playbackState->priority = 1; + playbackState->unkSH34 = 1; + continue; + } else if (playbackState->parentLayer->seqChannel->seqPlayer->muted && + (playbackState->parentLayer->seqChannel->muteBehavior + & (MUTE_BEHAVIOR_STOP_NOTES))) { + // do nothing + } else { + goto d; + } + + seq_channel_layer_note_release(playbackState->parentLayer); + audio_list_remove(¬e->listItem); + audio_list_push_front(¬e->listItem.pool->decaying, ¬e->listItem); + playbackState->priority = 1; + playbackState->unkSH34 = 2; + } else if (playbackState->unkSH34 == 0 && playbackState->priority >= 1) { + continue; + } +#else + if (!playbackState->parentLayer->enabled && playbackState->priority >= NOTE_PRIORITY_MIN) { + goto c; + } else if (playbackState->parentLayer->seqChannel->seqPlayer == NULL) { + eu_stubbed_printf_0("CAUTION:SUB IS SEPARATED FROM GROUP"); + sequence_channel_disable(playbackState->parentLayer->seqChannel); + playbackState->priority = NOTE_PRIORITY_STOPPING; + continue; + } else if (playbackState->parentLayer->seqChannel->seqPlayer->muted) { + if ((playbackState->parentLayer->seqChannel->muteBehavior + & (MUTE_BEHAVIOR_STOP_SCRIPT | MUTE_BEHAVIOR_STOP_NOTES))) { + goto c; + } + } + goto d; + if (1) { + c: + seq_channel_layer_note_release(playbackState->parentLayer); + audio_list_remove(¬e->listItem); + audio_list_push_front(¬e->listItem.pool->decaying, ¬e->listItem); + playbackState->priority = NOTE_PRIORITY_STOPPING; + } + } else if (playbackState->priority >= NOTE_PRIORITY_MIN) { + continue; + } +#endif + d: + if (playbackState->priority != NOTE_PRIORITY_DISABLED) { +#ifdef VERSION_SH + if (1) {} +#endif + noteSubEu = ¬e->noteSubEu; +#ifdef VERSION_SH + if (playbackState->unkSH34 >= 1 || noteSubEu->finished) { +#else + if (playbackState->priority == NOTE_PRIORITY_STOPPING || noteSubEu->finished) { +#endif + if (playbackState->adsr.state == ADSR_STATE_DISABLED || noteSubEu->finished) { + if (playbackState->wantedParentLayer != NO_LAYER) { + note_disable(note); + if (playbackState->wantedParentLayer->seqChannel != NULL) { + note_init_for_layer(note, playbackState->wantedParentLayer); + note_vibrato_init(note); + audio_list_remove(¬e->listItem); + audio_list_push_back(¬e->listItem.pool->active, ¬e->listItem); + playbackState->wantedParentLayer = NO_LAYER; + // don't skip + } else { + eu_stubbed_printf_0("Error:Wait Track disappear\n"); + note_disable(note); + audio_list_remove(¬e->listItem); + audio_list_push_back(¬e->listItem.pool->disabled, ¬e->listItem); + playbackState->wantedParentLayer = NO_LAYER; + goto skip; + } + } else { + note_disable(note); + audio_list_remove(¬e->listItem); + audio_list_push_back(¬e->listItem.pool->disabled, ¬e->listItem); + goto skip; + } + } +#ifndef VERSION_SH + if (1) { + } +#endif + } else if (playbackState->adsr.state == ADSR_STATE_DISABLED) { + note_disable(note); + audio_list_remove(¬e->listItem); + audio_list_push_back(¬e->listItem.pool->disabled, ¬e->listItem); + goto skip; + } + + scale = adsr_update(&playbackState->adsr); + note_vibrato_update(note); + attributes = &playbackState->attributes; +#ifdef VERSION_SH + if (playbackState->unkSH34 == 1 || playbackState->unkSH34 == 2) { + reverbInfo.freqScale = attributes->freqScale; + reverbInfo.velocity = attributes->velocity; + reverbInfo.pan = attributes->pan; + reverbInfo.reverbVol = attributes->reverbVol; + reverbInfo.reverbBits = attributes->reverbBits; + reverbInfo.synthesisVolume = attributes->synthesisVolume; + reverbInfo.filter = attributes->filter; + bookOffset = noteSubEu->bookOffset; + } else { + reverbInfo.freqScale = playbackState->parentLayer->noteFreqScale; + reverbInfo.velocity = playbackState->parentLayer->noteVelocity; + reverbInfo.pan = playbackState->parentLayer->notePan; + reverbInfo.reverbBits = playbackState->parentLayer->reverbBits; + reverbInfo.reverbVol = playbackState->parentLayer->seqChannel->reverbVol; + reverbInfo.synthesisVolume = playbackState->parentLayer->seqChannel->synthesisVolume; + reverbInfo.filter = playbackState->parentLayer->seqChannel->filter; + bookOffset = playbackState->parentLayer->seqChannel->bookOffset & 0x7; + if (playbackState->parentLayer->seqChannel->seqPlayer->muted + && (playbackState->parentLayer->seqChannel->muteBehavior & 8)) { + reverbInfo.freqScale = 0.0f; + reverbInfo.velocity = 0.0f; + } + } + + reverbInfo.freqScale *= playbackState->vibratoFreqScale * playbackState->portamentoFreqScale; + reverbInfo.freqScale *= gAudioBufferParameters.resampleRate; + reverbInfo.velocity *= scale; + note_set_vel_pan_reverb(note, &reverbInfo); +#else + if (playbackState->priority == NOTE_PRIORITY_STOPPING) { + frequency = attributes->freqScale; + velocity = attributes->velocity; + pan = attributes->pan; + reverbVol = attributes->reverbVol; + if (1) { + } + bookOffset = noteSubEu->bookOffset; + } else { + frequency = playbackState->parentLayer->noteFreqScale; + velocity = playbackState->parentLayer->noteVelocity; + pan = playbackState->parentLayer->notePan; + reverbVol = playbackState->parentLayer->seqChannel->reverbVol; + bookOffset = playbackState->parentLayer->seqChannel->bookOffset & 0x7; + } + + frequency *= playbackState->vibratoFreqScale * playbackState->portamentoFreqScale; + frequency *= gAudioBufferParameters.resampleRate; + velocity = velocity * scale * scale; + note_set_resampling_rate(note, frequency); + note_set_vel_pan_reverb(note, velocity, pan, reverbVol); +#endif + noteSubEu->bookOffset = bookOffset; + skip:; + } +#else + if (note->priority != NOTE_PRIORITY_DISABLED) { + if (note->priority == NOTE_PRIORITY_STOPPING || note->finished) { + if (note->adsrVolScale == 0 || note->finished) { + if (note->wantedParentLayer != NO_LAYER) { + note_disable2(note); + if (note->wantedParentLayer->seqChannel != NULL) { + if (note_init_for_layer(note, note->wantedParentLayer) == TRUE) { + note_disable2(note); + POP(¬e->listItem); + PREPEND(¬e->listItem, &gNoteFreeLists.disabled); + } else { + note_vibrato_init(note); + audio_list_push_back(¬e->listItem.pool->active, + POP(¬e->listItem)); + note->wantedParentLayer = NO_LAYER; + } + } else { + note_disable2(note); + audio_list_push_back(¬e->listItem.pool->disabled, POP(¬e->listItem)); + note->wantedParentLayer = NO_LAYER; + continue; + } + } else { + note_disable2(note); + audio_list_push_back(¬e->listItem.pool->disabled, POP(¬e->listItem)); + continue; + } + } + } else { + if (note->adsr.state == ADSR_STATE_DISABLED) { + note_disable2(note); + audio_list_push_back(¬e->listItem.pool->disabled, POP(¬e->listItem)); + continue; + } + } + + adsr_update(¬e->adsr); + note_vibrato_update(note); + attributes = ¬e->attributes; + if (note->priority == NOTE_PRIORITY_STOPPING) { + frequency = attributes->freqScale; + velocity = attributes->velocity; + pan = attributes->pan; + reverbVol = attributes->reverbVol; + } else { + frequency = note->parentLayer->noteFreqScale; + velocity = note->parentLayer->noteVelocity; + pan = note->parentLayer->notePan; + reverbVol = note->parentLayer->seqChannel->reverbVol; + } + + scale = note->adsrVolScale; + frequency *= note->vibratoFreqScale * note->portamentoFreqScale; + cap = 3.99992f; + if (gAiFrequency != 32006) { + frequency *= US_FLOAT(32000.0) / (f32) gAiFrequency; + } + frequency = (frequency < cap ? frequency : cap); + scale *= 4.3498e-5f; // ~1 / 23000 + velocity = velocity * scale * scale; + note_set_frequency(note, frequency); + note_set_vel_pan_reverb(note, velocity, pan, reverbVol); + continue; + } +#endif + } +#undef PREPEND +#undef POP +} + +#if defined(VERSION_SH) +// These three are matching but have been moved from above in shindou: +struct AudioBankSound *instrument_get_audio_bank_sound(struct Instrument *instrument, s32 semitone) { + struct AudioBankSound *sound; + if (semitone < instrument->normalRangeLo) { + sound = &instrument->lowNotesSound; + } else if (semitone <= instrument->normalRangeHi) { + sound = &instrument->normalNotesSound; + } else { + sound = &instrument->highNotesSound; + } + return sound; +} +struct Instrument *get_instrument_inner(s32 bankId, s32 instId) { + struct Instrument *inst; + + if (IS_BANK_LOAD_COMPLETE(bankId) == FALSE) { + gAudioErrorFlags = bankId + 0x10000000; + return NULL; + } + + if (instId >= gCtlEntries[bankId].numInstruments) { + gAudioErrorFlags = ((bankId << 8) + instId) + 0x3000000; + return NULL; + } + + inst = gCtlEntries[bankId].instruments[instId]; + if (inst == NULL) { + gAudioErrorFlags = ((bankId << 8) + instId) + 0x1000000; + return inst; + } + + return inst; +} + +struct Drum *get_drum(s32 bankId, s32 drumId) { + struct Drum *drum; + + if (IS_BANK_LOAD_COMPLETE(bankId) == FALSE) { + gAudioErrorFlags = bankId + 0x10000000; + return NULL; + } + + if (drumId >= gCtlEntries[bankId].numDrums) { + gAudioErrorFlags = ((bankId << 8) + drumId) + 0x4000000; + return NULL; + } + +#ifndef NO_SEGMENTED_MEMORY + if ((uintptr_t) gCtlEntries[bankId].drums < 0x80000000U) { + return NULL; + } +#endif + + drum = gCtlEntries[bankId].drums[drumId]; + if (drum == NULL) { + gAudioErrorFlags = ((bankId << 8) + drumId) + 0x5000000; + } + return drum; +} +#endif + +void seq_channel_layer_decay_release_internal(struct SequenceChannelLayer *seqLayer, s32 target) { + struct Note *note; + struct NoteAttributes *attributes; + + if (seqLayer == NO_LAYER) { + return; + } + +#ifdef VERSION_SH + seqLayer->status = SOUND_LOAD_STATUS_NOT_LOADED; +#endif + + if (seqLayer->note == NULL) { + return; + } + + note = seqLayer->note; + attributes = ¬e->attributes; + +#if defined(VERSION_JP) || defined(VERSION_US) + if (seqLayer->seqChannel != NULL && seqLayer->seqChannel->noteAllocPolicy == 0) { + seqLayer->note = NULL; + } +#endif + + if (note->wantedParentLayer == seqLayer) { + note->wantedParentLayer = NO_LAYER; + } + + if (note->parentLayer != seqLayer) { + +#if defined(VERSION_EU) || defined(VERSION_SH) + if (note->parentLayer == NO_LAYER && note->wantedParentLayer == NO_LAYER && + note->prevParentLayer == seqLayer && target != ADSR_STATE_DECAY) { + // Just guessing that this printf goes here... it's hard to parse. + eu_stubbed_printf_0("Slow Release Batting\n"); + note->adsr.fadeOutVel = gAudioBufferParameters.updatesPerFrameInv; + note->adsr.action |= ADSR_ACTION_RELEASE; + } +#endif + return; + } + +#ifndef VERSION_SH + seqLayer->status = SOUND_LOAD_STATUS_NOT_LOADED; +#endif + if (note->adsr.state != ADSR_STATE_DECAY) { + attributes->freqScale = seqLayer->noteFreqScale; + attributes->velocity = seqLayer->noteVelocity; + attributes->pan = seqLayer->notePan; +#ifdef VERSION_SH + attributes->reverbBits = seqLayer->reverbBits; +#endif + if (seqLayer->seqChannel != NULL) { + attributes->reverbVol = seqLayer->seqChannel->reverbVol; +#ifdef VERSION_SH + attributes->synthesisVolume = seqLayer->seqChannel->synthesisVolume; + attributes->filter = seqLayer->seqChannel->filter; + if (seqLayer->seqChannel->seqPlayer->muted && (seqLayer->seqChannel->muteBehavior & 8) != 0) { + note->noteSubEu.finished = TRUE; + } + note->priority = seqLayer->seqChannel->unkSH06; +#endif + } +#ifdef VERSION_SH + else { +#endif + note->priority = NOTE_PRIORITY_STOPPING; +#ifdef VERSION_SH + } +#endif + note->prevParentLayer = note->parentLayer; + note->parentLayer = NO_LAYER; + if (target == ADSR_STATE_RELEASE) { +#if defined(VERSION_EU) || defined(VERSION_SH) + note->adsr.fadeOutVel = gAudioBufferParameters.updatesPerFrameInv; +#else + note->adsr.fadeOutVel = 0x8000 / gAudioUpdatesPerFrame; +#endif + note->adsr.action |= ADSR_ACTION_RELEASE; +#ifdef VERSION_SH + note->unkSH34 = 2; +#endif + } else { +#ifdef VERSION_SH + note->unkSH34 = 1; +#endif + note->adsr.action |= ADSR_ACTION_DECAY; +#if defined(VERSION_EU) || defined(VERSION_SH) + if (seqLayer->adsr.releaseRate == 0) { + note->adsr.fadeOutVel = seqLayer->seqChannel->adsr.releaseRate * gAudioBufferParameters.unkUpdatesPerFrameScaled; + } else { + note->adsr.fadeOutVel = seqLayer->adsr.releaseRate * gAudioBufferParameters.unkUpdatesPerFrameScaled; + } + note->adsr.sustain = (FLOAT_CAST(seqLayer->seqChannel->adsr.sustain) * note->adsr.current) / 256.0f; +#else + if (seqLayer->adsr.releaseRate == 0) { + note->adsr.fadeOutVel = seqLayer->seqChannel->adsr.releaseRate * 24; + } else { + note->adsr.fadeOutVel = seqLayer->adsr.releaseRate * 24; + } + note->adsr.sustain = (note->adsr.current * seqLayer->seqChannel->adsr.sustain) / 0x10000; +#endif + } + } + + if (target == ADSR_STATE_DECAY) { + audio_list_remove(¬e->listItem); + audio_list_push_front(¬e->listItem.pool->decaying, ¬e->listItem); + } +} + +void seq_channel_layer_note_decay(struct SequenceChannelLayer *seqLayer) { + seq_channel_layer_decay_release_internal(seqLayer, ADSR_STATE_DECAY); +} + +void seq_channel_layer_note_release(struct SequenceChannelLayer *seqLayer) { + seq_channel_layer_decay_release_internal(seqLayer, ADSR_STATE_RELEASE); +} + +#if defined(VERSION_EU) || defined(VERSION_SH) +s32 build_synthetic_wave(struct Note *note, struct SequenceChannelLayer *seqLayer, s32 waveId) { + f32 freqScale; + f32 ratio; + u8 sampleCountIndex; + + if (waveId < 128) { +#ifdef VERSION_EU + stubbed_printf("Audio:Wavemem: Bad voiceno (%d)\n", waveId); +#endif + waveId = 128; + } + + freqScale = seqLayer->freqScale; + if (seqLayer->portamento.mode != 0 && 0.0f < seqLayer->portamento.extent) { + freqScale *= (seqLayer->portamento.extent + 1.0f); + } + if (freqScale < 1.0f) { + sampleCountIndex = 0; + ratio = 1.0465f; + } else if (freqScale < 2.0f) { + sampleCountIndex = 1; + ratio = 0.52325f; + } else if (freqScale < 4.0f) { + sampleCountIndex = 2; + ratio = 0.26263f; + } else { + sampleCountIndex = 3; + ratio = 0.13081f; + } + seqLayer->freqScale *= ratio; + note->waveId = waveId; + note->sampleCountIndex = sampleCountIndex; + + note->noteSubEu.sound.samples = &gWaveSamples[waveId - 128][sampleCountIndex * 64]; + + return sampleCountIndex; +} + +#else +void build_synthetic_wave(struct Note *note, struct SequenceChannelLayer *seqLayer) { + s32 i; + s32 j; + s32 pos; + s32 stepSize; + s32 offset; + u8 lim; + u8 origSampleCount = note->sampleCount; + + if (seqLayer->freqScale < US_FLOAT(1.0)) { + note->sampleCount = 64; + seqLayer->freqScale *= US_FLOAT(1.0465); + stepSize = 1; + } else if (seqLayer->freqScale < US_FLOAT(2.0)) { + note->sampleCount = 32; + seqLayer->freqScale *= US_FLOAT(0.52325); + stepSize = 2; + } else if (seqLayer->freqScale < US_FLOAT(4.0)) { + note->sampleCount = 16; + seqLayer->freqScale *= US_FLOAT(0.26263); + stepSize = 4; + } else { + note->sampleCount = 8; + seqLayer->freqScale *= US_FLOAT(0.13081); + stepSize = 8; + } + + if (note->sampleCount == origSampleCount && seqLayer->seqChannel->instOrWave == note->instOrWave) { + return; + } + + // Load wave sample + note->instOrWave = (u8) seqLayer->seqChannel->instOrWave; + for (i = -1, pos = 0; pos < 0x40; pos += stepSize) { + i++; + note->synthesisBuffers->samples[i] = gWaveSamples[seqLayer->seqChannel->instOrWave - 0x80][pos]; + } + + // Repeat sample + for (offset = note->sampleCount; offset < 0x40; offset += note->sampleCount) { + lim = note->sampleCount; + if (offset < 0 || offset > 0) { + for (j = 0; j < lim; j++) { + note->synthesisBuffers->samples[offset + j] = note->synthesisBuffers->samples[j]; + } + } else { + for (j = 0; j < lim; j++) { + note->synthesisBuffers->samples[offset + j] = note->synthesisBuffers->samples[j]; + } + } + } + + osWritebackDCache(note->synthesisBuffers->samples, sizeof(note->synthesisBuffers->samples)); +} +#endif + +void init_synthetic_wave(struct Note *note, struct SequenceChannelLayer *seqLayer) { +#if defined(VERSION_EU) || defined(VERSION_SH) + s32 sampleCountIndex; + s32 waveSampleCountIndex; + s32 waveId = seqLayer->instOrWave; + if (waveId == 0xff) { + waveId = seqLayer->seqChannel->instOrWave; + } + sampleCountIndex = note->sampleCountIndex; + waveSampleCountIndex = build_synthetic_wave(note, seqLayer, waveId); +#if defined(VERSION_EU) || defined(VERSION_SH) + note->synthesisState.samplePosInt = note->synthesisState.samplePosInt * euUnknownData_8030194c[waveSampleCountIndex] / euUnknownData_8030194c[sampleCountIndex]; +#else // Not a real change. Just temporary so I can remove this variable. + note->synthesisState.samplePosInt = note->synthesisState.samplePosInt * gDefaultShortNoteVelocityTable[waveSampleCountIndex] / gDefaultShortNoteVelocityTable[sampleCountIndex]; +#endif +#else + s32 sampleCount = note->sampleCount; + build_synthetic_wave(note, seqLayer); + if (sampleCount != 0) { + note->samplePosInt *= note->sampleCount / sampleCount; + } else { + note->samplePosInt = 0; + } +#endif +} + +void init_note_list(struct AudioListItem *list) { + list->prev = list; + list->next = list; + list->u.count = 0; +} + +void init_note_lists(struct NotePool *pool) { + init_note_list(&pool->disabled); + init_note_list(&pool->decaying); + init_note_list(&pool->releasing); + init_note_list(&pool->active); + pool->disabled.pool = pool; + pool->decaying.pool = pool; + pool->releasing.pool = pool; + pool->active.pool = pool; +} + +void init_note_free_list(void) { + s32 i; + + init_note_lists(&gNoteFreeLists); + for (i = 0; i < gMaxSimultaneousNotes; i++) { + gNotes[i].listItem.u.value = &gNotes[i]; + gNotes[i].listItem.prev = NULL; + audio_list_push_back(&gNoteFreeLists.disabled, &gNotes[i].listItem); + } +} + +void note_pool_clear(struct NotePool *pool) { + s32 i; + struct AudioListItem *source; + struct AudioListItem *cur; + struct AudioListItem *dest; + UNUSED s32 j; // unused in EU + + for (i = 0; i < 4; i++) { + switch (i) { + case 0: + source = &pool->disabled; + dest = &gNoteFreeLists.disabled; + break; + + case 1: + source = &pool->decaying; + dest = &gNoteFreeLists.decaying; + break; + + case 2: + source = &pool->releasing; + dest = &gNoteFreeLists.releasing; + break; + + case 3: + source = &pool->active; + dest = &gNoteFreeLists.active; + break; + } + +#if defined(VERSION_EU) || defined(VERSION_SH) + for (;;) { + cur = source->next; + if (cur == source) { + break; + } + if (cur == NULL) { + eu_stubbed_printf_0("Audio: C-Alloc : Dealloc voice is NULL\n"); + break; + } + audio_list_remove(cur); + audio_list_push_back(dest, cur); + } +#else + j = 0; + do { + cur = source->next; + if (cur == source) { + break; + } + audio_list_remove(cur); + audio_list_push_back(dest, cur); + j++; + } while (j <= gMaxSimultaneousNotes); +#endif + } +} + +void note_pool_fill(struct NotePool *pool, s32 count) { + s32 i; + s32 j; + struct Note *note; + struct AudioListItem *source; + struct AudioListItem *dest; + + note_pool_clear(pool); + + for (i = 0, j = 0; j < count; i++) { + if (i == 4) { + eu_stubbed_printf_1("Alloc Error:Dim voice-Alloc %d", count); + return; + } + + switch (i) { + case 0: + source = &gNoteFreeLists.disabled; + dest = &pool->disabled; + break; + + case 1: + source = &gNoteFreeLists.decaying; + dest = &pool->decaying; + break; + + case 2: + source = &gNoteFreeLists.releasing; + dest = &pool->releasing; + break; + + case 3: + source = &gNoteFreeLists.active; + dest = &pool->active; + break; + } + + while (j < count) { + note = audio_list_pop_back(source); + if (note == NULL) { + break; + } + audio_list_push_back(dest, ¬e->listItem); + j++; + } + } +} + +void audio_list_push_front(struct AudioListItem *list, struct AudioListItem *item) { + // add 'item' to the front of the list given by 'list', if it's not in any list + if (item->prev != NULL) { + eu_stubbed_printf_0("Error:Same List Add\n"); + } else { + item->prev = list; + item->next = list->next; + list->next->prev = item; + list->next = item; + list->u.count++; + item->pool = list->pool; + } +} + +void audio_list_remove(struct AudioListItem *item) { + // remove 'item' from the list it's in, if any + if (item->prev == NULL) { + eu_stubbed_printf_0("Already Cut\n"); + } else { + item->prev->next = item->next; + item->next->prev = item->prev; + item->prev = NULL; + } +} + +struct Note *pop_node_with_lower_prio(struct AudioListItem *list, s32 limit) { + struct AudioListItem *cur = list->next; + struct AudioListItem *best; + + if (cur == list) { + return NULL; + } + + for (best = cur; cur != list; cur = cur->next) { + if (((struct Note *) best->u.value)->priority >= ((struct Note *) cur->u.value)->priority) { + best = cur; + } + } + +#if defined(VERSION_EU) || defined(VERSION_SH) + if (best == NULL) { + return NULL; + } + + if (limit <= ((struct Note *) best->u.value)->priority) { + return NULL; + } +#else + if (limit < ((struct Note *) best->u.value)->priority) { + return NULL; + } +#endif + +#ifndef VERSION_SH + audio_list_remove(best); +#endif + return best->u.value; +} + +#if defined(VERSION_EU) || defined(VERSION_SH) +void note_init_for_layer(struct Note *note, struct SequenceChannelLayer *seqLayer) { + UNUSED s32 pad[4]; + s16 instId; + struct NoteSubEu *sub = ¬e->noteSubEu; + + note->prevParentLayer = NO_LAYER; + note->parentLayer = seqLayer; + note->priority = seqLayer->seqChannel->notePriority; + seqLayer->notePropertiesNeedInit = TRUE; + seqLayer->status = SOUND_LOAD_STATUS_DISCARDABLE; // "loaded" + seqLayer->note = note; + seqLayer->seqChannel->noteUnused = note; + seqLayer->seqChannel->layerUnused = seqLayer; + seqLayer->noteVelocity = 0.0f; + note_init(note); + instId = seqLayer->instOrWave; + if (instId == 0xff) { + instId = seqLayer->seqChannel->instOrWave; + } + sub->sound.audioBankSound = seqLayer->sound; + + if (instId >= 0x80) { + sub->isSyntheticWave = TRUE; + } else { + sub->isSyntheticWave = FALSE; + } + + if (sub->isSyntheticWave) { + build_synthetic_wave(note, seqLayer, instId); + } +#ifdef VERSION_SH + note->bankId = seqLayer->seqChannel->bankId; +#else + sub->bankId = seqLayer->seqChannel->bankId; +#endif + sub->stereoHeadsetEffects = seqLayer->seqChannel->stereoHeadsetEffects; + sub->reverbIndex = seqLayer->seqChannel->reverbIndex & 3; +} +#else +s32 note_init_for_layer(struct Note *note, struct SequenceChannelLayer *seqLayer) { + note->prevParentLayer = NO_LAYER; + note->parentLayer = seqLayer; + note->priority = seqLayer->seqChannel->notePriority; + if (IS_BANK_LOAD_COMPLETE(seqLayer->seqChannel->bankId) == FALSE) { + return TRUE; + } + + note->bankId = seqLayer->seqChannel->bankId; + note->stereoHeadsetEffects = seqLayer->seqChannel->stereoHeadsetEffects; + note->sound = seqLayer->sound; + seqLayer->status = SOUND_LOAD_STATUS_DISCARDABLE; // "loaded" + seqLayer->note = note; + seqLayer->seqChannel->noteUnused = note; + seqLayer->seqChannel->layerUnused = seqLayer; + if (note->sound == NULL) { + build_synthetic_wave(note, seqLayer); + } + note_init(note); + return FALSE; +} +#endif + +void func_80319728(struct Note *note, struct SequenceChannelLayer *seqLayer) { + seq_channel_layer_note_release(note->parentLayer); + note->wantedParentLayer = seqLayer; +} + +void note_release_and_take_ownership(struct Note *note, struct SequenceChannelLayer *seqLayer) { + note->wantedParentLayer = seqLayer; +#ifdef VERSION_SH + note->priority = seqLayer->seqChannel->notePriority; +#else + note->priority = NOTE_PRIORITY_STOPPING; +#endif + +#if defined(VERSION_EU) || defined(VERSION_SH) + note->adsr.fadeOutVel = gAudioBufferParameters.updatesPerFrameInv; +#else + note->adsr.fadeOutVel = 0x8000 / gAudioUpdatesPerFrame; +#endif + note->adsr.action |= ADSR_ACTION_RELEASE; +} + +struct Note *alloc_note_from_disabled(struct NotePool *pool, struct SequenceChannelLayer *seqLayer) { + struct Note *note = audio_list_pop_back(&pool->disabled); + if (note != NULL) { +#if defined(VERSION_EU) || defined(VERSION_SH) + note_init_for_layer(note, seqLayer); +#else + if (note_init_for_layer(note, seqLayer) == TRUE) { + audio_list_push_front(&gNoteFreeLists.disabled, ¬e->listItem); + return NULL; + } +#endif + audio_list_push_front(&pool->active, ¬e->listItem); + } + return note; +} + +struct Note *alloc_note_from_decaying(struct NotePool *pool, struct SequenceChannelLayer *seqLayer) { + struct Note *note = audio_list_pop_back(&pool->decaying); + if (note != NULL) { + note_release_and_take_ownership(note, seqLayer); + audio_list_push_back(&pool->releasing, ¬e->listItem); + } + return note; +} + +struct Note *alloc_note_from_active(struct NotePool *pool, struct SequenceChannelLayer *seqLayer) { +#ifdef VERSION_SH + struct Note *rNote; +#endif + struct Note *aNote; +#ifdef VERSION_SH + s32 rPriority, aPriority; + rPriority = aPriority = 0x10; + + rNote = pop_node_with_lower_prio(&pool->releasing, seqLayer->seqChannel->notePriority); + + if (rNote != NULL) { + rPriority = rNote->priority; + } +#endif + + aNote = pop_node_with_lower_prio(&pool->active, seqLayer->seqChannel->notePriority); + + if (aNote == NULL) { + eu_stubbed_printf_0("Audio: C-Alloc : lowerPrio is NULL\n"); + } else { +#ifdef VERSION_SH + aPriority = aNote->priority; +#else + func_80319728(aNote, seqLayer); + audio_list_push_back(&pool->releasing, &aNote->listItem); +#endif + } + +#ifdef VERSION_SH + if (rNote == NULL && aNote == NULL) { + return NULL; + } + + if (aPriority < rPriority) { + audio_list_remove(&aNote->listItem); + func_80319728(aNote, seqLayer); + audio_list_push_back(&pool->releasing, &aNote->listItem); + aNote->priority = seqLayer->seqChannel->notePriority; + return aNote; + } + rNote->wantedParentLayer = seqLayer; + rNote->priority = seqLayer->seqChannel->notePriority; + return rNote; +#else + return aNote; +#endif +} + +struct Note *alloc_note(struct SequenceChannelLayer *seqLayer) { + struct Note *ret; + u32 policy = seqLayer->seqChannel->noteAllocPolicy; + + if (policy & NOTE_ALLOC_LAYER) { + ret = seqLayer->note; + if (ret != NULL && ret->prevParentLayer == seqLayer +#if defined(VERSION_EU) || defined(VERSION_SH) + && ret->wantedParentLayer == NO_LAYER +#endif + ) { + note_release_and_take_ownership(ret, seqLayer); + audio_list_remove(&ret->listItem); +#if defined(VERSION_EU) || defined(VERSION_SH) + audio_list_push_back(&ret->listItem.pool->releasing, &ret->listItem); +#else + audio_list_push_back(&gNoteFreeLists.releasing, &ret->listItem); +#endif + return ret; + } + } + + if (policy & NOTE_ALLOC_CHANNEL) { + if (!(ret = alloc_note_from_disabled(&seqLayer->seqChannel->notePool, seqLayer)) + && !(ret = alloc_note_from_decaying(&seqLayer->seqChannel->notePool, seqLayer)) + && !(ret = alloc_note_from_active(&seqLayer->seqChannel->notePool, seqLayer))) { +#ifdef VERSION_SH + goto null_return; +#else + eu_stubbed_printf_0("Sub Limited Warning: Drop Voice"); + seqLayer->status = SOUND_LOAD_STATUS_NOT_LOADED; + return NULL; +#endif + } + return ret; + } + + if (policy & NOTE_ALLOC_SEQ) { + if (!(ret = alloc_note_from_disabled(&seqLayer->seqChannel->notePool, seqLayer)) + && !(ret = alloc_note_from_disabled(&seqLayer->seqChannel->seqPlayer->notePool, seqLayer)) + && !(ret = alloc_note_from_decaying(&seqLayer->seqChannel->notePool, seqLayer)) + && !(ret = alloc_note_from_decaying(&seqLayer->seqChannel->seqPlayer->notePool, seqLayer)) + && !(ret = alloc_note_from_active(&seqLayer->seqChannel->notePool, seqLayer)) + && !(ret = alloc_note_from_active(&seqLayer->seqChannel->seqPlayer->notePool, seqLayer))) { +#ifdef VERSION_SH + goto null_return; +#else + eu_stubbed_printf_0("Warning: Drop Voice"); + seqLayer->status = SOUND_LOAD_STATUS_NOT_LOADED; + return NULL; +#endif + } + return ret; + } + + if (policy & NOTE_ALLOC_GLOBAL_FREELIST) { + if (!(ret = alloc_note_from_disabled(&gNoteFreeLists, seqLayer)) + && !(ret = alloc_note_from_decaying(&gNoteFreeLists, seqLayer)) + && !(ret = alloc_note_from_active(&gNoteFreeLists, seqLayer))) { +#ifdef VERSION_SH + goto null_return; +#else + eu_stubbed_printf_0("Warning: Drop Voice"); + seqLayer->status = SOUND_LOAD_STATUS_NOT_LOADED; + return NULL; +#endif + } + return ret; + } + + if (!(ret = alloc_note_from_disabled(&seqLayer->seqChannel->notePool, seqLayer)) + && !(ret = alloc_note_from_disabled(&seqLayer->seqChannel->seqPlayer->notePool, seqLayer)) + && !(ret = alloc_note_from_disabled(&gNoteFreeLists, seqLayer)) + && !(ret = alloc_note_from_decaying(&seqLayer->seqChannel->notePool, seqLayer)) + && !(ret = alloc_note_from_decaying(&seqLayer->seqChannel->seqPlayer->notePool, seqLayer)) + && !(ret = alloc_note_from_decaying(&gNoteFreeLists, seqLayer)) + && !(ret = alloc_note_from_active(&seqLayer->seqChannel->notePool, seqLayer)) + && !(ret = alloc_note_from_active(&seqLayer->seqChannel->seqPlayer->notePool, seqLayer)) + && !(ret = alloc_note_from_active(&gNoteFreeLists, seqLayer))) { +#ifdef VERSION_SH + goto null_return; +#else + eu_stubbed_printf_0("Warning: Drop Voice"); + seqLayer->status = SOUND_LOAD_STATUS_NOT_LOADED; + return NULL; +#endif + } + return ret; + +#ifdef VERSION_SH +null_return: + seqLayer->status = SOUND_LOAD_STATUS_NOT_LOADED; + return NULL; +#endif +} + +#if defined(VERSION_JP) || defined(VERSION_US) +void reclaim_notes(void) { + struct Note *note; + s32 i; + s32 cond; + + for (i = 0; i < gMaxSimultaneousNotes; i++) { + note = &gNotes[i]; + if (note->parentLayer != NO_LAYER) { + cond = FALSE; + if (!note->parentLayer->enabled && note->priority >= NOTE_PRIORITY_MIN) { + cond = TRUE; + } else if (note->parentLayer->seqChannel == NULL) { + audio_list_push_back(&gLayerFreeList, ¬e->parentLayer->listItem); + seq_channel_layer_disable(note->parentLayer); + note->priority = NOTE_PRIORITY_STOPPING; + } else if (note->parentLayer->seqChannel->seqPlayer == NULL) { + sequence_channel_disable(note->parentLayer->seqChannel); + note->priority = NOTE_PRIORITY_STOPPING; + } else if (note->parentLayer->seqChannel->seqPlayer->muted) { + if (note->parentLayer->seqChannel->muteBehavior + & (MUTE_BEHAVIOR_STOP_SCRIPT | MUTE_BEHAVIOR_STOP_NOTES)) { + cond = TRUE; + } + } else { + cond = FALSE; + } + + if (cond) { + seq_channel_layer_note_release(note->parentLayer); + audio_list_remove(¬e->listItem); + audio_list_push_front(¬e->listItem.pool->disabled, ¬e->listItem); + note->priority = NOTE_PRIORITY_STOPPING; + } + } + } +} +#endif + +void note_init_all(void) { + struct Note *note; + s32 i; + + for (i = 0; i < gMaxSimultaneousNotes; i++) { + note = &gNotes[i]; +#if defined(VERSION_EU) || defined(VERSION_SH) + note->noteSubEu = gZeroNoteSub; +#else + note->enabled = FALSE; + note->stereoStrongRight = FALSE; + note->stereoStrongLeft = FALSE; + note->stereoHeadsetEffects = FALSE; +#endif + note->priority = NOTE_PRIORITY_DISABLED; +#ifdef VERSION_SH + note->unkSH34 = 0; +#endif + note->parentLayer = NO_LAYER; + note->wantedParentLayer = NO_LAYER; + note->prevParentLayer = NO_LAYER; +#if defined(VERSION_EU) || defined(VERSION_SH) + note->waveId = 0; +#else + note->reverbVol = 0; + note->usesHeadsetPanEffects = FALSE; + note->sampleCount = 0; + note->instOrWave = 0; + note->targetVolLeft = 0; + note->targetVolRight = 0; + note->frequency = 0.0f; + note->unused1 = 0x3f; +#endif + note->attributes.velocity = 0.0f; + note->adsrVolScale = 0; + note->adsr.state = ADSR_STATE_DISABLED; + note->adsr.action = 0; + note->vibratoState.active = FALSE; + note->portamento.cur = 0.0f; + note->portamento.speed = 0.0f; +#if defined(VERSION_SH) + note->synthesisState.synthesisBuffers = sound_alloc_uninitialized(&gNotesAndBuffersPool, sizeof(struct NoteSynthesisBuffers)); +#elif defined(VERSION_EU) + note->synthesisState.synthesisBuffers = soundAlloc(&gNotesAndBuffersPool, sizeof(struct NoteSynthesisBuffers)); +#else + note->synthesisBuffers = soundAlloc(&gNotesAndBuffersPool, sizeof(struct NoteSynthesisBuffers)); +#endif + } +} diff --git a/src/decomp/audio/playback.h b/src/decomp/audio/playback.h new file mode 100644 index 0000000..e2e15bf --- /dev/null +++ b/src/decomp/audio/playback.h @@ -0,0 +1,50 @@ +#ifndef AUDIO_PLAYBACK_H +#define AUDIO_PLAYBACK_H + +#include + +#include "internal.h" + +// Mask bits denoting where to allocate notes from, according to a channel's +// noteAllocPolicy. Despite being checked as bitmask bits, the bits are not +// orthogonal; rather, the smallest bit wins, except for NOTE_ALLOC_LAYER, +// which *is* orthogonal to the other. SEQ implicitly includes CHANNEL. +// If none of the CHANNEL/SEQ/GLOBAL_FREELIST bits are set, all three locations +// are tried. +#define NOTE_ALLOC_LAYER 1 +#define NOTE_ALLOC_CHANNEL 2 +#define NOTE_ALLOC_SEQ 4 +#define NOTE_ALLOC_GLOBAL_FREELIST 8 + +void process_notes(void); +void seq_channel_layer_note_decay(struct SequenceChannelLayer *seqLayer); +void seq_channel_layer_note_release(struct SequenceChannelLayer *seqLayer); +void init_synthetic_wave(struct Note *note, struct SequenceChannelLayer *seqLayer); +void init_note_lists(struct NotePool *pool); +void init_note_free_list(void); +void note_pool_clear(struct NotePool *pool); +void note_pool_fill(struct NotePool *pool, s32 count); +void audio_list_push_front(struct AudioListItem *list, struct AudioListItem *item); +void audio_list_remove(struct AudioListItem *item); +struct Note *alloc_note(struct SequenceChannelLayer *seqLayer); +void reclaim_notes(void); +void note_init_all(void); + +#if defined(VERSION_SH) +void note_set_vel_pan_reverb(struct Note *note, struct ReverbInfo *reverbInfo); +#elif defined(VERSION_EU) +void note_set_vel_pan_reverb(struct Note *note, f32 velocity, u8 pan, u8 reverbVol); +#endif + +#if defined(VERSION_EU) || defined(VERSION_SH) +struct AudioBankSound *instrument_get_audio_bank_sound(struct Instrument *instrument, s32 semitone); +struct Instrument *get_instrument_inner(s32 bankId, s32 instId); +struct Drum *get_drum(s32 bankId, s32 drumId); +void note_init_volume(struct Note *note); +void note_set_frequency(struct Note *note, f32 frequency); +void note_enable(struct Note *note); +void note_disable(struct Note *note); +#endif + + +#endif // AUDIO_PLAYBACK_H diff --git a/src/decomp/audio/port_eu.c b/src/decomp/audio/port_eu.c new file mode 100644 index 0000000..d607f77 --- /dev/null +++ b/src/decomp/audio/port_eu.c @@ -0,0 +1,351 @@ +#include +#include "internal.h" +#include "load.h" +#include "data.h" +#include "seqplayer.h" +#include "synthesis.h" + +#ifdef VERSION_EU + +#ifdef __sgi +#define stubbed_printf +#else +#define stubbed_printf(...) +#endif + +#define SAMPLES_TO_OVERPRODUCE 0x10 +#define EXTRA_BUFFERED_AI_SAMPLES_TARGET 0x40 + +#ifdef VERSION_JP +typedef u16 FadeT; +#else +typedef s32 FadeT; +#endif + +extern volatile u8 gAudioResetStatus; +extern u8 gAudioResetPresetIdToLoad; +extern OSMesgQueue *OSMesgQueues[]; +extern struct EuAudioCmd sAudioCmd[0x100]; + +void func_8031D690(s32 player, FadeT fadeInTime); +void seq_player_fade_to_zero_volume(s32 player, FadeT fadeOutTime); +void decrease_sample_dma_ttls(void); +s32 audio_shut_down_and_reset_step(void); +void func_802ad7ec(u32); + +#ifdef TARGET_N64 +struct SPTask *create_next_audio_frame_task(void) { + u32 samplesRemainingInAI; + s32 writtenCmds; + s32 index; + OSTask_t *task; + s32 flags; + s16 *currAiBuffer; + s32 oldDmaCount; + OSMesg sp30; + OSMesg sp2C; + + gAudioFrameCount++; + if (gAudioFrameCount % gAudioBufferParameters.presetUnk4 != 0) { + stubbed_printf("DAC:Lost 1 Frame.\n"); + return NULL; + } + + osSendMesg(OSMesgQueues[0], (OSMesg) gAudioFrameCount, 0); + + gAudioTaskIndex ^= 1; + gCurrAiBufferIndex++; + gCurrAiBufferIndex %= NUMAIBUFFERS; + index = (gCurrAiBufferIndex - 2 + NUMAIBUFFERS) % NUMAIBUFFERS; + samplesRemainingInAI = osAiGetLength() / 4; + + if (gAiBufferLengths[index] != 0) { + osAiSetNextBuffer(gAiBuffers[index], gAiBufferLengths[index] * 4); + } + + oldDmaCount = gCurrAudioFrameDmaCount; + if (oldDmaCount > AUDIO_FRAME_DMA_QUEUE_SIZE) { + stubbed_printf("DMA: Request queue over.( %d )\n", oldDmaCount); + } + gCurrAudioFrameDmaCount = 0; + + decrease_sample_dma_ttls(); + if (osRecvMesg(OSMesgQueues[2], &sp30, 0) != -1) { + gAudioResetPresetIdToLoad = (u8) (s32) sp30; + gAudioResetStatus = 5; + } + + if (gAudioResetStatus != 0) { + if (audio_shut_down_and_reset_step() == 0) { + if (gAudioResetStatus == 0) { + osSendMesg(OSMesgQueues[3], (OSMesg) (s32) gAudioResetPresetIdToLoad, OS_MESG_NOBLOCK); + } + return NULL; + } + } + + gAudioTask = &gAudioTasks[gAudioTaskIndex]; + gAudioCmd = gAudioCmdBuffers[gAudioTaskIndex]; + index = gCurrAiBufferIndex; + currAiBuffer = gAiBuffers[index]; + + gAiBufferLengths[index] = ((gAudioBufferParameters.samplesPerFrameTarget - samplesRemainingInAI + + EXTRA_BUFFERED_AI_SAMPLES_TARGET) & ~0xf) + SAMPLES_TO_OVERPRODUCE; + if (gAiBufferLengths[index] < gAudioBufferParameters.minAiBufferLength) { + gAiBufferLengths[index] = gAudioBufferParameters.minAiBufferLength; + } + if (gAiBufferLengths[index] > gAudioBufferParameters.maxAiBufferLength) { + gAiBufferLengths[index] = gAudioBufferParameters.maxAiBufferLength; + } + + if (osRecvMesg(OSMesgQueues[1], &sp2C, OS_MESG_NOBLOCK) != -1) { + func_802ad7ec((u32) sp2C); + } + + flags = 0; + gAudioCmd = synthesis_execute(gAudioCmd, &writtenCmds, currAiBuffer, gAiBufferLengths[index]); + gAudioRandom = ((gAudioRandom + gAudioFrameCount) * gAudioFrameCount); + gAudioRandom = gAudioRandom + writtenCmds / 8; + + index = gAudioTaskIndex; + gAudioTask->msgqueue = NULL; + gAudioTask->msg = NULL; + + task = &gAudioTask->task.t; + task->type = M_AUDTASK; + task->flags = flags; + task->ucode_boot = rspF3DBootStart; + task->ucode_boot_size = (u8 *) rspF3DBootEnd - (u8 *) rspF3DBootStart; + task->ucode = rspAspMainStart; + task->ucode_data = rspAspMainDataStart; + task->ucode_size = 0x800; // (this size is ignored) + task->ucode_data_size = (rspAspMainDataEnd - rspAspMainDataStart) * sizeof(u64); + task->dram_stack = NULL; + task->dram_stack_size = 0; + task->output_buff = NULL; + task->output_buff_size = NULL; + task->data_ptr = gAudioCmdBuffers[index]; + task->data_size = writtenCmds * sizeof(u64); + task->yield_data_ptr = NULL; + task->yield_data_size = 0; + return gAudioTask; +} +#else +struct SPTask *create_next_audio_frame_task(void) { + return NULL; +} +void create_next_audio_buffer(s16 *samples, u32 num_samples) { + s32 writtenCmds; + OSMesg msg; + gAudioFrameCount++; + decrease_sample_dma_ttls(); + if (osRecvMesg(OSMesgQueues[2], &msg, 0) != -1) { + gAudioResetPresetIdToLoad = (u8) (intptr_t) msg; + gAudioResetStatus = 5; + } + + if (gAudioResetStatus != 0) { + audio_reset_session(); + gAudioResetStatus = 0; + } + if (osRecvMesg(OSMesgQueues[1], &msg, OS_MESG_NOBLOCK) != -1) { + func_802ad7ec((u32) msg); + } + synthesis_execute(gAudioCmdBuffers[0], &writtenCmds, samples, num_samples); + gAudioRandom = ((gAudioRandom + gAudioFrameCount) * gAudioFrameCount); + gAudioRandom = gAudioRandom + writtenCmds / 8; +} +#endif + +void eu_process_audio_cmd(struct EuAudioCmd *cmd) { + s32 i; + + switch (cmd->u.s.op) { + case 0x81: + preload_sequence(cmd->u.s.arg2, 3); + break; + + case 0x82: + case 0x88: + load_sequence(cmd->u.s.arg1, cmd->u.s.arg2, cmd->u.s.arg3); + func_8031D690(cmd->u.s.arg1, cmd->u2.as_s32); + break; + + case 0x83: + if (gSequencePlayers[cmd->u.s.arg1].enabled != FALSE) { + if (cmd->u2.as_s32 == 0) { + sequence_player_disable(&gSequencePlayers[cmd->u.s.arg1]); + } + else { + seq_player_fade_to_zero_volume(cmd->u.s.arg1, cmd->u2.as_s32); + } + } + break; + + case 0xf0: + gSoundMode = cmd->u2.as_s32; + break; + + case 0xf1: + for (i = 0; i < 4; i++) { + gSequencePlayers[i].muted = TRUE; + gSequencePlayers[i].recalculateVolume = TRUE; + } + break; + + case 0xf2: + for (i = 0; i < 4; i++) { + gSequencePlayers[i].muted = FALSE; + gSequencePlayers[i].recalculateVolume = TRUE; + } + break; + } +} + +const char undefportcmd[] = "Undefined Port Command %d\n"; + +extern OSMesgQueue *OSMesgQueues[]; +extern u8 D_EU_80302010; +extern u8 D_EU_80302014; +extern OSMesg OSMesg0; +extern OSMesg OSMesg1; +extern OSMesg OSMesg2; +extern OSMesg OSMesg3; + +void seq_player_fade_to_zero_volume(s32 player, FadeT fadeOutTime) { + if (fadeOutTime == 0) { + fadeOutTime = 1; + } + gSequencePlayers[player].fadeVelocity = -(gSequencePlayers[player].fadeVolume / fadeOutTime); + gSequencePlayers[player].state = 2; + gSequencePlayers[player].fadeRemainingFrames = fadeOutTime; + +} + +void func_8031D690(s32 player, FadeT fadeInTime) { + if (fadeInTime != 0) { + gSequencePlayers[player].state = 1; + gSequencePlayers[player].fadeTimerUnkEu = fadeInTime; + gSequencePlayers[player].fadeRemainingFrames = fadeInTime; + gSequencePlayers[player].fadeVolume = 0.0f; + gSequencePlayers[player].fadeVelocity = 0.0f; + } +} + +void port_eu_init_queues(void) { + D_EU_80302010 = 0; + D_EU_80302014 = 0; + osCreateMesgQueue(OSMesgQueues[0], &OSMesg0, 1); + osCreateMesgQueue(OSMesgQueues[1], &OSMesg1, 4); + osCreateMesgQueue(OSMesgQueues[2], &OSMesg2, 1); + osCreateMesgQueue(OSMesgQueues[3], &OSMesg3, 1); +} + +void func_802ad6f0(s32 arg0, s32 *arg1) { + struct EuAudioCmd *cmd = &sAudioCmd[D_EU_80302010 & 0xff]; + cmd->u.first = arg0; + cmd->u2.as_u32 = *arg1; + D_EU_80302010++; +} + +void func_802ad728(u32 arg0, f32 arg1) { + func_802ad6f0(arg0, (s32*) &arg1); +} + +void func_802ad74c(u32 arg0, u32 arg1) { + func_802ad6f0(arg0, (s32*) &arg1); +} + +void func_802ad770(u32 arg0, s8 arg1) { + s32 sp1C = arg1 << 24; + func_802ad6f0(arg0, &sp1C); +} + +void func_802ad7a0(void) { + osSendMesg(OSMesgQueues[1], + (OSMesg)(u32)((D_EU_80302014 & 0xff) << 8 | (D_EU_80302010 & 0xff)), + OS_MESG_NOBLOCK); + D_EU_80302014 = D_EU_80302010; +} + +void func_802ad7ec(u32 arg0) { + struct EuAudioCmd *cmd; + struct SequencePlayer *seqPlayer; + struct SequenceChannel *chan; + u8 end = arg0 & 0xff; + u8 i = (arg0 >> 8) & 0xff; + + for (;;) { + if (i == end) break; + cmd = &sAudioCmd[i++ & 0xff]; + + if (cmd->u.s.arg1 < SEQUENCE_PLAYERS) { + seqPlayer = &gSequencePlayers[cmd->u.s.arg1]; + if ((cmd->u.s.op & 0x80) != 0) { + eu_process_audio_cmd(cmd); + } + else if ((cmd->u.s.op & 0x40) != 0) { + switch (cmd->u.s.op) { + case 0x41: + seqPlayer->fadeVolumeScale = cmd->u2.as_f32; + seqPlayer->recalculateVolume = TRUE; + break; + + case 0x47: + seqPlayer->tempo = cmd->u2.as_s32 * TATUMS_PER_BEAT; + break; + + case 0x48: + seqPlayer->transposition = cmd->u2.as_s8; + break; + + case 0x46: + seqPlayer->seqVariationEu[cmd->u.s.arg3] = cmd->u2.as_s8; + break; + } + } + else if (seqPlayer->enabled != FALSE && cmd->u.s.arg2 < 0x10) { + chan = seqPlayer->channels[cmd->u.s.arg2]; + if (IS_SEQUENCE_CHANNEL_VALID(chan)) + { + switch (cmd->u.s.op) { + case 1: + chan->volumeScale = cmd->u2.as_f32; + chan->changes.as_bitfields.volume = TRUE; + break; + case 2: + chan->volume = cmd->u2.as_f32; + chan->changes.as_bitfields.volume = TRUE; + break; + case 3: + chan->newPan = cmd->u2.as_s8; + chan->changes.as_bitfields.pan = TRUE; + break; + case 4: + chan->freqScale = cmd->u2.as_f32; + chan->changes.as_bitfields.freqScale = TRUE; + break; + case 5: + chan->reverbVol = cmd->u2.as_s8; + break; + case 6: + if (cmd->u.s.arg3 < 8) { + chan->soundScriptIO[cmd->u.s.arg3] = cmd->u2.as_s8; + } + break; + case 8: + chan->stopSomething2 = cmd->u2.as_s8; + } + } + } + } + + cmd->u.s.op = 0; + } +} + +void port_eu_init(void) { + port_eu_init_queues(); +} + +#endif diff --git a/src/decomp/audio/port_sh.c b/src/decomp/audio/port_sh.c new file mode 100644 index 0000000..66cbc3b --- /dev/null +++ b/src/decomp/audio/port_sh.c @@ -0,0 +1,597 @@ +#ifdef VERSION_SH +// TODO: merge this with port_eu.c? + +#include + +#include "data.h" +#include "heap.h" +#include "load.h" +#include "synthesis.h" +#include "internal.h" +#include "seqplayer.h" + +#define EXTRA_BUFFERED_AI_SAMPLES_TARGET 0x80 +#define SAMPLES_TO_OVERPRODUCE 0x10 + +extern s32 D_SH_80314FC8; +extern struct SPTask *D_SH_80314FCC; +extern u8 D_SH_80315098; +extern u8 D_SH_8031509C; +extern OSMesgQueue *D_SH_80350F68; + +void func_8031D690(s32 playerIndex, s32 numFrames); +void seq_player_fade_to_zero_volume(s32 arg0, s32 numFrames); +void func_802ad7ec(u32 arg0); + +#ifdef TARGET_N64 +struct SPTask *create_next_audio_frame_task(void) { + u32 samplesRemainingInAI; + s32 writtenCmds; + s32 index; + OSTask_t *task; + s32 flags; + s16 *currAiBuffer; + s32 oldDmaCount; + s32 sp38; + s32 sp34; + s32 writtenCmdsCopy; + + gAudioFrameCount++; + if (gAudioFrameCount % gAudioBufferParameters.presetUnk4 != 0) { + if ((gAudioFrameCount % gAudioBufferParameters.presetUnk4) + 1 == gAudioBufferParameters.presetUnk4) { + return D_SH_80314FCC; + } + return NULL; + } + osSendMesg(D_SH_80350F38, (OSMesg) gAudioFrameCount, OS_MESG_NOBLOCK); + + gAudioTaskIndex ^= 1; + gCurrAiBufferIndex++; + gCurrAiBufferIndex %= NUMAIBUFFERS; + index = (gCurrAiBufferIndex - 2 + NUMAIBUFFERS) % NUMAIBUFFERS; + samplesRemainingInAI = osAiGetLength() / 4; + + if (gAudioLoadLockSH < 0x10U) { + if (gAiBufferLengths[index] != 0) { + osAiSetNextBuffer(gAiBuffers[index], gAiBufferLengths[index] * 4); + } + } + + oldDmaCount = gCurrAudioFrameDmaCount; + if (oldDmaCount > AUDIO_FRAME_DMA_QUEUE_SIZE) { + } + gCurrAudioFrameDmaCount = 0; + + decrease_sample_dma_ttls(); + func_sh_802f41e4(gAudioResetStatus); + if (osRecvMesg(D_SH_80350F88, (OSMesg *) &sp38, OS_MESG_NOBLOCK) != -1) { + if (gAudioResetStatus == 0) { + gAudioResetStatus = 5; + } + gAudioResetPresetIdToLoad = (u8) sp38; + } + + if (gAudioResetStatus != 0) { + if (audio_shut_down_and_reset_step() == 0) { + if (gAudioResetStatus == 0) { + osSendMesg(D_SH_80350FA8, (OSMesg) (s32) gAudioResetPresetIdToLoad, OS_MESG_NOBLOCK); + } + D_SH_80314FCC = 0; + return NULL; + } + } + if (gAudioLoadLockSH >= 0x11U) { + return NULL; + } + if (gAudioLoadLockSH != 0) { + gAudioLoadLockSH++; + } + + gAudioTask = &gAudioTasks[gAudioTaskIndex]; + gAudioCmd = (u64 *) gAudioCmdBuffers[gAudioTaskIndex]; + index = gCurrAiBufferIndex; + currAiBuffer = gAiBuffers[index]; + + gAiBufferLengths[index] = (s16) ((((gAudioBufferParameters.samplesPerFrameTarget - samplesRemainingInAI) + + EXTRA_BUFFERED_AI_SAMPLES_TARGET) & ~0xf) + SAMPLES_TO_OVERPRODUCE); + if (gAiBufferLengths[index] < gAudioBufferParameters.minAiBufferLength) { + gAiBufferLengths[index] = gAudioBufferParameters.minAiBufferLength; + } + if (gAiBufferLengths[index] > gAudioBufferParameters.maxAiBufferLength) { + gAiBufferLengths[index] = gAudioBufferParameters.maxAiBufferLength; + } + + if (osRecvMesg(D_SH_80350F68, (OSMesg *) &sp34, 0) != -1) { + do { + func_802ad7ec(sp34); + } while (osRecvMesg(D_SH_80350F68, (OSMesg *) &sp34, 0) != -1); + } + + flags = 0; + gAudioCmd = synthesis_execute(gAudioCmd, &writtenCmds, currAiBuffer, gAiBufferLengths[index]); + gAudioRandom = (u32) (osGetCount() * (gAudioRandom + gAudioFrameCount)); + gAudioRandom = (u32) (gAiBuffers[index][gAudioFrameCount & 0xFF] + gAudioRandom); + + index = gAudioTaskIndex; + gAudioTask->msgqueue = NULL; + gAudioTask->msg = NULL; + + task = &gAudioTask->task.t; + task->type = M_AUDTASK; + task->flags = flags; + task->ucode_boot = rspF3DBootStart; + task->ucode_boot_size = (u8 *) rspF3DStart - (u8 *) rspF3DBootStart; + task->ucode = rspAspMainStart; + task->ucode_data = rspAspMainDataStart; + task->ucode_size = 0x1000; + task->ucode_data_size = (rspAspMainDataEnd - rspAspMainDataStart) * sizeof(u64); + task->dram_stack = NULL; + task->dram_stack_size = 0; + task->output_buff = NULL; + task->output_buff_size = NULL; + task->data_ptr = gAudioCmdBuffers[index]; + task->data_size = writtenCmds * sizeof(u64); + task->yield_data_ptr = NULL; + task->yield_data_size = 0; + + writtenCmdsCopy = writtenCmds; + if (D_SH_80314FC8 < writtenCmdsCopy) { + D_SH_80314FC8 = writtenCmdsCopy; + } + + if (gAudioBufferParameters.presetUnk4 == 1) { + return gAudioTask; + } else { + D_SH_80314FCC = gAudioTask; + return NULL; + } +} +#else +struct SPTask *create_next_audio_frame_task(void) { + return NULL; +} +void create_next_audio_buffer(s16 *samples, u32 num_samples) { + s32 writtenCmds; + OSMesg msg; + gAudioFrameCount++; + decrease_sample_dma_ttls(); + if (osRecvMesg(D_SH_80350F88, &msg, 0) != -1) { + gAudioResetPresetIdToLoad = (u8) (intptr_t) msg; + if (gAudioResetStatus == 0) { + gAudioResetStatus = 5; + } + } + + if (gAudioResetStatus != 0) { + audio_reset_session(); + gAudioResetStatus = 0; + } + while (osRecvMesg(D_SH_80350F68, &msg, OS_MESG_NOBLOCK) != -1) { + func_802ad7ec((u32) msg); + } + synthesis_execute(gAudioCmdBuffers[0], &writtenCmds, samples, num_samples); + gAudioRandom = ((gAudioRandom + gAudioFrameCount) * gAudioFrameCount); + gAudioRandom = gAudioRandom + writtenCmds / 8; + gCurrAudioFrameDmaCount = 0; +} +#endif + +void eu_process_audio_cmd(struct EuAudioCmd *cmd) { + s32 i; + struct Note *note; + struct NoteSubEu *sub; + + switch (cmd->u.s.op) { + case 0x81: + preload_sequence(cmd->u.s.arg2, 3); + break; + + case 0x82: + case 0x88: + load_sequence(cmd->u.s.arg1, cmd->u.s.arg2, cmd->u.s.arg3); + func_8031D690(cmd->u.s.arg1, cmd->u2.as_s32); + break; + + case 0x83: + if (gSequencePlayers[cmd->u.s.arg1].enabled != FALSE) { + if (cmd->u2.as_s32 == 0) { + sequence_player_disable(&gSequencePlayers[cmd->u.s.arg1]); + } + else { + seq_player_fade_to_zero_volume(cmd->u.s.arg1, cmd->u2.as_s32); + } + } + break; + + case 0x84: + break; + + case 0xf0: + gSoundMode = cmd->u2.as_s32; + break; + + case 0xf1: + for (i = 0; i < 4; i++) { + gSequencePlayers[i].muted = TRUE; + gSequencePlayers[i].recalculateVolume = TRUE; + } + break; + + case 0xf2: + if (cmd->u2.as_s32 == 1) { + for (i = 0; i < gMaxSimultaneousNotes; i++) { + note = &gNotes[i]; + sub = ¬e->noteSubEu; + if (note->noteSubEu.enabled && note->unkSH34 == 0) { + if ((note->parentLayer->seqChannel->muteBehavior & 8) != 0) { + sub->finished = TRUE; + } + } + } + } + for (i = 0; i < 4; i++) { + gSequencePlayers[i].muted = FALSE; + gSequencePlayers[i].recalculateVolume = TRUE; + } + break; + + case 0xf3: + func_sh_802f3024(cmd->u.s.arg1, cmd->u.s.arg2, cmd->u.s.arg3); + break; + + case 0xf4: + func_sh_802f30f4(cmd->u.s.arg1, cmd->u.s.arg2, cmd->u.s.arg3, &gUnkQueue1); + break; + + case 0xf5: + func_sh_802f3158(cmd->u.s.arg1, cmd->u.s.arg2, cmd->u.s.arg3, &gUnkQueue1); + break; + + case 0xf6: + func_sh_802f3288(cmd->u.s.arg2); + break; + } +} + +void seq_player_fade_to_zero_volume(s32 arg0, s32 fadeOutTime) { + struct SequencePlayer *player; + + if (fadeOutTime == 0) { + fadeOutTime = 1; + } + player = &gSequencePlayers[arg0]; + player->state = 2; + player->fadeRemainingFrames = fadeOutTime; + player->fadeVelocity = -(player->fadeVolume / (f32) fadeOutTime); +} + +void func_8031D690(s32 playerIndex, s32 fadeInTime) { + struct SequencePlayer *player; + + if (fadeInTime != 0) { + player = &gSequencePlayers[playerIndex]; + player->state = 1; + player->fadeTimerUnkEu = fadeInTime; + player->fadeRemainingFrames = fadeInTime; + player->fadeVolume = 0.0f; + player->fadeVelocity = 0.0f; + } +} + +void port_eu_init_queues(void) { + D_SH_80350F18 = 0; + D_SH_80350F19 = 0; + D_SH_80350F38 = &D_SH_80350F20; + D_SH_80350F68 = &D_SH_80350F50; + D_SH_80350F88 = &D_SH_80350F70; + D_SH_80350FA8 = &D_SH_80350F90; + osCreateMesgQueue(D_SH_80350F38, D_SH_80350F1C, 1); + osCreateMesgQueue(D_SH_80350F68, D_SH_80350F40, 4); + osCreateMesgQueue(D_SH_80350F88, D_SH_80350F6C, 1); + osCreateMesgQueue(D_SH_80350FA8, D_SH_80350F8C, 1); +} + +extern struct EuAudioCmd sAudioCmd[0x100]; +void func_802ad6f0(s32 arg0, s32 *arg1) { + struct EuAudioCmd *cmd = &sAudioCmd[D_SH_80350F18 & 0xff]; + cmd->u.first = arg0; + cmd->u2.as_u32 = *arg1; + D_SH_80350F18++; + if (D_SH_80350F18 == D_SH_80350F19) { + D_SH_80350F18--; + } +} + +void func_802ad728(u32 arg0, f32 arg1) { + func_802ad6f0(arg0, (s32 *) &arg1); +} + +void func_802ad74c(u32 arg0, u32 arg1) { + func_802ad6f0(arg0, (s32 *) &arg1); +} + +void func_802ad770(u32 arg0, s8 arg1) { + s32 sp1C = arg1 << 24; + func_802ad6f0(arg0, &sp1C); +} + +char shindouDebugPrint133[] = "AudioSend: %d -> %d (%d)\n"; + +void func_sh_802F64C8(void) { + static s32 D_SH_8031503C = 0; + s32 mesg; + + if (((D_SH_80350F18 - D_SH_80350F19 + 0x100) & 0xff) > D_SH_8031503C) { + D_SH_8031503C = (D_SH_80350F18 - D_SH_80350F19 + 0x100) & 0xff; + } + mesg = ((D_SH_80350F19 & 0xff) << 8) | (D_SH_80350F18 & 0xff); + osSendMesg(D_SH_80350F68, (OSMesg)mesg, OS_MESG_NOBLOCK); + D_SH_80350F19 = D_SH_80350F18; +} + +void func_sh_802f6540(void) { + D_SH_80350F19 = D_SH_80350F18; +} + +void func_802ad7ec(u32 arg0) { + struct EuAudioCmd *cmd; + struct SequencePlayer *seqPlayer; + struct SequenceChannel *chan; + u8 end; + + UNUSED static char shindouDebugPrint134[] = "Continue Port\n"; + UNUSED static char shindouDebugPrint135[] = "%d -> %d\n"; + UNUSED static char shindouDebugPrint136[] = "Sync-Frame Break. (Remain %d)\n"; + UNUSED static char shindouDebugPrint137[] = "Undefined Port Command %d\n"; + + static u8 D_SH_80315098 = 0; + static u8 D_SH_8031509C = 0; + + if (D_SH_8031509C == 0) { + D_SH_80315098 = (arg0 >> 8) & 0xff; + } + + end = arg0 & 0xff; + + for (;;) { + if (D_SH_80315098 == end) { + D_SH_8031509C = 0; + break; + } + cmd = &sAudioCmd[D_SH_80315098 & 0xff]; + D_SH_80315098++; + if (cmd->u.s.op == 0xf8) { + D_SH_8031509C = 1; + break; + } + else if ((cmd->u.s.op & 0xf0) == 0xf0) { + eu_process_audio_cmd(cmd); + } + else if (cmd->u.s.arg1 < SEQUENCE_PLAYERS) { + seqPlayer = &gSequencePlayers[cmd->u.s.arg1]; + if ((cmd->u.s.op & 0x80) != 0) { + eu_process_audio_cmd(cmd); + } + else if ((cmd->u.s.op & 0x40) != 0) { + switch (cmd->u.s.op) { + case 0x41: + if (seqPlayer->fadeVolumeScale != cmd->u2.as_f32) { + seqPlayer->fadeVolumeScale = cmd->u2.as_f32; + seqPlayer->recalculateVolume = TRUE; + } + break; + + case 0x47: + seqPlayer->tempo = cmd->u2.as_s32 * TATUMS_PER_BEAT; + break; + + case 0x49: + seqPlayer->tempoAdd = cmd->u2.as_s32 * TEMPO_SCALE; + break; + + case 0x48: + seqPlayer->transposition = cmd->u2.as_s8; + break; + + case 0x46: + seqPlayer->seqVariationEu[cmd->u.s.arg3] = cmd->u2.as_s8; + break; + } + } + else if (seqPlayer->enabled != FALSE && cmd->u.s.arg2 < 0x10) { + chan = seqPlayer->channels[cmd->u.s.arg2]; + if (IS_SEQUENCE_CHANNEL_VALID(chan)) + { + switch (cmd->u.s.op) { + case 1: + if (chan->volumeScale != cmd->u2.as_f32) { + chan->volumeScale = cmd->u2.as_f32; + chan->changes.as_bitfields.volume = TRUE; + } + break; + case 2: + if (chan->volume != cmd->u2.as_f32) { + chan->volume = cmd->u2.as_f32; + chan->changes.as_bitfields.volume = TRUE; + } + break; + case 3: + if (chan->newPan != cmd->u2.as_s8) { + chan->newPan = cmd->u2.as_s8; + chan->changes.as_bitfields.pan = TRUE; + } + break; + case 4: + if (chan->freqScale != cmd->u2.as_f32) { + chan->freqScale = cmd->u2.as_f32; + chan->changes.as_bitfields.freqScale = TRUE; + } + break; + case 5: + //! @bug u8 s8 comparison (but harmless) + if (chan->reverbVol != cmd->u2.as_s8) { + chan->reverbVol = cmd->u2.as_s8; + } + break; + case 6: + if (cmd->u.s.arg3 < 8) { + chan->soundScriptIO[cmd->u.s.arg3] = cmd->u2.as_s8; + } + break; + case 8: + chan->stopSomething2 = cmd->u2.as_s8; + break; + case 9: + chan->muteBehavior = cmd->u2.as_s8; + } + } + } + } + + cmd->u.s.op = 0; + } +} + +u32 func_sh_802f6878(s32 *arg0) { + u32 sp1C; + + if (osRecvMesg(&gUnkQueue1, (OSMesg *) &sp1C, 0) == -1) { + *arg0 = 0; + return 0U; + } + *arg0 = (s32) (sp1C & 0xFFFFFF); + return sp1C >> 0x18; +} + +u8 *func_sh_802f68e0(u32 index, u32 *a1) { + return func_sh_802f3220(index, a1); +} + +s32 func_sh_802f6900(void) { + s32 ret; + s32 sp18; + + ret = osRecvMesg(D_SH_80350FA8, (OSMesg *) &sp18, 0); + + if (ret == -1) { + return 0; + } + if (sp18 != gAudioResetPresetIdToLoad) { + return 0; + } else { + return 1; + } +} + +// TODO: (Scrub C) +void func_sh_802f6958(OSMesg mesg) { + s32 a; + OSMesg recvMesg; + + do { + a = -1; + } while (osRecvMesg(D_SH_80350FA8, &recvMesg, OS_MESG_NOBLOCK) != a); + func_sh_802f6540(); + osSendMesg(D_SH_80350F88, mesg, OS_MESG_NOBLOCK); +} + +void func_sh_802f69cc(void) { + gAudioLoadLockSH = 1; + func_sh_802f6958(0); + gAudioResetStatus = 0; +} + +s32 func_sh_802f6a08(s32 playerIndex, s32 channelIndex, s32 soundScriptIOIndex) { + struct SequenceChannel *seqChannel; + struct SequencePlayer *player; + + player = &gSequencePlayers[playerIndex]; + if (player->enabled) { + seqChannel = player->channels[channelIndex]; + if (IS_SEQUENCE_CHANNEL_VALID(seqChannel)) { + return seqChannel->soundScriptIO[soundScriptIOIndex]; + } + } + return -1; +} + +s8 func_sh_802f6a6c(s32 playerIndex, s32 index) { + return gSequencePlayers[playerIndex].seqVariationEu[index]; +} + +void port_eu_init(void) { + port_eu_init_queues(); +} + +char shindouDebugPrint138[] = "specchg conjunction error (Msg:%d Cur:%d)\n"; +char shindouDebugPrint139[] = "Error : Queue is not empty ( %x ) \n"; +char shindouDebugPrint140[] = "Audio: setvol: volume minus %f\n"; +char shindouDebugPrint141[] = "Audio: setvol: volume overflow %f\n"; +char shindouDebugPrint142[] = "Audio: setpitch: pitch zero or minus %f\n"; +char shindouDebugPrint143[] = "----------------------Double-Error CH: %x %f\n"; +char shindouDebugPrint144[] = "----------------------Double-Error NT: %x\n"; +char shindouDebugPrint145[] = "CAUTION:SUB IS SEPARATED FROM GROUP\n"; +char shindouDebugPrint146[] = "CAUTION:PAUSE EMERGENCY\n"; +char shindouDebugPrint147[] = "Error:Wait Track disappear\n"; +char shindouDebugPrint148[] = "Audio: voiceman: No bank error %d\n"; +char shindouDebugPrint149[] = "Audio: voiceman: progNo. overflow %d,%d\n"; +char shindouDebugPrint150[] = "ptr2 %x\n"; +char shindouDebugPrint151[] = "Audio: voiceman: progNo. undefined %d,%d\n"; +char shindouDebugPrint152[] = "Audio: voiceman: No bank error %d\n"; +char shindouDebugPrint153[] = "Audio: voiceman: Percussion Overflow %d,%d\n"; +char shindouDebugPrint154[] = "Audio: voiceman: Percussion table pointer (bank %d) is irregular %x.\n"; +char shindouDebugPrint155[] = "Audio: voiceman: Percpointer NULL %d,%d\n"; +char shindouDebugPrint156[] = "--4 %x\n"; +char shindouDebugPrint157[] = "NoteOff Comes during wait release %x (note %x)\n"; +char shindouDebugPrint158[] = "Slow Release Batting\n"; +u8 euUnknownData_8030194c[4] = { 0x40, 0x20, 0x10, 0x08 }; +char shindouDebugPrint159[] = "Audio:Wavemem: Bad voiceno (%d)\n"; +char shindouDebugPrint160[] = "Audio: C-Alloc : Dealloc voice is NULL\n"; +char shindouDebugPrint161[] = "Alloc Error:Dim voice-Alloc %d"; +char shindouDebugPrint162[] = "Error:Same List Add\n"; +char shindouDebugPrint163[] = "Already Cut\n"; +char shindouDebugPrint164[] = "Audio: C-Alloc : lowerPrio is NULL\n"; +char shindouDebugPrint165[] = "Intterupt UseStop %d (Kill %d)\n"; +char shindouDebugPrint166[] = "Intterupt RelWait %d (Kill %d)\n"; +char shindouDebugPrint167[] = "Drop Voice (Prio %x)\n"; +s32 D_SH_803154CC = 0; // file boundary + +// effects.c +char shindouDebugPrint168[] = "Audio:Envp: overflow %f\n"; +s32 D_SH_803154EC = 0; // file boundary + +// seqplayer.c +char shindouDebugPrint169[] = "Audio:Track:Warning: No Free Notetrack\n"; +char shindouDebugPrint170[] = "SUBTRACK DIM\n"; +char shindouDebugPrint171[] = "Audio:Track: Warning :SUBTRACK had been stolen by other Group.\n"; +char shindouDebugPrint172[] = "SEQID %d,BANKID %d\n"; +char shindouDebugPrint173[] = "ERR:SUBTRACK %d NOT ALLOCATED\n"; +char shindouDebugPrint174[] = "Stop Release\n"; +char shindouDebugPrint175[] = "Error:Same List Add\n"; +char shindouDebugPrint176[] = "Wait Time out!\n"; +char shindouDebugPrint177[] = "Macro Level Over Error!\n"; +char shindouDebugPrint178[] = "Macro Level Over Error!\n"; // Again +char shindouDebugPrint179[] = "WARNING: NPRG: cannot change %d\n"; +char shindouDebugPrint180[] = "Audio:Track:NOTE:UNDEFINED NOTE COM. %x\n"; +char shindouDebugPrint181[] = "Error: Subtrack no prg.\n"; +char shindouDebugPrint182[] = "ERR %x\n"; +char shindouDebugPrint183[] = "Note OverFlow %d\n"; +char shindouDebugPrint184[] = "trs %d , %d, %d\n"; +char shindouDebugPrint185[] = "Audio: Note:Velocity Error %d\n"; +char shindouDebugPrint186[] = "Audio:Track :Call Macro Level Over Error!\n"; +char shindouDebugPrint187[] = "Audio:Track :Loops Macro Level Over Error!\n"; +char shindouDebugPrint188[] = "SUB:ERR:BANK %d NOT CACHED.\n"; +char shindouDebugPrint189[] = "SUB:ERR:BANK %d NOT CACHED.\n"; +char shindouDebugPrint190[] = "Audio:Track: CTBLCALL Macro Level Over Error!\n"; +char shindouDebugPrint191[] = "Set Noise %d\n"; +char shindouDebugPrint192[] = "[%2x] \n"; +char shindouDebugPrint193[] = "Err :Sub %x ,address %x:Undefined SubTrack Function %x"; +char shindouDebugPrint194[] = "VoiceLoad Error Bank:%d,Prog:%d\n"; +char shindouDebugPrint195[] = "Disappear Sequence or Bank %d\n"; +char shindouDebugPrint196[] = "[FIN] group.\n"; +char shindouDebugPrint197[] = "Macro Level Over Error!\n"; +char shindouDebugPrint198[] = "Macro Level Over Error!\n"; +char shindouDebugPrint199[] = "Group:Undefine upper C0h command (%x)\n"; +char shindouDebugPrint200[] = "Group:Undefined Command\n"; + +#endif diff --git a/src/decomp/audio/seqplayer.c b/src/decomp/audio/seqplayer.c new file mode 100644 index 0000000..c9e95b6 --- /dev/null +++ b/src/decomp/audio/seqplayer.c @@ -0,0 +1,2873 @@ +#include + +#include "data.h" +#include "effects.h" +#include "external.h" +#include "heap.h" +#include "load.h" +#include "seqplayer.h" +#include "../../debug_print.h" + +#define PORTAMENTO_IS_SPECIAL(x) ((x).mode & 0x80) +#define PORTAMENTO_MODE(x) ((x).mode & ~0x80) +#define PORTAMENTO_MODE_1 1 +#define PORTAMENTO_MODE_2 2 +#define PORTAMENTO_MODE_3 3 +#define PORTAMENTO_MODE_4 4 +#define PORTAMENTO_MODE_5 5 + +#ifdef VERSION_SH +void seq_channel_layer_process_script_part1(struct SequenceChannelLayer *layer); +s32 seq_channel_layer_process_script_part2(struct SequenceChannelLayer *layer); +s32 seq_channel_layer_process_script_part3(struct SequenceChannelLayer *layer, s32 cmd); +s32 seq_channel_layer_process_script_part4(struct SequenceChannelLayer *layer, s32 cmd); +s32 seq_channel_layer_process_script_part5(struct SequenceChannelLayer *layer, s32 cmd); +#endif +void seq_channel_layer_process_script(struct SequenceChannelLayer *layer); +void sequence_channel_process_script(struct SequenceChannel *seqChannel); +u8 get_instrument(struct SequenceChannel *seqChannel, u8 instId, struct Instrument **instOut, + struct AdsrSettings *adsr); + +void sequence_channel_init(struct SequenceChannel *seqChannel) { + s32 i; + + seqChannel->enabled = FALSE; + seqChannel->finished = FALSE; + seqChannel->stopScript = FALSE; + seqChannel->stopSomething2 = FALSE; + seqChannel->hasInstrument = FALSE; + seqChannel->stereoHeadsetEffects = FALSE; + seqChannel->transposition = 0; + seqChannel->largeNotes = FALSE; +#if defined(VERSION_EU) || defined(VERSION_SH) + seqChannel->bookOffset = 0; + seqChannel->changes.as_u8 = 0xff; + seqChannel->scriptState.depth = 0; + seqChannel->newPan = 0x40; + seqChannel->panChannelWeight = 0x80; + seqChannel->noteUnused = NULL; + seqChannel->reverbIndex = 0; +#else + seqChannel->scriptState.depth = 0; + seqChannel->volume = 1.0f; + seqChannel->volumeScale = 1.0f; + seqChannel->freqScale = 1.0f; + seqChannel->pan = 0.5f; + seqChannel->panChannelWeight = 1.0f; + seqChannel->noteUnused = NULL; +#endif + seqChannel->reverbVol = 0; +#ifdef VERSION_SH + seqChannel->synthesisVolume = 0; +#endif + seqChannel->notePriority = NOTE_PRIORITY_DEFAULT; +#ifdef VERSION_SH + seqChannel->unkSH06 = 1; +#endif + seqChannel->delay = 0; + seqChannel->adsr.envelope = gDefaultEnvelope; + seqChannel->adsr.releaseRate = 0x20; + seqChannel->adsr.sustain = 0; +#if defined(VERSION_JP) || defined(VERSION_US) + seqChannel->updatesPerFrameUnused = gAudioUpdatesPerFrame; +#endif + seqChannel->vibratoRateTarget = 0x800; + seqChannel->vibratoRateStart = 0x800; + seqChannel->vibratoExtentTarget = 0; + seqChannel->vibratoExtentStart = 0; + seqChannel->vibratoRateChangeDelay = 0; + seqChannel->vibratoExtentChangeDelay = 0; + seqChannel->vibratoDelay = 0; +#ifdef VERSION_SH + seqChannel->filter = NULL; +#endif +#if defined(VERSION_EU) || defined(VERSION_SH) + seqChannel->volume = 1.0f; + seqChannel->volumeScale = 1.0f; + seqChannel->freqScale = 1.0f; +#endif + + for (i = 0; i < 8; i++) { + seqChannel->soundScriptIO[i] = -1; + } + + seqChannel->unused = FALSE; + init_note_lists(&seqChannel->notePool); +} + +s32 seq_channel_set_layer(struct SequenceChannel *seqChannel, s32 layerIndex) { + struct SequenceChannelLayer *layer; + + if (seqChannel->layers[layerIndex] == NULL) { +#if defined(VERSION_EU) || defined(VERSION_SH) + struct SequenceChannelLayer *layer; +#endif + layer = audio_list_pop_back(&gLayerFreeList); + seqChannel->layers[layerIndex] = layer; + if (layer == NULL) { + seqChannel->layers[layerIndex] = NULL; + return -1; + } + } else { + seq_channel_layer_note_decay(seqChannel->layers[layerIndex]); + } + + layer = seqChannel->layers[layerIndex]; + layer->seqChannel = seqChannel; + layer->adsr = seqChannel->adsr; + layer->adsr.releaseRate = 0; + layer->enabled = TRUE; + layer->stopSomething = FALSE; + layer->continuousNotes = FALSE; + layer->finished = FALSE; +#if defined(VERSION_EU) || defined(VERSION_SH) + layer->ignoreDrumPan = FALSE; +#endif +#ifdef VERSION_SH + layer->reverbBits.asByte = 0x40; +#endif + layer->portamento.mode = 0; + layer->scriptState.depth = 0; + layer->status = SOUND_LOAD_STATUS_NOT_LOADED; + layer->noteDuration = 0x80; +#if defined(VERSION_EU) || defined(VERSION_SH) + layer->pan = 0x40; +#endif + layer->transposition = 0; + layer->delay = 0; + layer->duration = 0; + layer->delayUnused = 0; + layer->note = NULL; + layer->instrument = NULL; +#if defined(VERSION_EU) || defined(VERSION_SH) + layer->freqScale = 1.0f; + layer->velocitySquare = 0.0f; +#ifdef VERSION_SH + layer->freqScaleMultiplier = 1.0f; +#endif + layer->instOrWave = 0xff; +#else + layer->velocitySquare = 0.0f; + layer->pan = 0.5f; +#endif + return 0; +} + +void seq_channel_layer_disable(struct SequenceChannelLayer *layer) { + if (layer != NULL) { + seq_channel_layer_note_decay(layer); + layer->enabled = FALSE; + layer->finished = TRUE; + } +} + +void seq_channel_layer_free(struct SequenceChannel *seqChannel, s32 layerIndex) { + struct SequenceChannelLayer *layer = seqChannel->layers[layerIndex]; + + if (layer != NULL) { +#if defined(VERSION_EU) || defined(VERSION_SH) + audio_list_push_back(&gLayerFreeList, &layer->listItem); +#else + struct AudioListItem *item = &layer->listItem; + if (item->prev == NULL) { + gLayerFreeList.prev->next = item; + item->prev = gLayerFreeList.prev; + item->next = &gLayerFreeList; + gLayerFreeList.prev = item; + gLayerFreeList.u.count++; + item->pool = gLayerFreeList.pool; + } +#endif + seq_channel_layer_disable(layer); + seqChannel->layers[layerIndex] = NULL; + } +} + +void sequence_channel_disable(struct SequenceChannel *seqChannel) { + s32 i; + for (i = 0; i < LAYERS_MAX; i++) { + seq_channel_layer_free(seqChannel, i); + } + + note_pool_clear(&seqChannel->notePool); + seqChannel->enabled = FALSE; + seqChannel->finished = TRUE; +} + +struct SequenceChannel *allocate_sequence_channel(void) { + s32 i; + for (i = 0; i < ARRAY_COUNT(gSequenceChannels); i++) { + if (gSequenceChannels[i].seqPlayer == NULL) { +#if defined(VERSION_EU) || defined(VERSION_SH) + return &gSequenceChannels[i]; +#else + return gSequenceChannels + i; +#endif + } + } + return &gSequenceChannelNone; +} + +void sequence_player_init_channels(struct SequencePlayer *seqPlayer, u16 channelBits) { + struct SequenceChannel *seqChannel; + s32 i; + + for (i = 0; i < CHANNELS_MAX; i++) { + if (channelBits & 1) { + seqChannel = seqPlayer->channels[i]; + if (IS_SEQUENCE_CHANNEL_VALID(seqChannel) == TRUE && seqChannel->seqPlayer == seqPlayer) { + sequence_channel_disable(seqChannel); + seqChannel->seqPlayer = NULL; + } + seqChannel = allocate_sequence_channel(); + if (IS_SEQUENCE_CHANNEL_VALID(seqChannel) == FALSE) { + eu_stubbed_printf_0("Audio:Track:Warning: No Free Notetrack\n"); + gAudioErrorFlags = i + 0x10000; + seqPlayer->channels[i] = seqChannel; + } else { + sequence_channel_init(seqChannel); + seqPlayer->channels[i] = seqChannel; + seqChannel->seqPlayer = seqPlayer; + seqChannel->bankId = seqPlayer->defaultBank[0]; + seqChannel->muteBehavior = seqPlayer->muteBehavior; + seqChannel->noteAllocPolicy = seqPlayer->noteAllocPolicy; + } + } +#if defined(VERSION_EU) || defined(VERSION_SH) + channelBits = channelBits >> 1; +#else + channelBits >>= 1; +#endif + } +} + +void sequence_player_disable_channels(struct SequencePlayer *seqPlayer, u16 channelBits) { + struct SequenceChannel *seqChannel; + s32 i; + + eu_stubbed_printf_0("SUBTRACK DIM\n"); + for (i = 0; i < CHANNELS_MAX; i++) { + if (channelBits & 1) { + seqChannel = seqPlayer->channels[i]; + if (IS_SEQUENCE_CHANNEL_VALID(seqChannel) == TRUE) { + if (seqChannel->seqPlayer == seqPlayer) { + sequence_channel_disable(seqChannel); + seqChannel->seqPlayer = NULL; + } +#if defined(VERSION_EU) || defined(VERSION_SH) + else { +#ifdef VERSION_EU + stubbed_printf("Audio:Track: Warning SUBTRACK PARENT CHANGED\n"); +#endif + } +#endif + seqPlayer->channels[i] = &gSequenceChannelNone; + } + } +#if defined(VERSION_EU) || defined(VERSION_SH) + channelBits = channelBits >> 1; +#else + channelBits >>= 1; +#endif + } +} + +void sequence_channel_enable(struct SequencePlayer *seqPlayer, u8 channelIndex, void *script) { + struct SequenceChannel *seqChannel = seqPlayer->channels[channelIndex]; + s32 i; + if (IS_SEQUENCE_CHANNEL_VALID(seqChannel) == FALSE) { +#ifdef VERSION_EU + struct SequencePlayer *bgMusic = &gSequencePlayers[0]; + struct SequencePlayer *miscMusic = &gSequencePlayers[1]; + + if (seqPlayer == bgMusic) { + stubbed_printf("GROUP 0:"); + } else if (seqPlayer == miscMusic) { + stubbed_printf("GROUP 1:"); + } else { + stubbed_printf("SEQID %d,BANKID %d\n", + seqPlayer->seqId, seqPlayer->defaultBank[0]); + } + stubbed_printf("ERR:SUBTRACK %d NOT ALLOCATED\n", channelIndex); +#endif + } else { + seqChannel->enabled = TRUE; + seqChannel->finished = FALSE; + seqChannel->scriptState.depth = 0; + seqChannel->scriptState.pc = script; + seqChannel->delay = 0; + for (i = 0; i < LAYERS_MAX; i++) { + if (seqChannel->layers[i] != NULL) { + seq_channel_layer_free(seqChannel, i); + } + } + } +} + +void sequence_player_disable(struct SequencePlayer *seqPlayer) { + sequence_player_disable_channels(seqPlayer, 0xffff); + note_pool_clear(&seqPlayer->notePool); + seqPlayer->finished = TRUE; + seqPlayer->enabled = FALSE; + + if (IS_SEQ_LOAD_COMPLETE(seqPlayer->seqId) +#ifdef VERSION_SH + && gSeqLoadStatus[seqPlayer->seqId] != 5 +#endif + ) { + gSeqLoadStatus[seqPlayer->seqId] = SOUND_LOAD_STATUS_DISCARDABLE; + } + + if (IS_BANK_LOAD_COMPLETE(seqPlayer->defaultBank[0]) +#ifdef VERSION_SH + && gBankLoadStatus[seqPlayer->defaultBank[0]] != 5 +#endif + ) { +#ifdef VERSION_SH + gBankLoadStatus[seqPlayer->defaultBank[0]] = 4; +#else + gBankLoadStatus[seqPlayer->defaultBank[0]] = SOUND_LOAD_STATUS_DISCARDABLE; +#endif + } + + // (Note that if this is called from alloc_bank_or_seq, the side will get swapped + // later in that function. Thus, we signal that we want to load into the slot + // of the bank that we no longer need.) +#if defined(VERSION_EU) || defined(VERSION_SH) + if (seqPlayer->defaultBank[0] == gBankLoadedPool.temporary.entries[0].id) { + gBankLoadedPool.temporary.nextSide = 1; + } else if (seqPlayer->defaultBank[0] == gBankLoadedPool.temporary.entries[1].id) { + gBankLoadedPool.temporary.nextSide = 0; + } +#else + if (gBankLoadedPool.temporary.entries[0].id == seqPlayer->defaultBank[0]) { + gBankLoadedPool.temporary.nextSide = 1; + } else if (gBankLoadedPool.temporary.entries[1].id == seqPlayer->defaultBank[0]) { + gBankLoadedPool.temporary.nextSide = 0; + } +#endif +} + +/** + * Add an item to the end of a list, if it's not already in any list. + */ +void audio_list_push_back(struct AudioListItem *list, struct AudioListItem *item) { + if (item->prev != NULL) { + eu_stubbed_printf_0("Error:Same List Add\n"); + } else { + list->prev->next = item; + item->prev = list->prev; + item->next = list; + list->prev = item; + list->u.count++; + item->pool = list->pool; + } +} + +/** + * Remove the last item from a list, and return it (or NULL if empty). + */ +void *audio_list_pop_back(struct AudioListItem *list) { + struct AudioListItem *item = list->prev; + if (item == list) { + return NULL; + } + item->prev->next = list; + list->prev = item->prev; + item->prev = NULL; + list->u.count--; + return item->u.value; +} + +void init_layer_freelist(void) { + s32 i; + + gLayerFreeList.prev = &gLayerFreeList; + gLayerFreeList.next = &gLayerFreeList; + gLayerFreeList.u.count = 0; + gLayerFreeList.pool = NULL; + + for (i = 0; i < ARRAY_COUNT(gSequenceLayers); i++) { +#if defined(VERSION_EU) || defined(VERSION_SH) + gSequenceLayers[i].listItem.u.value = &gSequenceLayers[i]; +#else + gSequenceLayers[i].listItem.u.value = gSequenceLayers + i; +#endif + gSequenceLayers[i].listItem.prev = NULL; + audio_list_push_back(&gLayerFreeList, &gSequenceLayers[i].listItem); + } +} + +u8 m64_read_u8(struct M64ScriptState *state) { + DEBUG_PRINT("m64_read_u8()"); + DEBUG_PRINT("- state at %x", state); + u8 *midiArg = state->pc++; + DEBUG_PRINT("- read u8 (%d) at (%x)", *midiArg, midiArg); + return *midiArg; +} + +s16 m64_read_s16(struct M64ScriptState *state) { + s16 ret = *(state->pc++) << 8; + ret = *(state->pc++) | ret; + return ret; +} + +u16 m64_read_compressed_u16(struct M64ScriptState *state) { + u16 ret = *(state->pc++); + if (ret & 0x80) { + ret = (ret << 8) & 0x7f00; + ret = *(state->pc++) | ret; + } + return ret; +} + +#if defined(VERSION_SH) +void seq_channel_layer_process_script(struct SequenceChannelLayer *layer) { + s32 cmd; + + if (layer->enabled == FALSE) { + return; + } + + if (layer->delay > 1) { + layer->delay--; + if (!layer->stopSomething && layer->delay <= layer->duration) { + seq_channel_layer_note_decay(layer); + layer->stopSomething = TRUE; + } + return; + } + + seq_channel_layer_process_script_part1(layer); + cmd = seq_channel_layer_process_script_part2(layer); + if (cmd != -1) { + cmd = seq_channel_layer_process_script_part3(layer, cmd); + if (cmd != -1) { + cmd = seq_channel_layer_process_script_part4(layer, cmd); + } + if (cmd != -1) { + seq_channel_layer_process_script_part5(layer, cmd); + } + + if (layer->stopSomething == TRUE) { + if (layer->note != NULL || layer->continuousNotes) { + seq_channel_layer_note_decay(layer); + } + } + } +} +#elif defined(VERSION_EU) +void seq_channel_layer_process_script(struct SequenceChannelLayer *layer) { + struct SequencePlayer *seqPlayer; + struct SequenceChannel *seqChannel; +#ifdef VERSION_EU + UNUSED u32 pad0; +#endif + struct M64ScriptState *state; + struct Portamento *portamento; + struct AudioBankSound *sound; + struct Instrument *instrument; + struct Drum *drum; + s32 temp_a0_5; +#ifdef VERSION_EU + u16 sp3A; + s32 sameSound; +#endif + UNUSED u32 pad1; +#ifndef VERSION_EU + u8 sameSound; +#endif + u8 cmd; + UNUSED u8 cmdSemitone; +#ifndef VERSION_EU + u16 sp3A; +#endif + f32 tuning; + s32 vel; + UNUSED s32 usedSemitone; + f32 freqScale; +#ifndef VERSION_EU + UNUSED f32 sp24; +#endif + f32 temp_f12; + f32 temp_f2; + + sameSound = TRUE; + if (layer->enabled == FALSE) { + return; + } + + if (layer->delay > 1) { + layer->delay--; + if (!layer->stopSomething && layer->delay <= layer->duration) { + seq_channel_layer_note_decay(layer); + layer->stopSomething = TRUE; + } + return; + } + + if (!layer->continuousNotes) { + seq_channel_layer_note_decay(layer); + } + + if (PORTAMENTO_MODE(layer->portamento) == PORTAMENTO_MODE_1 || + PORTAMENTO_MODE(layer->portamento) == PORTAMENTO_MODE_2) { + layer->portamento.mode = 0; + } + + seqChannel = layer->seqChannel; + seqPlayer = seqChannel->seqPlayer; +#if defined(VERSION_EU) || defined(VERSION_SH) + layer->notePropertiesNeedInit = TRUE; +#endif + + for (;;) { + state = &layer->scriptState; + cmd = m64_read_u8(state); + + if (cmd <= 0xc0) { + break; + } + + switch (cmd) { + case 0xff: // layer_end; function return or end of script + if (state->depth == 0) { + // N.B. this function call is *not* inlined even though it's + // within the same file, unlike in the rest of this function. + seq_channel_layer_disable(layer); + return; + } + state->pc = state->stack[--state->depth]; + break; + + case 0xfc: // layer_call + if (0 && state->depth >= 4) { + eu_stubbed_printf_0("Macro Level Over Error!\n"); + } + sp3A = m64_read_s16(state); + state->stack[state->depth++] = state->pc; + state->pc = seqPlayer->seqData + sp3A; + break; + + case 0xf8: // layer_loop; loop start, N iterations (or 256 if N = 0) + if (0 && state->depth >= 4) { + eu_stubbed_printf_0("Macro Level Over Error!\n"); + } + state->remLoopIters[state->depth] = m64_read_u8(state); + state->stack[state->depth++] = state->pc; + break; + + case 0xf7: // layer_loopend + if (--state->remLoopIters[state->depth - 1] != 0) { + state->pc = state->stack[state->depth - 1]; + } else { + state->depth--; + } + break; + + case 0xfb: // layer_jump + sp3A = m64_read_s16(state); + state->pc = seqPlayer->seqData + sp3A; + break; + +#if defined(VERSION_EU) || defined(VERSION_SH) + case 0xf4: + state->pc += (s8)m64_read_u8(state); + break; +#endif + + case 0xc1: // layer_setshortnotevelocity + case 0xca: // layer_setpan + temp_a0_5 = *(state->pc++); + if (cmd == 0xc1) { + layer->velocitySquare = (f32)(temp_a0_5 * temp_a0_5); + } else { +#if defined(VERSION_EU) || defined(VERSION_SH) + layer->pan = temp_a0_5; +#else + layer->pan = (f32) temp_a0_5 / US_FLOAT(128.0); +#endif + } + break; + + case 0xc2: // layer_transpose; set transposition in semitones + case 0xc9: // layer_setshortnoteduration + temp_a0_5 = *(state->pc++); + if (cmd == 0xc9) { + layer->noteDuration = temp_a0_5; + } else { + layer->transposition = temp_a0_5; + } + break; + + case 0xc4: // layer_somethingon + case 0xc5: // layer_somethingoff + if (cmd == 0xc4) { + layer->continuousNotes = TRUE; + } else { + layer->continuousNotes = FALSE; + } + seq_channel_layer_note_decay(layer); + break; + + case 0xc3: // layer_setshortnotedefaultplaypercentage + sp3A = m64_read_compressed_u16(state); + layer->shortNoteDefaultPlayPercentage = sp3A; + break; + + case 0xc6: // layer_setinstr + cmd = m64_read_u8(state); +#if defined(VERSION_JP) || defined(VERSION_US) + if (cmd < 127) { + cmd = get_instrument(seqChannel, cmd, &layer->instrument, &layer->adsr); + } +#else + if (cmd >= 0x7f) { + if (cmd == 0x7f) { + layer->instOrWave = 0; + } else { + layer->instOrWave = cmd; + layer->instrument = NULL; + } + + if (1) { + } + + if (cmd == 0xff) { + layer->adsr.releaseRate = 0; + } + break; + } + + if ((layer->instOrWave = get_instrument(seqChannel, cmd, &layer->instrument, &layer->adsr)) == 0) { + eu_stubbed_printf_1("WARNING: NPRG: cannot change %d\n", cmd); + layer->instOrWave = 0xff; + } +#endif + break; + + case 0xc7: // layer_portamento + layer->portamento.mode = m64_read_u8(state); + + // cmd is reused for the portamento's semitone + cmd = m64_read_u8(state) + seqChannel->transposition + + layer->transposition + seqPlayer->transposition; + + if (cmd >= 0x80) { + cmd = 0; + } + + layer->portamentoTargetNote = cmd; + + // If special, the next param is u8 instead of var + if (PORTAMENTO_IS_SPECIAL(layer->portamento)) { + layer->portamentoTime = *((state)->pc++); + break; + } + + sp3A = m64_read_compressed_u16(state); + layer->portamentoTime = sp3A; + break; + + case 0xc8: // layer_disableportamento + layer->portamento.mode = 0; + break; + +#if defined(VERSION_EU) || defined(VERSION_SH) + case 0xcb: + sp3A = m64_read_s16(state); + layer->adsr.envelope = (struct AdsrEnvelope *) (seqPlayer->seqData + sp3A); + layer->adsr.releaseRate = m64_read_u8(state); + break; + + case 0xcc: + layer->ignoreDrumPan = TRUE; + break; +#endif + + default: + switch (cmd & 0xf0) { + case 0xd0: // layer_setshortnotevelocityfromtable + sp3A = seqPlayer->shortNoteVelocityTable[cmd & 0xf]; + layer->velocitySquare = (f32)(sp3A * sp3A); + break; + case 0xe0: // layer_setshortnotedurationfromtable + layer->noteDuration = seqPlayer->shortNoteDurationTable[cmd & 0xf]; + break; + default: + eu_stubbed_printf_1("Audio:Track:NOTE:UNDEFINED NOTE COM. %x\n", cmd); + break; + } + } + } + + if (cmd == 0xc0) { // layer_delay + layer->delay = m64_read_compressed_u16(state); + layer->stopSomething = TRUE; + } else { + layer->stopSomething = FALSE; + + if (seqChannel->largeNotes == TRUE) { + switch (cmd & 0xc0) { + case 0x00: // layer_note0 (play percentage, velocity, duration) + sp3A = m64_read_compressed_u16(state); + vel = *(state->pc++); + layer->noteDuration = *(state->pc++); + layer->playPercentage = sp3A; + break; + + case 0x40: // layer_note1 (play percentage, velocity) + sp3A = m64_read_compressed_u16(state); + vel = *(state->pc++); + layer->noteDuration = 0; + layer->playPercentage = sp3A; + break; + + case 0x80: // layer_note2 (velocity, duration; uses last play percentage) + sp3A = layer->playPercentage; + vel = *(state->pc++); + layer->noteDuration = *(state->pc++); + break; + } + + // the remaining bits are used for the semitone + cmd -= (cmd & 0xc0); +#if defined(VERSION_EU) || defined(VERSION_SH) + layer->velocitySquare = (f32)(vel) * (f32)vel; +#else + layer->velocitySquare = vel * vel; +#endif + } else { + switch (cmd & 0xc0) { + case 0x00: // play note, type 0 (play percentage) + sp3A = m64_read_compressed_u16(state); + layer->playPercentage = sp3A; + break; + + case 0x40: // play note, type 1 (uses default play percentage) + sp3A = layer->shortNoteDefaultPlayPercentage; + break; + + case 0x80: // play note, type 2 (uses last play percentage) + sp3A = layer->playPercentage; + break; + } + + // the remaining bits are used for the semitone + cmd -= cmd & 0xc0; + } + + layer->delay = sp3A; +#if defined(VERSION_EU) || defined(VERSION_SH) + layer->duration = layer->noteDuration * sp3A >> 8; +#else + layer->duration = layer->noteDuration * sp3A / 256; +#endif + if ((seqPlayer->muted && (seqChannel->muteBehavior & MUTE_BEHAVIOR_STOP_NOTES) != 0) + || seqChannel->stopSomething2 +#if defined(VERSION_JP) || defined(VERSION_US) + || !seqChannel->hasInstrument +#endif + ) { + layer->stopSomething = TRUE; + } else { +#if defined(VERSION_EU) || defined(VERSION_SH) + s32 temp = layer->instOrWave; + if (temp == 0xff) temp = seqChannel->instOrWave; + if (temp == 0) +#else + if (seqChannel->instOrWave == 0) +#endif + { // drum + // cmd is reused for the drum semitone + cmd += seqChannel->transposition + layer->transposition; + +#if defined(VERSION_EU) + drum = get_drum(seqChannel->bankId, cmd); +#else + if (cmd >= gCtlEntries[seqChannel->bankId].numDrums) { + cmd = gCtlEntries[seqChannel->bankId].numDrums; + if (cmd == 0) { + // this goto looks a bit like a function return... + layer->stopSomething = TRUE; + goto skip; + } + + cmd--; + } + + drum = gCtlEntries[seqChannel->bankId].drums[cmd]; +#endif + if (drum == NULL) { + layer->stopSomething = TRUE; + } else { + layer->adsr.envelope = drum->envelope; + layer->adsr.releaseRate = drum->releaseRate; +#if defined(VERSION_EU) || defined(VERSION_SH) + if (!layer->ignoreDrumPan) { + layer->pan = drum->pan; + } +#else + layer->pan = FLOAT_CAST(drum->pan) / US_FLOAT(128.0); +#endif + layer->sound = &drum->sound; + layer->freqScale = layer->sound->tuning; + } +#if defined(VERSION_JP) || defined(VERSION_US) || defined(VERSION_SH) + skip:; +#endif + } else { // instrument + // cmd is reused for the instrument semitone + cmd += seqPlayer->transposition + seqChannel->transposition + layer->transposition; + + if (cmd >= 0x80) { + layer->stopSomething = TRUE; + } else { +#if defined(VERSION_EU) || defined(VERSION_SH) + if (layer->instOrWave == 0xffu) { + instrument = seqChannel->instrument; + } else { + instrument = layer->instrument; + } +#else + instrument = layer->instrument; + if (instrument == NULL) { + instrument = seqChannel->instrument; + } +#endif + + if (layer->portamento.mode != 0) { + if (layer->portamentoTargetNote < cmd) { + vel = cmd; + } else { + vel = layer->portamentoTargetNote; + } + + if (instrument != NULL) { +#if defined(VERSION_EU) + sound = instrument_get_audio_bank_sound(instrument, vel); +#else + sound = (u8) vel < instrument->normalRangeLo ? &instrument->lowNotesSound + : (u8) vel <= instrument->normalRangeHi ? + &instrument->normalNotesSound : &instrument->highNotesSound; +#endif + sameSound = (sound == layer->sound); + layer->sound = sound; + tuning = sound->tuning; + } else { + layer->sound = NULL; + tuning = 1.0f; + } + + temp_f2 = gNoteFrequencies[cmd] * tuning; + temp_f12 = gNoteFrequencies[layer->portamentoTargetNote] * tuning; + + portamento = &layer->portamento; + switch (PORTAMENTO_MODE(layer->portamento)) { + case PORTAMENTO_MODE_1: + case PORTAMENTO_MODE_3: + case PORTAMENTO_MODE_5: +#if defined(VERSION_JP) || defined(VERSION_US) + sp24 = temp_f2; +#endif + freqScale = temp_f12; + break; + + case PORTAMENTO_MODE_2: + case PORTAMENTO_MODE_4: +#if defined(VERSION_EU) || defined(VERSION_SH) + default: +#endif + freqScale = temp_f2; +#if defined(VERSION_JP) || defined(VERSION_US) + sp24 = temp_f12; +#endif + break; + } + +#if defined(VERSION_EU) || defined(VERSION_SH) + portamento->extent = temp_f2 / freqScale - 1.0f; +#else + portamento->extent = sp24 / freqScale - US_FLOAT(1.0); +#endif + + if (PORTAMENTO_IS_SPECIAL(layer->portamento)) { + portamento->speed = US_FLOAT(32512.0) * FLOAT_CAST(seqPlayer->tempo) + / ((f32) layer->delay * (f32) gTempoInternalToExternal + * FLOAT_CAST(layer->portamentoTime)); + } else { + portamento->speed = US_FLOAT(127.0) / FLOAT_CAST(layer->portamentoTime); + } + portamento->cur = 0.0f; + layer->freqScale = freqScale; + if (PORTAMENTO_MODE(layer->portamento) == PORTAMENTO_MODE_5) { + layer->portamentoTargetNote = cmd; + } + } else if (instrument != NULL) { +#if defined(VERSION_EU) + sound = instrument_get_audio_bank_sound(instrument, cmd); +#else + sound = cmd < instrument->normalRangeLo ? + &instrument->lowNotesSound : cmd <= instrument->normalRangeHi ? + &instrument->normalNotesSound : &instrument->highNotesSound; +#endif + sameSound = (sound == layer->sound); + layer->sound = sound; + layer->freqScale = gNoteFrequencies[cmd] * sound->tuning; + } else { + layer->sound = NULL; + layer->freqScale = gNoteFrequencies[cmd]; + } + } + } + layer->delayUnused = layer->delay; + } + } + + if (layer->stopSomething == TRUE) { + if (layer->note != NULL || layer->continuousNotes) { + seq_channel_layer_note_decay(layer); + } + return; + } + + cmd = FALSE; + if (!layer->continuousNotes) { + cmd = TRUE; + } else if (layer->note == NULL || layer->status == SOUND_LOAD_STATUS_NOT_LOADED) { + cmd = TRUE; + } else if (sameSound == FALSE) { + seq_channel_layer_note_decay(layer); + cmd = TRUE; + } +#if defined(VERSION_EU) || defined(VERSION_SH) + else if (layer != layer->note->parentLayer) { + cmd = TRUE; + } +#endif + else if (layer->sound == NULL) { + init_synthetic_wave(layer->note, layer); + } + + if (cmd != FALSE) { + layer->note = alloc_note(layer); + } + + if (layer->note != NULL && layer->note->parentLayer == layer) { + note_vibrato_init(layer->note); + } +#if defined(VERSION_EU) || defined(VERSION_SH) + if (seqChannel) { + } +#endif +} + +#ifdef VERSION_EU +u8 audioString106[] = "Audio: Note:Velocity Error %d\n"; +u8 audioString107[] = "Error: Your assignchannel is stolen.\n"; +#endif + +#else +// US/JP version with macros to simulate inlining by copt. Edit if you dare. +#include "copt/seq_channel_layer_process_script_copt.inc.c" +#endif + +#ifdef VERSION_SH +void seq_channel_layer_process_script_part1(struct SequenceChannelLayer *layer) { + if (!layer->continuousNotes) { + seq_channel_layer_note_decay(layer); + } else if (layer->note != NULL && layer->note->wantedParentLayer == layer) { + seq_channel_layer_note_decay(layer); + } + + if (PORTAMENTO_MODE(layer->portamento) == PORTAMENTO_MODE_1 || + PORTAMENTO_MODE(layer->portamento) == PORTAMENTO_MODE_2) { + layer->portamento.mode = 0; + } + + layer->notePropertiesNeedInit = TRUE; +} + +s32 seq_channel_layer_process_script_part5(struct SequenceChannelLayer *layer, s32 cmd) { + if (!layer->stopSomething && layer->sound != NULL && layer->sound->sample->codec == CODEC_SKIP && + layer->sound->sample->medium != 0) { + layer->stopSomething = TRUE; + return -1; + } + + if (layer->continuousNotes == 1 && layer->note != NULL && layer->status && cmd == 1 && + layer->note->parentLayer == layer) { + if (layer->sound == NULL) { + init_synthetic_wave(layer->note, layer); + } + } else { + if (cmd == 0) { + seq_channel_layer_note_decay(layer); + } + layer->note = alloc_note(layer); + } + if (layer->note != NULL && layer->note->parentLayer == layer) { + note_vibrato_init(layer->note); + } + return 0; +} + +s32 seq_channel_layer_process_script_part2(struct SequenceChannelLayer *layer) { + struct SequenceChannel *seqChannel = layer->seqChannel; + struct SequencePlayer *seqPlayer = seqChannel->seqPlayer; + struct M64ScriptState *state; + s32 temp_a0_5; + u16 sp3A; + u8 cmd; + + for (;;) { + state = &layer->scriptState; + cmd = m64_read_u8(state); + + if (cmd <= 0xc0) { + return cmd; + } + + switch (cmd) { + case 0xff: // layer_end; function return or end of script + if (state->depth == 0) { + // N.B. this function call is *not* inlined even though it's + // within the same file, unlike in the rest of this function. + seq_channel_layer_disable(layer); + return -1; + } + state->pc = state->stack[--state->depth]; + break; + + case 0xfc: // layer_call + if (0 && state->depth >= 4) { + eu_stubbed_printf_0("Macro Level Over Error!\n"); + } + sp3A = m64_read_s16(state); + state->stack[state->depth++] = state->pc; + state->pc = seqPlayer->seqData + sp3A; + break; + + case 0xf8: // layer_loop; loop start, N iterations (or 256 if N = 0) + if (0 && state->depth >= 4) { + eu_stubbed_printf_0("Macro Level Over Error!\n"); + } + state->remLoopIters[state->depth] = m64_read_u8(state); + state->stack[state->depth++] = state->pc; + break; + + case 0xf7: // layer_loopend + if (--state->remLoopIters[state->depth - 1] != 0) { + state->pc = state->stack[state->depth - 1]; + } else { + state->depth--; + } + break; + + case 0xfb: // layer_jump + sp3A = m64_read_s16(state); + state->pc = seqPlayer->seqData + sp3A; + break; + + case 0xf4: + state->pc += (s8)m64_read_u8(state); + break; + + case 0xc1: // layer_setshortnotevelocity + case 0xca: // layer_setpan + temp_a0_5 = *(state->pc++); + if (cmd == 0xc1) { + layer->velocitySquare = (f32) (temp_a0_5 * temp_a0_5) / (f32) (127 * 127); + } else { + layer->pan = temp_a0_5; + } + break; + + case 0xc2: // layer_transpose; set transposition in semitones + case 0xc9: // layer_setshortnoteduration + temp_a0_5 = *(state->pc++); + if (cmd == 0xc9) { + layer->noteDuration = temp_a0_5; + } else { + layer->transposition = temp_a0_5; + } + break; + + case 0xc4: // layer_somethingon + case 0xc5: // layer_somethingoff + if (cmd == 0xc4) { + layer->continuousNotes = TRUE; + } else { + layer->continuousNotes = FALSE; + } + seq_channel_layer_note_decay(layer); + break; + + case 0xc3: // layer_setshortnotedefaultplaypercentage + sp3A = m64_read_compressed_u16(state); + layer->shortNoteDefaultPlayPercentage = sp3A; + break; + + case 0xc6: // layer_setinstr + DEBUG_PRINT(" - cmd 0xc6, setinstr"); + + cmd = m64_read_u8(state); + DEBUG_PRINT(" - read %d from state", cmd); + + if (cmd >= 0x7f) { + if (cmd == 0x7f) { + layer->instOrWave = 0; + } else { + layer->instOrWave = cmd; + layer->instrument = NULL; + } + + if (1) { + } + + if (cmd == 0xff) { + layer->adsr.releaseRate = 0; + } + break; + } + + if ((layer->instOrWave = get_instrument(seqChannel, cmd, &layer->instrument, &layer->adsr)) == 0) { + eu_stubbed_printf_1("WARNING: NPRG: cannot change %d\n", cmd); + layer->instOrWave = 0xff; + } + break; + + case 0xc7: // layer_portamento + layer->portamento.mode = m64_read_u8(state); + + // cmd is reused for the portamento's semitone + cmd = m64_read_u8(state) + seqChannel->transposition + + layer->transposition + seqPlayer->transposition; + + if (cmd >= 0x80) { + cmd = 0; + } + + layer->portamentoTargetNote = cmd; + + // If special, the next param is u8 instead of var + if (PORTAMENTO_IS_SPECIAL(layer->portamento)) { + layer->portamentoTime = *((state)->pc++); + break; + } + + sp3A = m64_read_compressed_u16(state); + layer->portamentoTime = sp3A; + break; + + case 0xc8: // layer_disableportamento + layer->portamento.mode = 0; + break; + + case 0xcb: + sp3A = m64_read_s16(state); + layer->adsr.envelope = (struct AdsrEnvelope *) (seqPlayer->seqData + sp3A); + layer->adsr.releaseRate = m64_read_u8(state); + break; + + case 0xcc: + layer->ignoreDrumPan = TRUE; + break; + + case 0xcd: + layer->reverbBits.asByte = m64_read_u8(state); + break; + + case 0xce: + cmd = m64_read_u8(state) + 0x80; + layer->freqScaleMultiplier = unk_sh_data_1[cmd]; + // missing break :) + + default: + switch (cmd & 0xf0) { + case 0xd0: // layer_setshortnotevelocityfromtable + sp3A = seqPlayer->shortNoteVelocityTable[cmd & 0xf]; + layer->velocitySquare = (f32) (sp3A * sp3A) / (f32) (127 * 127); + break; + case 0xe0: // layer_setshortnotedurationfromtable + layer->noteDuration = seqPlayer->shortNoteDurationTable[cmd & 0xf]; + break; + default: + eu_stubbed_printf_1("Audio:Track:NOTE:UNDEFINED NOTE COM. %x\n", cmd); + break; + } + } + } + return cmd; +} + +s32 seq_channel_layer_process_script_part4(struct SequenceChannelLayer *layer, s32 cmd1) { + s32 sameSound = TRUE; + struct SequenceChannel *seqChannel = layer->seqChannel; + struct Portamento *portamento; + struct AudioBankSound *sound; + struct Instrument *instrument; + struct Drum *drum; + f32 tuning; + s32 vel; + f32 freqScale; + f32 sp24; + f32 temp_f12; + UNUSED s32 pad[2]; + struct SequencePlayer *seqPlayer = seqChannel->seqPlayer; + u8 cmd = cmd1; + f32 temp_f2; + + s32 temp = layer->instOrWave; + if (temp == 0xff) { + if (!seqChannel->hasInstrument) { + return -1; + } + temp = seqChannel->instOrWave; + } + if (temp == 0) { // drum + // cmd is reused for the drum semitone + cmd += seqChannel->transposition + layer->transposition; + + drum = get_drum(seqChannel->bankId, cmd); + if (drum == NULL) { + layer->stopSomething = TRUE; + layer->delayUnused = layer->delay; + return -1; + } else { + layer->adsr.envelope = drum->envelope; + layer->adsr.releaseRate = drum->releaseRate; + if (!layer->ignoreDrumPan) { + layer->pan = drum->pan; + } + layer->sound = &drum->sound; + layer->freqScale = layer->sound->tuning; + } + } else { // instrument + // cmd is reused for the instrument semitone + cmd += seqPlayer->transposition + seqChannel->transposition + layer->transposition; + + if (cmd >= 0x80) { + layer->stopSomething = TRUE; + return -1; + } else { + if (layer->instOrWave == 0xff) { + instrument = seqChannel->instrument; + } else { + instrument = layer->instrument; + } + + if (layer->portamento.mode != 0) { + if (layer->portamentoTargetNote < cmd) { + vel = cmd; + } else { + vel = layer->portamentoTargetNote; + } + + if (instrument != NULL) { + sound = instrument_get_audio_bank_sound(instrument, vel); + sameSound = (sound == layer->sound); + layer->sound = sound; + tuning = sound->tuning; + } else { + layer->sound = NULL; + tuning = 1.0f; + } + + temp_f2 = gNoteFrequencies[cmd] * tuning; + temp_f12 = gNoteFrequencies[layer->portamentoTargetNote] * tuning; + + portamento = &layer->portamento; + switch (PORTAMENTO_MODE(layer->portamento)) { + case PORTAMENTO_MODE_1: + case PORTAMENTO_MODE_3: + case PORTAMENTO_MODE_5: + sp24 = temp_f2; + freqScale = temp_f12; + break; + + case PORTAMENTO_MODE_2: + case PORTAMENTO_MODE_4: + freqScale = temp_f2; + sp24 = temp_f12; + break; + + default: + freqScale = temp_f2; + sp24 = temp_f2; + break; + } + + portamento->extent = sp24 / freqScale - 1.0f; + + if (PORTAMENTO_IS_SPECIAL(layer->portamento)) { + portamento->speed = US_FLOAT(32512.0) * FLOAT_CAST(seqPlayer->tempo) + / ((f32) layer->delay * (f32) gTempoInternalToExternal + * FLOAT_CAST(layer->portamentoTime)); + } else { + portamento->speed = US_FLOAT(127.0) / FLOAT_CAST(layer->portamentoTime); + } + portamento->cur = 0.0f; + layer->freqScale = freqScale; + if (PORTAMENTO_MODE(layer->portamento) == PORTAMENTO_MODE_5) { + layer->portamentoTargetNote = cmd; + } + } else if (instrument != NULL) { + sound = instrument_get_audio_bank_sound(instrument, cmd); + sameSound = (sound == layer->sound); + layer->sound = sound; + layer->freqScale = gNoteFrequencies[cmd] * sound->tuning; + } else { + layer->sound = NULL; + layer->freqScale = gNoteFrequencies[cmd]; + } + } + } + layer->delayUnused = layer->delay; + layer->freqScale *= layer->freqScaleMultiplier; + return sameSound; +} + +s32 seq_channel_layer_process_script_part3(struct SequenceChannelLayer *layer, s32 cmd) { + struct M64ScriptState *state = &layer->scriptState; + u16 sp3A; + s32 vel; + struct SequenceChannel *seqChannel = layer->seqChannel; + struct SequencePlayer *seqPlayer = seqChannel->seqPlayer; + + if (cmd == 0xc0) { // layer_delay + layer->delay = m64_read_compressed_u16(state); + layer->stopSomething = TRUE; + return -1; + } + + layer->stopSomething = FALSE; + + if (seqChannel->largeNotes == TRUE) { + switch (cmd & 0xc0) { + case 0x00: // layer_note0 (play percentage, velocity, duration) + sp3A = m64_read_compressed_u16(state); + vel = *(state->pc++); + layer->noteDuration = *(state->pc++); + layer->playPercentage = sp3A; + break; + + case 0x40: // layer_note1 (play percentage, velocity) + sp3A = m64_read_compressed_u16(state); + vel = *(state->pc++); + layer->noteDuration = 0; + layer->playPercentage = sp3A; + break; + + case 0x80: // layer_note2 (velocity, duration; uses last play percentage) + sp3A = layer->playPercentage; + vel = *(state->pc++); + layer->noteDuration = *(state->pc++); + break; + } + if (vel >= 0x80 || vel < 0) { + vel = 0x7f; + } + layer->velocitySquare = ((f32) vel * (f32) vel) / (f32) (0x7f * 0x7f); + // the remaining bits are used for the semitone + cmd -= (cmd & 0xc0); + } else { + switch (cmd & 0xc0) { + case 0x00: // play note, type 0 (play percentage) + sp3A = m64_read_compressed_u16(state); + layer->playPercentage = sp3A; + break; + + case 0x40: // play note, type 1 (uses default play percentage) + sp3A = layer->shortNoteDefaultPlayPercentage; + break; + + case 0x80: // play note, type 2 (uses last play percentage) + sp3A = layer->playPercentage; + break; + } + + // the remaining bits are used for the semitone + cmd -= cmd & 0xc0; + } + + layer->delay = sp3A; + layer->duration = layer->noteDuration * sp3A >> 8; + if ((seqPlayer->muted && (seqChannel->muteBehavior & 0x50) != 0) + || seqChannel->stopSomething2) + { + layer->stopSomething = TRUE; + return -1; + } + + return cmd; +} +#endif + +u8 get_instrument(struct SequenceChannel *seqChannel, u8 instId, struct Instrument **instOut, struct AdsrSettings *adsr) { + DEBUG_PRINT("@ get_instrument()"); + DEBUG_PRINT("- instrument id: %d", instId); + + struct Instrument *inst; +#if defined(VERSION_EU) || defined(VERSION_SH) + inst = get_instrument_inner(seqChannel->bankId, instId); + if (inst == NULL) + { + *instOut = NULL; + return 0; + } + adsr->envelope = inst->envelope; + adsr->releaseRate = inst->releaseRate; + *instOut = inst; + instId++; + return instId; +#else + UNUSED u32 pad; + + if (instId >= gCtlEntries[seqChannel->bankId].numInstruments) { + instId = gCtlEntries[seqChannel->bankId].numInstruments; + if (instId == 0) { + return 0; + } + instId--; + } + + inst = gCtlEntries[seqChannel->bankId].instruments[instId]; + if (inst == NULL) { + struct SequenceChannel seqChannelCpy = *seqChannel; + + while (instId != 0xff) { + inst = gCtlEntries[seqChannelCpy.bankId].instruments[instId]; + if (inst != NULL) { + break; + } + instId--; + } + } + + if (((uintptr_t) gBankLoadedPool.persistent.pool.start <= (uintptr_t) inst + && (uintptr_t) inst <= (uintptr_t)(gBankLoadedPool.persistent.pool.start + + gBankLoadedPool.persistent.pool.size)) + || ((uintptr_t) gBankLoadedPool.temporary.pool.start <= (uintptr_t) inst + && (uintptr_t) inst <= (uintptr_t)(gBankLoadedPool.temporary.pool.start + + gBankLoadedPool.temporary.pool.size))) { + adsr->envelope = inst->envelope; + adsr->releaseRate = inst->releaseRate; + *instOut = inst; + instId++; + return instId; + } + + gAudioErrorFlags = instId + 0x20000; + *instOut = NULL; + return 0; +#endif +} + +void set_instrument(struct SequenceChannel *seqChannel, u8 instId) { + DEBUG_PRINT("@ set_instrument()"); + DEBUG_PRINT("- bank id: %d", seqChannel->bankId); + DEBUG_PRINT("- instrument id: %d", instId); + + if (instId >= 0x80) { + seqChannel->instOrWave = instId; + seqChannel->instrument = NULL; + } else if (instId == 0x7f) { + seqChannel->instOrWave = 0; + seqChannel->instrument = (struct Instrument *) 1; + } else { +#if defined(VERSION_EU) || defined(VERSION_SH) + if ((seqChannel->instOrWave = + get_instrument(seqChannel, instId, &seqChannel->instrument, &seqChannel->adsr)) == 0) +#else + seqChannel->instOrWave = + get_instrument(seqChannel, instId, &seqChannel->instrument, &seqChannel->adsr); + if (seqChannel->instOrWave == 0) +#endif + { + seqChannel->hasInstrument = FALSE; + return; + } + } + seqChannel->hasInstrument = TRUE; +} + +void sequence_channel_set_volume(struct SequenceChannel *seqChannel, u8 volume) { + seqChannel->volume = FLOAT_CAST(volume) / US_FLOAT(127.0); +} + +void sequence_channel_process_script(struct SequenceChannel *seqChannel) { + DEBUG_PRINT("sequence_channel_process_script()"); + + struct M64ScriptState *state; + struct SequencePlayer *seqPlayer; + u8 cmd; + s8 temp; + u8 loBits; + u16 sp5A; + s32 sp38; + s8 value; + s32 i; + u8 *seqData; + + if (!seqChannel->enabled) { + return; + } + + if (seqChannel->stopScript) { + for (i = 0; i < LAYERS_MAX; i++) { + if (seqChannel->layers[i] != NULL) { + seq_channel_layer_process_script(seqChannel->layers[i]); + } + } + return; + } + + seqPlayer = seqChannel->seqPlayer; + if (seqPlayer->muted && (seqChannel->muteBehavior & MUTE_BEHAVIOR_STOP_SCRIPT) != 0) { + return; + } + + if (seqChannel->delay != 0) { + seqChannel->delay--; + } + + state = &seqChannel->scriptState; + if (seqChannel->delay == 0) { + for (;;) { + cmd = m64_read_u8(state); + DEBUG_PRINT("- handling command: %x", cmd); + +#if !defined(VERSION_EU) && !defined(VERSION_SH) + if (cmd == 0xff) // chan_end + { + if (state->depth == 0) { + sequence_channel_disable(seqChannel); + break; + } + state->depth--, state->pc = state->stack[state->depth]; + } + if (cmd == 0xfe) // chan_delay1 + { + break; + } + if (cmd == 0xfd) // chan_delay + { + seqChannel->delay = m64_read_compressed_u16(state); + break; + } + if (cmd == 0xf3) // chan_hang + { + seqChannel->stopScript = TRUE; + break; + } +#endif + +#ifdef VERSION_SH + if (cmd >= 0xb0) +#else + if (cmd > 0xc0) +#endif + { + switch (cmd) { + case 0xff: // chan_end +#if defined(VERSION_EU) || defined(VERSION_SH) + if (state->depth == 0) { + sequence_channel_disable(seqChannel); + goto out; + } else { + state->pc = state->stack[--state->depth]; + } +#endif + break; + +#if defined(VERSION_EU) || defined(VERSION_SH) + case 0xfe: // chan_delay1 + goto out; + + case 0xfd: // chan_delay + seqChannel->delay = m64_read_compressed_u16(state); + goto out; + + case 0xea: + seqChannel->stopScript = TRUE; + goto out; +#endif + case 0xfc: // chan_call + if (0 && state->depth >= 4) { + eu_stubbed_printf_0("Audio:Track :Call Macro Level Over Error!\n"); + } + sp5A = m64_read_s16(state); +#if defined(VERSION_EU) || defined(VERSION_SH) + state->stack[state->depth++] = state->pc; +#else + state->depth++, state->stack[state->depth - 1] = state->pc; +#endif + state->pc = seqPlayer->seqData + sp5A; + break; + + case 0xf8: // chan_loop; loop start, N iterations (or 256 if N = 0) + if (0 && state->depth >= 4) { + eu_stubbed_printf_0("Audio:Track :Loops Macro Level Over Error!\n"); + } + state->remLoopIters[state->depth] = m64_read_u8(state); +#if defined(VERSION_EU) || defined(VERSION_SH) + state->stack[state->depth++] = state->pc; +#else + state->depth++, state->stack[state->depth - 1] = state->pc; +#endif + break; + + case 0xf7: // chan_loopend + state->remLoopIters[state->depth - 1]--; + if (state->remLoopIters[state->depth - 1] != 0) { + state->pc = state->stack[state->depth - 1]; + } else { + state->depth--; + } + break; + + case 0xf6: // chan_break; break loop, if combined with jump + state->depth--; + break; + + case 0xfb: // chan_jump + case 0xfa: // chan_beqz + case 0xf9: // chan_bltz + case 0xf5: // chan_bgez + sp5A = m64_read_s16(state); + if (cmd == 0xfa && value != 0) + break; + if (cmd == 0xf9 && value >= 0) + break; + if (cmd == 0xf5 && value < 0) + break; + state->pc = seqPlayer->seqData + sp5A; + break; + +#if defined(VERSION_EU) || defined(VERSION_SH) + case 0xf4: // chan_jump_rel + case 0xf3: // chan_beqz_rel + case 0xf2: // chan_bltz_rel + temp = m64_read_u8(state); + if (cmd == 0xf3 && value != 0) + break; + if (cmd == 0xf2 && value >= 0) + break; + state->pc += temp; + break; +#endif + +#if defined(VERSION_EU) || defined(VERSION_SH) + case 0xf1: // chan_reservenotes +#else + case 0xf2: // chan_reservenotes +#endif + note_pool_clear(&seqChannel->notePool); + note_pool_fill(&seqChannel->notePool, m64_read_u8(state)); + break; + +#if defined(VERSION_EU) || defined(VERSION_SH) + case 0xf0: // chan_unreservenotes +#else + case 0xf1: // chan_unreservenotes +#endif + note_pool_clear(&seqChannel->notePool); + break; + + case 0xc2: // chan_setdyntable + sp5A = m64_read_s16(state); + seqChannel->dynTable = (void *) (seqPlayer->seqData + sp5A); + break; + + case 0xc5: // chan_dynsetdyntable + if (value != -1) { +#if defined(VERSION_EU) || defined(VERSION_SH) + seqData = (*seqChannel->dynTable)[value]; + sp38 = (u16)((seqData[0] << 8) + seqData[1]); + seqChannel->dynTable = (void *) (seqPlayer->seqData + sp38); +#else + sp5A = (u16)((((*seqChannel->dynTable)[value])[0] << 8) + (((*seqChannel->dynTable)[value])[1])); + seqChannel->dynTable = (void *) (seqPlayer->seqData + sp5A); +#endif + } + break; + +#if defined(VERSION_EU) || defined(VERSION_SH) + case 0xeb: // chan_setbankandinstr + cmd = m64_read_u8(state); + // Switch to the cmd's (0-indexed) bank in this sequence's + // bank set. Note that in the binary format (not in the JSON!) + // the banks are listed backwards, so we counts from the back. + // (gAlBankSets[offset] is number of banks) + sp38 = ((u16 *) gAlBankSets)[seqPlayer->seqId]; + loBits = *(sp38 + gAlBankSets); + cmd = gAlBankSets[(s32)sp38 + loBits - cmd]; + +#ifdef VERSION_SH + if (get_bank_or_seq(1, 2, cmd) != NULL) +#else + if (get_bank_or_seq(&gBankLoadedPool, 2, cmd) != NULL) +#endif + { + seqChannel->bankId = cmd; + } else { + eu_stubbed_printf_1("SUB:ERR:BANK %d NOT CACHED.\n", cmd); + } + // fallthrough +#endif + + case 0xc1: // chan_setinstr ("set program"?) + DEBUG_PRINT(" - cmd 0xc1, setinstr"); + + u8 instrId = m64_read_u8(state); + DEBUG_PRINT(" - read %d from state", instrId); + + set_instrument(seqChannel, instrId); + break; + + case 0xc3: // chan_largenotesoff + seqChannel->largeNotes = FALSE; + break; + + case 0xc4: // chan_largenoteson + seqChannel->largeNotes = TRUE; + break; + + case 0xdf: // chan_setvol + sequence_channel_set_volume(seqChannel, m64_read_u8(state)); +#if defined(VERSION_EU) || defined(VERSION_SH) + seqChannel->changes.as_bitfields.volume = TRUE; +#endif + break; + + case 0xe0: // chan_setvolscale + seqChannel->volumeScale = FLOAT_CAST(m64_read_u8(state)) / US_FLOAT(128.0); +#if defined(VERSION_EU) || defined(VERSION_SH) + seqChannel->changes.as_bitfields.volume = TRUE; +#endif + break; + + case 0xde: // chan_freqscale; pitch bend using raw frequency multiplier N/2^15 (N is u16) + sp5A = m64_read_s16(state); + seqChannel->freqScale = FLOAT_CAST(sp5A) / US_FLOAT(32768.0); +#if defined(VERSION_EU) || defined(VERSION_SH) + seqChannel->changes.as_bitfields.freqScale = TRUE; +#endif + break; + + case 0xd3: // chan_pitchbend; pitch bend by <= 1 octave in either direction (-127..127) + // (m64_read_u8(state) is really s8 here) +#ifdef VERSION_SH + cmd = m64_read_u8(state) + 128; +#else + cmd = m64_read_u8(state) + 127; +#endif + seqChannel->freqScale = gPitchBendFrequencyScale[cmd]; +#if defined(VERSION_EU) || defined(VERSION_SH) + seqChannel->changes.as_bitfields.freqScale = TRUE; +#endif + break; + +#ifdef VERSION_SH + case 0xee: + cmd = m64_read_u8(state) + 0x80; + seqChannel->freqScale = unk_sh_data_1[cmd]; + seqChannel->changes.as_bitfields.freqScale = TRUE; + break; +#endif + + case 0xdd: // chan_setpan +#if defined(VERSION_EU) || defined(VERSION_SH) + seqChannel->newPan = m64_read_u8(state); + seqChannel->changes.as_bitfields.pan = TRUE; +#else + seqChannel->pan = FLOAT_CAST(m64_read_u8(state)) / US_FLOAT(128.0); +#endif + break; + + case 0xdc: // chan_setpanmix; set proportion of pan to come from channel (0..128) +#if defined(VERSION_EU) || defined(VERSION_SH) + seqChannel->panChannelWeight = m64_read_u8(state); + seqChannel->changes.as_bitfields.pan = TRUE; +#else + seqChannel->panChannelWeight = FLOAT_CAST(m64_read_u8(state)) / US_FLOAT(128.0); +#endif + break; + + case 0xdb: // chan_transpose; set transposition in semitones + temp = *state->pc++; + seqChannel->transposition = temp; + break; + + case 0xda: // chan_setenvelope + sp5A = m64_read_s16(state); + seqChannel->adsr.envelope = (struct AdsrEnvelope *) (seqPlayer->seqData + sp5A); + break; + + case 0xd9: // chan_setdecayrelease + seqChannel->adsr.releaseRate = m64_read_u8(state); + break; + + case 0xd8: // chan_setvibratoextent + seqChannel->vibratoExtentTarget = m64_read_u8(state) * 8; + seqChannel->vibratoExtentStart = 0; + seqChannel->vibratoExtentChangeDelay = 0; + break; + + case 0xd7: // chan_setvibratorate + seqChannel->vibratoRateStart = seqChannel->vibratoRateTarget = + m64_read_u8(state) * 32; + seqChannel->vibratoRateChangeDelay = 0; + break; + + case 0xe2: // chan_setvibratoextentlinear + seqChannel->vibratoExtentStart = m64_read_u8(state) * 8; + seqChannel->vibratoExtentTarget = m64_read_u8(state) * 8; + seqChannel->vibratoExtentChangeDelay = m64_read_u8(state) * 16; + break; + + case 0xe1: // chan_setvibratoratelinear + seqChannel->vibratoRateStart = m64_read_u8(state) * 32; + seqChannel->vibratoRateTarget = m64_read_u8(state) * 32; + seqChannel->vibratoRateChangeDelay = m64_read_u8(state) * 16; + break; + + case 0xe3: // chan_setvibratodelay + seqChannel->vibratoDelay = m64_read_u8(state) * 16; + break; + +#if defined(VERSION_JP) || defined(VERSION_US) + case 0xd6: // chan_setupdatesperframe_unimplemented + cmd = m64_read_u8(state); + if (cmd == 0) { + cmd = gAudioUpdatesPerFrame; + } + seqChannel->updatesPerFrameUnused = cmd; + break; +#endif + + case 0xd4: // chan_setreverb + seqChannel->reverbVol = m64_read_u8(state); + break; + + case 0xc6: // chan_setbank; switch bank within set + DEBUG_PRINT(" - case 0xc6 - switch bank"); + + DEBUG_PRINT(" - seq id %d", seqPlayer->seqId); + + cmd = m64_read_u8(state); + DEBUG_PRINT(" - backwards bank id %d", cmd); + + // Switch to the temp's (0-indexed) bank in this sequence's + // bank set. Note that in the binary format (not in the JSON!) + // the banks are listed backwards, so we counts from the back. + // (gAlBankSets[offset] is number of banks) + sp5A = ((u16 *) gAlBankSets)[seqPlayer->seqId]; + loBits = *(sp5A + gAlBankSets); + cmd = gAlBankSets[sp5A + loBits - cmd]; + if (get_bank_or_seq(&gBankLoadedPool, 2, cmd) != NULL) + { + seqChannel->bankId = cmd; + } else { + eu_stubbed_printf_1("SUB:ERR:BANK %d NOT CACHED.\n", cmd); + } + break; + + case 0xc7: // chan_writeseq; write to sequence data (!) + { +#if !defined(VERSION_EU) && !defined(VERSION_SH) + u8 *seqData; +#endif + cmd = m64_read_u8(state); + sp5A = m64_read_s16(state); + seqData = seqPlayer->seqData + sp5A; + *seqData = (u8)value + cmd; + } + break; + + case 0xc8: // chan_subtract + case 0xc9: // chan_bitand + case 0xcc: // chan_setval + temp = m64_read_u8(state); + if (cmd == 0xc8) { + value -= temp; + } else if (cmd == 0xcc) { + value = temp; + } else { + value &= temp; + } + break; + +#ifdef VERSION_SH + case 0xcd: + sequence_channel_disable(seqPlayer->channels[m64_read_u8(state)]); + break; +#endif + + case 0xca: // chan_setmutebhv + seqChannel->muteBehavior = m64_read_u8(state); +#ifdef VERSION_SH + seqChannel->changes.as_bitfields.volume = TRUE; +#endif + break; + + case 0xcb: // chan_readseq + sp38 = (u16)m64_read_s16(state) + value; + value = seqPlayer->seqData[sp38]; + break; + +#ifdef VERSION_SH + case 0xce: + seqChannel->unkC8 = m64_read_s16(state); + break; + + case 0xcf: + sp5A = m64_read_s16(state); + seqData = seqPlayer->seqData + sp5A; + seqData[0] = (seqChannel->unkC8 >> 8) & 0xffff; + seqData[1] = (seqChannel->unkC8) & 0xffff; + break; +#endif + + case 0xd0: // chan_stereoheadseteffects + seqChannel->stereoHeadsetEffects = m64_read_u8(state); + break; + + case 0xd1: // chan_setnoteallocationpolicy + seqChannel->noteAllocPolicy = m64_read_u8(state); + break; + + case 0xd2: // chan_setsustain +#if defined(VERSION_EU) || defined(VERSION_SH) + seqChannel->adsr.sustain = m64_read_u8(state); +#else + seqChannel->adsr.sustain = m64_read_u8(state) << 8; +#endif + break; +#if defined(VERSION_EU) || defined(VERSION_SH) + case 0xe5: + seqChannel->reverbIndex = m64_read_u8(state); + break; +#endif + case 0xe4: // chan_dyncall + if (value != -1) { +#if defined(VERSION_EU) || defined(VERSION_SH) + if (state->depth >= 4) { + eu_stubbed_printf_0("Audio:Track: CTBLCALL Macro Level Over Error!\n"); + } +#endif + seqData = (*seqChannel->dynTable)[value]; +#if defined(VERSION_EU) || defined(VERSION_SH) + state->stack[state->depth++] = state->pc; + sp38 = (u16)((seqData[0] << 8) + seqData[1]); + state->pc = seqPlayer->seqData + sp38; + + if (0 && sp38 >= gSeqFileHeader->seqArray[seqPlayer->seqId].len) { + eu_stubbed_printf_3("Err :Sub %x ,address %x:Undefined SubTrack Function %x", seqChannel, state->pc, sp38); + } +#else + state->depth++, state->stack[state->depth - 1] = state->pc; + sp5A = ((seqData[0] << 8) + seqData[1]); + state->pc = seqPlayer->seqData + sp5A; +#endif + } + break; + +#if defined(VERSION_EU) || defined(VERSION_SH) + case 0xe6: + seqChannel->bookOffset = m64_read_u8(state); + break; + + case 0xe7: + sp5A = m64_read_s16(state); + seqData = seqPlayer->seqData + sp5A; + seqChannel->muteBehavior = *seqData++; + seqChannel->noteAllocPolicy = *seqData++; + seqChannel->notePriority = *seqData++; + seqChannel->transposition = (s8) *seqData++; + seqChannel->newPan = *seqData++; + seqChannel->panChannelWeight = *seqData++; + seqChannel->reverbVol = *seqData++; + seqChannel->reverbIndex = *seqData++; + seqChannel->changes.as_bitfields.pan = TRUE; + break; + + case 0xe8: + seqChannel->muteBehavior = m64_read_u8(state); + seqChannel->noteAllocPolicy = m64_read_u8(state); + seqChannel->notePriority = m64_read_u8(state); + seqChannel->transposition = (s8) m64_read_u8(state); + seqChannel->newPan = m64_read_u8(state); + seqChannel->panChannelWeight = m64_read_u8(state); + seqChannel->reverbVol = m64_read_u8(state); + seqChannel->reverbIndex = m64_read_u8(state); + seqChannel->changes.as_bitfields.pan = TRUE; + break; + + case 0xec: + seqChannel->vibratoExtentTarget = 0; + seqChannel->vibratoExtentStart = 0; + seqChannel->vibratoExtentChangeDelay = 0; + seqChannel->vibratoRateTarget = 0; + seqChannel->vibratoRateStart = 0; + seqChannel->vibratoRateChangeDelay = 0; + seqChannel->freqScale = 1.0f; + break; + + case 0xe9: // chan_setnotepriority +#ifdef VERSION_SH + cmd = m64_read_u8(state); + if ((cmd & 0xf) != 0) { + seqChannel->notePriority = cmd & 0xf; + } + cmd = cmd >> 4; + if (cmd != 0) { + seqChannel->unkSH06 = cmd; + } +#else + seqChannel->notePriority = m64_read_u8(state); +#endif + break; +#endif +#ifdef VERSION_SH + case 0xed: + seqChannel->synthesisVolume = m64_read_u8(state); + break; + + case 0xef: + m64_read_s16(state); + m64_read_u8(state); + break; + + case 0xb0: + sp5A = m64_read_s16(state); + seqData = seqPlayer->seqData + sp5A; + seqChannel->filter = (s16 *) (seqData); + break; + + case 0xb1: + seqChannel->filter = NULL; + break; + + case 0xb3: + if (seqChannel->filter != NULL) { + cmd = m64_read_u8(state); + if (cmd == 0) { + seqChannel->filter = NULL; + } else { + loBits = (cmd >> 4) & 0xf; + cmd &= 0xf; + fill_filter(seqChannel->filter, loBits, cmd); + } + } + break; + + case 0xb2: + i = (value * 2); + sp5A = m64_read_s16(state); + sp38 = sp5A + i; + seqChannel->unkC8 = *(u16 *) (seqPlayer->seqData + sp38); + break; + + case 0xb4: + seqChannel->dynTable = (void *) (seqPlayer->seqData + seqChannel->unkC8); + break; + + case 0xb5: + seqChannel->unkC8 = *(u16 *) (*seqChannel->dynTable)[value]; + break; + + case 0xb6: + value = (*seqChannel->dynTable)[0][value]; + break; +#endif + } + } else { +#ifdef VERSION_SH + if (cmd >= 0x80) { + loBits = cmd & 7; + switch (cmd & 0xf8) { + case 0x80: + if (seqChannel->layers[loBits] != NULL) { + value = seqChannel->layers[loBits]->finished; + } else { + value = -1; + } + break; + + case 0x88: + sp5A = m64_read_s16(state); + if (seq_channel_set_layer(seqChannel, loBits) == 0) { + if (1) {} + seqChannel->layers[loBits]->scriptState.pc = seqPlayer->seqData + sp5A; + } + break; + + case 0x90: + seq_channel_layer_free(seqChannel, loBits); + break; + + case 0x98: + if (value != -1 && seq_channel_set_layer(seqChannel, loBits) != -1) { + seqData = (*seqChannel->dynTable)[value]; + sp5A = ((seqData[0] << 8) + seqData[1]); + seqChannel->layers[loBits]->scriptState.pc = seqPlayer->seqData + sp5A; + } + break; + } + } else { +#endif + loBits = cmd & 0xf; + + switch (cmd & 0xf0) { +#ifdef VERSION_SH + case 0x00: + seqChannel->delay = loBits; + goto out; + + case 0x10: + seqChannel->soundScriptIO[loBits] = -1; + if (func_sh_802f47c8(seqChannel->bankId, (u8)value, &seqChannel->soundScriptIO[loBits]) == -1) { + } + break; +#else + case 0x00: // chan_testlayerfinished + if (seqChannel->layers[loBits] != NULL) { + value = seqChannel->layers[loBits]->finished; + } +#ifdef VERSION_EU + else { + value = -1; + } +#endif + break; +#endif + + // sh: 0x70 + case 0x70: // chan_iowriteval; write data back to audio lib + seqChannel->soundScriptIO[loBits] = value; + break; + +#ifdef VERSION_SH + case 0x60: +#else + case 0x80: // chan_ioreadval; read data from audio lib +#endif + DEBUG_PRINT("- cmd 0x80, read data from audio lib"); + DEBUG_PRINT(" - reading index: %d", loBits); + value = seqChannel->soundScriptIO[loBits]; + if (loBits < 4) { + seqChannel->soundScriptIO[loBits] = -1; + } + break; + + // sh: 0x50 + case 0x50: // chan_ioreadvalsub; subtract with read data from audio lib + value -= seqChannel->soundScriptIO[loBits]; + break; + +#ifndef VERSION_SH +#ifdef VERSION_EU + // sh: 0x00 + case 0x60: // chan_delayshort + seqChannel->delay = loBits; + goto out; +#endif + + case 0x90: // chan_setlayer + sp5A = m64_read_s16(state); + if (seq_channel_set_layer(seqChannel, loBits) == 0) { +#ifdef VERSION_EU + if (1) {} +#endif + seqChannel->layers[loBits]->scriptState.pc = seqPlayer->seqData + sp5A; + } + break; + + case 0xa0: // chan_freelayer + seq_channel_layer_free(seqChannel, loBits); + break; + + case 0xb0: // chan_dynsetlayer + if (value != -1 && seq_channel_set_layer(seqChannel, loBits) != -1) { + seqData = (*seqChannel->dynTable)[value]; + sp5A = ((seqData[0] << 8) + seqData[1]); + seqChannel->layers[loBits]->scriptState.pc = seqPlayer->seqData + sp5A; + } + break; + +#ifndef VERSION_EU + case 0x60: // chan_setnotepriority (arg must be >= 2) + seqChannel->notePriority = loBits; + break; +#endif +#endif + +#ifdef VERSION_SH + case 0x20: +#else + case 0x10: // chan_startchannel +#endif + sp5A = m64_read_s16(state); + sequence_channel_enable(seqPlayer, loBits, seqPlayer->seqData + sp5A); + break; + +#ifndef VERSION_SH + case 0x20: // chan_disablechannel + sequence_channel_disable(seqPlayer->channels[loBits]); + break; +#endif + + case 0x30: // chan_iowriteval2; write data back to audio lib for another channel + cmd = m64_read_u8(state); + seqPlayer->channels[loBits]->soundScriptIO[cmd] = value; + break; + + case 0x40: // chan_ioreadval2; read data from audio lib from another channel + cmd = m64_read_u8(state); + value = seqPlayer->channels[loBits]->soundScriptIO[cmd]; + break; + } +#ifdef VERSION_SH + } +#endif + } + } + } +#if defined(VERSION_EU) || defined(VERSION_SH) + out: +#endif + + for (i = 0; i < LAYERS_MAX; i++) { + if (seqChannel->layers[i] != 0) { + seq_channel_layer_process_script(seqChannel->layers[i]); + } + } +} + +void sequence_player_process_sequence(struct SequencePlayer *seqPlayer) { + DEBUG_PRINT("sequence_player_process_sequence()"); + + u8 cmd; +#ifdef VERSION_SH + UNUSED u32 pad; +#endif + u8 loBits; + u8 temp; + s32 value; + s32 i; + u16 u16v; + u8 *seqData; + struct M64ScriptState *state; +#if defined(VERSION_EU) || defined(VERSION_SH) + s32 temp32; +#endif + + if (seqPlayer->enabled == FALSE) { + return; + } + +#ifndef VERSION_SH + if (seqPlayer->bankDmaInProgress == TRUE) { +#ifdef VERSION_EU + if (osRecvMesg(&seqPlayer->bankDmaMesgQueue, NULL, 0) == -1) { + return; + } + if (seqPlayer->bankDmaRemaining == 0) { + seqPlayer->bankDmaInProgress = FALSE; + patch_audio_bank( + (struct AudioBank *) (gCtlEntries[seqPlayer->loadingBankId].instruments - 1), + gAlTbl->seqArray[seqPlayer->loadingBankId].offset, + gCtlEntries[seqPlayer->loadingBankId].numInstruments, + gCtlEntries[seqPlayer->loadingBankId].numDrums); + gCtlEntries[seqPlayer->loadingBankId].drums = + ((struct AudioBank *) (gCtlEntries[seqPlayer->loadingBankId].instruments - 1))->drums; + gBankLoadStatus[seqPlayer->loadingBankId] = SOUND_LOAD_STATUS_COMPLETE; + } else { + audio_dma_partial_copy_async(&seqPlayer->bankDmaCurrDevAddr, &seqPlayer->bankDmaCurrMemAddr, + &seqPlayer->bankDmaRemaining, &seqPlayer->bankDmaMesgQueue, + &seqPlayer->bankDmaIoMesg); + } +#else + if (seqPlayer->bankDmaMesg == NULL) { + return; + } + if (seqPlayer->bankDmaRemaining == 0) { + seqPlayer->bankDmaInProgress = FALSE; + patch_audio_bank(seqPlayer->loadingBank, gAlTbl->seqArray[seqPlayer->loadingBankId].offset, + seqPlayer->loadingBankNumInstruments, seqPlayer->loadingBankNumDrums); + gCtlEntries[seqPlayer->loadingBankId].numInstruments = seqPlayer->loadingBankNumInstruments; + gCtlEntries[seqPlayer->loadingBankId].numDrums = seqPlayer->loadingBankNumDrums; + gCtlEntries[seqPlayer->loadingBankId].instruments = seqPlayer->loadingBank->instruments; + gCtlEntries[seqPlayer->loadingBankId].drums = seqPlayer->loadingBank->drums; + gBankLoadStatus[seqPlayer->loadingBankId] = SOUND_LOAD_STATUS_COMPLETE; + } else { + osCreateMesgQueue(&seqPlayer->bankDmaMesgQueue, &seqPlayer->bankDmaMesg, 1); + seqPlayer->bankDmaMesg = NULL; + audio_dma_partial_copy_async(&seqPlayer->bankDmaCurrDevAddr, &seqPlayer->bankDmaCurrMemAddr, + &seqPlayer->bankDmaRemaining, &seqPlayer->bankDmaMesgQueue, + &seqPlayer->bankDmaIoMesg); + } +#endif + return; + } + + if (seqPlayer->seqDmaInProgress == TRUE) { +#ifdef VERSION_EU + if (osRecvMesg(&seqPlayer->seqDmaMesgQueue, NULL, 0) == -1) { + return; + } +#ifndef AVOID_UB + if (temp) { + } +#endif +#else + if (seqPlayer->seqDmaMesg == NULL) { + return; + } +#endif + seqPlayer->seqDmaInProgress = FALSE; + gSeqLoadStatus[seqPlayer->seqId] = SOUND_LOAD_STATUS_COMPLETE; + } +#endif + + // If discarded, bail out. + if (IS_SEQ_LOAD_COMPLETE(seqPlayer->seqId) == FALSE + || ( +#ifdef VERSION_SH + seqPlayer->defaultBank[0] != 0xff && +#endif + IS_BANK_LOAD_COMPLETE(seqPlayer->defaultBank[0]) == FALSE)) { + eu_stubbed_printf_1("Disappear Sequence or Bank %d\n", seqPlayer->seqId); + sequence_player_disable(seqPlayer); + return; + } + + // Remove possible SOUND_LOAD_STATUS_DISCARDABLE marks. +#ifdef VERSION_SH + if (gSeqLoadStatus[seqPlayer->seqId] != 5) +#endif + gSeqLoadStatus[seqPlayer->seqId] = SOUND_LOAD_STATUS_COMPLETE; + +#ifdef VERSION_SH + if (gBankLoadStatus[seqPlayer->defaultBank[0]] != 5) +#endif + gBankLoadStatus[seqPlayer->defaultBank[0]] = SOUND_LOAD_STATUS_COMPLETE; + + if (seqPlayer->muted && (seqPlayer->muteBehavior & MUTE_BEHAVIOR_STOP_SCRIPT) != 0) { + return; + } + + // Check if we surpass the number of ticks needed for a tatum, else stop. + seqPlayer->tempoAcc += seqPlayer->tempo; +#ifdef VERSION_SH + seqPlayer->tempoAcc += seqPlayer->tempoAdd; +#endif + if (seqPlayer->tempoAcc < gTempoInternalToExternal) { + return; + } + seqPlayer->tempoAcc -= (u16) gTempoInternalToExternal; + + state = &seqPlayer->scriptState; + if (seqPlayer->delay > 1) { +#ifndef AVOID_UB + if (temp) { + } +#endif + seqPlayer->delay--; + } else { +#if defined(VERSION_EU) || defined(VERSION_SH) + seqPlayer->recalculateVolume = 1; +#endif + for (;;) { + cmd = m64_read_u8(state); + if (cmd == 0xff) // seq_end + { + if (state->depth == 0) { + sequence_player_disable(seqPlayer); + break; + } +#if defined(VERSION_EU) || defined(VERSION_SH) + state->pc = state->stack[--state->depth]; +#else + state->depth--, state->pc = state->stack[state->depth]; +#endif + } + + if (cmd == 0xfd) // seq_delay + { + seqPlayer->delay = m64_read_compressed_u16(state); + break; + } + + if (cmd == 0xfe) // seq_delay1 + { + seqPlayer->delay = 1; + break; + } + + if (cmd >= 0xc0) { + switch (cmd) { + case 0xff: // seq_end + break; + + case 0xfc: // seq_call + u16v = m64_read_s16(state); + if (0 && state->depth >= 4) { + eu_stubbed_printf_0("Macro Level Over Error!\n"); + } +#if defined(VERSION_EU) || defined(VERSION_SH) + state->stack[state->depth++] = state->pc; +#else + state->depth++, state->stack[state->depth - 1] = state->pc; +#endif + state->pc = seqPlayer->seqData + u16v; + break; + + case 0xf8: // seq_loop; loop start, N iterations (or 256 if N = 0) + if (0 && state->depth >= 4) { + eu_stubbed_printf_0("Macro Level Over Error!\n"); + } + state->remLoopIters[state->depth] = m64_read_u8(state); +#if defined(VERSION_EU) || defined(VERSION_SH) + state->stack[state->depth++] = state->pc; +#else + state->depth++, state->stack[state->depth - 1] = state->pc; +#endif + break; + + case 0xf7: // seq_loopend + state->remLoopIters[state->depth - 1]--; + if (state->remLoopIters[state->depth - 1] != 0) { + state->pc = state->stack[state->depth - 1]; + } else { + state->depth--; + } + break; + + case 0xfb: // seq_jump + case 0xfa: // seq_beqz; jump if == 0 + case 0xf9: // seq_bltz; jump if < 0 + case 0xf5: // seq_bgez; jump if >= 0 + u16v = m64_read_s16(state); + if (cmd == 0xfa && value != 0) { + break; + } + if (cmd == 0xf9 && value >= 0) { + break; + } + if (cmd == 0xf5 && value < 0) { + break; + } + state->pc = seqPlayer->seqData + u16v; + break; + +#if defined(VERSION_EU) || defined(VERSION_SH) + case 0xf4: + case 0xf3: + case 0xf2: + temp = m64_read_u8(state); + if (cmd == 0xf3 && value != 0) { + break; + } + if (cmd == 0xf2 && value >= 0) { + break; + } + state->pc += (s8) temp; + break; +#endif + +#if defined(VERSION_EU) || defined(VERSION_SH) + case 0xf1: // seq_reservenotes +#else + case 0xf2: // seq_reservenotes +#endif + note_pool_clear(&seqPlayer->notePool); + note_pool_fill(&seqPlayer->notePool, m64_read_u8(state)); + break; + +#if defined(VERSION_EU) || defined(VERSION_SH) + case 0xf0: // seq_unreservenotes +#else + case 0xf1: // seq_unreservenotes +#endif + note_pool_clear(&seqPlayer->notePool); + break; + + case 0xdf: // seq_transpose; set transposition in semitones + seqPlayer->transposition = 0; + // fallthrough + + case 0xde: // seq_transposerel; add transposition + seqPlayer->transposition += (s8) m64_read_u8(state); + break; + + case 0xdd: // seq_settempo (bpm) +#ifndef VERSION_SH + case 0xdc: // seq_addtempo (bpm) +#endif +#ifdef VERSION_SH + seqPlayer->tempo = m64_read_u8(state) * TEMPO_SCALE; +#else + temp = m64_read_u8(state); + if (cmd == 0xdd) { + seqPlayer->tempo = temp * TEMPO_SCALE; + } else { + seqPlayer->tempo += (s8) temp * TEMPO_SCALE; + } +#endif + + if (seqPlayer->tempo > gTempoInternalToExternal) { + seqPlayer->tempo = gTempoInternalToExternal; + } + + //if (cmd){} + + if ((s16) seqPlayer->tempo <= 0) { + seqPlayer->tempo = 1; + } + break; + +#ifdef VERSION_SH + case 0xdc: // seq_addtempo (bpm) + seqPlayer->tempoAdd = (s8) m64_read_u8(state) * TEMPO_SCALE; + break; +#endif + +#if defined(VERSION_EU) || defined(VERSION_SH) + case 0xda: + cmd = m64_read_u8(state); + u16v = m64_read_s16(state); + switch (cmd) { + case SEQUENCE_PLAYER_STATE_0: + case SEQUENCE_PLAYER_STATE_FADE_OUT: + if (seqPlayer->state != SEQUENCE_PLAYER_STATE_2) { + seqPlayer->fadeTimerUnkEu = u16v; + seqPlayer->state = cmd; + } + break; + case SEQUENCE_PLAYER_STATE_2: + seqPlayer->fadeRemainingFrames = u16v; + seqPlayer->state = cmd; + seqPlayer->fadeVelocity = + (0.0f - seqPlayer->fadeVolume) / (s32)(u16v & 0xFFFFu); + break; + } + break; + + case 0xdb: + temp32 = m64_read_u8(state); + switch (seqPlayer->state) { + case SEQUENCE_PLAYER_STATE_2: + break; + case SEQUENCE_PLAYER_STATE_FADE_OUT: + seqPlayer->state = SEQUENCE_PLAYER_STATE_0; + seqPlayer->fadeVolume = 0.0f; + // fallthrough + case SEQUENCE_PLAYER_STATE_0: + seqPlayer->fadeRemainingFrames = seqPlayer->fadeTimerUnkEu; + if (seqPlayer->fadeTimerUnkEu != 0) { + seqPlayer->fadeVelocity = (temp32 / 127.0f - seqPlayer->fadeVolume) / FLOAT_CAST(seqPlayer->fadeRemainingFrames); + } else { + seqPlayer->fadeVolume = temp32 / 127.0f; + } + } + break; +#else + case 0xdb: // seq_setvol + cmd = m64_read_u8(state); + switch (seqPlayer->state) { + case SEQUENCE_PLAYER_STATE_2: + if (seqPlayer->fadeRemainingFrames != 0) { + f32 targetVolume = FLOAT_CAST(cmd) / US_FLOAT(127.0); + seqPlayer->fadeVelocity = (targetVolume - seqPlayer->fadeVolume) + / FLOAT_CAST(seqPlayer->fadeRemainingFrames); + break; + } + // fallthrough + case SEQUENCE_PLAYER_STATE_0: + seqPlayer->fadeVolume = FLOAT_CAST(cmd) / US_FLOAT(127.0); + break; + case SEQUENCE_PLAYER_STATE_FADE_OUT: + case SEQUENCE_PLAYER_STATE_4: + seqPlayer->volume = FLOAT_CAST(cmd) / US_FLOAT(127.0); + break; + } + break; + + case 0xda: // seq_changevol + temp = m64_read_u8(state); + seqPlayer->fadeVolume = + seqPlayer->fadeVolume + (f32)(s8) temp / US_FLOAT(127.0); + break; +#endif + +#if defined(VERSION_EU) || defined(VERSION_SH) + case 0xd9: + temp = m64_read_u8(state); + seqPlayer->fadeVolumeScale = (s8) temp / 127.0f; + break; +#endif + + case 0xd7: // seq_initchannels + u16v = m64_read_s16(state); + sequence_player_init_channels(seqPlayer, u16v); + break; + + case 0xd6: // seq_disablechannels + u16v = m64_read_s16(state); + sequence_player_disable_channels(seqPlayer, u16v); + break; + + case 0xd5: // seq_setmutescale + temp = m64_read_u8(state); + seqPlayer->muteVolumeScale = (f32)(s8) temp / US_FLOAT(127.0); + break; + + case 0xd4: // seq_mute + seqPlayer->muted = TRUE; + break; + + case 0xd3: // seq_setmutebhv + seqPlayer->muteBehavior = m64_read_u8(state); + break; + + case 0xd2: // seq_setshortnotevelocitytable + case 0xd1: // seq_setshortnotedurationtable + u16v = m64_read_s16(state); + seqData = seqPlayer->seqData + u16v; + if (cmd == 0xd2) { + seqPlayer->shortNoteVelocityTable = seqData; + } else { + seqPlayer->shortNoteDurationTable = seqData; + } + break; + + case 0xd0: // seq_setnoteallocationpolicy + seqPlayer->noteAllocPolicy = m64_read_u8(state); + break; + + case 0xcc: // seq_setval + value = m64_read_u8(state); + break; + + case 0xc9: // seq_bitand +#if defined(VERSION_EU) || defined(VERSION_SH) + value &= m64_read_u8(state); +#else + value = m64_read_u8(state) & value; +#endif + break; + + case 0xc8: // seq_subtract + value = value - m64_read_u8(state); + break; + +#ifdef VERSION_SH + case 0xc7: + cmd = m64_read_u8(state); + u16v = m64_read_s16(state); + seqData = seqPlayer->seqData + u16v; + *seqData = (u8)value + cmd; + break; + + case 0xc6: + seqPlayer->unkSh = TRUE; + return; +#endif + + default: + eu_stubbed_printf_1("Group:Undefine upper C0h command (%x)\n", cmd); + break; + } + } else { + loBits = cmd & 0xf; + switch (cmd & 0xf0) { + case 0x00: // seq_testchdisabled +#if defined(VERSION_EU) || defined(VERSION_SH) + value = seqPlayer->channels[loBits]->finished; +#else + if (IS_SEQUENCE_CHANNEL_VALID(seqPlayer->channels[loBits]) == TRUE) { + value = seqPlayer->channels[loBits]->finished; + } +#endif + break; + case 0x10: + break; + case 0x20: + break; + case 0x40: + break; + case 0x50: // seq_subvariation +#if defined(VERSION_EU) || defined(VERSION_SH) + value -= seqPlayer->seqVariationEu[0]; +#else + value -= seqPlayer->seqVariation; +#endif + break; + case 0x60: + break; + case 0x70: // seq_setvariation +#if defined(VERSION_EU) || defined(VERSION_SH) + seqPlayer->seqVariationEu[0] = value; +#else + seqPlayer->seqVariation = value; +#endif + break; + case 0x80: // seq_getvariation +#if defined(VERSION_EU) || defined(VERSION_SH) + value = seqPlayer->seqVariationEu[0]; +#else + value = seqPlayer->seqVariation; +#endif + break; + case 0x90: // seq_startchannel + u16v = m64_read_s16(state); + sequence_channel_enable(seqPlayer, loBits, seqPlayer->seqData + u16v); + break; + case 0xa0: + break; +#if !defined(VERSION_EU) && !defined(VERSION_SH) + case 0xd8: // (this makes no sense) + break; + case 0xd9: + break; +#endif + + default: + eu_stubbed_printf_0("Group:Undefined Command\n"); + break; + } + } + } + } + + for (i = 0; i < CHANNELS_MAX; i++) { +#if defined(VERSION_EU) || defined(VERSION_SH) + if (IS_SEQUENCE_CHANNEL_VALID(seqPlayer->channels[i]) == TRUE) { + sequence_channel_process_script(seqPlayer->channels[i]); + } +#else + if (seqPlayer->channels[i] != &gSequenceChannelNone) { + sequence_channel_process_script(seqPlayer->channels[i]); + } +#endif + } +} + +// This runs 240 times per second. +void process_sequences(UNUSED s32 iterationsRemaining) { + s32 i; + for (i = 0; i < SEQUENCE_PLAYERS; i++) { + if (gSequencePlayers[i].enabled == TRUE) { +#if defined(VERSION_EU) || defined(VERSION_SH) + sequence_player_process_sequence(&gSequencePlayers[i]); + sequence_player_process_sound(&gSequencePlayers[i]); +#else + sequence_player_process_sequence(gSequencePlayers + i); + sequence_player_process_sound(gSequencePlayers + i); +#endif + } + } +#if defined(VERSION_JP) || defined(VERSION_US) + reclaim_notes(); +#endif + process_notes(); +} + +void init_sequence_player(u32 player) { + struct SequencePlayer *seqPlayer = &gSequencePlayers[player]; +#if defined(VERSION_EU) || defined(VERSION_SH) + sequence_player_disable(seqPlayer); +#endif +#ifdef VERSION_SH + seqPlayer->unkSh = FALSE; +#else + seqPlayer->muted = FALSE; +#endif + seqPlayer->delay = 0; +#if defined(VERSION_EU) || defined(VERSION_SH) + seqPlayer->state = 1; +#else + seqPlayer->state = SEQUENCE_PLAYER_STATE_0; +#endif + seqPlayer->fadeRemainingFrames = 0; +#if defined(VERSION_EU) || defined(VERSION_SH) + seqPlayer->fadeTimerUnkEu = 0; +#endif + seqPlayer->tempoAcc = 0; + seqPlayer->tempo = 120 * TEMPO_SCALE; // 120 BPM +#ifdef VERSION_SH + seqPlayer->tempoAdd = 0; +#endif + seqPlayer->transposition = 0; +#ifndef VERSION_SH + seqPlayer->muteBehavior = MUTE_BEHAVIOR_STOP_SCRIPT | MUTE_BEHAVIOR_STOP_NOTES | MUTE_BEHAVIOR_SOFTEN; +#endif + seqPlayer->noteAllocPolicy = 0; + seqPlayer->shortNoteVelocityTable = gDefaultShortNoteVelocityTable; + seqPlayer->shortNoteDurationTable = gDefaultShortNoteDurationTable; + seqPlayer->fadeVolume = 1.0f; +#if defined(VERSION_EU) || defined(VERSION_SH) + seqPlayer->fadeVolumeScale = 1.0f; +#endif + seqPlayer->fadeVelocity = 0.0f; + seqPlayer->volume = 0.0f; + seqPlayer->muteVolumeScale = 0.5f; +} + +void init_sequence_players(void) { + // Initialization function, called from audio_init + s32 i, j; + + for (i = 0; i < ARRAY_COUNT(gSequenceChannels); i++) { + gSequenceChannels[i].seqPlayer = NULL; + gSequenceChannels[i].enabled = FALSE; +#if defined(VERSION_JP) || defined(VERSION_US) + } + + for (i = 0; i < ARRAY_COUNT(gSequenceChannels); i++) { +#endif + // @bug Size of wrong array. Zeroes out second half of gSequenceChannels[0], + // all of gSequenceChannels[1..31], and part of gSequenceLayers[0]. + // However, this is only called at startup, so it's harmless. +#ifdef AVOID_UB +#define LAYERS_SIZE LAYERS_MAX +#else +#define LAYERS_SIZE ARRAY_COUNT(gSequenceLayers) +#endif + for (j = 0; j < LAYERS_SIZE; j++) { + gSequenceChannels[i].layers[j] = NULL; + } + } + + init_layer_freelist(); + + for (i = 0; i < ARRAY_COUNT(gSequenceLayers); i++) { + gSequenceLayers[i].seqChannel = NULL; + gSequenceLayers[i].enabled = FALSE; + } + + for (i = 0; i < SEQUENCE_PLAYERS; i++) { + for (j = 0; j < CHANNELS_MAX; j++) { + gSequencePlayers[i].channels[j] = &gSequenceChannelNone; + } + +#if defined(VERSION_EU) || defined(VERSION_SH) + gSequencePlayers[i].seqVariationEu[0] = -1; +#else + gSequencePlayers[i].seqVariation = -1; +#endif +#ifdef VERSION_SH + gSequencePlayers[i].muteBehavior = MUTE_BEHAVIOR_STOP_SCRIPT | MUTE_BEHAVIOR_STOP_NOTES | MUTE_BEHAVIOR_SOFTEN; + gSequencePlayers[i].enabled = FALSE; + gSequencePlayers[i].muted = FALSE; +#endif + gSequencePlayers[i].bankDmaInProgress = FALSE; + gSequencePlayers[i].seqDmaInProgress = FALSE; + init_note_lists(&gSequencePlayers[i].notePool); + init_sequence_player(i); + } +} + diff --git a/src/decomp/audio/seqplayer.h b/src/decomp/audio/seqplayer.h new file mode 100644 index 0000000..d2fc134 --- /dev/null +++ b/src/decomp/audio/seqplayer.h @@ -0,0 +1,18 @@ +#ifndef AUDIO_SEQPLAYER_H +#define AUDIO_SEQPLAYER_H + +#include + +#include "internal.h" +#include "playback.h" + +void seq_channel_layer_disable(struct SequenceChannelLayer *seqPlayer); +void sequence_channel_disable(struct SequenceChannel *seqPlayer); +void sequence_player_disable(struct SequencePlayer* seqPlayer); +void audio_list_push_back(struct AudioListItem *list, struct AudioListItem *item); +void *audio_list_pop_back(struct AudioListItem *list); +void process_sequences(s32 iterationsRemaining); +void init_sequence_player(u32 player); +void init_sequence_players(void); + +#endif // AUDIO_SEQPLAYER_H diff --git a/src/decomp/audio/shindou_debug_prints.c b/src/decomp/audio/shindou_debug_prints.c new file mode 100644 index 0000000..261d452 --- /dev/null +++ b/src/decomp/audio/shindou_debug_prints.c @@ -0,0 +1,148 @@ +#include + +#ifdef VERSION_SH +// synthesis.c +char shindouDebugPrint1[] = "Terminate-Canceled Channel %d,Phase %d\n"; +char shindouDebugPrint2[] = "S->W\n"; +char shindouDebugPrint3[] = "W->S\n"; +char shindouDebugPrint4[] = "S-Resample Pitch %x (old %d -> delay %d)\n"; +s32 shindouDebugPrintPadding1[] = {0,0,0}; + +// heap.c +char shindouDebugPrint5[] = "Warning:Kill Note %x \n"; +char shindouDebugPrint6[] = "Kill Voice %d (ID %d) %d\n"; +char shindouDebugPrint7[] = "Warning: Running Sequence's data disappear!\n"; +char shindouDebugPrint8[] = "%x %x %x\n"; +char shindouDebugPrint9[] = "Audio:Memory:Heap OverFlow : Not Allocate %d!\n"; +char shindouDebugPrint10[] = "%x %x %x\n"; // Again +char shindouDebugPrint11[] = "Audio:Memory:Heap OverFlow : Not Allocate %d!\n"; // Again +char shindouDebugPrint12[] = "Audio:Memory:DataHeap Not Allocate \n"; +char shindouDebugPrint13[] = "StayHeap Not Allocate %d\n"; +char shindouDebugPrint14[] = "AutoHeap Not Allocate %d\n"; +char shindouDebugPrint15[] = "Status ID0 : %d ID1 : %d\n"; +char shindouDebugPrint16[] = "id 0 is Stopping\n"; +char shindouDebugPrint17[] = "id 0 is Stop\n"; +char shindouDebugPrint18[] = "id 1 is Stopping\n"; +char shindouDebugPrint19[] = "id 1 is Stop\n"; +char shindouDebugPrint20[] = "WARNING: NO FREE AUTOSEQ AREA.\n"; +char shindouDebugPrint21[] = "WARNING: NO STOP AUTO AREA.\n"; +char shindouDebugPrint22[] = " AND TRY FORCE TO STOP SIDE \n"; +char shindouDebugPrint23[] = "Check ID0 (seq ID %d) Useing ...\n"; +char shindouDebugPrint24[] = "Check ID1 (seq ID %d) Useing ...\n"; +char shindouDebugPrint25[] = "No Free Seq area.\n"; +char shindouDebugPrint26[] = "CH %d: ID %d\n"; +char shindouDebugPrint27[] = "TWO SIDES ARE LOADING... ALLOC CANCELED.\n"; +char shindouDebugPrint28[] = "WARNING: Before Area Overlaid After."; +char shindouDebugPrint29[] = "WARNING: After Area Overlaid Before."; +char shindouDebugPrint30[] = "MEMORY:SzHeapAlloc ERROR: sza->side %d\n"; +char shindouDebugPrint31[] = "Audio:MEMORY:SzHeap Overflow error. (%d bytes)\n"; +char shindouDebugPrint32[] = "Auto Heap Unhit for ID %d\n"; +char shindouDebugPrint33[] = "Heap Reconstruct Start %x\n"; +char shindouDebugPrint34[] = "---------------------------------------TEMPO %d %f\n"; +char shindouDebugPrint35[] = "%f \n"; +char shindouDebugPrint36[] = "%f \n"; // Again +char shindouDebugPrint37[] = "AHPBASE %x\n"; +char shindouDebugPrint38[] = "AHPCUR %x\n"; +char shindouDebugPrint39[] = "HeapTop %x\n"; +char shindouDebugPrint40[] = "SynoutRate %d / %d \n"; +char shindouDebugPrint41[] = "FXSIZE %d\n"; +char shindouDebugPrint42[] = "FXCOMP %d\n"; +char shindouDebugPrint43[] = "FXDOWN %d\n"; +char shindouDebugPrint44[] = "WaveCacheLen: %d\n"; +char shindouDebugPrint45[] = "SpecChange Finished\n"; +char shindouDebugPrint46[] = "Warning:Emem Over,not alloc %d\n"; +char shindouDebugPrint47[] = "Single AutoSize %d\n"; +char shindouDebugPrint48[] = "Single Ptr %x\n"; +char shindouDebugPrint49[] = "Request--------Single-Auto, %d\n"; +char shindouDebugPrint50[] = "Retry %x, %x, len %x\n"; +char shindouDebugPrint51[] = "DMAing list %d is killed.\n"; +char shindouDebugPrint52[] = "Try Kill %d \n"; +char shindouDebugPrint53[] = "Try Kill %x %x\n"; +char shindouDebugPrint54[] = "Try Kill %x %x %x\n"; +char shindouDebugPrint55[] = "Rom back %x %x \n"; +char shindouDebugPrint56[] = "Error sw NULL \n"; +char shindouDebugPrint57[] = "Request--------Single-Stay, %d\n"; +char shindouDebugPrint58[] = "Try Kill %d \n"; +char shindouDebugPrint59[] = "Try Kill %x %x\n"; +char shindouDebugPrint60[] = "Try Kill %x %x %x\n"; +s32 shindouDebugPrintPadding[] = {0, 0, 0}; + +// load.c +char shindouDebugPrint61[] = "CAUTION:WAVE CACHE FULL %d"; +char shindouDebugPrint62[] = "SUPERDMA"; +char shindouDebugPrint63[] = "Bank Change... top %d lba %d\n"; +char shindouDebugPrint64[] = "BankCount %d\n"; +char shindouDebugPrint65[] = "BANK LOAD MISS! FOR %d\n"; +char shindouDebugPrint66[] = "BankCount %d\n"; +char shindouDebugPrint67[] = "Flush Start\n"; +char shindouDebugPrint68[] = "%d ->%d\n"; +char shindouDebugPrint69[] = "useflag %d\n"; +char shindouDebugPrint70[] = "BankCount %d\n"; +char shindouDebugPrint71[] = "%2x "; +char shindouDebugPrint72[] = "StartSeq (Group %d,Seq %d) Process finish\n"; +char shindouDebugPrint73[] = "LoadCtrl, Ptr %x and Media is %d\n"; +char shindouDebugPrint74[] = "Load Bank, Type %d , ID %d\n"; +char shindouDebugPrint75[] = "get auto\n"; +char shindouDebugPrint76[] = "get s-auto %x\n"; +char shindouDebugPrint77[] = "Seq %d Write ID OK %d!\n"; +char shindouDebugPrint78[] = "Banknumber %d\n"; +char shindouDebugPrint79[] = "Bank Offset %x %d %d\n"; +char shindouDebugPrint80[] = "PEP Touch %x \n"; +char shindouDebugPrint81[] = "FastCopy"; +char shindouDebugPrint82[] = "FastCopy"; +char shindouDebugPrint83[] = "Error: Cannot DMA Media [%d]\n"; +char shindouDebugPrint84[] = "Warning: size not align 16 %x (%s)\n"; +char shindouDebugPrint85[] = "Load Bank BG, Type %d , ID %d\n"; +char shindouDebugPrint86[] = "get auto\n"; +char shindouDebugPrint87[] = "get s-auto %x\n"; +char shindouDebugPrint88[] = "Clear Workarea %x -%x size %x \n"; +char shindouDebugPrint89[] = "AudioHeap is %x\n"; +char shindouDebugPrint90[] = "Heap reset.Synth Change %x \n"; +char shindouDebugPrint91[] = "Heap %x %x %x\n"; +char shindouDebugPrint92[] = "Main Heap Initialize.\n"; +char shindouDebugPrint93[] = "%d :WaveA %d WaveB %d Inst %d,Perc %d\n"; +char shindouDebugPrint94[] = "---------- Init Completed. ------------\n"; +char shindouDebugPrint95[] = " Syndrv :[%6d]\n"; +char shindouDebugPrint96[] = " Seqdrv :[%6d]\n"; +char shindouDebugPrint97[] = " audiodata :[%6d]\n"; +char shindouDebugPrint98[] = "---------------------------------------\n"; +char shindouDebugPrint99[] = "Entry--- %d %d\n"; +char shindouDebugPrint100[] = "---Block LPS here\n"; +char shindouDebugPrint101[] = "===Block LPS end\n"; +char shindouDebugPrint102[] = "SLOWCOPY"; +char shindouDebugPrint103[] = "Req: Src %x Dest %x Len %x,media %d,retcode %d\n"; +char shindouDebugPrint104[] = "Remain Size %d\n"; +char shindouDebugPrint105[] = "---Block BG here\n"; +char shindouDebugPrint106[] = "===Block BG end\n"; +char shindouDebugPrint107[] = "Retcode %x\n"; +char shindouDebugPrint108[] = "Other Type: Not Write ID.\n"; +char shindouDebugPrint109[] = "BGLOAD:Error: dma length 0\n"; +char shindouDebugPrint110[] = "BGCOPY"; +char shindouDebugPrint111[] = "Error: Already wavetable is touched %x.\n"; +char shindouDebugPrint112[] = "Touch Warning: Length zero %x\n"; +char shindouDebugPrint113[] = "It's busy now!!!!! %d\n"; // This one's my favorite +char shindouDebugPrint114[] = "BG LOAD BUFFER is OVER.\n"; +char shindouDebugPrint115[] = "Warning: Length zero %x\n"; +char shindouDebugPrint116[] = "Wave Load %d \n"; +char shindouDebugPrint117[] = "Total Bg Wave Load %d \n"; +char shindouDebugPrint118[] = "Receive %d\n"; +char shindouDebugPrint119[] = "============Error: Magic is Broken after loading.\n"; +char shindouDebugPrint120[] = "Remain DMA: %d\n"; +char shindouDebugPrint121[] = "N start %d\n"; +char shindouDebugPrint122[] = "============Error: Magic is Broken: %x\n"; +char shindouDebugPrint123[] = "Error: No Handle.\n"; +char shindouDebugPrint124[] = "Success: %x\n"; + +// port_eu.c +char shindouDebugPrint125[] = "DAC:Lost 1 Frame.\n"; +char shindouDebugPrint126[] = "DMA: Request queue over.( %d )\n"; +char shindouDebugPrint127[] = "Spec Change Override. %d -> %d\n"; +char shindouDebugPrint128[] = "Audio:now-max tasklen is %d / %d\n"; +char shindouDebugPrint129[] = "Audio:Warning:ABI Tasklist length over (%d)\n"; +s32 D_SH_80314FC8 = 0x80; +struct SPTask *D_SH_80314FCC = NULL; +char shindouDebugPrint130[] = "BGLOAD Start %d\n"; +char shindouDebugPrint131[] = "Error: OverFlow Your Request\n"; +char shindouDebugPrint132[] = "---AudioSending (%d->%d) \n"; +// These continue in unk_shindou_audio_file.c +#endif diff --git a/src/decomp/audio/synthesis.c b/src/decomp/audio/synthesis.c new file mode 100644 index 0000000..d673171 --- /dev/null +++ b/src/decomp/audio/synthesis.c @@ -0,0 +1,1407 @@ +#ifndef VERSION_SH +#include + +#include "../../debug_print.h" +#include "synthesis.h" +#include "heap.h" +#include "data.h" +#include "load.h" +#include "seqplayer.h" +#include "internal.h" +#include "external.h" + +#ifndef TARGET_N64 +#include "../pc/mixer.h" +#endif + +#define DMEM_ADDR_TEMP 0x0 +#define DMEM_ADDR_RESAMPLED 0x20 +#define DMEM_ADDR_RESAMPLED2 0x160 +#define DMEM_ADDR_UNCOMPRESSED_NOTE 0x180 +#define DMEM_ADDR_NOTE_PAN_TEMP 0x200 +#define DMEM_ADDR_STEREO_STRONG_TEMP_DRY 0x200 +#define DMEM_ADDR_STEREO_STRONG_TEMP_WET 0x340 +#define DMEM_ADDR_COMPRESSED_ADPCM_DATA 0x3f0 +#define DMEM_ADDR_LEFT_CH 0x4c0 +#define DMEM_ADDR_RIGHT_CH 0x600 +#define DMEM_ADDR_WET_LEFT_CH 0x740 +#define DMEM_ADDR_WET_RIGHT_CH 0x880 + +#define aSetLoadBufferPair(pkt, c, off) \ + DEBUG_PRINT("- (in set load buffer pair, set buffer 1) "); \ + aSetBuffer(pkt, 0, c + DMEM_ADDR_WET_LEFT_CH, 0, DEFAULT_LEN_1CH - c); \ + DEBUG_PRINT("- (in set load buffer pair, load buffer 1) "); \ + aLoadBuffer(pkt, VIRTUAL_TO_PHYSICAL2(gSynthesisReverb.ringBuffer.left + (off))); \ + DEBUG_PRINT("- (in set load buffer pair, set buffer 2) "); \ + aSetBuffer(pkt, 0, c + DMEM_ADDR_WET_RIGHT_CH, 0, DEFAULT_LEN_1CH - c); \ + DEBUG_PRINT("- (in set load buffer pair, load buffer 2) "); \ + aLoadBuffer(pkt, VIRTUAL_TO_PHYSICAL2(gSynthesisReverb.ringBuffer.right + (off))) + +#define aSetSaveBufferPair(pkt, c, d, off) \ + aSetBuffer(pkt, 0, 0, c + DMEM_ADDR_WET_LEFT_CH, d); \ + aSaveBuffer(pkt, VIRTUAL_TO_PHYSICAL2(gSynthesisReverb.ringBuffer.left + (off))); \ + aSetBuffer(pkt, 0, 0, c + DMEM_ADDR_WET_RIGHT_CH, d); \ + aSaveBuffer(pkt, VIRTUAL_TO_PHYSICAL2(gSynthesisReverb.ringBuffer.right + (off))); + +#define ALIGN(val, amnt) (((val) + (1 << amnt) - 1) & ~((1 << amnt) - 1)) + +struct VolumeChange { + u16 sourceLeft; + u16 sourceRight; + u16 targetLeft; + u16 targetRight; +}; + +u64 *synthesis_do_one_audio_update(s16 *aiBuf, s32 bufLen, u64 *cmd, s32 updateIndex); +#ifdef VERSION_EU +u64 *synthesis_process_note(struct Note *note, struct NoteSubEu *noteSubEu, struct NoteSynthesisState *synthesisState, s16 *aiBuf, s32 bufLen, u64 *cmd); +u64 *load_wave_samples(u64 *cmd, struct NoteSubEu *noteSubEu, struct NoteSynthesisState *synthesisState, s32 nSamplesToLoad); +u64 *final_resample(u64 *cmd, struct NoteSynthesisState *synthesisState, s32 count, u16 pitch, u16 dmemIn, u32 flags); +u64 *process_envelope(u64 *cmd, struct NoteSubEu *noteSubEu, struct NoteSynthesisState *synthesisState, s32 nSamples, u16 inBuf, s32 headsetPanSettings, u32 flags); +u64 *note_apply_headset_pan_effects(u64 *cmd, struct NoteSubEu *noteSubEu, struct NoteSynthesisState *note, s32 bufLen, s32 flags, s32 leftRight); +#else +u64 *synthesis_process_notes(s16 *aiBuf, s32 bufLen, u64 *cmd); +u64 *load_wave_samples(u64 *cmd, struct Note *note, s32 nSamplesToLoad); +u64 *final_resample(u64 *cmd, struct Note *note, s32 count, u16 pitch, u16 dmemIn, u32 flags); +u64 *process_envelope(u64 *cmd, struct Note *note, s32 nSamples, u16 inBuf, s32 headsetPanSettings, + u32 flags); +u64 *process_envelope_inner(u64 *cmd, struct Note *note, s32 nSamples, u16 inBuf, + s32 headsetPanSettings, struct VolumeChange *vol); +u64 *note_apply_headset_pan_effects(u64 *cmd, struct Note *note, s32 bufLen, s32 flags, s32 leftRight); +#endif + +#ifdef VERSION_EU +struct SynthesisReverb gSynthesisReverbs[4]; +u8 sAudioSynthesisPad[0x10]; +#else +struct SynthesisReverb gSynthesisReverb; +u8 sAudioSynthesisPad[0x20]; +#endif + +#ifdef VERSION_EU +s16 gVolume; +s8 gUseReverb; +s8 gNumSynthesisReverbs; +struct NoteSubEu *gNoteSubsEu; +#endif + +#ifdef VERSION_EU +f32 gLeftVolRampings[3][1024]; +f32 gRightVolRampings[3][1024]; +f32 *gCurrentLeftVolRamping; // Points to any of the three left buffers above +f32 *gCurrentRightVolRamping; // Points to any of the three right buffers above + +u8 audioString1[] = "pitch %x: delaybytes %d : olddelay %d\n"; +u8 audioString2[] = "cont %x: delaybytes %d : olddelay %d\n"; +#endif + +#ifdef VERSION_EU +// Equivalent functionality as the US/JP version, +// just that the reverb structure is chosen from an array with index +void prepare_reverb_ring_buffer(s32 chunkLen, u32 updateIndex, s32 reverbIndex) { + struct ReverbRingBufferItem *item; + struct SynthesisReverb *reverb = &gSynthesisReverbs[reverbIndex]; + s32 srcPos; + s32 dstPos; + s32 nSamples; + s32 excessiveSamples; + s32 UNUSED pad[3]; + if (reverb->downsampleRate != 1) { + if (reverb->framesLeftToIgnore == 0) { + // Now that the RSP has finished, downsample the samples produced two frames ago by skipping + // samples. + item = &reverb->items[reverb->curFrame][updateIndex]; + + // Touches both left and right since they are adjacent in memory + osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH); + + for (srcPos = 0, dstPos = 0; dstPos < item->lengthA / 2; + srcPos += reverb->downsampleRate, dstPos++) { + reverb->ringBuffer.left[item->startPos + dstPos] = + item->toDownsampleLeft[srcPos]; + reverb->ringBuffer.right[item->startPos + dstPos] = + item->toDownsampleRight[srcPos]; + } + for (dstPos = 0; dstPos < item->lengthB / 2; srcPos += reverb->downsampleRate, dstPos++) { + reverb->ringBuffer.left[dstPos] = item->toDownsampleLeft[srcPos]; + reverb->ringBuffer.right[dstPos] = item->toDownsampleRight[srcPos]; + } + } + } + + item = &reverb->items[reverb->curFrame][updateIndex]; + nSamples = chunkLen / reverb->downsampleRate; + excessiveSamples = (nSamples + reverb->nextRingBufferPos) - reverb->bufSizePerChannel; + if (excessiveSamples < 0) { + // There is space in the ring buffer before it wraps around + item->lengthA = nSamples * 2; + item->lengthB = 0; + item->startPos = (s32) reverb->nextRingBufferPos; + reverb->nextRingBufferPos += nSamples; + } else { + // Ring buffer wrapped around + item->lengthA = (nSamples - excessiveSamples) * 2; + item->lengthB = excessiveSamples * 2; + item->startPos = reverb->nextRingBufferPos; + reverb->nextRingBufferPos = excessiveSamples; + } + // These fields are never read later + item->numSamplesAfterDownsampling = nSamples; + item->chunkLen = chunkLen; +} +#else +void prepare_reverb_ring_buffer(s32 chunkLen, u32 updateIndex) { + struct ReverbRingBufferItem *item; + s32 srcPos; + s32 dstPos; + s32 nSamples; + s32 numSamplesAfterDownsampling; + s32 excessiveSamples; + if (gReverbDownsampleRate != 1) { + if (gSynthesisReverb.framesLeftToIgnore == 0) { + // Now that the RSP has finished, downsample the samples produced two frames ago by skipping + // samples. + item = &gSynthesisReverb.items[gSynthesisReverb.curFrame][updateIndex]; + + // Touches both left and right since they are adjacent in memory + osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH); + + for (srcPos = 0, dstPos = 0; dstPos < item->lengthA / 2; + srcPos += gReverbDownsampleRate, dstPos++) { + gSynthesisReverb.ringBuffer.left[dstPos + item->startPos] = + item->toDownsampleLeft[srcPos]; + gSynthesisReverb.ringBuffer.right[dstPos + item->startPos] = + item->toDownsampleRight[srcPos]; + } + for (dstPos = 0; dstPos < item->lengthB / 2; srcPos += gReverbDownsampleRate, dstPos++) { + gSynthesisReverb.ringBuffer.left[dstPos] = item->toDownsampleLeft[srcPos]; + gSynthesisReverb.ringBuffer.right[dstPos] = item->toDownsampleRight[srcPos]; + } + } + } + item = &gSynthesisReverb.items[gSynthesisReverb.curFrame][updateIndex]; + + numSamplesAfterDownsampling = chunkLen / gReverbDownsampleRate; + if (((numSamplesAfterDownsampling + gSynthesisReverb.nextRingBufferPos) - gSynthesisReverb.bufSizePerChannel) < 0) { + // There is space in the ring buffer before it wraps around + item->lengthA = numSamplesAfterDownsampling * 2; + item->lengthB = 0; + item->startPos = (s32) gSynthesisReverb.nextRingBufferPos; + gSynthesisReverb.nextRingBufferPos += numSamplesAfterDownsampling; + } else { + // Ring buffer wrapped around + excessiveSamples = + (numSamplesAfterDownsampling + gSynthesisReverb.nextRingBufferPos) - gSynthesisReverb.bufSizePerChannel; + nSamples = numSamplesAfterDownsampling - excessiveSamples; + item->lengthA = nSamples * 2; + item->lengthB = excessiveSamples * 2; + item->startPos = gSynthesisReverb.nextRingBufferPos; + gSynthesisReverb.nextRingBufferPos = excessiveSamples; + } + // These fields are never read later + item->numSamplesAfterDownsampling = numSamplesAfterDownsampling; + item->chunkLen = chunkLen; +} +#endif + +#ifdef VERSION_EU +u64 *synthesis_load_reverb_ring_buffer(u64 *cmd, u16 addr, u16 srcOffset, s32 len, s32 reverbIndex) { + aSetBuffer(cmd++, 0, addr, 0, len); + aLoadBuffer(cmd++, VIRTUAL_TO_PHYSICAL2(&gSynthesisReverbs[reverbIndex].ringBuffer.left[srcOffset])); + + aSetBuffer(cmd++, 0, addr + DEFAULT_LEN_1CH, 0, len); + aLoadBuffer(cmd++, VIRTUAL_TO_PHYSICAL2(&gSynthesisReverbs[reverbIndex].ringBuffer.right[srcOffset])); + + return cmd; +} +#endif + +#ifdef VERSION_EU +u64 *synthesis_save_reverb_ring_buffer(u64 *cmd, u16 addr, u16 destOffset, s32 len, s32 reverbIndex) { + aSetBuffer(cmd++, 0, 0, addr, len); + aSaveBuffer(cmd++, VIRTUAL_TO_PHYSICAL2(&gSynthesisReverbs[reverbIndex].ringBuffer.left[destOffset])); + + aSetBuffer(cmd++, 0, 0, addr + DEFAULT_LEN_1CH, len); + aSaveBuffer(cmd++, VIRTUAL_TO_PHYSICAL2(&gSynthesisReverbs[reverbIndex].ringBuffer.right[destOffset])); + + return cmd; +} +#endif + +#ifdef VERSION_EU +void synthesis_load_note_subs_eu(s32 updateIndex) { + struct NoteSubEu *src; + struct NoteSubEu *dest; + s32 i; + + for (i = 0; i < gMaxSimultaneousNotes; i++) { + src = &gNotes[i].noteSubEu; + dest = &gNoteSubsEu[gMaxSimultaneousNotes * updateIndex + i]; + if (src->enabled) { + *dest = *src; + src->needsInit = FALSE; + } else { + dest->enabled = FALSE; + } + } +} +#endif + +#ifndef VERSION_EU +s32 get_volume_ramping(u16 sourceVol, u16 targetVol, s32 arg2) { + // This roughly computes 2^16 * (targetVol / sourceVol) ^ (8 / arg2), + // but with discretizations of targetVol, sourceVol and arg2. + f32 ret; + switch (arg2) { + default: + ret = gVolRampingLhs136[targetVol >> 8] * gVolRampingRhs136[sourceVol >> 8]; + break; + case 128: + ret = gVolRampingLhs128[targetVol >> 8] * gVolRampingRhs128[sourceVol >> 8]; + break; + case 136: + ret = gVolRampingLhs136[targetVol >> 8] * gVolRampingRhs136[sourceVol >> 8]; + break; + case 144: + ret = gVolRampingLhs144[targetVol >> 8] * gVolRampingRhs144[sourceVol >> 8]; + break; + } + return ret; +} +#endif + +#ifdef VERSION_EU +// TODO: (Scrub C) pointless mask and whitespace +u64 *synthesis_execute(u64 *cmdBuf, s32 *writtenCmds, s16 *aiBuf, s32 bufLen) { + s32 i, j; + f32 *leftVolRamp; + f32 *rightVolRamp; + u32 *aiBufPtr; + u64 *cmd = cmdBuf; + s32 chunkLen; + s32 nextVolRampTable; + + for (i = gAudioBufferParameters.updatesPerFrame; i > 0; i--) { + process_sequences(i - 1); + synthesis_load_note_subs_eu(gAudioBufferParameters.updatesPerFrame - i); + } + aSegment(cmd++, 0, 0); + aiBufPtr = (u32 *) aiBuf; + for (i = gAudioBufferParameters.updatesPerFrame; i > 0; i--) { + if (i == 1) { +#pragma GCC diagnostic push +#if defined(__clang__) +#pragma GCC diagnostic ignored "-Wself-assign" +#endif + // self-assignment has no affect when added here, could possibly simplify a macro definition + chunkLen = bufLen; nextVolRampTable = nextVolRampTable; leftVolRamp = gLeftVolRampings[nextVolRampTable]; rightVolRamp = gRightVolRampings[nextVolRampTable & 0xFFFFFFFF]; +#pragma GCC diagnostic pop + } else { + if (bufLen / i >= gAudioBufferParameters.samplesPerUpdateMax) { + chunkLen = gAudioBufferParameters.samplesPerUpdateMax; nextVolRampTable = 2; leftVolRamp = gLeftVolRampings[2]; rightVolRamp = gRightVolRampings[2]; + } else if (bufLen / i <= gAudioBufferParameters.samplesPerUpdateMin) { + chunkLen = gAudioBufferParameters.samplesPerUpdateMin; nextVolRampTable = 0; leftVolRamp = gLeftVolRampings[0]; rightVolRamp = gRightVolRampings[0]; + } else { + chunkLen = gAudioBufferParameters.samplesPerUpdate; nextVolRampTable = 1; leftVolRamp = gLeftVolRampings[1]; rightVolRamp = gRightVolRampings[1]; + } + } + gCurrentLeftVolRamping = leftVolRamp; + gCurrentRightVolRamping = rightVolRamp; + for (j = 0; j < gNumSynthesisReverbs; j++) { + if (gSynthesisReverbs[j].useReverb != 0) { + prepare_reverb_ring_buffer(chunkLen, gAudioBufferParameters.updatesPerFrame - i, j); + } + } + cmd = synthesis_do_one_audio_update((s16 *) aiBufPtr, chunkLen, cmd, gAudioBufferParameters.updatesPerFrame - i); + bufLen -= chunkLen; + aiBufPtr += chunkLen; + } + + for (j = 0; j < gNumSynthesisReverbs; j++) { + if (gSynthesisReverbs[j].framesLeftToIgnore != 0) { + gSynthesisReverbs[j].framesLeftToIgnore--; + } + gSynthesisReverbs[j].curFrame ^= 1; + } + *writtenCmds = cmd - cmdBuf; + return cmd; +} +#else +// bufLen will be divisible by 16 +u64 *synthesis_execute(u64 *cmdBuf, s32 *writtenCmds, s16 *aiBuf, s32 bufLen) { + DEBUG_PRINT("synthesis_execute()"); + s32 chunkLen; + s32 i; + u32 *aiBufPtr = (u32 *) aiBuf; + u64 *cmd = cmdBuf + 1; + s32 v0; + + aSegment(cmdBuf, 0, 0); + + for (i = gAudioUpdatesPerFrame; i > 0; i--) { + if (i == 1) { + // 'bufLen' will automatically be divisible by 8, no need to round + chunkLen = bufLen; + } else { + v0 = bufLen / i; + // chunkLen = v0 rounded to nearest multiple of 8 + chunkLen = v0 - (v0 & 7); + + if ((v0 & 7) >= 4) { + chunkLen += 8; + } + } + process_sequences(i - 1); + if (gSynthesisReverb.useReverb != 0) { + prepare_reverb_ring_buffer(chunkLen, gAudioUpdatesPerFrame - i); + } + cmd = synthesis_do_one_audio_update((s16 *) aiBufPtr, chunkLen, cmd, gAudioUpdatesPerFrame - i); + bufLen -= chunkLen; + aiBufPtr += chunkLen; + } + if (gSynthesisReverb.framesLeftToIgnore != 0) { + gSynthesisReverb.framesLeftToIgnore--; + } + gSynthesisReverb.curFrame ^= 1; + *writtenCmds = cmd - cmdBuf; + return cmd; +} +#endif + + +#ifdef VERSION_EU +u64 *synthesis_resample_and_mix_reverb(u64 *cmd, s32 bufLen, s16 reverbIndex, s16 updateIndex) { + struct ReverbRingBufferItem *item; + s16 startPad; + s16 paddedLengthA; + + item = &gSynthesisReverbs[reverbIndex].items[gSynthesisReverbs[reverbIndex].curFrame][updateIndex]; + + aClearBuffer(cmd++, DMEM_ADDR_WET_LEFT_CH, DEFAULT_LEN_2CH); + if (gSynthesisReverbs[reverbIndex].downsampleRate == 1) { + cmd = synthesis_load_reverb_ring_buffer(cmd, DMEM_ADDR_WET_LEFT_CH, item->startPos, item->lengthA, reverbIndex); + if (item->lengthB != 0) { + cmd = synthesis_load_reverb_ring_buffer(cmd, DMEM_ADDR_WET_LEFT_CH + item->lengthA, 0, item->lengthB, reverbIndex); + } + aSetBuffer(cmd++, 0, 0, 0, DEFAULT_LEN_2CH); + aMix(cmd++, 0, 0x7fff, DMEM_ADDR_WET_LEFT_CH, DMEM_ADDR_LEFT_CH); + aMix(cmd++, 0, 0x8000 + gSynthesisReverbs[reverbIndex].reverbGain, DMEM_ADDR_WET_LEFT_CH, DMEM_ADDR_WET_LEFT_CH); + } else { + startPad = (item->startPos % 8u) * 2; + paddedLengthA = ALIGN(startPad + item->lengthA, 4); + + cmd = synthesis_load_reverb_ring_buffer(cmd, DMEM_ADDR_RESAMPLED, (item->startPos - startPad / 2), DEFAULT_LEN_1CH, reverbIndex); + if (item->lengthB != 0) { + cmd = synthesis_load_reverb_ring_buffer(cmd, DMEM_ADDR_RESAMPLED + paddedLengthA, 0, DEFAULT_LEN_1CH - paddedLengthA, reverbIndex); + } + + aSetBuffer(cmd++, 0, DMEM_ADDR_RESAMPLED + startPad, DMEM_ADDR_WET_LEFT_CH, bufLen * 2); + aResample(cmd++, gSynthesisReverbs[reverbIndex].resampleFlags, gSynthesisReverbs[reverbIndex].resampleRate, VIRTUAL_TO_PHYSICAL2(gSynthesisReverbs[reverbIndex].resampleStateLeft)); + + aSetBuffer(cmd++, 0, DMEM_ADDR_RESAMPLED2 + startPad, DMEM_ADDR_WET_RIGHT_CH, bufLen * 2); + aResample(cmd++, gSynthesisReverbs[reverbIndex].resampleFlags, gSynthesisReverbs[reverbIndex].resampleRate, VIRTUAL_TO_PHYSICAL2(gSynthesisReverbs[reverbIndex].resampleStateRight)); + + aSetBuffer(cmd++, 0, 0, 0, DEFAULT_LEN_2CH); + aMix(cmd++, 0, 0x7fff, DMEM_ADDR_WET_LEFT_CH, DMEM_ADDR_LEFT_CH); + aMix(cmd++, 0, 0x8000 + gSynthesisReverbs[reverbIndex].reverbGain, DMEM_ADDR_WET_LEFT_CH, DMEM_ADDR_WET_LEFT_CH); + } + return cmd; +} + +u64 *synthesis_save_reverb_samples(u64 *cmd, s16 reverbIndex, s16 updateIndex) { + struct ReverbRingBufferItem *item; + + item = &gSynthesisReverbs[reverbIndex].items[gSynthesisReverbs[reverbIndex].curFrame][updateIndex]; + if (gSynthesisReverbs[reverbIndex].useReverb != 0) { + switch (gSynthesisReverbs[reverbIndex].downsampleRate) { + case 1: + // Put the oldest samples in the ring buffer into the wet channels + cmd = synthesis_save_reverb_ring_buffer(cmd, DMEM_ADDR_WET_LEFT_CH, item->startPos, item->lengthA, reverbIndex); + if (item->lengthB != 0) { + // Ring buffer wrapped + cmd = synthesis_save_reverb_ring_buffer(cmd, DMEM_ADDR_WET_LEFT_CH + item->lengthA, 0, item->lengthB, reverbIndex); + } + break; + + default: + // Downsampling is done later by CPU when RSP is done, therefore we need to have double + // buffering. Left and right buffers are adjacent in memory. + aSetBuffer(cmd++, 0, 0, DMEM_ADDR_WET_LEFT_CH, DEFAULT_LEN_2CH); + aSaveBuffer(cmd++, VIRTUAL_TO_PHYSICAL2(gSynthesisReverbs[reverbIndex].items[gSynthesisReverbs[reverbIndex].curFrame][updateIndex].toDownsampleLeft)); + gSynthesisReverbs[reverbIndex].resampleFlags = 0; + break; + } + } + return cmd; +} +#endif + +#ifdef VERSION_EU +u64 *synthesis_do_one_audio_update(s16 *aiBuf, s32 bufLen, u64 *cmd, s32 updateIndex) { + struct NoteSubEu *noteSubEu; + u8 noteIndices[56]; + s32 temp; + s32 i; + s16 j; + s16 notePos = 0; + + if (gNumSynthesisReverbs == 0) { + for (i = 0; i < gMaxSimultaneousNotes; i++) { + if (gNoteSubsEu[gMaxSimultaneousNotes * updateIndex + i].enabled) { + noteIndices[notePos++] = i; + } + } + } else { + for (j = 0; j < gNumSynthesisReverbs; j++) { + for (i = 0; i < gMaxSimultaneousNotes; i++) { + noteSubEu = &gNoteSubsEu[gMaxSimultaneousNotes * updateIndex + i]; + if (noteSubEu->enabled && j == noteSubEu->reverbIndex) { + noteIndices[notePos++] = i; + } + } + } + + for (i = 0; i < gMaxSimultaneousNotes; i++) { + noteSubEu = &gNoteSubsEu[gMaxSimultaneousNotes * updateIndex + i]; + if (noteSubEu->enabled && noteSubEu->reverbIndex >= gNumSynthesisReverbs) { + noteIndices[notePos++] = i; + } + } + } + aClearBuffer(cmd++, DMEM_ADDR_LEFT_CH, DEFAULT_LEN_2CH); + i = 0; + for (j = 0; j < gNumSynthesisReverbs; j++) { + gUseReverb = gSynthesisReverbs[j].useReverb; + if (gUseReverb != 0) { + cmd = synthesis_resample_and_mix_reverb(cmd, bufLen, j, updateIndex); + } + for (; i < notePos; i++) { + temp = updateIndex * gMaxSimultaneousNotes; + if (j == gNoteSubsEu[temp + noteIndices[i]].reverbIndex) { + cmd = synthesis_process_note(&gNotes[noteIndices[i]], + &gNoteSubsEu[temp + noteIndices[i]], + &gNotes[noteIndices[i]].synthesisState, + aiBuf, bufLen, cmd); + continue; + } else { + break; + } + } + if (gSynthesisReverbs[j].useReverb != 0) { + cmd = synthesis_save_reverb_samples(cmd, j, updateIndex); + } + } + for (; i < notePos; i++) { + temp = updateIndex * gMaxSimultaneousNotes; + if (IS_BANK_LOAD_COMPLETE(gNoteSubsEu[temp + noteIndices[i]].bankId) == TRUE) { + cmd = synthesis_process_note(&gNotes[noteIndices[i]], + &gNoteSubsEu[temp + noteIndices[i]], + &gNotes[noteIndices[i]].synthesisState, + aiBuf, bufLen, cmd); + } else { + gAudioErrorFlags = (gNoteSubsEu[temp + noteIndices[i]].bankId + (i << 8)) + 0x10000000; + } + } + + temp = bufLen * 2; + aSetBuffer(cmd++, 0, 0, DMEM_ADDR_TEMP, temp); + aInterleave(cmd++, DMEM_ADDR_LEFT_CH, DMEM_ADDR_RIGHT_CH); + aSetBuffer(cmd++, 0, 0, DMEM_ADDR_TEMP, temp * 2); + aSaveBuffer(cmd++, VIRTUAL_TO_PHYSICAL2(aiBuf)); + return cmd; +} +#else +u64 *synthesis_do_one_audio_update(s16 *aiBuf, s32 bufLen, u64 *cmd, s32 updateIndex) { + DEBUG_PRINT("synthesis_do_one_audio_update()"); + + UNUSED s32 pad1[1]; + s16 ra; + s16 t4; + UNUSED s32 pad[2]; + struct ReverbRingBufferItem *v1; + UNUSED s32 pad2[1]; + s16 temp; + + DEBUG_PRINT("- curFrame: %d", gSynthesisReverb.curFrame); + DEBUG_PRINT("- updateIndex: %d", updateIndex); + + v1 = &gSynthesisReverb.items[gSynthesisReverb.curFrame][updateIndex]; + DEBUG_PRINT("- v1: %x", v1); + + if (gSynthesisReverb.useReverb == 0) { + DEBUG_PRINT("- w/o reverb"); + aClearBuffer(cmd++, DMEM_ADDR_LEFT_CH, DEFAULT_LEN_2CH); + cmd = synthesis_process_notes(aiBuf, bufLen, cmd); + } else { + DEBUG_PRINT("- w/ reverb"); + if (gReverbDownsampleRate == 1) { + DEBUG_PRINT("- w/ reverb downsample"); + + // Put the oldest samples in the ring buffer into the wet channels + DEBUG_PRINT("- set load buffer pair 1"); + DEBUG_PRINT("- startPos: %d", v1->startPos); + aSetLoadBufferPair(cmd++, 0, v1->startPos); + if (v1->lengthB != 0) { + // Ring buffer wrapped + DEBUG_PRINT("- set load buffer pair 2"); + aSetLoadBufferPair(cmd++, v1->lengthA, 0); + temp = 0; + } + + // Use the reverb sound as initial sound for this audio update + DEBUG_PRINT("- dmem move"); + aDMEMMove(cmd++, DMEM_ADDR_WET_LEFT_CH, DMEM_ADDR_LEFT_CH, DEFAULT_LEN_2CH); + + // (Hopefully) lower the volume of the wet channels. New reverb will later be mixed into + // these channels. + DEBUG_PRINT("- set buffer"); + aSetBuffer(cmd++, 0, 0, 0, DEFAULT_LEN_2CH); + // 0x8000 here is -100% + DEBUG_PRINT("- mix"); + aMix(cmd++, 0, /*gain*/ 0x8000 + gSynthesisReverb.reverbGain, /*in*/ DMEM_ADDR_WET_LEFT_CH, + /*out*/ DMEM_ADDR_WET_LEFT_CH); + } else { + DEBUG_PRINT("- w/o reverb downsample"); + + // Same as above but upsample the previously downsampled samples used for reverb first + temp = 0; //! jesus christ + t4 = (v1->startPos & 7) * 2; + ra = ALIGN(v1->lengthA + t4, 4); + DEBUG_PRINT("- set load buffer pair"); + aSetLoadBufferPair(cmd++, 0, v1->startPos - t4 / 2); + if (v1->lengthB != 0) { + // Ring buffer wrapped + aSetLoadBufferPair(cmd++, ra, 0); + //! We need an empty statement (even an empty ';') here to make the function match (because IDO). + //! However, copt removes extraneous statements and dead code. So we need to trick copt + //! into thinking 'temp' could be undefined, and luckily the compiler optimizes out the + //! useless assignment. + ra = ra + temp; + } + DEBUG_PRINT("- set buffer 1"); + aSetBuffer(cmd++, 0, t4 + DMEM_ADDR_WET_LEFT_CH, DMEM_ADDR_LEFT_CH, bufLen << 1); + DEBUG_PRINT("- resample 1"); + aResample(cmd++, gSynthesisReverb.resampleFlags, (u16) gSynthesisReverb.resampleRate, VIRTUAL_TO_PHYSICAL2(gSynthesisReverb.resampleStateLeft)); + DEBUG_PRINT("- set buffer 2"); + aSetBuffer(cmd++, 0, t4 + DMEM_ADDR_WET_RIGHT_CH, DMEM_ADDR_RIGHT_CH, bufLen << 1); + DEBUG_PRINT("- resample 2"); + aResample(cmd++, gSynthesisReverb.resampleFlags, (u16) gSynthesisReverb.resampleRate, VIRTUAL_TO_PHYSICAL2(gSynthesisReverb.resampleStateRight)); + DEBUG_PRINT("- set buffer 3"); + aSetBuffer(cmd++, 0, 0, 0, DEFAULT_LEN_2CH); + DEBUG_PRINT("- mix"); + aMix(cmd++, 0, /*gain*/ 0x8000 + gSynthesisReverb.reverbGain, /*in*/ DMEM_ADDR_LEFT_CH, /*out*/ DMEM_ADDR_LEFT_CH); + DEBUG_PRINT("- dmem move"); + aDMEMMove(cmd++, DMEM_ADDR_LEFT_CH, DMEM_ADDR_WET_LEFT_CH, DEFAULT_LEN_2CH); + } + cmd = synthesis_process_notes(aiBuf, bufLen, cmd); + if (gReverbDownsampleRate == 1) { + aSetSaveBufferPair(cmd++, 0, v1->lengthA, v1->startPos); + if (v1->lengthB != 0) { + // Ring buffer wrapped + aSetSaveBufferPair(cmd++, v1->lengthA, v1->lengthB, 0); + } + } else { + // Downsampling is done later by CPU when RSP is done, therefore we need to have double + // buffering. Left and right buffers are adjacent in memory. + aSetBuffer(cmd++, 0, 0, DMEM_ADDR_WET_LEFT_CH, DEFAULT_LEN_2CH); + aSaveBuffer(cmd++, VIRTUAL_TO_PHYSICAL2(gSynthesisReverb.items[gSynthesisReverb.curFrame][updateIndex].toDownsampleLeft)); + gSynthesisReverb.resampleFlags = 0; + } + } + return cmd; +} +#endif + +#ifdef VERSION_EU +// Processes just one note, not all +u64 *synthesis_process_note(struct Note *note, struct NoteSubEu *noteSubEu, struct NoteSynthesisState *synthesisState, UNUSED s16 *aiBuf, s32 bufLen, u64 *cmd) { + UNUSED s32 pad0[3]; +#else +u64 *synthesis_process_notes(s16 *aiBuf, s32 bufLen, u64 *cmd) { + DEBUG_PRINT("synthesis_process_notes()"); + + s32 noteIndex; // sp174 + struct Note *note; // s7 + UNUSED u8 pad0[0x08]; +#endif + struct AudioBankSample *audioBookSample; // sp164, sp138 + struct AdpcmLoop *loopInfo; // sp160, sp134 + s16 *curLoadedBook = NULL; // sp154, sp130 +#ifdef VERSION_EU + UNUSED u8 padEU[0x04]; +#endif + UNUSED u8 pad8[0x04]; +#ifndef VERSION_EU + u16 resamplingRateFixedPoint; // sp5c, sp11A +#endif + s32 noteFinished; // 150 t2, sp124 + s32 restart; // 14c t3, sp120 + s32 flags; // sp148, sp11C +#ifdef VERSION_EU + u16 resamplingRateFixedPoint; // sp5c, sp11A +#endif + UNUSED u8 pad7[0x0c]; // sp100 + UNUSED s32 tempBufLen; +#ifdef VERSION_EU + s32 sp130; //sp128, sp104 + UNUSED u32 pad9; +#else + UNUSED u32 pad9; + s32 sp130; //sp128, sp104 +#endif + s32 nAdpcmSamplesProcessed; // signed required for US + s32 t0; +#ifdef VERSION_EU + u8 *sampleAddr; // sp120, spF4 + s32 s6; +#else + s32 s6; + u8 *sampleAddr; // sp120, spF4 +#endif + +#ifdef VERSION_EU + s32 samplesLenAdjusted; // 108, spEC + // Might have been used to store (samplesLenFixedPoint >> 0x10), but doing so causes strange + // behavior with the break near the end of the loop, causing US and JP to need a goto instead + UNUSED s32 samplesLenInt; + s32 endPos; // sp110, spE4 + s32 nSamplesToProcess; // sp10c/a0, spE0 + s32 s2; +#else + // Might have been used to store (samplesLenFixedPoint >> 0x10), but doing so causes strange + // behavior with the break near the end of the loop, causing US and JP to need a goto instead + UNUSED s32 samplesLenInt; + s32 samplesLenAdjusted; // 108 + s32 s2; + s32 endPos; // sp110, spE4 + s32 nSamplesToProcess; // sp10c/a0, spE0 +#endif + + s32 leftRight; + s32 s3; + s32 s5; //s4 + + u32 samplesLenFixedPoint; // v1_1 + s32 nSamplesInThisIteration; // v1_2 + u32 a3; +#ifndef VERSION_EU + s32 t9; +#endif + u8 *v0_2; + s32 nParts; // spE8, spBC + s32 curPart; // spE4, spB8 + +#ifndef VERSION_EU + f32 resamplingRate; // f12 +#endif + s32 temp; + +#ifdef VERSION_EU + s32 s5Aligned; +#endif + s32 resampledTempLen; // spD8, spAC + u16 noteSamplesDmemAddrBeforeResampling; // spD6, spAA + + + for (noteIndex = 0; noteIndex < gMaxSimultaneousNotes; noteIndex++) { + DEBUG_PRINT("- for note index %d/%d", noteIndex, gMaxSimultaneousNotes); + + DEBUG_PRINT("- getting note"); + note = &gNotes[noteIndex]; + + //! This function requires note->enabled to be volatile, but it breaks other functions like note_enable. + //! Casting to a struct with just the volatile bitfield works, but there may be a better way to match. + DEBUG_PRINT("- if note is enabled but not loaded"); + if (((struct vNote *)note)->enabled && IS_BANK_LOAD_COMPLETE(note->bankId) == FALSE) { + DEBUG_PRINT("- note is enabled but not loaded"); + gAudioErrorFlags = (note->bankId << 8) + noteIndex + 0x1000000; + continue; + } + + DEBUG_PRINT("- if note is enabled"); + if (((struct vNote *)note)->enabled) { + DEBUG_PRINT("$ note is enabled!"); + + flags = 0; + + DEBUG_PRINT("- if note needs to be init"); + if (note->needsInit == TRUE) { + flags = A_INIT; + note->samplePosInt = 0; + note->samplePosFrac = 0; + } + + DEBUG_PRINT("- if note frequency is less than 2"); + if (note->frequency < US_FLOAT(2.0)) { + nParts = 1; + if (note->frequency > US_FLOAT(1.99996)) { + note->frequency = US_FLOAT(1.99996); + } + resamplingRate = note->frequency; + } else { + // If frequency is > 2.0, the processing must be split into two parts + nParts = 2; + if (note->frequency >= US_FLOAT(3.99993)) { + note->frequency = US_FLOAT(3.99993); + } + resamplingRate = note->frequency * US_FLOAT(.5); + } + + resamplingRateFixedPoint = (u16)(s32)(resamplingRate * 32768.0f); + samplesLenFixedPoint = note->samplePosFrac + (resamplingRateFixedPoint * bufLen) * 2; + note->samplePosFrac = samplesLenFixedPoint & 0xFFFF; // 16-bit store, can't reuse + + DEBUG_PRINT("- if note sound is null"); + if (note->sound == NULL) { + // A wave synthesis note (not ADPCM) + + DEBUG_PRINT("- note is null, do wave synthesis"); + cmd = load_wave_samples(cmd, note, samplesLenFixedPoint >> 0x10); + noteSamplesDmemAddrBeforeResampling = DMEM_ADDR_UNCOMPRESSED_NOTE + note->samplePosInt * 2; + note->samplePosInt += (samplesLenFixedPoint >> 0x10); + flags = 0; + } + else { + // ADPCM note + DEBUG_PRINT("- @ handle adpcm note"); + + audioBookSample = note->sound->sample; + + loopInfo = audioBookSample->loop; + endPos = loopInfo->end; + sampleAddr = audioBookSample->sampleAddr; + resampledTempLen = 0; + for (curPart = 0; curPart < nParts; curPart++) { + DEBUG_PRINT("- for part %d", curPart); + nAdpcmSamplesProcessed = 0; // s8 + s5 = 0; // s4 + + if (nParts == 1) { + samplesLenAdjusted = samplesLenFixedPoint >> 0x10; + } else if ((samplesLenFixedPoint >> 0x10) & 1) { + samplesLenAdjusted = ((samplesLenFixedPoint >> 0x10) & ~1) + (curPart * 2); + } + else { + samplesLenAdjusted = (samplesLenFixedPoint >> 0x10); + } + + if (curLoadedBook != audioBookSample->book->book) { + u32 nEntries; // v1 + curLoadedBook = audioBookSample->book->book; + nEntries = audioBookSample->book->order * audioBookSample->book->npredictors; + DEBUG_PRINT("- loading adpcm"); + aLoadADPCM(cmd++, nEntries * 16, VIRTUAL_TO_PHYSICAL2(curLoadedBook)); + } + + while (nAdpcmSamplesProcessed != samplesLenAdjusted) { + s32 samplesRemaining; // v1 + s32 s0; + + noteFinished = FALSE; + restart = FALSE; + nSamplesToProcess = samplesLenAdjusted - nAdpcmSamplesProcessed; + s2 = note->samplePosInt & 0xf; + samplesRemaining = endPos - note->samplePosInt; + + if (s2 == 0 && note->restart == FALSE) { + s2 = 16; + } + s6 = 16 - s2; // a1 + + if (nSamplesToProcess < samplesRemaining) { + t0 = (nSamplesToProcess - s6 + 0xf) / 16; + s0 = t0 * 16; + s3 = s6 + s0 - nSamplesToProcess; + } else { + s0 = samplesRemaining + s2 - 0x10; + s3 = 0; + if (s0 <= 0) { + s0 = 0; + s6 = samplesRemaining; + } + t0 = (s0 + 0xf) / 16; + if (loopInfo->count != 0) { + // Loop around and restart + restart = 1; + } else { + noteFinished = 1; + } + } + + if (t0 != 0) { + temp = (note->samplePosInt - s2 + 0x10) / 16; + v0_2 = dma_sample_data( + (uintptr_t) (sampleAddr + temp * 9), + t0 * 9, flags, ¬e->sampleDmaIndex); + a3 = (u32)((uintptr_t) v0_2 & 0xf); + aSetBuffer(cmd++, 0, DMEM_ADDR_COMPRESSED_ADPCM_DATA, 0, t0 * 9 + a3); + aLoadBuffer(cmd++, VIRTUAL_TO_PHYSICAL2(v0_2 - a3)); + } else { + s0 = 0; + a3 = 0; + } + + DEBUG_PRINT("- if not note restart"); + if (note->restart != FALSE) { + aSetLoop(cmd++, VIRTUAL_TO_PHYSICAL2(audioBookSample->loop->state)); + flags = A_LOOP; // = 2 + note->restart = FALSE; + } + + nSamplesInThisIteration = s0 + s6 - s3; + if (nAdpcmSamplesProcessed == 0) { + aSetBuffer(cmd++, 0, DMEM_ADDR_COMPRESSED_ADPCM_DATA + a3, DMEM_ADDR_UNCOMPRESSED_NOTE, s0 * 2); + aADPCMdec(cmd++, flags, VIRTUAL_TO_PHYSICAL2(note->synthesisBuffers->adpcmdecState)); + sp130 = s2 * 2; + } else { + aSetBuffer(cmd++, 0, DMEM_ADDR_COMPRESSED_ADPCM_DATA + a3, DMEM_ADDR_UNCOMPRESSED_NOTE + ALIGN(s5, 5), s0 * 2); + aADPCMdec(cmd++, flags, VIRTUAL_TO_PHYSICAL2(note->synthesisBuffers->adpcmdecState)); + aDMEMMove(cmd++, DMEM_ADDR_UNCOMPRESSED_NOTE + ALIGN(s5, 5) + (s2 * 2), DMEM_ADDR_UNCOMPRESSED_NOTE + s5, (nSamplesInThisIteration) * 2); + } + + nAdpcmSamplesProcessed += nSamplesInThisIteration; + + switch (flags) { + case A_INIT: // = 1 + sp130 = 0; + s5 = s0 * 2 + s5; + break; + + case A_LOOP: // = 2 + s5 = nSamplesInThisIteration * 2 + s5; + break; + + default: + if (s5 != 0) { + s5 = nSamplesInThisIteration * 2 + s5; + } else { + s5 = (s2 + nSamplesInThisIteration) * 2; + } + break; + } + flags = 0; + + DEBUG_PRINT("- if note finished"); + if (noteFinished) { + aClearBuffer(cmd++, DMEM_ADDR_UNCOMPRESSED_NOTE + s5, + (samplesLenAdjusted - nAdpcmSamplesProcessed) * 2); + note->samplePosInt = 0; + note->finished = 1; + ((struct vNote *)note)->enabled = 0; + break; + } + + DEBUG_PRINT("- if restart"); + if (restart) { + note->restart = TRUE; + note->samplePosInt = loopInfo->start; + } else { + note->samplePosInt += nSamplesToProcess; + } + } + + switch (nParts) { + case 1: + noteSamplesDmemAddrBeforeResampling = DMEM_ADDR_UNCOMPRESSED_NOTE + sp130; + break; + + case 2: + switch (curPart) { + case 0: + aSetBuffer(cmd++, 0, DMEM_ADDR_UNCOMPRESSED_NOTE + sp130, DMEM_ADDR_RESAMPLED, samplesLenAdjusted + 4); + aResample(cmd++, A_INIT, 0xff60, VIRTUAL_TO_PHYSICAL2(note->synthesisBuffers->dummyResampleState)); + resampledTempLen = samplesLenAdjusted + 4; + noteSamplesDmemAddrBeforeResampling = DMEM_ADDR_RESAMPLED + 4; + if (note->finished != FALSE) { + aClearBuffer(cmd++, DMEM_ADDR_RESAMPLED + resampledTempLen, samplesLenAdjusted + 0x10); + } + break; + + case 1: + aSetBuffer(cmd++, 0, DMEM_ADDR_UNCOMPRESSED_NOTE + sp130, + DMEM_ADDR_RESAMPLED2, + samplesLenAdjusted + 8); + aResample(cmd++, A_INIT, 0xff60, + VIRTUAL_TO_PHYSICAL2( + note->synthesisBuffers->dummyResampleState)); + aDMEMMove(cmd++, DMEM_ADDR_RESAMPLED2 + 4, + DMEM_ADDR_RESAMPLED + resampledTempLen, + samplesLenAdjusted + 4); + break; + } + } + + if (note->finished != FALSE) { + break; + } + } + } + + flags = 0; + + if (note->needsInit == TRUE) { + flags = A_INIT; + note->needsInit = FALSE; + } + + cmd = final_resample(cmd, note, bufLen * 2, resamplingRateFixedPoint, + noteSamplesDmemAddrBeforeResampling, flags); + + if (note->headsetPanRight != 0 || note->prevHeadsetPanRight != 0) { + leftRight = 1; + } else if (note->headsetPanLeft != 0 || note->prevHeadsetPanLeft != 0) { + leftRight = 2; + } else { + leftRight = 0; + } + + cmd = process_envelope(cmd, note, bufLen, 0, leftRight, flags); + + if (note->usesHeadsetPanEffects) { + cmd = note_apply_headset_pan_effects(cmd, note, bufLen * 2, flags, leftRight); + } + } + } + + DEBUG_PRINT("- done handling notes"); + + DEBUG_PRINT("- setting buffer 1"); + t9 = bufLen * 2; + aSetBuffer(cmd++, 0, 0, DMEM_ADDR_TEMP, t9); + DEBUG_PRINT("- interleaving"); + aInterleave(cmd++, DMEM_ADDR_LEFT_CH, DMEM_ADDR_RIGHT_CH); + t9 *= 2; + DEBUG_PRINT("- setting buffer 2"); + aSetBuffer(cmd++, 0, 0, DMEM_ADDR_TEMP, t9); + DEBUG_PRINT("- saving buffer"); + aSaveBuffer(cmd++, VIRTUAL_TO_PHYSICAL2(aiBuf)); + + DEBUG_PRINT("- returning from process notes"); + return cmd; +} + + +u64 *load_wave_samples(u64 *cmd, struct Note *note, s32 nSamplesToLoad) { + s32 a3; + s32 i; + aSetBuffer(cmd++, /*flags*/ 0, /*dmemin*/ DMEM_ADDR_UNCOMPRESSED_NOTE, /*dmemout*/ 0, + /*count*/ sizeof(note->synthesisBuffers->samples)); + aLoadBuffer(cmd++, VIRTUAL_TO_PHYSICAL2(note->synthesisBuffers->samples)); + note->samplePosInt &= (note->sampleCount - 1); + a3 = 64 - note->samplePosInt; + if (a3 < nSamplesToLoad) { + for (i = 0; i <= (nSamplesToLoad - a3 + 63) / 64 - 1; i++) { + aDMEMMove(cmd++, /*dmemin*/ DMEM_ADDR_UNCOMPRESSED_NOTE, /*dmemout*/ DMEM_ADDR_UNCOMPRESSED_NOTE + (1 + i) * sizeof(note->synthesisBuffers->samples), /*count*/ sizeof(note->synthesisBuffers->samples)); + } + } + return cmd; +} + +u64 *final_resample(u64 *cmd, struct Note *note, s32 count, u16 pitch, u16 dmemIn, u32 flags) { + aSetBuffer(cmd++, /*flags*/ 0, dmemIn, /*dmemout*/ DMEM_ADDR_TEMP, count); + aResample(cmd++, flags, pitch, VIRTUAL_TO_PHYSICAL2(note->synthesisBuffers->finalResampleState)); + return cmd; +} + +#ifndef VERSION_EU +u64 *process_envelope(u64 *cmd, struct Note *note, s32 nSamples, u16 inBuf, s32 headsetPanSettings, + UNUSED u32 flags) { + UNUSED u8 pad[16]; + struct VolumeChange vol; + vol.sourceLeft = note->curVolLeft; + vol.sourceRight = note->curVolRight; + vol.targetLeft = note->targetVolLeft; + vol.targetRight = note->targetVolRight; + note->curVolLeft = vol.targetLeft; + note->curVolRight = vol.targetRight; + return process_envelope_inner(cmd, note, nSamples, inBuf, headsetPanSettings, &vol); +} + +u64 *process_envelope_inner(u64 *cmd, struct Note *note, s32 nSamples, u16 inBuf, + s32 headsetPanSettings, struct VolumeChange *vol) { + UNUSED u8 pad[3]; + u8 mixerFlags; + UNUSED u8 pad2[8]; + s32 rampLeft, rampRight; +#elif defined(VERSION_EU) +u64 *process_envelope(u64 *cmd, struct NoteSubEu *note, struct NoteSynthesisState *synthesisState, s32 nSamples, u16 inBuf, s32 headsetPanSettings, UNUSED u32 flags) { + UNUSED u8 pad1[20]; + u16 sourceRight; + u16 sourceLeft; + UNUSED u8 pad2[4]; + u16 targetLeft; + u16 targetRight; + s32 mixerFlags; + s32 rampLeft; + s32 rampRight; + + sourceLeft = synthesisState->curVolLeft; + sourceRight = synthesisState->curVolRight; + targetLeft = (note->targetVolLeft << 5); + targetRight = (note->targetVolRight << 5); + if (targetLeft == 0) { + targetLeft++; + } + if (targetRight == 0) { + targetRight++; + } + synthesisState->curVolLeft = targetLeft; + synthesisState->curVolRight = targetRight; +#endif + + // For aEnvMixer, five buffers and count are set using aSetBuffer. + // in, dry left, count without A_AUX flag. + // dry right, wet left, wet right with A_AUX flag. + + if (note->usesHeadsetPanEffects) { + aClearBuffer(cmd++, DMEM_ADDR_NOTE_PAN_TEMP, DEFAULT_LEN_1CH); + + switch (headsetPanSettings) { + case 1: + aSetBuffer(cmd++, 0, inBuf, DMEM_ADDR_NOTE_PAN_TEMP, nSamples * 2); + aSetBuffer(cmd++, A_AUX, DMEM_ADDR_RIGHT_CH, DMEM_ADDR_WET_LEFT_CH, + DMEM_ADDR_WET_RIGHT_CH); + break; + case 2: + aSetBuffer(cmd++, 0, inBuf, DMEM_ADDR_LEFT_CH, nSamples * 2); + aSetBuffer(cmd++, A_AUX, DMEM_ADDR_NOTE_PAN_TEMP, DMEM_ADDR_WET_LEFT_CH, + DMEM_ADDR_WET_RIGHT_CH); + break; + default: + aSetBuffer(cmd++, 0, inBuf, DMEM_ADDR_LEFT_CH, nSamples * 2); + aSetBuffer(cmd++, A_AUX, DMEM_ADDR_RIGHT_CH, DMEM_ADDR_WET_LEFT_CH, + DMEM_ADDR_WET_RIGHT_CH); + break; + } + } else { + // It's a bit unclear what the "stereo strong" concept does. + // Instead of mixing the opposite channel to the normal buffers, the sound is first + // mixed into a temporary buffer and then subtracted from the normal buffer. + if (note->stereoStrongRight) { + aClearBuffer(cmd++, DMEM_ADDR_STEREO_STRONG_TEMP_DRY, DEFAULT_LEN_2CH); + aSetBuffer(cmd++, 0, inBuf, DMEM_ADDR_STEREO_STRONG_TEMP_DRY, nSamples * 2); + aSetBuffer(cmd++, A_AUX, DMEM_ADDR_RIGHT_CH, DMEM_ADDR_STEREO_STRONG_TEMP_WET, + DMEM_ADDR_WET_RIGHT_CH); + } else if (note->stereoStrongLeft) { + aClearBuffer(cmd++, DMEM_ADDR_STEREO_STRONG_TEMP_DRY, DEFAULT_LEN_2CH); + aSetBuffer(cmd++, 0, inBuf, DMEM_ADDR_LEFT_CH, nSamples * 2); + aSetBuffer(cmd++, A_AUX, DMEM_ADDR_STEREO_STRONG_TEMP_DRY, DMEM_ADDR_WET_LEFT_CH, + DMEM_ADDR_STEREO_STRONG_TEMP_WET); + } else { + aSetBuffer(cmd++, 0, inBuf, DMEM_ADDR_LEFT_CH, nSamples * 2); + aSetBuffer(cmd++, A_AUX, DMEM_ADDR_RIGHT_CH, DMEM_ADDR_WET_LEFT_CH, DMEM_ADDR_WET_RIGHT_CH); + } + } + +#ifdef VERSION_EU + if (targetLeft == sourceLeft && targetRight == sourceRight && !note->envMixerNeedsInit) { +#else + if (vol->targetLeft == vol->sourceLeft && vol->targetRight == vol->sourceRight + && !note->envMixerNeedsInit) { +#endif + mixerFlags = A_CONTINUE; + } else { + mixerFlags = A_INIT; + +#ifdef VERSION_EU + rampLeft = gCurrentLeftVolRamping[targetLeft >> 5] * gCurrentRightVolRamping[sourceLeft >> 5]; + rampRight = gCurrentLeftVolRamping[targetRight >> 5] * gCurrentRightVolRamping[sourceRight >> 5]; +#else + rampLeft = get_volume_ramping(vol->sourceLeft, vol->targetLeft, nSamples); + rampRight = get_volume_ramping(vol->sourceRight, vol->targetRight, nSamples); +#endif + + // The operation's parameters change meanings depending on flags +#ifdef VERSION_EU + aSetVolume(cmd++, A_VOL | A_LEFT, sourceLeft, 0, 0); + aSetVolume(cmd++, A_VOL | A_RIGHT, sourceRight, 0, 0); + aSetVolume32(cmd++, A_RATE | A_LEFT, targetLeft, rampLeft); + aSetVolume32(cmd++, A_RATE | A_RIGHT, targetRight, rampRight); + aSetVolume(cmd++, A_AUX, gVolume, 0, note->reverbVol << 8); +#else + aSetVolume(cmd++, A_VOL | A_LEFT, vol->sourceLeft, 0, 0); + aSetVolume(cmd++, A_VOL | A_RIGHT, vol->sourceRight, 0, 0); + aSetVolume32(cmd++, A_RATE | A_LEFT, vol->targetLeft, rampLeft); + aSetVolume32(cmd++, A_RATE | A_RIGHT, vol->targetRight, rampRight); + aSetVolume(cmd++, A_AUX, gVolume, 0, note->reverbVolShifted); +#endif + } + +#ifdef VERSION_EU + if (gUseReverb && note->reverbVol != 0) { + aEnvMixer(cmd++, mixerFlags | A_AUX, + VIRTUAL_TO_PHYSICAL2(synthesisState->synthesisBuffers->mixEnvelopeState)); +#else + if (gSynthesisReverb.useReverb && note->reverbVol != 0) { + aEnvMixer(cmd++, mixerFlags | A_AUX, + VIRTUAL_TO_PHYSICAL2(note->synthesisBuffers->mixEnvelopeState)); +#endif + if (note->stereoStrongRight) { + aSetBuffer(cmd++, 0, 0, 0, nSamples * 2); + // 0x8000 is -100%, so subtract sound instead of adding... + aMix(cmd++, 0, /*gain*/ 0x8000, /*in*/ DMEM_ADDR_STEREO_STRONG_TEMP_DRY, + /*out*/ DMEM_ADDR_LEFT_CH); + aMix(cmd++, 0, /*gain*/ 0x8000, /*in*/ DMEM_ADDR_STEREO_STRONG_TEMP_WET, + /*out*/ DMEM_ADDR_WET_LEFT_CH); + } else if (note->stereoStrongLeft) { + aSetBuffer(cmd++, 0, 0, 0, nSamples * 2); + aMix(cmd++, 0, /*gain*/ 0x8000, /*in*/ DMEM_ADDR_STEREO_STRONG_TEMP_DRY, + /*out*/ DMEM_ADDR_RIGHT_CH); + aMix(cmd++, 0, /*gain*/ 0x8000, /*in*/ DMEM_ADDR_STEREO_STRONG_TEMP_WET, + /*out*/ DMEM_ADDR_WET_RIGHT_CH); + } + } else { +#ifdef VERSION_EU + aEnvMixer(cmd++, mixerFlags, VIRTUAL_TO_PHYSICAL2(synthesisState->synthesisBuffers->mixEnvelopeState)); +#else + aEnvMixer(cmd++, mixerFlags, VIRTUAL_TO_PHYSICAL2(note->synthesisBuffers->mixEnvelopeState)); +#endif + if (note->stereoStrongRight) { + aSetBuffer(cmd++, 0, 0, 0, nSamples * 2); + aMix(cmd++, 0, /*gain*/ 0x8000, /*in*/ DMEM_ADDR_STEREO_STRONG_TEMP_DRY, + /*out*/ DMEM_ADDR_LEFT_CH); + } else if (note->stereoStrongLeft) { + aSetBuffer(cmd++, 0, 0, 0, nSamples * 2); + aMix(cmd++, 0, /*gain*/ 0x8000, /*in*/ DMEM_ADDR_STEREO_STRONG_TEMP_DRY, + /*out*/ DMEM_ADDR_RIGHT_CH); + } + } + return cmd; +} + +#ifdef VERSION_EU +u64 *note_apply_headset_pan_effects(u64 *cmd, struct NoteSubEu *noteSubEu, struct NoteSynthesisState *note, s32 bufLen, s32 flags, s32 leftRight) { +#else +u64 *note_apply_headset_pan_effects(u64 *cmd, struct Note *note, s32 bufLen, s32 flags, s32 leftRight) { +#endif + u16 dest; + u16 pitch; +#ifdef VERSION_EU + u8 prevPanShift; + u8 panShift; + UNUSED u8 unkDebug; +#else + u16 prevPanShift; + u16 panShift; +#endif + + switch (leftRight) { + case 1: + dest = DMEM_ADDR_LEFT_CH; +#ifdef VERSION_EU + panShift = noteSubEu->headsetPanRight; +#else + panShift = note->headsetPanRight; +#endif + note->prevHeadsetPanLeft = 0; + prevPanShift = note->prevHeadsetPanRight; + note->prevHeadsetPanRight = panShift; + break; + case 2: + dest = DMEM_ADDR_RIGHT_CH; +#ifdef VERSION_EU + panShift = noteSubEu->headsetPanLeft; +#else + panShift = note->headsetPanLeft; +#endif + note->prevHeadsetPanRight = 0; + + prevPanShift = note->prevHeadsetPanLeft; + note->prevHeadsetPanLeft = panShift; + break; + default: + return cmd; + } + + if (flags != 1) { // A_INIT? + // Slightly adjust the sample rate in order to fit a change in pan shift + if (prevPanShift == 0) { + // Kind of a hack that moves the first samples into the resample state + aDMEMMove(cmd++, DMEM_ADDR_NOTE_PAN_TEMP, DMEM_ADDR_TEMP, 8); + aClearBuffer(cmd++, 8, 8); // Set pitch accumulator to 0 in the resample state + aDMEMMove(cmd++, DMEM_ADDR_NOTE_PAN_TEMP, DMEM_ADDR_TEMP + 0x10, + 0x10); // No idea, result seems to be overwritten later + + aSetBuffer(cmd++, 0, 0, DMEM_ADDR_TEMP, 32); + aSaveBuffer(cmd++, VIRTUAL_TO_PHYSICAL2(note->synthesisBuffers->panResampleState)); + +#ifdef VERSION_EU + pitch = (bufLen << 0xf) / (bufLen + panShift - prevPanShift + 8); + if (pitch) { + } +#else + pitch = (bufLen << 0xf) / (panShift + bufLen - prevPanShift + 8); +#endif + aSetBuffer(cmd++, 0, DMEM_ADDR_NOTE_PAN_TEMP + 8, DMEM_ADDR_TEMP, panShift + bufLen - prevPanShift); + aResample(cmd++, 0, pitch, VIRTUAL_TO_PHYSICAL2(note->synthesisBuffers->panResampleState)); + } else { + if (panShift == 0) { + pitch = (bufLen << 0xf) / (bufLen - prevPanShift - 4); + } else { + pitch = (bufLen << 0xf) / (bufLen + panShift - prevPanShift); + } + +#if defined(VERSION_EU) && !defined(AVOID_UB) + if (unkDebug) { // UB + } +#endif + aSetBuffer(cmd++, 0, DMEM_ADDR_NOTE_PAN_TEMP, DMEM_ADDR_TEMP, panShift + bufLen - prevPanShift); + aResample(cmd++, 0, pitch, VIRTUAL_TO_PHYSICAL2(note->synthesisBuffers->panResampleState)); + } + + if (prevPanShift != 0) { + aSetBuffer(cmd++, 0, DMEM_ADDR_NOTE_PAN_TEMP, 0, prevPanShift); + aLoadBuffer(cmd++, VIRTUAL_TO_PHYSICAL2(note->synthesisBuffers->panSamplesBuffer)); + aDMEMMove(cmd++, DMEM_ADDR_TEMP, DMEM_ADDR_NOTE_PAN_TEMP + prevPanShift, panShift + bufLen - prevPanShift); + } else { + aDMEMMove(cmd++, DMEM_ADDR_TEMP, DMEM_ADDR_NOTE_PAN_TEMP, panShift + bufLen - prevPanShift); + } + } else { + // Just shift right + aDMEMMove(cmd++, DMEM_ADDR_NOTE_PAN_TEMP, DMEM_ADDR_TEMP, bufLen); + aDMEMMove(cmd++, DMEM_ADDR_TEMP, DMEM_ADDR_NOTE_PAN_TEMP + panShift, bufLen); + aClearBuffer(cmd++, DMEM_ADDR_NOTE_PAN_TEMP, panShift); + } + + if (panShift) { + // Save excessive samples for next iteration + aSetBuffer(cmd++, 0, 0, DMEM_ADDR_NOTE_PAN_TEMP + bufLen, panShift); + aSaveBuffer(cmd++, VIRTUAL_TO_PHYSICAL2(note->synthesisBuffers->panSamplesBuffer)); + } + + aSetBuffer(cmd++, 0, 0, 0, bufLen); + aMix(cmd++, 0, /*gain*/ 0x7fff, /*in*/ DMEM_ADDR_NOTE_PAN_TEMP, /*out*/ dest); + + return cmd; +} + +#ifndef VERSION_EU +// Moved to playback.c in EU + +void note_init_volume(struct Note *note) { + note->targetVolLeft = 0; + note->targetVolRight = 0; + note->reverbVol = 0; + note->reverbVolShifted = 0; + note->unused2 = 0; + note->curVolLeft = 1; + note->curVolRight = 1; + note->frequency = 0.0f; +} + +void note_set_vel_pan_reverb(struct Note *note, f32 velocity, f32 pan, u8 reverbVol) { + s32 panIndex; + f32 volLeft; + f32 volRight; + // Anding with 127 avoids out-of-bounds reads when pan is outside of [0, 1]. + // This can occur during PU movement -- see the bug comment in get_sound_pan + // in external.c. An out-of-bounds read by itself doesn't crash, but if the + // resulting value is a nan or denormal, performing arithmetic on it crashes + // on console. +#ifdef VERSION_JP + panIndex = MIN((s32)(pan * 127.5), 127); +#else + panIndex = (s32)(pan * 127.5f) & 127; +#endif + if (note->stereoHeadsetEffects && gSoundMode == SOUND_MODE_HEADSET) { + s8 smallPanIndex; + s8 temp = (s8)(pan * 10.0f); + if (temp < 9) { + smallPanIndex = temp; + } else { + smallPanIndex = 9; + } + note->headsetPanLeft = gHeadsetPanQuantization[smallPanIndex]; + note->headsetPanRight = gHeadsetPanQuantization[9 - smallPanIndex]; + note->stereoStrongRight = FALSE; + note->stereoStrongLeft = FALSE; + note->usesHeadsetPanEffects = TRUE; + volLeft = gHeadsetPanVolume[panIndex]; + volRight = gHeadsetPanVolume[127 - panIndex]; + } else if (note->stereoHeadsetEffects && gSoundMode == SOUND_MODE_STEREO) { + u8 strongLeft; + u8 strongRight; + strongLeft = FALSE; + strongRight = FALSE; + note->headsetPanLeft = 0; + note->headsetPanRight = 0; + note->usesHeadsetPanEffects = FALSE; + volLeft = gStereoPanVolume[panIndex]; + volRight = gStereoPanVolume[127 - panIndex]; + if (panIndex < 0x20) { + strongLeft = TRUE; + } else if (panIndex > 0x60) { + strongRight = TRUE; + } + note->stereoStrongRight = strongRight; + note->stereoStrongLeft = strongLeft; + } else if (gSoundMode == SOUND_MODE_MONO) { + volLeft = .707f; + volRight = .707f; + } else { + volLeft = gDefaultPanVolume[panIndex]; + volRight = gDefaultPanVolume[127 - panIndex]; + } + + if (velocity < 0) { + velocity = 0; + } +#ifdef VERSION_JP + note->targetVolLeft = (u16)(velocity * volLeft) & ~0x80FF; // 0x7F00, but that doesn't match + note->targetVolRight = (u16)(velocity * volRight) & ~0x80FF; +#else + note->targetVolLeft = (u16)(s32)(velocity * volLeft) & ~0x80FF; + note->targetVolRight = (u16)(s32)(velocity * volRight) & ~0x80FF; +#endif + if (note->targetVolLeft == 0) { + note->targetVolLeft++; + } + if (note->targetVolRight == 0) { + note->targetVolRight++; + } + if (note->reverbVol != reverbVol) { + note->reverbVol = reverbVol; + note->reverbVolShifted = reverbVol << 8; + note->envMixerNeedsInit = TRUE; + return; + } + + if (note->needsInit) { + note->envMixerNeedsInit = TRUE; + } else { + note->envMixerNeedsInit = FALSE; + } +} + +void note_set_frequency(struct Note *note, f32 frequency) { + note->frequency = frequency; +} + +void note_enable(struct Note *note) { + note->enabled = TRUE; + note->needsInit = TRUE; + note->restart = FALSE; + note->finished = FALSE; + note->stereoStrongRight = FALSE; + note->stereoStrongLeft = FALSE; + note->usesHeadsetPanEffects = FALSE; + note->headsetPanLeft = 0; + note->headsetPanRight = 0; + note->prevHeadsetPanRight = 0; + note->prevHeadsetPanLeft = 0; +} + +void note_disable(struct Note *note) { + if (note->needsInit == TRUE) { + note->needsInit = FALSE; + } else { + note_set_vel_pan_reverb(note, 0, .5, 0); + } + note->priority = NOTE_PRIORITY_DISABLED; + note->enabled = FALSE; + note->finished = FALSE; + note->parentLayer = NO_LAYER; + note->prevParentLayer = NO_LAYER; +} +#endif +#endif diff --git a/src/decomp/audio/synthesis.h b/src/decomp/audio/synthesis.h new file mode 100644 index 0000000..9ca8643 --- /dev/null +++ b/src/decomp/audio/synthesis.h @@ -0,0 +1,99 @@ +#ifndef AUDIO_SYNTHESIS_H +#define AUDIO_SYNTHESIS_H + +#include "internal.h" + +#ifdef VERSION_SH +#define DEFAULT_LEN_1CH 0x180 +#define DEFAULT_LEN_2CH 0x300 +#else +#define DEFAULT_LEN_1CH 0x140 +#define DEFAULT_LEN_2CH 0x280 +#endif + +#if defined(VERSION_EU) || defined(VERSION_SH) +#define MAX_UPDATES_PER_FRAME 5 +#else +#define MAX_UPDATES_PER_FRAME 4 +#endif + +struct ReverbRingBufferItem +{ + s16 numSamplesAfterDownsampling; + s16 chunkLen; // never read + s16 *toDownsampleLeft; + s16 *toDownsampleRight; // data pointed to by left and right are adjacent in memory + s32 startPos; // start pos in ring buffer + s16 lengthA; // first length in ring buffer (from startPos, at most until end) + s16 lengthB; // second length in ring buffer (from pos 0) +}; // size = 0x14 + +struct SynthesisReverb +{ + /*0x00, 0x00, 0x00*/ u8 resampleFlags; + /*0x01, 0x01, 0x01*/ u8 useReverb; + /*0x02, 0x02, 0x02*/ u8 framesLeftToIgnore; + /*0x03, 0x03, 0x03*/ u8 curFrame; +#if defined(VERSION_EU) || defined(VERSION_SH) + /* 0x04, 0x04*/ u8 downsampleRate; +#ifdef VERSION_SH + /* 0x05*/ s8 unk5; +#endif + /* 0x06, 0x06*/ u16 windowSize; // same as bufSizePerChannel +#endif +#ifdef VERSION_SH + /* 0x08*/ u16 unk08; +#endif + /*0x04, 0x08, 0x0A*/ u16 reverbGain; + /*0x06, 0x0A, 0x0C*/ u16 resampleRate; +#ifdef VERSION_SH + /* 0x0E*/ u16 panRight; + /* 0x10*/ u16 panLeft; +#endif + /*0x08, 0x0C, 0x14*/ s32 nextRingBufferPos; + /*0x0C, 0x10, 0x18*/ s32 unkC; // never read + /*0x10, 0x14, 0x1C*/ s32 bufSizePerChannel; + struct + { + s16 *left; + s16 *right; + } ringBuffer; + /*0x1C, 0x20, 0x28*/ s16 *resampleStateLeft; + /*0x20, 0x24, 0x2C*/ s16 *resampleStateRight; + /*0x24, 0x28, 0x30*/ s16 *unk24; // never read + /*0x28, 0x2C, 0x34*/ s16 *unk28; // never read + /*0x2C, 0x30, 0x38*/ struct ReverbRingBufferItem items[2][MAX_UPDATES_PER_FRAME]; +#if defined(VERSION_EU) || defined(VERSION_SH) + // Only used in sh: + /* 0x100*/ s16 *unk100; + /* 0x104*/ s16 *unk104; + /* 0x108*/ s16 *unk108; + /* 0x10C*/ s16 *unk10C; +#endif +}; // 0xCC <= size <= 0x100 +#if defined(VERSION_EU) || defined(VERSION_SH) +extern struct SynthesisReverb gSynthesisReverbs[4]; +extern s8 gNumSynthesisReverbs; +extern struct NoteSubEu *gNoteSubsEu; +extern f32 gLeftVolRampings[3][1024]; +extern f32 gRightVolRampings[3][1024]; +extern f32 *gCurrentLeftVolRamping; // Points to any of the three left buffers above +extern f32 *gCurrentRightVolRamping; // Points to any of the three right buffers above +#else +extern struct SynthesisReverb gSynthesisReverb; +#endif + +#ifdef VERSION_SH +extern s16 D_SH_803479B4; +#endif + +u64 *synthesis_execute(u64 *cmdBuf, s32 *writtenCmds, s16 *aiBuf, s32 bufLen); +#if defined(VERSION_JP) || defined(VERSION_US) +void note_init_volume(struct Note *note); +void note_set_vel_pan_reverb(struct Note *note, f32 velocity, f32 pan, u8 reverbVol); +void note_set_frequency(struct Note *note, f32 frequency); +void note_enable(struct Note *note); +void note_disable(struct Note *note); +#endif + +#endif // AUDIO_SYNTHESIS_H diff --git a/src/decomp/audio/synthesis_sh.c b/src/decomp/audio/synthesis_sh.c new file mode 100644 index 0000000..706d5d8 --- /dev/null +++ b/src/decomp/audio/synthesis_sh.c @@ -0,0 +1,914 @@ +#ifdef VERSION_SH +#include + +#include "synthesis.h" +#include "heap.h" +#include "data.h" +#include "load.h" +#include "seqplayer.h" +#include "internal.h" +#include "external.h" + +#ifndef TARGET_N64 +#include "../pc/mixer.h" +#endif + + +#define DMEM_ADDR_TEMP 0x450 +#define DMEM_ADDR_RESAMPLED 0x470 +#define DMEM_ADDR_RESAMPLED2 0x5f0 +#define DMEM_ADDR_UNCOMPRESSED_NOTE 0x5f0 +#define DMEM_ADDR_NOTE_PAN_TEMP 0x650 +#define DMEM_ADDR_COMPRESSED_ADPCM_DATA 0x990 +#define DMEM_ADDR_LEFT_CH 0x990 +#define DMEM_ADDR_RIGHT_CH 0xb10 +#define DMEM_ADDR_WET_LEFT_CH 0xc90 +#define DMEM_ADDR_WET_RIGHT_CH 0xe10 + +#define aSetLoadBufferPair(pkt, c, off) \ + aSetBuffer(pkt, 0, c + DMEM_ADDR_WET_LEFT_CH, 0, DEFAULT_LEN_1CH - c); \ + aLoadBuffer(pkt, VIRTUAL_TO_PHYSICAL2(gSynthesisReverb.ringBuffer.left + (off))); \ + aSetBuffer(pkt, 0, c + DMEM_ADDR_WET_RIGHT_CH, 0, DEFAULT_LEN_1CH - c); \ + aLoadBuffer(pkt, VIRTUAL_TO_PHYSICAL2(gSynthesisReverb.ringBuffer.right + (off))) + +#define aSetSaveBufferPair(pkt, c, d, off) \ + aSetBuffer(pkt, 0, 0, c + DMEM_ADDR_WET_LEFT_CH, d); \ + aSaveBuffer(pkt, VIRTUAL_TO_PHYSICAL2(gSynthesisReverb.ringBuffer.left + (off))); \ + aSetBuffer(pkt, 0, 0, c + DMEM_ADDR_WET_RIGHT_CH, d); \ + aSaveBuffer(pkt, VIRTUAL_TO_PHYSICAL2(gSynthesisReverb.ringBuffer.right + (off))); + +#define ALIGN(val, amnt) (((val) + (1 << amnt) - 1) & ~((1 << amnt) - 1)) + +struct VolumeChange { + u16 sourceLeft; + u16 sourceRight; + u16 targetLeft; + u16 targetRight; +}; + +u64 *synthesis_do_one_audio_update(s16 *aiBuf, s32 bufLen, u64 *cmd, s32 updateIndex); +u64 *synthesis_process_note(s32 noteIndex, struct NoteSubEu *noteSubEu, struct NoteSynthesisState *synthesisState, s16 *aiBuf, s32 bufLen, u64 *cmd, s32 updateIndex); +u64 *load_wave_samples(u64 *cmd, struct NoteSubEu *noteSubEu, struct NoteSynthesisState *synthesisState, s32 nSamplesToLoad); +u64 *final_resample(u64 *cmd, struct NoteSynthesisState *synthesisState, s32 count, u16 pitch, u16 dmemIn, u32 flags); +u64 *process_envelope(u64 *cmd, struct NoteSubEu *noteSubEu, struct NoteSynthesisState *synthesisState, s32 nSamples, u16 inBuf, s32 headsetPanSettings, u32 flags); +u64 *note_apply_headset_pan_effects(u64 *cmd, struct NoteSubEu *noteSubEu, struct NoteSynthesisState *note, s32 bufLen, s32 flags, s32 leftRight); + +struct SynthesisReverb gSynthesisReverbs[4]; +u8 sAudioSynthesisPad[0x10]; + +s16 gVolume; +s8 gUseReverb; +s8 gNumSynthesisReverbs; +s16 D_SH_803479B4; // contains 4096 +struct NoteSubEu *gNoteSubsEu; + +// Equivalent functionality as the US/JP version, +// just that the reverb structure is chosen from an array with index +// Identical in EU. +void prepare_reverb_ring_buffer(s32 chunkLen, u32 updateIndex, s32 reverbIndex) { + struct ReverbRingBufferItem *item; + struct SynthesisReverb *reverb = &gSynthesisReverbs[reverbIndex]; + s32 srcPos; + s32 dstPos; + s32 nSamples; + s32 excessiveSamples; + s32 UNUSED pad[3]; + if (reverb->downsampleRate != 1) { + if (reverb->framesLeftToIgnore == 0) { + // Now that the RSP has finished, downsample the samples produced two frames ago by skipping + // samples. + item = &reverb->items[reverb->curFrame][updateIndex]; + + // Touches both left and right since they are adjacent in memory + osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH); + + for (srcPos = 0, dstPos = 0; dstPos < item->lengthA / 2; + srcPos += reverb->downsampleRate, dstPos++) { + reverb->ringBuffer.left[item->startPos + dstPos] = + item->toDownsampleLeft[srcPos]; + reverb->ringBuffer.right[item->startPos + dstPos] = + item->toDownsampleRight[srcPos]; + } + for (dstPos = 0; dstPos < item->lengthB / 2; srcPos += reverb->downsampleRate, dstPos++) { + reverb->ringBuffer.left[dstPos] = item->toDownsampleLeft[srcPos]; + reverb->ringBuffer.right[dstPos] = item->toDownsampleRight[srcPos]; + } + } + } + + item = &reverb->items[reverb->curFrame][updateIndex]; + nSamples = chunkLen / reverb->downsampleRate; + excessiveSamples = (nSamples + reverb->nextRingBufferPos) - reverb->bufSizePerChannel; + if (excessiveSamples < 0) { + // There is space in the ring buffer before it wraps around + item->lengthA = nSamples * 2; + item->lengthB = 0; + item->startPos = (s32) reverb->nextRingBufferPos; + reverb->nextRingBufferPos += nSamples; + } else { + // Ring buffer wrapped around + item->lengthA = (nSamples - excessiveSamples) * 2; + item->lengthB = excessiveSamples * 2; + item->startPos = reverb->nextRingBufferPos; + reverb->nextRingBufferPos = excessiveSamples; + } + // These fields are never read later + item->numSamplesAfterDownsampling = nSamples; + item->chunkLen = chunkLen; +} + +u64 *synthesis_load_reverb_ring_buffer(u64 *cmd, u16 addr, u16 srcOffset, s32 len, s32 reverbIndex) { + aLoadBuffer(cmd++, VIRTUAL_TO_PHYSICAL2(&gSynthesisReverbs[reverbIndex].ringBuffer.left[srcOffset]), + addr, len); + aLoadBuffer(cmd++, VIRTUAL_TO_PHYSICAL2(&gSynthesisReverbs[reverbIndex].ringBuffer.right[srcOffset]), + addr + DEFAULT_LEN_1CH, len); + return cmd; +} + +u64 *synthesis_save_reverb_ring_buffer(u64 *cmd, u16 addr, u16 destOffset, s32 len, s32 reverbIndex) { + aSaveBuffer(cmd++, addr, + VIRTUAL_TO_PHYSICAL2(&gSynthesisReverbs[reverbIndex].ringBuffer.left[destOffset]), len); + aSaveBuffer(cmd++, addr + DEFAULT_LEN_1CH, + VIRTUAL_TO_PHYSICAL2(&gSynthesisReverbs[reverbIndex].ringBuffer.right[destOffset]), len); + return cmd; +} + +void func_sh_802ed644(s32 updateIndexStart, s32 noteIndex) { + s32 i; + + for (i = updateIndexStart + 1; i < gAudioBufferParameters.updatesPerFrame; i++) { + if (!gNoteSubsEu[gMaxSimultaneousNotes * i + noteIndex].needsInit) { + gNoteSubsEu[gMaxSimultaneousNotes * i + noteIndex].enabled = FALSE; + } else { + break; + } + } +} + +void synthesis_load_note_subs_eu(s32 updateIndex) { + struct NoteSubEu *src; + struct NoteSubEu *dest; + s32 i; + + for (i = 0; i < gMaxSimultaneousNotes; i++) { + src = &gNotes[i].noteSubEu; + dest = &gNoteSubsEu[gMaxSimultaneousNotes * updateIndex + i]; + if (src->enabled) { + *dest = *src; + src->needsInit = FALSE; + } else { + dest->enabled = FALSE; + } + } +} + +// TODO: (Scrub C) pointless mask and whitespace +u64 *synthesis_execute(u64 *cmdBuf, s32 *writtenCmds, s16 *aiBuf, s32 bufLen) { + s32 i, j; + u32 *aiBufPtr; + u64 *cmd = cmdBuf; + s32 chunkLen; + + for (i = gAudioBufferParameters.updatesPerFrame; i > 0; i--) { + process_sequences(i - 1); + synthesis_load_note_subs_eu(gAudioBufferParameters.updatesPerFrame - i); + } + aiBufPtr = (u32 *) aiBuf; + for (i = gAudioBufferParameters.updatesPerFrame; i > 0; i--) { + if (i == 1) { + chunkLen = bufLen; + } else { + if (bufLen / i >= gAudioBufferParameters.samplesPerUpdateMax) { + chunkLen = gAudioBufferParameters.samplesPerUpdateMax; + } else if (bufLen / i <= gAudioBufferParameters.samplesPerUpdateMin) { + chunkLen = gAudioBufferParameters.samplesPerUpdateMin; + } else { + chunkLen = gAudioBufferParameters.samplesPerUpdate; + } + } + for (j = 0; j < gNumSynthesisReverbs; j++) { + if (gSynthesisReverbs[j].useReverb != 0) { + prepare_reverb_ring_buffer(chunkLen, gAudioBufferParameters.updatesPerFrame - i, j); + } + } + cmd = synthesis_do_one_audio_update((s16 *) aiBufPtr, chunkLen, cmd, gAudioBufferParameters.updatesPerFrame - i); + bufLen -= chunkLen; + aiBufPtr += chunkLen; + } + + for (j = 0; j < gNumSynthesisReverbs; j++) { + if (gSynthesisReverbs[j].framesLeftToIgnore != 0) { + gSynthesisReverbs[j].framesLeftToIgnore--; + } + gSynthesisReverbs[j].curFrame ^= 1; + } + *writtenCmds = cmd - cmdBuf; + return cmd; +} + +u64 *synthesis_resample_and_mix_reverb(u64 *cmd, s32 bufLen, s16 reverbIndex, s16 updateIndex) { + struct ReverbRingBufferItem *item; + s16 startPad; + s16 paddedLengthA; + + item = &gSynthesisReverbs[reverbIndex].items[gSynthesisReverbs[reverbIndex].curFrame][updateIndex]; + + if (gSynthesisReverbs[reverbIndex].downsampleRate == 1) { + cmd = synthesis_load_reverb_ring_buffer(cmd, DMEM_ADDR_WET_LEFT_CH, item->startPos, item->lengthA, reverbIndex); + if (item->lengthB != 0) { + cmd = synthesis_load_reverb_ring_buffer(cmd, DMEM_ADDR_WET_LEFT_CH + item->lengthA, 0, item->lengthB, reverbIndex); + } + aAddMixer(cmd++, DMEM_ADDR_WET_LEFT_CH, DMEM_ADDR_LEFT_CH, DEFAULT_LEN_2CH); + aMix(cmd++, 0x8000 + gSynthesisReverbs[reverbIndex].reverbGain, DMEM_ADDR_WET_LEFT_CH, DMEM_ADDR_WET_LEFT_CH, DEFAULT_LEN_2CH); + } else { + startPad = (item->startPos % 8u) * 2; + paddedLengthA = ALIGN(startPad + item->lengthA, 4); + + cmd = synthesis_load_reverb_ring_buffer(cmd, DMEM_ADDR_RESAMPLED, (item->startPos - startPad / 2), DEFAULT_LEN_1CH, reverbIndex); + if (item->lengthB != 0) { + cmd = synthesis_load_reverb_ring_buffer(cmd, DMEM_ADDR_RESAMPLED + paddedLengthA, 0, DEFAULT_LEN_1CH - paddedLengthA, reverbIndex); + } + + aSetBuffer(cmd++, 0, DMEM_ADDR_RESAMPLED + startPad, DMEM_ADDR_WET_LEFT_CH, bufLen * 2); + aResample(cmd++, gSynthesisReverbs[reverbIndex].resampleFlags, gSynthesisReverbs[reverbIndex].resampleRate, VIRTUAL_TO_PHYSICAL2(gSynthesisReverbs[reverbIndex].resampleStateLeft)); + + aSetBuffer(cmd++, 0, DMEM_ADDR_RESAMPLED2 + startPad, DMEM_ADDR_WET_RIGHT_CH, bufLen * 2); + aResample(cmd++, gSynthesisReverbs[reverbIndex].resampleFlags, gSynthesisReverbs[reverbIndex].resampleRate, VIRTUAL_TO_PHYSICAL2(gSynthesisReverbs[reverbIndex].resampleStateRight)); + + aAddMixer(cmd++, DMEM_ADDR_WET_LEFT_CH, DMEM_ADDR_LEFT_CH, DEFAULT_LEN_2CH); + aMix(cmd++, 0x8000 + gSynthesisReverbs[reverbIndex].reverbGain, DMEM_ADDR_WET_LEFT_CH, DMEM_ADDR_WET_LEFT_CH, DEFAULT_LEN_2CH); + } + if (gSynthesisReverbs[reverbIndex].panRight != 0 || gSynthesisReverbs[reverbIndex].panLeft != 0) { + // Leak some audio from the left reverb channel into the right reverb channel and vice versa (pan) + aDMEMMove(cmd++, DMEM_ADDR_WET_LEFT_CH, DMEM_ADDR_RESAMPLED, DEFAULT_LEN_1CH); + aMix(cmd++, gSynthesisReverbs[reverbIndex].panRight, DMEM_ADDR_WET_RIGHT_CH, DMEM_ADDR_WET_LEFT_CH, DEFAULT_LEN_1CH); + aMix(cmd++, gSynthesisReverbs[reverbIndex].panLeft, DMEM_ADDR_RESAMPLED, DMEM_ADDR_WET_RIGHT_CH, DEFAULT_LEN_1CH); + } + return cmd; +} + +u64 *synthesis_load_reverb_samples(u64 *cmd, s16 reverbIndex, s16 updateIndex) { + struct ReverbRingBufferItem *item; + struct SynthesisReverb *reverb; + + reverb = &gSynthesisReverbs[reverbIndex]; + item = &reverb->items[reverb->curFrame][updateIndex]; + // Get the oldest samples in the ring buffer into the wet channels + cmd = synthesis_load_reverb_ring_buffer(cmd, DMEM_ADDR_RESAMPLED, item->startPos, item->lengthA, reverbIndex); + if (item->lengthB != 0) { + // Ring buffer wrapped + cmd = synthesis_load_reverb_ring_buffer(cmd, DMEM_ADDR_RESAMPLED + item->lengthA, 0, item->lengthB, reverbIndex); + } + return cmd; +} + +u64 *synthesis_save_reverb_samples(u64 *cmd, s16 reverbIndex, s16 updateIndex) { + struct ReverbRingBufferItem *item; + + item = &gSynthesisReverbs[reverbIndex].items[gSynthesisReverbs[reverbIndex].curFrame][updateIndex]; + switch (gSynthesisReverbs[reverbIndex].downsampleRate) { + case 1: + // Put the oldest samples in the ring buffer into the wet channels + cmd = synthesis_save_reverb_ring_buffer(cmd, DMEM_ADDR_WET_LEFT_CH, item->startPos, item->lengthA, reverbIndex); + if (item->lengthB != 0) { + // Ring buffer wrapped + cmd = synthesis_save_reverb_ring_buffer(cmd, DMEM_ADDR_WET_LEFT_CH + item->lengthA, 0, item->lengthB, reverbIndex); + } + break; + + default: + // Downsampling is done later by CPU when RSP is done, therefore we need to have double + // buffering. Left and right buffers are adjacent in memory. + aSaveBuffer(cmd++, DMEM_ADDR_WET_LEFT_CH, + VIRTUAL_TO_PHYSICAL2(gSynthesisReverbs[reverbIndex].items[gSynthesisReverbs[reverbIndex].curFrame][updateIndex].toDownsampleLeft), DEFAULT_LEN_2CH); + break; + } + gSynthesisReverbs[reverbIndex].resampleFlags = 0; + return cmd; +} + +u64 *func_sh_802EDF24(u64 *cmd, s16 reverbIndex, s16 updateIndex) { + struct ReverbRingBufferItem *item; + struct SynthesisReverb *reverb; + + reverb = &gSynthesisReverbs[reverbIndex]; + item = &reverb->items[reverb->curFrame][updateIndex]; + // Put the oldest samples in the ring buffer into the wet channels + cmd = synthesis_save_reverb_ring_buffer(cmd, DMEM_ADDR_RESAMPLED, item->startPos, item->lengthA, reverbIndex); + if (item->lengthB != 0) { + // Ring buffer wrapped + cmd = synthesis_save_reverb_ring_buffer(cmd, DMEM_ADDR_RESAMPLED + item->lengthA, 0, item->lengthB, reverbIndex); + } + return cmd; +} + +u64 *synthesis_do_one_audio_update(s16 *aiBuf, s32 bufLen, u64 *cmd, s32 updateIndex) { + struct NoteSubEu *noteSubEu; + u8 noteIndices[56]; + s32 temp; + s32 i; + s16 j; + s16 notePos = 0; + + if (gNumSynthesisReverbs == 0) { + for (i = 0; i < gMaxSimultaneousNotes; i++) { + if (gNoteSubsEu[gMaxSimultaneousNotes * updateIndex + i].enabled) { + noteIndices[notePos++] = i; + } + } + } else { + for (j = 0; j < gNumSynthesisReverbs; j++) { + for (i = 0; i < gMaxSimultaneousNotes; i++) { + noteSubEu = &gNoteSubsEu[gMaxSimultaneousNotes * updateIndex + i]; + if (noteSubEu->enabled && j == noteSubEu->reverbIndex) { + noteIndices[notePos++] = i; + } + } + } + + for (i = 0; i < gMaxSimultaneousNotes; i++) { + noteSubEu = &gNoteSubsEu[gMaxSimultaneousNotes * updateIndex + i]; + if (noteSubEu->enabled && noteSubEu->reverbIndex >= gNumSynthesisReverbs) { + noteIndices[notePos++] = i; + } + } + } + aClearBuffer(cmd++, DMEM_ADDR_LEFT_CH, DEFAULT_LEN_2CH); + i = 0; + for (j = 0; j < gNumSynthesisReverbs; j++) { + gUseReverb = gSynthesisReverbs[j].useReverb; + if (gUseReverb != 0) { + cmd = synthesis_resample_and_mix_reverb(cmd, bufLen, j, updateIndex); + } + for (; i < notePos; i++) { + temp = updateIndex * gMaxSimultaneousNotes; + if (j == gNoteSubsEu[temp + noteIndices[i]].reverbIndex) { + cmd = synthesis_process_note(noteIndices[i], + &gNoteSubsEu[temp + noteIndices[i]], + &gNotes[noteIndices[i]].synthesisState, + aiBuf, bufLen, cmd, updateIndex); + continue; + } else { + break; + } + } + if (gSynthesisReverbs[j].useReverb != 0) { + if (gSynthesisReverbs[j].unk100 != NULL) { + aFilter(cmd++, 0x02, bufLen * 2, gSynthesisReverbs[j].unk100); + aFilter(cmd++, gSynthesisReverbs[j].resampleFlags, DMEM_ADDR_WET_LEFT_CH, gSynthesisReverbs[j].unk108); + } + if (gSynthesisReverbs[j].unk104 != NULL) { + aFilter(cmd++, 0x02, bufLen * 2, gSynthesisReverbs[j].unk104); + aFilter(cmd++, gSynthesisReverbs[j].resampleFlags, DMEM_ADDR_WET_RIGHT_CH, gSynthesisReverbs[j].unk10C); + } + cmd = synthesis_save_reverb_samples(cmd, j, updateIndex); + if (gSynthesisReverbs[j].unk5 != -1) { + if (gSynthesisReverbs[gSynthesisReverbs[j].unk5].downsampleRate == 1) { + cmd = synthesis_load_reverb_samples(cmd, gSynthesisReverbs[j].unk5, updateIndex); + aMix(cmd++, gSynthesisReverbs[j].unk08, DMEM_ADDR_WET_LEFT_CH, DMEM_ADDR_RESAMPLED, DEFAULT_LEN_2CH); + cmd = func_sh_802EDF24(cmd++, gSynthesisReverbs[j].unk5, updateIndex); + } + } + } + } + for (; i < notePos; i++) { + struct NoteSubEu *noteSubEu2 = &gNoteSubsEu[updateIndex * gMaxSimultaneousNotes + noteIndices[i]]; + cmd = synthesis_process_note(noteIndices[i], + noteSubEu2, + &gNotes[noteIndices[i]].synthesisState, + aiBuf, bufLen, cmd, updateIndex); + } + + temp = bufLen * 2; + aInterleave(cmd++, DMEM_ADDR_TEMP, DMEM_ADDR_LEFT_CH, DMEM_ADDR_RIGHT_CH, temp); + aSaveBuffer(cmd++, DMEM_ADDR_TEMP, VIRTUAL_TO_PHYSICAL2(aiBuf), temp * 2); + return cmd; +} + +u64 *synthesis_process_note(s32 noteIndex, struct NoteSubEu *noteSubEu, struct NoteSynthesisState *synthesisState, UNUSED s16 *aiBuf, s32 bufLen, u64 *cmd, s32 updateIndex) { + UNUSED s32 pad0[3]; + struct AudioBankSample *audioBookSample; // sp164, sp138 + struct AdpcmLoop *loopInfo; // sp160, sp134 + s16 *curLoadedBook; // sp154, sp130 + UNUSED u8 padEU[0x04]; + UNUSED u8 pad8[0x04]; + s32 noteFinished; // 150 t2, sp124 + s32 restart; // 14c t3, sp120 + s32 flags; // sp148, sp11C, t8 + u16 resamplingRateFixedPoint; // sp5c, sp11A + s32 nSamplesToLoad; //s0, Ec + UNUSED u8 pad7[0x0c]; // sp100 + s32 sp130; //sp128, sp104 + UNUSED s32 tempBufLen; + UNUSED u32 pad9; + s32 t0; + u8 *sampleAddr; // sp120, spF4 + s32 s6; + s32 samplesLenAdjusted; // 108, spEC + s32 nAdpcmSamplesProcessed; // signed required for US // spc0 + s32 endPos; // sp110, spE4 + s32 nSamplesToProcess; // sp10c/a0, spE0 + // Might have been used to store (samplesLenFixedPoint >> 0x10), but doing so causes strange + // behavior with the break near the end of the loop, causing US and JP to need a goto instead + UNUSED s32 samplesLenInt; + s32 s2; + s32 leftRight; //s0 + s32 s5; //s4 + u32 samplesLenFixedPoint; // v1_1 + s32 s3; // spA0 + s32 nSamplesInThisIteration; // v1_2 + u32 a3; + u8 *v0_2; + s32 unk_s6; // sp90 + s32 s5Aligned; + s32 sp88; + s32 sp84; + u32 temp; + s32 nParts; // spE8, spBC + s32 curPart; // spE4, spB8 + s32 aligned; + UNUSED u32 padSH1; + s32 resampledTempLen; // spD8, spAC, sp6c + u16 noteSamplesDmemAddrBeforeResampling; // spD6, spAA, sp6a -- 6C + UNUSED u32 padSH2; + UNUSED u32 padSH3; + UNUSED u32 padSH4; + struct Note *note; // sp58 + u16 sp56; // sp56 + u16 addr; + u8 synthesisVolume; + + curLoadedBook = NULL; + note = &gNotes[noteIndex]; + flags = 0; + if (noteSubEu->needsInit == TRUE) { + flags = A_INIT; + synthesisState->restart = 0; + synthesisState->samplePosInt = 0; + synthesisState->samplePosFrac = 0; + synthesisState->curVolLeft = 0; + synthesisState->curVolRight = 0; + synthesisState->prevHeadsetPanRight = 0; + synthesisState->prevHeadsetPanLeft = 0; + synthesisState->reverbVol = noteSubEu->reverbVol; + synthesisState->unk5 = 0; + note->noteSubEu.finished = 0; + } + + resamplingRateFixedPoint = noteSubEu->resamplingRateFixedPoint; + nParts = noteSubEu->hasTwoAdpcmParts + 1; + samplesLenFixedPoint = (resamplingRateFixedPoint * bufLen * 2) + synthesisState->samplePosFrac; + nSamplesToLoad = (samplesLenFixedPoint >> 0x10); + synthesisState->samplePosFrac = samplesLenFixedPoint & 0xFFFF; + + if ((synthesisState->unk5 == 1) && (nParts == 2)) { + nSamplesToLoad += 2; + sp56 = 2; + } else if ((synthesisState->unk5 == 2) && (nParts == 1)) { + nSamplesToLoad -= 4; + sp56 = 4; + } else { + sp56 = 0; + } + + + synthesisState->unk5 = nParts; + + if (noteSubEu->isSyntheticWave) { + cmd = load_wave_samples(cmd, noteSubEu, synthesisState, nSamplesToLoad); + noteSamplesDmemAddrBeforeResampling = (synthesisState->samplePosInt * 2) + DMEM_ADDR_UNCOMPRESSED_NOTE; + synthesisState->samplePosInt += nSamplesToLoad; + } else { + // ADPCM note + audioBookSample = noteSubEu->sound.audioBankSound->sample; + loopInfo = audioBookSample->loop; + endPos = loopInfo->end; + sampleAddr = audioBookSample->sampleAddr; + resampledTempLen = 0; + for (curPart = 0; curPart < nParts; curPart++) { + nAdpcmSamplesProcessed = 0; // s8 + s5 = 0; // s4 + + if (nParts == 1) { + samplesLenAdjusted = nSamplesToLoad; + } else if (nSamplesToLoad & 1) { + samplesLenAdjusted = (nSamplesToLoad & ~1) + (curPart * 2); + } else { + samplesLenAdjusted = nSamplesToLoad; + } + + if (audioBookSample->codec == CODEC_ADPCM) { + if (curLoadedBook != (*audioBookSample->book).book) { + u32 nEntries; + switch (noteSubEu->bookOffset) { + case 1: + curLoadedBook = euUnknownData_80301950 + 1; + break; + case 2: + curLoadedBook = euUnknownData_80301950 + 2; + break; + case 3: + default: + curLoadedBook = audioBookSample->book->book; + break; + } + nEntries = 16 * audioBookSample->book->order * audioBookSample->book->npredictors; + aLoadADPCM(cmd++, nEntries, VIRTUAL_TO_PHYSICAL2(curLoadedBook)); + } + } + + while (nAdpcmSamplesProcessed != samplesLenAdjusted) { + s32 samplesRemaining; // v1 + s32 s0; + + noteFinished = FALSE; + restart = FALSE; + s2 = synthesisState->samplePosInt & 0xf; + samplesRemaining = endPos - synthesisState->samplePosInt; + nSamplesToProcess = samplesLenAdjusted - nAdpcmSamplesProcessed; + + if (s2 == 0 && synthesisState->restart == FALSE) { + s2 = 16; + } + s6 = 16 - s2; // a1 + if (nSamplesToProcess < samplesRemaining) { + t0 = (nSamplesToProcess - s6 + 0xf) / 16; + s0 = t0 * 16; + s3 = s6 + s0 - nSamplesToProcess; + } else { + s0 = samplesRemaining - s6; + s3 = 0; + if (s0 <= 0) { + s0 = 0; + s6 = samplesRemaining; + } + t0 = (s0 + 0xf) / 16; + if (loopInfo->count != 0) { + // Loop around and restart + restart = 1; + } else { + noteFinished = 1; + } + } + switch (audioBookSample->codec) { + case CODEC_ADPCM: + unk_s6 = 9; + sp88 = 0x10; + sp84 = 0; + break; + case CODEC_S8: + unk_s6 = 0x10; + sp88 = 0x10; + sp84 = 0; + break; + case CODEC_SKIP: goto skip; + } + if (t0 != 0) { + temp = (synthesisState->samplePosInt + sp88 - s2) / 16; + if (audioBookSample->medium == 0) { + v0_2 = sp84 + (temp * unk_s6) + sampleAddr; + } else { + v0_2 = dma_sample_data((uintptr_t)(sp84 + (temp * unk_s6) + sampleAddr), + ALIGN(t0 * unk_s6 + 16, 4), flags, &synthesisState->sampleDmaIndex, audioBookSample->medium); + } + + a3 = ((uintptr_t)v0_2 & 0xf); + aligned = ALIGN(t0 * unk_s6 + 16, 4); + addr = (DMEM_ADDR_COMPRESSED_ADPCM_DATA - aligned) & 0xffff; + aLoadBuffer(cmd++, VIRTUAL_TO_PHYSICAL2(v0_2 - a3), addr, ALIGN(t0 * unk_s6 + 16, 4)); + } else { + s0 = 0; + a3 = 0; + } + if (synthesisState->restart != FALSE) { + aSetLoop(cmd++, VIRTUAL_TO_PHYSICAL2(audioBookSample->loop->state)); + flags = A_LOOP; // = 2 + synthesisState->restart = FALSE; + } + nSamplesInThisIteration = s0 + s6 - s3; + if (nAdpcmSamplesProcessed == 0) { + switch (audioBookSample->codec) { + case CODEC_ADPCM: + aligned = ALIGN(t0 * unk_s6 + 16, 4); + addr = (DMEM_ADDR_COMPRESSED_ADPCM_DATA - aligned) & 0xffff; + aSetBuffer(cmd++, 0, addr + a3, DMEM_ADDR_UNCOMPRESSED_NOTE, s0 * 2); + aADPCMdec(cmd++, flags, VIRTUAL_TO_PHYSICAL2(synthesisState->synthesisBuffers->adpcmdecState)); + break; + case CODEC_S8: + aligned = ALIGN(t0 * unk_s6 + 16, 4); + addr = (DMEM_ADDR_COMPRESSED_ADPCM_DATA - aligned) & 0xffff; + aSetBuffer(cmd++, 0, addr + a3, DMEM_ADDR_UNCOMPRESSED_NOTE, s0 * 2); + aS8Dec(cmd++, flags, VIRTUAL_TO_PHYSICAL2(synthesisState->synthesisBuffers->adpcmdecState)); + break; + } + sp130 = s2 * 2; + } else { + s5Aligned = ALIGN(s5 + 16, 4); + switch (audioBookSample->codec) { + case CODEC_ADPCM: + aligned = ALIGN(t0 * unk_s6 + 16, 4); + addr = (DMEM_ADDR_COMPRESSED_ADPCM_DATA - aligned) & 0xffff; + aSetBuffer(cmd++, 0, addr + a3, DMEM_ADDR_UNCOMPRESSED_NOTE + s5Aligned, s0 * 2); + aADPCMdec(cmd++, flags, VIRTUAL_TO_PHYSICAL2(synthesisState->synthesisBuffers->adpcmdecState)); + break; + case CODEC_S8: + aligned = ALIGN(t0 * unk_s6 + 16, 4); + addr = (DMEM_ADDR_COMPRESSED_ADPCM_DATA - aligned) & 0xffff; + aSetBuffer(cmd++, 0, addr + a3, DMEM_ADDR_UNCOMPRESSED_NOTE + s5Aligned, s0 * 2); + aS8Dec(cmd++, flags, VIRTUAL_TO_PHYSICAL2(synthesisState->synthesisBuffers->adpcmdecState)); + break; + } + aDMEMMove(cmd++, DMEM_ADDR_UNCOMPRESSED_NOTE + s5Aligned + (s2 * 2), DMEM_ADDR_UNCOMPRESSED_NOTE + s5, (nSamplesInThisIteration) * 2); + } + nAdpcmSamplesProcessed += nSamplesInThisIteration; + switch (flags) { + case A_INIT: // = 1 + sp130 = 0x20; + s5 = (s0 + 0x10) * 2; + break; + case A_LOOP: // = 2 + s5 = (nSamplesInThisIteration) * 2 + s5; + break; + default: + if (s5 != 0) { + s5 = (nSamplesInThisIteration) * 2 + s5; + } else { + s5 = (s2 + (nSamplesInThisIteration)) * 2; + } + break; + } + flags = 0; +skip: + if (noteFinished) { + aClearBuffer(cmd++, DMEM_ADDR_UNCOMPRESSED_NOTE + s5, + (samplesLenAdjusted - nAdpcmSamplesProcessed) * 2); + noteSubEu->finished = 1; + note->noteSubEu.finished = 1; + func_sh_802ed644(updateIndex, noteIndex); + break; + } + if (restart != 0) { + synthesisState->restart = TRUE; + synthesisState->samplePosInt = loopInfo->start; + } else { + synthesisState->samplePosInt += nSamplesToProcess; + } + } + + switch (nParts) { + case 1: + noteSamplesDmemAddrBeforeResampling = DMEM_ADDR_UNCOMPRESSED_NOTE + sp130; + break; + case 2: + switch (curPart) { + case 0: + aDownsampleHalf(cmd++, ALIGN(samplesLenAdjusted / 2, 3), DMEM_ADDR_UNCOMPRESSED_NOTE + sp130, DMEM_ADDR_RESAMPLED); + resampledTempLen = samplesLenAdjusted; + noteSamplesDmemAddrBeforeResampling = DMEM_ADDR_RESAMPLED; + if (noteSubEu->finished != FALSE) { + aClearBuffer(cmd++, noteSamplesDmemAddrBeforeResampling + resampledTempLen, samplesLenAdjusted + 0x10); + } + break; + case 1: + aDownsampleHalf(cmd++, ALIGN(samplesLenAdjusted / 2, 3), DMEM_ADDR_UNCOMPRESSED_NOTE + sp130, resampledTempLen + DMEM_ADDR_RESAMPLED); + break; + } + } + if (noteSubEu->finished != FALSE) { + break; + } + } + } + flags = 0; + if (noteSubEu->needsInit == TRUE) { + flags = A_INIT; + noteSubEu->needsInit = FALSE; + } + flags = flags | sp56; + cmd = final_resample(cmd, synthesisState, bufLen * 2, resamplingRateFixedPoint, + noteSamplesDmemAddrBeforeResampling, flags); + if ((flags & 1) != 0) { + flags = 1; + } + + if (noteSubEu->filter) { + aFilter(cmd++, 0x02, bufLen * 2, noteSubEu->filter); + aFilter(cmd++, flags, DMEM_ADDR_TEMP, synthesisState->synthesisBuffers->filterBuffer); + + } + + if (noteSubEu->bookOffset == 3) { + aUnknown25(cmd++, 0, bufLen * 2, DMEM_ADDR_TEMP, DMEM_ADDR_TEMP); + } + + synthesisVolume = noteSubEu->synthesisVolume; + if (synthesisVolume != 0) { + if (synthesisVolume < 0x10) { + synthesisVolume = 0x10; + } + + aHiLoGain(cmd++, synthesisVolume, (bufLen + 0x10) * 2, DMEM_ADDR_TEMP); + } + + if (noteSubEu->headsetPanRight != 0 || synthesisState->prevHeadsetPanRight != 0) { + leftRight = 1; + } else if (noteSubEu->headsetPanLeft != 0 || synthesisState->prevHeadsetPanLeft != 0) { + leftRight = 2; + } else { + leftRight = 0; + } + cmd = process_envelope(cmd, noteSubEu, synthesisState, bufLen, DMEM_ADDR_TEMP, leftRight, flags); + if (noteSubEu->usesHeadsetPanEffects) { + if ((flags & 1) == 0) { + flags = 0; + } + cmd = note_apply_headset_pan_effects(cmd, noteSubEu, synthesisState, bufLen * 2, flags, leftRight); + } + + return cmd; +} + +u64 *load_wave_samples(u64 *cmd, struct NoteSubEu *noteSubEu, struct NoteSynthesisState *synthesisState, s32 nSamplesToLoad) { + s32 a3; + s32 repeats; + aLoadBuffer(cmd++, VIRTUAL_TO_PHYSICAL2(noteSubEu->sound.samples), + DMEM_ADDR_UNCOMPRESSED_NOTE, 128); + + synthesisState->samplePosInt &= 0x3f; + a3 = 64 - synthesisState->samplePosInt; + if (a3 < nSamplesToLoad) { + repeats = (nSamplesToLoad - a3 + 63) / 64; + if (repeats != 0) { + aDuplicate(cmd++, + /*dmemin*/ DMEM_ADDR_UNCOMPRESSED_NOTE, + /*dmemout*/ DMEM_ADDR_UNCOMPRESSED_NOTE + 128, + /*copies*/ repeats); + } + } + return cmd; +} + +u64 *final_resample(u64 *cmd, struct NoteSynthesisState *synthesisState, s32 count, u16 pitch, u16 dmemIn, u32 flags) { + if (pitch == 0) { + aClearBuffer(cmd++, DMEM_ADDR_TEMP, count); + } else { + aSetBuffer(cmd++, /*flags*/ 0, dmemIn, /*dmemout*/ DMEM_ADDR_TEMP, count); + aResample(cmd++, flags, pitch, VIRTUAL_TO_PHYSICAL2(synthesisState->synthesisBuffers->finalResampleState)); + } + return cmd; +} + +u64 *process_envelope(u64 *cmd, struct NoteSubEu *note, struct NoteSynthesisState *synthesisState, s32 nSamples, u16 inBuf, s32 headsetPanSettings, UNUSED u32 flags) { + u16 sourceRight; + u16 sourceLeft; + u16 targetLeft; + u16 targetRight; + s16 rampLeft; + s16 rampRight; + s32 sourceReverbVol; + s16 rampReverb; + s32 reverbVolDiff = 0; + + sourceLeft = synthesisState->curVolLeft; + sourceRight = synthesisState->curVolRight; + targetLeft = note->targetVolLeft; + targetRight = note->targetVolRight; + targetLeft <<= 4; + targetRight <<= 4; + + if (targetLeft != sourceLeft) { + rampLeft = (targetLeft - sourceLeft) / (nSamples >> 3); + } else { + rampLeft = 0; + } + if (targetRight != sourceRight) { + rampRight = (targetRight - sourceRight) / (nSamples >> 3); + } else { + rampRight = 0; + } + + sourceReverbVol = synthesisState->reverbVol; + if (note->reverbVol != sourceReverbVol) { + reverbVolDiff = ((note->reverbVol & 0x7f) - (sourceReverbVol & 0x7f)) << 9; + rampReverb = reverbVolDiff / (nSamples >> 3); + synthesisState->reverbVol = note->reverbVol; + } else { + rampReverb = 0; + } + synthesisState->curVolLeft = sourceLeft + rampLeft * (nSamples >> 3); + synthesisState->curVolRight = sourceRight + rampRight * (nSamples >> 3); + + if (note->usesHeadsetPanEffects) { + aClearBuffer(cmd++, DMEM_ADDR_NOTE_PAN_TEMP, DEFAULT_LEN_1CH); + aEnvSetup1(cmd++, (sourceReverbVol & 0x7f) * 2, rampReverb, rampLeft, rampRight); + aEnvSetup2(cmd++, sourceLeft, sourceRight); + + switch (headsetPanSettings) { + case 1: + aEnvMixer(cmd++, + inBuf, nSamples, + (sourceReverbVol & 0x80) >> 7, + note->stereoStrongRight, note->stereoStrongLeft, + DMEM_ADDR_NOTE_PAN_TEMP, + DMEM_ADDR_RIGHT_CH, + DMEM_ADDR_WET_LEFT_CH, + DMEM_ADDR_WET_RIGHT_CH); + break; + case 2: + aEnvMixer(cmd++, + inBuf, nSamples, + (sourceReverbVol & 0x80) >> 7, + note->stereoStrongRight, note->stereoStrongLeft, + DMEM_ADDR_LEFT_CH, + DMEM_ADDR_NOTE_PAN_TEMP, + DMEM_ADDR_WET_LEFT_CH, + DMEM_ADDR_WET_RIGHT_CH); + break; + default: + aEnvMixer(cmd++, + inBuf, nSamples, + (sourceReverbVol & 0x80) >> 7, + note->stereoStrongRight, note->stereoStrongLeft, + DMEM_ADDR_LEFT_CH, + DMEM_ADDR_RIGHT_CH, + DMEM_ADDR_WET_LEFT_CH, + DMEM_ADDR_WET_RIGHT_CH); + break; + } + } else { + aEnvSetup1(cmd++, (sourceReverbVol & 0x7f) * 2, rampReverb, rampLeft, rampRight); + aEnvSetup2(cmd++, sourceLeft, sourceRight); + aEnvMixer(cmd++, + inBuf, nSamples, + (sourceReverbVol & 0x80) >> 7, + note->stereoStrongRight, note->stereoStrongLeft, + DMEM_ADDR_LEFT_CH, + DMEM_ADDR_RIGHT_CH, + DMEM_ADDR_WET_LEFT_CH, + DMEM_ADDR_WET_RIGHT_CH); + } + return cmd; +} + +u64 *note_apply_headset_pan_effects(u64 *cmd, struct NoteSubEu *noteSubEu, struct NoteSynthesisState *note, s32 bufLen, s32 flags, s32 leftRight) { + u16 dest; + u16 pitch; + u8 prevPanShift; + u8 panShift; + UNUSED u8 unkDebug; + + switch (leftRight) { + case 1: + dest = DMEM_ADDR_LEFT_CH; + panShift = noteSubEu->headsetPanRight; + note->prevHeadsetPanLeft = 0; + prevPanShift = note->prevHeadsetPanRight; + note->prevHeadsetPanRight = panShift; + break; + case 2: + dest = DMEM_ADDR_RIGHT_CH; + panShift = noteSubEu->headsetPanLeft; + note->prevHeadsetPanRight = 0; + + prevPanShift = note->prevHeadsetPanLeft; + note->prevHeadsetPanLeft = panShift; + break; + default: + return cmd; + } + + if (flags != 1) { // A_INIT? + // Slightly adjust the sample rate in order to fit a change in pan shift + if (panShift != prevPanShift) { + pitch = (((bufLen << 0xf) / 2) - 1) / ((bufLen + panShift - prevPanShift - 2) / 2); + aSetBuffer(cmd++, 0, DMEM_ADDR_NOTE_PAN_TEMP, DMEM_ADDR_TEMP, (bufLen + panShift) - prevPanShift); + aResampleZoh(cmd++, pitch, 0); + } else { + aDMEMMove(cmd++, DMEM_ADDR_NOTE_PAN_TEMP, DMEM_ADDR_TEMP, bufLen); + } + + if (prevPanShift != 0) { + aLoadBuffer(cmd++, VIRTUAL_TO_PHYSICAL2(note->synthesisBuffers->panSamplesBuffer), + DMEM_ADDR_NOTE_PAN_TEMP, ALIGN(prevPanShift, 4)); + aDMEMMove(cmd++, DMEM_ADDR_TEMP, DMEM_ADDR_NOTE_PAN_TEMP + prevPanShift, bufLen + panShift - prevPanShift); + } else { + aDMEMMove(cmd++, DMEM_ADDR_TEMP, DMEM_ADDR_NOTE_PAN_TEMP, bufLen + panShift); + } + } else { + // Just shift right + aDMEMMove(cmd++, DMEM_ADDR_NOTE_PAN_TEMP, DMEM_ADDR_TEMP, bufLen); + aClearBuffer(cmd++, DMEM_ADDR_NOTE_PAN_TEMP, panShift); + aDMEMMove(cmd++, DMEM_ADDR_TEMP, DMEM_ADDR_NOTE_PAN_TEMP + panShift, bufLen); + } + + if (panShift) { + // Save excessive samples for next iteration + aSaveBuffer(cmd++, DMEM_ADDR_NOTE_PAN_TEMP + bufLen, + VIRTUAL_TO_PHYSICAL2(note->synthesisBuffers->panSamplesBuffer), ALIGN(panShift, 4)); + } + + aAddMixer(cmd++, DMEM_ADDR_NOTE_PAN_TEMP, dest, (bufLen + 0x3f) & 0xffc0); + + return cmd; +} +#endif diff --git a/src/decomp/include/PR/abi.h b/src/decomp/include/PR/abi.h new file mode 100644 index 0000000..b4b2f84 --- /dev/null +++ b/src/decomp/include/PR/abi.h @@ -0,0 +1,1002 @@ +#ifndef _ABI_H_ +#define _ABI_H_ + +/************************************************************************** + * * + * Copyright (C) 1994, Silicon Graphics, Inc. * + * * + * These coded instructions, statements, and computer programs contain * + * unpublished proprietary information of Silicon Graphics, Inc., and * + * are protected by Federal copyright law. They may not be disclosed * + * to third parties or copied or duplicated in any form, in whole or * + * in part, without the prior written consent of Silicon Graphics, Inc. * + * * + **************************************************************************/ + +/************************************************************************** + * + * $Revision: 1.32 $ + * $Date: 1997/02/11 08:16:37 $ + * $Source: /exdisk2/cvs/N64OS/Master/cvsmdev2/PR/include/abi.h,v $ + * + **************************************************************************/ + +/* + * Header file for the Audio Binary Interface. + * This is included in the Media Binary Interface file + * mbi.h. + * + * This file follows the framework used for graphics. + * + */ + +/* Audio commands: */ +#define A_SPNOOP 0 +#define A_ADPCM 1 +#define A_CLEARBUFF 2 +#define A_RESAMPLE 5 +#define A_SETBUFF 8 +#define A_DMEMMOVE 10 +#define A_LOADADPCM 11 +#define A_MIXER 12 +#define A_INTERLEAVE 13 +#define A_SETLOOP 15 + +#ifndef VERSION_SH + +#define A_ENVMIXER 3 +#define A_LOADBUFF 4 +#define A_RESAMPLE 5 +#define A_SAVEBUFF 6 +#define A_SEGMENT 7 +#define A_SETVOL 9 +#define A_POLEF 14 + +#else + +#define A_ADDMIXER 4 +#define A_RESAMPLE_ZOH 6 +#define A_DMEMMOVE2 16 +#define A_DOWNSAMPLE_HALF 17 +#define A_ENVSETUP1 18 +#define A_ENVMIXER 19 +#define A_LOADBUFF 20 +#define A_SAVEBUFF 21 +#define A_ENVSETUP2 22 +#define A_S8DEC 23 +#define A_HILOGAIN 24 +#define A_UNK_25 25 +#define A_DUPLICATE 26 +#define A_FILTER 27 + +#endif + +#define ACMD_SIZE 32 +/* + * Audio flags + */ + +#define A_INIT 0x01 +#define A_CONTINUE 0x00 +#define A_LOOP 0x02 +#define A_OUT 0x02 +#define A_LEFT 0x02 +#define A_RIGHT 0x00 +#define A_VOL 0x04 +#define A_RATE 0x00 +#define A_AUX 0x08 +#define A_NOAUX 0x00 +#define A_MAIN 0x00 +#define A_MIX 0x10 + +/* + * BEGIN C-specific section: (typedef's) + */ +#if defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) + +/* + * Data Structures. + */ + +typedef struct { + unsigned int cmd:8; + unsigned int flags:8; + unsigned int gain:16; + unsigned int addr; +} Aadpcm; + +typedef struct { + unsigned int cmd:8; + unsigned int flags:8; + unsigned int gain:16; + unsigned int addr; +} Apolef; + +typedef struct { + unsigned int cmd:8; + unsigned int flags:8; + unsigned int pad1:16; + unsigned int addr; +} Aenvelope; + +typedef struct { + unsigned int cmd:8; + unsigned int pad1:8; + unsigned int dmem:16; + unsigned int pad2:16; + unsigned int count:16; +} Aclearbuff; + +typedef struct { + unsigned int cmd:8; + unsigned int pad1:8; + unsigned int pad2:16; + unsigned int inL:16; + unsigned int inR:16; +} Ainterleave; + +typedef struct { + unsigned int cmd:8; + unsigned int pad1:24; + unsigned int addr; +} Aloadbuff; + +typedef struct { + unsigned int cmd:8; + unsigned int flags:8; + unsigned int pad1:16; + unsigned int addr; +} Aenvmixer; + +typedef struct { + unsigned int cmd:8; + unsigned int flags:8; + unsigned int gain:16; + unsigned int dmemi:16; + unsigned int dmemo:16; +} Amixer; + +typedef struct { + unsigned int cmd:8; + unsigned int flags:8; + unsigned int dmem2:16; + unsigned int addr; +} Apan; + +typedef struct { + unsigned int cmd:8; + unsigned int flags:8; + unsigned int pitch:16; + unsigned int addr; +} Aresample; + +typedef struct { + unsigned int cmd:8; + unsigned int flags:8; + unsigned int pad1:16; + unsigned int addr; +} Areverb; + +typedef struct { + unsigned int cmd:8; + unsigned int pad1:24; + unsigned int addr; +} Asavebuff; + +typedef struct { + unsigned int cmd:8; + unsigned int pad1:24; + unsigned int pad2:2; + unsigned int number:4; + unsigned int base:24; +} Asegment; + +typedef struct { + unsigned int cmd:8; + unsigned int flags:8; + unsigned int dmemin:16; + unsigned int dmemout:16; + unsigned int count:16; +} Asetbuff; + +typedef struct { + unsigned int cmd:8; + unsigned int flags:8; + unsigned int vol:16; + unsigned int voltgt:16; + unsigned int volrate:16; +} Asetvol; + +typedef struct { + unsigned int cmd:8; + unsigned int pad1:8; + unsigned int dmemin:16; + unsigned int dmemout:16; + unsigned int count:16; +} Admemmove; + +typedef struct { + unsigned int cmd:8; + unsigned int pad1:8; + unsigned int count:16; + unsigned int addr; +} Aloadadpcm; + +typedef struct { + unsigned int cmd:8; + unsigned int pad1:8; + unsigned int pad2:16; + unsigned int addr; +} Asetloop; + +/* + * Generic Acmd Packet + */ + +typedef struct { + uintptr_t w0; + uintptr_t w1; +} Awords; + +typedef union { + Awords words; +#if IS_BIG_ENDIAN && !IS_64_BIT + Aadpcm adpcm; + Apolef polef; + Aclearbuff clearbuff; + Aenvelope envelope; + Ainterleave interleave; + Aloadbuff loadbuff; + Aenvmixer envmixer; + Aresample resample; + Areverb reverb; + Asavebuff savebuff; + Asegment segment; + Asetbuff setbuff; + Asetvol setvol; + Admemmove dmemmove; + Aloadadpcm loadadpcm; + Amixer mixer; + Asetloop setloop; +#endif + long long int force_union_align; /* dummy, force alignment */ +} Acmd; + +/* + * ADPCM State + */ +typedef short ADPCM_STATE[16]; + +/* + * Pole filter state + */ +typedef short POLEF_STATE[4]; + +/* + * Resampler state + */ +typedef short RESAMPLE_STATE[16]; + +/* + * Resampler constants + */ +#define UNITY_PITCH 0x8000 +#define MAX_RATIO 1.99996 /* within .03 cents of +1 octave */ + +/* + * Enveloper/Mixer state + */ +typedef short ENVMIX_STATE[40]; + +/* + * Macros to assemble the audio command list + */ + +/* + * Info about parameters: + * + * A "count" in the following macros is always measured in bytes. + * + * All volumes/gains are in Q1.15 signed fixed point numbers: + * 0x8000 is the minimum volume (-100%), negating the audio curve. + * 0x0000 is silent. + * 0x7fff is maximum volume (99.997%). + * + * All DRAM addresses refer to segmented addresses. A segment table shall + * first be set up by calling aSegment for each segment. When a DRAM + * address is later used as parameter, the 8 high bits will be an index + * to the segment table and the lower 24 bits are added to the base address + * stored in the segment table for this entry. The result is the physical address. + * With the newer rsp audio code, this segment table is not used. The address is + * used directly instead. + * + * Transfers to/from DRAM are executed using DMA and hence follow these restrictions: + * All DRAM addresses should be aligned by 8 bytes, or they will be + * rounded down to the nearest multiple of 8 bytes. + * All DRAM lengths should be aligned by 8 bytes, or they will be + * rounded up to the nearest multiple of 8 bytes. + */ + +/* + * Decompresses ADPCM data. + * Possible flags: A_INIT and A_LOOP. + * + * First set up internal data in DMEM: + * aLoadADPCM(cmd++, nEntries * 16, physicalAddressOfBook) + * aSetLoop(cmd++, physicalAddressOfLoopState) (if A_LOOP is set) + * + * Then before this command, call: + * aSetBuffer(cmd++, 0, in, out, count) + * + * Note: count will be rounded up to the nearest multiple of 32 bytes. + * + * ADPCM decompression works on a block of 16 (uncompressed) samples. + * The previous 2 samples and 9 bytes of input are decompressed to + * 16 new samples using the code book previously loaded. + * + * Before the algorithm starts, the previous 16 samples are loaded according to flag: + * A_INIT: all zeros + * A_LOOP: the address set by aSetLoop + * no flags: the DRAM address in the s parameter + * These 16 samples are immediately copied to the destination address. + * + * The result of "count" bytes will be written after these 16 initial samples. + * The last 16 samples written to the destination will also be written to + * the state address in DRAM. + */ +#define aADPCMdec(pkt, f, s) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = _SHIFTL(A_ADPCM, 24, 8) | _SHIFTL(f, 16, 8); \ + _a->words.w1 = (uintptr_t)(s); \ +} + +/* + * Not used in SM64. + */ +#define aPoleFilter(pkt, f, g, s) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_POLEF, 24, 8) | _SHIFTL(f, 16, 8) | \ + _SHIFTL(g, 0, 16)); \ + _a->words.w1 = (uintptr_t)(s); \ +} + +/* + * Clears DMEM data, where d is address and c is count, by writing zeros. + * + * Note: c is rounded up to the nearest multiple of 16 bytes. + */ +#define aClearBuffer(pkt, d, c) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = _SHIFTL(A_CLEARBUFF, 24, 8) | _SHIFTL(d, 0, 24); \ + _a->words.w1 = (uintptr_t)(c); \ +} + +/* + * Mixes an envelope with mono sound into 2 or 4 channels. + * Possible flags: A_INIT, A_AUX (indicates that 4 channels should be used). + * + * Before this command, call: + * aSetBuffer(cmd++, 0, inBuf, dryLeft, count) + * aSetBuffer(cmd++, A_AUX, dryRight, wetLeft, wetRight) + * + * The first time (A_INIT is set), volume also needs to be set: + * aSetVolume(cmd++, A_VOL | A_LEFT, initialVolumeLeft, 0, 0) + * aSetVolume(cmd++, A_VOL | A_RIGHT, initialVolumeRight, 0, 0) + * aSetVolume32(cmd++, A_RATE | A_LEFT, targetVolumeLeft, rampLeft) + * aSetVolume32(cmd++, A_RATE | A_RIGHT, targetVolumeRight, rampRight) + * aSetVolume(cmd++, A_AUX, dryVolume, 0, wetVolume) + * + * This command will now mix samples in inBuf into the destination buffers (dry and wet), + * but with the volume increased (or decreased) from initial volumes to target volumes, + * with the specified ramp rate. Once the target volume is reached, the volume stays + * at that level. Before the samples are finally mixed (added) into the destination + * buffers (dry and wet), the volume is changed according to dryVolume and wetVolume. + * + * Note: count will be rounded up to the nearest multiple of 16 bytes. + * Note: the wet channels are used for reverb. + * + */ +#define aEnvMixer(pkt, f, s) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = _SHIFTL(A_ENVMIXER, 24, 8) | _SHIFTL(f, 16, 8); \ + _a->words.w1 = (uintptr_t)(s); \ +} + +/* + * Interleaves two mono channels into stereo. + * + * First call: + * aSetBuffer(cmd++, 0, 0, output, count) + * + * The count refers to the size of each input. Hence 2 * count bytes will be written out. + * A left sample will be placed before the right sample. + * + * Note: count will be rounded up to the nearest multiple of 16 bytes. + */ +#define aInterleave(pkt, l, r) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = _SHIFTL(A_INTERLEAVE, 24, 8); \ + _a->words.w1 = _SHIFTL(l, 16, 16) | _SHIFTL(r, 0, 16); \ +} + +/* + * Loads a buffer from DRAM to DMEM. + * + * First call: + * aSetBuffer(cmd++, 0, in, 0, count) + * + * The in parameter to aSetBuffer is the destination in DMEM and the + * s parameter to this command is the source in DRAM. + */ +#define aLoadBuffer(pkt, s) \ +{ \ + DEBUG_PRINT("aLoadBuffer()"); \ + DEBUG_PRINT("- getting pkt"); \ + Acmd *_a = (Acmd *)pkt; \ + \ + DEBUG_PRINT("- setting first word"); \ + _a->words.w0 = _SHIFTL(A_LOADBUFF, 24, 8); \ + DEBUG_PRINT("- setting second word"); \ + _a->words.w1 = (uintptr_t)(s); \ +} + +/* + * Mixes audio. + * Possible flags: no flags used, although parameter present. + * + * First call: + * aSetBuffer(cmd++, 0, 0, 0, count) + * + * Input and output addresses are taken from the i and o parameters. + * The volume with which the input is changed is taken from the g parameter. + * After the volume of the input samples have been changed, the result + * is added to the output. + * + * Note: count will be rounded up to the nearest multiple of 32 bytes. + */ +#define aMix(pkt, f, g, i, o) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_MIXER, 24, 8) | _SHIFTL(f, 16, 8) | \ + _SHIFTL(g, 0, 16)); \ + _a->words.w1 = _SHIFTL(i,16, 16) | _SHIFTL(o, 0, 16); \ +} + +// Not present in the audio microcode. +#define aPan(pkt, f, d, s) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_PAN, 24, 8) | _SHIFTL(f, 16, 8) | \ + _SHIFTL(d, 0, 16)); \ + _a->words.w1 = (uintptr_t)(s); \ +} + +/* + * Resamples audio. + * Possible flags: A_INIT, A_OUT? (not used in SM64). + * + * First call: + * aSetBuffer(cmd++, 0, in, out, count) + * + * This command resamples the audio using the given frequency ratio (pitch) + * using a filter that uses a window of 4 source samples. This can be used + * either for just resampling audio to be able to be played back at a different + * sample rate, or to change the pitch if the result is played back at + * the same sample rate as the input. + * + * The frequency ratio is given in UQ1.15 fixed point format. + * For no change in frequency, use pitch 0x8000. + * For 1 octave up or downsampling to (roughly) half number of samples, use pitch 0xffff. + * For 1 octave down or upsampling to double as many samples, use pitch 0x4000. + * + * Note: count represents the number of output sample bytes and is rounded up to + * the nearest multiple of 16 bytes. + * + * The state consists of the four following source samples when the algorithm stopped as + * well as a fractional position, and is initialized to all zeros if A_INIT is given. + * Otherwise it is loaded from DRAM at address s. + * + * The algorithm starts by writing the four source samples from the state (or zero) + * to just before the input address given. It then creates one output sample by examining + * the four next source samples and then moving the source position zero or more + * samples forward. The first output sample (when A_INIT is given) is always 0. + * + * When "count" bytes have been written, the following four source samples + * are written to the state in DRAM as well as a fractional position. + */ +#define aResample(pkt, f, p, s) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_RESAMPLE, 24, 8) | _SHIFTL(f, 16, 8) |\ + _SHIFTL(p, 0, 16)); \ + _a->words.w1 = (uintptr_t)(s); \ +} + +/* + * Stores a buffer in DMEM to DRAM. + * + * First call: + * aSetBuffer(cmd++, 0, 0, out, count) + * + * The out parameter to aSetBuffer is the source in DMEM and the + * s parameter to this command is the destination in DRAM. + */ +#define aSaveBuffer(pkt, s) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = _SHIFTL(A_SAVEBUFF, 24, 8); \ + _a->words.w1 = (uintptr_t)(s); \ +} + +/* + * Sets up an entry in the segment table. + * + * The s parameter is a segment index, 0 to 15. + * The b parameter is the base offset. + */ +#define aSegment(pkt, s, b) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = _SHIFTL(A_SEGMENT, 24, 8); \ + _a->words.w1 = _SHIFTL(s, 24, 8) | _SHIFTL(b, 0, 24); \ +} + +/* + * Sets internal DMEM buffer addresses used for later commands. + * See each command for how to use aSetBuffer. + */ +#define aSetBuffer(pkt, f, i, o, c) \ +{ \ + DEBUG_PRINT("aSetBuffer()"); \ + DEBUG_PRINT("- getting pkt"); \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_SETBUFF, 24, 8) | _SHIFTL(f, 16, 8) | \ + _SHIFTL(i, 0, 16)); \ + _a->words.w1 = _SHIFTL(o, 16, 16) | _SHIFTL(c, 0, 16); \ +} + +/* + * Sets internal volume parameters. + * See aEnvMixer for more info. + */ +#define aSetVolume(pkt, f, v, t, r) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_SETVOL, 24, 8) | _SHIFTL(f, 16, 16) | \ + _SHIFTL(v, 0, 16)); \ + _a->words.w1 = _SHIFTL(t, 16, 16) | _SHIFTL(r, 0, 16); \ +} + +/* + * Sets the address to ADPCM loop state. + * + * The a parameter is a DRAM address. + * See aADPCMdec for more info. + */ +#define aSetLoop(pkt, a) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + _a->words.w0 = _SHIFTL(A_SETLOOP, 24, 8); \ + _a->words.w1 = (uintptr_t)(a); \ +} + +/* + * Copies memory in DMEM. + * + * Copies c bytes from address i to address o. + * + * Note: count is rounded up to the nearest multiple of 16 bytes. + * + * Note: This acts as memcpy where 16 bytes are moved at a time, therefore + * if input and output overlap, output address should be less than input address. + */ +#define aDMEMMove(pkt, i, o, c) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = _SHIFTL(A_DMEMMOVE, 24, 8) | _SHIFTL(i, 0, 24); \ + _a->words.w1 = _SHIFTL(o, 16, 16) | _SHIFTL(c, 0, 16); \ +} + +/* + * Loads ADPCM book from DRAM into DMEM. + * + * This command loads ADPCM table entries from DRAM to DMEM. + * + * The count parameter c should be a multiple of 16 bytes. + * The d parameter is a DRAM address. + */ +#define aLoadADPCM(pkt, c, d) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = _SHIFTL(A_LOADADPCM, 24, 8) | _SHIFTL(c, 0, 24); \ + _a->words.w1 = (uintptr_t) (d); \ +} + +// This is a version of aSetVolume which takes a single 32-bit parameter +// instead of two 16-bit ones. According to AziAudio, it is used to set +// ramping values when neither bit 4 nor bit 8 is set in the flags parameter. +// It does not appear in the official abi.h header. +/* + * Sets internal volume parameters. + * See aEnvMixer for more info. + */ +#define aSetVolume32(pkt, f, v, tr) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_SETVOL, 24, 8) | _SHIFTL(f, 16, 16) | \ + _SHIFTL(v, 0, 16)); \ + _a->words.w1 = (uintptr_t)(tr); \ +} + +#ifdef VERSION_SH +#undef aLoadBuffer +#undef aSaveBuffer +#undef aMix +#undef aEnvMixer +#undef aInterleave + +// New or modified operations in the new audio microcode below + +/** + * Decompresses S8 data. + * Possible flags: A_INIT and A_LOOP. + * + * First set up internal data in DMEM: + * aSetLoop(cmd++, physicalAddressOfLoopState) (if A_LOOP is set) + * + * Then before this command, call: + * aSetBuffer(cmd++, 0, in, out, count) + * + * Note: count will be rounded up to the nearest multiple of 32 bytes. + * + * S8 decompression works by expanding s8 bytes into s16 numbers, + * by performing a left shift of 8 steps. + * + * Before the algorithm starts, the previous 16 samples are loaded according to flag: + * A_INIT: all zeros + * A_LOOP: the address set by aSetLoop + * no flags: the DRAM address in the s parameter + * These 16 samples are immediately copied to the destination address. + * + * The result of "count" bytes will be written after these 16 initial samples. + * The last 16 samples written to the destination will also be written to + * the state address in DRAM. + */ +#define aS8Dec(pkt, f, s) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = _SHIFTL(A_S8DEC, 24, 8) | _SHIFTL(f, 16, 8); \ + _a->words.w1 = (uintptr_t)(s); \ +} + +/* + * Mix two tracks by simple clamped addition. + * + * s: DMEM source track 1 + * d: DMEM source track 2 and destination + * c: number of bytes to write + * + * Note: count is first rounded down to the nearest multiple of 16 bytes + * and then rounded up to the nearest multiple of 64 bytes. + */ +#define aAddMixer(pkt, s, d, c) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_ADDMIXER, 24, 8) | \ + _SHIFTL((c) >> 4, 16, 8) | _SHIFTL(0x7fff, 0, 16)); \ + _a->words.w1 = (_SHIFTL(s, 16, 16) | _SHIFTL(d, 0, 16)); \ +} + +/* + * Loads a buffer from DRAM to DMEM. + * + * s: DRAM source + * d: DMEM destination + * c: number of bytes to copy (rounded down to 16 byte alignment) + */ +#define aLoadBuffer(pkt, s, d, c) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = _SHIFTL(A_LOADBUFF, 24, 8) | \ + _SHIFTL((c) >> 4, 16, 8) | _SHIFTL(d, 0, 16); \ + _a->words.w1 = (uintptr_t)(s); \ +} + +/* + * Stores a buffer from DMEM to DRAM. + * + * s: DMEM source + * d: DRAM destination + * c: number of bytes to copy (rounded down to 16 byte alignment) + */ +#define aSaveBuffer(pkt, s, d, c) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = _SHIFTL(A_SAVEBUFF, 24, 8) | \ + _SHIFTL((c) >> 4, 16, 8) | _SHIFTL(s, 0, 16); \ + _a->words.w1 = (uintptr_t)(d); \ +} + +/* + * Duplicates 128 bytes of data a number of times. + * + * 128 bytes are read from source DMEM address s. + * Then c identical copies of these bytes are written to DMEM address d. + */ +#define aDuplicate(pkt, s, d, c) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_DUPLICATE, 24, 8) | \ + _SHIFTL(c, 16, 8) | _SHIFTL(s, 0, 16)); \ + _a->words.w1 = (_SHIFTL(d, 16, 16) | _SHIFTL(0x80, 0, 16)); \ +} + +/* + * Copies memory in DMEM, second version. + * + * Copies t * c bytes from address i to address o. + * + * Note: count is first rounded up to the nearest multiple of 32 bytes, + * before the multiplication by t. + * + * Note: This acts as memcpy where 32 bytes are moved at a time, therefore + * if input and output overlap, output address should be less than input address. + * + * Not used in SM64. + */ +#define aDMEMMove2(pkt, t, i, o, c) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = _SHIFTL(A_DMEMMOVE2, 24, 8) | \ + _SHIFTL(t, 16, 8) | _SHIFTL(i, 0, 16); \ + _a->words.w1 = _SHIFTL(o, 16, 16) | _SHIFTL(c, 0, 16); \ +} + +/* + * Fast resample. + * + * Before this command, call: + * aSetBuffer(cmd++, 0, in, out, count) + * + * This works like the other resample command but just takes the "nearest" sample, + * instead of a function of the four nearest samples. + * + * Initially the current position is calculated as (in << 16) + startFract. + * For every sample to create, the value is simply taken from the sample + * at address ((position >> 17) << 1). Then the current position is incremented + * by (pitch << 2). + * + * Note: count represents the number of output bytes to create, and is + * rounded up to the nearest multiple of 8 bytes. + */ +#define aResampleZoh(pkt, pitch, startFract) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_RESAMPLE_ZOH, 24, 8) | \ + _SHIFTL(pitch, 0, 16)); \ + _a->words.w1 = _SHIFTL(startFract, 0, 16); \ +} + +/* + * Fast downsampling by taking every other sample, discarding others. + * + * Note: nSamples refers to the number of output samples to create, and + * is first rounded up to the nearest multiple of 8. + */ +#define aDownsampleHalf(pkt, nSamples, i, o) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_DOWNSAMPLE_HALF, 24, 8) | \ + _SHIFTL(nSamples, 0, 16)); \ + _a->words.w1 = _SHIFTL(i, 16, 16) | _SHIFTL(o, 0, 16); \ +} + +/* + * Mixes audio. + * + * Input and output addresses are taken from the i and o parameters. + * The volume with which the input is changed is taken from the g parameter. + * After the volume of the input samples have been changed, the result + * is added to the output. + * + * Note: count is first rounded down to the nearest multiple of 16 bytes + * and then rounded up to the nearest multiple of 32 bytes. + */ +#define aMix(pkt, g, i, o, c) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_MIXER, 24, 8) | \ + _SHIFTL((c) >> 4, 16, 8) | _SHIFTL(g, 0, 16)); \ + _a->words.w1 = _SHIFTL(i, 16, 16) | _SHIFTL(o, 0, 16); \ +} + +/* + * See aEnvMixer for more info. + */ +#define aEnvSetup1(pkt, initialVolReverb, rampReverb, rampLeft, rampRight) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_ENVSETUP1, 24, 8) | \ + _SHIFTL(initialVolReverb, 16, 8) | \ + _SHIFTL(rampReverb, 0, 16)); \ + _a->words.w1 = _SHIFTL(rampLeft, 16, 16) | \ + _SHIFTL(rampRight, 0, 16); \ +} + +/* + * See aEnvMixer for more info. + */ +#define aEnvSetup2(pkt, initialVolLeft, initialVolRight) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = _SHIFTL(A_ENVSETUP2, 24, 8); \ + _a->words.w1 = _SHIFTL(initialVolLeft, 16, 16) | \ + _SHIFTL(initialVolRight, 0, 16); \ +} + +/* + * Mixes an envelope with mono sound into 4 channels. + * + * To allow for many parameters, a sequence of aEnvSetup1, aEnvSetup2, + * aEnvMixer shall always be called. + * + * The function works in blocks of 8 samples. + * However, nSamples is rounded up to the nearest multiple of 16 samples. + * + * For each sample in a block: + * 1. sampleLeft = in * volLeft * (negLeft ? -1 : 1) + * 2. sampleRight = in * volRight * (negRight ? -1 : 1) + * 3. dryLeft += sampleLeft + * 4. dryRight += sampleRight + * 5. if swapReverb: swap sampleLeft and sampleRight + * 6. wetLeft += sampleLeft * volReverb + * 7. wetRight += sampleRight * volReverb + * + * After each block, all vol variables are added by their corresponding + * ramp value. + * + * Each volume variable is treated as a UQ0.16 number. Make sure + * the ramp additions don't overflow, or wrapping will occur. + * The initialVolReverb parameter is only 8 bits, but will be left + * shifted 8 bits by the rsp. + */ +#define aEnvMixer(pkt, inBuf, nSamples, swapReverb, negLeft, negRight, \ + dryLeft, dryRight, wetLeft, wetRight) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_ENVMIXER, 24, 8) | \ + _SHIFTL((inBuf) >> 4, 16, 8) | \ + _SHIFTL(nSamples, 8, 8)) | \ + _SHIFTL(swapReverb, 2, 1) | _SHIFTL(negLeft, 1, 1) |\ + _SHIFTL(negRight, 0, 1); \ + _a->words.w1 = _SHIFTL((dryLeft) >> 4, 24, 8) | \ + _SHIFTL((dryRight) >> 4, 16, 8) | \ + _SHIFTL((wetLeft) >> 4, 8, 8) | \ + _SHIFTL((wetRight) >> 4, 0, 8); \ +} + +/* + * Interleaves two mono channels into stereo. + * + * The count refers to the size of each input. Hence 2 * count bytes + * will be written out. + * + * A left sample will be placed before the right sample. + * All addresses (output, left, right) are DMEM addresses. + * + * Note: count will be rounded up to the nearest multiple of 8 bytes. + * The previous version of this function rounded up to the nearest + * multiple of 16 bytes. + */ +#define aInterleave(pkt, o, l, r, c) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = _SHIFTL(A_INTERLEAVE, 24, 8) | \ + _SHIFTL((c) >> 4, 16, 8) | _SHIFTL(o, 0, 16); \ + _a->words.w1 = _SHIFTL(l, 16, 16) | _SHIFTL(r, 0, 16); \ +} + +/* + * Linear filter function. + * + * Calculates out[i] = sum all elements in the vector in[i..i-7] * filter[0..7], + * where "*" represents dot multiplication. The input/output contains s16 + * samples and filter contains Q1.15 signed fixed point numbers. + * Every result sample is rounded and clamped. + * + * First initiate by calling with the flag f set to 2, countOrBuf contains + * the length in bytes that shall be processed in the next call. The addr + * parameter shall contain the DRAM address to the filter table (16 bytes). + * The count will be rounded up to the nearest multiple of 16 bytes. + * + * The aFilter function shall then be called in direct succession, with flag + * set to either 0 or 1. The countOrBuf parameter shall contain the DMEM + * address for the input/output. The addr parameter shall contain the DRAM + * address for the state, containing the last previous 8 input samples. + * The state is always written to upon exit, but is only read at entry if + * the flag is 0 (otherwise all-zero samples are used instead). + */ +#define aFilter(pkt, f, countOrBuf, addr) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = _SHIFTL(A_FILTER, 24, 8) | _SHIFTL((f), 16, 8) | \ + _SHIFTL((countOrBuf), 0, 16); \ + _a->words.w1 = (uintptr_t)(addr); \ +} + +/* + * Modifies the volume of samples using a simple UQ4.4 gain multiplier. + * + * Performs the following: + * + * 1. Count c is rounded up to 32 byte alignment + * 2. g is a u8 that contains a UQ4.4 number + * 3. Modify each sample s, so that s = clamp_s16(s * g >> 4) + */ +#define aHiLoGain(pkt, g, buflen, i) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = _SHIFTL(A_HILOGAIN, 24, 8) | \ + _SHIFTL((g), 16, 8) | _SHIFTL((buflen), 0, 16); \ + _a->words.w1 = _SHIFTL((i), 16, 16); \ +} + +/* + * Performs the following: + * + * 1. Count c is rounded up to 64 byte alignment + * 2. f is added to i + * 3. i and o are from now treated as s16 pointers + * 4. 32 s16 samples are loaded from i to tbl + * 5. for (u32 idx = 0; idx * sizeof(s16) < c; idx++) + * o[idx] = clamp_s16((s32)o[idx] * (s32)tbl[idx % 32]); + */ +#define aUnknown25(pkt, f, c, o, i) \ +{ \ + Acmd *_a = (Acmd *)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_UNK_25, 24, 8) | \ + _SHIFTL((f), 16, 8) | _SHIFTL((c), 0, 16)); \ + _a->words.w1 = _SHIFTL((o), 16, 16) | _SHIFTL((i), 0, 16); \ +} + +#endif + +#endif /* _LANGUAGE_C */ + +#endif /* !_ABI_H_ */ diff --git a/src/decomp/include/PR/libaudio.h b/src/decomp/include/PR/libaudio.h new file mode 100644 index 0000000..81d1c18 --- /dev/null +++ b/src/decomp/include/PR/libaudio.h @@ -0,0 +1,23 @@ +#ifndef _ULTRA64_LIBAUDIO_H_ +#define _ULTRA64_LIBAUDIO_H_ + +#include "abi.h" +#include + +typedef struct +{ + u8 *offset __attribute__((aligned (8))); + s32 len __attribute__((aligned (8))); +} ALSeqData; + +typedef struct +{ + unsigned short revision; + unsigned short seqCount; + unsigned int pad; + ALSeqData seqArray[1]; +} __attribute__((aligned (16))) ALSeqFile; + +void alSeqFileNew(ALSeqFile *f, u8 *base); + +#endif diff --git a/src/decomp/include/PR/libultra.h b/src/decomp/include/PR/libultra.h new file mode 100644 index 0000000..f0dab41 --- /dev/null +++ b/src/decomp/include/PR/libultra.h @@ -0,0 +1,18 @@ +#ifndef _LIBULTRA_H +#define _LIBULTRA_H + +#define TV_TYPE_NTSC 1 +#define TV_TYPE_PAL 0 +#define TV_TYPE_MPAL 2 + +#define RESET_TYPE_COLD_RESET 0 +#define RESET_TYPE_NMI 1 +#define RESET_TYPE_BOOT_DISK 2 + +extern u32 osTvType; +extern u32 osRomBase; +extern u32 osResetType; +extern u32 osMemSize; +extern u8 osAppNmiBuffer[64]; + +#endif /* _LIBULTRA_H */ diff --git a/src/decomp/include/PR/os.h b/src/decomp/include/PR/os.h new file mode 100644 index 0000000..c8de719 --- /dev/null +++ b/src/decomp/include/PR/os.h @@ -0,0 +1,800 @@ + +/*==================================================================== + * os.h + * + * Copyright 1995, Silicon Graphics, Inc. + * All Rights Reserved. + * + * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, + * Inc.; the contents of this file may not be disclosed to third + * parties, copied or duplicated in any form, in whole or in part, + * without the prior written permission of Silicon Graphics, Inc. + * + * RESTRICTED RIGHTS LEGEND: + * Use, duplication or disclosure by the Government is subject to + * restrictions as set forth in subdivision (c)(1)(ii) of the Rights + * in Technical Data and Computer Software clause at DFARS + * 252.227-7013, and/or in similar or successor clauses in the FAR, + * DOD or NASA FAR Supplement. Unpublished - rights reserved under the + * Copyright Laws of the United States. + *====================================================================*/ + +/************************************************************************** + * + * $Revision: 1.149 $ + * $Date: 1997/12/15 04:30:52 $ + * $Source: /disk6/Master/cvsmdev2/PR/include/os.h,v $ + * + **************************************************************************/ + + +#ifndef _OS_H_ +#define _OS_H_ + +#ifdef _LANGUAGE_C_PLUS_PLUS +extern "C" { +#endif + +#include "ultratypes.h" +#include "PR/os_message.h" + +#if defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) + +/************************************************************************** + * + * Type definitions + * + */ + +/* + * Structure for device manager block + */ +typedef struct { + s32 active; /* Status flag */ + OSThread *thread; /* Calling thread */ + OSMesgQueue *cmdQueue; /* Command queue */ + OSMesgQueue *evtQueue; /* Event queue */ + OSMesgQueue *acsQueue; /* Access queue */ + /* Raw DMA routine */ + s32 (*dma)(s32, u32, void *, u32); + s32 (*edma)(OSPiHandle *, s32, u32, void *, u32); +} OSDevMgr; + +/* + * Structure for file system + */ + + + +typedef struct { + int status; + OSMesgQueue *queue; + int channel; + u8 id[32]; + u8 label[32]; + int version; + int dir_size; + int inode_table; /* block location */ + int minode_table; /* mirrioring inode_table */ + int dir_table; /* block location */ + int inode_start_page; /* page # */ + u8 banks; + u8 activebank; +} OSPfs; + + +typedef struct { + u32 file_size; /* bytes */ + u32 game_code; + u16 company_code; + char ext_name[4]; + char game_name[16]; +} OSPfsState; + +/* + * Structure for Profiler + */ +typedef struct { + u16 *histo_base; /* histogram base */ + u32 histo_size; /* histogram size */ + u32 *text_start; /* start of text segment */ + u32 *text_end; /* end of text segment */ +} OSProf; + +#endif /* defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) */ + +/************************************************************************** + * + * Global definitions + * + */ + +/* Thread states */ + +#define OS_STATE_STOPPED 1 +#define OS_STATE_RUNNABLE 2 +#define OS_STATE_RUNNING 4 +#define OS_STATE_WAITING 8 + +/* Events */ +#ifdef _FINALROM +#define OS_NUM_EVENTS 15 +#else +#define OS_NUM_EVENTS 23 +#endif + +#define OS_EVENT_SW1 0 /* CPU SW1 interrupt */ +#define OS_EVENT_SW2 1 /* CPU SW2 interrupt */ +#define OS_EVENT_CART 2 /* Cartridge interrupt: used by rmon */ +#define OS_EVENT_COUNTER 3 /* Counter int: used by VI/Timer Mgr */ +#define OS_EVENT_SP 4 /* SP task done interrupt */ +#define OS_EVENT_SI 5 /* SI (controller) interrupt */ +#define OS_EVENT_AI 6 /* AI interrupt */ +#define OS_EVENT_VI 7 /* VI interrupt: used by VI/Timer Mgr */ +#define OS_EVENT_PI 8 /* PI interrupt: used by PI Manager */ +#define OS_EVENT_DP 9 /* DP full sync interrupt */ +#define OS_EVENT_CPU_BREAK 10 /* CPU breakpoint: used by rmon */ +#define OS_EVENT_SP_BREAK 11 /* SP breakpoint: used by rmon */ +#define OS_EVENT_FAULT 12 /* CPU fault event: used by rmon */ +#define OS_EVENT_THREADSTATUS 13 /* CPU thread status: used by rmon */ +#define OS_EVENT_PRENMI 14 /* Pre NMI interrupt */ +#ifndef _FINALROM +#define OS_EVENT_RDB_READ_DONE 15 /* RDB read ok event: used by rmon */ +#define OS_EVENT_RDB_LOG_DONE 16 /* read of log data complete */ +#define OS_EVENT_RDB_DATA_DONE 17 /* read of hostio data complete */ +#define OS_EVENT_RDB_REQ_RAMROM 18 /* host needs ramrom access */ +#define OS_EVENT_RDB_FREE_RAMROM 19 /* host is done with ramrom access */ +#define OS_EVENT_RDB_DBG_DONE 20 +#define OS_EVENT_RDB_FLUSH_PROF 21 +#define OS_EVENT_RDB_ACK_PROF 22 +#endif + +/* Flags for debugging purpose */ + +#define OS_FLAG_CPU_BREAK 1 /* Break exception has occurred */ +#define OS_FLAG_FAULT 2 /* CPU fault has occurred */ + +/* Interrupt masks */ + +#define OS_IM_NONE 0x00000001 +#define OS_IM_SW1 0x00000501 +#define OS_IM_SW2 0x00000601 +#define OS_IM_CART 0x00000c01 +#define OS_IM_PRENMI 0x00001401 +#define OS_IM_RDBWRITE 0x00002401 +#define OS_IM_RDBREAD 0x00004401 +#define OS_IM_COUNTER 0x00008401 +#define OS_IM_CPU 0x0000ff01 +#define OS_IM_SP 0x00010401 +#define OS_IM_SI 0x00020401 +#define OS_IM_AI 0x00040401 +#define OS_IM_VI 0x00080401 +#define OS_IM_PI 0x00100401 +#define OS_IM_DP 0x00200401 +#define OS_IM_ALL 0x003fff01 +#define RCP_IMASK 0x003f0000 +#define RCP_IMASKSHIFT 16 + +/* Recommended thread priorities for the system threads */ + +#define OS_PRIORITY_MAX 255 +#define OS_PRIORITY_VIMGR 254 +#define OS_PRIORITY_RMON 250 +#define OS_PRIORITY_RMONSPIN 200 +#define OS_PRIORITY_PIMGR 150 +#define OS_PRIORITY_SIMGR 140 +#define OS_PRIORITY_APPMAX 127 +#define OS_PRIORITY_IDLE 0 /* Must be 0 */ + + +/* Flags to turn blocking on/off when sending/receiving message */ + +#define OS_MESG_NOBLOCK 0 +#define OS_MESG_BLOCK 1 + +/* Flags to indicate direction of data transfer */ + +#define OS_READ 0 /* device -> RDRAM */ +#define OS_WRITE 1 /* device <- RDRAM */ +#define OS_OTHERS 2 /* for Leo disk only */ + +/* + * I/O message types + */ +#define OS_MESG_TYPE_BASE (10) +#define OS_MESG_TYPE_LOOPBACK (OS_MESG_TYPE_BASE+0) +#define OS_MESG_TYPE_DMAREAD (OS_MESG_TYPE_BASE+1) +#define OS_MESG_TYPE_DMAWRITE (OS_MESG_TYPE_BASE+2) +#define OS_MESG_TYPE_VRETRACE (OS_MESG_TYPE_BASE+3) +#define OS_MESG_TYPE_COUNTER (OS_MESG_TYPE_BASE+4) +#define OS_MESG_TYPE_EDMAREAD (OS_MESG_TYPE_BASE+5) +#define OS_MESG_TYPE_EDMAWRITE (OS_MESG_TYPE_BASE+6) + +/* + * I/O message priority + */ +#define OS_MESG_PRI_NORMAL 0 +#define OS_MESG_PRI_HIGH 1 + +/* + * Page size argument for TLB routines + */ +#define OS_PM_4K 0x0000000 +#define OS_PM_16K 0x0006000 +#define OS_PM_64K 0x001e000 +#define OS_PM_256K 0x007e000 +#define OS_PM_1M 0x01fe000 +#define OS_PM_4M 0x07fe000 +#define OS_PM_16M 0x1ffe000 + +/* + * Stack size for I/O device managers: PIM (PI Manager), VIM (VI Manager), + * SIM (SI Manager) + * + */ +#define OS_PIM_STACKSIZE 4096 +#define OS_VIM_STACKSIZE 4096 +#define OS_SIM_STACKSIZE 4096 + +#define OS_MIN_STACKSIZE 72 + +/* + * Values for osTvType + */ +#define OS_TV_PAL 0 +#define OS_TV_NTSC 1 +#define OS_TV_MPAL 2 + +/* + * Video Interface (VI) mode type + */ +#define OS_VI_NTSC_LPN1 0 /* NTSC */ +#define OS_VI_NTSC_LPF1 1 +#define OS_VI_NTSC_LAN1 2 +#define OS_VI_NTSC_LAF1 3 +#define OS_VI_NTSC_LPN2 4 +#define OS_VI_NTSC_LPF2 5 +#define OS_VI_NTSC_LAN2 6 +#define OS_VI_NTSC_LAF2 7 +#define OS_VI_NTSC_HPN1 8 +#define OS_VI_NTSC_HPF1 9 +#define OS_VI_NTSC_HAN1 10 +#define OS_VI_NTSC_HAF1 11 +#define OS_VI_NTSC_HPN2 12 +#define OS_VI_NTSC_HPF2 13 + +#define OS_VI_PAL_LPN1 14 /* PAL */ +#define OS_VI_PAL_LPF1 15 +#define OS_VI_PAL_LAN1 16 +#define OS_VI_PAL_LAF1 17 +#define OS_VI_PAL_LPN2 18 +#define OS_VI_PAL_LPF2 19 +#define OS_VI_PAL_LAN2 20 +#define OS_VI_PAL_LAF2 21 +#define OS_VI_PAL_HPN1 22 +#define OS_VI_PAL_HPF1 23 +#define OS_VI_PAL_HAN1 24 +#define OS_VI_PAL_HAF1 25 +#define OS_VI_PAL_HPN2 26 +#define OS_VI_PAL_HPF2 27 + +#define OS_VI_MPAL_LPN1 28 /* MPAL - mainly Brazil */ +#define OS_VI_MPAL_LPF1 29 +#define OS_VI_MPAL_LAN1 30 +#define OS_VI_MPAL_LAF1 31 +#define OS_VI_MPAL_LPN2 32 +#define OS_VI_MPAL_LPF2 33 +#define OS_VI_MPAL_LAN2 34 +#define OS_VI_MPAL_LAF2 35 +#define OS_VI_MPAL_HPN1 36 +#define OS_VI_MPAL_HPF1 37 +#define OS_VI_MPAL_HAN1 38 +#define OS_VI_MPAL_HAF1 39 +#define OS_VI_MPAL_HPN2 40 +#define OS_VI_MPAL_HPF2 41 + +/* + * Video Interface (VI) special features + */ +#define OS_VI_GAMMA_ON 0x0001 +#define OS_VI_GAMMA_OFF 0x0002 +#define OS_VI_GAMMA_DITHER_ON 0x0004 +#define OS_VI_GAMMA_DITHER_OFF 0x0008 +#define OS_VI_DIVOT_ON 0x0010 +#define OS_VI_DIVOT_OFF 0x0020 +#define OS_VI_DITHER_FILTER_ON 0x0040 +#define OS_VI_DITHER_FILTER_OFF 0x0080 + +/* + * Video Interface (VI) mode attribute bit + */ +#define OS_VI_BIT_NONINTERLACE 0x0001 /* lo-res */ +#define OS_VI_BIT_INTERLACE 0x0002 /* lo-res */ +#define OS_VI_BIT_NORMALINTERLACE 0x0004 /* hi-res */ +#define OS_VI_BIT_DEFLICKINTERLACE 0x0008 /* hi-res */ +#define OS_VI_BIT_ANTIALIAS 0x0010 +#define OS_VI_BIT_POINTSAMPLE 0x0020 +#define OS_VI_BIT_16PIXEL 0x0040 +#define OS_VI_BIT_32PIXEL 0x0080 +#define OS_VI_BIT_LORES 0x0100 +#define OS_VI_BIT_HIRES 0x0200 +#define OS_VI_BIT_NTSC 0x0400 +#define OS_VI_BIT_PAL 0x0800 + +/* + * Leo Disk + */ + +/* transfer mode */ + +#define LEO_BLOCK_MODE 1 +#define LEO_TRACK_MODE 2 +#define LEO_SECTOR_MODE 3 + +/* + * Controllers number + */ + +#ifndef _HW_VERSION_1 +#define MAXCONTROLLERS 4 +#else +#define MAXCONTROLLERS 6 +#endif + +/* controller errors */ +#define CONT_NO_RESPONSE_ERROR 0x8 +#define CONT_OVERRUN_ERROR 0x4 +#ifdef _HW_VERSION_1 +#define CONT_FRAME_ERROR 0x2 +#define CONT_COLLISION_ERROR 0x1 +#endif + +/* Controller type */ + +#define CONT_ABSOLUTE 0x0001 +#define CONT_RELATIVE 0x0002 +#define CONT_JOYPORT 0x0004 +#define CONT_EEPROM 0x8000 +#define CONT_EEP16K 0x4000 +#define CONT_TYPE_MASK 0x1f07 +#define CONT_TYPE_NORMAL 0x0005 +#define CONT_TYPE_MOUSE 0x0002 + +/* Controller status */ + +#define CONT_CARD_ON 0x01 +#define CONT_CARD_PULL 0x02 +#define CONT_ADDR_CRC_ER 0x04 +#define CONT_EEPROM_BUSY 0x80 + +/* EEPROM TYPE */ + +#define EEPROM_TYPE_4K 0x01 +#define EEPROM_TYPE_16K 0x02 + +/* Buttons */ + +#define CONT_A 0x8000 +#define CONT_B 0x4000 +#define CONT_G 0x2000 +#define CONT_START 0x1000 +#define CONT_UP 0x0800 +#define CONT_DOWN 0x0400 +#define CONT_LEFT 0x0200 +#define CONT_RIGHT 0x0100 +#define CONT_L 0x0020 +#define CONT_R 0x0010 +#define CONT_E 0x0008 +#define CONT_D 0x0004 +#define CONT_C 0x0002 +#define CONT_F 0x0001 + +/* Nintendo's official button names */ + +#define A_BUTTON CONT_A +#define B_BUTTON CONT_B +#define L_TRIG CONT_L +#define R_TRIG CONT_R +#define Z_TRIG CONT_G +#define START_BUTTON CONT_START +#define U_JPAD CONT_UP +#define L_JPAD CONT_LEFT +#define R_JPAD CONT_RIGHT +#define D_JPAD CONT_DOWN +#define U_CBUTTONS CONT_E +#define L_CBUTTONS CONT_C +#define R_CBUTTONS CONT_F +#define D_CBUTTONS CONT_D + +/* File System size */ +#define OS_PFS_VERSION 0x0200 +#define OS_PFS_VERSION_HI (OS_PFS_VERSION >> 8) +#define OS_PFS_VERSION_LO (OS_PFS_VERSION & 255) + +#define PFS_FILE_NAME_LEN 16 +#define PFS_FILE_EXT_LEN 4 +#define BLOCKSIZE 32 /* bytes */ +#define PFS_ONE_PAGE 8 /* blocks */ +#define PFS_MAX_BANKS 62 + +/* File System flag */ + +#define PFS_READ 0 +#define PFS_WRITE 1 +#define PFS_CREATE 2 + +/* File System status */ +#define PFS_INITIALIZED 0x1 +#define PFS_CORRUPTED 0x2 /* File system was corrupted */ + +/* File System error number */ + +#define PFS_ERR_NOPACK 1 /* no memory card is plugged or */ +#define PFS_ERR_NEW_PACK 2 /* ram pack has been changed to a */ + /* different one */ +#define PFS_ERR_INCONSISTENT 3 /* need to run Pfschecker */ +#define PFS_ERR_CONTRFAIL CONT_OVERRUN_ERROR +#define PFS_ERR_INVALID 5 /* invalid parameter or file not exist*/ +#define PFS_ERR_BAD_DATA 6 /* the data read from pack are bad*/ +#define PFS_DATA_FULL 7 /* no free pages on ram pack */ +#define PFS_DIR_FULL 8 /* no free directories on ram pack*/ +#define PFS_ERR_EXIST 9 /* file exists */ +#define PFS_ERR_ID_FATAL 10 /* dead ram pack */ +#define PFS_ERR_DEVICE 11 /* wrong device type*/ + +/* definition for EEPROM */ + +#define EEPROM_MAXBLOCKS 64 +#define EEP16K_MAXBLOCKS 256 +#define EEPROM_BLOCK_SIZE 8 + +/* + * PI/EPI + */ +#define PI_DOMAIN1 0 +#define PI_DOMAIN2 1 + +/* + * Profiler constants + */ +#define PROF_MIN_INTERVAL 50 /* microseconds */ + +/* + * Boot addresses + */ +#define BOOT_ADDRESS_ULTRA 0x80000400 +#define BOOT_ADDRESS_COSIM 0x80002000 +#define BOOT_ADDRESS_EMU 0x20010000 +#define BOOT_ADDRESS_INDY 0x88100000 + +/* + * Size of buffer the retains contents after NMI + */ +#define OS_APP_NMI_BUFSIZE 64 + +#if defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) + +/************************************************************************** + * + * Macro definitions + * + */ + +/* PARTNER-N64 */ +#ifdef PTN64 +#define osReadHost osReadHost_pt +#define osWriteHost osWriteHost_pt +#endif + +/* Get count of valid messages in queue */ +#define MQ_GET_COUNT(mq) ((mq)->validCount) + +/* Figure out if message queue is empty or full */ +#define MQ_IS_EMPTY(mq) (MQ_GET_COUNT(mq) == 0) +#define MQ_IS_FULL(mq) (MQ_GET_COUNT(mq) >= (mq)->msgCount) + +/* + * CPU counter increments at 3/4 of bus clock rate: + * + * Bus Clock Proc Clock Counter (1/2 Proc Clock) + * --------- ---------- ------------------------ + * 62.5 Mhz 93.75 Mhz 46.875 Mhz + */ +extern u64 osClockRate; + +#define OS_CLOCK_RATE 62500000LL +#define OS_CPU_COUNTER (OS_CLOCK_RATE*3/4) +#define OS_NSEC_TO_CYCLES(n) (((u64)(n)*(OS_CPU_COUNTER/15625000LL))/(1000000000LL/15625000LL)) +#define OS_USEC_TO_CYCLES(n) (((u64)(n)*(OS_CPU_COUNTER/15625LL))/(1000000LL/15625LL)) +#define OS_CYCLES_TO_NSEC(c) (((u64)(c)*(1000000000LL/15625000LL))/(OS_CPU_COUNTER/15625000LL)) +#define OS_CYCLES_TO_USEC(c) (((u64)(c)*(1000000LL/15625LL))/(OS_CPU_COUNTER/15625LL)) + +/************************************************************************** + * + * Extern variables + * + */ +extern OSViMode osViModeTable[]; /* Global VI mode table */ + +extern OSViMode osViModeNtscLpn1; /* Individual VI NTSC modes */ +extern OSViMode osViModeNtscLpf1; +extern OSViMode osViModeNtscLan1; +extern OSViMode osViModeNtscLaf1; +extern OSViMode osViModeNtscLpn2; +extern OSViMode osViModeNtscLpf2; +extern OSViMode osViModeNtscLan2; +extern OSViMode osViModeNtscLaf2; +extern OSViMode osViModeNtscHpn1; +extern OSViMode osViModeNtscHpf1; +extern OSViMode osViModeNtscHan1; +extern OSViMode osViModeNtscHaf1; +extern OSViMode osViModeNtscHpn2; +extern OSViMode osViModeNtscHpf2; + +extern OSViMode osViModePalLpn1; /* Individual VI PAL modes */ +extern OSViMode osViModePalLpf1; +extern OSViMode osViModePalLan1; +extern OSViMode osViModePalLaf1; +extern OSViMode osViModePalLpn2; +extern OSViMode osViModePalLpf2; +extern OSViMode osViModePalLan2; +extern OSViMode osViModePalLaf2; +extern OSViMode osViModePalHpn1; +extern OSViMode osViModePalHpf1; +extern OSViMode osViModePalHan1; +extern OSViMode osViModePalHaf1; +extern OSViMode osViModePalHpn2; +extern OSViMode osViModePalHpf2; + +extern OSViMode osViModeMpalLpn1; /* Individual VI MPAL modes */ +extern OSViMode osViModeMpalLpf1; +extern OSViMode osViModeMpalLan1; +extern OSViMode osViModeMpalLaf1; +extern OSViMode osViModeMpalLpn2; +extern OSViMode osViModeMpalLpf2; +extern OSViMode osViModeMpalLan2; +extern OSViMode osViModeMpalLaf2; +extern OSViMode osViModeMpalHpn1; +extern OSViMode osViModeMpalHpf1; +extern OSViMode osViModeMpalHan1; +extern OSViMode osViModeMpalHaf1; +extern OSViMode osViModeMpalHpn2; +extern OSViMode osViModeMpalHpf2; + +extern s32 osRomType; /* Bulk or cartridge ROM. 0=cartridge 1=bulk */ +extern u32 osRomBase; /* Rom base address of the game image */ +extern u32 osTvType; /* 0 = PAL, 1 = NTSC, 2 = MPAL */ +extern u32 osResetType; /* 0 = cold reset, 1 = NMI */ +extern s32 osCicId; +extern s32 osVersion; +extern u32 osMemSize; /* Memory Size */ +extern s32 osAppNMIBuffer[]; + +extern OSIntMask __OSGlobalIntMask; /* global interrupt mask */ +extern OSPiHandle *__osPiTable; /* The head of OSPiHandle link list */ +extern OSPiHandle *__osDiskHandle; /* For exceptasm to get disk info*/ + + + +/************************************************************************** + * + * Function prototypes + * + */ + +/* Thread operations */ + +extern void osCreateThread(OSThread *, OSId, void (*)(void *), + void *, void *, OSPri); +extern void osDestroyThread(OSThread *); +extern void osYieldThread(void); +extern void osStartThread(OSThread *); +extern void osStopThread(OSThread *); +extern OSId osGetThreadId(OSThread *); +extern void osSetThreadPri(OSThread *, OSPri); +extern OSPri osGetThreadPri(OSThread *); + +/* Message operations */ + +extern void osCreateMesgQueue(OSMesgQueue *, OSMesg *, s32); +extern s32 osSendMesg(OSMesgQueue *, OSMesg, s32); +extern s32 osJamMesg(OSMesgQueue *, OSMesg, s32); +extern s32 osRecvMesg(OSMesgQueue *, OSMesg *, s32); + +/* Event operations */ + +extern void osSetEventMesg(OSEvent, OSMesgQueue *, OSMesg); + +/* Interrupt operations */ + +extern OSIntMask osGetIntMask(void); +extern OSIntMask osSetIntMask(OSIntMask); + +/* RDB port operations */ + +extern void osInitRdb(u8 *sendBuf, u32 sendSize); + +/* Cache operations and macros */ + +extern void osInvalDCache(void *, size_t); +extern void osInvalICache(void *, size_t); +extern void osWritebackDCache(void *, size_t); +extern void osWritebackDCacheAll(void); + +#define OS_DCACHE_ROUNDUP_ADDR(x) (void *)(((((u32)(x)+0xf)/0x10)*0x10)) +#define OS_DCACHE_ROUNDUP_SIZE(x) (u32)(((((u32)(x)+0xf)/0x10)*0x10)) + +/* TLB management routines */ + +extern void osMapTLB(s32, OSPageMask, void *, u32, u32, s32); +extern void osMapTLBRdb(void); +extern void osUnmapTLB(s32); +extern void osUnmapTLBAll(void); +extern void osSetTLBASID(s32); + +/* Address translation routines and macros */ + +extern u32 osVirtualToPhysical(void *); +extern void * osPhysicalToVirtual(u32); + +#define OS_K0_TO_PHYSICAL(x) (u32)(((char *)(x)-0x80000000)) +#define OS_K1_TO_PHYSICAL(x) (u32)(((char *)(x)-0xa0000000)) + +#define OS_PHYSICAL_TO_K0(x) (void *)(((u32)(x)+0x80000000)) +#define OS_PHYSICAL_TO_K1(x) (void *)(((u32)(x)+0xa0000000)) + +/* I/O operations */ + +/* Audio interface (Ai) */ +extern u32 osAiGetStatus(void); +extern u32 osAiGetLength(void); +extern s32 osAiSetFrequency(u32); +extern s32 osAiSetNextBuffer(void *, u32); + +/* Display processor interface (Dp) */ +extern u32 osDpGetStatus(void); +extern void osDpSetStatus(u32); +extern void osDpGetCounters(u32 *); +extern s32 osDpSetNextBuffer(void *, u64); + +/* Peripheral interface (Pi) */ +extern u32 osPiGetStatus(void); +extern s32 osPiGetDeviceType(void); +extern s32 osPiRawWriteIo(u32, u32); +extern s32 osPiRawReadIo(u32, u32 *); +extern s32 osPiRawStartDma(s32, u32, void *, u32); +extern s32 osPiWriteIo(u32, u32); +extern s32 osPiReadIo(u32, u32 *); +extern s32 osPiStartDma(OSIoMesg *, s32, s32, u32, void *, u32, + OSMesgQueue *); +extern void osCreatePiManager(OSPri, OSMesgQueue *, OSMesg *, s32); + +/* Video interface (Vi) */ +extern u32 osViGetStatus(void); +extern u32 osViGetCurrentMode(void); +extern u32 osViGetCurrentLine(void); +extern u32 osViGetCurrentField(void); +extern void *osViGetCurrentFramebuffer(void); +extern void *osViGetNextFramebuffer(void); +extern void osViSetXScale(f32); +extern void osViSetYScale(f32); +extern void osViSetSpecialFeatures(u32); +extern void osViSetMode(OSViMode *); +extern void osViSetEvent(OSMesgQueue *, OSMesg, u32); +extern void osViSwapBuffer(void *); +extern void osViBlack(u8); +extern void osViFade(u8, u16); +extern void osViRepeatLine(u8); +extern void osCreateViManager(OSPri); + +/* Timer interface */ + +extern OSTime osGetTime(void); +extern void osSetTime(OSTime); +extern u32 osSetTimer(OSTimer *, OSTime, OSTime, + OSMesgQueue *, OSMesg); +extern int osStopTimer(OSTimer *); + +/* Controller interface */ + +extern s32 osContInit(OSMesgQueue *, u8 *, OSContStatus *); +extern s32 osContReset(OSMesgQueue *, OSContStatus *); +extern s32 osContStartQuery(OSMesgQueue *); +extern s32 osContStartReadData(OSMesgQueue *); +#ifndef _HW_VERSION_1 +extern s32 osContSetCh(u8); +#endif +extern void osContGetQuery(OSContStatus *); +extern void osContGetReadData(OSContPad *); + +/* file system interface */ + +extern s32 osPfsInitPak(OSMesgQueue *, OSPfs *, int); +extern s32 osPfsRepairId(OSPfs *); +extern s32 osPfsInit(OSMesgQueue *, OSPfs *, int); +extern s32 osPfsReFormat(OSPfs *, OSMesgQueue *, int); +extern s32 osPfsChecker(OSPfs *); +extern s32 osPfsAllocateFile(OSPfs *, u16, u32, u8 *, u8 *, int, s32 *); +extern s32 osPfsFindFile(OSPfs *, u16, u32, u8 *, u8 *, s32 *); +extern s32 osPfsDeleteFile(OSPfs *, u16, u32, u8 *, u8 *); +extern s32 osPfsReadWriteFile(OSPfs *, s32, u8, int, int, u8 *); +extern s32 osPfsFileState(OSPfs *, s32, OSPfsState *); +extern s32 osPfsGetLabel(OSPfs *, u8 *, int *); +extern s32 osPfsSetLabel(OSPfs *, u8 *); +extern s32 osPfsIsPlug(OSMesgQueue *, u8 *); +extern s32 osPfsFreeBlocks(OSPfs *, s32 *); +extern s32 osPfsNumFiles(OSPfs *, s32 *, s32 *); + +/* EEPROM interface */ + +extern s32 osEepromProbe(OSMesgQueue *); +extern s32 osEepromRead(OSMesgQueue *, u8, u8 *); +extern s32 osEepromWrite(OSMesgQueue *, u8, u8 *); +extern s32 osEepromLongRead(OSMesgQueue *, u8, u8 *, int); +extern s32 osEepromLongWrite(OSMesgQueue *, u8, u8 *, int); + +/* MOTOR interface */ + +extern s32 osMotorInit(OSMesgQueue *, OSPfs *, int); +extern s32 osMotorStop(OSPfs *); +extern s32 osMotorStart(OSPfs *); + +/* Enhanced PI interface */ + +extern OSPiHandle *osCartRomInit(void); +extern OSPiHandle *osLeoDiskInit(void); +extern OSPiHandle *osDriveRomInit(void); + +extern s32 osEPiDeviceType(OSPiHandle *, OSPiInfo *); +extern s32 osEPiRawWriteIo(OSPiHandle *, u32 , u32); +extern s32 osEPiRawReadIo(OSPiHandle *, u32 , u32 *); +extern s32 osEPiRawStartDma(OSPiHandle *, s32 , u32 , void *, u32 ); +extern s32 osEPiWriteIo(OSPiHandle *, u32 , u32 ); +extern s32 osEPiReadIo(OSPiHandle *, u32 , u32 *); +extern s32 osEPiStartDma(OSPiHandle *, OSIoMesg *, s32); +extern s32 osEPiLinkHandle(OSPiHandle *); + +/* Profiler Interface */ + +extern void osProfileInit(OSProf *, u32 profcnt); +extern void osProfileStart(u32); +extern void osProfileFlush(void); +extern void osProfileStop(void); + +/* Game <> Host data transfer functions */ + +extern s32 osTestHost(void); +extern void osReadHost(void *, u32); +extern void osWriteHost(void *, u32); +extern void osAckRamromRead(void); +extern void osAckRamromWrite(void); + + +/* byte string operations */ + +extern void bcopy(const void *, void *, size_t); +extern int bcmp(const void *, const void *, int); +extern void bzero(void *, size_t); + +/* Miscellaneous operations */ + +extern void osInitialize(void); +extern u32 osGetCount(void); +extern void osExit(void); +extern u32 osGetMemSize(void); + +/* Printf */ + +extern int sprintf(char *s, const char *fmt, ...); +extern void osSyncPrintf(const char *fmt, ...); +extern void osAsyncPrintf(const char *fmt, ...); +extern int osSyncGetChars(char *buf); +extern int osAsyncGetChars(char *buf); + +#endif /* defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) */ + +#ifdef _LANGUAGE_C_PLUS_PLUS +} +#endif + +#endif /* !_OS_H */ diff --git a/src/decomp/include/PR/os_ai.h b/src/decomp/include/PR/os_ai.h new file mode 100644 index 0000000..b9df406 --- /dev/null +++ b/src/decomp/include/PR/os_ai.h @@ -0,0 +1,92 @@ + +/*==================================================================== + * os_ai.h + * + * Copyright 1995, Silicon Graphics, Inc. + * All Rights Reserved. + * + * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, + * Inc.; the contents of this file may not be disclosed to third + * parties, copied or duplicated in any form, in whole or in part, + * without the prior written permission of Silicon Graphics, Inc. + * + * RESTRICTED RIGHTS LEGEND: + * Use, duplication or disclosure by the Government is subject to + * restrictions as set forth in subdivision (c)(1)(ii) of the Rights + * in Technical Data and Computer Software clause at DFARS + * 252.227-7013, and/or in similar or successor clauses in the FAR, + * DOD or NASA FAR Supplement. Unpublished - rights reserved under the + * Copyright Laws of the United States. + *====================================================================*/ + +/*---------------------------------------------------------------------* + Copyright (C) 1998 Nintendo. (Originated by SGI) + + $RCSfile: os_ai.h,v $ + $Revision: 1.1 $ + $Date: 1998/10/09 08:01:04 $ + *---------------------------------------------------------------------*/ + +#ifndef _OS_AI_H_ +#define _OS_AI_H_ + +#ifdef _LANGUAGE_C_PLUS_PLUS +extern "C" { +#endif + +#include "ultratypes.h" + +#if defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) + +/************************************************************************** + * + * Type definitions + * + */ + + +#endif /* defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) */ + +/************************************************************************** + * + * Global definitions + * + */ + + +#if defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) + +/************************************************************************** + * + * Macro definitions + * + */ + + +/************************************************************************** + * + * Extern variables + * + */ + + +/************************************************************************** + * + * Function prototypes + * + */ + +/* Audio interface (Ai) */ +extern u32 osAiGetStatus(void); +extern u32 osAiGetLength(void); +extern s32 osAiSetFrequency(u32); +extern s32 osAiSetNextBuffer(void *, u32); + + +#endif /* defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) */ + +#ifdef _LANGUAGE_C_PLUS_PLUS +} +#endif + +#endif /* !_OS_AI_H_ */ diff --git a/src/decomp/include/PR/os_cache.h b/src/decomp/include/PR/os_cache.h new file mode 100644 index 0000000..e313840 --- /dev/null +++ b/src/decomp/include/PR/os_cache.h @@ -0,0 +1,96 @@ + +/*==================================================================== + * os_cache.h + * + * Copyright 1995, Silicon Graphics, Inc. + * All Rights Reserved. + * + * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, + * Inc.; the contents of this file may not be disclosed to third + * parties, copied or duplicated in any form, in whole or in part, + * without the prior written permission of Silicon Graphics, Inc. + * + * RESTRICTED RIGHTS LEGEND: + * Use, duplication or disclosure by the Government is subject to + * restrictions as set forth in subdivision (c)(1)(ii) of the Rights + * in Technical Data and Computer Software clause at DFARS + * 252.227-7013, and/or in similar or successor clauses in the FAR, + * DOD or NASA FAR Supplement. Unpublished - rights reserved under the + * Copyright Laws of the United States. + *====================================================================*/ + +/*---------------------------------------------------------------------* + Copyright (C) 1998 Nintendo. (Originated by SGI) + + $RCSfile: os_cache.h,v $ + $Revision: 1.1 $ + $Date: 1998/10/09 08:01:04 $ + *---------------------------------------------------------------------*/ + +#ifndef _OS_CACHE_H_ +#define _OS_CACHE_H_ + +#ifdef _LANGUAGE_C_PLUS_PLUS +extern "C" { +#endif + +#include "ultratypes.h" + +#if defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) + +/************************************************************************** + * + * Type definitions + * + */ + + +#endif /* defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) */ + +/************************************************************************** + * + * Global definitions + * + */ + + +#if defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) + +/************************************************************************** + * + * Macro definitions + * + */ + +#define OS_DCACHE_ROUNDUP_ADDR(x) (void *)(((((u32)(x)+0xf)/0x10)*0x10)) +#define OS_DCACHE_ROUNDUP_SIZE(x) (u32)(((((u32)(x)+0xf)/0x10)*0x10)) + + +/************************************************************************** + * + * Extern variables + * + */ + + +/************************************************************************** + * + * Function prototypes + * + */ + +/* Cache operations and macros */ + +extern void osInvalDCache(void *, size_t); +extern void osInvalICache(void *, size_t); +extern void osWritebackDCache(void *, size_t); +extern void osWritebackDCacheAll(void); + + +#endif /* defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) */ + +#ifdef _LANGUAGE_C_PLUS_PLUS +} +#endif + +#endif /* !_OS_CACHE_H_ */ diff --git a/src/decomp/include/PR/os_eeprom.h b/src/decomp/include/PR/os_eeprom.h new file mode 100644 index 0000000..2a03fb5 --- /dev/null +++ b/src/decomp/include/PR/os_eeprom.h @@ -0,0 +1,107 @@ + +/*==================================================================== + * os_eeprom.h + * + * Copyright 1995, Silicon Graphics, Inc. + * All Rights Reserved. + * + * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, + * Inc.; the contents of this file may not be disclosed to third + * parties, copied or duplicated in any form, in whole or in part, + * without the prior written permission of Silicon Graphics, Inc. + * + * RESTRICTED RIGHTS LEGEND: + * Use, duplication or disclosure by the Government is subject to + * restrictions as set forth in subdivision (c)(1)(ii) of the Rights + * in Technical Data and Computer Software clause at DFARS + * 252.227-7013, and/or in similar or successor clauses in the FAR, + * DOD or NASA FAR Supplement. Unpublished - rights reserved under the + * Copyright Laws of the United States. + *====================================================================*/ + +/*---------------------------------------------------------------------* + Copyright (C) 1998 Nintendo. (Originated by SGI) + + $RCSfile: os_eeprom.h,v $ + $Revision: 1.1 $ + $Date: 1998/10/09 08:01:06 $ + *---------------------------------------------------------------------*/ + +#ifndef _OS_EEPROM_H_ +#define _OS_EEPROM_H_ + +#ifdef _LANGUAGE_C_PLUS_PLUS +extern "C" { +#endif + +#include "ultratypes.h" +#include "os_message.h" + + +#if defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) + +/************************************************************************** + * + * Type definitions + * + */ + + +#endif /* defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) */ + +/************************************************************************** + * + * Global definitions + * + */ + +/* EEPROM TYPE */ + +#define EEPROM_TYPE_4K 0x01 +#define EEPROM_TYPE_16K 0x02 + +/* definition for EEPROM */ + +#define EEPROM_MAXBLOCKS 64 +#define EEP16K_MAXBLOCKS 256 +#define EEPROM_BLOCK_SIZE 8 + + +#if defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) + +/************************************************************************** + * + * Macro definitions + * + */ + + +/************************************************************************** + * + * Extern variables + * + */ + + +/************************************************************************** + * + * Function prototypes + * + */ + +/* EEPROM interface */ + +extern s32 osEepromProbe(OSMesgQueue *); +extern s32 osEepromRead(OSMesgQueue *, u8, u8 *); +extern s32 osEepromWrite(OSMesgQueue *, u8, u8 *); +extern s32 osEepromLongRead(OSMesgQueue *, u8, u8 *, int); +extern s32 osEepromLongWrite(OSMesgQueue *, u8, u8 *, int); + + +#endif /* defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) */ + +#ifdef _LANGUAGE_C_PLUS_PLUS +} +#endif + +#endif /* !_OS_EEPROM_H_ */ diff --git a/src/decomp/include/PR/os_exception.h b/src/decomp/include/PR/os_exception.h new file mode 100644 index 0000000..2750769 --- /dev/null +++ b/src/decomp/include/PR/os_exception.h @@ -0,0 +1,119 @@ + +/*==================================================================== + * os_exception.h + * + * Copyright 1995, Silicon Graphics, Inc. + * All Rights Reserved. + * + * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, + * Inc.; the contents of this file may not be disclosed to third + * parties, copied or duplicated in any form, in whole or in part, + * without the prior written permission of Silicon Graphics, Inc. + * + * RESTRICTED RIGHTS LEGEND: + * Use, duplication or disclosure by the Government is subject to + * restrictions as set forth in subdivision (c)(1)(ii) of the Rights + * in Technical Data and Computer Software clause at DFARS + * 252.227-7013, and/or in similar or successor clauses in the FAR, + * DOD or NASA FAR Supplement. Unpublished - rights reserved under the + * Copyright Laws of the United States. + *====================================================================*/ + +/*---------------------------------------------------------------------* + Copyright (C) 1998 Nintendo. (Originated by SGI) + + $RCSfile: os_exception.h,v $ + $Revision: 1.1 $ + $Date: 1998/10/09 08:01:07 $ + *---------------------------------------------------------------------*/ + +#ifndef _OS_EXCEPTION_H_ +#define _OS_EXCEPTION_H_ + +#ifdef _LANGUAGE_C_PLUS_PLUS +extern "C" { +#endif + +#include "ultratypes.h" + +#if defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) + +/************************************************************************** + * + * Type definitions + * + */ + +typedef u32 OSIntMask; +typedef u32 OSHWIntr; + +#endif /* defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) */ + +/************************************************************************** + * + * Global definitions + * + */ + +/* Flags for debugging purpose */ + +#define OS_FLAG_CPU_BREAK 1 /* Break exception has occurred */ +#define OS_FLAG_FAULT 2 /* CPU fault has occurred */ + +/* Interrupt masks */ + +#define OS_IM_NONE 0x00000001 +#define OS_IM_SW1 0x00000501 +#define OS_IM_SW2 0x00000601 +#define OS_IM_CART 0x00000c01 +#define OS_IM_PRENMI 0x00001401 +#define OS_IM_RDBWRITE 0x00002401 +#define OS_IM_RDBREAD 0x00004401 +#define OS_IM_COUNTER 0x00008401 +#define OS_IM_CPU 0x0000ff01 +#define OS_IM_SP 0x00010401 +#define OS_IM_SI 0x00020401 +#define OS_IM_AI 0x00040401 +#define OS_IM_VI 0x00080401 +#define OS_IM_PI 0x00100401 +#define OS_IM_DP 0x00200401 +#define OS_IM_ALL 0x003fff01 +#define RCP_IMASK 0x003f0000 +#define RCP_IMASKSHIFT 16 + + +#if defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) + +/************************************************************************** + * + * Macro definitions + * + */ + + +/************************************************************************** + * + * Extern variables + * + */ + + +/************************************************************************** + * + * Function prototypes + * + */ + +/* Interrupt operations */ + +extern OSIntMask osGetIntMask(void); +extern OSIntMask osSetIntMask(OSIntMask); + + +#endif /* defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) */ + +#ifdef _LANGUAGE_C_PLUS_PLUS +} +#endif + +#endif /* !_OS_EXCEPTION_H_ */ diff --git a/src/decomp/include/PR/os_internal.h b/src/decomp/include/PR/os_internal.h new file mode 100644 index 0000000..f07f5b7 --- /dev/null +++ b/src/decomp/include/PR/os_internal.h @@ -0,0 +1,19 @@ +#ifndef _ULTRA64_OS_INTERNAL_H_ +#define _ULTRA64_OS_INTERNAL_H_ +#include "os_message.h" + +/* Internal functions used by the operating system */ +/* Do not include this header in application code */ + +/* Variables */ + +//extern u64 osClockRate; + +/* Functions */ + +/*u32 __osProbeTLB(void *); +u32 __osDisableInt(void); +void __osRestoreInt(u32);*/ +OSThread *__osGetCurrFaultedThread(void); + +#endif diff --git a/src/decomp/include/PR/os_libc.h b/src/decomp/include/PR/os_libc.h new file mode 100644 index 0000000..94111c0 --- /dev/null +++ b/src/decomp/include/PR/os_libc.h @@ -0,0 +1,10 @@ +#ifndef _OS_LIBC_H_ +#define _OS_LIBC_H_ + +#include "ultratypes.h" + +// Old deprecated functions from strings.h, replaced by memcpy/memset. +extern void bcopy(const void *, void *, size_t); +extern void bzero(void *, size_t); + +#endif /* !_OS_LIBC_H_ */ diff --git a/src/decomp/include/PR/os_message.h b/src/decomp/include/PR/os_message.h new file mode 100644 index 0000000..51396c0 --- /dev/null +++ b/src/decomp/include/PR/os_message.h @@ -0,0 +1,164 @@ + +/*==================================================================== + * os_message.h + * + * Copyright 1995, Silicon Graphics, Inc. + * All Rights Reserved. + * + * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, + * Inc.; the contents of this file may not be disclosed to third + * parties, copied or duplicated in any form, in whole or in part, + * without the prior written permission of Silicon Graphics, Inc. + * + * RESTRICTED RIGHTS LEGEND: + * Use, duplication or disclosure by the Government is subject to + * restrictions as set forth in subdivision (c)(1)(ii) of the Rights + * in Technical Data and Computer Software clause at DFARS + * 252.227-7013, and/or in similar or successor clauses in the FAR, + * DOD or NASA FAR Supplement. Unpublished - rights reserved under the + * Copyright Laws of the United States. + *====================================================================*/ + +/*---------------------------------------------------------------------* + Copyright (C) 1998 Nintendo. (Originated by SGI) + + $RCSfile: os_message.h,v $ + $Revision: 1.1 $ + $Date: 1998/10/09 08:01:15 $ + *---------------------------------------------------------------------*/ + +#ifndef _OS_MESSAGE_H_ +#define _OS_MESSAGE_H_ + +#ifdef _LANGUAGE_C_PLUS_PLUS +extern "C" { +#endif + +#include "ultratypes.h" +#include "os_thread.h" + +#if defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) + +/************************************************************************** + * + * Type definitions + * + */ + +typedef u32 OSEvent; + +/* + * Structure for message + */ +typedef void * OSMesg; + +/* + * Structure for message queue + */ +typedef struct OSMesgQueue_s { + OSThread *mtqueue; /* Queue to store threads blocked + on empty mailboxes (receive) */ + OSThread *fullqueue; /* Queue to store threads blocked + on full mailboxes (send) */ + s32 validCount; /* Contains number of valid message */ + s32 first; /* Points to first valid message */ + s32 msgCount; /* Contains total # of messages */ + OSMesg *msg; /* Points to message buffer array */ +} OSMesgQueue; + + +#endif /* defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) */ + +/************************************************************************** + * + * Global definitions + * + */ + +/* Events */ +#ifdef _FINALROM +#define OS_NUM_EVENTS 15 +#else +#define OS_NUM_EVENTS 23 +#endif + +#define OS_EVENT_SW1 0 /* CPU SW1 interrupt */ +#define OS_EVENT_SW2 1 /* CPU SW2 interrupt */ +#define OS_EVENT_CART 2 /* Cartridge interrupt: used by rmon */ +#define OS_EVENT_COUNTER 3 /* Counter int: used by VI/Timer Mgr */ +#define OS_EVENT_SP 4 /* SP task done interrupt */ +#define OS_EVENT_SI 5 /* SI (controller) interrupt */ +#define OS_EVENT_AI 6 /* AI interrupt */ +#define OS_EVENT_VI 7 /* VI interrupt: used by VI/Timer Mgr */ +#define OS_EVENT_PI 8 /* PI interrupt: used by PI Manager */ +#define OS_EVENT_DP 9 /* DP full sync interrupt */ +#define OS_EVENT_CPU_BREAK 10 /* CPU breakpoint: used by rmon */ +#define OS_EVENT_SP_BREAK 11 /* SP breakpoint: used by rmon */ +#define OS_EVENT_FAULT 12 /* CPU fault event: used by rmon */ +#define OS_EVENT_THREADSTATUS 13 /* CPU thread status: used by rmon */ +#define OS_EVENT_PRENMI 14 /* Pre NMI interrupt */ +#ifndef _FINALROM +#define OS_EVENT_RDB_READ_DONE 15 /* RDB read ok event: used by rmon */ +#define OS_EVENT_RDB_LOG_DONE 16 /* read of log data complete */ +#define OS_EVENT_RDB_DATA_DONE 17 /* read of hostio data complete */ +#define OS_EVENT_RDB_REQ_RAMROM 18 /* host needs ramrom access */ +#define OS_EVENT_RDB_FREE_RAMROM 19 /* host is done with ramrom access */ +#define OS_EVENT_RDB_DBG_DONE 20 +#define OS_EVENT_RDB_FLUSH_PROF 21 +#define OS_EVENT_RDB_ACK_PROF 22 +#endif + +/* Flags to turn blocking on/off when sending/receiving message */ + +#define OS_MESG_NOBLOCK 0 +#define OS_MESG_BLOCK 1 + + +#if defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) + +/************************************************************************** + * + * Macro definitions + * + */ + +/* Get count of valid messages in queue */ +#define MQ_GET_COUNT(mq) ((mq)->validCount) + +/* Figure out if message queue is empty or full */ +#define MQ_IS_EMPTY(mq) (MQ_GET_COUNT(mq) == 0) +#define MQ_IS_FULL(mq) (MQ_GET_COUNT(mq) >= (mq)->msgCount) + + +/************************************************************************** + * + * Extern variables + * + */ + + +/************************************************************************** + * + * Function prototypes + * + */ + +/* Message operations */ + +extern void osCreateMesgQueue(OSMesgQueue *, OSMesg *, s32); +extern s32 osSendMesg(OSMesgQueue *, OSMesg, s32); +extern s32 osJamMesg(OSMesgQueue *, OSMesg, s32); +extern s32 osRecvMesg(OSMesgQueue *, OSMesg *, s32); + +/* Event operations */ + +extern void osSetEventMesg(OSEvent, OSMesgQueue *, OSMesg); + + +#endif /* defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) */ + +#ifdef _LANGUAGE_C_PLUS_PLUS +} +#endif + +#endif /* !_OS_MESSAGE_H_ */ diff --git a/src/decomp/include/PR/os_misc.h b/src/decomp/include/PR/os_misc.h new file mode 100644 index 0000000..eb0c793 --- /dev/null +++ b/src/decomp/include/PR/os_misc.h @@ -0,0 +1,11 @@ +#ifndef _ULTRA64_OS_MISC_H_ +#define _ULTRA64_OS_MISC_H_ +#include "ultratypes.h" +/* Miscellaneous OS functions */ + +void osInitialize(void); +u32 osGetCount(void); + +uintptr_t osVirtualToPhysical(void *); + +#endif diff --git a/src/decomp/include/PR/os_pi.h b/src/decomp/include/PR/os_pi.h new file mode 100644 index 0000000..8da37e2 --- /dev/null +++ b/src/decomp/include/PR/os_pi.h @@ -0,0 +1,88 @@ +#ifndef _ULTRA64_PI_H_ +#define _ULTRA64_PI_H_ +#include + +/* Ultra64 Parallel Interface */ + +/* Types */ + +typedef struct { +#if !defined(VERSION_EU) + u32 errStatus; +#endif + void *dramAddr; + void *C2Addr; + u32 sectorSize; + u32 C1ErrNum; + u32 C1ErrSector[4]; +} __OSBlockInfo; + +typedef struct { + u32 cmdType; // 0 + u16 transferMode; // 4 + u16 blockNum; // 6 + s32 sectorNum; // 8 + uintptr_t devAddr; // c +#if defined(VERSION_EU) + u32 errStatus; //error status added moved to blockinfo +#endif + u32 bmCtlShadow; // 10 + u32 seqCtlShadow; // 14 + __OSBlockInfo block[2]; // 18 +} __OSTranxInfo; + +typedef struct OSPiHandle_s { + struct OSPiHandle_s *next; + u8 type; + u8 latency; + u8 pageSize; + u8 relDuration; + u8 pulse; + u8 domain; + u32 baseAddress; + u32 speed; + __OSTranxInfo transferInfo; +} OSPiHandle; + +typedef struct { + u8 type; + uintptr_t address; +} OSPiInfo; + +typedef struct { + u16 type; + u8 pri; + u8 status; + OSMesgQueue *retQueue; +} OSIoMesgHdr; + +typedef struct { + /*0x00*/ OSIoMesgHdr hdr; + /*0x08*/ void *dramAddr; + /*0x0C*/ uintptr_t devAddr; + /*0x10*/ size_t size; +#if defined(VERSION_EU) || defined(VERSION_SH) + OSPiHandle *piHandle; // from the official definition +#endif +} OSIoMesg; + +/* Definitions */ + +#define OS_READ 0 // device -> RDRAM +#define OS_WRITE 1 // device <- RDRAM + +#define OS_MESG_PRI_NORMAL 0 +#define OS_MESG_PRI_HIGH 1 + +/* Functions */ + +s32 osPiStartDma(OSIoMesg *mb, s32 priority, s32 direction, uintptr_t devAddr, void *vAddr, + size_t nbytes, OSMesgQueue *mq); +void osCreatePiManager(OSPri pri, OSMesgQueue *cmdQ, OSMesg *cmdBuf, s32 cmdMsgCnt); +OSMesgQueue *osPiGetCmdQueue(void); +s32 osPiWriteIo(uintptr_t devAddr, u32 data); +s32 osPiReadIo(uintptr_t devAddr, u32 *data); + +s32 osPiRawStartDma(s32 dir, u32 cart_addr, void *dram_addr, size_t size); +s32 osEPiRawStartDma(OSPiHandle *piHandle, s32 dir, u32 cart_addr, void *dram_addr, size_t size); +#endif diff --git a/src/decomp/include/PR/os_rdp.h b/src/decomp/include/PR/os_rdp.h new file mode 100644 index 0000000..76077ba --- /dev/null +++ b/src/decomp/include/PR/os_rdp.h @@ -0,0 +1,92 @@ + +/*==================================================================== + * os_rdp.h + * + * Copyright 1995, Silicon Graphics, Inc. + * All Rights Reserved. + * + * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, + * Inc.; the contents of this file may not be disclosed to third + * parties, copied or duplicated in any form, in whole or in part, + * without the prior written permission of Silicon Graphics, Inc. + * + * RESTRICTED RIGHTS LEGEND: + * Use, duplication or disclosure by the Government is subject to + * restrictions as set forth in subdivision (c)(1)(ii) of the Rights + * in Technical Data and Computer Software clause at DFARS + * 252.227-7013, and/or in similar or successor clauses in the FAR, + * DOD or NASA FAR Supplement. Unpublished - rights reserved under the + * Copyright Laws of the United States. + *====================================================================*/ + +/*---------------------------------------------------------------------* + Copyright (C) 1998 Nintendo. (Originated by SGI) + + $RCSfile: os_rdp.h,v $ + $Revision: 1.1 $ + $Date: 1998/10/09 08:01:16 $ + *---------------------------------------------------------------------*/ + +#ifndef _OS_RDP_H_ +#define _OS_RDP_H_ + +#ifdef _LANGUAGE_C_PLUS_PLUS +extern "C" { +#endif + +#include "ultratypes.h" + +#if defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) + +/************************************************************************** + * + * Type definitions + * + */ + + +#endif /* defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) */ + +/************************************************************************** + * + * Global definitions + * + */ + + +#if defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) + +/************************************************************************** + * + * Macro definitions + * + */ + + +/************************************************************************** + * + * Extern variables + * + */ + + +/************************************************************************** + * + * Function prototypes + * + */ + +/* Display processor interface (Dp) */ +extern u32 osDpGetStatus(void); +extern void osDpSetStatus(u32); +extern void osDpGetCounters(u32 *); +extern s32 osDpSetNextBuffer(void *, u64); + + +#endif /* defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) */ + +#ifdef _LANGUAGE_C_PLUS_PLUS +} +#endif + +#endif /* !_OS_RDP_H_ */ diff --git a/src/decomp/include/PR/os_thread.h b/src/decomp/include/PR/os_thread.h new file mode 100644 index 0000000..76e528b --- /dev/null +++ b/src/decomp/include/PR/os_thread.h @@ -0,0 +1,75 @@ +#ifndef _ULTRA64_THREAD_H_ +#define _ULTRA64_THREAD_H_ +#include "ultratypes.h" +/* Recommended priorities for system threads */ +#define OS_PRIORITY_MAX 255 +#define OS_PRIORITY_VIMGR 254 +#define OS_PRIORITY_RMON 250 +#define OS_PRIORITY_RMONSPIN 200 +#define OS_PRIORITY_PIMGR 150 +#define OS_PRIORITY_SIMGR 140 +#define OS_PRIORITY_APPMAX 127 +#define OS_PRIORITY_IDLE 0 + +#define OS_STATE_STOPPED 1 +#define OS_STATE_RUNNABLE 2 +#define OS_STATE_RUNNING 4 +#define OS_STATE_WAITING 8 + +/* Types */ + +typedef s32 OSPri; +typedef s32 OSId; + +typedef union +{ + struct {f32 f_odd; f32 f_even;} f; +} __OSfp; + +typedef struct +{ + /* registers */ + /*0x20*/ u64 at, v0, v1, a0, a1, a2, a3; + /*0x58*/ u64 t0, t1, t2, t3, t4, t5, t6, t7; + /*0x98*/ u64 s0, s1, s2, s3, s4, s5, s6, s7; + /*0xD8*/ u64 t8, t9, gp, sp, s8, ra; + /*0x108*/ u64 lo, hi; + /*0x118*/ u32 sr, pc, cause, badvaddr, rcp; + /*0x12C*/ u32 fpcsr; + __OSfp fp0, fp2, fp4, fp6, fp8, fp10, fp12, fp14; + __OSfp fp16, fp18, fp20, fp22, fp24, fp26, fp28, fp30; +} __OSThreadContext; + +typedef struct +{ + u32 flag; + u32 count; + u64 time; +} __OSThreadprofile_s; + +typedef struct OSThread_s +{ + /*0x00*/ struct OSThread_s *next; + /*0x04*/ OSPri priority; + /*0x08*/ struct OSThread_s **queue; + /*0x0C*/ struct OSThread_s *tlnext; + /*0x10*/ u16 state; + /*0x12*/ u16 flags; + /*0x14*/ OSId id; + /*0x18*/ int fp; + /*0x1C*/ __OSThreadprofile_s *thprof; + /*0x20*/ __OSThreadContext context; +} OSThread; + + +/* Functions */ + +void osCreateThread(OSThread *thread, OSId id, void (*entry)(void *), + void *arg, void *sp, OSPri pri); +OSId osGetThreadId(OSThread *thread); +OSPri osGetThreadPri(OSThread *thread); +void osSetThreadPri(OSThread *thread, OSPri pri); +void osStartThread(OSThread *thread); +void osStopThread(OSThread *thread); + +#endif diff --git a/src/decomp/include/PR/os_time.h b/src/decomp/include/PR/os_time.h new file mode 100644 index 0000000..8c7c65f --- /dev/null +++ b/src/decomp/include/PR/os_time.h @@ -0,0 +1,27 @@ +#ifndef _ULTRA64_TIME_H_ +#define _ULTRA64_TIME_H_ + +#include "ultratypes.h" +#include "os_message.h" + +/* Types */ + +typedef struct OSTimer_str +{ + struct OSTimer_str *next; + struct OSTimer_str *prev; + u64 interval; + u64 remaining; + OSMesgQueue *mq; + OSMesg *msg; +} OSTimer; + +typedef u64 OSTime; + +/* Functions */ + +OSTime osGetTime(void); +void osSetTime(OSTime time); +u32 osSetTimer(OSTimer *, OSTime, OSTime, OSMesgQueue *, OSMesg); + +#endif diff --git a/src/decomp/include/PR/os_tlb.h b/src/decomp/include/PR/os_tlb.h new file mode 100644 index 0000000..15b485a --- /dev/null +++ b/src/decomp/include/PR/os_tlb.h @@ -0,0 +1,107 @@ + +/*==================================================================== + * os_tlb.h + * + * Copyright 1995, Silicon Graphics, Inc. + * All Rights Reserved. + * + * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, + * Inc.; the contents of this file may not be disclosed to third + * parties, copied or duplicated in any form, in whole or in part, + * without the prior written permission of Silicon Graphics, Inc. + * + * RESTRICTED RIGHTS LEGEND: + * Use, duplication or disclosure by the Government is subject to + * restrictions as set forth in subdivision (c)(1)(ii) of the Rights + * in Technical Data and Computer Software clause at DFARS + * 252.227-7013, and/or in similar or successor clauses in the FAR, + * DOD or NASA FAR Supplement. Unpublished - rights reserved under the + * Copyright Laws of the United States. + *====================================================================*/ + +/*---------------------------------------------------------------------* + Copyright (C) 1998 Nintendo. (Originated by SGI) + + $RCSfile: os_tlb.h,v $ + $Revision: 1.1 $ + $Date: 1998/10/09 08:01:20 $ + *---------------------------------------------------------------------*/ + +#ifndef _OS_TLB_H_ +#define _OS_TLB_H_ + +#ifdef _LANGUAGE_C_PLUS_PLUS +extern "C" { +#endif + +#include "ultratypes.h" + +#if defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) + +/************************************************************************** + * + * Type definitions + * + */ + +typedef u32 OSPageMask; + + +#endif /* defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) */ + +/************************************************************************** + * + * Global definitions + * + */ + +/* + * Page size argument for TLB routines + */ +#define OS_PM_4K 0x0000000 +#define OS_PM_16K 0x0006000 +#define OS_PM_64K 0x001e000 +#define OS_PM_256K 0x007e000 +#define OS_PM_1M 0x01fe000 +#define OS_PM_4M 0x07fe000 +#define OS_PM_16M 0x1ffe000 + + +#if defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) + +/************************************************************************** + * + * Macro definitions + * + */ + + +/************************************************************************** + * + * Extern variables + * + */ + + +/************************************************************************** + * + * Function prototypes + * + */ + +/* TLB management routines */ + +extern void osMapTLB(s32, OSPageMask, void *, u32, u32, s32); +extern void osMapTLBRdb(void); +extern void osUnmapTLB(s32); +extern void osUnmapTLBAll(void); +extern void osSetTLBASID(s32); + + +#endif /* defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) */ + +#ifdef _LANGUAGE_C_PLUS_PLUS +} +#endif + +#endif /* !_OS_TLB_H_ */ diff --git a/src/decomp/include/PR/os_vi.h b/src/decomp/include/PR/os_vi.h new file mode 100644 index 0000000..b487d1d --- /dev/null +++ b/src/decomp/include/PR/os_vi.h @@ -0,0 +1,117 @@ +#ifndef _ULTRA64_VI_H_ +#define _ULTRA64_VI_H_ + +#include "ultratypes.h" +#include "os_message.h" + +/* Ultra64 Video Interface */ + + +/* Special Features */ +#define OS_VI_GAMMA_ON 0x0001 +#define OS_VI_GAMMA_OFF 0x0002 +#define OS_VI_GAMMA_DITHER_ON 0x0004 +#define OS_VI_GAMMA_DITHER_OFF 0x0008 +#define OS_VI_DIVOT_ON 0x0010 +#define OS_VI_DIVOT_OFF 0x0020 +#define OS_VI_DITHER_FILTER_ON 0x0040 +#define OS_VI_DITHER_FILTER_OFF 0x0080 + +#define OS_VI_GAMMA 0x08 +#define OS_VI_GAMMA_DITHER 0x04 +#define OS_VI_DIVOT 0x10 +#define OS_VI_DITHER_FILTER 0x10000 +#define OS_VI_UNK200 0x200 +#define OS_VI_UNK100 0x100 + + +/* Types */ + +typedef struct +{ + u32 ctrl; + u32 width; + u32 burst; + u32 vSync; + u32 hSync; + u32 leap; + u32 hStart; + u32 xScale; + u32 vCurrent; +} OSViCommonRegs; + +typedef struct +{ + u32 origin; + u32 yScale; + u32 vStart; + u32 vBurst; + u32 vIntr; +} OSViFieldRegs; + +typedef struct +{ + u8 type; + OSViCommonRegs comRegs; + OSViFieldRegs fldRegs[2]; +} OSViMode; + +typedef struct +{ + /* 0x00 */ u16 unk00; //some kind of flags. swap buffer sets to 0x10 + /* 0x02 */ u16 retraceCount; + /* 0x04 */ void* buffer; + /* 0x08 */ OSViMode *modep; + /* 0x0c */ u32 features; + /* 0x10 */ OSMesgQueue *mq; + /* 0x14 */ OSMesg *msg; + /* 0x18 */ u32 unk18; + /* 0x1c */ u32 unk1c; + /* 0x20 */ u32 unk20; + /* 0x24 */ f32 unk24; + /* 0x28 */ u16 unk28; + /* 0x2c */ u32 unk2c; +} OSViContext; + +void osCreateViManager(OSPri pri); +void osViSetMode(OSViMode *mode); +void osViSetEvent(OSMesgQueue *mq, OSMesg msg, u32 retraceCount); +void osViBlack(u8 active); +void osViSetSpecialFeatures(u32 func); +void osViSwapBuffer(void *vaddr); + + +#define OS_VI_NTSC_LPN1 0 /* NTSC */ +#define OS_VI_NTSC_LPF1 1 +#define OS_VI_NTSC_LAN1 2 +#define OS_VI_NTSC_LAF1 3 +#define OS_VI_NTSC_LPN2 4 +#define OS_VI_NTSC_LPF2 5 +#define OS_VI_NTSC_LAN2 6 +#define OS_VI_NTSC_LAF2 7 +#define OS_VI_NTSC_HPN1 8 +#define OS_VI_NTSC_HPF1 9 +#define OS_VI_NTSC_HAN1 10 +#define OS_VI_NTSC_HAF1 11 +#define OS_VI_NTSC_HPN2 12 +#define OS_VI_NTSC_HPF2 13 + +#define OS_VI_PAL_LPN1 14 /* PAL */ +#define OS_VI_PAL_LPF1 15 +#define OS_VI_PAL_LAN1 16 +#define OS_VI_PAL_LAF1 17 +#define OS_VI_PAL_LPN2 18 +#define OS_VI_PAL_LPF2 19 +#define OS_VI_PAL_LAN2 20 +#define OS_VI_PAL_LAF2 21 +#define OS_VI_PAL_HPN1 22 +#define OS_VI_PAL_HPF1 23 +#define OS_VI_PAL_HAN1 24 +#define OS_VI_PAL_HAF1 25 +#define OS_VI_PAL_HPN2 26 +#define OS_VI_PAL_HPF2 27 + +extern OSViMode osViModeTable[]; /* Global VI mode table */ + + +#endif diff --git a/src/decomp/include/PR/sptask.h b/src/decomp/include/PR/sptask.h new file mode 100644 index 0000000..ec443ef --- /dev/null +++ b/src/decomp/include/PR/sptask.h @@ -0,0 +1,116 @@ +#ifndef _ULTRA64_SPTASK_H_ +#define _ULTRA64_SPTASK_H_ + +/* Task Types */ +#define M_GFXTASK 1 +#define M_AUDTASK 2 +#define M_VIDTASK 3 +#define M_HVQTASK 6 +#define M_HVQMTASK 7 + +#if (defined(F3DEX_GBI) || defined(F3DLP_GBI) || defined(F3DEX_GBI_2)) +#define OS_YIELD_DATA_SIZE 0xc00 +#else +#define OS_YIELD_DATA_SIZE 0x900 +#endif +#define OS_YIELD_AUDIO_SIZE 0x400 + +/* Flags */ +#define M_TASK_FLAG0 1 +#define M_TASK_FLAG1 2 +#ifdef VERSION_SH +#define M_TASK_FLAG2 4 +#endif + +/* SpStatus */ +#define SPSTATUS_CLEAR_HALT 0x00000001 +#define SPSTATUS_SET_HALT 0x00000002 +#define SPSTATUS_CLEAR_BROKE 0x00000004 +#define SPSTATUS_CLEAR_INTR 0x00000008 +#define SPSTATUS_SET_INTR 0x00000010 +#define SPSTATUS_CLEAR_SSTEP 0x00000020 +#define SPSTATUS_SET_SSTEP 0x00000040 +#define SPSTATUS_CLEAR_INTR_ON_BREAK 0x00000080 +#define SPSTATUS_SET_INTR_ON_BREAK 0x00000100 +#define SPSTATUS_CLEAR_SIGNAL0 0x00000200 +#define SPSTATUS_SET_SIGNAL0 0x00000400 +#define SPSTATUS_CLEAR_SIGNAL1 0x00000800 +#define SPSTATUS_SET_SIGNAL1 0x00001000 +#define SPSTATUS_CLEAR_SIGNAL2 0x00002000 +#define SPSTATUS_SET_SIGNAL2 0x00004000 +#define SPSTATUS_CLEAR_SIGNAL3 0x00008000 +#define SPSTATUS_SET_SIGNAL3 0x00010000 +#define SPSTATUS_CLEAR_SIGNAL4 0x00020000 +#define SPSTATUS_SET_SIGNAL4 0x00040000 +#define SPSTATUS_CLEAR_SIGNAL5 0x00080000 +#define SPSTATUS_SET_SIGNAL5 0x00100000 +#define SPSTATUS_CLEAR_SIGNAL6 0x00200000 +#define SPSTATUS_SET_SIGNAL6 0x00800000 +#define SPSTATUS_CLEAR_SIGNAL7 0x01000000 +#define SPSTATUS_SET_SIGNAL7 0x02000000 + +#define SPSTATUS_HALT 0x0001 +#define SPSTATUS_BROKE 0x0002 +#define SPSTATUS_DMA_BUSY 0x0004 +#define SPSTATUS_DMA_FULL 0x0008 +#define SPSTATUS_IO_FULL 0x0010 +#define SPSTATUS_SINGLE_STEP 0x0020 +#define SPSTATUS_INTERRUPT_ON_BREAK 0x0040 +#define SPSTATUS_SIGNAL0_SET 0x0080 +#define SPSTATUS_SIGNAL1_SET 0x0100 +#define SPSTATUS_SIGNAL2_SET 0x0200 +#define SPSTATUS_SIGNAL3_SET 0x0400 +#define SPSTATUS_SIGNAL4_SET 0x0800 +#define SPSTATUS_SIGNAL5_SET 0x1000 +#define SPSTATUS_SIGNAL6_SET 0x2000 +#define SPSTATUS_SIGNAL7_SET 0x4000 + +/* Types */ +/* Types */ + +typedef struct +{ + /*0x00*/ u32 type; + /*0x04*/ u32 flags; + + /*0x08*/ u64 *ucode_boot; + /*0x0C*/ u32 ucode_boot_size; + + /*0x10*/ u64 *ucode; + /*0x14*/ u32 ucode_size; + + /*0x18*/ u64 *ucode_data; + /*0x1C*/ u32 ucode_data_size; + + /*0x20*/ u64 *dram_stack; + /*0x24*/ u32 dram_stack_size; + + /*0x28*/ u64 *output_buff; + /*0x2C*/ u64 *output_buff_size; + + /*0x30*/ u64 *data_ptr; + /*0x34*/ u32 data_size; + + /*0x38*/ u64 *yield_data_ptr; + /*0x3C*/ u32 yield_data_size; +} OSTask_t; // size = 0x40 + +typedef union { + OSTask_t t; + long long int force_structure_alignment; +} OSTask; + +typedef u32 OSYieldResult; + +/* Functions */ + +#define osSpTaskStart(p) \ + osSpTaskLoad(p); \ + osSpTaskStartGo(p); + +void osSpTaskLoad(OSTask *task); +void osSpTaskStartGo(OSTask *task); +void osSpTaskYield(void); +OSYieldResult osSpTaskYielded(OSTask *task); + +#endif diff --git a/src/decomp/include/PR/ucode.h b/src/decomp/include/PR/ucode.h new file mode 100644 index 0000000..37fd197 --- /dev/null +++ b/src/decomp/include/PR/ucode.h @@ -0,0 +1,23 @@ +#ifndef _ULTRA64_UCODE_H_ +#define _ULTRA64_UCODE_H_ + +#define SP_DRAM_STACK_SIZE8 0x400 +#define SP_UCODE_SIZE 0x1000 +#define SP_UCODE_DATA_SIZE 0x800 + +// standard boot ucode +extern u64 rspF3DBootStart[], rspF3DBootEnd[]; + +// F3D ucode +extern u64 rspF3DStart[], rspF3DEnd[]; + +// F3D ucode data +extern u64 rspF3DDataStart[], rspF3DDataEnd[]; + +// aspMain (audio) ucode +extern u64 rspAspMainStart[], rspAspMainEnd[]; + +// aspMain ucode data +extern u64 rspAspMainDataStart[], rspAspMainDataEnd[]; + +#endif diff --git a/src/decomp/include/audio_defines.h b/src/decomp/include/audio_defines.h index 021d2d8..7517191 100644 --- a/src/decomp/include/audio_defines.h +++ b/src/decomp/include/audio_defines.h @@ -21,21 +21,56 @@ #define SOUNDARGS_SHIFT_SOUNDID 16 #define SOUNDARGS_SHIFT_PRIORITY 8 +/* Sound banks */ +#define SOUND_BANK_ACTION 0 +#define SOUND_BANK_MOVING 1 +#define SOUND_BANK_VOICE 2 +#define SOUND_BANK_GENERAL 3 +#define SOUND_BANK_ENV 4 +#define SOUND_BANK_OBJ 5 +#define SOUND_BANK_AIR 6 +#define SOUND_BANK_MENU 7 +#define SOUND_BANK_GENERAL2 8 +#define SOUND_BANK_OBJ2 9 +#define SOUND_BANK_COUNT 10 + +#define SOUND_BANKS_ALL_BITS 0xffff +#define SOUND_BANKS_ALL ((1 << SOUND_BANK_COUNT) - 1) +#define SOUND_BANKS_FOREGROUND (\ + (1 << SOUND_BANK_ACTION) |\ + (1 << SOUND_BANK_VOICE) |\ + (1 << SOUND_BANK_MENU)) +#define SOUND_BANKS_BACKGROUND (SOUND_BANKS_ALL & ~SOUND_BANKS_FOREGROUND) +#define SOUND_BANKS_DISABLED_DURING_INTRO_CUTSCENE (\ + (1 << SOUND_BANK_ENV) |\ + (1 << SOUND_BANK_OBJ) |\ + (1 << SOUND_BANK_GENERAL2) |\ + (1 << SOUND_BANK_OBJ2)) +#define SOUND_BANKS_DISABLED_AFTER_CREDITS (\ + (1 << SOUND_BANK_ACTION) |\ + (1 << SOUND_BANK_MOVING) |\ + (1 << SOUND_BANK_VOICE) |\ + (1 << SOUND_BANK_GENERAL)) + /* Audio Status */ #define SOUND_STATUS_STOPPED 0 #define SOUND_STATUS_STARTING 1 +#define SOUND_STATUS_WAITING SOUND_STATUS_STARTING #define SOUND_STATUS_PLAYING 2 /* Audio lower bitflags. TODO: Figure out what these mean and use them below. */ #define SOUND_LO_BITFLAG_UNK1 0x10 // fade in? +#define SOUND_LOWER_BACKGROUND_MUSIC SOUND_LO_BITFLAG_UNK1 #define SOUND_NO_ECHO 0x20 // not in JP #define SOUND_LO_BITFLAG_UNK8 0x80 // restart playing on each play_sound call? +#define SOUND_DISCRETE SOUND_LO_BITFLAG_UNK8 /* Audio playback bitflags. */ #define SOUND_NO_VOLUME_LOSS 0x1000000 // No volume loss with distance #define SOUND_VIBRATO 0x2000000 // Randomly alter frequency each audio frame #define SOUND_NO_PRIORITY_LOSS 0x4000000 // Do not prioritize closer sounds #define SOUND_NO_FREQUENCY_LOSS 0x8000000 // Frequency scale does not change with distance +#define SOUND_CONSTANT_FREQUENCY SOUND_NO_FREQUENCY_LOSS // silence #define NO_SOUND 0 @@ -558,4 +593,4 @@ #define SOUND_OBJ2_BOSS_DIALOG_GRUNT SOUND_ARG_LOAD(9, 0, 0x69, 0x40, 8) #define SOUND_OBJ2_MRI_SPINNING SOUND_ARG_LOAD(9, 0, 0x6B, 0x00, 8) -#endif // AUDIO_DEFINES_H +#endif // AUDIO_DEFINES_H \ No newline at end of file diff --git a/src/decomp/include/platform_info.h b/src/decomp/include/platform_info.h index 310aa4d..f8ccbe2 100644 --- a/src/decomp/include/platform_info.h +++ b/src/decomp/include/platform_info.h @@ -10,6 +10,6 @@ #define IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) #endif -#define DOUBLE_SIZE_ON_64_BIT(size) ((size) * (sizeof(void *) / 4)) +#define DOUBLE_SIZE_ON_64_BIT(size) ((size) * 2) #endif // PLATFORM_INFO_H diff --git a/src/decomp/include/types.h b/src/decomp/include/types.h index 51e6ed7..5272aa7 100644 --- a/src/decomp/include/types.h +++ b/src/decomp/include/types.h @@ -4,7 +4,7 @@ // This file contains various data types used in Super Mario 64 that don't yet // have an appropriate header. -// #include +#include "ultra64.h" #include "macros.h" #include "PR/ultratypes.h" @@ -62,19 +62,19 @@ enum SpTaskState { SPTASK_STATE_FINISHED_DP }; -// struct SPTask -// { -// /*0x00*/ OSTask task; -// /*0x40*/ OSMesgQueue *msgqueue; -// /*0x44*/ OSMesg msg; -// /*0x48*/ enum SpTaskState state; -// }; // size = 0x4C, align = 0x8 -// -// struct VblankHandler -// { -// OSMesgQueue *queue; -// OSMesg msg; -// }; +struct SPTask +{ + /*0x00*/ OSTask task; + /*0x40*/ OSMesgQueue *msgqueue; + /*0x44*/ OSMesg msg; + /*0x48*/ enum SpTaskState state; +}; // size = 0x4C, align = 0x8 + +struct VblankHandler +{ + OSMesgQueue *queue; + OSMesg msg; +}; #define ANIM_FLAG_NOLOOP (1 << 0) // 0x01 #define ANIM_FLAG_FORWARD (1 << 1) // 0x02 diff --git a/src/decomp/include/ultra64.h b/src/decomp/include/ultra64.h new file mode 100644 index 0000000..b8d9e4d --- /dev/null +++ b/src/decomp/include/ultra64.h @@ -0,0 +1,31 @@ +#ifndef _ULTRA64_H_ +#define _ULTRA64_H_ + +#include + +#ifndef _LANGUAGE_C +#define _LANGUAGE_C +#endif + +#include "PR/ultratypes.h" +#include "PR/os_exception.h" +#include "PR/os_misc.h" +#include "PR/os_rdp.h" +#include "PR/os_thread.h" +#include "PR/os_time.h" +#include "PR/os_message.h" +#include "PR/os_cont.h" +#include "PR/os_tlb.h" +#include "PR/sptask.h" +#include "PR/ucode.h" +#include "PR/os_cache.h" +#include "PR/os_vi.h" +#include "PR/os_pi.h" +#include "PR/os_internal.h" +#include "PR/os_eeprom.h" +#include "PR/os_libc.h" +#include "PR/os_ai.h" +#include "PR/libaudio.h" +#include "PR/libultra.h" + +#endif diff --git a/src/decomp/pc/alBnkfNew.c b/src/decomp/pc/alBnkfNew.c new file mode 100644 index 0000000..bb04df3 --- /dev/null +++ b/src/decomp/pc/alBnkfNew.c @@ -0,0 +1,93 @@ +#include "libultra_internal.h" +#include "libaudio_internal.h" + +#define PATCH(SRC, BASE, TYPE) //SRC = (TYPE)((uintptr_t) SRC + (uintptr_t) BASE) + +void alSeqFileNew(ALSeqFile *f, u8 *base) { + int i; + for (i = 0; i < f->seqCount; i++) { + PATCH(f->seqArray[i].offset, base, u8 *); + } +} + +static void _bnkfPatchBank(ALInstrument *inst, ALBankFile *f, u8 *table) { + int i; + ALSound *sound; + ALWaveTable *wavetable; + u8 *table2; + + if (inst->flags) { + return; + } + + inst->flags = 1; + + for (i = 0; i < inst->soundCount; i++) { + PATCH(inst->soundArray[i], f, ALSound *); + sound = inst->soundArray[i]; + if (sound->flags) { + continue; + } + + table2 = table; + + sound->flags = 1; + PATCH(sound->envelope, f, ALEnvelope *); + PATCH(sound->keyMap, f, ALKeyMap *); + PATCH(sound->wavetable, f, ALWaveTable *); + wavetable = sound->wavetable; + if (wavetable->flags) { + continue; + } + + wavetable->flags = 1; + PATCH(wavetable->base, table2, u8 *); + if (wavetable->type == 0) { + PATCH(wavetable->waveInfo.adpcmWave.book, f, ALADPCMBook *); + if (wavetable->waveInfo.adpcmWave.loop != NULL) { + PATCH(wavetable->waveInfo.adpcmWave.loop, f, ALADPCMloop *); + } + } else if (wavetable->type == 1) { + if (wavetable->waveInfo.rawWave.loop != NULL) { + PATCH(wavetable->waveInfo.rawWave.loop, f, ALRawLoop *); + } + } + } +} + +// Force adding another jr $ra. Has to be called or it doesn't get put in the +// right place. +static void unused(void) { +} + +void alBnkfNew(ALBankFile *f, u8 *table) { + ALBank *bank; + int i; + int j; + unused(); + if (f->revision != AL_BANK_VERSION) { + return; + } + + for (i = 0; i < f->bankCount; i++) { + PATCH(f->bankArray[i], f, ALBank *); + if (f->bankArray[i] == NULL) { + continue; + } + + bank = f->bankArray[i]; + if (bank->flags == 0) { + bank->flags = 1; + if (bank->percussion != NULL) { + PATCH(bank->percussion, f, ALInstrument *); + _bnkfPatchBank(bank->percussion, f, table); + } + for (j = 0; j < bank->instCount; j++) { + PATCH(bank->instArray[j], f, ALInstrument *); + if (bank->instArray[j] != NULL) { + _bnkfPatchBank(bank->instArray[j], f, table); + } + } + } + } +} diff --git a/src/decomp/pc/libaudio_internal.h b/src/decomp/pc/libaudio_internal.h new file mode 100644 index 0000000..ea63322 --- /dev/null +++ b/src/decomp/pc/libaudio_internal.h @@ -0,0 +1,127 @@ +#ifndef _LIBAUDIO_INTERNAL_H_ +#define _LIBAUDIO_INTERNAL_H_ +#include +#define AL_BANK_VERSION 0x4231 /* 'B1' */ + +typedef u8 ALPan; +typedef s32 ALMicroTime; + +/* Possible wavetable types */ +enum +{ + AL_ADPCM_WAVE = 0, + AL_RAW16_WAVE +}; + +typedef struct +{ + u32 start; + u32 end; + u32 count; +} ALRawLoop; + +typedef struct +{ + u32 start; + u32 end; + u32 count; + ADPCM_STATE state; +} ALADPCMloop; + +typedef struct +{ + s32 order; + s32 npredictors; + s16 book[1]; // variable size, 8-byte aligned +} ALADPCMBook; + +typedef struct +{ + ALMicroTime attackTime; + ALMicroTime decayTime; + ALMicroTime releaseTime; + u8 attackVolume; + u8 decayVolume; +} ALEnvelope; + +typedef struct +{ + u8 velocityMin; + u8 velocityMax; + u8 keyMin; + u8 keyMax; + u8 keyBase; + s8 detune; +} ALKeyMap; + +typedef struct +{ + ALADPCMloop *loop; + ALADPCMBook *book; +} ALADPCMWaveInfo; + +typedef struct +{ + ALRawLoop *loop; +} ALRAWWaveInfo; + +typedef struct ALWaveTable_s +{ + u8 *base; /* ptr to start of wave data */ + s32 len; /* length of data in bytes */ + u8 type; /* compression type */ + u8 flags; /* offset/address flags */ + union { + ALADPCMWaveInfo adpcmWave; + ALRAWWaveInfo rawWave; + } waveInfo; +} ALWaveTable; + +typedef struct ALSound_s +{ + ALEnvelope *envelope; + ALKeyMap *keyMap; + ALWaveTable *wavetable; /* offset to wavetable struct */ + ALPan samplePan; + u8 sampleVolume; + u8 flags; +} ALSound; + +typedef struct +{ + u8 volume; /* overall volume for this instrument */ + ALPan pan; /* 0 = hard left, 127 = hard right */ + u8 priority; /* voice priority for this instrument */ + u8 flags; + u8 tremType; /* the type of tremelo osc. to use */ + u8 tremRate; /* the rate of the tremelo osc. */ + u8 tremDepth; /* the depth of the tremelo osc */ + u8 tremDelay; /* the delay for the tremelo osc */ + u8 vibType; /* the type of tremelo osc. to use */ + u8 vibRate; /* the rate of the tremelo osc. */ + u8 vibDepth; /* the depth of the tremelo osc */ + u8 vibDelay; /* the delay for the tremelo osc */ + s16 bendRange; /* pitch bend range in cents */ + s16 soundCount; /* number of sounds in this array */ + ALSound *soundArray[1]; +} ALInstrument; + +typedef struct ALBank_s +{ + s16 instCount; /* number of programs in this bank */ + u8 flags; + u8 pad; + s32 sampleRate; /* e.g. 44100, 22050, etc... */ + ALInstrument *percussion; /* default percussion for GM */ + ALInstrument *instArray[1]; /* ARRAY of instruments */ +} ALBank; + +typedef struct +{ /* Note: sizeof won't be correct */ + s16 revision; /* format revision of this file */ + s16 bankCount; /* number of banks */ + ALBank *bankArray[1]; /* ARRAY of bank offsets */ +} ALBankFile; + +void alBnkfNew(ALBankFile *f, u8 *table); +#endif diff --git a/src/decomp/pc/libultra_internal.h b/src/decomp/pc/libultra_internal.h new file mode 100644 index 0000000..5639c6a --- /dev/null +++ b/src/decomp/pc/libultra_internal.h @@ -0,0 +1,99 @@ +#ifndef _LIBULTRA_INTERNAL_H_ +#define _LIBULTRA_INTERNAL_H_ +#include + +/* + * This define is needed because the original definitions in __osDequeueThread.c are declared + * seperately instead of part of a single struct, however some code alises over this memory + * assuming a unified structure. To fix this, we declare the full type here and then alias the + * symbol names to the correct members in AVOID_UB. + */ +#ifdef AVOID_UB +typedef struct OSThread_ListHead_s +{ + /*0x00*/ struct OSThread_s *next; + /*0x04*/ OSPri priority; + /*0x08*/ struct OSThread_s *queue; + /*0x0C*/ struct OSThread_s *tlnext; + /*0x10*/ struct OSThread_s *unk10; + /*0x14*/ u32 unk14; +} OSThread_ListHead; + +// Now fix the symbols to the new one. +extern OSThread_ListHead D_80334890_fix; + +#define D_80334890 D_80334890_fix.next +#define D_80334894 D_80334890_fix.priority +#define D_80334898 D_80334890_fix.queue +#define D_8033489C D_80334890_fix.tlnext +#define D_803348A0 D_80334890_fix.unk10 + +// Fix for the EEPROM array. +extern u32 D_80365E00[16]; + +// alias the last array element correctly +#define D_80365E3C D_80365E00[15] +#else +// Original OSThread_ListHead definitions +extern OSThread *D_80334890; +extern u32 D_80334894; +extern OSThread *D_80334898; +extern OSThread *D_8033489C; +extern OSThread *D_803348A0; + +// Original EEPROM definitions +extern u32 D_80365E00[15]; +extern u32 D_80365E3C; +#endif + +typedef struct { + u32 initialized; // probably something like initialized? + OSThread *mgrThread; + OSMesgQueue *cmdQueue; + OSMesgQueue *eventQueue; + OSMesgQueue *accessQueue; + s32 (*dma_func)(s32, u32, void *, size_t); +#if defined(VERSION_EU) || defined(VERSION_SH) + s32 (*edma_func)(OSPiHandle*, s32, u32, void *, size_t); +#else + u64 force_align; +#endif +} OSMgrArgs; + +s32 __osDisableInt(void); +void __osRestoreInt(s32); +void __osEnqueueAndYield(OSThread **); +void __osDequeueThread(OSThread **, OSThread *); +void __osEnqueueThread(OSThread **, OSThread *); +OSThread *__osPopThread(OSThread **); +s32 __osSiRawStartDma(s32, void *); +void __osSiCreateAccessQueue(void); +void __osSiGetAccess(void); +void __osSiRelAccess(void); +u32 __osProbeTLB(void *); +void __osPiCreateAccessQueue(void); +void __osPiGetAccess(void); +void __osSetSR(u32); +u32 __osGetSR(void); +void __osSetFpcCsr(u32); +s32 __osSiRawReadIo(void *, u32 *); +s32 __osSiRawWriteIo(void *, u32); +s32 osPiRawReadIo(u32 a0, u32 *a1); +void __osSpSetStatus(u32); +u32 __osSpGetStatus(void); +s32 __osSpSetPc(void *); +s32 __osSpDeviceBusy(void); +s32 __osSiDeviceBusy(void); +s32 __osSpRawStartDma(u32 dir, void *sp_ptr, void *dram_ptr, size_t size); +void __osViInit(void); +OSViContext *__osViGetCurrentContext(void); +OSViContext *__osViGetCurrentContext2(void); +void __osViSwapContext(void); +void __osSetTimerIntr(u64); +u64 __osInsertTimer(OSTimer *); +void __osSetCompare(u32); +s32 __osAiDeviceBusy(void); +void __osDispatchThread(void); +u32 __osGetCause(void); +s32 __osAtomicDec(u32 *); +#endif diff --git a/src/decomp/pc/mixer.c b/src/decomp/pc/mixer.c new file mode 100644 index 0000000..8c53ad4 --- /dev/null +++ b/src/decomp/pc/mixer.c @@ -0,0 +1,1188 @@ +#include +#include +#include +#include + +#include "mixer.h" +#include "../../debug_print.h" + +#ifdef __SSE4_1__ +#include +#define HAS_SSE41 1 +#define HAS_NEON 0 +#elif __ARM_NEON +#include +#define HAS_SSE41 0 +#define HAS_NEON 1 +#else +#define HAS_SSE41 0 +#define HAS_NEON 0 +#endif + +#pragma GCC optimize ("unroll-loops") + +#if HAS_SSE41 +#define LOADLH(l, h) _mm_castpd_si128(_mm_loadh_pd(_mm_load_sd((const double *)(l)), (const double *)(h))) +#endif + +#define ROUND_UP_64(v) (((v) + 63) & ~63) +#define ROUND_UP_32(v) (((v) + 31) & ~31) +#define ROUND_UP_16(v) (((v) + 15) & ~15) +#define ROUND_UP_8(v) (((v) + 7) & ~7) +#define ROUND_DOWN_16(v) ((v) & ~0xf) + +#ifdef NEW_AUDIO_UCODE +#define BUF_SIZE 2880 +#define BUF_U8(a) (rspa.buf.as_u8 + ((a) - 0x450)) +#define BUF_S16(a) (rspa.buf.as_s16 + ((a) - 0x450) / sizeof(int16_t)) +#else +#define BUF_SIZE 2512 +#define BUF_U8(a) (rspa.buf.as_u8 + (a)) +#define BUF_S16(a) (rspa.buf.as_s16 + (a) / sizeof(int16_t)) +#endif + +static struct { + uint16_t in; + uint16_t out; + uint16_t nbytes; + +#ifdef NEW_AUDIO_UCODE + uint16_t vol[2]; + uint16_t rate[2]; + uint16_t vol_wet; + uint16_t rate_wet; +#else + int16_t vol[2]; + + uint16_t dry_right; + uint16_t wet_left; + uint16_t wet_right; + + int16_t target[2]; + int32_t rate[2]; + + int16_t vol_dry; + int16_t vol_wet; +#endif + + ADPCM_STATE *adpcm_loop_state; + + int16_t adpcm_table[8][2][8]; + +#ifdef NEW_AUDIO_UCODE + uint16_t filter_count; + int16_t filter[8]; +#endif + + union { + int16_t as_s16[BUF_SIZE / sizeof(int16_t)]; + uint8_t as_u8[BUF_SIZE]; + } buf; +} rspa; + +static int16_t resample_table[64][4] = { + {0x0c39, 0x66ad, 0x0d46, 0xffdf}, {0x0b39, 0x6696, 0x0e5f, 0xffd8}, + {0x0a44, 0x6669, 0x0f83, 0xffd0}, {0x095a, 0x6626, 0x10b4, 0xffc8}, + {0x087d, 0x65cd, 0x11f0, 0xffbf}, {0x07ab, 0x655e, 0x1338, 0xffb6}, + {0x06e4, 0x64d9, 0x148c, 0xffac}, {0x0628, 0x643f, 0x15eb, 0xffa1}, + {0x0577, 0x638f, 0x1756, 0xff96}, {0x04d1, 0x62cb, 0x18cb, 0xff8a}, + {0x0435, 0x61f3, 0x1a4c, 0xff7e}, {0x03a4, 0x6106, 0x1bd7, 0xff71}, + {0x031c, 0x6007, 0x1d6c, 0xff64}, {0x029f, 0x5ef5, 0x1f0b, 0xff56}, + {0x022a, 0x5dd0, 0x20b3, 0xff48}, {0x01be, 0x5c9a, 0x2264, 0xff3a}, + {0x015b, 0x5b53, 0x241e, 0xff2c}, {0x0101, 0x59fc, 0x25e0, 0xff1e}, + {0x00ae, 0x5896, 0x27a9, 0xff10}, {0x0063, 0x5720, 0x297a, 0xff02}, + {0x001f, 0x559d, 0x2b50, 0xfef4}, {0xffe2, 0x540d, 0x2d2c, 0xfee8}, + {0xffac, 0x5270, 0x2f0d, 0xfedb}, {0xff7c, 0x50c7, 0x30f3, 0xfed0}, + {0xff53, 0x4f14, 0x32dc, 0xfec6}, {0xff2e, 0x4d57, 0x34c8, 0xfebd}, + {0xff0f, 0x4b91, 0x36b6, 0xfeb6}, {0xfef5, 0x49c2, 0x38a5, 0xfeb0}, + {0xfedf, 0x47ed, 0x3a95, 0xfeac}, {0xfece, 0x4611, 0x3c85, 0xfeab}, + {0xfec0, 0x4430, 0x3e74, 0xfeac}, {0xfeb6, 0x424a, 0x4060, 0xfeaf}, + {0xfeaf, 0x4060, 0x424a, 0xfeb6}, {0xfeac, 0x3e74, 0x4430, 0xfec0}, + {0xfeab, 0x3c85, 0x4611, 0xfece}, {0xfeac, 0x3a95, 0x47ed, 0xfedf}, + {0xfeb0, 0x38a5, 0x49c2, 0xfef5}, {0xfeb6, 0x36b6, 0x4b91, 0xff0f}, + {0xfebd, 0x34c8, 0x4d57, 0xff2e}, {0xfec6, 0x32dc, 0x4f14, 0xff53}, + {0xfed0, 0x30f3, 0x50c7, 0xff7c}, {0xfedb, 0x2f0d, 0x5270, 0xffac}, + {0xfee8, 0x2d2c, 0x540d, 0xffe2}, {0xfef4, 0x2b50, 0x559d, 0x001f}, + {0xff02, 0x297a, 0x5720, 0x0063}, {0xff10, 0x27a9, 0x5896, 0x00ae}, + {0xff1e, 0x25e0, 0x59fc, 0x0101}, {0xff2c, 0x241e, 0x5b53, 0x015b}, + {0xff3a, 0x2264, 0x5c9a, 0x01be}, {0xff48, 0x20b3, 0x5dd0, 0x022a}, + {0xff56, 0x1f0b, 0x5ef5, 0x029f}, {0xff64, 0x1d6c, 0x6007, 0x031c}, + {0xff71, 0x1bd7, 0x6106, 0x03a4}, {0xff7e, 0x1a4c, 0x61f3, 0x0435}, + {0xff8a, 0x18cb, 0x62cb, 0x04d1}, {0xff96, 0x1756, 0x638f, 0x0577}, + {0xffa1, 0x15eb, 0x643f, 0x0628}, {0xffac, 0x148c, 0x64d9, 0x06e4}, + {0xffb6, 0x1338, 0x655e, 0x07ab}, {0xffbf, 0x11f0, 0x65cd, 0x087d}, + {0xffc8, 0x10b4, 0x6626, 0x095a}, {0xffd0, 0x0f83, 0x6669, 0x0a44}, + {0xffd8, 0x0e5f, 0x6696, 0x0b39}, {0xffdf, 0x0d46, 0x66ad, 0x0c39} +}; + +static inline int16_t clamp16(int32_t v) { + if (v < -0x8000) { + return -0x8000; + } else if (v > 0x7fff) { + return 0x7fff; + } + return (int16_t)v; +} + +static inline int32_t clamp32(int64_t v) { + if (v < -0x7fffffff - 1) { + return -0x7fffffff - 1; + } else if (v > 0x7fffffff) { + return 0x7fffffff; + } + return (int32_t)v; +} + +void aClearBufferImpl(uint16_t addr, int nbytes) { + nbytes = ROUND_UP_16(nbytes); + memset(BUF_U8(addr), 0, nbytes); +} + +#ifdef NEW_AUDIO_UCODE +void aLoadBufferImpl(const void *source_addr, uint16_t dest_addr, uint16_t nbytes) { + memcpy(BUF_U8(dest_addr), source_addr, ROUND_DOWN_16(nbytes)); +} + +void aSaveBufferImpl(uint16_t source_addr, int16_t *dest_addr, uint16_t nbytes) { + memcpy(dest_addr, BUF_S16(source_addr), ROUND_DOWN_16(nbytes)); +} +#else +void aLoadBufferImpl(const void *source_addr) { + DEBUG_PRINT("aLoadBufferImpl()"); + DEBUG_PRINT("- source_addr: %x", source_addr); + memcpy(BUF_U8(rspa.in), source_addr, ROUND_UP_8(rspa.nbytes)); +} + +void aSaveBufferImpl(int16_t *dest_addr) { + memcpy(dest_addr, BUF_S16(rspa.out), ROUND_UP_8(rspa.nbytes)); +} +#endif + +void aLoadADPCMImpl(int num_entries_times_16, const int16_t *book_source_addr) { + memcpy(rspa.adpcm_table, book_source_addr, num_entries_times_16); +} + +void aSetBufferImpl(uint8_t flags, uint16_t in, uint16_t out, uint16_t nbytes) { +#ifndef NEW_AUDIO_UCODE + if (flags & A_AUX) { + rspa.dry_right = in; + rspa.wet_left = out; + rspa.wet_right = nbytes; + return; + } +#endif + rspa.in = in; + rspa.out = out; + rspa.nbytes = nbytes; +} + +#ifndef NEW_AUDIO_UCODE +void aSetVolumeImpl(uint8_t flags, int16_t v, int16_t t, int16_t r) { + if (flags & A_AUX) { + rspa.vol_dry = v; + rspa.vol_wet = r; + } else if (flags & A_VOL) { + if (flags & A_LEFT) { + rspa.vol[0] = v; + } else { + rspa.vol[1] = v; + } + } else { + if (flags & A_LEFT) { + rspa.target[0] = v; + rspa.rate[0] = (int32_t)((uint16_t)t << 16 | ((uint16_t)r)); + } else { + rspa.target[1] = v; + rspa.rate[1] = (int32_t)((uint16_t)t << 16 | ((uint16_t)r)); + } + } +} +#endif + +#ifdef NEW_AUDIO_UCODE +void aInterleaveImpl(uint16_t dest, uint16_t left, uint16_t right, uint16_t c) { + int count = ROUND_UP_8(c) / sizeof(int16_t) / 4; + int16_t *l = BUF_S16(left); + int16_t *r = BUF_S16(right); + int16_t *d = BUF_S16(dest); + while (count > 0) { + int16_t l0 = *l++; + int16_t l1 = *l++; + int16_t l2 = *l++; + int16_t l3 = *l++; + int16_t r0 = *r++; + int16_t r1 = *r++; + int16_t r2 = *r++; + int16_t r3 = *r++; + *d++ = l0; + *d++ = r0; + *d++ = l1; + *d++ = r1; + *d++ = l2; + *d++ = r2; + *d++ = l3; + *d++ = r3; + --count; + } +} +#else +void aInterleaveImpl(uint16_t left, uint16_t right) { + int count = ROUND_UP_16(rspa.nbytes) / sizeof(int16_t) / 8; + int16_t *l = BUF_S16(left); + int16_t *r = BUF_S16(right); + int16_t *d = BUF_S16(rspa.out); + while (count > 0) { + int16_t l0 = *l++; + int16_t l1 = *l++; + int16_t l2 = *l++; + int16_t l3 = *l++; + int16_t l4 = *l++; + int16_t l5 = *l++; + int16_t l6 = *l++; + int16_t l7 = *l++; + int16_t r0 = *r++; + int16_t r1 = *r++; + int16_t r2 = *r++; + int16_t r3 = *r++; + int16_t r4 = *r++; + int16_t r5 = *r++; + int16_t r6 = *r++; + int16_t r7 = *r++; + *d++ = l0; + *d++ = r0; + *d++ = l1; + *d++ = r1; + *d++ = l2; + *d++ = r2; + *d++ = l3; + *d++ = r3; + *d++ = l4; + *d++ = r4; + *d++ = l5; + *d++ = r5; + *d++ = l6; + *d++ = r6; + *d++ = l7; + *d++ = r7; + --count; + } +} +#endif + +void aDMEMMoveImpl(uint16_t in_addr, uint16_t out_addr, int nbytes) { + nbytes = ROUND_UP_16(nbytes); + memmove(BUF_U8(out_addr), BUF_U8(in_addr), nbytes); +} + +void aSetLoopImpl(ADPCM_STATE *adpcm_loop_state) { + rspa.adpcm_loop_state = adpcm_loop_state; +} + +void aADPCMdecImpl(uint8_t flags, ADPCM_STATE state) { +#if HAS_SSE41 + const __m128i tblrev = _mm_setr_epi8(12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1, -1, -1); + const __m128i pos0 = _mm_set_epi8(3, -1, 3, -1, 2, -1, 2, -1, 1, -1, 1, -1, 0, -1, 0, -1); + const __m128i pos1 = _mm_set_epi8(7, -1, 7, -1, 6, -1, 6, -1, 5, -1, 5, -1, 4, -1, 4, -1); + const __m128i mult = _mm_set_epi16(0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01); + const __m128i mask = _mm_set1_epi16((int16_t)0xf000); +#elif HAS_NEON + static const int8_t pos0_data[] = {-1, 0, -1, 0, -1, 1, -1, 1, -1, 2, -1, 2, -1, 3, -1, 3}; + static const int8_t pos1_data[] = {-1, 4, -1, 4, -1, 5, -1, 5, -1, 6, -1, 6, -1, 7, -1, 7}; + static const int16_t mult_data[] = {0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10}; + static const int16_t table_prefix_data[] = {0, 0, 0, 0, 0, 0, 0, 1 << 11}; + const int8x16_t pos0 = vld1q_s8(pos0_data); + const int8x16_t pos1 = vld1q_s8(pos1_data); + const int16x8_t mult = vld1q_s16(mult_data); + const int16x8_t mask = vdupq_n_s16((int16_t)0xf000); + const int16x8_t table_prefix = vld1q_s16(table_prefix_data); +#endif + uint8_t *in = BUF_U8(rspa.in); + int16_t *out = BUF_S16(rspa.out); + int nbytes = ROUND_UP_32(rspa.nbytes); + if (flags & A_INIT) { + memset(out, 0, 16 * sizeof(int16_t)); + } else if (flags & A_LOOP) { + memcpy(out, rspa.adpcm_loop_state, 16 * sizeof(int16_t)); + } else { + memcpy(out, state, 16 * sizeof(int16_t)); + } + out += 16; +#if HAS_SSE41 + __m128i prev_interleaved = _mm_set1_epi32((uint16_t)out[-2] | ((uint16_t)out[-1] << 16)); + //__m128i prev_interleaved = _mm_shuffle_epi32(_mm_loadu_si32(out - 2), 0); // GCC misses this? +#elif HAS_NEON + int16x8_t result = vld1q_s16(out - 8); +#endif + while (nbytes > 0) { + int shift = *in >> 4; // should be in 0..12 + int table_index = *in++ & 0xf; // should be in 0..7 + int16_t (*tbl)[8] = rspa.adpcm_table[table_index]; + int i; +#if HAS_SSE41 + // The _mm_loadu_si64 instruction was added in GCC 9, and results in the same + // asm as the following instructions, so better be compatible with old GCC. + //__m128i inv = _mm_loadu_si64(in); + uint64_t v; memcpy(&v, in, 8); + __m128i inv = _mm_set_epi64x(0, v); + __m128i invec[2] = {_mm_shuffle_epi8(inv, pos0), _mm_shuffle_epi8(inv, pos1)}; + __m128i tblvec0 = _mm_loadu_si128((const __m128i *)tbl[0]); + __m128i tblvec1 = _mm_loadu_si128((const __m128i *)(tbl[1])); + __m128i tbllo = _mm_unpacklo_epi16(tblvec0, tblvec1); + __m128i tblhi = _mm_unpackhi_epi16(tblvec0, tblvec1); + __m128i shiftcount = _mm_set_epi64x(0, 12 - shift); // _mm_cvtsi64_si128 does not exist on 32-bit x86 + __m128i tblvec1_rev[8]; + + tblvec1_rev[0] = _mm_insert_epi16(_mm_shuffle_epi8(tblvec1, tblrev), 1 << 11, 7); + tblvec1_rev[1] = _mm_bsrli_si128(tblvec1_rev[0], 2); + tblvec1_rev[2] = _mm_bsrli_si128(tblvec1_rev[0], 4); + tblvec1_rev[3] = _mm_bsrli_si128(tblvec1_rev[0], 6); + tblvec1_rev[4] = _mm_bsrli_si128(tblvec1_rev[0], 8); + tblvec1_rev[5] = _mm_bsrli_si128(tblvec1_rev[0], 10); + tblvec1_rev[6] = _mm_bsrli_si128(tblvec1_rev[0], 12); + tblvec1_rev[7] = _mm_bsrli_si128(tblvec1_rev[0], 14); + in += 8; + for (i = 0; i < 2; i++) { + __m128i acc0 = _mm_madd_epi16(prev_interleaved, tbllo); + __m128i acc1 = _mm_madd_epi16(prev_interleaved, tblhi); + __m128i muls[8]; + __m128i result; + invec[i] = _mm_sra_epi16(_mm_and_si128(_mm_mullo_epi16(invec[i], mult), mask), shiftcount); + + muls[7] = _mm_madd_epi16(tblvec1_rev[0], invec[i]); + muls[6] = _mm_madd_epi16(tblvec1_rev[1], invec[i]); + muls[5] = _mm_madd_epi16(tblvec1_rev[2], invec[i]); + muls[4] = _mm_madd_epi16(tblvec1_rev[3], invec[i]); + muls[3] = _mm_madd_epi16(tblvec1_rev[4], invec[i]); + muls[2] = _mm_madd_epi16(tblvec1_rev[5], invec[i]); + muls[1] = _mm_madd_epi16(tblvec1_rev[6], invec[i]); + muls[0] = _mm_madd_epi16(tblvec1_rev[7], invec[i]); + + acc0 = _mm_add_epi32(acc0, _mm_hadd_epi32(_mm_hadd_epi32(muls[0], muls[1]), _mm_hadd_epi32(muls[2], muls[3]))); + acc1 = _mm_add_epi32(acc1, _mm_hadd_epi32(_mm_hadd_epi32(muls[4], muls[5]), _mm_hadd_epi32(muls[6], muls[7]))); + + acc0 = _mm_srai_epi32(acc0, 11); + acc1 = _mm_srai_epi32(acc1, 11); + + result = _mm_packs_epi32(acc0, acc1); + _mm_storeu_si128((__m128i *)out, result); + out += 8; + + prev_interleaved = _mm_shuffle_epi32(result, _MM_SHUFFLE(3, 3, 3, 3)); + } +#elif HAS_NEON + int8x8_t inv = vld1_s8((int8_t *)in); + int16x8_t tblvec[2] = {vld1q_s16(tbl[0]), vld1q_s16(tbl[1])}; + int16x8_t invec[2] = {vreinterpretq_s16_s8(vcombine_s8(vtbl1_s8(inv, vget_low_s8(pos0)), + vtbl1_s8(inv, vget_high_s8(pos0)))), + vreinterpretq_s16_s8(vcombine_s8(vtbl1_s8(inv, vget_low_s8(pos1)), + vtbl1_s8(inv, vget_high_s8(pos1))))}; + int16x8_t shiftcount = vdupq_n_s16(shift - 12); // negative means right shift + int16x8_t tblvec1[8]; + + in += 8; + tblvec1[0] = vextq_s16(table_prefix, tblvec[1], 7); + invec[0] = vmulq_s16(invec[0], mult); + tblvec1[1] = vextq_s16(table_prefix, tblvec[1], 6); + invec[1] = vmulq_s16(invec[1], mult); + tblvec1[2] = vextq_s16(table_prefix, tblvec[1], 5); + tblvec1[3] = vextq_s16(table_prefix, tblvec[1], 4); + invec[0] = vandq_s16(invec[0], mask); + tblvec1[4] = vextq_s16(table_prefix, tblvec[1], 3); + invec[1] = vandq_s16(invec[1], mask); + tblvec1[5] = vextq_s16(table_prefix, tblvec[1], 2); + tblvec1[6] = vextq_s16(table_prefix, tblvec[1], 1); + invec[0] = vqshlq_s16(invec[0], shiftcount); + invec[1] = vqshlq_s16(invec[1], shiftcount); + tblvec1[7] = table_prefix; + for (i = 0; i < 2; i++) { + int32x4_t acc0; + int32x4_t acc1; + + acc1 = vmull_lane_s16(vget_high_s16(tblvec[0]), vget_high_s16(result), 2); + acc1 = vmlal_lane_s16(acc1, vget_high_s16(tblvec[1]), vget_high_s16(result), 3); + acc0 = vmull_lane_s16(vget_low_s16(tblvec[0]), vget_high_s16(result), 2); + acc0 = vmlal_lane_s16(acc0, vget_low_s16(tblvec[1]), vget_high_s16(result), 3); + + acc0 = vmlal_lane_s16(acc0, vget_low_s16(tblvec1[0]), vget_low_s16(invec[i]), 0); + acc0 = vmlal_lane_s16(acc0, vget_low_s16(tblvec1[1]), vget_low_s16(invec[i]), 1); + acc0 = vmlal_lane_s16(acc0, vget_low_s16(tblvec1[2]), vget_low_s16(invec[i]), 2); + acc0 = vmlal_lane_s16(acc0, vget_low_s16(tblvec1[3]), vget_low_s16(invec[i]), 3); + + acc1 = vmlal_lane_s16(acc1, vget_high_s16(tblvec1[0]), vget_low_s16(invec[i]), 0); + acc1 = vmlal_lane_s16(acc1, vget_high_s16(tblvec1[1]), vget_low_s16(invec[i]), 1); + acc1 = vmlal_lane_s16(acc1, vget_high_s16(tblvec1[2]), vget_low_s16(invec[i]), 2); + acc1 = vmlal_lane_s16(acc1, vget_high_s16(tblvec1[3]), vget_low_s16(invec[i]), 3); + acc1 = vmlal_lane_s16(acc1, vget_high_s16(tblvec1[4]), vget_high_s16(invec[i]), 0); + acc1 = vmlal_lane_s16(acc1, vget_high_s16(tblvec1[5]), vget_high_s16(invec[i]), 1); + acc1 = vmlal_lane_s16(acc1, vget_high_s16(tblvec1[6]), vget_high_s16(invec[i]), 2); + acc1 = vmlal_lane_s16(acc1, vget_high_s16(tblvec1[7]), vget_high_s16(invec[i]), 3); + + result = vcombine_s16(vqshrn_n_s32(acc0, 11), vqshrn_n_s32(acc1, 11)); + vst1q_s16(out, result); + out += 8; + } +#else + for (i = 0; i < 2; i++) { + int16_t ins[8]; + int16_t prev1 = out[-1]; + int16_t prev2 = out[-2]; + int j, k; + for (j = 0; j < 4; j++) { + ins[j * 2] = (((*in >> 4) << 28) >> 28) << shift; + ins[j * 2 + 1] = (((*in++ & 0xf) << 28) >> 28) << shift; + } + for (j = 0; j < 8; j++) { + int32_t acc = tbl[0][j] * prev2 + tbl[1][j] * prev1 + (ins[j] << 11); + for (k = 0; k < j; k++) { + acc += tbl[1][((j - k) - 1)] * ins[k]; + } + acc >>= 11; + *out++ = clamp16(acc); + } + } +#endif + nbytes -= 16 * sizeof(int16_t); + } + memcpy(state, out - 16, 16 * sizeof(int16_t)); +} + +void aResampleImpl(uint8_t flags, uint16_t pitch, RESAMPLE_STATE state) { + int16_t tmp[16]; + int16_t *in_initial = BUF_S16(rspa.in); + int16_t *in = in_initial; + int16_t *out = BUF_S16(rspa.out); + int nbytes = ROUND_UP_16(rspa.nbytes); + uint32_t pitch_accumulator; + int i; +#if !HAS_SSE41 && !HAS_NEON + int16_t *tbl; + int32_t sample; +#endif + if (flags & A_INIT) { + memset(tmp, 0, 5 * sizeof(int16_t)); + } else { + memcpy(tmp, state, 16 * sizeof(int16_t)); + } + if (flags & 2) { + memcpy(in - 8, tmp + 8, 8 * sizeof(int16_t)); + in -= tmp[5] / sizeof(int16_t); + } + in -= 4; + pitch_accumulator = (uint16_t)tmp[4]; + memcpy(in, tmp, 4 * sizeof(int16_t)); + +#if HAS_SSE41 + __m128i multiples = _mm_setr_epi16(0, 2, 4, 6, 8, 10, 12, 14); + __m128i pitchvec = _mm_set1_epi16((int16_t)pitch); + __m128i pitchvec_8_steps = _mm_set1_epi32((pitch << 1) * 8); + __m128i pitchacclo_vec = _mm_set1_epi32((uint16_t)pitch_accumulator); + __m128i pl = _mm_mullo_epi16(multiples, pitchvec); + __m128i ph = _mm_mulhi_epu16(multiples, pitchvec); + __m128i acc_a = _mm_add_epi32(_mm_unpacklo_epi16(pl, ph), pitchacclo_vec); + __m128i acc_b = _mm_add_epi32(_mm_unpackhi_epi16(pl, ph), pitchacclo_vec); + + do { + __m128i tbl_positions = _mm_srli_epi16(_mm_packus_epi32( + _mm_and_si128(acc_a, _mm_set1_epi32(0xffff)), + _mm_and_si128(acc_b, _mm_set1_epi32(0xffff))), 10); + + __m128i in_positions = _mm_packus_epi32(_mm_srli_epi32(acc_a, 16), _mm_srli_epi32(acc_b, 16)); + __m128i tbl_entries[4]; + __m128i samples[4]; + + /*for (i = 0; i < 4; i++) { + tbl_entries[i] = _mm_castpd_si128(_mm_loadh_pd(_mm_load_sd( + (const double *)resample_table[_mm_extract_epi16(tbl_positions, 2 * i)]), + (const double *)resample_table[_mm_extract_epi16(tbl_positions, 2 * i + 1)])); + samples[i] = _mm_castpd_si128(_mm_loadh_pd(_mm_load_sd( + (const double *)&in[_mm_extract_epi16(in_positions, 2 * i)]), + (const double *)&in[_mm_extract_epi16(in_positions, 2 * i + 1)])); + samples[i] = _mm_mulhrs_epi16(samples[i], tbl_entries[i]); + }*/ + tbl_entries[0] = LOADLH(resample_table[_mm_extract_epi16(tbl_positions, 0)], resample_table[_mm_extract_epi16(tbl_positions, 1)]); + tbl_entries[1] = LOADLH(resample_table[_mm_extract_epi16(tbl_positions, 2)], resample_table[_mm_extract_epi16(tbl_positions, 3)]); + tbl_entries[2] = LOADLH(resample_table[_mm_extract_epi16(tbl_positions, 4)], resample_table[_mm_extract_epi16(tbl_positions, 5)]); + tbl_entries[3] = LOADLH(resample_table[_mm_extract_epi16(tbl_positions, 6)], resample_table[_mm_extract_epi16(tbl_positions, 7)]); + samples[0] = LOADLH(&in[_mm_extract_epi16(in_positions, 0)], &in[_mm_extract_epi16(in_positions, 1)]); + samples[1] = LOADLH(&in[_mm_extract_epi16(in_positions, 2)], &in[_mm_extract_epi16(in_positions, 3)]); + samples[2] = LOADLH(&in[_mm_extract_epi16(in_positions, 4)], &in[_mm_extract_epi16(in_positions, 5)]); + samples[3] = LOADLH(&in[_mm_extract_epi16(in_positions, 6)], &in[_mm_extract_epi16(in_positions, 7)]); + samples[0] = _mm_mulhrs_epi16(samples[0], tbl_entries[0]); + samples[1] = _mm_mulhrs_epi16(samples[1], tbl_entries[1]); + samples[2] = _mm_mulhrs_epi16(samples[2], tbl_entries[2]); + samples[3] = _mm_mulhrs_epi16(samples[3], tbl_entries[3]); + + _mm_storeu_si128((__m128i *)out, _mm_hadds_epi16(_mm_hadds_epi16(samples[0], samples[1]), _mm_hadds_epi16(samples[2], samples[3]))); + + acc_a = _mm_add_epi32(acc_a, pitchvec_8_steps); + acc_b = _mm_add_epi32(acc_b, pitchvec_8_steps); + out += 8; + nbytes -= 8 * sizeof(int16_t); + } while (nbytes > 0); + in += (uint16_t)_mm_extract_epi16(acc_a, 1); + pitch_accumulator = (uint16_t)_mm_extract_epi16(acc_a, 0); +#elif HAS_NEON + static const uint16_t multiples_data[8] = {0, 2, 4, 6, 8, 10, 12, 14}; + uint16x8_t multiples = vld1q_u16(multiples_data); + uint32x4_t pitchvec_8_steps = vdupq_n_u32((pitch << 1) * 8); + uint32x4_t pitchacclo_vec = vdupq_n_u32((uint16_t)pitch_accumulator); + uint32x4_t acc_a = vmlal_n_u16(pitchacclo_vec, vget_low_u16(multiples), pitch); + uint32x4_t acc_b = vmlal_n_u16(pitchacclo_vec, vget_high_u16(multiples), pitch); + + do { + uint16x8x2_t unzipped = vuzpq_u16(vreinterpretq_u16_u32(acc_a), vreinterpretq_u16_u32(acc_b)); + uint16x8_t tbl_positions = vshrq_n_u16(unzipped.val[0], 10); + uint16x8_t in_positions = unzipped.val[1]; + int16x8_t tbl_entries[4]; + int16x8_t samples[4]; + int16x8x2_t unzipped1; + int16x8x2_t unzipped2; + + tbl_entries[0] = vcombine_s16(vld1_s16(resample_table[vgetq_lane_u16(tbl_positions, 0)]), vld1_s16(resample_table[vgetq_lane_u16(tbl_positions, 1)])); + tbl_entries[1] = vcombine_s16(vld1_s16(resample_table[vgetq_lane_u16(tbl_positions, 2)]), vld1_s16(resample_table[vgetq_lane_u16(tbl_positions, 3)])); + tbl_entries[2] = vcombine_s16(vld1_s16(resample_table[vgetq_lane_u16(tbl_positions, 4)]), vld1_s16(resample_table[vgetq_lane_u16(tbl_positions, 5)])); + tbl_entries[3] = vcombine_s16(vld1_s16(resample_table[vgetq_lane_u16(tbl_positions, 6)]), vld1_s16(resample_table[vgetq_lane_u16(tbl_positions, 7)])); + samples[0] = vcombine_s16(vld1_s16(&in[vgetq_lane_u16(in_positions, 0)]), vld1_s16(&in[vgetq_lane_u16(in_positions, 1)])); + samples[1] = vcombine_s16(vld1_s16(&in[vgetq_lane_u16(in_positions, 2)]), vld1_s16(&in[vgetq_lane_u16(in_positions, 3)])); + samples[2] = vcombine_s16(vld1_s16(&in[vgetq_lane_u16(in_positions, 4)]), vld1_s16(&in[vgetq_lane_u16(in_positions, 5)])); + samples[3] = vcombine_s16(vld1_s16(&in[vgetq_lane_u16(in_positions, 6)]), vld1_s16(&in[vgetq_lane_u16(in_positions, 7)])); + samples[0] = vqrdmulhq_s16(samples[0], tbl_entries[0]); + samples[1] = vqrdmulhq_s16(samples[1], tbl_entries[1]); + samples[2] = vqrdmulhq_s16(samples[2], tbl_entries[2]); + samples[3] = vqrdmulhq_s16(samples[3], tbl_entries[3]); + + unzipped1 = vuzpq_s16(samples[0], samples[1]); + unzipped2 = vuzpq_s16(samples[2], samples[3]); + samples[0] = vqaddq_s16(unzipped1.val[0], unzipped1.val[1]); + samples[1] = vqaddq_s16(unzipped2.val[0], unzipped2.val[1]); + unzipped1 = vuzpq_s16(samples[0], samples[1]); + samples[0] = vqaddq_s16(unzipped1.val[0], unzipped1.val[1]); + + vst1q_s16(out, samples[0]); + + acc_a = vaddq_u32(acc_a, pitchvec_8_steps); + acc_b = vaddq_u32(acc_b, pitchvec_8_steps); + out += 8; + nbytes -= 8 * sizeof(int16_t); + } while (nbytes > 0); + in += vgetq_lane_u16(vreinterpretq_u16_u32(acc_a), 1); + pitch_accumulator = vgetq_lane_u16(vreinterpretq_u16_u32(acc_a), 0); +#else + do { + for (i = 0; i < 8; i++) { + tbl = resample_table[pitch_accumulator * 64 >> 16]; + sample = ((in[0] * tbl[0] + 0x4000) >> 15) + + ((in[1] * tbl[1] + 0x4000) >> 15) + + ((in[2] * tbl[2] + 0x4000) >> 15) + + ((in[3] * tbl[3] + 0x4000) >> 15); + *out++ = clamp16(sample); + + pitch_accumulator += (pitch << 1); + in += pitch_accumulator >> 16; + pitch_accumulator %= 0x10000; + } + nbytes -= 8 * sizeof(int16_t); + } while (nbytes > 0); +#endif + + state[4] = (int16_t)pitch_accumulator; + memcpy(state, in, 4 * sizeof(int16_t)); + i = (in - in_initial + 4) & 7; + in -= i; + if (i != 0) { + i = -8 - i; + } + state[5] = i; + memcpy(state + 8, in, 8 * sizeof(int16_t)); +} + +#ifdef NEW_AUDIO_UCODE +void aEnvSetup1Impl(uint8_t initial_vol_wet, uint16_t rate_wet, uint16_t rate_left, uint16_t rate_right) { + rspa.vol_wet = (uint16_t)(initial_vol_wet << 8); + rspa.rate_wet = rate_wet; + rspa.rate[0] = rate_left; + rspa.rate[1] = rate_right; +} + +void aEnvSetup2Impl(uint16_t initial_vol_left, uint16_t initial_vol_right) { + rspa.vol[0] = initial_vol_left; + rspa.vol[1] = initial_vol_right; +} + +void aEnvMixerImpl(uint16_t in_addr, uint16_t n_samples, bool swap_reverb, + bool neg_left, bool neg_right, + uint16_t dry_left_addr, uint16_t dry_right_addr, + uint16_t wet_left_addr, uint16_t wet_right_addr) +{ + int16_t *in = BUF_S16(in_addr); + int16_t *dry[2] = {BUF_S16(dry_left_addr), BUF_S16(dry_right_addr)}; + int16_t *wet[2] = {BUF_S16(wet_left_addr), BUF_S16(wet_right_addr)}; + int16_t negs[2] = {neg_left ? -1 : 0, neg_right ? -1 : 0}; + int swapped[2] = {swap_reverb ? 1 : 0, swap_reverb ? 0 : 1}; + int n = ROUND_UP_16(n_samples); + + uint16_t vols[2] = {rspa.vol[0], rspa.vol[1]}; + uint16_t rates[2] = {rspa.rate[0], rspa.rate[1]}; + uint16_t vol_wet = rspa.vol_wet; + uint16_t rate_wet = rspa.rate_wet; + + do { + for (int i = 0; i < 8; i++) { + int16_t samples[2] = {*in, *in}; in++; + for (int j = 0; j < 2; j++) { + samples[j] = (samples[j] * vols[j] >> 16) ^ negs[j]; + *dry[j] = clamp16(*dry[j] + samples[j]); dry[j]++; + *wet[j] = clamp16(*wet[j] + (samples[swapped[j]] * vol_wet >> 16)); wet[j]++; + } + } + vols[0] += rates[0]; + vols[1] += rates[1]; + vol_wet += rate_wet; + + n -= 8; + } while (n > 0); +} +#else +void aEnvMixerImpl(uint8_t flags, ENVMIX_STATE state) { + int16_t *in = BUF_S16(rspa.in); + int16_t *dry[2] = {BUF_S16(rspa.out), BUF_S16(rspa.dry_right)}; + int16_t *wet[2] = {BUF_S16(rspa.wet_left), BUF_S16(rspa.wet_right)}; + int nbytes = ROUND_UP_16(rspa.nbytes); + +#if HAS_SSE41 + __m128 vols[2][2]; + __m128i dry_factor; + __m128i wet_factor; + __m128 target[2]; + __m128 rate[2]; + __m128i in_loaded; + __m128i vol_s16; + bool increasing[2]; + + int c; + + if (flags & A_INIT) { + float vol_init[2] = {rspa.vol[0], rspa.vol[1]}; + float rate_float[2] = {(float)rspa.rate[0] * (1.0f / 65536.0f), (float)rspa.rate[1] * (1.0f / 65536.0f)}; + float step_diff[2] = {vol_init[0] * (rate_float[0] - 1.0f), vol_init[1] * (rate_float[1] - 1.0f)}; + + for (c = 0; c < 2; c++) { + vols[c][0] = _mm_add_ps( + _mm_set_ps1(vol_init[c]), + _mm_mul_ps(_mm_set1_ps(step_diff[c]), _mm_setr_ps(1.0f / 8.0f, 2.0f / 8.0f, 3.0f / 8.0f, 4.0f / 8.0f))); + vols[c][1] = _mm_add_ps( + _mm_set_ps1(vol_init[c]), + _mm_mul_ps(_mm_set1_ps(step_diff[c]), _mm_setr_ps(5.0f / 8.0f, 6.0f / 8.0f, 7.0f / 8.0f, 8.0f / 8.0f))); + + increasing[c] = rate_float[c] >= 1.0f; + target[c] = _mm_set1_ps(rspa.target[c]); + rate[c] = _mm_set1_ps(rate_float[c]); + } + + dry_factor = _mm_set1_epi16(rspa.vol_dry); + wet_factor = _mm_set1_epi16(rspa.vol_wet); + + memcpy(state + 32, &rate_float[0], 4); + memcpy(state + 34, &rate_float[1], 4); + state[36] = rspa.target[0]; + state[37] = rspa.target[1]; + state[38] = rspa.vol_dry; + state[39] = rspa.vol_wet; + } else { + float floats[2]; + vols[0][0] = _mm_loadu_ps((const float *)state); + vols[0][1] = _mm_loadu_ps((const float *)(state + 8)); + vols[1][0] = _mm_loadu_ps((const float *)(state + 16)); + vols[1][1] = _mm_loadu_ps((const float *)(state + 24)); + memcpy(floats, state + 32, 8); + rate[0] = _mm_set1_ps(floats[0]); + rate[1] = _mm_set1_ps(floats[1]); + increasing[0] = floats[0] >= 1.0f; + increasing[1] = floats[1] >= 1.0f; + target[0] = _mm_set1_ps(state[36]); + target[1] = _mm_set1_ps(state[37]); + dry_factor = _mm_set1_epi16(state[38]); + wet_factor = _mm_set1_epi16(state[39]); + } + do { + in_loaded = _mm_loadu_si128((const __m128i *)in); + in += 8; + for (c = 0; c < 2; c++) { + if (increasing[c]) { + vols[c][0] = _mm_min_ps(vols[c][0], target[c]); + vols[c][1] = _mm_min_ps(vols[c][1], target[c]); + } else { + vols[c][0] = _mm_max_ps(vols[c][0], target[c]); + vols[c][1] = _mm_max_ps(vols[c][1], target[c]); + } + + vol_s16 = _mm_packs_epi32(_mm_cvtps_epi32(vols[c][0]), _mm_cvtps_epi32(vols[c][1])); + _mm_storeu_si128((__m128i *)dry[c], + _mm_adds_epi16( + _mm_loadu_si128((const __m128i *)dry[c]), + _mm_mulhrs_epi16(in_loaded, _mm_mulhrs_epi16(vol_s16, dry_factor)))); + dry[c] += 8; + + if (flags & A_AUX) { + _mm_storeu_si128((__m128i *)wet[c], + _mm_adds_epi16( + _mm_loadu_si128((const __m128i *)wet[c]), + _mm_mulhrs_epi16(in_loaded, _mm_mulhrs_epi16(vol_s16, wet_factor)))); + wet[c] += 8; + } + + vols[c][0] = _mm_mul_ps(vols[c][0], rate[c]); + vols[c][1] = _mm_mul_ps(vols[c][1], rate[c]); + } + + nbytes -= 8 * sizeof(int16_t); + } while (nbytes > 0); + + _mm_storeu_ps((float *)state, vols[0][0]); + _mm_storeu_ps((float *)(state + 8), vols[0][1]); + _mm_storeu_ps((float *)(state + 16), vols[1][0]); + _mm_storeu_ps((float *)(state + 24), vols[1][1]); +#elif HAS_NEON + float32x4_t vols[2][2]; + int16_t dry_factor; + int16_t wet_factor; + float32x4_t target[2]; + float rate[2]; + int16x8_t in_loaded; + int16x8_t vol_s16; + bool increasing[2]; + + int c; + + if (flags & A_INIT) { + float vol_init[2] = {rspa.vol[0], rspa.vol[1]}; + float rate_float[2] = {(float)rspa.rate[0] * (1.0f / 65536.0f), (float)rspa.rate[1] * (1.0f / 65536.0f)}; + float step_diff[2] = {vol_init[0] * (rate_float[0] - 1.0f), vol_init[1] * (rate_float[1] - 1.0f)}; + static const float step_dividers_data[2][4] = {{1.0f / 8.0f, 2.0f / 8.0f, 3.0f / 8.0f, 4.0f / 8.0f}, + {5.0f / 8.0f, 6.0f / 8.0f, 7.0f / 8.0f, 8.0f / 8.0f}}; + float32x4_t step_dividers[2] = {vld1q_f32(step_dividers_data[0]), vld1q_f32(step_dividers_data[1])}; + + for (c = 0; c < 2; c++) { + vols[c][0] = vaddq_f32(vdupq_n_f32(vol_init[c]), vmulq_n_f32(step_dividers[0], step_diff[c])); + vols[c][1] = vaddq_f32(vdupq_n_f32(vol_init[c]), vmulq_n_f32(step_dividers[1], step_diff[c])); + increasing[c] = rate_float[c] >= 1.0f; + target[c] = vdupq_n_f32(rspa.target[c]); + rate[c] = rate_float[c]; + } + + dry_factor = rspa.vol_dry; + wet_factor = rspa.vol_wet; + + memcpy(state + 32, &rate_float[0], 4); + memcpy(state + 34, &rate_float[1], 4); + state[36] = rspa.target[0]; + state[37] = rspa.target[1]; + state[38] = rspa.vol_dry; + state[39] = rspa.vol_wet; + } else { + vols[0][0] = vreinterpretq_f32_s16(vld1q_s16(state)); + vols[0][1] = vreinterpretq_f32_s16(vld1q_s16(state + 8)); + vols[1][0] = vreinterpretq_f32_s16(vld1q_s16(state + 16)); + vols[1][1] = vreinterpretq_f32_s16(vld1q_s16(state + 24)); + memcpy(&rate[0], state + 32, 4); + memcpy(&rate[1], state + 34, 4); + increasing[0] = rate[0] >= 1.0f; + increasing[1] = rate[1] >= 1.0f; + target[0] = vdupq_n_f32(state[36]); + target[1] = vdupq_n_f32(state[37]); + dry_factor = state[38]; + wet_factor = state[39]; + } + + do { + in_loaded = vld1q_s16(in); + in += 8; + for (c = 0; c < 2; c++) { + if (increasing[c]) { + vols[c][0] = vminq_f32(vols[c][0], target[c]); + vols[c][1] = vminq_f32(vols[c][1], target[c]); + } else { + vols[c][0] = vmaxq_f32(vols[c][0], target[c]); + vols[c][1] = vmaxq_f32(vols[c][1], target[c]); + } + + vol_s16 = vcombine_s16(vqmovn_s32(vcvtq_s32_f32(vols[c][0])), vqmovn_s32(vcvtq_s32_f32(vols[c][1]))); + vst1q_s16(dry[c], vqaddq_s16(vld1q_s16(dry[c]), vqrdmulhq_s16(in_loaded, vqrdmulhq_n_s16(vol_s16, dry_factor)))); + dry[c] += 8; + if (flags & A_AUX) { + vst1q_s16(wet[c], vqaddq_s16(vld1q_s16(wet[c]), vqrdmulhq_s16(in_loaded, vqrdmulhq_n_s16(vol_s16, wet_factor)))); + wet[c] += 8; + } + vols[c][0] = vmulq_n_f32(vols[c][0], rate[c]); + vols[c][1] = vmulq_n_f32(vols[c][1], rate[c]); + } + + nbytes -= 8 * sizeof(int16_t); + } while (nbytes > 0); + + vst1q_s16(state, vreinterpretq_s16_f32(vols[0][0])); + vst1q_s16(state + 8, vreinterpretq_s16_f32(vols[0][1])); + vst1q_s16(state + 16, vreinterpretq_s16_f32(vols[1][0])); + vst1q_s16(state + 24, vreinterpretq_s16_f32(vols[1][1])); +#else + int16_t target[2]; + int32_t rate[2]; + int16_t vol_dry, vol_wet; + + int32_t step_diff[2]; + int32_t vols[2][8]; + + int c, i; + + if (flags & A_INIT) { + target[0] = rspa.target[0]; + target[1] = rspa.target[1]; + rate[0] = rspa.rate[0]; + rate[1] = rspa.rate[1]; + vol_dry = rspa.vol_dry; + vol_wet = rspa.vol_wet; + step_diff[0] = rspa.vol[0] * (rate[0] - 0x10000) / 8; + step_diff[1] = rspa.vol[0] * (rate[1] - 0x10000) / 8; + + for (i = 0; i < 8; i++) { + vols[0][i] = clamp32((int64_t)(rspa.vol[0] << 16) + step_diff[0] * (i + 1)); + vols[1][i] = clamp32((int64_t)(rspa.vol[1] << 16) + step_diff[1] * (i + 1)); + } + } else { + memcpy(vols[0], state, 32); + memcpy(vols[1], state + 16, 32); + target[0] = state[32]; + target[1] = state[35]; + rate[0] = (state[33] << 16) | (uint16_t)state[34]; + rate[1] = (state[36] << 16) | (uint16_t)state[37]; + vol_dry = state[38]; + vol_wet = state[39]; + } + + do { + for (c = 0; c < 2; c++) { + for (i = 0; i < 8; i++) { + if ((rate[c] >> 16) > 0) { + // Increasing volume + if ((vols[c][i] >> 16) > target[c]) { + vols[c][i] = target[c] << 16; + } + } else { + // Decreasing volume + if ((vols[c][i] >> 16) < target[c]) { + vols[c][i] = target[c] << 16; + } + } + dry[c][i] = clamp16((dry[c][i] * 0x7fff + in[i] * (((vols[c][i] >> 16) * vol_dry + 0x4000) >> 15) + 0x4000) >> 15); + if (flags & A_AUX) { + wet[c][i] = clamp16((wet[c][i] * 0x7fff + in[i] * (((vols[c][i] >> 16) * vol_wet + 0x4000) >> 15) + 0x4000) >> 15); + } + vols[c][i] = clamp32((int64_t)vols[c][i] * rate[c] >> 16); + } + + dry[c] += 8; + if (flags & A_AUX) { + wet[c] += 8; + } + } + + nbytes -= 16; + in += 8; + } while (nbytes > 0); + + memcpy(state, vols[0], 32); + memcpy(state + 16, vols[1], 32); + state[32] = target[0]; + state[35] = target[1]; + state[33] = (int16_t)(rate[0] >> 16); + state[34] = (int16_t)rate[0]; + state[36] = (int16_t)(rate[1] >> 16); + state[37] = (int16_t)rate[1]; + state[38] = vol_dry; + state[39] = vol_wet; +#endif +} +#endif + +#ifdef NEW_AUDIO_UCODE +void aMixImpl(int16_t gain, uint16_t in_addr, uint16_t out_addr, uint16_t count) { + int nbytes = ROUND_UP_32(ROUND_DOWN_16(count)); +#else +void aMixImpl(int16_t gain, uint16_t in_addr, uint16_t out_addr) { + int nbytes = ROUND_UP_32(rspa.nbytes); +#endif + int16_t *in = BUF_S16(in_addr); + int16_t *out = BUF_S16(out_addr); +#if HAS_SSE41 + __m128i gain_vec = _mm_set1_epi16(gain); +#elif !HAS_NEON + int i; + int32_t sample; +#endif + +#if !HAS_NEON + if (gain == -0x8000) { + while (nbytes > 0) { +#if HAS_SSE41 + __m128i out1, out2, in1, in2; + out1 = _mm_loadu_si128((const __m128i *)out); + out2 = _mm_loadu_si128((const __m128i *)(out + 8)); + in1 = _mm_loadu_si128((const __m128i *)in); + in2 = _mm_loadu_si128((const __m128i *)(in + 8)); + + out1 = _mm_subs_epi16(out1, in1); + out2 = _mm_subs_epi16(out2, in2); + + _mm_storeu_si128((__m128i *)out, out1); + _mm_storeu_si128((__m128i *)(out + 8), out2); + + out += 16; + in += 16; +#else + for (i = 0; i < 16; i++) { + sample = *out - *in++; + *out++ = clamp16(sample); + } +#endif + + nbytes -= 16 * sizeof(int16_t); + } + } +#endif + + while (nbytes > 0) { +#if HAS_SSE41 + __m128i out1, out2, in1, in2; + out1 = _mm_loadu_si128((const __m128i *)out); + out2 = _mm_loadu_si128((const __m128i *)(out + 8)); + in1 = _mm_loadu_si128((const __m128i *)in); + in2 = _mm_loadu_si128((const __m128i *)(in + 8)); + + out1 = _mm_adds_epi16(out1, _mm_mulhrs_epi16(in1, gain_vec)); + out2 = _mm_adds_epi16(out2, _mm_mulhrs_epi16(in2, gain_vec)); + + _mm_storeu_si128((__m128i *)out, out1); + _mm_storeu_si128((__m128i *)(out + 8), out2); + + out += 16; + in += 16; +#elif HAS_NEON + int16x8_t out1, out2, in1, in2; + out1 = vld1q_s16(out); + out2 = vld1q_s16(out + 8); + in1 = vld1q_s16(in); + in2 = vld1q_s16(in + 8); + + out1 = vqaddq_s16(out1, vqrdmulhq_n_s16(in1, gain)); + out2 = vqaddq_s16(out2, vqrdmulhq_n_s16(in2, gain)); + + vst1q_s16(out, out1); + vst1q_s16(out + 8, out2); + + out += 16; + in += 16; +#else + for (i = 0; i < 16; i++) { + sample = ((*out * 0x7fff + *in++ * gain) + 0x4000) >> 15; + *out++ = clamp16(sample); + } +#endif + + nbytes -= 16 * sizeof(int16_t); + } +} + +#ifdef NEW_AUDIO_UCODE +void aS8DecImpl(uint8_t flags, ADPCM_STATE state) { + uint8_t *in = BUF_U8(rspa.in); + int16_t *out = BUF_S16(rspa.out); + int nbytes = ROUND_UP_32(rspa.nbytes); + if (flags & A_INIT) { + memset(out, 0, 16 * sizeof(int16_t)); + } else if (flags & A_LOOP) { + memcpy(out, rspa.adpcm_loop_state, 16 * sizeof(int16_t)); + } else { + memcpy(out, state, 16 * sizeof(int16_t)); + } + out += 16; + + while (nbytes > 0) { + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + + nbytes -= 16 * sizeof(int16_t); + } + + memcpy(state, out - 16, 16 * sizeof(int16_t)); +} + +void aAddMixerImpl(uint16_t in_addr, uint16_t out_addr, uint16_t count) { + int16_t *in = BUF_S16(in_addr); + int16_t *out = BUF_S16(out_addr); + int nbytes = ROUND_UP_64(ROUND_DOWN_16(count)); + + do { + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + + nbytes -= 16 * sizeof(int16_t); + } while (nbytes > 0); +} + +void aDuplicateImpl(uint16_t in_addr, uint16_t out_addr, uint16_t count) { + uint8_t *in = BUF_U8(in_addr); + uint8_t *out = BUF_U8(out_addr); + + uint8_t tmp[128]; + memcpy(tmp, in, 128); + do { + memcpy(out, tmp, 128); + out += 128; + } while (count-- > 0); +} + +void aDMEMMove2Impl(uint8_t t, uint16_t in_addr, uint16_t out_addr, uint16_t count) { + uint8_t *in = BUF_U8(in_addr); + uint8_t *out = BUF_U8(out_addr); + int nbytes = ROUND_UP_32(count); + + do { + memmove(out, in, nbytes); + in += nbytes; + out += nbytes; + } while (t-- > 0); +} + +void aResampleZohImpl(uint16_t pitch, uint16_t start_fract) { + int16_t *in = BUF_S16(rspa.in); + int16_t *out = BUF_S16(rspa.out); + int nbytes = ROUND_UP_8(rspa.nbytes); + uint32_t pos = start_fract; + uint32_t pitch_add = pitch << 2; + + do { + *out++ = in[pos >> 17]; pos += pitch_add; + *out++ = in[pos >> 17]; pos += pitch_add; + *out++ = in[pos >> 17]; pos += pitch_add; + *out++ = in[pos >> 17]; pos += pitch_add; + + nbytes -= 4 * sizeof(int16_t); + } while (nbytes > 0); +} + +void aDownsampleHalfImpl(uint16_t n_samples, uint16_t in_addr, uint16_t out_addr) { + int16_t *in = BUF_S16(in_addr); + int16_t *out = BUF_S16(out_addr); + int n = ROUND_UP_8(n_samples); + + do { + *out++ = *in++; in++; + *out++ = *in++; in++; + *out++ = *in++; in++; + *out++ = *in++; in++; + *out++ = *in++; in++; + *out++ = *in++; in++; + *out++ = *in++; in++; + *out++ = *in++; in++; + + n -= 8; + } while (n > 0); +} + +void aFilterImpl(uint8_t flags, uint16_t count_or_buf, int16_t state_or_filter[8]) { + if (flags > A_INIT) { + rspa.filter_count = ROUND_UP_16(count_or_buf); + memcpy(rspa.filter, state_or_filter, sizeof(rspa.filter)); + } else { + int16_t tmp[16]; + int count = rspa.filter_count; + int16_t *buf = BUF_S16(count_or_buf); + + if (flags == A_INIT) { + memset(tmp, 0, 8 * sizeof(int16_t)); + } else { + memcpy(tmp, state_or_filter, 8 * sizeof(int16_t)); + } + + do { + memcpy(tmp + 8, buf, 8 * sizeof(int16_t)); + for (int i = 0; i < 8; i++) { + int64_t sample = 0x4000; // round term + int16_t in = tmp[8 + i]; + for (int j = 1; j <= 8; j++) { + sample += in * tmp[8 + i - j]; + } + buf[i] = clamp16((int32_t)(sample >> 15)); + } + memcpy(tmp, tmp + 8, 8 * sizeof(int16_t)); + + buf += 8; + count -= 8 * sizeof(int16_t); + } while (count > 0); + + memcpy(state_or_filter, tmp, 8 * sizeof(int16_t)); + } +} + +void aHiLoGainImpl(uint8_t g, uint16_t count, uint16_t addr) { + int16_t *samples = BUF_S16(addr); + int nbytes = ROUND_UP_32(count); + + do { + *samples = clamp16((*samples * g) >> 4); samples++; + *samples = clamp16((*samples * g) >> 4); samples++; + *samples = clamp16((*samples * g) >> 4); samples++; + *samples = clamp16((*samples * g) >> 4); samples++; + *samples = clamp16((*samples * g) >> 4); samples++; + *samples = clamp16((*samples * g) >> 4); samples++; + *samples = clamp16((*samples * g) >> 4); samples++; + *samples = clamp16((*samples * g) >> 4); samples++; + + nbytes -= 8; + } while (nbytes > 0); +} + +void aUnknown25Impl(uint8_t f, uint16_t count, uint16_t out_addr, uint16_t in_addr) { + int nbytes = ROUND_UP_64(count); + int16_t *in = BUF_S16(in_addr + f); + int16_t *out = BUF_S16(out_addr); + int16_t tbl[32]; + + memcpy(tbl, in, 32 * sizeof(int16_t)); + do { + for (int i = 0; i < 32; i++) { + out[i] = clamp16(out[i] * tbl[i]); + } + out += 32; + nbytes -= 32 * sizeof(int16_t); + } while (nbytes > 0); +} +#endif diff --git a/src/decomp/pc/mixer.h b/src/decomp/pc/mixer.h new file mode 100644 index 0000000..5debc7f --- /dev/null +++ b/src/decomp/pc/mixer.h @@ -0,0 +1,117 @@ +#ifndef MIXER_H +#define MIXER_H + +#include +#include +#include + +#ifdef VERSION_SH +#define NEW_AUDIO_UCODE +#endif + +#undef aSegment +#undef aClearBuffer +#undef aSetBuffer +#undef aLoadBuffer +#undef aSaveBuffer +#undef aDMEMMove +#undef aMix +#undef aEnvMixer +#undef aResample +#undef aInterleave +#undef aSetVolume +#undef aSetVolume32 +#undef aSetLoop +#undef aLoadADPCM +#undef aADPCMdec +#undef aS8Dec +#undef aAddMixer +#undef aDuplicate +#undef aDMEMMove2 +#undef aResampleZoh +#undef aDownsampleHalf +#undef aEnvSetup1 +#undef aEnvSetup2 +#undef aFilter +#undef aHiLoGain +#undef aUnknown25 + +void aClearBufferImpl(uint16_t addr, int nbytes); +void aLoadADPCMImpl(int num_entries_times_16, const int16_t *book_source_addr); +void aSetBufferImpl(uint8_t flags, uint16_t in, uint16_t out, uint16_t nbytes); +void aDMEMMoveImpl(uint16_t in_addr, uint16_t out_addr, int nbytes); +void aSetLoopImpl(ADPCM_STATE *adpcm_loop_state); +void aADPCMdecImpl(uint8_t flags, ADPCM_STATE state); +void aResampleImpl(uint8_t flags, uint16_t pitch, RESAMPLE_STATE state); + +#ifndef NEW_AUDIO_UCODE +void aSetVolumeImpl(uint8_t flags, int16_t v, int16_t t, int16_t r); +void aLoadBufferImpl(const void *source_addr); +void aSaveBufferImpl(int16_t *dest_addr); +void aInterleaveImpl(uint16_t left, uint16_t right); +void aMixImpl(int16_t gain, uint16_t in_addr, uint16_t out_addr); +void aEnvMixerImpl(uint8_t flags, ENVMIX_STATE state); +#else +void aLoadBufferImpl(const void *source_addr, uint16_t dest_addr, uint16_t nbytes); +void aSaveBufferImpl(uint16_t source_addr, int16_t *dest_addr, uint16_t nbytes); +void aInterleaveImpl(uint16_t dest, uint16_t left, uint16_t right, uint16_t c); +void aMixImpl(int16_t gain, uint16_t in_addr, uint16_t out_addr, uint16_t count); +void aEnvSetup1Impl(uint8_t initial_vol_wet, uint16_t rate_wet, uint16_t rate_left, uint16_t rate_right); +void aEnvSetup2Impl(uint16_t initial_vol_left, uint16_t initial_vol_right); +void aEnvMixerImpl(uint16_t in_addr, uint16_t n_samples, bool swap_reverb, + bool neg_left, bool neg_right, + uint16_t dry_left_addr, uint16_t dry_right_addr, + uint16_t wet_left_addr, uint16_t wet_right_addr); +void aS8DecImpl(uint8_t flags, ADPCM_STATE state); +void aAddMixerImpl(uint16_t in_addr, uint16_t out_addr, uint16_t count); +void aDuplicateImpl(uint16_t in_addr, uint16_t out_addr, uint16_t count); +void aDMEMMove2Impl(uint8_t t, uint16_t in_addr, uint16_t out_addr, uint16_t count); +void aResampleZohImpl(uint16_t pitch, uint16_t start_fract); +void aDownsampleHalfImpl(uint16_t n_samples, uint16_t in_addr, uint16_t out_addr); +void aFilterImpl(uint8_t flags, uint16_t count_or_buf, int16_t state_or_filter[8]); +void aHiLoGainImpl(uint8_t g, uint16_t count, uint16_t addr); +void aUnknown25Impl(uint8_t f, uint16_t count, uint16_t out_addr, uint16_t in_addr); +#endif + +#define aSegment(pkt, s, b) do { } while(0) +#define aClearBuffer(pkt, d, c) aClearBufferImpl(d, c) +#define aLoadADPCM(pkt, c, d) aLoadADPCMImpl(c, d) +#define aSetBuffer(pkt, f, i, o, c) aSetBufferImpl(f, i, o, c) +#define aDMEMMove(pkt, i, o, c) aDMEMMoveImpl(i, o, c) +#define aSetLoop(pkt, a) aSetLoopImpl(a) +#define aADPCMdec(pkt, f, s) aADPCMdecImpl(f, s) +#define aResample(pkt, f, p, s) aResampleImpl(f, p, s) + +#ifndef NEW_AUDIO_UCODE +#define aSetVolume(pkt, f, v, t, r) aSetVolumeImpl(f, v, t, r) +#define aSetVolume32(pkt, f, v, tr) aSetVolume(pkt, f, v, (int16_t)((tr) >> 16), (int16_t)(tr)) +#define aLoadBuffer(pkt, s) aLoadBufferImpl(s) +#define aSaveBuffer(pkt, s) aSaveBufferImpl(s) +#define aInterleave(pkt, l, r) aInterleaveImpl(l, r) +#define aMix(pkt, f, g, i, o) aMixImpl(g, i, o) +#define aEnvMixer(pkt, f, s) aEnvMixerImpl(f, s) +#else +#define aLoadBuffer(pkt, s, d, c) aLoadBufferImpl(s, d, c) +#define aSaveBuffer(pkt, s, d, c) aSaveBufferImpl(s, d, c) +#define aInterleave(pkt, o, l, r, c) aInterleaveImpl(o, l, r, c) +#define aMix(pkt, g, i, o, c) aMixImpl(g, i, o, c) +#define aEnvSetup1(pkt, initialVolReverb, rampReverb, rampLeft, rampRight) \ + aEnvSetup1Impl(initialVolReverb, rampReverb, rampLeft, rampRight) +#define aEnvSetup2(pkt, initialVolLeft, initialVolRight) \ + aEnvSetup2Impl(initialVolLeft, initialVolRight) +#define aEnvMixer(pkt, inBuf, nSamples, swapReverb, negLeft, negRight, \ + dryLeft, dryRight, wetLeft, wetRight) \ + aEnvMixerImpl(inBuf, nSamples, swapReverb, negLeft, negRight, \ + dryLeft, dryRight, wetLeft, wetRight) +#define aS8Dec(pkt, f, s) aS8DecImpl(f, s) +#define aAddMixer(pkt, s, d, c) aAddMixerImpl(s, d, c) +#define aDuplicate(pkt, s, d, c) aDuplicateImpl(s, d, c) +#define aDMEMMove2(pkt, t, i, o, c) aDMEMMove2Impl(t, i, o, c) +#define aResampleZoh(pkt, pitch, startFract) aResampleZohImpl(pitch, startFract) +#define aDownsampleHalf(pkt, nSamples, i, o) aDownsampleHalfImpl(nSamples, i, o) +#define aFilter(pkt, f, countOrBuf, addr) aFilterImpl(f, countOrBuf, addr) +#define aHiLoGain(pkt, g, buflen, i) aHiLoGainImpl(g, buflen, i) +#define aUnknown25(pkt, f, c, o, i) aUnknown25Impl(f, c, o, i) +#endif + +#endif diff --git a/src/decomp/pc/ultra_reimplementation.c b/src/decomp/pc/ultra_reimplementation.c new file mode 100644 index 0000000..4957cce --- /dev/null +++ b/src/decomp/pc/ultra_reimplementation.c @@ -0,0 +1,216 @@ +#include +#include +#include "libultra_internal.h" +#include + +#ifdef TARGET_WEB +#include +#endif + +extern OSMgrArgs piMgrArgs; + +u64 osClockRate = 62500000; + +s32 osPiStartDma(UNUSED OSIoMesg *mb, UNUSED s32 priority, UNUSED s32 direction, + uintptr_t devAddr, void *vAddr, size_t nbytes, + UNUSED OSMesgQueue *mq) { + memcpy(vAddr, (const void *) devAddr, nbytes); + return 0; +} + +void osCreateMesgQueue(OSMesgQueue *mq, OSMesg *msgBuf, s32 count) { + mq->validCount = 0; + mq->first = 0; + mq->msgCount = count; + mq->msg = msgBuf; + return; +} + +void osSetEventMesg(UNUSED OSEvent e, UNUSED OSMesgQueue *mq, UNUSED OSMesg msg) { +} +s32 osJamMesg(UNUSED OSMesgQueue *mq, UNUSED OSMesg msg, UNUSED s32 flag) { + return 0; +} +s32 osSendMesg(UNUSED OSMesgQueue *mq, UNUSED OSMesg msg, UNUSED s32 flag) { +#if defined(VERSION_EU) || defined(VERSION_SH) + s32 index; + if (mq->validCount >= mq->msgCount) { + return -1; + } + index = (mq->first + mq->validCount) % mq->msgCount; + mq->msg[index] = msg; + mq->validCount++; +#endif + return 0; +} +s32 osRecvMesg(UNUSED OSMesgQueue *mq, UNUSED OSMesg *msg, UNUSED s32 flag) { +#if defined(VERSION_EU) || defined(VERSION_SH) + if (mq->validCount == 0) { + return -1; + } + if (msg != NULL) { + *msg = *(mq->first + mq->msg); + } + mq->first = (mq->first + 1) % mq->msgCount; + mq->validCount--; +#endif + return 0; +} + +uintptr_t osVirtualToPhysical(void *addr) { + return (uintptr_t) addr; +} + +void osCreateViManager(UNUSED OSPri pri) { +} +void osViSetMode(UNUSED OSViMode *mode) { +} +void osViSetEvent(UNUSED OSMesgQueue *mq, UNUSED OSMesg msg, UNUSED u32 retraceCount) { +} +void osViBlack(UNUSED u8 active) { +} +void osViSetSpecialFeatures(UNUSED u32 func) { +} +void osViSwapBuffer(UNUSED void *vaddr) { +} + +OSTime osGetTime(void) { + return 0; +} + +void osWritebackDCacheAll(void) { +} + +void osWritebackDCache(UNUSED void *a, UNUSED size_t b) { +} + +void osInvalDCache(UNUSED void *a, UNUSED size_t b) { +} + +u32 osGetCount(void) { + static u32 counter; + return counter++; +} + +s32 osAiSetFrequency(u32 freq) { + u32 a1; + s32 a2; + u32 D_8033491C; + +#ifdef VERSION_EU + D_8033491C = 0x02E6025C; +#else + D_8033491C = 0x02E6D354; +#endif + + a1 = D_8033491C / (float) freq + .5f; + + if (a1 < 0x84) { + return -1; + } + + a2 = (a1 / 66) & 0xff; + if (a2 > 16) { + a2 = 16; + } + + return D_8033491C / (s32) a1; +} + +s32 osEepromProbe(UNUSED OSMesgQueue *mq) { + return 1; +} + +s32 osEepromLongRead(UNUSED OSMesgQueue *mq, u8 address, u8 *buffer, int nbytes) { + u8 content[512]; + s32 ret = -1; + +#ifdef TARGET_WEB + if (EM_ASM_INT({ + var s = localStorage.sm64_save_file; + if (s && s.length === 684) { + try { + var binary = atob(s); + if (binary.length === 512) { + for (var i = 0; i < 512; i++) { + HEAPU8[$0 + i] = binary.charCodeAt(i); + } + return 1; + } + } catch (e) { + } + } + return 0; + }, content)) { + memcpy(buffer, content + address * 8, nbytes); + ret = 0; + } +#else + FILE *fp = fopen("sm64_save_file.bin", "rb"); + if (fp == NULL) { + return -1; + } + if (fread(content, 1, 512, fp) == 512) { + memcpy(buffer, content + address * 8, nbytes); + ret = 0; + } + fclose(fp); +#endif + return ret; +} + +s32 osEepromLongWrite(UNUSED OSMesgQueue *mq, u8 address, u8 *buffer, int nbytes) { + u8 content[512] = {0}; + if (address != 0 || nbytes != 512) { + osEepromLongRead(mq, 0, content, 512); + } + memcpy(content + address * 8, buffer, nbytes); + +#ifdef TARGET_WEB + EM_ASM({ + var str = ""; + for (var i = 0; i < 512; i++) { + str += String.fromCharCode(HEAPU8[$0 + i]); + } + localStorage.sm64_save_file = btoa(str); + }, content); + s32 ret = 0; +#else + FILE* fp = fopen("sm64_save_file.bin", "wb"); + if (fp == NULL) { + return -1; + } + s32 ret = fwrite(content, 1, 512, fp) == 512 ? 0 : -1; + fclose(fp); +#endif + return ret; +} + +s32 gNumVblanks; + +s32 osMotorInit(UNUSED OSMesgQueue *mq, UNUSED void *pfs, UNUSED int channel) { + return 0; +} + +s32 osMotorStart(UNUSED void *pfs) { + return 0; +} + +s32 osMotorStop(UNUSED void *pfs) { + return 0; +} + +OSPiHandle *osCartRomInit(void) { + static OSPiHandle handle; + return &handle; +} + +OSPiHandle *osDriveRomInit(void) { + static OSPiHandle handle; + return &handle; +} + +s32 osEPiStartDma(UNUSED OSPiHandle *pihandle, OSIoMesg *mb, UNUSED s32 direction) { + memcpy(mb->dramAddr, (const void *) mb->devAddr, mb->size); + osSendMesg(mb->hdr.retQueue, mb, OS_MESG_NOBLOCK); +} diff --git a/src/decomp/tools/convTypes.h b/src/decomp/tools/convTypes.h new file mode 100644 index 0000000..63dc5c8 --- /dev/null +++ b/src/decomp/tools/convTypes.h @@ -0,0 +1,97 @@ +#pragma once + +#include +#include + +#define TYPE_CTL 1 +#define TYPE_TBL 2 +#define TYPE_SEQ 3 + +struct CLoop +{ + unsigned int start; + unsigned int end; + int count; + unsigned int pad; + short state[1]; +}; + + +struct CBook +{ + int order; // must be 2 + int npredictors; // must be 2 + short table[32]; // 8 * order * npredictors +}; + +struct CSample{ + unsigned int zero; + uintptr_t addr; + struct CLoop* loop; // must not be null + struct CBook* book; // must not be null + unsigned int sample_size; +}; + +struct CSound{ + struct CSample* sample_addr; + float tuning; +}; + +struct delay_arg{ + unsigned short delay; + unsigned short arg; +}; + +struct CEnvelope{ + struct delay_arg delay_args[1]; // array of [(delay,arg)] +}; + +struct CDrum{ + unsigned char release_rate; + unsigned char pan; + unsigned char loaded; + unsigned char pad; + struct CSound snd; + struct CEnvelope* env_addr; +}; + +struct CInstrument{ + unsigned char loaded; + unsigned char normal_range_lo; + unsigned char normal_range_hi; + unsigned char release_rate; + struct CEnvelope* env_addr; + struct CSound sound_lo; + struct CSound sound_med; + struct CSound sound_hi; +}; + +struct TBL{ + unsigned char* data; +}; + +struct SEQ{ + unsigned char* data; +}; + +struct CTL +{ + unsigned int numInstruments; + unsigned int numDrums; + unsigned int shared; + unsigned int iso_date; + struct CDrum** drum_pointers; + struct CInstrument* instrument_pointers[1]; +}; + +struct seqObject{ + uintptr_t offset __attribute__((aligned (8))); + unsigned int len __attribute__((aligned (8))); +}; + +struct seqFile{ + unsigned short revision; + unsigned short seqCount; + unsigned int pad; + struct seqObject seqArray[1]; +} __attribute__((aligned (16))); \ No newline at end of file diff --git a/src/decomp/tools/convUtils.c b/src/decomp/tools/convUtils.c new file mode 100644 index 0000000..fa67888 --- /dev/null +++ b/src/decomp/tools/convUtils.c @@ -0,0 +1,374 @@ +#pragma once +#include "convUtils.h" + +#include "utils.h" +#include +#include +#include +#include "convUtils.h" +#include "convTypes.h" + +/** + * This code is based on the only documentation that exists (that I know of) for the SM64 CTL/TBL format + * as well as dylanpdx's audio extraction implementation. + * https://github.com/n64decomp/sm64/blob/1372ae1bb7cbedc03df366393188f4f05dcfc422/tools/disassemble_sound.py + * https://github.com/n64decomp/sm64/blob/1372ae1bb7cbedc03df366393188f4f05dcfc422/tools/assemble_sound.py + * https://github.com/Retro64Mod/libsm64-retro64 + */ + +#define ALIGN16(val) (((val) + 0xF) & ~0xF) + +unsigned char* gCtlSeqs; + +struct seqFile* parse_seqfile(unsigned char* seq){ /* Read SeqFile data */ + short revision = read_u16_be(seq); + short bankCount = read_u16_be(seq + 2); + + unsigned int size = sizeof(struct seqFile) + (bankCount-1) * sizeof(struct seqObject); + + struct seqFile* seqFile = (struct seqFile*)calloc(size, 1); + seqFile->revision = revision; + seqFile->seqCount = bankCount; + for (int i = 0; i < bankCount; i++){ // read bank offsets and sizes + seqFile->seqArray[i].offset = (uintptr_t)read_u32_be(seq + 4 + i * 8); + seqFile->seqArray[i].len = read_u32_be((seq + 4 + i * 8 + 4)); + } + + if (revision == TYPE_CTL){ + // CTL file, contains instrument and drum data, this is really the only one that needs to be parsed, the rest only needs a header change + gCtlSeqs = (unsigned char*)calloc(0x20B40, 1); // We only really need 0x20AD0 bytes but still + uintptr_t pos = (uintptr_t)gCtlSeqs; + for (int i = 0; i < bankCount; i++){ + uintptr_t start = pos; + struct CTL* ptr = parse_ctl_data(seq+(seqFile->seqArray[i].offset), &pos); + seqFile->seqArray[i].offset = ptr; + seqFile->seqArray[i].len = (unsigned int)(pos - start); + } + }else if (revision == TYPE_TBL){ + // TBL file, contains raw audio data + for (int i = 0; i < bankCount; i++){ + seqFile->seqArray[i].offset = seq+(seqFile->seqArray[i].offset); + } + }else if (revision == TYPE_SEQ){ + // SEQ file, contains music files (*.m64) + for (int i = 0; i < bankCount; i++){ + seqFile->seqArray[i].offset = seq+(seqFile->seqArray[i].offset); + } + } + + return seqFile; +} + +void ctl_free(){ + free(gCtlSeqs); +} + +void snd_ptrs_to_offsets(struct CSound* snd, uintptr_t ctlData){ + struct CSample* smp = snd->sample_addr; + if((uintptr_t)(smp->loop) > ctlData) + smp->loop = (struct CLoop*)((uintptr_t)(smp->loop) - ctlData); + if((uintptr_t)(smp->book) > ctlData) + smp->book = (struct CBook*)((uintptr_t)(smp->book) - ctlData); + snd->sample_addr = (struct CSample*)((uintptr_t)(snd->sample_addr) - ctlData); +} + +void ptrs_to_offsets(struct seqFile* ctl){ + if (ctl->revision != TYPE_CTL){ + return; + } + + for (int i = 0; i < ctl->seqCount; i++){ + struct CTL* ptr = (struct CTL*)ctl->seqArray[i].offset; + uintptr_t ctlData = (uintptr_t)ptr + 0x10; + // find all samples in the CTL file + for (int j = 0; j < ptr->numInstruments; j++){ + struct CInstrument* inst = ptr->instrument_pointers[j]; + if (inst==0x0) + continue; // null instrument. + inst->env_addr = (struct CEnvelope*)((uintptr_t)inst->env_addr - ctlData); + if (inst->sound_hi.sample_addr!=0x0){ + snd_ptrs_to_offsets(&(inst->sound_hi), ctlData); + } + if (inst->sound_med.sample_addr!=0x0){ + snd_ptrs_to_offsets(&(inst->sound_med), ctlData); + } + if (inst->sound_lo.sample_addr!=0x0){ + snd_ptrs_to_offsets(&(inst->sound_lo), ctlData); + } + ptr->instrument_pointers[j] = (struct CInstrument*)((uintptr_t)(inst) - ctlData); + } + if(ptr->numDrums != 0){ + for (int j = 0; j < ptr->numDrums; j++){ + struct CDrum* drum = ptr->drum_pointers[j]; + if (drum==0x0) + continue; // null drum. + drum->env_addr = (struct CEnvelope*)((uintptr_t)drum->env_addr - ctlData); + if (drum->snd.sample_addr!=0x0){ + snd_ptrs_to_offsets(&(drum->snd), ctlData); + } + ptr->drum_pointers[j] = (struct CDrum*)((uintptr_t)(drum) - ctlData); + } + ptr->drum_pointers = (struct CDrum**)((uintptr_t)(ptr->drum_pointers) - ctlData); + } + } +} + +struct CLoop* parse_loop(unsigned char* loop, uintptr_t* pos){ + uint32_t count = read_u32_be(loop + 8); // variable is signed, but the data is being read as unsigned. + unsigned int size = sizeof(struct CLoop) - 4; + if(count != 0){ + size = sizeof(struct CLoop) - 4 + sizeof(short) * 16; + } + struct CLoop* loop_ptr = (struct CLoop*)(*pos); + *pos += size; + *pos = ALIGN16(*pos); + + loop_ptr->start = read_u32_be(loop); + loop_ptr->end = read_u32_be(loop + 4); + loop_ptr->count = count; + loop_ptr->pad = read_u32_be(loop + 12); + + if (loop_ptr->count!=0){ + for (int i = 0;i<16;i++){ + loop_ptr->state[i]=read_u16_be(loop + 16 + i*2); + } + } + + return loop_ptr; +} + +struct CBook* parse_book(unsigned char* book, uintptr_t* pos){ + struct CBook* book_ptr = (struct CBook*)(*pos); + *pos += sizeof(struct CBook); + *pos = ALIGN16(*pos); + book_ptr->order = read_u32_be(book); + book_ptr->npredictors = read_u32_be(book + 4); // both are signed + unsigned char* table_data = book+8; + for (int i = 0; i < 8 * book_ptr->order * book_ptr->npredictors; i ++){ + book_ptr->table[i] = read_u16_be(table_data + i * 2); + } + return book_ptr; +} + +struct CSample* parse_sample(unsigned char* sample,unsigned char* ctl, uintptr_t* pos){ + struct CSample* samp = (struct CSample*)(*pos); + *pos += sizeof(struct CSample); + *pos = ALIGN16(*pos); + samp->zero=read_u32_be(sample); + samp->addr=read_u32_be(sample+4); + samp->loop=read_u32_be(sample+8);// loop address + samp->book=read_u32_be(sample+12);// book address + samp->sample_size=read_u32_be(sample+16); + + samp->book=parse_book(ctl+((uintptr_t)samp->book), pos); + samp->loop=parse_loop(ctl+((uintptr_t)samp->loop), pos); + return samp; +} + +struct CSound* parse_sound(unsigned char* sound,unsigned char* ctl, uintptr_t* pos, uintptr_t sndPos, struct SampleList* samples){ + struct CSound* snd = (struct CSound*)(sndPos); + snd->sample_addr=read_u32_be(sound); + snd->tuning = (float)read_f32_be(sound+4); + // if sample_addr is 0 then the sound is null + if (snd->sample_addr!=0){ + int smpIndex = -1; + for(int i = 0; i < samples->count; i++){ + if(samples->orig_addrs[i] == (uintptr_t)(snd->sample_addr)){ + smpIndex = i; + break; + } + } + if(smpIndex < 0){ + samples->orig_addrs[samples->count] = (uintptr_t)(snd->sample_addr); + snd->sample_addr = parse_sample(ctl+((uintptr_t)snd->sample_addr),ctl, pos); + samples->addrs[samples->count] = snd->sample_addr; + samples->count++; + } else { + snd->sample_addr = samples->addrs[smpIndex]; + } + } + return snd; +} + +struct CDrum* parse_drum(unsigned char* drum,unsigned char* ctl, uintptr_t* pos, struct SampleList* samples){ /* Read Drum data */ + struct CDrum* drumData = malloc(sizeof(struct CDrum)); + drumData->release_rate = drum[0]; + drumData->pan = drum[1]; + drumData->loaded = drum[2]; + drumData->pad = drum[3]; + drumData->snd=*parse_sound(drum+4,ctl, pos, &drumData->snd, samples); + drumData->env_addr=read_u32_be(drum+12); + return drumData; +} + +struct CEnvelope* parse_envelope(unsigned char* env, uintptr_t* pos, int* size){ + int count = 0; + while(1){ + unsigned short delay = read_u16_le(env + count * 4); + unsigned short arg = read_u16_le(env + count * 4 + 2); + unsigned short delayC = (-delay); + count++; + if ((1 <= delayC && delayC <= 3) || delay == 0) + break; + } + *size = sizeof(struct CEnvelope) + sizeof(struct delay_arg) * (count-1); + struct CEnvelope* envData = malloc(*size); + for (int i = 0; i < count; i++){ + envData->delay_args[i].delay = read_u16_le(env + i * 4); + envData->delay_args[i].arg = read_u16_le(env + i * 4 + 2); + } + return envData; +} + +struct CInstrument* parse_instrument(unsigned char* instrument,unsigned char* ctl, uintptr_t* pos, struct SampleList* samples){ + struct CInstrument* inst = malloc(sizeof(struct CInstrument)); + inst->loaded = instrument[0]; + inst->normal_range_lo = instrument[1]; + inst->normal_range_hi = instrument[2]; + inst->release_rate = instrument[3]; + inst->env_addr=read_u32_be(instrument+4); + inst->sound_lo=*parse_sound(instrument+8,ctl, pos, &(inst->sound_lo), samples); + inst->sound_med=*parse_sound(instrument+16,ctl, pos, &(inst->sound_med), samples); + inst->sound_hi=*parse_sound(instrument+24,ctl, pos, &(inst->sound_hi), samples); + + return inst; +} + +struct TBL* parse_tbl_data(unsigned char* tbl){ + struct TBL* tblData = malloc(sizeof(struct TBL)); + tblData->data = tbl; + return tblData; +} + + struct SEQ* parse_seq_data(unsigned char* seq){ + struct SEQ* seqData = malloc(sizeof(struct SEQ)); + seqData->data = seq; + return seqData; +} + +struct CTL* parse_ctl_data(unsigned char* ctlData, uintptr_t* pos){ + int instruments=read_u32_be(ctlData); + unsigned int size = sizeof(struct CTL) + sizeof(struct CInstrument*) * (instruments-1); + struct CTL* ctl = (struct CTL*)(*pos); + *pos += size; + *pos = ALIGN16(*pos); + #pragma region Parse CTL header + ctl->numInstruments = read_u32_be(ctlData); + ctl->numDrums = read_u32_be(ctlData + 4); + ctl->shared = read_u32_be(ctlData + 8); + ctl->iso_date = read_u32_be(ctlData + 12); + #pragma endregion + struct SampleList samples = {0}; + struct EnvelopeMeta envData[128] = {0}; + int envCount = 0; + samples.count = 0; + // header parsed, now read data + if(ctl->numDrums != 0) { + ctl->drum_pointers= (struct CDrum**)(*pos); + size = sizeof(struct CDrum*) * ctl->numDrums; + *pos += size; + *pos = ALIGN16(*pos); + int drumTablePtr = read_u32_be(ctlData + 16); + for (int i = 0; i < ctl->numDrums; i++){ + uint32_t data = read_u32_be(ctlData + drumTablePtr+16 + i * 4); + + struct CDrum* d = parse_drum(ctlData+data+16,ctlData+16, pos, &samples); + bool used = 0; + for(int j = 0; j < envCount; j++){ + if(envData[j].orig == (uintptr_t)d->env_addr){ + used = 1; + break; + } + } + if(used == 0){ + int size = 0; + unsigned char* addr = ctlData+((uintptr_t)d->env_addr)+16; + envData[envCount].orig = (uintptr_t)(d->env_addr); + envData[envCount].addr = parse_envelope(addr, pos, &size); + envData[envCount].size = size; + envCount++; + } + ctl->drum_pointers[i] = d; + } + *pos = ALIGN16(*pos); + } else { + ctl->drum_pointers= NULL; + } + // parse instrument data + int instTablePtr = 4; + for (int i = 0; i < ctl->numInstruments; i++){ + uint32_t data = read_u32_be(ctlData + 16 + instTablePtr + i * 4); + if (data == 0) + continue; + struct CInstrument* inst = parse_instrument(ctlData+16+data,ctlData+16, pos, &samples); + bool used = 0; + for(int j = 0; j < envCount; j++){ + if(envData[j].orig == (uintptr_t)inst->env_addr){ + used = 1; + break; + } + } + if(used == 0){ + int size = 0; + unsigned char* addr = ctlData+((uintptr_t)inst->env_addr)+16; + envData[envCount].orig = (uintptr_t)(inst->env_addr); + envData[envCount].addr = parse_envelope(addr, pos, &size); + envData[envCount].size = size; + envCount++; + } + ctl->instrument_pointers[i] = inst; + } + *pos = ALIGN16(*pos); + + // Copy envelopes to ctl + for (int i = 0; i < envCount; i++){ + struct CEnvelope* env = envData[i].addr; + memcpy((uint8_t*)(*pos), env, envData[i].size); + for (int j = 0; j < ctl->numInstruments; j++){ + struct CInstrument* inst = ctl->instrument_pointers[j]; + if (inst == 0x0) + continue; + if((uintptr_t)(inst->env_addr) == envData[i].orig){ + inst->env_addr = (struct CEnvelope*)(*pos); + } + } + for (int j = 0; j < ctl->numDrums; j++){ + struct CDrum* drum = ctl->drum_pointers[j]; + if (drum == 0x0) + continue; + if((uintptr_t)(drum->env_addr) == envData[i].orig){ + drum->env_addr = (struct CEnvelope*)(*pos); + } + } + free(env); + *pos += envData[i].size; + } + *pos = ALIGN16(*pos); + + // Copy instruments to ctl + for (int i = 0; i < ctl->numInstruments; i++){ + struct CInstrument* inst = ctl->instrument_pointers[i]; + if (inst == 0x0) + continue; + memcpy((uint8_t*)(*pos), inst, sizeof(struct CInstrument)); + free(inst); + ctl->instrument_pointers[i] = (struct CInstrument*)(*pos); + *pos += sizeof(struct CInstrument); + } + *pos = ALIGN16(*pos); + + // Copy drums to ctl + for (int i = 0; i < ctl->numDrums; i++){ + struct CDrum* drum = ctl->drum_pointers[i]; + if (drum == 0x0) + continue; + memcpy((uint8_t*)(*pos), drum, sizeof(struct CDrum)); + free(drum); + ctl->drum_pointers[i] = (struct CDrum*)(*pos); + *pos += sizeof(struct CDrum); + } + *pos = ALIGN16(*pos); + // + + return ctl; +} \ No newline at end of file diff --git a/src/decomp/tools/convUtils.h b/src/decomp/tools/convUtils.h new file mode 100644 index 0000000..5f5b30f --- /dev/null +++ b/src/decomp/tools/convUtils.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include "../pc/libaudio_internal.h" + +#define read_u16_le(p) ((uint8_t*)p)[1] * 0x100u + ((uint8_t*)p)[0] + +struct EnvelopeMeta { + uintptr_t orig; + struct CEnvelope* addr; + int size; +}; + +struct SampleList { + int count; + uintptr_t orig_addrs[256]; + struct CSample* addrs[256]; +}; + +struct seqFile* parse_seqfile(unsigned char* seq); +struct CTL* parse_ctl_data(unsigned char* ctlData, uintptr_t* pos); +struct TBL* parse_tbl_data(unsigned char* tbl); +struct SEQ* parse_seq_data(unsigned char* seq); +void ptrs_to_offsets(struct seqFile* ctl); +void ctl_free(); +#define INITIAL_GFX_ALLOC 10 +#define INITIAL_GEO_ALLOC 10 \ No newline at end of file diff --git a/src/libsm64.c b/src/libsm64.c index dcc32a5..c3df3e7 100644 --- a/src/libsm64.c +++ b/src/libsm64.c @@ -10,6 +10,7 @@ #include #include +#include "decomp/audio/external.h" #include "decomp/include/PR/os_cont.h" #include "decomp/engine/math_util.h" #include "decomp/include/sm64.h" @@ -29,6 +30,7 @@ #include "load_surfaces.h" #include "gfx_adapter.h" #include "load_anim_data.h" +#include "load_audio_data.h" #include "load_tex_data.h" #include "obj_pool.h" @@ -134,6 +136,30 @@ SM64_LIB_FN void sm64_global_terminate( void ) memory_terminate(); } +SM64_LIB_FN void sm64_audio_init( uint8_t *rom ) { + load_audio_banks( rom ); +} + +#define SAMPLES_HIGH 544 +#define SAMPLES_LOW 528 + +extern SM64_LIB_FN uint32_t sm64_audio_tick( uint32_t numQueuedSamples, uint32_t numDesiredSamples, int16_t *audio_buffer ) { + if ( !is_audio_initialized ) { + DEBUG_PRINT("Attempted to tick audio, but sm64_audio_init() has not been called yet."); + return 0; + } + + update_game_sound(); + + u32 num_audio_samples = numQueuedSamples < numDesiredSamples ? SAMPLES_HIGH : SAMPLES_LOW; + for (int i = 0; i < 2; i++) + { + create_next_audio_buffer( audio_buffer + i * ( 2 * num_audio_samples ), num_audio_samples ); + } + + return num_audio_samples; +} + SM64_LIB_FN void sm64_static_surfaces_load( const struct SM64Surface *surfaceArray, uint32_t numSurfaces ) { surfaces_load_static( surfaceArray, numSurfaces ); diff --git a/src/libsm64.h b/src/libsm64.h index 3621091..447f71b 100644 --- a/src/libsm64.h +++ b/src/libsm64.h @@ -133,6 +133,9 @@ extern SM64_LIB_FN void sm64_register_play_sound_function( SM64PlaySoundFunction extern SM64_LIB_FN void sm64_global_init( uint8_t *rom, uint8_t *outTexture ); extern SM64_LIB_FN void sm64_global_terminate( void ); +extern SM64_LIB_FN void sm64_audio_init( uint8_t *rom ); +extern SM64_LIB_FN uint32_t sm64_audio_tick( uint32_t numQueuedSamples, uint32_t numDesiredSamples, int16_t *audio_buffer ); + extern SM64_LIB_FN void sm64_static_surfaces_load( const struct SM64Surface *surfaceArray, uint32_t numSurfaces ); extern SM64_LIB_FN int32_t sm64_mario_create( float x, float y, float z ); diff --git a/src/load_audio_data.c b/src/load_audio_data.c new file mode 100644 index 0000000..5fe394f --- /dev/null +++ b/src/load_audio_data.c @@ -0,0 +1,27 @@ +#include "load_audio_data.h" + +#include "decomp/tools/convUtils.h" +#include "decomp/audio/load.h" +#include "decomp/audio/load_dat.h" + +bool is_audio_initialized = false; + +extern void load_audio_banks( uint8_t *rom ) { + uint8_t *rom2 = malloc( 0x800000 ); + + memcpy( rom2, rom, 0x800000 ); + rom = rom2; + gSoundDataADSR = parse_seqfile( rom+0x57B720 ); //ctl + gSoundDataRaw = parse_seqfile( rom+0x593560 ); //tbl + gMusicData = parse_seqfile( rom+0x7B0860 ); + gBankSetsData = rom+0x7CC621; + memmove( gBankSetsData+0x45,gBankSetsData+0x45-1,0x5B ); + gBankSetsData[0x45]=0x00; + ptrs_to_offsets( gSoundDataADSR ); + + audio_init(); + sound_init(); + sound_reset( 0 ); + + is_audio_initialized = true; +} \ No newline at end of file diff --git a/src/load_audio_data.h b/src/load_audio_data.h new file mode 100644 index 0000000..8e2140c --- /dev/null +++ b/src/load_audio_data.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include + +extern bool is_audio_initialized; + +extern void load_audio_banks( uint8_t *rom ); \ No newline at end of file diff --git a/src/play_sound.c b/src/play_sound.c index 0560ba3..59ccf5b 100644 --- a/src/play_sound.c +++ b/src/play_sound.c @@ -1,9 +1,20 @@ #include "play_sound.h" +#include "decomp/audio/external.h" +#include "debug_print.h" +#include "load_audio_data.h" + SM64PlaySoundFunctionPtr g_play_sound_func = NULL; extern void play_sound( uint32_t soundBits, f32 *pos ) { - if ( g_play_sound_func ) { - g_play_sound_func(soundBits, pos); - } + if ( is_audio_initialized ) { + DEBUG_PRINT("$ play_sound(%d) request %d; pos %f %f %f\n", soundBits,sSoundRequestCount,pos[0],pos[1],pos[2]); + sSoundRequests[sSoundRequestCount].soundBits = soundBits; + sSoundRequests[sSoundRequestCount].position = pos; + sSoundRequestCount++; + } + + if ( g_play_sound_func ) { + g_play_sound_func(soundBits, pos); + } } \ No newline at end of file