Improve audio docs and partial audio disassembly support (#268)

* audiodiasm

* start of sound disasm

* disasm

* source
This commit is contained in:
petrie911 2024-07-07 12:43:41 -05:00 committed by GitHub
parent 9dd58cd924
commit 83c5442915
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 2039 additions and 441 deletions

1
.gitignore vendored
View File

@ -18,6 +18,7 @@ ctx.c.m2c
*.otr
*.eeprom
assets/yaml/us/ast_test.yaml
/audio_data
src/assets/*
include/assets/*
/build

View File

@ -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

View File

@ -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 ====

View File

@ -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)

View File

@ -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[];

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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];

View File

@ -146,31 +146,31 @@ void func_80013A18(Note* note) {
}
void func_80013A84(Note* note) {
NotePlaybackState* temp_v0_3 = &note->playbackState;
VibratoState* temp_v1 = &temp_v0_3->vibratoState;
NotePlaybackState* noteState = &note->playbackState;
VibratoState* vibrato = &noteState->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) {

View File

@ -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) {

View File

@ -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++) {

View File

@ -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;

View File

@ -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 = &note->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 = &note->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(&note->listItem.pool->decaying, &note->listItem);
if (arg1 == 6) {
func_80012C40(note);
func_80012C00(&note->listItem.pool->decaying, &note->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++;

View File

@ -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;
}

View File

@ -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

View File

@ -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) {

View File

@ -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;

BIN
tools/aifc_decode Normal file

Binary file not shown.

669
tools/aifc_decode.c Normal file
View File

@ -0,0 +1,669 @@
/**
* Bruteforcing decoder for converting ADPCM-encoded AIFC into AIFF, in a way
* that roundtrips with vadpcm_enc.
*/
#include <unistd.h>
#include <assert.h>
#include <math.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
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;
}

867
tools/disassemble_sound.py Normal file
View File

@ -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> <ctl offset> <ctl size> <tbl offset> <tbl size>"
" [--shindou-headers <ctl header offset> <ctl header size>"
" <tbl header offset> <tbl header size>]"
" (<samples outdir> <sound bank outdir> |"
" --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()