/* 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 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307, 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" /* 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_hash_delete(void* value, int type); static int fluid_settings_tokenize(char* s, char *buf, char** ptr); typedef struct { char* value; char* def; int hints; fluid_list_t* options; fluid_str_update_t update; void* data; } fluid_str_setting_t; static fluid_str_setting_t* new_fluid_str_setting(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); 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) { 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); } } typedef struct { double value; double def; double min; double max; int hints; fluid_num_update_t update; void* data; } fluid_num_setting_t; 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); 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); } } typedef struct { int value; int def; int min; int max; int hints; fluid_int_update_t update; void* data; } fluid_int_setting_t; 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); 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); } } fluid_settings_t* new_fluid_settings() { fluid_settings_t* settings = new_fluid_hashtable(fluid_settings_hash_delete); if (settings == NULL) { return NULL; } fluid_settings_init(settings); return settings; } void delete_fluid_settings(fluid_settings_t* settings) { delete_fluid_hashtable(settings); } void fluid_settings_hash_delete(void* value, int type) { switch (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_hashtable((fluid_hashtable_t*) value); break; } } void fluid_settings_init(fluid_settings_t* settings) { fluid_synth_settings(settings); fluid_shell_settings(settings); fluid_audio_driver_settings(settings); fluid_midi_driver_settings(settings); } static int fluid_settings_tokenize(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; } ptr[n++] = tok; } return n; } /** returns 1 if the value exists, 0 otherwise */ static int fluid_settings_get(fluid_settings_t* settings, char** name, int len, void** value, int* type) { fluid_hashtable_t* table = settings; int t; void* v; int n; for (n = 0; n < len; n++) { if (table == NULL) { return 0; } if (!fluid_hashtable_lookup(table, name[n], &v, &t)) { return 0; } table = (t == FLUID_SET_TYPE)? (fluid_hashtable_t*) v : NULL; } if (value) { *value = v; } if (type) { *type = t; } return 1; } /** returns 1 if the value has been set, zero otherwise */ static int fluid_settings_set(fluid_settings_t* settings, char** name, int len, void* value, int type) { fluid_hashtable_t* table = settings; int t; void* v; int n, num = len - 1; for (n = 0; n < num; n++) { if (fluid_hashtable_lookup(table, name[n], &v, &t)) { if (t == FLUID_SET_TYPE) { table = (fluid_hashtable_t*) v; } else { /* path ends prematurely */ FLUID_LOG(FLUID_WARN, "'%s' is not a node", name[n]); return 0; } } else { /* create a new node */ fluid_hashtable_t* tmp; tmp = new_fluid_hashtable(fluid_settings_hash_delete); fluid_hashtable_insert(table, name[n], tmp, FLUID_SET_TYPE); table = tmp; } } fluid_hashtable_replace(table, name[num], value, type); 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) { int type; void* value; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; fluid_str_setting_t* setting; ntokens = fluid_settings_tokenize(name, buf, tokens); if (!fluid_settings_get(settings, tokens, ntokens, &value, &type)) { setting = new_fluid_str_setting(def, def, hints, fun, data); return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_STR_TYPE); } else { /* if variable already exists, don't change its value. */ if (type == FLUID_STR_TYPE) { setting = (fluid_str_setting_t*) value; setting->update = fun; setting->data = data; setting->def = def? FLUID_STRDUP(def) : NULL; setting->hints = hints; return 1; } else { FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name); return 1; } } } /** 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) { int type; void* value; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; ntokens = fluid_settings_tokenize(name, buf, tokens); if (!fluid_settings_get(settings, tokens, ntokens, &value, &type)) { /* insert a new setting */ fluid_num_setting_t* setting; setting = new_fluid_num_setting(min, max, def, hints, fun, data); return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_NUM_TYPE); } else { if (type == FLUID_NUM_TYPE) { /* update the existing setting but don't change its value */ fluid_num_setting_t* setting = (fluid_num_setting_t*) value; setting->update = fun; setting->data = data; setting->min = min; setting->max = max; setting->def = def; setting->hints = hints; return 1; } else { /* type mismatch */ FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name); return 0; } } } /** 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) { int type; void* value; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; ntokens = fluid_settings_tokenize(name, buf, tokens); if (!fluid_settings_get(settings, tokens, ntokens, &value, &type)) { /* insert a new setting */ fluid_int_setting_t* setting; setting = new_fluid_int_setting(min, max, def, hints, fun, data); return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_INT_TYPE); } else { if (type == FLUID_INT_TYPE) { /* update the existing setting but don't change its value */ fluid_int_setting_t* setting = (fluid_int_setting_t*) value; setting->update = fun; setting->data = data; setting->min = min; setting->max = max; setting->def = def; setting->hints = hints; return 1; } else { /* type mismatch */ FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name); return 0; } } } int fluid_settings_get_type(fluid_settings_t* settings, char* name) { int type; void* value; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; ntokens = fluid_settings_tokenize(name, buf, tokens); return (fluid_settings_get(settings, tokens, ntokens, &value, &type))? type : FLUID_NO_TYPE; } int fluid_settings_get_hints(fluid_settings_t* settings, char* name) { int type; void* value; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; ntokens = fluid_settings_tokenize(name, buf, tokens); if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) { if (type == FLUID_NUM_TYPE) { fluid_num_setting_t* setting = (fluid_num_setting_t*) value; return setting->hints; } else if (type == FLUID_STR_TYPE) { fluid_str_setting_t* setting = (fluid_str_setting_t*) value; return setting->hints; } else { return 0; } } else { return 0; } } int fluid_settings_is_realtime(fluid_settings_t* settings, char* name) { int type; void* value; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; ntokens = fluid_settings_tokenize(name, buf, tokens); if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) { if (type == FLUID_NUM_TYPE) { fluid_num_setting_t* setting = (fluid_num_setting_t*) value; return setting->update != NULL; } else if (type == FLUID_STR_TYPE) { fluid_str_setting_t* setting = (fluid_str_setting_t*) value; return setting->update != NULL; } else { return 0; } } else { return 0; } } int fluid_settings_setstr(fluid_settings_t* settings, char* name, char* str) { char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; int type; void* value; fluid_str_setting_t* setting; ntokens = fluid_settings_tokenize(name, buf, tokens); if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) { if (type != FLUID_STR_TYPE) { return 0; } setting = (fluid_str_setting_t*) value; if (setting->value) { FLUID_FREE(setting->value); } setting->value = str? FLUID_STRDUP(str) : NULL; if (setting->update) { (*setting->update)(setting->data, name, setting->value); } return 1; } else { /* insert a new setting */ fluid_str_setting_t* setting; setting = new_fluid_str_setting(str, NULL, 0, NULL, NULL); return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_STR_TYPE); } } int fluid_settings_getstr(fluid_settings_t* settings, char* name, char** str) { int type; void* value; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; ntokens = fluid_settings_tokenize(name, buf, tokens); if (fluid_settings_get(settings, tokens, ntokens, &value, &type) && (type == FLUID_STR_TYPE)) { fluid_str_setting_t* setting = (fluid_str_setting_t*) value; *str = setting->value; return 1; } *str = NULL; return 0; } int fluid_settings_str_equal(fluid_settings_t* settings, char* name, char* s) { int type; void* value; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; ntokens = fluid_settings_tokenize(name, buf, tokens); if (fluid_settings_get(settings, tokens, ntokens, &value, &type) && (type == FLUID_STR_TYPE)) { fluid_str_setting_t* setting = (fluid_str_setting_t*) value; return FLUID_STRCMP(setting->value, s) == 0; } return 0; } char* fluid_settings_getstr_default(fluid_settings_t* settings, char* name) { int type; void* value; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; ntokens = fluid_settings_tokenize(name, buf, tokens); if (fluid_settings_get(settings, tokens, ntokens, &value, &type) && (type == FLUID_STR_TYPE)) { fluid_str_setting_t* setting = (fluid_str_setting_t*) value; return setting->def; } else { return NULL; } } int fluid_settings_add_option(fluid_settings_t* settings, char* name, char* s) { int type; void* value; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; ntokens = fluid_settings_tokenize(name, buf, tokens); if (fluid_settings_get(settings, tokens, ntokens, &value, &type) && (type == FLUID_STR_TYPE)) { fluid_str_setting_t* setting = (fluid_str_setting_t*) value; char* copy = FLUID_STRDUP(s); setting->options = fluid_list_append(setting->options, copy); return 1; } else { return 0; } } int fluid_settings_remove_option(fluid_settings_t* settings, char* name, char* s) { int type; void* value; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; ntokens = fluid_settings_tokenize(name, buf, tokens); if (fluid_settings_get(settings, tokens, ntokens, &value, &type) && (type == FLUID_STR_TYPE)) { fluid_str_setting_t* setting = (fluid_str_setting_t*) value; 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); return 1; } list = fluid_list_next(list); } return 0; } else { return 0; } } int fluid_settings_setnum(fluid_settings_t* settings, char* name, double val) { int type; void* value; fluid_num_setting_t* setting; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; ntokens = fluid_settings_tokenize(name, buf, tokens); if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) { if (type != FLUID_NUM_TYPE) { return 0; } setting = (fluid_num_setting_t*) value; if (val < setting->min) { val = setting->min; } else if (val > setting->max) { val = setting->max; } setting->value = val; if (setting->update) { (*setting->update)(setting->data, name, val); } return 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; return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_NUM_TYPE); } } int fluid_settings_getnum(fluid_settings_t* settings, char* name, double* val) { int type; void* value; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; ntokens = fluid_settings_tokenize(name, buf, tokens); if (fluid_settings_get(settings, tokens, ntokens, &value, &type) && (type == FLUID_NUM_TYPE)) { fluid_num_setting_t* setting = (fluid_num_setting_t*) value; *val = setting->value; return 1; } return 0; } void fluid_settings_getnum_range(fluid_settings_t* settings, char* name, double* min, double* max) { int type; void* value; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; ntokens = fluid_settings_tokenize(name, buf, tokens); if (fluid_settings_get(settings, tokens, ntokens, &value, &type) && (type == FLUID_NUM_TYPE)) { fluid_num_setting_t* setting = (fluid_num_setting_t*) value; *min = setting->min; *max = setting->max; } } double fluid_settings_getnum_default(fluid_settings_t* settings, char* name) { int type; void* value; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; ntokens = fluid_settings_tokenize(name, buf, tokens); if (fluid_settings_get(settings, tokens, ntokens, &value, &type) && (type == FLUID_NUM_TYPE)) { fluid_num_setting_t* setting = (fluid_num_setting_t*) value; return setting->def; } else { return 0.0f; } } int fluid_settings_setint(fluid_settings_t* settings, char* name, int val) { int type; void* value; fluid_int_setting_t* setting; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; ntokens = fluid_settings_tokenize(name, buf, tokens); if (fluid_settings_get(settings, tokens, ntokens, &value, &type)) { if (type != FLUID_INT_TYPE) { return 0; } setting = (fluid_int_setting_t*) value; if (val < setting->min) { val = setting->min; } else if (val > setting->max) { val = setting->max; } setting->value = val; if (setting->update) { (*setting->update)(setting->data, name, val); } return 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; return fluid_settings_set(settings, tokens, ntokens, setting, FLUID_INT_TYPE); } } int fluid_settings_getint(fluid_settings_t* settings, char* name, int* val) { int type; void* value; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; ntokens = fluid_settings_tokenize(name, buf, tokens); if (fluid_settings_get(settings, tokens, ntokens, &value, &type) && (type == FLUID_INT_TYPE)) { fluid_int_setting_t* setting = (fluid_int_setting_t*) value; *val = setting->value; return 1; } return 0; } void fluid_settings_getint_range(fluid_settings_t* settings, char* name, int* min, int* max) { int type; void* value; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; ntokens = fluid_settings_tokenize(name, buf, tokens); if (fluid_settings_get(settings, tokens, ntokens, &value, &type) && (type == FLUID_INT_TYPE)) { fluid_int_setting_t* setting = (fluid_int_setting_t*) value; *min = setting->min; *max = setting->max; } } int fluid_settings_getint_default(fluid_settings_t* settings, char* name) { int type; void* value; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; ntokens = fluid_settings_tokenize(name, buf, tokens); if (fluid_settings_get(settings, tokens, ntokens, &value, &type) && (type == FLUID_INT_TYPE)) { fluid_int_setting_t* setting = (fluid_int_setting_t*) value; return setting->def; } else { return 0.0f; } } void fluid_settings_foreach_option(fluid_settings_t* settings, char* name, void* data, fluid_settings_foreach_option_t func) { int type; void* value; char* tokens[MAX_SETTINGS_TOKENS]; char buf[MAX_SETTINGS_LABEL+1]; int ntokens; if (!func) { return; } ntokens = fluid_settings_tokenize(name, buf, tokens); if (fluid_settings_get(settings, tokens, ntokens, &value, &type) && (type == FLUID_STR_TYPE)) { fluid_str_setting_t* setting = (fluid_str_setting_t*) value; fluid_list_t* list = setting->options; while (list) { char* option = (char*) fluid_list_get(list); (*func)(data, name, option); list = fluid_list_next(list); } } } static fluid_settings_foreach_t fluid_settings_foreach_func; static void* fluid_settings_foreach_data; int fluid_settings_foreach_iter(char* key, void* value, int type, void* data) { char path[1024]; if (data == 0) { snprintf(path, 1024, "%s", key); } else { snprintf(path, 1024, "%s.%s", (char*) data, key); } path[1023] = 0; switch (type) { case FLUID_NUM_TYPE: case FLUID_INT_TYPE: case FLUID_STR_TYPE: (*fluid_settings_foreach_func)(fluid_settings_foreach_data, path, type); break; case FLUID_SET_TYPE: fluid_hashtable_foreach((fluid_hashtable_t*) value, fluid_settings_foreach_iter, &path[0]); break; } return 0; } void fluid_settings_foreach(fluid_settings_t* settings, void* data, fluid_settings_foreach_t func) { fluid_settings_foreach_func = func; fluid_settings_foreach_data = data; fluid_hashtable_foreach(settings, fluid_settings_foreach_iter, 0); }