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((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     fluid_ostream_printf(out, "%2d  %s\n",
699 			fluid_sfont_get_id(sfont),
700 			fluid_sfont_get_name(sfont));
701   }
702 
703   return 0;
704 }
705 
706 int
fluid_handle_mstat(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)707 fluid_handle_mstat(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
708 {
709 /*    fluid_ostream_printf(out, "Dvr=%s, Dev=%s\n",  */
710 /*  	 fluid_midi_handler_get_driver_name(midi), */
711 /*  	 fluid_midi_handler_get_device_name(midi)); */
712 /*    fluid_ostream_printf(out, "Stat=%s, On=%d, Off=%d, Prog=%d, Pbend=%d, Err=%d\n",  */
713 /*  	 fluid_midi_handler_get_status(midi), */
714 /*  	 fluid_midi_handler_get_event_count(midi, 0x90), */
715 /*  	 fluid_midi_handler_get_event_count(midi, 0x80), */
716 /*  	 fluid_midi_handler_get_event_count(midi, 0xc0), */
717 /*  	 fluid_midi_handler_get_event_count(midi, 0xe0), */
718 /*  	 fluid_midi_handler_get_event_count(midi, 0)); */
719   fluid_ostream_printf(out, "not yet implemented\n");
720   return -1;
721 }
722 
723 /* Purpose:
724  * Response to 'rev_preset' command.
725  * Load the values from a reverb preset into the reverb unit. */
726 int
fluid_handle_reverbpreset(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)727 fluid_handle_reverbpreset(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
728 {
729   int reverb_preset_number;
730   if (ac < 1) {
731     fluid_ostream_printf(out, "rev_preset: too few arguments\n");
732     return -1;
733   }
734   reverb_preset_number = atoi(av[0]);
735   if (fluid_synth_set_reverb_preset(synth, reverb_preset_number)!=FLUID_OK){
736     fluid_ostream_printf(out, "rev_preset: Failed. Parameter out of range?\n");
737     return -1;
738   };
739   return 0;
740 }
741 
742 /* Purpose:
743  * Response to 'rev_setroomsize' command.
744  * Load the new room size into the reverb unit. */
745 int
fluid_handle_reverbsetroomsize(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)746 fluid_handle_reverbsetroomsize(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
747 {
748   fluid_real_t room_size;
749   if (ac < 1) {
750     fluid_ostream_printf(out, "rev_setroomsize: too few arguments.\n");
751     return -1;
752   }
753   room_size = atof(av[0]);
754   if (room_size < 0){
755     fluid_ostream_printf(out, "rev_setroomsize: Room size must be positive!\n");
756     return -1;
757   }
758   if (room_size > 1.2){
759     fluid_ostream_printf(out, "rev_setroomsize: Room size too big!\n");
760     return -1;
761   }
762   fluid_synth_set_reverb_full (synth, FLUID_REVMODEL_SET_ROOMSIZE,
763                                room_size, 0.0, 0.0, 0.0);
764   return 0;
765 }
766 
767 /* Purpose:
768  * Response to 'rev_setdamp' command.
769  * Load the new damp factor into the reverb unit. */
770 int
fluid_handle_reverbsetdamp(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)771 fluid_handle_reverbsetdamp(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
772 {
773   fluid_real_t damp;
774   if (ac < 1) {
775     fluid_ostream_printf(out, "rev_setdamp: too few arguments.\n");
776     return -1;
777   }
778   damp = atof(av[0]);
779   if ((damp < 0.0f) || (damp > 1)){
780     fluid_ostream_printf(out, "rev_setdamp: damp must be between 0 and 1!\n");
781     return -1;
782   }
783   fluid_synth_set_reverb_full (synth, FLUID_REVMODEL_SET_DAMPING,
784                                0.0, damp, 0.0, 0.0);
785   return 0;
786 }
787 
788 /* Purpose:
789  * Response to 'rev_setwidth' command.
790  * Load the new width into the reverb unit. */
791 int
fluid_handle_reverbsetwidth(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)792 fluid_handle_reverbsetwidth(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
793 {
794   fluid_real_t width;
795   if (ac < 1) {
796     fluid_ostream_printf(out, "rev_setwidth: too few arguments.\n");
797     return -1;
798   }
799   width = atof(av[0]);
800   if ((width < 0) || (width > 100)){
801     fluid_ostream_printf(out, "rev_setroomsize: Too wide! (0..100)\n");
802     return 0;
803   }
804   fluid_synth_set_reverb_full (synth, FLUID_REVMODEL_SET_WIDTH,
805                                0.0, 0.0, width, 0.0);
806   return 0;
807 }
808 
809 /* Purpose:
810  * Response to 'rev_setlevel' command.
811  * Load the new level into the reverb unit. */
812 int
fluid_handle_reverbsetlevel(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)813 fluid_handle_reverbsetlevel(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
814 {
815   fluid_real_t level;
816   if (ac < 1) {
817     fluid_ostream_printf(out, "rev_setlevel: too few arguments.\n");
818     return -1;
819   }
820   level = atof(av[0]);
821   if (fabsf(level) > 30){
822     fluid_ostream_printf(out, "rev_setlevel: Value too high! (Value of 10 =+20 dB)\n");
823     return 0;
824   }
825   fluid_synth_set_reverb_full (synth, FLUID_REVMODEL_SET_LEVEL,
826                                0.0, 0.0, 0.0, level);
827   return 0;
828 }
829 
830 /* Purpose:
831  * Response to 'reverb' command.
832  * Change the FLUID_REVERB flag in the synth */
833 int
fluid_handle_reverb(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)834 fluid_handle_reverb(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
835 {
836   if (ac < 1) {
837     fluid_ostream_printf(out, "reverb: too few arguments.\n");
838     return -1;
839   }
840 
841   if ((strcmp(av[0], "0") == 0) || (strcmp(av[0], "off") == 0)) {
842     fluid_synth_set_reverb_on(synth,0);
843   } else if ((strcmp(av[0], "1") == 0) || (strcmp(av[0], "on") == 0)) {
844     fluid_synth_set_reverb_on(synth,1);
845   } else {
846     fluid_ostream_printf(out, "reverb: invalid arguments %s [0|1|on|off]", av[0]);
847     return -1;
848   }
849 
850   return 0;
851 }
852 
853 
854 /* Purpose:
855  * Response to 'chorus_setnr' command */
856 int
fluid_handle_chorusnr(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)857 fluid_handle_chorusnr(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
858 {
859   int nr;
860   if (ac < 1) {
861     fluid_ostream_printf(out, "cho_set_nr: too few arguments.\n");
862     return -1;
863   }
864   nr = atoi(av[0]);
865   return fluid_synth_set_chorus_full (synth, FLUID_CHORUS_SET_NR, nr, 0.0, 0.0, 0.0, 0);
866 }
867 
868 /* Purpose:
869  * Response to 'chorus_setlevel' command */
870 int
fluid_handle_choruslevel(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)871 fluid_handle_choruslevel(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
872 {
873   fluid_real_t level;
874   if (ac < 1) {
875     fluid_ostream_printf(out, "cho_set_level: too few arguments.\n");
876     return -1;
877   }
878   level = atof(av[0]);
879   return fluid_synth_set_chorus_full (synth, FLUID_CHORUS_SET_LEVEL, 0, level, 0.0, 0.0, 0);
880 }
881 
882 /* Purpose:
883  * Response to 'chorus_setspeed' command */
884 int
fluid_handle_chorusspeed(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)885 fluid_handle_chorusspeed(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
886 {
887   fluid_real_t speed;
888   if (ac < 1) {
889     fluid_ostream_printf(out, "cho_set_speed: too few arguments.\n");
890     return -1;
891   }
892   speed = atof(av[0]);
893   return fluid_synth_set_chorus_full (synth, FLUID_CHORUS_SET_SPEED, 0, 0.0, speed, 0.0, 0);
894 }
895 
896 /* Purpose:
897  * Response to 'chorus_setdepth' command */
898 int
fluid_handle_chorusdepth(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)899 fluid_handle_chorusdepth(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
900 {
901   fluid_real_t depth;
902   if (ac < 1) {
903     fluid_ostream_printf(out, "cho_set_depth: too few arguments.\n");
904     return -1;
905   }
906   depth = atof(av[0]);
907   return fluid_synth_set_chorus_full (synth, FLUID_CHORUS_SET_DEPTH, 0, 0.0, 0.0, depth, 0);
908 }
909 
910 int
fluid_handle_chorus(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)911 fluid_handle_chorus(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
912 {
913   if (ac < 1) {
914     fluid_ostream_printf(out, "chorus: too few arguments\n");
915     return -1;
916   }
917 
918   if ((strcmp(av[0], "0") == 0) || (strcmp(av[0], "off") == 0)) {
919     fluid_synth_set_chorus_on(synth,0);
920   } else if ((strcmp(av[0], "1") == 0) || (strcmp(av[0], "on") == 0)) {
921     fluid_synth_set_chorus_on(synth,1);
922   } else {
923     fluid_ostream_printf(out, "chorus: invalid arguments %s [0|1|on|off]", av[0]);
924     return -1;
925   }
926 
927   return 0;
928 }
929 
930 /* Purpose:
931  * Response to the 'echo' command.
932  * The command itself is useful, when the synth is used via TCP/IP.
933  * It can signal for example, that a list of commands has been processed.
934  */
935 int
fluid_handle_echo(fluid_cmd_handler_t * handler,int ac,char ** av,fluid_ostream_t out)936 fluid_handle_echo(fluid_cmd_handler_t* handler, int ac, char** av, fluid_ostream_t out)
937 {
938   if (ac < 1) {
939     fluid_ostream_printf(out, "echo: too few arguments.\n");
940     return -1;
941   }
942 
943   fluid_ostream_printf(out, "%s\n",av[0]);
944 
945   return 0;
946 }
947 
948 int
fluid_handle_source(fluid_cmd_handler_t * handler,int ac,char ** av,fluid_ostream_t out)949 fluid_handle_source(fluid_cmd_handler_t* handler, int ac, char** av, fluid_ostream_t out)
950 {
951   if (ac < 1) {
952     fluid_ostream_printf(out, "source: too few arguments.\n");
953     return -1;
954   }
955 
956   fluid_source(handler, av[0]);
957 
958   return 0;
959 }
960 
961 /* Purpose:
962  * Response to 'gain' command. */
963 int
fluid_handle_gain(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)964 fluid_handle_gain(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
965 {
966   float gain;
967 
968   if (ac < 1) {
969     fluid_ostream_printf(out, "gain: too few arguments.\n");
970     return -1;
971   }
972 
973   gain = atof(av[0]);
974 
975   if ((gain < 0.0f) || (gain > 5.0f)) {
976     fluid_ostream_printf(out, "gain: value should be between '0' and '5'.\n");
977     return -1;
978   };
979 
980   fluid_synth_set_gain(synth, gain);
981 
982   return 0;
983 }
984 
985 /* Response to voice_count command */
986 static int
fluid_handle_voice_count(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)987 fluid_handle_voice_count (fluid_synth_t *synth, int ac, char **av,
988                           fluid_ostream_t out)
989 {
990   fluid_ostream_printf (out, "voice_count: %d\n",
991                         fluid_synth_get_active_voice_count (synth));
992   return FLUID_OK;
993 }
994 
995 /* Purpose:
996  * Response to 'interp' command. */
997 int
fluid_handle_interp(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)998 fluid_handle_interp(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
999 {
1000   int interp;
1001   int chan=-1; /* -1: Set all channels */
1002 
1003   if (ac < 1) {
1004     fluid_ostream_printf(out, "interp: too few arguments.\n");
1005     return -1;
1006   }
1007 
1008   interp = atoi(av[0]);
1009 
1010   if ((interp < 0) || (interp > FLUID_INTERP_HIGHEST)) {
1011     fluid_ostream_printf(out, "interp: Bad value\n");
1012     return -1;
1013   };
1014 
1015   fluid_synth_set_interp_method(synth, chan, interp);
1016 
1017   return 0;
1018 }
1019 
1020 /* Purpose:
1021  * Response to 'interp' command. */
1022 int
fluid_handle_interpc(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1023 fluid_handle_interpc(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1024 {
1025   int interp;
1026   int chan;
1027 
1028   if (ac < 2) {
1029     fluid_ostream_printf(out, "interpc: too few arguments.\n");
1030     return -1;
1031   }
1032 
1033   chan = atoi(av[0]);
1034   interp = atoi(av[1]);
1035 
1036   if ((chan < 0) || (chan >= fluid_synth_count_midi_channels(synth))){
1037     fluid_ostream_printf(out, "interp: Bad value for channel number.\n");
1038     return -1;
1039   };
1040   if ((interp < 0) || (interp > FLUID_INTERP_HIGHEST)) {
1041     fluid_ostream_printf(out, "interp: Bad value for interpolation method.\n");
1042     return -1;
1043   };
1044 
1045   fluid_synth_set_interp_method(synth, chan, interp);
1046 
1047   return 0;
1048 }
1049 
1050 int
fluid_handle_tuning(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1051 fluid_handle_tuning(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1052 {
1053   char *name;
1054   int bank, prog;
1055 
1056   if (ac < 3) {
1057     fluid_ostream_printf(out, "tuning: too few arguments.\n");
1058     return -1;
1059   }
1060 
1061   name = av[0];
1062 
1063   if (!fluid_is_number(av[1])) {
1064     fluid_ostream_printf(out, "tuning: 2nd argument should be a number.\n");
1065     return -1;
1066   }
1067   bank = atoi(av[1]);
1068   if ((bank < 0) || (bank >= 128)){
1069     fluid_ostream_printf(out, "tuning: invalid bank number.\n");
1070     return -1;
1071   };
1072 
1073   if (!fluid_is_number(av[2])) {
1074     fluid_ostream_printf(out, "tuning: 3rd argument should be a number.\n");
1075     return -1;
1076   }
1077   prog = atoi(av[2]);
1078   if ((prog < 0) || (prog >= 128)){
1079     fluid_ostream_printf(out, "tuning: invalid program number.\n");
1080     return -1;
1081   };
1082 
1083   fluid_synth_create_key_tuning(synth, bank, prog, name, NULL);
1084 
1085   return 0;
1086 }
1087 
1088 int
fluid_handle_tune(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1089 fluid_handle_tune(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1090 {
1091   int bank, prog, key;
1092   double pitch;
1093 
1094   if (ac < 4) {
1095     fluid_ostream_printf(out, "tune: too few arguments.\n");
1096     return -1;
1097   }
1098 
1099   if (!fluid_is_number(av[0])) {
1100     fluid_ostream_printf(out, "tune: 1st argument should be a number.\n");
1101     return -1;
1102   }
1103   bank = atoi(av[0]);
1104   if ((bank < 0) || (bank >= 128)){
1105     fluid_ostream_printf(out, "tune: invalid bank number.\n");
1106     return -1;
1107   };
1108 
1109   if (!fluid_is_number(av[1])) {
1110     fluid_ostream_printf(out, "tune: 2nd argument should be a number.\n");
1111     return -1;
1112   }
1113   prog = atoi(av[1]);
1114   if ((prog < 0) || (prog >= 128)){
1115     fluid_ostream_printf(out, "tune: invalid program number.\n");
1116     return -1;
1117   };
1118 
1119   if (!fluid_is_number(av[2])) {
1120     fluid_ostream_printf(out, "tune: 3rd argument should be a number.\n");
1121     return -1;
1122   }
1123   key = atoi(av[2]);
1124   if ((key < 0) || (key >= 128)){
1125     fluid_ostream_printf(out, "tune: invalid key number.\n");
1126     return -1;
1127   };
1128 
1129   pitch = atof(av[3]);
1130   if (pitch < 0.0f) {
1131     fluid_ostream_printf(out, "tune: invalid pitch.\n");
1132     return -1;
1133   };
1134 
1135   fluid_synth_tune_notes(synth, bank, prog, 1, &key, &pitch, 0);
1136 
1137   return 0;
1138 }
1139 
1140 int
fluid_handle_settuning(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1141 fluid_handle_settuning(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1142 {
1143   int chan, bank, prog;
1144 
1145   if (ac < 3) {
1146     fluid_ostream_printf(out, "settuning: too few arguments.\n");
1147     return -1;
1148   }
1149 
1150   if (!fluid_is_number(av[0])) {
1151     fluid_ostream_printf(out, "tune: 1st argument should be a number.\n");
1152     return -1;
1153   }
1154   chan = atoi(av[0]);
1155   if ((chan < 0) || (chan >= fluid_synth_count_midi_channels(synth))){
1156     fluid_ostream_printf(out, "tune: invalid channel number.\n");
1157     return -1;
1158   };
1159 
1160   if (!fluid_is_number(av[1])) {
1161     fluid_ostream_printf(out, "tuning: 2nd argument should be a number.\n");
1162     return -1;
1163   }
1164   bank = atoi(av[1]);
1165   if ((bank < 0) || (bank >= 128)){
1166     fluid_ostream_printf(out, "tuning: invalid bank number.\n");
1167     return -1;
1168   };
1169 
1170   if (!fluid_is_number(av[2])) {
1171     fluid_ostream_printf(out, "tuning: 3rd argument should be a number.\n");
1172     return -1;
1173   }
1174   prog = atoi(av[2]);
1175   if ((prog < 0) || (prog >= 128)){
1176     fluid_ostream_printf(out, "tuning: invalid program number.\n");
1177     return -1;
1178   };
1179 
1180   fluid_synth_select_tuning(synth, chan, bank, prog);
1181 
1182   return 0;
1183 }
1184 
1185 int
fluid_handle_resettuning(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1186 fluid_handle_resettuning(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1187 {
1188   int chan;
1189 
1190   if (ac < 1) {
1191     fluid_ostream_printf(out, "resettuning: too few arguments.\n");
1192     return -1;
1193   }
1194 
1195   if (!fluid_is_number(av[0])) {
1196     fluid_ostream_printf(out, "tune: 1st argument should be a number.\n");
1197     return -1;
1198   }
1199   chan = atoi(av[0]);
1200   if ((chan < 0) || (chan >= fluid_synth_count_midi_channels(synth))){
1201     fluid_ostream_printf(out, "tune: invalid channel number.\n");
1202     return -1;
1203   };
1204 
1205   fluid_synth_reset_tuning(synth, chan);
1206 
1207   return 0;
1208 }
1209 
1210 int
fluid_handle_tunings(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1211 fluid_handle_tunings(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1212 {
1213   int bank, prog;
1214   char name[256];
1215   int count = 0;
1216 
1217   fluid_synth_tuning_iteration_start(synth);
1218 
1219   while (fluid_synth_tuning_iteration_next(synth, &bank, &prog)) {
1220     fluid_synth_tuning_dump(synth, bank, prog, name, 256, NULL);
1221     fluid_ostream_printf(out, "%03d-%03d %s\n", bank, prog, name);
1222     count++;
1223   }
1224 
1225   if (count == 0) {
1226     fluid_ostream_printf(out, "No tunings available\n");
1227   }
1228 
1229   return 0;
1230 }
1231 
1232 int
fluid_handle_dumptuning(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1233 fluid_handle_dumptuning(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1234 {
1235   int bank, prog, i;
1236   double pitch[128];
1237   char name[256];
1238 
1239   if (ac < 2) {
1240     fluid_ostream_printf(out, "dumptuning: too few arguments.\n");
1241     return -1;
1242   }
1243 
1244   if (!fluid_is_number(av[0])) {
1245     fluid_ostream_printf(out, "dumptuning: 1st argument should be a number.\n");
1246     return -1;
1247   }
1248   bank = atoi(av[0]);
1249   if ((bank < 0) || (bank >= 128)){
1250     fluid_ostream_printf(out, "dumptuning: invalid bank number.\n");
1251     return -1;
1252   };
1253 
1254   if (!fluid_is_number(av[1])) {
1255     fluid_ostream_printf(out, "dumptuning: 2nd argument should be a number.\n");
1256     return -1;
1257   }
1258   prog = atoi(av[1]);
1259   if ((prog < 0) || (prog >= 128)){
1260     fluid_ostream_printf(out, "dumptuning: invalid program number.\n");
1261     return -1;
1262   };
1263 
1264   fluid_synth_tuning_dump(synth, bank, prog, name, 256, pitch);
1265 
1266   fluid_ostream_printf(out, "%03d-%03d %s:\n", bank, prog, name);
1267 
1268   for (i = 0; i < 128; i++) {
1269     fluid_ostream_printf(out, "key %03d, pitch %5.2f\n", i, pitch[i]);
1270   }
1271 
1272   return 0;
1273 }
1274 
1275 int
fluid_handle_set(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1276 fluid_handle_set(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1277 {
1278   int hints;
1279   int ival;
1280 
1281   if (ac < 2) {
1282     fluid_ostream_printf(out, "set: Too few arguments.\n");
1283     return -1;
1284   }
1285 
1286   switch (fluid_settings_get_type (synth->settings, av[0]))
1287   {
1288     case FLUID_NO_TYPE:
1289       fluid_ostream_printf (out, "set: Parameter '%s' not found.\n", av[0]);
1290       break;
1291     case FLUID_INT_TYPE:
1292       hints = fluid_settings_get_hints (synth->settings, av[0]);
1293 
1294       if (hints & FLUID_HINT_TOGGLED)
1295       {
1296         if (FLUID_STRCMP (av[1], "yes") == 0 || FLUID_STRCMP (av[1], "True") == 0
1297             || FLUID_STRCMP (av[1], "TRUE") == 0 || FLUID_STRCMP (av[1], "true") == 0
1298             || FLUID_STRCMP (av[1], "T") == 0)
1299           ival = 1;
1300         else ival = atoi (av[1]);
1301       }
1302       else ival = atoi (av[1]);
1303 
1304       fluid_synth_setint (synth, av[0], ival);
1305       break;
1306     case FLUID_NUM_TYPE:
1307       fluid_synth_setnum (synth, av[0], atof (av[1]));
1308       break;
1309     case FLUID_STR_TYPE:
1310       fluid_synth_setstr(synth, av[0], av[1]);
1311       break;
1312     case FLUID_SET_TYPE:
1313       fluid_ostream_printf (out, "set: Parameter '%s' is a node.\n", av[0]);
1314       break;
1315   }
1316 
1317   return 0;
1318 }
1319 
1320 int
fluid_handle_get(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1321 fluid_handle_get(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1322 {
1323   if (ac < 1) {
1324     fluid_ostream_printf(out, "get: too few arguments.\n");
1325     return -1;
1326   }
1327 
1328   switch (fluid_settings_get_type(fluid_synth_get_settings(synth), av[0])) {
1329   case FLUID_NO_TYPE:
1330     fluid_ostream_printf(out, "get: no such setting '%s'.\n", av[0]);
1331     return -1;
1332 
1333   case FLUID_NUM_TYPE: {
1334     double value;
1335     fluid_synth_getnum(synth, av[0], &value);
1336     fluid_ostream_printf(out, "%.3f", value);
1337     break;
1338   }
1339 
1340   case FLUID_INT_TYPE: {
1341     int value;
1342     fluid_synth_getint(synth, av[0], &value);
1343     fluid_ostream_printf(out, "%d", value);
1344     break;
1345   }
1346 
1347   case FLUID_STR_TYPE: {
1348     char* s;
1349     fluid_synth_dupstr(synth, av[0], &s);       /* ++ alloc string */
1350     fluid_ostream_printf(out, "%s", s ? s : "NULL");
1351     if (s) FLUID_FREE (s);      /* -- free string */
1352     break;
1353   }
1354 
1355   case FLUID_SET_TYPE:
1356     fluid_ostream_printf(out, "%s is a node", av[0]);
1357     break;
1358   }
1359 
1360   return 0;
1361 }
1362 
1363 struct _fluid_handle_settings_data_t {
1364   int len;
1365   fluid_synth_t* synth;
1366   fluid_ostream_t out;
1367 };
1368 
fluid_handle_settings_iter1(void * data,char * name,int type)1369 static void fluid_handle_settings_iter1(void* data, char* name, int type)
1370 {
1371   struct _fluid_handle_settings_data_t* d = (struct _fluid_handle_settings_data_t*) data;
1372 
1373   int len = FLUID_STRLEN(name);
1374   if (len > d->len) {
1375     d->len = len;
1376   }
1377 }
1378 
fluid_handle_settings_iter2(void * data,char * name,int type)1379 static void fluid_handle_settings_iter2(void* data, char* name, int type)
1380 {
1381   struct _fluid_handle_settings_data_t* d = (struct _fluid_handle_settings_data_t*) data;
1382 
1383   int len = FLUID_STRLEN(name);
1384   fluid_ostream_printf(d->out, "%s", name);
1385   while (len++ < d->len) {
1386     fluid_ostream_printf(d->out, " ");
1387   }
1388   fluid_ostream_printf(d->out, "   ");
1389 
1390   switch (fluid_settings_get_type(fluid_synth_get_settings(d->synth), name)) {
1391   case FLUID_NUM_TYPE: {
1392     double value;
1393     fluid_synth_getnum(d->synth, name, &value);
1394     fluid_ostream_printf(d->out, "%.3f\n", value);
1395     break;
1396   }
1397 
1398   case FLUID_INT_TYPE: {
1399     int value, hints;
1400     fluid_synth_getint(d->synth, name, &value);
1401     hints = fluid_settings_get_hints (d->synth->settings, name);
1402 
1403     if (!(hints & FLUID_HINT_TOGGLED))
1404       fluid_ostream_printf(d->out, "%d\n", value);
1405     else fluid_ostream_printf(d->out, "%s\n", value ? "True" : "False");
1406     break;
1407   }
1408 
1409   case FLUID_STR_TYPE: {
1410     char* s;
1411     fluid_synth_dupstr(d->synth, name, &s);     /* ++ alloc string */
1412     fluid_ostream_printf(d->out, "%s\n", s ? s : "NULL");
1413     if (s) FLUID_FREE (s);      /* -- free string */
1414     break;
1415   }
1416   }
1417 }
1418 
1419 int
fluid_handle_settings(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1420 fluid_handle_settings(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1421 {
1422   struct _fluid_handle_settings_data_t data;
1423 
1424   data.len = 0;
1425   data.synth = synth;
1426   data.out = out;
1427 
1428   fluid_settings_foreach(fluid_synth_get_settings(synth), &data, fluid_handle_settings_iter1);
1429   fluid_settings_foreach(fluid_synth_get_settings(synth), &data, fluid_handle_settings_iter2);
1430   return 0;
1431 }
1432 
1433 
1434 struct _fluid_handle_option_data_t {
1435   int first;
1436   fluid_ostream_t out;
1437 };
1438 
fluid_handle_print_option(void * data,char * name,char * option)1439 void fluid_handle_print_option(void* data, char* name, char* option)
1440 {
1441   struct _fluid_handle_option_data_t* d = (struct _fluid_handle_option_data_t*) data;
1442 
1443   if (d->first) {
1444     fluid_ostream_printf(d->out, "%s", option);
1445     d->first = 0;
1446   } else {
1447     fluid_ostream_printf(d->out, ", %s", option);
1448   }
1449 }
1450 
1451 int
fluid_handle_info(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1452 fluid_handle_info(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1453 {
1454   fluid_settings_t* settings = fluid_synth_get_settings(synth);
1455   struct _fluid_handle_option_data_t data;
1456 
1457   if (ac < 1) {
1458     fluid_ostream_printf(out, "info: too few arguments.\n");
1459     return -1;
1460   }
1461 
1462   switch (fluid_settings_get_type(settings, av[0])) {
1463   case FLUID_NO_TYPE:
1464     fluid_ostream_printf(out, "info: no such setting '%s'.\n", av[0]);
1465     return -1;
1466 
1467   case FLUID_NUM_TYPE: {
1468     double value, min, max;
1469     fluid_settings_getnum_range(settings, av[0], &min, &max);
1470     fluid_settings_getnum(settings, av[0], &value);
1471     fluid_ostream_printf(out, "%s:\n", av[0]);
1472     fluid_ostream_printf(out, "Type:          number\n");
1473     fluid_ostream_printf(out, "Value:         %.3f\n", value);
1474     fluid_ostream_printf(out, "Minimum value: %.3f\n", min);
1475     fluid_ostream_printf(out, "Maximum value: %.3f\n", max);
1476     fluid_ostream_printf(out, "Default value: %.3f\n",
1477 			fluid_settings_getnum_default(settings, av[0]));
1478     fluid_ostream_printf(out, "Real-time:     %s\n",
1479 			fluid_settings_is_realtime(settings, av[0])? "yes" : "no");
1480     break;
1481   }
1482 
1483   case FLUID_INT_TYPE: {
1484     int value, min, max, def, hints;
1485 
1486     fluid_settings_getint_range(settings, av[0], &min, &max);
1487     fluid_settings_getint(settings, av[0], &value);
1488     hints = fluid_settings_get_hints(settings, av[0]);
1489     def = fluid_settings_getint_default (settings, av[0]);
1490 
1491     fluid_ostream_printf(out, "%s:\n", av[0]);
1492 
1493     if (!(hints & FLUID_HINT_TOGGLED))
1494     {
1495       fluid_ostream_printf(out, "Type:          integer\n");
1496       fluid_ostream_printf(out, "Value:         %d\n", value);
1497       fluid_ostream_printf(out, "Minimum value: %d\n", min);
1498       fluid_ostream_printf(out, "Maximum value: %d\n", max);
1499       fluid_ostream_printf(out, "Default value: %d\n", def);
1500     }
1501     else
1502     {
1503       fluid_ostream_printf(out, "Type:          boolean\n");
1504       fluid_ostream_printf(out, "Value:         %s\n", value ? "True" : "False");
1505       fluid_ostream_printf(out, "Default value: %s\n", def ? "True" : "False");
1506     }
1507 
1508     fluid_ostream_printf(out, "Real-time:     %s\n",
1509 			fluid_settings_is_realtime(settings, av[0])? "yes" : "no");
1510     break;
1511   }
1512 
1513   case FLUID_STR_TYPE: {
1514     char *s;
1515     fluid_settings_dupstr(settings, av[0], &s);         /* ++ alloc string */
1516     fluid_ostream_printf(out, "%s:\n", av[0]);
1517     fluid_ostream_printf(out, "Type:          string\n");
1518     fluid_ostream_printf(out, "Value:         %s\n", s ? s : "NULL");
1519     fluid_ostream_printf(out, "Default value: %s\n",
1520 			fluid_settings_getstr_default(settings, av[0]));
1521 
1522     if (s) FLUID_FREE (s);
1523 
1524     data.out = out;
1525     data.first = 1;
1526     fluid_ostream_printf(out, "Options:       ");
1527     fluid_settings_foreach_option (settings, av[0], &data, fluid_handle_print_option);
1528     fluid_ostream_printf(out, "\n");
1529 
1530     fluid_ostream_printf(out, "Real-time:     %s\n",
1531 			fluid_settings_is_realtime(settings, av[0])? "yes" : "no");
1532     break;
1533   }
1534 
1535   case FLUID_SET_TYPE:
1536     fluid_ostream_printf(out, "%s:\n", av[0]);
1537     fluid_ostream_printf(out, "Type:          node\n");
1538     break;
1539   }
1540 
1541   return 0;
1542 }
1543 
1544 int
fluid_handle_reset(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1545 fluid_handle_reset(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1546 {
1547   fluid_synth_system_reset(synth);
1548   return 0;
1549 }
1550 
1551 int
fluid_handle_quit(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1552 fluid_handle_quit(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1553 {
1554   fluid_ostream_printf(out, "cheers!\n");
1555   return -2;
1556 }
1557 
1558 int
fluid_handle_help(fluid_synth_t * synth,int ac,char ** av,fluid_ostream_t out)1559 fluid_handle_help(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out)
1560 {
1561   /* Purpose:
1562    * Prints the help text for the command line commands.
1563    * Can be used as follows:
1564    * - help
1565    * - help (topic), where (topic) is 'general', 'chorus', etc.
1566    * - help all
1567    */
1568 
1569   char* topic = "help"; /* default, if no topic is given */
1570   int count = 0;
1571   int i;
1572 
1573   fluid_ostream_printf(out, "\n");
1574   /* 1st argument (optional): help topic */
1575   if (ac >= 1) {
1576     topic = av[0];
1577   }
1578   if (strcmp(topic,"help") == 0){
1579     /* "help help": Print a list of all topics */
1580     fluid_ostream_printf(out,
1581 			"*** Help topics:***\n"
1582 			"help all (prints all topics)\n");
1583     for (i = 0; fluid_commands[i].name != NULL; i++) {
1584       int listed_first_time = 1;
1585       int ii;
1586       for (ii = 0; ii < i; ii++){
1587 	if (strcmp(fluid_commands[i].topic, fluid_commands[ii].topic) == 0){
1588 	  listed_first_time = 0;
1589 	}; /* if topic has already been listed */
1590       }; /* for all topics (inner loop) */
1591       if (listed_first_time){
1592 	fluid_ostream_printf(out, "help %s\n",fluid_commands[i].topic);
1593       };
1594     }; /* for all topics (outer loop) */
1595   } else {
1596     /* help (arbitrary topic or "all") */
1597     for (i = 0; fluid_commands[i].name != NULL; i++) {
1598       fluid_cmd_t cmd = fluid_commands[i];
1599       if (cmd.help != NULL) {
1600 	if (strcmp(topic,"all") == 0 || strcmp(topic,cmd.topic) == 0){
1601 	  fluid_ostream_printf(out, "%s\n", fluid_commands[i].help);
1602 	  count++;
1603 	}; /* if it matches the topic */
1604       }; /* if help text exists */
1605     }; /* foreach command */
1606     if (count == 0){
1607       fluid_ostream_printf(out, "Unknown help topic. Try 'help help'.\n");
1608     };
1609   };
1610   return 0;
1611 }
1612 
1613 int
fluid_is_number(char * a)1614 fluid_is_number(char* a)
1615 {
1616   while (*a != 0) {
1617     if (((*a < '0') || (*a > '9')) && (*a != '-') && (*a != '+') && (*a != '.')) {
1618       return 0;
1619     }
1620     a++;
1621   }
1622   return 1;
1623 }
1624 
1625 int
fluid_is_empty(char * a)1626 fluid_is_empty(char* a)
1627 {
1628   while (*a != 0) {
1629     if ((*a != ' ') && (*a != '\t') && (*a != '\n') && (*a != '\r')) {
1630       return 0;
1631     }
1632     a++;
1633   }
1634   return 1;
1635 }
1636 
1637 char*
fluid_expand_path(char * path,char * new_path,int len)1638 fluid_expand_path(char* path, char* new_path, int len)
1639 {
1640 #if defined(WIN32) || defined(MACOS9)
1641   snprintf(new_path, len - 1, "%s", path);
1642 #else
1643   if ((path[0] == '~') && (path[1] == '/')) {
1644     char* home = getenv("HOME");
1645     if (home == NULL) {
1646       snprintf(new_path, len - 1, "%s", path);
1647     } else {
1648       snprintf(new_path, len - 1, "%s%s", home, &path[1]);
1649     }
1650   } else {
1651     snprintf(new_path, len - 1, "%s", path);
1652   }
1653 #endif
1654 
1655   new_path[len - 1] = 0;
1656   return new_path;
1657 }
1658 
1659 
1660 
1661 /*
1662  * Command
1663  */
1664 
fluid_cmd_copy(fluid_cmd_t * cmd)1665 fluid_cmd_t* fluid_cmd_copy(fluid_cmd_t* cmd)
1666 {
1667   fluid_cmd_t* copy = FLUID_NEW(fluid_cmd_t);
1668   if (copy == NULL) {
1669     FLUID_LOG (FLUID_PANIC, "Out of memory");
1670     return NULL;
1671   }
1672 
1673   copy->name = FLUID_STRDUP(cmd->name);
1674   copy->topic = FLUID_STRDUP(cmd->topic);
1675   copy->help = FLUID_STRDUP(cmd->help);
1676   copy->handler = cmd->handler;
1677   copy->data = cmd->data;
1678   return copy;
1679 }
1680 
delete_fluid_cmd(fluid_cmd_t * cmd)1681 void delete_fluid_cmd(fluid_cmd_t* cmd)
1682 {
1683   if (cmd->name) {
1684     FLUID_FREE(cmd->name);
1685   }
1686   if (cmd->topic) {
1687     FLUID_FREE(cmd->topic);
1688   }
1689   if (cmd->help) {
1690     FLUID_FREE(cmd->help);
1691   }
1692   FLUID_FREE(cmd);
1693 }
1694 
1695 /*
1696  * Command handler
1697  */
1698 
1699 static void
fluid_cmd_handler_destroy_hash_value(void * value)1700 fluid_cmd_handler_destroy_hash_value (void *value)
1701 {
1702   delete_fluid_cmd ((fluid_cmd_t *)value);
1703 }
1704 
1705 /**
1706  * Create a new command handler.
1707  * @param synth If not NULL, all the default synthesizer commands will be
1708  *   added to the new handler.
1709  * @return New command handler
1710  */
1711 fluid_cmd_handler_t *
new_fluid_cmd_handler(fluid_synth_t * synth)1712 new_fluid_cmd_handler(fluid_synth_t* synth)
1713 {
1714   int i;
1715   fluid_cmd_handler_t* handler;
1716 
1717   fluid_cmd_t source = {
1718     "source", "general", (fluid_cmd_func_t) fluid_handle_source, NULL,
1719     "source filename            Load a file and parse every line as a command"
1720   };
1721 
1722   handler = new_fluid_hashtable_full (fluid_str_hash, fluid_str_equal,
1723                                       NULL, fluid_cmd_handler_destroy_hash_value);
1724   if (handler == NULL) {
1725     return NULL;
1726   }
1727 
1728   if (synth != NULL) {
1729     for (i = 0; fluid_commands[i].name != NULL; i++) {
1730       fluid_commands[i].data = synth;
1731       fluid_cmd_handler_register(handler, &fluid_commands[i]);
1732       fluid_commands[i].data = NULL;
1733     }
1734   }
1735 
1736   source.data = handler;
1737   fluid_cmd_handler_register(handler, &source);
1738 
1739   return handler;
1740 }
1741 
1742 /**
1743  * Delete a command handler.
1744  * @param handler Command handler to delete
1745  */
1746 void
delete_fluid_cmd_handler(fluid_cmd_handler_t * handler)1747 delete_fluid_cmd_handler(fluid_cmd_handler_t* handler)
1748 {
1749   delete_fluid_hashtable (handler);
1750 }
1751 
1752 /**
1753  * Register a new command to the handler.
1754  * @param handler Command handler instance
1755  * @param cmd Command info (gets copied)
1756  * @return #FLUID_OK if command was inserted, #FLUID_FAILED otherwise
1757  */
1758 int
fluid_cmd_handler_register(fluid_cmd_handler_t * handler,fluid_cmd_t * cmd)1759 fluid_cmd_handler_register(fluid_cmd_handler_t* handler, fluid_cmd_t* cmd)
1760 {
1761   fluid_cmd_t* copy = fluid_cmd_copy(cmd);
1762   fluid_hashtable_insert(handler, copy->name, copy);
1763   return FLUID_OK;
1764 }
1765 
1766 /**
1767  * Unregister a command from a command handler.
1768  * @param handler Command handler instance
1769  * @param cmd Name of the command
1770  * @return TRUE if command was found and unregistered, FALSE otherwise
1771  */
1772 int
fluid_cmd_handler_unregister(fluid_cmd_handler_t * handler,const char * cmd)1773 fluid_cmd_handler_unregister(fluid_cmd_handler_t* handler, const char *cmd)
1774 {
1775   return fluid_hashtable_remove(handler, cmd);
1776 }
1777 
1778 int
fluid_cmd_handler_handle(fluid_cmd_handler_t * handler,int ac,char ** av,fluid_ostream_t out)1779 fluid_cmd_handler_handle(fluid_cmd_handler_t* handler, int ac, char** av, fluid_ostream_t out)
1780 {
1781   fluid_cmd_t* cmd;
1782 
1783   cmd = fluid_hashtable_lookup(handler, av[0]);
1784 
1785   if (cmd && cmd->handler)
1786     return (*cmd->handler)(cmd->data, ac - 1, av + 1, out);
1787 
1788   fluid_ostream_printf(out, "unknown command: %s (try help)\n", av[0]);
1789   return -1;
1790 }
1791 
1792 
1793 #if !defined(WITHOUT_SERVER)
1794 
1795 
1796 
1797 struct _fluid_server_t {
1798   fluid_server_socket_t* socket;
1799   fluid_settings_t* settings;
1800   fluid_server_newclient_func_t newclient;
1801   void* data;
1802   fluid_list_t* clients;
1803   fluid_mutex_t mutex;
1804 };
1805 
1806 static void fluid_server_handle_connection(fluid_server_t* server,
1807 					  fluid_socket_t client_socket,
1808 					  char* addr);
1809 static void fluid_server_close(fluid_server_t* server);
1810 
1811 /**
1812  * Create a new TCP/IP command shell server.
1813  * @param settings Settings instance to use for the shell
1814  * @param newclient Callback function to call for each new client connection
1815  * @param data User defined data to pass to \a newclient callback
1816  * @return New shell server instance or NULL on error
1817  */
1818 fluid_server_t*
new_fluid_server(fluid_settings_t * settings,fluid_server_newclient_func_t newclient,void * data)1819 new_fluid_server(fluid_settings_t* settings,
1820 		fluid_server_newclient_func_t newclient,
1821 		void* data)
1822 {
1823   fluid_server_t* server;
1824   int port;
1825 
1826   server = FLUID_NEW(fluid_server_t);
1827   if (server == NULL) {
1828     FLUID_LOG(FLUID_ERR, "Out of memory");
1829     return NULL;
1830   }
1831 
1832   server->settings = settings;
1833   server->clients = NULL;
1834   server->newclient = newclient;
1835   server->data = data;
1836 
1837   fluid_mutex_init(server->mutex);
1838 
1839   fluid_settings_getint(settings, "shell.port", &port);
1840 
1841   server->socket = new_fluid_server_socket(port,
1842 					  (fluid_server_func_t) fluid_server_handle_connection,
1843 					  server);
1844   if (server->socket == NULL) {
1845     FLUID_FREE(server);
1846     return NULL;
1847   }
1848 
1849   return server;
1850 }
1851 
1852 /**
1853  * Delete a TCP/IP shell server.
1854  * @param server Shell server instance
1855  */
1856 void
delete_fluid_server(fluid_server_t * server)1857 delete_fluid_server(fluid_server_t* server)
1858 {
1859   if (server == NULL) {
1860     return;
1861   }
1862 
1863   fluid_server_close(server);
1864 
1865   FLUID_FREE(server);
1866 }
1867 
fluid_server_close(fluid_server_t * server)1868 static void fluid_server_close(fluid_server_t* server)
1869 {
1870   fluid_list_t* list;
1871   fluid_list_t* clients;
1872   fluid_client_t* client;
1873 
1874   if (server == NULL) {
1875     return;
1876   }
1877 
1878   fluid_mutex_lock(server->mutex);
1879   clients = server->clients;
1880   server->clients = NULL;
1881   fluid_mutex_unlock(server->mutex);
1882 
1883   list = clients;
1884 
1885   while (list) {
1886     client = fluid_list_get(list);
1887     fluid_client_quit(client);
1888     list = fluid_list_next(list);
1889   }
1890 
1891   delete_fluid_list(clients);
1892 
1893   if (server->socket) {
1894     delete_fluid_server_socket(server->socket);
1895     server->socket = NULL;
1896   }
1897 }
1898 
1899 static void
fluid_server_handle_connection(fluid_server_t * server,fluid_socket_t client_socket,char * addr)1900 fluid_server_handle_connection(fluid_server_t* server, fluid_socket_t client_socket, char* addr)
1901 {
1902   fluid_client_t* client;
1903   fluid_cmd_handler_t* handler;
1904 
1905   handler = server->newclient(server->data, addr);
1906   if (handler == NULL) {
1907     return;
1908   }
1909 
1910   client = new_fluid_client(server, server->settings, handler, client_socket);
1911   if (client == NULL) {
1912     return;
1913   }
1914   fluid_server_add_client(server, client);
1915 }
1916 
fluid_server_add_client(fluid_server_t * server,fluid_client_t * client)1917 void fluid_server_add_client(fluid_server_t* server, fluid_client_t* client)
1918 {
1919   fluid_mutex_lock(server->mutex);
1920   server->clients = fluid_list_append(server->clients, client);
1921   fluid_mutex_unlock(server->mutex);
1922 }
1923 
fluid_server_remove_client(fluid_server_t * server,fluid_client_t * client)1924 void fluid_server_remove_client(fluid_server_t* server, fluid_client_t* client)
1925 {
1926   fluid_mutex_lock(server->mutex);
1927   server->clients = fluid_list_remove(server->clients, client);
1928   fluid_mutex_unlock(server->mutex);
1929 }
1930 
1931 /**
1932  * Join a shell server thread (wait until it quits).
1933  * @param server Shell server instance
1934  * @return #FLUID_OK on success, #FLUID_FAILED otherwise
1935  */
fluid_server_join(fluid_server_t * server)1936 int fluid_server_join(fluid_server_t* server)
1937 {
1938   return fluid_server_socket_join(server->socket);
1939 }
1940 
1941 
1942 
1943 
1944 struct _fluid_client_t {
1945   fluid_server_t* server;
1946   fluid_settings_t* settings;
1947   fluid_cmd_handler_t* handler;
1948   fluid_socket_t socket;
1949   fluid_thread_t* thread;
1950 };
1951 
1952 
1953 
fluid_client_run(fluid_client_t * client)1954 static void fluid_client_run(fluid_client_t* client)
1955 {
1956   fluid_shell_t shell;
1957   fluid_shell_init(&shell,
1958 		  client->settings,
1959 		  client->handler,
1960 		  fluid_socket_get_istream(client->socket),
1961 		  fluid_socket_get_ostream(client->socket));
1962   fluid_shell_run(&shell);
1963   fluid_server_remove_client(client->server, client);
1964   delete_fluid_client(client);
1965 }
1966 
1967 
1968 fluid_client_t*
new_fluid_client(fluid_server_t * server,fluid_settings_t * settings,fluid_cmd_handler_t * handler,fluid_socket_t sock)1969 new_fluid_client(fluid_server_t* server, fluid_settings_t* settings,
1970 		fluid_cmd_handler_t* handler, fluid_socket_t sock)
1971 {
1972   fluid_client_t* client;
1973 
1974   client = FLUID_NEW(fluid_client_t);
1975   if (client == NULL) {
1976     FLUID_LOG(FLUID_ERR, "Out of memory");
1977     return NULL;
1978   }
1979 
1980   client->server = server;
1981   client->socket = sock;
1982   client->settings = settings;
1983   client->handler = handler;
1984 
1985   client->thread = new_fluid_thread((fluid_thread_func_t) fluid_client_run, client,
1986                                     0, FALSE);
1987 
1988   if (client->thread == NULL) {
1989     fluid_socket_close(sock);
1990     FLUID_FREE(client);
1991     return NULL;
1992   }
1993 
1994   return client;
1995 }
1996 
fluid_client_quit(fluid_client_t * client)1997 void fluid_client_quit(fluid_client_t* client)
1998 {
1999   if (client->socket != INVALID_SOCKET) {
2000     fluid_socket_close(client->socket);
2001     client->socket = INVALID_SOCKET;
2002   }
2003   FLUID_LOG(FLUID_DBG, "fluid_client_quit: joining");
2004   fluid_thread_join(client->thread);
2005   FLUID_LOG(FLUID_DBG, "fluid_client_quit: done");
2006 }
2007 
delete_fluid_client(fluid_client_t * client)2008 void delete_fluid_client(fluid_client_t* client)
2009 {
2010   if (client->socket != INVALID_SOCKET) {
2011     fluid_socket_close(client->socket);
2012     client->socket = INVALID_SOCKET;
2013   }
2014   if (client->thread != NULL) {
2015     delete_fluid_thread(client->thread);
2016     client->thread = NULL;
2017   }
2018   FLUID_FREE(client);
2019 }
2020 
2021 
2022 #endif /* WITHOUT_SERVER */
2023