mirror of
https://github.com/zeldaret/oot.git
synced 2024-11-14 05:19:36 +00:00
[Audio 6/?] Build Soundfonts and the Soundfont Table (#2056)
* [Audio 6/?] Build Soundfonts and the Soundfont Table * Improve lots of error messages * First suggested changes * Make audio build debugging more friendly Co-authored-by: Dragorn421 <Dragorn421@users.noreply.github.com> * Some fixes from MM review * Make soundfont_table.h generation depend on the samplebank xmls since they are read, report from which soundfont the invalid pointer indirect warning originates from --------- Co-authored-by: Dragorn421 <Dragorn421@users.noreply.github.com>
This commit is contained in:
parent
17debe8620
commit
aa97586659
40 changed files with 2775 additions and 87 deletions
77
Makefile
77
Makefile
|
@ -237,9 +237,12 @@ 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
|
||||
SFC := tools/audio/sfc
|
||||
SFPATCH := tools/audio/sfpatch
|
||||
ATBLGEN := tools/audio/atblgen
|
||||
|
||||
SBCFLAGS := --matching
|
||||
SFCFLAGS := --matching
|
||||
|
||||
CFLAGS += $(CPP_DEFINES)
|
||||
CPPFLAGS += $(CPP_DEFINES)
|
||||
|
@ -267,7 +270,7 @@ else
|
|||
# Suppress warnings for wrong number of macro arguments (to fake variadic
|
||||
# macros) and Microsoft extensions such as anonymous structs (which the
|
||||
# compiler does support but warns for their usage).
|
||||
CFLAGS += -G 0 -non_shared -fullwarn -verbose -Xcpluscomm $(INC) -Wab,-r4300_mul -woff 516,609,649,838,712
|
||||
CFLAGS += -G 0 -non_shared -fullwarn -verbose -Xcpluscomm $(INC) -Wab,-r4300_mul -woff 516,609,649,838,712,807
|
||||
MIPS_VERSION := -mips2
|
||||
endif
|
||||
|
||||
|
@ -308,9 +311,11 @@ endif
|
|||
ifneq ($(wildcard $(EXTRACTED_DIR)/assets/audio),)
|
||||
SAMPLE_EXTRACT_DIRS := $(shell find $(EXTRACTED_DIR)/assets/audio/samples -type d)
|
||||
SAMPLEBANK_EXTRACT_DIRS := $(shell find $(EXTRACTED_DIR)/assets/audio/samplebanks -type d)
|
||||
SOUNDFONT_EXTRACT_DIRS := $(shell find $(EXTRACTED_DIR)/assets/audio/soundfonts -type d)
|
||||
else
|
||||
SAMPLE_EXTRACT_DIRS :=
|
||||
SAMPLEBANK_EXTRACT_DIRS :=
|
||||
SOUNDFONT_EXTRACT_DIRS :=
|
||||
endif
|
||||
|
||||
ifneq ($(wildcard assets/audio/samples),)
|
||||
|
@ -325,6 +330,12 @@ else
|
|||
SAMPLEBANK_DIRS :=
|
||||
endif
|
||||
|
||||
ifneq ($(wildcard assets/audio/soundfonts),)
|
||||
SOUNDFONT_DIRS := $(shell find assets/audio/soundfonts -type d)
|
||||
else
|
||||
SOUNDFONT_DIRS :=
|
||||
endif
|
||||
|
||||
SAMPLE_FILES := $(foreach dir,$(SAMPLE_DIRS),$(wildcard $(dir)/*.wav))
|
||||
SAMPLE_EXTRACT_FILES := $(foreach dir,$(SAMPLE_EXTRACT_DIRS),$(wildcard $(dir)/*.wav))
|
||||
AIFC_FILES := $(foreach f,$(SAMPLE_FILES),$(BUILD_DIR)/$(f:.wav=.aifc)) $(foreach f,$(SAMPLE_EXTRACT_FILES:.wav=.aifc),$(f:$(EXTRACTED_DIR)/%=$(BUILD_DIR)/%))
|
||||
|
@ -335,6 +346,13 @@ SAMPLEBANK_BUILD_XMLS := $(foreach f,$(SAMPLEBANK_XMLS),$(BUILD_DIR)/$f) $(for
|
|||
SAMPLEBANK_O_FILES := $(foreach f,$(SAMPLEBANK_BUILD_XMLS),$(f:.xml=.o))
|
||||
SAMPLEBANK_DEP_FILES := $(foreach f,$(SAMPLEBANK_O_FILES),$(f:.o=.d))
|
||||
|
||||
SOUNDFONT_XMLS := $(foreach dir,$(SOUNDFONT_DIRS),$(wildcard $(dir)/*.xml))
|
||||
SOUNDFONT_EXTRACT_XMLS := $(foreach dir,$(SOUNDFONT_EXTRACT_DIRS),$(wildcard $(dir)/*.xml))
|
||||
SOUNDFONT_BUILD_XMLS := $(foreach f,$(SOUNDFONT_XMLS),$(BUILD_DIR)/$f) $(foreach f,$(SOUNDFONT_EXTRACT_XMLS),$(f:$(EXTRACTED_DIR)/%=$(BUILD_DIR)/%))
|
||||
SOUNDFONT_O_FILES := $(foreach f,$(SOUNDFONT_BUILD_XMLS),$(f:.xml=.o))
|
||||
SOUNDFONT_HEADERS := $(foreach f,$(SOUNDFONT_BUILD_XMLS),$(f:.xml=.h))
|
||||
SOUNDFONT_DEP_FILES := $(foreach f,$(SOUNDFONT_O_FILES),$(f:.o=.d))
|
||||
|
||||
# create extracted directories
|
||||
$(shell mkdir -p $(EXTRACTED_DIR) $(EXTRACTED_DIR)/assets $(EXTRACTED_DIR)/text)
|
||||
|
||||
|
@ -388,12 +406,14 @@ $(shell mkdir -p $(foreach dir, \
|
|||
$(UNDECOMPILED_DATA_DIRS) \
|
||||
$(SAMPLE_DIRS) \
|
||||
$(SAMPLEBANK_DIRS) \
|
||||
$(SOUNDFONT_DIRS) \
|
||||
$(ASSET_BIN_DIRS_COMMITTED), \
|
||||
$(BUILD_DIR)/$(dir)))
|
||||
ifneq ($(wildcard $(EXTRACTED_DIR)/assets),)
|
||||
$(shell mkdir -p $(foreach dir, \
|
||||
$(SAMPLE_EXTRACT_DIRS) \
|
||||
$(SAMPLEBANK_EXTRACT_DIRS) \
|
||||
$(SOUNDFONT_EXTRACT_DIRS) \
|
||||
$(ASSET_BIN_DIRS_EXTRACTED), \
|
||||
$(dir:$(EXTRACTED_DIR)/%=$(BUILD_DIR)/%)))
|
||||
endif
|
||||
|
@ -565,7 +585,8 @@ $(ROMC): $(ROM) $(ELF) $(BUILD_DIR)/compress_ranges.txt
|
|||
$(PYTHON) -m ipl3checksum sum --cic 6105 --update $@
|
||||
|
||||
$(ELF): $(TEXTURE_FILES_OUT) $(ASSET_FILES_OUT) $(O_FILES) $(OVL_RELOC_FILES) $(LDSCRIPT) $(BUILD_DIR)/undefined_syms.txt \
|
||||
$(SAMPLEBANK_O_FILES)
|
||||
$(SAMPLEBANK_O_FILES) $(SOUNDFONT_O_FILES) \
|
||||
$(BUILD_DIR)/assets/audio/audiobank_padding.o
|
||||
$(LD) -T $(LDSCRIPT) -T $(BUILD_DIR)/undefined_syms.txt --no-check-sections --accept-unknown-input-arch --emit-relocs -Map $(MAP) -o $@
|
||||
|
||||
## Order-only prerequisites
|
||||
|
@ -700,6 +721,10 @@ $(BUILD_DIR)/assets/%.jpg.inc.c: $(EXTRACTED_DIR)/assets/%.jpg
|
|||
# Audio
|
||||
|
||||
AUDIO_BUILD_DEBUG ?= 0
|
||||
ifeq ($(AUDIO_BUILD_DEBUG),1)
|
||||
# for debugging only, make soundfonts depend on samplebanks so they can be linked against
|
||||
$(BUILD_DIR)/assets/audio/soundfonts/%.o: $(SAMPLEBANK_O_FILES)
|
||||
endif
|
||||
|
||||
# first build samples...
|
||||
|
||||
|
@ -747,11 +772,47 @@ ifeq ($(AUDIO_BUILD_DEBUG),1)
|
|||
@cmp $(@:.o=.bin) $(patsubst $(BUILD_DIR)/assets/audio/samplebanks/%,$(EXTRACTED_DIR)/baserom_audiotest/audiotable_files/%,$(@:.o=.bin)) && echo "$(<F) OK"
|
||||
endif
|
||||
|
||||
# also assemble the soundfonts and generate the associated headers...
|
||||
|
||||
$(BUILD_DIR)/assets/audio/soundfonts/%.xml: assets/audio/soundfonts/%.xml
|
||||
cat $< | $(BUILD_DIR_REPLACE) > $@
|
||||
|
||||
$(BUILD_DIR)/assets/audio/soundfonts/%.xml: $(EXTRACTED_DIR)/assets/audio/soundfonts/%.xml
|
||||
cat $< | $(BUILD_DIR_REPLACE) > $@
|
||||
|
||||
.PRECIOUS: $(BUILD_DIR)/assets/audio/soundfonts/%.c $(BUILD_DIR)/assets/audio/soundfonts/%.h $(BUILD_DIR)/assets/audio/soundfonts/%.name
|
||||
$(BUILD_DIR)/assets/audio/soundfonts/%.c $(BUILD_DIR)/assets/audio/soundfonts/%.h $(BUILD_DIR)/assets/audio/soundfonts/%.name: $(BUILD_DIR)/assets/audio/soundfonts/%.xml | $(SAMPLEBANK_BUILD_XMLS) $(AIFC_FILES)
|
||||
# This rule can be triggered for either the .c or .h file, so $@ may refer to either the .c or .h file. A simple
|
||||
# substitution $(@:.c=.h) will fail ~50% of the time with -j. Instead, don't assume anything about the suffix of $@.
|
||||
$(SFC) $(SFCFLAGS) --makedepend $(basename $@).d $< $(basename $@).c $(basename $@).h $(basename $@).name
|
||||
|
||||
-include $(SOUNDFONT_DEP_FILES)
|
||||
|
||||
$(BUILD_DIR)/assets/audio/soundfonts/%.o: $(BUILD_DIR)/assets/audio/soundfonts/%.c $(BUILD_DIR)/assets/audio/soundfonts/%.name
|
||||
# compile c to unlinked object
|
||||
$(CC) -c $(CFLAGS) $(MIPS_VERSION) $(OPTFLAGS) -I include/audio -o $(@:.o=.tmp) $<
|
||||
# partial link
|
||||
$(LD) -r -T linker_scripts/soundfont.ld $(@:.o=.tmp) -o $(@:.o=.tmp2)
|
||||
# patch defined symbols to be ABS symbols so that they remain file-relative offsets forever
|
||||
$(SFPATCH) $(@:.o=.tmp2) $(@:.o=.tmp2)
|
||||
# write start and size symbols afterwards, filename != symbolic name so source symbolic name from the .name file written by sfc
|
||||
$(OBJCOPY) --add-symbol $$(cat $(<:.c=.name))_Start=.rodata:0,global --redefine-sym __LEN__=$$(cat $(<:.c=.name))_Size $(@:.o=.tmp2) $@
|
||||
# cleanup temp files
|
||||
@$(RM) $(@:.o=.tmp) $(@:.o=.tmp2)
|
||||
ifeq ($(AUDIO_BUILD_DEBUG),1)
|
||||
$(LD) $(foreach f,$(SAMPLEBANK_O_FILES),-R $f) -T linker_scripts/soundfont.ld $@ -o $(@:.o=.elf)
|
||||
$(OBJCOPY) -O binary -j.rodata $(@:.o=.elf) $(@:.o=.bin)
|
||||
@(cmp $(@:.o=.bin) $(patsubst $(BUILD_DIR)/assets/audio/soundfonts/%,$(EXTRACTED_DIR)/baserom_audiotest/audiobank_files/%,$(@:.o=.bin)) && echo "$(<F) OK" || (mkdir -p NONMATCHINGS/soundfonts && cp $(@:.o=.bin) NONMATCHINGS/soundfonts/$(@F:.o=.bin)))
|
||||
endif
|
||||
|
||||
# put together the tables
|
||||
|
||||
$(BUILD_DIR)/assets/audio/samplebank_table.h: $(SAMPLEBANK_BUILD_XMLS)
|
||||
$(ATBLGEN) --banks $@ $^
|
||||
|
||||
$(BUILD_DIR)/assets/audio/soundfont_table.h: $(SOUNDFONT_BUILD_XMLS) $(SAMPLEBANK_BUILD_XMLS)
|
||||
$(ATBLGEN) --fonts $@ $(SOUNDFONT_BUILD_XMLS)
|
||||
|
||||
# build the tables into objects, move data -> rodata
|
||||
|
||||
$(BUILD_DIR)/src/audio/tables/samplebank_table.o: src/audio/tables/samplebank_table.c $(BUILD_DIR)/assets/audio/samplebank_table.h
|
||||
|
@ -762,6 +823,18 @@ endif
|
|||
$(LD) -r -T linker_scripts/data_with_rodata.ld $(@:.o=.tmp) -o $@
|
||||
@$(RM) $(@:.o=.tmp)
|
||||
|
||||
$(BUILD_DIR)/src/audio/tables/soundfont_table.o: src/audio/tables/soundfont_table.c $(BUILD_DIR)/assets/audio/soundfont_table.h $(SOUNDFONT_HEADERS)
|
||||
ifneq ($(RUN_CC_CHECK),0)
|
||||
$(CC_CHECK) $<
|
||||
endif
|
||||
$(CC) -c $(CFLAGS) $(MIPS_VERSION) $(OPTFLAGS) -o $(@:.o=.tmp) $<
|
||||
$(LD) -r -T linker_scripts/data_with_rodata.ld $(@:.o=.tmp) -o $@
|
||||
@$(RM) $(@:.o=.tmp)
|
||||
|
||||
# Extra audiobank padding that doesn't belong to any soundfont file
|
||||
$(BUILD_DIR)/assets/audio/audiobank_padding.o:
|
||||
echo ".section .rodata; .fill 0x20" | $(AS) $(ASFLAGS) -o $@
|
||||
|
||||
-include $(DEP_FILES)
|
||||
|
||||
# Print target for debugging
|
||||
|
|
|
@ -29,10 +29,6 @@ incbins:
|
|||
segment: code
|
||||
vram: 0x8012A7C0
|
||||
size: 0x400
|
||||
- name: gSoundFontTable
|
||||
segment: code
|
||||
vram: 0x801550D0
|
||||
size: 0x270
|
||||
- name: gSequenceFontTable
|
||||
segment: code
|
||||
vram: 0x80155340
|
||||
|
|
|
@ -21,10 +21,6 @@ incbins:
|
|||
segment: code
|
||||
vram: 0x800E3A10
|
||||
size: 0xAF0
|
||||
- name: gSoundFontTable
|
||||
segment: code
|
||||
vram: 0x80110470
|
||||
size: 0x270
|
||||
- name: gSequenceFontTable
|
||||
segment: code
|
||||
vram: 0x801106E0
|
||||
|
|
|
@ -21,10 +21,6 @@ incbins:
|
|||
segment: code
|
||||
vram: 0x800E3A30
|
||||
size: 0xAF0
|
||||
- name: gSoundFontTable
|
||||
segment: code
|
||||
vram: 0x80110490
|
||||
size: 0x270
|
||||
- name: gSequenceFontTable
|
||||
segment: code
|
||||
vram: 0x80110700
|
||||
|
|
|
@ -21,10 +21,6 @@ incbins:
|
|||
segment: code
|
||||
vram: 0x800E60B0
|
||||
size: 0xAF0
|
||||
- name: gSoundFontTable
|
||||
segment: code
|
||||
vram: 0x80112C80
|
||||
size: 0x270
|
||||
- name: gSequenceFontTable
|
||||
segment: code
|
||||
vram: 0x80112EF0
|
||||
|
|
|
@ -21,10 +21,6 @@ incbins:
|
|||
segment: code
|
||||
vram: 0x800E60B0
|
||||
size: 0xAF0
|
||||
- name: gSoundFontTable
|
||||
segment: code
|
||||
vram: 0x80112C80
|
||||
size: 0x270
|
||||
- name: gSequenceFontTable
|
||||
segment: code
|
||||
vram: 0x80112EF0
|
||||
|
|
|
@ -21,10 +21,6 @@ incbins:
|
|||
segment: code
|
||||
vram: 0x800E60D0
|
||||
size: 0xAF0
|
||||
- name: gSoundFontTable
|
||||
segment: code
|
||||
vram: 0x80112CA0
|
||||
size: 0x270
|
||||
- name: gSequenceFontTable
|
||||
segment: code
|
||||
vram: 0x80112F10
|
||||
|
|
|
@ -21,10 +21,6 @@ incbins:
|
|||
segment: code
|
||||
vram: 0x800E6090
|
||||
size: 0xAF0
|
||||
- name: gSoundFontTable
|
||||
segment: code
|
||||
vram: 0x80112C60
|
||||
size: 0x270
|
||||
- name: gSequenceFontTable
|
||||
segment: code
|
||||
vram: 0x80112ED0
|
||||
|
|
|
@ -21,10 +21,6 @@ incbins:
|
|||
segment: code
|
||||
vram: 0x800E60B0
|
||||
size: 0xAF0
|
||||
- name: gSoundFontTable
|
||||
segment: code
|
||||
vram: 0x80112C80
|
||||
size: 0x270
|
||||
- name: gSequenceFontTable
|
||||
segment: code
|
||||
vram: 0x80112EF0
|
||||
|
|
|
@ -24,10 +24,6 @@ incbins:
|
|||
segment: code
|
||||
vram: 0x800E5E70
|
||||
size: 0xAF0
|
||||
- name: gSoundFontTable
|
||||
segment: code
|
||||
vram: 0x80113DF0
|
||||
size: 0x270
|
||||
- name: gSequenceFontTable
|
||||
segment: code
|
||||
vram: 0x80114060
|
||||
|
|
|
@ -9,9 +9,6 @@
|
|||
|
||||
.balign 16
|
||||
|
||||
glabel gSoundFontTable
|
||||
.incbin "incbin/gSoundFontTable"
|
||||
|
||||
glabel gSequenceFontTable
|
||||
.incbin "incbin/gSequenceFontTable"
|
||||
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
#define __attribute__(x)
|
||||
#endif
|
||||
|
||||
#define UNUSED __attribute__((unused))
|
||||
#define FALLTHROUGH __attribute__((fallthrough))
|
||||
#define NORETURN __attribute__((noreturn))
|
||||
#define NO_REORDER __attribute__((no_reorder))
|
||||
#define UNUSED __attribute__((unused))
|
||||
#define FALLTHROUGH __attribute__((fallthrough))
|
||||
#define NORETURN __attribute__((noreturn))
|
||||
#define NO_REORDER __attribute__((no_reorder))
|
||||
#define SECTION_DATA __attribute__((section(".data")))
|
||||
|
||||
#endif
|
||||
|
|
46
include/audio/soundfont_file.h
Normal file
46
include/audio/soundfont_file.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
#ifndef SOUNDFONT_FILE_H
|
||||
#define SOUNDFONT_FILE_H
|
||||
|
||||
#include "libc/stdbool.h"
|
||||
#include "alignment.h"
|
||||
#include "attributes.h"
|
||||
#include "z64audio.h"
|
||||
|
||||
// Envelope definitions
|
||||
|
||||
#define ENVELOPE_POINT(delay, target) { (delay), (target) }
|
||||
#define ENVELOPE_DISABLE() { ADSR_DISABLE, 0 }
|
||||
#define ENVELOPE_HANG() { ADSR_HANG, 0 }
|
||||
#define ENVELOPE_GOTO(index) { ADSR_GOTO, (index) }
|
||||
#define ENVELOPE_RESTART() { ADSR_RESTART, 0 }
|
||||
|
||||
// Instrument definitions
|
||||
|
||||
#define INSTR_SAMPLE_NONE { NULL, 0.0f }
|
||||
#define INSTR_SAMPLE_LO_NONE 0
|
||||
#define INSTR_SAMPLE_HI_NONE 127
|
||||
|
||||
// Explicit padding is sometimes required where soundfont data was padded to 0x10 bytes (possibly due to source file
|
||||
// splits in the original soundfonts?)
|
||||
// It's less convenient for us to emit multiple files per soundfont, so instead we fill in the padding manually.
|
||||
|
||||
#ifndef GLUE
|
||||
#define GLUE(a,b) a##b
|
||||
#endif
|
||||
#ifndef GLUE2
|
||||
#define GLUE2(a,b) GLUE(a,b)
|
||||
#endif
|
||||
|
||||
#ifdef __sgi
|
||||
// For IDO, we have to add explicit padding arrays
|
||||
#define SF_PAD4() static u8 GLUE2(_pad, __LINE__) [] = { 0,0,0,0 }
|
||||
#define SF_PAD8() static u8 GLUE2(_pad, __LINE__) [] = { 0,0,0,0,0,0,0,0 }
|
||||
#define SF_PADC() static u8 GLUE2(_pad, __LINE__) [] = { 0,0,0,0,0,0,0,0,0,0,0,0 }
|
||||
#else
|
||||
// For other compilers, the soundfont compiler (sfc) emits alignment attributes that handle this automatically
|
||||
#define SF_PAD4()
|
||||
#define SF_PAD8()
|
||||
#define SF_PADC()
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -168,7 +168,7 @@ extern s32 __osPfsLastChannel;
|
|||
extern const TempoData gTempoData;
|
||||
extern const AudioHeapInitSizes gAudioHeapInitSizes;
|
||||
extern s16 gOcarinaSongItemMap[];
|
||||
extern u8 gSoundFontTable[];
|
||||
extern AudioTable gSoundFontTable;
|
||||
extern u8 gSequenceFontTable[];
|
||||
extern u8 gSequenceTable[];
|
||||
extern AudioTable gSampleBankTable;
|
||||
|
|
|
@ -177,18 +177,32 @@ typedef struct EnvelopePoint {
|
|||
/* 0x2 */ s16 arg;
|
||||
} EnvelopePoint; // size = 0x4
|
||||
|
||||
typedef struct AdpcmLoop {
|
||||
typedef struct AdpcmLoopHeader {
|
||||
/* 0x00 */ u32 start;
|
||||
/* 0x04 */ u32 end;
|
||||
/* 0x08 */ u32 count;
|
||||
/* 0x04 */ u32 end; // s16 sample position where the loop ends
|
||||
/* 0x08 */ u32 count; // The number of times the loop is played before the sound completes. Setting count to -1 indicates that the loop should play indefinitely.
|
||||
/* 0x0C */ char unk_0C[0x4];
|
||||
} AdpcmLoopHeader; // size = 0x10
|
||||
|
||||
typedef struct AdpcmLoop {
|
||||
/* 0x00 */ AdpcmLoopHeader header;
|
||||
/* 0x10 */ s16 predictorState[16]; // only exists if count != 0. 8-byte aligned
|
||||
} AdpcmLoop; // size = 0x30 (or 0x10)
|
||||
|
||||
typedef struct AdpcmBook {
|
||||
typedef struct AdpcmBookHeader {
|
||||
/* 0x00 */ s32 order;
|
||||
/* 0x04 */ s32 numPredictors;
|
||||
/* 0x08 */ s16 book[1]; // size 8 * order * numPredictors. 8-byte aligned
|
||||
} AdpcmBookHeader; // size = 0x8
|
||||
|
||||
/**
|
||||
* The procedure used to design the codeBook is based on an adaptive clustering algorithm.
|
||||
* The size of the codeBook is (8 * order * numPredictors) and is 8-byte aligned
|
||||
*/
|
||||
typedef s16 AdpcmBookData[];
|
||||
|
||||
typedef struct AdpcmBook {
|
||||
/* 0x00 */ AdpcmBookHeader header;
|
||||
/* 0x08 */ AdpcmBookData book; // size 8 * order * numPredictors. 8-byte aligned
|
||||
} AdpcmBook; // size >= 0x8
|
||||
|
||||
typedef struct Sample {
|
||||
|
|
19
linker_scripts/soundfont.ld
Normal file
19
linker_scripts/soundfont.ld
Normal file
|
@ -0,0 +1,19 @@
|
|||
OUTPUT_ARCH (mips)
|
||||
|
||||
/* Soundfont Linker Script, maps data into rodata and adds a file length symbol */
|
||||
|
||||
SECTIONS {
|
||||
|
||||
.rodata :
|
||||
{
|
||||
*(.data*)
|
||||
*(.rodata*)
|
||||
. = ALIGN(16);
|
||||
__LEN__ = . - ADDR(.rodata);
|
||||
}
|
||||
|
||||
/DISCARD/ :
|
||||
{
|
||||
*(*);
|
||||
}
|
||||
}
|
43
spec
43
spec
|
@ -141,8 +141,46 @@ endseg
|
|||
|
||||
beginseg
|
||||
name "Audiobank"
|
||||
address 0x10 // fake RAM address to avoid map lookup inaccuracies
|
||||
include "$(BUILD_DIR)/baserom/Audiobank.o"
|
||||
address 0
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_0.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_1.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_2.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_3.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_4.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_5.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_6.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_7.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_8.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_9.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_10.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_11.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_12.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_13.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_14.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_15.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_16.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_17.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_18.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_19.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_20.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_21.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_22.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_23.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_24.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_25.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_26.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_27.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_28.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_29.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_30.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_31.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_32.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_33.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_34.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_35.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_36.o"
|
||||
include "$(BUILD_DIR)/assets/audio/soundfonts/Soundfont_37.o"
|
||||
include "$(BUILD_DIR)/assets/audio/audiobank_padding.o"
|
||||
endseg
|
||||
|
||||
beginseg
|
||||
|
@ -650,6 +688,7 @@ beginseg
|
|||
// combined object before the final link.
|
||||
include "$(BUILD_DIR)/src/code/z_message_z_game_over.o"
|
||||
include "$(BUILD_DIR)/src/code/z_construct.o"
|
||||
include "$(BUILD_DIR)/src/audio/tables/soundfont_table.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"
|
||||
|
|
|
@ -1002,9 +1002,9 @@ void AudioHeap_Init(void) {
|
|||
reverb->sample.medium = MEDIUM_RAM;
|
||||
reverb->sample.size = reverb->windowSize * SAMPLE_SIZE;
|
||||
reverb->sample.sampleAddr = (u8*)reverb->leftRingBuf;
|
||||
reverb->loop.start = 0;
|
||||
reverb->loop.count = 1;
|
||||
reverb->loop.end = reverb->windowSize;
|
||||
reverb->loop.header.start = 0;
|
||||
reverb->loop.header.count = 1;
|
||||
reverb->loop.header.end = reverb->windowSize;
|
||||
|
||||
if (reverb->downsampleRate != 1) {
|
||||
reverb->unk_0E = 0x8000 / reverb->downsampleRate;
|
||||
|
|
|
@ -1214,7 +1214,7 @@ void AudioLoad_Init(void* heap, u32 heapSize) {
|
|||
|
||||
// Set audio tables pointers
|
||||
gAudioCtx.sequenceTable = (AudioTable*)gSequenceTable;
|
||||
gAudioCtx.soundFontTable = (AudioTable*)gSoundFontTable;
|
||||
gAudioCtx.soundFontTable = &gSoundFontTable;
|
||||
gAudioCtx.sampleBankTable = &gSampleBankTable;
|
||||
gAudioCtx.sequenceFontTable = gSequenceFontTable;
|
||||
|
||||
|
|
|
@ -946,7 +946,7 @@ s32 AudioSeq_SeqLayerProcessScriptStep4(SequenceLayer* layer, s32 cmd) {
|
|||
|
||||
if (layer->delay == 0) {
|
||||
if (layer->tunedSample != NULL) {
|
||||
time = layer->tunedSample->sample->loop->end;
|
||||
time = layer->tunedSample->sample->loop->header.end;
|
||||
} else {
|
||||
time = 0.0f;
|
||||
}
|
||||
|
|
|
@ -796,7 +796,7 @@ Acmd* AudioSynth_ProcessNote(s32 noteIndex, NoteSubEu* noteSubEu, NoteSynthesisS
|
|||
} else {
|
||||
sample = noteSubEu->tunedSample->sample;
|
||||
loopInfo = sample->loop;
|
||||
loopEndPos = loopInfo->end;
|
||||
loopEndPos = loopInfo->header.end;
|
||||
sampleAddr = (u32)sample->sampleAddr;
|
||||
resampledTempLen = 0;
|
||||
|
||||
|
@ -829,7 +829,7 @@ Acmd* AudioSynth_ProcessNote(s32 noteIndex, NoteSubEu* noteSubEu, NoteSynthesisS
|
|||
if (1) {}
|
||||
if (1) {}
|
||||
if (1) {}
|
||||
nEntries = SAMPLES_PER_FRAME * sample->book->order * sample->book->numPredictors;
|
||||
nEntries = SAMPLES_PER_FRAME * sample->book->header.order * sample->book->header.numPredictors;
|
||||
aLoadADPCM(cmd++, nEntries, gAudioCtx.curLoadedBook);
|
||||
}
|
||||
}
|
||||
|
@ -861,7 +861,7 @@ Acmd* AudioSynth_ProcessNote(s32 noteIndex, NoteSubEu* noteSubEu, NoteSynthesisS
|
|||
nSamplesInFirstFrame = nSamplesUntilLoopEnd;
|
||||
}
|
||||
nFramesToDecode = (nSamplesToDecode + SAMPLES_PER_FRAME - 1) / SAMPLES_PER_FRAME;
|
||||
if (loopInfo->count != 0) {
|
||||
if (loopInfo->header.count != 0) {
|
||||
// Loop around and restart
|
||||
restart = true;
|
||||
} else {
|
||||
|
@ -1019,7 +1019,7 @@ Acmd* AudioSynth_ProcessNote(s32 noteIndex, NoteSubEu* noteSubEu, NoteSynthesisS
|
|||
} else {
|
||||
if (restart) {
|
||||
synthState->restart = true;
|
||||
synthState->samplePosInt = loopInfo->start;
|
||||
synthState->samplePosInt = loopInfo->header.start;
|
||||
} else {
|
||||
synthState->samplePosInt += nSamplesToProcess;
|
||||
}
|
||||
|
|
|
@ -795,7 +795,7 @@ s32 func_800E6590(s32 seqPlayerIndex, s32 channelIndex, s32 layerIndex) {
|
|||
if (tunedSample == NULL) {
|
||||
return 0;
|
||||
}
|
||||
loopEnd = tunedSample->sample->loop->end;
|
||||
loopEnd = tunedSample->sample->loop->header.end;
|
||||
samplePos = note->synthesisState.samplePosInt;
|
||||
return loopEnd - samplePos;
|
||||
}
|
||||
|
|
50
src/audio/tables/soundfont_table.c
Normal file
50
src/audio/tables/soundfont_table.c
Normal file
|
@ -0,0 +1,50 @@
|
|||
#include "attributes.h"
|
||||
#include "z64audio.h"
|
||||
|
||||
// Symbol definition
|
||||
|
||||
extern AudioTable gSoundFontTable;
|
||||
#pragma weak gSoundFontTable = sSoundFontTableHeader
|
||||
|
||||
// Externs for table
|
||||
|
||||
#define DEFINE_SOUNDFONT(name, medium, cachePolicy, sampleBankNormal, sampleBankDD, nInstruments, nDrums, nSfx) \
|
||||
extern u8 name##_Start[]; \
|
||||
extern u8 name##_Size[];
|
||||
|
||||
#include "assets/audio/soundfont_table.h"
|
||||
|
||||
#undef DEFINE_SOUNDFONT
|
||||
|
||||
// Table header
|
||||
|
||||
NO_REORDER AudioTableHeader sSoundFontTableHeader = {
|
||||
// The table contains the number of soundfonts, count them with the preprocessor
|
||||
#define DEFINE_SOUNDFONT(name, medium, cachePolicy, sampleBankNormal, sampleBankDD, nInstruments, nDrums, nSfx) 1 +
|
||||
|
||||
#include "assets/audio/soundfont_table.h"
|
||||
0,
|
||||
|
||||
#undef DEFINE_SOUNDFONT
|
||||
|
||||
0,
|
||||
0x00000000,
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
};
|
||||
|
||||
// Table body
|
||||
|
||||
NO_REORDER AudioTableEntry sSoundFontTableEntries[] = {
|
||||
#define DEFINE_SOUNDFONT(name, medium, cachePolicy, sampleBankNormal, sampleBankDD, nInstruments, nDrums, nSfx) \
|
||||
{ (u32)name##_Start, \
|
||||
(u32)name##_Size, \
|
||||
(medium), \
|
||||
(cachePolicy), \
|
||||
((sampleBankNormal) << 8) | (sampleBankDD), \
|
||||
((nInstruments) << 8) | (nDrums), \
|
||||
(nSfx) },
|
||||
|
||||
#include "assets/audio/soundfont_table.h"
|
||||
|
||||
#undef DEFINE_SOUNDFONT
|
||||
};
|
|
@ -4,7 +4,7 @@
|
|||
#define GFXPOOL_HEAD_MAGIC 0x1234
|
||||
#define GFXPOOL_TAIL_MAGIC 0x5678
|
||||
|
||||
#pragma increment_block_number "gc-eu:128 gc-eu-mq:128"
|
||||
#pragma increment_block_number "gc-eu:128 gc-eu-mq:128 gc-jp:128 gc-jp-ce:128 gc-jp-mq:128 gc-us:128 gc-us-mq:128"
|
||||
|
||||
/**
|
||||
* The time at which the previous `Graph_Update` ended.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#pragma increment_block_number "gc-eu:128 gc-eu-mq:128"
|
||||
#pragma increment_block_number "gc-eu:128 gc-eu-mq:128 gc-jp:128 gc-jp-ce:128 gc-jp-mq:128 gc-us:128 gc-us-mq:128"
|
||||
|
||||
#include "global.h"
|
||||
#include "terminal.h"
|
||||
|
|
|
@ -12,8 +12,6 @@ typedef s32 (*ColChkLineFunc)(PlayState*, CollisionCheckContext*, Collider*, Vec
|
|||
|
||||
#define SAC_ENABLE (1 << 0)
|
||||
|
||||
#pragma increment_block_number "gc-eu:64 gc-eu-mq:64 gc-jp:64 gc-jp-ce:64 gc-jp-mq:64 gc-us:64 gc-us-mq:64"
|
||||
|
||||
#if OOT_DEBUG
|
||||
/**
|
||||
* Draws a red triangle with vertices vA, vB, and vC.
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
#include "global.h"
|
||||
#include "quake.h"
|
||||
#include "terminal.h"
|
||||
|
@ -5,8 +6,6 @@
|
|||
|
||||
#include "z64frame_advance.h"
|
||||
|
||||
#pragma increment_block_number "gc-eu:252 gc-eu-mq:252 gc-jp:0 gc-jp-ce:0 gc-jp-mq:0 gc-us:0 gc-us-mq:0"
|
||||
|
||||
TransitionTile gTransitionTile;
|
||||
s32 gTransitionTileState;
|
||||
VisMono gPlayVisMono;
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include "cic6105.h"
|
||||
#endif
|
||||
|
||||
#pragma increment_block_number "gc-eu:171 gc-eu-mq:171 gc-jp:173 gc-jp-ce:173 gc-jp-mq:173 gc-us:173 gc-us-mq:173"
|
||||
#pragma increment_block_number "gc-eu:164 gc-eu-mq:164 gc-jp:166 gc-jp-ce:166 gc-jp-mq:166 gc-us:166 gc-us-mq:166"
|
||||
|
||||
#define FLAGS ACTOR_FLAG_4
|
||||
|
||||
|
|
2
tools/audio/.gitignore
vendored
2
tools/audio/.gitignore
vendored
|
@ -1,4 +1,6 @@
|
|||
__pycache__/
|
||||
|
||||
atblgen
|
||||
sfpatch
|
||||
sbc
|
||||
sfc
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
PROGRAMS := atblgen sbc
|
||||
PROGRAMS := atblgen sfpatch sbc sfc
|
||||
|
||||
ifeq ($(shell which xml2-config),)
|
||||
$(error xml2-config not found. Did you install libxml2-dev?)
|
||||
|
@ -30,14 +30,18 @@ format:
|
|||
$(CLANG_FORMAT) $(FORMAT_ARGS) $(shell find . -maxdepth 1 -type f -name "*.[ch]")
|
||||
$(MAKE) -C sampleconv format
|
||||
|
||||
atblgen_SOURCES := audio_tablegen.c samplebank.c xml.c util.c
|
||||
sbc_SOURCES := samplebank_compiler.c samplebank.c aifc.c xml.c util.c
|
||||
atblgen_SOURCES := audio_tablegen.c samplebank.c soundfont.c xml.c util.c
|
||||
sfpatch_SOURCES := sfpatch.c util.c
|
||||
sbc_SOURCES := samplebank_compiler.c samplebank.c aifc.c xml.c util.c
|
||||
sfc_SOURCES := soundfont_compiler.c samplebank.c soundfont.c aifc.c xml.c util.c
|
||||
|
||||
atblgen_CFLAGS := $(XML_CFLAGS)
|
||||
sbc_CFLAGS := $(XML_CFLAGS)
|
||||
sfc_CFLAGS := $(XML_CFLAGS)
|
||||
|
||||
atblgen_LDFLAGS := $(XML_LDFLAGS)
|
||||
sbc_LDFLAGS := $(XML_LDFLAGS)
|
||||
sfc_LDFLAGS := $(XML_LDFLAGS)
|
||||
|
||||
define COMPILE =
|
||||
$(1): $($1_SOURCES)
|
||||
|
|
|
@ -16,11 +16,6 @@
|
|||
#include "aifc.h"
|
||||
#include "util.h"
|
||||
|
||||
#define CC4_CHECK(buf, str) \
|
||||
((buf)[0] == (str)[0] && (buf)[1] == (str)[1] && (buf)[2] == (str)[2] && (buf)[3] == (str)[3])
|
||||
|
||||
#define CC4(c1, c2, c3, c4) (((c1) << 24) | ((c2) << 16) | ((c3) << 8) | (c4))
|
||||
|
||||
#define FREAD(file, data, size) \
|
||||
do { \
|
||||
if (fread((data), (size), 1, (file)) != 1) { \
|
||||
|
@ -486,7 +481,7 @@ aifc_read(aifc_data *af, const char *path, uint8_t *match_buf, size_t *match_buf
|
|||
long read_size = ftell(in) - start - 8;
|
||||
|
||||
if (read_size > chunk_size)
|
||||
error("overran chunk: %lu vs %u\n", read_size, chunk_size);
|
||||
error("overran chunk: %lu vs %u", read_size, chunk_size);
|
||||
else if (read_size < chunk_size)
|
||||
warning("did not read entire %.*s chunk: %lu vs %u", 4, cc4, read_size, chunk_size);
|
||||
|
||||
|
|
|
@ -72,4 +72,9 @@ aifc_dispose(aifc_data *af);
|
|||
// Subtract 21, if negative wrap into [0, 128)
|
||||
#define NOTE_MIDI_TO_Z64(b) (((b)-21 < 0) ? ((b)-21 + 128) : ((b)-21))
|
||||
|
||||
#define CC4_CHECK(buf, str) \
|
||||
((buf)[0] == (str)[0] && (buf)[1] == (str)[1] && (buf)[2] == (str)[2] && (buf)[3] == (str)[3])
|
||||
|
||||
#define CC4(c1, c2, c3, c4) (((c1) << 24) | ((c2) << 16) | ((c3) << 8) | (c4))
|
||||
|
||||
#endif
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "samplebank.h"
|
||||
#include "soundfont.h"
|
||||
#include "xml.h"
|
||||
#include "util.h"
|
||||
|
||||
|
@ -51,7 +52,7 @@ tablegen_samplebanks(const char *sb_hdr_out, const char **samplebanks_paths, int
|
|||
|
||||
xmlDocPtr document = xmlReadFile(path, NULL, XML_PARSE_NONET);
|
||||
if (document == NULL)
|
||||
error("Could not read xml file \"%s\"\n", path);
|
||||
error("Could not read xml file \"%s\"", path);
|
||||
|
||||
read_samplebank_xml(&samplebanks[i], document);
|
||||
}
|
||||
|
@ -122,7 +123,7 @@ tablegen_samplebanks(const char *sb_hdr_out, const char **samplebanks_paths, int
|
|||
|
||||
for (size_t i = 0; i < indices_len; i++) {
|
||||
if (index_info[i].index_type == INDEX_NONE)
|
||||
error("Missing samplebank index %lu", i);
|
||||
error("No samplebank for index %lu", i);
|
||||
}
|
||||
|
||||
// Emit the table
|
||||
|
@ -164,6 +165,131 @@ tablegen_samplebanks(const char *sb_hdr_out, const char **samplebanks_paths, int
|
|||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* Soundfonts */
|
||||
|
||||
static int
|
||||
validate_samplebank_index(soundfont *sf, samplebank *sb, int ptr_idx)
|
||||
{
|
||||
if (ptr_idx != -1) {
|
||||
// Validate pointer index
|
||||
bool found = false;
|
||||
|
||||
for (size_t i = 0; i < sb->num_pointers; i++) {
|
||||
if (ptr_idx == sb->pointer_indices[i]) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
warning("In Soundfont %s: Invalid pointer indirect %d for samplebank %s", sf->info.name, ptr_idx, sb->name);
|
||||
|
||||
return ptr_idx;
|
||||
} else {
|
||||
return sb->index;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
tablegen_soundfonts(const char *sf_hdr_out, char **soundfonts_paths, int num_soundfont_files)
|
||||
{
|
||||
soundfont *soundfonts = malloc(num_soundfont_files * sizeof(soundfont));
|
||||
int max_index = 0;
|
||||
|
||||
for (int i = 0; i < num_soundfont_files; i++) {
|
||||
char *path = soundfonts_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\"", path);
|
||||
|
||||
xmlNodePtr root = xmlDocGetRootElement(document);
|
||||
if (!strequ(XMLSTR_TO_STR(root->name), "Soundfont"))
|
||||
error("Root node must be <Soundfont>");
|
||||
|
||||
soundfont *sf = &soundfonts[i];
|
||||
|
||||
// Transform the xml path into a header include path
|
||||
// Assumption: replacing .xml -> .h forms a valid header include path
|
||||
size_t pathlen = strlen(path);
|
||||
path[pathlen - 3] = 'h';
|
||||
path[pathlen - 2] = '\0';
|
||||
|
||||
read_soundfont_info(sf, root);
|
||||
|
||||
if (max_index < sf->info.index)
|
||||
max_index = sf->info.index;
|
||||
}
|
||||
|
||||
struct soundfont_file_info {
|
||||
soundfont *soundfont;
|
||||
int normal_bank_index;
|
||||
int dd_bank_index;
|
||||
char *name;
|
||||
};
|
||||
struct soundfont_file_info *finfo = calloc(max_index + 1, sizeof(struct soundfont_file_info));
|
||||
|
||||
for (int i = 0; i < num_soundfont_files; i++) {
|
||||
soundfont *sf = &soundfonts[i];
|
||||
|
||||
// Resolve samplebank indices
|
||||
|
||||
int normal_idx = validate_samplebank_index(sf, &sf->sb, sf->info.pointer_index);
|
||||
|
||||
int dd_idx = 255;
|
||||
if (sf->info.bank_path_dd != NULL)
|
||||
dd_idx = validate_samplebank_index(sf, &sf->sbdd, sf->info.pointer_index_dd);
|
||||
|
||||
// Add info
|
||||
|
||||
if (finfo[sf->info.index].soundfont != NULL)
|
||||
error("Overlapping soundfont indices, saw index %u more than once", sf->info.index);
|
||||
|
||||
finfo[sf->info.index].soundfont = &soundfonts[i];
|
||||
finfo[sf->info.index].normal_bank_index = normal_idx;
|
||||
finfo[sf->info.index].dd_bank_index = dd_idx;
|
||||
finfo[sf->info.index].name = soundfonts_paths[i];
|
||||
}
|
||||
|
||||
// Make sure there are no gaps
|
||||
for (int i = 0; i < max_index + 1; i++) {
|
||||
if (finfo[i].soundfont == NULL)
|
||||
error("No soundfont for index %d", i);
|
||||
}
|
||||
|
||||
FILE *out = fopen(sf_hdr_out, "w");
|
||||
|
||||
fprintf(out,
|
||||
// clang-format off
|
||||
"/**" "\n"
|
||||
" * DEFINE_SOUNDFONT(name, medium, cachePolicy, sampleBankNormal, "
|
||||
"sampleBankDD, nInstruments, nDrums, nSfx)" "\n"
|
||||
" */" "\n"
|
||||
// clang-format on
|
||||
);
|
||||
|
||||
for (int i = 0; i < max_index + 1; i++) {
|
||||
soundfont *sf = finfo[i].soundfont;
|
||||
|
||||
fprintf(out,
|
||||
// clang-format off
|
||||
"#include \"%s\"" "\n"
|
||||
"DEFINE_SOUNDFONT(%s, %s, %s, %d, %d, SF%d_NUM_INSTRUMENTS, SF%d_NUM_DRUMS, SF%d_NUM_SFX)" "\n",
|
||||
// clang-format on
|
||||
finfo[i].name, sf->info.name, sf->info.medium, sf->info.cache_policy, finfo[i].normal_bank_index,
|
||||
finfo[i].dd_bank_index, sf->info.index, sf->info.index, sf->info.index);
|
||||
}
|
||||
|
||||
fclose(out);
|
||||
|
||||
free(soundfonts);
|
||||
free(finfo);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* Common */
|
||||
|
||||
static int
|
||||
|
@ -173,9 +299,10 @@ usage(const char *progname)
|
|||
// clang-format off
|
||||
"%s: Generate code tables for audio data" "\n"
|
||||
"Usage:" "\n"
|
||||
" %s --banks <samplebank_table.h> <samplebank xml files...>" "\n",
|
||||
" %s --banks <samplebank_table.h> <samplebank xml files...>" "\n"
|
||||
" %s --fonts <soundfont_table.h> <soundfont xml files...>" "\n",
|
||||
// clang-format on
|
||||
progname, progname);
|
||||
progname, progname, progname);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -200,6 +327,15 @@ main(int argc, char **argv)
|
|||
int num_samplebank_files = argc - 3;
|
||||
|
||||
ret = tablegen_samplebanks(sb_hdr_out, samplebanks_paths, num_samplebank_files);
|
||||
} else if (strequ(mode, "--fonts")) {
|
||||
if (argc < 4)
|
||||
return usage(progname);
|
||||
|
||||
const char *sf_hdr_out = argv[2];
|
||||
char **soundfonts_paths = &argv[3];
|
||||
int num_soundfont_files = argc - 3;
|
||||
|
||||
ret = tablegen_soundfonts(sf_hdr_out, soundfonts_paths, num_soundfont_files);
|
||||
} else {
|
||||
return usage(progname);
|
||||
}
|
||||
|
|
235
tools/audio/elf32.h
Normal file
235
tools/audio/elf32.h
Normal file
|
@ -0,0 +1,235 @@
|
|||
/* SPDX-FileCopyrightText: Copyright (C) 2024 ZeldaRET */
|
||||
/* SPDX-License-Identifier: CC0-1.0 */
|
||||
#ifndef ELF32_H_
|
||||
#define ELF32_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#define elf32_read16(x) be16toh(x)
|
||||
#define elf32_write16(x) htobe16(x)
|
||||
#define elf32_read32(x) be32toh(x)
|
||||
#define elf32_write32(x) htobe32(x)
|
||||
|
||||
#ifndef ELF32_QUALIFIERS
|
||||
#define ELF32_QUALIFIERS static UNUSED ALWAYS_INLINE
|
||||
#endif
|
||||
|
||||
#define GET_PTR(data, offset) ((void *)&((uint8_t *)(data))[(offset)])
|
||||
|
||||
#define EI_NIDENT 16
|
||||
#define EI_MAG0 0x00
|
||||
#define EI_MAG1 0x01
|
||||
#define EI_MAG2 0x02
|
||||
#define EI_MAG3 0x03
|
||||
#define EI_CLASS 0x04
|
||||
#define EI_DATA 0x05
|
||||
#define EI_VERSION 0x06
|
||||
#define EI_OSABI 0x07
|
||||
#define EI_ABIVERSION 0x08
|
||||
#define EI_PAD 0x09
|
||||
|
||||
typedef struct {
|
||||
uint8_t e_ident[EI_NIDENT];
|
||||
uint16_t e_type;
|
||||
uint16_t e_machine;
|
||||
uint32_t e_version;
|
||||
uint32_t e_entry;
|
||||
uint32_t e_phoff;
|
||||
uint32_t e_shoff;
|
||||
uint32_t e_flags;
|
||||
uint16_t e_ehsize;
|
||||
uint16_t e_phentsize;
|
||||
uint16_t e_phnum;
|
||||
uint16_t e_shentsize;
|
||||
uint16_t e_shnum;
|
||||
uint16_t e_shstrndx;
|
||||
} Elf32_Ehdr;
|
||||
|
||||
#define ELF32_HAS_MAGIC(ehdr) \
|
||||
((ehdr)->e_ident[EI_MAG0] == '\x7F' && (ehdr)->e_ident[EI_MAG1] == 'E' && (ehdr)->e_ident[EI_MAG2] == 'L' && \
|
||||
(ehdr)->e_ident[EI_MAG3] == 'F')
|
||||
|
||||
#define ELF32_IS_32(ehdr) ((ehdr)->e_ident[EI_CLASS] == 1 /*EI_CLASS_32*/)
|
||||
|
||||
#define ELF32_IS_BE(ehdr) ((ehdr)->e_ident[EI_DATA] == 2 /*EI_DATA_BE*/)
|
||||
|
||||
typedef struct {
|
||||
uint32_t sh_name;
|
||||
uint32_t sh_type;
|
||||
uint32_t sh_flags;
|
||||
uint32_t sh_addr;
|
||||
uint32_t sh_offset;
|
||||
uint32_t sh_size;
|
||||
uint32_t sh_link;
|
||||
uint32_t sh_info;
|
||||
uint32_t sh_addralign;
|
||||
uint32_t sh_entsize;
|
||||
} Elf32_Shdr;
|
||||
|
||||
typedef struct {
|
||||
uint32_t st_name;
|
||||
uint32_t st_value;
|
||||
uint32_t st_size;
|
||||
uint8_t st_info;
|
||||
uint8_t st_other;
|
||||
uint16_t st_shndx;
|
||||
} Elf32_Sym;
|
||||
|
||||
// sh_type
|
||||
|
||||
#define SHT_NULL 0x00000000
|
||||
#define SHT_PROGBITS 0x00000001
|
||||
#define SHT_SYMTAB 0x00000002
|
||||
#define SHT_STRTAB 0x00000003
|
||||
#define SHT_RELA 0x00000004
|
||||
#define SHT_HASH 0x00000005
|
||||
#define SHT_DYNAMIC 0x00000006
|
||||
#define SHT_NOTE 0x00000007
|
||||
#define SHT_NOBITS 0x00000008
|
||||
#define SHT_REL 0x00000009
|
||||
#define SHT_SHLIB 0x0000000A
|
||||
#define SHT_DYNSYM 0x0000000B
|
||||
#define SHT_INIT_ARRAY 0x0000000E
|
||||
#define SHT_FINI_ARRAY 0x0000000F
|
||||
#define SHT_PREINIT_ARRAY 0x00000010
|
||||
#define SHT_GROUP 0x00000011
|
||||
#define SHT_SYMTAB_SHNDX 0x00000012
|
||||
#define SHT_NUM 0x00000013
|
||||
#define SHT_LOOS 0x60000000
|
||||
// MIPS specific
|
||||
#define SHT_MIPS_DEBUG 0x70000005
|
||||
#define SHT_MIPS_REGINFO 0x70000006
|
||||
#define SHT_MIPS_OPTIONS 0x7000000D
|
||||
|
||||
// st_shndx
|
||||
|
||||
#define SHN_UND 0x0000
|
||||
#define SHN_ABS 0xFFF1
|
||||
#define SHN_COMMON 0xFFF2
|
||||
#define SHN_LORESERVE 0xFF00
|
||||
#define SHN_XINDEX 0xFFFF
|
||||
|
||||
// st_info [3:0]
|
||||
|
||||
#define ST_NOTYPE 0
|
||||
#define ST_OBJECT 1
|
||||
#define ST_FUNC 2
|
||||
#define ST_SECTION 3
|
||||
#define ST_FILE 4
|
||||
|
||||
// st_info [7:4]
|
||||
|
||||
#define SB_LOCAL 0
|
||||
#define SB_GLOBAL 1
|
||||
#define SB_WEAK 2
|
||||
|
||||
#define ELF32_ERR_PREFIX "[ELF32] "
|
||||
|
||||
ELF32_QUALIFIERS void
|
||||
validate_read(size_t offset, size_t size, size_t data_size)
|
||||
{
|
||||
if (offset + size > data_size)
|
||||
error(ELF32_ERR_PREFIX "Could not read %ld bytes at %08lX", size, offset);
|
||||
}
|
||||
|
||||
ELF32_QUALIFIERS void *
|
||||
elf32_read(const char *path, size_t *data_size_out)
|
||||
{
|
||||
size_t data_size;
|
||||
void *data = util_read_whole_file(path, &data_size);
|
||||
if (data == NULL)
|
||||
error(ELF32_ERR_PREFIX "File is empty?");
|
||||
|
||||
validate_read(0, sizeof(Elf32_Ehdr), data_size);
|
||||
|
||||
Elf32_Ehdr *ehdr = GET_PTR(data, 0);
|
||||
|
||||
if (!ELF32_HAS_MAGIC(ehdr))
|
||||
error(ELF32_ERR_PREFIX "Not an ELF file?");
|
||||
if (!ELF32_IS_32(ehdr))
|
||||
error(ELF32_ERR_PREFIX "Not ELF32?");
|
||||
if (!ELF32_IS_BE(ehdr))
|
||||
error(ELF32_ERR_PREFIX "Not big-endian?");
|
||||
|
||||
*data_size_out = data_size;
|
||||
return data;
|
||||
}
|
||||
|
||||
ELF32_QUALIFIERS Elf32_Shdr *
|
||||
elf32_get_symtab(void *data, size_t data_size)
|
||||
{
|
||||
Elf32_Ehdr *ehdr = GET_PTR(data, 0);
|
||||
uint32_t e_shoff = elf32_read32(ehdr->e_shoff);
|
||||
uint16_t e_shnum = elf32_read16(ehdr->e_shnum);
|
||||
|
||||
Elf32_Shdr *shdr = GET_PTR(data, e_shoff);
|
||||
for (size_t i = 0; i < e_shnum; i++, shdr++) {
|
||||
validate_read(e_shoff + i * sizeof(Elf32_Shdr), sizeof(Elf32_Shdr), data_size);
|
||||
|
||||
if (elf32_read32(shdr->sh_type) == SHT_SYMTAB) {
|
||||
// there should be only one section of this type
|
||||
return shdr;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ELF32_QUALIFIERS Elf32_Shdr *
|
||||
ef32_section_foridx(size_t idx, void *data, size_t data_size)
|
||||
{
|
||||
Elf32_Ehdr *ehdr = GET_PTR(data, 0);
|
||||
uint32_t e_shoff = elf32_read32(ehdr->e_shoff);
|
||||
uint16_t e_shnum = elf32_read16(ehdr->e_shnum);
|
||||
Elf32_Shdr *shdr = GET_PTR(data, e_shoff);
|
||||
|
||||
if (idx >= e_shnum)
|
||||
return NULL;
|
||||
|
||||
validate_read(e_shoff + idx * sizeof(Elf32_Shdr), sizeof(Elf32_Shdr), data_size);
|
||||
return &shdr[idx];
|
||||
}
|
||||
|
||||
ELF32_QUALIFIERS Elf32_Shdr *
|
||||
elf32_get_shstrtab(void *data, size_t data_size)
|
||||
{
|
||||
Elf32_Ehdr *ehdr = GET_PTR(data, 0);
|
||||
return ef32_section_foridx(elf32_read16(ehdr->e_shstrndx), data, data_size);
|
||||
}
|
||||
|
||||
ELF32_QUALIFIERS const char *
|
||||
elf32_get_string(size_t offset, Elf32_Shdr *strtab, void *data, size_t data_size)
|
||||
{
|
||||
uint32_t sh_offset = elf32_read32(strtab->sh_offset);
|
||||
|
||||
validate_read(sh_offset + offset, 1, data_size);
|
||||
return (const char *)GET_PTR(data, sh_offset + offset);
|
||||
}
|
||||
|
||||
ELF32_QUALIFIERS Elf32_Shdr *
|
||||
elf32_section_forname(const char *name, Elf32_Shdr *shstrtab, void *data, size_t data_size)
|
||||
{
|
||||
Elf32_Ehdr *ehdr = GET_PTR(data, 0);
|
||||
uint32_t e_shoff = elf32_read32(ehdr->e_shoff);
|
||||
uint16_t e_shnum = elf32_read16(ehdr->e_shnum);
|
||||
|
||||
Elf32_Shdr *shdr = GET_PTR(data, e_shoff);
|
||||
for (size_t i = 0; i < e_shnum; i++, shdr++) {
|
||||
validate_read(e_shoff + i * sizeof(Elf32_Shdr), sizeof(Elf32_Shdr), data_size);
|
||||
|
||||
const char *s_name = elf32_get_string(elf32_read32(shdr->sh_name), shstrtab, data, data_size);
|
||||
if (strequ(s_name, name)) {
|
||||
return shdr;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ELF32_QUALIFIERS Elf32_Shdr *
|
||||
elf32_get_strtab(void *data, size_t data_size)
|
||||
{
|
||||
return elf32_section_forname(".strtab", elf32_get_shstrtab(data, data_size), data, data_size);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1252,7 +1252,7 @@ vadpcm_dec(container_data *ctnr, UNUSED const codec_spec *codec, const enc_dec_o
|
|||
assert(memcmp(input, encoded, frame_size) == 0);
|
||||
} else {
|
||||
fails++;
|
||||
error("FAIL [%d/%d]\n", cur_pos, nSamples);
|
||||
error("FAIL [%d/%d]", cur_pos, nSamples);
|
||||
}
|
||||
|
||||
// Bring the match closer to the original decode (not strictly
|
||||
|
@ -1284,7 +1284,7 @@ vadpcm_dec(container_data *ctnr, UNUSED const codec_spec *codec, const enc_dec_o
|
|||
}
|
||||
|
||||
if (fails != 0)
|
||||
error("Decoding failures: %d\n", fails);
|
||||
error("Decoding failures: %d", fails);
|
||||
|
||||
// Convert VADPCM loop to regular loop, if it exists
|
||||
|
||||
|
|
|
@ -486,7 +486,7 @@ aiff_aifc_common_read(container_data *out, FILE *in, UNUSED bool matching, uint3
|
|||
long read_size = ftell(in) - start - 8;
|
||||
|
||||
if (read_size > chunk_size)
|
||||
error("overran chunk: %lu vs %u\n", read_size, chunk_size);
|
||||
error("overran chunk: %lu vs %u", read_size, chunk_size);
|
||||
else if (read_size < chunk_size)
|
||||
warning("did not read entire %.*s chunk: %lu vs %u", 4, cc4, read_size, chunk_size);
|
||||
|
||||
|
@ -589,7 +589,7 @@ aiff_aifc_common_write(container_data *in, const char *path, bool aifc, bool mat
|
|||
{
|
||||
FILE *out = fopen(path, "wb");
|
||||
if (out == NULL)
|
||||
error("Failed to open %s for writing\n", path);
|
||||
error("Failed to open %s for writing", path);
|
||||
|
||||
const char *aifc_head = "FORM\0\0\0\0AIFC";
|
||||
const char *aiff_head = "FORM\0\0\0\0AIFF";
|
||||
|
|
57
tools/audio/sfpatch.c
Normal file
57
tools/audio/sfpatch.c
Normal file
|
@ -0,0 +1,57 @@
|
|||
/* SPDX-FileCopyrightText: Copyright (C) 2024 ZeldaRET */
|
||||
/* SPDX-License-Identifier: CC0-1.0 */
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "elf32.h"
|
||||
#include "util.h"
|
||||
|
||||
/**
|
||||
* Converts symbols defined in an ELF file to ABS symbols so their values remain
|
||||
* unchanged after linking against them. This is used for soundfonts as references
|
||||
* to symbols defined in the soundfont should remain file-relative even after the
|
||||
* final link.
|
||||
*/
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
if (argc < 3) {
|
||||
fprintf(stderr, "Usage: %s in.elf out.elf\n", argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// read input elf file
|
||||
|
||||
size_t data_size;
|
||||
void *data = elf32_read(argv[1], &data_size);
|
||||
|
||||
// locate symtab
|
||||
|
||||
Elf32_Shdr *symtab = elf32_get_symtab(data, data_size);
|
||||
if (symtab == NULL)
|
||||
error("Symtab not found");
|
||||
|
||||
uint32_t sh_offset = elf32_read32(symtab->sh_offset);
|
||||
uint32_t sh_size = elf32_read32(symtab->sh_size);
|
||||
|
||||
// patch defined symbols to be ABS
|
||||
|
||||
Elf32_Sym *sym = GET_PTR(data, sh_offset);
|
||||
Elf32_Sym *sym_end = GET_PTR(data, sh_offset + sh_size);
|
||||
|
||||
for (size_t i = 0; sym < sym_end; sym++, i++) {
|
||||
validate_read(sh_offset + i * sizeof(Elf32_Sym), sizeof(Elf32_Sym), data_size);
|
||||
|
||||
if (elf32_read16(sym->st_shndx) != SHN_UND)
|
||||
sym->st_shndx = elf32_write16(SHN_ABS);
|
||||
}
|
||||
|
||||
// write output elf file
|
||||
|
||||
util_write_whole_file(argv[2], data, data_size);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
70
tools/audio/soundfont.c
Normal file
70
tools/audio/soundfont.c
Normal file
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
* 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 "soundfont.h"
|
||||
#include "xml.h"
|
||||
#include "util.h"
|
||||
|
||||
envelope_data *
|
||||
sf_get_envelope(soundfont *sf, const char *name)
|
||||
{
|
||||
LL_FOREACH(envelope_data *, env, sf->envelopes) {
|
||||
if (env->points != NULL && strequ(name, env->name))
|
||||
return env;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sample_data *
|
||||
sample_data_forname(soundfont *sf, const char *name)
|
||||
{
|
||||
LL_FOREACH(sample_data *, sample, sf->samples) {
|
||||
if (strequ(sample->name, name))
|
||||
return sample;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
read_soundfont_info(soundfont *sf, xmlNodePtr node)
|
||||
{
|
||||
static const xml_attr_spec spec = {
|
||||
{"Name", false, xml_parse_c_identifier, offsetof(soundfont, info.name) },
|
||||
{ "Index", false, xml_parse_int, offsetof(soundfont, info.index) },
|
||||
{ "Medium", false, xml_parse_c_identifier, offsetof(soundfont, info.medium) },
|
||||
{ "CachePolicy", false, xml_parse_c_identifier, offsetof(soundfont, info.cache_policy) },
|
||||
{ "SampleBank", false, xml_parse_string, offsetof(soundfont, info.bank_path) },
|
||||
{ "Indirect", true, xml_parse_int, offsetof(soundfont, info.pointer_index) },
|
||||
{ "SampleBankDD", true, xml_parse_string, offsetof(soundfont, info.bank_path_dd) },
|
||||
{ "IndirectDD", true, xml_parse_int, offsetof(soundfont, info.pointer_index_dd) },
|
||||
{ "LoopsHaveFrames", true, xml_parse_bool, offsetof(soundfont, info.loops_have_frames)},
|
||||
{ "NumInstruments", true, xml_parse_uint, offsetof(soundfont, info.num_instruments) },
|
||||
{ "PadToSize", true, xml_parse_uint, offsetof(soundfont, info.pad_to_size) },
|
||||
};
|
||||
sf->info.num_instruments = 0;
|
||||
sf->info.num_drums = 0;
|
||||
sf->info.num_effects = 0;
|
||||
sf->info.bank_path_dd = NULL;
|
||||
sf->info.pointer_index = -1;
|
||||
sf->info.pointer_index_dd = -1;
|
||||
sf->info.loops_have_frames = false;
|
||||
sf->info.pad_to_size = 0;
|
||||
xml_parse_node_by_spec(sf, node, spec, ARRAY_COUNT(spec));
|
||||
|
||||
xmlDocPtr sb_doc = xmlReadFile(sf->info.bank_path, NULL, XML_PARSE_NONET);
|
||||
if (sb_doc == NULL)
|
||||
error("Failed to read sample bank xml file \"%s\"", sf->info.bank_path);
|
||||
read_samplebank_xml(&sf->sb, sb_doc);
|
||||
|
||||
if (sf->info.bank_path_dd != NULL) {
|
||||
xmlDocPtr sbdd_doc = xmlReadFile(sf->info.bank_path_dd, NULL, XML_PARSE_NONET);
|
||||
if (sbdd_doc == NULL)
|
||||
error("Failed to read sample bank xml file \"%s\"", sf->info.bank_path);
|
||||
read_samplebank_xml(&sf->sbdd, sbdd_doc);
|
||||
}
|
||||
}
|
170
tools/audio/soundfont.h
Normal file
170
tools/audio/soundfont.h
Normal file
|
@ -0,0 +1,170 @@
|
|||
/**
|
||||
* 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/.
|
||||
*/
|
||||
#ifndef SOUNDFONT_H_
|
||||
#define SOUNDFONT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "aifc.h"
|
||||
#include "samplebank.h"
|
||||
|
||||
// special delay values
|
||||
#define ADSR_DISABLE 0
|
||||
#define ADSR_HANG -1
|
||||
#define ADSR_GOTO -2
|
||||
#define ADSR_RESTART -3
|
||||
|
||||
#define INSTR_LO_NONE 0
|
||||
#define INSTR_HI_NONE 127
|
||||
|
||||
typedef struct sample_data {
|
||||
struct sample_data *next;
|
||||
|
||||
const char *name;
|
||||
double sample_rate;
|
||||
int8_t base_note;
|
||||
bool is_dd;
|
||||
bool cached;
|
||||
aifc_data aifc;
|
||||
} sample_data;
|
||||
|
||||
typedef struct {
|
||||
int16_t delay;
|
||||
int16_t arg;
|
||||
} envelope_point;
|
||||
|
||||
typedef struct envelope_data {
|
||||
struct envelope_data *next;
|
||||
|
||||
const char *name;
|
||||
uint8_t release;
|
||||
envelope_point *points;
|
||||
size_t n_points;
|
||||
bool used;
|
||||
} envelope_data;
|
||||
|
||||
typedef struct instr_data {
|
||||
struct instr_data *next;
|
||||
|
||||
unsigned int program_number;
|
||||
const char *name;
|
||||
const char *envelope_name;
|
||||
|
||||
// for matching only
|
||||
bool unused;
|
||||
|
||||
// these are provided as-is for unused (name == NULL) otherwise they are read from the aifc file
|
||||
double sample_rate_mid;
|
||||
double sample_rate_lo;
|
||||
double sample_rate_hi;
|
||||
int8_t base_note_mid;
|
||||
int8_t base_note_lo;
|
||||
int8_t base_note_hi;
|
||||
|
||||
envelope_data *envelope;
|
||||
uint16_t release;
|
||||
const char *sample_name_low;
|
||||
const char *sample_name_mid;
|
||||
const char *sample_name_high;
|
||||
int8_t sample_low_end;
|
||||
int8_t sample_high_start;
|
||||
sample_data *sample_low;
|
||||
sample_data *sample_mid;
|
||||
sample_data *sample_high;
|
||||
float sample_low_tuning;
|
||||
float sample_mid_tuning;
|
||||
float sample_high_tuning;
|
||||
} instr_data;
|
||||
|
||||
typedef struct drum_data {
|
||||
struct drum_data *next;
|
||||
|
||||
const char *name;
|
||||
const char *sample_name;
|
||||
const char *envelope_name;
|
||||
envelope_data *envelope;
|
||||
uint16_t release;
|
||||
int8_t note;
|
||||
int8_t note_start;
|
||||
int8_t note_end;
|
||||
int pan;
|
||||
sample_data *sample;
|
||||
double sample_rate;
|
||||
int8_t base_note;
|
||||
} drum_data;
|
||||
|
||||
typedef struct sfx_data {
|
||||
struct sfx_data *next;
|
||||
|
||||
const char *name;
|
||||
const char *sample_name;
|
||||
sample_data *sample;
|
||||
double sample_rate;
|
||||
int8_t base_note;
|
||||
float tuning;
|
||||
} sfx_data;
|
||||
|
||||
typedef struct {
|
||||
bool matching;
|
||||
|
||||
struct {
|
||||
const char *name;
|
||||
const char *symbol;
|
||||
int index;
|
||||
const char *medium;
|
||||
const char *cache_policy;
|
||||
const char *bank_path;
|
||||
int pointer_index;
|
||||
const char *bank_path_dd;
|
||||
int pointer_index_dd;
|
||||
unsigned int pad_to_size;
|
||||
unsigned int num_instruments; // or the maximum program number (+1), since this also includes empty slots
|
||||
// between instruments
|
||||
unsigned int num_drums;
|
||||
unsigned int num_effects;
|
||||
bool loops_have_frames;
|
||||
} info;
|
||||
|
||||
uint32_t program_number_bitset[4];
|
||||
|
||||
envelope_data *envelopes;
|
||||
envelope_data *envelope_last;
|
||||
|
||||
samplebank sb;
|
||||
samplebank sbdd;
|
||||
|
||||
sample_data *samples;
|
||||
sample_data *sample_last;
|
||||
|
||||
instr_data *instruments;
|
||||
instr_data *instrument_last;
|
||||
|
||||
drum_data *drums;
|
||||
drum_data *drums_last;
|
||||
|
||||
sfx_data *sfx;
|
||||
sfx_data *sfx_last;
|
||||
|
||||
uint8_t *match_padding;
|
||||
size_t match_padding_num;
|
||||
} soundfont;
|
||||
|
||||
#define NOTE_UNSET (INT8_MIN)
|
||||
#define RELEASE_UNSET (UINT16_MAX)
|
||||
|
||||
envelope_data *
|
||||
sf_get_envelope(soundfont *sf, const char *name);
|
||||
|
||||
sample_data *
|
||||
sample_data_forname(soundfont *sf, const char *name);
|
||||
|
||||
void
|
||||
read_soundfont_info(soundfont *sf, xmlNodePtr node);
|
||||
|
||||
#endif
|
1814
tools/audio/soundfont_compiler.c
Normal file
1814
tools/audio/soundfont_compiler.c
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue