summaryrefslogtreecommitdiff
path: root/libs/fluidsynth/src/fluid_defsfont.c
diff options
context:
space:
mode:
Diffstat (limited to 'libs/fluidsynth/src/fluid_defsfont.c')
-rw-r--r--libs/fluidsynth/src/fluid_defsfont.c3432
1 files changed, 3432 insertions, 0 deletions
diff --git a/libs/fluidsynth/src/fluid_defsfont.c b/libs/fluidsynth/src/fluid_defsfont.c
new file mode 100644
index 0000000000..c395218411
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_defsfont.c
@@ -0,0 +1,3432 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * SoundFont file loading code borrowed from Smurf SoundFont Editor
+ * Copyright (C) 1999-2001 Josh Green
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+#include "fluid_defsfont.h"
+/* Todo: Get rid of that 'include' */
+#include "fluid_sys.h"
+
+/***************************************************************
+ *
+ * SFONT LOADER
+ */
+
+fluid_sfloader_t* new_fluid_defsfloader(fluid_settings_t* settings)
+{
+ fluid_sfloader_t* loader;
+
+ loader = FLUID_NEW(fluid_sfloader_t);
+ if (loader == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ loader->data = settings;
+ loader->free = delete_fluid_defsfloader;
+ loader->load = fluid_defsfloader_load;
+
+ return loader;
+}
+
+int delete_fluid_defsfloader(fluid_sfloader_t* loader)
+{
+ if (loader) {
+ FLUID_FREE(loader);
+ }
+ return FLUID_OK;
+}
+
+fluid_sfont_t* fluid_defsfloader_load(fluid_sfloader_t* loader, const char* filename)
+{
+ fluid_defsfont_t* defsfont;
+ fluid_sfont_t* sfont;
+
+ defsfont = new_fluid_defsfont(loader->data);
+
+ if (defsfont == NULL) {
+ return NULL;
+ }
+
+ if (fluid_defsfont_load(defsfont, filename) == FLUID_FAILED) {
+ delete_fluid_defsfont(defsfont);
+ return NULL;
+ }
+
+ sfont = FLUID_NEW(fluid_sfont_t);
+ if (sfont == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ sfont->data = defsfont;
+ sfont->free = fluid_defsfont_sfont_delete;
+ sfont->get_name = fluid_defsfont_sfont_get_name;
+ sfont->get_preset = fluid_defsfont_sfont_get_preset;
+ sfont->iteration_start = fluid_defsfont_sfont_iteration_start;
+ sfont->iteration_next = fluid_defsfont_sfont_iteration_next;
+
+ return sfont;
+}
+
+
+
+/***************************************************************
+ *
+ * PUBLIC INTERFACE
+ */
+
+int fluid_defsfont_sfont_delete(fluid_sfont_t* sfont)
+{
+ if (delete_fluid_defsfont(sfont->data) != 0) {
+ return -1;
+ }
+ FLUID_FREE(sfont);
+ return 0;
+}
+
+char* fluid_defsfont_sfont_get_name(fluid_sfont_t* sfont)
+{
+ return fluid_defsfont_get_name((fluid_defsfont_t*) sfont->data);
+}
+
+#if 0
+fluid_sample_t* fluid_defsfont_get_sample(fluid_defsfont_t* sfont, char *s)
+{
+ /* This function is here just to avoid an ABI/SONAME bump, see ticket #98. Should never be used. */
+ return NULL;
+}
+#endif
+
+fluid_preset_t*
+fluid_defsfont_sfont_get_preset(fluid_sfont_t* sfont, unsigned int bank, unsigned int prenum)
+{
+ fluid_preset_t* preset = NULL;
+ fluid_defpreset_t* defpreset;
+ fluid_defsfont_t* defsfont = sfont->data;
+
+ defpreset = fluid_defsfont_get_preset(defsfont, bank, prenum);
+
+ if (defpreset == NULL) {
+ return NULL;
+ }
+
+ if (defsfont->preset_stack_size > 0) {
+ defsfont->preset_stack_size--;
+ preset = defsfont->preset_stack[defsfont->preset_stack_size];
+ }
+ if (!preset)
+ preset = FLUID_NEW(fluid_preset_t);
+ if (!preset) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ preset->sfont = sfont;
+ preset->data = defpreset;
+ preset->free = fluid_defpreset_preset_delete;
+ preset->get_name = fluid_defpreset_preset_get_name;
+ preset->get_banknum = fluid_defpreset_preset_get_banknum;
+ preset->get_num = fluid_defpreset_preset_get_num;
+ preset->noteon = fluid_defpreset_preset_noteon;
+ preset->notify = NULL;
+
+ return preset;
+}
+
+void fluid_defsfont_sfont_iteration_start(fluid_sfont_t* sfont)
+{
+ fluid_defsfont_iteration_start((fluid_defsfont_t*) sfont->data);
+}
+
+int fluid_defsfont_sfont_iteration_next(fluid_sfont_t* sfont, fluid_preset_t* preset)
+{
+ preset->free = fluid_defpreset_preset_delete;
+ preset->get_name = fluid_defpreset_preset_get_name;
+ preset->get_banknum = fluid_defpreset_preset_get_banknum;
+ preset->get_num = fluid_defpreset_preset_get_num;
+ preset->noteon = fluid_defpreset_preset_noteon;
+ preset->notify = NULL;
+
+ return fluid_defsfont_iteration_next((fluid_defsfont_t*) sfont->data, preset);
+}
+
+int fluid_defpreset_preset_delete(fluid_preset_t* preset)
+{
+ fluid_defpreset_t* defpreset = preset ? preset->data : NULL;
+ fluid_defsfont_t* sfont = defpreset ? defpreset->sfont : NULL;
+
+ if (sfont && sfont->preset_stack_size < sfont->preset_stack_capacity) {
+ sfont->preset_stack[sfont->preset_stack_size] = preset;
+ sfont->preset_stack_size++;
+ }
+ else
+ FLUID_FREE(preset);
+
+ return 0;
+}
+
+char* fluid_defpreset_preset_get_name(fluid_preset_t* preset)
+{
+ return fluid_defpreset_get_name((fluid_defpreset_t*) preset->data);
+}
+
+int fluid_defpreset_preset_get_banknum(fluid_preset_t* preset)
+{
+ return fluid_defpreset_get_banknum((fluid_defpreset_t*) preset->data);
+}
+
+int fluid_defpreset_preset_get_num(fluid_preset_t* preset)
+{
+ return fluid_defpreset_get_num((fluid_defpreset_t*) preset->data);
+}
+
+int fluid_defpreset_preset_noteon(fluid_preset_t* preset, fluid_synth_t* synth,
+ int chan, int key, int vel)
+{
+ return fluid_defpreset_noteon((fluid_defpreset_t*) preset->data, synth, chan, key, vel);
+}
+
+
+
+
+/***************************************************************
+ *
+ * CACHED SAMPLEDATA LOADER
+ */
+
+typedef struct _fluid_cached_sampledata_t {
+ struct _fluid_cached_sampledata_t *next;
+
+ char* filename;
+ time_t modification_time;
+ int num_references;
+ int mlock;
+
+ const short* sampledata;
+ unsigned int samplesize;
+} fluid_cached_sampledata_t;
+
+static fluid_cached_sampledata_t* all_cached_sampledata = NULL;
+static fluid_mutex_t cached_sampledata_mutex = FLUID_MUTEX_INIT;
+
+static int fluid_get_file_modification_time(char *filename, time_t *modification_time)
+{
+#if defined(WIN32) || defined(__OS2__)
+ *modification_time = 0;
+ return FLUID_OK;
+#else
+ struct stat buf;
+
+ if (stat(filename, &buf) == -1) {
+ return FLUID_FAILED;
+ }
+
+ *modification_time = buf.st_mtime;
+ return FLUID_OK;
+#endif
+}
+
+static int fluid_cached_sampledata_load(char *filename, unsigned int samplepos,
+ unsigned int samplesize, short **sampledata, int try_mlock)
+{
+ fluid_file fd = NULL;
+ short *loaded_sampledata = NULL;
+ fluid_cached_sampledata_t* cached_sampledata = NULL;
+ time_t modification_time;
+
+ fluid_mutex_lock(cached_sampledata_mutex);
+
+ if (fluid_get_file_modification_time(filename, &modification_time) == FLUID_FAILED) {
+ FLUID_LOG(FLUID_WARN, "Unable to read modificaton time of soundfont file.");
+ modification_time = 0;
+ }
+
+ for (cached_sampledata = all_cached_sampledata; cached_sampledata; cached_sampledata = cached_sampledata->next) {
+ if (strcmp(filename, cached_sampledata->filename))
+ continue;
+ if (cached_sampledata->modification_time != modification_time)
+ continue;
+ if (cached_sampledata->samplesize != samplesize) {
+ FLUID_LOG(FLUID_ERR, "Cached size of soundfont doesn't match actual size of soundfont (cached: %u. actual: %u)",
+ cached_sampledata->samplesize, samplesize);
+ continue;
+ }
+
+ if (try_mlock && !cached_sampledata->mlock) {
+ if (fluid_mlock(cached_sampledata->sampledata, samplesize) != 0)
+ FLUID_LOG(FLUID_WARN, "Failed to pin the sample data to RAM; swapping is possible.");
+ else
+ cached_sampledata->mlock = try_mlock;
+ }
+
+ cached_sampledata->num_references++;
+ loaded_sampledata = (short*) cached_sampledata->sampledata;
+ goto success_exit;
+ }
+
+ fd = FLUID_FOPEN(filename, "rb");
+ if (fd == NULL) {
+ FLUID_LOG(FLUID_ERR, "Can't open soundfont file");
+ goto error_exit;
+ }
+ if (FLUID_FSEEK(fd, samplepos, SEEK_SET) == -1) {
+ perror("error");
+ FLUID_LOG(FLUID_ERR, "Failed to seek position in data file");
+ goto error_exit;
+ }
+
+
+ loaded_sampledata = (short*) FLUID_MALLOC(samplesize);
+ if (loaded_sampledata == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ goto error_exit;
+ }
+ if (FLUID_FREAD(loaded_sampledata, 1, samplesize, fd) < samplesize) {
+ FLUID_LOG(FLUID_ERR, "Failed to read sample data");
+ goto error_exit;
+ }
+
+ FLUID_FCLOSE(fd);
+ fd = NULL;
+
+
+ cached_sampledata = (fluid_cached_sampledata_t*) FLUID_MALLOC(sizeof(fluid_cached_sampledata_t));
+ if (cached_sampledata == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory.");
+ goto error_exit;
+ }
+
+ /* Lock the memory to disable paging. It's okay if this fails. It
+ probably means that the user doesn't have to required permission. */
+ cached_sampledata->mlock = 0;
+ if (try_mlock) {
+ if (fluid_mlock(loaded_sampledata, samplesize) != 0)
+ FLUID_LOG(FLUID_WARN, "Failed to pin the sample data to RAM; swapping is possible.");
+ else
+ cached_sampledata->mlock = try_mlock;
+ }
+
+ /* If this machine is big endian, the sample have to byte swapped */
+ if (FLUID_IS_BIG_ENDIAN) {
+ unsigned char* cbuf;
+ unsigned char hi, lo;
+ unsigned int i, j;
+ short s;
+ cbuf = (unsigned char*)loaded_sampledata;
+ for (i = 0, j = 0; j < samplesize; i++) {
+ lo = cbuf[j++];
+ hi = cbuf[j++];
+ s = (hi << 8) | lo;
+ loaded_sampledata[i] = s;
+ }
+ }
+
+ cached_sampledata->filename = (char*) FLUID_MALLOC(strlen(filename) + 1);
+ if (cached_sampledata->filename == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory.");
+ goto error_exit;
+ }
+
+ sprintf(cached_sampledata->filename, "%s", filename);
+ cached_sampledata->modification_time = modification_time;
+ cached_sampledata->num_references = 1;
+ cached_sampledata->sampledata = loaded_sampledata;
+ cached_sampledata->samplesize = samplesize;
+
+ cached_sampledata->next = all_cached_sampledata;
+ all_cached_sampledata = cached_sampledata;
+
+
+ success_exit:
+ fluid_mutex_unlock(cached_sampledata_mutex);
+ *sampledata = loaded_sampledata;
+ return FLUID_OK;
+
+ error_exit:
+ if (fd != NULL) {
+ FLUID_FCLOSE(fd);
+ }
+ if (loaded_sampledata != NULL) {
+ FLUID_FREE(loaded_sampledata);
+ }
+
+ if (cached_sampledata != NULL) {
+ if (cached_sampledata->filename != NULL) {
+ FLUID_FREE(cached_sampledata->filename);
+ }
+ FLUID_FREE(cached_sampledata);
+ }
+
+ fluid_mutex_unlock(cached_sampledata_mutex);
+ *sampledata = NULL;
+ return FLUID_FAILED;
+}
+
+static int fluid_cached_sampledata_unload(const short *sampledata)
+{
+ fluid_cached_sampledata_t* prev = NULL;
+ fluid_cached_sampledata_t* cached_sampledata;
+
+ fluid_mutex_lock(cached_sampledata_mutex);
+ cached_sampledata = all_cached_sampledata;
+
+ while (cached_sampledata != NULL) {
+ if (sampledata == cached_sampledata->sampledata) {
+
+ cached_sampledata->num_references--;
+
+ if (cached_sampledata->num_references == 0) {
+ if (cached_sampledata->mlock)
+ fluid_munlock(cached_sampledata->sampledata, cached_sampledata->samplesize);
+ FLUID_FREE((short*) cached_sampledata->sampledata);
+ FLUID_FREE(cached_sampledata->filename);
+
+ if (prev != NULL) {
+ prev->next = cached_sampledata->next;
+ } else {
+ all_cached_sampledata = cached_sampledata->next;
+ }
+
+ FLUID_FREE(cached_sampledata);
+ }
+
+ goto success_exit;
+ }
+
+ prev = cached_sampledata;
+ cached_sampledata = cached_sampledata->next;
+ }
+
+ FLUID_LOG(FLUID_ERR, "Trying to free sampledata not found in cache.");
+ goto error_exit;
+
+ success_exit:
+ fluid_mutex_unlock(cached_sampledata_mutex);
+ return FLUID_OK;
+
+ error_exit:
+ fluid_mutex_unlock(cached_sampledata_mutex);
+ return FLUID_FAILED;
+}
+
+
+
+
+/***************************************************************
+ *
+ * SFONT
+ */
+
+/*
+ * new_fluid_defsfont
+ */
+fluid_defsfont_t* new_fluid_defsfont(fluid_settings_t* settings)
+{
+ fluid_defsfont_t* sfont;
+ int i;
+
+ sfont = FLUID_NEW(fluid_defsfont_t);
+ if (sfont == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ sfont->filename = NULL;
+ sfont->samplepos = 0;
+ sfont->samplesize = 0;
+ sfont->sample = NULL;
+ sfont->sampledata = NULL;
+ sfont->preset = NULL;
+ fluid_settings_getint(settings, "synth.lock-memory", &sfont->mlock);
+
+ /* Initialise preset cache, so we don't have to call malloc on program changes.
+ Usually, we have at most one preset per channel plus one temporarily used,
+ so optimise for that case. */
+ fluid_settings_getint(settings, "synth.midi-channels", &sfont->preset_stack_capacity);
+ sfont->preset_stack_capacity++;
+ sfont->preset_stack_size = 0;
+ sfont->preset_stack = FLUID_ARRAY(fluid_preset_t*, sfont->preset_stack_capacity);
+ if (!sfont->preset_stack) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ FLUID_FREE(sfont);
+ return NULL;
+ }
+
+ for (i = 0; i < sfont->preset_stack_capacity; i++) {
+ sfont->preset_stack[i] = FLUID_NEW(fluid_preset_t);
+ if (!sfont->preset_stack[i]) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ delete_fluid_defsfont(sfont);
+ return NULL;
+ }
+ sfont->preset_stack_size++;
+ }
+
+ return sfont;
+}
+
+/*
+ * delete_fluid_defsfont
+ */
+int delete_fluid_defsfont(fluid_defsfont_t* sfont)
+{
+ fluid_list_t *list;
+ fluid_defpreset_t* preset;
+ fluid_sample_t* sample;
+
+ /* Check that no samples are currently used */
+ for (list = sfont->sample; list; list = fluid_list_next(list)) {
+ sample = (fluid_sample_t*) fluid_list_get(list);
+ if (fluid_sample_refcount(sample) != 0) {
+ return -1;
+ }
+ }
+
+ if (sfont->filename != NULL) {
+ FLUID_FREE(sfont->filename);
+ }
+
+ for (list = sfont->sample; list; list = fluid_list_next(list)) {
+ delete_fluid_sample((fluid_sample_t*) fluid_list_get(list));
+ }
+
+ if (sfont->sample) {
+ delete_fluid_list(sfont->sample);
+ }
+
+ if (sfont->sampledata != NULL) {
+ fluid_cached_sampledata_unload(sfont->sampledata);
+ }
+
+ while (sfont->preset_stack_size > 0)
+ FLUID_FREE(sfont->preset_stack[--sfont->preset_stack_size]);
+ FLUID_FREE(sfont->preset_stack);
+
+ preset = sfont->preset;
+ while (preset != NULL) {
+ sfont->preset = preset->next;
+ delete_fluid_defpreset(preset);
+ preset = sfont->preset;
+ }
+
+ FLUID_FREE(sfont);
+ return FLUID_OK;
+}
+
+/*
+ * fluid_defsfont_get_name
+ */
+char* fluid_defsfont_get_name(fluid_defsfont_t* sfont)
+{
+ return sfont->filename;
+}
+
+
+/*
+ * fluid_defsfont_load
+ */
+int fluid_defsfont_load(fluid_defsfont_t* sfont, const char* file)
+{
+ SFData* sfdata;
+ fluid_list_t *p;
+ SFPreset* sfpreset;
+ SFSample* sfsample;
+ fluid_sample_t* sample;
+ fluid_defpreset_t* preset = NULL;
+
+ sfont->filename = FLUID_MALLOC(1 + FLUID_STRLEN(file));
+ if (sfont->filename == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return FLUID_FAILED;
+ }
+ FLUID_STRCPY(sfont->filename, file);
+
+ /* The actual loading is done in the sfont and sffile files */
+ sfdata = sfload_file(file);
+ if (sfdata == NULL) {
+ FLUID_LOG(FLUID_ERR, "Couldn't load soundfont file");
+ return FLUID_FAILED;
+ }
+
+ /* Keep track of the position and size of the sample data because
+ it's loaded separately (and might be unoaded/reloaded in future) */
+ sfont->samplepos = sfdata->samplepos;
+ sfont->samplesize = sfdata->samplesize;
+
+ /* load sample data in one block */
+ if (fluid_defsfont_load_sampledata(sfont) != FLUID_OK)
+ goto err_exit;
+
+ /* Create all the sample headers */
+ p = sfdata->sample;
+ while (p != NULL) {
+ sfsample = (SFSample *) p->data;
+
+ sample = new_fluid_sample();
+ if (sample == NULL) goto err_exit;
+
+ if (fluid_sample_import_sfont(sample, sfsample, sfont) != FLUID_OK)
+ goto err_exit;
+
+ /* Store reference to FluidSynth sample in SFSample for later IZone fixups */
+ sfsample->fluid_sample = sample;
+
+ fluid_defsfont_add_sample(sfont, sample);
+ fluid_voice_optimize_sample(sample);
+ p = fluid_list_next(p);
+ }
+
+ /* Load all the presets */
+ p = sfdata->preset;
+ while (p != NULL) {
+ sfpreset = (SFPreset *) p->data;
+ preset = new_fluid_defpreset(sfont);
+ if (preset == NULL) goto err_exit;
+
+ if (fluid_defpreset_import_sfont(preset, sfpreset, sfont) != FLUID_OK)
+ goto err_exit;
+
+ fluid_defsfont_add_preset(sfont, preset);
+ p = fluid_list_next(p);
+ }
+ sfont_close (sfdata);
+
+ return FLUID_OK;
+
+err_exit:
+ sfont_close (sfdata);
+ if (preset != NULL)
+ delete_fluid_defpreset(preset);
+ return FLUID_FAILED;
+}
+
+/* fluid_defsfont_add_sample
+ *
+ * Add a sample to the SoundFont
+ */
+int fluid_defsfont_add_sample(fluid_defsfont_t* sfont, fluid_sample_t* sample)
+{
+ sfont->sample = fluid_list_append(sfont->sample, sample);
+ return FLUID_OK;
+}
+
+/* fluid_defsfont_add_preset
+ *
+ * Add a preset to the SoundFont
+ */
+int fluid_defsfont_add_preset(fluid_defsfont_t* sfont, fluid_defpreset_t* preset)
+{
+ fluid_defpreset_t *cur, *prev;
+ if (sfont->preset == NULL) {
+ preset->next = NULL;
+ sfont->preset = preset;
+ } else {
+ /* sort them as we go along. very basic sorting trick. */
+ cur = sfont->preset;
+ prev = NULL;
+ while (cur != NULL) {
+ if ((preset->bank < cur->bank)
+ || ((preset->bank == cur->bank) && (preset->num < cur->num))) {
+ if (prev == NULL) {
+ preset->next = cur;
+ sfont->preset = preset;
+ } else {
+ preset->next = cur;
+ prev->next = preset;
+ }
+ return FLUID_OK;
+ }
+ prev = cur;
+ cur = cur->next;
+ }
+ preset->next = NULL;
+ prev->next = preset;
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_defsfont_load_sampledata
+ */
+int
+fluid_defsfont_load_sampledata(fluid_defsfont_t* sfont)
+{
+ return fluid_cached_sampledata_load(sfont->filename, sfont->samplepos,
+ sfont->samplesize, &sfont->sampledata, sfont->mlock);
+}
+
+/*
+ * fluid_defsfont_get_preset
+ */
+fluid_defpreset_t* fluid_defsfont_get_preset(fluid_defsfont_t* sfont, unsigned int bank, unsigned int num)
+{
+ fluid_defpreset_t* preset = sfont->preset;
+ while (preset != NULL) {
+ if ((preset->bank == bank) && ((preset->num == num))) {
+ return preset;
+ }
+ preset = preset->next;
+ }
+ return NULL;
+}
+
+/*
+ * fluid_defsfont_iteration_start
+ */
+void fluid_defsfont_iteration_start(fluid_defsfont_t* sfont)
+{
+ sfont->iter_cur = sfont->preset;
+}
+
+/*
+ * fluid_defsfont_iteration_next
+ */
+int fluid_defsfont_iteration_next(fluid_defsfont_t* sfont, fluid_preset_t* preset)
+{
+ if (sfont->iter_cur == NULL) {
+ return 0;
+ }
+
+ preset->data = (void*) sfont->iter_cur;
+ sfont->iter_cur = fluid_defpreset_next(sfont->iter_cur);
+ return 1;
+}
+
+/***************************************************************
+ *
+ * PRESET
+ */
+
+/*
+ * new_fluid_defpreset
+ */
+fluid_defpreset_t*
+new_fluid_defpreset(fluid_defsfont_t* sfont)
+{
+ fluid_defpreset_t* preset = FLUID_NEW(fluid_defpreset_t);
+ if (preset == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ preset->next = NULL;
+ preset->sfont = sfont;
+ preset->name[0] = 0;
+ preset->bank = 0;
+ preset->num = 0;
+ preset->global_zone = NULL;
+ preset->zone = NULL;
+ return preset;
+}
+
+/*
+ * delete_fluid_defpreset
+ */
+int
+delete_fluid_defpreset(fluid_defpreset_t* preset)
+{
+ int err = FLUID_OK;
+ fluid_preset_zone_t* zone;
+ if (preset->global_zone != NULL) {
+ if (delete_fluid_preset_zone(preset->global_zone) != FLUID_OK) {
+ err = FLUID_FAILED;
+ }
+ preset->global_zone = NULL;
+ }
+ zone = preset->zone;
+ while (zone != NULL) {
+ preset->zone = zone->next;
+ if (delete_fluid_preset_zone(zone) != FLUID_OK) {
+ err = FLUID_FAILED;
+ }
+ zone = preset->zone;
+ }
+ FLUID_FREE(preset);
+ return err;
+}
+
+int
+fluid_defpreset_get_banknum(fluid_defpreset_t* preset)
+{
+ return preset->bank;
+}
+
+int
+fluid_defpreset_get_num(fluid_defpreset_t* preset)
+{
+ return preset->num;
+}
+
+char*
+fluid_defpreset_get_name(fluid_defpreset_t* preset)
+{
+ return preset->name;
+}
+
+/*
+ * fluid_defpreset_next
+ */
+fluid_defpreset_t*
+fluid_defpreset_next(fluid_defpreset_t* preset)
+{
+ return preset->next;
+}
+
+
+/*
+ * fluid_defpreset_noteon
+ */
+int
+fluid_defpreset_noteon(fluid_defpreset_t* preset, fluid_synth_t* synth, int chan, int key, int vel)
+{
+ fluid_preset_zone_t *preset_zone, *global_preset_zone;
+ fluid_inst_t* inst;
+ fluid_inst_zone_t *inst_zone, *global_inst_zone;
+ fluid_sample_t* sample;
+ fluid_voice_t* voice;
+ fluid_mod_t * mod;
+ fluid_mod_t * mod_list[FLUID_NUM_MOD]; /* list for 'sorting' preset modulators */
+ int mod_list_count;
+ int i;
+
+ global_preset_zone = fluid_defpreset_get_global_zone(preset);
+
+ /* run thru all the zones of this preset */
+ preset_zone = fluid_defpreset_get_zone(preset);
+ while (preset_zone != NULL) {
+
+ /* check if the note falls into the key and velocity range of this
+ preset */
+ if (fluid_preset_zone_inside_range(preset_zone, key, vel)) {
+
+ inst = fluid_preset_zone_get_inst(preset_zone);
+ global_inst_zone = fluid_inst_get_global_zone(inst);
+
+ /* run thru all the zones of this instrument */
+ inst_zone = fluid_inst_get_zone(inst);
+ while (inst_zone != NULL) {
+
+ /* make sure this instrument zone has a valid sample */
+ sample = fluid_inst_zone_get_sample(inst_zone);
+ if ((sample == NULL) || fluid_sample_in_rom(sample)) {
+ inst_zone = fluid_inst_zone_next(inst_zone);
+ continue;
+ }
+
+ /* check if the note falls into the key and velocity range of this
+ instrument */
+
+ if (fluid_inst_zone_inside_range(inst_zone, key, vel) && (sample != NULL)) {
+
+ /* this is a good zone. allocate a new synthesis process and
+ initialize it */
+
+ voice = fluid_synth_alloc_voice(synth, sample, chan, key, vel);
+ if (voice == NULL) {
+ return FLUID_FAILED;
+ }
+
+
+ /* Instrument level, generators */
+
+ for (i = 0; i < GEN_LAST; i++) {
+
+ /* SF 2.01 section 9.4 'bullet' 4:
+ *
+ * A generator in a local instrument zone supersedes a
+ * global instrument zone generator. Both cases supersede
+ * the default generator -> voice_gen_set */
+
+ if (inst_zone->gen[i].flags){
+ fluid_voice_gen_set(voice, i, inst_zone->gen[i].val);
+
+ } else if ((global_inst_zone != NULL) && (global_inst_zone->gen[i].flags)) {
+ fluid_voice_gen_set(voice, i, global_inst_zone->gen[i].val);
+
+ } else {
+ /* The generator has not been defined in this instrument.
+ * Do nothing, leave it at the default.
+ */
+ }
+
+ } /* for all generators */
+
+ /* global instrument zone, modulators: Put them all into a
+ * list. */
+
+ mod_list_count = 0;
+
+ if (global_inst_zone){
+ mod = global_inst_zone->mod;
+ while (mod){
+ mod_list[mod_list_count++] = mod;
+ mod = mod->next;
+ }
+ }
+
+ /* local instrument zone, modulators.
+ * Replace modulators with the same definition in the list:
+ * SF 2.01 page 69, 'bullet' 8
+ */
+ mod = inst_zone->mod;
+
+ while (mod){
+
+ /* 'Identical' modulators will be deleted by setting their
+ * list entry to NULL. The list length is known, NULL
+ * entries will be ignored later. SF2.01 section 9.5.1
+ * page 69, 'bullet' 3 defines 'identical'. */
+
+ for (i = 0; i < mod_list_count; i++){
+ if (mod_list[i] && fluid_mod_test_identity(mod,mod_list[i])){
+ mod_list[i] = NULL;
+ }
+ }
+
+ /* Finally add the new modulator to to the list. */
+ mod_list[mod_list_count++] = mod;
+ mod = mod->next;
+ }
+
+ /* Add instrument modulators (global / local) to the voice. */
+ for (i = 0; i < mod_list_count; i++){
+
+ mod = mod_list[i];
+
+ if (mod != NULL){ /* disabled modulators CANNOT be skipped. */
+
+ /* Instrument modulators -supersede- existing (default)
+ * modulators. SF 2.01 page 69, 'bullet' 6 */
+ fluid_voice_add_mod(voice, mod, FLUID_VOICE_OVERWRITE);
+ }
+ }
+
+ /* Preset level, generators */
+
+ for (i = 0; i < GEN_LAST; i++) {
+
+ /* SF 2.01 section 8.5 page 58: If some generators are
+ * encountered at preset level, they should be ignored */
+ if ((i != GEN_STARTADDROFS)
+ && (i != GEN_ENDADDROFS)
+ && (i != GEN_STARTLOOPADDROFS)
+ && (i != GEN_ENDLOOPADDROFS)
+ && (i != GEN_STARTADDRCOARSEOFS)
+ && (i != GEN_ENDADDRCOARSEOFS)
+ && (i != GEN_STARTLOOPADDRCOARSEOFS)
+ && (i != GEN_KEYNUM)
+ && (i != GEN_VELOCITY)
+ && (i != GEN_ENDLOOPADDRCOARSEOFS)
+ && (i != GEN_SAMPLEMODE)
+ && (i != GEN_EXCLUSIVECLASS)
+ && (i != GEN_OVERRIDEROOTKEY)) {
+
+ /* SF 2.01 section 9.4 'bullet' 9: A generator in a
+ * local preset zone supersedes a global preset zone
+ * generator. The effect is -added- to the destination
+ * summing node -> voice_gen_incr */
+
+ if (preset_zone->gen[i].flags) {
+ fluid_voice_gen_incr(voice, i, preset_zone->gen[i].val);
+ } else if ((global_preset_zone != NULL) && global_preset_zone->gen[i].flags) {
+ fluid_voice_gen_incr(voice, i, global_preset_zone->gen[i].val);
+ } else {
+ /* The generator has not been defined in this preset
+ * Do nothing, leave it unchanged.
+ */
+ }
+ } /* if available at preset level */
+ } /* for all generators */
+
+
+ /* Global preset zone, modulators: put them all into a
+ * list. */
+ mod_list_count = 0;
+ if (global_preset_zone){
+ mod = global_preset_zone->mod;
+ while (mod){
+ mod_list[mod_list_count++] = mod;
+ mod = mod->next;
+ }
+ }
+
+ /* Process the modulators of the local preset zone. Kick
+ * out all identical modulators from the global preset zone
+ * (SF 2.01 page 69, second-last bullet) */
+
+ mod = preset_zone->mod;
+ while (mod){
+ for (i = 0; i < mod_list_count; i++){
+ if (mod_list[i] && fluid_mod_test_identity(mod,mod_list[i])){
+ mod_list[i] = NULL;
+ }
+ }
+
+ /* Finally add the new modulator to the list. */
+ mod_list[mod_list_count++] = mod;
+ mod = mod->next;
+ }
+
+ /* Add preset modulators (global / local) to the voice. */
+ for (i = 0; i < mod_list_count; i++){
+ mod = mod_list[i];
+ if ((mod != NULL) && (mod->amount != 0)) { /* disabled modulators can be skipped. */
+
+ /* Preset modulators -add- to existing instrument /
+ * default modulators. SF2.01 page 70 first bullet on
+ * page */
+ fluid_voice_add_mod(voice, mod, FLUID_VOICE_ADD);
+ }
+ }
+
+ /* add the synthesis process to the synthesis loop. */
+ fluid_synth_start_voice(synth, voice);
+
+ /* Store the ID of the first voice that was created by this noteon event.
+ * Exclusive class may only terminate older voices.
+ * That avoids killing voices, which have just been created.
+ * (a noteon event can create several voice processes with the same exclusive
+ * class - for example when using stereo samples)
+ */
+ }
+
+ inst_zone = fluid_inst_zone_next(inst_zone);
+ }
+ }
+ preset_zone = fluid_preset_zone_next(preset_zone);
+ }
+
+ return FLUID_OK;
+}
+
+/*
+ * fluid_defpreset_set_global_zone
+ */
+int
+fluid_defpreset_set_global_zone(fluid_defpreset_t* preset, fluid_preset_zone_t* zone)
+{
+ preset->global_zone = zone;
+ return FLUID_OK;
+}
+
+/*
+ * fluid_defpreset_import_sfont
+ */
+int
+fluid_defpreset_import_sfont(fluid_defpreset_t* preset,
+ SFPreset* sfpreset,
+ fluid_defsfont_t* sfont)
+{
+ fluid_list_t *p;
+ SFZone* sfzone;
+ fluid_preset_zone_t* zone;
+ int count;
+ char zone_name[256];
+ if ((sfpreset->name != NULL) && (FLUID_STRLEN(sfpreset->name) > 0)) {
+ FLUID_STRCPY(preset->name, sfpreset->name);
+ } else {
+ FLUID_SPRINTF(preset->name, "Bank%d,Preset%d", sfpreset->bank, sfpreset->prenum);
+ }
+ preset->bank = sfpreset->bank;
+ preset->num = sfpreset->prenum;
+ p = sfpreset->zone;
+ count = 0;
+ while (p != NULL) {
+ sfzone = (SFZone *) p->data;
+ FLUID_SPRINTF(zone_name, "%s/%d", preset->name, count);
+ zone = new_fluid_preset_zone(zone_name);
+ if (zone == NULL) {
+ return FLUID_FAILED;
+ }
+ if (fluid_preset_zone_import_sfont(zone, sfzone, sfont) != FLUID_OK) {
+ delete_fluid_preset_zone(zone);
+ return FLUID_FAILED;
+ }
+ if ((count == 0) && (fluid_preset_zone_get_inst(zone) == NULL)) {
+ fluid_defpreset_set_global_zone(preset, zone);
+ } else if (fluid_defpreset_add_zone(preset, zone) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+ p = fluid_list_next(p);
+ count++;
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_defpreset_add_zone
+ */
+int
+fluid_defpreset_add_zone(fluid_defpreset_t* preset, fluid_preset_zone_t* zone)
+{
+ if (preset->zone == NULL) {
+ zone->next = NULL;
+ preset->zone = zone;
+ } else {
+ zone->next = preset->zone;
+ preset->zone = zone;
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_defpreset_get_zone
+ */
+fluid_preset_zone_t*
+fluid_defpreset_get_zone(fluid_defpreset_t* preset)
+{
+ return preset->zone;
+}
+
+/*
+ * fluid_defpreset_get_global_zone
+ */
+fluid_preset_zone_t*
+fluid_defpreset_get_global_zone(fluid_defpreset_t* preset)
+{
+ return preset->global_zone;
+}
+
+/*
+ * fluid_preset_zone_next
+ */
+fluid_preset_zone_t*
+fluid_preset_zone_next(fluid_preset_zone_t* preset)
+{
+ return preset->next;
+}
+
+/*
+ * new_fluid_preset_zone
+ */
+fluid_preset_zone_t*
+new_fluid_preset_zone(char *name)
+{
+ int size;
+ fluid_preset_zone_t* zone = NULL;
+ zone = FLUID_NEW(fluid_preset_zone_t);
+ if (zone == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ zone->next = NULL;
+ size = 1 + FLUID_STRLEN(name);
+ zone->name = FLUID_MALLOC(size);
+ if (zone->name == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ FLUID_FREE(zone);
+ return NULL;
+ }
+ FLUID_STRCPY(zone->name, name);
+ zone->inst = NULL;
+ zone->keylo = 0;
+ zone->keyhi = 128;
+ zone->vello = 0;
+ zone->velhi = 128;
+
+ /* Flag all generators as unused (default, they will be set when they are found
+ * in the sound font).
+ * This also sets the generator values to default, but that is of no concern here.*/
+ fluid_gen_set_default_values(&zone->gen[0]);
+ zone->mod = NULL; /* list of modulators */
+ return zone;
+}
+
+/***************************************************************
+ *
+ * PRESET_ZONE
+ */
+
+/*
+ * delete_fluid_preset_zone
+ */
+int
+delete_fluid_preset_zone(fluid_preset_zone_t* zone)
+{
+ fluid_mod_t *mod, *tmp;
+
+ mod = zone->mod;
+ while (mod) /* delete the modulators */
+ {
+ tmp = mod;
+ mod = mod->next;
+ fluid_mod_delete (tmp);
+ }
+
+ if (zone->name) FLUID_FREE (zone->name);
+ if (zone->inst) delete_fluid_inst (zone->inst);
+ FLUID_FREE(zone);
+ return FLUID_OK;
+}
+
+/*
+ * fluid_preset_zone_import_sfont
+ */
+int
+fluid_preset_zone_import_sfont(fluid_preset_zone_t* zone, SFZone *sfzone, fluid_defsfont_t* sfont)
+{
+ fluid_list_t *r;
+ SFGen* sfgen;
+ int count;
+ for (count = 0, r = sfzone->gen; r != NULL; count++) {
+ sfgen = (SFGen *) r->data;
+ switch (sfgen->id) {
+ case GEN_KEYRANGE:
+ zone->keylo = (int) sfgen->amount.range.lo;
+ zone->keyhi = (int) sfgen->amount.range.hi;
+ break;
+ case GEN_VELRANGE:
+ zone->vello = (int) sfgen->amount.range.lo;
+ zone->velhi = (int) sfgen->amount.range.hi;
+ break;
+ default:
+ /* FIXME: some generators have an unsigne word amount value but i don't know which ones */
+ zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword;
+ zone->gen[sfgen->id].flags = GEN_SET;
+ break;
+ }
+ r = fluid_list_next(r);
+ }
+ if ((sfzone->instsamp != NULL) && (sfzone->instsamp->data != NULL)) {
+ zone->inst = (fluid_inst_t*) new_fluid_inst();
+ if (zone->inst == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return FLUID_FAILED;
+ }
+ if (fluid_inst_import_sfont(zone->inst, (SFInst *) sfzone->instsamp->data, sfont) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+ }
+
+ /* Import the modulators (only SF2.1 and higher) */
+ for (count = 0, r = sfzone->mod; r != NULL; count++) {
+
+ SFMod* mod_src = (SFMod *)r->data;
+ fluid_mod_t * mod_dest = fluid_mod_new();
+ int type;
+
+ if (mod_dest == NULL){
+ return FLUID_FAILED;
+ }
+ mod_dest->next = NULL; /* pointer to next modulator, this is the end of the list now.*/
+
+ /* *** Amount *** */
+ mod_dest->amount = mod_src->amount;
+
+ /* *** Source *** */
+ mod_dest->src1 = mod_src->src & 127; /* index of source 1, seven-bit value, SF2.01 section 8.2, page 50 */
+ mod_dest->flags1 = 0;
+
+ /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/
+ if (mod_src->src & (1<<7)){
+ mod_dest->flags1 |= FLUID_MOD_CC;
+ } else {
+ mod_dest->flags1 |= FLUID_MOD_GC;
+ }
+
+ /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/
+ if (mod_src->src & (1<<8)){
+ mod_dest->flags1 |= FLUID_MOD_NEGATIVE;
+ } else {
+ mod_dest->flags1 |= FLUID_MOD_POSITIVE;
+ }
+
+ /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/
+ if (mod_src->src & (1<<9)){
+ mod_dest->flags1 |= FLUID_MOD_BIPOLAR;
+ } else {
+ mod_dest->flags1 |= FLUID_MOD_UNIPOLAR;
+ }
+
+ /* modulator source types: SF2.01 section 8.2.1 page 52 */
+ type=(mod_src->src) >> 10;
+ type &= 63; /* type is a 6-bit value */
+ if (type == 0){
+ mod_dest->flags1 |= FLUID_MOD_LINEAR;
+ } else if (type == 1){
+ mod_dest->flags1 |= FLUID_MOD_CONCAVE;
+ } else if (type == 2){
+ mod_dest->flags1 |= FLUID_MOD_CONVEX;
+ } else if (type == 3){
+ mod_dest->flags1 |= FLUID_MOD_SWITCH;
+ } else {
+ /* This shouldn't happen - unknown type!
+ * Deactivate the modulator by setting the amount to 0. */
+ mod_dest->amount=0;
+ }
+
+ /* *** Dest *** */
+ mod_dest->dest = mod_src->dest; /* index of controlled generator */
+
+ /* *** Amount source *** */
+ mod_dest->src2 = mod_src->amtsrc & 127; /* index of source 2, seven-bit value, SF2.01 section 8.2, p.50 */
+ mod_dest->flags2 = 0;
+
+ /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/
+ if (mod_src->amtsrc & (1<<7)){
+ mod_dest->flags2 |= FLUID_MOD_CC;
+ } else {
+ mod_dest->flags2 |= FLUID_MOD_GC;
+ }
+
+ /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/
+ if (mod_src->amtsrc & (1<<8)){
+ mod_dest->flags2 |= FLUID_MOD_NEGATIVE;
+ } else {
+ mod_dest->flags2 |= FLUID_MOD_POSITIVE;
+ }
+
+ /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/
+ if (mod_src->amtsrc & (1<<9)){
+ mod_dest->flags2 |= FLUID_MOD_BIPOLAR;
+ } else {
+ mod_dest->flags2 |= FLUID_MOD_UNIPOLAR;
+ }
+
+ /* modulator source types: SF2.01 section 8.2.1 page 52 */
+ type = (mod_src->amtsrc) >> 10;
+ type &= 63; /* type is a 6-bit value */
+ if (type == 0){
+ mod_dest->flags2 |= FLUID_MOD_LINEAR;
+ } else if (type == 1){
+ mod_dest->flags2 |= FLUID_MOD_CONCAVE;
+ } else if (type == 2){
+ mod_dest->flags2 |= FLUID_MOD_CONVEX;
+ } else if (type == 3){
+ mod_dest->flags2 |= FLUID_MOD_SWITCH;
+ } else {
+ /* This shouldn't happen - unknown type!
+ * Deactivate the modulator by setting the amount to 0. */
+ mod_dest->amount=0;
+ }
+
+ /* *** Transform *** */
+ /* SF2.01 only uses the 'linear' transform (0).
+ * Deactivate the modulator by setting the amount to 0 in any other case.
+ */
+ if (mod_src->trans !=0){
+ mod_dest->amount = 0;
+ }
+
+ /* Store the new modulator in the zone The order of modulators
+ * will make a difference, at least in an instrument context: The
+ * second modulator overwrites the first one, if they only differ
+ * in amount. */
+ if (count == 0){
+ zone->mod = mod_dest;
+ } else {
+ fluid_mod_t * last_mod = zone->mod;
+
+ /* Find the end of the list */
+ while (last_mod->next != NULL){
+ last_mod=last_mod->next;
+ }
+
+ last_mod->next = mod_dest;
+ }
+
+ r = fluid_list_next(r);
+ } /* foreach modulator */
+
+ return FLUID_OK;
+}
+
+/*
+ * fluid_preset_zone_get_inst
+ */
+fluid_inst_t*
+fluid_preset_zone_get_inst(fluid_preset_zone_t* zone)
+{
+ return zone->inst;
+}
+
+/*
+ * fluid_preset_zone_inside_range
+ */
+int
+fluid_preset_zone_inside_range(fluid_preset_zone_t* zone, int key, int vel)
+{
+ return ((zone->keylo <= key) &&
+ (zone->keyhi >= key) &&
+ (zone->vello <= vel) &&
+ (zone->velhi >= vel));
+}
+
+/***************************************************************
+ *
+ * INST
+ */
+
+/*
+ * new_fluid_inst
+ */
+fluid_inst_t*
+new_fluid_inst()
+{
+ fluid_inst_t* inst = FLUID_NEW(fluid_inst_t);
+ if (inst == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ inst->name[0] = 0;
+ inst->global_zone = NULL;
+ inst->zone = NULL;
+ return inst;
+}
+
+/*
+ * delete_fluid_inst
+ */
+int
+delete_fluid_inst(fluid_inst_t* inst)
+{
+ fluid_inst_zone_t* zone;
+ int err = FLUID_OK;
+ if (inst->global_zone != NULL) {
+ if (delete_fluid_inst_zone(inst->global_zone) != FLUID_OK) {
+ err = FLUID_FAILED;
+ }
+ inst->global_zone = NULL;
+ }
+ zone = inst->zone;
+ while (zone != NULL) {
+ inst->zone = zone->next;
+ if (delete_fluid_inst_zone(zone) != FLUID_OK) {
+ err = FLUID_FAILED;
+ }
+ zone = inst->zone;
+ }
+ FLUID_FREE(inst);
+ return err;
+}
+
+/*
+ * fluid_inst_set_global_zone
+ */
+int
+fluid_inst_set_global_zone(fluid_inst_t* inst, fluid_inst_zone_t* zone)
+{
+ inst->global_zone = zone;
+ return FLUID_OK;
+}
+
+/*
+ * fluid_inst_import_sfont
+ */
+int
+fluid_inst_import_sfont(fluid_inst_t* inst, SFInst *sfinst, fluid_defsfont_t* sfont)
+{
+ fluid_list_t *p;
+ SFZone* sfzone;
+ fluid_inst_zone_t* zone;
+ char zone_name[256];
+ int count;
+
+ p = sfinst->zone;
+ if ((sfinst->name != NULL) && (FLUID_STRLEN(sfinst->name) > 0)) {
+ FLUID_STRCPY(inst->name, sfinst->name);
+ } else {
+ FLUID_STRCPY(inst->name, "<untitled>");
+ }
+
+ count = 0;
+ while (p != NULL) {
+
+ sfzone = (SFZone *) p->data;
+ FLUID_SPRINTF(zone_name, "%s/%d", inst->name, count);
+
+ zone = new_fluid_inst_zone(zone_name);
+ if (zone == NULL) {
+ return FLUID_FAILED;
+ }
+
+ if (fluid_inst_zone_import_sfont(zone, sfzone, sfont) != FLUID_OK) {
+ delete_fluid_inst_zone(zone);
+ return FLUID_FAILED;
+ }
+
+ if ((count == 0) && (fluid_inst_zone_get_sample(zone) == NULL)) {
+ fluid_inst_set_global_zone(inst, zone);
+
+ } else if (fluid_inst_add_zone(inst, zone) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+
+ p = fluid_list_next(p);
+ count++;
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_inst_add_zone
+ */
+int
+fluid_inst_add_zone(fluid_inst_t* inst, fluid_inst_zone_t* zone)
+{
+ if (inst->zone == NULL) {
+ zone->next = NULL;
+ inst->zone = zone;
+ } else {
+ zone->next = inst->zone;
+ inst->zone = zone;
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_inst_get_zone
+ */
+fluid_inst_zone_t*
+fluid_inst_get_zone(fluid_inst_t* inst)
+{
+ return inst->zone;
+}
+
+/*
+ * fluid_inst_get_global_zone
+ */
+fluid_inst_zone_t*
+fluid_inst_get_global_zone(fluid_inst_t* inst)
+{
+ return inst->global_zone;
+}
+
+/***************************************************************
+ *
+ * INST_ZONE
+ */
+
+/*
+ * new_fluid_inst_zone
+ */
+fluid_inst_zone_t*
+new_fluid_inst_zone(char* name)
+{
+ int size;
+ fluid_inst_zone_t* zone = NULL;
+ zone = FLUID_NEW(fluid_inst_zone_t);
+ if (zone == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ zone->next = NULL;
+ size = 1 + FLUID_STRLEN(name);
+ zone->name = FLUID_MALLOC(size);
+ if (zone->name == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ FLUID_FREE(zone);
+ return NULL;
+ }
+ FLUID_STRCPY(zone->name, name);
+ zone->sample = NULL;
+ zone->keylo = 0;
+ zone->keyhi = 128;
+ zone->vello = 0;
+ zone->velhi = 128;
+
+ /* Flag the generators as unused.
+ * This also sets the generator values to default, but they will be overwritten anyway, if used.*/
+ fluid_gen_set_default_values(&zone->gen[0]);
+ zone->mod=NULL; /* list of modulators */
+ return zone;
+}
+
+/*
+ * delete_fluid_inst_zone
+ */
+int
+delete_fluid_inst_zone(fluid_inst_zone_t* zone)
+{
+ fluid_mod_t *mod, *tmp;
+
+ mod = zone->mod;
+ while (mod) /* delete the modulators */
+ {
+ tmp = mod;
+ mod = mod->next;
+ fluid_mod_delete (tmp);
+ }
+
+ if (zone->name) FLUID_FREE (zone->name);
+ FLUID_FREE(zone);
+ return FLUID_OK;
+}
+
+/*
+ * fluid_inst_zone_next
+ */
+fluid_inst_zone_t*
+fluid_inst_zone_next(fluid_inst_zone_t* zone)
+{
+ return zone->next;
+}
+
+/*
+ * fluid_inst_zone_import_sfont
+ */
+int
+fluid_inst_zone_import_sfont(fluid_inst_zone_t* zone, SFZone *sfzone, fluid_defsfont_t* sfont)
+{
+ fluid_list_t *r;
+ SFGen* sfgen;
+ int count;
+
+ for (count = 0, r = sfzone->gen; r != NULL; count++) {
+ sfgen = (SFGen *) r->data;
+ switch (sfgen->id) {
+ case GEN_KEYRANGE:
+ zone->keylo = (int) sfgen->amount.range.lo;
+ zone->keyhi = (int) sfgen->amount.range.hi;
+ break;
+ case GEN_VELRANGE:
+ zone->vello = (int) sfgen->amount.range.lo;
+ zone->velhi = (int) sfgen->amount.range.hi;
+ break;
+ default:
+ /* FIXME: some generators have an unsigned word amount value but
+ i don't know which ones */
+ zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword;
+ zone->gen[sfgen->id].flags = GEN_SET;
+ break;
+ }
+ r = fluid_list_next(r);
+ }
+
+ /* FIXME */
+/* if (zone->gen[GEN_EXCLUSIVECLASS].flags == GEN_SET) { */
+/* FLUID_LOG(FLUID_DBG, "ExclusiveClass=%d\n", (int) zone->gen[GEN_EXCLUSIVECLASS].val); */
+/* } */
+
+ /* fixup sample pointer */
+ if ((sfzone->instsamp != NULL) && (sfzone->instsamp->data != NULL))
+ zone->sample = ((SFSample *)(sfzone->instsamp->data))->fluid_sample;
+
+ /* Import the modulators (only SF2.1 and higher) */
+ for (count = 0, r = sfzone->mod; r != NULL; count++) {
+ SFMod* mod_src = (SFMod *) r->data;
+ int type;
+ fluid_mod_t* mod_dest;
+
+ mod_dest = fluid_mod_new();
+ if (mod_dest == NULL){
+ return FLUID_FAILED;
+ }
+
+ mod_dest->next = NULL; /* pointer to next modulator, this is the end of the list now.*/
+
+ /* *** Amount *** */
+ mod_dest->amount = mod_src->amount;
+
+ /* *** Source *** */
+ mod_dest->src1 = mod_src->src & 127; /* index of source 1, seven-bit value, SF2.01 section 8.2, page 50 */
+ mod_dest->flags1 = 0;
+
+ /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/
+ if (mod_src->src & (1<<7)){
+ mod_dest->flags1 |= FLUID_MOD_CC;
+ } else {
+ mod_dest->flags1 |= FLUID_MOD_GC;
+ }
+
+ /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/
+ if (mod_src->src & (1<<8)){
+ mod_dest->flags1 |= FLUID_MOD_NEGATIVE;
+ } else {
+ mod_dest->flags1 |= FLUID_MOD_POSITIVE;
+ }
+
+ /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/
+ if (mod_src->src & (1<<9)){
+ mod_dest->flags1 |= FLUID_MOD_BIPOLAR;
+ } else {
+ mod_dest->flags1 |= FLUID_MOD_UNIPOLAR;
+ }
+
+ /* modulator source types: SF2.01 section 8.2.1 page 52 */
+ type = (mod_src->src) >> 10;
+ type &= 63; /* type is a 6-bit value */
+ if (type == 0){
+ mod_dest->flags1 |= FLUID_MOD_LINEAR;
+ } else if (type == 1){
+ mod_dest->flags1 |= FLUID_MOD_CONCAVE;
+ } else if (type == 2){
+ mod_dest->flags1 |= FLUID_MOD_CONVEX;
+ } else if (type == 3){
+ mod_dest->flags1 |= FLUID_MOD_SWITCH;
+ } else {
+ /* This shouldn't happen - unknown type!
+ * Deactivate the modulator by setting the amount to 0. */
+ mod_dest->amount = 0;
+ }
+
+ /* *** Dest *** */
+ mod_dest->dest=mod_src->dest; /* index of controlled generator */
+
+ /* *** Amount source *** */
+ mod_dest->src2=mod_src->amtsrc & 127; /* index of source 2, seven-bit value, SF2.01 section 8.2, page 50 */
+ mod_dest->flags2 = 0;
+
+ /* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/
+ if (mod_src->amtsrc & (1<<7)){
+ mod_dest->flags2 |= FLUID_MOD_CC;
+ } else {
+ mod_dest->flags2 |= FLUID_MOD_GC;
+ }
+
+ /* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/
+ if (mod_src->amtsrc & (1<<8)){
+ mod_dest->flags2 |= FLUID_MOD_NEGATIVE;
+ } else {
+ mod_dest->flags2 |= FLUID_MOD_POSITIVE;
+ }
+
+ /* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/
+ if (mod_src->amtsrc & (1<<9)){
+ mod_dest->flags2 |= FLUID_MOD_BIPOLAR;
+ } else {
+ mod_dest->flags2 |= FLUID_MOD_UNIPOLAR;
+ }
+
+ /* modulator source types: SF2.01 section 8.2.1 page 52 */
+ type=(mod_src->amtsrc) >> 10;
+ type &= 63; /* type is a 6-bit value */
+ if (type == 0){
+ mod_dest->flags2 |= FLUID_MOD_LINEAR;
+ } else if (type == 1){
+ mod_dest->flags2 |= FLUID_MOD_CONCAVE;
+ } else if (type == 2){
+ mod_dest->flags2 |= FLUID_MOD_CONVEX;
+ } else if (type == 3){
+ mod_dest->flags2 |= FLUID_MOD_SWITCH;
+ } else {
+ /* This shouldn't happen - unknown type!
+ * Deactivate the modulator by setting the amount to 0. */
+ mod_dest->amount = 0;
+ }
+
+ /* *** Transform *** */
+ /* SF2.01 only uses the 'linear' transform (0).
+ * Deactivate the modulator by setting the amount to 0 in any other case.
+ */
+ if (mod_src->trans !=0){
+ mod_dest->amount = 0;
+ }
+
+ /* Store the new modulator in the zone
+ * The order of modulators will make a difference, at least in an instrument context:
+ * The second modulator overwrites the first one, if they only differ in amount. */
+ if (count == 0){
+ zone->mod=mod_dest;
+ } else {
+ fluid_mod_t * last_mod=zone->mod;
+ /* Find the end of the list */
+ while (last_mod->next != NULL){
+ last_mod=last_mod->next;
+ }
+ last_mod->next=mod_dest;
+ }
+
+ r = fluid_list_next(r);
+ } /* foreach modulator */
+ return FLUID_OK;
+}
+
+/*
+ * fluid_inst_zone_get_sample
+ */
+fluid_sample_t*
+fluid_inst_zone_get_sample(fluid_inst_zone_t* zone)
+{
+ return zone->sample;
+}
+
+/*
+ * fluid_inst_zone_inside_range
+ */
+int
+fluid_inst_zone_inside_range(fluid_inst_zone_t* zone, int key, int vel)
+{
+ return ((zone->keylo <= key) &&
+ (zone->keyhi >= key) &&
+ (zone->vello <= vel) &&
+ (zone->velhi >= vel));
+}
+
+/***************************************************************
+ *
+ * SAMPLE
+ */
+
+/*
+ * new_fluid_sample
+ */
+fluid_sample_t*
+new_fluid_sample()
+{
+ fluid_sample_t* sample = NULL;
+
+ sample = FLUID_NEW(fluid_sample_t);
+ if (sample == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ memset(sample, 0, sizeof(fluid_sample_t));
+ sample->valid = 1;
+
+ return sample;
+}
+
+/*
+ * delete_fluid_sample
+ */
+int
+delete_fluid_sample(fluid_sample_t* sample)
+{
+ FLUID_FREE(sample);
+ return FLUID_OK;
+}
+
+/*
+ * fluid_sample_in_rom
+ */
+int
+fluid_sample_in_rom(fluid_sample_t* sample)
+{
+ return (sample->sampletype & FLUID_SAMPLETYPE_ROM);
+}
+
+/*
+ * fluid_sample_import_sfont
+ */
+int
+fluid_sample_import_sfont(fluid_sample_t* sample, SFSample* sfsample, fluid_defsfont_t* sfont)
+{
+ FLUID_STRCPY(sample->name, sfsample->name);
+ sample->data = sfont->sampledata;
+ sample->start = sfsample->start;
+ sample->end = sfsample->start + sfsample->end;
+ sample->loopstart = sfsample->start + sfsample->loopstart;
+ sample->loopend = sfsample->start + sfsample->loopend;
+ sample->samplerate = sfsample->samplerate;
+ sample->origpitch = sfsample->origpitch;
+ sample->pitchadj = sfsample->pitchadj;
+ sample->sampletype = sfsample->sampletype;
+
+ if (sample->sampletype & FLUID_SAMPLETYPE_ROM) {
+ sample->valid = 0;
+ FLUID_LOG(FLUID_WARN, "Ignoring sample %s: can't use ROM samples", sample->name);
+ }
+ if (sample->end - sample->start < 8) {
+ sample->valid = 0;
+ FLUID_LOG(FLUID_WARN, "Ignoring sample %s: too few sample data points", sample->name);
+ } else {
+/* if (sample->loopstart < sample->start + 8) { */
+/* FLUID_LOG(FLUID_WARN, "Fixing sample %s: at least 8 data points required before loop start", sample->name); */
+/* sample->loopstart = sample->start + 8; */
+/* } */
+/* if (sample->loopend > sample->end - 8) { */
+/* FLUID_LOG(FLUID_WARN, "Fixing sample %s: at least 8 data points required after loop end", sample->name); */
+/* sample->loopend = sample->end - 8; */
+/* } */
+ }
+ return FLUID_OK;
+}
+
+
+
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+
+
+
+/*=================================sfload.c========================
+ Borrowed from Smurf SoundFont Editor by Josh Green
+ =================================================================*/
+
+/*
+ functions for loading data from sfont files, with appropriate byte swapping
+ on big endian machines. Sfont IDs are not swapped because the ID read is
+ equivalent to the matching ID list in memory regardless of LE/BE machine
+*/
+
+#if FLUID_IS_BIG_ENDIAN
+
+#define READCHUNK(var,fd) G_STMT_START { \
+ if (!safe_fread(var, 8, fd)) \
+ return(FAIL); \
+ ((SFChunk *)(var))->size = GUINT32_FROM_LE(((SFChunk *)(var))->size); \
+} G_STMT_END
+
+#define READD(var,fd) G_STMT_START { \
+ unsigned int _temp; \
+ if (!safe_fread(&_temp, 4, fd)) \
+ return(FAIL); \
+ var = GINT32_FROM_LE(_temp); \
+} G_STMT_END
+
+#define READW(var,fd) G_STMT_START { \
+ unsigned short _temp; \
+ if (!safe_fread(&_temp, 2, fd)) \
+ return(FAIL); \
+ var = GINT16_FROM_LE(_temp); \
+} G_STMT_END
+
+#else
+
+#define READCHUNK(var,fd) G_STMT_START { \
+ if (!safe_fread(var, 8, fd)) \
+ return(FAIL); \
+ ((SFChunk *)(var))->size = GUINT32_FROM_LE(((SFChunk *)(var))->size); \
+} G_STMT_END
+
+#define READD(var,fd) G_STMT_START { \
+ unsigned int _temp; \
+ if (!safe_fread(&_temp, 4, fd)) \
+ return(FAIL); \
+ var = GINT32_FROM_LE(_temp); \
+} G_STMT_END
+
+#define READW(var,fd) G_STMT_START { \
+ unsigned short _temp; \
+ if (!safe_fread(&_temp, 2, fd)) \
+ return(FAIL); \
+ var = GINT16_FROM_LE(_temp); \
+} G_STMT_END
+
+#endif
+
+
+#define READID(var,fd) G_STMT_START { \
+ if (!safe_fread(var, 4, fd)) \
+ return(FAIL); \
+} G_STMT_END
+
+#define READSTR(var,fd) G_STMT_START { \
+ if (!safe_fread(var, 20, fd)) \
+ return(FAIL); \
+ (*var)[20] = '\0'; \
+} G_STMT_END
+
+#define READB(var,fd) G_STMT_START { \
+ if (!safe_fread(&var, 1, fd)) \
+ return(FAIL); \
+} G_STMT_END
+
+#define FSKIP(size,fd) G_STMT_START { \
+ if (!safe_fseek(fd, size, SEEK_CUR)) \
+ return(FAIL); \
+} G_STMT_END
+
+#define FSKIPW(fd) G_STMT_START { \
+ if (!safe_fseek(fd, 2, SEEK_CUR)) \
+ return(FAIL); \
+} G_STMT_END
+
+/* removes and advances a fluid_list_t pointer */
+#define SLADVREM(list, item) G_STMT_START { \
+ fluid_list_t *_temp = item; \
+ item = fluid_list_next(item); \
+ list = fluid_list_remove_link(list, _temp); \
+ delete1_fluid_list(_temp); \
+} G_STMT_END
+
+static int chunkid (unsigned int id);
+static int load_body (unsigned int size, SFData * sf, FILE * fd);
+static int read_listchunk (SFChunk * chunk, FILE * fd);
+static int process_info (int size, SFData * sf, FILE * fd);
+static int process_sdta (unsigned int size, SFData * sf, FILE * fd);
+static int pdtahelper (unsigned int expid, unsigned int reclen, SFChunk * chunk,
+ int * size, FILE * fd);
+static int process_pdta (int size, SFData * sf, FILE * fd);
+static int load_phdr (int size, SFData * sf, FILE * fd);
+static int load_pbag (int size, SFData * sf, FILE * fd);
+static int load_pmod (int size, SFData * sf, FILE * fd);
+static int load_pgen (int size, SFData * sf, FILE * fd);
+static int load_ihdr (int size, SFData * sf, FILE * fd);
+static int load_ibag (int size, SFData * sf, FILE * fd);
+static int load_imod (int size, SFData * sf, FILE * fd);
+static int load_igen (int size, SFData * sf, FILE * fd);
+static int load_shdr (unsigned int size, SFData * sf, FILE * fd);
+static int fixup_pgen (SFData * sf);
+static int fixup_igen (SFData * sf);
+static int fixup_sample (SFData * sf);
+
+char idlist[] = {
+ "RIFFLISTsfbkINFOsdtapdtaifilisngINAMiromiverICRDIENGIPRD"
+ "ICOPICMTISFTsnamsmplphdrpbagpmodpgeninstibagimodigenshdr"
+};
+
+static unsigned int sdtachunk_size;
+
+/* sound font file load functions */
+static int
+chunkid (unsigned int id)
+{
+ unsigned int i;
+ unsigned int *p;
+
+ p = (unsigned int *) & idlist;
+ for (i = 0; i < sizeof (idlist) / sizeof (int); i++, p += 1)
+ if (*p == id)
+ return (i + 1);
+
+ return (UNKN_ID);
+}
+
+SFData *
+sfload_file (const char * fname)
+{
+ SFData *sf = NULL;
+ FILE *fd;
+ int fsize = 0;
+ int err = FALSE;
+
+ if (!(fd = fopen (fname, "rb")))
+ {
+ FLUID_LOG (FLUID_ERR, _("Unable to open file \"%s\""), fname);
+ return (NULL);
+ }
+
+ if (!(sf = FLUID_NEW (SFData)))
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ fclose(fd);
+ err = TRUE;
+ }
+
+ if (!err)
+ {
+ memset (sf, 0, sizeof (SFData)); /* zero sfdata */
+ sf->fname = FLUID_STRDUP (fname); /* copy file name */
+ sf->sffd = fd;
+ }
+
+ /* get size of file */
+ if (!err && fseek (fd, 0L, SEEK_END) == -1)
+ { /* seek to end of file */
+ err = TRUE;
+ FLUID_LOG (FLUID_ERR, _("Seek to end of file failed"));
+ }
+ if (!err && (fsize = ftell (fd)) == -1)
+ { /* position = size */
+ err = TRUE;
+ FLUID_LOG (FLUID_ERR, _("Get end of file position failed"));
+ }
+ if (!err)
+ rewind (fd);
+
+ if (!err && !load_body (fsize, sf, fd))
+ err = TRUE; /* load the sfont */
+
+ if (err)
+ {
+ if (sf)
+ sfont_close (sf);
+ return (NULL);
+ }
+
+ return (sf);
+}
+
+static int
+load_body (unsigned int size, SFData * sf, FILE * fd)
+{
+ SFChunk chunk;
+
+ READCHUNK (&chunk, fd); /* load RIFF chunk */
+ if (chunkid (chunk.id) != RIFF_ID) { /* error if not RIFF */
+ FLUID_LOG (FLUID_ERR, _("Not a RIFF file"));
+ return (FAIL);
+ }
+
+ READID (&chunk.id, fd); /* load file ID */
+ if (chunkid (chunk.id) != SFBK_ID) { /* error if not SFBK_ID */
+ FLUID_LOG (FLUID_ERR, _("Not a SoundFont file"));
+ return (FAIL);
+ }
+
+ if (chunk.size != size - 8) {
+ gerr (ErrCorr, _("SoundFont file size mismatch"));
+ return (FAIL);
+ }
+
+ /* Process INFO block */
+ if (!read_listchunk (&chunk, fd))
+ return (FAIL);
+ if (chunkid (chunk.id) != INFO_ID)
+ return (gerr (ErrCorr, _("Invalid ID found when expecting INFO chunk")));
+ if (!process_info (chunk.size, sf, fd))
+ return (FAIL);
+
+ /* Process sample chunk */
+ if (!read_listchunk (&chunk, fd))
+ return (FAIL);
+ if (chunkid (chunk.id) != SDTA_ID)
+ return (gerr (ErrCorr,
+ _("Invalid ID found when expecting SAMPLE chunk")));
+ if (!process_sdta (chunk.size, sf, fd))
+ return (FAIL);
+
+ /* process HYDRA chunk */
+ if (!read_listchunk (&chunk, fd))
+ return (FAIL);
+ if (chunkid (chunk.id) != PDTA_ID)
+ return (gerr (ErrCorr, _("Invalid ID found when expecting HYDRA chunk")));
+ if (!process_pdta (chunk.size, sf, fd))
+ return (FAIL);
+
+ if (!fixup_pgen (sf))
+ return (FAIL);
+ if (!fixup_igen (sf))
+ return (FAIL);
+ if (!fixup_sample (sf))
+ return (FAIL);
+
+ /* sort preset list by bank, preset # */
+ sf->preset = fluid_list_sort (sf->preset,
+ (fluid_compare_func_t) sfont_preset_compare_func);
+
+ return (OK);
+}
+
+static int
+read_listchunk (SFChunk * chunk, FILE * fd)
+{
+ READCHUNK (chunk, fd); /* read list chunk */
+ if (chunkid (chunk->id) != LIST_ID) /* error if ! list chunk */
+ return (gerr (ErrCorr, _("Invalid chunk id in level 0 parse")));
+ READID (&chunk->id, fd); /* read id string */
+ chunk->size -= 4;
+ return (OK);
+}
+
+static int
+process_info (int size, SFData * sf, FILE * fd)
+{
+ SFChunk chunk;
+ unsigned char id;
+ char *item;
+ unsigned short ver;
+
+ while (size > 0)
+ {
+ READCHUNK (&chunk, fd);
+ size -= 8;
+
+ id = chunkid (chunk.id);
+
+ if (id == IFIL_ID)
+ { /* sound font version chunk? */
+ if (chunk.size != 4)
+ return (gerr (ErrCorr,
+ _("Sound font version info chunk has invalid size")));
+
+ READW (ver, fd);
+ sf->version.major = ver;
+ READW (ver, fd);
+ sf->version.minor = ver;
+
+ if (sf->version.major < 2) {
+ FLUID_LOG (FLUID_ERR,
+ _("Sound font version is %d.%d which is not"
+ " supported, convert to version 2.0x"),
+ sf->version.major,
+ sf->version.minor);
+ return (FAIL);
+ }
+
+ if (sf->version.major > 2) {
+ FLUID_LOG (FLUID_WARN,
+ _("Sound font version is %d.%d which is newer than"
+ " what this version of FLUID Synth was designed for (v2.0x)"),
+ sf->version.major,
+ sf->version.minor);
+ return (FAIL);
+ }
+ }
+ else if (id == IVER_ID)
+ { /* ROM version chunk? */
+ if (chunk.size != 4)
+ return (gerr (ErrCorr,
+ _("ROM version info chunk has invalid size")));
+
+ READW (ver, fd);
+ sf->romver.major = ver;
+ READW (ver, fd);
+ sf->romver.minor = ver;
+ }
+ else if (id != UNKN_ID)
+ {
+ if ((id != ICMT_ID && chunk.size > 256) || (chunk.size > 65536)
+ || (chunk.size % 2))
+ return (gerr (ErrCorr,
+ _("INFO sub chunk %.4s has invalid chunk size"
+ " of %d bytes"), &chunk.id, chunk.size));
+
+ /* alloc for chunk id and da chunk */
+ if (!(item = FLUID_MALLOC (chunk.size + 1)))
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return (FAIL);
+ }
+
+ /* attach to INFO list, sfont_close will cleanup if FAIL occurs */
+ sf->info = fluid_list_append (sf->info, item);
+
+ *(unsigned char *) item = id;
+ if (!safe_fread (&item[1], chunk.size, fd))
+ return (FAIL);
+
+ /* force terminate info item (don't forget uint8 info ID) */
+ *(item + chunk.size) = '\0';
+ }
+ else
+ return (gerr (ErrCorr, _("Invalid chunk id in INFO chunk")));
+ size -= chunk.size;
+ }
+
+ if (size < 0)
+ return (gerr (ErrCorr, _("INFO chunk size mismatch")));
+
+ return (OK);
+}
+
+static int
+process_sdta (unsigned int size, SFData * sf, FILE * fd)
+{
+ SFChunk chunk;
+
+ if (size == 0)
+ return (OK); /* no sample data? */
+
+ /* read sub chunk */
+ READCHUNK (&chunk, fd);
+ size -= 8;
+
+ if (chunkid (chunk.id) != SMPL_ID)
+ return (gerr (ErrCorr,
+ _("Expected SMPL chunk found invalid id instead")));
+
+ /* SDTA chunk may also contain sm24 chunk for 24 bit samples
+ * (not yet supported), only an error if SMPL chunk size is
+ * greater than SDTA. */
+ if (chunk.size > size)
+ return (gerr (ErrCorr, _("SDTA chunk size mismatch")));
+
+ /* sample data follows */
+ sf->samplepos = ftell (fd);
+
+ /* used in fixup_sample() to check validity of sample headers */
+ sdtachunk_size = chunk.size;
+ sf->samplesize = chunk.size;
+
+ FSKIP (size, fd);
+
+ return (OK);
+}
+
+static int
+pdtahelper (unsigned int expid, unsigned int reclen, SFChunk * chunk,
+ int * size, FILE * fd)
+{
+ unsigned int id;
+ char *expstr;
+
+ expstr = CHNKIDSTR (expid); /* in case we need it */
+
+ READCHUNK (chunk, fd);
+ *size -= 8;
+
+ if ((id = chunkid (chunk->id)) != expid)
+ return (gerr (ErrCorr, _("Expected"
+ " PDTA sub-chunk \"%.4s\" found invalid id instead"), expstr));
+
+ if (chunk->size % reclen) /* valid chunk size? */
+ return (gerr (ErrCorr,
+ _("\"%.4s\" chunk size is not a multiple of %d bytes"), expstr,
+ reclen));
+ if ((*size -= chunk->size) < 0)
+ return (gerr (ErrCorr,
+ _("\"%.4s\" chunk size exceeds remaining PDTA chunk size"), expstr));
+ return (OK);
+}
+
+static int
+process_pdta (int size, SFData * sf, FILE * fd)
+{
+ SFChunk chunk;
+
+ if (!pdtahelper (PHDR_ID, SFPHDRSIZE, &chunk, &size, fd))
+ return (FAIL);
+ if (!load_phdr (chunk.size, sf, fd))
+ return (FAIL);
+
+ if (!pdtahelper (PBAG_ID, SFBAGSIZE, &chunk, &size, fd))
+ return (FAIL);
+ if (!load_pbag (chunk.size, sf, fd))
+ return (FAIL);
+
+ if (!pdtahelper (PMOD_ID, SFMODSIZE, &chunk, &size, fd))
+ return (FAIL);
+ if (!load_pmod (chunk.size, sf, fd))
+ return (FAIL);
+
+ if (!pdtahelper (PGEN_ID, SFGENSIZE, &chunk, &size, fd))
+ return (FAIL);
+ if (!load_pgen (chunk.size, sf, fd))
+ return (FAIL);
+
+ if (!pdtahelper (IHDR_ID, SFIHDRSIZE, &chunk, &size, fd))
+ return (FAIL);
+ if (!load_ihdr (chunk.size, sf, fd))
+ return (FAIL);
+
+ if (!pdtahelper (IBAG_ID, SFBAGSIZE, &chunk, &size, fd))
+ return (FAIL);
+ if (!load_ibag (chunk.size, sf, fd))
+ return (FAIL);
+
+ if (!pdtahelper (IMOD_ID, SFMODSIZE, &chunk, &size, fd))
+ return (FAIL);
+ if (!load_imod (chunk.size, sf, fd))
+ return (FAIL);
+
+ if (!pdtahelper (IGEN_ID, SFGENSIZE, &chunk, &size, fd))
+ return (FAIL);
+ if (!load_igen (chunk.size, sf, fd))
+ return (FAIL);
+
+ if (!pdtahelper (SHDR_ID, SFSHDRSIZE, &chunk, &size, fd))
+ return (FAIL);
+ if (!load_shdr (chunk.size, sf, fd))
+ return (FAIL);
+
+ return (OK);
+}
+
+/* preset header loader */
+static int
+load_phdr (int size, SFData * sf, FILE * fd)
+{
+ int i, i2;
+ SFPreset *p, *pr = NULL; /* ptr to current & previous preset */
+ unsigned short zndx, pzndx = 0;
+
+ if (size % SFPHDRSIZE || size == 0)
+ return (gerr (ErrCorr, _("Preset header chunk size is invalid")));
+
+ i = size / SFPHDRSIZE - 1;
+ if (i == 0)
+ { /* at least one preset + term record */
+ FLUID_LOG (FLUID_WARN, _("File contains no presets"));
+ FSKIP (SFPHDRSIZE, fd);
+ return (OK);
+ }
+
+ for (; i > 0; i--)
+ { /* load all preset headers */
+ p = FLUID_NEW (SFPreset);
+ sf->preset = fluid_list_append (sf->preset, p);
+ p->zone = NULL; /* In case of failure, sfont_close can cleanup */
+ READSTR (&p->name, fd); /* possible read failure ^ */
+ READW (p->prenum, fd);
+ READW (p->bank, fd);
+ READW (zndx, fd);
+ READD (p->libr, fd);
+ READD (p->genre, fd);
+ READD (p->morph, fd);
+
+ if (pr)
+ { /* not first preset? */
+ if (zndx < pzndx)
+ return (gerr (ErrCorr, _("Preset header indices not monotonic")));
+ i2 = zndx - pzndx;
+ while (i2--)
+ {
+ pr->zone = fluid_list_prepend (pr->zone, NULL);
+ }
+ }
+ else if (zndx > 0) /* 1st preset, warn if ofs >0 */
+ FLUID_LOG (FLUID_WARN, _("%d preset zones not referenced, discarding"), zndx);
+ pr = p; /* update preset ptr */
+ pzndx = zndx;
+ }
+
+ FSKIP (24, fd);
+ READW (zndx, fd); /* Read terminal generator index */
+ FSKIP (12, fd);
+
+ if (zndx < pzndx)
+ return (gerr (ErrCorr, _("Preset header indices not monotonic")));
+ i2 = zndx - pzndx;
+ while (i2--)
+ {
+ pr->zone = fluid_list_prepend (pr->zone, NULL);
+ }
+
+ return (OK);
+}
+
+/* preset bag loader */
+static int
+load_pbag (int size, SFData * sf, FILE * fd)
+{
+ fluid_list_t *p, *p2;
+ SFZone *z, *pz = NULL;
+ unsigned short genndx, modndx;
+ unsigned short pgenndx = 0, pmodndx = 0;
+ unsigned short i;
+
+ if (size % SFBAGSIZE || size == 0) /* size is multiple of SFBAGSIZE? */
+ return (gerr (ErrCorr, _("Preset bag chunk size is invalid")));
+
+ p = sf->preset;
+ while (p)
+ { /* traverse through presets */
+ p2 = ((SFPreset *) (p->data))->zone;
+ while (p2)
+ { /* traverse preset's zones */
+ if ((size -= SFBAGSIZE) < 0)
+ return (gerr (ErrCorr, _("Preset bag chunk size mismatch")));
+ z = FLUID_NEW (SFZone);
+ p2->data = z;
+ z->gen = NULL; /* Init gen and mod before possible failure, */
+ z->mod = NULL; /* to ensure proper cleanup (sfont_close) */
+ READW (genndx, fd); /* possible read failure ^ */
+ READW (modndx, fd);
+ z->instsamp = NULL;
+
+ if (pz)
+ { /* if not first zone */
+ if (genndx < pgenndx)
+ return (gerr (ErrCorr,
+ _("Preset bag generator indices not monotonic")));
+ if (modndx < pmodndx)
+ return (gerr (ErrCorr,
+ _("Preset bag modulator indices not monotonic")));
+ i = genndx - pgenndx;
+ while (i--)
+ pz->gen = fluid_list_prepend (pz->gen, NULL);
+ i = modndx - pmodndx;
+ while (i--)
+ pz->mod = fluid_list_prepend (pz->mod, NULL);
+ }
+ pz = z; /* update previous zone ptr */
+ pgenndx = genndx; /* update previous zone gen index */
+ pmodndx = modndx; /* update previous zone mod index */
+ p2 = fluid_list_next (p2);
+ }
+ p = fluid_list_next (p);
+ }
+
+ size -= SFBAGSIZE;
+ if (size != 0)
+ return (gerr (ErrCorr, _("Preset bag chunk size mismatch")));
+
+ READW (genndx, fd);
+ READW (modndx, fd);
+
+ if (!pz)
+ {
+ if (genndx > 0)
+ FLUID_LOG (FLUID_WARN, _("No preset generators and terminal index not 0"));
+ if (modndx > 0)
+ FLUID_LOG (FLUID_WARN, _("No preset modulators and terminal index not 0"));
+ return (OK);
+ }
+
+ if (genndx < pgenndx)
+ return (gerr (ErrCorr, _("Preset bag generator indices not monotonic")));
+ if (modndx < pmodndx)
+ return (gerr (ErrCorr, _("Preset bag modulator indices not monotonic")));
+ i = genndx - pgenndx;
+ while (i--)
+ pz->gen = fluid_list_prepend (pz->gen, NULL);
+ i = modndx - pmodndx;
+ while (i--)
+ pz->mod = fluid_list_prepend (pz->mod, NULL);
+
+ return (OK);
+}
+
+/* preset modulator loader */
+static int
+load_pmod (int size, SFData * sf, FILE * fd)
+{
+ fluid_list_t *p, *p2, *p3;
+ SFMod *m;
+
+ p = sf->preset;
+ while (p)
+ { /* traverse through all presets */
+ p2 = ((SFPreset *) (p->data))->zone;
+ while (p2)
+ { /* traverse this preset's zones */
+ p3 = ((SFZone *) (p2->data))->mod;
+ while (p3)
+ { /* load zone's modulators */
+ if ((size -= SFMODSIZE) < 0)
+ return (gerr (ErrCorr,
+ _("Preset modulator chunk size mismatch")));
+ m = FLUID_NEW (SFMod);
+ p3->data = m;
+ READW (m->src, fd);
+ READW (m->dest, fd);
+ READW (m->amount, fd);
+ READW (m->amtsrc, fd);
+ READW (m->trans, fd);
+ p3 = fluid_list_next (p3);
+ }
+ p2 = fluid_list_next (p2);
+ }
+ p = fluid_list_next (p);
+ }
+
+ /*
+ If there isn't even a terminal record
+ Hmmm, the specs say there should be one, but..
+ */
+ if (size == 0)
+ return (OK);
+
+ size -= SFMODSIZE;
+ if (size != 0)
+ return (gerr (ErrCorr, _("Preset modulator chunk size mismatch")));
+ FSKIP (SFMODSIZE, fd); /* terminal mod */
+
+ return (OK);
+}
+
+/* -------------------------------------------------------------------
+ * preset generator loader
+ * generator (per preset) loading rules:
+ * Zones with no generators or modulators shall be annihilated
+ * Global zone must be 1st zone, discard additional ones (instrumentless zones)
+ *
+ * generator (per zone) loading rules (in order of decreasing precedence):
+ * KeyRange is 1st in list (if exists), else discard
+ * if a VelRange exists only preceded by a KeyRange, else discard
+ * if a generator follows an instrument discard it
+ * if a duplicate generator exists replace previous one
+ * ------------------------------------------------------------------- */
+static int
+load_pgen (int size, SFData * sf, FILE * fd)
+{
+ fluid_list_t *p, *p2, *p3, *dup, **hz = NULL;
+ SFZone *z;
+ SFGen *g;
+ SFGenAmount genval;
+ unsigned short genid;
+ int level, skip, drop, gzone, discarded;
+
+ p = sf->preset;
+ while (p)
+ { /* traverse through all presets */
+ gzone = FALSE;
+ discarded = FALSE;
+ p2 = ((SFPreset *) (p->data))->zone;
+ if (p2)
+ hz = &p2;
+ while (p2)
+ { /* traverse preset's zones */
+ level = 0;
+ z = (SFZone *) (p2->data);
+ p3 = z->gen;
+ while (p3)
+ { /* load zone's generators */
+ dup = NULL;
+ skip = FALSE;
+ drop = FALSE;
+ if ((size -= SFGENSIZE) < 0)
+ return (gerr (ErrCorr,
+ _("Preset generator chunk size mismatch")));
+
+ READW (genid, fd);
+
+ if (genid == Gen_KeyRange)
+ { /* nothing precedes */
+ if (level == 0)
+ {
+ level = 1;
+ READB (genval.range.lo, fd);
+ READB (genval.range.hi, fd);
+ }
+ else
+ skip = TRUE;
+ }
+ else if (genid == Gen_VelRange)
+ { /* only KeyRange precedes */
+ if (level <= 1)
+ {
+ level = 2;
+ READB (genval.range.lo, fd);
+ READB (genval.range.hi, fd);
+ }
+ else
+ skip = TRUE;
+ }
+ else if (genid == Gen_Instrument)
+ { /* inst is last gen */
+ level = 3;
+ READW (genval.uword, fd);
+ ((SFZone *) (p2->data))->instsamp = GINT_TO_POINTER (genval.uword + 1);
+ break; /* break out of generator loop */
+ }
+ else
+ {
+ level = 2;
+ if (gen_validp (genid))
+ { /* generator valid? */
+ READW (genval.sword, fd);
+ dup = gen_inlist (genid, z->gen);
+ }
+ else
+ skip = TRUE;
+ }
+
+ if (!skip)
+ {
+ if (!dup)
+ { /* if gen ! dup alloc new */
+ g = FLUID_NEW (SFGen);
+ p3->data = g;
+ g->id = genid;
+ }
+ else
+ {
+ g = (SFGen *) (dup->data); /* ptr to orig gen */
+ drop = TRUE;
+ }
+ g->amount = genval;
+ }
+ else
+ { /* Skip this generator */
+ discarded = TRUE;
+ drop = TRUE;
+ FSKIPW (fd);
+ }
+
+ if (!drop)
+ p3 = fluid_list_next (p3); /* next gen */
+ else
+ SLADVREM (z->gen, p3); /* drop place holder */
+
+ } /* generator loop */
+
+ if (level == 3)
+ SLADVREM (z->gen, p3); /* zone has inst? */
+ else
+ { /* congratulations its a global zone */
+ if (!gzone)
+ { /* Prior global zones? */
+ gzone = TRUE;
+
+ /* if global zone is not 1st zone, relocate */
+ if (*hz != p2)
+ {
+ void* save = p2->data;
+ FLUID_LOG (FLUID_WARN,
+ _("Preset \"%s\": Global zone is not first zone"),
+ ((SFPreset *) (p->data))->name);
+ SLADVREM (*hz, p2);
+ *hz = fluid_list_prepend (*hz, save);
+ continue;
+ }
+ }
+ else
+ { /* previous global zone exists, discard */
+ FLUID_LOG (FLUID_WARN,
+ _("Preset \"%s\": Discarding invalid global zone"),
+ ((SFPreset *) (p->data))->name);
+ sfont_zone_delete (sf, hz, (SFZone *) (p2->data));
+ }
+ }
+
+ while (p3)
+ { /* Kill any zones following an instrument */
+ discarded = TRUE;
+ if ((size -= SFGENSIZE) < 0)
+ return (gerr (ErrCorr,
+ _("Preset generator chunk size mismatch")));
+ FSKIP (SFGENSIZE, fd);
+ SLADVREM (z->gen, p3);
+ }
+
+ p2 = fluid_list_next (p2); /* next zone */
+ }
+ if (discarded)
+ FLUID_LOG(FLUID_WARN,
+ _("Preset \"%s\": Some invalid generators were discarded"),
+ ((SFPreset *) (p->data))->name);
+ p = fluid_list_next (p);
+ }
+
+ /* in case there isn't a terminal record */
+ if (size == 0)
+ return (OK);
+
+ size -= SFGENSIZE;
+ if (size != 0)
+ return (gerr (ErrCorr, _("Preset generator chunk size mismatch")));
+ FSKIP (SFGENSIZE, fd); /* terminal gen */
+
+ return (OK);
+}
+
+/* instrument header loader */
+static int
+load_ihdr (int size, SFData * sf, FILE * fd)
+{
+ int i, i2;
+ SFInst *p, *pr = NULL; /* ptr to current & previous instrument */
+ unsigned short zndx, pzndx = 0;
+
+ if (size % SFIHDRSIZE || size == 0) /* chunk size is valid? */
+ return (gerr (ErrCorr, _("Instrument header has invalid size")));
+
+ size = size / SFIHDRSIZE - 1;
+ if (size == 0)
+ { /* at least one preset + term record */
+ FLUID_LOG (FLUID_WARN, _("File contains no instruments"));
+ FSKIP (SFIHDRSIZE, fd);
+ return (OK);
+ }
+
+ for (i = 0; i < size; i++)
+ { /* load all instrument headers */
+ p = FLUID_NEW (SFInst);
+ sf->inst = fluid_list_append (sf->inst, p);
+ p->zone = NULL; /* For proper cleanup if fail (sfont_close) */
+ READSTR (&p->name, fd); /* Possible read failure ^ */
+ READW (zndx, fd);
+
+ if (pr)
+ { /* not first instrument? */
+ if (zndx < pzndx)
+ return (gerr (ErrCorr,
+ _("Instrument header indices not monotonic")));
+ i2 = zndx - pzndx;
+ while (i2--)
+ pr->zone = fluid_list_prepend (pr->zone, NULL);
+ }
+ else if (zndx > 0) /* 1st inst, warn if ofs >0 */
+ FLUID_LOG (FLUID_WARN, _("%d instrument zones not referenced, discarding"),
+ zndx);
+ pzndx = zndx;
+ pr = p; /* update instrument ptr */
+ }
+
+ FSKIP (20, fd);
+ READW (zndx, fd);
+
+ if (zndx < pzndx)
+ return (gerr (ErrCorr, _("Instrument header indices not monotonic")));
+ i2 = zndx - pzndx;
+ while (i2--)
+ pr->zone = fluid_list_prepend (pr->zone, NULL);
+
+ return (OK);
+}
+
+/* instrument bag loader */
+static int
+load_ibag (int size, SFData * sf, FILE * fd)
+{
+ fluid_list_t *p, *p2;
+ SFZone *z, *pz = NULL;
+ unsigned short genndx, modndx, pgenndx = 0, pmodndx = 0;
+ int i;
+
+ if (size % SFBAGSIZE || size == 0) /* size is multiple of SFBAGSIZE? */
+ return (gerr (ErrCorr, _("Instrument bag chunk size is invalid")));
+
+ p = sf->inst;
+ while (p)
+ { /* traverse through inst */
+ p2 = ((SFInst *) (p->data))->zone;
+ while (p2)
+ { /* load this inst's zones */
+ if ((size -= SFBAGSIZE) < 0)
+ return (gerr (ErrCorr, _("Instrument bag chunk size mismatch")));
+ z = FLUID_NEW (SFZone);
+ p2->data = z;
+ z->gen = NULL; /* In case of failure, */
+ z->mod = NULL; /* sfont_close can clean up */
+ READW (genndx, fd); /* READW = possible read failure */
+ READW (modndx, fd);
+ z->instsamp = NULL;
+
+ if (pz)
+ { /* if not first zone */
+ if (genndx < pgenndx)
+ return (gerr (ErrCorr,
+ _("Instrument generator indices not monotonic")));
+ if (modndx < pmodndx)
+ return (gerr (ErrCorr,
+ _("Instrument modulator indices not monotonic")));
+ i = genndx - pgenndx;
+ while (i--)
+ pz->gen = fluid_list_prepend (pz->gen, NULL);
+ i = modndx - pmodndx;
+ while (i--)
+ pz->mod = fluid_list_prepend (pz->mod, NULL);
+ }
+ pz = z; /* update previous zone ptr */
+ pgenndx = genndx;
+ pmodndx = modndx;
+ p2 = fluid_list_next (p2);
+ }
+ p = fluid_list_next (p);
+ }
+
+ size -= SFBAGSIZE;
+ if (size != 0)
+ return (gerr (ErrCorr, _("Instrument chunk size mismatch")));
+
+ READW (genndx, fd);
+ READW (modndx, fd);
+
+ if (!pz)
+ { /* in case that all are no zoners */
+ if (genndx > 0)
+ FLUID_LOG (FLUID_WARN,
+ _("No instrument generators and terminal index not 0"));
+ if (modndx > 0)
+ FLUID_LOG (FLUID_WARN,
+ _("No instrument modulators and terminal index not 0"));
+ return (OK);
+ }
+
+ if (genndx < pgenndx)
+ return (gerr (ErrCorr, _("Instrument generator indices not monotonic")));
+ if (modndx < pmodndx)
+ return (gerr (ErrCorr, _("Instrument modulator indices not monotonic")));
+ i = genndx - pgenndx;
+ while (i--)
+ pz->gen = fluid_list_prepend (pz->gen, NULL);
+ i = modndx - pmodndx;
+ while (i--)
+ pz->mod = fluid_list_prepend (pz->mod, NULL);
+
+ return (OK);
+}
+
+/* instrument modulator loader */
+static int
+load_imod (int size, SFData * sf, FILE * fd)
+{
+ fluid_list_t *p, *p2, *p3;
+ SFMod *m;
+
+ p = sf->inst;
+ while (p)
+ { /* traverse through all inst */
+ p2 = ((SFInst *) (p->data))->zone;
+ while (p2)
+ { /* traverse this inst's zones */
+ p3 = ((SFZone *) (p2->data))->mod;
+ while (p3)
+ { /* load zone's modulators */
+ if ((size -= SFMODSIZE) < 0)
+ return (gerr (ErrCorr,
+ _("Instrument modulator chunk size mismatch")));
+ m = FLUID_NEW (SFMod);
+ p3->data = m;
+ READW (m->src, fd);
+ READW (m->dest, fd);
+ READW (m->amount, fd);
+ READW (m->amtsrc, fd);
+ READW (m->trans, fd);
+ p3 = fluid_list_next (p3);
+ }
+ p2 = fluid_list_next (p2);
+ }
+ p = fluid_list_next (p);
+ }
+
+ /*
+ If there isn't even a terminal record
+ Hmmm, the specs say there should be one, but..
+ */
+ if (size == 0)
+ return (OK);
+
+ size -= SFMODSIZE;
+ if (size != 0)
+ return (gerr (ErrCorr, _("Instrument modulator chunk size mismatch")));
+ FSKIP (SFMODSIZE, fd); /* terminal mod */
+
+ return (OK);
+}
+
+/* load instrument generators (see load_pgen for loading rules) */
+static int
+load_igen (int size, SFData * sf, FILE * fd)
+{
+ fluid_list_t *p, *p2, *p3, *dup, **hz = NULL;
+ SFZone *z;
+ SFGen *g;
+ SFGenAmount genval;
+ unsigned short genid;
+ int level, skip, drop, gzone, discarded;
+
+ p = sf->inst;
+ while (p)
+ { /* traverse through all instruments */
+ gzone = FALSE;
+ discarded = FALSE;
+ p2 = ((SFInst *) (p->data))->zone;
+ if (p2)
+ hz = &p2;
+ while (p2)
+ { /* traverse this instrument's zones */
+ level = 0;
+ z = (SFZone *) (p2->data);
+ p3 = z->gen;
+ while (p3)
+ { /* load zone's generators */
+ dup = NULL;
+ skip = FALSE;
+ drop = FALSE;
+ if ((size -= SFGENSIZE) < 0)
+ return (gerr (ErrCorr, _("IGEN chunk size mismatch")));
+
+ READW (genid, fd);
+
+ if (genid == Gen_KeyRange)
+ { /* nothing precedes */
+ if (level == 0)
+ {
+ level = 1;
+ READB (genval.range.lo, fd);
+ READB (genval.range.hi, fd);
+ }
+ else
+ skip = TRUE;
+ }
+ else if (genid == Gen_VelRange)
+ { /* only KeyRange precedes */
+ if (level <= 1)
+ {
+ level = 2;
+ READB (genval.range.lo, fd);
+ READB (genval.range.hi, fd);
+ }
+ else
+ skip = TRUE;
+ }
+ else if (genid == Gen_SampleId)
+ { /* sample is last gen */
+ level = 3;
+ READW (genval.uword, fd);
+ ((SFZone *) (p2->data))->instsamp = GINT_TO_POINTER (genval.uword + 1);
+ break; /* break out of generator loop */
+ }
+ else
+ {
+ level = 2;
+ if (gen_valid (genid))
+ { /* gen valid? */
+ READW (genval.sword, fd);
+ dup = gen_inlist (genid, z->gen);
+ }
+ else
+ skip = TRUE;
+ }
+
+ if (!skip)
+ {
+ if (!dup)
+ { /* if gen ! dup alloc new */
+ g = FLUID_NEW (SFGen);
+ p3->data = g;
+ g->id = genid;
+ }
+ else
+ {
+ g = (SFGen *) (dup->data);
+ drop = TRUE;
+ }
+ g->amount = genval;
+ }
+ else
+ { /* skip this generator */
+ discarded = TRUE;
+ drop = TRUE;
+ FSKIPW (fd);
+ }
+
+ if (!drop)
+ p3 = fluid_list_next (p3); /* next gen */
+ else
+ SLADVREM (z->gen, p3);
+
+ } /* generator loop */
+
+ if (level == 3)
+ SLADVREM (z->gen, p3); /* zone has sample? */
+ else
+ { /* its a global zone */
+ if (!gzone)
+ {
+ gzone = TRUE;
+
+ /* if global zone is not 1st zone, relocate */
+ if (*hz != p2)
+ {
+ void* save = p2->data;
+ FLUID_LOG (FLUID_WARN,
+ _("Instrument \"%s\": Global zone is not first zone"),
+ ((SFPreset *) (p->data))->name);
+ SLADVREM (*hz, p2);
+ *hz = fluid_list_prepend (*hz, save);
+ continue;
+ }
+ }
+ else
+ { /* previous global zone exists, discard */
+ FLUID_LOG (FLUID_WARN,
+ _("Instrument \"%s\": Discarding invalid global zone"),
+ ((SFInst *) (p->data))->name);
+ sfont_zone_delete (sf, hz, (SFZone *) (p2->data));
+ }
+ }
+
+ while (p3)
+ { /* Kill any zones following a sample */
+ discarded = TRUE;
+ if ((size -= SFGENSIZE) < 0)
+ return (gerr (ErrCorr,
+ _("Instrument generator chunk size mismatch")));
+ FSKIP (SFGENSIZE, fd);
+ SLADVREM (z->gen, p3);
+ }
+
+ p2 = fluid_list_next (p2); /* next zone */
+ }
+ if (discarded)
+ FLUID_LOG(FLUID_WARN,
+ _("Instrument \"%s\": Some invalid generators were discarded"),
+ ((SFInst *) (p->data))->name);
+ p = fluid_list_next (p);
+ }
+
+ /* for those non-terminal record cases, grr! */
+ if (size == 0)
+ return (OK);
+
+ size -= SFGENSIZE;
+ if (size != 0)
+ return (gerr (ErrCorr, _("IGEN chunk size mismatch")));
+ FSKIP (SFGENSIZE, fd); /* terminal gen */
+
+ return (OK);
+}
+
+/* sample header loader */
+static int
+load_shdr (unsigned int size, SFData * sf, FILE * fd)
+{
+ unsigned int i;
+ SFSample *p;
+
+ if (size % SFSHDRSIZE || size == 0) /* size is multiple of SHDR size? */
+ return (gerr (ErrCorr, _("Sample header has invalid size")));
+
+ size = size / SFSHDRSIZE - 1;
+ if (size == 0)
+ { /* at least one sample + term record? */
+ FLUID_LOG (FLUID_WARN, _("File contains no samples"));
+ FSKIP (SFSHDRSIZE, fd);
+ return (OK);
+ }
+
+ /* load all sample headers */
+ for (i = 0; i < size; i++)
+ {
+ p = FLUID_NEW (SFSample);
+ sf->sample = fluid_list_append (sf->sample, p);
+ READSTR (&p->name, fd);
+ READD (p->start, fd);
+ READD (p->end, fd); /* - end, loopstart and loopend */
+ READD (p->loopstart, fd); /* - will be checked and turned into */
+ READD (p->loopend, fd); /* - offsets in fixup_sample() */
+ READD (p->samplerate, fd);
+ READB (p->origpitch, fd);
+ READB (p->pitchadj, fd);
+ FSKIPW (fd); /* skip sample link */
+ READW (p->sampletype, fd);
+ p->samfile = 0;
+ }
+
+ FSKIP (SFSHDRSIZE, fd); /* skip terminal shdr */
+
+ return (OK);
+}
+
+/* "fixup" (inst # -> inst ptr) instrument references in preset list */
+static int
+fixup_pgen (SFData * sf)
+{
+ fluid_list_t *p, *p2, *p3;
+ SFZone *z;
+ int i;
+
+ p = sf->preset;
+ while (p)
+ {
+ p2 = ((SFPreset *) (p->data))->zone;
+ while (p2)
+ { /* traverse this preset's zones */
+ z = (SFZone *) (p2->data);
+ if ((i = GPOINTER_TO_INT (z->instsamp)))
+ { /* load instrument # */
+ p3 = fluid_list_nth (sf->inst, i - 1);
+ if (!p3)
+ return (gerr (ErrCorr,
+ _("Preset %03d %03d: Invalid instrument reference"),
+ ((SFPreset *) (p->data))->bank,
+ ((SFPreset *) (p->data))->prenum));
+ z->instsamp = p3;
+ }
+ else
+ z->instsamp = NULL;
+ p2 = fluid_list_next (p2);
+ }
+ p = fluid_list_next (p);
+ }
+
+ return (OK);
+}
+
+/* "fixup" (sample # -> sample ptr) sample references in instrument list */
+static int
+fixup_igen (SFData * sf)
+{
+ fluid_list_t *p, *p2, *p3;
+ SFZone *z;
+ int i;
+
+ p = sf->inst;
+ while (p)
+ {
+ p2 = ((SFInst *) (p->data))->zone;
+ while (p2)
+ { /* traverse instrument's zones */
+ z = (SFZone *) (p2->data);
+ if ((i = GPOINTER_TO_INT (z->instsamp)))
+ { /* load sample # */
+ p3 = fluid_list_nth (sf->sample, i - 1);
+ if (!p3)
+ return (gerr (ErrCorr,
+ _("Instrument \"%s\": Invalid sample reference"),
+ ((SFInst *) (p->data))->name));
+ z->instsamp = p3;
+ }
+ p2 = fluid_list_next (p2);
+ }
+ p = fluid_list_next (p);
+ }
+
+ return (OK);
+}
+
+/* convert sample end, loopstart and loopend to offsets and check if valid */
+static int
+fixup_sample (SFData * sf)
+{
+ fluid_list_t *p;
+ SFSample *sam;
+
+ p = sf->sample;
+ while (p)
+ {
+ sam = (SFSample *) (p->data);
+
+ /* if sample is not a ROM sample and end is over the sample data chunk
+ or sam start is greater than 4 less than the end (at least 4 samples) */
+ if ((!(sam->sampletype & FLUID_SAMPLETYPE_ROM)
+ && sam->end > sdtachunk_size) || sam->start > (sam->end - 4))
+ {
+ FLUID_LOG (FLUID_WARN, _("Sample '%s' start/end file positions are invalid,"
+ " disabling and will not be saved"), sam->name);
+
+ /* disable sample by setting all sample markers to 0 */
+ sam->start = sam->end = sam->loopstart = sam->loopend = 0;
+
+ return (OK);
+ }
+ else if (sam->loopend > sam->end || sam->loopstart >= sam->loopend
+ || sam->loopstart <= sam->start)
+ { /* loop is fowled?? (cluck cluck :) */
+ /* can pad loop by 8 samples and ensure at least 4 for loop (2*8+4) */
+ if ((sam->end - sam->start) >= 20)
+ {
+ sam->loopstart = sam->start + 8;
+ sam->loopend = sam->end - 8;
+ }
+ else
+ { /* loop is fowled, sample is tiny (can't pad 8 samples) */
+ sam->loopstart = sam->start + 1;
+ sam->loopend = sam->end - 1;
+ }
+ }
+
+ /* convert sample end, loopstart, loopend to offsets from sam->start */
+ sam->end -= sam->start + 1; /* marks last sample, contrary to SF spec. */
+ sam->loopstart -= sam->start;
+ sam->loopend -= sam->start;
+
+ p = fluid_list_next (p);
+ }
+
+ return (OK);
+}
+
+/*=================================sfont.c========================
+ Smurf SoundFont Editor
+ ================================================================*/
+
+
+/* optimum chunk area sizes (could be more optimum) */
+#define PRESET_CHUNK_OPTIMUM_AREA 256
+#define INST_CHUNK_OPTIMUM_AREA 256
+#define SAMPLE_CHUNK_OPTIMUM_AREA 256
+#define ZONE_CHUNK_OPTIMUM_AREA 256
+#define MOD_CHUNK_OPTIMUM_AREA 256
+#define GEN_CHUNK_OPTIMUM_AREA 256
+
+unsigned short badgen[] = { Gen_Unused1, Gen_Unused2, Gen_Unused3, Gen_Unused4,
+ Gen_Reserved1, Gen_Reserved2, Gen_Reserved3, 0
+};
+
+unsigned short badpgen[] = { Gen_StartAddrOfs, Gen_EndAddrOfs, Gen_StartLoopAddrOfs,
+ Gen_EndLoopAddrOfs, Gen_StartAddrCoarseOfs, Gen_EndAddrCoarseOfs,
+ Gen_StartLoopAddrCoarseOfs, Gen_Keynum, Gen_Velocity,
+ Gen_EndLoopAddrCoarseOfs, Gen_SampleModes, Gen_ExclusiveClass,
+ Gen_OverrideRootKey, 0
+};
+
+/* close SoundFont file and delete a SoundFont structure */
+void
+sfont_close (SFData * sf)
+{
+ fluid_list_t *p, *p2;
+
+ if (sf->sffd)
+ fclose (sf->sffd);
+
+ if (sf->fname)
+ free (sf->fname);
+
+ p = sf->info;
+ while (p)
+ {
+ free (p->data);
+ p = fluid_list_next (p);
+ }
+ delete_fluid_list(sf->info);
+ sf->info = NULL;
+
+ p = sf->preset;
+ while (p)
+ { /* loop over presets */
+ p2 = ((SFPreset *) (p->data))->zone;
+ while (p2)
+ { /* loop over preset's zones */
+ sfont_free_zone (p2->data);
+ p2 = fluid_list_next (p2);
+ } /* free preset's zone list */
+ delete_fluid_list (((SFPreset *) (p->data))->zone);
+ FLUID_FREE (p->data); /* free preset chunk */
+ p = fluid_list_next (p);
+ }
+ delete_fluid_list (sf->preset);
+ sf->preset = NULL;
+
+ p = sf->inst;
+ while (p)
+ { /* loop over instruments */
+ p2 = ((SFInst *) (p->data))->zone;
+ while (p2)
+ { /* loop over inst's zones */
+ sfont_free_zone (p2->data);
+ p2 = fluid_list_next (p2);
+ } /* free inst's zone list */
+ delete_fluid_list (((SFInst *) (p->data))->zone);
+ FLUID_FREE (p->data);
+ p = fluid_list_next (p);
+ }
+ delete_fluid_list (sf->inst);
+ sf->inst = NULL;
+
+ p = sf->sample;
+ while (p)
+ {
+ FLUID_FREE (p->data);
+ p = fluid_list_next (p);
+ }
+ delete_fluid_list (sf->sample);
+ sf->sample = NULL;
+
+ FLUID_FREE (sf);
+}
+
+/* free all elements of a zone (Preset or Instrument) */
+void
+sfont_free_zone (SFZone * zone)
+{
+ fluid_list_t *p;
+
+ if (!zone)
+ return;
+
+ p = zone->gen;
+ while (p)
+ { /* Free gen chunks for this zone */
+ if (p->data)
+ FLUID_FREE (p->data);
+ p = fluid_list_next (p);
+ }
+ delete_fluid_list (zone->gen); /* free genlist */
+
+ p = zone->mod;
+ while (p)
+ { /* Free mod chunks for this zone */
+ if (p->data)
+ FLUID_FREE (p->data);
+ p = fluid_list_next (p);
+ }
+ delete_fluid_list (zone->mod); /* free modlist */
+
+ FLUID_FREE (zone); /* free zone chunk */
+}
+
+/* preset sort function, first by bank, then by preset # */
+int
+sfont_preset_compare_func (void* a, void* b)
+{
+ int aval, bval;
+
+ aval = (int) (((SFPreset *) a)->bank) << 16 | ((SFPreset *) a)->prenum;
+ bval = (int) (((SFPreset *) b)->bank) << 16 | ((SFPreset *) b)->prenum;
+
+ return (aval - bval);
+}
+
+/* delete zone from zone list */
+void
+sfont_zone_delete (SFData * sf, fluid_list_t ** zlist, SFZone * zone)
+{
+ *zlist = fluid_list_remove (*zlist, (void*) zone);
+ sfont_free_zone (zone);
+}
+
+/* Find generator in gen list */
+fluid_list_t *
+gen_inlist (int gen, fluid_list_t * genlist)
+{ /* is generator in gen list? */
+ fluid_list_t *p;
+
+ p = genlist;
+ while (p)
+ {
+ if (p->data == NULL)
+ return (NULL);
+ if (gen == ((SFGen *) p->data)->id)
+ break;
+ p = fluid_list_next (p);
+ }
+ return (p);
+}
+
+/* check validity of instrument generator */
+int
+gen_valid (int gen)
+{ /* is generator id valid? */
+ int i = 0;
+
+ if (gen > Gen_MaxValid)
+ return (FALSE);
+ while (badgen[i] && badgen[i] != gen)
+ i++;
+ return (badgen[i] == 0);
+}
+
+/* check validity of preset generator */
+int
+gen_validp (int gen)
+{ /* is preset generator valid? */
+ int i = 0;
+
+ if (!gen_valid (gen))
+ return (FALSE);
+ while (badpgen[i] && badpgen[i] != (unsigned short) gen)
+ i++;
+ return (badpgen[i] == 0);
+}
+
+/*================================util.c===========================*/
+
+/* Logging function, returns FAIL to use as a return value in calling funcs */
+int
+gerr (int ev, char * fmt, ...)
+{
+ va_list args;
+
+ va_start (args, fmt);
+ vprintf(fmt, args);
+ va_end (args);
+
+ printf("\n");
+
+ return (FAIL);
+}
+
+int
+safe_fread (void *buf, int count, FILE * fd)
+{
+ if (fread (buf, count, 1, fd) != 1)
+ { /* size_t = count, nmemb = 1 */
+ if (feof (fd))
+ gerr (ErrEof, _("EOF while attemping to read %d bytes"), count);
+ else
+ FLUID_LOG (FLUID_ERR, _("File read failed"));
+ return (FAIL);
+ }
+ return (OK);
+}
+
+int
+safe_fseek (FILE * fd, long ofs, int whence)
+{
+ if (fseek (fd, ofs, whence) == -1) {
+ FLUID_LOG (FLUID_ERR, _("File seek failed with offset = %ld and whence = %d"), ofs, whence);
+ return (FAIL);
+ }
+ return (OK);
+}