From: Andre Noll Date: Tue, 27 Dec 2016 21:19:03 +0000 (+0100) Subject: Convert para_fade to subcommands, rename it to para_mixer. X-Git-Tag: v0.6.0~2^2~5 X-Git-Url: http://git.tue.mpg.de/?a=commitdiff_plain;h=7031f4e380b429e9c0eea1b8e5484dabdbb12ae5;p=paraslash.git Convert para_fade to subcommands, rename it to para_mixer. para_fade operates in one of several modes, but the command line options are shared. So most of the options are ignored for any particular mode. With lopsub we can do better and define one subcommand per mode so that options which are only relevant to one subcommand can be made local to that subcommand. With subcommands the syntax of the command changes, which breaks existing scripts. So it seems to be prudent to rename the executable from para_fade to para_mixer. The old name was a bit misleading anyway since the command can do much more than just fade the volume. --- diff --git a/Makefile.in b/Makefile.in index 2fee61cb..e1f7634e 100644 --- a/Makefile.in +++ b/Makefile.in @@ -19,7 +19,7 @@ client_objs := @client_objs@ gui_objs := @gui_objs@ audiod_objs := @audiod_objs@ audioc_objs := @audioc_objs@ -fade_objs := @fade_objs@ +mixer_objs := @mixer_objs@ server_objs := @server_objs@ write_objs := @write_objs@ afh_objs := @afh_objs@ diff --git a/Makefile.real b/Makefile.real index 669a469f..1fe29d46 100644 --- a/Makefile.real +++ b/Makefile.real @@ -36,7 +36,7 @@ test_dir := t # sort removes duplicate words, which is all we need here all_objs := $(sort $(recv_objs) $(filter_objs) $(client_objs) $(gui_objs) \ - $(audiod_objs) $(audioc_objs) $(fade_objs) $(server_objs) \ + $(audiod_objs) $(audioc_objs) $(mixer_objs) $(server_objs) \ $(write_objs) $(afh_objs) $(play_objs)) deps := $(addprefix $(dep_dir)/, $(all_objs:.o=.d)) @@ -45,7 +45,7 @@ audioc_objs += audioc.lsg.o audiod_objs += $(addsuffix _cmd.lsg.o, recv filter audiod write) \ client.lsg.o audiod.lsg.o client_objs += client.lsg.o -fade_objs += fade.lsg.o +mixer_objs += mixer.lsg.o filter_objs += filter_cmd.lsg.o filter.lsg.o gui_objs += gui.lsg.o play_objs += $(addsuffix _cmd.lsg.o, recv filter play write) play.lsg.o @@ -65,7 +65,7 @@ client_objs := $(addprefix $(object_dir)/, $(client_objs)) gui_objs := $(addprefix $(object_dir)/, $(gui_objs)) audiod_objs := $(addprefix $(object_dir)/, $(audiod_objs)) audioc_objs := $(addprefix $(object_dir)/, $(audioc_objs)) -fade_objs := $(addprefix $(object_dir)/, $(fade_objs)) +mixer_objs := $(addprefix $(object_dir)/, $(mixer_objs)) server_objs := $(addprefix $(object_dir)/, $(server_objs)) write_objs := $(addprefix $(object_dir)/, $(write_objs)) afh_objs := $(addprefix $(object_dir)/, $(afh_objs)) @@ -267,7 +267,7 @@ para_play \ para_write \ para_play \ para_audiod \ -para_fade \ +para_mixer \ : LDFLAGS += \ $(oss_ldflags) \ $(alsa_ldflags) @@ -276,7 +276,7 @@ para_afh \ para_audioc \ para_audiod \ para_client \ -para_fade \ +para_mixer \ para_filter \ para_gui \ para_play \ diff --git a/configure.ac b/configure.ac index 54a313ad..4fe45795 100644 --- a/configure.ac +++ b/configure.ac @@ -580,23 +580,23 @@ if test -n "$CRYPTOLIB"; then else build_audiod="no" fi -########################################################################### fade +########################################################################### mixer if test $HAVE_OSS = yes -o $HAVE_ALSA = yes; then - build_fade="yes" - executables="$executables fade" - fade_errlist_objs="fade exec string fd version" + build_mixer="yes" + executables="$executables mixer" + mixer_errlist_objs="mixer exec string fd version" if test $HAVE_OSS = yes; then - fade_errlist_objs="$fade_errlist_objs oss_mix" + mixer_errlist_objs="$mixer_errlist_objs oss_mix" mixers="${mixers}oss " default_mixer="OSS_MIX" fi if test $HAVE_ALSA = yes; then - fade_errlist_objs="$fade_errlist_objs alsa_mix" + mixer_errlist_objs="$mixer_errlist_objs alsa_mix" mixers="${mixers}alsa " default_mixer="ALSA_MIX" fi - fade_objs="$fade_errlist_objs" - AC_SUBST(fade_objs, add_dot_o($fade_objs)) + mixer_objs="$mixer_errlist_objs" + AC_SUBST(mixer_objs, add_dot_o($mixer_objs)) enum="$( for i in $mixers; do printf "${i}_MIX, " | tr '[a-z]' '[A-Z]' @@ -618,7 +618,7 @@ if test $HAVE_OSS = yes -o $HAVE_ALSA = yes; then array="$(for i in $mixers; do printf '{.init = '$i'_mix_init},'; done)" AC_DEFINE_UNQUOTED(MIXER_ARRAY, $array, array of supported mixers) else - build_fade="no" + build_mixer="no" AC_MSG_WARN([no mixer support]) fi ########################################################################### gui @@ -927,7 +927,7 @@ audio format handlers: $audio_format_handlers para_server: $build_server para_gui: $build_gui -para_fade: $build_fade +para_mixer: $build_mixer para_client: $build_client para_audiod: $build_audiod ]) diff --git a/fade.c b/fade.c deleted file mode 100644 index 34237c6b..00000000 --- a/fade.c +++ /dev/null @@ -1,441 +0,0 @@ -/* - * Copyright (C) 1998 Andre Noll - * - * Licensed under the GPL v2. For licencing details see COPYING. - */ - -/** \file fade.c A volume fader and alarm clock for OSS. */ - -#include -#include - -#include "fade.lsg.h" -#include "para.h" -#include "fd.h" -#include "string.h" -#include "mix.h" -#include "error.h" -#include "version.h" - -/** Array of error strings. */ -DEFINE_PARA_ERRLIST; - -enum mixer_id {MIXER_ENUM}; -static char *mixer_name[] = {MIXER_NAMES}; -DECLARE_MIXER_INITS; -static struct mixer supported_mixer[] = {MIXER_ARRAY}; -#define FOR_EACH_MIXER(i) for ((i) = 0; (i) < NUM_SUPPORTED_MIXERS; (i)++) - -static struct lls_parse_result *lpr; - -#define CMD_PTR (lls_cmd(0, fade_suite)) -#define OPT_RESULT(_name) (lls_opt_result(LSG_FADE_PARA_FADE_OPT_ ## _name, lpr)) -#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name))) -#define OPT_STRING_VAL(_name) (lls_string_val(0, OPT_RESULT(_name))) -#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name))) - -static int loglevel; -static __printf_2_3 void date_log(int ll, const char *fmt, ...) -{ - va_list argp; - time_t t1; - struct tm *tm; - - if (ll < loglevel) - return; - time(&t1); - tm = localtime(&t1); - fprintf(stderr, "%d:%02d:%02d ", tm->tm_hour, tm->tm_min, tm->tm_sec); - va_start(argp, fmt); - vprintf(fmt, argp); - va_end(argp); -} -__printf_2_3 void (*para_log)(int, const char*, ...) = date_log; - -static int set_channel(struct mixer *m, struct mixer_handle *h, const char *channel) -{ - - PARA_NOTICE_LOG("using %s mixer channel\n", channel? - channel : "default"); - return m->set_channel(h, channel); -} - -/* Fade to new volume in fade_time seconds. */ -static int fade(struct mixer *m, struct mixer_handle *h, int new_vol, int fade_time) -{ - int vol, diff, incr, ret; - unsigned secs; - struct timespec ts; - unsigned long long tmp, tmp2; /* Careful with that axe, Eugene! */ - - if (fade_time <= 0) - return m->set(h, new_vol); - secs = fade_time; - ret = m->get(h); - if (ret < 0) - goto out; - vol = ret; - PARA_NOTICE_LOG("fading %s from %d to %d in %u seconds\n", - OPT_STRING_VAL(MIXER_CHANNEL), vol, new_vol, secs); - diff = new_vol - vol; - if (!diff) { - sleep(secs); - goto out; - } - incr = diff > 0? 1: -1; - diff = diff > 0? diff: -diff; - tmp = secs * 1000 / diff; - tmp2 = tmp % 1000; - while ((new_vol - vol) * incr > 0) { - ts.tv_nsec = tmp2 * 1000000; /* really nec ?*/ - ts.tv_sec = tmp / 1000; /* really nec ?*/ - //printf("ts.tv_sec: %i\n", ts.tv_nsec); - vol += incr; - ret = m->set(h, vol); - if (ret < 0) - goto out; - //printf("vol = %i\n", vol); - nanosleep(&ts, NULL); - } -out: - return ret; -} - -static void client_cmd(const char *cmd) -{ - int ret, status, fds[3] = {0, 0, 0}; - pid_t pid; - char *cmdline = make_message(BINDIR "/para_client %s", cmd); - - PARA_NOTICE_LOG("%s\n", cmdline); - ret = para_exec_cmdline_pid(&pid, cmdline, fds); - free(cmdline); - if (ret < 0) { - PARA_ERROR_LOG("%s\n", para_strerror(-ret)); - goto fail; - } - do - pid = waitpid(pid, &status, 0); - while (pid == -1 && errno == EINTR); - if (pid < 0) { - PARA_ERROR_LOG("%s\n", strerror(errno)); - goto fail; - } - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) - goto fail; - return; -fail: - PARA_EMERG_LOG("command \"%s\" failed\n", cmd); - exit(EXIT_FAILURE); -} - -static void change_afs_mode(const char *afs_mode) -{ - char *cmd; - - client_cmd("stop"); - if (!afs_mode) - return; - cmd = make_message("select %s", afs_mode); - client_cmd(cmd); - free(cmd); -} - -static int set_initial_volume(struct mixer *m, struct mixer_handle *h) -{ - int i, ret; - - for (i = 0; i < OPT_GIVEN(IVOL); i++) { - const char *val = lls_string_val(i, OPT_RESULT(IVOL)); - char *p, *ch, *arg = para_strdup(val); - int32_t iv; - p = strchr(arg, ':'); - if (p) { - *p = '\0'; - p++; - ch = arg; - } else { - p = arg; - ch = NULL; - } - ret = para_atoi32(p, &iv); - if (ret < 0) { - free(arg); - return ret; - } - ret = set_channel(m, h, ch); - if (!ch) - ch = "default"; - if (ret < 0) { - PARA_WARNING_LOG("ignoring channel %s\n", ch); - ret = 0; - } else { - PARA_INFO_LOG("initial volume %s: %d\n", ch, iv); - ret = m->set(h, iv); - } - free(arg); - if (ret < 0) - return ret; - } - return 1; -} - -static int sweet_dreams(struct mixer *m, struct mixer_handle *h) -{ - time_t t1, wake_time_epoch; - unsigned int delay; - struct tm *tm; - int ret, min = OPT_UINT32_VAL(WAKE_MIN); - const char *fo_mood = OPT_STRING_VAL(FO_MOOD); - const char *fi_mood = OPT_STRING_VAL(FI_MOOD); - const char *sleep_mood = OPT_STRING_VAL(SLEEP_MOOD); - int fit = OPT_UINT32_VAL(FI_TIME); - int fot = OPT_UINT32_VAL(FO_TIME); - int fiv = OPT_UINT32_VAL(FI_VOL); - int fov = OPT_UINT32_VAL(FO_VOL); - - /* calculate wake time */ - time(&t1); - if (OPT_GIVEN(WAKE_HOUR)) { - int hour = OPT_UINT32_VAL(WAKE_HOUR); - tm = localtime(&t1); - if (tm->tm_hour > hour || (tm->tm_hour == hour && tm->tm_min> min)) { - t1 += 86400; /* wake time is tomorrow */ - tm = localtime(&t1); - } - tm->tm_hour = hour; - tm->tm_min = min; - tm->tm_sec = 0; - } else { - t1 += 9 * 60 * 60; /* nine hours from now */ - PARA_INFO_LOG("default wake time: %lu\n", (long unsigned)t1); - tm = localtime(&t1); - } - wake_time_epoch = mktime(tm); - PARA_INFO_LOG("waketime: %d:%02d\n", tm->tm_hour, tm->tm_min); - client_cmd("stop"); - sleep(1); - if (fot) { - ret = set_initial_volume(m, h); - if (ret < 0) - return ret; - change_afs_mode(fo_mood); - client_cmd("play"); - ret = set_channel(m, h, OPT_STRING_VAL(MIXER_CHANNEL)); - if (ret < 0) - return ret; - ret = fade(m, h, fov, fot); - if (ret < 0) - return ret; - } else { - ret = m->set(h, fov); - if (ret < 0) - return ret; - } - if (OPT_GIVEN(SLEEP_MOOD)) { - change_afs_mode(sleep_mood); - client_cmd("play"); - } else - client_cmd("stop"); - if (!fit) - return 1; - change_afs_mode(fi_mood); - for (;;) { - time(&t1); - if (wake_time_epoch <= t1 + fit) - break; - delay = wake_time_epoch - t1 - fit; - PARA_INFO_LOG("sleeping %u seconds (%u:%02u)\n", - delay, delay / 3600, - (delay % 3600) / 60); - sleep(delay); - } - client_cmd("play"); - ret = fade(m, h, fiv, fit); - PARA_INFO_LOG("fade complete, returning\n"); - return ret; -} - -static int snooze(struct mixer *m, struct mixer_handle *h) -{ - int ret, val; - - if (OPT_UINT32_VAL(SO_TIME) == 0) - return 1; - ret = m->get(h); - if (ret < 0) - return ret; - val = ret; - if (val < OPT_UINT32_VAL(SO_VOL)) - ret = m->set(h, OPT_UINT32_VAL(SO_VOL)); - else - ret = fade(m, h, OPT_UINT32_VAL(SO_VOL), - OPT_UINT32_VAL(SO_TIME)); - if (ret < 0) - return ret; - client_cmd("pause"); - PARA_NOTICE_LOG("%" PRIu32 " seconds snooze time...\n", - OPT_UINT32_VAL(SNOOZE_TIME)); - sleep(OPT_UINT32_VAL(SNOOZE_TIME)); - client_cmd("play"); - return fade(m, h, OPT_UINT32_VAL(SI_VOL), OPT_UINT32_VAL(SI_TIME)); -} - -static void init_mixers(void) -{ - int i; - - FOR_EACH_MIXER(i) { - struct mixer *m = &supported_mixer[i]; - PARA_DEBUG_LOG("initializing mixer API #%d (%s)\n", - i, mixer_name[i]); - m->init(m); - } -} - -static int set_val(struct mixer *m, struct mixer_handle *h) -{ - return m->set(h, OPT_UINT32_VAL(VAL)); -} - -static struct mixer *get_mixer_or_die(void) -{ - int i; - - if (!OPT_GIVEN(MIXER_API)) - i = DEFAULT_MIXER; - else - FOR_EACH_MIXER(i) - if (!strcmp(mixer_name[i], OPT_STRING_VAL(MIXER_API))) - break; - if (i < NUM_SUPPORTED_MIXERS) { - PARA_NOTICE_LOG("using %s mixer API\n", mixer_name[i]); - return supported_mixer + i; - } - printf("available mixer APIs: "); - FOR_EACH_MIXER(i) { - int d = (i == DEFAULT_MIXER); - printf("%s%s%s ", d? "[" : "", mixer_name[i], d? "]" : ""); - } - printf("\n"); - exit(EXIT_FAILURE); -} - -static void handle_help_flags(void) -{ - char *help; - - if (OPT_GIVEN(DETAILED_HELP)) - help = lls_long_help(CMD_PTR); - else if (OPT_GIVEN(HELP)) - help = lls_short_help(CMD_PTR); - else - return; - printf("%s\n", help); - free(help); - exit(EXIT_SUCCESS); -} - -/** - * The main function of para_fade. - * - * The executable is linked with the alsa or the oss mixer API, or both. It has - * a custom log function which prefixes log messages with the current date. - * - * \param argc Argument counter. - * \param argv Argument vector. - * - * \return EXIT_SUCCESS or EXIT_FAILURE. - */ -int main(int argc, char *argv[]) -{ - const struct lls_command *cmd = CMD_PTR; - int ret; - char *cf, *errctx; - struct mixer *m; - struct mixer_handle *h; - void *map; - size_t sz; - - ret = lls(lls_parse(argc, argv, cmd, &lpr, &errctx)); - if (ret < 0) - goto fail; - loglevel = OPT_UINT32_VAL(LOGLEVEL); - version_handle_flag("fade", OPT_GIVEN(VERSION)); - handle_help_flags(); - - if (OPT_GIVEN(CONFIG_FILE)) - cf = para_strdup(OPT_STRING_VAL(CONFIG_FILE)); - else { - char *home = para_homedir(); - cf = make_message("%s/.paraslash/fade.conf", home); - free(home); - } - ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL); - if (ret < 0) { - if (ret != -E_EMPTY && ret != -ERRNO_TO_PARA_ERROR(ENOENT)) - goto free_cf; - if (ret == -ERRNO_TO_PARA_ERROR(ENOENT) && OPT_GIVEN(CONFIG_FILE)) - goto free_cf; - } else { - int cf_argc; - char **cf_argv; - struct lls_parse_result *cf_lpr, *merged_lpr; - ret = lls(lls_convert_config(map, sz, NULL, &cf_argv, &errctx)); - para_munmap(map, sz); - if (ret < 0) - goto free_cf; - cf_argc = ret; - ret = lls(lls_parse(cf_argc, cf_argv, CMD_PTR, &cf_lpr, &errctx)); - lls_free_argv(cf_argv); - if (ret < 0) - goto free_cf; - ret = lls(lls_merge(lpr, cf_lpr, CMD_PTR, &merged_lpr, - &errctx)); - lls_free_parse_result(cf_lpr, CMD_PTR); - if (ret < 0) - goto free_cf; - lls_free_parse_result(lpr, CMD_PTR); - lpr = merged_lpr; - loglevel = OPT_UINT32_VAL(LOGLEVEL); - } - init_mixers(); - m = get_mixer_or_die(); - ret = m->open(OPT_STRING_VAL(MIXER_DEVICE), &h); - if (ret < 0) - goto free_cf; - ret = set_channel(m, h, OPT_STRING_VAL(MIXER_CHANNEL)); - if (ret == -E_BAD_CHANNEL) { - char *channels = m->get_channels(h); - printf("Available channels: %s\n", channels); - free(channels); - } - if (ret < 0) - goto close_mixer; - if (strcmp(OPT_STRING_VAL(MODE), "fade") == 0) - ret = fade(m, h, OPT_UINT32_VAL(FADE_VOL), - OPT_UINT32_VAL(FADE_TIME)); - else if (strcmp(OPT_STRING_VAL(MODE), "snooze") == 0) - ret = snooze(m, h); - else if (strcmp(OPT_STRING_VAL(MODE), "set") == 0) - ret = set_val(m, h); - else if (strcmp(OPT_STRING_VAL(MODE), "sleep") == 0) - ret = sweet_dreams(m, h); - else { - PARA_ERROR_LOG("invalid mode: %s\n", OPT_STRING_VAL(MODE)); - ret = -ERRNO_TO_PARA_ERROR(EINVAL); - } -close_mixer: - m->close(&h); -free_cf: - free(cf); - lls_free_parse_result(lpr, CMD_PTR); - if (ret >= 0) - return EXIT_SUCCESS; -fail: - if (errctx) - PARA_ERROR_LOG("%s\n", errctx); - free(errctx); - PARA_EMERG_LOG("%s\n", para_strerror(-ret)); - return EXIT_FAILURE; -} diff --git a/m4/lls/fade.suite.m4 b/m4/lls/fade.suite.m4 deleted file mode 100644 index 5e0eee2a..00000000 --- a/m4/lls/fade.suite.m4 +++ /dev/null @@ -1,227 +0,0 @@ -m4_define(PROGRAM, para_fade) -m4_define(DEFAULT_CONFIG_FILE, ~/.paraslash/fade.conf) -[suite fade] -version-string = GIT_VERSION() -[supercommand para_fade] - purpose = alarm clock and volume-fader for OSS and ALSA - [description] - para_fade is a simple program which adjusts the settings of an audio - mixing device. It can set the level of a mixer channel, or fade the - level from one value to another in a given time period. In sleep and - snooze mode, it contacts para_server to start or stop streaming. - [/description] - m4_include(common-option-section.m4) - m4_include(help.m4) - m4_include(detailed-help.m4) - m4_include(version.m4) - m4_include(config-file.m4) - m4_include(loglevel.m4) - m4_include(per-command-options-section.m4) - [option mode] - short_opt = o - summary = select mode of operation (sleep, fade, set or snooze) - arg_info = required_arg - arg_type = string - default_val = sleep - typestr = mode - [help] - sleep: Change to the initial volume and select the initial - afs mood/playlist. Then fade out until the fade-out volume is - reached. Switch to the sleep mood/playlist until wake time minus - fade-in time. Finally switch to the wake mood/playlist and fade to - the fade-in volume. - - fade: Fade the volume to the given value in the given time. - - set: Just set the value and exit. - - snooze: Fade out, sleep a bit, and fade in. - [/help] - [option mixer-api] - short_opt = a - summary = select alternative mixer API - arg_info = required_arg - arg_type = string - typestr = api - [help] - ALSA is preferred over OSS if both APIs are supported and this option - is not given. To see the supported mixer APIs, use this option with - an invalid string as the mixer API, e.g. --mixer-api help. - [/help] - [option mixer-device] - short_opt = m - summary = set mixer device - arg_info = required_arg - arg_type = string - typestr = device - [help] - The default device (used if this option is not given) depends - on the selected mixer API. For ALSA, the default is 'hw:0' which - corresponds to the first sound device. For OSS, '/dev/mixer' is used - as the default. - [/help] - [option mixer-channel] - short_opt = C - summary = select the analog mixer channel - arg_info = required_arg - arg_type = string - typestr = channel - [help] - For the ALSA mixer API, the possible values are determined at runtime - depending on the hardware and can be printed by specifying an invalid - mixer channel, for example --mixer-channel help. The default channel is - 'Master'. - - For OSS the possible values are invariably 'volume', 'bass', 'treble', - 'synth', 'pcm', 'speaker', 'line', 'mic', 'cd', 'imix', 'altpcm', - 'reclev', 'igain', 'ogain'. However, not all listed channels might be - supported on any particular hardware. The default channel is 'volume'. - [/help] - [option ignored] - summary = Options for sleep mode - flag ignored - [option ivol] - summary = set initial volume - arg_info = required_arg - arg_type = string - default_val = 60 - flag multiple - typestr = [channel:]volume - [help] - Used as the start volume, before fading out to the fade-out volume. The - channel part may be omitted, in which case the default channel is - used. This option may be given multiple times. - [/help] - [option fo-mood] - summary = mood or playlist for fade-out - arg_info = required_arg - arg_type = string - typestr = mood_spec - default_val = m/fade - [help] - This mood (or playlist) is selected right after setting the initial - volume. - [/help] - [option fo-time] - summary = duration of fade-out period - arg_info = required_arg - arg_type = uint32 - typestr = seconds - default_val = 1800 - [help] - No fading if this is set to 0. - [/help] - [option fo-vol] - summary = destination volume for fade-out - arg_info = required_arg - arg_type = uint32 - typestr = volume - default_val = 20 - [option sleep-mood] - summary = mood/playlist between fade-out and fade-in - arg_info = required_arg - arg_type = string - typestr = mood_spec - default_val = m/sleep - [help] - Select the given mood or playlist after the fade-out. If unset, - playback is stopped until fade-in starts. - [/help] - [option wake-hour] - short_opt = H - summary = A number between 0 and 23 - arg_info = required_arg - arg_type = uint32 - typestr = hour - [help] - If this is not given, the default is computed as now + 9 hours. - [/help] - [option wake-min] - short_opt = M - summary = A number between 0 and 59 - arg_info = required_arg - arg_type = uint32 - typestr = minutes - [option fi-mood] - summary = mood or playlist for fade-in - arg_info = required_arg - arg_type = string - typestr = mood_spec - default_val = m/wake - [help] - This mood or playlist is selected right before fade-in begins. - [/help] - [option fi-time] - summary = duration of fade-in period - arg_info = required_arg - arg_type = uint32 - typestr = seconds - default_val = 1200 - [help] - No fading if this is set to 0. - [/help] - [option fi-vol] - summary = destination volume for fade-in - arg_info = required_arg - arg_type = uint32 - typestr = volume - default_val = 80 - [option ignored] - summary = Options for snooze mode - flag ignored - [option so-time] - summary = duration of fade-out period - arg_info = required_arg - arg_type = uint32 - typestr = seconds - default_val = 30 - [option so-vol] - summary = destination volume for fade-out - arg_info = required_arg - arg_type = uint32 - typestr = volume - default_val = 20 - [option snooze-time] - summary = delay between end of fade-out and begin of fade-in - arg_info = required_arg - arg_type = uint32 - typestr = seconds - default_val = 600 - [option si-time] - summary = duration of fade-in period - arg_info = required_arg - arg_type = uint32 - typestr = seconds - default_val = 180 - [option si-vol] - summary = destination volume for fade-in - arg_info = required_arg - arg_type = uint32 - typestr = volume - default_val = 80 - [option ignored] - summary = Options for fade mode - flag ignored - [option fade-vol] - short_opt = f - summary = destination volume for fading - arg_info = required_arg - arg_type = uint32 - typestr = volume - default_val = 50 - [option fade-time] - short_opt = t - summary = duration of fade period - arg_info = required_arg - arg_type = uint32 - typestr = seconds - default_val = 5 - [option ignored] - summary = Options for set mode - flag ignored - [option val] - summary = mixer channel value to set - arg_info = required_arg - arg_type = uint32 - typestr = value - default_val = 10 diff --git a/m4/lls/mixer.suite.m4 b/m4/lls/mixer.suite.m4 new file mode 100644 index 00000000..83d6ad95 --- /dev/null +++ b/m4/lls/mixer.suite.m4 @@ -0,0 +1,222 @@ +m4_define(PROGRAM, para_mixer) +m4_define(DEFAULT_CONFIG_FILE, ~/.paraslash/mixer.conf) +[suite mixer] +version-string = GIT_VERSION() +caption = List of subcommands +[supercommand para_mixer] + purpose = alarm clock and volume-fader for OSS and ALSA + synopsis = [] -- [] [] + [description] + para_mixer adjusts the settings of an audio mixing device. It can + set the level of a mixer channel, or fade the level from one value + to another in a given time period. The sleep and snooze subcommands + contact para_server to start or stop streaming. + [/description] + m4_include(common-option-section.m4) + m4_include(help.m4) + m4_include(detailed-help.m4) + m4_include(version.m4) + m4_include(config-file.m4) + m4_include(loglevel.m4) + m4_include(per-command-options-section.m4) + [help] + These options apply to several subcommands. + [/help] + [option mixer-api] + short_opt = a + summary = select alternative mixer API + arg_info = required_arg + arg_type = string + typestr = api + [help] + ALSA is preferred over OSS if both APIs are supported and this option + is not given. To see the supported mixer APIs, use this option with + an invalid string as the mixer API, e.g. --mixer-api help. + [/help] + [option mixer-device] + short_opt = m + summary = set mixer device + arg_info = required_arg + arg_type = string + typestr = device + [help] + The default device (used if this option is not given) depends + on the selected mixer API. For ALSA, the default is 'hw:0' which + corresponds to the first sound device. For OSS, '/dev/mixer' is used + as the default. + [/help] + [option mixer-channel] + short_opt = C + summary = select a mixer channel + arg_info = required_arg + arg_type = string + typestr = channel + [help] + For the ALSA mixer API, the possible values are determined at runtime + depending on the hardware and can be printed by specifying an invalid + mixer channel, for example --mixer-channel help. The default channel is + 'Master'. + + For OSS the possible values are invariably 'volume', 'bass', 'treble', + 'synth', 'pcm', 'speaker', 'line', 'mic', 'cd', 'imix', 'altpcm', + 'reclev', 'igain', 'ogain'. However, not all listed channels might be + supported on any particular hardware. The default channel is 'volume'. + [/help] +[subcommand help] + purpose = print subcommand help + non-opts-name = [] + [option long] + summary = print the long help text + short_opt = l + +[subcommand set] + purpose = set a channel to the given value and exit + [option val] + summary = mixer channel value to set + arg_info = required_arg + arg_type = uint32 + typestr = value + default_val = 10 +[subcommand fade] + purpose = fade a channel to the given value in the given time + [option fade-vol] + short_opt = f + summary = destination volume for fading + arg_info = required_arg + arg_type = uint32 + typestr = volume + default_val = 50 + [option fade-time] + short_opt = t + summary = duration of fade period + arg_info = required_arg + arg_type = uint32 + typestr = seconds + default_val = 5 +[subcommand snooze] + purpose = fade out, pause, sleep, play, fade in + [option so-time] + summary = duration of fade-out period + arg_info = required_arg + arg_type = uint32 + typestr = seconds + default_val = 30 + [option so-vol] + summary = destination volume for fade-out + arg_info = required_arg + arg_type = uint32 + typestr = volume + default_val = 20 + [option snooze-time] + summary = delay between end of fade-out and begin of fade-in + arg_info = required_arg + arg_type = uint32 + typestr = seconds + default_val = 600 + [option si-time] + summary = duration of fade-in period + arg_info = required_arg + arg_type = uint32 + typestr = seconds + default_val = 180 + [option si-vol] + summary = destination volume for fade-in + arg_info = required_arg + arg_type = uint32 + typestr = volume + default_val = 80 +[subcommand sleep] + purpose = stream, fade out, sleep, fade in + [description] + Change to the initial volume and select the initial mood/playlist. + Fade out to the given fade-out volume in the specified time. Switch + to the sleep mood/playlist and wait until wake time minus fade-in + time. Finally, switch to the wake mood/playlist and fade in to the + fade-in volume. + [/description] + [option ivol] + summary = set initial volume + arg_info = required_arg + arg_type = string + default_val = 60 + flag multiple + typestr = [channel:]volume + [help] + Used as the start volume, before fading out to the fade-out volume. The + channel part may be omitted, in which case the default channel is + used. This option may be given multiple times. + [/help] + [option fo-mood] + summary = mood or playlist for fade-out + arg_info = required_arg + arg_type = string + typestr = mood_spec + default_val = m/fade + [help] + This mood (or playlist) is selected right after setting the initial + volume. + [/help] + [option fo-time] + summary = duration of fade-out period + arg_info = required_arg + arg_type = uint32 + typestr = seconds + default_val = 1800 + [help] + No fading if this is set to 0. + [/help] + [option fo-vol] + summary = destination volume for fade-out + arg_info = required_arg + arg_type = uint32 + typestr = volume + default_val = 20 + [option sleep-mood] + summary = mood/playlist between fade-out and fade-in + arg_info = required_arg + arg_type = string + typestr = mood_spec + default_val = m/sleep + [help] + Select the given mood or playlist after the fade-out. If unset, + playback is stopped until fade-in starts. + [/help] + [option wake-hour] + short_opt = H + summary = A number between 0 and 23 + arg_info = required_arg + arg_type = uint32 + typestr = hour + [help] + If this is not given, the default is computed as now + 9 hours. + [/help] + [option wake-min] + short_opt = M + summary = A number between 0 and 59 + arg_info = required_arg + arg_type = uint32 + typestr = minutes + [option fi-mood] + summary = mood or playlist for fade-in + arg_info = required_arg + arg_type = string + typestr = mood_spec + default_val = m/wake + [help] + This mood or playlist is selected right before fade-in begins. + [/help] + [option fi-time] + summary = duration of fade-in period + arg_info = required_arg + arg_type = uint32 + typestr = seconds + default_val = 1200 + [help] + No fading if this is set to 0. + [/help] + [option fi-vol] + summary = destination volume for fade-in + arg_info = required_arg + arg_type = uint32 + typestr = volume + default_val = 80 diff --git a/mix.h b/mix.h index 60c4392f..fe85d746 100644 --- a/mix.h +++ b/mix.h @@ -4,7 +4,7 @@ * Licensed under the GPL v2. For licencing details see COPYING. */ -/** \file mix.h Mixer API (used by para_fade). */ +/** \file mix.h Mixer API for para_mixer. */ /** * Opaque structure which corresponds to an instance of a mixer. diff --git a/mixer.c b/mixer.c new file mode 100644 index 00000000..2089c5b1 --- /dev/null +++ b/mixer.c @@ -0,0 +1,541 @@ +/* + * Copyright (C) 1998 Andre Noll + * + * Licensed under the GPL v2. For licencing details see COPYING. + */ + +/** \file mixer.c A volume fader and alarm clock for OSS. */ + +#include +#include + +#include "mixer.lsg.h" +#include "para.h" +#include "fd.h" +#include "string.h" +#include "mix.h" +#include "error.h" +#include "version.h" + +/** Array of error strings. */ +DEFINE_PARA_ERRLIST; + +enum mixer_id {MIXER_ENUM}; +static char *mixer_name[] = {MIXER_NAMES}; +DECLARE_MIXER_INITS; +static struct mixer supported_mixer[] = {MIXER_ARRAY}; +#define FOR_EACH_MIXER(i) for ((i) = 0; (i) < NUM_SUPPORTED_MIXERS; (i)++) + +static struct lls_parse_result *lpr, *sub_lpr; + +#define CMD_PTR(_cmd) (lls_cmd(LSG_MIXER_CMD_ ## _cmd, mixer_suite)) +#define OPT_RESULT(_cmd, _opt) (lls_opt_result( \ + LSG_MIXER_ ## _cmd ## _OPT_ ## _opt, (LSG_MIXER_CMD_ ## _cmd == 0)? lpr : sub_lpr)) +#define OPT_GIVEN(_cmd, _opt) (lls_opt_given(OPT_RESULT(_cmd, _opt))) +#define OPT_STRING_VAL(_cmd, _opt) (lls_string_val(0, OPT_RESULT(_cmd, _opt))) +#define OPT_UINT32_VAL(_cmd, _opt) (lls_uint32_val(0, OPT_RESULT(_cmd, _opt))) + +typedef int (*mixer_subcommand_handler_t)(struct mixer *, struct mixer_handle *); + +#define EXPORT_CMD(_cmd) const mixer_subcommand_handler_t \ + lsg_mixer_com_ ## _cmd ## _user_data = &com_ ## _cmd; + +static int loglevel; +static __printf_2_3 void date_log(int ll, const char *fmt, ...) +{ + va_list argp; + time_t t1; + struct tm *tm; + + if (ll < loglevel) + return; + time(&t1); + tm = localtime(&t1); + fprintf(stderr, "%d:%02d:%02d ", tm->tm_hour, tm->tm_min, tm->tm_sec); + va_start(argp, fmt); + vprintf(fmt, argp); + va_end(argp); +} +__printf_2_3 void (*para_log)(int, const char*, ...) = date_log; + +static int set_channel(struct mixer *m, struct mixer_handle *h, const char *channel) +{ + + PARA_NOTICE_LOG("using %s mixer channel\n", channel? + channel : "default"); + return m->set_channel(h, channel); +} + +/* Fade to new volume in fade_time seconds. */ +static int fade(struct mixer *m, struct mixer_handle *h, uint32_t new_vol, + uint32_t fade_time) +{ + int vol, diff, incr, ret; + unsigned secs; + struct timespec ts; + unsigned long long tmp, tmp2; /* Careful with that axe, Eugene! */ + + if (fade_time <= 0) + return m->set(h, new_vol); + secs = fade_time; + ret = m->get(h); + if (ret < 0) + goto out; + vol = ret; + PARA_NOTICE_LOG("fading %s from %d to %u in %u seconds\n", + OPT_STRING_VAL(PARA_MIXER, MIXER_CHANNEL), vol, new_vol, secs); + diff = new_vol - vol; + if (!diff) { + sleep(secs); + goto out; + } + incr = diff > 0? 1: -1; + diff = diff > 0? diff: -diff; + tmp = secs * 1000 / diff; + tmp2 = tmp % 1000; + while ((new_vol - vol) * incr > 0) { + ts.tv_nsec = tmp2 * 1000000; /* really nec ?*/ + ts.tv_sec = tmp / 1000; /* really nec ?*/ + //printf("ts.tv_sec: %i\n", ts.tv_nsec); + vol += incr; + ret = m->set(h, vol); + if (ret < 0) + goto out; + //printf("vol = %i\n", vol); + nanosleep(&ts, NULL); + } +out: + return ret; +} +static int com_fade(struct mixer *m, struct mixer_handle *h) +{ + uint32_t new_vol = OPT_UINT32_VAL(FADE, FADE_VOL); + uint32_t fade_time = OPT_UINT32_VAL(FADE, FADE_TIME); + return fade(m, h, new_vol, fade_time); +} +EXPORT_CMD(fade); + +static void client_cmd(const char *cmd) +{ + int ret, status, fds[3] = {0, 0, 0}; + pid_t pid; + char *cmdline = make_message(BINDIR "/para_client %s", cmd); + + PARA_NOTICE_LOG("%s\n", cmdline); + ret = para_exec_cmdline_pid(&pid, cmdline, fds); + free(cmdline); + if (ret < 0) { + PARA_ERROR_LOG("%s\n", para_strerror(-ret)); + goto fail; + } + do + pid = waitpid(pid, &status, 0); + while (pid == -1 && errno == EINTR); + if (pid < 0) { + PARA_ERROR_LOG("%s\n", strerror(errno)); + goto fail; + } + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + goto fail; + return; +fail: + PARA_EMERG_LOG("command \"%s\" failed\n", cmd); + exit(EXIT_FAILURE); +} + +static void change_afs_mode(const char *afs_mode) +{ + char *cmd; + + client_cmd("stop"); + if (!afs_mode) + return; + cmd = make_message("select %s", afs_mode); + client_cmd(cmd); + free(cmd); +} + +static int set_initial_volume(struct mixer *m, struct mixer_handle *h) +{ + int i, ret; + + for (i = 0; i < OPT_GIVEN(SLEEP, IVOL); i++) { + const char *val = lls_string_val(i, OPT_RESULT(SLEEP, IVOL)); + char *p, *ch, *arg = para_strdup(val); + int32_t iv; + p = strchr(arg, ':'); + if (p) { + *p = '\0'; + p++; + ch = arg; + } else { + p = arg; + ch = NULL; + } + ret = para_atoi32(p, &iv); + if (ret < 0) { + free(arg); + return ret; + } + ret = set_channel(m, h, ch); + if (!ch) + ch = "default"; + if (ret < 0) { + PARA_WARNING_LOG("ignoring channel %s\n", ch); + ret = 0; + } else { + PARA_INFO_LOG("initial volume %s: %d\n", ch, iv); + ret = m->set(h, iv); + } + free(arg); + if (ret < 0) + return ret; + } + return 1; +} + +static int com_sleep(struct mixer *m, struct mixer_handle *h) +{ + time_t t1, wake_time_epoch; + unsigned int delay; + struct tm *tm; + int ret, min = OPT_UINT32_VAL(SLEEP, WAKE_MIN); + const char *fo_mood = OPT_STRING_VAL(SLEEP, FO_MOOD); + const char *fi_mood = OPT_STRING_VAL(SLEEP, FI_MOOD); + const char *sleep_mood = OPT_STRING_VAL(SLEEP, SLEEP_MOOD); + int fit = OPT_UINT32_VAL(SLEEP, FI_TIME); + int fot = OPT_UINT32_VAL(SLEEP, FO_TIME); + int fiv = OPT_UINT32_VAL(SLEEP, FI_VOL); + int fov = OPT_UINT32_VAL(SLEEP, FO_VOL); + + /* calculate wake time */ + time(&t1); + if (OPT_GIVEN(SLEEP, WAKE_HOUR)) { + int hour = OPT_UINT32_VAL(SLEEP, WAKE_HOUR); + tm = localtime(&t1); + if (tm->tm_hour > hour || (tm->tm_hour == hour && tm->tm_min> min)) { + t1 += 86400; /* wake time is tomorrow */ + tm = localtime(&t1); + } + tm->tm_hour = hour; + tm->tm_min = min; + tm->tm_sec = 0; + } else { + t1 += 9 * 60 * 60; /* nine hours from now */ + PARA_INFO_LOG("default wake time: %lu\n", (long unsigned)t1); + tm = localtime(&t1); + } + wake_time_epoch = mktime(tm); + PARA_INFO_LOG("waketime: %d:%02d\n", tm->tm_hour, tm->tm_min); + client_cmd("stop"); + sleep(1); + if (fot) { + ret = set_initial_volume(m, h); + if (ret < 0) + return ret; + change_afs_mode(fo_mood); + client_cmd("play"); + ret = set_channel(m, h, OPT_STRING_VAL(PARA_MIXER, MIXER_CHANNEL)); + if (ret < 0) + return ret; + ret = fade(m, h, fov, fot); + if (ret < 0) + return ret; + } else { + ret = m->set(h, fov); + if (ret < 0) + return ret; + } + if (OPT_GIVEN(SLEEP, SLEEP_MOOD)) { + change_afs_mode(sleep_mood); + client_cmd("play"); + } else + client_cmd("stop"); + if (!fit) + return 1; + change_afs_mode(fi_mood); + for (;;) { + time(&t1); + if (wake_time_epoch <= t1 + fit) + break; + delay = wake_time_epoch - t1 - fit; + PARA_INFO_LOG("sleeping %u seconds (%u:%02u)\n", + delay, delay / 3600, + (delay % 3600) / 60); + sleep(delay); + } + client_cmd("play"); + ret = fade(m, h, fiv, fit); + PARA_INFO_LOG("fade complete, returning\n"); + return ret; +} +EXPORT_CMD(sleep); + +static int com_snooze(struct mixer *m, struct mixer_handle *h) +{ + int ret, val; + + if (OPT_UINT32_VAL(SNOOZE, SO_TIME) == 0) + return 1; + ret = m->get(h); + if (ret < 0) + return ret; + val = ret; + if (val < OPT_UINT32_VAL(SNOOZE, SO_VOL)) + ret = m->set(h, OPT_UINT32_VAL(SNOOZE, SO_VOL)); + else + ret = fade(m, h, OPT_UINT32_VAL(SNOOZE, SO_VOL), + OPT_UINT32_VAL(SNOOZE, SO_TIME)); + if (ret < 0) + return ret; + client_cmd("pause"); + PARA_NOTICE_LOG("%" PRIu32 " seconds snooze time...\n", + OPT_UINT32_VAL(SNOOZE, SNOOZE_TIME)); + sleep(OPT_UINT32_VAL(SNOOZE, SNOOZE_TIME)); + client_cmd("play"); + return fade(m, h, OPT_UINT32_VAL(SNOOZE, SI_VOL), + OPT_UINT32_VAL(SNOOZE, SI_TIME)); +} +EXPORT_CMD(snooze); + +static void init_mixers(void) +{ + int i; + + FOR_EACH_MIXER(i) { + struct mixer *m = &supported_mixer[i]; + PARA_DEBUG_LOG("initializing mixer API #%d (%s)\n", + i, mixer_name[i]); + m->init(m); + } +} + +static int com_set(struct mixer *m, struct mixer_handle *h) +{ + return m->set(h, OPT_UINT32_VAL(SET, VAL)); +} +EXPORT_CMD(set); + +static struct mixer *get_mixer_or_die(void) +{ + int i; + + if (!OPT_GIVEN(PARA_MIXER, MIXER_API)) + i = DEFAULT_MIXER; + else + FOR_EACH_MIXER(i) + if (!strcmp(mixer_name[i], + OPT_STRING_VAL(PARA_MIXER, MIXER_API))) + break; + if (i < NUM_SUPPORTED_MIXERS) { + PARA_NOTICE_LOG("using %s mixer API\n", mixer_name[i]); + return supported_mixer + i; + } + printf("available mixer APIs: "); + FOR_EACH_MIXER(i) { + int d = (i == DEFAULT_MIXER); + printf("%s%s%s ", d? "[" : "", mixer_name[i], d? "]" : ""); + } + printf("\n"); + exit(EXIT_FAILURE); +} + +static void show_subcommands(void) +{ + const struct lls_command *cmd; + int i; + printf("Subcommands:\n"); + for (i = 1; (cmd = lls_cmd(i, mixer_suite)); i++) { + const char *name = lls_command_name(cmd); + const char *purpose = lls_purpose(cmd); + printf("%-20s%s\n", name, purpose); + } +} + + +static int com_help(__a_unused struct mixer *m, __a_unused struct mixer_handle *h) +{ + const struct lls_command *cmd; + const struct lls_opt_result *r_l = OPT_RESULT(HELP, LONG); + char *txt, *errctx; + const char *name; + int ret; + + ret = lls_check_arg_count(sub_lpr, 0, 1, NULL); + if (ret < 0) + return ret; + if (lls_num_inputs(sub_lpr) == 0) { + show_subcommands(); + return 0; + } + name = lls_input(0, sub_lpr); + ret = lls(lls_lookup_subcmd(name, mixer_suite, &errctx)); + if (ret < 0) { + if (errctx) + PARA_ERROR_LOG("%s\n", errctx); + free(errctx); + return ret; + } + cmd = lls_cmd(ret, mixer_suite); + if (lls_opt_given(r_l)) + txt = lls_long_help(cmd); + else + txt = lls_short_help(cmd); + printf("%s", txt); + free(txt); + return 0; +} +EXPORT_CMD(help); + +static void handle_help_flags(void) +{ + char *help; + + if (OPT_GIVEN(PARA_MIXER, DETAILED_HELP)) + help = lls_long_help(CMD_PTR(PARA_MIXER)); + else if (OPT_GIVEN(PARA_MIXER, HELP)) + help = lls_short_help(CMD_PTR(PARA_MIXER)); + else + return; + printf("%s", help); + free(help); + show_subcommands(); + exit(EXIT_SUCCESS); +} + +static int parse_and_merge_config_file(const struct lls_command *cmd) +{ + int ret; + int cf_argc; + char **cf_argv; + char *cf, *errctx = NULL; + struct lls_parse_result **lprp, *cf_lpr, *merged_lpr; + void *map; + size_t sz; + const char *subcmd_name; + + if (cmd == lls_cmd(0, mixer_suite)) { + lprp = &lpr; + subcmd_name = NULL; + } else { + lprp = &sub_lpr; + subcmd_name = lls_command_name(cmd); + } + if (OPT_GIVEN(PARA_MIXER, CONFIG_FILE)) + cf = para_strdup(OPT_STRING_VAL(PARA_MIXER, CONFIG_FILE)); + else { + char *home = para_homedir(); + cf = make_message("%s/.paraslash/mixer.conf", home); + free(home); + } + ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL); + if (ret < 0) { + if (ret != -E_EMPTY && ret != -ERRNO_TO_PARA_ERROR(ENOENT)) + goto free_cf; + if (ret == -ERRNO_TO_PARA_ERROR(ENOENT) && + OPT_GIVEN(PARA_MIXER, CONFIG_FILE)) + goto free_cf; + } else { + ret = lls(lls_convert_config(map, sz, subcmd_name, &cf_argv, &errctx)); + para_munmap(map, sz); + if (ret < 0) + goto free_cf; + cf_argc = ret; + ret = lls(lls_parse(cf_argc, cf_argv, cmd, &cf_lpr, &errctx)); + lls_free_argv(cf_argv); + if (ret < 0) + goto free_cf; + ret = lls(lls_merge(*lprp, cf_lpr, cmd, &merged_lpr, &errctx)); + lls_free_parse_result(cf_lpr, cmd); + if (ret < 0) + goto free_cf; + lls_free_parse_result(*lprp, cmd); + *lprp = merged_lpr; + loglevel = OPT_UINT32_VAL(PARA_MIXER, LOGLEVEL); + } + ret = 1; +free_cf: + free(cf); + if (errctx) + PARA_ERROR_LOG("%s\n", errctx); + free(errctx); + return ret; +} + +/** + * The main function of para_mixer. + * + * The executable is linked with the alsa or the oss mixer API, or both. It has + * a custom log function which prefixes log messages with the current date. + * + * \param argc Argument counter. + * \param argv Argument vector. + * + * \return EXIT_SUCCESS or EXIT_FAILURE. + */ +int main(int argc, char *argv[]) +{ + const struct lls_command *cmd = CMD_PTR(PARA_MIXER); + int ret; + char *errctx; + const char *subcmd; + struct mixer *m; + struct mixer_handle *h; + unsigned n; + + ret = lls(lls_parse(argc, argv, cmd, &lpr, &errctx)); + if (ret < 0) + goto fail; + loglevel = OPT_UINT32_VAL(PARA_MIXER, LOGLEVEL); + version_handle_flag("mixer", OPT_GIVEN(PARA_MIXER, VERSION)); + handle_help_flags(); + + n = lls_num_inputs(lpr); + if (n == 0) { + show_subcommands(); + ret = 0; + goto free_lpr; + } + ret = parse_and_merge_config_file(cmd); + if (ret < 0) + goto free_lpr; + subcmd = lls_input(0, lpr); + ret = lls(lls_lookup_subcmd(subcmd, mixer_suite, &errctx)); + if (ret < 0) + goto fail; + cmd = lls_cmd(ret, mixer_suite); + ret = lls(lls_parse(n, argv + argc - n, cmd, &sub_lpr, &errctx)); + if (ret < 0) + goto free_lpr; + ret = parse_and_merge_config_file(cmd); + if (ret < 0) + goto free_lpr; + init_mixers(); + m = get_mixer_or_die(); + ret = m->open(OPT_STRING_VAL(PARA_MIXER, MIXER_DEVICE), &h); + if (ret < 0) + goto free_sub_lpr; + ret = set_channel(m, h, OPT_STRING_VAL(PARA_MIXER, MIXER_CHANNEL)); + if (ret == -E_BAD_CHANNEL) { + char *channels = m->get_channels(h); + printf("Available channels: %s\n", channels); + free(channels); + } + if (ret < 0) + goto close_mixer; + ret = (*(mixer_subcommand_handler_t *)(lls_user_data(cmd)))(m ,h); +close_mixer: + m->close(&h); +free_sub_lpr: + lls_free_parse_result(sub_lpr, cmd); +free_lpr: + lls_free_parse_result(lpr, CMD_PTR(PARA_MIXER)); + if (ret >= 0) + return EXIT_SUCCESS; +fail: + if (errctx) + PARA_ERROR_LOG("%s\n", errctx); + free(errctx); + PARA_EMERG_LOG("%s\n", para_strerror(-ret)); + return EXIT_FAILURE; +} diff --git a/t/test-lib.sh b/t/test-lib.sh index 1f9913e3..9c6516e2 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -311,7 +311,7 @@ fi fixup_dirs [[ -z "$o_executables" ]] && o_executables="para_afh para_audioc para_audiod - para_client para_fade para_filter para_gui para_recv para_server + para_client para_mixer para_filter para_gui para_recv para_server para_write" for exe in $o_executables; do export $(tr 'a-z' 'A-Z' <<< $exe)="$o_executables_dir/$exe" diff --git a/web/documentation.in.html b/web/documentation.in.html index b226943b..9e8b59ce 100644 --- a/web/documentation.in.html +++ b/web/documentation.in.html @@ -29,7 +29,7 @@ [para_filter] [para_write] [para_gui] - [para_fade] + [para_mixere] [para_play]

diff --git a/web/manual.md b/web/manual.md index 9bc86016..1fde2953 100644 --- a/web/manual.md +++ b/web/manual.md @@ -167,7 +167,7 @@ window. Appearance can be customized via themes. para_gui provides key-bindings for the most common server commands and new key-bindings can be added easily. -### para_fade ### +### para_mixer ### An alarm clock and volume-fader for OSS and ALSA.