Carry sound font ids in 16 bits (#7260)

Each streamed track brings its own sound font, so a pack with more than
~217 of them pushes font ids past 255. Stored in 8 bits they wrapped onto
vanilla fonts or onto 0xFF, which reads as "no font": the track played
vanilla instruments or nothing at all.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
David Racine
2026-10-04 19:39:10 -04:00
committed by GitHub
parent 7459f6b905
commit 9308ba7940
8 changed files with 41 additions and 30 deletions
+2 -2
View File
@@ -1973,7 +1973,7 @@ s32 AudioLoad_SyncLoadInstrument(s32 fontId, s32 instId, s32 drumId);
void AudioLoad_AsyncLoadSeq(s32 seqId, s32 arg1, s32 retData, OSMesgQueue* retQueue);
void AudioLoad_AsyncLoadSampleBank(s32 sampleBankId, s32 arg1, s32 retData, OSMesgQueue* retQueue);
void AudioLoad_AsyncLoadFont(s32 fontId, s32 arg1, s32 retData, OSMesgQueue* retQueue);
u8* AudioLoad_GetFontsForSequence(s32 seqId, u32* arg1);
u16* AudioLoad_GetFontsForSequence(s32 seqId, u32* arg1);
void AudioLoad_DiscardSeqFonts(s32 seqId);
s32 AudioLoad_SyncInitSeqPlayer(s32 playerIdx, s32 seqId, s32 arg2);
s32 AudioLoad_SyncInitSeqPlayerSkipTicks(s32 playerIdx, s32 seqId, s32 arg2);
@@ -1995,7 +1995,7 @@ void Audio_QueueCmdS8(u32 arg0, s8 arg1);
void Audio_QueueCmdU16(u32 arg0, u16 arg1);
s32 Audio_ScheduleProcessCmds(void);
u32 func_800E5E20(u32* arg0);
u8* func_800E5E84(s32 arg0, u32* arg1);
u16* func_800E5E84(s32 arg0, u32* arg1);
s32 func_800E5EDC(void);
s32 func_800E5F88(s32 arg0);
void Audio_PreNMIInternal(void);
+6 -4
View File
@@ -9,6 +9,8 @@ extern "C" {
#define NO_LAYER ((SequenceLayer*)(-1))
#define FONT_ID_NONE 0xFFFF
#define TATUMS_PER_BEAT 48
#define IS_SEQUENCE_CHANNEL_VALID(ptr) ((uintptr_t)(ptr) != (uintptr_t)&gAudioContext.sequenceChannelNone)
@@ -268,7 +270,7 @@ typedef struct {
/* 0x002 */ u8 noteAllocPolicy;
/* 0x003 */ u8 muteBehavior;
/* 0x004 */ u16 seqId;
/* 0x005 */ u8 defaultFont;
/* 0x005 */ u16 defaultFont;
/* 0x006 */ u8 unk_06[1];
/* 0x007 */ s8 playerIdx;
/* 0x008 */ u16 tempo; // tatums per minute
@@ -376,7 +378,7 @@ typedef struct SequenceChannel {
/* 0x04 */ u8 reverb; // or dry/wet mix
/* 0x05 */ u8 notePriority; // 0-3
/* 0x06 */ u8 someOtherPriority;
/* 0x07 */ u8 fontId;
/* 0x07 */ u16 fontId;
/* 0x08 */ u8 reverbIndex;
/* 0x09 */ u8 bookOffset;
/* 0x0A */ u8 newPan;
@@ -507,7 +509,7 @@ typedef struct {
/* 0x00 */ u8 priority;
/* 0x01 */ u8 waveId;
/* 0x02 */ u8 sampleCountIndex;
/* 0x03 */ u8 fontId;
/* 0x03 */ u16 fontId;
/* 0x04 */ u8 unk_04;
/* 0x05 */ u8 stereoHeadsetEffects;
/* 0x06 */ s16 adsrVolScaleUnused;
@@ -1176,7 +1178,7 @@ typedef struct {
uint8_t medium;
uint8_t cachePolicy;
int32_t numFonts;
uint8_t fonts[16];
uint16_t fonts[16];
} SequenceData;
void Audio_SetGameVolume(int player_id, f32 volume);
+1 -1
View File
@@ -12,7 +12,7 @@ typedef struct {
uint8_t medium;
uint8_t cachePolicy;
uint32_t numFonts;
uint8_t fonts[16];
uint16_t fonts[16];
} Sequence;
class AudioSequence : public Ship::Resource<Sequence> {
+14 -6
View File
@@ -300,7 +300,7 @@ void AudioLoad_InitSampleDmaBuffers(s32 arg0) {
// SOH [Port] Completely reworked from decomp: SAF custom fontIds can exceed the native table; bounds-check against
// fontMapSize to avoid OOB reads.
s32 AudioLoad_IsFontLoadComplete(s32 fontId) {
if (fontId == 0xFF) {
if (fontId == FONT_ID_NONE) {
return true;
}
// Resolve indirection (identity for FONT_TABLE today, but kept for parity with other tables).
@@ -337,7 +337,7 @@ s32 AudioLoad_IsSampleLoadComplete(s32 sampleBankId) {
}
void AudioLoad_SetFontLoadStatus(s32 fontId, s32 status) {
if ((fontId != 0xFF) && ((size_t)fontId < fontMapSize) && (gAudioContext.fontLoadStatus[fontId] != 5)) {
if ((fontId != FONT_ID_NONE) && ((size_t)fontId < fontMapSize) && (gAudioContext.fontLoadStatus[fontId] != 5)) {
gAudioContext.fontLoadStatus[fontId] = status;
}
}
@@ -395,7 +395,7 @@ SoundFontData* AudioLoad_SyncLoadSeqFonts(s32 seqId, u32* outDefaultFontId) {
return NULL;
}
fontId = 0xFF;
fontId = FONT_ID_NONE;
index = ((u16*)gAudioContext.sequenceFontTable)[seqId];
numFonts = gAudioContext.sequenceFontTable[index++];
@@ -490,7 +490,7 @@ void AudioLoad_AsyncLoadFont(s32 fontId, s32 arg1, s32 retData, OSMesgQueue* ret
AudioLoad_AsyncLoad(FONT_TABLE, fontId, 0, retData, retQueue);
}
u8* AudioLoad_GetFontsForSequence(s32 seqId, u32* outNumFonts) {
u16* AudioLoad_GetFontsForSequence(s32 seqId, u32* outNumFonts) {
s32 index;
// Check for NA_BGM_DISABLED and account for seqId that are stripped with `& 0xFF` by the caller
@@ -585,7 +585,7 @@ s32 AudioLoad_SyncInitSeqPlayerInternal(s32 playerIdx, s32 seqId, s32 arg2) {
AudioSeq_SequencePlayerDisable(seqPlayer);
fontId = 0xFF;
fontId = FONT_ID_NONE;
// seqId is the resolved 16-bit id from func_800F9280(). Reject ids with no loaded sequence; the
// map has sequenceMapSize + 0xF slots (custom ids skip the reserved 129-135 range).
@@ -1407,9 +1407,13 @@ void AudioLoad_Init(void* heap, size_t heapSize) {
SequenceData* sDat = ResourceMgr_LoadSeqPtrByName(customSeqList[j]);
if (sDat->numFonts == -1) {
uint8_t crcBytes[sizeof(uint64_t)];
uint64_t crc;
memcpy(&crc, sDat->fonts, sizeof(uint64_t));
for (size_t b = 0; b < sizeof(crcBytes); b++) {
crcBytes[b] = (uint8_t)sDat->fonts[b];
}
memcpy(&crc, crcBytes, sizeof(crc));
const char* res = ResourceGetNameByCrc(crc);
if (res == NULL) {
// Passing a null buffer and length of 0 to snprintf will return the required numbers of characters the
@@ -1426,6 +1430,10 @@ void AudioLoad_Init(void* heap, size_t heapSize) {
continue;
}
SoundFont* sf = ResourceMgr_LoadAudioSoundFontByName(res);
if (sf->fntIndex >= FONT_ID_NONE) {
LUSLOG_ERROR("Font limit (0xFFFF) exceeded; sequence \"%s\" skipped.", customSeqList[j]);
continue;
}
memset(&sDat->fonts[0], 0, sizeof(sDat->fonts));
sDat->fonts[0] = sf->fntIndex;
sDat->numFonts = 1;
+4 -4
View File
@@ -327,7 +327,7 @@ SoundFontSound* Audio_InstrumentGetSound(Instrument* instrument, s32 semitone) {
Instrument* Audio_GetInstrumentInner(s32 fontId, s32 instId) {
Instrument* inst;
if (fontId == 0xFF) {
if (fontId == FONT_ID_NONE) {
return NULL;
}
@@ -355,7 +355,7 @@ Instrument* Audio_GetInstrumentInner(s32 fontId, s32 instId) {
Drum* Audio_GetDrum(s32 fontId, s32 drumId) {
Drum* drum = NULL;
if (fontId == 0xFF) {
if (fontId == FONT_ID_NONE) {
return NULL;
}
@@ -379,7 +379,7 @@ Drum* Audio_GetDrum(s32 fontId, s32 drumId) {
SoundFontSound* Audio_GetSfx(s32 fontId, s32 sfxId) {
SoundFontSound* sfx = NULL;
if (fontId == 0xFF) {
if (fontId == FONT_ID_NONE) {
return NULL;
}
@@ -405,7 +405,7 @@ SoundFontSound* Audio_GetSfx(s32 fontId, s32 sfxId) {
}
s32 Audio_SetFontInstrument(s32 instrumentType, s32 fontId, s32 index, void* value) {
if (fontId == 0xFF) {
if (fontId == FONT_ID_NONE) {
return -1;
}
+11 -10
View File
@@ -1005,6 +1005,7 @@ void AudioSeq_SequenceChannelProcessScript(SequenceChannel* channel) {
u32 parameters[3];
s8 signedParam;
u8 command = AudioSeq_ScriptReadU8(scriptState);
u16 fontId;
u8 lowBits;
u8 highBits;
s32 result;
@@ -1060,20 +1061,20 @@ void AudioSeq_SequenceChannelProcessScript(SequenceChannel* channel) {
break;
case 0xEB:
result = (u8)parameters[0];
command = (u8)parameters[0];
fontId = (u8)parameters[0];
if (seqPlayer->defaultFont != 0xFF) {
if (seqPlayer->defaultFont != FONT_ID_NONE) {
if (gAudioContext.seqReplaced[seqPlayer->playerIdx]) {
seqPlayer->seqId = gAudioContext.seqToPlay[seqPlayer->playerIdx];
gAudioContext.seqReplaced[seqPlayer->playerIdx] = 0;
}
u16 seqId = AudioEditor_GetReplacementSeq(seqPlayer->seqId);
SequenceData sDat = ResourceMgr_LoadSeqByName(sequenceMap[seqId]);
command = sDat.fonts[sDat.numFonts - result - 1];
fontId = sDat.fonts[sDat.numFonts - result - 1];
}
if (AudioHeap_SearchCaches(FONT_TABLE, CACHE_EITHER, command)) {
channel->fontId = command;
if (AudioHeap_SearchCaches(FONT_TABLE, CACHE_EITHER, fontId)) {
channel->fontId = fontId;
}
parameters[0] = parameters[1];
@@ -1175,9 +1176,9 @@ void AudioSeq_SequenceChannelProcessScript(SequenceChannel* channel) {
break;
case 0xC6:
result = (u8)parameters[0];
command = (u8)parameters[0];
fontId = (u8)parameters[0];
if (seqPlayer->defaultFont != 0xFF) {
if (seqPlayer->defaultFont != FONT_ID_NONE) {
if (gAudioContext.seqReplaced[seqPlayer->playerIdx]) {
seqPlayer->seqId = gAudioContext.seqToPlay[seqPlayer->playerIdx];
gAudioContext.seqReplaced[seqPlayer->playerIdx] = 0;
@@ -1190,11 +1191,11 @@ void AudioSeq_SequenceChannelProcessScript(SequenceChannel* channel) {
if (result > sDat.numFonts)
break;
command = sDat.fonts[(sDat.numFonts - result - 1)];
fontId = sDat.fonts[(sDat.numFonts - result - 1)];
}
if (AudioHeap_SearchCaches(FONT_TABLE, CACHE_EITHER, command)) {
channel->fontId = command;
if (AudioHeap_SearchCaches(FONT_TABLE, CACHE_EITHER, fontId)) {
channel->fontId = fontId;
}
break;
+1 -1
View File
@@ -520,7 +520,7 @@ u32 func_800E5E20(u32* out) {
return sp1C >> 0x18;
}
u8* func_800E5E84(s32 arg0, u32* arg1) {
u16* func_800E5E84(s32 arg0, u32* arg1) {
return AudioLoad_GetFontsForSequence(arg0, arg1);
}
+2 -2
View File
@@ -5016,8 +5016,8 @@ void func_800F5C2C(void) {
void Audio_PlayFanfare(u16 seqId) {
u16 curSeqId;
u32 outNumFonts;
u8* curFontId;
u8* requestedFontId;
u16* curFontId;
u16* requestedFontId;
curSeqId = func_800FA0B4(SEQ_PLAYER_FANFARE);