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 Library General Public License
7  * as published by the Free Software Foundation; either version 2 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  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library 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 <glib.h>
22 
23 #include "fluidsynth_priv.h"
24 #include "fluid_cmd.h"
25 #include "fluid_synth.h"
26 #include "fluid_settings.h"
27 #include "fluid_hash.h"
28 #include "fluid_sys.h"
29 #include "fluid_midi_router.h"
30 #include "fluid_sfont.h"
31 #include "fluid_chan.h"
32 
33 #if WITH_READLINE
34 #include <readline/readline.h>
35 #include <readline/history.h>
36 #endif
37 
38 #define MAX_TOKENS 100 /* LADSPA plugins need lots of parameters */
39 #define MAX_COMMAND_LEN 1024	/* max command length accepted by fluid_command() */
40 #define FLUID_WORKLINELENGTH 1024 /* LADSPA plugins use long command lines */
41 
42 struct _fluid_shell_t {
43   fluid_settings_t* settings;
44   fluid_cmd_handler_t* handler;
45   fluid_thread_t* thread;
46   fluid_istream_t in;
47   fluid_ostream_t out;
48 };
49 
50 static int fluid_shell_run(fluid_shell_t* shell);
51 static void fluid_shell_init(fluid_shell_t* shell,
52                              fluid_settings_t* settings, fluid_cmd_handler_t* handler,
53                              fluid_istream_t in, fluid_ostream_t out);
54 static int fluid_handle_voice_count (fluid_synth_t *synth, int ac, char **av,
55                                      fluid_ostream_t out);
56 
fluid_shell_settings(fluid_settings_t * settings)57 void fluid_shell_settings(fluid_settings_t* settings)
58 {
59   fluid_settings_register_str(settings, "shell.prompt", "", 0, NULL, NULL);
60   fluid_settings_register_int(settings, "shell.port", 9800, 1, 65535, 0, NULL, NULL);
61 }
62 
63 
64 /** the table of all handled commands */
65 
66 fluid_cmd_t fluid_commands[] = {
67   { "help", "general", (fluid_cmd_func_t) fluid_handle_help, NULL,
68     "help                       Show help topics ('help TOPIC' for more info)" },
69   { "quit", "general", (fluid_cmd_func_t) fluid_handle_quit, NULL,
70     "quit                       Quit the synthesizer" },
71   { "noteon", "event", (fluid_cmd_func_t) fluid_handle_noteon, NULL,
72     "noteon chan key vel        Send noteon" },
73   { "noteoff", "event", (fluid_cmd_func_t) fluid_handle_noteoff, NULL,
74     "noteoff chan key           Send noteoff"  },
75   { "pitch_bend", "event", (fluid_cmd_func_t) fluid_handle_pitch_bend, NULL,
76     "pitch_bend chan offset           Bend pitch"  },
77   { "pitch_bend_range", "event", (fluid_cmd_func_t) fluid_handle_pitch_bend_range, NULL,
78     "pitch_bend chan range           Set bend pitch range"  },
79   { "cc", "event", (fluid_cmd_func_t) fluid_handle_cc, NULL,
80     "cc chan ctrl value         Send control-change message" },
81   { "prog", "event", (fluid_cmd_func_t) fluid_handle_prog, NULL,
82     "prog chan num              Send program-change message" },
83   { "select", "event", (fluid_cmd_func_t) fluid_handle_select, NULL,
84     "select chan sfont bank prog  Combination of bank-select and program-change" },
85   { "load", "general", (fluid_cmd_func_t) fluid_handle_load, NULL,
86     "load file [reset] [bankofs] Load SoundFont (reset=0|1, def 1; bankofs=n, def 0)" },
87   { "unload", "general", (fluid_cmd_func_t) fluid_handle_unload, NULL,
88     "unload id [reset]          Unload SoundFont by ID (reset=0|1, default 1)"},
89   { "reload", "general", (fluid_cmd_func_t) fluid_handle_reload, NULL,
90     "reload id                  Reload the SoundFont with the specified ID" },
91   { "fonts", "general", (fluid_cmd_func_t) fluid_handle_fonts, NULL,
92     "fonts                      Display the list of loaded SoundFonts" },
93   { "inst", "general", (fluid_cmd_func_t) fluid_handle_inst, NULL,
94     "inst font                  Print out the available instruments for the font" },
95   { "channels", "general", (fluid_cmd_func_t) fluid_handle_channels, NULL,
96     "channels [-verbose]        Print out preset of all channels" },
97   { "interp", "general", (fluid_cmd_func_t) fluid_handle_interp, NULL,
98     "interp num                 Choose interpolation method for all channels" },
99   { "interpc", "general", (fluid_cmd_func_t) fluid_handle_interpc, NULL,
100     "interpc chan num           Choose interpolation method for one channel" },
101   { "rev_preset", "reverb", (fluid_cmd_func_t) fluid_handle_reverbpreset, NULL,
102     "rev_preset num             Load preset num into the reverb unit" },
103   { "rev_setroomsize", "reverb", (fluid_cmd_func_t) fluid_handle_reverbsetroomsize, NULL,
104     "rev_setroomsize num        Change reverb room size" },
105   { "rev_setdamp", "reverb", (fluid_cmd_func_t) fluid_handle_reverbsetdamp, NULL,
106     "rev_setdamp num            Change reverb damping" },
107   { "rev_setwidth", "reverb", (fluid_cmd_func_t) fluid_handle_reverbsetwidth, NULL,
108     "rev_setwidth num           Change reverb width" },
109   { "rev_setlevel", "reverb", (fluid_cmd_func_t) fluid_handle_reverbsetlevel, NULL,
110     "rev_setlevel num           Change reverb level" },
111   { "reverb", "reverb", (fluid_cmd_func_t) fluid_handle_reverb, NULL,
112     "reverb [0|1|on|off]        Turn the reverb on or off" },
113   { "cho_set_nr", "chorus", (fluid_cmd_func_t) fluid_handle_chorusnr, NULL,
114     "cho_set_nr n               Use n delay lines (default 3)" },
115   { "cho_set_level", "chorus", (fluid_cmd_func_t) fluid_handle_choruslevel, NULL,
116     "cho_set_level num          Set output level of each chorus line to num" },
117   { "cho_set_speed", "chorus", (fluid_cmd_func_t) fluid_handle_chorusspeed, NULL,
118     "cho_set_speed num          Set mod speed of chorus to num (Hz)" },
119   { "cho_set_depth", "chorus", (fluid_cmd_func_t) fluid_handle_chorusdepth, NULL,
120     "cho_set_depth num          Set chorus modulation depth to num (ms)" },
121   { "chorus", "chorus", (fluid_cmd_func_t) fluid_handle_chorus, NULL,
122     "chorus [0|1|on|off]        Turn the chorus on or off" },
123   { "gain", "general", (fluid_cmd_func_t) fluid_handle_gain, NULL,
124     "gain value                 Set the master gain (0 < gain < 5)" },
125   { "voice_count", "general", (fluid_cmd_func_t) fluid_handle_voice_count, NULL,
126     "voice_count                Get number of active synthesis voices" },
127   { "tuning", "tuning", (fluid_cmd_func_t) fluid_handle_tuning, NULL,
128     "tuning name bank prog      Create a tuning with name, bank number, \n"
129     "                           and program number (0 <= bank,prog <= 127)" },
130   { "tune", "tuning", (fluid_cmd_func_t) fluid_handle_tune, NULL,
131     "tune bank prog key pitch   Tune a key" },
132   { "settuning", "tuning", (fluid_cmd_func_t) fluid_handle_settuning, NULL,
133     "settuning chan bank prog   Set the tuning for a MIDI channel" },
134   { "resettuning", "tuning", (fluid_cmd_func_t) fluid_handle_resettuning, NULL,
135     "resettuning chan           Restore the default tuning of a MIDI channel" },
136   { "tunings", "tuning", (fluid_cmd_func_t) fluid_handle_tunings, NULL,
137     "tunings                    Print the list of available tunings" },
138   { "dumptuning", "tuning", (fluid_cmd_func_t) fluid_handle_dumptuning, NULL,
139     "dumptuning bank prog       Print the pitch details of the tuning" },
140   { "reset", "general", (fluid_cmd_func_t) fluid_handle_reset, NULL,
141     "reset                      System reset (all notes off, reset controllers)" },
142   { "set", "settings", (fluid_cmd_func_t) fluid_handle_set, NULL,
143     "set name value             Set the value of a controller or settings" },
144   { "get", "settings", (fluid_cmd_func_t) fluid_handle_get, NULL,
145     "get name                   Get the value of a controller or settings" },
146   { "info", "settings", (fluid_cmd_func_t) fluid_handle_info, NULL,
147     "info name                  Get information about a controller or settings" },
148   { "settings", "settings", (fluid_cmd_func_t) fluid_handle_settings, NULL,
149     "settings                   Print out all settings" },
150   { "echo", "general", (fluid_cmd_func_t) fluid_handle_echo, NULL,
151     "echo arg                   Print arg" },
152   /* LADSPA-related commands */
153 #ifdef LADSPA
154   { "ladspa_clear", "ladspa", (fluid_cmd_func_t) fluid_LADSPA_handle_clear, NULL,
155     "ladspa_clear               Resets LADSPA effect unit to bypass state"},
156   { "ladspa_add", "ladspa", (fluid_cmd_func_t) fluid_LADSPA_handle_add, NULL,
157     "ladspa_add lib plugin n1 <- p1 n2 -> p2 ... Loads and connects LADSPA plugin"},
158   { "ladspa_start", "ladspa", (fluid_cmd_func_t) fluid_LADSPA_handle_start, NULL,
159     "ladspa_start               Starts LADSPA effect unit"},
160   { "ladspa_declnode", "ladspa", (fluid_cmd_func_t) fluid_LADSPA_handle_declnode, NULL,
161     "ladspa_declnode node value Declares control node `node' with value `value'"},
162   { "ladspa_setnode", "ladspa", (fluid_cmd_func_t) fluid_LADSPA_handle_setnode, NULL,
163     "ladspa_setnode node value  Assigns `value' to `node'"},
164 #endif
165   { "router_clear", "router", (fluid_cmd_func_t) fluid_midi_router_handle_clear, NULL,
166     "router_clear               Clears all routing rules from the midi router"},
167   { "router_default", "router", (fluid_cmd_func_t) fluid_midi_router_handle_default, NULL,
168     "router_default             Resets the midi router to default state"},
169   { "router_begin", "router", (fluid_cmd_func_t) fluid_midi_router_handle_begin, NULL,
170     "router_begin [note|cc|prog|pbend|cpress|kpress]: Starts a new routing rule"},
171   { "router_chan", "router", (fluid_cmd_func_t) fluid_midi_router_handle_chan, NULL,
172     "router_chan min max mul add      filters and maps midi channels on current rule"},
173   { "router_par1", "router", (fluid_cmd_func_t) fluid_midi_router_handle_par1, NULL,
174     "router_par1 min max mul add      filters and maps parameter 1 (key/ctrl nr)"},
175   { "router_par2", "router", (fluid_cmd_func_t) fluid_midi_router_handle_par2, NULL,
176     "router_par2 min max mul add      filters and maps parameter 2 (vel/cc val)"},
177   { "router_end", "router", (fluid_cmd_func_t) fluid_midi_router_handle_end, NULL,
178     "router_end                 closes and commits the current routing rule"},
179   { NULL, NULL, NULL, NULL, NULL }
180 };
181 
182 /**
183  * Process a string command.
184  * NOTE: FluidSynth 1.0.8 and above no longer modifies the 'cmd' string.
185  * @param handler FluidSynth command handler
186  * @param cmd Command string (NOTE: Gets modified by FluidSynth prior to 1.0.8)
187  * @param out Output stream to display command response to
188  * @return Integer value corresponding to: -1 on command error, 0 on success,
189  *   1 if 'cmd' is a comment or is empty and -2 if quit was issued
190  */
191 int
fluid_command(fluid_cmd_handler_t * handler,const char * cmd,fluid_ostream_t out)192 fluid_command(fluid_cmd_handler_t* handler, const char *cmd, fluid_ostream_t out)
193 {
194   int result, num_tokens = 0;
195   char** tokens = NULL;
196 
197   if (cmd[0] == '#' || cmd[0] == '\0') {
198     return 1;
199   }
200 
201   if (!g_shell_parse_argv(cmd, &num_tokens, &tokens, NULL)) {
202     fluid_ostream_printf(out, "Error parsing command\n");
203     return -1;
204   }
205 
206   result = fluid_cmd_handler_handle(handler, num_tokens, &tokens[0], out);
207   g_strfreev(tokens);
208 
209   return result;
210 }
211 
212 /**
213  * Create a new FluidSynth command shell.
214  * @param settings Setting parameters to use with the shell
215  * @param handler Command handler
216  * @param in Input stream
217  * @param out Output stream
218  * @param thread TRUE if shell should be run in a separate thread, FALSE to run
219  *   it in the current thread (function blocks until "quit")
220  * @return New shell instance or NULL on error
221  */
222 fluid_shell_t *
new_fluid_shell(fluid_settings_t * settings,fluid_cmd_handler_t * handler,fluid_istream_t in,fluid_ostream_t out,int thread)223 new_fluid_shell(fluid_settings_t* settings, fluid_cmd_handler_t* handler,
224                 fluid_istream_t in, fluid_ostream_t out, int thread)
225 {
226   fluid_shell_t* shell = FLUID_NEW(fluid_shell_t);
227   if (shell == NULL) {
228     FLUID_LOG (FLUID_PANIC, "Out of memory");
229     return NULL;
230   }
231 
232 
233   fluid_shell_init(shell, settings, handler, in, out);
234 
235   if (thread) {
236     shell->thread = new_fluid_thread("shell", (fluid_thread_func_t) fluid_shell_run, shell,
237                                      0, TRUE);
238     if (shell->thread == NULL) {
239       delete_fluid_shell(shell);
240       return NULL;
241     }
242   } else {
243     shell->thread = NULL;
244     fluid_shell_run(shell);
245   }
246 
247   return shell;
248 }
249 
250 static void
fluid_shell_init(fluid_shell_t * shell,fluid_settings_t * settings,fluid_cmd_handler_t * handler,fluid_istream_t in,fluid_ostream_t out)251 fluid_shell_init(fluid_shell_t* shell,
252 		 fluid_settings_t* settings, fluid_cmd_handler_t* handler,
253 		 fluid_istream_t in, fluid_ostream_t out)
254 {
255   shell->settings = settings;
256   shell->handler = handler;
257   shell->in = in;
258   shell->out = out;
259 }
260 
261 /**
262  * Delete a FluidSynth command shell.
263  * @param shell Command shell instance
264  */
265 void
delete_fluid_shell(fluid_shell_t * shell)266 delete_fluid_shell(fluid_shell_t* shell)
267 {
268   if (shell->thread != NULL) {
269     delete_fluid_thread(shell->thread);
270   }
271 
272   FLUID_FREE(shell);
273 }
274 
275 static int
fluid_shell_run(fluid_shell_t * shell)276 fluid_shell_run(fluid_shell_t* shell)
277 {
278   char workline[FLUID_WORKLINELENGTH];
279   char* prompt = NULL;
280   int cont = 1;
281   int errors = 0;
282   int n;
283 
284   if (shell->settings)
285     fluid_settings_dupstr(shell->settings, "shell.prompt", &prompt);    /* ++ alloc prompt */
286 
287   /* handle user input */
288   while (cont) {
289 
290     n = fluid_istream_readline(shell->in, shell->out, prompt ? prompt : "", workline, FLUID_WORKLINELENGTH);
291 
292     if (n < 0) {
293       break;
294     }
295 
296 #if WITH_READLINE
297     if (shell->in == fluid_get_stdin()) {
298       add_history(workline);
299     }
300 #endif
301 
302     /* handle the command */
303     switch (fluid_command(shell->handler, workline, shell->out)) {
304 
305     case 1: /* empty line or comment */
306       break;
307 
308     case -1: /* erronous command */
309       errors++;
310     case 0: /* valid command */
311       break;
312 
313     case -2: /* quit */
314       cont = 0;
315       break;
316     }
317 
318     if (n == 0) {
319        break;
320     }
321   }
322 
323   if (prompt) FLUID_FREE (prompt);      /* -- free prompt */
324 
325   return errors;
326 }
327 
328 /**
329  * A convenience function to create a shell interfacing to standard input/output
330  * console streams.
331  * @param settings Settings instance for the shell
332  * @param handler Command handler callback
333  */
334 void
fluid_usershell(fluid_settings_t * settings,fluid_cmd_handler_t * handler)335 fluid_usershell(fluid_settings_t* settings, fluid_cmd_handler_t* handler)
336 {
337   fluid_shell_t shell;
338   fluid_shell_init(&shell, settings, handler, fluid_get_stdin(), fluid_get_stdout());
339   fluid_shell_run(&shell);
340 }
341 
342 /**
343  * Execute shell commands in a file.
344  * @param handler Command handler callback
345  * @param filename File name
346  * @return 0 on success, a value >1 on error
347  */
348 int
fluid_source(fluid_cmd_handler_t * handler,const char * filename)349 fluid_source(fluid_cmd_handler_t* handler, const char *filename)
350 {
351   int file;
352   fluid_shell_t shell;
353 
354 #ifdef WIN32
355   file = _open(filename, _O_RDONLY);
356 #else
357   file = open(filename, O_RDONLY);
358 #endif
359   if (file < 0) {
360     return file;
361   }
362   fluid_shell_init(&shell, NULL, handler, file, fluid_get_stdout());
363   return fluid_shell_run(&shell);
364 }
365 
366 /**
367  * Get the user specific FluidSynth command file name.
368  * @param buf Caller supplied string buffer to store file name to.
369  * @param len Length of \a buf
370  * @return Returns \a buf pointer or NULL if no user command file for this system type.
371  */
372 char*
fluid_get_userconf(char * buf,int len)373 fluid_get_userconf(char* buf, int len)
374 {
375 #if defined(WIN32) || defined(MACOS9)
376   return NULL;
377 #else
378   char* home = getenv("HOME");
379   if (home == NULL) {
380     return NULL;
381   } else {
382     snprintf(buf, len, "%s/.fluidsynth", home);
383     return buf;
384   }
385 #endif
386 }
387 
388 /**
389  * Get the system FluidSynth command file name.
390  * @param buf Caller supplied string buffer to store file name to.
391  * @param len Length of \a buf
392  * @return Returns \a buf pointer or NULL if no system command file for this system type.
393  */
394 char*
fluid_get_sysconf(char * buf,int len)395 fluid_get_sysconf(char* buf, int len)
396 {
397 #if defined(WIN32) || defined(MACOS9)
398   return NULL;
399 #else
400   snprintf(buf, len, "/etc/fluidsynth.conf");
401   return buf;
402 #endif
403 }
404 
405 
406 /*
407  *  handlers
408  */
409 int
fluid_handle_noteon(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)410 fluid_handle_noteon(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
411 {
412   if (ac < 3) {
413     fluid_ostream_printf(out, "noteon: too few arguments\n");
414     return -1;
415   }
416   if (!fluid_is_number(av[0]) || !fluid_is_number(av[1]) || !fluid_is_number(av[2])) {
417     fluid_ostream_printf(out, "noteon: invalid argument\n");
418     return -1;
419   }
420   return fluid_synth_noteon(synth, atoi(av[0]), atoi(av[1]), atoi(av[2]));
421 }
422 
423 int
fluid_handle_noteoff(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)424 fluid_handle_noteoff(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
425 {
426   if (ac < 2) {
427     fluid_ostream_printf(out, "noteoff: too few arguments\n");
428     return -1;
429   }
430   if (!fluid_is_number(av[0]) || !fluid_is_number(av[1])) {
431     fluid_ostream_printf(out, "noteon: invalid argument\n");
432     return -1;
433   }
434   return fluid_synth_noteoff(synth, atoi(av[0]), atoi(av[1]));
435 }
436 
437 int
fluid_handle_pitch_bend(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)438 fluid_handle_pitch_bend(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
439 {
440   if (ac < 2) {
441     fluid_ostream_printf(out, "pitch_bend: too few arguments\n");
442     return -1;
443   }
444   if (!fluid_is_number(av[0]) || !fluid_is_number(av[1])) {
445     fluid_ostream_printf(out, "pitch_bend: invalid argument\n");
446     return -1;
447   }
448   return fluid_synth_pitch_bend(synth, atoi(av[0]), atoi(av[1]));
449 }
450 
451 int
fluid_handle_pitch_bend_range(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)452 fluid_handle_pitch_bend_range(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
453 {
454   int channum;
455   int value;
456   if (ac < 2) {
457     fluid_ostream_printf(out, "pitch_bend_range: too few arguments\n");
458     return -1;
459   }
460   if (!fluid_is_number(av[0]) || !fluid_is_number(av[1])) {
461     fluid_ostream_printf(out, "pitch_bend_range: invalid argument\n");
462     return -1;
463   }
464   channum = atoi(av[0]);
465   value = atoi(av[1]);
466   fluid_channel_set_pitch_wheel_sensitivity(synth->channel[channum], value);
467   return 0;
468 }
469 
470 int
fluid_handle_cc(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)471 fluid_handle_cc(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
472 {
473   if (ac < 3) {
474     fluid_ostream_printf(out, "cc: too few arguments\n");
475     return -1;
476   }
477   if (!fluid_is_number(av[0]) || !fluid_is_number(av[1]) || !fluid_is_number(av[2])) {
478     fluid_ostream_printf(out, "cc: invalid argument\n");
479     return -1;
480   }
481   return fluid_synth_cc(synth, atoi(av[0]), atoi(av[1]), atoi(av[2]));
482 }
483 
484 int
fluid_handle_prog(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)485 fluid_handle_prog(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
486 {
487   if (ac < 2) {
488     fluid_ostream_printf(out, "prog: too few arguments\n");
489     return -1;
490   }
491   if (!fluid_is_number(av[0]) || !fluid_is_number(av[1])) {
492     fluid_ostream_printf(out, "prog: invalid argument\n");
493     return -1;
494   }
495   return fluid_synth_program_change(synth, atoi(av[0]), atoi(av[1]));
496 }
497 
498 int
fluid_handle_select(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)499 fluid_handle_select(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
500 {
501   int sfont_id;
502   int chan;
503   int bank;
504   int prog;
505 
506   if (ac < 4) {
507     fluid_ostream_printf(out, "preset: too few arguments\n");
508     return -1;
509   }
510   if (!fluid_is_number(av[0]) || !fluid_is_number(av[1])
511       || !fluid_is_number(av[2]) || !fluid_is_number(av[3])) {
512     fluid_ostream_printf(out, "preset: invalid argument\n");
513     return -1;
514   }
515 
516   chan = atoi(av[0]);
517   sfont_id = atoi(av[1]);
518   bank = atoi(av[2]);
519   prog = atoi(av[3]);
520 
521   if (sfont_id != 0) {
522     return fluid_synth_program_select(synth, chan, sfont_id, bank, prog);
523   } else {
524     if (fluid_synth_bank_select(synth, chan, bank) == FLUID_OK) {
525       return fluid_synth_program_change(synth, chan, prog);
526     }
527     return FLUID_FAILED;
528   }
529 }
530 
531 int
fluid_handle_inst(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)532 fluid_handle_inst(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
533 {
534   int font;
535   fluid_sfont_t* sfont;
536   fluid_preset_t preset;
537   int offset;
538 
539   if (ac < 1) {
540     fluid_ostream_printf(out, "inst: too few arguments\n");
541     return -1;
542   }
543 
544   if (!fluid_is_number(av[0])) {
545     fluid_ostream_printf(out, "inst: invalid argument\n");
546     return -1;
547   }
548 
549   font = atoi(av[0]);
550 
551   sfont = fluid_synth_get_sfont_by_id(synth, font);
552   offset = fluid_synth_get_bank_offset(synth, font);
553 
554   if (sfont == NULL) {
555     fluid_ostream_printf(out, "inst: invalid font number\n");
556     return -1;
557   }
558 
559   fluid_sfont_iteration_start(sfont);
560 
561   while (fluid_sfont_iteration_next(sfont, &preset)) {
562     fluid_ostream_printf(out, "%03d-%03d %s\n",
563 			fluid_preset_get_banknum(&preset) + offset,
564 			fluid_preset_get_num(&preset),
565 			fluid_preset_get_name(&preset));
566   }
567 
568   return 0;
569 }
570 
571 
572 int
fluid_handle_channels(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)573 fluid_handle_channels(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
574 {
575   fluid_synth_channel_info_t info;
576   int verbose = 0;
577   int i;
578 
579   if (ac > 0 && strcmp( av[0], "-verbose") == 0) verbose = 1;
580 
581   for (i = 0; i < fluid_synth_count_midi_channels (synth); i++)
582   {
583     fluid_synth_get_channel_info (synth, i, &info);
584 
585     if (!verbose)
586       fluid_ostream_printf (out, "chan %d, %s\n", i,
587                             info.assigned ? info.name : "no preset");
588     else
589       fluid_ostream_printf (out, "chan %d, sfont %d, bank %d, preset %d, %s\n", i,
590                             info.sfont_id, info.bank, info.program,
591                             info.assigned ? info.name : "no preset");
592   }
593 
594   return 0;
595 }
596 
597 int
fluid_handle_load(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)598 fluid_handle_load(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
599 {
600   char buf[1024];
601   int id;
602   int reset = 1;
603   int offset = 0;
604 
605   if (ac < 1) {
606     fluid_ostream_printf(out, "load: too few arguments\n");
607     return -1;
608   }
609   if (ac == 2) {
610     reset = atoi(av[1]);
611   }
612   if (ac == 3) {
613     offset = atoi(av[2]);
614   }
615 
616   /* Load the SoundFont without resetting the programs. The reset will
617    * be done later (if requested). */
618   id = fluid_synth_sfload(synth, fluid_expand_path(av[0], buf, 1024), 0);
619 
620   if (id == -1) {
621     fluid_ostream_printf(out, "failed to load the SoundFont\n");
622     return -1;
623   } else {
624     fluid_ostream_printf(out, "loaded SoundFont has ID %d\n", id);
625   }
626 
627   if (offset) {
628     fluid_synth_set_bank_offset(synth, id, offset);
629   }
630 
631   /* The reset should be done after the offset is set. */
632   if (reset) {
633     fluid_synth_program_reset(synth);
634   }
635 
636   return 0;
637 }
638 
639 int
fluid_handle_unload(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)640 fluid_handle_unload(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
641 {
642   int reset = 1;
643   if (ac < 1) {
644     fluid_ostream_printf(out, "unload: too few arguments\n");
645     return -1;
646   }
647   if (!fluid_is_number(av[0])) {
648     fluid_ostream_printf(out, "unload: expected a number as argument\n");
649     return -1;
650   }
651   if (ac == 2) {
652     reset = atoi(av[1]);
653   }
654   if (fluid_synth_sfunload(synth, atoi(av[0]), reset) != 0) {
655     fluid_ostream_printf(out, "failed to unload the SoundFont\n");
656     return -1;
657   }
658   return 0;
659 }
660 
661 int
fluid_handle_reload(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)662 fluid_handle_reload(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
663 {
664   if (ac < 1) {
665     fluid_ostream_printf(out, "reload: too few arguments\n");
666     return -1;
667   }
668   if (!fluid_is_number(av[0])) {
669     fluid_ostream_printf(out, "reload: expected a number as argument\n");
670     return -1;
671   }
672   if (fluid_synth_sfreload(synth, atoi(av[0])) == -1) {
673     fluid_ostream_printf(out, "failed to reload the SoundFont\n");
674     return -1;
675   }
676   return 0;
677 }
678 
679 
680 int
fluid_handle_fonts(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)681 fluid_handle_fonts(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
682 {
683   int i;
684   fluid_sfont_t* sfont;
685   int num;
686 
687   num = fluid_synth_sfcount(synth);
688 
689   if (num == 0) {
690     fluid_ostream_printf(out, "no SoundFont loaded (try load)\n");
691     return 0;
692   }
693 
694   fluid_ostream_printf(out, "ID  Name\n");
695 
696   for (i = 0; i < num; i++) {
697     sfont = fluid_synth_get_sfont(synth, i);
698     if (sfont) {
699       fluid_ostream_printf(out, "%2d  %s\n",
700                        fluid_sfont_get_id(sfont),
701                        fluid_sfont_get_name(sfont));
702     }
703     else {
704       fluid_ostream_printf(out, "sfont is \"NULL\" for index %d\n", i);
705     }
706   }
707 
708   return 0;
709 }
710 
711 int
fluid_handle_mstat(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)712 fluid_handle_mstat(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
713 {
714 /*    fluid_ostream_printf(out, "Dvr=%s, Dev=%s\n",  */
715 /*  	 fluid_midi_handler_get_driver_name(midi), */
716 /*  	 fluid_midi_handler_get_device_name(midi)); */
717 /*    fluid_ostream_printf(out, "Stat=%s, On=%d, Off=%d, Prog=%d, Pbend=%d, Err=%d\n",  */
718 /*  	 fluid_midi_handler_get_status(midi), */
719 /*  	 fluid_midi_handler_get_event_count(midi, 0x90), */
720 /*  	 fluid_midi_handler_get_event_count(midi, 0x80), */
721 /*  	 fluid_midi_handler_get_event_count(midi, 0xc0), */
722 /*  	 fluid_midi_handler_get_event_count(midi, 0xe0), */
723 /*  	 fluid_midi_handler_get_event_count(midi, 0)); */
724   fluid_ostream_printf(out, "not yet implemented\n");
725   return -1;
726 }
727 
728 /* Purpose:
729  * Response to 'rev_preset' command.
730  * Load the values from a reverb preset into the reverb unit. */
731 int
fluid_handle_reverbpreset(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)732 fluid_handle_reverbpreset(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
733 {
734   int reverb_preset_number;
735   if (ac < 1) {
736     fluid_ostream_printf(out, "rev_preset: too few arguments\n");
737     return -1;
738   }
739   reverb_preset_number = atoi(av[0]);
740   if (fluid_synth_set_reverb_preset(synth, reverb_preset_number)!=FLUID_OK){
741     fluid_ostream_printf(out, "rev_preset: Failed. Parameter out of range?\n");
742     return -1;
743   };
744   return 0;
745 }
746 
747 /* Purpose:
748  * Response to 'rev_setroomsize' command.
749  * Load the new room size into the reverb unit. */
750 int
fluid_handle_reverbsetroomsize(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)751 fluid_handle_reverbsetroomsize(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
752 {
753   fluid_real_t room_size;
754   if (ac < 1) {
755     fluid_ostream_printf(out, "rev_setroomsize: too few arguments.\n");
756     return -1;
757   }
758   room_size = atof(av[0]);
759   if (room_size < 0){
760     fluid_ostream_printf(out, "rev_setroomsize: Room size must be positive!\n");
761     return -1;
762   }
763   if (room_size > 1.2){
764     fluid_ostream_printf(out, "rev_setroomsize: Room size too big!\n");
765     return -1;
766   }
767   fluid_synth_set_reverb_full (synth, FLUID_REVMODEL_SET_ROOMSIZE,
768                                room_size, 0.0, 0.0, 0.0);
769   return 0;
770 }
771 
772 /* Purpose:
773  * Response to 'rev_setdamp' command.
774  * Load the new damp factor into the reverb unit. */
775 int
fluid_handle_reverbsetdamp(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)776 fluid_handle_reverbsetdamp(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
777 {
778   fluid_real_t damp;
779   if (ac < 1) {
780     fluid_ostream_printf(out, "rev_setdamp: too few arguments.\n");
781     return -1;
782   }
783   damp = atof(av[0]);
784   if ((damp < 0.0f) || (damp > 1)){
785     fluid_ostream_printf(out, "rev_setdamp: damp must be between 0 and 1!\n");
786     return -1;
787   }
788   fluid_synth_set_reverb_full (synth, FLUID_REVMODEL_SET_DAMPING,
789                                0.0, damp, 0.0, 0.0);
790   return 0;
791 }
792 
793 /* Purpose:
794  * Response to 'rev_setwidth' command.
795  * Load the new width into the reverb unit. */
796 int
fluid_handle_reverbsetwidth(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)797 fluid_handle_reverbsetwidth(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
798 {
799   fluid_real_t width;
800   if (ac < 1) {
801     fluid_ostream_printf(out, "rev_setwidth: too few arguments.\n");
802     return -1;
803   }
804   width = atof(av[0]);
805   if ((width < 0) || (width > 100)){
806     fluid_ostream_printf(out, "rev_setroomsize: Too wide! (0..100)\n");
807     return 0;
808   }
809   fluid_synth_set_reverb_full (synth, FLUID_REVMODEL_SET_WIDTH,
810                                0.0, 0.0, width, 0.0);
811   return 0;
812 }
813 
814 /* Purpose:
815  * Response to 'rev_setlevel' command.
816  * Load the new level into the reverb unit. */
817 int
fluid_handle_reverbsetlevel(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)818 fluid_handle_reverbsetlevel(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
819 {
820   fluid_real_t level;
821   if (ac < 1) {
822     fluid_ostream_printf(out, "rev_setlevel: too few arguments.\n");
823     return -1;
824   }
825   level = atof(av[0]);
826   if (abs(level) > 30){
827     fluid_ostream_printf(out, "rev_setlevel: Value too high! (Value of 10 =+20 dB)\n");
828     return 0;
829   }
830   fluid_synth_set_reverb_full (synth, FLUID_REVMODEL_SET_LEVEL,
831                                0.0, 0.0, 0.0, level);
832   return 0;
833 }
834 
835 /* Purpose:
836  * Response to 'reverb' command.
837  * Change the FLUID_REVERB flag in the synth */
838 int
fluid_handle_reverb(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)839 fluid_handle_reverb(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
840 {
841   if (ac < 1) {
842     fluid_ostream_printf(out, "reverb: too few arguments.\n");
843     return -1;
844   }
845 
846   if ((strcmp(av[0], "0") == 0) || (strcmp(av[0], "off") == 0)) {
847     fluid_synth_set_reverb_on(synth,0);
848   } else if ((strcmp(av[0], "1") == 0) || (strcmp(av[0], "on") == 0)) {
849     fluid_synth_set_reverb_on(synth,1);
850   } else {
851     fluid_ostream_printf(out, "reverb: invalid arguments %s [0|1|on|off]", av[0]);
852     return -1;
853   }
854 
855   return 0;
856 }
857 
858 
859 /* Purpose:
860  * Response to 'chorus_setnr' command */
861 int
fluid_handle_chorusnr(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)862 fluid_handle_chorusnr(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
863 {
864   int nr;
865   if (ac < 1) {
866     fluid_ostream_printf(out, "cho_set_nr: too few arguments.\n");
867     return -1;
868   }
869   nr = atoi(av[0]);
870   return fluid_synth_set_chorus_full (synth, FLUID_CHORUS_SET_NR, nr, 0.0, 0.0, 0.0, 0);
871 }
872 
873 /* Purpose:
874  * Response to 'chorus_setlevel' command */
875 int
fluid_handle_choruslevel(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)876 fluid_handle_choruslevel(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
877 {
878   fluid_real_t level;
879   if (ac < 1) {
880     fluid_ostream_printf(out, "cho_set_level: too few arguments.\n");
881     return -1;
882   }
883   level = atof(av[0]);
884   return fluid_synth_set_chorus_full (synth, FLUID_CHORUS_SET_LEVEL, 0, level, 0.0, 0.0, 0);
885 }
886 
887 /* Purpose:
888  * Response to 'chorus_setspeed' command */
889 int
fluid_handle_chorusspeed(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)890 fluid_handle_chorusspeed(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
891 {
892   fluid_real_t speed;
893   if (ac < 1) {
894     fluid_ostream_printf(out, "cho_set_speed: too few arguments.\n");
895     return -1;
896   }
897   speed = atof(av[0]);
898   return fluid_synth_set_chorus_full (synth, FLUID_CHORUS_SET_SPEED, 0, 0.0, speed, 0.0, 0);
899 }
900 
901 /* Purpose:
902  * Response to 'chorus_setdepth' command */
903 int
fluid_handle_chorusdepth(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)904 fluid_handle_chorusdepth(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
905 {
906   fluid_real_t depth;
907   if (ac < 1) {
908     fluid_ostream_printf(out, "cho_set_depth: too few arguments.\n");
909     return -1;
910   }
911   depth = atof(av[0]);
912   return fluid_synth_set_chorus_full (synth, FLUID_CHORUS_SET_DEPTH, 0, 0.0, 0.0, depth, 0);
913 }
914 
915 int
fluid_handle_chorus(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)916 fluid_handle_chorus(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
917 {
918   if (ac < 1) {
919     fluid_ostream_printf(out, "chorus: too few arguments\n");
920     return -1;
921   }
922 
923   if ((strcmp(av[0], "0") == 0) || (strcmp(av[0], "off") == 0)) {
924     fluid_synth_set_chorus_on(synth,0);
925   } else if ((strcmp(av[0], "1") == 0) || (strcmp(av[0], "on") == 0)) {
926     fluid_synth_set_chorus_on(synth,1);
927   } else {
928     fluid_ostream_printf(out, "chorus: invalid arguments %s [0|1|on|off]", av[0]);
929     return -1;
930   }
931 
932   return 0;
933 }
934 
935 /* Purpose:
936  * Response to the 'echo' command.
937  * The command itself is useful, when the synth is used via TCP/IP.
938  * It can signal for example, that a list of commands has been processed.
939  */
940 int
fluid_handle_echo(fluid_cmd_handler_t * handler,int ac,char ** av,fluid_ostream_t out)941 fluid_handle_echo(fluid_cmd_handler_t* handler, int ac, char** av, fluid_ostream_t out)
942 {
943   if (ac < 1) {
944     fluid_ostream_printf(out, "echo: too few arguments.\n");
945     return -1;
946   }
947 
948   fluid_ostream_printf(out, "%s\n",av[0]);
949 
950   return 0;
951 }
952 
953 int
fluid_handle_source(fluid_cmd_handler_t * handler,int ac,char ** av,fluid_ostream_t out)954 fluid_handle_source(fluid_cmd_handler_t* handler, int ac, char** av, fluid_ostream_t out)
955 {
956   if (ac < 1) {
957     fluid_ostream_printf(out, "source: too few arguments.\n");
958     return -1;
959   }
960 
961   fluid_source(handler, av[0]);
962 
963   return 0;
964 }
965 
966 /* Purpose:
967  * Response to 'gain' command. */
968 int
fluid_handle_gain(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)969 fluid_handle_gain(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
970 {
971   float gain;
972 
973   if (ac < 1) {
974     fluid_ostream_printf(out, "gain: too few arguments.\n");
975     return -1;
976   }
977 
978   gain = atof(av[0]);
979 
980   if ((gain < 0.0f) || (gain > 5.0f)) {
981     fluid_ostream_printf(out, "gain: value should be between '0' and '5'.\n");
982     return -1;
983   };
984 
985   fluid_synth_set_gain(synth, gain);
986 
987   return 0;
988 }
989 
990 /* Response to voice_count command */
991 static int
fluid_handle_voice_count(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)992 fluid_handle_voice_count (fluid_synth_t *synth, int ac, char **av,
993                           fluid_ostream_t out)
994 {
995   fluid_ostream_printf (out, "voice_count: %d\n",
996                         fluid_synth_get_active_voice_count (synth));
997   return FLUID_OK;
998 }
999 
1000 /* Purpose:
1001  * Response to 'interp' command. */
1002 int
fluid_handle_interp(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1003 fluid_handle_interp(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1004 {
1005   int interp;
1006   int chan=-1; /* -1: Set all channels */
1007 
1008   if (ac < 1) {
1009     fluid_ostream_printf(out, "interp: too few arguments.\n");
1010     return -1;
1011   }
1012 
1013   interp = atoi(av[0]);
1014 
1015   if ((interp < 0) || (interp > FLUID_INTERP_HIGHEST)) {
1016     fluid_ostream_printf(out, "interp: Bad value\n");
1017     return -1;
1018   };
1019 
1020   fluid_synth_set_interp_method(synth, chan, interp);
1021 
1022   return 0;
1023 }
1024 
1025 /* Purpose:
1026  * Response to 'interp' command. */
1027 int
fluid_handle_interpc(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1028 fluid_handle_interpc(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1029 {
1030   int interp;
1031   int chan;
1032 
1033   if (ac < 2) {
1034     fluid_ostream_printf(out, "interpc: too few arguments.\n");
1035     return -1;
1036   }
1037 
1038   chan = atoi(av[0]);
1039   interp = atoi(av[1]);
1040 
1041   if ((chan < 0) || (chan >= fluid_synth_count_midi_channels(synth))){
1042     fluid_ostream_printf(out, "interp: Bad value for channel number.\n");
1043     return -1;
1044   };
1045   if ((interp < 0) || (interp > FLUID_INTERP_HIGHEST)) {
1046     fluid_ostream_printf(out, "interp: Bad value for interpolation method.\n");
1047     return -1;
1048   };
1049 
1050   fluid_synth_set_interp_method(synth, chan, interp);
1051 
1052   return 0;
1053 }
1054 
1055 int
fluid_handle_tuning(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1056 fluid_handle_tuning(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1057 {
1058   char *name;
1059   int bank, prog;
1060 
1061   if (ac < 3) {
1062     fluid_ostream_printf(out, "tuning: too few arguments.\n");
1063     return -1;
1064   }
1065 
1066   name = av[0];
1067 
1068   if (!fluid_is_number(av[1])) {
1069     fluid_ostream_printf(out, "tuning: 2nd argument should be a number.\n");
1070     return -1;
1071   }
1072   bank = atoi(av[1]);
1073   if ((bank < 0) || (bank >= 128)){
1074     fluid_ostream_printf(out, "tuning: invalid bank number.\n");
1075     return -1;
1076   };
1077 
1078   if (!fluid_is_number(av[2])) {
1079     fluid_ostream_printf(out, "tuning: 3rd argument should be a number.\n");
1080     return -1;
1081   }
1082   prog = atoi(av[2]);
1083   if ((prog < 0) || (prog >= 128)){
1084     fluid_ostream_printf(out, "tuning: invalid program number.\n");
1085     return -1;
1086   };
1087 
1088   fluid_synth_create_key_tuning(synth, bank, prog, name, NULL);
1089 
1090   return 0;
1091 }
1092 
1093 int
fluid_handle_tune(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1094 fluid_handle_tune(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1095 {
1096   int bank, prog, key;
1097   double pitch;
1098 
1099   if (ac < 4) {
1100     fluid_ostream_printf(out, "tune: too few arguments.\n");
1101     return -1;
1102   }
1103 
1104   if (!fluid_is_number(av[0])) {
1105     fluid_ostream_printf(out, "tune: 1st argument should be a number.\n");
1106     return -1;
1107   }
1108   bank = atoi(av[0]);
1109   if ((bank < 0) || (bank >= 128)){
1110     fluid_ostream_printf(out, "tune: invalid bank number.\n");
1111     return -1;
1112   };
1113 
1114   if (!fluid_is_number(av[1])) {
1115     fluid_ostream_printf(out, "tune: 2nd argument should be a number.\n");
1116     return -1;
1117   }
1118   prog = atoi(av[1]);
1119   if ((prog < 0) || (prog >= 128)){
1120     fluid_ostream_printf(out, "tune: invalid program number.\n");
1121     return -1;
1122   };
1123 
1124   if (!fluid_is_number(av[2])) {
1125     fluid_ostream_printf(out, "tune: 3rd argument should be a number.\n");
1126     return -1;
1127   }
1128   key = atoi(av[2]);
1129   if ((key < 0) || (key >= 128)){
1130     fluid_ostream_printf(out, "tune: invalid key number.\n");
1131     return -1;
1132   };
1133 
1134   pitch = atof(av[3]);
1135   if (pitch < 0.0f) {
1136     fluid_ostream_printf(out, "tune: invalid pitch.\n");
1137     return -1;
1138   };
1139 
1140   fluid_synth_tune_notes(synth, bank, prog, 1, &key, &pitch, 0);
1141 
1142   return 0;
1143 }
1144 
1145 int
fluid_handle_settuning(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1146 fluid_handle_settuning(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1147 {
1148   int chan, bank, prog;
1149 
1150   if (ac < 3) {
1151     fluid_ostream_printf(out, "settuning: too few arguments.\n");
1152     return -1;
1153   }
1154 
1155   if (!fluid_is_number(av[0])) {
1156     fluid_ostream_printf(out, "tune: 1st argument should be a number.\n");
1157     return -1;
1158   }
1159   chan = atoi(av[0]);
1160   if ((chan < 0) || (chan >= fluid_synth_count_midi_channels(synth))){
1161     fluid_ostream_printf(out, "tune: invalid channel number.\n");
1162     return -1;
1163   };
1164 
1165   if (!fluid_is_number(av[1])) {
1166     fluid_ostream_printf(out, "tuning: 2nd argument should be a number.\n");
1167     return -1;
1168   }
1169   bank = atoi(av[1]);
1170   if ((bank < 0) || (bank >= 128)){
1171     fluid_ostream_printf(out, "tuning: invalid bank number.\n");
1172     return -1;
1173   };
1174 
1175   if (!fluid_is_number(av[2])) {
1176     fluid_ostream_printf(out, "tuning: 3rd argument should be a number.\n");
1177     return -1;
1178   }
1179   prog = atoi(av[2]);
1180   if ((prog < 0) || (prog >= 128)){
1181     fluid_ostream_printf(out, "tuning: invalid program number.\n");
1182     return -1;
1183   };
1184 
1185   fluid_synth_select_tuning(synth, chan, bank, prog);
1186 
1187   return 0;
1188 }
1189 
1190 int
fluid_handle_resettuning(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1191 fluid_handle_resettuning(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1192 {
1193   int chan;
1194 
1195   if (ac < 1) {
1196     fluid_ostream_printf(out, "resettuning: too few arguments.\n");
1197     return -1;
1198   }
1199 
1200   if (!fluid_is_number(av[0])) {
1201     fluid_ostream_printf(out, "tune: 1st argument should be a number.\n");
1202     return -1;
1203   }
1204   chan = atoi(av[0]);
1205   if ((chan < 0) || (chan >= fluid_synth_count_midi_channels(synth))){
1206     fluid_ostream_printf(out, "tune: invalid channel number.\n");
1207     return -1;
1208   };
1209 
1210   fluid_synth_reset_tuning(synth, chan);
1211 
1212   return 0;
1213 }
1214 
1215 int
fluid_handle_tunings(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1216 fluid_handle_tunings(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1217 {
1218   int bank, prog;
1219   char name[256];
1220   int count = 0;
1221 
1222   fluid_synth_tuning_iteration_start(synth);
1223 
1224   while (fluid_synth_tuning_iteration_next(synth, &bank, &prog)) {
1225     fluid_synth_tuning_dump(synth, bank, prog, name, 256, NULL);
1226     fluid_ostream_printf(out, "%03d-%03d %s\n", bank, prog, name);
1227     count++;
1228   }
1229 
1230   if (count == 0) {
1231     fluid_ostream_printf(out, "No tunings available\n");
1232   }
1233 
1234   return 0;
1235 }
1236 
1237 int
fluid_handle_dumptuning(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1238 fluid_handle_dumptuning(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1239 {
1240   int bank, prog, i;
1241   double pitch[128];
1242   char name[256];
1243 
1244   if (ac < 2) {
1245     fluid_ostream_printf(out, "dumptuning: too few arguments.\n");
1246     return -1;
1247   }
1248 
1249   if (!fluid_is_number(av[0])) {
1250     fluid_ostream_printf(out, "dumptuning: 1st argument should be a number.\n");
1251     return -1;
1252   }
1253   bank = atoi(av[0]);
1254   if ((bank < 0) || (bank >= 128)){
1255     fluid_ostream_printf(out, "dumptuning: invalid bank number.\n");
1256     return -1;
1257   };
1258 
1259   if (!fluid_is_number(av[1])) {
1260     fluid_ostream_printf(out, "dumptuning: 2nd argument should be a number.\n");
1261     return -1;
1262   }
1263   prog = atoi(av[1]);
1264   if ((prog < 0) || (prog >= 128)){
1265     fluid_ostream_printf(out, "dumptuning: invalid program number.\n");
1266     return -1;
1267   };
1268 
1269   fluid_synth_tuning_dump(synth, bank, prog, name, 256, pitch);
1270 
1271   fluid_ostream_printf(out, "%03d-%03d %s:\n", bank, prog, name);
1272 
1273   for (i = 0; i < 128; i++) {
1274     fluid_ostream_printf(out, "key %03d, pitch %5.2f\n", i, pitch[i]);
1275   }
1276 
1277   return 0;
1278 }
1279 
1280 int
fluid_handle_set(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1281 fluid_handle_set(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1282 {
1283   int hints;
1284   int ival;
1285 
1286   if (ac < 2) {
1287     fluid_ostream_printf(out, "set: Too few arguments.\n");
1288     return -1;
1289   }
1290 
1291   switch (fluid_settings_get_type (synth->settings, av[0]))
1292   {
1293     case FLUID_NO_TYPE:
1294       fluid_ostream_printf (out, "set: Parameter '%s' not found.\n", av[0]);
1295       break;
1296     case FLUID_INT_TYPE:
1297       hints = fluid_settings_get_hints (synth->settings, av[0]);
1298 
1299       if (hints & FLUID_HINT_TOGGLED)
1300       {
1301         if (FLUID_STRCMP (av[1], "yes") == 0 || FLUID_STRCMP (av[1], "True") == 0
1302             || FLUID_STRCMP (av[1], "TRUE") == 0 || FLUID_STRCMP (av[1], "true") == 0
1303             || FLUID_STRCMP (av[1], "T") == 0)
1304           ival = 1;
1305         else ival = atoi (av[1]);
1306       }
1307       else ival = atoi (av[1]);
1308 
1309       fluid_synth_setint (synth, av[0], ival);
1310       break;
1311     case FLUID_NUM_TYPE:
1312       fluid_synth_setnum (synth, av[0], atof (av[1]));
1313       break;
1314     case FLUID_STR_TYPE:
1315       fluid_synth_setstr(synth, av[0], av[1]);
1316       break;
1317     case FLUID_SET_TYPE:
1318       fluid_ostream_printf (out, "set: Parameter '%s' is a node.\n", av[0]);
1319       break;
1320   }
1321 
1322   return 0;
1323 }
1324 
1325 int
fluid_handle_get(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1326 fluid_handle_get(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1327 {
1328   if (ac < 1) {
1329     fluid_ostream_printf(out, "get: too few arguments.\n");
1330     return -1;
1331   }
1332 
1333   switch (fluid_settings_get_type(fluid_synth_get_settings(synth), av[0])) {
1334   case FLUID_NO_TYPE:
1335     fluid_ostream_printf(out, "get: no such setting '%s'.\n", av[0]);
1336     return -1;
1337 
1338   case FLUID_NUM_TYPE: {
1339     double value;
1340     fluid_synth_getnum(synth, av[0], &value);
1341     fluid_ostream_printf(out, "%.3f", value);
1342     break;
1343   }
1344 
1345   case FLUID_INT_TYPE: {
1346     int value;
1347     fluid_synth_getint(synth, av[0], &value);
1348     fluid_ostream_printf(out, "%d", value);
1349     break;
1350   }
1351 
1352   case FLUID_STR_TYPE: {
1353     char* s;
1354     fluid_synth_dupstr(synth, av[0], &s);       /* ++ alloc string */
1355     fluid_ostream_printf(out, "%s", s ? s : "NULL");
1356     if (s) FLUID_FREE (s);      /* -- free string */
1357     break;
1358   }
1359 
1360   case FLUID_SET_TYPE:
1361     fluid_ostream_printf(out, "%s is a node", av[0]);
1362     break;
1363   }
1364 
1365   return 0;
1366 }
1367 
1368 struct _fluid_handle_settings_data_t {
1369   int len;
1370   fluid_synth_t* synth;
1371   fluid_ostream_t out;
1372 };
1373 
fluid_handle_settings_iter1(void * data,char * name,int type)1374 static void fluid_handle_settings_iter1(void* data, char* name, int type)
1375 {
1376   struct _fluid_handle_settings_data_t* d = (struct _fluid_handle_settings_data_t*) data;
1377 
1378   int len = FLUID_STRLEN(name);
1379   if (len > d->len) {
1380     d->len = len;
1381   }
1382 }
1383 
fluid_handle_settings_iter2(void * data,char * name,int type)1384 static void fluid_handle_settings_iter2(void* data, char* name, int type)
1385 {
1386   struct _fluid_handle_settings_data_t* d = (struct _fluid_handle_settings_data_t*) data;
1387 
1388   int len = FLUID_STRLEN(name);
1389   fluid_ostream_printf(d->out, "%s", name);
1390   while (len++ < d->len) {
1391     fluid_ostream_printf(d->out, " ");
1392   }
1393   fluid_ostream_printf(d->out, "   ");
1394 
1395   switch (fluid_settings_get_type(fluid_synth_get_settings(d->synth), name)) {
1396   case FLUID_NUM_TYPE: {
1397     double value;
1398     fluid_synth_getnum(d->synth, name, &value);
1399     fluid_ostream_printf(d->out, "%.3f\n", value);
1400     break;
1401   }
1402 
1403   case FLUID_INT_TYPE: {
1404     int value, hints;
1405     fluid_synth_getint(d->synth, name, &value);
1406     hints = fluid_settings_get_hints (d->synth->settings, name);
1407 
1408     if (!(hints & FLUID_HINT_TOGGLED))
1409       fluid_ostream_printf(d->out, "%d\n", value);
1410     else fluid_ostream_printf(d->out, "%s\n", value ? "True" : "False");
1411     break;
1412   }
1413 
1414   case FLUID_STR_TYPE: {
1415     char* s;
1416     fluid_synth_dupstr(d->synth, name, &s);     /* ++ alloc string */
1417     fluid_ostream_printf(d->out, "%s\n", s ? s : "NULL");
1418     if (s) FLUID_FREE (s);      /* -- free string */
1419     break;
1420   }
1421   }
1422 }
1423 
1424 int
fluid_handle_settings(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1425 fluid_handle_settings(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1426 {
1427   struct _fluid_handle_settings_data_t data;
1428 
1429   data.len = 0;
1430   data.synth = synth;
1431   data.out = out;
1432 
1433   fluid_settings_foreach(fluid_synth_get_settings(synth), &data, fluid_handle_settings_iter1);
1434   fluid_settings_foreach(fluid_synth_get_settings(synth), &data, fluid_handle_settings_iter2);
1435   return 0;
1436 }
1437 
1438 
1439 struct _fluid_handle_option_data_t {
1440   int first;
1441   fluid_ostream_t out;
1442 };
1443 
fluid_handle_print_option(void * data,char * name,char * option)1444 void fluid_handle_print_option(void* data, char* name, char* option)
1445 {
1446   struct _fluid_handle_option_data_t* d = (struct _fluid_handle_option_data_t*) data;
1447 
1448   if (d->first) {
1449     fluid_ostream_printf(d->out, "%s", option);
1450     d->first = 0;
1451   } else {
1452     fluid_ostream_printf(d->out, ", %s", option);
1453   }
1454 }
1455 
1456 int
fluid_handle_info(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1457 fluid_handle_info(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1458 {
1459   fluid_settings_t* settings = fluid_synth_get_settings(synth);
1460   struct _fluid_handle_option_data_t data;
1461 
1462   if (ac < 1) {
1463     fluid_ostream_printf(out, "info: too few arguments.\n");
1464     return -1;
1465   }
1466 
1467   switch (fluid_settings_get_type(settings, av[0])) {
1468   case FLUID_NO_TYPE:
1469     fluid_ostream_printf(out, "info: no such setting '%s'.\n", av[0]);
1470     return -1;
1471 
1472   case FLUID_NUM_TYPE: {
1473     double value, min, max;
1474     fluid_settings_getnum_range(settings, av[0], &min, &max);
1475     fluid_settings_getnum(settings, av[0], &value);
1476     fluid_ostream_printf(out, "%s:\n", av[0]);
1477     fluid_ostream_printf(out, "Type:          number\n");
1478     fluid_ostream_printf(out, "Value:         %.3f\n", value);
1479     fluid_ostream_printf(out, "Minimum value: %.3f\n", min);
1480     fluid_ostream_printf(out, "Maximum value: %.3f\n", max);
1481     fluid_ostream_printf(out, "Default value: %.3f\n",
1482 			fluid_settings_getnum_default(settings, av[0]));
1483     fluid_ostream_printf(out, "Real-time:     %s\n",
1484 			fluid_settings_is_realtime(settings, av[0])? "yes" : "no");
1485     break;
1486   }
1487 
1488   case FLUID_INT_TYPE: {
1489     int value, min, max, def, hints;
1490 
1491     fluid_settings_getint_range(settings, av[0], &min, &max);
1492     fluid_settings_getint(settings, av[0], &value);
1493     hints = fluid_settings_get_hints(settings, av[0]);
1494     def = fluid_settings_getint_default (settings, av[0]);
1495 
1496     fluid_ostream_printf(out, "%s:\n", av[0]);
1497 
1498     if (!(hints & FLUID_HINT_TOGGLED))
1499     {
1500       fluid_ostream_printf(out, "Type:          integer\n");
1501       fluid_ostream_printf(out, "Value:         %d\n", value);
1502       fluid_ostream_printf(out, "Minimum value: %d\n", min);
1503       fluid_ostream_printf(out, "Maximum value: %d\n", max);
1504       fluid_ostream_printf(out, "Default value: %d\n", def);
1505     }
1506     else
1507     {
1508       fluid_ostream_printf(out, "Type:          boolean\n");
1509       fluid_ostream_printf(out, "Value:         %s\n", value ? "True" : "False");
1510       fluid_ostream_printf(out, "Default value: %s\n", def ? "True" : "False");
1511     }
1512 
1513     fluid_ostream_printf(out, "Real-time:     %s\n",
1514 			fluid_settings_is_realtime(settings, av[0])? "yes" : "no");
1515     break;
1516   }
1517 
1518   case FLUID_STR_TYPE: {
1519     char *s;
1520     fluid_settings_dupstr(settings, av[0], &s);         /* ++ alloc string */
1521     fluid_ostream_printf(out, "%s:\n", av[0]);
1522     fluid_ostream_printf(out, "Type:          string\n");
1523     fluid_ostream_printf(out, "Value:         %s\n", s ? s : "NULL");
1524     fluid_ostream_printf(out, "Default value: %s\n",
1525 			fluid_settings_getstr_default(settings, av[0]));
1526 
1527     if (s) FLUID_FREE (s);
1528 
1529     data.out = out;
1530     data.first = 1;
1531     fluid_ostream_printf(out, "Options:       ");
1532     fluid_settings_foreach_option (settings, av[0], &data, fluid_handle_print_option);
1533     fluid_ostream_printf(out, "\n");
1534 
1535     fluid_ostream_printf(out, "Real-time:     %s\n",
1536 			fluid_settings_is_realtime(settings, av[0])? "yes" : "no");
1537     break;
1538   }
1539 
1540   case FLUID_SET_TYPE:
1541     fluid_ostream_printf(out, "%s:\n", av[0]);
1542     fluid_ostream_printf(out, "Type:          node\n");
1543     break;
1544   }
1545 
1546   return 0;
1547 }
1548 
1549 int
fluid_handle_reset(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1550 fluid_handle_reset(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1551 {
1552   fluid_synth_system_reset(synth);
1553   return 0;
1554 }
1555 
1556 int
fluid_handle_quit(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1557 fluid_handle_quit(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1558 {
1559   fluid_ostream_printf(out, "cheers!\n");
1560   return -2;
1561 }
1562 
1563 int
fluid_handle_help(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1564 fluid_handle_help(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1565 {
1566   /* Purpose:
1567    * Prints the help text for the command line commands.
1568    * Can be used as follows:
1569    * - help
1570    * - help (topic), where (topic) is 'general', 'chorus', etc.
1571    * - help all
1572    */
1573 
1574   char* topic = "help"; /* default, if no topic is given */
1575   int count = 0;
1576   int i;
1577 
1578   fluid_ostream_printf(out, "\n");
1579   /* 1st argument (optional): help topic */
1580   if (ac >= 1) {
1581     topic = av[0];
1582   }
1583   if (strcmp(topic,"help") == 0){
1584     /* "help help": Print a list of all topics */
1585     fluid_ostream_printf(out,
1586 			"*** Help topics:***\n"
1587 			"help all (prints all topics)\n");
1588     for (i = 0; fluid_commands[i].name != NULL; i++) {
1589       int listed_first_time = 1;
1590       int ii;
1591       for (ii = 0; ii < i; ii++){
1592 	if (strcmp(fluid_commands[i].topic, fluid_commands[ii].topic) == 0){
1593 	  listed_first_time = 0;
1594 	}; /* if topic has already been listed */
1595       }; /* for all topics (inner loop) */
1596       if (listed_first_time){
1597 	fluid_ostream_printf(out, "help %s\n",fluid_commands[i].topic);
1598       };
1599     }; /* for all topics (outer loop) */
1600   } else {
1601     /* help (arbitrary topic or "all") */
1602     for (i = 0; fluid_commands[i].name != NULL; i++) {
1603       fluid_cmd_t cmd = fluid_commands[i];
1604       if (cmd.help != NULL) {
1605 	if (strcmp(topic,"all") == 0 || strcmp(topic,cmd.topic) == 0){
1606 	  fluid_ostream_printf(out, "%s\n", fluid_commands[i].help);
1607 	  count++;
1608 	}; /* if it matches the topic */
1609       }; /* if help text exists */
1610     }; /* foreach command */
1611     if (count == 0){
1612       fluid_ostream_printf(out, "Unknown help topic. Try 'help help'.\n");
1613     };
1614   };
1615   return 0;
1616 }
1617 
1618 int
fluid_is_number(char * a)1619 fluid_is_number(char* a)
1620 {
1621   while (*a != 0) {
1622     if (((*a < '0') || (*a > '9')) && (*a != '-') && (*a != '+') && (*a != '.')) {
1623       return 0;
1624     }
1625     a++;
1626   }
1627   return 1;
1628 }
1629 
1630 int
fluid_is_empty(char * a)1631 fluid_is_empty(char* a)
1632 {
1633   while (*a != 0) {
1634     if ((*a != ' ') && (*a != '\t') && (*a != '\n') && (*a != '\r')) {
1635       return 0;
1636     }
1637     a++;
1638   }
1639   return 1;
1640 }
1641 
1642 char*
fluid_expand_path(char * path,char * new_path,int len)1643 fluid_expand_path(char* path, char* new_path, int len)
1644 {
1645 #if defined(WIN32) || defined(MACOS9)
1646   snprintf(new_path, len - 1, "%s", path);
1647 #else
1648   if ((path[0] == '~') && (path[1] == '/')) {
1649     char* home = getenv("HOME");
1650     if (home == NULL) {
1651       snprintf(new_path, len - 1, "%s", path);
1652     } else {
1653       snprintf(new_path, len - 1, "%s%s", home, &path[1]);
1654     }
1655   } else {
1656     snprintf(new_path, len - 1, "%s", path);
1657   }
1658 #endif
1659 
1660   new_path[len - 1] = 0;
1661   return new_path;
1662 }
1663 
1664 
1665 
1666 /*
1667  * Command
1668  */
1669 
fluid_cmd_copy(fluid_cmd_t * cmd)1670 fluid_cmd_t* fluid_cmd_copy(fluid_cmd_t* cmd)
1671 {
1672   fluid_cmd_t* copy = FLUID_NEW(fluid_cmd_t);
1673   if (copy == NULL) {
1674     FLUID_LOG (FLUID_PANIC, "Out of memory");
1675     return NULL;
1676   }
1677 
1678   copy->name = FLUID_STRDUP(cmd->name);
1679   copy->topic = FLUID_STRDUP(cmd->topic);
1680   copy->help = FLUID_STRDUP(cmd->help);
1681   copy->handler = cmd->handler;
1682   copy->data = cmd->data;
1683   return copy;
1684 }
1685 
delete_fluid_cmd(fluid_cmd_t * cmd)1686 void delete_fluid_cmd(fluid_cmd_t* cmd)
1687 {
1688   if (cmd->name) {
1689     FLUID_FREE(cmd->name);
1690   }
1691   if (cmd->topic) {
1692     FLUID_FREE(cmd->topic);
1693   }
1694   if (cmd->help) {
1695     FLUID_FREE(cmd->help);
1696   }
1697   FLUID_FREE(cmd);
1698 }
1699 
1700 /*
1701  * Command handler
1702  */
1703 
1704 static void
fluid_cmd_handler_destroy_hash_value(void * value)1705 fluid_cmd_handler_destroy_hash_value (void *value)
1706 {
1707   delete_fluid_cmd ((fluid_cmd_t *)value);
1708 }
1709 
1710 /**
1711  * Create a new command handler.
1712  * @param synth If not NULL, all the default synthesizer commands will be
1713  *   added to the new handler.
1714  * @return New command handler
1715  */
1716 fluid_cmd_handler_t *
new_fluid_cmd_handler(fluid_synth_t * synth)1717 new_fluid_cmd_handler(fluid_synth_t* synth)
1718 {
1719   int i;
1720   fluid_cmd_handler_t* handler;
1721 
1722   fluid_cmd_t source = {
1723     "source", "general", (fluid_cmd_func_t) fluid_handle_source, NULL,
1724     "source filename            Load a file and parse every line as a command"
1725   };
1726 
1727   handler = new_fluid_hashtable_full (fluid_str_hash, fluid_str_equal,
1728                                       NULL, fluid_cmd_handler_destroy_hash_value);
1729   if (handler == NULL) {
1730     return NULL;
1731   }
1732 
1733   if (synth != NULL) {
1734     for (i = 0; fluid_commands[i].name != NULL; i++) {
1735       fluid_commands[i].data = synth;
1736       fluid_cmd_handler_register(handler, &fluid_commands[i]);
1737       fluid_commands[i].data = NULL;
1738     }
1739   }
1740 
1741   source.data = handler;
1742   fluid_cmd_handler_register(handler, &source);
1743 
1744   return handler;
1745 }
1746 
1747 /**
1748  * Delete a command handler.
1749  * @param handler Command handler to delete
1750  */
1751 void
delete_fluid_cmd_handler(fluid_cmd_handler_t * handler)1752 delete_fluid_cmd_handler(fluid_cmd_handler_t* handler)
1753 {
1754   delete_fluid_hashtable (handler);
1755 }
1756 
1757 /**
1758  * Register a new command to the handler.
1759  * @param handler Command handler instance
1760  * @param cmd Command info (gets copied)
1761  * @return #FLUID_OK if command was inserted, #FLUID_FAILED otherwise
1762  */
1763 int
fluid_cmd_handler_register(fluid_cmd_handler_t * handler,fluid_cmd_t * cmd)1764 fluid_cmd_handler_register(fluid_cmd_handler_t* handler, fluid_cmd_t* cmd)
1765 {
1766   fluid_cmd_t* copy = fluid_cmd_copy(cmd);
1767   fluid_hashtable_insert(handler, copy->name, copy);
1768   return FLUID_OK;
1769 }
1770 
1771 /**
1772  * Unregister a command from a command handler.
1773  * @param handler Command handler instance
1774  * @param cmd Name of the command
1775  * @return TRUE if command was found and unregistered, FALSE otherwise
1776  */
1777 int
fluid_cmd_handler_unregister(fluid_cmd_handler_t * handler,const char * cmd)1778 fluid_cmd_handler_unregister(fluid_cmd_handler_t* handler, const char *cmd)
1779 {
1780   return fluid_hashtable_remove(handler, cmd);
1781 }
1782 
1783 int
fluid_cmd_handler_handle(fluid_cmd_handler_t * handler,int ac,char ** av,fluid_ostream_t out)1784 fluid_cmd_handler_handle(fluid_cmd_handler_t* handler, int ac, char** av, fluid_ostream_t out)
1785 {
1786   fluid_cmd_t* cmd;
1787 
1788   cmd = fluid_hashtable_lookup(handler, av[0]);
1789 
1790   if (cmd && cmd->handler)
1791     return (*cmd->handler)(cmd->data, ac - 1, av + 1, out);
1792 
1793   fluid_ostream_printf(out, "unknown command: %s (try help)\n", av[0]);
1794   return -1;
1795 }
1796