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