Update Fluidsynth to 2.0.1
[ardour.git] / libs / fluidsynth / src / fluid_settings.c
1 /* FluidSynth - A Software Synthesizer
2  *
3  * Copyright (C) 2003  Peter Hanappe and others.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public License
7  * as published by the Free Software Foundation; either version 2.1 of
8  * the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free
17  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18  * 02110-1301, USA
19  */
20
21 #include "fluid_sys.h"
22 #include "fluid_hash.h"
23 #include "fluid_synth.h"
24 #include "fluid_settings.h"
25 #include "fluid_midi.h"
26
27 /* maximum allowed components of a settings variable (separated by '.') */
28 #define MAX_SETTINGS_TOKENS 8   /* currently only a max of 3 are used */
29 #define MAX_SETTINGS_LABEL 256  /* max length of a settings variable label */
30
31 static void fluid_settings_init(fluid_settings_t *settings);
32 static void fluid_settings_key_destroy_func(void *value);
33 static void fluid_settings_value_destroy_func(void *value);
34 static int fluid_settings_tokenize(const char *s, char *buf, char **ptr);
35
36 /* Common structure to all settings nodes */
37 typedef struct
38 {
39     char *value;
40     char *def;
41     int hints;
42     fluid_list_t *options;
43     fluid_str_update_t update;
44     void *data;
45 } fluid_str_setting_t;
46
47 typedef struct
48 {
49     double value;
50     double def;
51     double min;
52     double max;
53     int hints;
54     fluid_num_update_t update;
55     void *data;
56 } fluid_num_setting_t;
57
58 typedef struct
59 {
60     int value;
61     int def;
62     int min;
63     int max;
64     int hints;
65     fluid_int_update_t update;
66     void *data;
67 } fluid_int_setting_t;
68
69 typedef struct
70 {
71     fluid_hashtable_t *hashtable;
72 } fluid_set_setting_t;
73
74 typedef struct
75 {
76     int type;             /**< fluid_types_enum */
77
78     union
79     {
80         fluid_str_setting_t str;
81         fluid_num_setting_t num;
82         fluid_int_setting_t i;
83         fluid_set_setting_t set;
84     };
85 } fluid_setting_node_t;
86
87 static fluid_setting_node_t *
88 new_fluid_str_setting(const char *value, const char *def, int hints)
89 {
90     fluid_setting_node_t *node;
91     fluid_str_setting_t *str;
92
93     node = FLUID_NEW(fluid_setting_node_t);
94
95     if(!node)
96     {
97         FLUID_LOG(FLUID_ERR, "Out of memory");
98         return NULL;
99     }
100
101     node->type = FLUID_STR_TYPE;
102
103     str = &node->str;
104     str->value = value ? FLUID_STRDUP(value) : NULL;
105     str->def = def ? FLUID_STRDUP(def) : NULL;
106     str->hints = hints;
107     str->options = NULL;
108     str->update = NULL;
109     str->data = NULL;
110     return node;
111 }
112
113 static void
114 delete_fluid_str_setting(fluid_setting_node_t *node)
115 {
116     fluid_return_if_fail(node != NULL);
117
118     FLUID_ASSERT(node->type == FLUID_STR_TYPE);
119
120     FLUID_FREE(node->str.value);
121     FLUID_FREE(node->str.def);
122
123     if(node->str.options)
124     {
125         fluid_list_t *list = node->str.options;
126
127         while(list)
128         {
129             FLUID_FREE(list->data);
130             list = fluid_list_next(list);
131         }
132
133         delete_fluid_list(node->str.options);
134     }
135
136     FLUID_FREE(node);
137 }
138
139
140 static fluid_setting_node_t *
141 new_fluid_num_setting(double min, double max, double def, int hints)
142 {
143     fluid_setting_node_t *node;
144     fluid_num_setting_t *num;
145
146     node = FLUID_NEW(fluid_setting_node_t);
147
148     if(!node)
149     {
150         FLUID_LOG(FLUID_ERR, "Out of memory");
151         return NULL;
152     }
153
154     node->type = FLUID_NUM_TYPE;
155
156     num = &node->num;
157     num->value = def;
158     num->def = def;
159     num->min = min;
160     num->max = max;
161     num->hints = hints;
162     num->update = NULL;
163     num->data = NULL;
164
165     return node;
166 }
167
168 static void
169 delete_fluid_num_setting(fluid_setting_node_t *node)
170 {
171     fluid_return_if_fail(node != NULL);
172
173     FLUID_ASSERT(node->type == FLUID_NUM_TYPE);
174     FLUID_FREE(node);
175 }
176
177 static fluid_setting_node_t *
178 new_fluid_int_setting(int min, int max, int def, int hints)
179 {
180     fluid_setting_node_t *node;
181     fluid_int_setting_t *i;
182
183     node = FLUID_NEW(fluid_setting_node_t);
184
185     if(!node)
186     {
187         FLUID_LOG(FLUID_ERR, "Out of memory");
188         return NULL;
189     }
190
191     node->type = FLUID_INT_TYPE;
192
193     i = &node->i;
194     i->value = def;
195     i->def = def;
196     i->min = min;
197     i->max = max;
198     i->hints = hints;
199     i->update = NULL;
200     i->data = NULL;
201     return node;
202 }
203
204 static void
205 delete_fluid_int_setting(fluid_setting_node_t *node)
206 {
207     fluid_return_if_fail(node != NULL);
208
209     FLUID_ASSERT(node->type == FLUID_INT_TYPE);
210     FLUID_FREE(node);
211 }
212
213 static fluid_setting_node_t *
214 new_fluid_set_setting(void)
215 {
216     fluid_setting_node_t *node;
217     fluid_set_setting_t *set;
218
219     node = FLUID_NEW(fluid_setting_node_t);
220
221     if(!node)
222     {
223         FLUID_LOG(FLUID_ERR, "Out of memory");
224         return NULL;
225     }
226
227     node->type = FLUID_SET_TYPE;
228     set = &node->set;
229
230     set->hashtable = new_fluid_hashtable_full(fluid_str_hash, fluid_str_equal,
231                      fluid_settings_key_destroy_func,
232                      fluid_settings_value_destroy_func);
233
234     if(!set->hashtable)
235     {
236         FLUID_FREE(node);
237         return NULL;
238     }
239
240     return node;
241 }
242
243 static void
244 delete_fluid_set_setting(fluid_setting_node_t *node)
245 {
246     fluid_return_if_fail(node != NULL);
247
248     FLUID_ASSERT(node->type == FLUID_SET_TYPE);
249     delete_fluid_hashtable(node->set.hashtable);
250     FLUID_FREE(node);
251 }
252
253 /**
254  * Create a new settings object
255  * @return the pointer to the settings object
256  */
257 fluid_settings_t *
258 new_fluid_settings(void)
259 {
260     fluid_settings_t *settings;
261
262     settings = new_fluid_hashtable_full(fluid_str_hash, fluid_str_equal,
263                                         fluid_settings_key_destroy_func,
264                                         fluid_settings_value_destroy_func);
265
266     if(settings == NULL)
267     {
268         return NULL;
269     }
270
271     fluid_rec_mutex_init(settings->mutex);
272     fluid_settings_init(settings);
273     return settings;
274 }
275
276 /**
277  * Delete the provided settings object
278  * @param settings a settings object
279  */
280 void
281 delete_fluid_settings(fluid_settings_t *settings)
282 {
283     fluid_return_if_fail(settings != NULL);
284
285     fluid_rec_mutex_destroy(settings->mutex);
286     delete_fluid_hashtable(settings);
287 }
288
289 /* Settings hash key destroy function */
290 static void
291 fluid_settings_key_destroy_func(void *value)
292 {
293     FLUID_FREE(value);    /* Free the string key value */
294 }
295
296 /* Settings hash value destroy function */
297 static void
298 fluid_settings_value_destroy_func(void *value)
299 {
300     fluid_setting_node_t *node = value;
301
302     switch(node->type)
303     {
304     case FLUID_NUM_TYPE:
305         delete_fluid_num_setting(node);
306         break;
307
308     case FLUID_INT_TYPE:
309         delete_fluid_int_setting(node);
310         break;
311
312     case FLUID_STR_TYPE:
313         delete_fluid_str_setting(node);
314         break;
315
316     case FLUID_SET_TYPE:
317         delete_fluid_set_setting(node);
318         break;
319     }
320 }
321
322 void
323 fluid_settings_init(fluid_settings_t *settings)
324 {
325     fluid_return_if_fail(settings != NULL);
326
327     fluid_synth_settings(settings);
328 #if 0
329     fluid_shell_settings(settings);
330     fluid_player_settings(settings);
331     fluid_file_renderer_settings(settings);
332     fluid_audio_driver_settings(settings);
333     fluid_midi_driver_settings(settings);
334 #endif
335 }
336
337 static int
338 fluid_settings_tokenize(const char *s, char *buf, char **ptr)
339 {
340     char *tokstr, *tok;
341     int n = 0;
342
343     if(FLUID_STRLEN(s) > MAX_SETTINGS_LABEL)
344     {
345         FLUID_LOG(FLUID_ERR, "Setting variable name exceeded max length of %d chars",
346                   MAX_SETTINGS_LABEL);
347         return 0;
348     }
349
350     FLUID_STRCPY(buf, s);       /* copy string to buffer, since it gets modified */
351     tokstr = buf;
352
353     while((tok = fluid_strtok(&tokstr, ".")))
354     {
355         if(n >= MAX_SETTINGS_TOKENS)
356         {
357             FLUID_LOG(FLUID_ERR, "Setting variable name exceeded max token count of %d",
358                       MAX_SETTINGS_TOKENS);
359             return 0;
360         }
361         else
362         {
363             ptr[n++] = tok;
364         }
365     }
366
367     return n;
368 }
369
370 /**
371  * Get a setting name, value and type
372  *
373  * @param settings a settings object
374  * @param name Settings name
375  * @param value Location to store setting node if found
376  * @return #FLUID_OK if the node exists, #FLUID_FAILED otherwise
377  */
378 static int
379 fluid_settings_get(fluid_settings_t *settings, const char *name,
380                    fluid_setting_node_t **value)
381 {
382     fluid_hashtable_t *table = settings;
383     fluid_setting_node_t *node = NULL;
384     char *tokens[MAX_SETTINGS_TOKENS];
385     char buf[MAX_SETTINGS_LABEL + 1];
386     int ntokens;
387     int n;
388
389     ntokens = fluid_settings_tokenize(name, buf, tokens);
390
391     if(table == NULL || ntokens <= 0)
392     {
393         return FLUID_FAILED;
394     }
395
396     for(n = 0; n < ntokens; n++)
397     {
398
399         node = fluid_hashtable_lookup(table, tokens[n]);
400
401         if(!node)
402         {
403             return FLUID_FAILED;
404         }
405
406         table = (node->type == FLUID_SET_TYPE) ? node->set.hashtable : NULL;
407     }
408
409     if(value)
410     {
411         *value = node;
412     }
413
414     return FLUID_OK;
415 }
416
417 /**
418  * Set a setting name, value and type, replacing it if already exists
419  *
420  * @param settings a settings object
421  * @param name Settings name
422  * @param value Node instance to assign (used directly)
423  * @return #FLUID_OK if the value has been set, #FLUID_FAILED otherwise
424  */
425 static int
426 fluid_settings_set(fluid_settings_t *settings, const char *name, fluid_setting_node_t *value)
427 {
428     fluid_hashtable_t *table = settings;
429     fluid_setting_node_t *node;
430     char *tokens[MAX_SETTINGS_TOKENS];
431     char buf[MAX_SETTINGS_LABEL + 1];
432     int n, num;
433     char *dupname;
434
435     num = fluid_settings_tokenize(name, buf, tokens);
436
437     if(num == 0)
438     {
439         return FLUID_FAILED;
440     }
441
442     num--;
443
444     for(n = 0; n < num; n++)
445     {
446
447         node = fluid_hashtable_lookup(table, tokens[n]);
448
449         if(node)
450         {
451
452             if(node->type == FLUID_SET_TYPE)
453             {
454                 table = node->set.hashtable;
455             }
456             else
457             {
458                 /* path ends prematurely */
459                 FLUID_LOG(FLUID_WARN, "'%s' is not a node. Name of the setting was '%s'", tokens[n], name);
460                 return FLUID_FAILED;
461             }
462
463         }
464         else
465         {
466             /* create a new node */
467             fluid_setting_node_t *setnode;
468
469             dupname = FLUID_STRDUP(tokens[n]);
470             setnode = new_fluid_set_setting();
471
472             if(!dupname || !setnode)
473             {
474                 if(dupname)
475                 {
476                     FLUID_FREE(dupname);
477                 }
478                 else
479                 {
480                     FLUID_LOG(FLUID_ERR, "Out of memory");
481                 }
482
483                 if(setnode)
484                 {
485                     delete_fluid_set_setting(setnode);
486                 }
487
488                 return FLUID_FAILED;
489             }
490
491             fluid_hashtable_insert(table, dupname, setnode);
492             table = setnode->set.hashtable;
493         }
494     }
495
496     dupname = FLUID_STRDUP(tokens[num]);
497
498     if(!dupname)
499     {
500         FLUID_LOG(FLUID_ERR, "Out of memory");
501         return FLUID_FAILED;
502     }
503
504     fluid_hashtable_insert(table, dupname, value);
505
506     return FLUID_OK;
507 }
508
509 /**
510  * Registers a new string value for the specified setting.
511  *
512  * @param settings a settings object
513  * @param name the setting's name
514  * @param def the default value for the setting
515  * @param hints the hints for the setting
516  * @return #FLUID_OK if the value has been register correctly, #FLUID_FAILED otherwise
517  */
518 int
519 fluid_settings_register_str(fluid_settings_t *settings, const char *name, const char *def, int hints)
520 {
521     fluid_setting_node_t *node;
522     int retval = FLUID_FAILED;
523
524     fluid_return_val_if_fail(settings != NULL, retval);
525     fluid_return_val_if_fail(name != NULL, retval);
526     fluid_return_val_if_fail(name[0] != '\0', retval);
527
528     fluid_rec_mutex_lock(settings->mutex);
529
530     if(fluid_settings_get(settings, name, &node) != FLUID_OK)
531     {
532         node = new_fluid_str_setting(def, def, hints);
533         retval = fluid_settings_set(settings, name, node);
534
535         if(retval != FLUID_OK)
536         {
537             delete_fluid_str_setting(node);
538         }
539     }
540     else
541     {
542         /* if variable already exists, don't change its value. */
543         if(node->type == FLUID_STR_TYPE)
544         {
545             fluid_str_setting_t *setting = &node->str;
546             setting->def = def ? FLUID_STRDUP(def) : NULL;
547             setting->hints = hints;
548             retval = FLUID_OK;
549         }
550         else
551         {
552             FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name);
553         }
554     }
555
556     fluid_rec_mutex_unlock(settings->mutex);
557
558     return retval;
559 }
560
561 /**
562  * Registers a new float value for the specified setting.
563  *
564  * @param settings a settings object
565  * @param name the setting's name
566  * @param def the default value for the setting
567  * @param min the smallest allowed value for the setting
568  * @param max the largest allowed value for the setting
569  * @param hints the hints for the setting
570  * @return #FLUID_OK if the value has been register correctly, #FLUID_FAILED otherwise
571  */
572 int
573 fluid_settings_register_num(fluid_settings_t *settings, const char *name, double def,
574                             double min, double max, int hints)
575 {
576     fluid_setting_node_t *node;
577     int retval = FLUID_FAILED;
578
579     fluid_return_val_if_fail(settings != NULL, retval);
580     fluid_return_val_if_fail(name != NULL, retval);
581     fluid_return_val_if_fail(name[0] != '\0', retval);
582
583     /* For now, all floating point settings are bounded below and above */
584     hints |= FLUID_HINT_BOUNDED_BELOW | FLUID_HINT_BOUNDED_ABOVE;
585
586     fluid_rec_mutex_lock(settings->mutex);
587
588     if(fluid_settings_get(settings, name, &node) != FLUID_OK)
589     {
590         /* insert a new setting */
591         node = new_fluid_num_setting(min, max, def, hints);
592         retval = fluid_settings_set(settings, name, node);
593
594         if(retval != FLUID_OK)
595         {
596             delete_fluid_num_setting(node);
597         }
598     }
599     else
600     {
601         if(node->type == FLUID_NUM_TYPE)
602         {
603             /* update the existing setting but don't change its value */
604             fluid_num_setting_t *setting = &node->num;
605             setting->min = min;
606             setting->max = max;
607             setting->def = def;
608             setting->hints = hints;
609             retval = FLUID_OK;
610         }
611         else
612         {
613             /* type mismatch */
614             FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name);
615         }
616     }
617
618     fluid_rec_mutex_unlock(settings->mutex);
619
620     return retval;
621 }
622
623 /**
624  * Registers a new integer value for the specified setting.
625  *
626  * @param settings a settings object
627  * @param name the setting's name
628  * @param def the default value for the setting
629  * @param min the smallest allowed value for the setting
630  * @param max the largest allowed value for the setting
631  * @param hints the hints for the setting
632  * @return #FLUID_OK if the value has been register correctly, #FLUID_FAILED otherwise
633  */
634 int
635 fluid_settings_register_int(fluid_settings_t *settings, const char *name, int def,
636                             int min, int max, int hints)
637 {
638     fluid_setting_node_t *node;
639     int retval = FLUID_FAILED;
640
641     fluid_return_val_if_fail(settings != NULL, retval);
642     fluid_return_val_if_fail(name != NULL, retval);
643     fluid_return_val_if_fail(name[0] != '\0', retval);
644
645     /* For now, all integer settings are bounded below and above */
646     hints |= FLUID_HINT_BOUNDED_BELOW | FLUID_HINT_BOUNDED_ABOVE;
647
648     fluid_rec_mutex_lock(settings->mutex);
649
650     if(fluid_settings_get(settings, name, &node) != FLUID_OK)
651     {
652         /* insert a new setting */
653         node = new_fluid_int_setting(min, max, def, hints);
654         retval = fluid_settings_set(settings, name, node);
655
656         if(retval != FLUID_OK)
657         {
658             delete_fluid_int_setting(node);
659         }
660     }
661     else
662     {
663         if(node->type == FLUID_INT_TYPE)
664         {
665             /* update the existing setting but don't change its value */
666             fluid_int_setting_t *setting = &node->i;
667             setting->min = min;
668             setting->max = max;
669             setting->def = def;
670             setting->hints = hints;
671             retval = FLUID_OK;
672         }
673         else
674         {
675             /* type mismatch */
676             FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name);
677         }
678     }
679
680     fluid_rec_mutex_unlock(settings->mutex);
681
682     return retval;
683 }
684
685 /**
686  * Registers a callback for the specified string setting.
687  *
688  * @param settings a settings object
689  * @param name the setting's name
690  * @param callback an update function for the setting
691  * @param data user supplied data passed to the update function
692  * @return #FLUID_OK if the callback has been set, #FLUID_FAILED otherwise
693  */
694 int fluid_settings_callback_str(fluid_settings_t *settings, const char *name,
695                                 fluid_str_update_t callback, void *data)
696 {
697     fluid_setting_node_t *node;
698     fluid_str_setting_t *setting;
699
700     fluid_return_val_if_fail(settings != NULL, FLUID_FAILED);
701     fluid_return_val_if_fail(name != NULL, FLUID_FAILED);
702     fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED);
703
704     fluid_rec_mutex_lock(settings->mutex);
705
706     if((fluid_settings_get(settings, name, &node) != FLUID_OK)
707             || node->type != FLUID_STR_TYPE)
708     {
709         fluid_rec_mutex_unlock(settings->mutex);
710         return FLUID_FAILED;
711     }
712
713     setting = &node->str;
714     setting->update = callback;
715     setting->data = data;
716
717     fluid_rec_mutex_unlock(settings->mutex);
718     return FLUID_OK;
719 }
720
721 /**
722  * Registers a callback for the specified numeric setting.
723  *
724  * @param settings a settings object
725  * @param name the setting's name
726  * @param callback an update function for the setting
727  * @param data user supplied data passed to the update function
728  * @return #FLUID_OK if the callback has been set, #FLUID_FAILED otherwise
729  */
730 int fluid_settings_callback_num(fluid_settings_t *settings, const char *name,
731                                 fluid_num_update_t callback, void *data)
732 {
733     fluid_setting_node_t *node;
734     fluid_num_setting_t *setting;
735
736     fluid_return_val_if_fail(settings != NULL, FLUID_FAILED);
737     fluid_return_val_if_fail(name != NULL, FLUID_FAILED);
738     fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED);
739
740     fluid_rec_mutex_lock(settings->mutex);
741
742     if((fluid_settings_get(settings, name, &node) != FLUID_OK)
743             || node->type != FLUID_NUM_TYPE)
744     {
745         fluid_rec_mutex_unlock(settings->mutex);
746         return FLUID_FAILED;
747     }
748
749     setting = &node->num;
750     setting->update = callback;
751     setting->data = data;
752
753     fluid_rec_mutex_unlock(settings->mutex);
754     return FLUID_OK;
755 }
756
757 /**
758  * Registers a callback for the specified int setting.
759  *
760  * @param settings a settings object
761  * @param name the setting's name
762  * @param callback an update function for the setting
763  * @param data user supplied data passed to the update function
764  * @return #FLUID_OK if the callback has been set, #FLUID_FAILED otherwise
765  */
766 int fluid_settings_callback_int(fluid_settings_t *settings, const char *name,
767                                 fluid_int_update_t callback, void *data)
768 {
769     fluid_setting_node_t *node;
770     fluid_int_setting_t *setting;
771
772     fluid_return_val_if_fail(settings != NULL, FLUID_FAILED);
773     fluid_return_val_if_fail(name != NULL, FLUID_FAILED);
774     fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED);
775
776     fluid_rec_mutex_lock(settings->mutex);
777
778     if((fluid_settings_get(settings, name, &node) != FLUID_OK)
779             || node->type != FLUID_INT_TYPE)
780     {
781         fluid_rec_mutex_unlock(settings->mutex);
782         return FLUID_FAILED;
783     }
784
785     setting = &node->i;
786     setting->update = callback;
787     setting->data = data;
788
789     fluid_rec_mutex_unlock(settings->mutex);
790     return FLUID_OK;
791 }
792
793 /**
794  * Get the type of the setting with the given name
795  *
796  * @param settings a settings object
797  * @param name a setting's name
798  * @return the type for the named setting (see #fluid_types_enum), or #FLUID_NO_TYPE when it does not exist
799  */
800 int
801 fluid_settings_get_type(fluid_settings_t *settings, const char *name)
802 {
803     fluid_setting_node_t *node;
804     int type = FLUID_NO_TYPE;
805
806     fluid_return_val_if_fail(settings != NULL, type);
807     fluid_return_val_if_fail(name != NULL, type);
808     fluid_return_val_if_fail(name[0] != '\0', type);
809
810     fluid_rec_mutex_lock(settings->mutex);
811
812     if(fluid_settings_get(settings, name, &node) == FLUID_OK)
813     {
814         type = node->type;
815     }
816
817     fluid_rec_mutex_unlock(settings->mutex);
818
819     return type;
820 }
821
822 /**
823  * Get the hints for the named setting as an integer bitmap
824  *
825  * @param settings a settings object
826  * @param name a setting's name
827  * @param hints set to the hints associated to the setting if it exists
828  * @return #FLUID_OK if hints associated to the named setting exist, #FLUID_FAILED otherwise
829  */
830 int
831 fluid_settings_get_hints(fluid_settings_t *settings, const char *name, int *hints)
832 {
833     fluid_setting_node_t *node;
834     int retval = FLUID_FAILED;
835
836     fluid_return_val_if_fail(settings != NULL, retval);
837     fluid_return_val_if_fail(name != NULL, retval);
838     fluid_return_val_if_fail(name[0] != '\0', retval);
839
840     fluid_rec_mutex_lock(settings->mutex);
841
842     if(fluid_settings_get(settings, name, &node) == FLUID_OK)
843     {
844         if(node->type == FLUID_NUM_TYPE)
845         {
846             fluid_num_setting_t *setting = &node->num;
847             *hints = setting->hints;
848             retval = FLUID_OK;
849         }
850         else if(node->type == FLUID_STR_TYPE)
851         {
852             fluid_str_setting_t *setting = &node->str;
853             *hints = setting->hints;
854             retval = FLUID_OK;
855         }
856         else if(node->type == FLUID_INT_TYPE)
857         {
858             fluid_int_setting_t *setting = &node->i;
859             *hints = setting->hints;
860             retval = FLUID_OK;
861         }
862     }
863
864     fluid_rec_mutex_unlock(settings->mutex);
865
866     return retval;
867 }
868
869 /**
870  * Ask whether the setting is changeable in real-time.
871  *
872  * @param settings a settings object
873  * @param name a setting's name
874  * @return TRUE if the setting is changeable in real-time, FALSE otherwise
875  */
876 int
877 fluid_settings_is_realtime(fluid_settings_t *settings, const char *name)
878 {
879     fluid_setting_node_t *node;
880     int isrealtime = FALSE;
881
882     fluid_return_val_if_fail(settings != NULL, 0);
883     fluid_return_val_if_fail(name != NULL, 0);
884     fluid_return_val_if_fail(name[0] != '\0', 0);
885
886     fluid_rec_mutex_lock(settings->mutex);
887
888     if(fluid_settings_get(settings, name, &node) == FLUID_OK)
889     {
890         if(node->type == FLUID_NUM_TYPE)
891         {
892             fluid_num_setting_t *setting = &node->num;
893             isrealtime = setting->update != NULL;
894         }
895         else if(node->type == FLUID_STR_TYPE)
896         {
897             fluid_str_setting_t *setting = &node->str;
898             isrealtime = setting->update != NULL;
899         }
900         else if(node->type == FLUID_INT_TYPE)
901         {
902             fluid_int_setting_t *setting = &node->i;
903             isrealtime = setting->update != NULL;
904         }
905     }
906
907     fluid_rec_mutex_unlock(settings->mutex);
908
909     return isrealtime;
910 }
911
912 /**
913  * Set a string value for a named setting
914  *
915  * @param settings a settings object
916  * @param name a setting's name
917  * @param str new string value
918  * @return #FLUID_OK if the value has been set, #FLUID_FAILED otherwise
919  */
920 int
921 fluid_settings_setstr(fluid_settings_t *settings, const char *name, const char *str)
922 {
923     fluid_setting_node_t *node;
924     fluid_str_setting_t *setting;
925     char *new_value = NULL;
926     fluid_str_update_t callback = NULL;
927     void *data = NULL;
928
929     fluid_return_val_if_fail(settings != NULL, FLUID_FAILED);
930     fluid_return_val_if_fail(name != NULL, FLUID_FAILED);
931     fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED);
932
933     fluid_rec_mutex_lock(settings->mutex);
934
935     if((fluid_settings_get(settings, name, &node) != FLUID_OK)
936             || (node->type != FLUID_STR_TYPE))
937     {
938         goto error_recovery;
939     }
940
941     setting = &node->str;
942
943     if(setting->value)
944     {
945         FLUID_FREE(setting->value);
946     }
947
948     if(str)
949     {
950         new_value = FLUID_STRDUP(str);
951
952         if(new_value == NULL)
953         {
954             FLUID_LOG(FLUID_ERR, "Out of memory");
955             goto error_recovery;
956         }
957     }
958
959     setting->value = new_value;
960
961     callback = setting->update;
962     data = setting->data;
963
964     /* Release the mutex before calling the update callback, to avoid
965      * possible deadlocks with FluidSynths API lock */
966     fluid_rec_mutex_unlock(settings->mutex);
967
968     if(callback)
969     {
970         (*callback)(data, name, new_value);
971     }
972
973     return FLUID_OK;
974
975 error_recovery:
976     fluid_rec_mutex_unlock(settings->mutex);
977     return FLUID_FAILED;
978 }
979
980 /**
981  * Copy the value of a string setting into the provided buffer (thread safe)
982  * @param settings a settings object
983  * @param name a setting's name
984  * @param str Caller supplied buffer to copy string value to
985  * @param len Size of 'str' buffer (no more than len bytes will be written, which
986  *   will always include a zero terminator)
987  * @return #FLUID_OK if the value exists, #FLUID_FAILED otherwise
988  * @since 1.1.0
989  *
990  * @note A size of 256 should be more than sufficient for the string buffer.
991  */
992 int
993 fluid_settings_copystr(fluid_settings_t *settings, const char *name,
994                        char *str, int len)
995 {
996     fluid_setting_node_t *node;
997     int retval = FLUID_FAILED;
998
999     fluid_return_val_if_fail(settings != NULL, retval);
1000     fluid_return_val_if_fail(name != NULL, retval);
1001     fluid_return_val_if_fail(name[0] != '\0', retval);
1002     fluid_return_val_if_fail(str != NULL, retval);
1003     fluid_return_val_if_fail(len > 0, retval);
1004
1005     str[0] = 0;
1006
1007     fluid_rec_mutex_lock(settings->mutex);
1008
1009     if(fluid_settings_get(settings, name, &node) == FLUID_OK)
1010     {
1011         if(node->type == FLUID_STR_TYPE)
1012         {
1013             fluid_str_setting_t *setting = &node->str;
1014
1015             if(setting->value)
1016             {
1017                 FLUID_STRNCPY(str, setting->value, len);
1018             }
1019
1020             retval = FLUID_OK;
1021         }
1022         else if(node->type == FLUID_INT_TYPE)       /* Handle boolean integers for backwards compatibility */
1023         {
1024             fluid_int_setting_t *setting = &node->i;
1025
1026             if(setting->hints & FLUID_HINT_TOGGLED)
1027             {
1028                 FLUID_STRNCPY(str, setting->value ? "yes" : "no", len);
1029
1030                 retval = FLUID_OK;
1031             }
1032         }
1033     }
1034
1035     fluid_rec_mutex_unlock(settings->mutex);
1036
1037     return retval;
1038 }
1039
1040 /**
1041  * Duplicate the value of a string setting
1042  * @param settings a settings object
1043  * @param name a setting's name
1044  * @param str Location to store pointer to allocated duplicate string
1045  * @return #FLUID_OK if the value exists and was successfully duplicated, #FLUID_FAILED otherwise
1046  * @since 1.1.0
1047  *
1048  * Like fluid_settings_copystr() but allocates a new copy of the string.  Caller
1049  * owns the string and should free it with free() when done using it.
1050  */
1051 int
1052 fluid_settings_dupstr(fluid_settings_t *settings, const char *name, char **str)
1053 {
1054     fluid_setting_node_t *node;
1055     int retval = FLUID_FAILED;
1056
1057     fluid_return_val_if_fail(settings != NULL, retval);
1058     fluid_return_val_if_fail(name != NULL, retval);
1059     fluid_return_val_if_fail(name[0] != '\0', retval);
1060     fluid_return_val_if_fail(str != NULL, retval);
1061
1062     fluid_rec_mutex_lock(settings->mutex);
1063
1064     if(fluid_settings_get(settings, name, &node) == FLUID_OK)
1065     {
1066         if(node->type == FLUID_STR_TYPE)
1067         {
1068             fluid_str_setting_t *setting = &node->str;
1069
1070             if(setting->value)
1071             {
1072                 *str = FLUID_STRDUP(setting->value);
1073
1074                 if(!*str)
1075                 {
1076                     FLUID_LOG(FLUID_ERR, "Out of memory");
1077                 }
1078             }
1079
1080             if(!setting->value || *str)
1081             {
1082                 retval = FLUID_OK;    /* Don't set to FLUID_OK if out of memory */
1083             }
1084         }
1085         else if(node->type == FLUID_INT_TYPE)       /* Handle boolean integers for backwards compatibility */
1086         {
1087             fluid_int_setting_t *setting = &node->i;
1088
1089             if(setting->hints & FLUID_HINT_TOGGLED)
1090             {
1091                 *str = FLUID_STRDUP(setting->value ? "yes" : "no");
1092
1093                 if(!*str)
1094                 {
1095                     FLUID_LOG(FLUID_ERR, "Out of memory");
1096                 }
1097
1098                 if(!setting->value || *str)
1099                 {
1100                     retval = FLUID_OK;    /* Don't set to FLUID_OK if out of memory */
1101                 }
1102             }
1103         }
1104     }
1105
1106     fluid_rec_mutex_unlock(settings->mutex);
1107
1108     return retval;
1109 }
1110
1111
1112 /**
1113  * Test a string setting for some value.
1114  *
1115  * @param settings a settings object
1116  * @param name a setting's name
1117  * @param s a string to be tested
1118  * @return TRUE if the value exists and is equal to 's', FALSE otherwise
1119  */
1120 int
1121 fluid_settings_str_equal(fluid_settings_t *settings, const char *name, const char *s)
1122 {
1123     fluid_setting_node_t *node;
1124     int retval = FALSE;
1125
1126     fluid_return_val_if_fail(settings != NULL, retval);
1127     fluid_return_val_if_fail(name != NULL, retval);
1128     fluid_return_val_if_fail(name[0] != '\0', retval);
1129     fluid_return_val_if_fail(s != NULL, retval);
1130
1131     fluid_rec_mutex_lock(settings->mutex);
1132
1133     if(fluid_settings_get(settings, name, &node) == FLUID_OK)
1134     {
1135         if(node->type == FLUID_STR_TYPE)
1136         {
1137             fluid_str_setting_t *setting = &node->str;
1138
1139             if(setting->value)
1140             {
1141                 retval = FLUID_STRCMP(setting->value, s) == 0;
1142             }
1143         }
1144         else if(node->type == FLUID_INT_TYPE)       /* Handle boolean integers for backwards compatibility */
1145         {
1146             fluid_int_setting_t *setting = &node->i;
1147
1148             if(setting->hints & FLUID_HINT_TOGGLED)
1149             {
1150                 retval = FLUID_STRCMP(setting->value ? "yes" : "no", s) == 0;
1151             }
1152         }
1153     }
1154
1155     fluid_rec_mutex_unlock(settings->mutex);
1156
1157     return retval;
1158 }
1159
1160 /**
1161  * Get the default value of a string setting.  Note that the returned string is
1162  * not owned by the caller and should not be modified or freed.
1163  *
1164  * @param settings a settings object
1165  * @param name a setting's name
1166  * @param def the default string value of the setting if it exists
1167  * @return FLUID_OK on success, FLUID_FAILED otherwise
1168  */
1169 int
1170 fluid_settings_getstr_default(fluid_settings_t *settings, const char *name, char **def)
1171 {
1172     fluid_setting_node_t *node;
1173     char *retval = NULL;
1174
1175     fluid_return_val_if_fail(settings != NULL, FLUID_FAILED);
1176     fluid_return_val_if_fail(name != NULL, FLUID_FAILED);
1177     fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED);
1178
1179     fluid_rec_mutex_lock(settings->mutex);
1180
1181     if(fluid_settings_get(settings, name, &node) == FLUID_OK)
1182     {
1183         if(node->type == FLUID_STR_TYPE)
1184         {
1185             fluid_str_setting_t *setting = &node->str;
1186             retval = setting->def;
1187         }
1188         else if(node->type == FLUID_INT_TYPE)       /* Handle boolean integers for backwards compatibility */
1189         {
1190             fluid_int_setting_t *setting = &node->i;
1191
1192             if(setting->hints & FLUID_HINT_TOGGLED)
1193             {
1194                 retval = setting->def ? "yes" : "no";
1195             }
1196         }
1197     }
1198
1199     *def = retval;
1200     fluid_rec_mutex_unlock(settings->mutex);
1201
1202     return retval != NULL ? FLUID_OK : FLUID_FAILED;
1203 }
1204
1205 /**
1206  * Add an option to a string setting (like an enumeration value).
1207  * @param settings a settings object
1208  * @param name a setting's name
1209  * @param s option string to add
1210  * @return #FLUID_OK if the setting exists and option was added, #FLUID_FAILED otherwise
1211  *
1212  * Causes the setting's #FLUID_HINT_OPTIONLIST hint to be set.
1213  */
1214 int
1215 fluid_settings_add_option(fluid_settings_t *settings, const char *name, const char *s)
1216 {
1217     fluid_setting_node_t *node;
1218     int retval = FLUID_FAILED;
1219
1220     fluid_return_val_if_fail(settings != NULL, retval);
1221     fluid_return_val_if_fail(name != NULL, retval);
1222     fluid_return_val_if_fail(name[0] != '\0', retval);
1223     fluid_return_val_if_fail(s != NULL, retval);
1224
1225     fluid_rec_mutex_lock(settings->mutex);
1226
1227     if(fluid_settings_get(settings, name, &node) == FLUID_OK
1228             && (node->type == FLUID_STR_TYPE))
1229     {
1230         fluid_str_setting_t *setting = &node->str;
1231         char *copy = FLUID_STRDUP(s);
1232         setting->options = fluid_list_append(setting->options, copy);
1233         setting->hints |= FLUID_HINT_OPTIONLIST;
1234         retval = FLUID_OK;
1235     }
1236
1237     fluid_rec_mutex_unlock(settings->mutex);
1238
1239     return retval;
1240 }
1241
1242 /**
1243  * Remove an option previously assigned by fluid_settings_add_option().
1244  * @param settings a settings object
1245  * @param name a setting's name
1246  * @param s option string to remove
1247  * @return #FLUID_OK if the setting exists and option was removed, #FLUID_FAILED otherwise
1248  */
1249 int
1250 fluid_settings_remove_option(fluid_settings_t *settings, const char *name, const char *s)
1251 {
1252     fluid_setting_node_t *node;
1253     int retval = FLUID_FAILED;
1254
1255     fluid_return_val_if_fail(settings != NULL, retval);
1256     fluid_return_val_if_fail(name != NULL, retval);
1257     fluid_return_val_if_fail(name[0] != '\0', retval);
1258     fluid_return_val_if_fail(s != NULL, retval);
1259
1260     fluid_rec_mutex_lock(settings->mutex);
1261
1262     if(fluid_settings_get(settings, name, &node) == FLUID_OK
1263             && (node->type == FLUID_STR_TYPE))
1264     {
1265
1266         fluid_str_setting_t *setting = &node->str;
1267         fluid_list_t *list = setting->options;
1268
1269         while(list)
1270         {
1271             char *option = (char *) fluid_list_get(list);
1272
1273             if(FLUID_STRCMP(s, option) == 0)
1274             {
1275                 FLUID_FREE(option);
1276                 setting->options = fluid_list_remove_link(setting->options, list);
1277                 retval = FLUID_OK;
1278                 break;
1279             }
1280
1281             list = fluid_list_next(list);
1282         }
1283     }
1284
1285     fluid_rec_mutex_unlock(settings->mutex);
1286
1287     return retval;
1288 }
1289
1290 /**
1291  * Set a numeric value for a named setting.
1292  *
1293  * @param settings a settings object
1294  * @param name a setting's name
1295  * @param val new setting's value
1296  * @return #FLUID_OK if the value has been set, #FLUID_FAILED otherwise
1297  */
1298 int
1299 fluid_settings_setnum(fluid_settings_t *settings, const char *name, double val)
1300 {
1301     fluid_setting_node_t *node;
1302     fluid_num_setting_t *setting;
1303     fluid_num_update_t callback = NULL;
1304     void *data = NULL;
1305
1306     fluid_return_val_if_fail(settings != NULL, FLUID_FAILED);
1307     fluid_return_val_if_fail(name != NULL, FLUID_FAILED);
1308     fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED);
1309
1310     fluid_rec_mutex_lock(settings->mutex);
1311
1312     if((fluid_settings_get(settings, name, &node) != FLUID_OK)
1313             || (node->type != FLUID_NUM_TYPE))
1314     {
1315         goto error_recovery;
1316     }
1317
1318     setting = &node->num;
1319
1320     if(val < setting->min || val > setting->max)
1321     {
1322         FLUID_LOG(FLUID_DBG, "requested set value for %s out of range", name);
1323         goto error_recovery;
1324     }
1325
1326     setting->value = val;
1327
1328     callback = setting->update;
1329     data = setting->data;
1330
1331     /* Release the mutex before calling the update callback, to avoid
1332      * possible deadlocks with FluidSynths API lock */
1333     fluid_rec_mutex_unlock(settings->mutex);
1334
1335     if(callback)
1336     {
1337         (*callback)(data, name, val);
1338     }
1339
1340     return FLUID_OK;
1341
1342 error_recovery:
1343     fluid_rec_mutex_unlock(settings->mutex);
1344     return FLUID_FAILED;
1345 }
1346
1347 /**
1348  * Get the numeric value of a named setting
1349  *
1350  * @param settings a settings object
1351  * @param name a setting's name
1352  * @param val variable pointer to receive the setting's numeric value
1353  * @return #FLUID_OK if the value exists, #FLUID_FAILED otherwise
1354  */
1355 int
1356 fluid_settings_getnum(fluid_settings_t *settings, const char *name, double *val)
1357 {
1358     fluid_setting_node_t *node;
1359     int retval = FLUID_FAILED;
1360
1361     fluid_return_val_if_fail(settings != NULL, retval);
1362     fluid_return_val_if_fail(name != NULL, retval);
1363     fluid_return_val_if_fail(name[0] != '\0', retval);
1364     fluid_return_val_if_fail(val != NULL, retval);
1365
1366     fluid_rec_mutex_lock(settings->mutex);
1367
1368     if(fluid_settings_get(settings, name, &node) == FLUID_OK
1369             && (node->type == FLUID_NUM_TYPE))
1370     {
1371         fluid_num_setting_t *setting = &node->num;
1372         *val = setting->value;
1373         retval = FLUID_OK;
1374     }
1375
1376     fluid_rec_mutex_unlock(settings->mutex);
1377
1378     return retval;
1379 }
1380
1381 /**
1382  * float-typed wrapper for fluid_settings_getnum
1383  *
1384  * @param settings a settings object
1385  * @param name a setting's name
1386  * @param val variable pointer to receive the setting's float value
1387  * @return #FLUID_OK if the value exists, #FLUID_FAILED otherwise
1388  */
1389 int fluid_settings_getnum_float(fluid_settings_t *settings, const char *name, float *val)
1390 {
1391     double tmp;
1392
1393     if(fluid_settings_getnum(settings, name, &tmp) == FLUID_OK)
1394     {
1395         *val = tmp;
1396         return FLUID_OK;
1397     }
1398
1399     return FLUID_FAILED;
1400 }
1401
1402 /**
1403  * Get the range of values of a numeric setting
1404  *
1405  * @param settings a settings object
1406  * @param name a setting's name
1407  * @param min setting's range lower limit
1408  * @param max setting's range upper limit
1409  * @return #FLUID_OK if the setting's range exists, #FLUID_FAILED otherwise
1410  */
1411 int
1412 fluid_settings_getnum_range(fluid_settings_t *settings, const char *name,
1413                             double *min, double *max)
1414 {
1415     fluid_setting_node_t *node;
1416     int retval = FLUID_FAILED;
1417
1418     fluid_return_val_if_fail(settings != NULL, retval);
1419     fluid_return_val_if_fail(name != NULL, retval);
1420     fluid_return_val_if_fail(name[0] != '\0', retval);
1421     fluid_return_val_if_fail(min != NULL, retval);
1422     fluid_return_val_if_fail(max != NULL, retval);
1423
1424     fluid_rec_mutex_lock(settings->mutex);
1425
1426     if(fluid_settings_get(settings, name, &node) == FLUID_OK
1427             && (node->type == FLUID_NUM_TYPE))
1428     {
1429         fluid_num_setting_t *setting = &node->num;
1430         *min = setting->min;
1431         *max = setting->max;
1432         retval = FLUID_OK;
1433     }
1434
1435     fluid_rec_mutex_unlock(settings->mutex);
1436
1437     return retval;
1438 }
1439
1440 /**
1441  * Get the default value of a named numeric (double) setting
1442  *
1443  * @param settings a settings object
1444  * @param name a setting's name
1445  * @param val set to the default value if the named setting exists
1446  * @return #FLUID_OK if the default value of the named setting exists, #FLUID_FAILED otherwise
1447  */
1448 int
1449 fluid_settings_getnum_default(fluid_settings_t *settings, const char *name, double *val)
1450 {
1451     fluid_setting_node_t *node;
1452     int retval = FLUID_FAILED;
1453
1454     fluid_return_val_if_fail(settings != NULL, retval);
1455     fluid_return_val_if_fail(name != NULL, retval);
1456     fluid_return_val_if_fail(name[0] != '\0', retval);
1457     fluid_return_val_if_fail(val != NULL, retval);
1458
1459     fluid_rec_mutex_lock(settings->mutex);
1460
1461     if(fluid_settings_get(settings, name, &node) == FLUID_OK
1462             && (node->type == FLUID_NUM_TYPE))
1463     {
1464         fluid_num_setting_t *setting = &node->num;
1465         *val = setting->def;
1466         retval = FLUID_OK;
1467     }
1468
1469     fluid_rec_mutex_unlock(settings->mutex);
1470
1471     return retval;
1472 }
1473
1474 /**
1475  * Set an integer value for a setting
1476  *
1477  * @param settings a settings object
1478  * @param name a setting's name
1479  * @param val new setting's integer value
1480  * @return #FLUID_OK if the value has been set, #FLUID_FAILED otherwise
1481  */
1482 int
1483 fluid_settings_setint(fluid_settings_t *settings, const char *name, int val)
1484 {
1485     fluid_setting_node_t *node;
1486     fluid_int_setting_t *setting;
1487     fluid_int_update_t callback = NULL;
1488     void *data = NULL;
1489
1490     fluid_return_val_if_fail(settings != NULL, FLUID_FAILED);
1491     fluid_return_val_if_fail(name != NULL, FLUID_FAILED);
1492     fluid_return_val_if_fail(name[0] != '\0', FLUID_FAILED);
1493
1494     fluid_rec_mutex_lock(settings->mutex);
1495
1496     if((fluid_settings_get(settings, name, &node) != FLUID_OK)
1497             || (node->type != FLUID_INT_TYPE))
1498     {
1499         goto error_recovery;
1500     }
1501
1502     setting = &node->i;
1503
1504     if(val < setting->min || val > setting->max)
1505     {
1506         FLUID_LOG(FLUID_DBG, "requested set value for %s out of range", name);
1507         goto error_recovery;
1508     }
1509
1510     setting->value = val;
1511
1512     callback = setting->update;
1513     data = setting->data;
1514
1515     /* Release the mutex before calling the update callback, to avoid
1516      * possible deadlocks with FluidSynths API lock */
1517     fluid_rec_mutex_unlock(settings->mutex);
1518
1519     if(callback)
1520     {
1521         (*callback)(data, name, val);
1522     }
1523
1524     return FLUID_OK;
1525
1526 error_recovery:
1527     fluid_rec_mutex_unlock(settings->mutex);
1528     return FLUID_FAILED;
1529 }
1530
1531 /**
1532  * Get an integer value setting.
1533  *
1534  * @param settings a settings object
1535  * @param name a setting's name
1536  * @param val pointer to a variable to receive the setting's integer value
1537  * @return #FLUID_OK if the value exists, #FLUID_FAILED otherwise
1538  */
1539 int
1540 fluid_settings_getint(fluid_settings_t *settings, const char *name, int *val)
1541 {
1542     fluid_setting_node_t *node;
1543     int retval = FLUID_FAILED;
1544
1545     fluid_return_val_if_fail(settings != NULL, retval);
1546     fluid_return_val_if_fail(name != NULL, retval);
1547     fluid_return_val_if_fail(name[0] != '\0', retval);
1548     fluid_return_val_if_fail(val != NULL, retval);
1549
1550     fluid_rec_mutex_lock(settings->mutex);
1551
1552     if(fluid_settings_get(settings, name, &node) == FLUID_OK
1553             && (node->type == FLUID_INT_TYPE))
1554     {
1555         fluid_int_setting_t *setting = &node->i;
1556         *val = setting->value;
1557         retval = FLUID_OK;
1558     }
1559
1560     fluid_rec_mutex_unlock(settings->mutex);
1561
1562     return retval;
1563 }
1564
1565 /**
1566  * Get the range of values of an integer setting
1567  * @param settings a settings object
1568  * @param name a setting's name
1569  * @param min setting's range lower limit
1570  * @param max setting's range upper limit
1571  * @return #FLUID_OK if the setting's range exists, #FLUID_FAILED otherwise
1572  */
1573 int
1574 fluid_settings_getint_range(fluid_settings_t *settings, const char *name,
1575                             int *min, int *max)
1576 {
1577     fluid_setting_node_t *node;
1578     int retval = FLUID_FAILED;
1579
1580     fluid_return_val_if_fail(settings != NULL, retval);
1581     fluid_return_val_if_fail(name != NULL, retval);
1582     fluid_return_val_if_fail(name[0] != '\0', retval);
1583     fluid_return_val_if_fail(min != NULL, retval);
1584     fluid_return_val_if_fail(max != NULL, retval);
1585
1586     fluid_rec_mutex_lock(settings->mutex);
1587
1588     if(fluid_settings_get(settings, name, &node) == FLUID_OK
1589             && (node->type == FLUID_INT_TYPE))
1590     {
1591         fluid_int_setting_t *setting = &node->i;
1592         *min = setting->min;
1593         *max = setting->max;
1594         retval = FLUID_OK;
1595     }
1596
1597     fluid_rec_mutex_unlock(settings->mutex);
1598
1599     return retval;
1600 }
1601
1602 /**
1603  * Get the default value of an integer setting.
1604  *
1605  * @param settings a settings object
1606  * @param name a setting's name
1607  * @param val set to the setting's default integer value if it exists
1608  * @return #FLUID_OK if the setting's default integer value exists, #FLUID_FAILED otherwise
1609  */
1610 int fluid_settings_getint_default(fluid_settings_t *settings, const char *name, int *val)
1611 {
1612     fluid_setting_node_t *node;
1613     int retval = FLUID_FAILED;
1614
1615     fluid_return_val_if_fail(settings != NULL, retval);
1616     fluid_return_val_if_fail(name != NULL, retval);
1617     fluid_return_val_if_fail(name[0] != '\0', retval);
1618     fluid_return_val_if_fail(val != NULL, retval);
1619
1620     fluid_rec_mutex_lock(settings->mutex);
1621
1622     if(fluid_settings_get(settings, name, &node) == FLUID_OK
1623             && (node->type == FLUID_INT_TYPE))
1624     {
1625         fluid_int_setting_t *setting = &node->i;
1626         *val = setting->def;
1627         retval = FLUID_OK;
1628     }
1629
1630     fluid_rec_mutex_unlock(settings->mutex);
1631
1632     return retval;
1633 }
1634
1635 /**
1636  * Iterate the available options for a named string setting, calling the provided
1637  * callback function for each existing option.
1638  *
1639  * @param settings a settings object
1640  * @param name a setting's name
1641  * @param data any user provided pointer
1642  * @param func callback function to be called on each iteration
1643  *
1644  * @note Starting with FluidSynth 1.1.0 the \a func callback is called for each
1645  * option in alphabetical order.  Sort order was undefined in previous versions.
1646  */
1647 void
1648 fluid_settings_foreach_option(fluid_settings_t *settings, const char *name,
1649                               void *data, fluid_settings_foreach_option_t func)
1650 {
1651     fluid_setting_node_t *node;
1652     fluid_str_setting_t *setting;
1653     fluid_list_t *p, *newlist = NULL;
1654
1655     fluid_return_if_fail(settings != NULL);
1656     fluid_return_if_fail(name != NULL);
1657     fluid_return_if_fail(name[0] != '\0');
1658     fluid_return_if_fail(func != NULL);
1659
1660     fluid_rec_mutex_lock(settings->mutex);        /* ++ lock */
1661
1662     if(fluid_settings_get(settings, name, &node) != FLUID_OK
1663             || node->type != FLUID_STR_TYPE)
1664     {
1665         fluid_rec_mutex_unlock(settings->mutex);    /* -- unlock */
1666         return;
1667     }
1668
1669     setting = &node->str;
1670
1671     /* Duplicate option list */
1672     for(p = setting->options; p; p = p->next)
1673     {
1674         newlist = fluid_list_append(newlist, fluid_list_get(p));
1675     }
1676
1677     /* Sort by name */
1678     newlist = fluid_list_sort(newlist, fluid_list_str_compare_func);
1679
1680     for(p = newlist; p; p = p->next)
1681     {
1682         (*func)(data, name, (const char *)fluid_list_get(p));
1683     }
1684
1685     fluid_rec_mutex_unlock(settings->mutex);    /* -- unlock */
1686
1687     delete_fluid_list(newlist);
1688 }
1689
1690 /**
1691  * Count option string values for a string setting.
1692  * @param settings a settings object
1693  * @param name Name of setting
1694  * @return Count of options for this string setting (0 if none, -1 if not found
1695  *   or not a string setting)
1696  * @since 1.1.0
1697  */
1698 int
1699 fluid_settings_option_count(fluid_settings_t *settings, const char *name)
1700 {
1701     fluid_setting_node_t *node;
1702     int count = -1;
1703
1704     fluid_return_val_if_fail(settings != NULL, -1);
1705     fluid_return_val_if_fail(name != NULL, -1);
1706     fluid_return_val_if_fail(name[0] != '\0', -1);
1707
1708     fluid_rec_mutex_lock(settings->mutex);
1709
1710     if(fluid_settings_get(settings, name, &node) == FLUID_OK
1711             && node->type == FLUID_STR_TYPE)
1712     {
1713         count = fluid_list_size(node->str.options);
1714     }
1715
1716     fluid_rec_mutex_unlock(settings->mutex);
1717
1718     return (count);
1719 }
1720
1721 /**
1722  * Concatenate options for a string setting together with a separator between.
1723  * @param settings Settings object
1724  * @param name Settings name
1725  * @param separator String to use between options (NULL to use ", ")
1726  * @return Newly allocated string or NULL on error (out of memory, not a valid
1727  *   setting \a name or not a string setting).  Free the string when finished with it.
1728  * @since 1.1.0
1729  */
1730 char *
1731 fluid_settings_option_concat(fluid_settings_t *settings, const char *name,
1732                              const char *separator)
1733 {
1734     fluid_setting_node_t *node;
1735     fluid_str_setting_t *setting;
1736     fluid_list_t *p, *newlist = NULL;
1737     size_t count, len;
1738     char *str, *option;
1739
1740     fluid_return_val_if_fail(settings != NULL, NULL);
1741     fluid_return_val_if_fail(name != NULL, NULL);
1742     fluid_return_val_if_fail(name[0] != '\0', NULL);
1743
1744     if(!separator)
1745     {
1746         separator = ", ";
1747     }
1748
1749     fluid_rec_mutex_lock(settings->mutex);        /* ++ lock */
1750
1751     if(fluid_settings_get(settings, name, &node) != FLUID_OK
1752             || node->type != FLUID_STR_TYPE)
1753     {
1754         fluid_rec_mutex_unlock(settings->mutex);    /* -- unlock */
1755         return (NULL);
1756     }
1757
1758     setting = &node->str;
1759
1760     /* Duplicate option list, count options and get total string length */
1761     for(p = setting->options, count = 0, len = 0; p; p = p->next, count++)
1762     {
1763         option = fluid_list_get(p);
1764
1765         if(option)
1766         {
1767             newlist = fluid_list_append(newlist, option);
1768             len += FLUID_STRLEN(option);
1769         }
1770     }
1771
1772     if(count > 1)
1773     {
1774         len += (count - 1) * FLUID_STRLEN(separator);
1775     }
1776
1777     len++;        /* For terminator */
1778
1779     /* Sort by name */
1780     newlist = fluid_list_sort(newlist, fluid_list_str_compare_func);
1781
1782     str = FLUID_MALLOC(len);
1783
1784     if(str)
1785     {
1786         str[0] = 0;
1787
1788         for(p = newlist; p; p = p->next)
1789         {
1790             option = fluid_list_get(p);
1791             strcat(str, option);
1792
1793             if(p->next)
1794             {
1795                 strcat(str, separator);
1796             }
1797         }
1798     }
1799
1800     fluid_rec_mutex_unlock(settings->mutex);    /* -- unlock */
1801
1802     delete_fluid_list(newlist);
1803
1804     if(!str)
1805     {
1806         FLUID_LOG(FLUID_ERR, "Out of memory");
1807     }
1808
1809     return (str);
1810 }
1811
1812 /* Structure passed to fluid_settings_foreach_iter recursive function */
1813 typedef struct
1814 {
1815     char path[MAX_SETTINGS_LABEL + 1];    /* Maximum settings label length */
1816     fluid_list_t *names;                  /* For fluid_settings_foreach() */
1817 } fluid_settings_foreach_bag_t;
1818
1819 static int
1820 fluid_settings_foreach_iter(void *key, void *value, void *data)
1821 {
1822     fluid_settings_foreach_bag_t *bag = data;
1823     char *keystr = key;
1824     fluid_setting_node_t *node = value;
1825     size_t pathlen;
1826     char *s;
1827
1828     pathlen = FLUID_STRLEN(bag->path);
1829
1830     if(pathlen > 0)
1831     {
1832         bag->path[pathlen] = '.';
1833         bag->path[pathlen + 1] = 0;
1834     }
1835
1836     strcat(bag->path, keystr);
1837
1838     switch(node->type)
1839     {
1840     case FLUID_NUM_TYPE:
1841     case FLUID_INT_TYPE:
1842     case FLUID_STR_TYPE:
1843         s = FLUID_STRDUP(bag->path);
1844
1845         if(s)
1846         {
1847             bag->names = fluid_list_append(bag->names, s);
1848         }
1849
1850         break;
1851
1852     case FLUID_SET_TYPE:
1853         fluid_hashtable_foreach(node->set.hashtable,
1854                                 fluid_settings_foreach_iter, bag);
1855         break;
1856     }
1857
1858     bag->path[pathlen] = 0;
1859
1860     return 0;
1861 }
1862
1863 /**
1864  * Iterate the existing settings defined in a settings object, calling the
1865  * provided callback function for each setting.
1866  *
1867  * @param settings a settings object
1868  * @param data any user provided pointer
1869  * @param func callback function to be called on each iteration
1870  *
1871  * @note Starting with FluidSynth 1.1.0 the \a func callback is called for each
1872  * setting in alphabetical order.  Sort order was undefined in previous versions.
1873  */
1874 void
1875 fluid_settings_foreach(fluid_settings_t *settings, void *data,
1876                        fluid_settings_foreach_t func)
1877 {
1878     fluid_settings_foreach_bag_t bag;
1879     fluid_setting_node_t *node;
1880     fluid_list_t *p;
1881
1882     fluid_return_if_fail(settings != NULL);
1883     fluid_return_if_fail(func != NULL);
1884
1885     bag.path[0] = 0;
1886     bag.names = NULL;
1887
1888     fluid_rec_mutex_lock(settings->mutex);
1889
1890     /* Add all node names to the bag.names list */
1891     fluid_hashtable_foreach(settings, fluid_settings_foreach_iter, &bag);
1892
1893     /* Sort names */
1894     bag.names = fluid_list_sort(bag.names, fluid_list_str_compare_func);
1895
1896     /* Loop over names and call the callback */
1897     for(p = bag.names; p; p = p->next)
1898     {
1899         if(fluid_settings_get(settings, (const char *)(p->data), &node) == FLUID_OK
1900                 && node)
1901         {
1902             (*func)(data, (const char *)(p->data), node->type);
1903         }
1904
1905         FLUID_FREE(p->data);        /* -- Free name */
1906     }
1907
1908     fluid_rec_mutex_unlock(settings->mutex);
1909
1910     delete_fluid_list(bag.names);         /* -- Free names list */
1911 }
1912
1913 /**
1914  * Split a comma-separated list of integers and fill the passed
1915  * in buffer with the parsed values.
1916  *
1917  * @param str the comma-separated string to split
1918  * @param buf user-supplied buffer to hold the parsed numbers
1919  * @param buf_len length of user-supplied buffer
1920  * @return number of parsed values or -1 on failure
1921  */
1922 int fluid_settings_split_csv(const char *str, int *buf, int buf_len)
1923 {
1924     char *s;
1925     char *tok;
1926     char *tokstr;
1927     int n = 0;
1928
1929     s = tokstr = FLUID_STRDUP(str);
1930
1931     if(s == NULL)
1932     {
1933         FLUID_LOG(FLUID_ERR, "Out of memory");
1934         return -1;
1935     }
1936
1937     while((tok = fluid_strtok(&tokstr, ",")) && n < buf_len)
1938     {
1939         buf[n++] = atoi(tok);
1940     }
1941
1942     FLUID_FREE(s);
1943
1944     return n;
1945 }