diff --git a/Makefile b/Makefile index e0026e2a5f..772f45fb9a 100644 --- a/Makefile +++ b/Makefile @@ -237,6 +237,7 @@ BUILD_DIR_REPLACE := sed -e 's|$$(BUILD_DIR)|$(BUILD_DIR)|g' AUDIO_EXTRACT := $(PYTHON) tools/audio_extraction.py SAMPLECONV := tools/audio/sampleconv/sampleconv SBC := tools/audio/sbc +ATBLGEN := tools/audio/atblgen SBCFLAGS := --matching @@ -745,6 +746,21 @@ ifeq ($(AUDIO_BUILD_DEBUG),1) @cmp $(@:.o=.bin) $(patsubst $(BUILD_DIR)/assets/audio/samplebanks/%,$(EXTRACTED_DIR)/baserom_audiotest/audiotable_files/%,$(@:.o=.bin)) && echo "$( rodata + +$(BUILD_DIR)/src/audio/tables/samplebank_table.o: src/audio/tables/samplebank_table.c $(BUILD_DIR)/assets/audio/samplebank_table.h +ifneq ($(RUN_CC_CHECK),0) + $(CC_CHECK) $< +endif + $(CC) -c $(CFLAGS) $(MIPS_VERSION) $(OPTFLAGS) -o $(@:.o=.tmp) $< + $(LD) -r -T linker_scripts/audio_table_rodata.ld $(@:.o=.tmp) -o $@ + @$(RM) $(@:.o=.tmp) + -include $(DEP_FILES) # Print target for debugging diff --git a/baseroms/gc-eu-mq-dbg/config.yml b/baseroms/gc-eu-mq-dbg/config.yml index 512337ac0b..c9841df4c2 100644 --- a/baseroms/gc-eu-mq-dbg/config.yml +++ b/baseroms/gc-eu-mq-dbg/config.yml @@ -41,10 +41,6 @@ incbins: segment: code vram: 0x80155500 size: 0x6F0 - - name: gSampleBankTable - segment: code - vram: 0x80155BF0 - size: 0x80 - name: aspMainData segment: code vram: 0x80155C70 diff --git a/baseroms/gc-eu-mq/config.yml b/baseroms/gc-eu-mq/config.yml index ad493eab93..6a44327d54 100644 --- a/baseroms/gc-eu-mq/config.yml +++ b/baseroms/gc-eu-mq/config.yml @@ -33,10 +33,6 @@ incbins: segment: code vram: 0x801108A0 size: 0x6F0 - - name: gSampleBankTable - segment: code - vram: 0x80110F90 - size: 0x80 - name: aspMainData segment: code vram: 0x80111010 diff --git a/baseroms/gc-eu/config.yml b/baseroms/gc-eu/config.yml index 0773143f50..a17207c192 100644 --- a/baseroms/gc-eu/config.yml +++ b/baseroms/gc-eu/config.yml @@ -33,10 +33,6 @@ incbins: segment: code vram: 0x801108C0 size: 0x6F0 - - name: gSampleBankTable - segment: code - vram: 0x80110FB0 - size: 0x80 - name: aspMainData segment: code vram: 0x80111030 diff --git a/baseroms/gc-jp-ce/config.yml b/baseroms/gc-jp-ce/config.yml index 40488a239b..853c09a025 100644 --- a/baseroms/gc-jp-ce/config.yml +++ b/baseroms/gc-jp-ce/config.yml @@ -33,10 +33,6 @@ incbins: segment: code vram: 0x801130B0 size: 0x6F0 - - name: gSampleBankTable - segment: code - vram: 0x801137A0 - size: 0x80 - name: aspMainData segment: code vram: 0x80113820 diff --git a/baseroms/gc-jp-mq/config.yml b/baseroms/gc-jp-mq/config.yml index d1726da23a..e3b01e7ede 100644 --- a/baseroms/gc-jp-mq/config.yml +++ b/baseroms/gc-jp-mq/config.yml @@ -33,10 +33,6 @@ incbins: segment: code vram: 0x801130B0 size: 0x6F0 - - name: gSampleBankTable - segment: code - vram: 0x801137A0 - size: 0x80 - name: aspMainData segment: code vram: 0x80113820 diff --git a/baseroms/gc-jp/config.yml b/baseroms/gc-jp/config.yml index e3d01b9350..4479cd93be 100644 --- a/baseroms/gc-jp/config.yml +++ b/baseroms/gc-jp/config.yml @@ -33,10 +33,6 @@ incbins: segment: code vram: 0x801130D0 size: 0x6F0 - - name: gSampleBankTable - segment: code - vram: 0x801137C0 - size: 0x80 - name: aspMainData segment: code vram: 0x80113840 diff --git a/baseroms/gc-us-mq/config.yml b/baseroms/gc-us-mq/config.yml index 74b989ff1e..ec6f1f1be4 100644 --- a/baseroms/gc-us-mq/config.yml +++ b/baseroms/gc-us-mq/config.yml @@ -33,10 +33,6 @@ incbins: segment: code vram: 0x80113090 size: 0x6F0 - - name: gSampleBankTable - segment: code - vram: 0x80113780 - size: 0x80 - name: aspMainData segment: code vram: 0x80113800 diff --git a/baseroms/gc-us/config.yml b/baseroms/gc-us/config.yml index d4102a9208..7612a6970d 100644 --- a/baseroms/gc-us/config.yml +++ b/baseroms/gc-us/config.yml @@ -33,10 +33,6 @@ incbins: segment: code vram: 0x801130B0 size: 0x6F0 - - name: gSampleBankTable - segment: code - vram: 0x801137A0 - size: 0x80 - name: aspMainData segment: code vram: 0x80113820 diff --git a/baseroms/ntsc-1.2/config.yml b/baseroms/ntsc-1.2/config.yml index 89a4c73e02..4455c86879 100644 --- a/baseroms/ntsc-1.2/config.yml +++ b/baseroms/ntsc-1.2/config.yml @@ -36,10 +36,6 @@ incbins: segment: code vram: 0x80114220 size: 0x6F0 - - name: gSampleBankTable - segment: code - vram: 0x80114910 - size: 0x80 - name: aspMainData segment: code vram: 0x80114990 diff --git a/data/audio_tables.rodata.s b/data/audio_tables.rodata.s index 4f6655c861..88f9cee98a 100644 --- a/data/audio_tables.rodata.s +++ b/data/audio_tables.rodata.s @@ -17,6 +17,3 @@ glabel gSequenceFontTable glabel gSequenceTable .incbin "incbin/gSequenceTable" - -glabel gSampleBankTable - .incbin "incbin/gSampleBankTable" diff --git a/include/attributes.h b/include/attributes.h index f58cc8122d..ce1365fde3 100644 --- a/include/attributes.h +++ b/include/attributes.h @@ -8,5 +8,6 @@ #define UNUSED __attribute__((unused)) #define FALLTHROUGH __attribute__((fallthrough)) #define NORETURN __attribute__((noreturn)) +#define NO_REORDER __attribute__((no_reorder)) #endif diff --git a/include/variables.h b/include/variables.h index 3bb6a6794d..b25f7f31d8 100644 --- a/include/variables.h +++ b/include/variables.h @@ -171,7 +171,7 @@ extern s16 gOcarinaSongItemMap[]; extern u8 gSoundFontTable[]; extern u8 gSequenceFontTable[]; extern u8 gSequenceTable[]; -extern u8 gSampleBankTable[]; +extern AudioTable gSampleBankTable; extern SaveContext gSaveContext; diff --git a/include/z64audio.h b/include/z64audio.h index 006e6d3186..6c2a34edf0 100644 --- a/include/z64audio.h +++ b/include/z64audio.h @@ -1,6 +1,9 @@ #ifndef Z64_AUDIO_H #define Z64_AUDIO_H +#include "ultra64.h" +#include "sequence.h" + typedef void (*AudioCustomUpdateFunction)(void); @@ -110,6 +113,14 @@ typedef enum AudioCacheType { /* 3 */ CACHE_PERMANENT } AudioCacheType; +typedef enum AudioCacheLoadType { + /* 0 */ CACHE_LOAD_PERMANENT, + /* 1 */ CACHE_LOAD_PERSISTENT, + /* 2 */ CACHE_LOAD_TEMPORARY, + /* 3 */ CACHE_LOAD_EITHER, + /* 4 */ CACHE_LOAD_EITHER_NOSYNC +} AudioCacheLoadType; + typedef enum AudioLoadStatus { /* 0 */ LOAD_STATUS_NOT_LOADED, // the entry data is not loaded /* 1 */ LOAD_STATUS_IN_PROGRESS, // the entry data is being loaded asynchronously @@ -807,6 +818,13 @@ typedef struct AudioSlowLoad { /* 0x4C */ OSIoMesg ioMesg; } AudioSlowLoad; // size = 0x64 +typedef struct AudioTableHeader { + /* 0x00 */ s16 numEntries; + /* 0x02 */ s16 unkMediumParam; + /* 0x04 */ uintptr_t romAddr; + /* 0x08 */ char pad[0x8]; +} AudioTableHeader; // size = 0x10 + typedef struct AudioTableEntry { /* 0x00 */ u32 romAddr; /* 0x04 */ u32 size; @@ -818,10 +836,7 @@ typedef struct AudioTableEntry { } AudioTableEntry; // size = 0x10 typedef struct AudioTable { - /* 0x00 */ s16 numEntries; - /* 0x02 */ s16 unkMediumParam; - /* 0x04 */ u32 romAddr; - /* 0x08 */ char pad[0x8]; + /* 0x00 */ AudioTableHeader header; /* 0x10 */ AudioTableEntry entries[1]; // (dynamic size) } AudioTable; // size >= 0x20 diff --git a/linker_scripts/audio_table_rodata.ld b/linker_scripts/audio_table_rodata.ld new file mode 100644 index 0000000000..27eab49fc4 --- /dev/null +++ b/linker_scripts/audio_table_rodata.ld @@ -0,0 +1,17 @@ +OUTPUT_ARCH (mips) + +/* Audio Table Linker Script, maps data into rodata */ + +SECTIONS { + + .rodata : + { + *(.data*) + *(.rodata*) + } + + /DISCARD/ : + { + *(*); + } +} diff --git a/spec b/spec index 6c926d9566..2edafc4fb2 100644 --- a/spec +++ b/spec @@ -649,6 +649,7 @@ beginseg include_no_data "$(BUILD_DIR)/src/code/z_game_over.o" include "$(BUILD_DIR)/src/code/z_construct.o" include "$(BUILD_DIR)/data/audio_tables.rodata.o" + include "$(BUILD_DIR)/src/audio/tables/samplebank_table.o" include "$(BUILD_DIR)/data/rsp.text.o" include "$(BUILD_DIR)/data/rsp.rodata.o" endseg diff --git a/src/audio/lib/heap.c b/src/audio/lib/heap.c index 2a0f859dff..e68ef6eec9 100644 --- a/src/audio/lib/heap.c +++ b/src/audio/lib/heap.c @@ -1265,7 +1265,7 @@ void AudioHeap_DiscardSampleCacheEntry(SampleCacheEntry* entry) { s32 sampleBankId2; s32 fontId; - numFonts = gAudioCtx.soundFontTable->numEntries; + numFonts = gAudioCtx.soundFontTable->header.numEntries; for (fontId = 0; fontId < numFonts; fontId++) { sampleBankId1 = gAudioCtx.soundFontList[fontId].sampleBankId1; sampleBankId2 = gAudioCtx.soundFontList[fontId].sampleBankId2; @@ -1322,7 +1322,7 @@ void AudioHeap_DiscardSampleCaches(void) { s32 fontId; s32 j; - numFonts = gAudioCtx.soundFontTable->numEntries; + numFonts = gAudioCtx.soundFontTable->header.numEntries; for (fontId = 0; fontId < numFonts; fontId++) { sampleBankId1 = gAudioCtx.soundFontList[fontId].sampleBankId1; sampleBankId2 = gAudioCtx.soundFontList[fontId].sampleBankId2; @@ -1392,7 +1392,7 @@ void AudioHeap_ApplySampleBankCacheInternal(s32 apply, s32 sampleBankId) { s32 pad[4]; sampleBankTable = gAudioCtx.sampleBankTable; - numFonts = gAudioCtx.soundFontTable->numEntries; + numFonts = gAudioCtx.soundFontTable->header.numEntries; change.oldAddr = (u32)AudioHeap_SearchCaches(SAMPLE_TABLE, CACHE_EITHER, sampleBankId); if (change.oldAddr == 0) { return; diff --git a/src/audio/lib/load.c b/src/audio/lib/load.c index 1491790205..807bab3171 100644 --- a/src/audio/lib/load.c +++ b/src/audio/lib/load.c @@ -338,10 +338,10 @@ void AudioLoad_SetSampleFontLoadStatus(s32 sampleBankId, s32 loadStatus) { void AudioLoad_InitTable(AudioTable* table, u32 romAddr, u16 unkMediumParam) { s32 i; - table->unkMediumParam = unkMediumParam; - table->romAddr = romAddr; + table->header.unkMediumParam = unkMediumParam; + table->header.romAddr = romAddr; - for (i = 0; i < table->numEntries; i++) { + for (i = 0; i < table->header.numEntries; i++) { if ((table->entries[i].size != 0) && (table->entries[i].medium == MEDIUM_CART)) { table->entries[i].romAddr += romAddr; } @@ -401,7 +401,7 @@ s32 AudioLoad_SyncLoadSample(Sample* sample, s32 fontId) { if (sample->medium == MEDIUM_UNK) { AudioLoad_SyncDmaUnkMedium((u32)sample->sampleAddr, sampleAddr, sample->size, - gAudioCtx.sampleBankTable->unkMediumParam); + gAudioCtx.sampleBankTable->header.unkMediumParam); } else { AudioLoad_SyncDma((u32)sample->sampleAddr, sampleAddr, sample->size, sample->medium); } @@ -707,7 +707,7 @@ void* AudioLoad_SyncLoad(u32 tableType, u32 id, s32* didAllocate) { *didAllocate = true; if (medium == MEDIUM_UNK) { - AudioLoad_SyncDmaUnkMedium(romAddr, ramAddr, size, (s16)table->unkMediumParam); + AudioLoad_SyncDmaUnkMedium(romAddr, ramAddr, size, (s16)table->header.unkMediumParam); } else { AudioLoad_SyncDma(romAddr, ramAddr, size, medium); } @@ -1066,8 +1066,8 @@ void* AudioLoad_AsyncLoadInner(s32 tableType, s32 id, s32 nChunks, s32 retData, } if (medium == MEDIUM_UNK) { - AudioLoad_StartAsyncLoadUnkMedium((s16)table->unkMediumParam, devAddr, ramAddr, size, medium, nChunks, - retQueue, MK_ASYNC_MSG(retData, tableType, id, loadStatus)); + AudioLoad_StartAsyncLoadUnkMedium((s16)table->header.unkMediumParam, devAddr, ramAddr, size, medium, + nChunks, retQueue, MK_ASYNC_MSG(retData, tableType, id, loadStatus)); } else { AudioLoad_StartAsyncLoad(devAddr, ramAddr, size, medium, nChunks, retQueue, MK_ASYNC_MSG(retData, tableType, realId, loadStatus)); @@ -1215,10 +1215,10 @@ void AudioLoad_Init(void* heap, u32 heapSize) { // Set audio tables pointers gAudioCtx.sequenceTable = (AudioTable*)gSequenceTable; gAudioCtx.soundFontTable = (AudioTable*)gSoundFontTable; - gAudioCtx.sampleBankTable = (AudioTable*)gSampleBankTable; + gAudioCtx.sampleBankTable = &gSampleBankTable; gAudioCtx.sequenceFontTable = gSequenceFontTable; - gAudioCtx.numSequences = gAudioCtx.sequenceTable->numEntries; + gAudioCtx.numSequences = gAudioCtx.sequenceTable->header.numEntries; gAudioCtx.specId = 0; gAudioCtx.resetStatus = 1; // Set reset to immediately initialize the audio heap @@ -1229,7 +1229,7 @@ void AudioLoad_Init(void* heap, u32 heapSize) { AudioLoad_InitTable(gAudioCtx.sequenceTable, (u32)_AudioseqSegmentRomStart, 0); AudioLoad_InitTable(gAudioCtx.soundFontTable, (u32)_AudiobankSegmentRomStart, 0); AudioLoad_InitTable(gAudioCtx.sampleBankTable, (u32)_AudiotableSegmentRomStart, 0); - numFonts = gAudioCtx.soundFontTable->numEntries; + numFonts = gAudioCtx.soundFontTable->header.numEntries; gAudioCtx.soundFontList = AudioHeap_Alloc(&gAudioCtx.initPool, numFonts * sizeof(SoundFont)); for (i = 0; i < numFonts; i++) { @@ -1295,7 +1295,7 @@ s32 AudioLoad_SlowLoadSample(s32 fontId, s32 instId, s8* status) { slowLoad->seqOrFontId = fontId; slowLoad->instId = instId; if (slowLoad->medium == MEDIUM_UNK) { - slowLoad->unkMediumParam = gAudioCtx.sampleBankTable->unkMediumParam; + slowLoad->unkMediumParam = gAudioCtx.sampleBankTable->header.unkMediumParam; } gAudioCtx.slowLoadPos ^= 1; @@ -1439,7 +1439,7 @@ s32 AudioLoad_SlowLoadSeq(s32 seqId, u8* ramAddr, s8* status) { slowLoad->seqOrFontId = seqId; if (slowLoad->medium == MEDIUM_UNK) { - slowLoad->unkMediumParam = seqTable->unkMediumParam; + slowLoad->unkMediumParam = seqTable->header.unkMediumParam; } gAudioCtx.slowLoadPos ^= 1; @@ -1616,7 +1616,7 @@ void AudioLoad_ProcessAsyncLoad(AudioAsyncLoad* asyncLoad, s32 resetStatus) { if (asyncLoad->bytesRemaining < asyncLoad->chunkSize) { if (asyncLoad->medium == MEDIUM_UNK) { AudioLoad_AsyncDmaUnkMedium(asyncLoad->curDevAddr, asyncLoad->curRamAddr, asyncLoad->bytesRemaining, - sampleBankTable->unkMediumParam); + sampleBankTable->header.unkMediumParam); } else { AudioLoad_AsyncDma(asyncLoad, asyncLoad->bytesRemaining); } @@ -1626,7 +1626,7 @@ void AudioLoad_ProcessAsyncLoad(AudioAsyncLoad* asyncLoad, s32 resetStatus) { if (asyncLoad->medium == MEDIUM_UNK) { AudioLoad_AsyncDmaUnkMedium(asyncLoad->curDevAddr, asyncLoad->curRamAddr, asyncLoad->chunkSize, - sampleBankTable->unkMediumParam); + sampleBankTable->header.unkMediumParam); } else { AudioLoad_AsyncDma(asyncLoad, asyncLoad->chunkSize); } @@ -1783,7 +1783,7 @@ void AudioLoad_RelocateFontAndPreloadSamples(s32 fontId, SoundFontData* fontData case false: if (sample->medium == MEDIUM_UNK) { AudioLoad_SyncDmaUnkMedium((u32)sample->sampleAddr, sampleRamAddr, sample->size, - gAudioCtx.sampleBankTable->unkMediumParam); + gAudioCtx.sampleBankTable->header.unkMediumParam); sample->sampleAddr = sampleRamAddr; sample->medium = MEDIUM_RAM; } else { @@ -2039,7 +2039,7 @@ void AudioLoad_PreloadSamplesForFont(s32 fontId, s32 async, SampleBankRelocInfo* case false: if (sample->medium == MEDIUM_UNK) { AudioLoad_SyncDmaUnkMedium((u32)sample->sampleAddr, addr, sample->size, - gAudioCtx.sampleBankTable->unkMediumParam); + gAudioCtx.sampleBankTable->header.unkMediumParam); sample->sampleAddr = addr; sample->medium = MEDIUM_RAM; } else { diff --git a/src/audio/tables/samplebank_table.c b/src/audio/tables/samplebank_table.c new file mode 100644 index 0000000000..58ebf06f22 --- /dev/null +++ b/src/audio/tables/samplebank_table.c @@ -0,0 +1,50 @@ +#include "attributes.h" +#include "z64audio.h" + +// Symbol definition + +extern AudioTable gSampleBankTable; +#pragma weak gSampleBankTable = sSampleBankTableHeader + +// Externs for table + +#define DEFINE_SAMPLE_BANK(name, medium, cachePolicy) \ + extern u8 name##_Start[]; \ + extern u8 name##_Size[]; +#define DEFINE_SAMPLE_BANK_PTR(index, medium, cachePolicy) /*empty*/ + +#include "assets/audio/samplebank_table.h" + +#undef DEFINE_SAMPLE_BANK +#undef DEFINE_SAMPLE_BANK_PTR + +// Table header + +NO_REORDER AudioTableHeader sSampleBankTableHeader = { +// The table contains the number of samplebanks, count them with the preprocessor +#define DEFINE_SAMPLE_BANK(name, medium, cachePolicy) 1 + +#define DEFINE_SAMPLE_BANK_PTR(index, medium, cachePolicy) 1 + + +#include "assets/audio/samplebank_table.h" + 0, + +#undef DEFINE_SAMPLE_BANK +#undef DEFINE_SAMPLE_BANK_PTR + + 0, + 0x00000000, + { 0, 0, 0, 0, 0, 0, 0, 0 }, +}; + +// Table body + +NO_REORDER AudioTableEntry sSampleBankTableEntries[] = { +#define DEFINE_SAMPLE_BANK(name, medium, cachePolicy) \ + { (u32)name##_Start, (u32)name##_Size, (medium), (cachePolicy), 0, 0, 0 }, +#define DEFINE_SAMPLE_BANK_PTR(index, medium, cachePolicy) { (index), 0, (medium), (cachePolicy), 0, 0, 0 }, + +#include "assets/audio/samplebank_table.h" + +#undef DEFINE_SAMPLE_BANK +#undef DEFINE_SAMPLE_BANK_PTR +}; diff --git a/src/code/z_play.c b/src/code/z_play.c index 7ebb6ccea6..ab3b475009 100644 --- a/src/code/z_play.c +++ b/src/code/z_play.c @@ -4,6 +4,8 @@ #include "z64frame_advance.h" +#pragma increment_block_number "gc-eu:24 gc-eu-mq:24 gc-jp:28 gc-jp-ce:28 gc-jp-mq:28 gc-us:28 gc-us-mq:28" + TransitionTile gTransitionTile; s32 gTransitionTileState; VisMono gPlayVisMono; diff --git a/src/overlays/actors/ovl_Fishing/z_fishing.c b/src/overlays/actors/ovl_Fishing/z_fishing.c index 42595eb432..fd81943337 100644 --- a/src/overlays/actors/ovl_Fishing/z_fishing.c +++ b/src/overlays/actors/ovl_Fishing/z_fishing.c @@ -14,7 +14,7 @@ #include "cic6105.h" #endif -#pragma increment_block_number "gc-eu:204 gc-eu-mq:204 gc-jp:206 gc-jp-ce:206 gc-jp-mq:206 gc-us:206 gc-us-mq:206" +#pragma increment_block_number "gc-eu:198 gc-eu-mq:198 gc-jp:200 gc-jp-ce:200 gc-jp-mq:200 gc-us:200 gc-us-mq:200" #define FLAGS ACTOR_FLAG_4 diff --git a/tools/audio/.gitignore b/tools/audio/.gitignore index f1de1d520d..8a45b347ef 100644 --- a/tools/audio/.gitignore +++ b/tools/audio/.gitignore @@ -1,3 +1,4 @@ __pycache__/ +atblgen sbc diff --git a/tools/audio/Makefile b/tools/audio/Makefile index 5068591798..9b859a84c3 100644 --- a/tools/audio/Makefile +++ b/tools/audio/Makefile @@ -1,4 +1,4 @@ -PROGRAMS := sbc +PROGRAMS := atblgen sbc ifeq ($(shell which xml2-config),) $(error xml2-config not found. Did you install libxml2-dev?) @@ -30,11 +30,14 @@ format: $(CLANG_FORMAT) $(FORMAT_ARGS) $(shell find . -maxdepth 1 -type f -name "*.[ch]") $(MAKE) -C sampleconv format -sbc_SOURCES := samplebank_compiler.c samplebank.c aifc.c xml.c util.c +atblgen_SOURCES := audio_tablegen.c samplebank.c xml.c util.c +sbc_SOURCES := samplebank_compiler.c samplebank.c aifc.c xml.c util.c -sbc_CFLAGS := $(XML_CFLAGS) +atblgen_CFLAGS := $(XML_CFLAGS) +sbc_CFLAGS := $(XML_CFLAGS) -sbc_LDFLAGS := $(XML_LDFLAGS) +atblgen_LDFLAGS := $(XML_LDFLAGS) +sbc_LDFLAGS := $(XML_LDFLAGS) define COMPILE = $(1): $($1_SOURCES) diff --git a/tools/audio/audio_tablegen.c b/tools/audio/audio_tablegen.c new file mode 100644 index 0000000000..b9fba94cc5 --- /dev/null +++ b/tools/audio/audio_tablegen.c @@ -0,0 +1,208 @@ +/** + * SPDX-FileCopyrightText: Copyright (C) 2024 ZeldaRET + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "samplebank.h" +#include "xml.h" +#include "util.h" + +/* Utility */ + +static bool +is_xml(const char *path) +{ + size_t len = strlen(path); + + if (len < 4) + return false; + if (path[len - 4] == '.' && tolower(path[len - 3]) == 'x' && tolower(path[len - 2]) == 'm' && + tolower(path[len - 1]) == 'l') + return true; + + return false; +} + +/* Samplebanks */ + +static int +tablegen_samplebanks(const char *sb_hdr_out, const char **samplebanks_paths, int num_samplebank_files) +{ + // Read in all samplebank xml files + + samplebank *samplebanks = malloc(num_samplebank_files * sizeof(samplebank)); + + for (int i = 0; i < num_samplebank_files; i++) { + const char *path = samplebanks_paths[i]; + + if (!is_xml(path)) + error("Not an xml file? (\"%s\")", path); + + xmlDocPtr document = xmlReadFile(path, NULL, XML_PARSE_NONET); + if (document == NULL) + error("Could not read xml file \"%s\"\n", path); + + read_samplebank_xml(&samplebanks[i], document); + } + + // Find largest index, including pointer indices + + size_t index_max = 0; + + for (int i = 0; i < num_samplebank_files; i++) { + samplebank *sb = &samplebanks[i]; + unsigned index = sb->index; + if (index > index_max) + index_max = index; + + for (size_t j = 0; j < sb->num_pointers; j++) { + index = sb->pointer_indices[j]; + if (index > index_max) + index_max = index; + } + } + + size_t indices_len = index_max + 1; + + // Classify indices and check that no two indices are the same + +#define INDEX_NONE 0 +#define INDEX_NOPOINTER 1 +#define INDEX_POINTER 2 + + struct sb_index_info { + const char *name; + unsigned index_type; + unsigned ptr_index; + const char *medium; + const char *cache_policy; + }; + struct sb_index_info *index_info = calloc(indices_len, sizeof(struct sb_index_info)); + + for (int i = 0; i < num_samplebank_files; i++) { + samplebank *sb = &samplebanks[i]; + unsigned index = sb->index; + + if (index_info[index].index_type != INDEX_NONE) + error("Overlapping samplebank indices, saw index %u more than once", index); + index_info[index].index_type = INDEX_NOPOINTER; + + index_info[index].name = sb->name; + index_info[index].medium = sb->medium; + index_info[index].cache_policy = sb->cache_policy; + + unsigned real_index = index; + + // Add pointers defined for this bank + for (size_t j = 0; j < sb->num_pointers; j++) { + index = sb->pointer_indices[j]; + + if (index_info[index].index_type != INDEX_NONE) + error("Overlapping samplebank indices, saw index %u more than once", index); + index_info[index].index_type = INDEX_POINTER; + + index_info[index].ptr_index = real_index; + index_info[index].medium = sb->medium; + index_info[index].cache_policy = sb->cache_policy; + } + } + + // Check that we have no gaps in the indices + + for (size_t i = 0; i < indices_len; i++) { + if (index_info[i].index_type == INDEX_NONE) + error("Missing samplebank index %lu", i); + } + + // Emit the table + + FILE *out = fopen(sb_hdr_out, "w"); + if (out == NULL) + error("Failed to open samplebank header output"); + + fprintf(out, + // clang-format off + "/**" "\n" + " * DEFINE_SAMPLE_BANK(name, medium, cachePolicy)" "\n" + " * DEFINE_SAMPLE_BANK_PTR(index, medium, cachePolicy)" "\n" + " */" "\n" + // clang-format on + ); + + for (size_t i = 0; i < indices_len; i++) { + unsigned index_type = index_info[i].index_type; + + // By this point we shouldn't have any INDEX_NONEs, since it would have been caught before this + // when we checked for gaps + assert(index_type == INDEX_NOPOINTER || index_type == INDEX_POINTER); + + if (index_type == INDEX_NOPOINTER) { + fprintf(out, "DEFINE_SAMPLE_BANK (%s, %s, %s)\n", index_info[i].name, index_info[i].medium, + index_info[i].cache_policy); + } else { + fprintf(out, "DEFINE_SAMPLE_BANK_PTR(%u, %s, %s)\n", index_info[i].ptr_index, index_info[i].medium, + index_info[i].cache_policy); + } + } + + fclose(out); + + free(index_info); + free(samplebanks); + + return EXIT_SUCCESS; +} + +/* Common */ + +static int +usage(const char *progname) +{ + fprintf(stderr, + // clang-format off + "%s: Generate code tables for audio data" "\n" + "Usage:" "\n" + " %s --banks " "\n", + // clang-format on + progname, progname); + return EXIT_FAILURE; +} + +int +main(int argc, char **argv) +{ + int ret = EXIT_SUCCESS; + + const char *progname = argv[0]; + + if (argc < 2) + return usage(progname); + + const char *mode = argv[1]; + + if (strequ(mode, "--banks")) { + if (argc < 4) + return usage(progname); + + const char *sb_hdr_out = argv[2]; + const char **samplebanks_paths = (const char **)&argv[3]; + int num_samplebank_files = argc - 3; + + ret = tablegen_samplebanks(sb_hdr_out, samplebanks_paths, num_samplebank_files); + } else { + return usage(progname); + } + + return ret; +}