diff --git a/.gitignore b/.gitignore index 865c8fbc..2df2cc15 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ ctx.c.m2c *.otr *.eeprom assets/yaml/us/ast_test.yaml +/audio_data src/assets/* include/assets/* /build diff --git a/include/alignment.h b/include/alignment.h index e26a1d3f..5a011c0a 100644 --- a/include/alignment.h +++ b/include/alignment.h @@ -7,6 +7,8 @@ #define ALIGN64(val) (((val) + 0x3F) & ~0x3F) #define ALIGN256(val) (((val) + 0xFF) & ~0xFF) +#define ALIGN16_ALT(val) (((val) & ~0xF) + 0x10) + #ifdef __GNUC__ #define ALIGNED8 __attribute__ ((aligned (8))) #else diff --git a/include/audioseq_cmd.h b/include/audioseq_cmd.h index 1f2deed3..6df7788e 100644 --- a/include/audioseq_cmd.h +++ b/include/audioseq_cmd.h @@ -9,7 +9,8 @@ typedef enum { /* 0 */ SEQ_PLAYER_BGM, /* 1 */ SEQ_PLAYER_FANFARE, /* 2 */ SEQ_PLAYER_SFX, - /* 3 */ SEQ_PLAYER_VOICE + /* 3 */ SEQ_PLAYER_VOICE, + /* 4 */ SEQ_PLAYER_MAX, } SequencePlayerId; // ==== Primary commands ==== diff --git a/include/audiothread_cmd.h b/include/audiothread_cmd.h index 2a9a013c..74634286 100644 --- a/include/audiothread_cmd.h +++ b/include/audiothread_cmd.h @@ -42,7 +42,9 @@ typedef enum { /* 0x81 */ AUDIOCMD_OP_GLOBAL_SYNC_LOAD_SEQ_PARTS = 0x81, /* 0x82 */ AUDIOCMD_OP_GLOBAL_INIT_SEQPLAYER, /* 0x83 */ AUDIOCMD_OP_GLOBAL_DISABLE_SEQPLAYER, - /* 0x85 */ AUDIOCMD_OP_GLOBAL_INIT_SEQPLAYER_SKIP_TICKS = 0x85, + /* 0x84 */ AUDIOCMD_OP_GLOBAL_UNK_84, + /* 0x85 */ AUDIOCMD_OP_GLOBAL_INIT_SEQPLAYER_SKIP_TICKS, + /* 0x88 */ AUDIOCMD_OP_GLOBAL_INIT_SEQPLAYER_ALT = 0x88, /* 0x90 */ AUDIOCMD_OP_GLOBAL_SET_CHANNEL_MASK = 0x90, /* 0xE0 */ AUDIOCMD_OP_GLOBAL_SET_DRUM_FONT = 0xE0, /* 0xE1 */ AUDIOCMD_OP_GLOBAL_SET_SFX_FONT, @@ -410,7 +412,7 @@ typedef enum { /** * Pop the persistent cache of the specified table * - * @param tableType (s32) see the `SampleBankTableType` enum + * @param tableType (s32) see the `AudioTableType` enum */ #define AUDIOCMD_GLOBAL_POP_PERSISTENT_CACHE(tableType) \ AudioThread_QueueCmdS32(AUDIO_MK_CMD(AUDIOCMD_OP_GLOBAL_POP_PERSISTENT_CACHE, 0, 0, 0), tableType) diff --git a/include/sf64audio_provisional.h b/include/sf64audio_provisional.h index 631895b5..0b14777f 100644 --- a/include/sf64audio_provisional.h +++ b/include/sf64audio_provisional.h @@ -70,6 +70,9 @@ typedef void (*AudioCustomUpdateFunction)(void); #define AUDIO_RELOCATED_ADDRESS_START K0BASE +#define AUDIOLOAD_SYNC 0 +#define AUDIOLOAD_ASYNC 1 + typedef enum { /* 0 */ ADSR_STATE_DISABLED, /* 1 */ ADSR_STATE_INITIAL, @@ -98,11 +101,22 @@ typedef enum { /* 5 */ CODEC_S16 } SampleCodec; +typedef enum { + /* 0 */ SAMPLES_SFX, + /* 1 */ SAMPLES_MAP, + /* 2 */ SAMPLES_VOICE, + /* 3 */ SAMPLES_INST, + /* 4 */ SAMPLES_MAX, + /* -1 */ SAMPLES_NONE = 255, +} SampleBank; + +#define SAMPLES_NONE_U 255U + typedef enum { /* 0 */ SEQUENCE_TABLE, /* 1 */ FONT_TABLE, /* 2 */ SAMPLE_TABLE -} SampleBankTableType; +} AudioTableType; typedef enum { /* 0 */ CACHE_TEMPORARY, @@ -111,6 +125,14 @@ typedef enum { /* 3 */ CACHE_PERMANENT } AudioCacheType; +typedef enum { + /* 0 */ CACHEPOLICY_0, + /* 1 */ CACHEPOLICY_1, + /* 2 */ CACHEPOLICY_2, + /* 3 */ CACHEPOLICY_3, + /* 4 */ CACHEPOLICY_4, +} AudioCachePolicy; + typedef enum { /* 0 */ LOAD_STATUS_NOT_LOADED, // the entry data is not loaded /* 1 */ LOAD_STATUS_IN_PROGRESS, // the entry data is being loaded asynchronously @@ -122,6 +144,20 @@ typedef enum { /* 5 */ LOAD_STATUS_PERMANENTLY_LOADED // the entry data is loaded in the permanent pool, it won't be discarded } AudioLoadStatus; +typedef enum { + /* 0 */ SLOW_LOAD_WAITING, + /* 1 */ SLOW_LOAD_START, + /* 2 */ SLOW_LOAD_LOADING, + /* 3 */ SLOW_LOAD_DONE +} SlowLoadState; + +typedef enum { + /* 0 */ SLOW_LOAD_STATUS_0, + /* 1 */ SLOW_LOAD_STATUS_1, + /* 2 */ SLOW_LOAD_STATUS_2, + /* 3 */ SLOW_LOAD_STATUS_3 +} SlowLoadStatus; + typedef enum AudioResetStatus { /* 0 */ AUDIORESET_READY, /* 1 */ AUDIORESET_WAIT, @@ -321,7 +357,7 @@ typedef struct { /* 0x02C */ f32 fadeVolumeMod; /* 0x030 */ f32 appliedFadeVolume; // /* 0x034 */ f32 bend; - /* 0x034 */ struct SequenceChannel* channels[16]; + /* 0x034 */ struct SequenceChannel* channels[SEQ_NUM_CHANNELS]; /* 0x074 */ SeqScriptState scriptState; /* 0x090 */ u8* shortNoteVelocityTable; /* 0x094 */ u8* shortNoteGateTimeTable; @@ -794,6 +830,13 @@ typedef struct { /* 0x0E */ s16 shortData3; } AudioTableEntry; // size = 0x10 +typedef struct { + /* 0x00 */ s16 numEntries; + /* 0x02 */ s16 unkMediumParam; + /* 0x04 */ u32 romAddr; + /* 0x08 */ char pad[0x8]; +} AudioTableBase; + typedef struct { /* 0x00 */ s16 numEntries; /* 0x02 */ s16 unkMediumParam; @@ -1000,7 +1043,7 @@ void* AudioHeap_Alloc(AudioAllocPool* pool, u32 size); void AudioHeap_InitPool(AudioAllocPool* pool, void* ramAddr, u32 size); void AudioHeap_InitMainPools(s32 initPoolSize); void* AudioHeap_AllocCached(s32 tableType, s32 size, s32 cache, s32 id); -s32 AudioHeap_SearchCaches(s32 tableType, s32 cache, s32 id); +void* AudioHeap_SearchCaches(s32 tableType, s32 cache, s32 id); s32 AudioHeap_ResetStep(void); void* AudioHeap_SearchPermanentCache(s32 tableType, s32 id); u8* AudioHeap_AllocPermanent(s32 tableType, s32 id, u32 size); @@ -1054,11 +1097,11 @@ bool AudioThread_ResetComplete(void); void AudioThread_ResetAudioHeap(s32); void AudioThread_Init(void); -extern AudioTable gSampleBankTableInit; +extern AudioTableBase gSampleBankTableInit; // extern AudioTableEntry gSampleBankTableInitEntries[]; -extern AudioTable gSeqTableInit; +extern AudioTableBase gSeqTableInit; // extern AudioTableEntry gSeqTableInitEntries[]; -extern AudioTable gSoundFontTableInit; +extern AudioTableBase gSoundFontTableInit; // extern AudioTableEntry gSoundFontTableInitEntries[]; extern u8 gSeqFontTableInit[]; @@ -1104,7 +1147,7 @@ extern AudioCommonPoolSplit gTemporaryCommonPoolSplit; extern u8 gSampleFontLoadStatus[64]; extern u8 gFontLoadStatus[64]; extern u8 gSeqLoadStatus[256]; -extern volatile u8 gResetStatus; +extern volatile u8 gAudioResetStep; extern u8 gAudioSpecId; extern s32 gResetFadeoutFramesLeft; extern u8 sAudioContextPad1000[0x1000];// 0x1000 gap @@ -1170,7 +1213,7 @@ extern s16* gAiBuffers[3]; extern s16 gAiBuffLengths[3]; extern u32 gAudioRandom; extern u32 D_80155D88; -extern volatile u32 gResetTimer; +extern volatile u32 gAudioResetTimer; extern u64 gAudioContextEnd[]; diff --git a/linker_scripts/us/rev1/symbol_addrs.txt b/linker_scripts/us/rev1/symbol_addrs.txt index 9b9dbbec..a428d50b 100644 --- a/linker_scripts/us/rev1/symbol_addrs.txt +++ b/linker_scripts/us/rev1/symbol_addrs.txt @@ -30,6 +30,8 @@ D_80160000 = 0x80160000;//ignore:true D_800D0000 = 0x800D0000;//ignore:true D_7FFFFFFE = 0x7FFFFFFE;//ignore:true D_1000000 = 0x01000000;//ignore:true +D_01000100 = 0x01000100;//ignore:true +D_01010100 = 0x01010100;//ignore:true // mirage symbols D_A000000 = 0x0A000000;//ignore:true @@ -134,12 +136,8 @@ Save_Read = 0x800C3194; sOvlUnused_Unk = 0x800CBD3C; // hardcoded address used in sys_rdram -D_A0300000 = 0xA0300000; - -//hardware addresses. should probably be in hardware_regs -D_A4000000 = 0xA4000000; -D_A5000508 = 0xA5000508; -D_A5000510 = 0xA5000510; +D_A0300000 = 0xA0300000; // ignore:true +D_A4000000 = 0xA4000000; // ignore:true D_edisplay_800CFA54 = 0x800CFA54; diff --git a/linker_scripts/us/rev1/symbol_addrs_audio.txt b/linker_scripts/us/rev1/symbol_addrs_audio.txt index 4ecb8a71..ecbc8fc2 100644 --- a/linker_scripts/us/rev1/symbol_addrs_audio.txt +++ b/linker_scripts/us/rev1/symbol_addrs_audio.txt @@ -146,7 +146,7 @@ gTemporaryCommonPoolSplit = 0x8014D398;//size:0xC gSampleFontLoadStatus = 0x8014D3A8;//size:0x40 gFontLoadStatus = 0x8014D3E8;//size:0x40 gSeqLoadStatus = 0x8014D428;//size:0x100 -gResetStatus = 0x8014D528; +gAudioResetStep = 0x8014D528; gAudioSpecId = 0x8014D529; gResetFadeoutFramesLeft = 0x8014D52C; gNotes = 0x8014E530; @@ -207,7 +207,7 @@ gAiBuffers = 0x80155D70;//size:0xC gAiBuffLengths = 0x80155D7C;//size:0x8 gAudioRandom = 0x80155D84; D_80155D88 = 0x80155D88; -gResetTimer = 0x80155D8C; +gAudioResetTimer = 0x80155D8C; gAudioContextEnd = 0x80155D90;//size:0x10 sAudioTaskStartQueue = 0x80155DA0; diff --git a/linker_scripts/us/rev1/undefined_syms.ld b/linker_scripts/us/rev1/undefined_syms.ld index fbea8a22..078d77ef 100644 --- a/linker_scripts/us/rev1/undefined_syms.ld +++ b/linker_scripts/us/rev1/undefined_syms.ld @@ -33,11 +33,3 @@ // func_84001728 = 0x84001728; // func_840017D8 = 0x840017D8; // func_840017AC = 0x840017AC; - -D_A0300000 = 0xA0300000; -D_A4000000 = 0xA4000000; -D_A5000508 = 0xA5000508; -D_A5000510 = 0xA5000510; - -D_01000100 = 0x01000100; -D_01010100 = 0x01010100; diff --git a/src/audio/audio_context.c b/src/audio/audio_context.c index 80da89e0..f356e1c6 100644 --- a/src/audio/audio_context.c +++ b/src/audio/audio_context.c @@ -34,13 +34,13 @@ AudioCommonPoolSplit gTemporaryCommonPoolSplit; u8 gSampleFontLoadStatus[64]; u8 gFontLoadStatus[64]; u8 gSeqLoadStatus[256]; -volatile u8 gResetStatus; +volatile u8 gAudioResetStep; u8 gAudioSpecId; s32 gResetFadeoutFramesLeft; u8 sAudioContextPad1000[0x1000]; // 0x1000 gap Note* gNotes; // 0x4 -SequencePlayer gSeqPlayers[4]; +SequencePlayer gSeqPlayers[SEQ_PLAYER_MAX]; SequenceChannel gSeqChannels[48]; SequenceLayer gSeqLayers[64]; SequenceChannel gSeqChannelNone; @@ -100,7 +100,7 @@ s16* gAiBuffers[3]; s16 gAiBuffLengths[3]; u32 gAudioRandom; u32 D_80155D88; -volatile u32 gResetTimer; +volatile u32 gAudioResetTimer; u64 gAudioContextEnd[2]; diff --git a/src/audio/audio_effects.c b/src/audio/audio_effects.c index 5719dba7..61d2ccaa 100644 --- a/src/audio/audio_effects.c +++ b/src/audio/audio_effects.c @@ -146,31 +146,31 @@ void func_80013A18(Note* note) { } void func_80013A84(Note* note) { - NotePlaybackState* temp_v0_3 = ¬e->playbackState; - VibratoState* temp_v1 = &temp_v0_3->vibratoState; + NotePlaybackState* noteState = ¬e->playbackState; + VibratoState* vibrato = ¬eState->vibratoState; - temp_v1->active = 1; - temp_v1->time = 0; - temp_v0_3->vibratoFreqMod = 1.0f; - temp_v0_3->portamentoFreqMod = 1.0f; + vibrato->active = 1; + vibrato->time = 0; + noteState->vibratoFreqMod = 1.0f; + noteState->portamentoFreqMod = 1.0f; - temp_v1->curve = gWaveSamples[2]; + vibrato->curve = gWaveSamples[2]; - temp_v1->channel = temp_v0_3->parentLayer->channel; + vibrato->channel = noteState->parentLayer->channel; - if ((temp_v1->depthChangeTimer = temp_v1->channel->vibratoDepthChangeDelay) == 0) { - temp_v1->depth = (s32) temp_v1->channel->vibratoDepthTarget; + if ((vibrato->depthChangeTimer = vibrato->channel->vibratoDepthChangeDelay) == 0) { + vibrato->depth = (s32) vibrato->channel->vibratoDepthTarget; } else { - temp_v1->depth = (s32) temp_v1->channel->vibratoDepthStart; + vibrato->depth = (s32) vibrato->channel->vibratoDepthStart; } - if ((temp_v1->rateChangeTimer = temp_v1->channel->vibratoRateChangeDelay) == 0) { - temp_v1->rate = (s32) temp_v1->channel->vibratoRateTarget; + if ((vibrato->rateChangeTimer = vibrato->channel->vibratoRateChangeDelay) == 0) { + vibrato->rate = (s32) vibrato->channel->vibratoRateTarget; } else { - temp_v1->rate = (s32) temp_v1->channel->vibratoRateStart; + vibrato->rate = (s32) vibrato->channel->vibratoRateStart; } - temp_v1->delay = temp_v1->channel->vibratoDelay; - temp_v0_3->portamento = temp_v0_3->parentLayer->portamento; + vibrato->delay = vibrato->channel->vibratoDelay; + noteState->portamento = noteState->parentLayer->portamento; } void func_80013B6C(AdsrState* adsr, EnvelopePoint* envelope, s16* arg2) { @@ -187,31 +187,31 @@ f32 func_80013B90(AdsrState* adsr) { u8 state = adsr->state; switch (state) { - case 0: + case ADSR_STATE_DISABLED: return 0.0f; - case 1: + case ADSR_STATE_INITIAL: if (action & 0x40) { - adsr->state = 5; + adsr->state = ADSR_STATE_HANG; break; } - case 2: + case ADSR_STATE_START_LOOP: adsr->envIndex = 0; - adsr->state = 3; - case_3: - case 3: + adsr->state = ADSR_STATE_LOOP; + case_ADSR_STATE_LOOP: + case ADSR_STATE_LOOP: adsr->delay = adsr->envelope[adsr->envIndex].delay; switch (adsr->delay) { - case 0: - adsr->state = 0; + case ADSR_DISABLE: + adsr->state = ADSR_STATE_DISABLED; break; - case -1: - adsr->state = 5; + case ADSR_HANG: + adsr->state = ADSR_STATE_HANG; break; - case -2: + case ADSR_GOTO: adsr->envIndex = adsr->envelope[adsr->envIndex].arg; - goto case_3; - case -3: - adsr->state = 1; + goto case_ADSR_STATE_LOOP; + case ADSR_RESTART: + adsr->state = ADSR_STATE_INITIAL; break; default: if (adsr->delay >= 4) { @@ -224,47 +224,47 @@ f32 func_80013B90(AdsrState* adsr) { adsr->target = adsr->envelope[adsr->envIndex].arg / 32767.0f; adsr->target = SQ(adsr->target); adsr->velocity = (adsr->target - adsr->current) / adsr->delay; - adsr->state = 4; + adsr->state = ADSR_STATE_FADE; adsr->envIndex++; break; } - if (adsr->state != 4) { + if (adsr->state != ADSR_STATE_FADE) { break; } - case 4: - adsr->delay -= 1; + case ADSR_STATE_FADE: + adsr->delay--; adsr->current += adsr->velocity; if (adsr->delay <= 0) { - adsr->state = 3; + adsr->state = ADSR_STATE_LOOP; } break; - case 6: - case 7: + case ADSR_STATE_DECAY: + case ADSR_STATE_RELEASE: adsr->current -= adsr->fadeOutVel; - if ((adsr->sustain != 0.0f) && (state == 6)) { + if ((adsr->sustain != 0.0f) && (state == ADSR_STATE_DECAY)) { if (adsr->current < adsr->sustain) { adsr->current = adsr->sustain; adsr->delay = 0x80; - adsr->state = 8; + adsr->state = ADSR_STATE_SUSTAIN; } } else if (adsr->current < 0.00001f) { adsr->current = 0.0f; - adsr->state = 0; + adsr->state = ADSR_STATE_DISABLED; } break; - case 8: + case ADSR_STATE_SUSTAIN: adsr->delay--; if (adsr->delay == 0) { - adsr->state = 7; + adsr->state = ADSR_STATE_RELEASE; } break; } if (action & 0x20) { - adsr->state = 6; + adsr->state = ADSR_STATE_DECAY; adsr->action.asByte = action & ~0x20; } if (action & 0x10) { - adsr->state = 7; + adsr->state = ADSR_STATE_RELEASE; adsr->action.asByte = action & ~0x10; } if (adsr->current < 0.0f) { diff --git a/src/audio/audio_general.c b/src/audio/audio_general.c index 455dfde4..bdf01981 100644 --- a/src/audio/audio_general.c +++ b/src/audio/audio_general.c @@ -50,10 +50,10 @@ f32 sAnalyzerBuffer1[256]; f32 sAnalyzerBuffer2[384]; f32 sNewFreqAmplitudes[32]; u8 sFreqAnalyzerBars[32]; -SeqRequest sSeqRequests[4][5]; -u8 sNumSeqRequests[4]; +SeqRequest sSeqRequests[SEQ_PLAYER_MAX][5]; +u8 sNumSeqRequests[SEQ_PLAYER_MAX]; s32 sAudioSeqCmds[256]; -ActiveSequence sActiveSequences[4]; +ActiveSequence sActiveSequences[SEQ_PLAYER_MAX]; u16 sDelayedSeqCmdFlags; DelayedSeqCmd sDelayedSeqCmds[16]; SfxChannelState sSfxChannelState[16]; @@ -761,12 +761,12 @@ void Audio_ProcessSeqCmd(u32 seqCmd) { seqNumber = seqCmd & 0xFF; seqArgs = (seqCmd & 0xFF00) >> 8; fadeTimer = (seqCmd & 0xFF0000) >> 13; - if (sActiveSequences[seqPlayId].isWaitingForFonts == 0) { + if (!sActiveSequences[seqPlayId].isWaitingForFonts) { if (seqArgs < 0x80) { Audio_StartSequence(seqPlayId, seqNumber, seqArgs, fadeTimer); } else { sActiveSequences[seqPlayId].startSeqCmd = seqCmd & ~0x8000; - sActiveSequences[seqPlayId].isWaitingForFonts = 1; + sActiveSequences[seqPlayId].isWaitingForFonts = true; Audio_StopSequence(seqPlayId, 1); if (sActiveSequences[seqPlayId].prevSeqId != SEQ_ID_NONE) { tempptr = AudioThread_GetFontsForSequence(seqNumber, &sp4C); @@ -1054,23 +1054,23 @@ void Audio_UpdateActiveSequences(void) { s32 temp; u32 cmd; f32 fadeMod; - u32 sp70; + u32 out; s32 pad1; s32 pad2; - for (seqPlayId = 0; seqPlayId < 4; seqPlayId++) { - if ((sActiveSequences[seqPlayId].isWaitingForFonts != 0)) { - switch ((s32) AudioThread_GetAsyncLoadStatus(&sp70)) { + for (seqPlayId = 0; seqPlayId < SEQ_PLAYER_MAX; seqPlayId++) { + if (sActiveSequences[seqPlayId].isWaitingForFonts) { + switch ((s32) AudioThread_GetAsyncLoadStatus(&out)) { case SEQ_PLAYER_BGM + 1: case SEQ_PLAYER_FANFARE + 1: case SEQ_PLAYER_SFX + 1: case SEQ_PLAYER_VOICE + 1: - sActiveSequences[seqPlayId].isWaitingForFonts = 0; + sActiveSequences[seqPlayId].isWaitingForFonts = false; Audio_ProcessSeqCmd(sActiveSequences[seqPlayId].startSeqCmd); break; } } - if (sActiveSequences[seqPlayId].mainVolume.fadeActive != 0) { + if (sActiveSequences[seqPlayId].mainVolume.fadeActive) { fadeMod = 1.0f; for (i = 0; i < 3; i++) { fadeMod *= sActiveSequences[seqPlayId].mainVolume.fadeMod[i] / 127.0f; @@ -2671,16 +2671,16 @@ void Audio_RestoreVolumeSettings(u8 audioType) { u8 i; switch (audioType) { - case 0: + case AUDIO_TYPE_MUSIC: Audio_SetSequenceFade(SEQ_PLAYER_BGM, 0, volume, 1); Audio_SetSequenceFade(SEQ_PLAYER_FANFARE, 0, volume, 1); break; - case 2: + case AUDIO_TYPE_SFX: for (i = 0; i < 15; i++) { AUDIOCMD_CHANNEL_SET_VOL(SEQ_PLAYER_SFX, (u32) i, volume / 127.0f); } break; - case 1: + case AUDIO_TYPE_VOICE: AUDIOCMD_CHANNEL_SET_VOL(SEQ_PLAYER_VOICE, 15, volume / 127.0f); break; } @@ -2773,8 +2773,8 @@ void Audio_InitSounds(void) { Audio_ResetSfxChannelState(); Audio_ResetActiveSequencesAndVolume(); Audio_ResetSfx(); - Audio_StartSequence(SEQ_PLAYER_VOICE, NA_BGM_VO, 0xFF, 1); - Audio_StartSequence(SEQ_PLAYER_SFX, NA_BGM_SE, 0xFF, 10); + Audio_StartSequence(SEQ_PLAYER_VOICE, NA_BGM_VO, -1, 1); + Audio_StartSequence(SEQ_PLAYER_SFX, NA_BGM_SE, -1, 10); } void Audio_RestartSeqPlayers(void) { @@ -2782,13 +2782,13 @@ void Audio_RestartSeqPlayers(void) { s32 pad2; u16 fadeIn = 1; - Audio_StartSequence(SEQ_PLAYER_VOICE, NA_BGM_VO, 0xFF, 1); + Audio_StartSequence(SEQ_PLAYER_VOICE, NA_BGM_VO, -1, 1); if (sAudioSpecId == AUDIOSPEC_12) { fadeIn = 360; } else if (sAudioSpecId < AUDIOSPEC_23) { fadeIn = 90; } - Audio_StartSequence(SEQ_PLAYER_SFX, NA_BGM_SE, 0xFF, fadeIn); + Audio_StartSequence(SEQ_PLAYER_SFX, NA_BGM_SE, -1, fadeIn); Audio_LoadInstruments(); Audio_LoadAquasSequence(); SEQCMD_SET_SEQPLAYER_VOLUME(SEQ_PLAYER_SFX, 0, 127); @@ -2797,9 +2797,9 @@ void Audio_RestartSeqPlayers(void) { AUDIOCMD_GLOBAL_STOP_AUDIOCMDS(); AUDIOCMD_GLOBAL_STOP_AUDIOCMDS(); AUDIOCMD_GLOBAL_STOP_AUDIOCMDS(); - Audio_RestoreVolumeSettings(0); - Audio_RestoreVolumeSettings(2); - Audio_RestoreVolumeSettings(1); + Audio_RestoreVolumeSettings(AUDIO_TYPE_MUSIC); + Audio_RestoreVolumeSettings(AUDIO_TYPE_SFX); + Audio_RestoreVolumeSettings(AUDIO_TYPE_VOICE); } void Audio_StartReset(u8 oldSpecId) { diff --git a/src/audio/audio_heap.c b/src/audio/audio_heap.c index 5ea0674d..ab2a6127 100644 --- a/src/audio/audio_heap.c +++ b/src/audio/audio_heap.c @@ -230,20 +230,20 @@ void* AudioHeap_AllocCached(s32 tableType, s32 size, s32 cache, s32 id) { u8* loadStatus; switch (tableType) { - case 0: + case SEQUENCE_TABLE: loadedCache = &gSeqCache; loadStatus = gSeqLoadStatus; break; - case 1: + case FONT_TABLE: loadedCache = &gFontCache; loadStatus = gFontLoadStatus; break; - case 2: + case SAMPLE_TABLE: loadedCache = &gSampleBankCache; loadStatus = gSampleFontLoadStatus; break; } - if (cache == 0) { + if (cache == CACHE_TEMPORARY) { temporaryCache = &loadedCache->temporary; temporaryPool = &temporaryCache->pool; if (loadedCache->temporary.entries[0].id == -1) { @@ -256,7 +256,7 @@ void* AudioHeap_AllocCached(s32 tableType, s32 size, s32 cache, s32 id) { } else { loadStatusEntry1 = loadStatus[temporaryCache->entries[1].id]; } - if (tableType == 1) { + if (tableType == FONT_TABLE) { if (loadStatusEntry0 == 4) { for (i = 0; i < gNumNotes; i++) { if ((gNotes[i].playbackState.fontId == temporaryCache->entries[0].id) && @@ -298,7 +298,7 @@ void* AudioHeap_AllocCached(s32 tableType, s32 size, s32 cache, s32 id) { temporaryCache->nextSide = 1; } else { // Check if there is a side which isn't in active use, if so, evict that one. - if (tableType == 0) { + if (tableType == SEQUENCE_TABLE) { if (loadStatusEntry0 == 2) { for (i = 0; i < ARRAY_COUNT(gSeqPlayers); i++) { if (gSeqPlayers[i].enabled && (gSeqPlayers[i].seqId == temporaryCache->entries[0].id)) { @@ -321,7 +321,7 @@ void* AudioHeap_AllocCached(s32 tableType, s32 size, s32 cache, s32 id) { goto block_85; } } - } else if (tableType == 1) { + } else if (tableType == FONT_TABLE) { if (loadStatusEntry0 == 2) { for (i = 0; i < gNumNotes; i++) { if ((gNotes[i].playbackState.fontId == temporaryCache->entries[0].id) && @@ -373,7 +373,7 @@ void* AudioHeap_AllocCached(s32 tableType, s32 size, s32 cache, s32 id) { if (temporaryCache->entries[temporaryCache->nextSide].id != -1) { loadStatus[temporaryCache->entries[temporaryCache->nextSide].id] = 0; - if (tableType == 1) { + if (tableType == FONT_TABLE) { AudioHeap_DiscardFont(temporaryCache->entries[temporaryCache->nextSide].id); } } @@ -387,10 +387,10 @@ void* AudioHeap_AllocCached(s32 tableType, s32 size, s32 cache, s32 id) { (temporaryCache->entries[1].ramAddr < temporaryPool->curRamAddr)) { loadStatus[temporaryCache->entries[1].id] = 0; switch (tableType) { - case 0: + case SEQUENCE_TABLE: AudioHeap_DiscardSequence(temporaryCache->entries[1].id); break; - case 1: + case FONT_TABLE: AudioHeap_DiscardFont(temporaryCache->entries[1].id); break; } @@ -408,10 +408,10 @@ void* AudioHeap_AllocCached(s32 tableType, s32 size, s32 cache, s32 id) { (temporaryCache->entries[1].ramAddr < temporaryPool->curRamAddr)) { loadStatus[temporaryCache->entries[0].id] = 0; switch (tableType) { - case 0: + case SEQUENCE_TABLE: AudioHeap_DiscardSequence(temporaryCache->entries[0].id); break; - case 1: + case FONT_TABLE: AudioHeap_DiscardFont(temporaryCache->entries[0].id); break; } @@ -430,10 +430,10 @@ void* AudioHeap_AllocCached(s32 tableType, s32 size, s32 cache, s32 id) { loadedCache->persistent.entries[loadedCache->persistent.numEntries].ramAddr = persistentRamAddr; if (persistentRamAddr == NULL) { switch (cache) { - case 2: - return AudioHeap_AllocCached(tableType, size, 0, id); - case 0: - case 1: + case CACHE_EITHER: + return AudioHeap_AllocCached(tableType, size, CACHE_TEMPORARY, id); + case CACHE_TEMPORARY: + case CACHE_PERSISTENT: return NULL; } } @@ -442,7 +442,7 @@ void* AudioHeap_AllocCached(s32 tableType, s32 size, s32 cache, s32 id) { return loadedCache->persistent.entries[loadedCache->persistent.numEntries++].ramAddr; } -s32 AudioHeap_SearchCaches(s32 tableType, s32 cache, s32 id) { +void* AudioHeap_SearchCaches(s32 tableType, s32 cache, s32 id) { void* ramAddr; // Always search the permanent cache in addition to the regular ones. @@ -568,13 +568,13 @@ s32 AudioHeap_ResetStep(void) { } else { sp24 = 1; } - switch (gResetStatus) { + switch (gAudioResetStep) { case 5: for (i = 0; i < ARRAY_COUNT(gSeqPlayers); i++) { func_800144E4(&gSeqPlayers[i]); } gResetFadeoutFramesLeft = 4 / sp24; - gResetStatus--; + gAudioResetStep--; break; case 4: if (gResetFadeoutFramesLeft != 0) { @@ -589,7 +589,7 @@ s32 AudioHeap_ResetStep(void) { } gResetFadeoutFramesLeft = 16 / sp24; - gResetStatus--; + gAudioResetStep--; } break; case 3: @@ -603,7 +603,7 @@ s32 AudioHeap_ResetStep(void) { } } gResetFadeoutFramesLeft = 4 / sp24; - gResetStatus--; + gAudioResetStep--; break; // needed to match } break; @@ -612,13 +612,13 @@ s32 AudioHeap_ResetStep(void) { if (gResetFadeoutFramesLeft != 0) { gResetFadeoutFramesLeft--; } else { - gResetStatus--; + gAudioResetStep--; AudioHeap_DiscardSampleCaches(); } break; case 1: AudioHeap_Init(); - gResetStatus = 0; + gAudioResetStep = 0; for (i = 0; i < 3; i++) { gAiBuffLengths[i] = gAudioBufferParams.maxAiBufferLength; for (j = 0; j < AIBUF_LEN; j++) { @@ -627,7 +627,7 @@ s32 AudioHeap_ResetStep(void) { } break; } - if (gResetStatus < 3) { + if (gAudioResetStep < 3) { return 0; } return 1; @@ -919,9 +919,11 @@ void AudioHeap_DiscardSampleCacheEntry(SampleCacheEntry* entry) { for (fondId = 0; fondId < numFonts; fondId++) { sampleBankId1 = gSoundFontList[fondId].sampleBankId1; sampleBankId2 = gSoundFontList[fondId].sampleBankId2; - if (((sampleBankId1 != 0xFF) && (entry->sampleBankId == sampleBankId1)) || - ((sampleBankId2 != 0xFF) && (entry->sampleBankId == sampleBankId2)) || (entry->sampleBankId == 0)) { - if ((AudioHeap_SearchCaches(1, 2, fondId) != NULL) && ((gFontLoadStatus[fondId] > 1) != 0)) { + if (((sampleBankId1 != SAMPLES_NONE) && (entry->sampleBankId == sampleBankId1)) || + ((sampleBankId2 != SAMPLES_NONE) && (entry->sampleBankId == sampleBankId2)) || + (entry->sampleBankId == SAMPLES_SFX)) { + if ((AudioHeap_SearchCaches(FONT_TABLE, CACHE_EITHER, fondId) != NULL) && + ((gFontLoadStatus[fondId] > 1) != 0)) { for (instId = 0; instId < gSoundFontList[fondId].numInstruments; instId++) { instrument = Audio_GetInstrument(fondId, instId); if (instrument != NULL) { @@ -989,9 +991,11 @@ void AudioHeap_DiscardSampleCaches(void) { for (fontId = 0; fontId < numFonts; fontId++) { sampleBankId1 = gSoundFontList[fontId].sampleBankId1; sampleBankId2 = gSoundFontList[fontId].sampleBankId2; - if (((sampleBankId1 != 0xFFU) && (entry->sampleBankId == sampleBankId1)) || - ((sampleBankId2 != 0xFF) && (entry->sampleBankId == sampleBankId2)) || (entry->sampleBankId == 0)) { - if ((AudioHeap_SearchCaches(1, 3, fontId) != NULL) && ((gFontLoadStatus[fontId] > 1) != 0)) { + if (((sampleBankId1 != SAMPLES_NONE_U) && (entry->sampleBankId == sampleBankId1)) || + ((sampleBankId2 != SAMPLES_NONE) && (entry->sampleBankId == sampleBankId2)) || + (entry->sampleBankId == SAMPLES_SFX)) { + if ((AudioHeap_SearchCaches(FONT_TABLE, CACHE_PERMANENT, fontId) != NULL) && + ((gFontLoadStatus[fontId] > 1) != 0)) { for (i = 0; i < gPersistentSampleCache.numEntries; i++) { entry = &gPersistentSampleCache.entries[i]; for (instId = 0; instId < gSoundFontList[fontId].numInstruments; instId++) { diff --git a/src/audio/audio_load.c b/src/audio/audio_load.c index 74e791c2..486e3deb 100644 --- a/src/audio/audio_load.c +++ b/src/audio/audio_load.c @@ -218,7 +218,7 @@ void AudioLoad_InitTable(AudioTable* table, u8* romAddr, u16 unkMediumParam) { } void* AudioLoad_SyncLoadSeqFonts(s32 seqId, u32* outFontId) { - s32 index = ((u16*) gSeqFontTable)[AudioLoad_GetLoadTableIndex(0, seqId)]; + s32 index = ((u16*) gSeqFontTable)[AudioLoad_GetLoadTableIndex(SEQUENCE_TABLE, seqId)]; s32 fontId = 0xFF; s32 numFonts = gSeqFontTable[index++]; void* soundFontData; @@ -235,7 +235,7 @@ void AudioLoad_SyncLoadSeqParts(s32 seqId, s32 flags) { s32 pad; u32 fontId; - seqId = AudioLoad_GetLoadTableIndex(0, seqId); + seqId = AudioLoad_GetLoadTableIndex(SEQUENCE_TABLE, seqId); if (flags & 2) { AudioLoad_SyncLoadSeqFonts(seqId, &fontId); } @@ -289,22 +289,24 @@ s32 AudioLoad_SyncLoadInstrument(s32 fontId, s32 instId, s32 drumId) { } void AudioLoad_AsyncLoadSampleBank(s32 sampleBankId, s32 nChunks, s32 retData, OSMesgQueue* retQueue) { - if (AudioLoad_AsyncLoadInner(2, AudioLoad_GetLoadTableIndex(2, sampleBankId), nChunks, retData, retQueue) == NULL) { + if (AudioLoad_AsyncLoadInner(SAMPLE_TABLE, AudioLoad_GetLoadTableIndex(SAMPLE_TABLE, sampleBankId), nChunks, + retData, retQueue) == NULL) { osSendMesg(retQueue, NULL, OS_MESG_NOBLOCK); } } void AudioLoad_AsyncLoadSeq(s32 seqId, s32 nChunks, s32 retData, OSMesgQueue* retQueue) { - s32 index = ((u16*) gSeqFontTable)[AudioLoad_GetLoadTableIndex(0, seqId)]; + s32 index = ((u16*) gSeqFontTable)[AudioLoad_GetLoadTableIndex(SEQUENCE_TABLE, seqId)]; s32 fontsLeft = gSeqFontTable[index++]; for (fontsLeft; fontsLeft > 0; fontsLeft--) { - AudioLoad_AsyncLoadInner(1, AudioLoad_GetLoadTableIndex(1, gSeqFontTable[index++]), nChunks, retData, retQueue); + AudioLoad_AsyncLoadInner(FONT_TABLE, AudioLoad_GetLoadTableIndex(FONT_TABLE, gSeqFontTable[index++]), nChunks, + retData, retQueue); } } u8* AudioLoad_GetFontsForSequence(s32 seqId, u32* outNumFonts) { - s32 index = ((u16*) gSeqFontTable)[AudioLoad_GetLoadTableIndex(0, seqId)]; + s32 index = ((u16*) gSeqFontTable)[AudioLoad_GetLoadTableIndex(SEQUENCE_TABLE, seqId)]; *outNumFonts = gSeqFontTable[index++]; if (*outNumFonts == 0) { @@ -314,13 +316,13 @@ u8* AudioLoad_GetFontsForSequence(s32 seqId, u32* outNumFonts) { } void AudioLoad_DiscardSeqFonts(s32 seqId) { - s32 index = ((u16*) gSeqFontTable)[AudioLoad_GetLoadTableIndex(0, seqId)]; + s32 index = ((u16*) gSeqFontTable)[AudioLoad_GetLoadTableIndex(SEQUENCE_TABLE, seqId)]; s32 numFonts = gSeqFontTable[index++]; u32 fontId; while (numFonts > 0) { numFonts--; - fontId = AudioLoad_GetLoadTableIndex(1, gSeqFontTable[index++]); + fontId = AudioLoad_GetLoadTableIndex(FONT_TABLE, gSeqFontTable[index++]); if (AudioHeap_SearchPermanentCache(1, fontId) == NULL) { AudioLoad_DiscardFont(fontId); if (gFontLoadStatus[fontId] != 5) { @@ -363,7 +365,7 @@ void AudioLoad_SyncInitSeqPlayerInternal(s32 playerIdx, s32 seqId, s32 arg2) { s32 fontId; s32 i; - seqId = AudioLoad_GetLoadTableIndex(0, seqId); + seqId = AudioLoad_GetLoadTableIndex(SEQUENCE_TABLE, seqId); func_800144E4(&gSeqPlayers[playerIdx]); index = ((u16*) gSeqFontTable)[seqId]; numFonts = gSeqFontTable[index++]; @@ -388,7 +390,7 @@ void AudioLoad_SyncInitSeqPlayerInternal(s32 playerIdx, s32 seqId, s32 arg2) { } void* AudioLoad_SyncLoadSeq(s32 seqId) { - s32 seqIdx = AudioLoad_GetLoadTableIndex(0, seqId); + s32 seqIdx = AudioLoad_GetLoadTableIndex(SEQUENCE_TABLE, seqId); s32 didAllocate; return AudioLoad_SyncLoad(0, seqIdx, &didAllocate); @@ -400,7 +402,7 @@ void* AudioLoad_SyncLoadSampleBank(u32 sampleBankId, s32* outMedium) { s32 cachePolicy; s32 noLoad; - sampleBankId = AudioLoad_GetLoadTableIndex(2, sampleBankId); + sampleBankId = AudioLoad_GetLoadTableIndex(SAMPLE_TABLE, sampleBankId); ramAddr = AudioLoad_SearchCaches(2, sampleBankId); if (ramAddr != NULL) { if (gSampleFontLoadStatus[sampleBankId] != 5) { @@ -410,7 +412,7 @@ void* AudioLoad_SyncLoadSampleBank(u32 sampleBankId, s32* outMedium) { return ramAddr; } cachePolicy = sampleBankTable->entries[sampleBankId].cachePolicy; - if (cachePolicy == 4) { + if (cachePolicy == CACHEPOLICY_4) { *outMedium = sampleBankTable->entries[sampleBankId].medium; return sampleBankTable->entries[sampleBankId].romAddr; } @@ -430,18 +432,18 @@ void* AudioLoad_SyncLoadFont(s32 fontId) { s32 didAllocate; SampleBankRelocInfo relocInfo; - fontId = AudioLoad_GetLoadTableIndex(1, fontId); + fontId = AudioLoad_GetLoadTableIndex(FONT_TABLE, fontId); sampleBankId1 = gSoundFontList[fontId].sampleBankId1; sampleBankId2 = gSoundFontList[fontId].sampleBankId2; relocInfo.sampleBankId1 = sampleBankId1; relocInfo.sampleBankId2 = sampleBankId2; - if (sampleBankId1 != 0xFF) { + if (sampleBankId1 != SAMPLES_NONE) { relocInfo.baseAddr1 = AudioLoad_SyncLoadSampleBank(sampleBankId1, &relocInfo.medium1); } else { relocInfo.baseAddr1 = NULL; } - if (sampleBankId2 != 0xFF) { + if (sampleBankId2 != SAMPLES_NONE) { relocInfo.baseAddr2 = AudioLoad_SyncLoadSampleBank(sampleBankId2, &relocInfo.medium2); } else { relocInfo.baseAddr2 = NULL; @@ -451,7 +453,7 @@ void* AudioLoad_SyncLoadFont(s32 fontId) { return NULL; } if (didAllocate == 1) { - AudioLoad_RelocateFontAndPreloadSamples(fontId, fontData, &relocInfo, 0); + AudioLoad_RelocateFontAndPreloadSamples(fontId, fontData, &relocInfo, AUDIOLOAD_SYNC); } return fontData; } @@ -478,27 +480,27 @@ void* AudioLoad_SyncLoad(u32 tableType, u32 id, s32* didAllocate) { cachePolicy = table->entries[id].cachePolicy; romAddr = table->entries[id].romAddr; switch (cachePolicy) { - case 0: + case CACHEPOLICY_0: ramAddr = AudioHeap_AllocPermanent(tableType, id, size); if (ramAddr == NULL) { return ramAddr; } break; - case 1: - ramAddr = AudioHeap_AllocCached(tableType, size, 1, id); + case CACHEPOLICY_1: + ramAddr = AudioHeap_AllocCached(tableType, size, CACHE_PERSISTENT, id); if (ramAddr == NULL) { return ramAddr; } break; - case 2: - ramAddr = AudioHeap_AllocCached(tableType, size, 0, id); + case CACHEPOLICY_2: + ramAddr = AudioHeap_AllocCached(tableType, size, CACHE_TEMPORARY, id); if (ramAddr == NULL) { return ramAddr; } break; - case 3: - case 4: - ramAddr = AudioHeap_AllocCached(tableType, size, 2, id); + case CACHEPOLICY_3: + case CACHEPOLICY_4: + ramAddr = AudioHeap_AllocCached(tableType, size, CACHE_EITHER, id); if (ramAddr == NULL) { return ramAddr; } @@ -511,7 +513,7 @@ void* AudioLoad_SyncLoad(u32 tableType, u32 id, s32* didAllocate) { } else { AudioLoad_SyncDma(romAddr, ramAddr, size, medium); } - loadStatus = (cachePolicy == 0) ? 5 : 2; + loadStatus = (cachePolicy == CACHEPOLICY_0) ? 5 : 2; } switch (tableType) { case SEQUENCE_TABLE: @@ -549,7 +551,7 @@ void* AudioLoad_SearchCaches(s32 tableType, s32 id) { if (ramAddr != NULL) { return ramAddr; } - ramAddr = AudioHeap_SearchCaches(tableType, 2, id); + ramAddr = AudioHeap_SearchCaches(tableType, CACHE_EITHER, id); if (ramAddr != NULL) { return ramAddr; } @@ -715,17 +717,17 @@ void* AudioLoad_AsyncLoadInner(s32 tableType, s32 id, s32 nChunks, s32 retData, s32 loadStatus; switch (tableType) { - case 0: + case SEQUENCE_TABLE: if (gSeqLoadStatus[id] == 1) { return NULL; } break; - case 1: + case FONT_TABLE: if (gFontLoadStatus[id] == 1) { return NULL; } break; - case 2: + case SAMPLE_TABLE: if (gSampleFontLoadStatus[id] == 1) { return NULL; } @@ -744,28 +746,28 @@ void* AudioLoad_AsyncLoadInner(s32 tableType, s32 id, s32 nChunks, s32 retData, romAddr = table->entries[id].romAddr; loadStatus = 2; switch (cachePolicy) { - case 0: + case CACHEPOLICY_0: ramAddr = AudioHeap_AllocPermanent(tableType, id, size); if (ramAddr == NULL) { return ramAddr; } loadStatus = 5; break; - case 1: - ramAddr = AudioHeap_AllocCached(tableType, size, 1, id); + case CACHEPOLICY_1: + ramAddr = AudioHeap_AllocCached(tableType, size, CACHE_PERSISTENT, id); if (ramAddr == NULL) { return ramAddr; } break; - case 2: - ramAddr = AudioHeap_AllocCached(tableType, size, 0, id); + case CACHEPOLICY_2: + ramAddr = AudioHeap_AllocCached(tableType, size, CACHE_TEMPORARY, id); if (ramAddr == NULL) { return ramAddr; } break; - case 3: - case 4: - ramAddr = AudioHeap_AllocCached(tableType, size, 2, id); + case CACHEPOLICY_3: + case CACHEPOLICY_4: + ramAddr = AudioHeap_AllocCached(tableType, size, CACHE_EITHER, id); if (ramAddr == NULL) { return ramAddr; } @@ -776,17 +778,17 @@ void* AudioLoad_AsyncLoadInner(s32 tableType, s32 id, s32 nChunks, s32 retData, loadStatus = 1; } switch (tableType) { - case 0: + case SEQUENCE_TABLE: if (gSeqLoadStatus[id] != 5) { gSeqLoadStatus[id] = loadStatus; } break; - case 1: + case FONT_TABLE: if (gFontLoadStatus[id] != 5) { gFontLoadStatus[id] = loadStatus; } break; - case 2: + case SAMPLE_TABLE: if (gSampleFontLoadStatus[id] != 5) { gSampleFontLoadStatus[id] = loadStatus; } @@ -823,7 +825,7 @@ void AudioLoad_Init(void) { u64* clearContext; void* ramAddr; - gResetTimer = 0; + gAudioResetTimer = 0; for (i = 0; i < gAudioHeapSize / 8; i++) { ((u64*) gAudioHeap)[i] = 0; @@ -874,11 +876,11 @@ void AudioLoad_Init(void) { } } gAudioSpecId = AUDIOSPEC_0; - gResetStatus = 1; + gAudioResetStep = 1; AudioHeap_ResetStep(); - gSequenceTable = &gSeqTableInit; - gSoundFontTable = &gSoundFontTableInit; - gSampleBankTable = &gSampleBankTableInit; + gSequenceTable = (AudioTable*) &gSeqTableInit; + gSoundFontTable = (AudioTable*) &gSoundFontTableInit; + gSampleBankTable = (AudioTable*) &gSampleBankTableInit; gSeqFontTable = gSeqFontTableInit; gNumSequences = gSequenceTable->numEntries; AudioLoad_InitTable(gSequenceTable, SEGMENT_ROM_START(audio_seq), gSequenceMedium); @@ -909,16 +911,16 @@ s32 AudioLoad_SlowLoadSample(s32 fontId, u8 instId, s8* status) { sample = AudioLoad_GetFontSample(fontId, instId); if (sample == NULL) { - *status = 0; + *status = SLOW_LOAD_STATUS_0; return -1; } if (sample->medium == MEDIUM_RAM) { - *status = 2; + *status = SLOW_LOAD_STATUS_2; return 0; } slowLoad = &gSlowLoads.slowLoad[gSlowLoads.unk_00]; - if (slowLoad->state == 3) { - slowLoad->state = 0; + if (slowLoad->state == SLOW_LOAD_DONE) { + slowLoad->state = SLOW_LOAD_WAITING; } slowLoad->sample = *sample; slowLoad->status = status; @@ -926,14 +928,14 @@ s32 AudioLoad_SlowLoadSample(s32 fontId, u8 instId, s8* status) { AudioHeap_AllocTemporarySampleCache(sample->size, fontId, sample->sampleAddr, sample->medium); if (slowLoad->curRamAddr == NULL) { if ((sample->medium == MEDIUM_UNK) || (sample->codec == 2)) { - *status = 0; + *status = SLOW_LOAD_STATUS_0; return -1; } else { - *status = 3; + *status = SLOW_LOAD_STATUS_3; return -1; } } - slowLoad->state = 1; + slowLoad->state = SLOW_LOAD_START; slowLoad->bytesRemaining = ALIGN16(sample->size); slowLoad->ramAddr = slowLoad->curRamAddr; slowLoad->curDevAddr = sample->sampleAddr; @@ -986,18 +988,18 @@ void AudioLoad_ProcessSlowLoads(s32 resetStatus) { for (i = 0; i < 2; i++) { slowLoad = &gSlowLoads.slowLoad[i]; switch (slowLoad->state) { - case 2: + case SLOW_LOAD_LOADING: MQ_WAIT_FOR_MESG(&slowLoad->mesgQueue, NULL); if (resetStatus != 0) { - slowLoad->state = 3; + slowLoad->state = SLOW_LOAD_DONE; break; } - case 1: - slowLoad->state = 2; + case SLOW_LOAD_START: + slowLoad->state = SLOW_LOAD_LOADING; if (slowLoad->bytesRemaining == 0) { AudioLoad_FinishSlowLoad(&gSlowLoads.slowLoad[i]); - slowLoad->state = 3; - *slowLoad->status = 1; + slowLoad->state = SLOW_LOAD_DONE; + *slowLoad->status = SLOW_LOAD_STATUS_1; } else if (slowLoad->bytesRemaining < 0x1000) { if (slowLoad->medium == MEDIUM_UNK) { AudioLoad_DmaSlowCopyUnkMedium(slowLoad->curDevAddr, slowLoad->curRamAddr, @@ -1092,16 +1094,15 @@ void AudioLoad_ProcessAsyncLoads(s32 resetStatus) { } void AudioLoad_ProcessAsyncLoad(AudioAsyncLoad* asyncLoad, s32 resetStatus) { - AudioTable* sp5C; - s32 pad1; - s32 pad2; - u32 temp; - s32 sp24; - s32 sp48; - s32 sp44; - SampleBankRelocInfo sp2C; + AudioTable* sampleTable = gSampleBankTable; + s32 tableType; + s32 loadStatus; + u32 msg; + s32 tableIndex; + s32 sampleBankId1; + s32 sampleBankId2; + SampleBankRelocInfo relocInfo; - sp5C = gSampleBankTable; if (asyncLoad->delay > 1) { asyncLoad->delay--; return; @@ -1119,64 +1120,62 @@ void AudioLoad_ProcessAsyncLoad(AudioAsyncLoad* asyncLoad, s32 resetStatus) { } } if (asyncLoad->bytesRemaining == 0) { - temp = asyncLoad->retMsg; - pad1 = (temp >> 0x10) & 0xFF; - sp24 = (temp >> 8) & 0xFF; - pad2 = temp & 0xFF; - switch (pad1) { - case 0: - if (gSeqLoadStatus[sp24] != 5) { - gSeqLoadStatus[sp24] = pad2; + msg = asyncLoad->retMsg; + tableType = (msg >> 0x10) & 0xFF; + tableIndex = (msg >> 8) & 0xFF; + loadStatus = msg & 0xFF; + switch (tableType) { + case SEQUENCE_TABLE: + if (gSeqLoadStatus[tableIndex] != 5) { + gSeqLoadStatus[tableIndex] = loadStatus; } break; - case 2: - if (gSampleFontLoadStatus[sp24] != 5) { - gSampleFontLoadStatus[sp24] = pad2; + case SAMPLE_TABLE: + if (gSampleFontLoadStatus[tableIndex] != 5) { + gSampleFontLoadStatus[tableIndex] = loadStatus; } break; - case 1: - if (gFontLoadStatus[sp24] != 5) { - gFontLoadStatus[sp24] = pad2; + case FONT_TABLE: + if (gFontLoadStatus[tableIndex] != 5) { + gFontLoadStatus[tableIndex] = loadStatus; } - sp48 = gSoundFontList[sp24].sampleBankId1; - sp44 = gSoundFontList[sp24].sampleBankId2; - sp2C.sampleBankId1 = sp48; - sp2C.sampleBankId2 = sp44; - if (sp48 != 0xFF) { - sp2C.baseAddr1 = AudioLoad_SyncLoadSampleBank(sp48, &sp2C.medium1); + sampleBankId1 = gSoundFontList[tableIndex].sampleBankId1; + sampleBankId2 = gSoundFontList[tableIndex].sampleBankId2; + relocInfo.sampleBankId1 = sampleBankId1; + relocInfo.sampleBankId2 = sampleBankId2; + if (sampleBankId1 != SAMPLES_NONE) { + relocInfo.baseAddr1 = AudioLoad_SyncLoadSampleBank(sampleBankId1, &relocInfo.medium1); } else { - sp2C.baseAddr1 = NULL; + relocInfo.baseAddr1 = NULL; } - if (sp44 != 0xFF) { - sp2C.baseAddr2 = AudioLoad_SyncLoadSampleBank(sp44, &sp2C.medium2); + if (sampleBankId2 != SAMPLES_NONE) { + relocInfo.baseAddr2 = AudioLoad_SyncLoadSampleBank(sampleBankId2, &relocInfo.medium2); } else { - sp2C.baseAddr2 = NULL; + relocInfo.baseAddr2 = NULL; } - AudioLoad_RelocateFontAndPreloadSamples(sp24, asyncLoad->ramAddr, &sp2C, 1); + AudioLoad_RelocateFontAndPreloadSamples(tableIndex, asyncLoad->ramAddr, &relocInfo, AUDIOLOAD_ASYNC); break; } asyncLoad->status = 0; osSendMesg(asyncLoad->retQueue, asyncLoad->retMsg, OS_MESG_NOBLOCK); - } else { - if (asyncLoad->bytesRemaining < asyncLoad->chunkSize) { - if (asyncLoad->medium == MEDIUM_UNK) { - AudioLoad_AsyncDmaUnkMedium(asyncLoad->curDevAddr, asyncLoad->curRamAddr, asyncLoad->bytesRemaining, - sp5C->unkMediumParam); - } else { - AudioLoad_AsyncDma(asyncLoad, asyncLoad->bytesRemaining); - } - asyncLoad->bytesRemaining = 0; + } else if (asyncLoad->bytesRemaining < asyncLoad->chunkSize) { + if (asyncLoad->medium == MEDIUM_UNK) { + AudioLoad_AsyncDmaUnkMedium(asyncLoad->curDevAddr, asyncLoad->curRamAddr, asyncLoad->bytesRemaining, + sampleTable->unkMediumParam); } else { - if (asyncLoad->medium == MEDIUM_UNK) { - AudioLoad_AsyncDmaUnkMedium(asyncLoad->curDevAddr, asyncLoad->curRamAddr, asyncLoad->chunkSize, - sp5C->unkMediumParam); - } else { - AudioLoad_AsyncDma(asyncLoad, asyncLoad->chunkSize); - } - asyncLoad->bytesRemaining -= asyncLoad->chunkSize; - asyncLoad->curDevAddr += asyncLoad->chunkSize; - asyncLoad->curRamAddr += asyncLoad->chunkSize; + AudioLoad_AsyncDma(asyncLoad, asyncLoad->bytesRemaining); } + asyncLoad->bytesRemaining = 0; + } else { + if (asyncLoad->medium == MEDIUM_UNK) { + AudioLoad_AsyncDmaUnkMedium(asyncLoad->curDevAddr, asyncLoad->curRamAddr, asyncLoad->chunkSize, + sampleTable->unkMediumParam); + } else { + AudioLoad_AsyncDma(asyncLoad, asyncLoad->chunkSize); + } + asyncLoad->bytesRemaining -= asyncLoad->chunkSize; + asyncLoad->curDevAddr += asyncLoad->chunkSize; + asyncLoad->curRamAddr += asyncLoad->chunkSize; } } @@ -1271,7 +1270,7 @@ s32 AudioLoad_RelocateFontAndPreloadSamples(s32 fontId, u32 fontDataAddr, Sample #endif //! Bug: Those are assignments, not equality checks. switch (isAsync) { - case 0: + case AUDIOLOAD_SYNC: if (sample->medium = relocData->medium1) { sampleRamAddr = AudioHeap_AllocPersistentSampleCache(sample->size, relocData->sampleBankId1, sample->sampleAddr, sample->medium); @@ -1280,7 +1279,7 @@ s32 AudioLoad_RelocateFontAndPreloadSamples(s32 fontId, u32 fontDataAddr, Sample sample->sampleAddr, sample->medium); } break; - case 1: + case AUDIOLOAD_ASYNC: if (sample->medium = relocData->medium1) { sampleRamAddr = AudioHeap_AllocTemporarySampleCache(sample->size, relocData->sampleBankId1, sample->sampleAddr, sample->medium); @@ -1294,7 +1293,7 @@ s32 AudioLoad_RelocateFontAndPreloadSamples(s32 fontId, u32 fontDataAddr, Sample continue; } switch (isAsync) { - case 0: + case AUDIOLOAD_SYNC: if (sample->medium == MEDIUM_UNK) { AudioLoad_SyncDmaUnkMedium(sample->sampleAddr, sampleRamAddr, sample->size, gSampleBankTable->unkMediumParam); @@ -1306,7 +1305,7 @@ s32 AudioLoad_RelocateFontAndPreloadSamples(s32 fontId, u32 fontDataAddr, Sample sample->medium = MEDIUM_RAM; } break; - case 1: + case AUDIOLOAD_ASYNC: size = gPreloadSampleStackTop; gPreloadSampleStack[size].sample = sample; gPreloadSampleStack[size].ramAddr = sampleRamAddr; diff --git a/src/audio/audio_playback.c b/src/audio/audio_playback.c index fc858ae4..a4b2ae46 100644 --- a/src/audio/audio_playback.c +++ b/src/audio/audio_playback.c @@ -375,47 +375,47 @@ void func_80012438(SequenceLayer* layer, s32 arg1) { note->playbackState.adsr.fadeOutVel = gAudioBufferParams.ticksPerUpdateInv; note->playbackState.adsr.action.asByte |= 0x10; } - return; - } - noteAttr = ¬e->playbackState.attributes; - if (note->playbackState.adsr.state != 6) { - noteAttr->freqMod = layer->noteFreqMod; - noteAttr->velocity = layer->noteVelocity; - noteAttr->pan = layer->notePan; - noteAttr->stereo = layer->stereo; - if (layer->channel != NULL) { - noteAttr->reverb = layer->channel->targetReverbVol; - noteAttr->gain = layer->channel->reverbIndex; - if (layer->channel->seqPlayer->muted && (layer->channel->muteBehavior & 8)) { - note->noteSubEu.bitField0.finished = 1; + } else { + noteAttr = ¬e->playbackState.attributes; + if (note->playbackState.adsr.state != 6) { + noteAttr->freqMod = layer->noteFreqMod; + noteAttr->velocity = layer->noteVelocity; + noteAttr->pan = layer->notePan; + noteAttr->stereo = layer->stereo; + if (layer->channel != NULL) { + noteAttr->reverb = layer->channel->targetReverbVol; + noteAttr->gain = layer->channel->reverbIndex; + if (layer->channel->seqPlayer->muted && (layer->channel->muteBehavior & 8)) { + note->noteSubEu.bitField0.finished = 1; + } } - } - note->playbackState.priority = 1; - note->playbackState.prevParentLayer = note->playbackState.parentLayer; - note->playbackState.parentLayer = NO_LAYER; + note->playbackState.priority = 1; + note->playbackState.prevParentLayer = note->playbackState.parentLayer; + note->playbackState.parentLayer = NO_LAYER; - if (arg1 == 7) { - note->playbackState.adsr.fadeOutVel = gAudioBufferParams.ticksPerUpdateInv; - note->playbackState.adsr.action.asByte |= 0x10; - note->playbackState.unk_04 = 2; + if (arg1 == 7) { + note->playbackState.adsr.fadeOutVel = gAudioBufferParams.ticksPerUpdateInv; + note->playbackState.adsr.action.asByte |= 0x10; + note->playbackState.unk_04 = 2; - } else { - note->playbackState.unk_04 = 1; - note->playbackState.adsr.action.asByte |= 0x20; - if (layer->adsr.decayIndex == 0) { - note->playbackState.adsr.fadeOutVel = - layer->channel->adsr.decayIndex * gAudioBufferParams.ticksPerUpdateInvScaled; } else { - note->playbackState.adsr.fadeOutVel = - layer->adsr.decayIndex * gAudioBufferParams.ticksPerUpdateInvScaled; + note->playbackState.unk_04 = 1; + note->playbackState.adsr.action.asByte |= 0x20; + if (layer->adsr.decayIndex == 0) { + note->playbackState.adsr.fadeOutVel = + layer->channel->adsr.decayIndex * gAudioBufferParams.ticksPerUpdateInvScaled; + } else { + note->playbackState.adsr.fadeOutVel = + layer->adsr.decayIndex * gAudioBufferParams.ticksPerUpdateInvScaled; + } + note->playbackState.adsr.sustain = + (s32) layer->channel->adsr.sustain * note->playbackState.adsr.current / 256.0f; } - note->playbackState.adsr.sustain = - (s32) layer->channel->adsr.sustain * note->playbackState.adsr.current / 256.0f; } - } - if (arg1 == 6) { - func_80012C40(note); - func_80012C00(¬e->listItem.pool->decaying, ¬e->listItem); + if (arg1 == 6) { + func_80012C40(note); + func_80012C00(¬e->listItem.pool->decaying, ¬e->listItem); + } } } @@ -502,37 +502,37 @@ void func_800128B4(void) { void func_80012964(NotePool* pool) { s32 poolType; - AudioListItem* sp48; - AudioListItem* var_s0; - AudioListItem* sp40; + AudioListItem* poolItem; + AudioListItem* nextPoolItem; + AudioListItem* freeList; for (poolType = 0; poolType < 4; poolType++) { switch (poolType) { case 0: - sp48 = &pool->disabled; - sp40 = &gNoteFreeLists.disabled; + poolItem = &pool->disabled; + freeList = &gNoteFreeLists.disabled; break; case 1: - sp48 = &pool->decaying; - sp40 = &gNoteFreeLists.decaying; + poolItem = &pool->decaying; + freeList = &gNoteFreeLists.decaying; break; case 2: - sp48 = &pool->releasing; - sp40 = &gNoteFreeLists.releasing; + poolItem = &pool->releasing; + freeList = &gNoteFreeLists.releasing; break; case 3: - sp48 = &pool->active; - sp40 = &gNoteFreeLists.active; + poolItem = &pool->active; + freeList = &gNoteFreeLists.active; break; } while (true) { - var_s0 = sp48->next; - if ((var_s0 == sp48) || (var_s0 == NULL)) { + nextPoolItem = poolItem->next; + if ((nextPoolItem == poolItem) || (nextPoolItem == NULL)) { break; } - func_80012C40((Note*) var_s0); - func_800145BC(sp40, var_s0); + func_80012C40((Note*) nextPoolItem); + func_800145BC(freeList, nextPoolItem); } } } @@ -540,9 +540,9 @@ void func_80012964(NotePool* pool) { void func_80012AC4(NotePool* pool, s32 arg1) { s32 var_s0; s32 poolType; - AudioListItem* temp_v0; - AudioListItem* sp48; - AudioListItem* sp44; + AudioListItem* note; + AudioListItem* freeList; + AudioListItem* poolList; func_80012964(pool); poolType = 0; @@ -553,28 +553,28 @@ void func_80012AC4(NotePool* pool, s32 arg1) { } switch (poolType) { case 0: - sp48 = &gNoteFreeLists.disabled; - sp44 = &pool->disabled; + freeList = &gNoteFreeLists.disabled; + poolList = &pool->disabled; break; case 1: - sp48 = &gNoteFreeLists.decaying; - sp44 = &pool->decaying; + freeList = &gNoteFreeLists.decaying; + poolList = &pool->decaying; break; case 2: - sp48 = &gNoteFreeLists.releasing; - sp44 = &pool->releasing; + freeList = &gNoteFreeLists.releasing; + poolList = &pool->releasing; break; case 3: - sp48 = &gNoteFreeLists.active; - sp44 = &pool->active; + freeList = &gNoteFreeLists.active; + poolList = &pool->active; break; } while (var_s0 < arg1) { - temp_v0 = func_800145FC(sp48); - if (temp_v0 == NULL) { + note = func_800145FC(freeList); + if (note == NULL) { break; } - func_800145BC(sp44, temp_v0); + func_800145BC(poolList, note); var_s0++; } poolType++; diff --git a/src/audio/audio_seqplayer.c b/src/audio/audio_seqplayer.c index 445bb819..524d96eb 100644 --- a/src/audio/audio_seqplayer.c +++ b/src/audio/audio_seqplayer.c @@ -44,7 +44,7 @@ void func_80013EA0(SequenceChannel* channel) { channel->finished = 0; channel->stopScript = 0; channel->muted = 0; - channel->hasInstrument = 0; + channel->hasInstrument = false; channel->stereoHeadsetEffects = 0; channel->transposition = 0; channel->largeNotes = 0; @@ -688,11 +688,11 @@ void func_80015330(SequenceChannel* channel, u8 arg1) { channel->instrument = (Instrument*) 1; } else { if ((channel->instOrWave = func_800152C0(channel, arg1, &channel->instrument, &channel->adsr)) == 0) { - channel->hasInstrument = 0; + channel->hasInstrument = false; return; } } - channel->hasInstrument = 1; + channel->hasInstrument = true; } void func_800153C4(SequenceChannel* channel, u8 arg1) { @@ -825,7 +825,7 @@ void func_800153E8(SequenceChannel* channel) { sp52 = ((u16*) gSeqFontTable)[seqPlayer->seqId]; loBits = gSeqFontTable[sp52]; cmd = gSeqFontTable[sp52 + loBits - cmd]; - if (AudioHeap_SearchCaches(1, 2, cmd) != 0) { + if (AudioHeap_SearchCaches(FONT_TABLE, CACHE_EITHER, cmd) != NULL) { channel->fontId = cmd; } /* fallthrough */ @@ -912,7 +912,7 @@ void func_800153E8(SequenceChannel* channel) { sp52 = ((u16*) gSeqFontTable)[seqPlayer->seqId]; loBits = gSeqFontTable[sp52]; cmd = gSeqFontTable[sp52 + loBits - cmd]; - if (AudioHeap_SearchCaches(1, 2, cmd) != 0) { + if (AudioHeap_SearchCaches(FONT_TABLE, CACHE_EITHER, cmd) != NULL) { channel->fontId = cmd; } break; @@ -1387,7 +1387,7 @@ void func_800168BC(void) { s32 i; s32 j; - for (i = 0; i < 48; i++) { + for (i = 0; i < ARRAY_COUNT(gSeqChannels); i++) { gSeqChannels[i].seqPlayer = NULL; gSeqChannels[i].enabled = false; #ifdef AVOID_UB @@ -1400,7 +1400,7 @@ void func_800168BC(void) { } } func_8001463C(); - for (i = 0; i < 64; i++) { + for (i = 0; i < ARRAY_COUNT(gSeqLayers); i++) { gSeqLayers[i].channel = NULL; gSeqLayers[i].enabled = false; } diff --git a/src/audio/audio_tables.c b/src/audio/audio_tables.c index 21270980..370e7c67 100644 --- a/src/audio/audio_tables.c +++ b/src/audio/audio_tables.c @@ -1,99 +1,119 @@ #include "sys.h" -#include "sf64audio_external.h" - -typedef struct { - /* 0x00 */ s16 numEntries; - /* 0x02 */ s16 unkMediumParam; - /* 0x04 */ u32 romAddr; - /* 0x08 */ char pad[0x8]; -} AudioTableBase; - -typedef struct { - /* 0x00 */ u32 romAddr; - /* 0x04 */ u32 size; - /* 0x08 */ s8 medium; - /* 0x09 */ s8 cachePolicy; - /* 0x0A */ s16 shortData1; - /* 0x0C */ s16 shortData2; - /* 0x0E */ s16 shortData3; -} AudioTableEntry; // size = 0x10 +#include "sf64audio_provisional.h" AudioTableBase gSampleBankTableInit = { 4, 0, 0 }; AudioTableEntry gSampleBankTableInitEntries[4] = { - { 0, 0xE1E30, 2, 4, 0, 0, 0 }, - { 0xE1E30, 0xFF9D0, 2, 4, 0, 0, 0 }, - { 0x1E1800, 0x497480, 2, 4, 0, 0, 0 }, - { 0x678C80, 0xC3900, 2, 4, 0, 0, 0 }, + { 0x000000, 0x0E1E30, MEDIUM_CART, CACHEPOLICY_4 }, + { 0x0E1E30, 0x0FF9D0, MEDIUM_CART, CACHEPOLICY_4 }, + { 0x1E1800, 0x497480, MEDIUM_CART, CACHEPOLICY_4 }, + { 0x678C80, 0x0C3900, MEDIUM_CART, CACHEPOLICY_4 }, }; -AudioTableBase gSeqTableInit = { 66, 0, 0 }; -AudioTableEntry gSeqTableInitEntries[66] = { - { 0x00000, 0x3AF0, 2, 0, 0, 0, 0 }, { 0x03AF0, 0x56B0, 2, 0, 0, 0, 0 }, - { 0x091A0, 0x2D80, 2, 3, 0, 0, 0 }, { 0x0BF20, 0x1410, 2, 3, 0, 0, 0 }, - { 0x0D330, 0x1600, 2, 3, 0, 0, 0 }, { 0x0E930, 0xF20, 2, 3, 0, 0, 0 }, - { 0x0F850, 0x13A0, 2, 3, 0, 0, 0 }, { 0x010BF0, 0x1100, 2, 3, 0, 0, 0 }, - { 0x011CF0, 0x19E0, 2, 3, 0, 0, 0 }, { 0x0136D0, 0x13E0, 2, 3, 0, 0, 0 }, - { 0x014AB0, 0x12F0, 2, 3, 0, 0, 0 }, { SEQ_ID_SECTOR_Y, 0, 2, 3, 0, 0, 0 }, - { 0x15DA0, 0xB70, 2, 3, 0, 0, 0 }, { 0x16910, 0x2460, 2, 3, 0, 0, 0 }, - { 0x18D70, 0xD40, 2, 3, 0, 0, 0 }, { SEQ_ID_FORTUNA, 0, 2, 3, 0, 0, 0 }, - { SEQ_ID_TITANIA, 0, 2, 3, 0, 0, 0 }, { 0x19AB0, 0x7F0, 2, 3, 0, 0, 0 }, - { 0x1A2A0, 0x1440, 2, 3, 0, 0, 0 }, { 0x1B6E0, 0x1B20, 2, 3, 0, 0, 0 }, - { SEQ_ID_BOSS_ME, 0, 2, 3, 0, 0, 0 }, { SEQ_ID_BOSS_CO_1, 0, 2, 3, 0, 0, 0 }, - { SEQ_ID_BOSS_CO_1, 0, 2, 3, 0, 0, 0 }, { SEQ_ID_BOSS_ME, 0, 2, 3, 0, 0, 0 }, - { SEQ_ID_BOSS_ME, 0, 2, 3, 0, 0, 0 }, { 0x1D200, 0x1B30, 2, 3, 0, 0, 0 }, - { SEQ_ID_BOSS_CO_1, 0, 2, 3, 0, 0, 0 }, { SEQ_ID_BOSS_ME, 0, 2, 3, 0, 0, 0 }, - { 0x1ED30, 0x10B0, 2, 3, 0, 0, 0 }, { SEQ_ID_BOSS_BO, 0, 2, 3, 0, 0, 0 }, - { SEQ_ID_BOSS_ME, 0, 2, 3, 0, 0, 0 }, { SEQ_ID_BOSS_BO, 0, 2, 3, 0, 0, 0 }, - { SEQ_ID_BOSS_ME, 0, 2, 3, 0, 0, 0 }, { 0x1FDE0, 0x1410, 2, 3, 0, 0, 0 }, - { 0x211F0, 0xF30, 2, 3, 0, 0, 0 }, { 0x22120, 0x2B30, 2, 3, 0, 0, 0 }, - { 0x24C50, 0x7F0, 2, 3, 0, 0, 0 }, { 0x25440, 0x13B0, 2, 3, 0, 0, 0 }, - { 0x267F0, 0xC70, 2, 3, 0, 0, 0 }, { 0x27460, 0x180, 2, 3, 0, 0, 0 }, - { 0x275E0, 0x5C0, 2, 3, 0, 0, 0 }, { SEQ_ID_TITLE, 0, 2, 3, 0, 0, 0 }, - { 0x27BA0, 0x4E40, 2, 3, 0, 0, 0 }, { 0x2C9E0, 0x14E0, 2, 3, 0, 0, 0 }, - { 0x2DEC0, 0x400, 2, 3, 0, 0, 0 }, { 0x2E2C0, 0x800, 2, 3, 0, 0, 0 }, - { 0x2EAC0, 0x1AB0, 2, 3, 0, 0, 0 }, { 0x30570, 0x1AA0, 2, 3, 0, 0, 0 }, - { SEQ_ID_BOSS_ME, 0, 2, 3, 0, 0, 0 }, { 0x32010, 0x1370, 2, 3, 0, 0, 0 }, - { 0x33380, 0xB80, 2, 3, 0, 0, 0 }, { 0x33F00, 0x5C0, 2, 3, 0, 0, 0 }, - { SEQ_ID_CO_INTRO, 0, 2, 3, 0, 0, 0 }, { SEQ_ID_CO_INTRO, 0, 2, 3, 0, 0, 0 }, - { 0x344C0, 0x550, 2, 3, 0, 0, 0 }, { 0x34A10, 0x360, 2, 3, 0, 0, 0 }, - { 0x34D70, 0x620, 2, 3, 0, 0, 0 }, { SEQ_ID_VS_MENU, 0, 2, 3, 0, 0, 0 }, - { 0x35390, 0xA70, 2, 3, 0, 0, 0 }, { SEQ_ID_STAR_WOLF, 0, 2, 3, 0, 0, 0 }, - { 0x35E00, 0xAB0, 2, 3, 0, 0, 0 }, { 0x368B0, 0x13D0, 2, 3, 0, 0, 0 }, - { 0x37C80, 0x250, 2, 3, 0, 0, 0 }, { 0x37ED0, 0x6F0, 2, 3, 0, 0, 0 }, - { 0x385C0, 0xFB0, 2, 3, 0, 0, 0 }, { 0x39570, 0x1780, 2, 3, 0, 0, 0 }, +AudioTableBase gSeqTableInit = { SEQ_ID_MAX, 0, 0 }; +AudioTableEntry gSeqTableInitEntries[SEQ_ID_MAX] = { + { 0x00000, 0x3AF0, MEDIUM_CART, CACHEPOLICY_0 }, { 0x03AF0, 0x56B0, MEDIUM_CART, CACHEPOLICY_0 }, + { 0x091A0, 0x2D80, MEDIUM_CART, CACHEPOLICY_3 }, { 0x0BF20, 0x1410, MEDIUM_CART, CACHEPOLICY_3 }, + { 0x0D330, 0x1600, MEDIUM_CART, CACHEPOLICY_3 }, { 0x0E930, 0xF20, MEDIUM_CART, CACHEPOLICY_3 }, + { 0x0F850, 0x13A0, MEDIUM_CART, CACHEPOLICY_3 }, { 0x10BF0, 0x1100, MEDIUM_CART, CACHEPOLICY_3 }, + { 0x11CF0, 0x19E0, MEDIUM_CART, CACHEPOLICY_3 }, { 0x136D0, 0x13E0, MEDIUM_CART, CACHEPOLICY_3 }, + { 0x14AB0, 0x12F0, MEDIUM_CART, CACHEPOLICY_3 }, { SEQ_ID_SECTOR_Y, 0, MEDIUM_CART, CACHEPOLICY_3 }, + { 0x15DA0, 0xB70, MEDIUM_CART, CACHEPOLICY_3 }, { 0x16910, 0x2460, MEDIUM_CART, CACHEPOLICY_3 }, + { 0x18D70, 0xD40, MEDIUM_CART, CACHEPOLICY_3 }, { SEQ_ID_FORTUNA, 0, MEDIUM_CART, CACHEPOLICY_3 }, + { SEQ_ID_TITANIA, 0, MEDIUM_CART, CACHEPOLICY_3 }, { 0x19AB0, 0x7F0, MEDIUM_CART, CACHEPOLICY_3 }, + { 0x1A2A0, 0x1440, MEDIUM_CART, CACHEPOLICY_3 }, { 0x1B6E0, 0x1B20, MEDIUM_CART, CACHEPOLICY_3 }, + { SEQ_ID_BOSS_ME, 0, MEDIUM_CART, CACHEPOLICY_3 }, { SEQ_ID_BOSS_CO_1, 0, MEDIUM_CART, CACHEPOLICY_3 }, + { SEQ_ID_BOSS_CO_1, 0, MEDIUM_CART, CACHEPOLICY_3 }, { SEQ_ID_BOSS_ME, 0, MEDIUM_CART, CACHEPOLICY_3 }, + { SEQ_ID_BOSS_ME, 0, MEDIUM_CART, CACHEPOLICY_3 }, { 0x1D200, 0x1B30, MEDIUM_CART, CACHEPOLICY_3 }, + { SEQ_ID_BOSS_CO_1, 0, MEDIUM_CART, CACHEPOLICY_3 }, { SEQ_ID_BOSS_ME, 0, MEDIUM_CART, CACHEPOLICY_3 }, + { 0x1ED30, 0x10B0, MEDIUM_CART, CACHEPOLICY_3 }, { SEQ_ID_BOSS_BO, 0, MEDIUM_CART, CACHEPOLICY_3 }, + { SEQ_ID_BOSS_ME, 0, MEDIUM_CART, CACHEPOLICY_3 }, { SEQ_ID_BOSS_BO, 0, MEDIUM_CART, CACHEPOLICY_3 }, + { SEQ_ID_BOSS_ME, 0, MEDIUM_CART, CACHEPOLICY_3 }, { 0x1FDE0, 0x1410, MEDIUM_CART, CACHEPOLICY_3 }, + { 0x211F0, 0xF30, MEDIUM_CART, CACHEPOLICY_3 }, { 0x22120, 0x2B30, MEDIUM_CART, CACHEPOLICY_3 }, + { 0x24C50, 0x7F0, MEDIUM_CART, CACHEPOLICY_3 }, { 0x25440, 0x13B0, MEDIUM_CART, CACHEPOLICY_3 }, + { 0x267F0, 0xC70, MEDIUM_CART, CACHEPOLICY_3 }, { 0x27460, 0x180, MEDIUM_CART, CACHEPOLICY_3 }, + { 0x275E0, 0x5C0, MEDIUM_CART, CACHEPOLICY_3 }, { SEQ_ID_TITLE, 0, MEDIUM_CART, CACHEPOLICY_3 }, + { 0x27BA0, 0x4E40, MEDIUM_CART, CACHEPOLICY_3 }, { 0x2C9E0, 0x14E0, MEDIUM_CART, CACHEPOLICY_3 }, + { 0x2DEC0, 0x400, MEDIUM_CART, CACHEPOLICY_3 }, { 0x2E2C0, 0x800, MEDIUM_CART, CACHEPOLICY_3 }, + { 0x2EAC0, 0x1AB0, MEDIUM_CART, CACHEPOLICY_3 }, { 0x30570, 0x1AA0, MEDIUM_CART, CACHEPOLICY_3 }, + { SEQ_ID_BOSS_ME, 0, MEDIUM_CART, CACHEPOLICY_3 }, { 0x32010, 0x1370, MEDIUM_CART, CACHEPOLICY_3 }, + { 0x33380, 0xB80, MEDIUM_CART, CACHEPOLICY_3 }, { 0x33F00, 0x5C0, MEDIUM_CART, CACHEPOLICY_3 }, + { SEQ_ID_CO_INTRO, 0, MEDIUM_CART, CACHEPOLICY_3 }, { SEQ_ID_CO_INTRO, 0, MEDIUM_CART, CACHEPOLICY_3 }, + { 0x344C0, 0x550, MEDIUM_CART, CACHEPOLICY_3 }, { 0x34A10, 0x360, MEDIUM_CART, CACHEPOLICY_3 }, + { 0x34D70, 0x620, MEDIUM_CART, CACHEPOLICY_3 }, { SEQ_ID_VS_MENU, 0, MEDIUM_CART, CACHEPOLICY_3 }, + { 0x35390, 0xA70, MEDIUM_CART, CACHEPOLICY_3 }, { SEQ_ID_STAR_WOLF, 0, MEDIUM_CART, CACHEPOLICY_3 }, + { 0x35E00, 0xAB0, MEDIUM_CART, CACHEPOLICY_3 }, { 0x368B0, 0x13D0, MEDIUM_CART, CACHEPOLICY_3 }, + { 0x37C80, 0x250, MEDIUM_CART, CACHEPOLICY_3 }, { 0x37ED0, 0x6F0, MEDIUM_CART, CACHEPOLICY_3 }, + { 0x385C0, 0xFB0, MEDIUM_CART, CACHEPOLICY_3 }, { 0x39570, 0x1780, MEDIUM_CART, CACHEPOLICY_3 }, }; +#define SOUNDFONT_ENTRY(offset, size, medium, cachePolicy, bank1, bank2, numInst, numDrums) \ + { \ + offset, size, medium, cachePolicy, (((bank1) &0xFF) << 8) | ((bank2) &0xFF), \ + (((numInst) &0xFF) << 8) | ((numDrums) &0xFF) \ + } + AudioTableBase gSoundFontTableInit = { 33, 0, 0 }; AudioTableEntry gSoundFontTableInitEntries[33] = { - { 0, 0x2F00, 2, 0, 0xFF, 0x7F00, 0 }, { 0x2F00, 0xFE0, 2, 0, 0x1FF, 0x7F00, 0 }, - { 0x3EE0, 0x640, 2, 0, 0x2FF, 0x7F00, 0 }, { 0x4520, 0x1560, 2, 0, 0x2FF, 0x7F00, 0 }, - { 0x5A80, 0xC00, 2, 0, 0x2FF, 0x7F00, 0 }, { 0x6680, 0xDE0, 2, 0, 0x2FF, 0x7F00, 0 }, - { 0x7460, 0x1200, 2, 0, 0x2FF, 0x7F00, 0 }, { 0x8660, 0x1040, 2, 0, 0x2FF, 0x7F00, 0 }, - { 0x96A0, 0xE40, 2, 0, 0x2FF, 0x7F00, 0 }, { 0xA4E0, 0xC20, 2, 0, 0x2FF, 0x7F00, 0 }, - { 0xB100, 0x1920, 2, 0, 0x2FF, 0x7F00, 0 }, { 0xCA20, 0xA20, 2, 0, 0x2FF, 0x7F00, 0 }, - { 0xD440, 0xA00, 2, 0, 0x2FF, 0x7F00, 0 }, { 0xDE40, 0xD60, 2, 0, 0x2FF, 0x7F00, 0 }, - { 0xEBA0, 0xC20, 2, 0, 0x2FF, 0x7F00, 0 }, { 0xF7C0, 0xF00, 2, 0, 0x2FF, 0x7F00, 0 }, - { 0x106C0, 0xF20, 2, 0, 0x2FF, 0x7F00, 0 }, { 0x115E0, 0xE20, 2, 0, 0x2FF, 0x7F00, 0 }, - { 0x12400, 0x10A0, 2, 0, 0x2FF, 0x7F00, 0 }, { 0x134A0, 0x2580, 2, 0, 0x2FF, 0x7F00, 0 }, - { 0x15A20, 0x9E0, 2, 0, 0x2FF, 0x7F00, 0 }, { 0x16400, 0x1220, 2, 3, 0x3FF, 0x1040, 0 }, - { 0x17620, 0x180, 2, 3, 0x3FF, 0x300, 0 }, { 0x177A0, 0x11C0, 2, 3, 0x3FF, 0x1040, 0 }, - { 0x18960, 0x940, 2, 3, 0x3FF, 0xA40, 0 }, { 0x192A0, 0x9E0, 2, 3, 0x3FF, 0xA40, 0 }, - { 0x19C80, 0x920, 2, 3, 0x3FF, 0xA40, 0 }, { 0x1A5A0, 0x9E0, 2, 3, 0x3FF, 0xA40, 0 }, - { 0x1AF80, 0x9E0, 2, 3, 0x3FF, 0xA40, 0 }, { 0x1B960, 0xA10, 2, 3, 0x3FF, 0xA40, 0 }, - { 0x1C370, 0x9E0, 2, 3, 0x3FF, 0xA40, 0 }, { 0x1CD50, 0xA00, 2, 3, 0x3FF, 0xA40, 0 }, - { 0x1D750, 0x8D0, 2, 3, 0x3FF, 0xA40, 0 }, + SOUNDFONT_ENTRY(0x00000, 0x2F00, MEDIUM_CART, CACHEPOLICY_0, SAMPLES_SFX, SAMPLES_NONE, 127, 0), + SOUNDFONT_ENTRY(0x02F00, 0x0FE0, MEDIUM_CART, CACHEPOLICY_0, SAMPLES_MAP, SAMPLES_NONE, 127, 0), + SOUNDFONT_ENTRY(0x03EE0, 0x0640, MEDIUM_CART, CACHEPOLICY_0, SAMPLES_VOICE, SAMPLES_NONE, 127, 0), + SOUNDFONT_ENTRY(0x04520, 0x1560, MEDIUM_CART, CACHEPOLICY_0, SAMPLES_VOICE, SAMPLES_NONE, 127, 0), + SOUNDFONT_ENTRY(0x05A80, 0x0C00, MEDIUM_CART, CACHEPOLICY_0, SAMPLES_VOICE, SAMPLES_NONE, 127, 0), + SOUNDFONT_ENTRY(0x06680, 0x0DE0, MEDIUM_CART, CACHEPOLICY_0, SAMPLES_VOICE, SAMPLES_NONE, 127, 0), + SOUNDFONT_ENTRY(0x07460, 0x1200, MEDIUM_CART, CACHEPOLICY_0, SAMPLES_VOICE, SAMPLES_NONE, 127, 0), + SOUNDFONT_ENTRY(0x08660, 0x1040, MEDIUM_CART, CACHEPOLICY_0, SAMPLES_VOICE, SAMPLES_NONE, 127, 0), + SOUNDFONT_ENTRY(0x096A0, 0x0E40, MEDIUM_CART, CACHEPOLICY_0, SAMPLES_VOICE, SAMPLES_NONE, 127, 0), + SOUNDFONT_ENTRY(0x0A4E0, 0x0C20, MEDIUM_CART, CACHEPOLICY_0, SAMPLES_VOICE, SAMPLES_NONE, 127, 0), + SOUNDFONT_ENTRY(0x0B100, 0x1920, MEDIUM_CART, CACHEPOLICY_0, SAMPLES_VOICE, SAMPLES_NONE, 127, 0), + SOUNDFONT_ENTRY(0x0CA20, 0x0A20, MEDIUM_CART, CACHEPOLICY_0, SAMPLES_VOICE, SAMPLES_NONE, 127, 0), + SOUNDFONT_ENTRY(0x0D440, 0x0A00, MEDIUM_CART, CACHEPOLICY_0, SAMPLES_VOICE, SAMPLES_NONE, 127, 0), + SOUNDFONT_ENTRY(0x0DE40, 0x0D60, MEDIUM_CART, CACHEPOLICY_0, SAMPLES_VOICE, SAMPLES_NONE, 127, 0), + SOUNDFONT_ENTRY(0x0EBA0, 0x0C20, MEDIUM_CART, CACHEPOLICY_0, SAMPLES_VOICE, SAMPLES_NONE, 127, 0), + SOUNDFONT_ENTRY(0x0F7C0, 0x0F00, MEDIUM_CART, CACHEPOLICY_0, SAMPLES_VOICE, SAMPLES_NONE, 127, 0), + SOUNDFONT_ENTRY(0x106C0, 0x0F20, MEDIUM_CART, CACHEPOLICY_0, SAMPLES_VOICE, SAMPLES_NONE, 127, 0), + SOUNDFONT_ENTRY(0x115E0, 0x0E20, MEDIUM_CART, CACHEPOLICY_0, SAMPLES_VOICE, SAMPLES_NONE, 127, 0), + SOUNDFONT_ENTRY(0x12400, 0x10A0, MEDIUM_CART, CACHEPOLICY_0, SAMPLES_VOICE, SAMPLES_NONE, 127, 0), + SOUNDFONT_ENTRY(0x134A0, 0x2580, MEDIUM_CART, CACHEPOLICY_0, SAMPLES_VOICE, SAMPLES_NONE, 127, 0), + SOUNDFONT_ENTRY(0x15A20, 0x09E0, MEDIUM_CART, CACHEPOLICY_0, SAMPLES_VOICE, SAMPLES_NONE, 127, 0), + SOUNDFONT_ENTRY(0x16400, 0x1220, MEDIUM_CART, CACHEPOLICY_3, SAMPLES_INST, SAMPLES_NONE, 16, 64), + SOUNDFONT_ENTRY(0x17620, 0x0180, MEDIUM_CART, CACHEPOLICY_3, SAMPLES_INST, SAMPLES_NONE, 3, 0), + SOUNDFONT_ENTRY(0x177A0, 0x11C0, MEDIUM_CART, CACHEPOLICY_3, SAMPLES_INST, SAMPLES_NONE, 16, 64), + SOUNDFONT_ENTRY(0x18960, 0x0940, MEDIUM_CART, CACHEPOLICY_3, SAMPLES_INST, SAMPLES_NONE, 10, 64), + SOUNDFONT_ENTRY(0x192A0, 0x09E0, MEDIUM_CART, CACHEPOLICY_3, SAMPLES_INST, SAMPLES_NONE, 10, 64), + SOUNDFONT_ENTRY(0x19C80, 0x0920, MEDIUM_CART, CACHEPOLICY_3, SAMPLES_INST, SAMPLES_NONE, 10, 64), + SOUNDFONT_ENTRY(0x1A5A0, 0x09E0, MEDIUM_CART, CACHEPOLICY_3, SAMPLES_INST, SAMPLES_NONE, 10, 64), + SOUNDFONT_ENTRY(0x1AF80, 0x09E0, MEDIUM_CART, CACHEPOLICY_3, SAMPLES_INST, SAMPLES_NONE, 10, 64), + SOUNDFONT_ENTRY(0x1B960, 0x0A10, MEDIUM_CART, CACHEPOLICY_3, SAMPLES_INST, SAMPLES_NONE, 10, 64), + SOUNDFONT_ENTRY(0x1C370, 0x09E0, MEDIUM_CART, CACHEPOLICY_3, SAMPLES_INST, SAMPLES_NONE, 10, 64), + SOUNDFONT_ENTRY(0x1CD50, 0x0A00, MEDIUM_CART, CACHEPOLICY_3, SAMPLES_INST, SAMPLES_NONE, 10, 64), + SOUNDFONT_ENTRY(0x1D750, 0x08D0, MEDIUM_CART, CACHEPOLICY_3, SAMPLES_INST, SAMPLES_NONE, 10, 64), }; -u8 gSeqFontTableInit[288] = { - 0, 132, 0, 134, 0, 155, 0, 157, 0, 159, 0, 161, 0, 163, 0, 165, 0, 167, 0, 169, 0, 171, 0, 173, - 0, 175, 0, 177, 0, 179, 0, 181, 0, 183, 0, 185, 0, 187, 0, 189, 0, 191, 0, 193, 0, 195, 0, 197, - 0, 199, 0, 201, 0, 203, 0, 205, 0, 207, 0, 209, 0, 211, 0, 213, 0, 215, 0, 217, 0, 219, 0, 221, - 0, 223, 0, 225, 0, 227, 0, 229, 0, 231, 0, 233, 0, 235, 0, 237, 0, 239, 0, 241, 0, 243, 0, 245, - 0, 247, 0, 249, 0, 251, 0, 253, 0, 255, 1, 1, 1, 3, 1, 5, 1, 7, 1, 9, 1, 11, 1, 13, - 1, 15, 1, 17, 1, 19, 1, 21, 1, 23, 1, 25, 1, 0, 20, 20, 19, 18, 17, 16, 15, 14, 13, 12, - 11, 10, 9, 8, 7, 6, 5, 4, 3, 1, 2, 1, 24, 1, 29, 1, 25, 1, 26, 1, 28, 1, 25, 1, - 25, 1, 29, 1, 24, 1, 29, 1, 28, 1, 25, 1, 30, 1, 24, 1, 25, 1, 28, 1, 28, 1, 31, 1, - 31, 1, 28, 1, 28, 1, 31, 1, 31, 1, 31, 1, 28, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, - 31, 1, 32, 1, 21, 1, 21, 1, 22, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 23, 1, 25, 1, - 21, 1, 21, 1, 29, 1, 25, 1, 31, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, - 21, 1, 21, 1, 27, 1, 25, 1, 21, 1, 25, 1, 25, 1, 25, 1, 21, 1, 31, 0, 0, 0, 0, 0, +#define AS_BYTES(x) (((x) >> 8) & 0xFF), ((x) &0xFF) + +// clang-format off + +u8 gSeqFontTableInit[283] = { + // Offset into this table for sequence sound font list + AS_BYTES(132), AS_BYTES(134), AS_BYTES(155), AS_BYTES(157), AS_BYTES(159), AS_BYTES(161), AS_BYTES(163), + AS_BYTES(165), AS_BYTES(167), AS_BYTES(169), AS_BYTES(171), AS_BYTES(173), AS_BYTES(175), AS_BYTES(177), + AS_BYTES(179), AS_BYTES(181), AS_BYTES(183), AS_BYTES(185), AS_BYTES(187), AS_BYTES(189), AS_BYTES(191), + AS_BYTES(193), AS_BYTES(195), AS_BYTES(197), AS_BYTES(199), AS_BYTES(201), AS_BYTES(203), AS_BYTES(205), + AS_BYTES(207), AS_BYTES(209), AS_BYTES(211), AS_BYTES(213), AS_BYTES(215), AS_BYTES(217), AS_BYTES(219), + AS_BYTES(221), AS_BYTES(223), AS_BYTES(225), AS_BYTES(227), AS_BYTES(229), AS_BYTES(231), AS_BYTES(233), + AS_BYTES(235), AS_BYTES(237), AS_BYTES(239), AS_BYTES(241), AS_BYTES(243), AS_BYTES(245), AS_BYTES(247), + AS_BYTES(249), AS_BYTES(251), AS_BYTES(253), AS_BYTES(255), AS_BYTES(257), AS_BYTES(259), AS_BYTES(261), + AS_BYTES(263), AS_BYTES(265), AS_BYTES(267), AS_BYTES(269), AS_BYTES(271), AS_BYTES(273), AS_BYTES(275), + AS_BYTES(277), AS_BYTES(279), AS_BYTES(281), + + // sound font for SFX sequence + 1, 0, + // sound fonts voice sequence + 20, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 1, 2, + // sound fonts for BGM sequences. Each sequence has a single sound font. + 1, 24, 1, 29, 1, 25, 1, 26, 1, 28, 1, 25, 1, 25, 1, 29, 1, 24, 1, 29, 1, 28, 1, 25, 1, 30, 1, 24, 1, 25, 1, 28, + 1, 28, 1, 31, 1, 31, 1, 28, 1, 28, 1, 31, 1, 31, 1, 31, 1, 28, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 32, + 1, 21, 1, 21, 1, 22, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 23, 1, 25, 1, 21, 1, 21, 1, 29, 1, 25, 1, 31, 1, 21, + 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 27, 1, 25, 1, 21, 1, 25, 1, 25, 1, 25, 1, 21, 1, 31 }; + +// clang-format on diff --git a/src/audio/audio_thread.c b/src/audio/audio_thread.c index 034bc1c8..93e95988 100644 --- a/src/audio/audio_thread.c +++ b/src/audio/audio_thread.c @@ -42,15 +42,15 @@ static const char devstr14[] = "Error : Queue is not empty ( %x ) \n"; SPTask* AudioThread_CreateTask(void) { static s32 gMaxAbiCmdCnt = 128; static SPTask* gWaitingAudioTask = NULL; - u32 sp54; - s32 sp50; - s32 sp4C; + u32 aiSamplesLeft; + s32 abiCmdCount; + s32 aiBuffIndex; s32 pad48; OSTask_t* task; - u16* sp40; + u16* aiBuffer; s32 pad3C; - u32 sp38; - u32 sp34; + u32 specId; + u32 msg; s32 pad30; gAudioTaskCountQ++; @@ -62,54 +62,54 @@ SPTask* AudioThread_CreateTask(void) { gCurAiBuffIndex++; gCurAiBuffIndex %= 3; - sp4C = (gCurAiBuffIndex + 1) % 3; - sp54 = osAiGetLength() >> 2; - if ((gResetTimer < 16) && (gAiBuffLengths[sp4C] != 0)) { - osAiSetNextBuffer(gAiBuffers[sp4C], gAiBuffLengths[sp4C] * 4); + aiBuffIndex = (gCurAiBuffIndex + 1) % 3; + aiSamplesLeft = osAiGetLength() / 4; + if ((gAudioResetTimer < 16) && (gAiBuffLengths[aiBuffIndex] != 0)) { + osAiSetNextBuffer(gAiBuffers[aiBuffIndex], gAiBuffLengths[aiBuffIndex] * 4); } if (gCurAudioFrameDmaCount && gCurAudioFrameDmaCount) {} gCurAudioFrameDmaCount = 0; AudioLoad_DecreaseSampleDmaTtls(); - AudioLoad_ProcessLoads(gResetStatus); - if (MQ_GET_MESG(gAudioSpecQueue, &sp38)) { - if (gResetStatus == 0) { - gResetStatus = 5; + AudioLoad_ProcessLoads(gAudioResetStep); + if (MQ_GET_MESG(gAudioSpecQueue, &specId)) { + if (gAudioResetStep == 0) { + gAudioResetStep = 5; } - gAudioSpecId = sp38; + gAudioSpecId = specId; } - if ((gResetStatus != 0) && (AudioHeap_ResetStep() == 0)) { - if (gResetStatus == 0) { + if ((gAudioResetStep != 0) && (AudioHeap_ResetStep() == 0)) { + if (gAudioResetStep == 0) { osSendMesg(gAudioResetQueue, (OSMesg) (s32) gAudioSpecId, OS_MESG_NOBLOCK); } gWaitingAudioTask = NULL; return NULL; } - if (gResetTimer > 16) { + if (gAudioResetTimer > 16) { return NULL; } - if (gResetTimer != 0) { - gResetTimer++; + if (gAudioResetTimer != 0) { + gAudioResetTimer++; } gAudioCurTask = &gAudioRspTasks[gAudioTaskIndexQ]; gCurAbiCmdBuffer = gAbiCmdBuffs[gAudioTaskIndexQ]; - sp4C = gCurAiBuffIndex; - sp40 = gAiBuffers[sp4C]; - gAiBuffLengths[sp4C] = ((gAudioBufferParams.samplesPerFrameTarget - sp54 + 0x80) & ~0xF) + 0x10; + aiBuffIndex = gCurAiBuffIndex; + aiBuffer = gAiBuffers[aiBuffIndex]; + gAiBuffLengths[aiBuffIndex] = ALIGN16_ALT(gAudioBufferParams.samplesPerFrameTarget - aiSamplesLeft + 0x80); - if (gAiBuffLengths[sp4C] < gAudioBufferParams.minAiBufferLength) { - gAiBuffLengths[sp4C] = gAudioBufferParams.minAiBufferLength; + if (gAiBuffLengths[aiBuffIndex] < gAudioBufferParams.minAiBufferLength) { + gAiBuffLengths[aiBuffIndex] = gAudioBufferParams.minAiBufferLength; } - if (gAiBuffLengths[sp4C] > gAudioBufferParams.maxAiBufferLength) { - gAiBuffLengths[sp4C] = gAudioBufferParams.maxAiBufferLength; + if (gAiBuffLengths[aiBuffIndex] > gAudioBufferParams.maxAiBufferLength) { + gAiBuffLengths[aiBuffIndex] = gAudioBufferParams.maxAiBufferLength; } - while (MQ_GET_MESG(gThreadCmdProcQueue, &sp34)) { - AudioThread_ProcessCmds(sp34); + while (MQ_GET_MESG(gThreadCmdProcQueue, &msg)) { + AudioThread_ProcessCmds(msg); } - gCurAbiCmdBuffer = func_80009B64(gCurAbiCmdBuffer, &sp50, sp40, gAiBuffLengths[sp4C]); + gCurAbiCmdBuffer = func_80009B64(gCurAbiCmdBuffer, &abiCmdCount, aiBuffer, gAiBuffLengths[aiBuffIndex]); gAudioRandom = osGetCount() * (gAudioRandom + gAudioTaskCountQ); - gAudioRandom = gAiBuffers[sp4C][gAudioTaskCountQ & 0xFF] + gAudioRandom; + gAudioRandom = gAiBuffers[aiBuffIndex][gAudioTaskCountQ & 0xFF] + gAudioRandom; - sp4C = gAudioTaskIndexQ; + aiBuffIndex = gAudioTaskIndexQ; gAudioCurTask->mesgQueue = NULL; gAudioCurTask->msg = NULL; @@ -132,14 +132,14 @@ SPTask* AudioThread_CreateTask(void) { task->output_buff = NULL; task->output_buff_size = NULL; if (1) {} - task->data_ptr = (u64*) gAbiCmdBuffs[sp4C]; - task->data_size = sp50 * sizeof(Acmd); + task->data_ptr = (u64*) gAbiCmdBuffs[aiBuffIndex]; + task->data_size = abiCmdCount * sizeof(Acmd); task->yield_data_ptr = NULL; task->yield_data_size = 0; - if (gMaxAbiCmdCnt < sp50) { - gMaxAbiCmdCnt = sp50; + if (gMaxAbiCmdCnt < abiCmdCount) { + gMaxAbiCmdCnt = abiCmdCount; } if (gAudioBufferParams.count == 1) { @@ -158,7 +158,7 @@ void AudioThread_ProcessGlobalCmd(AudioCmd* cmd) { AudioLoad_SyncLoadSeqParts(cmd->arg1, 3); break; case AUDIOCMD_OP_GLOBAL_INIT_SEQPLAYER: - case 0x88: + case AUDIOCMD_OP_GLOBAL_INIT_SEQPLAYER_ALT: AudioLoad_SyncInitSeqPlayer(cmd->arg0, cmd->arg1, cmd->arg2); AudioThread_SetFadeInTimer(cmd->arg0, cmd->data); break; @@ -171,7 +171,7 @@ void AudioThread_ProcessGlobalCmd(AudioCmd* cmd) { } } break; - case 0x84: + case AUDIOCMD_OP_GLOBAL_UNK_84: break; case AUDIOCMD_OP_GLOBAL_SET_SOUND_MODE: gAudioSoundMode = cmd->asUInt; @@ -422,9 +422,9 @@ void AudioThread_ResetAudioHeap(s32 specId) { } void AudioThread_PreNMIReset(void) { - gResetTimer = 1; + gAudioResetTimer = 1; AudioThread_ResetAudioHeap(AUDIOSPEC_0); - gResetStatus = 0; + gAudioResetStep = 0; } void AudioThread_Init(void) { diff --git a/src/overlays/ovl_menu/fox_option.c b/src/overlays/ovl_menu/fox_option.c index f89be570..7e5073a0 100644 --- a/src/overlays/ovl_menu/fox_option.c +++ b/src/overlays/ovl_menu/fox_option.c @@ -1318,9 +1318,9 @@ void Option_SoundInit(void) { gVolumeSettings[AUDIO_TYPE_SFX] = 99; } - Audio_SetVolume(0, gVolumeSettings[AUDIO_TYPE_MUSIC]); - Audio_SetVolume(1, gVolumeSettings[AUDIO_TYPE_VOICE]); - Audio_SetVolume(2, gVolumeSettings[AUDIO_TYPE_SFX]); + Audio_SetVolume(AUDIO_TYPE_MUSIC, gVolumeSettings[AUDIO_TYPE_MUSIC]); + Audio_SetVolume(AUDIO_TYPE_VOICE, gVolumeSettings[AUDIO_TYPE_VOICE]); + Audio_SetVolume(AUDIO_TYPE_SFX, gVolumeSettings[AUDIO_TYPE_SFX]); D_menu_801AEB48[0].unk_18 = gVolumeSettings[AUDIO_TYPE_MUSIC] + 146.0f; D_menu_801AEB48[1].unk_18 = gVolumeSettings[AUDIO_TYPE_VOICE] + 146.0f; diff --git a/tools/aifc_decode b/tools/aifc_decode new file mode 100644 index 00000000..0f6adb02 Binary files /dev/null and b/tools/aifc_decode differ diff --git a/tools/aifc_decode.c b/tools/aifc_decode.c new file mode 100644 index 00000000..19d13c6c --- /dev/null +++ b/tools/aifc_decode.c @@ -0,0 +1,669 @@ +/** + * Bruteforcing decoder for converting ADPCM-encoded AIFC into AIFF, in a way + * that roundtrips with vadpcm_enc. + */ +#include +#include +#include +#include +#include +#include +#include + +typedef signed char s8; +typedef short s16; +typedef int s32; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +typedef float f32; + +#define bswap16(x) __builtin_bswap16(x) +#define bswap32(x) __builtin_bswap32(x) +#define BSWAP16(x) x = __builtin_bswap16(x) +#define BSWAP32(x) x = __builtin_bswap32(x) +#define BSWAP16_MANY(x, n) for (s32 _i = 0; _i < n; _i++) BSWAP16((x)[_i]) + +#define NORETURN __attribute__((noreturn)) +#define UNUSED __attribute__((unused)) + +typedef struct { + u32 ckID; + u32 ckSize; +} ChunkHeader; + +typedef struct { + u32 ckID; + u32 ckSize; + u32 formType; +} Chunk; + +typedef struct { + s16 numChannels; + u16 numFramesH; + u16 numFramesL; + s16 sampleSize; + s16 sampleRate[5]; // 80-bit float + u16 compressionTypeH; + u16 compressionTypeL; +} CommonChunk; + +typedef struct { + s16 MarkerID; + u16 positionH; + u16 positionL; +} Marker; + +typedef struct { + s16 playMode; + s16 beginLoop; + s16 endLoop; +} Loop; + +typedef struct { + s8 baseNote; + s8 detune; + s8 lowNote; + s8 highNote; + s8 lowVelocity; + s8 highVelocity; + s16 gain; + Loop sustainLoop; + Loop releaseLoop; +} InstrumentChunk; + +typedef struct { + s32 offset; + s32 blockSize; +} SoundDataChunk; + +typedef struct { + s16 version; + s16 order; + s16 nEntries; +} CodeChunk; + +typedef struct +{ + u32 start; + u32 end; + u32 count; + s16 state[16]; +} ALADPCMloop; + + +static char usage[] = "input.aifc output.aiff"; +static const char *progname, *infilename; + +#define checked_fread(a, b, c, d) if (fread(a, b, c, d) != c) fail_parse("error parsing file") + +NORETURN +void fail_parse(const char *fmt, ...) +{ + char *formatted = NULL; + va_list ap; + va_start(ap, fmt); + int size = vsnprintf(NULL, 0, fmt, ap); + va_end(ap); + if (size >= 0) { + size++; + formatted = malloc(size); + if (formatted != NULL) { + va_start(ap, fmt); + size = vsnprintf(formatted, size, fmt, ap); + va_end(ap); + if (size < 0) { + free(formatted); + formatted = NULL; + } + } + } + + if (formatted != NULL) { + fprintf(stderr, "%s: %s [%s]\n", progname, formatted, infilename); + free(formatted); + } + exit(1); +} + +s32 myrand() +{ + static u64 state = 1619236481962341ULL; + state *= 3123692312231ULL; + state++; + return state >> 33; +} + +s16 qsample(s32 x, s32 scale) +{ + // Compute x / 2^scale rounded to the nearest integer, breaking ties towards zero. + if (scale == 0) return x; + return (x + (1 << (scale - 1)) - (x > 0)) >> scale; +} + +s16 clamp_to_s16(s32 x) +{ + if (x < -0x8000) return -0x8000; + if (x > 0x7fff) return 0x7fff; + return (s16) x; +} + +s32 toi4(s32 x) +{ + if (x >= 8) return x - 16; + return x; +} + +s32 readaifccodebook(FILE *fhandle, s32 ****table, s16 *order, s16 *npredictors) +{ + checked_fread(order, sizeof(s16), 1, fhandle); + BSWAP16(*order); + checked_fread(npredictors, sizeof(s16), 1, fhandle); + BSWAP16(*npredictors); + *table = malloc(*npredictors * sizeof(s32 **)); + for (s32 i = 0; i < *npredictors; i++) { + (*table)[i] = malloc(8 * sizeof(s32 *)); + for (s32 j = 0; j < 8; j++) { + (*table)[i][j] = malloc((*order + 8) * sizeof(s32)); + } + } + + for (s32 i = 0; i < *npredictors; i++) { + s32 **table_entry = (*table)[i]; + for (s32 j = 0; j < *order; j++) { + for (s32 k = 0; k < 8; k++) { + s16 ts; + checked_fread(&ts, sizeof(s16), 1, fhandle); + BSWAP16(ts); + table_entry[k][j] = ts; + } + } + + for (s32 k = 1; k < 8; k++) { + table_entry[k][*order] = table_entry[k - 1][*order - 1]; + } + + table_entry[0][*order] = 1 << 11; + + for (s32 k = 1; k < 8; k++) { + s32 j = 0; + for (; j < k; j++) { + table_entry[j][k + *order] = 0; + } + + for (; j < 8; j++) { + table_entry[j][k + *order] = table_entry[j - k][*order]; + } + } + } + return 0; +} + +ALADPCMloop *readlooppoints(FILE *ifile, s16 *nloops) +{ + checked_fread(nloops, sizeof(s16), 1, ifile); + BSWAP16(*nloops); + ALADPCMloop *al = malloc(*nloops * sizeof(ALADPCMloop)); + for (s32 i = 0; i < *nloops; i++) { + checked_fread(&al[i], sizeof(ALADPCMloop), 1, ifile); + BSWAP32(al[i].start); + BSWAP32(al[i].end); + BSWAP32(al[i].count); + BSWAP16_MANY(al[i].state, 16); + } + return al; +} + +s32 inner_product(s32 length, s32 *v1, s32 *v2) +{ + s32 out = 0; + for (s32 i = 0; i < length; i++) { + out += v1[i] * v2[i]; + } + + // Compute "out / 2^11", rounded down. + s32 dout = out / (1 << 11); + s32 fiout = dout * (1 << 11); + return dout - (out - fiout < 0); +} + +void my_decodeframe(u8 *frame, s32 *state, s32 order, s32 ***coefTable) +{ + s32 ix[16]; + + u8 header = frame[0]; + s32 scale = 1 << (header >> 4); + s32 optimalp = header & 0xf; + + for (s32 i = 0; i < 16; i += 2) { + u8 c = frame[1 + i/2]; + ix[i] = c >> 4; + ix[i + 1] = c & 0xf; + } + + for (s32 i = 0; i < 16; i++) { + if (ix[i] >= 8) ix[i] -= 16; + ix[i] *= scale; + } + + for (s32 j = 0; j < 2; j++) { + s32 in_vec[16]; + if (j == 0) { + for (s32 i = 0; i < order; i++) { + in_vec[i] = state[16 - order + i]; + } + } else { + for (s32 i = 0; i < order; i++) { + in_vec[i] = state[8 - order + i]; + } + } + + for (s32 i = 0; i < 8; i++) { + s32 ind = j * 8 + i; + in_vec[order + i] = ix[ind]; + state[ind] = inner_product(order + i, coefTable[optimalp][i], in_vec) + ix[ind]; + } + } +} + +void my_encodeframe(u8 *out, s16 *inBuffer, s32 *state, s32 ***coefTable, s32 order, s32 npredictors) +{ + s16 ix[16]; + s32 prediction[16]; + s32 inVector[16]; + s32 saveState[16]; + s32 optimalp = 0; + s32 scale; + s32 ie[16]; + s32 e[16]; + f32 min = 1e30; + + for (s32 k = 0; k < npredictors; k++) { + for (s32 j = 0; j < 2; j++) { + for (s32 i = 0; i < order; i++) { + inVector[i] = (j == 0 ? state[16 - order + i] : inBuffer[8 - order + i]); + } + + for (s32 i = 0; i < 8; i++) { + prediction[j * 8 + i] = inner_product(order + i, coefTable[k][i], inVector); + e[j * 8 + i] = inVector[i + order] = inBuffer[j * 8 + i] - prediction[j * 8 + i]; + } + } + + f32 se = 0.0f; + for (s32 j = 0; j < 16; j++) { + se += (f32) e[j] * (f32) e[j]; + } + + if (se < min) { + min = se; + optimalp = k; + } + } + + for (s32 j = 0; j < 2; j++) { + for (s32 i = 0; i < order; i++) { + inVector[i] = (j == 0 ? state[16 - order + i] : inBuffer[8 - order + i]); + } + + for (s32 i = 0; i < 8; i++) { + prediction[j * 8 + i] = inner_product(order + i, coefTable[optimalp][i], inVector); + e[j * 8 + i] = inVector[i + order] = inBuffer[j * 8 + i] - prediction[j * 8 + i]; + } + } + + for (s32 i = 0; i < 16; i++) { + ie[i] = clamp_to_s16(e[i]); + } + + s32 max = 0; + for (s32 i = 0; i < 16; i++) { + if (abs(ie[i]) > abs(max)) { + max = ie[i]; + } + } + + for (scale = 0; scale <= 12; scale++) { + if (max <= 7 && max >= -8) break; + max /= 2; + } + + for (s32 i = 0; i < 16; i++) { + saveState[i] = state[i]; + } + + for (s32 nIter = 0, again = 1; nIter < 2 && again; nIter++) { + again = 0; + if (nIter == 1) scale++; + if (scale > 12) { + scale = 12; + } + + for (s32 j = 0; j < 2; j++) { + s32 base = j * 8; + for (s32 i = 0; i < order; i++) { + inVector[i] = (j == 0 ? + saveState[16 - order + i] : state[8 - order + i]); + } + + for (s32 i = 0; i < 8; i++) { + prediction[base + i] = inner_product(order + i, coefTable[optimalp][i], inVector); + s32 se = inBuffer[base + i] - prediction[base + i]; + ix[base + i] = qsample(se, scale); + s32 cV = clamp_to_s16(ix[base + i]) - ix[base + i]; + if (cV > 1 || cV < -1) again = 1; + ix[base + i] += cV; + inVector[i + order] = ix[base + i] * (1 << scale); + state[base + i] = prediction[base + i] + inVector[i + order]; + } + } + } + + u8 header = (scale << 4) | (optimalp & 0xf); + out[0] = header; + for (s32 i = 0; i < 16; i += 2) { + u8 c = ((ix[i] & 0xf) << 4) | (ix[i + 1] & 0xf); + out[1 + i/2] = c; + } +} + +void permute(s16 *out, s32 *in, s32 scale) +{ + for (s32 i = 0; i < 16; i++) { + out[i] = clamp_to_s16(in[i] - scale / 2 + myrand() % (scale + 1)); + } +} + +void write_header(FILE *ofile, const char *id, s32 size) +{ + fwrite(id, 4, 1, ofile); + BSWAP32(size); + fwrite(&size, sizeof(s32), 1, ofile); +} + +int main(int argc, char **argv) +{ + s16 order = -1; + s16 nloops = 0; + ALADPCMloop *aloops = NULL; + s16 npredictors = -1; + s32 ***coefTable = NULL; + s32 state[16]; + s32 soundPointer = -1; + s32 currPos = 0; + s32 nSamples = 0; + Chunk FormChunk; + ChunkHeader Header; + CommonChunk CommChunk; + InstrumentChunk InstChunk; + SoundDataChunk SndDChunk; + FILE *ifile; + FILE *ofile; + progname = argv[0]; + + if (argc < 3) { + fprintf(stderr, "%s %s\n", progname, usage); + exit(1); + } + + infilename = argv[1]; + + if ((ifile = fopen(infilename, "rb")) == NULL) { + fail_parse("AIFF-C file could not be opened"); + exit(1); + } + + if ((ofile = fopen(argv[2], "wb")) == NULL) { + fprintf(stderr, "%s: output file could not be opened [%s]\n", progname, argv[2]); + exit(1); + } + + memset(&InstChunk, 0, sizeof(InstChunk)); + + checked_fread(&FormChunk, sizeof(FormChunk), 1, ifile); + BSWAP32(FormChunk.ckID); + BSWAP32(FormChunk.formType); + if ((FormChunk.ckID != 0x464f524d) || (FormChunk.formType != 0x41494643)) { // FORM, AIFC + fail_parse("not an AIFF-C file"); + } + + for (;;) { + s32 num = fread(&Header, sizeof(Header), 1, ifile); + u32 ts; + if (num <= 0) break; + BSWAP32(Header.ckID); + BSWAP32(Header.ckSize); + + Header.ckSize++; + Header.ckSize &= ~1; + s32 offset = ftell(ifile); + + switch (Header.ckID) { + case 0x434f4d4d: // COMM + checked_fread(&CommChunk, sizeof(CommChunk), 1, ifile); + BSWAP16(CommChunk.numChannels); + BSWAP16(CommChunk.numFramesH); + BSWAP16(CommChunk.numFramesL); + BSWAP16(CommChunk.sampleSize); + BSWAP16(CommChunk.compressionTypeH); + BSWAP16(CommChunk.compressionTypeL); + s32 cType = (CommChunk.compressionTypeH << 16) + CommChunk.compressionTypeL; + if (cType != 0x56415043) { // VAPC + fail_parse("file is of the wrong compression type"); + } + if (CommChunk.numChannels != 1) { + fail_parse("file contains %d channels, only 1 channel supported", CommChunk.numChannels); + } + if (CommChunk.sampleSize != 16) { + fail_parse("file contains %d bit samples, only 16 bit samples supported", CommChunk.sampleSize); + } + + nSamples = (CommChunk.numFramesH << 16) + CommChunk.numFramesL; + + // Allow broken input lengths + if (nSamples % 16) { + nSamples--; + } + + if (nSamples % 16 != 0) { + fail_parse("number of chunks must be a multiple of 16, found %d", nSamples); + } + break; + + case 0x53534e44: // SSND + checked_fread(&SndDChunk, sizeof(SndDChunk), 1, ifile); + BSWAP32(SndDChunk.offset); + BSWAP32(SndDChunk.blockSize); + assert(SndDChunk.offset == 0); + assert(SndDChunk.blockSize == 0); + soundPointer = ftell(ifile); + break; + + case 0x4150504c: // APPL + checked_fread(&ts, sizeof(u32), 1, ifile); + BSWAP32(ts); + if (ts == 0x73746f63) { // stoc + u8 len; + checked_fread(&len, 1, 1, ifile); + if (len == 11) { + char ChunkName[12]; + s16 version; + checked_fread(ChunkName, 11, 1, ifile); + ChunkName[11] = '\0'; + if (strcmp("VADPCMCODES", ChunkName) == 0) { + checked_fread(&version, sizeof(s16), 1, ifile); + BSWAP16(version); + if (version != 1) { + fail_parse("Unknown codebook chunk version"); + } + readaifccodebook(ifile, &coefTable, &order, &npredictors); + } + else if (strcmp("VADPCMLOOPS", ChunkName) == 0) { + checked_fread(&version, sizeof(s16), 1, ifile); + BSWAP16(version); + if (version != 1) { + fail_parse("Unknown loop chunk version"); + } + aloops = readlooppoints(ifile, &nloops); + if (nloops != 1) { + fail_parse("Only a single loop supported"); + } + } + } + } + break; + } + + fseek(ifile, offset + Header.ckSize, SEEK_SET); + } + + if (coefTable == NULL) { + fail_parse("Codebook missing from bitstream"); + } + + for (s32 i = 0; i < order; i++) { + state[15 - i] = 0; + } + + u32 outputBytes = nSamples * sizeof(s16); + u8 *outputBuf = malloc(outputBytes); + + fseek(ifile, soundPointer, SEEK_SET); + while (currPos < nSamples) { + u8 input[9]; + u8 encoded[9]; + s32 lastState[16]; + s32 decoded[16]; + s16 guess[16]; + s16 origGuess[16]; + + memcpy(lastState, state, sizeof(lastState)); + checked_fread(input, 9, 1, ifile); + + // Decode for real + my_decodeframe(input, state, order, coefTable); + memcpy(decoded, state, sizeof(lastState)); + + // Create a guess from that, by clamping to 16 bits + for (s32 i = 0; i < 16; i++) { + origGuess[i] = clamp_to_s16(state[i]); + } + + // Encode the guess + memcpy(state, lastState, sizeof(lastState)); + memcpy(guess, origGuess, sizeof(guess)); + my_encodeframe(encoded, guess, state, coefTable, order, npredictors); + + // If it doesn't match, randomly round numbers until it does. + if (memcmp(input, encoded, 9) != 0) { + s32 scale = 1 << (input[0] >> 4); + do { + permute(guess, decoded, scale); + memcpy(state, lastState, sizeof(lastState)); + my_encodeframe(encoded, guess, state, coefTable, order, npredictors); + } while (memcmp(input, encoded, 9) != 0); + + // Bring the matching closer to the original decode (not strictly + // necessary, but it will move us closer to the target on average). + for (s32 failures = 0; failures < 50; failures++) { + s32 ind = myrand() % 16; + s32 old = guess[ind]; + if (old == origGuess[ind]) continue; + guess[ind] = origGuess[ind]; + if (myrand() % 2) guess[ind] += (old - origGuess[ind]) / 2; + memcpy(state, lastState, sizeof(lastState)); + my_encodeframe(encoded, guess, state, coefTable, order, npredictors); + if (memcmp(input, encoded, 9) == 0) { + failures = -1; + } + else { + guess[ind] = old; + } + } + } + + memcpy(state, decoded, sizeof(lastState)); + BSWAP16_MANY(guess, 16); + memcpy(outputBuf + currPos * 2, guess, sizeof(guess)); + currPos += 16; + } + + // Write an incomplete file header. We'll fill in the size later. + fwrite("FORM\0\0\0\0AIFF", 12, 1, ofile); + + // Subtract 4 from the COMM size to skip the compression field. + write_header(ofile, "COMM", sizeof(CommonChunk) - 4); + CommChunk.numFramesH = nSamples >> 16; + CommChunk.numFramesL = nSamples & 0xffff; + BSWAP16(CommChunk.numChannels); + BSWAP16(CommChunk.numFramesH); + BSWAP16(CommChunk.numFramesL); + BSWAP16(CommChunk.sampleSize); + fwrite(&CommChunk, sizeof(CommonChunk) - 4, 1, ofile); + + if (nloops > 0) { + s32 startPos = aloops[0].start, endPos = aloops[0].end; + const char *markerNames[2] = {"start", "end"}; + Marker markers[2] = { + {1, startPos >> 16, startPos & 0xffff}, + {2, endPos >> 16, endPos & 0xffff} + }; + write_header(ofile, "MARK", 2 + 2 * sizeof(Marker) + 1 + 5 + 1 + 3); + s16 numMarkers = bswap16(2); + fwrite(&numMarkers, sizeof(s16), 1, ofile); + for (s32 i = 0; i < 2; i++) { + u8 len = (u8) strlen(markerNames[i]); + BSWAP16(markers[i].MarkerID); + BSWAP16(markers[i].positionH); + BSWAP16(markers[i].positionL); + fwrite(&markers[i], sizeof(Marker), 1, ofile); + fwrite(&len, 1, 1, ofile); + fwrite(markerNames[i], len, 1, ofile); + } + + write_header(ofile, "INST", sizeof(InstrumentChunk)); + InstChunk.sustainLoop.playMode = bswap16(1); + InstChunk.sustainLoop.beginLoop = bswap16(1); + InstChunk.sustainLoop.endLoop = bswap16(2); + InstChunk.releaseLoop.playMode = 0; + InstChunk.releaseLoop.beginLoop = 0; + InstChunk.releaseLoop.endLoop = 0; + fwrite(&InstChunk, sizeof(InstrumentChunk), 1, ofile); + } + + // Save the coefficient table for use when encoding. Ideally this wouldn't + // be needed and "tabledesign -s 1" would generate the right table, but in + // practice it's difficult to adjust samples to make that happen. + write_header(ofile, "APPL", 4 + 12 + sizeof(CodeChunk) + npredictors * order * 8 * 2); + fwrite("stoc", 4, 1, ofile); + CodeChunk cChunk; + cChunk.version = bswap16(1); + cChunk.order = bswap16(order); + cChunk.nEntries = bswap16(npredictors); + fwrite("\x0bVADPCMCODES", 12, 1, ofile); + fwrite(&cChunk, sizeof(CodeChunk), 1, ofile); + for (s32 i = 0; i < npredictors; i++) { + for (s32 j = 0; j < order; j++) { + for (s32 k = 0; k < 8; k++) { + s16 ts = bswap16(coefTable[i][k][j]); + fwrite(&ts, sizeof(s16), 1, ofile); + } + } + } + + write_header(ofile, "SSND", outputBytes + 8); + SndDChunk.offset = 0; + SndDChunk.blockSize = 0; + fwrite(&SndDChunk, sizeof(SoundDataChunk), 1, ofile); + fwrite(outputBuf, outputBytes, 1, ofile); + + // Fix the size in the header + s32 fileSize = bswap32(ftell(ofile) - 8); + fseek(ofile, 4, SEEK_SET); + fwrite(&fileSize, 4, 1, ofile); + + fclose(ifile); + fclose(ofile); + return 0; +} diff --git a/tools/disassemble_sound.py b/tools/disassemble_sound.py new file mode 100644 index 00000000..bf82e746 --- /dev/null +++ b/tools/disassemble_sound.py @@ -0,0 +1,867 @@ +#!/usr/bin/env python3 +from collections import namedtuple, defaultdict +import tempfile +import subprocess +import uuid +import json +import os +import re +import struct +import sys + +TYPE_CTL = 1 +TYPE_TBL = 2 + + +class AifcEntry: + def __init__(self, data, book, loop): + self.name = None + self.data = data + self.book = book + self.loop = loop + self.tunings = [] + + +class SampleBank: + def __init__(self, name, data, offset): + self.offset = offset + self.name = name + self.data = data + self.entries = {} + + def add_sample(self, offset, sample_size, book, loop): + assert sample_size % 2 == 0 + if sample_size % 9 != 0: + # print(sample_size) + assert sample_size % 9 == 1 + sample_size -= 1 + + if offset in self.entries: + entry = self.entries[offset] + assert entry.book == book + assert entry.loop == loop + # print(len(entry.data), sample_size) + assert len(entry.data) == sample_size + else: + entry = AifcEntry(self.data[offset : offset + sample_size], book, loop) + self.entries[offset] = entry + + return entry + + +Sound = namedtuple("Sound", ["sample_addr", "tuning"]) +Drum = namedtuple("Drum", ["name", "addr", "release_rate", "pan", "envelope", "sound"]) +Inst = namedtuple( + "Inst", + [ + "name", + "addr", + "release_rate", + "normal_range_lo", + "normal_range_hi", + "envelope", + "sound_lo", + "sound_med", + "sound_hi", + ], +) +Book = namedtuple("Book", ["order", "npredictors", "table"]) +Loop = namedtuple("Loop", ["start", "end", "count", "state"]) +Envelope = namedtuple("Envelope", ["name", "entries"]) +Bank = namedtuple( + "Bank", + [ + "name", + "iso_date", + "sample_bank", + "insts", + "drums", + "all_insts", + "inst_list", + "envelopes", + "samples", + ], +) + + +def align(val, al): + return (val + (al - 1)) & -al + + +name_tbl = {} + + +def gen_name(prefix, name_table=[]): + if prefix not in name_tbl: + name_tbl[prefix] = 0 + ind = name_tbl[prefix] + name_tbl[prefix] += 1 + if ind < len(name_table): + return name_table[ind] + return prefix + str(ind) + + +def parse_bcd(data): + ret = 0 + for c in data: + ret *= 10 + ret += c >> 4 + ret *= 10 + ret += c & 15 + return ret + + +def serialize_f80(num): + num = float(num) + (f64,) = struct.unpack(">Q", struct.pack(">d", num)) + f64_sign_bit = f64 & 2 ** 63 + if num == 0.0: + if f64_sign_bit: + return b"\x80" + b"\0" * 9 + else: + return b"\0" * 10 + exponent = (f64 ^ f64_sign_bit) >> 52 + assert exponent != 0, "can't handle denormals" + assert exponent != 0x7FF, "can't handle infinity/nan" + exponent -= 1023 + f64_mantissa_bits = f64 & (2 ** 52 - 1) + f80_sign_bit = f64_sign_bit << (80 - 64) + f80_exponent = (exponent + 0x3FFF) << 64 + f80_mantissa_bits = 2 ** 63 | (f64_mantissa_bits << (63 - 52)) + f80 = f80_sign_bit | f80_exponent | f80_mantissa_bits + return struct.pack(">HQ", f80 >> 64, f80 & (2 ** 64 - 1)) + + +def round_f32(num): + enc = struct.pack(">f", num) + for decimals in range(5, 20): + num2 = round(num, decimals) + if struct.pack(">f", num2) == enc: + return num2 + return num + + +def parse_sound(data): + sample_addr, tuning = struct.unpack(">If", data) + if sample_addr == 0: + assert tuning == 0 + return None + return Sound(sample_addr, tuning) + + +def parse_drum(data, addr): + name = gen_name("drum") + release_rate, pan, loaded, pad = struct.unpack(">BBBB", data[:4]) + assert loaded == 0 + assert pad == 0 + sound = parse_sound(data[4:12]) + (env_addr,) = struct.unpack(">I", data[12:]) + assert env_addr != 0 + return Drum(name, addr, release_rate, pan, env_addr, sound) + + +def parse_inst(data, addr): + name = gen_name("inst") + loaded, normal_range_lo, normal_range_hi, release_rate, env_addr = struct.unpack( + ">BBBBI", data[:8] + ) + assert env_addr != 0 + sound_lo = parse_sound(data[8:16]) + sound_med = parse_sound(data[16:24]) + sound_hi = parse_sound(data[24:]) + if sound_lo is None: + assert normal_range_lo == 0 + if sound_hi is None: + assert normal_range_hi == 127 + return Inst( + name, + addr, + release_rate, + normal_range_lo, + normal_range_hi, + env_addr, + sound_lo, + sound_med, + sound_hi, + ) + + +def parse_loop(addr, bank_data): + start, end, count, pad = struct.unpack(">IIiI", bank_data[addr : addr + 16]) + assert pad == 0 + if count != 0: + state = struct.unpack(">16h", bank_data[addr + 16 : addr + 48]) + else: + state = None + return Loop(start, end, count, state) + + +def parse_book(addr, bank_data): + order, npredictors = struct.unpack(">ii", bank_data[addr : addr + 8]) + + assert order == 2 + # assert npredictors == 2 + # if (npredictors != 2): + # print(addr, order, npredictors) + table_data = bank_data[addr + 8 : addr + 8 + 16 * order * npredictors] + table = [] + for i in range(0, 16 * order * npredictors, 2): + table.append(struct.unpack(">h", table_data[i : i + 2])[0]) + return Book(order, npredictors, table) + + +def parse_sample(data, bank_data, sample_bank, is_shindou): + if is_shindou: + sample_size, addr, loop, book = struct.unpack(">IIII", data) + sample_size &= 0xFFFFFF + else: + zero, addr, loop, book, sample_size = struct.unpack(">IIIII", data) + assert zero == 0 + assert loop != 0 + print(sample_size, addr, loop, book) + assert book != 0 + loop = parse_loop(loop, bank_data) + book = parse_book(book, bank_data) + + return sample_bank.add_sample(addr, sample_size, book, loop) + + +def parse_envelope(addr, data_bank): + entries = [] + while True: + delay, arg = struct.unpack(">HH", data_bank[addr : addr + 4]) + entries.append((delay, arg)) + addr += 4 + if 1 <= (-delay) % 2 ** 16 <= 3: + break + return entries + + +def parse_ctl_header(header): + num_instruments, num_drums, shared = struct.unpack(">III", header[:12]) + date = parse_bcd(header[12:]) + y = date // 10000 + m = date // 100 % 100 + d = date % 100 + iso_date = "{:02}-{:02}-{:02}".format(y, m, d) + assert shared in [0, 1] + return num_instruments, num_drums, iso_date + + +def parse_ctl(parsed_header, data, sample_bank, index, is_shindou): + name_tbl.clear() + name = "{:02X}".format(index) + num_instruments, num_drums, iso_date = parsed_header + print("{}: {}, {} + {}".format(name, iso_date, num_instruments, num_drums)) + # print(len(data)) + + (drum_base_addr,) = struct.unpack(">I", data[:4]) + drum_addrs = [] + if num_drums != 0: + assert drum_base_addr != 0 + for i in range(num_drums): + (drum_addr,) = struct.unpack( + ">I", data[drum_base_addr + i * 4 : drum_base_addr + i * 4 + 4] + ) + # assert drum_addr != 0 + # print(i, drum_addr) + if(drum_addr == 0): + continue + drum_addrs.append(drum_addr) + else: + assert drum_base_addr == 0 + + inst_base_addr = 4 + inst_addrs = [] + inst_list = [] + for i in range(num_instruments): + (inst_addr,) = struct.unpack( + ">I", data[inst_base_addr + i * 4 : inst_base_addr + i * 4 + 4] + ) + if inst_addr == 0: + inst_list.append(None) + else: + inst_list.append(inst_addr) + inst_addrs.append(inst_addr) + + inst_addrs.sort() + assert drum_addrs == sorted(drum_addrs) + if drum_addrs and inst_addrs: + assert max(inst_addrs) < min(drum_addrs) + + # print(inst_addrs) + + if len(set(inst_addrs)) != len(inst_addrs): + print(index) + # assert len(set(inst_addrs)) == len(inst_addrs) + # assert len(set(drum_addrs)) == len(drum_addrs) + + insts = [] + for inst_addr in inst_addrs: + insts.append(parse_inst(data[inst_addr : inst_addr + 32], inst_addr)) + + drums = [] + for drum_addr in drum_addrs: + drums.append(parse_drum(data[drum_addr : drum_addr + 16], drum_addr)) + + env_addrs = set() + sample_addrs = set() + tunings = defaultdict(lambda: []) + for inst in insts: + for sound in [inst.sound_lo, inst.sound_med, inst.sound_hi]: + if sound is not None: + sample_addrs.add(sound.sample_addr) + tunings[sound.sample_addr].append(sound.tuning) + env_addrs.add(inst.envelope) + for drum in drums: + sample_addrs.add(drum.sound.sample_addr) + tunings[drum.sound.sample_addr].append(drum.sound.tuning) + env_addrs.add(drum.envelope) + + # Put drums somewhere in the middle of the instruments to make sample + # addresses come in increasing order. (This logic isn't totally right, + # but it works for our purposes.) + all_insts = [] + need_drums = len(drums) > 0 + for inst in insts: + if need_drums and any( + s.sample_addr > drums[0].sound.sample_addr + for s in [inst.sound_lo, inst.sound_med, inst.sound_hi] + if s is not None + ): + all_insts.append(drums) + need_drums = False + all_insts.append(inst) + + if need_drums: + all_insts.append(drums) + + samples = {} + + for addr in sorted(sample_addrs): + sample_size = 16 if is_shindou else 20 + sample_data = data[addr : addr + sample_size] + print("\t\t", addr) + samples[addr] = parse_sample(sample_data, data, sample_bank, is_shindou) + samples[addr].tunings.extend(tunings[addr]) + + env_data = {} + used_env_addrs = set() + for addr in sorted(env_addrs): + env = parse_envelope(addr, data) + env_data[addr] = env + for i in range(align(len(env), 4)): + used_env_addrs.add(addr + i * 4) + + # Unused envelopes + unused_envs = set() + if used_env_addrs: + for addr in range(min(used_env_addrs) + 4, max(used_env_addrs), 4): + if addr not in used_env_addrs: + unused_envs.add(addr) + (stub_marker,) = struct.unpack(">I", data[addr : addr + 4]) + assert stub_marker == 0 + env = parse_envelope(addr, data) + env_data[addr] = env + for i in range(align(len(env), 4)): + used_env_addrs.add(addr + i * 4) + + envelopes = {} + for addr in sorted(env_data.keys()): + env_name = gen_name("envelope") + if addr in unused_envs: + env_name += "_unused" + envelopes[addr] = Envelope(env_name, env_data[addr]) + + return Bank( + name, + iso_date, + sample_bank, + insts, + drums, + all_insts, + inst_list, + envelopes, + samples, + ) + + +def parse_seqfile(data, filetype): + magic, num_entries = struct.unpack(">HH", data[:4]) + assert magic == filetype + prev = align(4 + num_entries * 8, 16) + entries = [] + for i in range(num_entries): + offset, length = struct.unpack(">II", data[4 + i * 8 : 4 + i * 8 + 8]) + if filetype == TYPE_CTL: + assert offset == prev + else: + assert offset <= prev + prev = max(prev, offset + length) + entries.append((offset, length)) + assert all(x == 0 for x in data[prev:]) + return entries + + +def parse_sh_header(data, filetype): + (num_entries,) = struct.unpack(">H", data[:2]) + assert data[2:16] == b"\0" * 14 + prev = 0 + entries = [] + for i in range(num_entries): + subdata = data[16 + 16 * i : 32 + 16 * i] + offset, length, magic = struct.unpack(">IIH", subdata[:10]) + assert offset == prev + # assert magic == (0x0204 if filetype == TYPE_TBL else 0x0203) + prev = offset + length + if filetype == TYPE_CTL: + assert subdata[14:16] == b"\0" * 2 + sample_bank_index, magic2, num_instruments, num_drums = struct.unpack( + ">BBBB", subdata[10:14] + ) + assert magic2 == 0xFF + # num_drums >>= 4 + entries.append( + (offset, length, (sample_bank_index, num_instruments, num_drums)) + ) + else: + assert subdata[10:16] == b"\0" * 6 + entries.append((offset, length)) + return entries + + +def parse_tbl(data, entries): + seen = {} + tbls = [] + sample_banks = [] + sample_bank_map = {} + for (offset, length) in entries: + if offset not in seen: + name = gen_name("sample_bank") + seen[offset] = name + sample_bank = SampleBank(name, data[offset : offset + length], offset) + sample_banks.append(sample_bank) + sample_bank_map[name] = sample_bank + tbls.append(seen[offset]) + return tbls, sample_banks, sample_bank_map + + +class AifcWriter: + def __init__(self, out): + self.out = out + self.sections = [] + self.total_size = 0 + + def add_section(self, tp, data): + assert isinstance(tp, bytes) + assert isinstance(data, bytes) + self.sections.append((tp, data)) + self.total_size += align(len(data), 2) + 8 + + def add_custom_section(self, tp, data): + self.add_section(b"APPL", b"stoc" + self.pstring(tp) + data) + + def pstring(self, data): + return bytes([len(data)]) + data + (b"" if len(data) % 2 else b"\0") + + def finish(self): + # total_size isn't used, and is regularly wrong. In particular, vadpcm_enc + # preserves the size of the input file... + self.total_size += 4 + self.out.write(b"FORM" + struct.pack(">I", self.total_size) + b"AIFC") + for (tp, data) in self.sections: + self.out.write(tp + struct.pack(">I", len(data))) + self.out.write(data) + if len(data) % 2: + self.out.write(b"\0") + + +def write_aifc(entry, out): + writer = AifcWriter(out) + num_channels = 1 + data = entry.data + assert len(data) % 9 == 0 + if len(data) % 2 == 1: + data += b"\0" + # (Computing num_frames this way makes it off by one when the data length + # is odd. It matches vadpcm_enc, though.) + num_frames = len(data) * 16 // 9 + sample_size = 16 # bits per sample + + if len(set(entry.tunings)) == 1: + sample_rate = 32000 * entry.tunings[0] + else: + # Some drum sounds in sample bank B don't have unique sample rates, so + # we have to guess. This doesn't matter for matching, it's just to make + # the sounds easy to listen to. + if min(entry.tunings) <= 0.5 <= max(entry.tunings): + sample_rate = 16000 + elif min(entry.tunings) <= 1.0 <= max(entry.tunings): + sample_rate = 32000 + elif min(entry.tunings) <= 1.5 <= max(entry.tunings): + sample_rate = 48000 + elif min(entry.tunings) <= 2.5 <= max(entry.tunings): + sample_rate = 80000 + else: + sample_rate = 16000 * (min(entry.tunings) + max(entry.tunings)) + + writer.add_section( + b"COMM", + struct.pack(">hIh", num_channels, num_frames, sample_size) + + serialize_f80(sample_rate) + + b"VAPC" + + writer.pstring(b"VADPCM ~4-1"), + ) + writer.add_section(b"INST", b"\0" * 20) + table_data = b"".join(struct.pack(">h", x) for x in entry.book.table) + writer.add_custom_section( + b"VADPCMCODES", + struct.pack(">hhh", 1, entry.book.order, entry.book.npredictors) + table_data, + ) + writer.add_section(b"SSND", struct.pack(">II", 0, 0) + data) + if entry.loop.count != 0: + writer.add_custom_section( + b"VADPCMLOOPS", + struct.pack( + ">HHIIi16h", + 1, + 1, + entry.loop.start, + entry.loop.end, + entry.loop.count, + *entry.loop.state + ), + ) + writer.finish() + + +def write_aiff(entry, filename): + temp = tempfile.NamedTemporaryFile(suffix=".aifc", delete=False) + try: + write_aifc(entry, temp) + temp.flush() + temp.close() + aifc_decode = os.path.join(os.path.dirname(__file__), "aifc_decode") + subprocess.run([aifc_decode, temp.name, filename], check=True) + finally: + temp.close() + os.remove(temp.name) + + +# Modified from https://stackoverflow.com/a/25935321/1359139, cc by-sa 3.0 +class NoIndent(object): + def __init__(self, value): + self.value = value + + +class NoIndentEncoder(json.JSONEncoder): + def __init__(self, *args, **kwargs): + super(NoIndentEncoder, self).__init__(*args, **kwargs) + self._replacement_map = {} + + def default(self, o): + def ignore_noindent(o): + if isinstance(o, NoIndent): + return o.value + return self.default(o) + + if isinstance(o, NoIndent): + key = uuid.uuid4().hex + self._replacement_map[key] = json.dumps(o.value, default=ignore_noindent) + return "@@%s@@" % (key,) + else: + return super(NoIndentEncoder, self).default(o) + + def encode(self, o): + result = super(NoIndentEncoder, self).encode(o) + repl_map = self._replacement_map + + def repl(m): + key = m.group()[3:-3] + return repl_map[key] + + return re.sub(r"\"@@[0-9a-f]*?@@\"", repl, result) + + +def inst_ifdef_json(bank_index, inst_index): + if bank_index == 7 and inst_index >= 13: + return NoIndent(["VERSION_US", "VERSION_EU"]) + if bank_index == 8 and inst_index >= 16: + return NoIndent(["VERSION_US", "VERSION_EU"]) + if bank_index == 10 and inst_index >= 14: + return NoIndent(["VERSION_US", "VERSION_EU"]) + return None + + +def main(): + args = [] + need_help = False + only_samples = False + only_samples_list = [] + shindou_headers = None + if sys.argv[1].startswith("files"): + with open(sys.argv[2], "rb") as ctl_file, open(sys.argv[3], "rb") as tbl_file, \ + open(sys.argv[4], "rb") as header_file: + + ctl_data = ctl_file.read() + tbl_data = tbl_file.read() + if sys.argv[1].endswith("jp"): + header_start = 0xC1360 - 0x1050 + elif sys.argv[1].endswith("eu"): + header_start = 0xC4D20 - 0x1050 + else: + header_start = 0xC4210 - 0x1050 + header_file.seek(header_start) + hlen = int.from_bytes(header_file.read(2), "big") + header_file.seek(-2, 1) + tbl_header_data = header_file.read((1 + hlen) * 0x10) + hlen = int.from_bytes(header_file.read(2), "big") + header_file.seek(hlen * 0x10 + 0xE, 1) + hlen = int.from_bytes(header_file.read(2), "big") + header_file.seek(-2, 1) + ctl_header_data = header_file.read((1 + hlen) * 0x10) + + shindou_headers = True + samples_out_dir = sys.argv[5] + banks_out_dir = sys.argv[6] + else: + skip_next = 0 + for i, a in enumerate(sys.argv[1:], 1): + if skip_next > 0: + skip_next -= 1 + continue + if a == "--help" or a == "-h": + need_help = True + elif a == "--only-samples": + only_samples = True + elif a == "--shindou-headers": + shindou_headers = sys.argv[i + 1 : i + 5] + skip_next = 4 + elif a.startswith("-"): + print("Unrecognized option " + a) + sys.exit(1) + elif only_samples: + only_samples_list.append(a) + else: + args.append(a) + + expected_num_args = 5 + (0 if only_samples else 2) + if ( + need_help + or len(args) != expected_num_args + or (shindou_headers and len(shindou_headers) != 4) + ): + print( + "Usage: {}" + " <.z64 rom> " + " [--shindou-headers " + " ]" + " ( |" + " --only-samples file:index ...)".format(sys.argv[0]) + ) + sys.exit(0 if need_help else 1) + + rom_file = open(args[0], "rb") + + def read_at(offset, size): + rom_file.seek(int(offset)) + return rom_file.read(int(size)) + + ctl_data = read_at(args[1], args[2]) + tbl_data = read_at(args[3], args[4]) + + ctl_header_data = None + tbl_header_data = None + if shindou_headers: + ctl_header_data = read_at(shindou_headers[0], shindou_headers[1]) + tbl_header_data = read_at(shindou_headers[2], shindou_headers[3]) + + if not only_samples: + samples_out_dir = args[5] + banks_out_dir = args[6] + + banks = [] + + if shindou_headers: + ctl_entries = parse_sh_header(ctl_header_data, TYPE_CTL) + tbl_entries = parse_sh_header(tbl_header_data, TYPE_TBL) + + sample_banks = parse_tbl(tbl_data, tbl_entries)[1] + print(len(ctl_entries)) + for index, (offset, length, sh_meta) in enumerate(ctl_entries): + sample_bank = sample_banks[sh_meta[0]] + entry = ctl_data[offset : offset + length] + header = (sh_meta[1], sh_meta[2], "0000-00-00") + banks.append(parse_ctl(header, entry, sample_bank, index, True)) + else: + ctl_entries = parse_seqfile(ctl_data, TYPE_CTL) + tbl_entries = parse_seqfile(tbl_data, TYPE_TBL) + assert len(ctl_entries) == len(tbl_entries) + + tbls, sample_banks, sample_bank_map = parse_tbl(tbl_data, tbl_entries) + + for index, (offset, length), sample_bank_name in zip( + range(len(ctl_entries)), ctl_entries, tbls + ): + sample_bank = sample_bank_map[sample_bank_name] + entry = ctl_data[offset : offset + length] + header = parse_ctl_header(entry[:16]) + banks.append(parse_ctl(header, entry[16:], sample_bank, index, False)) + + # Special mode used for asset extraction: generate aifc files, with paths + # given by command line arguments + if only_samples: + index_to_filename = {} + created_dirs = set() + for arg in only_samples_list: + filename, index = arg.rsplit(":", 1) + index_to_filename[int(index)] = filename + index = -1 + for sample_bank in sample_banks: + offsets = sorted(set(sample_bank.entries.keys())) + for offset in offsets: + entry = sample_bank.entries[offset] + index += 1 + if index in index_to_filename: + filename = index_to_filename[index] + dir = os.path.dirname(filename) + if dir not in created_dirs: + os.makedirs(dir, exist_ok=True) + created_dirs.add(dir) + write_aiff(entry, filename) + return + + # Generate aiff files + + for sample_bank in sample_banks: + dir = os.path.join(samples_out_dir, sample_bank.name) + os.makedirs(dir, exist_ok=True) + + offsets = sorted(set(sample_bank.entries.keys())) + print(sample_bank.name, len(offsets), 'entries') + offsets.append(len(sample_bank.data)) + + assert 0 in offsets + + for offset, next_offset, index in zip( + offsets, offsets[1:], range(len(offsets)) + ): + entry = sample_bank.entries[offset] + entry.name = "{:02X}".format(index) + size = next_offset - offset + + assert size % 16 == 0 + if not (size - 15 <= len(entry.data) <= size): + print(index, offset, size, len(entry.data)) + # continue + # assert size - 15 <= len(entry.data) <= size + # if index % 10 == 0: + # print(index, offset, size, len(entry.data)) + if index == 299 or index == 554 or index == 912: + # print(index, offset, size, len(entry.data)) + continue + # garbage = sample_bank.data[offset + len(entry.data) : offset + size] + # if len(entry.data) % 2 == 1: + # assert garbage[0] == 0 + # if next_offset != offsets[-1]: + # # (The last chunk follows a more complex garbage pattern) + # assert all(x == 0 for x in garbage) + filename = os.path.join(dir, entry.name + ".aiff") + write_aiff(entry, filename) + + # Generate sound bank .json files + os.makedirs(banks_out_dir, exist_ok=True) + for bank_index, bank in enumerate(banks): + filename = os.path.join(banks_out_dir, bank.name + ".json") + with open(filename, "w") as out: + + def sound_to_json(sound): + entry = bank.samples[sound.sample_addr] + if len(set(entry.tunings)) == 1: + return entry.name + return {"sample": entry.name, "tuning": round_f32(sound.tuning)} + + bank_json = { + "date": bank.iso_date, + "sample_bank": bank.sample_bank.name, + "envelopes": {}, + "instruments": {}, + "instrument_list": [], + } + addr_to_name = {} + + # Envelopes + for env in bank.envelopes.values(): + env_json = [] + for (delay, arg) in env.entries: + if delay == 0: + ins = "stop" + assert arg == 0 + elif delay == 2 ** 16 - 1: + ins = "hang" + assert arg == 0 + elif delay == 2 ** 16 - 2: + ins = ["goto", arg] + elif delay == 2 ** 16 - 3: + ins = "restart" + assert arg == 0 + else: + ins = [delay, arg] + env_json.append(NoIndent(ins)) + bank_json["envelopes"][env.name] = env_json + + # Instruments/drums + for inst_index, inst in enumerate(bank.all_insts): + if isinstance(inst, Inst): + inst_json = { + "ifdef": inst_ifdef_json(bank_index, inst_index), + "release_rate": inst.release_rate, + "normal_range_lo": inst.normal_range_lo, + "normal_range_hi": inst.normal_range_hi, + "envelope": bank.envelopes[inst.envelope].name, + } + + if inst_json["ifdef"] is None: + del inst_json["ifdef"] + + if inst.sound_lo is not None: + inst_json["sound_lo"] = NoIndent(sound_to_json(inst.sound_lo)) + else: + del inst_json["normal_range_lo"] + + inst_json["sound"] = NoIndent(sound_to_json(inst.sound_med)) + + if inst.sound_hi is not None: + inst_json["sound_hi"] = NoIndent(sound_to_json(inst.sound_hi)) + else: + del inst_json["normal_range_hi"] + + bank_json["instruments"][inst.name] = inst_json + addr_to_name[inst.addr] = inst.name + + else: + assert isinstance(inst, list) + drums_list_json = [] + for drum in inst: + drum_json = { + "release_rate": drum.release_rate, + "pan": drum.pan, + "envelope": bank.envelopes[drum.envelope].name, + "sound": sound_to_json(drum.sound), + } + drums_list_json.append(NoIndent(drum_json)) + bank_json["instruments"]["percussion"] = drums_list_json + + # Instrument lists + for addr in bank.inst_list: + if addr is None: + bank_json["instrument_list"].append(None) + else: + bank_json["instrument_list"].append(addr_to_name[addr]) + + out.write(json.dumps(bank_json, indent=4, cls=NoIndentEncoder)) + out.write("\n") + + +if __name__ == "__main__": + main()