diff --git a/cmake_admin/FluidUnitTest.cmake b/cmake_admin/FluidUnitTest.cmake index afd80b6b..108ecd22 100644 --- a/cmake_admin/FluidUnitTest.cmake +++ b/cmake_admin/FluidUnitTest.cmake @@ -27,6 +27,32 @@ macro ( ADD_FLUID_TEST _test ) endmacro ( ADD_FLUID_TEST ) +macro ( ADD_FLUID_TEST_UTIL _util ) + ADD_EXECUTABLE(${_util} ${_util}.c $ ) + + # only build this unit test when explicitly requested by "make check" + set_target_properties(${_util} PROPERTIES EXCLUDE_FROM_ALL TRUE) + + # import necessary compile flags and dependency libraries + if ( FLUID_CPPFLAGS ) + set_target_properties ( ${_util} PROPERTIES COMPILE_FLAGS ${FLUID_CPPFLAGS} ) + endif ( FLUID_CPPFLAGS ) + TARGET_LINK_LIBRARIES(${_util} $) + + # use the local include path to look for fluidsynth.h, as we cannot be sure fluidsynth is already installed + target_include_directories(${_util} + PUBLIC + $ # include auto generated headers + $ # include "normal" public (sub-)headers + $ # include private headers + $ # include all other header search paths needed by libfluidsynth (esp. glib) + ) + + # append the current unit test to check-target as dependency + add_dependencies(check ${_util}) + +endmacro ( ADD_FLUID_TEST_UTIL ) + macro ( ADD_FLUID_DEMO _demo ) ADD_EXECUTABLE(${_demo} ${_demo}.c ) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 42658c6d..5d109523 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -30,6 +30,8 @@ ADD_FLUID_TEST(test_seq_event_queue_remove) ADD_FLUID_TEST(test_jack_obtaining_synth) ADD_FLUID_TEST(test_utf8_open) +ADD_FLUID_TEST_UTIL(dump_sfont) + if ( LIBSNDFILE_HASVORBIS ) ADD_FLUID_TEST(test_sf3_sfont_loading) endif ( LIBSNDFILE_HASVORBIS ) diff --git a/test/dump_sfont.c b/test/dump_sfont.c new file mode 100644 index 00000000..2ec768b7 --- /dev/null +++ b/test/dump_sfont.c @@ -0,0 +1,424 @@ +#include "test.h" +#include "fluidsynth.h" +#include "fluid_sfont.h" +#include "fluid_defsfont.h" +#include "fluid_sys.h" + +static void dump_sample(fluid_sample_t *sample); +static void dump_gens(const fluid_gen_t gen[]); +static void dump_mod(const fluid_mod_t *mod); +static void dump_preset_zone(fluid_preset_zone_t *zone); +static void dump_preset(fluid_preset_t *preset); +static void dump_inst_zone(fluid_inst_zone_t *zone); +static void dump_inst(fluid_inst_t *inst); +static void dump_defsfont(fluid_defsfont_t *defsfont); +static int inst_compare_func(void *a, void *b); +static fluid_list_t *collect_preset_insts(fluid_preset_t *preset, fluid_list_t *inst_list); + +#define FMT_BUFSIZE (4096) +static int indent_level = 0; +static FILE *output = NULL; + +static void fmt(const char *format, ...); +static void indent(void); +static void outdent(void); + + +int main(int argc, char **argv) +{ + int ret = FLUID_FAILED; + int id; + fluid_sfont_t *sfont; + fluid_defsfont_t *defsfont; + fluid_settings_t *settings; + fluid_synth_t *synth; + const char *adrivers[1] = { NULL }; + + if (argc < 2) + { + fprintf(stderr, "Usage:\n"); + fprintf(stderr, " dump_sfont [output_file]\n"); + return FLUID_FAILED; + } + + fluid_audio_driver_register(adrivers); + + settings = new_fluid_settings(); + if (settings == NULL) + { + return FLUID_FAILED; + } + + synth = new_fluid_synth(settings); + if (synth == NULL) + { + goto EXIT; + } + + id = fluid_synth_sfload(synth, argv[1], 1); + if (id < 0) + { + goto EXIT; + } + + sfont = fluid_synth_get_sfont_by_id(synth, id); + if (sfont == NULL) + { + goto EXIT; + } + + defsfont = (fluid_defsfont_t *)fluid_sfont_get_data(sfont); + if (defsfont == NULL) + { + goto EXIT; + } + + if (argc < 3) + { + output = stdout; + } + else + { + output = fopen(argv[2], "w"); + if (output == NULL) + { + fprintf(stderr, "Unable to open output file %s", argv[2]); + goto EXIT; + } + } + + dump_defsfont(defsfont); + + ret = FLUID_OK; + +EXIT: + if (output && output != stdout) + { + fclose(output); + } + delete_fluid_synth(synth); + delete_fluid_settings(settings); + + return ret; +} + +static void dump_sample(fluid_sample_t *sample) +{ + fmt("name: %s", sample->name); + fmt("source_start: %u", sample->source_start); + fmt("source_end: %u", sample->source_end); + fmt("source_loopstart: %u", sample->source_loopstart); + fmt("source_loopend: %u", sample->source_loopend); + + fmt("start: %u", sample->start); + fmt("end: %u", sample->end); + fmt("loopstart: %u", sample->loopstart); + fmt("loopend: %u", sample->loopend); + + fmt("samplerate: %u", sample->samplerate); + fmt("origpitch: %u", sample->origpitch); + fmt("pitchadj: %u", sample->pitchadj); + fmt("sampletype: %u", sample->sampletype); +} + +static void dump_gens(const fluid_gen_t gen[]) +{ + int i; + + /* only dump generators if at least one is set */ + for (i = 0; i < GEN_LAST; i++) + { + if (gen[i].flags) + { + break; + } + } + + if (i == GEN_LAST) + { + return; + } + + fmt("generators:"); + indent(); + for (i = 0; i < GEN_LAST; i++) + { + if (gen[i].flags) + { + fmt("%d: %.2f", i, gen[i].val); + } + } + outdent(); +} + +static void dump_mod(const fluid_mod_t *mod) +{ + fmt("dest: %u", mod->dest); + fmt("src1: %u", mod->src1); + fmt("flags1: %u", mod->flags1); + fmt("src2: %u", mod->src2); + fmt("flags2: %u", mod->flags2); + fmt("amount: %.2f", mod->amount); +} + +static void dump_preset_zone(fluid_preset_zone_t *zone) +{ + int i; + fluid_mod_t *mod; + + fmt("name: %s", zone->name); + if (zone->inst) + { + fmt("instrument: %s (index %d)", zone->inst->name, zone->inst->source_idx); + } + fmt("key_range: %d - %d", zone->range.keylo, zone->range.keyhi); + fmt("vel_range: %d - %d", zone->range.vello, zone->range.velhi); + dump_gens(zone->gen); + + if (zone->mod) + { + fmt("modulators:"); + for (i = 0, mod = zone->mod; mod; mod = mod->next, i++) + { + fmt("- modulator: %d", i); + indent(); + dump_mod(mod); + outdent(); + } + } +} + +static void dump_preset(fluid_preset_t *preset) +{ + int i; + fluid_preset_zone_t *zone; + + fluid_defpreset_t *defpreset = fluid_preset_get_data(preset); + if (defpreset == NULL) + { + return; + } + + fmt("name: %s", defpreset->name); + fmt("bank: %u", defpreset->bank); + fmt("num: %u", defpreset->num); + + if (defpreset->global_zone) + { + fmt("global_zone:"); + indent(); + dump_preset_zone(defpreset->global_zone); + outdent(); + } + + fmt("zones:"); + for (i = 0, zone = defpreset->zone; zone; zone = fluid_preset_zone_next(zone), i++) + { + fmt("- zone: %d", i); + if (zone == NULL) + { + continue; + } + + indent(); + dump_preset_zone(zone); + outdent(); + } +} + +static void dump_inst_zone(fluid_inst_zone_t *zone) +{ + int i; + fluid_mod_t *mod; + + fmt("name: %s", zone->name); + if (zone->sample) + { + fmt("sample: %s", zone->sample->name); + } + fmt("key_range: %d - %d", zone->range.keylo, zone->range.keyhi); + fmt("vel_range: %d - %d", zone->range.vello, zone->range.velhi); + dump_gens(zone->gen); + if (zone->mod) + { + fmt("modulators:"); + for (i = 0, mod = zone->mod; mod; mod = mod->next, i++) + { + fmt("- modulator: %d", i); + indent(); + dump_mod(mod); + outdent(); + } + } +} + +static void dump_inst(fluid_inst_t *inst) +{ + int i; + fluid_inst_zone_t *zone; + + fmt("name: %s", inst->name); + + if (inst->global_zone) + { + fmt("global_zone:"); + indent(); + dump_inst_zone(inst->global_zone); + outdent(); + } + + fmt("zones:"); + for (i = 0, zone = inst->zone; zone; zone = fluid_inst_zone_next(zone), i++) + { + fmt("- zone: %d", i); + if (zone == NULL) + { + continue; + } + + indent(); + dump_inst_zone(zone); + outdent(); + } +} + +static int inst_compare_func(void *a, void *b) +{ + const fluid_inst_t *inst_a = a; + const fluid_inst_t *inst_b = b; + + return inst_a->source_idx - inst_b->source_idx; +} + +static fluid_list_t *collect_preset_insts(fluid_preset_t *preset, fluid_list_t *inst_list) +{ + fluid_preset_zone_t *zone; + fluid_defpreset_t *defpreset = fluid_preset_get_data(preset); + if (defpreset == NULL) + { + return inst_list; + } + + if (defpreset->global_zone && defpreset->global_zone->inst && + fluid_list_idx(inst_list, defpreset->global_zone->inst) == -1) + { + inst_list = fluid_list_prepend(inst_list, defpreset->global_zone->inst); + } + + for (zone = defpreset->zone; zone; zone = fluid_preset_zone_next(zone)) + { + if (zone->inst && (fluid_list_idx(inst_list, zone->inst) == -1)) + { + inst_list = fluid_list_prepend(inst_list, zone->inst); + } + } + + return inst_list; +} + + +static void dump_defsfont(fluid_defsfont_t *defsfont) +{ + int i; + fluid_list_t *list; + fluid_sample_t *sample; + fluid_preset_t *preset; + fluid_inst_t *inst; + fluid_list_t *inst_list = NULL; + + fmt("samplepos: %u", defsfont->samplepos); + fmt("samplesize: %u", defsfont->samplesize); + fmt("sample24pos: %u", defsfont->sample24pos); + fmt("sample24size: %u", defsfont->sample24size); + + fmt("presets:"); + for (i = 0, list = defsfont->preset; list; list = fluid_list_next(list), i++) + { + preset = (fluid_preset_t *)fluid_list_get(list); + fmt("- preset: %d", i); + if (preset == NULL) + { + continue; + } + + indent(); + dump_preset(preset); + outdent(); + fmt(""); + + inst_list = collect_preset_insts(preset, inst_list); + } + + inst_list = fluid_list_sort(inst_list, (fluid_compare_func_t)inst_compare_func); + + fmt("instruments:"); + for (list = inst_list; list; list = fluid_list_next(list)) + { + inst = (fluid_inst_t *)fluid_list_get(list); + fmt("- instrument: %d", inst->source_idx); + indent(); + dump_inst(inst); + outdent(); + fmt(""); + } + + delete_fluid_list(inst_list); + + fmt("samples:"); + for (i = 0, list = defsfont->sample; list; list = fluid_list_next(list), i++) + { + sample = (fluid_sample_t *)fluid_list_get(list); + fmt("- sample: %d", i); + if (sample == NULL) + { + continue; + } + + indent(); + dump_sample(sample); + outdent(); + fmt(""); + } +} + + +static void fmt(const char *format, ...) +{ + char buf[FMT_BUFSIZE]; + va_list args; + int len; + int i; + + va_start(args, format); + len = FLUID_VSNPRINTF(buf, FMT_BUFSIZE, format, args); + va_end(args); + + if (len < 0) + { + FLUID_LOG(FLUID_ERR, "max buffer size exceeded"); + return; + } + + buf[FMT_BUFSIZE - 1] = '\0'; + + for (i = 0; i < indent_level; i++) + { + fprintf(output, " "); + } + + fwrite(buf, 1, FLUID_STRLEN(buf), output); + fprintf(output, "\n"); +} + +static void indent(void) +{ + indent_level += 1; +} + +static void outdent(void) +{ + if (indent_level > 0) + { + indent_level -= 1; + } +}