From e37c277b0a754a5a8f61ec70536008198e38c127 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Sun, 23 Apr 2017 12:37:11 +0100 Subject: [PATCH] Add a "run_this_when_volume_is_set" facility to call a program when the volume is set. The AirPlay volume is passed. --- common.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ common.h | 3 ++- player.c | 2 ++ shairport.c | 4 ++++ 4 files changed, 56 insertions(+), 1 deletion(-) diff --git a/common.c b/common.c index 5c10c918..c13f1857 100644 --- a/common.c +++ b/common.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -454,6 +455,53 @@ uint8_t *rsa_apply(uint8_t *input, int inlen, int *outlen, int mode) { } #endif +void command_set_volume(double volume) { + if (config.cmd_set_volume) { + /*Spawn a child to run the program.*/ + pid_t pid = fork(); + if (pid == 0) { /* child process */ + size_t command_buffer_size = strlen(config.cmd_set_volume)+32; + char* command_buffer = (char*)malloc(command_buffer_size); + if (command_buffer==NULL) { + inform("Couldn't allocate memory for set_volume argument string"); + } else { + memset(command_buffer,0,command_buffer_size); + sprintf(command_buffer, "%s%f", config.cmd_set_volume,volume); + // debug(1,"command_buffer is \"%s\".",command_buffer); + int argC; + char **argV; + // debug(1,"set_volume command found."); + if (poptParseArgvString(command_buffer, &argC, (const char ***)&argV) != + 0) { + // note that argV should be free()'d after use, but we expect this fork to exit + // eventually. + warn("Can't decipher on-set-volume command arguments \"%s\".",command_buffer); + free(argV); + free(command_buffer); + } else { + free(command_buffer); + // debug(1,"Executing on-set-volume command %s with %d arguments.",argV[0],argC); + execv(argV[0], argV); + warn("Execution of on-set-volume command \"%s\" failed to start", config.cmd_set_volume); + // debug(1, "Error executing on-set-volume command %s", config.cmd_set_volume); + exit(127); /* only if execv fails */ + } + } + + } else { + if (config.cmd_blocking) { /* pid!=0 means parent process and if blocking is true, wait for + process to finish */ + pid_t rc = waitpid(pid, 0, 0); /* wait for child to exit */ + if (rc != pid) { + warn("Execution of on-set-volume command returned an error."); + debug(1, "on-set-volume command %s finished with error %d", config.cmd_set_volume,errno); + } + } + // debug(1,"Continue after on-set-volume command"); + } + } +} + void command_start(void) { if (config.cmd_start) { /*Spawn a child to run the program.*/ diff --git a/common.h b/common.h index 89f2901a..cb4b4b02 100644 --- a/common.h +++ b/common.h @@ -113,7 +113,7 @@ typedef struct { int logOutputLevel; // log output level int statistics_requested, use_negotiated_latencies; enum playback_mode_type playback_mode; - char *cmd_start, *cmd_stop; + char *cmd_start, *cmd_stop, *cmd_set_volume; int cmd_blocking; double tolerance; // allow this much drift before attempting to correct it enum stuffing_type packet_stuffing; @@ -203,6 +203,7 @@ config_t config_file_stuff; void command_start(void); void command_stop(void); +void command_set_volume(double volume); void shairport_shutdown(); // void shairport_startup_complete(void); diff --git a/player.c b/player.c index 5dbce717..459b2cc8 100644 --- a/player.c +++ b/player.c @@ -2109,6 +2109,8 @@ void player_volume(double airplay_volume, rtsp_conn_info* conn) { // Thus, we ask our vol2attn function for an appropriate dB between -96.3 and 0 dB and translate // it back to a number. + command_set_volume(airplay_volume); + int32_t hw_min_db, hw_max_db, hw_range_db, range_to_use, min_db, max_db; // hw_range_db is a flag; if 0 means no mixer diff --git a/shairport.c b/shairport.c index 3dfdb7f1..356de378 100644 --- a/shairport.c +++ b/shairport.c @@ -499,6 +499,10 @@ int parse_options(int argc, char **argv) { config.volume_max_db_set = 1; } + if (config_lookup_string(config.cfg, "general.run_this_when_volume_is_set", &str)) { + config.cmd_set_volume = (char *)str; + } + /* Get the playback_mode setting */ if (config_lookup_string(config.cfg, "general.playback_mode", &str)) { if (strcasecmp(str, "stereo") == 0)