Path: blob/master/libs/fluidsynth/src/utils/fluid_settings.c
4396 views
/* FluidSynth - A Software Synthesizer1*2* Copyright (C) 2003 Peter Hanappe and others.3*4* This library is free software; you can redistribute it and/or5* modify it under the terms of the GNU Lesser General Public License6* as published by the Free Software Foundation; either version 2.1 of7* the License, or (at your option) any later version.8*9* This library is distributed in the hope that it will be useful, but10* WITHOUT ANY WARRANTY; without even the implied warranty of11* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU12* Lesser General Public License for more details.13*14* You should have received a copy of the GNU Lesser General Public15* License along with this library; if not, write to the Free16* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA17* 02110-1301, USA18*/1920#include "fluid_sys.h"21#include "fluid_hash.h"22#include "fluid_synth.h"23#if 0 /* unused in Wine */24#include "fluid_cmd.h"25#include "fluid_adriver.h"26#include "fluid_mdriver.h"27#endif /* unused in Wine */28#include "fluid_settings.h"29#include "fluid_midi.h"3031/* maximum allowed components of a settings variable (separated by '.') */32#define MAX_SETTINGS_TOKENS 8 /* currently only a max of 3 are used */33#define MAX_SETTINGS_LABEL 256 /* max length of a settings variable label */3435static void fluid_settings_init(fluid_settings_t *settings);36static void fluid_settings_key_destroy_func(void *value);37static void fluid_settings_value_destroy_func(void *value);38static int fluid_settings_tokenize(const char *s, char *buf, char **ptr);3940/* Common structure to all settings nodes */41typedef struct42{43char *value;44char *def;45int hints;46fluid_list_t *options;47fluid_str_update_t update;48void *data;49} fluid_str_setting_t;5051typedef struct52{53double value;54double def;55double min;56double max;57int hints;58fluid_num_update_t update;59void *data;60} fluid_num_setting_t;6162typedef struct63{64int value;65int def;66int min;67int max;68int hints;69fluid_int_update_t update;70void *data;71} fluid_int_setting_t;7273typedef struct74{75fluid_hashtable_t *hashtable;76} fluid_set_setting_t;7778typedef struct79{80int type; /**< fluid_types_enum */8182union83{84fluid_str_setting_t str;85fluid_num_setting_t num;86fluid_int_setting_t i;87fluid_set_setting_t set;88};89} fluid_setting_node_t;9091static fluid_setting_node_t *92new_fluid_str_setting(const char *value, const char *def, int hints)93{94fluid_setting_node_t *node;95fluid_str_setting_t *str;9697node = FLUID_NEW(fluid_setting_node_t);9899if(!node)100{101FLUID_LOG(FLUID_ERR, "Out of memory");102return NULL;103}104105node->type = FLUID_STR_TYPE;106107str = &node->str;108str->value = value ? FLUID_STRDUP(value) : NULL;109str->def = def ? FLUID_STRDUP(def) : NULL;110str->hints = hints;111str->options = NULL;112str->update = NULL;113str->data = NULL;114return node;115}116117static void118delete_fluid_str_setting(fluid_setting_node_t *node)119{120fluid_return_if_fail(node != NULL);121122FLUID_ASSERT(node->type == FLUID_STR_TYPE);123124FLUID_FREE(node->str.value);125FLUID_FREE(node->str.def);126127if(node->str.options)128{129fluid_list_t *list = node->str.options;130131while(list)132{133FLUID_FREE(list->data);134list = fluid_list_next(list);135}136137delete_fluid_list(node->str.options);138}139140FLUID_FREE(node);141}142143144static fluid_setting_node_t *145new_fluid_num_setting(double min, double max, double def, int hints)146{147fluid_setting_node_t *node;148fluid_num_setting_t *num;149150node = FLUID_NEW(fluid_setting_node_t);151152if(!node)153{154FLUID_LOG(FLUID_ERR, "Out of memory");155return NULL;156}157158node->type = FLUID_NUM_TYPE;159160num = &node->num;161num->value = def;162num->def = def;163num->min = min;164num->max = max;165num->hints = hints;166num->update = NULL;167num->data = NULL;168169return node;170}171172static void173delete_fluid_num_setting(fluid_setting_node_t *node)174{175fluid_return_if_fail(node != NULL);176177FLUID_ASSERT(node->type == FLUID_NUM_TYPE);178FLUID_FREE(node);179}180181static fluid_setting_node_t *182new_fluid_int_setting(int min, int max, int def, int hints)183{184fluid_setting_node_t *node;185fluid_int_setting_t *i;186187node = FLUID_NEW(fluid_setting_node_t);188189if(!node)190{191FLUID_LOG(FLUID_ERR, "Out of memory");192return NULL;193}194195node->type = FLUID_INT_TYPE;196197i = &node->i;198i->value = def;199i->def = def;200i->min = min;201i->max = max;202i->hints = hints;203i->update = NULL;204i->data = NULL;205return node;206}207208static void209delete_fluid_int_setting(fluid_setting_node_t *node)210{211fluid_return_if_fail(node != NULL);212213FLUID_ASSERT(node->type == FLUID_INT_TYPE);214FLUID_FREE(node);215}216217static fluid_setting_node_t *218new_fluid_set_setting(void)219{220fluid_setting_node_t *node;221fluid_set_setting_t *set;222223node = FLUID_NEW(fluid_setting_node_t);224225if(!node)226{227FLUID_LOG(FLUID_ERR, "Out of memory");228return NULL;229}230231node->type = FLUID_SET_TYPE;232set = &node->set;233234set->hashtable = new_fluid_hashtable_full(fluid_str_hash, fluid_str_equal,235fluid_settings_key_destroy_func,236fluid_settings_value_destroy_func);237238if(!set->hashtable)239{240FLUID_FREE(node);241return NULL;242}243244return node;245}246247static void248delete_fluid_set_setting(fluid_setting_node_t *node)249{250fluid_return_if_fail(node != NULL);251252FLUID_ASSERT(node->type == FLUID_SET_TYPE);253delete_fluid_hashtable(node->set.hashtable);254FLUID_FREE(node);255}256257/**258* Create a new settings object259*260* @return the pointer to the settings object261*/262fluid_settings_t *263new_fluid_settings(void)264{265fluid_settings_t *settings;266267settings = new_fluid_hashtable_full(fluid_str_hash, fluid_str_equal,268fluid_settings_key_destroy_func,269fluid_settings_value_destroy_func);270271if(settings == NULL)272{273return NULL;274}275276fluid_rec_mutex_init(settings->mutex);277fluid_settings_init(settings);278return settings;279}280281/**282* Delete the provided settings object283*284* @param settings a settings object285*/286void287delete_fluid_settings(fluid_settings_t *settings)288{289fluid_return_if_fail(settings != NULL);290291fluid_rec_mutex_destroy(settings->mutex);292delete_fluid_hashtable(settings);293}294295/* Settings hash key destroy function */296static void297fluid_settings_key_destroy_func(void *value)298{299FLUID_FREE(value); /* Free the string key value */300}301302/* Settings hash value destroy function */303static void304fluid_settings_value_destroy_func(void *value)305{306fluid_setting_node_t *node = value;307308switch(node->type)309{310case FLUID_NUM_TYPE:311delete_fluid_num_setting(node);312break;313314case FLUID_INT_TYPE:315delete_fluid_int_setting(node);316break;317318case FLUID_STR_TYPE:319delete_fluid_str_setting(node);320break;321322case FLUID_SET_TYPE:323delete_fluid_set_setting(node);324break;325}326}327328void329fluid_settings_init(fluid_settings_t *settings)330{331fluid_return_if_fail(settings != NULL);332333fluid_synth_settings(settings);334#if 0 /* unused in Wine */335fluid_shell_settings(settings);336fluid_player_settings(settings);337fluid_file_renderer_settings(settings);338fluid_audio_driver_settings(settings);339fluid_midi_driver_settings(settings);340#endif /* unused in Wine */341}342343static int344fluid_settings_tokenize(const char *s, char *buf, char **ptr)345{346char *tokstr, *tok;347int n = 0;348349if(FLUID_STRLEN(s) > MAX_SETTINGS_LABEL)350{351FLUID_LOG(FLUID_ERR, "Setting variable name exceeded max length of %d chars",352MAX_SETTINGS_LABEL);353return 0;354}355356FLUID_STRCPY(buf, s); /* copy string to buffer, since it gets modified */357tokstr = buf;358359while((tok = fluid_strtok(&tokstr, ".")))360{361if(n >= MAX_SETTINGS_TOKENS)362{363FLUID_LOG(FLUID_ERR, "Setting variable name exceeded max token count of %d",364MAX_SETTINGS_TOKENS);365return 0;366}367else368{369ptr[n++] = tok;370}371}372373return n;374}375376/**377* Get a setting name, value and type378*379* @param settings a settings object380* @param name Settings name381* @param value Location to store setting node if found382* @return #FLUID_OK if the node exists, #FLUID_FAILED otherwise383*/384static int385fluid_settings_get(fluid_settings_t *settings, const char *name,386fluid_setting_node_t **value)387{388fluid_hashtable_t *table = settings;389fluid_setting_node_t *node = NULL;390char *tokens[MAX_SETTINGS_TOKENS];391char buf[MAX_SETTINGS_LABEL + 1];392int ntokens;393int n;394395ntokens = fluid_settings_tokenize(name, buf, tokens);396397if(table == NULL || ntokens <= 0)398{399return FLUID_FAILED;400}401402for(n = 0; n < ntokens; n++)403{404405node = fluid_hashtable_lookup(table, tokens[n]);406407if(!node)408{409return FLUID_FAILED;410}411412table = (node->type == FLUID_SET_TYPE) ? node->set.hashtable : NULL;413}414415if(value)416{417*value = node;418}419420return FLUID_OK;421}422423/**424* Set a setting name, value and type, replacing it if already exists425*426* @param settings a settings object427* @param name Settings name428* @param value Node instance to assign (used directly)429* @return #FLUID_OK if the value has been set, #FLUID_FAILED otherwise430*/431static int432fluid_settings_set(fluid_settings_t *settings, const char *name, fluid_setting_node_t *value)433{434fluid_hashtable_t *table = settings;435fluid_setting_node_t *node;436char *tokens[MAX_SETTINGS_TOKENS];437char buf[MAX_SETTINGS_LABEL + 1];438int n, num;439char *dupname;440441num = fluid_settings_tokenize(name, buf, tokens);442443if(num == 0)444{445return FLUID_FAILED;446}447448num--;449450for(n = 0; n < num; n++)451{452453node = fluid_hashtable_lookup(table, tokens[n]);454455if(node)456{457458if(node->type == FLUID_SET_TYPE)459{460table = node->set.hashtable;461}462else463{464/* path ends prematurely */465FLUID_LOG(FLUID_ERR, "'%s' is not a node. Name of the setting was '%s'", tokens[n], name);466return FLUID_FAILED;467}468469}470else471{472/* create a new node */473fluid_setting_node_t *setnode;474475dupname = FLUID_STRDUP(tokens[n]);476setnode = new_fluid_set_setting();477478if(!dupname || !setnode)479{480if(dupname)481{482FLUID_FREE(dupname);483}484else485{486FLUID_LOG(FLUID_ERR, "Out of memory");487}488489if(setnode)490{491delete_fluid_set_setting(setnode);492}493494return FLUID_FAILED;495}496497fluid_hashtable_insert(table, dupname, setnode);498table = setnode->set.hashtable;499}500}501502dupname = FLUID_STRDUP(tokens[num]);503504if(!dupname)505{506FLUID_LOG(FLUID_ERR, "Out of memory");507return FLUID_FAILED;508}509510fluid_hashtable_insert(table, dupname, value);511512return FLUID_OK;513}514515/**516* Registers a new string value for the specified setting.517*518* @param settings a settings object519* @param name the setting's name520* @param def the default value for the setting521* @param hints the hints for the setting522* @return #FLUID_OK if the value has been register correctly, #FLUID_FAILED otherwise523*/524int525fluid_settings_register_str(fluid_settings_t *settings, const char *name, const char *def, int hints)526{527fluid_setting_node_t *node;528int retval = FLUID_FAILED;529530fluid_return_val_if_fail(settings != NULL, retval);531fluid_return_val_if_fail(name != NULL, retval);532fluid_return_val_if_fail(name[0] != '\0', retval);533534fluid_rec_mutex_lock(settings->mutex);535536if(fluid_settings_get(settings, name, &node) != FLUID_OK)537{538node = new_fluid_str_setting(def, def, hints);539retval = fluid_settings_set(settings, name, node);540541if(retval != FLUID_OK)542{543delete_fluid_str_setting(node);544}545}546else547{548/* if variable already exists, don't change its value. */549if(node->type == FLUID_STR_TYPE)550{551fluid_str_setting_t *setting = &node->str;552FLUID_FREE(setting->def);553setting->def = def ? FLUID_STRDUP(def) : NULL;554setting->hints = hints;555retval = FLUID_OK;556}557else558{559FLUID_LOG(FLUID_ERR, "Failed to register string setting '%s' as it already exists with a different type", name);560}561}562563fluid_rec_mutex_unlock(settings->mutex);564565return retval;566}567568/**569* Registers a new float value for the specified setting.570*571* @param settings a settings object572* @param name the setting's name573* @param def the default value for the setting574* @param min the smallest allowed value for the setting575* @param max the largest allowed value for the setting576* @param hints the hints for the setting577* @return #FLUID_OK if the value has been register correctly, #FLUID_FAILED otherwise578*/579int580fluid_settings_register_num(fluid_settings_t *settings, const char *name, double def,581double min, double max, int hints)582{583fluid_setting_node_t *node;584int retval = FLUID_FAILED;585586fluid_return_val_if_fail(settings != NULL, retval);587fluid_return_val_if_fail(name != NULL, retval);588fluid_return_val_if_fail(name[0] != '\0', retval);589590/* For now, all floating point settings are bounded below and above */591hints |= FLUID_HINT_BOUNDED_BELOW | FLUID_HINT_BOUNDED_ABOVE;592593fluid_rec_mutex_lock(settings->mutex);594595if(fluid_settings_get(settings, name, &node) != FLUID_OK)596{597/* insert a new setting */598node = new_fluid_num_setting(min, max, def, hints);599retval = fluid_settings_set(settings, name, node);600601if(retval != FLUID_OK)602{603delete_fluid_num_setting(node);604}605}606else607{608if(node->type == FLUID_NUM_TYPE)609{610/* update the existing setting but don't change its value */611fluid_num_setting_t *setting = &node->num;612setting->min = min;613setting->max = max;614setting->def = def;615setting->hints = hints;616retval = FLUID_OK;617}618else619{620/* type mismatch */621FLUID_LOG(FLUID_ERR, "Failed to register numeric setting '%s' as it already exists with a different type", name);622}623}624625fluid_rec_mutex_unlock(settings->mutex);626627return retval;628}629630/**631* Registers a new integer value for the specified setting.632*633* @param settings a settings object634* @param name the setting's name635* @param def the default value for the setting636* @param min the smallest allowed value for the setting637* @param max the largest allowed value for the setting638* @param hints the hints for the setting639* @return #FLUID_OK if the value has been register correctly, #FLUID_FAILED otherwise640*/641int642fluid_settings_register_int(fluid_settings_t *settings, const char *name, int def,643int min, int max, int hints)644{645fluid_setting_node_t *node;646int retval = FLUID_FAILED;647648fluid_return_val_if_fail(settings != NULL, retval);649fluid_return_val_if_fail(name != NULL, retval);650fluid_return_val_if_fail(name[0] != '\0', retval);651652/* For now, all integer settings are bounded below and above */653hints |= FLUID_HINT_BOUNDED_BELOW | FLUID_HINT_BOUNDED_ABOVE;654655fluid_rec_mutex_lock(settings->mutex);656657if(fluid_settings_get(settings, name, &node) != FLUID_OK)658{659/* insert a new setting */660node = new_fluid_int_setting(min, max, def, hints);661retval = fluid_settings_set(settings, name, node);662663if(retval != FLUID_OK)664{665delete_fluid_int_setting(node);666}667}668else669{670if(node->type == FLUID_INT_TYPE)671{672/* update the existing setting but don't change its value */673fluid_int_setting_t *setting = &node->i;674setting->min = min;675setting->max = max;676setting->def = def;677setting->hints = hints;678retval = FLUID_OK;679}680else681{682/* type mismatch */683FLUID_LOG(FLUID_ERR, "Failed to register int setting '%s' as it already exists with a different type", name);684}685}686687fluid_rec_mutex_unlock(settings->mutex);688689return retval;690}691692/**693* Registers a callback for the specified string setting.694*695* @param settings a settings object696* @param name the setting's name697* @param callback an update function for the setting698* @param data user supplied data passed to the update function699* @return #FLUID_OK if the callback has been set, #FLUID_FAILED otherwise700*/701int fluid_settings_callback_str(fluid_settings_t *settings, const char *name,702fluid_str_update_t callback, void *data)703{704fluid_setting_node_t *node;705fluid_str_setting_t *setting;706707fluid_return_val_if_fail(settings != NULL, FLUID_FAILED);708fluid_return_val_if_fail(name != NULL, FLUID_FAILED);709fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED);710711fluid_rec_mutex_lock(settings->mutex);712713if((fluid_settings_get(settings, name, &node) != FLUID_OK)714|| node->type != FLUID_STR_TYPE)715{716fluid_rec_mutex_unlock(settings->mutex);717return FLUID_FAILED;718}719720setting = &node->str;721setting->update = callback;722setting->data = data;723724fluid_rec_mutex_unlock(settings->mutex);725return FLUID_OK;726}727728/**729* Registers a callback for the specified numeric setting.730*731* @param settings a settings object732* @param name the setting's name733* @param callback an update function for the setting734* @param data user supplied data passed to the update function735* @return #FLUID_OK if the callback has been set, #FLUID_FAILED otherwise736*/737int fluid_settings_callback_num(fluid_settings_t *settings, const char *name,738fluid_num_update_t callback, void *data)739{740fluid_setting_node_t *node;741fluid_num_setting_t *setting;742743fluid_return_val_if_fail(settings != NULL, FLUID_FAILED);744fluid_return_val_if_fail(name != NULL, FLUID_FAILED);745fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED);746747fluid_rec_mutex_lock(settings->mutex);748749if((fluid_settings_get(settings, name, &node) != FLUID_OK)750|| node->type != FLUID_NUM_TYPE)751{752fluid_rec_mutex_unlock(settings->mutex);753return FLUID_FAILED;754}755756setting = &node->num;757setting->update = callback;758setting->data = data;759760fluid_rec_mutex_unlock(settings->mutex);761return FLUID_OK;762}763764/**765* Registers a callback for the specified int setting.766*767* @param settings a settings object768* @param name the setting's name769* @param callback an update function for the setting770* @param data user supplied data passed to the update function771* @return #FLUID_OK if the callback has been set, #FLUID_FAILED otherwise772*/773int fluid_settings_callback_int(fluid_settings_t *settings, const char *name,774fluid_int_update_t callback, void *data)775{776fluid_setting_node_t *node;777fluid_int_setting_t *setting;778779fluid_return_val_if_fail(settings != NULL, FLUID_FAILED);780fluid_return_val_if_fail(name != NULL, FLUID_FAILED);781fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED);782783fluid_rec_mutex_lock(settings->mutex);784785if((fluid_settings_get(settings, name, &node) != FLUID_OK)786|| node->type != FLUID_INT_TYPE)787{788fluid_rec_mutex_unlock(settings->mutex);789return FLUID_FAILED;790}791792setting = &node->i;793setting->update = callback;794setting->data = data;795796fluid_rec_mutex_unlock(settings->mutex);797return FLUID_OK;798}799800void* fluid_settings_get_user_data(fluid_settings_t * settings, const char *name)801{802fluid_setting_node_t *node;803void* retval = NULL;804805fluid_return_val_if_fail(settings != NULL, NULL);806fluid_return_val_if_fail(name != NULL, NULL);807fluid_return_val_if_fail(name[0] != '\0', NULL);808809fluid_rec_mutex_lock(settings->mutex);810811if(fluid_settings_get(settings, name, &node) == FLUID_OK)812{813if(node->type == FLUID_NUM_TYPE)814{815fluid_num_setting_t *setting = &node->num;816retval = setting->data;817}818else if(node->type == FLUID_STR_TYPE)819{820fluid_str_setting_t *setting = &node->str;821retval = setting->data;822}823else if(node->type == FLUID_INT_TYPE)824{825fluid_int_setting_t *setting = &node->i;826retval = setting->data;827}828}829830fluid_rec_mutex_unlock(settings->mutex);831return retval;832}833834/**835* Get the type of the setting with the given name836*837* @param settings a settings object838* @param name a setting's name839* @return the type for the named setting (see #fluid_types_enum), or #FLUID_NO_TYPE when it does not exist840*/841int842fluid_settings_get_type(fluid_settings_t *settings, const char *name)843{844fluid_setting_node_t *node;845int type = FLUID_NO_TYPE;846847fluid_return_val_if_fail(settings != NULL, type);848fluid_return_val_if_fail(name != NULL, type);849fluid_return_val_if_fail(name[0] != '\0', type);850851fluid_rec_mutex_lock(settings->mutex);852853if(fluid_settings_get(settings, name, &node) == FLUID_OK)854{855type = node->type;856}857858fluid_rec_mutex_unlock(settings->mutex);859860return type;861}862863/**864* Get the hints for the named setting as an integer bitmap865*866* @param settings a settings object867* @param name a setting's name868* @param hints set to the hints associated to the setting if it exists869* @return #FLUID_OK if hints associated to the named setting exist, #FLUID_FAILED otherwise870*/871int872fluid_settings_get_hints(fluid_settings_t *settings, const char *name, int *hints)873{874fluid_setting_node_t *node;875int retval = FLUID_FAILED;876877fluid_return_val_if_fail(settings != NULL, retval);878fluid_return_val_if_fail(name != NULL, retval);879fluid_return_val_if_fail(name[0] != '\0', retval);880881fluid_rec_mutex_lock(settings->mutex);882883if(fluid_settings_get(settings, name, &node) == FLUID_OK)884{885if(node->type == FLUID_NUM_TYPE)886{887fluid_num_setting_t *setting = &node->num;888*hints = setting->hints;889retval = FLUID_OK;890}891else if(node->type == FLUID_STR_TYPE)892{893fluid_str_setting_t *setting = &node->str;894*hints = setting->hints;895retval = FLUID_OK;896}897else if(node->type == FLUID_INT_TYPE)898{899fluid_int_setting_t *setting = &node->i;900*hints = setting->hints;901retval = FLUID_OK;902}903}904905fluid_rec_mutex_unlock(settings->mutex);906907return retval;908}909910/**911* Ask whether the setting is changeable in real-time.912*913* @param settings a settings object914* @param name a setting's name915* @return TRUE if the setting is changeable in real-time, FALSE otherwise916*917* @note Before using this function, make sure the @p settings object has already been used to create918* a synthesizer, a MIDI driver, an audio driver, a MIDI player, or a command handler (depending on919* which settings you want to query).920*/921int922fluid_settings_is_realtime(fluid_settings_t *settings, const char *name)923{924fluid_setting_node_t *node;925int isrealtime = FALSE;926927fluid_return_val_if_fail(settings != NULL, 0);928fluid_return_val_if_fail(name != NULL, 0);929fluid_return_val_if_fail(name[0] != '\0', 0);930931fluid_rec_mutex_lock(settings->mutex);932933if(fluid_settings_get(settings, name, &node) == FLUID_OK)934{935if(node->type == FLUID_NUM_TYPE)936{937fluid_num_setting_t *setting = &node->num;938isrealtime = setting->update != NULL;939}940else if(node->type == FLUID_STR_TYPE)941{942fluid_str_setting_t *setting = &node->str;943isrealtime = setting->update != NULL;944}945else if(node->type == FLUID_INT_TYPE)946{947fluid_int_setting_t *setting = &node->i;948isrealtime = setting->update != NULL;949}950}951952fluid_rec_mutex_unlock(settings->mutex);953954return isrealtime;955}956957/**958* Set a string value for a named setting959*960* @param settings a settings object961* @param name a setting's name962* @param str new string value963* @return #FLUID_OK if the value has been set, #FLUID_FAILED otherwise964*/965int966fluid_settings_setstr(fluid_settings_t *settings, const char *name, const char *str)967{968fluid_setting_node_t *node;969fluid_str_setting_t *setting;970char *new_value = NULL;971fluid_str_update_t callback = NULL;972void *data = NULL;973974fluid_return_val_if_fail(settings != NULL, FLUID_FAILED);975fluid_return_val_if_fail(name != NULL, FLUID_FAILED);976fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED);977978fluid_rec_mutex_lock(settings->mutex);979980if((fluid_settings_get(settings, name, &node) != FLUID_OK)981|| (node->type != FLUID_STR_TYPE))982{983FLUID_LOG(FLUID_ERR, "Unknown string setting '%s'", name);984goto error_recovery;985}986987setting = &node->str;988989if(setting->value)990{991FLUID_FREE(setting->value);992}993994if(str)995{996new_value = FLUID_STRDUP(str);997998if(new_value == NULL)999{1000FLUID_LOG(FLUID_ERR, "Out of memory");1001goto error_recovery;1002}1003}10041005setting->value = new_value;10061007callback = setting->update;1008data = setting->data;10091010/* Release the mutex before calling the update callback, to avoid1011* possible deadlocks with FluidSynths API lock */1012fluid_rec_mutex_unlock(settings->mutex);10131014if(callback)1015{1016(*callback)(data, name, new_value);1017}10181019return FLUID_OK;10201021error_recovery:1022fluid_rec_mutex_unlock(settings->mutex);1023return FLUID_FAILED;1024}10251026/**1027* Copy the value of a string setting into the provided buffer (thread safe)1028*1029* @param settings a settings object1030* @param name a setting's name1031* @param str Caller supplied buffer to copy string value to1032* @param len Size of 'str' buffer (no more than len bytes will be written, which1033* will always include a zero terminator)1034* @return #FLUID_OK if the value exists, #FLUID_FAILED otherwise1035*1036* @note A size of 256 should be more than sufficient for the string buffer.1037*1038* @since 1.1.01039*/1040int1041fluid_settings_copystr(fluid_settings_t *settings, const char *name,1042char *str, int len)1043{1044fluid_setting_node_t *node;1045int retval = FLUID_FAILED;10461047fluid_return_val_if_fail(settings != NULL, retval);1048fluid_return_val_if_fail(name != NULL, retval);1049fluid_return_val_if_fail(name[0] != '\0', retval);1050fluid_return_val_if_fail(str != NULL, retval);1051fluid_return_val_if_fail(len > 0, retval);10521053str[0] = 0;10541055fluid_rec_mutex_lock(settings->mutex);10561057if(fluid_settings_get(settings, name, &node) == FLUID_OK)1058{1059if(node->type == FLUID_STR_TYPE)1060{1061fluid_str_setting_t *setting = &node->str;10621063if(setting->value)1064{1065FLUID_STRNCPY(str, setting->value, len);1066}10671068retval = FLUID_OK;1069}1070else if(node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */1071{1072fluid_int_setting_t *setting = &node->i;10731074if(setting->hints & FLUID_HINT_TOGGLED)1075{1076FLUID_STRNCPY(str, setting->value ? "yes" : "no", len);10771078retval = FLUID_OK;1079}1080}1081}10821083fluid_rec_mutex_unlock(settings->mutex);10841085return retval;1086}10871088/**1089* Duplicate the value of a string setting1090*1091* @param settings a settings object1092* @param name a setting's name1093* @param str Location to store pointer to allocated duplicate string1094* @return #FLUID_OK if the value exists and was successfully duplicated, #FLUID_FAILED otherwise1095*1096* Like fluid_settings_copystr() but allocates a new copy of the string. Caller1097* owns the string and should free it with fluid_free() when done using it.1098*1099* @since 1.1.01100*/1101int1102fluid_settings_dupstr(fluid_settings_t *settings, const char *name, char **str)1103{1104fluid_setting_node_t *node;1105int retval = FLUID_FAILED;11061107fluid_return_val_if_fail(settings != NULL, retval);1108fluid_return_val_if_fail(name != NULL, retval);1109fluid_return_val_if_fail(name[0] != '\0', retval);1110fluid_return_val_if_fail(str != NULL, retval);11111112fluid_rec_mutex_lock(settings->mutex);11131114if(fluid_settings_get(settings, name, &node) == FLUID_OK)1115{1116if(node->type == FLUID_STR_TYPE)1117{1118fluid_str_setting_t *setting = &node->str;11191120if(setting->value)1121{1122*str = FLUID_STRDUP(setting->value);11231124if(!*str)1125{1126FLUID_LOG(FLUID_ERR, "Out of memory");1127}1128}11291130if(!setting->value || *str)1131{1132retval = FLUID_OK; /* Don't set to FLUID_OK if out of memory */1133}1134}1135else if(node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */1136{1137fluid_int_setting_t *setting = &node->i;11381139if(setting->hints & FLUID_HINT_TOGGLED)1140{1141*str = FLUID_STRDUP(setting->value ? "yes" : "no");11421143if(!*str)1144{1145FLUID_LOG(FLUID_ERR, "Out of memory");1146}11471148if(!setting->value || *str)1149{1150retval = FLUID_OK; /* Don't set to FLUID_OK if out of memory */1151}1152}1153}1154}11551156fluid_rec_mutex_unlock(settings->mutex);11571158return retval;1159}116011611162/**1163* Test a string setting for some value.1164*1165* @param settings a settings object1166* @param name a setting's name1167* @param s a string to be tested1168* @return TRUE if the value exists and is equal to \c s, FALSE otherwise1169*/1170int1171fluid_settings_str_equal(fluid_settings_t *settings, const char *name, const char *s)1172{1173fluid_setting_node_t *node;1174int retval = FALSE;11751176fluid_return_val_if_fail(settings != NULL, retval);1177fluid_return_val_if_fail(name != NULL, retval);1178fluid_return_val_if_fail(name[0] != '\0', retval);1179fluid_return_val_if_fail(s != NULL, retval);11801181fluid_rec_mutex_lock(settings->mutex);11821183if(fluid_settings_get(settings, name, &node) == FLUID_OK)1184{1185if(node->type == FLUID_STR_TYPE)1186{1187fluid_str_setting_t *setting = &node->str;11881189if(setting->value)1190{1191retval = FLUID_STRCMP(setting->value, s) == 0;1192}1193}1194else if(node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */1195{1196fluid_int_setting_t *setting = &node->i;11971198if(setting->hints & FLUID_HINT_TOGGLED)1199{1200retval = FLUID_STRCMP(setting->value ? "yes" : "no", s) == 0;1201}1202}1203}12041205fluid_rec_mutex_unlock(settings->mutex);12061207return retval;1208}12091210/**1211* Get the default value of a string setting.1212*1213* @param settings a settings object1214* @param name a setting's name1215* @param def the default string value of the setting if it exists1216* @return FLUID_OK if a default value exists, FLUID_FAILED otherwise1217*1218* @note The returned string is not owned by the caller and should not be modified or freed.1219*/1220int1221fluid_settings_getstr_default(fluid_settings_t *settings, const char *name, char **def)1222{1223fluid_setting_node_t *node;1224char *retval = NULL;12251226fluid_return_val_if_fail(settings != NULL, FLUID_FAILED);1227fluid_return_val_if_fail(name != NULL, FLUID_FAILED);1228fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED);12291230fluid_rec_mutex_lock(settings->mutex);12311232if(fluid_settings_get(settings, name, &node) == FLUID_OK)1233{1234if(node->type == FLUID_STR_TYPE)1235{1236fluid_str_setting_t *setting = &node->str;1237retval = setting->def;1238}1239else if(node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */1240{1241fluid_int_setting_t *setting = &node->i;12421243if(setting->hints & FLUID_HINT_TOGGLED)1244{1245retval = setting->def ? "yes" : "no";1246}1247}1248}12491250*def = retval;1251fluid_rec_mutex_unlock(settings->mutex);12521253return retval != NULL ? FLUID_OK : FLUID_FAILED;1254}12551256/**1257* Add an option to a string setting (like an enumeration value).1258*1259* @param settings a settings object1260* @param name a setting's name1261* @param s option string to add1262* @return #FLUID_OK if the setting exists and option was added, #FLUID_FAILED otherwise1263*1264* Causes the setting's #FLUID_HINT_OPTIONLIST hint to be set.1265*/1266int1267fluid_settings_add_option(fluid_settings_t *settings, const char *name, const char *s)1268{1269fluid_setting_node_t *node;1270int retval = FLUID_FAILED;12711272fluid_return_val_if_fail(settings != NULL, retval);1273fluid_return_val_if_fail(name != NULL, retval);1274fluid_return_val_if_fail(name[0] != '\0', retval);1275fluid_return_val_if_fail(s != NULL, retval);12761277fluid_rec_mutex_lock(settings->mutex);12781279if(fluid_settings_get(settings, name, &node) == FLUID_OK1280&& (node->type == FLUID_STR_TYPE))1281{1282fluid_str_setting_t *setting = &node->str;1283char *copy = FLUID_STRDUP(s);1284setting->options = fluid_list_append(setting->options, copy);1285setting->hints |= FLUID_HINT_OPTIONLIST;1286retval = FLUID_OK;1287}12881289fluid_rec_mutex_unlock(settings->mutex);12901291return retval;1292}12931294/**1295* Remove an option previously assigned by fluid_settings_add_option().1296*1297* @param settings a settings object1298* @param name a setting's name1299* @param s option string to remove1300* @return #FLUID_OK if the setting exists and option was removed, #FLUID_FAILED otherwise1301*/1302int1303fluid_settings_remove_option(fluid_settings_t *settings, const char *name, const char *s)1304{1305fluid_setting_node_t *node;1306int retval = FLUID_FAILED;13071308fluid_return_val_if_fail(settings != NULL, retval);1309fluid_return_val_if_fail(name != NULL, retval);1310fluid_return_val_if_fail(name[0] != '\0', retval);1311fluid_return_val_if_fail(s != NULL, retval);13121313fluid_rec_mutex_lock(settings->mutex);13141315if(fluid_settings_get(settings, name, &node) == FLUID_OK1316&& (node->type == FLUID_STR_TYPE))1317{13181319fluid_str_setting_t *setting = &node->str;1320fluid_list_t *list = setting->options;13211322while(list)1323{1324char *option = (char *) fluid_list_get(list);13251326if(FLUID_STRCMP(s, option) == 0)1327{1328FLUID_FREE(option);1329setting->options = fluid_list_remove_link(setting->options, list);1330retval = FLUID_OK;1331break;1332}13331334list = fluid_list_next(list);1335}1336}13371338fluid_rec_mutex_unlock(settings->mutex);13391340return retval;1341}13421343/**1344* Set a numeric value for a named setting.1345*1346* @param settings a settings object1347* @param name a setting's name1348* @param val new setting's value1349* @return #FLUID_OK if the value has been set, #FLUID_FAILED otherwise1350*/1351int1352fluid_settings_setnum(fluid_settings_t *settings, const char *name, double val)1353{1354fluid_setting_node_t *node;1355fluid_num_setting_t *setting;1356fluid_num_update_t callback = NULL;1357void *data = NULL;13581359fluid_return_val_if_fail(settings != NULL, FLUID_FAILED);1360fluid_return_val_if_fail(name != NULL, FLUID_FAILED);1361fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED);13621363fluid_rec_mutex_lock(settings->mutex);13641365if((fluid_settings_get(settings, name, &node) != FLUID_OK)1366|| (node->type != FLUID_NUM_TYPE))1367{1368FLUID_LOG(FLUID_ERR, "Unknown numeric setting '%s'", name);1369goto error_recovery;1370}13711372setting = &node->num;13731374if(val < setting->min || val > setting->max)1375{1376FLUID_LOG(FLUID_ERR, "requested set value for '%s' out of range", name);1377goto error_recovery;1378}13791380setting->value = val;13811382callback = setting->update;1383data = setting->data;13841385/* Release the mutex before calling the update callback, to avoid1386* possible deadlocks with FluidSynths API lock */1387fluid_rec_mutex_unlock(settings->mutex);13881389if(callback)1390{1391(*callback)(data, name, val);1392}13931394return FLUID_OK;13951396error_recovery:1397fluid_rec_mutex_unlock(settings->mutex);1398return FLUID_FAILED;1399}14001401/**1402* Get the numeric value of a named setting1403*1404* @param settings a settings object1405* @param name a setting's name1406* @param val variable pointer to receive the setting's numeric value1407* @return #FLUID_OK if the value exists, #FLUID_FAILED otherwise1408*/1409int1410fluid_settings_getnum(fluid_settings_t *settings, const char *name, double *val)1411{1412fluid_setting_node_t *node;1413int retval = FLUID_FAILED;14141415fluid_return_val_if_fail(settings != NULL, retval);1416fluid_return_val_if_fail(name != NULL, retval);1417fluid_return_val_if_fail(name[0] != '\0', retval);1418fluid_return_val_if_fail(val != NULL, retval);14191420fluid_rec_mutex_lock(settings->mutex);14211422if(fluid_settings_get(settings, name, &node) == FLUID_OK1423&& (node->type == FLUID_NUM_TYPE))1424{1425fluid_num_setting_t *setting = &node->num;1426*val = setting->value;1427retval = FLUID_OK;1428}14291430fluid_rec_mutex_unlock(settings->mutex);14311432return retval;1433}14341435/**1436* float-typed wrapper for fluid_settings_getnum1437*1438* @param settings a settings object1439* @param name a setting's name1440* @param val variable pointer to receive the setting's float value1441* @return #FLUID_OK if the value exists, #FLUID_FAILED otherwise1442*/1443int fluid_settings_getnum_float(fluid_settings_t *settings, const char *name, float *val)1444{1445double tmp;14461447if(fluid_settings_getnum(settings, name, &tmp) == FLUID_OK)1448{1449*val = tmp;1450return FLUID_OK;1451}14521453return FLUID_FAILED;1454}14551456/**1457* Get the range of values of a numeric setting1458*1459* @param settings a settings object1460* @param name a setting's name1461* @param min setting's range lower limit1462* @param max setting's range upper limit1463* @return #FLUID_OK if the setting's range exists, #FLUID_FAILED otherwise1464*/1465int1466fluid_settings_getnum_range(fluid_settings_t *settings, const char *name,1467double *min, double *max)1468{1469fluid_setting_node_t *node;1470int retval = FLUID_FAILED;14711472fluid_return_val_if_fail(settings != NULL, retval);1473fluid_return_val_if_fail(name != NULL, retval);1474fluid_return_val_if_fail(name[0] != '\0', retval);1475fluid_return_val_if_fail(min != NULL, retval);1476fluid_return_val_if_fail(max != NULL, retval);14771478fluid_rec_mutex_lock(settings->mutex);14791480if(fluid_settings_get(settings, name, &node) == FLUID_OK1481&& (node->type == FLUID_NUM_TYPE))1482{1483fluid_num_setting_t *setting = &node->num;1484*min = setting->min;1485*max = setting->max;1486retval = FLUID_OK;1487}14881489fluid_rec_mutex_unlock(settings->mutex);14901491return retval;1492}14931494/**1495* Get the default value of a named numeric (double) setting1496*1497* @param settings a settings object1498* @param name a setting's name1499* @param val set to the default value if the named setting exists1500* @return #FLUID_OK if the default value of the named setting exists, #FLUID_FAILED otherwise1501*/1502int1503fluid_settings_getnum_default(fluid_settings_t *settings, const char *name, double *val)1504{1505fluid_setting_node_t *node;1506int retval = FLUID_FAILED;15071508fluid_return_val_if_fail(settings != NULL, retval);1509fluid_return_val_if_fail(name != NULL, retval);1510fluid_return_val_if_fail(name[0] != '\0', retval);1511fluid_return_val_if_fail(val != NULL, retval);15121513fluid_rec_mutex_lock(settings->mutex);15141515if(fluid_settings_get(settings, name, &node) == FLUID_OK1516&& (node->type == FLUID_NUM_TYPE))1517{1518fluid_num_setting_t *setting = &node->num;1519*val = setting->def;1520retval = FLUID_OK;1521}15221523fluid_rec_mutex_unlock(settings->mutex);15241525return retval;1526}15271528/**1529* Set an integer value for a setting1530*1531* @param settings a settings object1532* @param name a setting's name1533* @param val new setting's integer value1534* @return #FLUID_OK if the value has been set, #FLUID_FAILED otherwise1535*/1536int1537fluid_settings_setint(fluid_settings_t *settings, const char *name, int val)1538{1539fluid_setting_node_t *node;1540fluid_int_setting_t *setting;1541fluid_int_update_t callback = NULL;1542void *data = NULL;15431544fluid_return_val_if_fail(settings != NULL, FLUID_FAILED);1545fluid_return_val_if_fail(name != NULL, FLUID_FAILED);1546fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED);15471548fluid_rec_mutex_lock(settings->mutex);15491550if((fluid_settings_get(settings, name, &node) != FLUID_OK)1551|| (node->type != FLUID_INT_TYPE))1552{1553FLUID_LOG(FLUID_ERR, "Unknown integer parameter '%s'", name);1554goto error_recovery;1555}15561557setting = &node->i;15581559if(val < setting->min || val > setting->max)1560{1561FLUID_LOG(FLUID_ERR, "requested set value for setting '%s' out of range", name);1562goto error_recovery;1563}15641565setting->value = val;15661567callback = setting->update;1568data = setting->data;15691570/* Release the mutex before calling the update callback, to avoid1571* possible deadlocks with FluidSynths API lock */1572fluid_rec_mutex_unlock(settings->mutex);15731574if(callback)1575{1576(*callback)(data, name, val);1577}15781579return FLUID_OK;15801581error_recovery:1582fluid_rec_mutex_unlock(settings->mutex);1583return FLUID_FAILED;1584}15851586/**1587* Get an integer value setting.1588*1589* @param settings a settings object1590* @param name a setting's name1591* @param val pointer to a variable to receive the setting's integer value1592* @return #FLUID_OK if the value exists, #FLUID_FAILED otherwise1593*/1594int1595fluid_settings_getint(fluid_settings_t *settings, const char *name, int *val)1596{1597fluid_setting_node_t *node;1598int retval = FLUID_FAILED;15991600fluid_return_val_if_fail(settings != NULL, retval);1601fluid_return_val_if_fail(name != NULL, retval);1602fluid_return_val_if_fail(name[0] != '\0', retval);1603fluid_return_val_if_fail(val != NULL, retval);16041605fluid_rec_mutex_lock(settings->mutex);16061607if(fluid_settings_get(settings, name, &node) == FLUID_OK1608&& (node->type == FLUID_INT_TYPE))1609{1610fluid_int_setting_t *setting = &node->i;1611*val = setting->value;1612retval = FLUID_OK;1613}16141615fluid_rec_mutex_unlock(settings->mutex);16161617return retval;1618}16191620/**1621* Get the range of values of an integer setting1622*1623* @param settings a settings object1624* @param name a setting's name1625* @param min setting's range lower limit1626* @param max setting's range upper limit1627* @return #FLUID_OK if the setting's range exists, #FLUID_FAILED otherwise1628*/1629int1630fluid_settings_getint_range(fluid_settings_t *settings, const char *name,1631int *min, int *max)1632{1633fluid_setting_node_t *node;1634int retval = FLUID_FAILED;16351636fluid_return_val_if_fail(settings != NULL, retval);1637fluid_return_val_if_fail(name != NULL, retval);1638fluid_return_val_if_fail(name[0] != '\0', retval);1639fluid_return_val_if_fail(min != NULL, retval);1640fluid_return_val_if_fail(max != NULL, retval);16411642fluid_rec_mutex_lock(settings->mutex);16431644if(fluid_settings_get(settings, name, &node) == FLUID_OK1645&& (node->type == FLUID_INT_TYPE))1646{1647fluid_int_setting_t *setting = &node->i;1648*min = setting->min;1649*max = setting->max;1650retval = FLUID_OK;1651}16521653fluid_rec_mutex_unlock(settings->mutex);16541655return retval;1656}16571658/**1659* Get the default value of an integer setting.1660*1661* @param settings a settings object1662* @param name a setting's name1663* @param val set to the setting's default integer value if it exists1664* @return #FLUID_OK if the setting's default integer value exists, #FLUID_FAILED otherwise1665*/1666int fluid_settings_getint_default(fluid_settings_t *settings, const char *name, int *val)1667{1668fluid_setting_node_t *node;1669int retval = FLUID_FAILED;16701671fluid_return_val_if_fail(settings != NULL, retval);1672fluid_return_val_if_fail(name != NULL, retval);1673fluid_return_val_if_fail(name[0] != '\0', retval);1674fluid_return_val_if_fail(val != NULL, retval);16751676fluid_rec_mutex_lock(settings->mutex);16771678if(fluid_settings_get(settings, name, &node) == FLUID_OK1679&& (node->type == FLUID_INT_TYPE))1680{1681fluid_int_setting_t *setting = &node->i;1682*val = setting->def;1683retval = FLUID_OK;1684}16851686fluid_rec_mutex_unlock(settings->mutex);16871688return retval;1689}16901691/**1692* Iterate the available options for a named string setting, calling the provided1693* callback function for each existing option.1694*1695* @param settings a settings object1696* @param name a setting's name1697* @param data any user provided pointer1698* @param func callback function to be called on each iteration1699*1700* @note Starting with FluidSynth 1.1.0 the \p func callback is called for each1701* option in alphabetical order. Sort order was undefined in previous versions.1702*/1703void1704fluid_settings_foreach_option(fluid_settings_t *settings, const char *name,1705void *data, fluid_settings_foreach_option_t func)1706{1707fluid_setting_node_t *node;1708fluid_str_setting_t *setting;1709fluid_list_t *p, *newlist = NULL;17101711fluid_return_if_fail(settings != NULL);1712fluid_return_if_fail(name != NULL);1713fluid_return_if_fail(name[0] != '\0');1714fluid_return_if_fail(func != NULL);17151716fluid_rec_mutex_lock(settings->mutex); /* ++ lock */17171718if(fluid_settings_get(settings, name, &node) != FLUID_OK1719|| node->type != FLUID_STR_TYPE)1720{1721fluid_rec_mutex_unlock(settings->mutex); /* -- unlock */1722return;1723}17241725setting = &node->str;17261727/* Duplicate option list */1728for(p = setting->options; p; p = p->next)1729{1730newlist = fluid_list_append(newlist, fluid_list_get(p));1731}17321733/* Sort by name */1734newlist = fluid_list_sort(newlist, fluid_list_str_compare_func);17351736for(p = newlist; p; p = p->next)1737{1738(*func)(data, name, (const char *)fluid_list_get(p));1739}17401741fluid_rec_mutex_unlock(settings->mutex); /* -- unlock */17421743delete_fluid_list(newlist);1744}17451746/**1747* Count option string values for a string setting.1748*1749* @param settings a settings object1750* @param name Name of setting1751* @return Count of options for this string setting (0 if none, -1 if not found1752* or not a string setting)1753*1754* @since 1.1.01755*/1756int1757fluid_settings_option_count(fluid_settings_t *settings, const char *name)1758{1759fluid_setting_node_t *node;1760int count = -1;17611762fluid_return_val_if_fail(settings != NULL, -1);1763fluid_return_val_if_fail(name != NULL, -1);1764fluid_return_val_if_fail(name[0] != '\0', -1);17651766fluid_rec_mutex_lock(settings->mutex);17671768if(fluid_settings_get(settings, name, &node) == FLUID_OK1769&& node->type == FLUID_STR_TYPE)1770{1771count = fluid_list_size(node->str.options);1772}17731774fluid_rec_mutex_unlock(settings->mutex);17751776return (count);1777}17781779/**1780* Concatenate options for a string setting together with a separator between.1781*1782* @param settings Settings object1783* @param name Settings name1784* @param separator String to use between options (NULL to use ", ")1785* @return Newly allocated string or NULL on error (out of memory, not a valid1786* setting \p name or not a string setting). Free the string when finished with it by using fluid_free().1787*1788* @since 1.1.01789*/1790char *1791fluid_settings_option_concat(fluid_settings_t *settings, const char *name,1792const char *separator)1793{1794fluid_setting_node_t *node;1795fluid_list_t *p, *newlist = NULL;1796size_t count, len;1797char *str, *option;17981799fluid_return_val_if_fail(settings != NULL, NULL);1800fluid_return_val_if_fail(name != NULL, NULL);1801fluid_return_val_if_fail(name[0] != '\0', NULL);18021803if(!separator)1804{1805separator = ", ";1806}18071808fluid_rec_mutex_lock(settings->mutex); /* ++ lock */18091810if(fluid_settings_get(settings, name, &node) != FLUID_OK1811|| node->type != FLUID_STR_TYPE)1812{1813fluid_rec_mutex_unlock(settings->mutex); /* -- unlock */1814return (NULL);1815}18161817/* Duplicate option list, count options and get total string length */1818for(p = node->str.options, count = 0, len = 0; p; p = p->next)1819{1820option = fluid_list_get(p);18211822if(option)1823{1824newlist = fluid_list_append(newlist, option);1825len += FLUID_STRLEN(option);1826count++;1827}1828}18291830if(count > 1)1831{1832len += (count - 1) * FLUID_STRLEN(separator);1833}18341835len++; /* For terminator */18361837/* Sort by name */1838newlist = fluid_list_sort(newlist, fluid_list_str_compare_func);18391840str = FLUID_MALLOC(len);18411842if(str)1843{1844str[0] = 0;18451846for(p = newlist; p; p = p->next)1847{1848option = fluid_list_get(p);1849strcat(str, option);18501851if(p->next)1852{1853strcat(str, separator);1854}1855}1856}18571858fluid_rec_mutex_unlock(settings->mutex); /* -- unlock */18591860delete_fluid_list(newlist);18611862if(!str)1863{1864FLUID_LOG(FLUID_ERR, "Out of memory");1865}18661867return (str);1868}18691870/* Structure passed to fluid_settings_foreach_iter recursive function */1871typedef struct1872{1873char path[MAX_SETTINGS_LABEL + 1]; /* Maximum settings label length */1874fluid_list_t *names; /* For fluid_settings_foreach() */1875} fluid_settings_foreach_bag_t;18761877static int1878fluid_settings_foreach_iter(void *key, void *value, void *data)1879{1880fluid_settings_foreach_bag_t *bag = data;1881char *keystr = key;1882fluid_setting_node_t *node = value;1883size_t pathlen;1884char *s;18851886pathlen = FLUID_STRLEN(bag->path);18871888if(pathlen > 0)1889{1890bag->path[pathlen] = '.';1891bag->path[pathlen + 1] = 0;1892}18931894strcat(bag->path, keystr);18951896switch(node->type)1897{1898case FLUID_NUM_TYPE:1899case FLUID_INT_TYPE:1900case FLUID_STR_TYPE:1901s = FLUID_STRDUP(bag->path);19021903if(s)1904{1905bag->names = fluid_list_append(bag->names, s);1906}19071908break;19091910case FLUID_SET_TYPE:1911fluid_hashtable_foreach(node->set.hashtable,1912fluid_settings_foreach_iter, bag);1913break;1914}19151916bag->path[pathlen] = 0;19171918return 0;1919}19201921/**1922* Iterate the existing settings defined in a settings object, calling the1923* provided callback function for each setting.1924*1925* @param settings a settings object1926* @param data any user provided pointer1927* @param func callback function to be called on each iteration1928*1929* @note Starting with FluidSynth 1.1.0 the \p func callback is called for each1930* setting in alphabetical order. Sort order was undefined in previous versions.1931*/1932void1933fluid_settings_foreach(fluid_settings_t *settings, void *data,1934fluid_settings_foreach_t func)1935{1936fluid_settings_foreach_bag_t bag;1937fluid_setting_node_t *node;1938fluid_list_t *p;19391940fluid_return_if_fail(settings != NULL);1941fluid_return_if_fail(func != NULL);19421943bag.path[0] = 0;1944bag.names = NULL;19451946fluid_rec_mutex_lock(settings->mutex);19471948/* Add all node names to the bag.names list */1949fluid_hashtable_foreach(settings, fluid_settings_foreach_iter, &bag);19501951/* Sort names */1952bag.names = fluid_list_sort(bag.names, fluid_list_str_compare_func);19531954/* Loop over names and call the callback */1955for(p = bag.names; p; p = p->next)1956{1957if(fluid_settings_get(settings, (const char *)(p->data), &node) == FLUID_OK1958&& node)1959{1960(*func)(data, (const char *)(p->data), node->type);1961}19621963FLUID_FREE(p->data); /* -- Free name */1964}19651966fluid_rec_mutex_unlock(settings->mutex);19671968delete_fluid_list(bag.names); /* -- Free names list */1969}19701971/**1972* Split a comma-separated list of integers and fill the passed1973* in buffer with the parsed values.1974*1975* @param str the comma-separated string to split1976* @param buf user-supplied buffer to hold the parsed numbers1977* @param buf_len length of user-supplied buffer1978* @return number of parsed values or -1 on failure1979*/1980int fluid_settings_split_csv(const char *str, int *buf, int buf_len)1981{1982char *s;1983char *tok;1984char *tokstr;1985int n = 0;19861987s = tokstr = FLUID_STRDUP(str);19881989if(s == NULL)1990{1991FLUID_LOG(FLUID_ERR, "Out of memory");1992return -1;1993}19941995while((tok = fluid_strtok(&tokstr, ",")) && n < buf_len)1996{1997buf[n++] = atoi(tok);1998}19992000FLUID_FREE(s);20012002return n;2003}200420052006