1 /* FluidSynth - A Software Synthesizer
2  *
3  * Copyright (C) 2003  Peter Hanappe and others.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public License
7  * as published by the Free Software Foundation; either version 2.1 of
8  * the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free
17  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18  * 02110-1301, USA
19  */
20 
21 #include "fluid_cmd.h"
22 #include "fluid_synth.h"
23 #include "fluid_settings.h"
24 #include "fluid_hash.h"
25 #include "fluid_midi_router.h"
26 #include "fluid_sfont.h"
27 #include "fluid_chan.h"
28 
29 /* FIXME: LADSPA used to need a lot of parameters on a single line. This is not
30  * necessary anymore, so the limits below could probably be reduced */
31 #define MAX_TOKENS 100
32 #define MAX_COMMAND_LEN 1024	/* max command length accepted by fluid_command() */
33 #define FLUID_WORKLINELENGTH 1024
34 
35 #define FLUID_ENTRY_COMMAND(data) fluid_cmd_handler_t* handler=(fluid_cmd_handler_t*)(data)
36 
37 /* the shell cmd handler struct */
38 struct _fluid_cmd_handler_t
39 {
40     fluid_settings_t *settings;
41     fluid_synth_t *synth;
42     fluid_midi_router_t *router;
43     fluid_player_t *player;
44     fluid_cmd_hash_t *commands;
45 
46     fluid_midi_router_rule_t *cmd_rule;        /* Rule currently being processed by shell command handler */
47     int cmd_rule_type;                         /* Type of the rule (#fluid_midi_router_rule_type) */
48 };
49 
50 
51 struct _fluid_shell_t
52 {
53     fluid_settings_t *settings;
54     fluid_cmd_handler_t *handler;
55     fluid_thread_t *thread;
56     fluid_istream_t in;
57     fluid_ostream_t out;
58 };
59 
60 
61 static fluid_thread_return_t fluid_shell_run(void *data);
62 static void fluid_shell_init(fluid_shell_t *shell,
63                              fluid_settings_t *settings, fluid_cmd_handler_t *handler,
64                              fluid_istream_t in, fluid_ostream_t out);
65 static int fluid_handle_voice_count(void *data, int ac, char **av,
66                                     fluid_ostream_t out);
67 
fluid_shell_settings(fluid_settings_t * settings)68 void fluid_shell_settings(fluid_settings_t *settings)
69 {
70     fluid_settings_register_str(settings, "shell.prompt", "", 0);
71     fluid_settings_register_int(settings, "shell.port", 9800, 1, 65535, 0);
72 }
73 
74 
75 /** the table of all handled commands */
76 
77 static const fluid_cmd_t fluid_commands[] =
78 {
79     /* general commands */
80     {
81         "help", "general", fluid_handle_help,
82         "help                       Shows help topics ('help TOPIC' for more info)"
83     },
84     {
85         "quit", "general", fluid_handle_quit,
86         "quit                       Quit the synthesizer"
87     },
88     {
89         "source", "general", fluid_handle_source,
90         "source filename            Loads a file and parse every line as a command"
91     },
92     /* event commands */
93     {
94         "noteon", "event", fluid_handle_noteon,
95         "noteon chan key vel        Sends noteon"
96     },
97     {
98         "noteoff", "event", fluid_handle_noteoff,
99         "noteoff chan key           Sends noteoff"
100     },
101     {
102         "pitch_bend", "event", fluid_handle_pitch_bend,
103         "pitch_bend chan offset     Bends pitch"
104     },
105     {
106         "pitch_bend_range", "event", fluid_handle_pitch_bend_range,
107         "pitch_bend_range chn range Sets pitch bend range for the given midi channel"
108     },
109     {
110         "cc", "event", fluid_handle_cc,
111         "cc chan ctrl value         Sends control-change message"
112     },
113     {
114         "prog", "event", fluid_handle_prog,
115         "prog chan num              Sends program-change message"
116     },
117     {
118         "select", "event", fluid_handle_select,
119         "select chan sfont bank prog  Combination of bank-select and program-change"
120     },
121     {
122         "load", "general", fluid_handle_load,
123         "load file [reset] [bankofs] Loads SoundFont (reset=0|1, def 1; bankofs=n, def 0)"
124     },
125     {
126         "unload", "general", fluid_handle_unload,
127         "unload id [reset]          Unloads SoundFont by ID (reset=0|1, default 1)"
128     },
129     {
130         "reload", "general", fluid_handle_reload,
131         "reload id                  Reload the SoundFont with the specified ID"
132     },
133     {
134         "fonts", "general", fluid_handle_fonts,
135         "fonts                      Display the list of loaded SoundFonts"
136     },
137     {
138         "inst", "general", fluid_handle_inst,
139         "inst font                  Print out the available instruments for the font"
140     },
141     {
142         "channels", "general", fluid_handle_channels,
143         "channels [-verbose]        Print out preset of all channels"
144     },
145     {
146         "interp", "general", fluid_handle_interp,
147         "interp num                 Choose interpolation method for all channels"
148     },
149     {
150         "interpc", "general", fluid_handle_interpc,
151         "interpc chan num           Choose interpolation method for one channel"
152     },
153     /* polymono commands */
154     {
155         "basicchannels", "polymono", fluid_handle_basicchannels,
156         "basicchannels                         Prints the list of basic channels"
157     },
158     {
159         "resetbasicchannels", "polymono", fluid_handle_resetbasicchannels,
160         "resetbasicchannels [chan1 chan2..]    Resets all or some basic channels"
161     },
162     {
163         "setbasicchannels", "polymono", fluid_handle_setbasicchannels,
164         "setbasicchannels [chan mode val...]   Sets default, adds basic channels"
165     },
166     {
167         "channelsmode", "polymono", fluid_handle_channelsmode,
168         "channelsmode [chan1 chan2..]          Prints channels mode"
169     },
170     {
171         "legatomode", "polymono", fluid_handle_legatomode,
172         "legatomode [chan1 chan2..]            Prints channels legato mode"
173     },
174     {
175         "setlegatomode", "polymono", fluid_handle_setlegatomode,
176         "setlegatomode chan mode [chan mode..] Sets legato mode"
177     },
178     {
179         "portamentomode", "polymono", fluid_handle_portamentomode,
180         "portamentomode [chan1 chan2..]        Prints channels portamento mode"
181     },
182     {
183         "setportamentomode", "polymono", fluid_handle_setportamentomode,
184         "setportamentomode chan mode [chan mode..] Sets portamento mode"
185     },
186     {
187         "breathmode", "polymono", fluid_handle_breathmode,
188         "breathmode [chan1 chan2..]            Prints channels breath mode"
189     },
190     {
191         "setbreathmode", "polymono", fluid_handle_setbreathmode,
192         "setbreathmode chan poly(1/0) mono(1/0) breath_sync(1/0) [..] Sets breath mode"
193     },
194     /* reverb commands */
195     {
196         "rev_preset", "reverb", fluid_handle_reverbpreset,
197         "rev_preset num              Load preset num into all reverb unit"
198     },
199     {
200         "rev_setroomsize", "reverb", fluid_handle_reverbsetroomsize,
201         "rev_setroomsize [group] num Set room size of all or one reverb group to num"
202     },
203     {
204         "rev_setdamp", "reverb", fluid_handle_reverbsetdamp,
205         "rev_setdamp [group] num     Set damping of all or one reverb group to num"
206     },
207     {
208         "rev_setwidth", "reverb", fluid_handle_reverbsetwidth,
209         "rev_setwidth [group] num    Set width of all or one reverb group to num"
210     },
211     {
212         "rev_setlevel", "reverb", fluid_handle_reverbsetlevel,
213         "rev_setlevel [group] num    Set output level of all or one reverb group to num"
214     },
215     {
216         "reverb", "reverb", fluid_handle_reverb,
217         "reverb [group] 0|1|on|off   Turn all or one reverb group on or off"
218     },
219     /* chorus commands */
220     {
221         "cho_set_nr", "chorus", fluid_handle_chorusnr,
222         "cho_set_nr [group] n        Set n delay lines (default 3) in all or one chorus group"
223     },
224     {
225         "cho_set_level", "chorus", fluid_handle_choruslevel,
226         "cho_set_level [group] num   Set output level of all or one chorus group to num"
227     },
228     {
229         "cho_set_speed", "chorus", fluid_handle_chorusspeed,
230         "cho_set_speed [group] num   Set mod speed of all or one chorus group to num (Hz)"
231     },
232     {
233         "cho_set_depth", "chorus", fluid_handle_chorusdepth,
234         "cho_set_depth [group] num   Set modulation depth of all or one chorus group to num (ms)"
235     },
236     {
237         "chorus", "chorus", fluid_handle_chorus,
238         "chorus [group] 0|1|on|off   Turn all or one chorus group on or off"
239     },
240     {
241         "gain", "general", fluid_handle_gain,
242         "gain value                 Set the master gain (0 < gain < 5)"
243     },
244     {
245         "voice_count", "general", fluid_handle_voice_count,
246         "voice_count                Get number of active synthesis voices"
247     },
248     /* tuning commands */
249     {
250         "tuning", "tuning", fluid_handle_tuning,
251         "tuning name bank prog      Create a tuning with name, bank number, \n"
252         "                           and program number (0 <= bank,prog <= 127)"
253     },
254     {
255         "tune", "tuning", fluid_handle_tune,
256         "tune bank prog key pitch   Tune a key"
257     },
258     {
259         "settuning", "tuning", fluid_handle_settuning,
260         "settuning chan bank prog   Set the tuning for a MIDI channel"
261     },
262     {
263         "resettuning", "tuning", fluid_handle_resettuning,
264         "resettuning chan           Restore the default tuning of a MIDI channel"
265     },
266     {
267         "tunings", "tuning", fluid_handle_tunings,
268         "tunings                    Print the list of available tunings"
269     },
270     {
271         "dumptuning", "tuning", fluid_handle_dumptuning,
272         "dumptuning bank prog       Print the pitch details of the tuning"
273     },
274     {
275         "reset", "general", fluid_handle_reset,
276         "reset                      System reset (all notes off, reset controllers)"
277     },
278     /* settings commands */
279     {
280         "set", "settings", fluid_handle_set,
281         "set name value             Set the value of a setting (must be a real-time setting to take effect immediately)"
282     },
283     {
284         "get", "settings", fluid_handle_get,
285         "get name                   Get the value of a setting"
286     },
287     {
288         "info", "settings", fluid_handle_info,
289         "info name                  Get information about a setting"
290     },
291     {
292         "settings", "settings", fluid_handle_settings,
293         "settings                   Print out all settings"
294     },
295     {
296         "echo", "general", fluid_handle_echo,
297         "echo arg                   Print arg"
298     },
299     /* Sleep command, useful to insert a delay between commands */
300     {
301         "sleep", "general", fluid_handle_sleep,
302         "sleep  duration            sleep duration (in ms)"
303     },
304     /* LADSPA-related commands */
305 #ifdef LADSPA
306     {
307         "ladspa_effect", "ladspa", fluid_handle_ladspa_effect,
308         "ladspa_effect              Create a new effect from a LADSPA plugin"
309     },
310     {
311         "ladspa_link", "ladspa", fluid_handle_ladspa_link,
312         "ladspa_link                Connect an effect port to a host port or buffer"
313     },
314     {
315         "ladspa_buffer", "ladspa", fluid_handle_ladspa_buffer,
316         "ladspa_buffer              Create a LADSPA buffer"
317     },
318     {
319         "ladspa_set", "ladspa", fluid_handle_ladspa_set,
320         "ladspa_set                 Set the value of an effect control port"
321     },
322     {
323         "ladspa_check", "ladspa", fluid_handle_ladspa_check,
324         "ladspa_check               Check LADSPA configuration"
325     },
326     {
327         "ladspa_start", "ladspa", fluid_handle_ladspa_start,
328         "ladspa_start               Start LADSPA effects"
329     },
330     {
331         "ladspa_stop", "ladspa", fluid_handle_ladspa_stop,
332         "ladspa_stop                Stop LADSPA effect unit"
333     },
334     {
335         "ladspa_reset", "ladspa", fluid_handle_ladspa_reset,
336         "ladspa_reset               Stop and reset LADSPA effects"
337     },
338 #endif
339     /* router commands */
340     {
341         "router_clear", "router", fluid_handle_router_clear,
342         "router_clear               Clears all routing rules from the midi router"
343     },
344     {
345         "router_default", "router", fluid_handle_router_default,
346         "router_default             Resets the midi router to default state"
347     },
348     {
349         "router_begin", "router", fluid_handle_router_begin,
350         "router_begin [note|cc|prog|pbend|cpress|kpress]: Starts a new routing rule"
351     },
352     {
353         "router_chan", "router", fluid_handle_router_chan,
354         "router_chan min max mul add      filters and maps midi channels on current rule"
355     },
356     {
357         "router_par1", "router", fluid_handle_router_par1,
358         "router_par1 min max mul add      filters and maps parameter 1 (key/ctrl nr)"
359     },
360     {
361         "router_par2", "router", fluid_handle_router_par2,
362         "router_par2 min max mul add      filters and maps parameter 2 (vel/cc val)"
363     },
364     {
365         "router_end", "router", fluid_handle_router_end,
366         "router_end                 closes and commits the current routing rule"
367     },
368     /* Midi file player commands */
369     {
370         "player_start", "player", fluid_handle_player_start,
371         "player_start               Start playing from the beginning of current song"
372     },
373     {
374         "player_stop", "player", fluid_handle_player_stop,
375         "player_stop                Stop playing (cannot be executed in a user command file)"
376     },
377     {
378         "player_cont", "player", fluid_handle_player_continue,
379         "player_cont                Continue playing (cannot be executed in a user command file)"
380     },
381     {
382         "player_seek", "player", fluid_handle_player_seek,
383         "player_seek num            Move forward/backward in current song to +/-num ticks"
384     },
385     {
386         "player_next", "player", fluid_handle_player_next_song,
387         "player_next                Move to next song (cannot be executed in a user command file)"
388     },
389     {
390         "player_loop", "player", fluid_handle_player_loop,
391         "player_loop num            Set loop number to num (-1 = loop forever)"
392     },
393     {
394         "player_tempo_bpm", "player", fluid_handle_player_tempo_bpm,
395         "player_tempo_bpm num       Set tempo to num beats per minute"
396     },
397     {
398         "player_tempo_int", "player", fluid_handle_player_tempo_int,
399         "player_tempo_int [mul]     Set internal tempo multiplied by mul (default mul=1.0)"
400     },
401 #if WITH_PROFILING
402     /* Profiling commands */
403     {
404         "profile", "profile", fluid_handle_profile,
405         "profile                        Prints default parameters used by prof_start"
406     },
407     {
408         "prof_set_notes", "profile",  fluid_handle_prof_set_notes,
409         "prof_set_notes nbr [bank prog] Sets notes number generated by prof_start"
410     },
411     {
412         "prof_set_print", "profile",  fluid_handle_prof_set_print,
413         "prof_set_print mode            Sets print mode (0:simple, 1:full infos)"
414     },
415     {
416         "prof_start", "profile",      fluid_handle_prof_start,
417         "prof_start [n_prof [dur]]      Starts n_prof measures of duration(ms) each"
418     }
419 #endif
420 };
421 
422 /**
423  * Process a string command.
424  *
425  * @param handler FluidSynth command handler
426  * @param cmd Command string (NOTE: Gets modified by FluidSynth prior to 1.0.8)
427  * @param out Output stream to display command response to
428  * @return Integer value corresponding to: -1 on command error, 0 on success,
429  *   1 if 'cmd' is a comment or is empty and -2 if quit was issued
430  *
431  * @note FluidSynth 1.0.8 and above no longer modifies the 'cmd' string.
432  */
433 int
fluid_command(fluid_cmd_handler_t * handler,const char * cmd,fluid_ostream_t out)434 fluid_command(fluid_cmd_handler_t *handler, const char *cmd, fluid_ostream_t out)
435 {
436     int result, num_tokens = 0;
437     char **tokens = NULL;
438 
439     if(cmd[0] == '#' || cmd[0] == '\0')
440     {
441         return 1;
442     }
443 
444     if(!g_shell_parse_argv(cmd, &num_tokens, &tokens, NULL))
445     {
446         fluid_ostream_printf(out, "Error parsing command\n");
447         return FLUID_FAILED;
448     }
449 
450     result = fluid_cmd_handler_handle(handler, num_tokens, &tokens[0], out);
451     g_strfreev(tokens);
452 
453     return result;
454 }
455 
456 /**
457  * Create a new FluidSynth command shell.
458  *
459  * @param settings Setting parameters to use with the shell
460  * @param handler Command handler
461  * @param in Input stream
462  * @param out Output stream
463  * @param thread TRUE if shell should be run in a separate thread, FALSE to run
464  *   it in the current thread (function blocks until "quit")
465  * @return New shell instance or NULL on error
466  */
467 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)468 new_fluid_shell(fluid_settings_t *settings, fluid_cmd_handler_t *handler,
469                 fluid_istream_t in, fluid_ostream_t out, int thread)
470 {
471     fluid_shell_t *shell = FLUID_NEW(fluid_shell_t);
472 
473     if(shell == NULL)
474     {
475         FLUID_LOG(FLUID_PANIC, "Out of memory");
476         return NULL;
477     }
478 
479 
480     fluid_shell_init(shell, settings, handler, in, out);
481 
482     if(thread)
483     {
484         shell->thread = new_fluid_thread("shell", fluid_shell_run, shell,
485                                          0, TRUE);
486 
487         if(shell->thread == NULL)
488         {
489             delete_fluid_shell(shell);
490             return NULL;
491         }
492     }
493     else
494     {
495         shell->thread = NULL;
496         fluid_shell_run(shell);
497     }
498 
499     return shell;
500 }
501 
502 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)503 fluid_shell_init(fluid_shell_t *shell,
504                  fluid_settings_t *settings, fluid_cmd_handler_t *handler,
505                  fluid_istream_t in, fluid_ostream_t out)
506 {
507     shell->settings = settings;
508     shell->handler = handler;
509     shell->in = in;
510     shell->out = out;
511 }
512 
513 /**
514  * Delete a FluidSynth command shell.
515  * @param shell Command shell instance
516  */
517 void
delete_fluid_shell(fluid_shell_t * shell)518 delete_fluid_shell(fluid_shell_t *shell)
519 {
520     fluid_return_if_fail(shell != NULL);
521 
522     if(shell->thread != NULL)
523     {
524         delete_fluid_thread(shell->thread);
525     }
526 
527     FLUID_FREE(shell);
528 }
529 
530 static fluid_thread_return_t
fluid_shell_run(void * data)531 fluid_shell_run(void *data)
532 {
533     fluid_shell_t *shell = (fluid_shell_t *)data;
534     char workline[FLUID_WORKLINELENGTH];
535     char *prompt = NULL;
536     int cont = 1;
537     int errors = FALSE;
538     int n;
539 
540     if(shell->settings)
541     {
542         fluid_settings_dupstr(shell->settings, "shell.prompt", &prompt);    /* ++ alloc prompt */
543     }
544 
545     /* handle user input */
546     while(cont)
547     {
548 
549         n = fluid_istream_readline(shell->in, shell->out, prompt ? prompt : "", workline, FLUID_WORKLINELENGTH);
550 
551         if(n < 0)
552         {
553             FLUID_LOG(FLUID_PANIC, "An error occurred while reading from stdin.");
554             break;
555         }
556 
557         /* handle the command */
558         switch(fluid_command(shell->handler, workline, shell->out))
559         {
560 
561         case 1: /* empty line or comment */
562             break;
563 
564         case FLUID_FAILED: /* erroneous command */
565             errors = TRUE;
566 
567         case FLUID_OK: /* valid command */
568             break;
569 
570         case -2: /* quit */
571             cont = 0;
572             break;
573         }
574 
575         if(n == 0)
576         {
577             if(shell->settings)
578             {
579                 FLUID_LOG(FLUID_INFO, "Received EOF while reading commands, exiting the shell.");
580             }
581             break;
582         }
583     }
584 
585     FLUID_FREE(prompt);    /* -- free prompt */
586 
587     /* return FLUID_THREAD_RETURN_VALUE on success, something else on failure */
588     return errors ? (fluid_thread_return_t)(-1) : FLUID_THREAD_RETURN_VALUE;
589 }
590 
591 /**
592  * A convenience function to create a shell interfacing to standard input/output
593  * console streams.
594  *
595  * @param settings Settings instance for the shell
596  * @param handler Command handler callback
597  *
598  * The shell is run in the current thread, this function will only
599  * return after the \c quit command has been issued.
600  */
601 void
fluid_usershell(fluid_settings_t * settings,fluid_cmd_handler_t * handler)602 fluid_usershell(fluid_settings_t *settings, fluid_cmd_handler_t *handler)
603 {
604     fluid_shell_t shell;
605     fluid_shell_init(&shell, settings, handler, fluid_get_stdin(), fluid_get_stdout());
606     fluid_shell_run(&shell);
607 }
608 
609 /**
610  * Execute shell commands in a file.
611  *
612  * @param handler Command handler callback
613  * @param filename File name
614  * @return 0 on success, a negative value on error
615  */
616 int
fluid_source(fluid_cmd_handler_t * handler,const char * filename)617 fluid_source(fluid_cmd_handler_t *handler, const char *filename)
618 {
619     int file;
620     fluid_shell_t shell;
621     int result;
622 
623 #ifdef WIN32
624     file = _open(filename, _O_RDONLY);
625 #else
626     file = open(filename, O_RDONLY);
627 #endif
628 
629     if(file < 0)
630     {
631         return file;
632     }
633 
634     fluid_shell_init(&shell, NULL, handler, file, fluid_get_stdout());
635     result = (fluid_shell_run(&shell) == FLUID_THREAD_RETURN_VALUE) ? 0 : -1;
636 
637 #ifdef WIN32
638     _close(file);
639 #else
640     close(file);
641 #endif
642 
643     return result;
644 }
645 
646 /**
647  * Get the user specific FluidSynth command file name.
648  *
649  * @param buf Caller supplied string buffer to store file name to.
650  * @param len Length of \a buf
651  * @return Returns \a buf pointer or NULL if no user command file for this system type.
652  *
653  * On Windows this is currently @c "%USERPROFILE%\fluidsynth.cfg".
654  * For anything else (except MACOS9) @c "$HOME/.fluidsynth".
655  */
656 char *
fluid_get_userconf(char * buf,int len)657 fluid_get_userconf(char *buf, int len)
658 {
659     const char *home = NULL;
660     const char *config_file;
661 #if defined(WIN32)
662     home = getenv("USERPROFILE");
663     config_file = "\\fluidsynth.cfg";
664 
665 #elif !defined(MACOS9)
666     home = getenv("HOME");
667     config_file = "/.fluidsynth";
668 
669 #endif
670 
671     if(home == NULL)
672     {
673         return NULL;
674     }
675     else
676     {
677         FLUID_SNPRINTF(buf, len, "%s%s", home, config_file);
678         return buf;
679     }
680 }
681 
682 /**
683  * Get the system FluidSynth command file name.
684  *
685  * @param buf Caller supplied string buffer to store file name to.
686  * @param len Length of \a buf
687  * @return Returns \a buf pointer or NULL if no system command file for this system type.
688  *
689  * Windows and MACOS9 do not have a system-wide config file currently. For anything else it
690  * returns @c "/etc/fluidsynth.conf".
691  */
692 char *
fluid_get_sysconf(char * buf,int len)693 fluid_get_sysconf(char *buf, int len)
694 {
695 #if defined(WIN32) || defined(MACOS9)
696     return NULL;
697 #else
698     FLUID_SNPRINTF(buf, len, "/etc/fluidsynth.conf");
699     return buf;
700 #endif
701 }
702 
703 
704 /*
705  *  handlers
706  */
707 int
fluid_handle_noteon(void * data,int ac,char ** av,fluid_ostream_t out)708 fluid_handle_noteon(void *data, int ac, char **av, fluid_ostream_t out)
709 {
710     FLUID_ENTRY_COMMAND(data);
711 
712     if(ac < 3)
713     {
714         fluid_ostream_printf(out, "noteon: too few arguments\n");
715         return FLUID_FAILED;
716     }
717 
718     if(!fluid_is_number(av[0]) || !fluid_is_number(av[1]) || !fluid_is_number(av[2]))
719     {
720         fluid_ostream_printf(out, "noteon: invalid argument\n");
721         return FLUID_FAILED;
722     }
723 
724     return fluid_synth_noteon(handler->synth, atoi(av[0]), atoi(av[1]), atoi(av[2]));
725 }
726 
727 int
fluid_handle_noteoff(void * data,int ac,char ** av,fluid_ostream_t out)728 fluid_handle_noteoff(void *data, int ac, char **av, fluid_ostream_t out)
729 {
730     FLUID_ENTRY_COMMAND(data);
731 
732     if(ac < 2)
733     {
734         fluid_ostream_printf(out, "noteoff: too few arguments\n");
735         return FLUID_FAILED;
736     }
737 
738     if(!fluid_is_number(av[0]) || !fluid_is_number(av[1]))
739     {
740         fluid_ostream_printf(out, "noteon: invalid argument\n");
741         return FLUID_FAILED;
742     }
743 
744     return fluid_synth_noteoff(handler->synth, atoi(av[0]), atoi(av[1]));
745 }
746 
747 int
fluid_handle_pitch_bend(void * data,int ac,char ** av,fluid_ostream_t out)748 fluid_handle_pitch_bend(void *data, int ac, char **av, fluid_ostream_t out)
749 {
750     FLUID_ENTRY_COMMAND(data);
751 
752     if(ac < 2)
753     {
754         fluid_ostream_printf(out, "pitch_bend: too few arguments\n");
755         return FLUID_FAILED;
756     }
757 
758     if(!fluid_is_number(av[0]) || !fluid_is_number(av[1]))
759     {
760         fluid_ostream_printf(out, "pitch_bend: invalid argument\n");
761         return FLUID_FAILED;
762     }
763 
764     return fluid_synth_pitch_bend(handler->synth, atoi(av[0]), atoi(av[1]));
765 }
766 
767 int
fluid_handle_pitch_bend_range(void * data,int ac,char ** av,fluid_ostream_t out)768 fluid_handle_pitch_bend_range(void *data, int ac, char **av, fluid_ostream_t out)
769 {
770     FLUID_ENTRY_COMMAND(data);
771     int channum;
772     int value;
773 
774     if(ac < 2)
775     {
776         fluid_ostream_printf(out, "pitch_bend_range: too few arguments\n");
777         return FLUID_FAILED;
778     }
779 
780     if(!fluid_is_number(av[0]) || !fluid_is_number(av[1]))
781     {
782         fluid_ostream_printf(out, "pitch_bend_range: invalid argument\n");
783         return FLUID_FAILED;
784     }
785 
786     channum = atoi(av[0]);
787     value = atoi(av[1]);
788     fluid_channel_set_pitch_wheel_sensitivity(handler->synth->channel[channum], value);
789     return FLUID_OK;
790 }
791 
792 int
fluid_handle_cc(void * data,int ac,char ** av,fluid_ostream_t out)793 fluid_handle_cc(void *data, int ac, char **av, fluid_ostream_t out)
794 {
795     FLUID_ENTRY_COMMAND(data);
796 
797     if(ac < 3)
798     {
799         fluid_ostream_printf(out, "cc: too few arguments\n");
800         return FLUID_FAILED;
801     }
802 
803     if(!fluid_is_number(av[0]) || !fluid_is_number(av[1]) || !fluid_is_number(av[2]))
804     {
805         fluid_ostream_printf(out, "cc: invalid argument\n");
806         return FLUID_FAILED;
807     }
808 
809     return fluid_synth_cc(handler->synth, atoi(av[0]), atoi(av[1]), atoi(av[2]));
810 }
811 
812 int
fluid_handle_prog(void * data,int ac,char ** av,fluid_ostream_t out)813 fluid_handle_prog(void *data, int ac, char **av, fluid_ostream_t out)
814 {
815     FLUID_ENTRY_COMMAND(data);
816 
817     if(ac < 2)
818     {
819         fluid_ostream_printf(out, "prog: too few arguments\n");
820         return FLUID_FAILED;
821     }
822 
823     if(!fluid_is_number(av[0]) || !fluid_is_number(av[1]))
824     {
825         fluid_ostream_printf(out, "prog: invalid argument\n");
826         return FLUID_FAILED;
827     }
828 
829     return fluid_synth_program_change(handler->synth, atoi(av[0]), atoi(av[1]));
830 }
831 
832 int
fluid_handle_select(void * data,int ac,char ** av,fluid_ostream_t out)833 fluid_handle_select(void *data, int ac, char **av, fluid_ostream_t out)
834 {
835     FLUID_ENTRY_COMMAND(data);
836     int sfont_id;
837     int chan;
838     int bank;
839     int prog;
840 
841     if(ac < 4)
842     {
843         fluid_ostream_printf(out, "preset: too few arguments\n");
844         return FLUID_FAILED;
845     }
846 
847     if(!fluid_is_number(av[0]) || !fluid_is_number(av[1])
848             || !fluid_is_number(av[2]) || !fluid_is_number(av[3]))
849     {
850         fluid_ostream_printf(out, "preset: invalid argument\n");
851         return FLUID_FAILED;
852     }
853 
854     chan = atoi(av[0]);
855     sfont_id = atoi(av[1]);
856     bank = atoi(av[2]);
857     prog = atoi(av[3]);
858 
859     if(sfont_id != 0)
860     {
861         return fluid_synth_program_select(handler->synth, chan, sfont_id, bank, prog);
862     }
863     else
864     {
865         if(fluid_synth_bank_select(handler->synth, chan, bank) == FLUID_OK)
866         {
867             return fluid_synth_program_change(handler->synth, chan, prog);
868         }
869 
870         return FLUID_FAILED;
871     }
872 }
873 
874 int
fluid_handle_inst(void * data,int ac,char ** av,fluid_ostream_t out)875 fluid_handle_inst(void *data, int ac, char **av, fluid_ostream_t out)
876 {
877     FLUID_ENTRY_COMMAND(data);
878     int font;
879     fluid_sfont_t *sfont;
880     fluid_preset_t *preset;
881     int offset;
882 
883     if(ac < 1)
884     {
885         fluid_ostream_printf(out, "inst: too few arguments\n");
886         return FLUID_FAILED;
887     }
888 
889     if(!fluid_is_number(av[0]))
890     {
891         fluid_ostream_printf(out, "inst: invalid argument\n");
892         return FLUID_FAILED;
893     }
894 
895     font = atoi(av[0]);
896 
897     sfont = fluid_synth_get_sfont_by_id(handler->synth, font);
898     offset = fluid_synth_get_bank_offset(handler->synth, font);
899 
900     if(sfont == NULL)
901     {
902         fluid_ostream_printf(out, "inst: invalid font number\n");
903         return FLUID_FAILED;
904     }
905 
906     fluid_sfont_iteration_start(sfont);
907 
908     while((preset = fluid_sfont_iteration_next(sfont)) != NULL)
909     {
910         fluid_ostream_printf(out, "%03d-%03d %s\n",
911                              fluid_preset_get_banknum(preset) + offset,
912                              fluid_preset_get_num(preset),
913                              fluid_preset_get_name(preset));
914     }
915 
916     return FLUID_OK;
917 }
918 
919 
920 int
fluid_handle_channels(void * data,int ac,char ** av,fluid_ostream_t out)921 fluid_handle_channels(void *data, int ac, char **av, fluid_ostream_t out)
922 {
923     FLUID_ENTRY_COMMAND(data);
924     fluid_preset_t *preset;
925     int verbose = 0;
926     int i;
927 
928     if(ac > 0 && FLUID_STRCMP(av[0], "-verbose") == 0)
929     {
930         verbose = 1;
931     }
932 
933     for(i = 0; i < fluid_synth_count_midi_channels(handler->synth); i++)
934     {
935         preset = fluid_synth_get_channel_preset(handler->synth, i);
936 
937         if(preset == NULL)
938         {
939             fluid_ostream_printf(out, "chan %d, no preset\n", i);
940         }
941         else if(!verbose)
942         {
943             fluid_ostream_printf(out, "chan %d, %s\n", i, fluid_preset_get_name(preset));
944         }
945         else
946         {
947             fluid_ostream_printf(out,
948                                  "chan %d, sfont %d, bank %d, preset %d, %s\n",
949                                  i,
950                                  fluid_sfont_get_id(preset->sfont),
951                                  fluid_preset_get_banknum(preset),
952                                  fluid_preset_get_num(preset),
953                                  fluid_preset_get_name(preset));
954         }
955     }
956 
957     return FLUID_OK;
958 }
959 
960 int
fluid_handle_load(void * data,int ac,char ** av,fluid_ostream_t out)961 fluid_handle_load(void *data, int ac, char **av, fluid_ostream_t out)
962 {
963     FLUID_ENTRY_COMMAND(data);
964     char buf[1024];
965     int id;
966     int reset = 1;
967     int offset = 0;
968 
969     if(ac < 1)
970     {
971         fluid_ostream_printf(out, "load: too few arguments\n");
972         return FLUID_FAILED;
973     }
974 
975     if(ac == 2)
976     {
977         reset = atoi(av[1]);
978     }
979 
980     if(ac == 3)
981     {
982         offset = atoi(av[2]);
983     }
984 
985     /* Load the SoundFont without resetting the programs. The reset will
986      * be done later (if requested). */
987     id = fluid_synth_sfload(handler->synth, fluid_expand_path(av[0], buf, 1024), 0);
988 
989     if(id == -1)
990     {
991         fluid_ostream_printf(out, "failed to load the SoundFont\n");
992         return FLUID_FAILED;
993     }
994     else
995     {
996         fluid_ostream_printf(out, "loaded SoundFont has ID %d\n", id);
997     }
998 
999     if(offset)
1000     {
1001         fluid_synth_set_bank_offset(handler->synth, id, offset);
1002     }
1003 
1004     /* The reset should be done after the offset is set. */
1005     if(reset)
1006     {
1007         fluid_synth_program_reset(handler->synth);
1008     }
1009 
1010     return FLUID_OK;
1011 }
1012 
1013 int
fluid_handle_unload(void * data,int ac,char ** av,fluid_ostream_t out)1014 fluid_handle_unload(void *data, int ac, char **av, fluid_ostream_t out)
1015 {
1016     FLUID_ENTRY_COMMAND(data);
1017     int reset = 1;
1018 
1019     if(ac < 1)
1020     {
1021         fluid_ostream_printf(out, "unload: too few arguments\n");
1022         return FLUID_FAILED;
1023     }
1024 
1025     if(!fluid_is_number(av[0]))
1026     {
1027         fluid_ostream_printf(out, "unload: expected a number as argument\n");
1028         return FLUID_FAILED;
1029     }
1030 
1031     if(ac == 2)
1032     {
1033         reset = atoi(av[1]);
1034     }
1035 
1036     if(fluid_synth_sfunload(handler->synth, atoi(av[0]), reset) != 0)
1037     {
1038         fluid_ostream_printf(out, "failed to unload the SoundFont\n");
1039         return FLUID_FAILED;
1040     }
1041 
1042     return FLUID_OK;
1043 }
1044 
1045 int
fluid_handle_reload(void * data,int ac,char ** av,fluid_ostream_t out)1046 fluid_handle_reload(void *data, int ac, char **av, fluid_ostream_t out)
1047 {
1048     FLUID_ENTRY_COMMAND(data);
1049 
1050     if(ac < 1)
1051     {
1052         fluid_ostream_printf(out, "reload: too few arguments\n");
1053         return FLUID_FAILED;
1054     }
1055 
1056     if(!fluid_is_number(av[0]))
1057     {
1058         fluid_ostream_printf(out, "reload: expected a number as argument\n");
1059         return FLUID_FAILED;
1060     }
1061 
1062     if(fluid_synth_sfreload(handler->synth, atoi(av[0])) == -1)
1063     {
1064         fluid_ostream_printf(out, "failed to reload the SoundFont\n");
1065         return FLUID_FAILED;
1066     }
1067 
1068     return FLUID_OK;
1069 }
1070 
1071 
1072 int
fluid_handle_fonts(void * data,int ac,char ** av,fluid_ostream_t out)1073 fluid_handle_fonts(void *data, int ac, char **av, fluid_ostream_t out)
1074 {
1075     FLUID_ENTRY_COMMAND(data);
1076     int i;
1077     fluid_sfont_t *sfont;
1078     int num;
1079 
1080     num = fluid_synth_sfcount(handler->synth);
1081 
1082     if(num == 0)
1083     {
1084         fluid_ostream_printf(out, "no SoundFont loaded (try load)\n");
1085         return FLUID_OK;
1086     }
1087 
1088     fluid_ostream_printf(out, "ID  Name\n");
1089 
1090     for(i = 0; i < num; i++)
1091     {
1092         sfont = fluid_synth_get_sfont(handler->synth, i);
1093 
1094         if(sfont)
1095         {
1096             fluid_ostream_printf(out, "%2d  %s\n",
1097                                  fluid_sfont_get_id(sfont),
1098                                  fluid_sfont_get_name(sfont));
1099         }
1100         else
1101         {
1102             fluid_ostream_printf(out, "sfont is \"NULL\" for index %d\n", i);
1103         }
1104     }
1105 
1106     return FLUID_OK;
1107 }
1108 
1109 /* Purpose:
1110  * Response to 'rev_preset' command.
1111  * Load the values from a reverb preset into the reverb unit. */
1112 int
fluid_handle_reverbpreset(void * data,int ac,char ** av,fluid_ostream_t out)1113 fluid_handle_reverbpreset(void *data, int ac, char **av, fluid_ostream_t out)
1114 {
1115     FLUID_ENTRY_COMMAND(data);
1116     int reverb_preset_number;
1117 
1118     fluid_ostream_printf(out, "rev_preset is deprecated and will be removed in a future release!\n");
1119 
1120     if(ac < 1)
1121     {
1122         fluid_ostream_printf(out, "rev_preset: too few arguments\n");
1123         return FLUID_FAILED;
1124     }
1125 
1126     reverb_preset_number = atoi(av[0]);
1127 
1128     if(fluid_synth_set_reverb_preset(handler->synth, reverb_preset_number) != FLUID_OK)
1129     {
1130         fluid_ostream_printf(out, "rev_preset: Failed. Parameter out of range?\n");
1131         return FLUID_FAILED;
1132     };
1133 
1134     return FLUID_OK;
1135 }
1136 
1137 /*
1138   The function is useful for reverb and chorus commands which have
1139   1 or 2 parameters.
1140   The function checks that there is 1 or 2 arguments.
1141   When there is 2 parameters it checks the first argument that must be
1142   an fx group index in the range[0..synth->effects_groups-1].
1143 
1144   return the group index:
1145   -1, when the command is for all fx groups.
1146   0 to synth->effects_groups-1, when the command is for this group index.
1147   -2 if error.
1148 */
check_fx_group_idx(int ac,char ** av,fluid_ostream_t out,fluid_synth_t * synth,const char * name_cde)1149 static int check_fx_group_idx(int ac, char **av, fluid_ostream_t out,
1150                               fluid_synth_t *synth, const char *name_cde)
1151 {
1152     int fx_group; /* fx unit index */
1153     int ngroups;  /* count of fx groups */
1154 
1155     /* One or 2 arguments allowed */
1156     if(ac < 1 || ac > 2)
1157     {
1158         fluid_ostream_printf(out, "%s: needs 1 or 2 arguments\n", name_cde);
1159         return -2;
1160     }
1161 
1162     /* check optional first argument which is a fx group index */
1163     fx_group = -1;
1164 
1165     if(ac > 1)
1166     {
1167         fx_group = atoi(av[0]); /* get fx group index */
1168         ngroups = fluid_synth_count_effects_groups(synth);
1169 
1170         if(!fluid_is_number(av[0]) || fx_group < 0 || fx_group >= ngroups)
1171         {
1172             fluid_ostream_printf(out, "%s: group index \"%s\" must be in range [%d..%d]\n",
1173                                  name_cde, av[0], 0, ngroups - 1);
1174             return -2;
1175         }
1176     }
1177 
1178     return fx_group;
1179 }
1180 
1181 /* parameter value */
1182 struct value
1183 {
1184     const char *name;
1185     double min;
1186     double max;
1187 };
1188 
1189 /*
1190   check 2 arguments for reverb commands : fx group index  , value
1191   - group index must be an integer in the range [-1..synth->effects_groups].
1192   - value must be a double in the range [min..max]
1193   @param param a pointer on a value to return the second value argument.
1194   return the fx group index:
1195   -1 when the command is for all fx group.
1196   0 to synth->effects_groups-1 when the command is for this group index.
1197   -2 if error.
1198 */
check_fx_reverb_param(int ac,char ** av,fluid_ostream_t out,fluid_synth_t * synth,const char * name_cde,const struct value * value,fluid_real_t * param)1199 static int check_fx_reverb_param(int ac, char **av, fluid_ostream_t out,
1200                                  fluid_synth_t *synth, const char *name_cde,
1201                                  const struct value *value,
1202                                  fluid_real_t *param)
1203 {
1204     /* get and check fx group index argument */
1205     int fx_group = check_fx_group_idx(ac, av, out, synth, name_cde);
1206 
1207     if(fx_group >= -1)
1208     {
1209         fluid_real_t val;
1210 
1211         /* get and check value argument */
1212         ac--;
1213         val = atof(av[ac]);
1214 
1215         if(!fluid_is_number(av[ac]) || val < value->min || val > value->max)
1216         {
1217             fluid_ostream_printf(out, "%s: %s \"%s\" must be in range [%f..%f]\n",
1218                                  name_cde, value->name, av[ac], value->min, value->max);
1219             return -2;
1220         }
1221 
1222         *param = val;
1223     }
1224 
1225     return fx_group;
1226 }
1227 
1228 /* Purpose:
1229  * Response to fluid_handle_reverbsetxxxx commands
1230  */
1231 static int
fluid_handle_reverb_command(void * data,int ac,char ** av,fluid_ostream_t out,int param)1232 fluid_handle_reverb_command(void *data, int ac, char **av, fluid_ostream_t out,
1233                             int param)
1234 {
1235     int fx_group;
1236 
1237     /* reverb commands name table */
1238     static const char *const name_cde[FLUID_REVERB_PARAM_LAST] =
1239     {"rev_setroomsize", "rev_setdamp", "rev_setwidth", "rev_setlevel"};
1240 
1241     /* name and min/max values table */
1242     static struct value values[FLUID_REVERB_PARAM_LAST] =
1243     {
1244         {"room size", 0, 0},
1245         {"damp", 0, 0},
1246         {"width", 0, 0},
1247         {"level", 0, 0}
1248     };
1249 
1250     FLUID_ENTRY_COMMAND(data);
1251     fluid_real_t value;
1252 
1253     fluid_settings_getnum_range(handler->settings, "synth.reverb.room-size",
1254                                 &values[FLUID_REVERB_ROOMSIZE].min,
1255                                 &values[FLUID_REVERB_ROOMSIZE].max);
1256 
1257     fluid_settings_getnum_range(handler->settings, "synth.reverb.damp",
1258                                 &values[FLUID_REVERB_DAMP].min,
1259                                 &values[FLUID_REVERB_DAMP].max);
1260 
1261 
1262     fluid_settings_getnum_range(handler->settings, "synth.reverb.width",
1263                                 &values[FLUID_REVERB_WIDTH].min,
1264                                 &values[FLUID_REVERB_WIDTH].max);
1265 
1266     fluid_settings_getnum_range(handler->settings, "synth.reverb.level",
1267                                 &values[FLUID_REVERB_LEVEL].min,
1268                                 &values[FLUID_REVERB_LEVEL].max);
1269 
1270     /* get and check command arguments */
1271     fx_group = check_fx_reverb_param(ac, av, out, handler->synth,
1272                                      name_cde[param], &values[param], &value);
1273 
1274     if(fx_group >= -1)
1275     {
1276         /* run reverb function */
1277         fluid_synth_reverb_set_param(handler->synth, fx_group, param, value);
1278         return FLUID_OK;
1279     }
1280 
1281     return FLUID_FAILED;
1282 }
1283 
1284 /* Purpose:
1285  * Response to 'rev_setroomsize' command.
1286  * Load the new room size into the reverb fx group.
1287  * Example: rev_setroomzize 0 0.5
1288  * load roomsize 0.5 in the reverb fx group at index 0
1289  */
1290 int
fluid_handle_reverbsetroomsize(void * data,int ac,char ** av,fluid_ostream_t out)1291 fluid_handle_reverbsetroomsize(void *data, int ac, char **av, fluid_ostream_t out)
1292 {
1293     return fluid_handle_reverb_command(data, ac, av, out, FLUID_REVERB_ROOMSIZE);
1294 }
1295 
1296 /* Purpose:
1297  * Response to 'rev_setdamp' command.
1298  * Load the new damp factor into the reverb fx group.
1299  * Example: rev_setdamp 1 0.5
1300  * load damp 0.5 in the reverb fx group at index 1
1301  */
1302 int
fluid_handle_reverbsetdamp(void * data,int ac,char ** av,fluid_ostream_t out)1303 fluid_handle_reverbsetdamp(void *data, int ac, char **av, fluid_ostream_t out)
1304 {
1305     return fluid_handle_reverb_command(data, ac, av, out, FLUID_REVERB_DAMP);
1306 }
1307 
1308 /* Purpose:
1309  * Response to 'rev_setwidth' command.
1310  * Load the new width into the reverb fx group.
1311  * Example: rev_setwidth 1 0.5
1312  * load width 0.5 in the reverb fx group at index 1.
1313  */
1314 int
fluid_handle_reverbsetwidth(void * data,int ac,char ** av,fluid_ostream_t out)1315 fluid_handle_reverbsetwidth(void *data, int ac, char **av, fluid_ostream_t out)
1316 {
1317     return fluid_handle_reverb_command(data, ac, av, out, FLUID_REVERB_WIDTH);
1318 }
1319 
1320 /* Purpose:
1321  * Response to 'rev_setlevel' command.
1322  * Load the new level into the reverb fx group.
1323  * Example: rev_setlevel 1 0.5
1324  * load level 0.5 in the reverb fx group at index 1.
1325  */
1326 int
fluid_handle_reverbsetlevel(void * data,int ac,char ** av,fluid_ostream_t out)1327 fluid_handle_reverbsetlevel(void *data, int ac, char **av, fluid_ostream_t out)
1328 {
1329     return fluid_handle_reverb_command(data, ac, av, out, FLUID_REVERB_LEVEL);
1330 }
1331 
1332 /* reverb/chorus on/off commands enum */
1333 enum rev_chor_on_cde
1334 {
1335     REVERB_ON_CDE,
1336     CHORUS_ON_CDE,
1337     NBR_REV_CHOR_ON_CDE
1338 };
1339 
1340 /* Purpose:
1341  * Set one or all reverb/chorus units on or off
1342  */
1343 static int
fluid_handle_reverb_chorus_on_command(void * data,int ac,char ** av,fluid_ostream_t out,enum rev_chor_on_cde cde)1344 fluid_handle_reverb_chorus_on_command(void *data, int ac, char **av, fluid_ostream_t out,
1345                                       enum rev_chor_on_cde cde)
1346 {
1347     /* commands name table */
1348     static const char *const name_cde[NBR_REV_CHOR_ON_CDE] = {"reverb", "chorus"};
1349     /* functions table */
1350     static int (*onoff_func[NBR_REV_CHOR_ON_CDE])(fluid_synth_t *, int, int) =
1351     {
1352         fluid_synth_reverb_on, fluid_synth_chorus_on
1353     };
1354 
1355     FLUID_ENTRY_COMMAND(data);
1356     int onoff;
1357 
1358     /* get and check fx group index argument */
1359     int fx_group = check_fx_group_idx(ac, av, out, handler->synth, name_cde[cde]);
1360 
1361     if(fx_group >= -1)
1362     {
1363         ac--;
1364 
1365         /* check argument value */
1366         if((FLUID_STRCMP(av[ac], "0") == 0) || (FLUID_STRCMP(av[ac], "off") == 0))
1367         {
1368             onoff = 0;
1369         }
1370         else if((FLUID_STRCMP(av[ac], "1") == 0) || (FLUID_STRCMP(av[ac], "on") == 0))
1371         {
1372             onoff = 1;
1373         }
1374         else
1375         {
1376             fluid_ostream_printf(out, "%s: invalid arguments %s [0|1|on|off]\n",
1377                                  name_cde[cde], av[ac]);
1378             return FLUID_FAILED;
1379         }
1380 
1381         /* run on/off function */
1382         return onoff_func[cde](handler->synth, fx_group, onoff);
1383     }
1384 
1385     return FLUID_FAILED;
1386 }
1387 
1388 /* Purpose:
1389  * Response to: reverb [fx group] on command.
1390  * Examples:
1391  * reverb off ,disable all reverb groups.
1392  * reverb 1 on ,enable reverb group at index 1.
1393  */
1394 int
fluid_handle_reverb(void * data,int ac,char ** av,fluid_ostream_t out)1395 fluid_handle_reverb(void *data, int ac, char **av, fluid_ostream_t out)
1396 {
1397     return fluid_handle_reverb_chorus_on_command(data, ac, av, out, REVERB_ON_CDE);
1398 }
1399 
1400 /* Purpose:
1401  * Response to fluid_handle_chorus_xxx commands
1402  */
1403 static int
fluid_handle_chorus_command(void * data,int ac,char ** av,fluid_ostream_t out,int param)1404 fluid_handle_chorus_command(void *data, int ac, char **av, fluid_ostream_t out,
1405                             int param)
1406 {
1407     /* chorus commands name table */
1408     static const char *const name_cde[FLUID_CHORUS_PARAM_LAST - 1] =
1409     {"cho_set_nr", "cho_set_level", "cho_set_speed", "cho_set_depth"};
1410 
1411     /* value name table */
1412     static const char *const name_value[FLUID_CHORUS_PARAM_LAST - 1] =
1413     {"nr", "level", "speed", "depth"};
1414 
1415     /* setting name (except lfo waveform type) */
1416     static const char *name[FLUID_CHORUS_PARAM_LAST-1] =
1417     {
1418         "synth.chorus.nr", "synth.chorus.level",
1419         "synth.chorus.speed", "synth.chorus.depth"
1420     };
1421 
1422     FLUID_ENTRY_COMMAND(data);
1423 
1424     /* get and check index fx group index argument */
1425     int fx_group = check_fx_group_idx(ac, av, out, handler->synth, name_cde[param]);
1426 
1427     if(fx_group >= -1)
1428     {
1429         double value;
1430         /* get and check value argument */
1431         ac--;
1432 
1433         if(!fluid_is_number(av[ac]))
1434         {
1435             fluid_ostream_printf(out, "%s: %s \"%s\" must be a number\n",
1436                                  name_cde[param], name_value[param], av[ac]);
1437             return FLUID_FAILED;
1438         }
1439 
1440         if(param == FLUID_CHORUS_NR) /* commands with integer parameter */
1441         {
1442             int min, max;
1443             int int_value = atoi(av[ac]);
1444 
1445             fluid_settings_getint_range(handler->settings, name[param], &min, &max);
1446             if(int_value < min || int_value > max)
1447             {
1448                 fluid_ostream_printf(out, "%s: %s \"%s\" must be in range [%d..%d]\n",
1449                                      name_cde[param], name_value[param], av[ac], min, max);
1450                 return FLUID_FAILED;
1451             }
1452             value = (double)int_value;
1453         }
1454         else /* commands with float parameter */
1455         {
1456             double min, max;
1457             value = atof(av[ac]);
1458 
1459             fluid_settings_getnum_range(handler->settings, name[param], &min, &max);
1460             if(value < min || value > max)
1461             {
1462                 fluid_ostream_printf(out, "%s: %s \"%s\" must be in range [%f..%f]\n",
1463                                      name_cde[param], name_value[param], av[ac], min, max);
1464                 return FLUID_FAILED;
1465             }
1466         }
1467 
1468         /* run chorus function */
1469         fluid_synth_chorus_set_param(handler->synth, fx_group, param, value);
1470         return FLUID_OK;
1471     }
1472 
1473     return FLUID_FAILED;
1474 }
1475 
1476 /* Purpose:
1477  * Response to 'cho_set_nr' command
1478  * Load the new voice count into the chorus fx group.
1479  * Example: cho_set_nr 1 3
1480  * load 3 voices in the chorus fx group at index 1.
1481  */
1482 int
fluid_handle_chorusnr(void * data,int ac,char ** av,fluid_ostream_t out)1483 fluid_handle_chorusnr(void *data, int ac, char **av, fluid_ostream_t out)
1484 {
1485     return fluid_handle_chorus_command(data, ac, av, out, FLUID_CHORUS_NR);
1486 }
1487 
1488 /* Purpose:
1489  * Response to 'cho_setlevel' command
1490  * Example: cho_set_level 1 3
1491  * load level 3 in the chorus fx group at index 1.
1492  */
1493 int
fluid_handle_choruslevel(void * data,int ac,char ** av,fluid_ostream_t out)1494 fluid_handle_choruslevel(void *data, int ac, char **av, fluid_ostream_t out)
1495 {
1496     return fluid_handle_chorus_command(data, ac, av, out, FLUID_CHORUS_LEVEL);
1497 }
1498 
1499 /* Purpose:
1500  * Response to 'cho_setspeed' command
1501  * Example: cho_set_speed 1 0.1
1502  * load speed 0.1 in the chorus fx group at index 1.
1503  */
1504 int
fluid_handle_chorusspeed(void * data,int ac,char ** av,fluid_ostream_t out)1505 fluid_handle_chorusspeed(void *data, int ac, char **av, fluid_ostream_t out)
1506 {
1507     return fluid_handle_chorus_command(data, ac, av, out, FLUID_CHORUS_SPEED);
1508 }
1509 
1510 /* Purpose:
1511  * Response to 'cho_setdepth' command
1512  * Example: cho_set_depth 1 0.3
1513  * load depth 0.3 in the chorus fx group at index 1.
1514  */
1515 int
fluid_handle_chorusdepth(void * data,int ac,char ** av,fluid_ostream_t out)1516 fluid_handle_chorusdepth(void *data, int ac, char **av, fluid_ostream_t out)
1517 {
1518     return fluid_handle_chorus_command(data, ac, av, out, FLUID_CHORUS_DEPTH);
1519 }
1520 
1521 /* Purpose:
1522  * Response to: chorus [fx group] on command.
1523  * Examples:
1524  * chorus off ,disable all chorus groups.
1525  * chorus 1 on ,enable chorus group at index 1.
1526  */
1527 int
fluid_handle_chorus(void * data,int ac,char ** av,fluid_ostream_t out)1528 fluid_handle_chorus(void *data, int ac, char **av, fluid_ostream_t out)
1529 {
1530     return fluid_handle_reverb_chorus_on_command(data, ac, av, out, CHORUS_ON_CDE);
1531 }
1532 
1533 /* Purpose:
1534  * Response to the 'echo' command.
1535  * The command itself is useful, when the synth is used via TCP/IP.
1536  * It can signal for example, that a list of commands has been processed.
1537  */
1538 int
fluid_handle_echo(void * data,int ac,char ** av,fluid_ostream_t out)1539 fluid_handle_echo(void *data, int ac, char **av, fluid_ostream_t out)
1540 {
1541     if(ac < 1)
1542     {
1543         fluid_ostream_printf(out, "echo: too few arguments.\n");
1544         return FLUID_FAILED;
1545     }
1546 
1547     fluid_ostream_printf(out, "%s\n", av[0]);
1548 
1549     return FLUID_OK;
1550 }
1551 
1552 /* Purpose:
1553  * Sleep during a time in ms
1554  * The command itself is useful to insert a delay between commands.
1555  * It can help for example to build a small song using noteon/noteoff commands
1556  * in a command file.
1557  */
1558 int
fluid_handle_sleep(void * data,int ac,char ** av,fluid_ostream_t out)1559 fluid_handle_sleep(void *data, int ac, char **av, fluid_ostream_t out)
1560 {
1561     if(ac < 1)
1562     {
1563         fluid_ostream_printf(out, "sleep: too few arguments.\n");
1564         return -1;
1565     }
1566 
1567     if(!fluid_is_number(av[0]))
1568     {
1569         fluid_ostream_printf(out, "sleep: argument should be a number in ms.\n");
1570         return -1;
1571     }
1572 
1573     fluid_msleep(atoi(av[0]));	/* delay in milliseconds */
1574 
1575     return 0;
1576 }
1577 
1578 int
fluid_handle_source(void * data,int ac,char ** av,fluid_ostream_t out)1579 fluid_handle_source(void *data, int ac, char **av, fluid_ostream_t out)
1580 {
1581     FLUID_ENTRY_COMMAND(data);
1582 
1583     if(ac < 1)
1584     {
1585         fluid_ostream_printf(out, "source: too few arguments.\n");
1586         return FLUID_FAILED;
1587     }
1588 
1589     fluid_source(handler, av[0]);
1590 
1591     return FLUID_OK;
1592 }
1593 
1594 /* Purpose:
1595  * Response to 'gain' command. */
1596 int
fluid_handle_gain(void * data,int ac,char ** av,fluid_ostream_t out)1597 fluid_handle_gain(void *data, int ac, char **av, fluid_ostream_t out)
1598 {
1599     FLUID_ENTRY_COMMAND(data);
1600     float gain;
1601 
1602     if(ac < 1)
1603     {
1604         fluid_ostream_printf(out, "gain: too few arguments.\n");
1605         return FLUID_FAILED;
1606     }
1607 
1608     gain = atof(av[0]);
1609 
1610     if((gain < 0.0f) || (gain > 5.0f))
1611     {
1612         fluid_ostream_printf(out, "gain: value should be between '0' and '5'.\n");
1613         return FLUID_FAILED;
1614     };
1615 
1616     fluid_synth_set_gain(handler->synth, gain);
1617 
1618     return FLUID_OK;
1619 }
1620 
1621 /* Response to voice_count command */
1622 static int
fluid_handle_voice_count(void * data,int ac,char ** av,fluid_ostream_t out)1623 fluid_handle_voice_count(void *data, int ac, char **av,
1624                          fluid_ostream_t out)
1625 {
1626     FLUID_ENTRY_COMMAND(data);
1627     fluid_ostream_printf(out, "voice_count: %d\n",
1628                          fluid_synth_get_active_voice_count(handler->synth));
1629     return FLUID_OK;
1630 }
1631 
1632 /* Purpose:
1633  * Response to 'interp' command. */
1634 int
fluid_handle_interp(void * data,int ac,char ** av,fluid_ostream_t out)1635 fluid_handle_interp(void *data, int ac, char **av, fluid_ostream_t out)
1636 {
1637     FLUID_ENTRY_COMMAND(data);
1638     int interp;
1639     int chan = -1; /* -1: Set all channels */
1640 
1641     if(ac < 1)
1642     {
1643         fluid_ostream_printf(out, "interp: too few arguments.\n");
1644         return FLUID_FAILED;
1645     }
1646 
1647     interp = atoi(av[0]);
1648 
1649     if((interp < 0) || (interp > FLUID_INTERP_HIGHEST))
1650     {
1651         fluid_ostream_printf(out, "interp: Bad value\n");
1652         return FLUID_FAILED;
1653     };
1654 
1655     fluid_synth_set_interp_method(handler->synth, chan, interp);
1656 
1657     return FLUID_OK;
1658 }
1659 
1660 /* Purpose:
1661  * Response to 'interp' command. */
1662 int
fluid_handle_interpc(void * data,int ac,char ** av,fluid_ostream_t out)1663 fluid_handle_interpc(void *data, int ac, char **av, fluid_ostream_t out)
1664 {
1665     FLUID_ENTRY_COMMAND(data);
1666     int interp;
1667     int chan;
1668 
1669     if(ac < 2)
1670     {
1671         fluid_ostream_printf(out, "interpc: too few arguments.\n");
1672         return FLUID_FAILED;
1673     }
1674 
1675     chan = atoi(av[0]);
1676     interp = atoi(av[1]);
1677 
1678     if((chan < 0) || (chan >= fluid_synth_count_midi_channels(handler->synth)))
1679     {
1680         fluid_ostream_printf(out, "interp: Bad value for channel number.\n");
1681         return FLUID_FAILED;
1682     };
1683 
1684     if((interp < 0) || (interp > FLUID_INTERP_HIGHEST))
1685     {
1686         fluid_ostream_printf(out, "interp: Bad value for interpolation method.\n");
1687         return FLUID_FAILED;
1688     };
1689 
1690     fluid_synth_set_interp_method(handler->synth, chan, interp);
1691 
1692     return FLUID_OK;
1693 }
1694 
1695 int
fluid_handle_tuning(void * data,int ac,char ** av,fluid_ostream_t out)1696 fluid_handle_tuning(void *data, int ac, char **av, fluid_ostream_t out)
1697 {
1698     FLUID_ENTRY_COMMAND(data);
1699     char *name;
1700     int bank, prog;
1701 
1702     if(ac < 3)
1703     {
1704         fluid_ostream_printf(out, "tuning: too few arguments.\n");
1705         return FLUID_FAILED;
1706     }
1707 
1708     name = av[0];
1709 
1710     if(!fluid_is_number(av[1]))
1711     {
1712         fluid_ostream_printf(out, "tuning: 2nd argument should be a number.\n");
1713         return FLUID_FAILED;
1714     }
1715 
1716     bank = atoi(av[1]);
1717 
1718     if((bank < 0) || (bank >= 128))
1719     {
1720         fluid_ostream_printf(out, "tuning: invalid bank number.\n");
1721         return FLUID_FAILED;
1722     };
1723 
1724     if(!fluid_is_number(av[2]))
1725     {
1726         fluid_ostream_printf(out, "tuning: 3rd argument should be a number.\n");
1727         return FLUID_FAILED;
1728     }
1729 
1730     prog = atoi(av[2]);
1731 
1732     if((prog < 0) || (prog >= 128))
1733     {
1734         fluid_ostream_printf(out, "tuning: invalid program number.\n");
1735         return FLUID_FAILED;
1736     };
1737 
1738     fluid_synth_activate_key_tuning(handler->synth, bank, prog, name, NULL, FALSE);
1739 
1740     return FLUID_OK;
1741 }
1742 
1743 int
fluid_handle_tune(void * data,int ac,char ** av,fluid_ostream_t out)1744 fluid_handle_tune(void *data, int ac, char **av, fluid_ostream_t out)
1745 {
1746     FLUID_ENTRY_COMMAND(data);
1747     int bank, prog, key;
1748     double pitch;
1749 
1750     if(ac < 4)
1751     {
1752         fluid_ostream_printf(out, "tune: too few arguments.\n");
1753         return FLUID_FAILED;
1754     }
1755 
1756     if(!fluid_is_number(av[0]))
1757     {
1758         fluid_ostream_printf(out, "tune: 1st argument should be a number.\n");
1759         return FLUID_FAILED;
1760     }
1761 
1762     bank = atoi(av[0]);
1763 
1764     if((bank < 0) || (bank >= 128))
1765     {
1766         fluid_ostream_printf(out, "tune: invalid bank number.\n");
1767         return FLUID_FAILED;
1768     };
1769 
1770     if(!fluid_is_number(av[1]))
1771     {
1772         fluid_ostream_printf(out, "tune: 2nd argument should be a number.\n");
1773         return FLUID_FAILED;
1774     }
1775 
1776     prog = atoi(av[1]);
1777 
1778     if((prog < 0) || (prog >= 128))
1779     {
1780         fluid_ostream_printf(out, "tune: invalid program number.\n");
1781         return FLUID_FAILED;
1782     };
1783 
1784     if(!fluid_is_number(av[2]))
1785     {
1786         fluid_ostream_printf(out, "tune: 3rd argument should be a number.\n");
1787         return FLUID_FAILED;
1788     }
1789 
1790     key = atoi(av[2]);
1791 
1792     if((key < 0) || (key >= 128))
1793     {
1794         fluid_ostream_printf(out, "tune: invalid key number.\n");
1795         return FLUID_FAILED;
1796     };
1797 
1798     pitch = atof(av[3]);
1799 
1800     if(pitch < 0.0f)
1801     {
1802         fluid_ostream_printf(out, "tune: invalid pitch.\n");
1803         return FLUID_FAILED;
1804     };
1805 
1806     fluid_synth_tune_notes(handler->synth, bank, prog, 1, &key, &pitch, 0);
1807 
1808     return FLUID_OK;
1809 }
1810 
1811 int
fluid_handle_settuning(void * data,int ac,char ** av,fluid_ostream_t out)1812 fluid_handle_settuning(void *data, int ac, char **av, fluid_ostream_t out)
1813 {
1814     FLUID_ENTRY_COMMAND(data);
1815     int chan, bank, prog;
1816 
1817     if(ac < 3)
1818     {
1819         fluid_ostream_printf(out, "settuning: too few arguments.\n");
1820         return FLUID_FAILED;
1821     }
1822 
1823     if(!fluid_is_number(av[0]))
1824     {
1825         fluid_ostream_printf(out, "tune: 1st argument should be a number.\n");
1826         return FLUID_FAILED;
1827     }
1828 
1829     chan = atoi(av[0]);
1830 
1831     if((chan < 0) || (chan >= fluid_synth_count_midi_channels(handler->synth)))
1832     {
1833         fluid_ostream_printf(out, "tune: invalid channel number.\n");
1834         return FLUID_FAILED;
1835     };
1836 
1837     if(!fluid_is_number(av[1]))
1838     {
1839         fluid_ostream_printf(out, "tuning: 2nd argument should be a number.\n");
1840         return FLUID_FAILED;
1841     }
1842 
1843     bank = atoi(av[1]);
1844 
1845     if((bank < 0) || (bank >= 128))
1846     {
1847         fluid_ostream_printf(out, "tuning: invalid bank number.\n");
1848         return FLUID_FAILED;
1849     };
1850 
1851     if(!fluid_is_number(av[2]))
1852     {
1853         fluid_ostream_printf(out, "tuning: 3rd argument should be a number.\n");
1854         return FLUID_FAILED;
1855     }
1856 
1857     prog = atoi(av[2]);
1858 
1859     if((prog < 0) || (prog >= 128))
1860     {
1861         fluid_ostream_printf(out, "tuning: invalid program number.\n");
1862         return FLUID_FAILED;
1863     };
1864 
1865     fluid_synth_activate_tuning(handler->synth, chan, bank, prog, FALSE);
1866 
1867     return FLUID_OK;
1868 }
1869 
1870 int
fluid_handle_resettuning(void * data,int ac,char ** av,fluid_ostream_t out)1871 fluid_handle_resettuning(void *data, int ac, char **av, fluid_ostream_t out)
1872 {
1873     FLUID_ENTRY_COMMAND(data);
1874     int chan;
1875 
1876     if(ac < 1)
1877     {
1878         fluid_ostream_printf(out, "resettuning: too few arguments.\n");
1879         return FLUID_FAILED;
1880     }
1881 
1882     if(!fluid_is_number(av[0]))
1883     {
1884         fluid_ostream_printf(out, "tune: 1st argument should be a number.\n");
1885         return FLUID_FAILED;
1886     }
1887 
1888     chan = atoi(av[0]);
1889 
1890     if((chan < 0) || (chan >= fluid_synth_count_midi_channels(handler->synth)))
1891     {
1892         fluid_ostream_printf(out, "tune: invalid channel number.\n");
1893         return FLUID_FAILED;
1894     };
1895 
1896     fluid_synth_deactivate_tuning(handler->synth, chan, FALSE);
1897 
1898     return FLUID_OK;
1899 }
1900 
1901 int
fluid_handle_tunings(void * data,int ac,char ** av,fluid_ostream_t out)1902 fluid_handle_tunings(void *data, int ac, char **av, fluid_ostream_t out)
1903 {
1904     FLUID_ENTRY_COMMAND(data);
1905     int bank, prog;
1906     char name[256];
1907     int count = 0;
1908 
1909     fluid_synth_tuning_iteration_start(handler->synth);
1910 
1911     while(fluid_synth_tuning_iteration_next(handler->synth, &bank, &prog))
1912     {
1913         fluid_synth_tuning_dump(handler->synth, bank, prog, name, 256, NULL);
1914         fluid_ostream_printf(out, "%03d-%03d %s\n", bank, prog, name);
1915         count++;
1916     }
1917 
1918     if(count == 0)
1919     {
1920         fluid_ostream_printf(out, "No tunings available\n");
1921     }
1922 
1923     return FLUID_OK;
1924 }
1925 
1926 int
fluid_handle_dumptuning(void * data,int ac,char ** av,fluid_ostream_t out)1927 fluid_handle_dumptuning(void *data, int ac, char **av, fluid_ostream_t out)
1928 {
1929     FLUID_ENTRY_COMMAND(data);
1930     int bank, prog, i, res;
1931     double pitch[128];
1932     char name[256];
1933 
1934     if(ac < 2)
1935     {
1936         fluid_ostream_printf(out, "dumptuning: too few arguments.\n");
1937         return FLUID_FAILED;
1938     }
1939 
1940     if(!fluid_is_number(av[0]))
1941     {
1942         fluid_ostream_printf(out, "dumptuning: 1st argument should be a number.\n");
1943         return FLUID_FAILED;
1944     }
1945 
1946     bank = atoi(av[0]);
1947 
1948     if((bank < 0) || (bank >= 128))
1949     {
1950         fluid_ostream_printf(out, "dumptuning: invalid bank number.\n");
1951         return FLUID_FAILED;
1952     };
1953 
1954     if(!fluid_is_number(av[1]))
1955     {
1956         fluid_ostream_printf(out, "dumptuning: 2nd argument should be a number.\n");
1957         return FLUID_FAILED;
1958     }
1959 
1960     prog = atoi(av[1]);
1961 
1962     if((prog < 0) || (prog >= 128))
1963     {
1964         fluid_ostream_printf(out, "dumptuning: invalid program number.\n");
1965         return FLUID_FAILED;
1966     };
1967 
1968     res = fluid_synth_tuning_dump(handler->synth, bank, prog, name, 256, pitch);
1969 
1970     if(FLUID_OK != res)
1971     {
1972         fluid_ostream_printf(out, "Tuning %03d-%03d does not exist.\n", bank, prog);
1973         return FLUID_FAILED;
1974     }
1975 
1976     fluid_ostream_printf(out, "%03d-%03d %s:\n", bank, prog, name);
1977 
1978     for(i = 0; i < 128; i++)
1979     {
1980         fluid_ostream_printf(out, "key %03d, pitch %5.2f\n", i, pitch[i]);
1981     }
1982 
1983     return FLUID_OK;
1984 }
1985 
1986 int
fluid_handle_set(void * data,int ac,char ** av,fluid_ostream_t out)1987 fluid_handle_set(void *data, int ac, char **av, fluid_ostream_t out)
1988 {
1989     FLUID_ENTRY_COMMAND(data);
1990     int hints;
1991     int ival, icur;
1992     double fval, fcur;
1993     char *scur;
1994     int ret = FLUID_FAILED;
1995 
1996     if(ac < 2)
1997     {
1998         fluid_ostream_printf(out, "set: Too few arguments.\n");
1999         return ret;
2000     }
2001 
2002     switch(fluid_settings_get_type(handler->settings, av[0]))
2003     {
2004     case FLUID_NO_TYPE:
2005         fluid_ostream_printf(out, "set: Parameter '%s' not found.\n", av[0]);
2006         return ret;
2007 
2008     case FLUID_INT_TYPE:
2009         if(fluid_settings_get_hints(handler->settings, av[0], &hints) == FLUID_OK
2010                 && hints & FLUID_HINT_TOGGLED)
2011         {
2012             if(FLUID_STRCASECMP(av[1], "yes") == 0
2013                     || FLUID_STRCASECMP(av[1], "true") == 0
2014                     || FLUID_STRCASECMP(av[1], "t") == 0)
2015             {
2016                 ival = 1;
2017             }
2018             else
2019             {
2020                 ival = atoi(av[1]);
2021             }
2022         }
2023         else
2024         {
2025             ival = atoi(av[1]);
2026         }
2027 
2028         fluid_settings_getint(handler->settings, av[0], &icur);
2029         if (icur == ival)
2030         {
2031             return FLUID_OK;
2032         }
2033 
2034         ret = fluid_settings_setint(handler->settings, av[0], ival);
2035         break;
2036 
2037     case FLUID_NUM_TYPE:
2038         fval = atof(av[1]);
2039         fluid_settings_getnum(handler->settings, av[0], &fcur);
2040         if (fcur == fval)
2041         {
2042             return FLUID_OK;
2043         }
2044 
2045         ret = fluid_settings_setnum(handler->settings, av[0], fval);
2046         break;
2047 
2048     case FLUID_STR_TYPE:
2049         fluid_settings_dupstr(handler->settings, av[0], &scur);
2050 
2051         if(scur && !FLUID_STRCMP(scur, av[1]))
2052         {
2053             FLUID_FREE(scur);
2054             return FLUID_OK;
2055         }
2056         ret = fluid_settings_setstr(handler->settings, av[0], av[1]);
2057         FLUID_FREE(scur);
2058         break;
2059 
2060     case FLUID_SET_TYPE:
2061         fluid_ostream_printf(out, "set: Parameter '%s' is a node.\n", av[0]);
2062         return FLUID_FAILED;
2063 
2064     default:
2065         fluid_ostream_printf(out, "Unhandled settings type.");
2066         return FLUID_FAILED;
2067     }
2068 
2069     if(ret == FLUID_FAILED)
2070     {
2071         fluid_ostream_printf(out, "set: Value out of range. Try 'info %s' for valid ranges\n", av[0]);
2072     }
2073 
2074     if((handler->synth != NULL || handler->router != NULL) && !fluid_settings_is_realtime(handler->settings, av[0]))
2075     {
2076         fluid_ostream_printf(out, "Warning: '%s' is not a realtime setting, changes won't take effect.\n", av[0]);
2077     }
2078 
2079     return ret;
2080 }
2081 
2082 int
fluid_handle_get(void * data,int ac,char ** av,fluid_ostream_t out)2083 fluid_handle_get(void *data, int ac, char **av, fluid_ostream_t out)
2084 {
2085     FLUID_ENTRY_COMMAND(data);
2086 
2087     if(ac < 1)
2088     {
2089         fluid_ostream_printf(out, "get: too few arguments.\n");
2090         return FLUID_FAILED;
2091     }
2092 
2093     switch(fluid_settings_get_type(handler->settings, av[0]))
2094     {
2095     case FLUID_NO_TYPE:
2096         fluid_ostream_printf(out, "get: no such setting '%s'.\n", av[0]);
2097         return FLUID_FAILED;
2098 
2099     case FLUID_NUM_TYPE:
2100     {
2101         double value;
2102         fluid_settings_getnum(handler->settings, av[0], &value);
2103         fluid_ostream_printf(out, "%.3f\n", value);
2104         break;
2105     }
2106 
2107     case FLUID_INT_TYPE:
2108     {
2109         int value;
2110         fluid_settings_getint(handler->settings, av[0], &value);
2111         fluid_ostream_printf(out, "%d\n", value);
2112         break;
2113     }
2114 
2115     case FLUID_STR_TYPE:
2116     {
2117         char *s = NULL;
2118         fluid_settings_dupstr(handler->settings, av[0], &s);       /* ++ alloc string */
2119         fluid_ostream_printf(out, "%s\n", s ? s : "NULL");
2120         FLUID_FREE(s);    /* -- free string */
2121 
2122         break;
2123     }
2124 
2125     case FLUID_SET_TYPE:
2126         fluid_ostream_printf(out, "%s is a node\n", av[0]);
2127         break;
2128     }
2129 
2130     return FLUID_OK;
2131 }
2132 
2133 struct _fluid_handle_settings_data_t
2134 {
2135     size_t len;
2136     fluid_settings_t *settings;
2137     fluid_ostream_t out;
2138 };
2139 
fluid_handle_settings_iter1(void * data,const char * name,int type)2140 static void fluid_handle_settings_iter1(void *data, const char *name, int type)
2141 {
2142     struct _fluid_handle_settings_data_t *d = (struct _fluid_handle_settings_data_t *) data;
2143 
2144     size_t len = FLUID_STRLEN(name);
2145 
2146     if(len > d->len)
2147     {
2148         d->len = len;
2149     }
2150 }
2151 
fluid_handle_settings_iter2(void * data,const char * name,int type)2152 static void fluid_handle_settings_iter2(void *data, const char *name, int type)
2153 {
2154     struct _fluid_handle_settings_data_t *d = (struct _fluid_handle_settings_data_t *) data;
2155 
2156     size_t len = FLUID_STRLEN(name);
2157     fluid_ostream_printf(d->out, "%s", name);
2158 
2159     while(len++ < d->len)
2160     {
2161         fluid_ostream_printf(d->out, " ");
2162     }
2163 
2164     fluid_ostream_printf(d->out, "   ");
2165 
2166     switch(fluid_settings_get_type(d->settings, name))
2167     {
2168     case FLUID_NUM_TYPE:
2169     {
2170         double value;
2171         fluid_settings_getnum(d->settings, name, &value);
2172         fluid_ostream_printf(d->out, "%.3f\n", value);
2173         break;
2174     }
2175 
2176     case FLUID_INT_TYPE:
2177     {
2178         int value, hints;
2179         fluid_settings_getint(d->settings, name, &value);
2180 
2181         if(fluid_settings_get_hints(d->settings, name, &hints) == FLUID_OK)
2182         {
2183             if(!(hints & FLUID_HINT_TOGGLED))
2184             {
2185                 fluid_ostream_printf(d->out, "%d\n", value);
2186             }
2187             else
2188             {
2189                 fluid_ostream_printf(d->out, "%s\n", value ? "True" : "False");
2190             }
2191         }
2192 
2193         break;
2194     }
2195 
2196     case FLUID_STR_TYPE:
2197     {
2198         char *s = NULL;
2199         fluid_settings_dupstr(d->settings, name, &s);     /* ++ alloc string */
2200         fluid_ostream_printf(d->out, "%s\n", s ? s : "NULL");
2201         FLUID_FREE(s);    /* -- free string */
2202 
2203         break;
2204     }
2205     }
2206 }
2207 
2208 int
fluid_handle_settings(void * d,int ac,char ** av,fluid_ostream_t out)2209 fluid_handle_settings(void *d, int ac, char **av, fluid_ostream_t out)
2210 {
2211     FLUID_ENTRY_COMMAND(d);
2212     struct _fluid_handle_settings_data_t data;
2213 
2214     data.len = 0;
2215     data.settings = handler->settings;
2216     data.out = out;
2217 
2218     fluid_settings_foreach(handler->settings, &data, fluid_handle_settings_iter1);
2219     fluid_settings_foreach(handler->settings, &data, fluid_handle_settings_iter2);
2220     return FLUID_OK;
2221 }
2222 
2223 
2224 struct _fluid_handle_option_data_t
2225 {
2226     int first;
2227     fluid_ostream_t out;
2228 };
2229 
fluid_handle_print_option(void * data,const char * name,const char * option)2230 void fluid_handle_print_option(void *data, const char *name, const char *option)
2231 {
2232     struct _fluid_handle_option_data_t *d = (struct _fluid_handle_option_data_t *) data;
2233 
2234     if(d->first)
2235     {
2236         fluid_ostream_printf(d->out, "%s", option);
2237         d->first = 0;
2238     }
2239     else
2240     {
2241         fluid_ostream_printf(d->out, ", %s", option);
2242     }
2243 }
2244 
2245 int
fluid_handle_info(void * d,int ac,char ** av,fluid_ostream_t out)2246 fluid_handle_info(void *d, int ac, char **av, fluid_ostream_t out)
2247 {
2248     FLUID_ENTRY_COMMAND(d);
2249     fluid_settings_t *settings = handler->settings;
2250     struct _fluid_handle_option_data_t data;
2251 
2252     if(ac < 1)
2253     {
2254         fluid_ostream_printf(out, "info: too few arguments.\n");
2255         return FLUID_FAILED;
2256     }
2257 
2258     switch(fluid_settings_get_type(settings, av[0]))
2259     {
2260     case FLUID_NO_TYPE:
2261         fluid_ostream_printf(out, "info: no such setting '%s'.\n", av[0]);
2262         return FLUID_FAILED;
2263 
2264     case FLUID_NUM_TYPE:
2265     {
2266         double value, min, max, def;
2267 
2268         if(fluid_settings_getnum_range(settings, av[0], &min, &max) == FLUID_OK
2269                 && fluid_settings_getnum(settings, av[0], &value) == FLUID_OK
2270                 && fluid_settings_getnum_default(settings, av[0], &def) == FLUID_OK)
2271         {
2272             fluid_ostream_printf(out, "%s:\n", av[0]);
2273             fluid_ostream_printf(out, "Type:          number\n");
2274             fluid_ostream_printf(out, "Value:         %.3f\n", value);
2275             fluid_ostream_printf(out, "Minimum value: %.3f\n", min);
2276             fluid_ostream_printf(out, "Maximum value: %.3f\n", max);
2277             fluid_ostream_printf(out, "Default value: %.3f\n", def);
2278             fluid_ostream_printf(out, "Real-time:     %s\n",
2279                                  fluid_settings_is_realtime(settings, av[0]) ? "yes" : "no");
2280         }
2281         else
2282         {
2283             fluid_ostream_printf(out, "An error occurred when processing %s\n", av[0]);
2284         }
2285 
2286         break;
2287     }
2288 
2289     case FLUID_INT_TYPE:
2290     {
2291         int value, min, max, def, hints;
2292 
2293         if(fluid_settings_getint_range(settings, av[0], &min, &max) == FLUID_OK
2294                 && fluid_settings_getint(settings, av[0], &value) == FLUID_OK
2295                 && fluid_settings_get_hints(settings, av[0], &hints) == FLUID_OK
2296                 && fluid_settings_getint_default(settings, av[0], &def) == FLUID_OK)
2297         {
2298             fluid_ostream_printf(out, "%s:\n", av[0]);
2299 
2300             if(!(hints & FLUID_HINT_TOGGLED))
2301             {
2302                 fluid_ostream_printf(out, "Type:          integer\n");
2303                 fluid_ostream_printf(out, "Value:         %d\n", value);
2304                 fluid_ostream_printf(out, "Minimum value: %d\n", min);
2305                 fluid_ostream_printf(out, "Maximum value: %d\n", max);
2306                 fluid_ostream_printf(out, "Default value: %d\n", def);
2307             }
2308             else
2309             {
2310                 fluid_ostream_printf(out, "Type:          boolean\n");
2311                 fluid_ostream_printf(out, "Value:         %s\n", value ? "True" : "False");
2312                 fluid_ostream_printf(out, "Default value: %s\n", def ? "True" : "False");
2313             }
2314 
2315             fluid_ostream_printf(out, "Real-time:     %s\n",
2316                                  fluid_settings_is_realtime(settings, av[0]) ? "yes" : "no");
2317         }
2318         else
2319         {
2320             fluid_ostream_printf(out, "An error occurred when processing %s\n", av[0]);
2321         }
2322 
2323         break;
2324     }
2325 
2326     case FLUID_STR_TYPE:
2327     {
2328         char *s = NULL;
2329         fluid_settings_dupstr(settings, av[0], &s);         /* ++ alloc string */
2330         fluid_ostream_printf(out, "%s:\n", av[0]);
2331         fluid_ostream_printf(out, "Type:          string\n");
2332         fluid_ostream_printf(out, "Value:         %s\n", s ? s : "NULL");
2333         FLUID_FREE(s);    /* -- free string */
2334 
2335         fluid_settings_getstr_default(settings, av[0], &s);
2336         fluid_ostream_printf(out, "Default value: %s\n", s);
2337 
2338         data.out = out;
2339         data.first = 1;
2340         fluid_ostream_printf(out, "Options:       ");
2341         fluid_settings_foreach_option(settings, av[0], &data, fluid_handle_print_option);
2342         fluid_ostream_printf(out, "\n");
2343 
2344         fluid_ostream_printf(out, "Real-time:     %s\n",
2345                              fluid_settings_is_realtime(settings, av[0]) ? "yes" : "no");
2346         break;
2347     }
2348 
2349     case FLUID_SET_TYPE:
2350         fluid_ostream_printf(out, "%s:\n", av[0]);
2351         fluid_ostream_printf(out, "Type:          node\n");
2352         break;
2353     }
2354 
2355     return FLUID_OK;
2356 }
2357 
2358 int
fluid_handle_reset(void * data,int ac,char ** av,fluid_ostream_t out)2359 fluid_handle_reset(void *data, int ac, char **av, fluid_ostream_t out)
2360 {
2361     FLUID_ENTRY_COMMAND(data);
2362     fluid_synth_system_reset(handler->synth);
2363     return FLUID_OK;
2364 }
2365 
2366 int
fluid_handle_quit(void * data,int ac,char ** av,fluid_ostream_t out)2367 fluid_handle_quit(void *data, int ac, char **av, fluid_ostream_t out)
2368 {
2369     fluid_ostream_printf(out, "cheers!\n");
2370     return -2;
2371 }
2372 
2373 int
fluid_handle_help(void * data,int ac,char ** av,fluid_ostream_t out)2374 fluid_handle_help(void *data, int ac, char **av, fluid_ostream_t out)
2375 {
2376     /* Purpose:
2377      * Prints the help text for the command line commands.
2378      * Can be used as follows:
2379      * - help
2380      * - help (topic), where (topic) is 'general', 'chorus', etc.
2381      * - help all
2382      */
2383     char *topic = "help"; /* default, if no topic is given */
2384     int count = 0;
2385     unsigned int i;
2386 
2387     fluid_ostream_printf(out, "\n");
2388 
2389     /* 1st argument (optional): help topic */
2390     if(ac >= 1)
2391     {
2392         topic = av[0];
2393     }
2394 
2395     if(FLUID_STRCMP(topic, "help") == 0)
2396     {
2397         /* "help help": Print a list of all topics */
2398         fluid_ostream_printf(out,
2399                              "*** Help topics:***\n"
2400                              "help all (prints all topics)\n");
2401 
2402         for(i = 0; i < FLUID_N_ELEMENTS(fluid_commands); i++)
2403         {
2404             int listed_first_time = 1;
2405             unsigned int ii;
2406 
2407             for(ii = 0; ii < i; ii++)
2408             {
2409                 if(FLUID_STRCMP(fluid_commands[i].topic, fluid_commands[ii].topic) == 0)
2410                 {
2411                     listed_first_time = 0;
2412                 }; /* if topic has already been listed */
2413             }; /* for all topics (inner loop) */
2414 
2415             if(listed_first_time)
2416             {
2417                 fluid_ostream_printf(out, "help %s\n", fluid_commands[i].topic);
2418             };
2419         }; /* for all topics (outer loop) */
2420     }
2421     else
2422     {
2423         /* help (arbitrary topic or "all") */
2424         for(i = 0; i < FLUID_N_ELEMENTS(fluid_commands); i++)
2425         {
2426             if(fluid_commands[i].help != NULL)
2427             {
2428                 if(FLUID_STRCMP(topic, "all") == 0 || FLUID_STRCMP(topic, fluid_commands[i].topic) == 0)
2429                 {
2430                     fluid_ostream_printf(out, "%s\n", fluid_commands[i].help);
2431                     count++;
2432                 }; /* if it matches the topic */
2433             }; /* if help text exists */
2434         }; /* foreach command */
2435 
2436         if(count == 0)
2437         {
2438             fluid_ostream_printf(out, "Unknown help topic. Try 'help help'.\n");
2439         };
2440     };
2441 
2442     return FLUID_OK;
2443 }
2444 
2445 #define CHECK_VALID_ROUTER(_router, _out)                                                \
2446   if (router == NULL) {                                                                  \
2447     fluid_ostream_printf(out, "cannot execute router command without a midi router.\n"); \
2448     return FLUID_FAILED;                                                                 \
2449   }
2450 
2451 /* Command handler for "router_clear" command */
fluid_handle_router_clear(void * data,int ac,char ** av,fluid_ostream_t out)2452 int fluid_handle_router_clear(void *data, int ac, char **av, fluid_ostream_t out)
2453 {
2454     FLUID_ENTRY_COMMAND(data);
2455     fluid_midi_router_t *router = handler->router;
2456 
2457     if(ac != 0)
2458     {
2459         fluid_ostream_printf(out, "router_clear needs no arguments.\n");
2460         return FLUID_FAILED;
2461     }
2462 
2463     CHECK_VALID_ROUTER(router, out);
2464 
2465     fluid_midi_router_clear_rules(router);
2466 
2467     return FLUID_OK;
2468 }
2469 
2470 /* Command handler for "router_default" command */
fluid_handle_router_default(void * data,int ac,char ** av,fluid_ostream_t out)2471 int fluid_handle_router_default(void *data, int ac, char **av, fluid_ostream_t out)
2472 {
2473     FLUID_ENTRY_COMMAND(data);
2474     fluid_midi_router_t *router = handler->router;
2475 
2476     if(ac != 0)
2477     {
2478         fluid_ostream_printf(out, "router_default needs no arguments.\n");
2479         return FLUID_FAILED;
2480     }
2481 
2482     CHECK_VALID_ROUTER(router, out);
2483 
2484     fluid_midi_router_set_default_rules(router);
2485 
2486     return FLUID_OK;
2487 }
2488 
2489 /* Command handler for "router_begin" command */
fluid_handle_router_begin(void * data,int ac,char ** av,fluid_ostream_t out)2490 int fluid_handle_router_begin(void *data, int ac, char **av, fluid_ostream_t out)
2491 {
2492     FLUID_ENTRY_COMMAND(data);
2493     fluid_midi_router_t *router = handler->router;
2494 
2495     if(ac != 1)
2496     {
2497         fluid_ostream_printf(out, "router_begin requires [note|cc|prog|pbend|cpress|kpress]\n");
2498         return FLUID_FAILED;
2499     }
2500 
2501     CHECK_VALID_ROUTER(router, out);
2502 
2503     if(FLUID_STRCMP(av[0], "note") == 0)
2504     {
2505         handler->cmd_rule_type = FLUID_MIDI_ROUTER_RULE_NOTE;
2506     }
2507     else if(FLUID_STRCMP(av[0], "cc") == 0)
2508     {
2509         handler->cmd_rule_type = FLUID_MIDI_ROUTER_RULE_CC;
2510     }
2511     else if(FLUID_STRCMP(av[0], "prog") == 0)
2512     {
2513         handler->cmd_rule_type = FLUID_MIDI_ROUTER_RULE_PROG_CHANGE;
2514     }
2515     else if(FLUID_STRCMP(av[0], "pbend") == 0)
2516     {
2517         handler->cmd_rule_type = FLUID_MIDI_ROUTER_RULE_PITCH_BEND;
2518     }
2519     else if(FLUID_STRCMP(av[0], "cpress") == 0)
2520     {
2521         handler->cmd_rule_type = FLUID_MIDI_ROUTER_RULE_CHANNEL_PRESSURE;
2522     }
2523     else if(FLUID_STRCMP(av[0], "kpress") == 0)
2524     {
2525         handler->cmd_rule_type = FLUID_MIDI_ROUTER_RULE_KEY_PRESSURE;
2526     }
2527     else
2528     {
2529         fluid_ostream_printf(out, "router_begin requires [note|cc|prog|pbend|cpress|kpress]\n");
2530         return FLUID_FAILED;
2531     }
2532 
2533     if(handler->cmd_rule)
2534     {
2535         delete_fluid_midi_router_rule(handler->cmd_rule);
2536     }
2537 
2538     handler->cmd_rule = new_fluid_midi_router_rule();
2539 
2540     if(!handler->cmd_rule)
2541     {
2542         return FLUID_FAILED;
2543     }
2544 
2545     return FLUID_OK;
2546 }
2547 
2548 /* Command handler for "router_end" command */
fluid_handle_router_end(void * data,int ac,char ** av,fluid_ostream_t out)2549 int fluid_handle_router_end(void *data, int ac, char **av, fluid_ostream_t out)
2550 {
2551     FLUID_ENTRY_COMMAND(data);
2552     fluid_midi_router_t *router = handler->router;
2553 
2554     if(ac != 0)
2555     {
2556         fluid_ostream_printf(out, "router_end needs no arguments.\n");
2557         return FLUID_FAILED;
2558     }
2559 
2560     CHECK_VALID_ROUTER(router, out);
2561 
2562     if(!handler->cmd_rule)
2563     {
2564         fluid_ostream_printf(out, "No active router_begin command.\n");
2565         return FLUID_FAILED;
2566     }
2567 
2568     /* Add the rule */
2569     if(fluid_midi_router_add_rule(router, handler->cmd_rule, handler->cmd_rule_type) != FLUID_OK)
2570     {
2571         delete_fluid_midi_router_rule(handler->cmd_rule);    /* Free on failure */
2572     }
2573 
2574     handler->cmd_rule = NULL;
2575 
2576     return FLUID_OK;
2577 }
2578 
2579 /* Command handler for "router_chan" command */
fluid_handle_router_chan(void * data,int ac,char ** av,fluid_ostream_t out)2580 int fluid_handle_router_chan(void *data, int ac, char **av, fluid_ostream_t out)
2581 {
2582     FLUID_ENTRY_COMMAND(data);
2583     fluid_midi_router_t *router = handler->router;
2584 
2585     if(ac != 4)
2586     {
2587         fluid_ostream_printf(out, "router_chan needs four args: min, max, mul, add.");
2588         return FLUID_FAILED;
2589     }
2590 
2591     CHECK_VALID_ROUTER(router, out);
2592 
2593     if(!handler->cmd_rule)
2594     {
2595         fluid_ostream_printf(out, "No active router_begin command.\n");
2596         return FLUID_FAILED;
2597     }
2598 
2599     fluid_midi_router_rule_set_chan(handler->cmd_rule, atoi(av[0]), atoi(av[1]),
2600                                     atof(av[2]), atoi(av[3]));
2601     return FLUID_OK;
2602 }
2603 
2604 /* Command handler for "router_par1" command */
fluid_handle_router_par1(void * data,int ac,char ** av,fluid_ostream_t out)2605 int fluid_handle_router_par1(void *data, int ac, char **av, fluid_ostream_t out)
2606 {
2607     FLUID_ENTRY_COMMAND(data);
2608     fluid_midi_router_t *router = handler->router;
2609 
2610     if(ac != 4)
2611     {
2612         fluid_ostream_printf(out, "router_par1 needs four args: min, max, mul, add.");
2613         return FLUID_FAILED;
2614     }
2615 
2616     CHECK_VALID_ROUTER(router, out);
2617 
2618     if(!handler->cmd_rule)
2619     {
2620         fluid_ostream_printf(out, "No active router_begin command.\n");
2621         return FLUID_FAILED;
2622     }
2623 
2624     fluid_midi_router_rule_set_param1(handler->cmd_rule, atoi(av[0]), atoi(av[1]),
2625                                       atof(av[2]), atoi(av[3]));
2626     return FLUID_OK;
2627 }
2628 
2629 /* Command handler for "router_par2" command */
fluid_handle_router_par2(void * data,int ac,char ** av,fluid_ostream_t out)2630 int fluid_handle_router_par2(void *data, int ac, char **av, fluid_ostream_t out)
2631 {
2632     FLUID_ENTRY_COMMAND(data);
2633     fluid_midi_router_t *router = handler->router;
2634 
2635     if(ac != 4)
2636     {
2637         fluid_ostream_printf(out, "router_par2 needs four args: min, max, mul, add.");
2638         return FLUID_FAILED;
2639     }
2640 
2641     CHECK_VALID_ROUTER(router, out);
2642 
2643     if(!handler->cmd_rule)
2644     {
2645         fluid_ostream_printf(out, "No active router_begin command.\n");
2646         return FLUID_FAILED;
2647     }
2648 
2649     fluid_midi_router_rule_set_param2(handler->cmd_rule, atoi(av[0]), atoi(av[1]),
2650                                       atof(av[2]), atoi(av[3]));
2651     return FLUID_OK;
2652 }
2653 
2654 /**  commands Poly/mono mode *************************************************/
2655 
2656 static const char *const mode_name[] =
2657 {
2658     "poly omni on (0)", "mono omni on (1)",
2659     "poly omni off(2)", "mono omni off(3)"
2660 };
2661 
2662 /*
2663   Prints result message for commands: basicchannels, resetbasicchannels.
2664   Prints all basic channels and print a warning if there is no basic channel.
2665 
2666   @param synth the synth instance.
2667   @param out output stream.
2668 */
print_basic_channels(fluid_synth_t * synth,fluid_ostream_t out)2669 static int print_basic_channels(fluid_synth_t *synth, fluid_ostream_t out)
2670 {
2671     static const char warning_msg[] = "Warning: no basic channels. All MIDI channels are disabled.\n"
2672                                       "Make use of setbasicchannels to set at least a default basic channel.\n";
2673 
2674     int n_chan = synth->midi_channels;
2675     int i, n = 0;
2676 
2677     /* prints all basic channels */
2678     for(i = 0; i < n_chan; i++)
2679     {
2680         int basic_chan, mode_chan, val;
2681 
2682         if(fluid_synth_get_basic_channel(synth, i, &basic_chan, &mode_chan, &val) == FLUID_OK)
2683         {
2684             if(basic_chan == i)
2685             {
2686                 n++;
2687                 fluid_ostream_printf(out, "Basic channel:%3d, %s, nbr:%3d\n", i,
2688                                      mode_name[mode_chan &  FLUID_CHANNEL_MODE_MASK ],
2689                                      val);
2690             }
2691         }
2692         else
2693         {
2694             return FLUID_FAILED; /* error */
2695         }
2696     }
2697 
2698     /* prints a warning if there is no basic channel */
2699     if(n == 0)
2700     {
2701         fluid_ostream_printf(out, warning_msg);
2702     }
2703 
2704     return FLUID_OK;
2705 }
2706 
2707 /*-----------------------------------------------------------------------------
2708   basicchannels
2709    Prints the list of all MIDI basic channels information
2710    example:
2711 
2712 	Basic channel:  0, poly omni on (0), nbr:  3
2713 	Basic channel:  3, poly omni off(2), nbr:  1
2714 	Basic channel:  8, mono omni off(3), nbr:  2
2715 	Basic channel: 13, mono omni on (1), nbr:  3
2716 */
fluid_handle_basicchannels(void * data,int ac,char ** av,fluid_ostream_t out)2717 int fluid_handle_basicchannels(void *data, int ac, char **av,
2718                                fluid_ostream_t out)
2719 {
2720     FLUID_ENTRY_COMMAND(data);
2721     fluid_synth_t *synth = handler->synth;
2722     return print_basic_channels(synth, out);
2723 }
2724 
2725 /*
2726   Searches a mode name and returns the channel mode number.
2727   name must be: poly_omnion,  mono_omnion, poly_omnioff, mono_omnioff.
2728   @param name to search.
2729   @return channel mode number (0 to 3) if name is valid, -1 otherwise.
2730 */
get_channel_mode_num(char * name)2731 static int get_channel_mode_num(char *name)
2732 {
2733     /* argument names for channel mode parameter (see resetbasicchannels and
2734        setbasicchannels commands*/
2735     static const char *const name_channel_mode [FLUID_CHANNEL_MODE_LAST] =
2736     {"poly_omnion", "mono_omnion", "poly_omnioff", "mono_omnioff"};
2737     int i;
2738 
2739     for(i = 0 ; i <  FLUID_CHANNEL_MODE_LAST; i++)
2740     {
2741         if(! FLUID_STRCMP(name, name_channel_mode[i]))
2742         {
2743             return i;
2744         }
2745     }
2746 
2747     return -1;
2748 }
2749 
2750 static const char invalid_arg_msg[] = "invalid argument\n";
2751 /*
2752  checks basic channels arguments: chan1 mode1 val  chan2 mode2 val2  ...
2753  All arguments can be numeric. mode parameter can be a name.
2754  Each group entry must have 3 parameters (chan,mode,val).
2755 
2756  @param ac argument count.
2757  @param av argument table.
2758  @param out output stream.
2759  @param name_cde command name prefix.
2760  @return 0 if arguments are valid, -1 otherwise.
2761 */
check_basicchannels_arguments(int ac,char ** av,fluid_ostream_t out,char const * name_cde)2762 static int check_basicchannels_arguments(int ac, char **av,
2763         fluid_ostream_t out, char const *name_cde)
2764 {
2765     static const char too_few_arg_msg[] = "too few argument, chan mode val [chan mode val]...\n";
2766     int i;
2767 
2768     for(i = 0; i < ac; i++)
2769     {
2770         /* checks parameters for list entries: 	chan1 mode1 val  chan2 mode2 val2  ...*/
2771         /* all parameters can be numeric. mode parameter can be a name. */
2772         if(!fluid_is_number(av[i]) &&
2773                 ((i % 3 != 1) || get_channel_mode_num(av[i]) < 0))
2774         {
2775             fluid_ostream_printf(out, "%s: %s", name_cde, invalid_arg_msg);
2776             return -1;
2777         }
2778     }
2779 
2780     if(ac % 3)
2781     {
2782         /* each group entry needs 3 parameters: basicchan,mode,val */
2783         fluid_ostream_printf(out, "%s: channel %d, %s\n", name_cde,
2784                              atoi(av[((ac / 3) * 3)]), too_few_arg_msg);
2785         return -1;
2786     }
2787 
2788     return 0;
2789 }
2790 
2791 /*
2792  checks channels arguments: chan1  chan2   ...
2793  all arguments must be numeric.
2794 
2795  @param ac argument count.
2796  @param av argument table.
2797  @param out output stream.
2798  @param name_cde command name prefix.
2799  @return 0 if arguments are valid, -1 otherwise.
2800 */
check_channels_arguments(int ac,char ** av,fluid_ostream_t out,char const * name_cde)2801 static int check_channels_arguments(int ac, char **av,
2802                                     fluid_ostream_t out, char const *name_cde)
2803 {
2804     int i;
2805 
2806     for(i = 0; i < ac; i++)
2807     {
2808         if(!fluid_is_number(av[i]))
2809         {
2810             fluid_ostream_printf(out, "%s: %s", name_cde, invalid_arg_msg);
2811             return -1;
2812         }
2813     }
2814 
2815     return 0;
2816 }
2817 
2818 /*-----------------------------------------------------------------------------
2819   resetbasicchannels
2820 
2821   With no parameters the command resets all basic channels.
2822   Note: Be aware than when a synth instance has no basic channels, all channels
2823   are disabled.
2824   In the intend to get some MIDI channels enabled, use the command setbasicchannels.
2825 
2826   resetbasicchannels chan1  [chan2  .  .  .]
2827   Resets basic channel group chan1, basic channel group chan2 . . .
2828 */
fluid_handle_resetbasicchannels(void * data,int ac,char ** av,fluid_ostream_t out)2829 int fluid_handle_resetbasicchannels(void *data, int ac, char **av,
2830                                     fluid_ostream_t out)
2831 {
2832     static const char name_cde[] = "resetbasicchannels";
2833     FLUID_ENTRY_COMMAND(data);
2834     fluid_synth_t *synth = handler->synth;
2835 
2836     /* checks channels arguments: chan1 chan2 .... */
2837     if(check_channels_arguments(ac, av, out, name_cde) < 0)
2838     {
2839         return -1;
2840     }
2841 
2842     if(ac)
2843     {
2844         /* resetbasicchannels chan1  [chan2  .  .  .] */
2845         int i;
2846 
2847         for(i = 0; i < ac; i++)
2848         {
2849             int chan = atoi(av[i]);
2850             int result = fluid_synth_reset_basic_channel(synth, chan);
2851 
2852             if(result == FLUID_FAILED)
2853             {
2854                 fluid_ostream_printf(out, "%s: channel %3d, %s", name_cde, chan, invalid_arg_msg);
2855             }
2856         }
2857     }
2858     else
2859     {
2860         /* resets all basic channels */
2861         fluid_synth_reset_basic_channel(synth, -1);
2862     }
2863 
2864     /* prints result */
2865     return print_basic_channels(synth, out);
2866 }
2867 
2868 /*-----------------------------------------------------------------------------
2869   setbasicchannels
2870 
2871   With no parameters the command sets one channel basic at basic channel 0 in
2872   Omni On Poly (i.e all the MIDI channels are polyphonic).
2873 
2874   setbasicchannels chan1 mode1 nbr1    [chan2 mode2 nbr2]  ...  ...
2875 
2876   Adds basic channel 1 and 2
2877 
2878   The command fails if any channels overlaps any existing basic channel groups.
2879   To make room if necessary, existing basic channel groups can be cleared using
2880   resetbasicchannels command.
2881   Mode can be a numeric value or a name:
2882       numeric: 0 to 3 or
2883       name: poly_omnion , mono_omnion, poly_omnioff, mono_omnioff.
2884 */
fluid_handle_setbasicchannels(void * data,int ac,char ** av,fluid_ostream_t out)2885 int fluid_handle_setbasicchannels(void *data, int ac, char **av,
2886                                   fluid_ostream_t out)
2887 {
2888     static const char name_cde[] = "setbasicchannels";
2889     FLUID_ENTRY_COMMAND(data);
2890     fluid_synth_t *synth = handler->synth;
2891     int result;
2892     int i, n ;
2893 
2894     if(!ac)
2895     {
2896         /* sets one default basic channel */
2897         fluid_synth_reset_basic_channel(synth, -1); /* reset all basic channels */
2898         /* sets one basic channel Omni On Poly (i.e all the MIDI channels are polyphonic) */
2899         fluid_synth_set_basic_channel(synth, 0, FLUID_CHANNEL_MODE_OMNION_POLY, 0);
2900         return 0;
2901     }
2902 
2903     /* checks parameters: 	chan1 mode1 val1  chan2 mode2 val2 */
2904     if(check_basicchannels_arguments(ac, av, out, name_cde) < 0)
2905     {
2906         return -1;
2907     }
2908 
2909     n = ac / 3; /* number of basic channel information */
2910 
2911     for(i = 0; i < n; i++)
2912     {
2913         int basicchan, mode, val;
2914 
2915         basicchan = atoi(av[(i * 3)]);  /* chan is numeric */
2916 
2917         if(fluid_is_number(av[(i * 3) + 1]))
2918         {
2919             /* chan is numeric */
2920             mode = atoi(av[(i * 3) + 1]);
2921         }
2922         else
2923         {
2924             /* mode is a name */
2925             mode = get_channel_mode_num(av[(i * 3) + 1]);
2926         }
2927 
2928         val = atoi(av[(i * 3) + 2]);    /* val is numeric */
2929 
2930         /* changes or sets basic channels */
2931         result = fluid_synth_set_basic_channel(synth, basicchan, mode, val);
2932 
2933         if(result == FLUID_FAILED)
2934         {
2935             fluid_ostream_printf(out, "%s: channel %3d, mode %3d, nbr %3d, %s",
2936                                  name_cde, basicchan, mode, val, invalid_arg_msg);
2937         }
2938     }
2939 
2940     return 0;
2941 }
2942 
2943 /*
2944  Print result message : "channel:x is outside MIDI channel count(y)"
2945  for commands: channelsmode, portamentomode, legatomode, breathmode,setbreathmode.
2946  @param out output stream.
2947  @param name_cde command name prefix.
2948  @param chan, MIDI channel number x.
2949  @param n_chan, number of MIDI channels y.
2950 */
print_channel_is_outside_count(fluid_ostream_t out,char const * name_cde,int chan,int n_chan)2951 static void print_channel_is_outside_count(fluid_ostream_t out, char const *name_cde,
2952         int chan, int n_chan)
2953 {
2954     fluid_ostream_printf(out, "%s: channel %3d is outside MIDI channel count(%d)\n",
2955                          name_cde, chan, n_chan);
2956 }
2957 
2958 
2959 /*-----------------------------------------------------------------------------
2960   channelsmode
2961      Prints channel mode of all MIDI channels (Poly/mono, Enabled, Basic Channel)
2962      example
2963 
2964      Channel    , Status , Type         , Mode            , Nbr of channels
2965      channel:  0, disabled
2966      channel:  1, disabled
2967      channel:  2, disabled
2968      channel:  3, disabled
2969      channel:  4, disabled
2970      channel:  5, enabled, basic channel, mono omni off(3), nbr:  2
2971      channel:  6, enabled, --           , mono            , --
2972      channel:  7, disabled
2973      channel:  8, disabled
2974      channel:  9, disabled
2975      channel: 10, enabled, basic channel, mono omni off(3), nbr:  4
2976      channel: 11, enabled, --           , mono            , --
2977      channel: 12, enabled, --           , mono            , --
2978      channel: 13, enabled, --           , mono            , --
2979      channel: 14, disabled
2980      channel: 15, disabled
2981 
2982   channelsmode chan1 chan2
2983      Prints only channel mode of MIDI channels chan1, chan2
2984 */
fluid_handle_channelsmode(void * data,int ac,char ** av,fluid_ostream_t out)2985 int fluid_handle_channelsmode(void *data, int ac, char **av,
2986                               fluid_ostream_t out)
2987 {
2988     static const char header[] =
2989         "Channel    , Status , Type         , Mode            , Nbr of channels\n";
2990     static const char name_cde[] = "channelsmode";
2991     FLUID_ENTRY_COMMAND(data);
2992     fluid_synth_t *synth = handler->synth;
2993 
2994     int i, n, n_chan = synth->midi_channels;
2995 
2996     /* checks parameters: 	chan1 chan2 .... */
2997     if(check_channels_arguments(ac, av, out, name_cde) < 0)
2998     {
2999         return -1;
3000     }
3001 
3002     if(ac)
3003     {
3004         n = ac; /* prints ac MIDI channels number */
3005     }
3006     else
3007     {
3008         n = n_chan; /* prints all MIDI channels number */
3009     }
3010 
3011     /* prints header */
3012     fluid_ostream_printf(out, header);
3013 
3014     for(i = 0; i < n; i++)
3015     {
3016         int basic_chan, mode, val;
3017         int chan = ac ? atoi(av[i]) : i;
3018         int result = fluid_synth_get_basic_channel(synth, chan, &basic_chan, &mode, &val);
3019 
3020         if(result == FLUID_OK)
3021         {
3022             if(basic_chan != FLUID_FAILED)
3023             {
3024                 /* This channel is enabled */
3025                 const char *p_basicchan;  /* field basic channel */
3026                 const char *p_mode;  /* field mode */
3027                 const char *p_nbr; /* field Nbr */
3028                 static const char blank[] = "--"; /* field empty */
3029 
3030                 if(chan == basic_chan)
3031                 {
3032                     /* This channel is a basic channel */
3033                     char nbr[10]; /* field Nbr */
3034                     FLUID_SNPRINTF(nbr, sizeof(nbr), "nbr:%3d", val);
3035                     p_nbr = nbr;
3036                     p_mode = mode_name[mode];
3037                     p_basicchan = "basic channel";
3038                 }
3039                 else
3040                 {
3041                     /* This channel is member of a basic channel group */
3042                     p_basicchan = blank;
3043 
3044                     if(mode & FLUID_CHANNEL_POLY_OFF)
3045                     {
3046                         p_mode = "mono";
3047                     }
3048                     else
3049                     {
3050                         p_mode = "poly";
3051                     }
3052 
3053                     p_nbr = blank;
3054                 }
3055 
3056                 fluid_ostream_printf(out,
3057                                      "channel:%3d, enabled, %-13s, %-16s, %s\n",
3058                                      chan,
3059                                      p_basicchan,
3060                                      p_mode,
3061                                      p_nbr);
3062             }
3063             else
3064             {
3065                 fluid_ostream_printf(out, "channel:%3d, disabled\n", chan);
3066             }
3067         }
3068         else
3069         {
3070             print_channel_is_outside_count(out, name_cde, chan, n_chan);
3071 
3072             if(i < n - 1)
3073             {
3074                 fluid_ostream_printf(out, header);
3075             }
3076         }
3077     }
3078 
3079     return 0;
3080 }
3081 
3082 /**  commands mono legato mode ***********************************************/
3083 /*
3084  Prints result message for commands: legatomode, portamentomode.
3085  @param result result from the command (FLUID_OK,FLUID_FAILED).
3086  @param out output stream.
3087  @param name_cde command name prefix.
3088  @param chan MIDI channel number to display.
3089  @param name_mode name of the mode to display.
3090  @param n_chan, number of MIDI channels.
3091 */
print_result_get_channel_mode(int result,fluid_ostream_t out,char const * name_cde,int chan,char const * name_mode,int n_chan)3092 static void print_result_get_channel_mode(int result, fluid_ostream_t out,
3093         char const *name_cde, int chan,
3094         char const *name_mode,	int n_chan)
3095 {
3096     if(result == FLUID_OK)
3097     {
3098         fluid_ostream_printf(out, "%s: channel %3d, %s\n", name_cde, chan, name_mode);
3099     }
3100     else
3101     {
3102         print_channel_is_outside_count(out, name_cde, chan, n_chan);
3103     }
3104 }
3105 
3106 /*-----------------------------------------------------------------------------
3107  legatomode
3108      Prints legato mode of all MIDI channels
3109      example
3110 
3111      channel:  0, (1)multi-retrigger
3112      channel:  1, (0)retrigger
3113      channel:  2, (1)multi-retrigger
3114      .....
3115 
3116  legatomode chan1 chan2
3117      Prints only legato mode of MIDI channels chan1, chan2
3118 */
fluid_handle_legatomode(void * data,int ac,char ** av,fluid_ostream_t out)3119 int fluid_handle_legatomode(void *data, int ac, char **av,
3120                             fluid_ostream_t out)
3121 {
3122     static const char name_cde[] = "legatomode";
3123     static const char *const name_legato_mode[FLUID_CHANNEL_LEGATO_MODE_LAST] =
3124     {	"(0)retrigger", "(1)multi-retrigger"	};
3125 
3126     FLUID_ENTRY_COMMAND(data);
3127     fluid_synth_t *synth = handler->synth;
3128     int mode = 0;
3129     int i, n, n_chan = synth->midi_channels;
3130 
3131     /* checks channels arguments: chan1 chan2 .... */
3132     if(check_channels_arguments(ac, av, out, name_cde) < 0)
3133     {
3134         return -1;
3135     }
3136 
3137     if(ac)
3138     {
3139         n = ac; /* prints ac MIDI channels number */
3140     }
3141     else
3142     {
3143         n = n_chan; /* prints all MIDI channels number */
3144     }
3145 
3146     /* prints header */
3147     fluid_ostream_printf(out, "Channel    , legato mode\n");
3148 
3149     for(i = 0; i < n; i++)
3150     {
3151         int chan = ac ? atoi(av[i]) : i;
3152         int result = fluid_synth_get_legato_mode(synth, chan, &mode);
3153         print_result_get_channel_mode(result, out, name_cde, chan, name_legato_mode[mode], n_chan);
3154     }
3155 
3156     return 0;
3157 }
3158 
3159 /*
3160  checks channels arguments by group:
3161  -example by group of 2 arguments: chan1 val1  chan2 val2  .. ..
3162  -example by group of 4 arguments: chan1 val1 val2 val3   chan2 val1 val2 val3  ....
3163 
3164  all arguments must be numeric.
3165 
3166  @param ac argument count.
3167  @param av argument table.
3168  @param nbr_arg_group number of arguments by group expected.
3169  @param out output stream.
3170  @param name_cde command name prefix.
3171  @param nbr_arg_group_msg message when the number of argument by group is invalid.
3172  @return 0 if arguments are valid, -1 otherwise.
3173 */
check_channels_group_arguments(int ac,char ** av,int nbr_arg_group,fluid_ostream_t out,char const * name_cde,char const * nbr_arg_group_msg)3174 static int check_channels_group_arguments(int ac, char **av, int nbr_arg_group,
3175         fluid_ostream_t out,
3176         char const *name_cde,
3177         char const *nbr_arg_group_msg
3178                                          )
3179 {
3180     if(ac)
3181     {
3182         /* checks channels numeric arguments */
3183         if(check_channels_arguments(ac, av, out, name_cde) < 0)
3184         {
3185             return -1;
3186         }
3187 
3188         if(ac % nbr_arg_group)
3189         {
3190             /* each group entry needs nbr_arg_group parameters */
3191             fluid_ostream_printf(out, "%s: channel %d, %s\n", name_cde,
3192                                  atoi(av[((ac / nbr_arg_group) * nbr_arg_group)]),
3193                                  nbr_arg_group_msg);
3194             return -1;
3195         }
3196     }
3197     else
3198     {
3199         fluid_ostream_printf(out, "%s: %s", name_cde, nbr_arg_group_msg);
3200         return -1;
3201     }
3202 
3203     return 0;
3204 }
3205 
3206 /*
3207  Prints result message for commands: setlegatomode, setportamentomode.
3208  @param result result from the command (FLUID_FAILED).
3209  @param out output stream.
3210  @param name_cde command name prefix.
3211  @param chan, MIDI channel number to display.
3212  @param mode, mode value to display.
3213 */
print_result_set_channel_mode(int result,fluid_ostream_t out,char const * name_cde,int chan,int mode)3214 static void print_result_set_channel_mode(int result, fluid_ostream_t out,
3215         char const *name_cde,
3216         int chan, int mode)
3217 {
3218     if(result == FLUID_FAILED)
3219     {
3220         fluid_ostream_printf(out, "%s: channel %3d, mode %3d, %s", name_cde, chan, mode, invalid_arg_msg);
3221     }
3222 }
3223 
3224 static const char too_few_arg_chan_mode_msg[] = "too few argument, chan mode [chan mode]...\n";
3225 /*-----------------------------------------------------------------------------
3226   setlegatomode chan0 mode1 [chan1 mode0] ..  ..
3227 
3228   Changes legato mode for channels chan0 and [chan1]
3229 */
fluid_handle_setlegatomode(void * data,int ac,char ** av,fluid_ostream_t out)3230 int fluid_handle_setlegatomode(void *data, int ac, char **av,
3231                                fluid_ostream_t out)
3232 {
3233     static const char name_cde[] = "setlegatomode";
3234     FLUID_ENTRY_COMMAND(data);
3235     fluid_synth_t *synth = handler->synth;
3236     int i, n ;
3237 
3238     /* checks channels arguments by group of 2: chan1 val1 chan2 val1 ..  ..*/
3239     if(check_channels_group_arguments(ac, av, 2, out, name_cde, too_few_arg_chan_mode_msg) < 0)
3240     {
3241         return -1;
3242     }
3243 
3244     n = ac / 2; /* number of legato groups information */
3245 
3246     for(i = 0; i < n; i++)
3247     {
3248         int chan = atoi(av[(i * 2)]);
3249         int mode = atoi(av[(i * 2) + 1]);
3250         /* changes legato mode */
3251 
3252         int result = fluid_synth_set_legato_mode(synth, chan, mode);
3253         print_result_set_channel_mode(result, out, name_cde, chan, mode);
3254     }
3255 
3256     return 0;
3257 }
3258 
3259 /**  commands mono/poly portamento mode **************************************/
3260 
3261 /*-----------------------------------------------------------------------------
3262  portamentomode
3263      Prints portamento mode of all MIDI channels
3264      example
3265 
3266      channel:  0, (2)staccato only
3267      channel:  1, (1)legato only
3268      channel:  2, (0)each note
3269      channel:  3, (1)legato only
3270      .....
3271 
3272  portamentomode chan1 chan2
3273      Prints only portamento mode of MIDI channels chan1, chan2
3274 */
fluid_handle_portamentomode(void * data,int ac,char ** av,fluid_ostream_t out)3275 int fluid_handle_portamentomode(void *data, int ac, char **av,
3276                                 fluid_ostream_t out)
3277 {
3278     static const char name_cde[] = "portamentomode";
3279     static const char *const name_portamento_mode[FLUID_CHANNEL_PORTAMENTO_MODE_LAST] =
3280     { "(0)each note", "(1)legato only", "(2)staccato only"	};
3281 
3282     FLUID_ENTRY_COMMAND(data);
3283     fluid_synth_t *synth = handler->synth;
3284     int mode = 0;
3285     int i, n, n_chan = synth->midi_channels;
3286 
3287     /* checks channels arguments: chan1 chan2 . . . */
3288     if(check_channels_arguments(ac, av, out, name_cde) < 0)
3289     {
3290         return -1;
3291     }
3292 
3293     if(ac)
3294     {
3295         n = ac; /* prints ac MIDI channels number */
3296     }
3297     else
3298     {
3299         n = n_chan; /* prints all MIDI channels number */
3300     }
3301 
3302     /* prints header */
3303     fluid_ostream_printf(out, "Channel    , portamento mode\n");
3304 
3305     for(i = 0; i < n; i++)
3306     {
3307         int chan = ac ? atoi(av[i]) : i;
3308         int result = fluid_synth_get_portamento_mode(synth, chan, &mode);
3309         print_result_get_channel_mode(result, out, name_cde, chan, name_portamento_mode[mode], n_chan);
3310     }
3311 
3312     return 0;
3313 }
3314 
3315 /*-----------------------------------------------------------------------------
3316   setportamentomode chan1 mode1 [chan2 mode2] ..  ..
3317 
3318   Changes portamento mode for channels chan1 and [chan2]
3319 */
fluid_handle_setportamentomode(void * data,int ac,char ** av,fluid_ostream_t out)3320 int fluid_handle_setportamentomode(void *data, int ac, char **av,
3321                                    fluid_ostream_t out)
3322 {
3323     static const char name_cde[] = "setportamentomode";
3324     FLUID_ENTRY_COMMAND(data);
3325     fluid_synth_t *synth = handler->synth;
3326     int i, n ;
3327 
3328     /* checks channels arguments by group of 2: chan1 val1 chan2 val1 ..  .. */
3329     if(check_channels_group_arguments(ac, av, 2, out, name_cde, too_few_arg_chan_mode_msg) < 0)
3330     {
3331         return -1;
3332     }
3333 
3334     n = ac / 2; /* number of portamento groups information */
3335 
3336     for(i = 0; i < n; i++)
3337     {
3338         int chan = atoi(av[(i * 2)]);
3339         int mode = atoi(av[(i * 2) + 1]);
3340         /* changes portamento mode */
3341 
3342         int result = fluid_synth_set_portamento_mode(synth, chan, mode);
3343         print_result_set_channel_mode(result, out, name_cde, chan, mode);
3344     }
3345 
3346     return 0;
3347 }
3348 
3349 /**  commands mono/poly breath mode *******************************************/
3350 /*-----------------------------------------------------------------------------
3351  breathmode
3352     Prints breath options of all MIDI channels.
3353 	poly breath on/off, mono breath on/off, breath sync on/off
3354 
3355     example
3356 
3357 	Channel    , poly breath , mono breath , breath sync
3358 	channel:  0, off         , off         , off
3359 	channel:  1, off         , off         , off
3360 	channel:  2, off         , off         , off
3361 	.....
3362 
3363  breathmode chan1 chan2
3364      Prints only breath mode of MIDI channels chan1, chan2
3365 */
fluid_handle_breathmode(void * data,int ac,char ** av,fluid_ostream_t out)3366 int fluid_handle_breathmode(void *data, int ac, char **av,
3367                             fluid_ostream_t out)
3368 {
3369     static const char name_cde[] = "breathmode";
3370     static const char *const header = "Channel    , poly breath , mono breath , breath sync\n";
3371     FLUID_ENTRY_COMMAND(data);
3372     fluid_synth_t *synth = handler->synth;
3373     int breathmode;
3374     int i, n, n_chan = synth->midi_channels;
3375 
3376     /* checks channels arguments: chan1 chan2 . . . */
3377     if(check_channels_arguments(ac, av, out, name_cde) < 0)
3378     {
3379         return -1;
3380     }
3381 
3382     if(ac)
3383     {
3384         n = ac; /* prints ac MIDI channels number */
3385     }
3386     else
3387     {
3388         n = n_chan; /* prints all MIDI channels number */
3389     }
3390 
3391     /* prints header */
3392     fluid_ostream_printf(out, header);
3393 
3394     for(i = 0; i < n; i++)
3395     {
3396         int chan = ac ? atoi(av[i]) : i;
3397         int result = fluid_synth_get_breath_mode(synth, chan, &breathmode);
3398 
3399         if(result == FLUID_OK)
3400         {
3401             static const char on_msg[] = "on";
3402             static const char off_msg[] = "off";
3403             const char *msg_poly_breath, * msg_mono_breath, * msg_breath_sync;
3404 
3405             if(breathmode &  FLUID_CHANNEL_BREATH_POLY)
3406             {
3407                 msg_poly_breath = on_msg;
3408             }
3409             else
3410             {
3411                 msg_poly_breath = off_msg;
3412             }
3413 
3414             if(breathmode &  FLUID_CHANNEL_BREATH_MONO)
3415             {
3416                 msg_mono_breath = on_msg;
3417             }
3418             else
3419             {
3420                 msg_mono_breath = off_msg;
3421             }
3422 
3423             if(breathmode &  FLUID_CHANNEL_BREATH_SYNC)
3424             {
3425                 msg_breath_sync = on_msg;
3426             }
3427             else
3428             {
3429                 msg_breath_sync = off_msg;
3430             }
3431 
3432             fluid_ostream_printf(out, "channel:%3d, %-12s, %-12s, %-11s\n", chan,
3433                                  msg_poly_breath, msg_mono_breath, msg_breath_sync);
3434         }
3435         else
3436         {
3437             print_channel_is_outside_count(out, name_cde, chan, n_chan);
3438 
3439             if(i < n - 1)
3440             {
3441                 fluid_ostream_printf(out, header);
3442             }
3443         }
3444     }
3445 
3446     return 0;
3447 }
3448 
3449 /*-----------------------------------------------------------------------------
3450   setbreathmode chan1 poly_breath_mode(1/0) mono_breath_mode(1/0) mono_breath_sync(1/0)
3451 
3452   Changes breath options for channels chan1 [chan2] ..  ..
3453 
3454   Example:  setbreathmode  4 0 1 1
3455 
3456   Parameter 1 is the channel number (i.e 4).
3457   Parameter 2 is the " Breath modulator " enable/disable  for poly mode (i.e disabled).
3458   Parameter 3 is the " Breath modulator " enable/disable for mono mode (i.e enabled).
3459   Parameter 4 is "breath sync noteOn/Off" enable/disable for mono mode only (i.e enabled).
3460 
3461 */
fluid_handle_setbreathmode(void * data,int ac,char ** av,fluid_ostream_t out)3462 int fluid_handle_setbreathmode(void *data, int ac, char **av,
3463                                fluid_ostream_t out)
3464 {
3465     static const char name_cde[] = "setbreathmode";
3466     static const char too_few_arg_breath_msg[] =
3467         "too few argument:\nchan 1/0(breath poly) 1/0(breath mono) 1/0(breath sync mono)[..]\n";
3468 
3469     FLUID_ENTRY_COMMAND(data);
3470     fluid_synth_t *synth = handler->synth;
3471     int i, n, n_chan = synth->midi_channels;
3472 
3473     /* checks channels arguments by group of 4:
3474     chan1 val1 val2 val3   chan2 val1 val2 val3 ....  ....*/
3475     if(check_channels_group_arguments(ac, av, 4, out, name_cde, too_few_arg_breath_msg) < 0)
3476     {
3477         return -1;
3478     }
3479 
3480     n = ac / 4; /* number of breath groups information */
3481 
3482     for(i = 0; i < n; i++)
3483     {
3484         int result;
3485         int chan = atoi(av[(i * 4)]);
3486         int poly_breath = atoi(av[(i * 4) + 1]);
3487         int mono_breath = atoi(av[(i * 4) + 2]);
3488         int breath_sync = atoi(av[(i * 4) + 3]);
3489         int breath_infos = 0;
3490 
3491         /* changes  breath infos */
3492         if(poly_breath)
3493         {
3494             breath_infos |= FLUID_CHANNEL_BREATH_POLY;
3495         }
3496 
3497         if(mono_breath)
3498         {
3499             breath_infos |= FLUID_CHANNEL_BREATH_MONO;
3500         }
3501 
3502         if(breath_sync)
3503         {
3504             breath_infos |= FLUID_CHANNEL_BREATH_SYNC;
3505         }
3506 
3507         result = fluid_synth_set_breath_mode(synth, chan, breath_infos);
3508 
3509         if(result == FLUID_FAILED)
3510         {
3511             print_channel_is_outside_count(out, name_cde, chan, n_chan);
3512         }
3513     }
3514 
3515     return 0;
3516 }
3517 
3518 /**  commands  for Midi file player ******************************************/
3519 
3520 /* check player argument */
player_check_arg(const char * name_cde,int ac,char ** av,fluid_ostream_t out)3521 int player_check_arg(const char *name_cde, int ac, char **av, fluid_ostream_t out)
3522 {
3523     /* check if there is one argument that is a number */
3524     if(ac != 1 || !fluid_is_number(av[0]))
3525     {
3526         fluid_ostream_printf(out, "%s: %s", name_cde, invalid_arg_msg);
3527         return FLUID_FAILED;
3528     }
3529     return FLUID_OK;
3530 }
3531 
3532 /* print current position, total ticks, and tempo
3533  * @current_tick position to display. if -1 the function displays the value
3534  *  returned by fluid_player_get_current_tick().
3535 */
player_print_position(fluid_player_t * player,int current_tick,fluid_ostream_t out)3536 void player_print_position(fluid_player_t *player, int current_tick, fluid_ostream_t out)
3537 {
3538     int total_ticks = fluid_player_get_total_ticks(player);
3539     int tempo_bpm = fluid_player_get_bpm(player);
3540     if(current_tick == -1)
3541     {
3542         current_tick = fluid_player_get_current_tick(player);
3543     }
3544     fluid_ostream_printf(out, "player current pos:%d, end:%d, bpm:%d\n\n",
3545                          current_tick, total_ticks, tempo_bpm);
3546 }
3547 
3548 /* player commands enum */
3549 enum
3550 {
3551     PLAYER_LOOP_CDE, /* player_loop num,(Set loop number to num) */
3552     PLAYER_SEEK_CDE, /* player_seek num (Move forward/backward to +/-num ticks) */
3553     PLAYER_STOP_CDE,      /* player_stop    (Stop playing) */
3554     PLAYER_CONT_CDE,      /* player_cont    (Continue playing) */
3555     PLAYER_NEXT_CDE,      /* player_next    (Move to next song) */
3556     PLAYER_START_CDE      /* player_start   (Move to start of song) */
3557 };
3558 
3559 /* Command handler: see player commands enum (above)
3560  * @cmd player commands enumeration value.
3561 */
fluid_handle_player_cde(void * data,int ac,char ** av,fluid_ostream_t out,int cmd)3562 int fluid_handle_player_cde(void *data, int ac, char **av, fluid_ostream_t out, int cmd)
3563 {
3564     FLUID_ENTRY_COMMAND(data);
3565     int arg = 0, was_running;
3566     int seek = -1;  /* current seek position in tick */
3567 
3568     /* commands name table */
3569     static const char *name_cde[] =
3570     {"player_loop", "player_seek"};
3571 
3572     /* get argument for PLAYER_LOOP_CDE, PLAYER_SEEK_CDE */
3573     if(cmd <= PLAYER_SEEK_CDE)
3574     {
3575         /* check argument */
3576         if(player_check_arg(name_cde[cmd], ac, av, out) == FLUID_FAILED)
3577         {
3578             return FLUID_FAILED;
3579         }
3580 
3581         arg = atoi(av[0]);
3582     }
3583 
3584     if(cmd == PLAYER_LOOP_CDE)  /* player_loop */
3585     {
3586         fluid_player_set_loop(handler->player, arg);
3587         return FLUID_OK;
3588     }
3589 
3590     if(cmd == PLAYER_CONT_CDE)  /* player_cont */
3591     {
3592         fluid_player_play(handler->player);
3593         return FLUID_OK;
3594     }
3595 
3596     was_running = fluid_player_get_status(handler->player) == FLUID_PLAYER_PLAYING;
3597     if(was_running)
3598     {
3599         fluid_player_stop(handler->player);  /* player_stop */
3600     }
3601 
3602     if(cmd != PLAYER_STOP_CDE)
3603     {
3604         /* seek for player_next, player_seek, player_start */
3605         /* set seek to maximum position */
3606         seek = fluid_player_get_total_ticks(handler->player);
3607 
3608         if(cmd == PLAYER_SEEK_CDE)
3609         {
3610             /* Move position forward/backward +/- num ticks*/
3611             arg  += fluid_player_get_current_tick(handler->player);
3612 
3613             /* keep seek between minimum and maximum in current song */
3614             if(arg < 0)
3615             {
3616                 seek = 0; /* minimum position */
3617             }
3618             else if(!was_running || arg < seek)
3619             {
3620                 seek = arg; /* seek < maximum position */
3621             }
3622         }
3623 
3624         if(cmd == PLAYER_START_CDE)  /* player_start */
3625         {
3626             seek = 0; /* beginning of the current song */
3627         }
3628 
3629         fluid_player_seek(handler->player, seek);
3630         if(was_running)
3631         {
3632             fluid_player_play(handler->player);
3633         }
3634     }
3635     /* display position */
3636     player_print_position(handler->player, seek, out);
3637 
3638     return FLUID_OK;
3639 }
3640 
3641 /* Command handler for "player_start" command */
fluid_handle_player_start(void * data,int ac,char ** av,fluid_ostream_t out)3642 int fluid_handle_player_start(void *data, int ac, char **av, fluid_ostream_t out)
3643 {
3644     return fluid_handle_player_cde(data, ac, av, out, PLAYER_START_CDE);
3645 }
3646 
3647 /* Command handler for "player_stop" command */
fluid_handle_player_stop(void * data,int ac,char ** av,fluid_ostream_t out)3648 int fluid_handle_player_stop(void *data, int ac, char **av, fluid_ostream_t out)
3649 {
3650     return fluid_handle_player_cde(data, ac, av, out, PLAYER_STOP_CDE);
3651 }
3652 
3653 /* Command handler for "player_continue" command */
fluid_handle_player_continue(void * data,int ac,char ** av,fluid_ostream_t out)3654 int fluid_handle_player_continue(void *data, int ac, char **av, fluid_ostream_t out)
3655 {
3656     return fluid_handle_player_cde(data, ac, av, out, PLAYER_CONT_CDE);
3657 }
3658 /* Command handler for "player_seek" command */
fluid_handle_player_seek(void * data,int ac,char ** av,fluid_ostream_t out)3659 int fluid_handle_player_seek(void *data, int ac, char **av, fluid_ostream_t out)
3660 {
3661     return fluid_handle_player_cde(data, ac, av, out, PLAYER_SEEK_CDE);
3662 }
3663 
3664 /* Command handler for "player_next" command */
fluid_handle_player_next_song(void * data,int ac,char ** av,fluid_ostream_t out)3665 int fluid_handle_player_next_song(void *data, int ac, char **av, fluid_ostream_t out)
3666 {
3667     return fluid_handle_player_cde(data, ac, av, out, PLAYER_NEXT_CDE);
3668 }
3669 
3670 /* Command handler for "player_loop" command */
fluid_handle_player_loop(void * data,int ac,char ** av,fluid_ostream_t out)3671 int fluid_handle_player_loop(void *data, int ac, char **av, fluid_ostream_t out)
3672 {
3673     return fluid_handle_player_cde(data, ac, av, out, PLAYER_LOOP_CDE);
3674 }
3675 
3676 /* Command handler for player tempo commands:
3677    player_tempo_int [mul], set the player to internal tempo multiplied by mul
3678    player_tempo_bpm bpm, set the player to external tempo in beat per minute.
3679    examples:
3680     player_tempo_int      set the player to internal tempo with a default
3681                           multiplier set to 1.0.
3682 
3683     player_tempo_int 0.5  set the player to internal tempo divided by 2.
3684 
3685     player_tempo_bpm 75, set the player to external tempo of 75 beats per minute.
3686 */
fluid_handle_player_tempo_cde(void * data,int ac,char ** av,fluid_ostream_t out,int cmd)3687 int fluid_handle_player_tempo_cde(void *data, int ac, char **av, fluid_ostream_t out, int cmd)
3688 {
3689     FLUID_ENTRY_COMMAND(data);
3690     /* default multiplier for player_tempo_int command without argument*/
3691     double arg = 1.0F;
3692 
3693     /* commands name table */
3694     static const char *name_cde[] =
3695     {"player_tempo_int", "player_tempo_bpm"};
3696 
3697     static const struct /* argument infos */
3698     {
3699         double min;
3700         double max;
3701         char *name;
3702     }argument[2] = {{0.1F, 10.F, "multiplier"}, {1.0F, 600.0F, "bpm"}};
3703 
3704     /* get argument for: player_tempo_int [mul],  player_tempo_bpm bpm */
3705     if((cmd == FLUID_PLAYER_TEMPO_EXTERNAL_BPM) || ac)
3706     {
3707         /* check argument presence */
3708         if(player_check_arg(name_cde[cmd], ac, av, out) == FLUID_FAILED)
3709         {
3710             return FLUID_FAILED;
3711         }
3712 
3713         arg = atof(av[0]);
3714 
3715         /* check if argument is in valid range */
3716         if(arg < argument[cmd].min || arg > argument[cmd].max)
3717         {
3718             fluid_ostream_printf(out, "%s: %s %f must be in range [%f..%f]\n",
3719                                  name_cde[cmd], argument[cmd].name, arg,
3720                                  argument[cmd].min, argument[cmd].max);
3721             return FLUID_FAILED;
3722         }
3723     }
3724 
3725     fluid_player_set_tempo(handler->player, cmd, arg);
3726 
3727     return FLUID_OK;
3728 }
3729 
3730 /* Command handler for "player_tempo_int [mul]" command */
fluid_handle_player_tempo_int(void * data,int ac,char ** av,fluid_ostream_t out)3731 int fluid_handle_player_tempo_int(void *data, int ac, char **av, fluid_ostream_t out)
3732 {
3733     return fluid_handle_player_tempo_cde(data, ac, av, out, FLUID_PLAYER_TEMPO_INTERNAL);
3734 }
3735 
3736 /* Command handler for "player_tempo_bpm bmp" command */
fluid_handle_player_tempo_bpm(void * data,int ac,char ** av,fluid_ostream_t out)3737 int fluid_handle_player_tempo_bpm(void *data, int ac, char **av, fluid_ostream_t out)
3738 {
3739     return fluid_handle_player_tempo_cde(data, ac, av, out, FLUID_PLAYER_TEMPO_EXTERNAL_BPM);
3740 }
3741 
3742 #ifdef LADSPA
3743 
3744 #define CHECK_LADSPA_ENABLED(_fx, _out) \
3745   if (_fx == NULL) \
3746   { \
3747     fluid_ostream_printf(_out, "LADSPA is not enabled.\n"); \
3748     return FLUID_FAILED; \
3749   }
3750 
3751 #define CHECK_LADSPA_INACTIVE(_fx, _out) \
3752   if (fluid_ladspa_is_active(_fx)) \
3753   { \
3754     fluid_ostream_printf(_out, "LADSPA already started.\n"); \
3755     return FLUID_FAILED; \
3756   }
3757 
3758 #define LADSPA_ERR_LEN (1024)
3759 
3760 /**
3761  * ladspa_start
3762  */
fluid_handle_ladspa_start(void * data,int ac,char ** av,fluid_ostream_t out)3763 int fluid_handle_ladspa_start(void *data, int ac, char **av, fluid_ostream_t out)
3764 {
3765     FLUID_ENTRY_COMMAND(data);
3766     fluid_ladspa_fx_t *fx = handler->synth->ladspa_fx;
3767     char error[LADSPA_ERR_LEN];
3768 
3769     if(ac != 0)
3770     {
3771         fluid_ostream_printf(out, "ladspa_start does not accept any arguments\n");
3772         return FLUID_FAILED;
3773     }
3774 
3775     CHECK_LADSPA_ENABLED(fx, out);
3776     CHECK_LADSPA_INACTIVE(fx, out);
3777 
3778     if(fluid_ladspa_check(fx, error, LADSPA_ERR_LEN) != FLUID_OK)
3779     {
3780         fluid_ostream_printf(out, "Unable to start LADSPA: %s", error);
3781         return FLUID_FAILED;
3782     }
3783 
3784     if(fluid_ladspa_activate(fx) != FLUID_OK)
3785     {
3786         fluid_ostream_printf(out, "Unable to start LADSPA.\n");
3787         return FLUID_FAILED;
3788     }
3789 
3790     return FLUID_OK;
3791 }
3792 
3793 /**
3794  * ladspa_stop
3795  */
fluid_handle_ladspa_stop(void * data,int ac,char ** av,fluid_ostream_t out)3796 int fluid_handle_ladspa_stop(void *data, int ac, char **av, fluid_ostream_t out)
3797 {
3798     FLUID_ENTRY_COMMAND(data);
3799     fluid_ladspa_fx_t *fx = handler->synth->ladspa_fx;
3800 
3801     if(ac != 0)
3802     {
3803         fluid_ostream_printf(out, "ladspa_stop does not accept any arguments\n");
3804         return FLUID_FAILED;
3805     }
3806 
3807     CHECK_LADSPA_ENABLED(fx, out);
3808 
3809     if(!fluid_ladspa_is_active(fx))
3810     {
3811         fluid_ostream_printf(out, "LADSPA has not been started.\n");
3812     }
3813 
3814     if(fluid_ladspa_deactivate(fx) != FLUID_OK)
3815     {
3816         fluid_ostream_printf(out, "Unable to stop LADSPA.\n");
3817         return FLUID_FAILED;
3818     }
3819 
3820     return FLUID_OK;
3821 }
3822 
3823 /**
3824  * ladspa_reset
3825  */
fluid_handle_ladspa_reset(void * data,int ac,char ** av,fluid_ostream_t out)3826 int fluid_handle_ladspa_reset(void *data, int ac, char **av, fluid_ostream_t out)
3827 {
3828     FLUID_ENTRY_COMMAND(data);
3829     fluid_ladspa_fx_t *fx = handler->synth->ladspa_fx;
3830 
3831     if(ac != 0)
3832     {
3833         fluid_ostream_printf(out, "ladspa_reset does not accept any arguments\n");
3834         return FLUID_FAILED;
3835     }
3836 
3837     CHECK_LADSPA_ENABLED(fx, out);
3838 
3839     fluid_ladspa_reset(fx);
3840 
3841     return FLUID_OK;
3842 }
3843 
3844 /**
3845  * ladspa_check
3846  */
fluid_handle_ladspa_check(void * data,int ac,char ** av,fluid_ostream_t out)3847 int fluid_handle_ladspa_check(void *data, int ac, char **av, fluid_ostream_t out)
3848 {
3849     FLUID_ENTRY_COMMAND(data);
3850     fluid_ladspa_fx_t *fx = handler->synth->ladspa_fx;
3851     char error[LADSPA_ERR_LEN];
3852 
3853     if(ac != 0)
3854     {
3855         fluid_ostream_printf(out, "ladspa_reset does not accept any arguments\n");
3856         return FLUID_FAILED;
3857     }
3858 
3859     CHECK_LADSPA_ENABLED(fx, out);
3860 
3861     if(fluid_ladspa_check(fx, error, LADSPA_ERR_LEN) != FLUID_OK)
3862     {
3863         fluid_ostream_printf(out, "LADSPA check failed: %s", error);
3864         return FLUID_FAILED;
3865     }
3866 
3867     fluid_ostream_printf(out, "LADSPA check ok\n");
3868 
3869     return FLUID_OK;
3870 }
3871 
3872 /**
3873  * ladspa_set <effect> <port> <value>
3874  */
fluid_handle_ladspa_set(void * data,int ac,char ** av,fluid_ostream_t out)3875 int fluid_handle_ladspa_set(void *data, int ac, char **av, fluid_ostream_t out)
3876 {
3877     FLUID_ENTRY_COMMAND(data);
3878     fluid_ladspa_fx_t *fx = handler->synth->ladspa_fx;
3879 
3880     if(ac != 3)
3881     {
3882         fluid_ostream_printf(out, "ladspa_set needs three arguments: <effect> <port> <value>\n");
3883         return FLUID_FAILED;
3884     };
3885 
3886     CHECK_LADSPA_ENABLED(fx, out);
3887 
3888     /* Redundant check, just here to give a more detailed error message */
3889     if(!fluid_ladspa_effect_port_exists(fx, av[0], av[1]))
3890     {
3891         fluid_ostream_printf(out, "Port '%s' not found on effect '%s'\n", av[1], av[0]);
3892         return FLUID_FAILED;
3893     }
3894 
3895     if(fluid_ladspa_effect_set_control(fx, av[0], av[1], atof(av[2])) != FLUID_OK)
3896     {
3897         fluid_ostream_printf(out, "Failed to set port '%s' on effect '%s', "
3898                              "maybe it is not a control port?\n", av[1], av[0]);
3899         return FLUID_FAILED;
3900     }
3901 
3902     return FLUID_OK;
3903 };
3904 
3905 /**
3906  * ladspa_buffer <name>
3907  */
fluid_handle_ladspa_buffer(void * data,int ac,char ** av,fluid_ostream_t out)3908 int fluid_handle_ladspa_buffer(void *data, int ac, char **av, fluid_ostream_t out)
3909 {
3910     FLUID_ENTRY_COMMAND(data);
3911     fluid_ladspa_fx_t *fx = handler->synth->ladspa_fx;
3912 
3913     if(ac != 1)
3914     {
3915         fluid_ostream_printf(out, "ladspa_buffer needs one argument: <name>\n");
3916         return FLUID_FAILED;
3917     };
3918 
3919     CHECK_LADSPA_ENABLED(fx, out);
3920 
3921     CHECK_LADSPA_INACTIVE(fx, out);
3922 
3923     if(fluid_ladspa_add_buffer(fx, av[0]) != FLUID_OK)
3924     {
3925         fluid_ostream_printf(out, "Failed to add buffer\n");
3926         return FLUID_FAILED;
3927     }
3928 
3929     return FLUID_OK;
3930 };
3931 
3932 /**
3933  * ladspa_effect <name> <library> [plugin] [--mix [gain]]
3934  */
fluid_handle_ladspa_effect(void * data,int ac,char ** av,fluid_ostream_t out)3935 int fluid_handle_ladspa_effect(void *data, int ac, char **av, fluid_ostream_t out)
3936 {
3937     FLUID_ENTRY_COMMAND(data);
3938     fluid_ladspa_fx_t *fx = handler->synth->ladspa_fx;
3939     char *plugin_name = NULL;
3940     int pos;
3941     int mix = FALSE;
3942     float gain = 1.0f;
3943 
3944     if(ac < 2 || ac > 5)
3945     {
3946         fluid_ostream_printf(out, "ladspa_effect invalid arguments: "
3947                              "<name> <library> [plugin] [--mix [gain]]\n");
3948         return FLUID_FAILED;
3949     }
3950 
3951     pos = 2;
3952 
3953     /* If the first optional arg is not --mix, then it must be the plugin label */
3954     if((pos < ac) && (FLUID_STRCMP(av[pos], "--mix") != 0))
3955     {
3956         plugin_name = av[pos];
3957         pos++;
3958     }
3959 
3960     /* If this optional arg is --mix and there's an argument after it, that that
3961      * must be the gain */
3962     if((pos < ac) && (FLUID_STRCMP(av[pos], "--mix") == 0))
3963     {
3964         mix = TRUE;
3965 
3966         if(pos + 1 < ac)
3967         {
3968             gain = atof(av[pos + 1]);
3969         }
3970     }
3971 
3972     CHECK_LADSPA_ENABLED(fx, out);
3973     CHECK_LADSPA_INACTIVE(fx, out);
3974 
3975     if(fluid_ladspa_add_effect(fx, av[0], av[1], plugin_name) != FLUID_OK)
3976     {
3977         fluid_ostream_printf(out, "Failed to create effect\n");
3978         return FLUID_FAILED;
3979     }
3980 
3981     if(mix)
3982     {
3983         if(!fluid_ladspa_effect_can_mix(fx, av[0]))
3984         {
3985             fluid_ostream_printf(out, "Effect '%s' does not support --mix mode\n", av[0]);
3986             return FLUID_FAILED;
3987         }
3988 
3989         if(fluid_ladspa_effect_set_mix(fx, av[0], mix, gain) != FLUID_OK)
3990         {
3991             fluid_ostream_printf(out, "Failed to set --mix mode\n");
3992             return FLUID_FAILED;
3993         }
3994     }
3995 
3996     return FLUID_OK;
3997 }
3998 
3999 /*
4000  * ladspa_link <effect> <port> <buffer or host port>
4001  */
fluid_handle_ladspa_link(void * data,int ac,char ** av,fluid_ostream_t out)4002 int fluid_handle_ladspa_link(void *data, int ac, char **av, fluid_ostream_t out)
4003 {
4004     FLUID_ENTRY_COMMAND(data);
4005     fluid_ladspa_fx_t *fx = handler->synth->ladspa_fx;
4006 
4007     if(ac != 3)
4008     {
4009         fluid_ostream_printf(out, "ladspa_link needs 3 arguments: "
4010                              "<effect> <port> <buffer or host name>\n");
4011         return FLUID_FAILED;
4012     }
4013 
4014     CHECK_LADSPA_ENABLED(fx, out);
4015     CHECK_LADSPA_INACTIVE(fx, out);
4016 
4017     if(!fluid_ladspa_effect_port_exists(fx, av[0], av[1]))
4018     {
4019         fluid_ostream_printf(out, "Port '%s' not found on effect '%s'\n", av[1], av[0]);
4020         return FLUID_FAILED;
4021     }
4022 
4023     if(!fluid_ladspa_host_port_exists(fx, av[2]) && !fluid_ladspa_buffer_exists(fx, av[2]))
4024     {
4025         fluid_ostream_printf(out, "Host port or buffer '%s' not found.\n", av[2]);
4026         return FLUID_FAILED;
4027     }
4028 
4029     if(fluid_ladspa_effect_link(fx, av[0], av[1], av[2]) != FLUID_OK)
4030     {
4031         fluid_ostream_printf(out, "Failed to link port\n");
4032         return FLUID_FAILED;
4033     }
4034 
4035     return FLUID_OK;
4036 }
4037 
4038 #endif /* LADSPA */
4039 
4040 #if WITH_PROFILING
4041 /*
4042 * Locks profile command to prevent simultaneous changes by an other shell
4043 * (may be a server shell (tcp)).
4044 *
4045 * @return FLUID_OK if success , otherwise FLUID_FAILED.
4046 */
fluid_profile_lock_command(fluid_ostream_t out)4047 static int fluid_profile_lock_command(fluid_ostream_t out)
4048 {
4049     if(! fluid_atomic_int_compare_and_exchange(&fluid_profile_lock, 0, 1))
4050     {
4051         fluid_ostream_printf(out,
4052                              "profile command in use in another shell. Try later!\n");
4053         return FLUID_FAILED;
4054     }
4055 
4056     return FLUID_OK;
4057 }
4058 
4059 /*
4060 * Unlocks profile command.
4061 */
fluid_profile_unlock_command(void)4062 static void fluid_profile_unlock_command(void)
4063 {
4064     fluid_atomic_int_set(&fluid_profile_lock, 0);
4065 }
4066 
4067 /*
4068 * command: profile
4069 *
4070 * Prints default parameters used by prof_start command.
4071 *
4072 * Notes:0, bank:0, prog:16, print:0, n_prof:1, dur:500 ms.
4073 *
4074 * @return FLUID_OK if success , otherwise FLUID_FAILED.
4075 */
4076 int
fluid_handle_profile(void * data,int ac,char ** av,fluid_ostream_t out)4077 fluid_handle_profile(void *data, int ac, char **av, fluid_ostream_t out)
4078 {
4079     /* locks to prevent simultaneous changes by an other shell */
4080     /* (may be a server shell (tcp)) */
4081     if(fluid_profile_lock_command(out) == FLUID_FAILED)
4082     {
4083         return FLUID_FAILED;
4084     }
4085 
4086     /* prints default parameters */
4087     fluid_ostream_printf(out,
4088                          " Notes:%d, bank:%d, prog:%d, print:%d, n_prof:%d, dur:%d ms\n",
4089                          fluid_profile_notes, fluid_profile_bank, fluid_profile_prog,
4090                          fluid_profile_print,
4091                          fluid_profile_n_prof, fluid_profile_dur);
4092 
4093     /* unlocks */
4094     fluid_profile_unlock_command();
4095     return FLUID_OK;
4096 }
4097 
4098 /*
4099 * command: prof_set_notes  nbr  [bank prog]
4100 *
4101 * Sets notes number generated by prof_start command.
4102 *
4103 * nbr: notes numbers (generated during command "prof_start").
4104 * bank, prog: preset bank and program number (default value if not specified).
4105 *
4106 * @return FLUID_OK if success , otherwise FLUID_FAILED.
4107 */
4108 int
fluid_handle_prof_set_notes(void * data,int ac,char ** av,fluid_ostream_t out)4109 fluid_handle_prof_set_notes(void *data, int ac, char **av, fluid_ostream_t out)
4110 {
4111     unsigned short nbr;  /* previous parameters */
4112     unsigned char bank, prog;  /* previous parameters */
4113     int r; /* return */
4114 
4115     /* locks to prevent simultaneous changes by an other shell  */
4116     if(fluid_profile_lock_command(out) == FLUID_FAILED)
4117     {
4118         return FLUID_FAILED;
4119     }
4120 
4121     /* checks parameters */
4122     if(ac < 1)
4123     {
4124         fluid_ostream_printf(out, "profile_notes: too few arguments\n");
4125         fluid_profile_unlock_command();
4126         return FLUID_FAILED;
4127     }
4128 
4129     /* gets default parameters */
4130     nbr = fluid_profile_notes, bank = fluid_profile_bank;
4131     prog = fluid_profile_prog;
4132 
4133     r = fluid_is_number(av[0]);
4134 
4135     if(r)
4136     {
4137         /* checks nbr */
4138         nbr = atoi(av[0]);	/* get nbr parameter */
4139 
4140         if(ac >= 2)
4141         {
4142             /* [bank prog] are optional */
4143             if(ac >= 3)
4144             {
4145                 r = fluid_is_number(av[1]) && fluid_is_number(av[2]);
4146 
4147                 if(r)
4148                 {
4149                     bank = atoi(av[1]);	/* gets bank parameter */
4150                     prog = atoi(av[2]);	/* gets prog parameter */
4151                 }
4152             }
4153             else
4154             {
4155                 /* prog is needed */
4156                 fluid_ostream_printf(out, "profile_set_notes: too few arguments\n");
4157                 fluid_profile_unlock_command();
4158                 return FLUID_FAILED;
4159             }
4160         }
4161     }
4162 
4163     if(!r)
4164     {
4165         fluid_ostream_printf(out, "profile_set_notes: invalid argument\n");
4166         fluid_profile_unlock_command();
4167         return FLUID_FAILED;
4168     }
4169 
4170     /* Saves new parameters */
4171     fluid_profile_notes = nbr;
4172     fluid_profile_bank = bank;
4173     fluid_profile_prog = prog;
4174 
4175     /* unlocks */
4176     fluid_profile_unlock_command();
4177     return FLUID_OK;
4178 }
4179 
4180 /*
4181 * command: prof_set_print  mode
4182 *
4183 * The command sets the print mode.
4184 *
4185 * mode: result print mode(used by prof_start").
4186 *       0: simple printing, >0: full printing
4187 *
4188 * @return FLUID_OK if success , otherwise FLUID_FAILED.
4189 */
4190 int
fluid_handle_prof_set_print(void * data,int ac,char ** av,fluid_ostream_t out)4191 fluid_handle_prof_set_print(void *data, int ac, char **av, fluid_ostream_t out)
4192 {
4193     int r;
4194 
4195     /* locks to prevent simultaneous changes by an other shell  */
4196     if(fluid_profile_lock_command(out) == FLUID_FAILED)
4197     {
4198         return FLUID_FAILED;
4199     }
4200 
4201     /* checks parameters */
4202     if(ac < 1)
4203     {
4204         fluid_ostream_printf(out, "profile_set_print: too few arguments\n");
4205         fluid_profile_unlock_command();
4206         return FLUID_FAILED;
4207     }
4208 
4209     /* gets parameters */
4210     if(fluid_is_number(av[0]))
4211     {
4212         /* checks and gets mode */
4213         fluid_profile_print =  atoi(av[0]);	/* gets and saves mode parameter */
4214         r = FLUID_OK;
4215     }
4216     else
4217     {
4218         fluid_ostream_printf(out, "profile_set_print: invalid argument\n");
4219         r = FLUID_FAILED;
4220     }
4221 
4222     /* unlocks */
4223     fluid_profile_unlock_command();
4224     return r;
4225 }
4226 
4227 /*
4228 * Generates simultaneous notes for precise profiling.
4229 *
4230 * @param synth, synthesizer instance.
4231 * @param notes, the number of notes to generate.
4232 * @param bank, prog, soundfont bank preset number used.
4233 * @param out, stream output device.
4234 * @return the number of voices generated. It can be lower than notes number
4235 *  when the preset have instrument only on few key range.
4236 */
fluid_profile_send_notes(fluid_synth_t * synth,int notes,int bank,int prog,fluid_ostream_t out)4237 static unsigned short fluid_profile_send_notes(fluid_synth_t *synth, int notes,
4238         int bank, int prog,
4239         fluid_ostream_t out)
4240 {
4241     int n;         /* number of notes generated */
4242     int n_voices, n_actives = 0; /* Maximum voices, voices generated */
4243     int n_chan, chan, key ;
4244     /* MIDI channels count and maximum polyphony */
4245     n_chan = fluid_synth_count_midi_channels(synth); /* channels count */
4246     n_voices = fluid_synth_get_polyphony(synth); /* maximum voices */
4247     /* */
4248     fluid_ostream_printf(out, "Generating %d notes, ", notes);
4249 
4250     for(n = 0, key = FLUID_PROFILE_LAST_KEY + 1, chan = -1; n < notes; n++, key++)
4251     {
4252         if(key > FLUID_PROFILE_LAST_KEY)
4253         {
4254             /* next channel */
4255             chan++;
4256 
4257             if(chan >= n_chan)
4258             {
4259                 break;    /* stops generation */
4260             }
4261 
4262             /* select preset */
4263             fluid_synth_bank_select(synth, chan, bank);
4264             fluid_synth_program_change(synth, chan, prog);
4265             key = FLUID_PROFILE_FIRST_KEY;
4266         }
4267 
4268         fluid_synth_noteon(synth, chan, key, FLUID_PROFILE_DEFAULT_VEL);
4269         n_actives = fluid_synth_get_active_voice_count(synth); /* running voices */
4270 
4271         if(n_actives >= n_voices)
4272         {
4273             fluid_ostream_printf(out, "max polyphony reached:%d, ", n_voices);
4274             break;  /* stops notes generation */
4275         }
4276     }
4277 
4278     fluid_ostream_printf(out, "generated voices:%d\n", n_actives);
4279     return n_actives;
4280 }
4281 
4282 /*
4283 * command: prof_start  [n_prof   [dur] ]
4284 *
4285 * Starts n_prof measures of dur duration(ms) each.
4286 *
4287 * n_prof  number of measures (default value if not specified).
4288 * dur: measure duration (ms) (default value if not specified).
4289 *
4290 * The result of each measure is displayed.
4291 *
4292 * Note: The command ends when the last measure ends or when the user
4293 * cancels the command using <ENTER> key (cancellation using <ENTER>
4294 * is implemented using FLUID_PROFILE_CANCEL macro in fluid_sys.h).
4295 *
4296 * @return FLUID_OK if success , otherwise FLUID_FAILED.
4297 */
4298 int
fluid_handle_prof_start(void * data,int ac,char ** av,fluid_ostream_t out)4299 fluid_handle_prof_start(void *data, int ac, char **av, fluid_ostream_t out)
4300 {
4301     FLUID_ENTRY_COMMAND(data);
4302     fluid_synth_t *synth = handler->synth;
4303 
4304     unsigned short n_prof, n, dur; /* previous parameters */
4305     unsigned int total_dur, rem_dur; /* total and remainder duration (ms) */
4306     unsigned short notes; /* notes number to generate */
4307     int n_actives = 0;  /* actives voices */
4308     float gain; /* current gain */
4309     int r = 1;	/* checking parameter result */
4310 
4311     /* Locks to prevent simultaneous command by an other shell */
4312     if(fluid_profile_lock_command(out) == FLUID_FAILED)
4313     {
4314         return FLUID_FAILED;
4315     }
4316 
4317     /* Gets previous parameters values */
4318     n_prof = fluid_profile_n_prof;  /* number of measuses */
4319     dur = fluid_profile_dur;        /* duration of one measure (ms) */
4320 
4321     /* check parameters */
4322     if(ac >= 1)
4323     {
4324         r = fluid_is_number(av[0]);
4325 
4326         if(r)
4327         {
4328             n_prof = atoi(av[0]);	/* gets n_prof parameter */
4329 
4330             if(ac >= 2)
4331             {
4332                 r = fluid_is_number(av[1]);
4333 
4334                 if(r)
4335                 {
4336                     dur = atoi(av[1]);/* gets dur parameter */
4337                 }
4338             }
4339         }
4340     }
4341 
4342     if(!r || n_prof < 1 || dur < 1)
4343     {
4344         fluid_ostream_printf(out, "profile_start: invalid argument\n");
4345         fluid_profile_unlock_command();
4346         return FLUID_FAILED;
4347     }
4348 
4349     /* Saves new parameters */
4350     fluid_profile_n_prof = n_prof;
4351     fluid_profile_dur = dur;
4352 
4353     /* Saves current gain */
4354     gain = fluid_synth_get_gain(synth);
4355 
4356     /* Generates notes if any */
4357     notes =  fluid_profile_notes;
4358 
4359     if(notes)
4360     {
4361         /* checks if the synth is playing */
4362         /* Warn the user */
4363         if(fluid_synth_get_active_voice_count(synth))
4364         {
4365             fluid_ostream_printf(out,
4366                                  "Warning: can't generate notes, please stop any playing\n");
4367         }
4368         else
4369         {
4370             float send_gain;
4371             /* sets low gain before sending notes */
4372             fluid_synth_set_gain(synth, 0.01);
4373             /* sends notes */
4374             n_actives = fluid_profile_send_notes(synth, notes, fluid_profile_bank,
4375                                                  fluid_profile_prog, out);
4376             /* compensates gain to avoid a loud sound */
4377             send_gain =  1.0 * pow(10, (n_actives * FLUID_PROFILE_VOICE_ATTEN) / 20);
4378             fluid_synth_set_gain(synth, send_gain);
4379 
4380             /* Before starting profiling immediately we wait to ensures that voices are
4381                currently synthesized by audio rendering API. This ensure that macro
4382                probes will register the expected number of actives voices.
4383             */
4384             fluid_msleep(200); /* wait 200 ms */
4385         }
4386     }
4387 
4388     /* Starts - waits - prints n_prof measures */
4389     fluid_ostream_printf(out, "Number of measures(n_prof):%d, duration of one measure(dur):%dms\n",
4390                          n_prof, dur);
4391 
4392     /* Clears any previous <ENTER> pending key */
4393     fluid_profile_is_cancel_req();
4394 
4395     total_dur = rem_dur = n_prof * dur;
4396 
4397     for(n = 0 ; n < n_prof; rem_dur -= dur, n++)
4398     {
4399         unsigned int end_ticks;/* ending position (in ticks) */
4400         unsigned int tm, ts, rm, rs;
4401         int status;
4402         ts = total_dur / 1000;
4403         tm = ts / 60;
4404         ts = ts % 60;  /* total minutes and seconds */
4405 
4406         rs = rem_dur / 1000;
4407         rm = rs / 60;
4408         rs = rs % 60;  /* remainder minutes and seconds */
4409 
4410         /* Prints total and remainder duration */
4411 #ifdef FLUID_PROFILE_CANCEL
4412         fluid_ostream_printf(out,
4413                              "\nProfiling time(mm:ss): Total=%d:%d  Remainder=%d:%d, press <ENTER> to cancel\n",
4414                              tm, ts, rm, rs);
4415 #else
4416         fluid_ostream_printf(out,
4417                              "\nProfiling time(mm:ss): Total=%d:%d  Remainder=%d:%d\n",
4418                              tm, ts, rm, rs);
4419 #endif
4420 
4421         /* converts duration(ms) in end position in ticks. */
4422         end_ticks = fluid_atomic_int_get(&synth->ticks_since_start) +
4423                     dur * synth->sample_rate / 1000;
4424         /* requests to start the measurement in audio rendering API */
4425         fluid_profile_start_stop(end_ticks, n);
4426 
4427         /* waits while running */
4428         do
4429         {
4430             /* passive waiting */
4431             fluid_msleep(500); /* wait 500 ms */
4432             status = fluid_profile_get_status();
4433         }
4434         while(status  == PROFILE_RUNNING);
4435 
4436         /* checks if data are ready */
4437         if(status == PROFILE_READY)
4438         {
4439             /* profiling data are ready, prints profile data */
4440             fluid_profiling_print_data(synth->sample_rate, out);
4441         }
4442         /* checks if the measurement has been cancelled */
4443         else if(status == PROFILE_CANCELED || status == PROFILE_STOP)
4444         {
4445             fluid_ostream_printf(out, "Profiling cancelled.\n");
4446             break; /* cancel the command */
4447         }
4448     }
4449 
4450     /* Stops voices if any had been generated */
4451     if(n_actives)
4452     {
4453         fluid_ostream_printf(out, "Stopping %d voices...", n_actives);
4454         fluid_synth_system_reset(synth);
4455 
4456         /* waits until all voices become inactives */
4457         do
4458         {
4459             fluid_msleep(10);   /* wait 10 ms */
4460             n_actives = fluid_synth_get_active_voice_count(synth);
4461         }
4462         while(n_actives);
4463 
4464         fluid_ostream_printf(out, "voices stopped.\n");
4465     }
4466 
4467     /* Restores initial gain */
4468     fluid_synth_set_gain(synth, gain);
4469 
4470     /* Unlocks */
4471     fluid_profile_unlock_command();
4472     return FLUID_OK;
4473 }
4474 #endif /* WITH_PROFILING */
4475 
4476 int
fluid_is_number(char * a)4477 fluid_is_number(char *a)
4478 {
4479     while(*a != 0)
4480     {
4481         if(((*a < '0') || (*a > '9')) && (*a != '-') && (*a != '+') && (*a != '.'))
4482         {
4483             return FALSE;
4484         }
4485 
4486         a++;
4487     }
4488 
4489     return TRUE;
4490 }
4491 
4492 char *
fluid_expand_path(char * path,char * new_path,int len)4493 fluid_expand_path(char *path, char *new_path, int len)
4494 {
4495 #if defined(WIN32) || defined(MACOS9)
4496     FLUID_SNPRINTF(new_path, len - 1, "%s", path);
4497 #else
4498 
4499     if((path[0] == '~') && (path[1] == '/'))
4500     {
4501         char *home = getenv("HOME");
4502 
4503         if(home == NULL)
4504         {
4505             FLUID_SNPRINTF(new_path, len - 1, "%s", path);
4506         }
4507         else
4508         {
4509             FLUID_SNPRINTF(new_path, len - 1, "%s%s", home, &path[1]);
4510         }
4511     }
4512     else
4513     {
4514         FLUID_SNPRINTF(new_path, len - 1, "%s", path);
4515     }
4516 
4517 #endif
4518 
4519     new_path[len - 1] = 0;
4520     return new_path;
4521 }
4522 
4523 
4524 
4525 /*
4526  * Command
4527  */
4528 
fluid_cmd_copy(const fluid_cmd_t * cmd)4529 fluid_cmd_t *fluid_cmd_copy(const fluid_cmd_t *cmd)
4530 {
4531     fluid_cmd_t *copy = FLUID_NEW(fluid_cmd_t);
4532 
4533     if(copy == NULL)
4534     {
4535         FLUID_LOG(FLUID_PANIC, "Out of memory");
4536         return NULL;
4537     }
4538 
4539     copy->name = FLUID_STRDUP(cmd->name);
4540     copy->topic = FLUID_STRDUP(cmd->topic);
4541     copy->help = FLUID_STRDUP(cmd->help);
4542     copy->handler = cmd->handler;
4543     return copy;
4544 }
4545 
delete_fluid_cmd(fluid_cmd_t * cmd)4546 void delete_fluid_cmd(fluid_cmd_t *cmd)
4547 {
4548     fluid_return_if_fail(cmd != NULL);
4549     FLUID_FREE(cmd->name);
4550     FLUID_FREE(cmd->topic);
4551     FLUID_FREE(cmd->help);
4552     FLUID_FREE(cmd);
4553 }
4554 
4555 /*
4556  * Command handler
4557  */
4558 
4559 static void
fluid_cmd_handler_destroy_hash_value(void * value)4560 fluid_cmd_handler_destroy_hash_value(void *value)
4561 {
4562     delete_fluid_cmd((fluid_cmd_t *)value);
4563 }
4564 
4565 /**
4566  * Create a new command handler.
4567  *
4568  * See new_fluid_cmd_handler2() for more information.
4569  */
new_fluid_cmd_handler(fluid_synth_t * synth,fluid_midi_router_t * router)4570 fluid_cmd_handler_t *new_fluid_cmd_handler(fluid_synth_t *synth, fluid_midi_router_t *router)
4571 {
4572     return new_fluid_cmd_handler2(fluid_synth_get_settings(synth), synth, router, NULL);
4573 }
4574 
4575 /**
4576  * Create a new command handler.
4577  *
4578  * @param settings If not NULL, all the settings related commands will be added to the new handler. The @p settings
4579  * object must be the same as the one you used for creating the @p synth and @p router. Otherwise the
4580  * behaviour is undefined.
4581  * @param synth If not NULL, all the default synthesizer commands will be added to the new handler.
4582  * @param router If not NULL, all the default midi_router commands will be added to the new handler.
4583  * @param player If not NULL, all the default midi file player commands will be added to the new handler.
4584  * @return New command handler, or NULL if alloc failed
4585  */
new_fluid_cmd_handler2(fluid_settings_t * settings,fluid_synth_t * synth,fluid_midi_router_t * router,fluid_player_t * player)4586 fluid_cmd_handler_t *new_fluid_cmd_handler2(fluid_settings_t *settings,
4587                                             fluid_synth_t *synth,
4588                                             fluid_midi_router_t *router,
4589                                             fluid_player_t *player)
4590 {
4591     unsigned int i;
4592     fluid_cmd_handler_t *handler;
4593 
4594     handler = FLUID_NEW(fluid_cmd_handler_t);
4595 
4596     if(handler == NULL)
4597     {
4598         return NULL;
4599     }
4600 
4601     FLUID_MEMSET(handler, 0, sizeof(*handler));
4602 
4603     handler->commands = new_fluid_hashtable_full(fluid_str_hash, fluid_str_equal,
4604                         NULL, fluid_cmd_handler_destroy_hash_value);
4605 
4606     if(handler->commands == NULL)
4607     {
4608         FLUID_FREE(handler);
4609         return NULL;
4610     }
4611 
4612     handler->settings = settings;
4613     handler->synth = synth;
4614     handler->router = router;
4615     handler->player = player;
4616 
4617     for(i = 0; i < FLUID_N_ELEMENTS(fluid_commands); i++)
4618     {
4619         const fluid_cmd_t *cmd = &fluid_commands[i];
4620         int is_settings_cmd = FLUID_STRCMP(cmd->topic, "settings") == 0;
4621         int is_router_cmd = FLUID_STRCMP(cmd->topic, "router") == 0;
4622         int is_player_cmd = FLUID_STRCMP(cmd->topic, "player") == 0;
4623         int is_synth_cmd = !(is_settings_cmd || is_router_cmd || is_player_cmd);
4624 
4625         int no_cmd = is_settings_cmd && settings == NULL;   /* no settings command */
4626         no_cmd = no_cmd || (is_router_cmd && router == NULL); /* no router command */
4627         no_cmd = no_cmd || (is_player_cmd && player == NULL); /* no player command */
4628         no_cmd = no_cmd || (is_synth_cmd && synth == NULL);   /* no synth command */
4629 
4630         if(no_cmd)
4631         {
4632             /* register a no-op command, this avoids an unknown command error later on */
4633             fluid_cmd_t noop = *cmd;
4634             noop.handler = NULL;
4635             fluid_cmd_handler_register(handler, &noop);
4636         }
4637         else
4638         {
4639             fluid_cmd_handler_register(handler, cmd);
4640         }
4641     }
4642 
4643     return handler;
4644 }
4645 
4646 /**
4647  * Delete a command handler.
4648  *
4649  * @param handler Command handler to delete
4650  */
4651 void
delete_fluid_cmd_handler(fluid_cmd_handler_t * handler)4652 delete_fluid_cmd_handler(fluid_cmd_handler_t *handler)
4653 {
4654     fluid_return_if_fail(handler != NULL);
4655 
4656     delete_fluid_hashtable(handler->commands);
4657     FLUID_FREE(handler);
4658 }
4659 
4660 /**
4661  * Register a new command to the handler.
4662  *
4663  * @param handler Command handler instance
4664  * @param cmd Command info (gets copied)
4665  * @return #FLUID_OK if command was inserted, #FLUID_FAILED otherwise
4666  */
4667 int
fluid_cmd_handler_register(fluid_cmd_handler_t * handler,const fluid_cmd_t * cmd)4668 fluid_cmd_handler_register(fluid_cmd_handler_t *handler, const fluid_cmd_t *cmd)
4669 {
4670     fluid_cmd_t *copy = fluid_cmd_copy(cmd);
4671     fluid_hashtable_insert(handler->commands, copy->name, copy);
4672     return FLUID_OK;
4673 }
4674 
4675 /**
4676  * Unregister a command from a command handler.
4677  *
4678  * @param handler Command handler instance
4679  * @param cmd Name of the command
4680  * @return TRUE if command was found and unregistered, FALSE otherwise
4681  */
4682 int
fluid_cmd_handler_unregister(fluid_cmd_handler_t * handler,const char * cmd)4683 fluid_cmd_handler_unregister(fluid_cmd_handler_t *handler, const char *cmd)
4684 {
4685     return fluid_hashtable_remove(handler->commands, cmd);
4686 }
4687 
4688 int
fluid_cmd_handler_handle(void * data,int ac,char ** av,fluid_ostream_t out)4689 fluid_cmd_handler_handle(void *data, int ac, char **av, fluid_ostream_t out)
4690 {
4691     FLUID_ENTRY_COMMAND(data);
4692     fluid_cmd_t *cmd;
4693 
4694     cmd = fluid_hashtable_lookup(handler->commands, av[0]);
4695 
4696     if(cmd)
4697     {
4698         if(cmd->handler)
4699         {
4700             return (*cmd->handler)(handler, ac - 1, av + 1, out);
4701         }
4702         else
4703         {
4704             /* no-op command */
4705             return 1;
4706         }
4707     }
4708     else
4709     {
4710         fluid_ostream_printf(out, "unknown command: %s (try help)\n", av[0]);
4711         return FLUID_FAILED;
4712     }
4713 }
4714 
4715 
4716 #ifdef NETWORK_SUPPORT
4717 
4718 struct _fluid_server_t
4719 {
4720     fluid_server_socket_t *socket;
4721     fluid_settings_t *settings;
4722     fluid_synth_t *synth;
4723     fluid_midi_router_t *router;
4724     fluid_player_t *player;
4725     fluid_list_t *clients;
4726     fluid_mutex_t mutex;
4727 };
4728 
fluid_server_close(fluid_server_t * server)4729 static void fluid_server_close(fluid_server_t *server)
4730 {
4731     fluid_list_t *list;
4732     fluid_list_t *clients;
4733     fluid_client_t *client;
4734 
4735     fluid_return_if_fail(server != NULL);
4736 
4737     fluid_mutex_lock(server->mutex);
4738     clients = server->clients;
4739     server->clients = NULL;
4740     fluid_mutex_unlock(server->mutex);
4741 
4742     list = clients;
4743 
4744     while(list)
4745     {
4746         client = fluid_list_get(list);
4747         fluid_client_quit(client);
4748         list = fluid_list_next(list);
4749     }
4750 
4751     delete_fluid_list(clients);
4752 
4753     if(server->socket)
4754     {
4755         delete_fluid_server_socket(server->socket);
4756         server->socket = NULL;
4757     }
4758 }
4759 
4760 static int
fluid_server_handle_connection(fluid_server_t * server,fluid_socket_t client_socket,char * addr)4761 fluid_server_handle_connection(fluid_server_t *server, fluid_socket_t client_socket, char *addr)
4762 {
4763     fluid_client_t *client;
4764 
4765     client = new_fluid_client(server, server->settings, client_socket);
4766 
4767     if(client == NULL)
4768     {
4769         return -1;
4770     }
4771 
4772     fluid_server_add_client(server, client);
4773 
4774     return 0;
4775 }
4776 
fluid_server_add_client(fluid_server_t * server,fluid_client_t * client)4777 void fluid_server_add_client(fluid_server_t *server, fluid_client_t *client)
4778 {
4779     fluid_mutex_lock(server->mutex);
4780     server->clients = fluid_list_append(server->clients, client);
4781     fluid_mutex_unlock(server->mutex);
4782 }
4783 
fluid_server_remove_client(fluid_server_t * server,fluid_client_t * client)4784 void fluid_server_remove_client(fluid_server_t *server, fluid_client_t *client)
4785 {
4786     fluid_mutex_lock(server->mutex);
4787     server->clients = fluid_list_remove(server->clients, client);
4788     fluid_mutex_unlock(server->mutex);
4789 }
4790 
4791 struct _fluid_client_t
4792 {
4793     fluid_server_t *server;
4794     fluid_settings_t *settings;
4795     fluid_cmd_handler_t *handler;
4796     fluid_socket_t socket;
4797     fluid_thread_t *thread;
4798 };
4799 
4800 
fluid_client_run(void * data)4801 static fluid_thread_return_t fluid_client_run(void *data)
4802 {
4803     fluid_shell_t shell;
4804     fluid_client_t *client = (fluid_client_t *)data;
4805 
4806     fluid_shell_init(&shell,
4807                      client->settings,
4808                      client->handler,
4809                      fluid_socket_get_istream(client->socket),
4810                      fluid_socket_get_ostream(client->socket));
4811     fluid_shell_run(&shell);
4812     fluid_server_remove_client(client->server, client);
4813     delete_fluid_client(client);
4814 
4815     return FLUID_THREAD_RETURN_VALUE;
4816 }
4817 
4818 
4819 fluid_client_t *
new_fluid_client(fluid_server_t * server,fluid_settings_t * settings,fluid_socket_t sock)4820 new_fluid_client(fluid_server_t *server, fluid_settings_t *settings, fluid_socket_t sock)
4821 {
4822     fluid_client_t *client;
4823 
4824     client = FLUID_NEW(fluid_client_t);
4825 
4826     if(client == NULL)
4827     {
4828         FLUID_LOG(FLUID_ERR, "Out of memory");
4829         return NULL;
4830     }
4831 
4832     client->server = server;
4833     client->socket = sock;
4834     client->settings = settings;
4835     client->handler = new_fluid_cmd_handler2(fluid_synth_get_settings(server->synth),
4836                                              server->synth, server->router,
4837                                              server->player);
4838     client->thread = new_fluid_thread("client", fluid_client_run, client,
4839                                       0, FALSE);
4840 
4841     if(client->handler == NULL || client->thread == NULL)
4842     {
4843         goto error_recovery;
4844     }
4845 
4846     return client;
4847 
4848 error_recovery:
4849     FLUID_LOG(FLUID_ERR, "Out of memory");
4850     delete_fluid_client(client);
4851     return NULL;
4852 
4853 }
4854 
fluid_client_quit(fluid_client_t * client)4855 void fluid_client_quit(fluid_client_t *client)
4856 {
4857     fluid_socket_close(client->socket);
4858 
4859     FLUID_LOG(FLUID_DBG, "fluid_client_quit: joining");
4860     fluid_thread_join(client->thread);
4861     FLUID_LOG(FLUID_DBG, "fluid_client_quit: done");
4862 }
4863 
delete_fluid_client(fluid_client_t * client)4864 void delete_fluid_client(fluid_client_t *client)
4865 {
4866     fluid_return_if_fail(client != NULL);
4867 
4868     delete_fluid_cmd_handler(client->handler);
4869     fluid_socket_close(client->socket);
4870     delete_fluid_thread(client->thread);
4871 
4872     FLUID_FREE(client);
4873 }
4874 
4875 #endif /* NETWORK_SUPPORT */
4876 
4877 /**
4878  * Create a new TCP/IP command shell server.
4879  *
4880  * See new_fluid_server2() for more information.
4881  */
4882 fluid_server_t *
new_fluid_server(fluid_settings_t * settings,fluid_synth_t * synth,fluid_midi_router_t * router)4883 new_fluid_server(fluid_settings_t *settings,
4884                  fluid_synth_t *synth, fluid_midi_router_t *router)
4885 {
4886     return new_fluid_server2(settings, synth, router, NULL);
4887 }
4888 
4889 /**
4890  * Create a new TCP/IP command shell server.
4891  *
4892  * @param settings Settings instance to use for the shell
4893  * @param synth If not NULL, the synth instance for the command handler to be used by the client
4894  * @param router If not NULL, the midi_router instance for the command handler to be used by the client
4895  * @param player If not NULL, the player instance for the command handler to be used by the client
4896  * @return New shell server instance or NULL on error
4897  */
4898 fluid_server_t *
new_fluid_server2(fluid_settings_t * settings,fluid_synth_t * synth,fluid_midi_router_t * router,fluid_player_t * player)4899 new_fluid_server2(fluid_settings_t *settings,
4900                  fluid_synth_t *synth, fluid_midi_router_t *router,
4901                  fluid_player_t *player)
4902 {
4903 #ifdef NETWORK_SUPPORT
4904     fluid_server_t *server;
4905     int port;
4906 
4907     server = FLUID_NEW(fluid_server_t);
4908 
4909     if(server == NULL)
4910     {
4911         FLUID_LOG(FLUID_ERR, "Out of memory");
4912         return NULL;
4913     }
4914 
4915     server->settings = settings;
4916     server->clients = NULL;
4917     server->synth = synth;
4918     server->router = router;
4919     server->player = player;
4920 
4921     fluid_mutex_init(server->mutex);
4922 
4923     fluid_settings_getint(settings, "shell.port", &port);
4924 
4925     server->socket = new_fluid_server_socket(port,
4926                      (fluid_server_func_t) fluid_server_handle_connection,
4927                      server);
4928 
4929     if(server->socket == NULL)
4930     {
4931         FLUID_FREE(server);
4932         return NULL;
4933     }
4934 
4935     return server;
4936 #else
4937     FLUID_LOG(FLUID_WARN, "Network support disabled on this platform.");
4938     return NULL;
4939 #endif
4940 }
4941 
4942 /**
4943  * Delete a TCP/IP shell server.
4944  *
4945  * @param server Shell server instance
4946  */
4947 void
delete_fluid_server(fluid_server_t * server)4948 delete_fluid_server(fluid_server_t *server)
4949 {
4950 #ifdef NETWORK_SUPPORT
4951     fluid_return_if_fail(server != NULL);
4952 
4953     fluid_server_close(server);
4954 
4955     FLUID_FREE(server);
4956 #endif
4957 }
4958 
4959 /**
4960  * Join a shell server thread (wait until it quits).
4961  *
4962  * @param server Shell server instance
4963  * @return #FLUID_OK on success, #FLUID_FAILED otherwise
4964  */
fluid_server_join(fluid_server_t * server)4965 int fluid_server_join(fluid_server_t *server)
4966 {
4967 #ifdef NETWORK_SUPPORT
4968     return fluid_server_socket_join(server->socket);
4969 #else
4970     return FLUID_FAILED;
4971 #endif
4972 }
4973