summaryrefslogtreecommitdiff
path: root/libs/fluidsynth/src/fluid_settings.c
diff options
context:
space:
mode:
Diffstat (limited to 'libs/fluidsynth/src/fluid_settings.c')
-rw-r--r--libs/fluidsynth/src/fluid_settings.c1602
1 files changed, 1602 insertions, 0 deletions
diff --git a/libs/fluidsynth/src/fluid_settings.c b/libs/fluidsynth/src/fluid_settings.c
new file mode 100644
index 0000000000..2061c90f90
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_settings.c
@@ -0,0 +1,1602 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * 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 "fluidsynth_priv.h"
+#include "fluid_sys.h"
+#include "fluid_hash.h"
+#include "fluid_synth.h"
+//#include "fluid_cmd.h"
+//#include "fluid_adriver.h"
+//#include "fluid_mdriver.h"
+#include "fluid_settings.h"
+#include "fluid_midi.h"
+
+/* Defined in fluid_filerenderer.c */
+extern void fluid_file_renderer_settings (fluid_settings_t* settings);
+
+/* maximum allowed components of a settings variable (separated by '.') */
+#define MAX_SETTINGS_TOKENS 8 /* currently only a max of 3 are used */
+#define MAX_SETTINGS_LABEL 256 /* max length of a settings variable label */
+
+static void fluid_settings_init(fluid_settings_t* settings);
+static void fluid_settings_key_destroy_func(void* value);
+static void fluid_settings_value_destroy_func(void* value);
+static int fluid_settings_tokenize(const char *s, char *buf, char **ptr);
+
+/* Common structure to all settings nodes */
+typedef struct {
+ int type; /**< fluid_types_enum */
+} fluid_setting_node_t;
+
+typedef struct {
+ fluid_setting_node_t node;
+ char* value;
+ char* def;
+ int hints;
+ fluid_list_t* options;
+ fluid_str_update_t update;
+ void* data;
+} fluid_str_setting_t;
+
+typedef struct {
+ fluid_setting_node_t node;
+ double value;
+ double def;
+ double min;
+ double max;
+ int hints;
+ fluid_num_update_t update;
+ void* data;
+} fluid_num_setting_t;
+
+typedef struct {
+ fluid_setting_node_t node;
+ int value;
+ int def;
+ int min;
+ int max;
+ int hints;
+ fluid_int_update_t update;
+ void* data;
+} fluid_int_setting_t;
+
+typedef struct {
+ fluid_setting_node_t node;
+ fluid_hashtable_t *hashtable;
+} fluid_set_setting_t;
+
+
+static fluid_str_setting_t*
+new_fluid_str_setting(const char* value, char* def, int hints, fluid_str_update_t fun, void* data)
+{
+ fluid_str_setting_t* str;
+
+ str = FLUID_NEW(fluid_str_setting_t);
+
+ if (!str)
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ str->node.type = FLUID_STR_TYPE;
+ str->value = value? FLUID_STRDUP(value) : NULL;
+ str->def = def? FLUID_STRDUP(def) : NULL;
+ str->hints = hints;
+ str->options = NULL;
+ str->update = fun;
+ str->data = data;
+ return str;
+}
+
+static void
+delete_fluid_str_setting(fluid_str_setting_t* str)
+{
+ if (!str) return;
+
+ if (str->value) FLUID_FREE(str->value);
+ if (str->def) FLUID_FREE(str->def);
+
+ if (str->options) {
+ fluid_list_t* list = str->options;
+
+ while (list) {
+ FLUID_FREE (list->data);
+ list = fluid_list_next(list);
+ }
+
+ delete_fluid_list(str->options);
+ }
+
+ FLUID_FREE(str);
+}
+
+
+static fluid_num_setting_t*
+new_fluid_num_setting(double min, double max, double def,
+ int hints, fluid_num_update_t fun, void* data)
+{
+ fluid_num_setting_t* setting;
+
+ setting = FLUID_NEW(fluid_num_setting_t);
+
+ if (!setting)
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ setting->node.type = FLUID_NUM_TYPE;
+ setting->value = def;
+ setting->def = def;
+ setting->min = min;
+ setting->max = max;
+ setting->hints = hints;
+ setting->update = fun;
+ setting->data = data;
+ return setting;
+}
+
+static void
+delete_fluid_num_setting(fluid_num_setting_t* setting)
+{
+ if (setting) FLUID_FREE(setting);
+}
+
+static fluid_int_setting_t*
+new_fluid_int_setting(int min, int max, int def,
+ int hints, fluid_int_update_t fun, void* data)
+{
+ fluid_int_setting_t* setting;
+
+ setting = FLUID_NEW(fluid_int_setting_t);
+
+ if (!setting)
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ setting->node.type = FLUID_INT_TYPE;
+ setting->value = def;
+ setting->def = def;
+ setting->min = min;
+ setting->max = max;
+ setting->hints = hints;
+ setting->update = fun;
+ setting->data = data;
+ return setting;
+}
+
+static void
+delete_fluid_int_setting(fluid_int_setting_t* setting)
+{
+ if (setting) FLUID_FREE(setting);
+}
+
+static fluid_set_setting_t*
+new_fluid_set_setting(void)
+{
+ fluid_set_setting_t* setting;
+
+ setting = FLUID_NEW(fluid_set_setting_t);
+
+ if (!setting)
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ setting->node.type = FLUID_SET_TYPE;
+ setting->hashtable = new_fluid_hashtable_full(fluid_str_hash, fluid_str_equal,
+ fluid_settings_key_destroy_func,
+ fluid_settings_value_destroy_func);
+ if (!setting->hashtable)
+ {
+ FLUID_FREE (setting);
+ return NULL;
+ }
+
+ return setting;
+}
+
+static void
+delete_fluid_set_setting(fluid_set_setting_t* setting)
+{
+ if (setting)
+ {
+ delete_fluid_hashtable(setting->hashtable);
+ FLUID_FREE(setting);
+ }
+}
+
+/**
+ * Create a new settings object
+ * @return the pointer to the settings object
+ */
+fluid_settings_t *
+new_fluid_settings(void)
+{
+ fluid_settings_t* settings;
+
+ settings = new_fluid_hashtable_full(fluid_str_hash, fluid_str_equal,
+ fluid_settings_key_destroy_func,
+ fluid_settings_value_destroy_func);
+ if (settings == NULL) return NULL;
+
+ fluid_rec_mutex_init (settings->mutex);
+ fluid_settings_init(settings);
+ return settings;
+}
+
+/**
+ * Delete the provided settings object
+ * @param settings a settings object
+ */
+void
+delete_fluid_settings(fluid_settings_t* settings)
+{
+ fluid_return_if_fail (settings != NULL);
+
+ fluid_rec_mutex_destroy (settings->mutex);
+ delete_fluid_hashtable(settings);
+}
+
+/* Settings hash key destroy function */
+static void
+fluid_settings_key_destroy_func(void* value)
+{
+ FLUID_FREE (value); /* Free the string key value */
+}
+
+/* Settings hash value destroy function */
+static void
+fluid_settings_value_destroy_func(void* value)
+{
+ fluid_setting_node_t *node = value;
+
+ switch (node->type) {
+ case FLUID_NUM_TYPE:
+ delete_fluid_num_setting((fluid_num_setting_t*) value);
+ break;
+ case FLUID_INT_TYPE:
+ delete_fluid_int_setting((fluid_int_setting_t*) value);
+ break;
+ case FLUID_STR_TYPE:
+ delete_fluid_str_setting((fluid_str_setting_t*) value);
+ break;
+ case FLUID_SET_TYPE:
+ delete_fluid_set_setting((fluid_set_setting_t*) value);
+ break;
+ }
+}
+
+void
+fluid_settings_init(fluid_settings_t* settings)
+{
+ fluid_return_if_fail (settings != NULL);
+
+ fluid_synth_settings(settings);
+ //fluid_shell_settings(settings);
+ fluid_player_settings(settings);
+#if 0
+ fluid_file_renderer_settings(settings);
+ fluid_audio_driver_settings(settings);
+ fluid_midi_driver_settings(settings);
+#endif
+}
+
+static int
+fluid_settings_tokenize(const char *s, char *buf, char **ptr)
+{
+ char *tokstr, *tok;
+ int n = 0;
+
+ if (strlen (s) > MAX_SETTINGS_LABEL)
+ {
+ FLUID_LOG(FLUID_ERR, "Setting variable name exceeded max length of %d chars",
+ MAX_SETTINGS_LABEL);
+ return 0;
+ }
+
+ FLUID_STRCPY(buf, s); /* copy string to buffer, since it gets modified */
+ tokstr = buf;
+
+ while ((tok = fluid_strtok (&tokstr, ".")))
+ {
+ if (n >= MAX_SETTINGS_TOKENS)
+ {
+ FLUID_LOG(FLUID_ERR, "Setting variable name exceeded max token count of %d",
+ MAX_SETTINGS_TOKENS);
+ return 0;
+ } else
+ ptr[n++] = tok;
+ }
+
+ return n;
+}
+
+/**
+ * Get a setting name, value and type
+ *
+ * @param settings a settings object
+ * @param name Settings name
+ * @param value Location to store setting node if found
+ * @return 1 if the node exists, 0 otherwise
+ */
+static int
+fluid_settings_get(fluid_settings_t* settings, const char *name,
+ fluid_setting_node_t **value)
+{
+ fluid_hashtable_t* table = settings;
+ fluid_setting_node_t *node = NULL;
+ char* tokens[MAX_SETTINGS_TOKENS];
+ char buf[MAX_SETTINGS_LABEL+1];
+ int ntokens;
+ int n;
+
+ ntokens = fluid_settings_tokenize (name, buf, tokens);
+
+ if (table == NULL || ntokens <= 0) return 0;
+
+ for (n = 0; n < ntokens; n++) {
+
+ node = fluid_hashtable_lookup(table, tokens[n]);
+ if (!node) return 0;
+
+ table = (node->type == FLUID_SET_TYPE) ? ((fluid_set_setting_t *)node)->hashtable : NULL;
+ }
+
+ if (value) *value = node;
+
+ return 1;
+}
+
+/**
+ * Set a setting name, value and type, replacing it if already exists
+ *
+ * @param settings a settings object
+ * @param name Settings name
+ * @param value Node instance to assign (used directly)
+ * @return 1 if the value has been set, zero otherwise
+ */
+static int
+fluid_settings_set(fluid_settings_t* settings, const char *name, void* value)
+{
+ fluid_hashtable_t* table = settings;
+ fluid_setting_node_t *node;
+ char* tokens[MAX_SETTINGS_TOKENS];
+ char buf[MAX_SETTINGS_LABEL+1];
+ int n, num;
+ char *dupname;
+
+ num = fluid_settings_tokenize (name, buf, tokens) - 1;
+ if (num == 0)
+ return 0;
+
+ for (n = 0; n < num; n++) {
+
+ node = fluid_hashtable_lookup(table, tokens[n]);
+
+ if (node) {
+
+ if (node->type == FLUID_SET_TYPE) {
+ table = ((fluid_set_setting_t *)node)->hashtable;
+ } else {
+ /* path ends prematurely */
+ FLUID_LOG(FLUID_WARN, "'%s' is not a node", name[n]);
+ return 0;
+ }
+
+ } else {
+ /* create a new node */
+ fluid_set_setting_t* setnode;
+
+ dupname = FLUID_STRDUP (tokens[n]);
+ setnode = new_fluid_set_setting ();
+
+ if (!dupname || !setnode)
+ {
+ if (dupname) FLUID_FREE (dupname);
+ else FLUID_LOG(FLUID_ERR, "Out of memory");
+
+ if (setnode) delete_fluid_set_setting (setnode);
+
+ return 0;
+ }
+
+ fluid_hashtable_insert(table, dupname, setnode);
+ table = setnode->hashtable;
+ }
+ }
+
+ dupname = FLUID_STRDUP (tokens[num]);
+
+ if (!dupname)
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return 0;
+ }
+
+ fluid_hashtable_insert(table, dupname, value);
+
+ return 1;
+}
+
+/** returns 1 if the value has been registered correctly, 0
+ otherwise */
+int
+fluid_settings_register_str(fluid_settings_t* settings, char* name, char* def, int hints,
+ fluid_str_update_t fun, void* data)
+{
+ fluid_setting_node_t *node;
+ fluid_str_setting_t* setting;
+ int retval;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (!fluid_settings_get(settings, name, &node)) {
+ setting = new_fluid_str_setting(def, def, hints, fun, data);
+ retval = fluid_settings_set(settings, name, setting);
+ if (retval != 1) delete_fluid_str_setting (setting);
+ } else {
+ /* if variable already exists, don't change its value. */
+ if (node->type == FLUID_STR_TYPE) {
+ setting = (fluid_str_setting_t*) node;
+ setting->update = fun;
+ setting->data = data;
+ setting->def = def? FLUID_STRDUP(def) : NULL;
+ setting->hints = hints;
+ retval = 1;
+ } else {
+ FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name);
+ retval = 0;
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/** returns 1 if the value has been register correctly, zero
+ otherwise */
+int
+fluid_settings_register_num(fluid_settings_t* settings, char* name, double def,
+ double min, double max, int hints,
+ fluid_num_update_t fun, void* data)
+{
+ fluid_setting_node_t *node;
+ int retval;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+
+ /* For now, all floating point settings are bounded below and above */
+ hints |= FLUID_HINT_BOUNDED_BELOW | FLUID_HINT_BOUNDED_ABOVE;
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (!fluid_settings_get(settings, name, &node)) {
+ /* insert a new setting */
+ fluid_num_setting_t* setting;
+ setting = new_fluid_num_setting(min, max, def, hints, fun, data);
+ retval = fluid_settings_set(settings, name, setting);
+ if (retval != 1) delete_fluid_num_setting (setting);
+ } else {
+ if (node->type == FLUID_NUM_TYPE) {
+ /* update the existing setting but don't change its value */
+ fluid_num_setting_t* setting = (fluid_num_setting_t*) node;
+ setting->update = fun;
+ setting->data = data;
+ setting->min = min;
+ setting->max = max;
+ setting->def = def;
+ setting->hints = hints;
+ retval = 1;
+ } else {
+ /* type mismatch */
+ FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name);
+ retval = 0;
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/** returns 1 if the value has been register correctly, zero
+ otherwise. */
+int
+fluid_settings_register_int(fluid_settings_t* settings, char* name, int def,
+ int min, int max, int hints,
+ fluid_int_update_t fun, void* data)
+{
+ fluid_setting_node_t *node;
+ int retval;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+
+ /* For now, all integer settings are bounded below and above */
+ hints |= FLUID_HINT_BOUNDED_BELOW | FLUID_HINT_BOUNDED_ABOVE;
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (!fluid_settings_get(settings, name, &node)) {
+ /* insert a new setting */
+ fluid_int_setting_t* setting;
+ setting = new_fluid_int_setting(min, max, def, hints, fun, data);
+ retval = fluid_settings_set(settings, name, setting);
+ if (retval != 1) delete_fluid_int_setting (setting);
+ } else {
+ if (node->type == FLUID_INT_TYPE) {
+ /* update the existing setting but don't change its value */
+ fluid_int_setting_t* setting = (fluid_int_setting_t*) node;
+ setting->update = fun;
+ setting->data = data;
+ setting->min = min;
+ setting->max = max;
+ setting->def = def;
+ setting->hints = hints;
+ retval = 1;
+ } else {
+ /* type mismatch */
+ FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name);
+ retval = 0;
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Get the type of the setting with the given name
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @return the type for the named setting, or #FLUID_NO_TYPE when it does not exist
+ */
+int
+fluid_settings_get_type(fluid_settings_t* settings, const char *name)
+{
+ fluid_setting_node_t *node;
+ int type;
+
+ fluid_return_val_if_fail (settings != NULL, FLUID_NO_TYPE);
+ fluid_return_val_if_fail (name != NULL, FLUID_NO_TYPE);
+ fluid_return_val_if_fail (name[0] != '\0', FLUID_NO_TYPE);
+
+ fluid_rec_mutex_lock (settings->mutex);
+ type = fluid_settings_get (settings, name, &node) ? node->type : FLUID_NO_TYPE;
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return (type);
+}
+
+/**
+ * Get the hints for the named setting as an integer bitmap
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @return the hints associated to the named setting if it exists, zero otherwise
+ */
+int
+fluid_settings_get_hints(fluid_settings_t* settings, const char *name)
+{
+ fluid_setting_node_t *node;
+ int hints = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)) {
+ if (node->type == FLUID_NUM_TYPE) {
+ fluid_num_setting_t* setting = (fluid_num_setting_t*) node;
+ hints = setting->hints;
+ } else if (node->type == FLUID_STR_TYPE) {
+ fluid_str_setting_t* setting = (fluid_str_setting_t*) node;
+ hints = setting->hints;
+ } else if (node->type == FLUID_INT_TYPE) {
+ fluid_int_setting_t* setting = (fluid_int_setting_t*) node;
+ hints = setting->hints;
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return hints;
+}
+
+/**
+ * Ask whether the setting is changeable in real-time.
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @return non zero if the setting is changeable in real-time
+ */
+int
+fluid_settings_is_realtime(fluid_settings_t* settings, const char *name)
+{
+ fluid_setting_node_t *node;
+ int isrealtime = FALSE;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)) {
+ if (node->type == FLUID_NUM_TYPE) {
+ fluid_num_setting_t* setting = (fluid_num_setting_t*) node;
+ isrealtime = setting->update != NULL;
+ } else if (node->type == FLUID_STR_TYPE) {
+ fluid_str_setting_t* setting = (fluid_str_setting_t*) node;
+ isrealtime = setting->update != NULL;
+ } else if (node->type == FLUID_INT_TYPE) {
+ fluid_int_setting_t* setting = (fluid_int_setting_t*) node;
+ isrealtime = setting->update != NULL;
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return isrealtime;
+}
+
+/**
+ * Set a string value for a named setting
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param str new string value
+ * @return 1 if the value has been set, 0 otherwise
+ */
+int
+fluid_settings_setstr(fluid_settings_t* settings, const char *name, const char *str)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get (settings, name, &node)) {
+ if (node->type == FLUID_STR_TYPE) {
+ fluid_str_setting_t *setting = (fluid_str_setting_t *)node;
+
+ if (setting->value) FLUID_FREE (setting->value);
+ setting->value = str ? FLUID_STRDUP (str) : NULL;
+
+ /* Call under lock to keep update() synchronized with the current value */
+ if (setting->update) (*setting->update)(setting->data, name, str);
+ retval = 1;
+ }
+ else if (node->type == FLUID_INT_TYPE) /* Handle yes/no for boolean values for backwards compatibility */
+ {
+ fluid_int_setting_t *setting = (fluid_int_setting_t *)node;
+
+ if (setting->hints & FLUID_HINT_TOGGLED)
+ {
+ if (FLUID_STRCMP (str, "yes") == 0)
+ {
+ setting->value = TRUE;
+ if (setting->update) (*setting->update)(setting->data, name, TRUE);
+ }
+ else if (FLUID_STRCMP (str, "no") == 0)
+ {
+ setting->value = FALSE;
+ if (setting->update) (*setting->update)(setting->data, name, FALSE);
+ }
+ }
+ }
+ } else {
+ /* insert a new setting */
+ fluid_str_setting_t* setting;
+ setting = new_fluid_str_setting(str, NULL, 0, NULL, NULL);
+ retval = fluid_settings_set(settings, name, setting);
+ if (retval != 1) delete_fluid_str_setting (setting);
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Copy the value of a string setting
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param str Caller supplied buffer to copy string value to
+ * @param len Size of 'str' buffer (no more than len bytes will be written, which
+ * will always include a zero terminator)
+ * @return 1 if the value exists, 0 otherwise
+ * @since 1.1.0
+ *
+ * Like fluid_settings_getstr() but is thread safe. A size of 256 should be
+ * more than sufficient for the string buffer.
+ */
+int
+fluid_settings_copystr(fluid_settings_t* settings, const char *name,
+ char *str, int len)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+ fluid_return_val_if_fail (str != NULL, 0);
+ fluid_return_val_if_fail (len > 0, 0);
+
+ str[0] = 0;
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get (settings, name, &node))
+ {
+ if (node->type == FLUID_STR_TYPE)
+ {
+ fluid_str_setting_t *setting = (fluid_str_setting_t *)node;
+
+ if (setting->value)
+ {
+ FLUID_STRNCPY (str, setting->value, len);
+ str[len - 1] = 0; /* Force terminate, in case of truncation */
+ }
+
+ retval = 1;
+ }
+ else if (node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */
+ {
+ fluid_int_setting_t *setting = (fluid_int_setting_t *)node;
+
+ if (setting->hints & FLUID_HINT_TOGGLED)
+ {
+ FLUID_STRNCPY (str, setting->value ? "yes" : "no", len);
+ str[len - 1] = 0; /* Force terminate, in case of truncation */
+
+ retval = 1;
+ }
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Duplicate the value of a string setting
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param str Location to store pointer to allocated duplicate string
+ * @return 1 if the value exists and was successfully duplicated, 0 otherwise
+ * @since 1.1.0
+ *
+ * Like fluid_settings_copystr() but allocates a new copy of the string. Caller
+ * owns the string and should free it with free() when done using it.
+ */
+int
+fluid_settings_dupstr(fluid_settings_t* settings, const char *name, char** str)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+ fluid_return_val_if_fail (str != NULL, 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node))
+ {
+ if (node->type == FLUID_STR_TYPE)
+ {
+ fluid_str_setting_t *setting = (fluid_str_setting_t *)node;
+
+ if (setting->value)
+ {
+ *str = FLUID_STRDUP (setting->value);
+ if (!*str) FLUID_LOG (FLUID_ERR, "Out of memory");
+ }
+
+ if (!setting->value || *str) retval = 1; /* Don't set to 1 if out of memory */
+ }
+ else if (node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */
+ {
+ fluid_int_setting_t *setting = (fluid_int_setting_t *)node;
+
+ if (setting->hints & FLUID_HINT_TOGGLED)
+ {
+ *str = FLUID_STRDUP (setting->value ? "yes" : "no");
+ if (!*str) FLUID_LOG (FLUID_ERR, "Out of memory");
+
+ if (!setting->value || *str) retval = 1; /* Don't set to 1 if out of memory */
+ }
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Get the value of a string setting
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param str Location to store pointer to the settings string value
+ * @return 1 if the value exists, 0 otherwise
+ * @deprecated
+ *
+ * If the value does not exists, 'str' is set to NULL. Otherwise, 'str' will
+ * point to the value. The application does not own the returned value and it
+ * is valid only until a new value is assigned to the setting of the given name.
+ *
+ * NOTE: In a multi-threaded environment, caller must ensure that the setting
+ * being read by fluid_settings_getstr() is not assigned during the
+ * duration of callers use of the setting's value. Use fluid_settings_copystr()
+ * or fluid_settings_dupstr() which does not have this restriction.
+ */
+int
+fluid_settings_getstr(fluid_settings_t* settings, const char *name, char** str)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+ fluid_return_val_if_fail (str != NULL, 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node))
+ {
+ if (node->type == FLUID_STR_TYPE)
+ {
+ fluid_str_setting_t *setting = (fluid_str_setting_t *)node;
+ *str = setting->value;
+ retval = 1;
+ }
+ else if (node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */
+ {
+ fluid_int_setting_t *setting = (fluid_int_setting_t *)node;
+
+ if (setting->hints & FLUID_HINT_TOGGLED)
+ {
+ *str = setting->value ? "yes" : "no";
+ retval = 1;
+ }
+ }
+ }
+ else *str = NULL;
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Test a string setting for some value.
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param s a string to be tested
+ * @return 1 if the value exists and is equal to 's', 0 otherwise
+ */
+int
+fluid_settings_str_equal (fluid_settings_t* settings, const char *name, const char *s)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+ fluid_return_val_if_fail (s != NULL, 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get (settings, name, &node))
+ {
+ if (node->type == FLUID_STR_TYPE)
+ {
+ fluid_str_setting_t *setting = (fluid_str_setting_t *)node;
+ if (setting->value) retval = FLUID_STRCMP (setting->value, s) == 0;
+ }
+ else if (node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */
+ {
+ fluid_int_setting_t *setting = (fluid_int_setting_t *)node;
+
+ if (setting->hints & FLUID_HINT_TOGGLED)
+ retval = FLUID_STRCMP (setting->value ? "yes" : "no", s) == 0;
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Get the default value of a string setting. Note that the returned string is
+ * not owned by the caller and should not be modified or freed.
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @return the default string value of the setting if it exists, NULL otherwise
+ */
+char*
+fluid_settings_getstr_default(fluid_settings_t* settings, const char *name)
+{
+ fluid_setting_node_t *node;
+ char *retval = NULL;
+
+ fluid_return_val_if_fail (settings != NULL, NULL);
+ fluid_return_val_if_fail (name != NULL, NULL);
+ fluid_return_val_if_fail (name[0] != '\0', NULL);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get (settings, name, &node))
+ {
+ if (node->type == FLUID_STR_TYPE)
+ {
+ fluid_str_setting_t* setting = (fluid_str_setting_t*) node;
+ retval = setting->def;
+ }
+ else if (node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */
+ {
+ fluid_int_setting_t *setting = (fluid_int_setting_t *)node;
+
+ if (setting->hints & FLUID_HINT_TOGGLED)
+ retval = setting->def ? "yes" : "no";
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Add an option to a string setting (like an enumeration value).
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param s option string to add
+ * @return 1 if the setting exists and option was added, 0 otherwise
+ *
+ * Causes the setting's #FLUID_HINT_OPTIONLIST hint to be set.
+ */
+int
+fluid_settings_add_option(fluid_settings_t* settings, const char *name, const char *s)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+ fluid_return_val_if_fail (s != NULL, 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)
+ && (node->type == FLUID_STR_TYPE)) {
+ fluid_str_setting_t* setting = (fluid_str_setting_t*) node;
+ char* copy = FLUID_STRDUP(s);
+ setting->options = fluid_list_append(setting->options, copy);
+ setting->hints |= FLUID_HINT_OPTIONLIST;
+ retval = 1;
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Remove an option previously assigned by fluid_settings_add_option().
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param s option string to remove
+ * @return 1 if the setting exists and option was removed, 0 otherwise
+ */
+int
+fluid_settings_remove_option(fluid_settings_t* settings, const char *name, const char* s)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+ fluid_return_val_if_fail (s != NULL, 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)
+ && (node->type == FLUID_STR_TYPE)) {
+
+ fluid_str_setting_t* setting = (fluid_str_setting_t*) node;
+ fluid_list_t* list = setting->options;
+
+ while (list) {
+ char* option = (char*) fluid_list_get(list);
+ if (FLUID_STRCMP(s, option) == 0) {
+ FLUID_FREE (option);
+ setting->options = fluid_list_remove_link(setting->options, list);
+ retval = 1;
+ break;
+ }
+ list = fluid_list_next(list);
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Set a numeric value for a named setting.
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param val new setting's value
+ * @return 1 if the value has been set, 0 otherwise
+ */
+int
+fluid_settings_setnum(fluid_settings_t* settings, const char *name, double val)
+{
+ fluid_setting_node_t *node;
+ fluid_num_setting_t* setting;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)) {
+ if (node->type == FLUID_NUM_TYPE) {
+ setting = (fluid_num_setting_t*) node;
+
+ if (val < setting->min) val = setting->min;
+ else if (val > setting->max) val = setting->max;
+
+ setting->value = val;
+
+ /* Call under lock to keep update() synchronized with the current value */
+ if (setting->update) (*setting->update)(setting->data, name, val);
+ retval = 1;
+ }
+ } else {
+ /* insert a new setting */
+ fluid_num_setting_t* setting;
+ setting = new_fluid_num_setting(-1e10, 1e10, 0.0f, 0, NULL, NULL);
+ setting->value = val;
+ retval = fluid_settings_set(settings, name, setting);
+ if (retval != 1) delete_fluid_num_setting (setting);
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Get the numeric value of a named setting
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param val variable pointer to receive the setting's numeric value
+ * @return 1 if the value exists, 0 otherwise
+ */
+int
+fluid_settings_getnum(fluid_settings_t* settings, const char *name, double* val)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+ fluid_return_val_if_fail (val != NULL, 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)
+ && (node->type == FLUID_NUM_TYPE)) {
+ fluid_num_setting_t* setting = (fluid_num_setting_t*) node;
+ *val = setting->value;
+ retval = 1;
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Get the range of values of a numeric setting
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param min setting's range lower limit
+ * @param max setting's range upper limit
+ */
+void
+fluid_settings_getnum_range(fluid_settings_t* settings, const char *name,
+ double* min, double* max)
+{
+ fluid_setting_node_t *node;
+
+ fluid_return_if_fail (settings != NULL);
+ fluid_return_if_fail (name != NULL);
+ fluid_return_if_fail (name[0] != '\0');
+ fluid_return_if_fail (min != NULL);
+ fluid_return_if_fail (max != NULL);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)
+ && (node->type == FLUID_NUM_TYPE)) {
+ fluid_num_setting_t* setting = (fluid_num_setting_t*) node;
+ *min = setting->min;
+ *max = setting->max;
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+}
+
+/**
+ * Get the default value of a named numeric (double) setting
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @return the default value if the named setting exists, 0.0f otherwise
+ */
+double
+fluid_settings_getnum_default(fluid_settings_t* settings, const char *name)
+{
+ fluid_setting_node_t *node;
+ double retval = 0.0;
+
+ fluid_return_val_if_fail (settings != NULL, 0.0);
+ fluid_return_val_if_fail (name != NULL, 0.0);
+ fluid_return_val_if_fail (name[0] != '\0', 0.0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)
+ && (node->type == FLUID_NUM_TYPE)) {
+ fluid_num_setting_t* setting = (fluid_num_setting_t*) node;
+ retval = setting->def;
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Set an integer value for a setting
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param val new setting's integer value
+ * @return 1 if the value has been set, 0 otherwise
+ */
+int
+fluid_settings_setint(fluid_settings_t* settings, const char *name, int val)
+{
+ fluid_setting_node_t *node;
+ fluid_int_setting_t* setting;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)) {
+ if (node->type == FLUID_INT_TYPE) {
+ setting = (fluid_int_setting_t*) node;
+
+ if (val < setting->min) val = setting->min;
+ else if (val > setting->max) val = setting->max;
+
+ setting->value = val;
+
+ /* Call under lock to keep update() synchronized with the current value */
+ if (setting->update) (*setting->update)(setting->data, name, val);
+ retval = 1;
+ }
+ } else {
+ /* insert a new setting */
+ fluid_int_setting_t* setting;
+ setting = new_fluid_int_setting(INT_MIN, INT_MAX, 0, 0, NULL, NULL);
+ setting->value = val;
+ retval = fluid_settings_set(settings, name, setting);
+ if (retval != 1) delete_fluid_int_setting (setting);
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Get an integer value setting.
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param val pointer to a variable to receive the setting's integer value
+ * @return 1 if the value exists, 0 otherwise
+ */
+int
+fluid_settings_getint(fluid_settings_t* settings, const char *name, int* val)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+ fluid_return_val_if_fail (val != NULL, 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)
+ && (node->type == FLUID_INT_TYPE)) {
+ fluid_int_setting_t* setting = (fluid_int_setting_t*) node;
+ *val = setting->value;
+ retval = 1;
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Get the range of values of an integer setting
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param min setting's range lower limit
+ * @param max setting's range upper limit
+ */
+void
+fluid_settings_getint_range(fluid_settings_t* settings, const char *name,
+ int* min, int* max)
+{
+ fluid_setting_node_t *node;
+
+ fluid_return_if_fail (settings != NULL);
+ fluid_return_if_fail (name != NULL);
+ fluid_return_if_fail (name[0] != '\0');
+ fluid_return_if_fail (min != NULL);
+ fluid_return_if_fail (max != NULL);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)
+ && (node->type == FLUID_INT_TYPE)) {
+ fluid_int_setting_t* setting = (fluid_int_setting_t*) node;
+ *min = setting->min;
+ *max = setting->max;
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+}
+
+/**
+ * Get the default value of an integer setting.
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @return the setting's default integer value it it exists, zero otherwise
+ */
+int
+fluid_settings_getint_default(fluid_settings_t* settings, const char *name)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)
+ && (node->type == FLUID_INT_TYPE)) {
+ fluid_int_setting_t* setting = (fluid_int_setting_t*) node;
+ retval = setting->def;
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Iterate the available options for a named string setting, calling the provided
+ * callback function for each existing option.
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param data any user provided pointer
+ * @param func callback function to be called on each iteration
+ *
+ * NOTE: Starting with FluidSynth 1.1.0 the \a func callback is called for each
+ * option in alphabetical order. Sort order was undefined in previous versions.
+ */
+void
+fluid_settings_foreach_option (fluid_settings_t* settings, const char *name,
+ void* data, fluid_settings_foreach_option_t func)
+{
+ fluid_setting_node_t *node;
+ fluid_str_setting_t *setting;
+ fluid_list_t *p, *newlist = NULL;
+
+ fluid_return_if_fail (settings != NULL);
+ fluid_return_if_fail (name != NULL);
+ fluid_return_if_fail (name[0] != '\0');
+ fluid_return_if_fail (func != NULL);
+
+ fluid_rec_mutex_lock (settings->mutex); /* ++ lock */
+
+ if (!fluid_settings_get (settings, name, &node) || node->type != FLUID_STR_TYPE)
+ {
+ fluid_rec_mutex_unlock (settings->mutex); /* -- unlock */
+ return;
+ }
+
+ setting = (fluid_str_setting_t*)node;
+
+ /* Duplicate option list */
+ for (p = setting->options; p; p = p->next)
+ newlist = fluid_list_append (newlist, fluid_list_get (p));
+
+ /* Sort by name */
+ newlist = fluid_list_sort (newlist, fluid_list_str_compare_func);
+
+ for (p = newlist; p; p = p->next)
+ (*func)(data, (char *)name, (char *)fluid_list_get (p));
+
+ fluid_rec_mutex_unlock (settings->mutex); /* -- unlock */
+
+ delete_fluid_list (newlist);
+}
+
+/**
+ * Count option string values for a string setting.
+ * @param settings a settings object
+ * @param name Name of setting
+ * @return Count of options for this string setting (0 if none, -1 if not found
+ * or not a string setting)
+ * @since 1.1.0
+ */
+int
+fluid_settings_option_count (fluid_settings_t *settings, const char *name)
+{
+ fluid_setting_node_t *node;
+ int count = -1;
+
+ fluid_return_val_if_fail (settings != NULL, -1);
+ fluid_return_val_if_fail (name != NULL, -1);
+ fluid_return_val_if_fail (name[0] != '\0', -1);
+
+ fluid_rec_mutex_lock (settings->mutex);
+ if (fluid_settings_get(settings, name, &node) && node->type == FLUID_STR_TYPE)
+ count = fluid_list_size (((fluid_str_setting_t *)node)->options);
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return (count);
+}
+
+/**
+ * Concatenate options for a string setting together with a separator between.
+ * @param settings Settings object
+ * @param name Settings name
+ * @param separator String to use between options (NULL to use ", ")
+ * @return Newly allocated string or NULL on error (out of memory, not a valid
+ * setting \a name or not a string setting). Free the string when finished with it.
+ * @since 1.1.0
+ */
+char *
+fluid_settings_option_concat (fluid_settings_t *settings, const char *name,
+ const char *separator)
+{
+ fluid_setting_node_t *node;
+ fluid_str_setting_t *setting;
+ fluid_list_t *p, *newlist = NULL;
+ int count, len;
+ char *str, *option;
+
+ fluid_return_val_if_fail (settings != NULL, NULL);
+ fluid_return_val_if_fail (name != NULL, NULL);
+ fluid_return_val_if_fail (name[0] != '\0', NULL);
+
+ if (!separator) separator = ", ";
+
+ fluid_rec_mutex_lock (settings->mutex); /* ++ lock */
+
+ if (!fluid_settings_get (settings, name, &node) || node->type != FLUID_STR_TYPE)
+ {
+ fluid_rec_mutex_unlock (settings->mutex); /* -- unlock */
+ return (NULL);
+ }
+
+ setting = (fluid_str_setting_t*)node;
+
+ /* Duplicate option list, count options and get total string length */
+ for (p = setting->options, count = 0, len = 0; p; p = p->next, count++)
+ {
+ option = fluid_list_get (p);
+
+ if (option)
+ {
+ newlist = fluid_list_append (newlist, option);
+ len += strlen (option);
+ }
+ }
+
+ if (count > 1) len += (count - 1) * strlen (separator);
+ len++; /* For terminator */
+
+ /* Sort by name */
+ newlist = fluid_list_sort (newlist, fluid_list_str_compare_func);
+
+ str = FLUID_MALLOC (len);
+
+ if (str)
+ {
+ str[0] = 0;
+ for (p = newlist; p; p = p->next)
+ {
+ option = fluid_list_get (p);
+ strcat (str, option);
+ if (p->next) strcat (str, separator);
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex); /* -- unlock */
+
+ delete_fluid_list (newlist);
+
+ if (!str) FLUID_LOG (FLUID_ERR, "Out of memory");
+
+ return (str);
+}
+
+/* Structure passed to fluid_settings_foreach_iter recursive function */
+typedef struct
+{
+ char path[MAX_SETTINGS_LABEL+1]; /* Maximum settings label length */
+ fluid_list_t *names; /* For fluid_settings_foreach() */
+} fluid_settings_foreach_bag_t;
+
+static int
+fluid_settings_foreach_iter (void* key, void* value, void* data)
+{
+ fluid_settings_foreach_bag_t *bag = data;
+ char *keystr = key;
+ fluid_setting_node_t *node = value;
+ int pathlen;
+ char *s;
+
+ pathlen = strlen (bag->path);
+
+ if (pathlen > 0)
+ {
+ bag->path[pathlen] = '.';
+ bag->path[pathlen + 1] = 0;
+ }
+
+ strcat (bag->path, keystr);
+
+ switch (node->type) {
+ case FLUID_NUM_TYPE:
+ case FLUID_INT_TYPE:
+ case FLUID_STR_TYPE:
+ s = FLUID_STRDUP (bag->path);
+ if (s) bag->names = fluid_list_append (bag->names, s);
+ break;
+ case FLUID_SET_TYPE:
+ fluid_hashtable_foreach(((fluid_set_setting_t *)value)->hashtable,
+ fluid_settings_foreach_iter, bag);
+ break;
+ }
+
+ bag->path[pathlen] = 0;
+
+ return 0;
+}
+
+/**
+ * Iterate the existing settings defined in a settings object, calling the
+ * provided callback function for each setting.
+ *
+ * @param settings a settings object
+ * @param data any user provided pointer
+ * @param func callback function to be called on each iteration
+ *
+ * NOTE: Starting with FluidSynth 1.1.0 the \a func callback is called for each
+ * setting in alphabetical order. Sort order was undefined in previous versions.
+ */
+void
+fluid_settings_foreach (fluid_settings_t* settings, void* data,
+ fluid_settings_foreach_t func)
+{
+ fluid_settings_foreach_bag_t bag;
+ fluid_setting_node_t *node;
+ fluid_list_t *p;
+ int r;
+
+ fluid_return_if_fail (settings != NULL);
+ fluid_return_if_fail (func != NULL);
+
+ bag.path[0] = 0;
+ bag.names = NULL;
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ /* Add all node names to the bag.names list */
+ fluid_hashtable_foreach (settings, fluid_settings_foreach_iter, &bag);
+
+ /* Sort names */
+ bag.names = fluid_list_sort (bag.names, fluid_list_str_compare_func);
+
+ /* Loop over names and call the callback */
+ for (p = bag.names; p; p = p->next)
+ {
+ r = fluid_settings_get (settings, (char *)(p->data), &node);
+ if (r && node) (*func) (data, (char *)(p->data), node->type);
+ FLUID_FREE (p->data); /* -- Free name */
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ delete_fluid_list (bag.names); /* -- Free names list */
+}