fluidsynth/src/utils/fluid_settings.c
2017-09-16 17:30:57 +02:00

1544 lines
41 KiB
C

/* 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 Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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, const 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);
fluid_file_renderer_settings(settings);
fluid_audio_driver_settings(settings);
fluid_midi_driver_settings(settings);
}
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, const char* name, const 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, const 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, const 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 into the provided buffer (thread safe)
* @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
*
* @note 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;
}
/**
* 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 */
}