1
0
Fork 0
mirror of https://github.com/zeldaret/oot.git synced 2025-07-03 22:44:30 +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:
Tharo 2024-08-28 02:09:59 +01:00 committed by GitHub
parent 17debe8620
commit aa97586659
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
40 changed files with 2775 additions and 87 deletions

View file

@ -1,4 +1,6 @@
__pycache__/
atblgen
sfpatch
sbc
sfc

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff