/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public License * as published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307, USA */ #include "fluidsynth_priv.h" #include "fluid_cmd.h" #include "fluid_synth.h" #include "fluid_settings.h" #include "fluid_io.h" #include "fluid_hash.h" #include "fluid_sys.h" #include "fluid_io.h" #include "fluid_midi_router.h" #include "fluid_sfont.h" #if WITH_READLINE #include #include #endif #define MAX_TOKENS 100 /* LADSPA plugins need lots of parameters */ #define MAX_COMMAND_LEN 1024 /* max command length accepted by fluid_command() */ #define FLUID_WORKLINELENGTH 1024 /* LADSPA plugins use long command lines */ void fluid_shell_settings(fluid_settings_t* settings) { fluid_settings_register_str(settings, "shell.prompt", "", 0, NULL, NULL); fluid_settings_register_int(settings, "shell.port", 9800, 1, 65535, 0, NULL, NULL); } /** the table of all handled commands */ fluid_cmd_t fluid_commands[] = { { "help", "general", (fluid_cmd_func_t) fluid_handle_help, NULL, "help Command summary. 'help help' for more help topics" }, { "quit", "general", (fluid_cmd_func_t) fluid_handle_quit, NULL, "quit Quit the synthesizer" }, { "noteon", "event", (fluid_cmd_func_t) fluid_handle_noteon, NULL, "noteon chan key vel Send noteon" }, { "noteoff", "event", (fluid_cmd_func_t) fluid_handle_noteoff, NULL, "noteoff chan key Send noteoff" }, { "cc", "event", (fluid_cmd_func_t) fluid_handle_cc, NULL, "cc chan ctrl value Send control-change message" }, { "prog", "event", (fluid_cmd_func_t) fluid_handle_prog, NULL, "prog chan num Send program-change message" }, { "select", "event", (fluid_cmd_func_t) fluid_handle_select, NULL, "select chan sfont bank prog Combination of bank-select and program-change" }, { "load", "general", (fluid_cmd_func_t) fluid_handle_load, NULL, "load file [reset] [bankofs] Load SoundFont (reset=0|1, def 1; bankofs=n, def 0)" }, { "unload", "general", (fluid_cmd_func_t) fluid_handle_unload, NULL, "unload id [reset] Unload SoundFont by ID (reset=0|1, default 1)"}, { "reload", "general", (fluid_cmd_func_t) fluid_handle_reload, NULL, "reload id Reload the SoundFont with the specified ID" }, { "fonts", "general", (fluid_cmd_func_t) fluid_handle_fonts, NULL, "fonts Display the list of loaded SoundFonts" }, { "inst", "general", (fluid_cmd_func_t) fluid_handle_inst, NULL, "inst font Print out the available instruments for the font" }, { "channels", "general", (fluid_cmd_func_t) fluid_handle_channels, NULL, "channels [-verbose] Print out preset of all channels" }, { "interp", "general", (fluid_cmd_func_t) fluid_handle_interp, NULL, "interp num Choose interpolation method for all channels" }, { "interpc", "general", (fluid_cmd_func_t) fluid_handle_interpc, NULL, "interpc chan num Choose interpolation method for one channel" }, { "rev_preset", "reverb", (fluid_cmd_func_t) fluid_handle_reverbpreset, NULL, "rev_preset num Load preset num into the reverb unit" }, { "rev_setroomsize", "reverb", (fluid_cmd_func_t) fluid_handle_reverbsetroomsize, NULL, "rev_setroomsize num Change reverb room size" }, { "rev_setdamp", "reverb", (fluid_cmd_func_t) fluid_handle_reverbsetdamp, NULL, "rev_setdamp num Change reverb damping" }, { "rev_setwidth", "reverb", (fluid_cmd_func_t) fluid_handle_reverbsetwidth, NULL, "rev_setwidth num Change reverb width" }, { "rev_setlevel", "reverb", (fluid_cmd_func_t) fluid_handle_reverbsetlevel, NULL, "rev_setlevel num Change reverb level" }, { "reverb", "reverb", (fluid_cmd_func_t) fluid_handle_reverb, NULL, "reverb [0|1|on|off] Turn the reverb on or off" }, { "cho_set_nr", "chorus", (fluid_cmd_func_t) fluid_handle_chorusnr, NULL, "cho_set_nr n Use n delay lines (default 3)" }, { "cho_set_level", "chorus", (fluid_cmd_func_t) fluid_handle_choruslevel, NULL, "cho_set_level num Set output level of each chorus line to num" }, { "cho_set_speed", "chorus", (fluid_cmd_func_t) fluid_handle_chorusspeed, NULL, "cho_set_speed num Set mod speed of chorus to num (Hz)" }, { "cho_set_depth", "chorus", (fluid_cmd_func_t) fluid_handle_chorusdepth, NULL, "cho_set_depth num Set chorus modulation depth to num (ms)" }, { "chorus", "chorus", (fluid_cmd_func_t) fluid_handle_chorus, NULL, "chorus [0|1|on|off] Turn the chorus on or off" }, { "gain", "general", (fluid_cmd_func_t) fluid_handle_gain, NULL, "gain value Set the master gain (0 < gain < 5)" }, { "tuning", "tuning", (fluid_cmd_func_t) fluid_handle_tuning, NULL, "tuning name bank prog Create a tuning with name, bank number, \n" " and program number (0 <= bank,prog <= 127)" }, { "tune", "tuning", (fluid_cmd_func_t) fluid_handle_tune, NULL, "tune bank prog key pitch Tune a key" }, { "settuning", "tuning", (fluid_cmd_func_t) fluid_handle_settuning, NULL, "settuning chan bank prog Set the tuning for a MIDI channel" }, { "resettuning", "tuning", (fluid_cmd_func_t) fluid_handle_resettuning, NULL, "resettuning chan Restore the default tuning of a MIDI channel" }, { "tunings", "tuning", (fluid_cmd_func_t) fluid_handle_tunings, NULL, "tunings Print the list of available tunings" }, { "dumptuning", "tuning", (fluid_cmd_func_t) fluid_handle_dumptuning, NULL, "dumptuning bank prog Print the pitch details of the tuning" }, { "reset", "general", (fluid_cmd_func_t) fluid_handle_reset, NULL, "reset System reset (all notes off, reset controllers)" }, { "set", "settings", (fluid_cmd_func_t) fluid_handle_set, NULL, "set name value Set the value of a controller or settings" }, { "get", "settings", (fluid_cmd_func_t) fluid_handle_get, NULL, "get name Get the value of a controller or settings" }, { "info", "settings", (fluid_cmd_func_t) fluid_handle_info, NULL, "info name Get information about a controller or settings" }, { "settings", "settings", (fluid_cmd_func_t) fluid_handle_settings, NULL, "settings Print out all settings" }, { "echo", "general", (fluid_cmd_func_t) fluid_handle_echo, NULL, "echo arg Print arg" }, /* LADSPA-related commands */ #ifdef LADSPA { "ladspa_clear", "ladspa", (fluid_cmd_func_t) fluid_LADSPA_handle_clear, NULL, "ladspa_clear Resets LADSPA effect unit to bypass state"}, { "ladspa_add", "ladspa", (fluid_cmd_func_t) fluid_LADSPA_handle_add, NULL, "ladspa_add lib plugin n1 <- p1 n2 -> p2 ... Loads and connects LADSPA plugin"}, { "ladspa_start", "ladspa", (fluid_cmd_func_t) fluid_LADSPA_handle_start, NULL, "ladspa_start Starts LADSPA effect unit"}, { "ladspa_declnode", "ladspa", (fluid_cmd_func_t) fluid_LADSPA_handle_declnode, NULL, "ladspa_declnode node value Declares control node `node' with value `value'"}, { "ladspa_setnode", "ladspa", (fluid_cmd_func_t) fluid_LADSPA_handle_setnode, NULL, "ladspa_setnode node value Assigns `value' to `node'"}, #endif { "router_clear", "router", (fluid_cmd_func_t) fluid_midi_router_handle_clear, NULL, "router_clear Clears all routing rules from the midi router"}, { "router_default", "router", (fluid_cmd_func_t) fluid_midi_router_handle_default, NULL, "router_default Resets the midi router to default state"}, { "router_begin", "router", (fluid_cmd_func_t) fluid_midi_router_handle_begin, NULL, "router_begin [note|cc|prog|pbend|cpress|kpress]: Starts a new routing rule"}, { "router_chan", "router", (fluid_cmd_func_t) fluid_midi_router_handle_chan, NULL, "router_chan min max mul add filters and maps midi channels on current rule"}, { "router_par1", "router", (fluid_cmd_func_t) fluid_midi_router_handle_par1, NULL, "router_par1 min max mul add filters and maps parameter 1 (key/ctrl nr)"}, { "router_par2", "router", (fluid_cmd_func_t) fluid_midi_router_handle_par2, NULL, "router_par2 min max mul add filters and maps parameter 2 (vel/cc val)"}, { "router_end", "router", (fluid_cmd_func_t) fluid_midi_router_handle_end, NULL, "router_end closes and commits the current routing rule"}, { NULL, NULL, NULL, NULL, NULL } }; /** * Process a string command. * NOTE: FluidSynth 1.0.8+ no longer modifies the 'cmd' string. * @param handle FluidSynth command handler * @param cmd Command string (NOTE: Gets modified by FluidSynth prior to 1.0.8) * @param out Output stream to display command response to * @return Integer value corresponding to: -1 on command error, 0 on success, * 1 if 'cmd' is a comment or is empty and -2 if quit was issued */ int fluid_command(fluid_cmd_handler_t* handler, char* cmd, fluid_ostream_t out) { char* token[MAX_TOKENS]; char buf[MAX_COMMAND_LEN+1]; char *strtok, *tok; int num_tokens = 0; if (cmd[0] == '#') { return 1; } if (strlen (cmd) > MAX_COMMAND_LEN) { fluid_ostream_printf(out, "Command exceeded max length of %d chars\n", MAX_COMMAND_LEN); return -1; } FLUID_STRCPY(buf, cmd); /* copy - since fluid_strtok thrashes it */ strtok = buf; /* tokenize the input line */ while ((tok = fluid_strtok (&strtok, " \t\n\r"))) token[num_tokens++] = tok; if (num_tokens == 0) return 1; /* handle the command */ return fluid_cmd_handler_handle(handler, num_tokens, &token[0], out); } struct _fluid_shell_t { fluid_settings_t* settings; fluid_cmd_handler_t* handler; fluid_thread_t* thread; fluid_istream_t in; fluid_ostream_t out; }; int fluid_shell_run(fluid_shell_t* shell); 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); 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) { fluid_shell_t* shell = FLUID_NEW(fluid_shell_t); if (shell == NULL) { FLUID_LOG (FLUID_PANIC, "Out of memory"); return NULL; } fluid_shell_init(shell, settings, handler, in, out); if (thread) { shell->thread = new_fluid_thread((fluid_thread_func_t) fluid_shell_run, shell, 1); if (shell->thread == NULL) { delete_fluid_shell(shell); return NULL; } } else { shell->thread = NULL; fluid_shell_run(shell); } return shell; } 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) { shell->settings = settings; shell->handler = handler; shell->in = in; shell->out = out; } void delete_fluid_shell(fluid_shell_t* shell) { if (shell->thread != NULL) { delete_fluid_thread(shell->thread); } FLUID_FREE(shell); } int fluid_shell_run(fluid_shell_t* shell) { char workline[FLUID_WORKLINELENGTH]; char* prompt = ""; int cont = 1; int errors = 0; int n; if (shell->settings) { fluid_settings_getstr(shell->settings, "shell.prompt", &prompt); } /* handle user input */ while (cont) { n = fluid_istream_readline(shell->in, prompt, workline, FLUID_WORKLINELENGTH); if (n < 0) { break; } #if WITH_READLINE if (shell->in == fluid_get_stdin()) { add_history(workline); } #endif /* handle the command */ switch (fluid_command(shell->handler, workline, shell->out)) { case 1: /* empty line or comment */ break; case -1: /* erronous command */ errors++; case 0: /* valid command */ break; case -2: /* quit */ cont = 0; break; } if (n == 0) { break; } } return errors; } void fluid_usershell(fluid_settings_t* settings, fluid_cmd_handler_t* handler) { fluid_shell_t shell; fluid_shell_init(&shell, settings, handler, fluid_get_stdin(), fluid_get_stdout()); fluid_shell_run(&shell); } int fluid_source(fluid_cmd_handler_t* handler, char* filename) { int file; fluid_shell_t shell; #ifdef WIN32 file = _open(filename, _O_RDONLY); #else file = open(filename, O_RDONLY); #endif if (file < 0) { return file; } fluid_shell_init(&shell, NULL, handler, file, fluid_get_stdout()); return fluid_shell_run(&shell); } char* fluid_get_userconf(char* buf, int len) { #if defined(WIN32) || defined(MACOS9) return NULL; #else char* home = getenv("HOME"); if (home == NULL) { return NULL; } else { snprintf(buf, len, "%s/.fluidsynth", home); return buf; } #endif } char* fluid_get_sysconf(char* buf, int len) { #if defined(WIN32) || defined(MACOS9) return NULL; #else snprintf(buf, len, "/etc/fluidsynth.conf"); return buf; #endif } /* * handlers */ int fluid_handle_noteon(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { if (ac < 3) { fluid_ostream_printf(out, "noteon: too few arguments\n"); return -1; } if (!fluid_is_number(av[0]) || !fluid_is_number(av[1]) || !fluid_is_number(av[2])) { fluid_ostream_printf(out, "noteon: invalid argument\n"); return -1; } return fluid_synth_noteon(synth, atoi(av[0]), atoi(av[1]), atoi(av[2])); } int fluid_handle_noteoff(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { if (ac < 2) { fluid_ostream_printf(out, "noteoff: too few arguments\n"); return -1; } if (!fluid_is_number(av[0]) || !fluid_is_number(av[1])) { fluid_ostream_printf(out, "noteon: invalid argument\n"); return -1; } return fluid_synth_noteoff(synth, atoi(av[0]), atoi(av[1])); } int fluid_handle_cc(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { if (ac < 3) { fluid_ostream_printf(out, "cc: too few arguments\n"); return -1; } if (!fluid_is_number(av[0]) || !fluid_is_number(av[1]) || !fluid_is_number(av[2])) { fluid_ostream_printf(out, "cc: invalid argument\n"); return -1; } return fluid_synth_cc(synth, atoi(av[0]), atoi(av[1]), atoi(av[2])); } int fluid_handle_prog(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { if (ac < 2) { fluid_ostream_printf(out, "prog: too few arguments\n"); return -1; } if (!fluid_is_number(av[0]) || !fluid_is_number(av[1])) { fluid_ostream_printf(out, "prog: invalid argument\n"); return -1; } return fluid_synth_program_change(synth, atoi(av[0]), atoi(av[1])); } int fluid_handle_select(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int sfont_id; int chan; int bank; int prog; if (ac < 4) { fluid_ostream_printf(out, "preset: too few arguments\n"); return -1; } if (!fluid_is_number(av[0]) || !fluid_is_number(av[1]) || !fluid_is_number(av[2]) || !fluid_is_number(av[3])) { fluid_ostream_printf(out, "preset: invalid argument\n"); return -1; } chan = atoi(av[0]); sfont_id = atoi(av[1]); bank = atoi(av[2]); prog = atoi(av[3]); if (sfont_id != 0) { return fluid_synth_program_select(synth, chan, sfont_id, bank, prog); } else { if (fluid_synth_bank_select(synth, chan, bank) == FLUID_OK) { return fluid_synth_program_change(synth, chan, prog); } return FLUID_FAILED; } } int fluid_handle_inst(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int font; fluid_sfont_t* sfont; fluid_preset_t preset; int offset; if (ac < 1) { fluid_ostream_printf(out, "inst: too few arguments\n"); return -1; } if (!fluid_is_number(av[0])) { fluid_ostream_printf(out, "inst: invalid argument\n"); return -1; } font = atoi(av[0]); sfont = fluid_synth_get_sfont_by_id(synth, font); offset = fluid_synth_get_bank_offset(synth, font); if (sfont == NULL) { fluid_ostream_printf(out, "inst: invalid font number\n"); return -1; } fluid_sfont_iteration_start(sfont); while (fluid_sfont_iteration_next(sfont, &preset)) { fluid_ostream_printf(out, "%03d-%03d %s\n", fluid_preset_get_banknum(&preset) + offset, fluid_preset_get_num(&preset), fluid_preset_get_name(&preset)); } return 0; } int fluid_handle_channels(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int i; fluid_preset_t* preset; int verbose = 0; if (ac > 0 && strcmp( av[0], "-verbose") == 0) verbose = 1; for (i = 0; i < fluid_synth_count_midi_channels(synth); i++) { preset = fluid_synth_get_channel_preset(synth, i); if (preset == NULL) fluid_ostream_printf(out, "chan %d, no preset\n", i); else if (!verbose) fluid_ostream_printf(out, "chan %d, %s\n", i, fluid_preset_get_name(preset)); else fluid_ostream_printf(out, "chan %d, sfont %d, bank %d, preset %d, %s\n", i, fluid_sfont_get_id( preset->sfont), fluid_preset_get_banknum(preset), fluid_preset_get_num(preset), fluid_preset_get_name(preset)); } return 0; } int fluid_handle_load(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { char buf[1024]; int id; int reset = 1; int offset = 0; if (ac < 1) { fluid_ostream_printf(out, "load: too few arguments\n"); return -1; } if (ac == 2) { reset = atoi(av[1]); } if (ac == 3) { offset = atoi(av[2]); } /* Load the SoundFont without resetting the programs. The reset will * be done later (if requested). */ id = fluid_synth_sfload(synth, fluid_expand_path(av[0], buf, 1024), 0); if (id == -1) { fluid_ostream_printf(out, "failed to load the SoundFont\n"); return -1; } else { fluid_ostream_printf(out, "loaded SoundFont has ID %d\n", id); } if (offset) { fluid_synth_set_bank_offset(synth, id, offset); } /* The reset should be done after the offset is set. */ if (reset) { fluid_synth_program_reset(synth); } return 0; } int fluid_handle_unload(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int reset = 1; if (ac < 1) { fluid_ostream_printf(out, "unload: too few arguments\n"); return -1; } if (!fluid_is_number(av[0])) { fluid_ostream_printf(out, "unload: expected a number as argument\n"); return -1; } if (ac == 2) { reset = atoi(av[1]); } if (fluid_synth_sfunload(synth, atoi(av[0]), reset) != 0) { fluid_ostream_printf(out, "failed to unload the SoundFont\n"); return -1; } return 0; } int fluid_handle_reload(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { if (ac < 1) { fluid_ostream_printf(out, "reload: too few arguments\n"); return -1; } if (!fluid_is_number(av[0])) { fluid_ostream_printf(out, "reload: expected a number as argument\n"); return -1; } if (fluid_synth_sfreload(synth, atoi(av[0])) == -1) { fluid_ostream_printf(out, "failed to reload the SoundFont\n"); return -1; } return 0; } int fluid_handle_fonts(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int i; fluid_sfont_t* sfont; int num; num = fluid_synth_sfcount(synth); if (num == 0) { fluid_ostream_printf(out, "no SoundFont loaded (try load)\n"); return 0; } fluid_ostream_printf(out, "ID Name\n"); for (i = 0; i < num; i++) { sfont = fluid_synth_get_sfont(synth, i); fluid_ostream_printf(out, "%2d %s\n", fluid_sfont_get_id(sfont), fluid_sfont_get_name(sfont)); } return 0; } int fluid_handle_mstat(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { /* fluid_ostream_printf(out, "Dvr=%s, Dev=%s\n", */ /* fluid_midi_handler_get_driver_name(midi), */ /* fluid_midi_handler_get_device_name(midi)); */ /* fluid_ostream_printf(out, "Stat=%s, On=%d, Off=%d, Prog=%d, Pbend=%d, Err=%d\n", */ /* fluid_midi_handler_get_status(midi), */ /* fluid_midi_handler_get_event_count(midi, 0x90), */ /* fluid_midi_handler_get_event_count(midi, 0x80), */ /* fluid_midi_handler_get_event_count(midi, 0xc0), */ /* fluid_midi_handler_get_event_count(midi, 0xe0), */ /* fluid_midi_handler_get_event_count(midi, 0)); */ fluid_ostream_printf(out, "not yet implemented\n"); return -1; } /* Purpose: * Response to 'rev_preset' command. * Load the values from a reverb preset into the reverb unit. */ int fluid_handle_reverbpreset(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int reverb_preset_number; if (ac < 1) { fluid_ostream_printf(out, "rev_preset: too few arguments\n"); return -1; } reverb_preset_number = atoi(av[0]); if (fluid_synth_set_reverb_preset(synth, reverb_preset_number)!=FLUID_OK){ fluid_ostream_printf(out, "rev_preset: Failed. Parameter out of range?\n"); return -1; }; return 0; } /* Purpose: * Response to 'rev_setroomsize' command. * Load the new room size into the reverb unit. */ int fluid_handle_reverbsetroomsize(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { fluid_real_t room_size; if (ac < 1) { fluid_ostream_printf(out, "rev_setroomsize: too few arguments.\n"); return -1; } room_size = atof(av[0]); if (room_size < 0){ fluid_ostream_printf(out, "rev_setroomsize: Room size must be positive!\n"); return -1; } if (room_size > 1.2){ fluid_ostream_printf(out, "rev_setroomsize: Room size too big!\n"); return -1; } fluid_revmodel_setroomsize(synth->reverb, room_size); return 0; } /* Purpose: * Response to 'rev_setdamp' command. * Load the new damp factor into the reverb unit. */ int fluid_handle_reverbsetdamp(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { fluid_real_t damp; if (ac < 1) { fluid_ostream_printf(out, "rev_setdamp: too few arguments.\n"); return -1; } damp = atof(av[0]); if ((damp < 0.0f) || (damp > 1)){ fluid_ostream_printf(out, "rev_setdamp: damp must be between 0 and 1!\n"); return -1; } fluid_revmodel_setdamp(synth->reverb, damp); return 0; } /* Purpose: * Response to 'rev_setwidth' command. * Load the new width into the reverb unit. */ int fluid_handle_reverbsetwidth(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { fluid_real_t width; if (ac < 1) { fluid_ostream_printf(out, "rev_setwidth: too few arguments.\n"); return -1; } width = atof(av[0]); if ((width < 0) || (width > 100)){ fluid_ostream_printf(out, "rev_setroomsize: Too wide! (0..100)\n"); return 0; } fluid_revmodel_setwidth(synth->reverb, width); return 0; } /* Purpose: * Response to 'rev_setlevel' command. * Load the new level into the reverb unit. */ int fluid_handle_reverbsetlevel(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { fluid_real_t level; if (ac < 1) { fluid_ostream_printf(out, "rev_setlevel: too few arguments.\n"); return -1; } level = atof(av[0]); if (abs(level) > 30){ fluid_ostream_printf(out, "rev_setlevel: Value too high! (Value of 10 =+20 dB)\n"); return 0; } fluid_revmodel_setlevel(synth->reverb, level); return 0; } /* Purpose: * Response to 'reverb' command. * Change the FLUID_REVERB flag in the synth */ int fluid_handle_reverb(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { if (ac < 1) { fluid_ostream_printf(out, "reverb: too few arguments.\n"); return -1; } if ((strcmp(av[0], "0") == 0) || (strcmp(av[0], "off") == 0)) { fluid_synth_set_reverb_on(synth,0); } else if ((strcmp(av[0], "1") == 0) || (strcmp(av[0], "on") == 0)) { fluid_synth_set_reverb_on(synth,1); } else { fluid_ostream_printf(out, "reverb: invalid arguments %s [0|1|on|off]", av[0]); return -1; } return 0; } /* Purpose: * Response to 'chorus_setnr' command */ int fluid_handle_chorusnr(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int nr; if (ac < 1) { fluid_ostream_printf(out, "cho_set_nr: too few arguments.\n"); return -1; } nr = atoi(av[0]); fluid_chorus_set_nr(synth->chorus, nr); return fluid_chorus_update(synth->chorus); } /* Purpose: * Response to 'chorus_setlevel' command */ int fluid_handle_choruslevel(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { fluid_real_t level; if (ac < 1) { fluid_ostream_printf(out, "cho_set_level: too few arguments.\n"); return -1; } level = atof(av[0]); fluid_chorus_set_level(synth->chorus, level); return fluid_chorus_update(synth->chorus); } /* Purpose: * Response to 'chorus_setspeed' command */ int fluid_handle_chorusspeed(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { fluid_real_t speed; if (ac < 1) { fluid_ostream_printf(out, "cho_set_speed: too few arguments.\n"); return -1; } speed = atof(av[0]); fluid_chorus_set_speed_Hz(synth->chorus, speed); return fluid_chorus_update(synth->chorus); } /* Purpose: * Response to 'chorus_setdepth' command */ int fluid_handle_chorusdepth(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { fluid_real_t depth; if (ac < 1) { fluid_ostream_printf(out, "cho_set_depth: too few arguments.\n"); return -1; } depth = atof(av[0]); fluid_chorus_set_depth_ms(synth->chorus, depth); return fluid_chorus_update(synth->chorus); } int fluid_handle_chorus(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { if (ac < 1) { fluid_ostream_printf(out, "chorus: too few arguments\n"); return -1; } if ((strcmp(av[0], "0") == 0) || (strcmp(av[0], "off") == 0)) { fluid_synth_set_chorus_on(synth,0); } else if ((strcmp(av[0], "1") == 0) || (strcmp(av[0], "on") == 0)) { fluid_synth_set_chorus_on(synth,1); } else { fluid_ostream_printf(out, "chorus: invalid arguments %s [0|1|on|off]", av[0]); return -1; } return 0; } /* Purpose: * Response to the 'echo' command. * The command itself is useful, when the synth is used via TCP/IP. * It can signal for example, that a list of commands has been processed. */ int fluid_handle_echo(fluid_cmd_handler_t* handler, int ac, char** av, fluid_ostream_t out) { if (ac < 1) { fluid_ostream_printf(out, "echo: too few arguments.\n"); return -1; } fluid_ostream_printf(out, "%s\n",av[0]); return 0; } int fluid_handle_source(fluid_cmd_handler_t* handler, int ac, char** av, fluid_ostream_t out) { if (ac < 1) { fluid_ostream_printf(out, "source: too few arguments.\n"); return -1; } fluid_source(handler, av[0]); return 0; } /* Purpose: * Response to 'gain' command. */ int fluid_handle_gain(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { float gain; if (ac < 1) { fluid_ostream_printf(out, "gain: too few arguments.\n"); return -1; } gain = atof(av[0]); if ((gain < 0.0f) || (gain > 5.0f)) { fluid_ostream_printf(out, "gain: value should be between '0' and '5'.\n"); return -1; }; fluid_synth_set_gain(synth, gain); return 0; } /* Purpose: * Response to 'interp' command. */ int fluid_handle_interp(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int interp; int chan=-1; /* -1: Set all channels */ if (ac < 1) { fluid_ostream_printf(out, "interp: too few arguments.\n"); return -1; } interp = atoi(av[0]); if ((interp < 0) || (interp > FLUID_INTERP_HIGHEST)) { fluid_ostream_printf(out, "interp: Bad value\n"); return -1; }; fluid_synth_set_interp_method(synth, chan, interp); return 0; } /* Purpose: * Response to 'interp' command. */ int fluid_handle_interpc(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int interp; int chan; if (ac < 2) { fluid_ostream_printf(out, "interpc: too few arguments.\n"); return -1; } chan = atoi(av[0]); interp = atoi(av[1]); if ((chan < 0) || (chan >= fluid_synth_count_midi_channels(synth))){ fluid_ostream_printf(out, "interp: Bad value for channel number.\n"); return -1; }; if ((interp < 0) || (interp > FLUID_INTERP_HIGHEST)) { fluid_ostream_printf(out, "interp: Bad value for interpolation method.\n"); return -1; }; fluid_synth_set_interp_method(synth, chan, interp); return 0; } int fluid_handle_tuning(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { char *name; int bank, prog; if (ac < 3) { fluid_ostream_printf(out, "tuning: too few arguments.\n"); return -1; } name = av[0]; if (!fluid_is_number(av[1])) { fluid_ostream_printf(out, "tuning: 2nd argument should be a number.\n"); return -1; } bank = atoi(av[1]); if ((bank < 0) || (bank >= 128)){ fluid_ostream_printf(out, "tuning: invalid bank number.\n"); return -1; }; if (!fluid_is_number(av[2])) { fluid_ostream_printf(out, "tuning: 3rd argument should be a number.\n"); return -1; } prog = atoi(av[2]); if ((prog < 0) || (prog >= 128)){ fluid_ostream_printf(out, "tuning: invalid program number.\n"); return -1; }; fluid_synth_create_key_tuning(synth, bank, prog, name, NULL); return 0; } int fluid_handle_tune(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int bank, prog, key; double pitch; if (ac < 4) { fluid_ostream_printf(out, "tune: too few arguments.\n"); return -1; } if (!fluid_is_number(av[0])) { fluid_ostream_printf(out, "tune: 1st argument should be a number.\n"); return -1; } bank = atoi(av[0]); if ((bank < 0) || (bank >= 128)){ fluid_ostream_printf(out, "tune: invalid bank number.\n"); return -1; }; if (!fluid_is_number(av[1])) { fluid_ostream_printf(out, "tune: 2nd argument should be a number.\n"); return -1; } prog = atoi(av[1]); if ((prog < 0) || (prog >= 128)){ fluid_ostream_printf(out, "tune: invalid program number.\n"); return -1; }; if (!fluid_is_number(av[2])) { fluid_ostream_printf(out, "tune: 3rd argument should be a number.\n"); return -1; } key = atoi(av[2]); if ((key < 0) || (key >= 128)){ fluid_ostream_printf(out, "tune: invalid key number.\n"); return -1; }; pitch = atof(av[3]); if (pitch < 0.0f) { fluid_ostream_printf(out, "tune: invalid pitch.\n"); return -1; }; fluid_synth_tune_notes(synth, bank, prog, 1, &key, &pitch, 0); return 0; } int fluid_handle_settuning(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int chan, bank, prog; if (ac < 3) { fluid_ostream_printf(out, "settuning: too few arguments.\n"); return -1; } if (!fluid_is_number(av[0])) { fluid_ostream_printf(out, "tune: 1st argument should be a number.\n"); return -1; } chan = atoi(av[0]); if ((chan < 0) || (chan >= fluid_synth_count_midi_channels(synth))){ fluid_ostream_printf(out, "tune: invalid channel number.\n"); return -1; }; if (!fluid_is_number(av[1])) { fluid_ostream_printf(out, "tuning: 2nd argument should be a number.\n"); return -1; } bank = atoi(av[1]); if ((bank < 0) || (bank >= 128)){ fluid_ostream_printf(out, "tuning: invalid bank number.\n"); return -1; }; if (!fluid_is_number(av[2])) { fluid_ostream_printf(out, "tuning: 3rd argument should be a number.\n"); return -1; } prog = atoi(av[2]); if ((prog < 0) || (prog >= 128)){ fluid_ostream_printf(out, "tuning: invalid program number.\n"); return -1; }; fluid_synth_select_tuning(synth, chan, bank, prog); return 0; } int fluid_handle_resettuning(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int chan; if (ac < 1) { fluid_ostream_printf(out, "resettuning: too few arguments.\n"); return -1; } if (!fluid_is_number(av[0])) { fluid_ostream_printf(out, "tune: 1st argument should be a number.\n"); return -1; } chan = atoi(av[0]); if ((chan < 0) || (chan >= fluid_synth_count_midi_channels(synth))){ fluid_ostream_printf(out, "tune: invalid channel number.\n"); return -1; }; fluid_synth_reset_tuning(synth, chan); return 0; } int fluid_handle_tunings(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int bank, prog; char name[256]; int count = 0; fluid_synth_tuning_iteration_start(synth); while (fluid_synth_tuning_iteration_next(synth, &bank, &prog)) { fluid_synth_tuning_dump(synth, bank, prog, name, 256, NULL); fluid_ostream_printf(out, "%03d-%03d %s\n", bank, prog, name); count++; } if (count == 0) { fluid_ostream_printf(out, "No tunings available\n"); } return 0; } int fluid_handle_dumptuning(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { int bank, prog, i; double pitch[128]; char name[256]; if (ac < 2) { fluid_ostream_printf(out, "dumptuning: too few arguments.\n"); return -1; } if (!fluid_is_number(av[0])) { fluid_ostream_printf(out, "dumptuning: 1st argument should be a number.\n"); return -1; } bank = atoi(av[0]); if ((bank < 0) || (bank >= 128)){ fluid_ostream_printf(out, "dumptuning: invalid bank number.\n"); return -1; }; if (!fluid_is_number(av[1])) { fluid_ostream_printf(out, "dumptuning: 2nd argument should be a number.\n"); return -1; } prog = atoi(av[1]); if ((prog < 0) || (prog >= 128)){ fluid_ostream_printf(out, "dumptuning: invalid program number.\n"); return -1; }; fluid_synth_tuning_dump(synth, bank, prog, name, 256, pitch); fluid_ostream_printf(out, "%03d-%03d %s:\n", bank, prog, name); for (i = 0; i < 128; i++) { fluid_ostream_printf(out, "key %03d, pitch %5.2f\n", i, pitch[i]); } return 0; } int fluid_handle_set(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { if (ac < 2) { fluid_ostream_printf(out, "set: too few arguments.\n"); return -1; } if (fluid_is_number(av[1])) { if (FLUID_STRCHR(av[1], '.') != NULL) { fluid_synth_setnum(synth, av[0], atof(av[1])); } else { fluid_synth_setint(synth, av[0], atoi(av[1])); } } else { fluid_synth_setstr(synth, av[0], av[1]); } return 0; } int fluid_handle_get(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { if (ac < 1) { fluid_ostream_printf(out, "get: too few arguments.\n"); return -1; } switch (fluid_settings_get_type(fluid_synth_get_settings(synth), av[0])) { case FLUID_NO_TYPE: fluid_ostream_printf(out, "get: no such settings '%s'.", av[0]); return -1; case FLUID_NUM_TYPE: { double value; fluid_synth_getnum(synth, av[0], &value); fluid_ostream_printf(out, "%.3f", value); break; } case FLUID_INT_TYPE: { int value; fluid_synth_getint(synth, av[0], &value); fluid_ostream_printf(out, "%d", value); break; } case FLUID_STR_TYPE: { char* s; fluid_synth_getstr(synth, av[0], &s); fluid_ostream_printf(out, "%s", s); break; } case FLUID_SET_TYPE: fluid_ostream_printf(out, "%s is a node", av[0]); break; } return 0; } struct _fluid_handle_settings_data_t { int len; fluid_synth_t* synth; fluid_ostream_t out; }; static void fluid_handle_settings_iter1(void* data, char* name, int type) { struct _fluid_handle_settings_data_t* d = (struct _fluid_handle_settings_data_t*) data; int len = FLUID_STRLEN(name); if (len > d->len) { d->len = len; } } static void fluid_handle_settings_iter2(void* data, char* name, int type) { struct _fluid_handle_settings_data_t* d = (struct _fluid_handle_settings_data_t*) data; int len = FLUID_STRLEN(name); fluid_ostream_printf(d->out, "%s", name); while (len++ < d->len) { fluid_ostream_printf(d->out, " "); } fluid_ostream_printf(d->out, " "); switch (fluid_settings_get_type(fluid_synth_get_settings(d->synth), name)) { case FLUID_NUM_TYPE: { double value; fluid_synth_getnum(d->synth, name, &value); fluid_ostream_printf(d->out, "%.3f\n", value); break; } case FLUID_INT_TYPE: { int value; fluid_synth_getint(d->synth, name, &value); fluid_ostream_printf(d->out, "%d\n", value); break; } case FLUID_STR_TYPE: { char* s; fluid_synth_getstr(d->synth, name, &s); fluid_ostream_printf(d->out, "%s\n", s); break; } } } int fluid_handle_settings(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { struct _fluid_handle_settings_data_t data; data.len = 0; data.synth = synth; data.out = out; fluid_settings_foreach(fluid_synth_get_settings(synth), &data, fluid_handle_settings_iter1); fluid_settings_foreach(fluid_synth_get_settings(synth), &data, fluid_handle_settings_iter2); return 0; } struct _fluid_handle_option_data_t { int first; fluid_ostream_t out; }; void fluid_handle_print_option(void* data, char* name, char* option) { struct _fluid_handle_option_data_t* d = (struct _fluid_handle_option_data_t*) data; if (d->first) { fluid_ostream_printf(d->out, "%s", option); d->first = 0; } else { fluid_ostream_printf(d->out, ", %s", option); } } int fluid_handle_info(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { fluid_settings_t* settings = fluid_synth_get_settings(synth); struct _fluid_handle_option_data_t data; if (ac < 1) { fluid_ostream_printf(out, "info: too few arguments.\n"); return -1; } switch (fluid_settings_get_type(settings, av[0])) { case FLUID_NO_TYPE: fluid_ostream_printf(out, "info: no such settings '%s'.", av[0]); return -1; case FLUID_NUM_TYPE: { double value, min, max; fluid_settings_getnum_range(settings, av[0], &min, &max); fluid_settings_getnum(settings, av[0], &value); fluid_ostream_printf(out, "%s:\n", av[0]); fluid_ostream_printf(out, "Type: number\n"); fluid_ostream_printf(out, "Value: %.3f\n", value); fluid_ostream_printf(out, "Minimum value: %.3f\n", min); fluid_ostream_printf(out, "Maximum value: %.3f\n", max); fluid_ostream_printf(out, "Default value: %.3f\n", fluid_settings_getnum_default(settings, av[0])); fluid_ostream_printf(out, "Real-time: %s\n", fluid_settings_is_realtime(settings, av[0])? "yes" : "no"); break; } case FLUID_INT_TYPE: { int value, min, max; fluid_settings_getint_range(settings, av[0], &min, &max); fluid_settings_getint(settings, av[0], &value); fluid_ostream_printf(out, "%s:\n", av[0]); fluid_ostream_printf(out, "Type: integer\n"); fluid_ostream_printf(out, "Value: %d\n", value); fluid_ostream_printf(out, "Minimum value: %d\n", min); fluid_ostream_printf(out, "Maximum value: %d\n", max); fluid_ostream_printf(out, "Default value: %d\n", fluid_settings_getint_default(settings, av[0])); fluid_ostream_printf(out, "Real-time: %s\n", fluid_settings_is_realtime(settings, av[0])? "yes" : "no"); break; } case FLUID_STR_TYPE: { char *s; fluid_settings_getstr(settings, av[0], &s); fluid_ostream_printf(out, "%s:\n", av[0]); fluid_ostream_printf(out, "Type: string\n"); fluid_ostream_printf(out, "Value: %s\n", s); fluid_ostream_printf(out, "Default value: %s\n", fluid_settings_getstr_default(settings, av[0])); data.out = out; data.first = 1; fluid_ostream_printf(out, "Options: "); fluid_settings_foreach_option(settings, av[0], &data, fluid_handle_print_option); fluid_ostream_printf(out, "\n"); fluid_ostream_printf(out, "Real-time: %s\n", fluid_settings_is_realtime(settings, av[0])? "yes" : "no"); break; } case FLUID_SET_TYPE: fluid_ostream_printf(out, "%s is a node", av[0]); break; } return 0; } int fluid_handle_reset(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { fluid_synth_system_reset(synth); return 0; } int fluid_handle_quit(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { fluid_ostream_printf(out, "cheers!\n"); return -2; } int fluid_handle_help(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out) { /* Purpose: * Prints the help text for the command line commands. * Can be used as follows: * - help * - help (topic), where (topic) is 'general', 'chorus', etc. * - help all * - help help */ char* topic = "general"; /* default, if no topic is given */ int count = 0; int i; fluid_ostream_printf(out, "\n"); /* 1st argument (optional): help topic */ if (ac >= 1) { topic = av[0]; } if (strcmp(topic,"help") == 0){ /* "help help": Print a list of all topics */ fluid_ostream_printf(out, "*** Help topics:***\n" "help help (prints this list)\n" "help all (prints all topics)\n"); for (i = 0; fluid_commands[i].name != NULL; i++) { int listed_first_time = 1; int ii; for (ii = 0; ii < i; ii++){ if (strcmp(fluid_commands[i].topic, fluid_commands[ii].topic) == 0){ listed_first_time = 0; }; /* if topic has already been listed */ }; /* for all topics (inner loop) */ if (listed_first_time){ fluid_ostream_printf(out, "help %s\n",fluid_commands[i].topic); }; }; /* for all topics (outer loop) */ } else { /* help (arbitrary topic or "all") */ for (i = 0; fluid_commands[i].name != NULL; i++) { fluid_cmd_t cmd = fluid_commands[i]; if (cmd.help != NULL) { if (strcmp(topic,"all") == 0 || strcmp(topic,cmd.topic) == 0){ fluid_ostream_printf(out, "%s\n", fluid_commands[i].help); count++; }; /* if it matches the topic */ }; /* if help text exists */ }; /* foreach command */ if (count == 0){ fluid_ostream_printf(out, "Unknown help topic. Try 'help help'.\n"); }; }; return 0; } int fluid_is_number(char* a) { while (*a != 0) { if (((*a < '0') || (*a > '9')) && (*a != '-') && (*a != '+') && (*a != '.')) { return 0; } a++; } return 1; } int fluid_is_empty(char* a) { while (*a != 0) { if ((*a != ' ') && (*a != '\t') && (*a != '\n') && (*a != '\r')) { return 0; } a++; } return 1; } char* fluid_expand_path(char* path, char* new_path, int len) { #if defined(WIN32) || defined(MACOS9) snprintf(new_path, len - 1, "%s", path); #else if ((path[0] == '~') && (path[1] == '/')) { char* home = getenv("HOME"); if (home == NULL) { snprintf(new_path, len - 1, "%s", path); } else { snprintf(new_path, len - 1, "%s%s", home, &path[1]); } } else { snprintf(new_path, len - 1, "%s", path); } #endif new_path[len - 1] = 0; return new_path; } /* * Command */ fluid_cmd_t* fluid_cmd_copy(fluid_cmd_t* cmd) { fluid_cmd_t* copy = FLUID_NEW(fluid_cmd_t); if (copy == NULL) { FLUID_LOG (FLUID_PANIC, "Out of memory"); return NULL; } copy->name = FLUID_STRDUP(cmd->name); copy->topic = FLUID_STRDUP(cmd->topic); copy->help = FLUID_STRDUP(cmd->help); copy->handler = cmd->handler; copy->data = cmd->data; return copy; } void delete_fluid_cmd(fluid_cmd_t* cmd) { if (cmd->name) { FLUID_FREE(cmd->name); } if (cmd->topic) { FLUID_FREE(cmd->topic); } if (cmd->help) { FLUID_FREE(cmd->help); } FLUID_FREE(cmd); } /* * Command handler */ void fluid_cmd_handler_delete(void* value, int type) { delete_fluid_cmd((fluid_cmd_t*) value); } fluid_cmd_handler_t* new_fluid_cmd_handler(fluid_synth_t* synth) { int i; fluid_cmd_handler_t* handler; fluid_cmd_t source = { "source", "general", (fluid_cmd_func_t) fluid_handle_source, NULL, "source filename Load a file and parse every line as a command" }; handler = new_fluid_hashtable(fluid_cmd_handler_delete); if (handler == NULL) { return NULL; } if (synth != NULL) { for (i = 0; fluid_commands[i].name != NULL; i++) { fluid_commands[i].data = synth; fluid_cmd_handler_register(handler, &fluid_commands[i]); fluid_commands[i].data = NULL; } } source.data = handler; fluid_cmd_handler_register(handler, &source); return handler; } void delete_fluid_cmd_handler(fluid_cmd_handler_t* handler) { delete_fluid_hashtable(handler); } int fluid_cmd_handler_register(fluid_cmd_handler_t* handler, fluid_cmd_t* cmd) { fluid_cmd_t* copy = fluid_cmd_copy(cmd); fluid_hashtable_insert(handler, copy->name, copy, 0); return 0; } int fluid_cmd_handler_unregister(fluid_cmd_handler_t* handler, char* cmd) { return fluid_hashtable_remove(handler, cmd); } int fluid_cmd_handler_handle(fluid_cmd_handler_t* handler, int ac, char** av, fluid_ostream_t out) { void *vp; /* use a void pointer to avoid GCC "type-punned pointer" warning */ fluid_cmd_t* cmd; if (fluid_hashtable_lookup(handler, av[0], &vp, NULL) && ((fluid_cmd_t *)vp)->handler) { cmd = vp; return (*cmd->handler)(cmd->data, ac - 1, av + 1, out); } else { fluid_ostream_printf(out, "unknown command: %s (try help)\n", av[0]); return -1; } } #if !defined(WITHOUT_SERVER) struct _fluid_server_t { fluid_server_socket_t* socket; fluid_settings_t* settings; fluid_server_newclient_func_t newclient; void* data; fluid_list_t* clients; fluid_mutex_t mutex; }; static void fluid_server_handle_connection(fluid_server_t* server, fluid_socket_t client_socket, char* addr); static void fluid_server_close(fluid_server_t* server); fluid_server_t* new_fluid_server(fluid_settings_t* settings, fluid_server_newclient_func_t newclient, void* data) { fluid_server_t* server; int port; server = FLUID_NEW(fluid_server_t); if (server == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } server->settings = settings; server->clients = NULL; server->newclient = newclient; server->data = data; fluid_mutex_init(server->mutex); fluid_settings_getint(settings, "shell.port", &port); server->socket = new_fluid_server_socket(port, (fluid_server_func_t) fluid_server_handle_connection, server); if (server->socket == NULL) { FLUID_FREE(server); return NULL; } return server; } void delete_fluid_server(fluid_server_t* server) { if (server == NULL) { return; } fluid_server_close(server); FLUID_FREE(server); } static void fluid_server_close(fluid_server_t* server) { fluid_list_t* list; fluid_list_t* clients; fluid_client_t* client; if (server == NULL) { return; } fluid_mutex_lock(server->mutex); clients = server->clients; server->clients = NULL; fluid_mutex_unlock(server->mutex); list = clients; while (list) { client = fluid_list_get(list); fluid_client_quit(client); list = fluid_list_next(list); } delete_fluid_list(clients); if (server->socket) { delete_fluid_server_socket(server->socket); server->socket = NULL; } } static void fluid_server_handle_connection(fluid_server_t* server, fluid_socket_t client_socket, char* addr) { fluid_client_t* client; fluid_cmd_handler_t* handler; handler = server->newclient(server->data, addr); if (handler == NULL) { return; } client = new_fluid_client(server, server->settings, handler, client_socket); if (client == NULL) { return; } fluid_server_add_client(server, client); } void fluid_server_add_client(fluid_server_t* server, fluid_client_t* client) { fluid_mutex_lock(server->mutex); server->clients = fluid_list_append(server->clients, client); fluid_mutex_unlock(server->mutex); } void fluid_server_remove_client(fluid_server_t* server, fluid_client_t* client) { fluid_mutex_lock(server->mutex); server->clients = fluid_list_remove(server->clients, client); fluid_mutex_unlock(server->mutex); } int fluid_server_join(fluid_server_t* server) { return fluid_server_socket_join(server->socket); } struct _fluid_client_t { fluid_server_t* server; fluid_settings_t* settings; fluid_cmd_handler_t* handler; fluid_socket_t socket; fluid_thread_t* thread; }; static void fluid_client_run(fluid_client_t* client) { fluid_shell_t shell; fluid_shell_init(&shell, client->settings, client->handler, fluid_socket_get_istream(client->socket), fluid_socket_get_ostream(client->socket)); fluid_shell_run(&shell); fluid_server_remove_client(client->server, client); delete_fluid_client(client); } fluid_client_t* new_fluid_client(fluid_server_t* server, fluid_settings_t* settings, fluid_cmd_handler_t* handler, fluid_socket_t sock) { fluid_client_t* client; client = FLUID_NEW(fluid_client_t); if (client == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } client->server = server; client->socket = sock; client->settings = settings; client->handler = handler; client->thread = new_fluid_thread((fluid_thread_func_t) fluid_client_run, client, 0); if (client->thread == NULL) { fluid_socket_close(sock); FLUID_FREE(client); return NULL; } return client; } void fluid_client_quit(fluid_client_t* client) { if (client->socket != INVALID_SOCKET) { fluid_socket_close(client->socket); client->socket = INVALID_SOCKET; } FLUID_LOG(FLUID_DBG, "fluid_client_quit: joining"); fluid_thread_join(client->thread); FLUID_LOG(FLUID_DBG, "fluid_client_quit: done"); } void delete_fluid_client(fluid_client_t* client) { if (client->socket != INVALID_SOCKET) { fluid_socket_close(client->socket); client->socket = INVALID_SOCKET; } if (client->thread != NULL) { delete_fluid_thread(client->thread); client->thread = NULL; } FLUID_FREE(client); } #endif /* WITHOUT_SERVER */