diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 54272cd8..78ab68d0 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -17,7 +17,7 @@ jobs: matrix: CC: [""] CXX: [""] - CMAKE_FLAGS: ["-Denable-profiling=1","-Denable-floats=1 -Denable-profiling=1","-Denable-floats=1","-Denable-trap-on-fpe=1","-Denable-fpe-check=1","-Denable-ipv6=0","-Denable-network=0","-Denable-aufile=0","-DBUILD_SHARED_LIBS=0","-Denable-ubsan=1"] + CMAKE_FLAGS: ["-Denable-profiling=1","-Denable-floats=1 -Denable-profiling=1","-Denable-floats=1","-Denable-trap-on-fpe=1","-Denable-fpe-check=1","-Denable-ipv6=0","-Denable-network=0","-Denable-aufile=0","-DBUILD_SHARED_LIBS=0","-Denable-ubsan=1 -Denable-debug=1"] include: - CC: "gcc-7" CXX: "g++-7" diff --git a/src/sfloader/fluid_sffile.c b/src/sfloader/fluid_sffile.c index a4b05569..931da8f9 100644 --- a/src/sfloader/fluid_sffile.c +++ b/src/sfloader/fluid_sffile.c @@ -112,71 +112,6 @@ static const uint32_t idlist[] = SM24_FCC }; -/* generator types */ -typedef enum -{ - Gen_StartAddrOfs, - Gen_EndAddrOfs, - Gen_StartLoopAddrOfs, - Gen_EndLoopAddrOfs, - Gen_StartAddrCoarseOfs, - Gen_ModLFO2Pitch, - Gen_VibLFO2Pitch, - Gen_ModEnv2Pitch, - Gen_FilterFc, - Gen_FilterQ, - Gen_ModLFO2FilterFc, - Gen_ModEnv2FilterFc, - Gen_EndAddrCoarseOfs, - Gen_ModLFO2Vol, - Gen_Unused1, - Gen_ChorusSend, - Gen_ReverbSend, - Gen_Pan, - Gen_Unused2, - Gen_Unused3, - Gen_Unused4, - Gen_ModLFODelay, - Gen_ModLFOFreq, - Gen_VibLFODelay, - Gen_VibLFOFreq, - Gen_ModEnvDelay, - Gen_ModEnvAttack, - Gen_ModEnvHold, - Gen_ModEnvDecay, - Gen_ModEnvSustain, - Gen_ModEnvRelease, - Gen_Key2ModEnvHold, - Gen_Key2ModEnvDecay, - Gen_VolEnvDelay, - Gen_VolEnvAttack, - Gen_VolEnvHold, - Gen_VolEnvDecay, - Gen_VolEnvSustain, - Gen_VolEnvRelease, - Gen_Key2VolEnvHold, - Gen_Key2VolEnvDecay, - Gen_Instrument, - Gen_Reserved1, - Gen_KeyRange, - Gen_VelRange, - Gen_StartLoopAddrCoarseOfs, - Gen_Keynum, - Gen_Velocity, - Gen_Attenuation, - Gen_Reserved2, - Gen_EndLoopAddrCoarseOfs, - Gen_CoarseTune, - Gen_FineTune, - Gen_SampleId, - Gen_SampleModes, - Gen_Reserved3, - Gen_ScaleTune, - Gen_ExclusiveClass, - Gen_OverrideRootKey, - Gen_Dummy -} Gen_Type; - #define Gen_MaxValid Gen_Dummy - 1 /* maximum valid generator */ #define Gen_Count Gen_Dummy /* count of generators */ #define GenArrSize sizeof(SFGenAmount) * Gen_Count /* gen array size */ @@ -303,11 +238,11 @@ static int process_pdta(SFData *sf, int size); static int load_phdr(SFData *sf, unsigned int size); static int load_pbag(SFData *sf, int size); static int load_pmod(SFData *sf, int size); -static int load_pgen(SFData *sf, int size); +extern int load_pgen(SFData *sf, int size); static int load_ihdr(SFData *sf, unsigned int size); static int load_ibag(SFData *sf, int size); static int load_imod(SFData *sf, int size); -static int load_igen(SFData *sf, int size); +extern int load_igen(SFData *sf, int size); static int load_shdr(SFData *sf, unsigned int size); static int fixup_pgen(SFData *sf); static int fixup_igen(SFData *sf); @@ -320,11 +255,6 @@ static fluid_list_t *find_gen_by_id(int gen, fluid_list_t *genlist); static int valid_inst_genid(unsigned short genid); static int valid_preset_genid(unsigned short genid); - -static void delete_preset(SFPreset *preset); -static void delete_inst(SFInst *inst); -static void delete_zone(SFZone *zone); - static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigned int end_byte, short **data); static int fluid_sffile_read_wav(SFData *sf, unsigned int start, unsigned int end, short **data, char **data24); @@ -1366,7 +1296,7 @@ static int load_pmod(SFData *sf, int size) * if a generator follows an instrument discard it * if a duplicate generator exists replace previous one * ------------------------------------------------------------------- */ -static int load_pgen(SFData *sf, int size) +int load_pgen(SFData *sf, int size) { fluid_list_t *dup, **hz = NULL; fluid_list_t *preset_list; @@ -1885,7 +1815,7 @@ static int load_imod(SFData *sf, int size) } /* load instrument generators (see load_pgen for loading rules) */ -static int load_igen(SFData *sf, int size) +int load_igen(SFData *sf, int size) { fluid_list_t *dup, **hz = NULL; fluid_list_t *inst_list; @@ -2249,7 +2179,7 @@ static int fixup_igen(SFData *sf) return TRUE; } -static void delete_preset(SFPreset *preset) +void delete_preset(SFPreset *preset) { fluid_list_t *entry; SFZone *zone; @@ -2273,7 +2203,7 @@ static void delete_preset(SFPreset *preset) FLUID_FREE(preset); } -static void delete_inst(SFInst *inst) +void delete_inst(SFInst *inst) { fluid_list_t *entry; SFZone *zone; @@ -2299,7 +2229,7 @@ static void delete_inst(SFInst *inst) /* Free all elements of a zone (Preset or Instrument) */ -static void delete_zone(SFZone *zone) +void delete_zone(SFZone *zone) { fluid_list_t *entry; diff --git a/src/sfloader/fluid_sffile.h b/src/sfloader/fluid_sffile.h index 8dc53c79..b1574c3f 100644 --- a/src/sfloader/fluid_sffile.h +++ b/src/sfloader/fluid_sffile.h @@ -228,4 +228,77 @@ int fluid_sffile_parse_presets(SFData *sf); int fluid_sffile_read_sample_data(SFData *sf, unsigned int sample_start, unsigned int sample_end, int sample_type, short **data, char **data24); + +/* extern only for unit test purposes */ +int load_igen(SFData *sf, int size); +int load_pgen(SFData *sf, int size); +void delete_preset(SFPreset *preset); +void delete_inst(SFInst *inst); +void delete_zone(SFZone *zone); + +/* generator types */ +typedef enum +{ + Gen_StartAddrOfs, + Gen_EndAddrOfs, + Gen_StartLoopAddrOfs, + Gen_EndLoopAddrOfs, + Gen_StartAddrCoarseOfs, + Gen_ModLFO2Pitch, + Gen_VibLFO2Pitch, + Gen_ModEnv2Pitch, + Gen_FilterFc, + Gen_FilterQ, + Gen_ModLFO2FilterFc, + Gen_ModEnv2FilterFc, + Gen_EndAddrCoarseOfs, + Gen_ModLFO2Vol, + Gen_Unused1, + Gen_ChorusSend, + Gen_ReverbSend, + Gen_Pan, + Gen_Unused2, + Gen_Unused3, + Gen_Unused4, + Gen_ModLFODelay, + Gen_ModLFOFreq, + Gen_VibLFODelay, + Gen_VibLFOFreq, + Gen_ModEnvDelay, + Gen_ModEnvAttack, + Gen_ModEnvHold, + Gen_ModEnvDecay, + Gen_ModEnvSustain, + Gen_ModEnvRelease, + Gen_Key2ModEnvHold, + Gen_Key2ModEnvDecay, + Gen_VolEnvDelay, + Gen_VolEnvAttack, + Gen_VolEnvHold, + Gen_VolEnvDecay, + Gen_VolEnvSustain, + Gen_VolEnvRelease, + Gen_Key2VolEnvHold, + Gen_Key2VolEnvDecay, + Gen_Instrument, + Gen_Reserved1, + Gen_KeyRange, + Gen_VelRange, + Gen_StartLoopAddrCoarseOfs, + Gen_Keynum, + Gen_Velocity, + Gen_Attenuation, + Gen_Reserved2, + Gen_EndLoopAddrCoarseOfs, + Gen_CoarseTune, + Gen_FineTune, + Gen_SampleId, + Gen_SampleModes, + Gen_Reserved3, + Gen_ScaleTune, + Gen_ExclusiveClass, + Gen_OverrideRootKey, + Gen_Dummy +} Gen_Type; + #endif /* _FLUID_SFFILE_H */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f8847cf3..42658c6d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -22,6 +22,7 @@ ADD_FLUID_TEST(test_synth_process) ADD_FLUID_TEST(test_ct2hz) ADD_FLUID_TEST(test_sample_validate) ADD_FLUID_TEST(test_sfont_unloading) +ADD_FLUID_TEST(test_sfont_zone) ADD_FLUID_TEST(test_seq_event_queue_sort) ADD_FLUID_TEST(test_seq_scale) ADD_FLUID_TEST(test_seq_evt_order) diff --git a/test/test_sfont_zone.c b/test/test_sfont_zone.c new file mode 100644 index 00000000..b65c7c64 --- /dev/null +++ b/test/test_sfont_zone.c @@ -0,0 +1,472 @@ + +#include "test.h" +#include "fluidsynth.h" +#include "sfloader/fluid_sfont.h" +#include "sfloader/fluid_defsfont.h" +#include "sfloader/fluid_sffile.h" +#include "utils/fluid_sys.h" + + +#define SET_BUF2(START, SIZE) \ + do \ + { \ + file_buf = START; \ + file_end = (START) + (SIZE); \ + } while (0) +#define SET_BUF(BUF) SET_BUF2(BUF, FLUID_N_ELEMENTS(BUF)) +#define UNSET_BUF \ + do \ + { \ + file_buf = NULL; \ + file_end = NULL; \ + } while (0) + + +typedef struct +{ + // pointer to the start of the file_buf + const unsigned char *start; + // actual size of the buffer + unsigned int size; + // expected end address of the buffer + const unsigned char *end; +} buf_t; + +static const unsigned char *file_buf = NULL; +static const unsigned char *file_end = NULL; +static int test_reader(void *buf, fluid_long_long_t count, void *h) +{ + if (file_buf + count > file_end) + { + return FLUID_FAILED; + } + FLUID_MEMCPY(buf, file_buf, count); + file_buf += count; + return FLUID_OK; +} + +static int test_seek(void *handle, fluid_long_long_t offset, int origin) +{ + if (origin == SEEK_CUR) + { + file_buf += offset; + if (file_buf > file_end) + { + return FLUID_FAILED; + } + return FLUID_OK; + } + + // shouldn't happen? + TEST_ASSERT(0); +} + +static const fluid_file_callbacks_t fcb = +{ + NULL, &test_reader, &test_seek, NULL, NULL +}; + +static SFZone* new_test_zone(fluid_list_t** parent_list, int gen_count) +{ + int i; + SFZone *zone = FLUID_NEW(SFZone); + TEST_ASSERT(zone != NULL); + FLUID_MEMSET(zone, 0, sizeof(*zone)); + + for (i = 0; i < gen_count; i++) + { + zone->gen = fluid_list_prepend(zone->gen, NULL); + } + + if(parent_list != NULL) + { + *parent_list = fluid_list_append(*parent_list, zone); + } + + return zone; +} + +// test the good case first: one zone, with two generators and one terminal generator +static void good_test_1zone_2gen_1termgen(int (*load_func)(SFData *sf, int size), SFData* sf, SFZone *zone) +{ + const SFGen *gen; + static const unsigned char buf[] = + { + Gen_KeyRange, 0, 60, 127, Gen_VelRange, 0, 60, 127, 0, 0, 0, 0 + }; + SET_BUF(buf); + TEST_ASSERT(load_func(sf, FLUID_N_ELEMENTS(buf))); + + gen = fluid_list_get(fluid_list_nth(zone->gen, 0)); + TEST_ASSERT(gen != NULL); + TEST_ASSERT(gen->id == Gen_KeyRange); + TEST_ASSERT(gen->amount.range.lo == 60); + TEST_ASSERT(gen->amount.range.hi == 127); + + gen = fluid_list_get(fluid_list_nth(zone->gen, 1)); + TEST_ASSERT(gen != NULL); + TEST_ASSERT(gen->id == Gen_VelRange); + TEST_ASSERT(gen->amount.range.lo == 60); + TEST_ASSERT(gen->amount.range.hi == 127); + + TEST_ASSERT(file_buf == buf + sizeof(buf)); + UNSET_BUF; +} + +// bad case: too few generators in buffer, triggering a chunk size mismatch +static void bad_test_too_short_gen_buffer(int (*load_func)(SFData *sf, int size), SFData *sf, SFZone *zone) +{ + const Gen_Type final_gen = (load_func == &load_pgen) ? Gen_Instrument : Gen_SampleId; + SFGen *gen; + unsigned int i; + static const unsigned char buf1[] = { Gen_KeyRange, 0, 0 }; + static const unsigned char buf2[] = { Gen_KeyRange, 0 }; + static const unsigned char buf3[] = { Gen_KeyRange }; + static const unsigned char buf8[] = { Gen_VelRange, 0, 0 }; + static const unsigned char buf9[] = { Gen_VelRange, 0 }; + static const unsigned char buf10[] = { Gen_VelRange }; + static const unsigned char buf4[] = { Gen_VelRange, 0, 0, 127, Gen_CoarseTune, 0, 4 }; + static const unsigned char buf5[] = { Gen_VelRange, 0, 0, 127, Gen_CoarseTune, 0 }; + static const unsigned char buf6[] = { Gen_VelRange, 0, 0, 127, Gen_CoarseTune }; + const unsigned char buf11[] = { Gen_VelRange, 0, 0, 127, final_gen, 0, 4 }; + const unsigned char buf12[] = { Gen_VelRange, 0, 0, 127, final_gen, 0 }; + const unsigned char buf13[] = { Gen_VelRange, 0, 0, 127, final_gen }; + static const unsigned char buf7[] = { Gen_KeyRange, 0, 60, 127, Gen_OverrideRootKey }; + + static const buf_t buf_with_one_gen[] = + { + { buf1, sizeof(buf1), buf1 + sizeof(buf1) }, + { buf2, sizeof(buf2),buf2 + sizeof(buf2) }, + { buf3, sizeof(buf3), buf3 }, + { buf8, sizeof(buf8), buf8 + sizeof(buf8) }, + { buf9, sizeof(buf9), buf9 + sizeof(buf9) }, + { buf10, sizeof(buf10), buf10 } + }; + + const buf_t buf_with_two_gen[] = + { + { buf4, sizeof(buf4), buf4 + sizeof(buf4) -1 }, + { buf5, sizeof(buf5), buf5 + sizeof(buf5) }, + { buf6, sizeof(buf6), buf6 + sizeof(buf6) - 1 }, + { buf11, sizeof(buf11), buf11 + sizeof(buf11) - 1 }, + { buf12, sizeof(buf12), buf12 + sizeof(buf12) }, + { buf13, sizeof(buf13), buf13 + sizeof(buf13) -1} + }; + + for (i = 0; i < FLUID_N_ELEMENTS(buf_with_one_gen); i++) + { + SET_BUF2(buf_with_one_gen[i].start, buf_with_one_gen[i].size); + TEST_ASSERT(load_func(sf, 8 /* pretend that our input buffer is big enough, to make it fail in the fcbs later */) == FALSE); + gen = fluid_list_get(fluid_list_nth(zone->gen, 0)); + TEST_ASSERT(gen == NULL); + TEST_ASSERT(file_buf == buf_with_one_gen[i].end); + UNSET_BUF; + } + + for (i = 0; i < FLUID_N_ELEMENTS(buf_with_two_gen); i++) + { + SET_BUF2(buf_with_two_gen[i].start, buf_with_two_gen[i].size); + TEST_ASSERT(load_func(sf, 8) == FALSE); + gen = fluid_list_get(fluid_list_nth(zone->gen, 0)); + TEST_ASSERT(gen != NULL); + FLUID_FREE(gen); + zone->gen->data = NULL; + gen = fluid_list_get(fluid_list_nth(zone->gen, 1)); + TEST_ASSERT(gen == NULL); + TEST_ASSERT(file_buf == buf_with_two_gen[i].end); + UNSET_BUF; + } + + SET_BUF(buf7); + TEST_ASSERT(load_func(sf, FLUID_N_ELEMENTS(buf7)) == FALSE); + gen = fluid_list_get(fluid_list_nth(zone->gen, 0)); + TEST_ASSERT(gen != NULL); + TEST_ASSERT(gen->id == Gen_KeyRange); + TEST_ASSERT(gen->amount.range.lo == 60); + TEST_ASSERT(gen->amount.range.hi == 127); + + TEST_ASSERT(file_buf == buf7 + sizeof(buf7) - 1); + UNSET_BUF; +} + +// bad case: one zone, with two similar generators +static void bad_test_duplicate_gen(int (*load_func)(SFData *sf, int size), SFData *sf, SFZone *zone) +{ + const SFGen *gen; + static const unsigned char buf[] = { Gen_CoarseTune, 0, 5, 0, Gen_CoarseTune, 0, 10, 0 }; + + SET_BUF(buf); + TEST_ASSERT(load_func(sf, FLUID_N_ELEMENTS(buf))); + + gen = fluid_list_get(fluid_list_nth(zone->gen, 0)); + TEST_ASSERT(gen != NULL); + TEST_ASSERT(gen->id == Gen_CoarseTune); + TEST_ASSERT(gen->amount.range.lo == 10); + TEST_ASSERT(gen->amount.range.hi == 0); + + gen = fluid_list_get(fluid_list_nth(zone->gen, 1)); + TEST_ASSERT(gen == NULL); + + TEST_ASSERT(file_buf == buf + sizeof(buf)); + UNSET_BUF; +} + +// bad case: with one zone, generators in wrong order +static void bad_test_gen_wrong_order(int (*load_func)(SFData *sf, int size), SFData *sf, SFZone *zone) +{ + const SFGen *gen; + static const unsigned char buf[] = + { + Gen_VelRange, 0, 60, 127, + Gen_KeyRange, 0, 60, 127, + Gen_Instrument, 0, 0xDD, 0xDD + }; + SET_BUF(buf); + TEST_ASSERT(load_func(sf, FLUID_N_ELEMENTS(buf))); + + gen = fluid_list_get(fluid_list_nth(zone->gen, 0)); + TEST_ASSERT(gen != NULL); + TEST_ASSERT(gen->id == Gen_VelRange); + TEST_ASSERT(gen->amount.range.lo == 60); + TEST_ASSERT(gen->amount.range.hi == 127); + + // The INSTRUMENT generator is mistakenly accepted by load_igen. This will be fixed by Marcus' PR. + // Once merge, this if clause should be removed. + if (load_func != &load_igen) + { + gen = fluid_list_get(fluid_list_nth(zone->gen, 1)); + TEST_ASSERT(gen == NULL); + } + + gen = fluid_list_get(fluid_list_nth(zone->gen, 2)); + TEST_ASSERT(gen == NULL); + + if (load_func == &load_pgen) + { + TEST_ASSERT(FLUID_POINTER_TO_UINT(zone->instsamp) == 0xDDDD + 1); + zone->instsamp = NULL; + } + + TEST_ASSERT(file_buf == buf + sizeof(buf)); + UNSET_BUF; +} + +// This test-case is derived from the invalid SoundFont provided in #808 +static void bad_test_issue_808(int (*load_func)(SFData *sf, int size), SFData *sf, SFZone *zone1) +{ + const SFGen *gen; + static const unsigned char buf[] = + { + // zone 1 + Gen_ReverbSend, 0, 50, 0, + Gen_VolEnvRelease, 0, 206, 249, + // zone 2 + Gen_KeyRange, 0, 0, 35, + Gen_OverrideRootKey, 0, 43, 0, + Gen_StartAddrCoarseOfs, 0, 0, 0, + Gen_SampleModes, 0, 1, 0, + Gen_StartAddrOfs, 0, 0, 0 + }; + + SET_BUF(buf); + TEST_ASSERT(load_func(sf, FLUID_N_ELEMENTS(buf))); + + gen = fluid_list_get(fluid_list_nth(zone1->gen, 0)); + TEST_ASSERT(gen != NULL); + TEST_ASSERT(gen->id == Gen_ReverbSend); + TEST_ASSERT(gen->amount.range.lo == 50); + TEST_ASSERT(gen->amount.range.hi == 0); + + gen = fluid_list_get(fluid_list_nth(zone1->gen, 1)); + TEST_ASSERT(gen != NULL); + TEST_ASSERT(gen->id == Gen_VolEnvRelease); + TEST_ASSERT(gen->amount.range.lo == 206); + TEST_ASSERT(gen->amount.range.hi == 249); + + gen = fluid_list_get(fluid_list_nth(zone1->gen, 2)); + TEST_ASSERT(gen == NULL); + + TEST_ASSERT(file_buf == buf + sizeof(buf)); + UNSET_BUF; +} + +// This test-case has a single zone which has additional generators after the final generator, while some of them are incomplete and others still have an extra (maybe incomplete) terminal gen. +static void bad_test_additional_gens_after_final_gen(int (*load_func)(SFData *sf, int size), SFData *sf, SFZone *zone1) +{ + unsigned int i; + SFGen *gen; + const Gen_Type final_gen = (load_func == &load_pgen) ? Gen_Instrument : Gen_SampleId; + + const unsigned char buf1[] = + { + // zone 1 + Gen_KeyRange, 0, 60, 127, + Gen_Unused1, 0, 0xFF, 0xFF, + final_gen, 0, 0xDD, 0xDD, + Gen_KeyRange, 0, 0, 35, + Gen_OverrideRootKey, 0, 43, 0, + 0, 0, 0, 0 // terminal generator + }; + + const unsigned char buf2[] = + { + // zone 1 + Gen_KeyRange, 0, 60, 127, + Gen_Unused1, 0, 0xFF, 0xFF, + final_gen, 0, 0xDD, 0xDD, + Gen_KeyRange, 0, 0, 35, + Gen_OverrideRootKey, 0, 43, 0, + 0, 0, 0 // incomplete terminal generator + }; + + const unsigned char buf3[] = + { + // zone 1 + Gen_KeyRange, 0, 60, 127, + Gen_Unused1, 0, 0xFF, 0xFF, + final_gen, 0, 0xDD, 0xDD, + Gen_KeyRange, 0, 0, 35, + Gen_OverrideRootKey, 0, 43 + }; + + const unsigned char buf4[] = + { + // zone 1 + Gen_KeyRange, 0, 60, 127, + Gen_Unused1, 0, 0xFF, 0xFF, + final_gen, 0, 0xDD, 0xDD, + Gen_KeyRange, 0, 0, 35, + Gen_OverrideRootKey, 0 + }; + + const buf_t buf[] = + { + { buf1, sizeof(buf1), buf1 + sizeof(buf1) }, + { buf2, sizeof(buf2), buf2 + sizeof(buf2) - 3 }, + { buf3, sizeof(buf3), buf3 + sizeof(buf3) - 3 }, + { buf4, sizeof(buf4), buf4 + sizeof(buf4) - 2 }, + }; + + // the first test case should return true, all others false + int expected_ret_val = TRUE; + for (i = 0; i < FLUID_N_ELEMENTS(buf); i++) + { + SET_BUF2(buf[i].start, buf[i].size); + TEST_ASSERT(load_func(sf, buf[i].size) == expected_ret_val); + expected_ret_val = FALSE; + + gen = fluid_list_get(fluid_list_nth(zone1->gen, 0)); + TEST_ASSERT(gen != NULL); + TEST_ASSERT(gen->id == Gen_KeyRange); + TEST_ASSERT(gen->amount.range.lo == 60); + TEST_ASSERT(gen->amount.range.hi == 127); + + // delete this generator + FLUID_FREE(gen); + zone1->gen->data = NULL; + + gen = fluid_list_get(fluid_list_nth(zone1->gen, 1)); + TEST_ASSERT(gen == NULL); + + gen = fluid_list_get(fluid_list_nth(zone1->gen, 2)); + TEST_ASSERT(gen == NULL); + + gen = fluid_list_get(fluid_list_nth(zone1->gen, 3)); + TEST_ASSERT(gen == NULL); + + gen = fluid_list_get(fluid_list_nth(zone1->gen, 4)); + TEST_ASSERT(gen == NULL); + + TEST_ASSERT(FLUID_POINTER_TO_UINT(zone1->instsamp) == 0xDDDD + 1); + zone1->instsamp = NULL; + + TEST_ASSERT(file_buf == buf[i].end); + UNSET_BUF; + + // The test cases above expect zone1 to be pre-populated with 5 generators + delete_fluid_list(zone1->gen); + zone1->gen = NULL; + zone1->gen = fluid_list_prepend(zone1->gen, NULL); + zone1->gen = fluid_list_prepend(zone1->gen, NULL); + zone1->gen = fluid_list_prepend(zone1->gen, NULL); + zone1->gen = fluid_list_prepend(zone1->gen, NULL); + zone1->gen = fluid_list_prepend(zone1->gen, NULL); + } +} + +int main(void) +{ + // prepare a soundfont that has one preset and one instrument, with up to 2 zones + + SFZone *zone1; + SFData *sf = FLUID_NEW(SFData); + SFPreset *preset = FLUID_NEW(SFPreset); + SFInst *inst = FLUID_NEW(SFInst); + + TEST_ASSERT(sf != NULL); + FLUID_MEMSET(sf, 0, sizeof(*sf)); + TEST_ASSERT(preset != NULL); + FLUID_MEMSET(preset, 0, sizeof(*preset)); + TEST_ASSERT(inst != NULL); + FLUID_MEMSET(inst, 0, sizeof(*inst)); + + sf->fcbs = &fcb; + sf->preset = fluid_list_append(sf->preset, preset); + sf->inst = fluid_list_append(sf->inst, inst); + + // Calls the given test function for 1 zone once for preset and once for inst case. + #define TEST_CASE_1(TEST_FUNC, GEN_COUNT) \ + do \ + { \ + zone1 = new_test_zone(&preset->zone, GEN_COUNT); \ + TEST_FUNC(&load_pgen, sf, zone1); \ + delete_zone(zone1); \ + delete_fluid_list(preset->zone); \ + preset->zone = NULL; \ + \ + zone1 = new_test_zone(&inst->zone, GEN_COUNT); \ + TEST_FUNC(&load_igen, sf, zone1); \ + delete_zone(zone1); \ + delete_fluid_list(inst->zone); \ + inst->zone = NULL; \ + } while (0) + + TEST_CASE_1(good_test_1zone_2gen_1termgen, 2); + TEST_CASE_1(good_test_1zone_2gen_1termgen, 3); + + TEST_CASE_1(bad_test_too_short_gen_buffer, 2); + + TEST_CASE_1(bad_test_duplicate_gen, 2); + + TEST_CASE_1(bad_test_gen_wrong_order, 3); + + TEST_CASE_1(bad_test_additional_gens_after_final_gen, 5); + + zone1 = new_test_zone(&preset->zone, 2); + (void)new_test_zone(&preset->zone, 5); + bad_test_issue_808(&load_pgen, sf, zone1); + // zone 2 was dropped + TEST_ASSERT(preset->zone->next == NULL); + delete_zone(zone1); + // zone2 already deleted + delete_fluid_list(preset->zone); + preset->zone = NULL; + + zone1 = new_test_zone(&inst->zone, 2); + (void)new_test_zone(&inst->zone, 5); + bad_test_issue_808(&load_igen, sf, zone1); + // zone 2 was dropped + TEST_ASSERT(inst->zone->next == NULL); + delete_zone(zone1); + // zone2 already deleted + delete_fluid_list(inst->zone); + inst->zone = NULL; + + delete_inst(inst); + delete_preset(preset); + delete_fluid_list(sf->inst); + delete_fluid_list(sf->preset); + // we cannot call fluid_sffile_close here, because it would destroy the mutex which is not initialized + FLUID_FREE(sf); + return EXIT_SUCCESS; +}