Add shell commands to the MIDI File Player (#713)

This commit is contained in:
jjceresa 2021-01-15 19:04:02 +01:00 committed by GitHub
parent ca6bcda7d9
commit 4f2cb370a1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 351 additions and 55 deletions

View file

@ -63,7 +63,8 @@ FLUIDSYNTH_API
fluid_cmd_handler_t *new_fluid_cmd_handler(fluid_synth_t *synth, fluid_midi_router_t *router);
FLUIDSYNTH_API
fluid_cmd_handler_t *new_fluid_cmd_handler2(fluid_settings_t *settings, fluid_synth_t *synth, fluid_midi_router_t *router);
fluid_cmd_handler_t *new_fluid_cmd_handler2(fluid_settings_t *settings, fluid_synth_t *synth,
fluid_midi_router_t *router, fluid_player_t *player);
FLUIDSYNTH_API
void delete_fluid_cmd_handler(fluid_cmd_handler_t *handler);
@ -130,6 +131,11 @@ FLUIDSYNTH_API
fluid_server_t *new_fluid_server(fluid_settings_t *settings,
fluid_synth_t *synth, fluid_midi_router_t *router);
FLUIDSYNTH_API
fluid_server_t *new_fluid_server2(fluid_settings_t *settings,
fluid_synth_t *synth, fluid_midi_router_t *router,
fluid_player_t *player);
FLUIDSYNTH_API void delete_fluid_server(fluid_server_t *server);
FLUIDSYNTH_API int fluid_server_join(fluid_server_t *server);

View file

@ -40,6 +40,7 @@ struct _fluid_cmd_handler_t
fluid_settings_t *settings;
fluid_synth_t *synth;
fluid_midi_router_t *router;
fluid_player_t *player;
fluid_cmd_hash_t *commands;
fluid_midi_router_rule_t *cmd_rule; /* Rule currently being processed by shell command handler */
@ -193,7 +194,7 @@ static const fluid_cmd_t fluid_commands[] =
/* reverb commands */
{
"rev_preset", "reverb", fluid_handle_reverbpreset,
"rev_preset num Load preset num into all reverb unit"
"rev_preset num Load preset num into all reverb unit"
},
{
"rev_setroomsize", "reverb", fluid_handle_reverbsetroomsize,
@ -213,7 +214,7 @@ static const fluid_cmd_t fluid_commands[] =
},
{
"reverb", "reverb", fluid_handle_reverb,
"reverb [0|1|on|off] Turn all reverb groups on or off"
"reverb [0|1|on|off] Turn all reverb groups on or off"
},
/* chorus commands */
{
@ -234,7 +235,7 @@ static const fluid_cmd_t fluid_commands[] =
},
{
"chorus", "chorus", fluid_handle_chorus,
"chorus [0|1|on|off] Turn all chorus groups on or off"
"chorus [0|1|on|off] Turn all chorus groups on or off"
},
{
"gain", "general", fluid_handle_gain,
@ -364,6 +365,39 @@ static const fluid_cmd_t fluid_commands[] =
"router_end", "router", fluid_handle_router_end,
"router_end closes and commits the current routing rule"
},
/* Midi file player commands */
{
"player_start", "player", fluid_handle_player_start,
"player_start Start playing from the beginning of current song"
},
{
"player_stop", "player", fluid_handle_player_stop,
"player_stop Stop playing"
},
{
"player_cont", "player", fluid_handle_player_continue,
"player_cont Continue playing"
},
{
"player_step", "player", fluid_handle_player_step,
"player_step num Move forward/backward in current song to +/-num ticks"
},
{
"player_next", "player", fluid_handle_player_next_song,
"player_next Move to next song"
},
{
"player_loop", "player", fluid_handle_player_loop,
"player_loop num Set loop number to num (-1 = loop forever)"
},
{
"player_tempo_bpm", "player", fluid_handle_player_tempo_bpm,
"player_tempo_bpm num Set tempo to num beats per minute"
},
{
"player_tempo_int", "player", fluid_handle_player_tempo_int,
"player_tempo_int [mul] Set internal tempo multiplied by mul (default mul=1.0)"
},
#if WITH_PROFILING
/* Profiling commands */
{
@ -3457,6 +3491,213 @@ int fluid_handle_setbreathmode(void *data, int ac, char **av,
return 0;
}
/** commands for Midi file player ******************************************/
/* check player argument */
int player_check_arg(const char *name_cde, int ac, char **av, fluid_ostream_t out)
{
/* check if there is one argument that is a number */
if(ac != 1 || !fluid_is_number(av[0]))
{
fluid_ostream_printf(out, "%s: %s", name_cde, invalid_arg_msg);
return FLUID_FAILED;
}
return FLUID_OK;
}
/* print current position and total ticks */
void player_print_position(fluid_player_t *player, fluid_ostream_t out)
{
int current_tick = fluid_player_get_current_tick(player);
int total_ticks = fluid_player_get_total_ticks(player);
int tempo_bpm = fluid_player_get_bpm(player);
fluid_ostream_printf(out, "player current pos:%d, end:%d, bpm:%d\n\n",
current_tick, total_ticks, tempo_bpm);
}
/* player commands enum */
enum
{
PLAYER_LOOP_CDE, /* player_loop num,(Set loop number to num) */
PLAYER_STEP_CDE, /* player_step num (Move forward/backward to +/-num ticks) */
PLAYER_STOP_CDE, /* player_stop (Stop playing) */
PLAYER_CONT_CDE, /* player_cont (Continue playing) */
PLAYER_NEXT_CDE, /* player_next (Move to next song) */
PLAYER_START_CDE /* player_start (Move to start of song) */
};
/* Command handler for player commands: player_step, player_loop, player_tempo_bpm */
int fluid_handle_player_cde(void *data, int ac, char **av, fluid_ostream_t out, int cmd)
{
FLUID_ENTRY_COMMAND(data);
int arg;
/* commands name table */
static const char *name_cde[] =
{"player_loop", "player_step"};
/* get argument for PLAYER_LOOP_CDE, PLAYER_STEP_CDE */
if(cmd <= PLAYER_STEP_CDE)
{
/* check argument */
if(player_check_arg(name_cde[cmd], ac, av, out) == FLUID_FAILED)
{
return FLUID_FAILED;
}
arg = atoi(av[0]);
}
if(cmd == PLAYER_LOOP_CDE) /* player_loop */
{
fluid_player_set_loop(handler->player, arg);
return FLUID_OK;
}
if(cmd == PLAYER_CONT_CDE) /* player_cont */
{
fluid_player_play(handler->player);
return FLUID_OK;
}
fluid_player_stop(handler->player); /* player_stop */
if(cmd != PLAYER_STOP_CDE)
{
/* seek for player_next, player_step, player_start */
/* set seek to maximum position */
int seek = fluid_player_get_total_ticks(handler->player);
if(cmd == PLAYER_STEP_CDE)
{
/* Move position forward/backward +/- num ticks*/
arg += fluid_player_get_current_tick(handler->player);
/* keep seek between minimum and maximum in current song */
if(arg < 0)
{
seek = 0; /* minimum position */
}
else if(arg < seek)
{
seek = arg; /* seek < maximum position */
}
}
if(cmd == PLAYER_START_CDE) /* player_start */
{
seek = 0; /* beginning of the current song */
}
fluid_player_seek(handler->player, seek);
fluid_player_play(handler->player);
}
/* display position */
player_print_position(handler->player, out);
return FLUID_OK;
}
/* Command handler for "player_start" command */
int fluid_handle_player_start(void *data, int ac, char **av, fluid_ostream_t out)
{
return fluid_handle_player_cde(data, ac, av, out, PLAYER_START_CDE);
}
/* Command handler for "player_stop" command */
int fluid_handle_player_stop(void *data, int ac, char **av, fluid_ostream_t out)
{
return fluid_handle_player_cde(data, ac, av, out, PLAYER_STOP_CDE);
}
/* Command handler for "player_continue" command */
int fluid_handle_player_continue(void *data, int ac, char **av, fluid_ostream_t out)
{
return fluid_handle_player_cde(data, ac, av, out, PLAYER_CONT_CDE);
}
/* Command handler for "player_step" command */
int fluid_handle_player_step(void *data, int ac, char **av, fluid_ostream_t out)
{
return fluid_handle_player_cde(data, ac, av, out, PLAYER_STEP_CDE);
}
/* Command handler for "player_next" command */
int fluid_handle_player_next_song(void *data, int ac, char **av, fluid_ostream_t out)
{
return fluid_handle_player_cde(data, ac, av, out, PLAYER_NEXT_CDE);
}
/* Command handler for "player_loop" command */
int fluid_handle_player_loop(void *data, int ac, char **av, fluid_ostream_t out)
{
return fluid_handle_player_cde(data, ac, av, out, PLAYER_LOOP_CDE);
}
/* Command handler for player tempo commands:
player_tempo_int [mul], set the player to internal tempo multiplied by mul
player_tempo_bpm bpm, set the player to external tempo in beat per minute.
examples:
player_tempo_int set the player to internal tempo with a default
multiplier set to 1.0.
player_tempo_int 0.5 set the player to internal tempo divided by 2.
player_tempo_bpm 75, set the player to external tempo of 75 beats per minute.
*/
int fluid_handle_player_tempo_cde(void *data, int ac, char **av, fluid_ostream_t out, int cmd)
{
FLUID_ENTRY_COMMAND(data);
/* default multiplier for player_tempo_int command without argument*/
double arg = 1.0F;
/* commands name table */
static const char *name_cde[] =
{"player_tempo_int", "player_tempo_bpm"};
static const struct /* argument infos */
{
double min;
double max;
char *name;
}argument[2] = {{0.1F, 10.F, "multiplier"}, {1.0F, 600.0F, "bpm"}};
/* get argument for: player_tempo_int [mul], player_tempo_bpm bpm */
if((cmd == FLUID_PLAYER_TEMPO_EXTERNAL_BPM) || ac)
{
/* check argument presence */
if(player_check_arg(name_cde[cmd], ac, av, out) == FLUID_FAILED)
{
return FLUID_FAILED;
}
arg = atof(av[0]);
/* check if argument is in valid range */
if(arg < argument[cmd].min || arg > argument[cmd].max)
{
fluid_ostream_printf(out, "%s: %s %f must be in range [%f..%f]\n",
name_cde[cmd], argument[cmd].name, arg,
argument[cmd].min, argument[cmd].max);
return FLUID_FAILED;
}
}
fluid_player_set_tempo(handler->player, cmd, arg);
return FLUID_OK;
}
/* Command handler for "player_tempo_int [mul]" command */
int fluid_handle_player_tempo_int(void *data, int ac, char **av, fluid_ostream_t out)
{
return fluid_handle_player_tempo_cde(data, ac, av, out, FLUID_PLAYER_TEMPO_INTERNAL);
}
/* Command handler for "player_tempo_bpm bmp" command */
int fluid_handle_player_tempo_bpm(void *data, int ac, char **av, fluid_ostream_t out)
{
return fluid_handle_player_tempo_cde(data, ac, av, out, FLUID_PLAYER_TEMPO_EXTERNAL_BPM);
}
#ifdef LADSPA
@ -4288,7 +4529,7 @@ fluid_cmd_handler_destroy_hash_value(void *value)
*/
fluid_cmd_handler_t *new_fluid_cmd_handler(fluid_synth_t *synth, fluid_midi_router_t *router)
{
return new_fluid_cmd_handler2(fluid_synth_get_settings(synth), synth, router);
return new_fluid_cmd_handler2(fluid_synth_get_settings(synth), synth, router, NULL);
}
/**
@ -4299,9 +4540,13 @@ fluid_cmd_handler_t *new_fluid_cmd_handler(fluid_synth_t *synth, fluid_midi_rout
* behaviour is undefined.
* @param synth If not NULL, all the default synthesizer commands will be added to the new handler.
* @param router If not NULL, all the default midi_router commands will be added to the new handler.
* @param player If not NULL, all the default midi file player commands will be added to the new handler.
* @return New command handler, or NULL if alloc failed
*/
fluid_cmd_handler_t *new_fluid_cmd_handler2(fluid_settings_t* settings, fluid_synth_t *synth, fluid_midi_router_t *router)
fluid_cmd_handler_t *new_fluid_cmd_handler2(fluid_settings_t *settings,
fluid_synth_t *synth,
fluid_midi_router_t *router,
fluid_player_t *player)
{
unsigned int i;
fluid_cmd_handler_t *handler;
@ -4327,16 +4572,22 @@ fluid_cmd_handler_t *new_fluid_cmd_handler2(fluid_settings_t* settings, fluid_sy
handler->settings = settings;
handler->synth = synth;
handler->router = router;
handler->player = player;
for(i = 0; i < FLUID_N_ELEMENTS(fluid_commands); i++)
{
const fluid_cmd_t *cmd = &fluid_commands[i];
int is_router_cmd = FLUID_STRCMP(cmd->topic, "router") == 0;
int is_settings_cmd = FLUID_STRCMP(cmd->topic, "settings") == 0;
int is_router_cmd = FLUID_STRCMP(cmd->topic, "router") == 0;
int is_player_cmd = FLUID_STRCMP(cmd->topic, "player") == 0;
int is_synth_cmd = !(is_settings_cmd || is_router_cmd || is_player_cmd);
if((is_router_cmd && router == NULL) ||
(is_settings_cmd && settings == NULL) ||
(!is_router_cmd && !is_settings_cmd && synth == NULL))
int no_cmd = is_settings_cmd && settings == NULL; /* no settings command */
no_cmd = no_cmd || (is_router_cmd && router == NULL); /* no router command */
no_cmd = no_cmd || (is_player_cmd && player == NULL); /* no player command */
no_cmd = no_cmd || (is_synth_cmd && synth == NULL); /* no synth command */
if(no_cmd)
{
/* register a no-op command, this avoids an unknown command error later on */
fluid_cmd_t noop = *cmd;
@ -4430,6 +4681,7 @@ struct _fluid_server_t
fluid_settings_t *settings;
fluid_synth_t *synth;
fluid_midi_router_t *router;
fluid_player_t *player;
fluid_list_t *clients;
fluid_mutex_t mutex;
};
@ -4540,7 +4792,9 @@ new_fluid_client(fluid_server_t *server, fluid_settings_t *settings, fluid_socke
client->server = server;
client->socket = sock;
client->settings = settings;
client->handler = new_fluid_cmd_handler(server->synth, server->router);
client->handler = new_fluid_cmd_handler2(fluid_synth_get_settings(server->synth),
server->synth, server->router,
server->player);
client->thread = new_fluid_thread("client", fluid_client_run, client,
0, FALSE);
@ -4583,14 +4837,28 @@ void delete_fluid_client(fluid_client_t *client)
/**
* Create a new TCP/IP command shell server.
*
* @param settings Settings instance to use for the shell
* @param synth If not NULL, the synth instance for the command handler to be used by the client
* @param router If not NULL, the midi_router instance for the command handler to be used by the client
* @return New shell server instance or NULL on error
* See new_fluid_server2() for more information.
*/
fluid_server_t *
new_fluid_server(fluid_settings_t *settings,
fluid_synth_t *synth, fluid_midi_router_t *router)
{
return new_fluid_server2(settings, synth, router, NULL);
}
/**
* Create a new TCP/IP command shell server.
*
* @param settings Settings instance to use for the shell
* @param synth If not NULL, the synth instance for the command handler to be used by the client
* @param router If not NULL, the midi_router instance for the command handler to be used by the client
* @param player If not NULL, the player instance for the command handler to be used by the client
* @return New shell server instance or NULL on error
*/
fluid_server_t *
new_fluid_server2(fluid_settings_t *settings,
fluid_synth_t *synth, fluid_midi_router_t *router,
fluid_player_t *player)
{
#ifdef NETWORK_SUPPORT
fluid_server_t *server;
@ -4608,6 +4876,7 @@ new_fluid_server(fluid_settings_t *settings,
server->clients = NULL;
server->synth = synth;
server->router = router;
server->player = player;
fluid_mutex_init(server->mutex);

View file

@ -84,6 +84,15 @@ int fluid_handle_router_chan(void *data, int ac, char **av, fluid_ostream_t out)
int fluid_handle_router_par1(void *data, int ac, char **av, fluid_ostream_t out);
int fluid_handle_router_par2(void *data, int ac, char **av, fluid_ostream_t out);
int fluid_handle_player_start(void *data, int ac, char **av, fluid_ostream_t out);
int fluid_handle_player_stop(void *data, int ac, char **av, fluid_ostream_t out);
int fluid_handle_player_continue(void *data, int ac, char **av, fluid_ostream_t out);
int fluid_handle_player_next_song(void *data, int ac, char **av, fluid_ostream_t out);
int fluid_handle_player_step(void *data, int ac, char **av, fluid_ostream_t out);
int fluid_handle_player_loop(void *data, int ac, char **av, fluid_ostream_t out);
int fluid_handle_player_tempo_bpm(void *data, int ac, char **av, fluid_ostream_t out);
int fluid_handle_player_tempo_int(void *data, int ac, char **av, fluid_ostream_t out);
#if WITH_PROFILING
int fluid_handle_profile(void *data, int ac, char **av, fluid_ostream_t out);
int fluid_handle_prof_set_notes(void *data, int ac, char **av, fluid_ostream_t out);

View file

@ -307,21 +307,23 @@ fast_render_loop(fluid_settings_t *settings, fluid_synth_t *synth, fluid_player_
1)creating the settings.
2)reading/setting all options in command line.
2.5)read configuration file the first time and execute all "set" commands
3)creating the synth.
4)loading the soundfonts specified in command line
3)read configuration file the first time and execute all "set" commands
4)creating the synth.
5)loading the soundfonts specified in command line
(multiple soundfonts loading is possible).
5)create the audio driver (if not fast rendering).
6)create the router.
7)create the midi driver connected to the router.
8)create a player and add it any midifile specified in command line.
6)loading a default soundfont if no soundfont are supplied.
7)create the router.
8)create the midi driver connected to the router.
9)create a player and add it any midifile specified in command line.
(multiple midifiles loading is possible).
9)loading a default soundfont if needed before starting the player.
10)create a command handler.
11)reading the entire configuration file for the second time and submit it to the command handler.
12)create a tcp shell if any requested.
13)create a synchronous user shell if interactive.
14)entering fast rendering loop if requested.
11)reading the entire configuration file for the second time and submit it
to the command handler before starting the player.
12)Start the player.
13)create a tcp shell if any requested.
14)entering fast rendering loop if requested, otherwise
15)create the audio driver (i.e synthesis thread) and a synchronous user
shell if interactive.
*/
int main(int argc, char **argv)
{
@ -843,7 +845,7 @@ int main(int argc, char **argv)
/* Handle set commands before creating the synth */
if(config_file != NULL)
{
cmd_handler = new_fluid_cmd_handler2(settings, NULL, NULL);
cmd_handler = new_fluid_cmd_handler2(settings, NULL, NULL, NULL);
if(cmd_handler == NULL)
{
fprintf(stderr, "Failed to create the early command handler\n");
@ -891,6 +893,24 @@ int main(int argc, char **argv)
}
}
/* Try to load the default soundfont, if no soundfont specified */
if(fluid_synth_get_sfont(synth, 0) == NULL)
{
char *s;
if(fluid_settings_dupstr(settings, "synth.default-soundfont", &s) != FLUID_OK)
{
s = NULL;
}
if((s != NULL) && (s[0] != '\0'))
{
fluid_synth_sfload(synth, s, 1);
}
FLUID_FREE(s);
}
router = new_fluid_midi_router(
settings,
dump ? fluid_midi_dump_postrouter : fluid_synth_handle_midi_event,
@ -922,7 +942,7 @@ int main(int argc, char **argv)
}
}
/* play the midi files, if any */
/* create the player and add any midi files, if requested */
for(i = arg1; i < argc; i++)
{
if((argv[i][0] != '-') && fluid_is_midifile(argv[i]))
@ -948,32 +968,8 @@ int main(int argc, char **argv)
}
}
/* start the player */
if(player != NULL)
{
/* Try to load the default soundfont, if no soundfont specified */
if(fluid_synth_get_sfont(synth, 0) == NULL)
{
char *s;
if(fluid_settings_dupstr(settings, "synth.default-soundfont", &s) != FLUID_OK)
{
s = NULL;
}
if((s != NULL) && (s[0] != '\0'))
{
fluid_synth_sfload(synth, s, 1);
}
FLUID_FREE(s);
}
fluid_player_play(player);
}
/* try to load and execute the user or system configuration file */
cmd_handler = new_fluid_cmd_handler(synth, router);
cmd_handler = new_fluid_cmd_handler2(settings, synth, router, player);
if(cmd_handler == NULL)
{
@ -986,12 +982,23 @@ int main(int argc, char **argv)
fprintf(stderr, "Failed to execute command configuration file '%s'\n", config_file);
}
/* start the player. Must be done after executing commands configuration file.
This allows any existing player commands to be run prior the player is started.
Example:
player_tempo_bpm 60 # set a low tempo
player_loop -1 # loop song forever
*/
if(player != NULL)
{
fluid_player_play(player);
}
/* run the server, if requested */
#ifdef NETWORK_SUPPORT
if(with_server)
{
server = new_fluid_server(settings, synth, router);
server = new_fluid_server2(settings, synth, router, player);
if(server == NULL)
{

View file

@ -2392,6 +2392,11 @@ int fluid_player_set_midi_tempo(fluid_player_t *player, int tempo)
*/
int fluid_player_set_bpm(fluid_player_t *player, int bpm)
{
if(bpm <= 0)
{
return FLUID_FAILED; /* to avoid a division by 0 */
}
return fluid_player_set_midi_tempo(player, 60000000L / bpm);
}