From: Andre Noll Date: Sun, 10 Apr 2016 20:44:30 +0000 (+0200) Subject: Convert filters to lopsub. X-Git-Tag: v0.6.0~2^2~31 X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=commitdiff_plain;h=4adde8dae3317fa83b81e7a860c9ed9133e99bb0 Convert filters to lopsub. This replaces the *_filter.m4 gengetopt files by the filter_cmd lopsub suite, where each filter is realized as a subcommand. Due to this change, para_filter needs to be linked with -llopsub. The filter structure is now stored in the user_data pointer provided by lopsub, allowing to get rid of the global filters[] array, the FILTER_ENUM macro and the corresponding enumeration constants. The removal of the ->goo_help member of struct filter makes this structure constant. Hence ->init() of struct filter can also go away. We still can tell whether a filter is supported by checking the user_data pointer: if it is NULL, the filter is unsupported. The new filter_supported() helper in filter_common.c is provided for convenience. Parsing of the filter command line options is now performed generically, and the ->parse_config() method is renamed to ->setup(), an optional function which is supposed to perform semantic checks and the one-time setup of the filter, if any. It is accompanied by ->teardown() which replaces ->free_config(). The conversion of the individual filters is easy since most filters have a simple syntax or take no arguments at all. The resample_filter, however, needs a different way to copy the wav parameters from the lopsub parse result to the wav parms structure. A suitable macro, LLS_COPY_WAV_PARMS is added to check_wav.h for this purpose. The old COPY_WAV_PARMS needs to stay until para_write, the only other user of the macro, has been converted as well. The section heading of the manual page has changed slightly, causing t0005 to fail. Hence this test needs a slight adjustment. --- diff --git a/Makefile.in b/Makefile.in index 2a2bdaf5..e72b518c 100644 --- a/Makefile.in +++ b/Makefile.in @@ -16,7 +16,6 @@ HELP2MAN := @HELP2MAN@ ggo_descriptions_declared := @ggo_descriptions_declared@ executables := @executables@ -filters := @filters@ writers := @writers@ recv_objs := @recv_objs@ diff --git a/Makefile.real b/Makefile.real index e119919d..2dec9847 100644 --- a/Makefile.real +++ b/Makefile.real @@ -47,15 +47,22 @@ converted_executables := audioc client fade play unconverted_executables := $(filter-out $(converted_executables), $(executables)) audioc_objs += audioc.lsg.o -audiod_objs += audiod_cmd.lsg.o recv_cmd.lsg.o client.lsg.o +audiod_objs += $(addsuffix _cmd.lsg.o, recv filter audiod) client.lsg.o +client_objs += client.lsg.o fade_objs += fade.lsg.o -server_objs += server_cmd.lsg.o -play_objs += play_cmd.lsg.o recv_cmd.lsg.o play.lsg.o +filter_objs += filter_cmd.lsg.o +play_objs += $(addsuffix _cmd.lsg.o, recv filter play) play.lsg.o recv_objs += recv_cmd.lsg.o -client_objs += client.lsg.o +server_objs += server_cmd.lsg.o m4_deps := $(addprefix $(m4depdir)/, $(addsuffix .m4d, $(unconverted_executables))) -m4_lls_deps := audiod_cmd server_cmd play_cmd recv_cmd $(converted_executables) +m4_lls_deps := \ + audiod_cmd \ + server_cmd \ + play_cmd \ + recv_cmd \ + filter_cmd \ + $(converted_executables) m4_lls_deps := $(addprefix $(lls_suite_dir)/, $(addsuffix .m4d, $(m4_lls_deps))) # now prefix all objects with object dir @@ -150,19 +157,21 @@ else Q := @ endif -server_command_lists := $(lls_suite_dir)/server_cmd.lsg.man -audiod_command_lists := \ - $(lls_suite_dir)/audiod_cmd.lsg.man \ - $(lls_suite_dir)/recv_cmd.lsg.man +audiod_command_lists := $(addprefix $(lls_suite_dir)/, \ + $(addsuffix _cmd.lsg.man, audiod recv filter)) +filter_command_lists := $(lls_suite_dir)/filter_cmd.lsg.man play_command_lists := $(lls_suite_dir)/play_cmd.lsg.man recv_command_lists := $(lls_suite_dir)/recv_cmd.lsg.man +server_command_lists := $(lls_suite_dir)/server_cmd.lsg.man $(man_dir)/para_server.1: $(server_command_lists) +$(man_dir)/para_filter.1: $(filter_command_lists) $(man_dir)/para_audiod.1: $(audiod_command_lists) $(man_dir)/para_play.1: $(play_command_lists) $(man_dir)/para_recv.1: $(recv_command_lists) $(man_dir)/para_server.1: man_util_command_lists := $(server_command_lists) +$(man_dir)/para_filter.1: man_util_command_lists := $(filter_command_lists) $(man_dir)/para_audiod.1: man_util_command_lists := $(audiod_command_lists) $(man_dir)/para_play.1: man_util_command_lists := $(play_command_lists) $(man_dir)/para_recv.1: man_util_command_lists := $(recv_command_lists) @@ -307,6 +316,7 @@ para_audioc \ para_audiod \ para_client \ para_fade \ +para_filter \ para_play \ para_recv \ para_server \ diff --git a/aacdec_filter.c b/aacdec_filter.c index 5725ce04..bbb756a9 100644 --- a/aacdec_filter.c +++ b/aacdec_filter.c @@ -15,7 +15,6 @@ #include "para.h" #include "list.h" #include "sched.h" -#include "ggo.h" #include "buffer_tree.h" #include "filter.h" #include "error.h" @@ -206,18 +205,10 @@ err: return ret; } -/** - * The init function of the aacdec filter. - * - * \param f Pointer to the filter struct to initialize. - * - * \sa filter::init - */ -void aacdec_filter_init(struct filter *f) -{ - f->open = aacdec_open; - f->close = aacdec_close; - f->pre_select = generic_filter_pre_select; - f->post_select = aacdec_post_select; - f->execute = aacdec_execute; -} +const struct filter lsg_filter_cmd_com_aacdec_user_data = { + .open = aacdec_open, + .close = aacdec_close, + .pre_select = generic_filter_pre_select, + .post_select = aacdec_post_select, + .execute = aacdec_execute +}; diff --git a/amp_filter.c b/amp_filter.c index 5193d7c1..f3d0d87d 100644 --- a/amp_filter.c +++ b/amp_filter.c @@ -7,12 +7,12 @@ /** \file amp_filter.c Paraslash's amplify filter. */ #include +#include +#include "filter_cmd.lsg.h" #include "para.h" -#include "amp_filter.cmdline.h" #include "list.h" #include "sched.h" -#include "ggo.h" #include "buffer_tree.h" #include "filter.h" #include "string.h" @@ -31,33 +31,18 @@ static void amp_close(struct filter_node *fn) free(fn->private_data); } -static int amp_parse_config(int argc, char **argv, void **config) -{ - struct amp_filter_args_info *conf = para_calloc(sizeof(*conf)); - int ret; - - amp_filter_cmdline_parser(argc, argv, conf); - ret = -ERRNO_TO_PARA_ERROR(EINVAL); - if (conf->amp_arg < 0) - goto err; - *config = conf; - return 1; -err: - free(conf); - return ret; -} - static void amp_open(struct filter_node *fn) { struct private_amp_data *pad = para_calloc(sizeof(*pad)); - struct amp_filter_args_info *conf = fn->conf; + unsigned given = FILTER_CMD_OPT_GIVEN(AMP, AMP, fn->lpr); + uint32_t amp_arg = FILTER_CMD_OPT_UINT32_VAL(AMP, AMP, fn->lpr); fn->private_data = pad; fn->min_iqs = 2; - if (!conf->amp_given && stat_item_values[SI_AMPLIFICATION]) + if (!given && stat_item_values[SI_AMPLIFICATION]) sscanf(stat_item_values[SI_AMPLIFICATION], "%u", &pad->amp); else - pad->amp = conf->amp_arg; + pad->amp = amp_arg; PARA_INFO_LOG("amplification: %u (scaling factor: %1.2f)\n", pad->amp, pad->amp / 64.0 + 1.0); } @@ -116,26 +101,9 @@ err: return ret; } -static void amp_free_config(void *conf) -{ - amp_filter_cmdline_parser_free(conf); -} - -/** - * The init function of the amplify filter. - * - * \param f Pointer to the struct to initialize. - */ -void amp_filter_init(struct filter *f) -{ - struct amp_filter_args_info dummy; - - amp_filter_cmdline_parser_init(&dummy); - f->open = amp_open; - f->close = amp_close; - f->pre_select = generic_filter_pre_select; - f->post_select = amp_post_select; - f->parse_config = amp_parse_config; - f->free_config = amp_free_config; - f->help = (struct ggo_help)DEFINE_GGO_HELP(amp_filter); -} +const struct filter lsg_filter_cmd_com_amp_user_data = { + .open = amp_open, + .close = amp_close, + .pre_select = generic_filter_pre_select, + .post_select = amp_post_select, +}; diff --git a/audiod.c b/audiod.c index 56ab0c9a..5f4acb43 100644 --- a/audiod.c +++ b/audiod.c @@ -60,6 +60,8 @@ struct audio_format_info { unsigned *filter_nums; /** Pointer to the array of filter configurations. */ void **filter_conf; + /** Parsed filter command line, one parse result per filter. */ + struct lls_parse_result **filter_lpr; /** the number of filters that should be activated for this audio format */ unsigned int num_writers; /** Array of writer numbers to be activated. */ @@ -592,17 +594,20 @@ static void open_filters(struct slot_info *s) parent = s->receiver_node->btrn; for (i = 0; i < nf; i++) { char buf[20]; + const char *name; const struct filter *f = filter_get(a->filter_nums[i]); fn = s->fns + i; fn->filter_num = a->filter_nums[i]; fn->conf = a->filter_conf[i]; + fn->lpr = a->filter_lpr[i]; + name = filter_name(fn->filter_num); fn->btrn = btr_new_node(&(struct btr_node_description) - EMBRACE(.name = f->name, .parent = parent, + EMBRACE(.name = name, .parent = parent, .handler = f->execute, .context = fn)); if (f->open) f->open(fn); - sprintf(buf, "%s (slot %d)", f->name, (int)(s - slot)); + sprintf(buf, "%s (slot %d)", name, (int)(s - slot)); fn->task = task_register(&(struct task_info) { .name = buf, .pre_select = f->pre_select, @@ -611,7 +616,7 @@ static void open_filters(struct slot_info *s) }, &sched); parent = fn->btrn; PARA_NOTICE_LOG("%s filter %d/%d (%s) started in slot %d\n", - audio_formats[s->format], i, nf, f->name, (int)(s - slot)); + audio_formats[s->format], i, nf, name, (int)(s - slot)); } } @@ -852,19 +857,22 @@ static int add_filter(int format, const char *cmdline) struct audio_format_info *a = &afi[format]; int filter_num, nf = a->num_filters; void *cfg; + struct lls_parse_result *flpr; - filter_num = check_filter_arg(cmdline, &cfg); - if (filter_num < 0) - return filter_num; + filter_num = filter_setup(cmdline, &cfg, &flpr); + a->filter_lpr = para_realloc(a->filter_lpr, + (nf + 1) * sizeof(flpr)); a->filter_conf = para_realloc(a->filter_conf, (nf + 1) * sizeof(void *)); a->filter_nums = para_realloc(a->filter_nums, (nf + 1) * sizeof(unsigned)); + a->filter_nums[nf] = filter_num; a->filter_conf[nf] = cfg; + a->filter_lpr[nf] = flpr; a->num_filters++; PARA_INFO_LOG("%s filter %d: %s\n", audio_formats[format], nf, - filter_get(filter_num)->name); + filter_name(filter_num)); return filter_num; } @@ -991,19 +999,19 @@ static int init_default_filters(void) /* add "dec" to audio format name */ tmp = make_message("%sdec", audio_formats[i]); for (j = 0; filter_get(j); j++) - if (!strcmp(tmp, filter_get(j)->name)) + if (!strcmp(tmp, filter_name(j))) break; free(tmp); ret = -E_UNSUPPORTED_FILTER; if (!filter_get(j)) goto out; - tmp = para_strdup(filter_get(j)->name); + tmp = para_strdup(filter_name(j)); ret = add_filter(i, tmp); free(tmp); if (ret < 0) goto out; PARA_INFO_LOG("%s -> default filter: %s\n", audio_formats[i], - filter_get(j)->name); + filter_name(j)); } out: return ret; @@ -1496,7 +1504,6 @@ int main(int argc, char *argv[]) version_handle_flag("audiod", conf.version_given); /* init receivers/filters/writers early to make help work */ recv_init(); - filter_init(); writer_init(); if (conf.help_given || conf.detailed_help_given) print_help_and_die(); diff --git a/check_wav.h b/check_wav.h index 0957fe03..31417de6 100644 --- a/check_wav.h +++ b/check_wav.h @@ -46,6 +46,20 @@ struct wav_params { (dst)->sample_format_arg = (src)->sample_format_arg; \ (dst)->sample_format_given = (src)->sample_format_given; +#define LLS_COPY_WAV_PARMS(_dst, _pfx, _lpr) \ + (_dst)->channels_given = lls_opt_given(lls_opt_result( \ + _pfx ## _OPT_CHANNELS, (_lpr))); \ + (_dst)->sample_rate_given = lls_opt_given(lls_opt_result( \ + _pfx ## _OPT_SAMPLE_RATE, (_lpr))); \ + (_dst)->sample_format_given = lls_opt_given(lls_opt_result( \ + _pfx ## _OPT_SAMPLE_FORMAT, (_lpr))); \ + (_dst)->channels_arg = lls_uint32_val(0, lls_opt_result( \ + _pfx ## _OPT_CHANNELS, (_lpr))); \ + (_dst)->sample_rate_arg = lls_uint32_val(0, lls_opt_result( \ + _pfx ## _OPT_SAMPLE_RATE, (_lpr))); \ + (_dst)->sample_format_arg = lls_uint32_val(0, lls_opt_result( \ + _pfx ## _OPT_SAMPLE_FORMAT, (_lpr))); + struct check_wav_context *check_wav_init(struct btr_node *parent, struct btr_node *child, struct wav_params *params, struct btr_node **cw_btrn); diff --git a/compress_filter.c b/compress_filter.c index d0d96fd9..65897642 100644 --- a/compress_filter.c +++ b/compress_filter.c @@ -11,23 +11,23 @@ */ #include +#include +#include "filter_cmd.lsg.h" #include "para.h" -#include "compress_filter.cmdline.h" #include "list.h" #include "sched.h" -#include "ggo.h" #include "buffer_tree.h" #include "filter.h" #include "string.h" #include "error.h" +#define U32_OPTVAL(_opt, _lpr) (FILTER_CMD_OPT_UINT32_VAL(COMPRESS, _opt, _lpr)) + /** Data specific to the compress filter. */ struct private_compress_data { /** The current multiplier. */ unsigned current_gain; - /** Points to the configuration data for this instance of the compress filter. */ - struct compress_filter_args_info *conf; /** Maximal admissible gain. */ unsigned max_gain; /** Number of samples already seen. */ @@ -51,9 +51,9 @@ static int compress_post_select(__a_unused struct sched *s, void *context) char *inbuf; size_t length, i; int16_t *ip, *op; - unsigned gain_shift = pcd->conf->inertia_arg + pcd->conf->damp_arg, - mask = (1 << pcd->conf->blocksize_arg) - 1; - + uint32_t inertia = U32_OPTVAL(INERTIA, fn->lpr); + unsigned gain_shift = inertia + U32_OPTVAL(DAMP, fn->lpr), + mask = (1 << U32_OPTVAL(BLOCKSIZE, fn->lpr)) - 1; //inplace = false; next_buffer: ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL); @@ -86,7 +86,7 @@ next_buffer: if (sample > 32767) { /* clip */ sample = 32767; pcd->current_gain = (3 * pcd->current_gain + - (1 << pcd->conf->inertia_arg)) / 4; + (1 << inertia)) / 4; pcd->peak = 0; } else if (sample > pcd->peak) pcd->peak = sample; @@ -95,12 +95,12 @@ next_buffer: continue; // PARA_DEBUG_LOG("gain: %u, peak: %u\n", pcd->current_gain, // pcd->peak); - if (pcd->peak < pcd->conf->target_level_arg) { + if (pcd->peak < U32_OPTVAL(TARGET_LEVEL, fn->lpr)) { if (pcd->current_gain < pcd->max_gain) pcd->current_gain++; } else pcd->current_gain = PARA_MAX(pcd->current_gain - 2, - 1U << pcd->conf->inertia_arg); + 1U << inertia); pcd->peak = 0; } if (inplace) @@ -116,47 +116,21 @@ err: return ret; } -/** TODO: Add sanity checks */ -static int compress_parse_config(int argc, char **argv, void **config) -{ - struct compress_filter_args_info *conf = para_calloc(sizeof(*conf)); - - compress_filter_cmdline_parser(argc, argv, conf); - *config = conf; - return 1; -} - static void compress_open(struct filter_node *fn) { - struct private_compress_data *pcd = para_calloc( - sizeof(struct private_compress_data)); - pcd->conf = fn->conf; + struct private_compress_data *pcd = para_calloc(sizeof(*pcd)); + uint32_t inertia = U32_OPTVAL(INERTIA, fn->lpr); + uint32_t aggressiveness = U32_OPTVAL(AGGRESSIVENESS, fn->lpr); + fn->private_data = pcd; fn->min_iqs = 2; /* 16 bit audio */ - pcd->current_gain = 1 << pcd->conf->inertia_arg; - pcd->max_gain = 1 << (pcd->conf->inertia_arg + pcd->conf->aggressiveness_arg); -} - -static void compress_free_config(void *conf) -{ - compress_filter_cmdline_parser_free(conf); + pcd->current_gain = 1U << inertia; + pcd->max_gain = 1U << (inertia + aggressiveness); } -/** - * The init function of the compress filter. - * - * \param f Pointer to the struct to initialize. - */ -void compress_filter_init(struct filter *f) -{ - struct compress_filter_args_info dummy; - - compress_filter_cmdline_parser_init(&dummy); - f->open = compress_open; - f->close = compress_close; - f->pre_select = generic_filter_pre_select; - f->post_select = compress_post_select; - f->parse_config = compress_parse_config; - f->free_config = compress_free_config; - f->help = (struct ggo_help)DEFINE_GGO_HELP(compress_filter); -} +const struct filter lsg_filter_cmd_com_compress_user_data = { + .open = compress_open, + .close = compress_close, + .pre_select = generic_filter_pre_select, + .post_select = compress_post_select, +}; diff --git a/configure.ac b/configure.ac index d592fba8..62aa16d3 100644 --- a/configure.ac +++ b/configure.ac @@ -508,11 +508,7 @@ if test -n "$CRYPTOLIB"; then audiod_audio_formats="wma" audiod_cmdline_objs="$audiod_cmdline_objs audiod - compress_filter file_write - amp_filter - prebuffer_filter - sync_filter " audiod_errlist_objs="$audiod_errlist_objs audiod @@ -584,7 +580,6 @@ if test -n "$CRYPTOLIB"; then fi if test $HAVE_MAD = yes; then audiod_audio_formats="$audiod_audio_formats mp3" - audiod_cmdline_objs="$audiod_cmdline_objs mp3dec_filter" audiod_errlist_objs="$audiod_errlist_objs mp3dec_filter" fi if test $HAVE_OSS = yes; then @@ -601,7 +596,6 @@ if test -n "$CRYPTOLIB"; then } if test $HAVE_SAMPLERATE = yes; then audiod_errlist_objs="$audiod_errlist_objs resample_filter check_wav" - audiod_cmdline_objs="$audiod_cmdline_objs resample_filter" fi audiod_objs="add_cmdline($audiod_cmdline_objs) $audiod_errlist_objs" AC_SUBST(audiod_objs, add_dot_o($audiod_objs)) @@ -681,15 +675,6 @@ else AC_MSG_WARN([no curses lib, cannot build para_gui]) fi ######################################################################## filter -filters=" - compress - wav - amp - fecdec - wmadec - prebuffer - sync -" filter_errlist_objs=" filter_common wav_filter @@ -717,54 +702,23 @@ filter_errlist_objs=" " filter_cmdline_objs=" filter - compress_filter - amp_filter - prebuffer_filter - sync_filter " -NEED_VORBIS_OBJECTS && { - filters="$filters oggdec" - filter_errlist_objs="$filter_errlist_objs oggdec_filter" -} -NEED_SPEEX_OBJECTS && { - filters="$filters spxdec" - filter_errlist_objs="$filter_errlist_objs spxdec_filter spx_common" -} -NEED_OPUS_OBJECTS && { - filters="$filters opusdec" - filter_errlist_objs="$filter_errlist_objs opusdec_filter opus_common" -} -NEED_FLAC_OBJECTS && { - filter_errlist_objs="$filter_errlist_objs flacdec_filter" - filters="$filters flacdec" -} +NEED_VORBIS_OBJECTS && filter_errlist_objs="$filter_errlist_objs oggdec_filter" +NEED_SPEEX_OBJECTS && filter_errlist_objs="$filter_errlist_objs spxdec_filter spx_common" +NEED_OPUS_OBJECTS && filter_errlist_objs="$filter_errlist_objs opusdec_filter opus_common" +NEED_FLAC_OBJECTS && filter_errlist_objs="$filter_errlist_objs flacdec_filter" if test $HAVE_FAAD = yes; then filter_errlist_objs="$filter_errlist_objs aacdec_filter aac_common" - filters="$filters aacdec" fi if test $HAVE_MAD = yes; then - filter_cmdline_objs="$filter_cmdline_objs mp3dec_filter" filter_errlist_objs="$filter_errlist_objs mp3dec_filter" - filters="$filters mp3dec" fi if test $HAVE_SAMPLERATE = yes; then filter_errlist_objs="$filter_errlist_objs resample_filter check_wav" - filter_cmdline_objs="$filter_cmdline_objs resample_filter" - filters="$filters resample" fi -filters="$(echo $filters)" -AC_SUBST(filters) filter_objs="add_cmdline($filter_cmdline_objs) $filter_errlist_objs" AC_SUBST(filter_objs, add_dot_o($filter_objs)) - -enum="$(for i in $filters; do printf "${i}_FILTER, " | tr '[a-z]' '[A-Z]'; done)" -AC_DEFINE_UNQUOTED(FILTER_ENUM, $enum NUM_SUPPORTED_FILTERS, - enum of supported filters) -inits="$(for i in $filters; do printf 'extern void '$i'_filter_init(struct filter *f); '; done)" -AC_DEFINE_UNQUOTED(DECLARE_FILTER_INITS, $inits, init functions of the supported filters) -array="$(for i in $filters; do printf '{.name = "'$i'", .init = '$i'_filter_init},'; done)" -AC_DEFINE_UNQUOTED(FILTER_ARRAY, $array, array of supported filters) ########################################################################## recv recv_cmdline_objs=" recv @@ -875,11 +829,7 @@ play_errlist_objs=" sync_filter " play_cmdline_objs=" - compress_filter - amp_filter - prebuffer_filter file_write - sync_filter " if test "$have_core_audio" = "yes"; then play_errlist_objs="$play_errlist_objs osx_write ipc" @@ -911,7 +861,6 @@ if test $HAVE_MP4V2 = yes || test $HAVE_FAAD = yes; then play_errlist_objs="$play_errlist_objs aac_common" fi if test $HAVE_MAD = yes; then - play_cmdline_objs="$play_cmdline_objs mp3dec_filter" play_errlist_objs="$play_errlist_objs mp3dec_filter" fi if test $HAVE_OSS = yes; then @@ -931,7 +880,6 @@ if test $HAVE_READLINE = yes; then fi if test $HAVE_SAMPLERATE = yes; then play_errlist_objs="$play_errlist_objs resample_filter check_wav" - play_cmdline_objs="$play_cmdline_objs resample_filter" fi play_objs="add_cmdline($play_cmdline_objs) $play_errlist_objs" @@ -1052,9 +1000,7 @@ readline (interactive CLIs): $HAVE_READLINE id3 version 2 support: $HAVE_ID3TAG faad: $HAVE_FAAD mp4v2: $HAVE_MP4V2 - audio format handlers: $audio_format_handlers -filters: $(echo $filters) writers: $writers para_server: $build_server diff --git a/error.h b/error.h index 404bdf42..9c8d7d34 100644 --- a/error.h +++ b/error.h @@ -59,7 +59,6 @@ PARA_ERROR(BAD_CT, "invalid chunk table or bad FEC configuration"), \ PARA_ERROR(BAD_FEATURE, "invalid feature request"), \ PARA_ERROR(BAD_FEC_HEADER, "invalid fec header"), \ - PARA_ERROR(BAD_FILTER_OPTIONS, "invalid filter option given"), \ PARA_ERROR(BAD_LL, "invalid loglevel"), \ PARA_ERROR(BAD_PATH, "invalid path"), \ PARA_ERROR(BAD_PRIVATE_KEY, "invalid private key"), \ diff --git a/fecdec_filter.c b/fecdec_filter.c index 1c3a3784..1b95ea4c 100644 --- a/fecdec_filter.c +++ b/fecdec_filter.c @@ -12,7 +12,6 @@ #include "error.h" #include "list.h" #include "sched.h" -#include "ggo.h" #include "buffer_tree.h" #include "filter.h" #include "string.h" @@ -481,15 +480,9 @@ static void fecdec_open(struct filter_node *fn) fn->min_iqs = FEC_HEADER_SIZE; } -/** - * The init function of the fecdec filter. - * - * \param f Struct to initialize. - */ -void fecdec_filter_init(struct filter *f) -{ - f->close = fecdec_close; - f->open = fecdec_open; - f->pre_select = generic_filter_pre_select; - f->post_select = fecdec_post_select; -} +const struct filter lsg_filter_cmd_com_fecdec_user_data = { + .open = fecdec_open, + .pre_select = generic_filter_pre_select, + .post_select = fecdec_post_select, + .close = fecdec_close, +}; diff --git a/filter.c b/filter.c index 81901896..1deaf6da 100644 --- a/filter.c +++ b/filter.c @@ -57,7 +57,7 @@ __noreturn static void print_help_and_die(void) bool d = conf.detailed_help_given; ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS); - print_filter_helps(d? GPH_MODULE_FLAGS_DETAILED : GPH_MODULE_FLAGS); + print_filter_helps(d); exit(EXIT_SUCCESS); } @@ -109,10 +109,10 @@ int main(int argc, char *argv[]) const struct filter *f; struct btr_node *parent; struct filter_node **fns; + struct lls_parse_result *filter_lpr; filter_cmdline_parser(argc, argv, &conf); /* aborts on errors */ loglevel = get_loglevel_by_name(conf.loglevel_arg); - filter_init(); ret = parse_config(); if (ret < 0) goto out; @@ -123,22 +123,20 @@ int main(int argc, char *argv[]) fns = para_malloc(conf.filter_given * sizeof(*fns)); for (i = 0, parent = sit->btrn; i < conf.filter_given; i++) { char *fa = conf.filter_arg[i]; + const char *name; struct filter_node *fn; struct task_info ti; fn = fns[i] = para_calloc(sizeof(*fn)); - ret = check_filter_arg(fa, &fn->conf); - if (ret < 0) { - free(fn); - goto out_cleanup; - } - fn->filter_num = ret; + fn->filter_num = filter_setup(fa, &fn->conf, &filter_lpr); + name = filter_name(fn->filter_num); + fn->lpr = filter_lpr; + PARA_DEBUG_LOG("filter #%d: %s\n", i, name); f = filter_get(fn->filter_num); - PARA_DEBUG_LOG("filter #%d: %s\n", i, f->name); fn->btrn = btr_new_node(&(struct btr_node_description) - EMBRACE(.name = f->name, .parent = parent, + EMBRACE(.name = name, .parent = parent, .handler = f->execute, .context = fn)); - ti.name = f->name; + ti.name = name; ti.pre_select = f->pre_select; ti.post_select = f->post_select; ti.context = fn; @@ -156,7 +154,6 @@ int main(int argc, char *argv[]) btr_log_tree(sit->btrn, LL_INFO); ret = schedule(&s); sched_shutdown(&s); -out_cleanup: for (i--; i >= 0; i--) { struct filter_node *fn = fns[i]; @@ -164,8 +161,8 @@ out_cleanup: if (f->close) f->close(fn); btr_remove_node(&fn->btrn); - if (f->free_config) - f->free_config(fn->conf); + if (f->teardown) + f->teardown(fn->lpr, fn->conf); free(fn); } free(fns); diff --git a/filter.h b/filter.h index 0bd54690..03e79d91 100644 --- a/filter.h +++ b/filter.h @@ -6,9 +6,6 @@ /** \file filter.h Filter-related structures and exported symbols from filter_common.c. */ -/** The list of supported filters. */ -enum filter_enum {FILTER_ENUM}; - /** * Describes one running instance of a filter. */ @@ -24,6 +21,8 @@ struct filter_node { struct list_head callbacks; /** A pointer to the configuration of this instance. */ void *conf; + /** The parsed command line, merged with options given in the config file. */ + struct lls_parse_result *lpr; /** The buffer tree node. */ struct btr_node *btrn; /** The task corresponding to this filter node. */ @@ -35,26 +34,16 @@ struct filter_node { /** * The structure associated with a paraslash filter. * - * Paraslash filters are "modules" which are used to transform an audio stream. - * struct filter contains pointers to functions that must be supplied by the - * filter code in order to be used by the driving application (currently - * para_audiod and para_filter). + * Paraslash filters are "modules" which transform an audio stream. struct + * filter contains methods which are implemented by each filter. * * Note: As several instances of the same filter may be running at the same * time, all these filter functions must be reentrant; no static non-constant * variables may be used. + * * \sa mp3dec_filter.c, oggdec_filter.c, wav_filter.c, compress_filter.c, filter_node */ struct filter { - /** The name of the filter. */ - const char *name; - /** - * Pointer to the filter init routine. - * - * This function is only called once at startup. It must initialize the - * other non-optional function pointers of this structure. - */ - void (*init)(struct filter *f); /** * Open one instance of this filter. * @@ -73,27 +62,29 @@ struct filter { */ void (*close)(struct filter_node *fn); /** - * A pointer to the filter's command line parser. + * Prepare the filter according to command line options. * - * If this optional function pointer is not NULL, any filter options - * are passed from the main program to this command line parser once at - * application startup. The command line parser should check its - * command line options given by \a argc and \a argv and abort on - * errors. Success must be indicated by a non-negative return value. In - * this case the function should return a pointer to the - * filter-specific configuration data determined by \a argc and \a - * argv. On failure, a negative paraslash error code must be returned. + * In addition to the syntactic checks which are automatically performed + * by the lopsub functions, some filters like to also check the command + * line arguments semantically. Moreover, since applications may open + * the filter many times with the same options, filters need a method + * which allows them to precompute once those parts of the setup which + * depend only on the command line options. + * + * If this function pointer is not NULL, the function is called once at + * startup. The returned pointer value is made available to the ->open + * method via the ->conf pointer of struct filter_node. + * + * Filters are supposed to abort if the setup fails. If the function + * returns, it is assumed to have succeeded. */ - int (*parse_config)(int argc, char **argv, void **config); + void *(*setup)(const struct lls_parse_result *lpr); /** - * Deallocate the memory for the configuration. + * Deallocate precomputed resources. * - * This is called to free whatever ->parse_config() has allocated. + * This should free whatever ->setup() has allocated. */ - void (*free_config)(void *conf); - - /** The help texts for this filter. */ - struct ggo_help help; + void (*teardown)(const struct lls_parse_result *lpr, void *conf); /** * Set scheduler timeout and add file descriptors to fd sets. * @@ -121,9 +112,18 @@ struct filter { btr_command_handler execute; }; -void filter_init(void); -int check_filter_arg(const char *fa, void **conf); -void print_filter_helps(unsigned flags); +void print_filter_helps(bool detailed); +int filter_setup(const char *fa, void **conf, struct lls_parse_result **lprp); +#define FILTER_CMD(_num) (lls_cmd(_num, filter_cmd_suite)) +#define FILTER_CMD_OPT_RESULT(_cmd, _opt, _lpr) \ + (lls_opt_result(LSG_FILTER_CMD_ ## _cmd ## _OPT_ ## _opt, _lpr)) +#define FILTER_CMD_OPT_GIVEN(_cmd, _opt, _lpr) \ + (lls_opt_given(FILTER_CMD_OPT_RESULT(_cmd, _opt, _lpr))) +#define FILTER_CMD_OPT_UINT32_VAL(_cmd, _opt, _lpr) \ + (lls_uint32_val(0, FILTER_CMD_OPT_RESULT(_cmd, _opt, _lpr))) +#define FILTER_CMD_OPT_STRING_VAL(_cmd, _opt, _lpr) \ + (lls_string_val(0, FILTER_CMD_OPT_RESULT(_cmd, _opt, _lpr))) + void generic_filter_pre_select(struct sched *s, void *context); int decoder_execute(const char *cmd, unsigned sample_rate, unsigned channels, char **result); @@ -139,6 +139,6 @@ static inline void write_int16_host_endian(char *buf, int val) #endif } -DECLARE_FILTER_INITS - +/** Make a filter pointer from the filter number. */ const struct filter *filter_get(int filter_num); +const char *filter_name(int filter_num); diff --git a/filter_common.c b/filter_common.c index e9b97e54..5a5e9d03 100644 --- a/filter_common.c +++ b/filter_common.c @@ -8,22 +8,20 @@ #include #include +#include +#include "filter_cmd.lsg.h" #include "para.h" #include "list.h" #include "sched.h" #include "fd.h" -#include "ggo.h" #include "buffer_tree.h" #include "filter.h" #include "error.h" #include "string.h" -/** Iterate over the array of supported filters. */ -#define FOR_EACH_SUPPORTED_FILTER(j) for (j = 0; j < NUM_SUPPORTED_FILTERS; j++) - -/** The array of supported filters. */ -static struct filter filters[NUM_SUPPORTED_FILTERS] = {FILTER_ARRAY}; +/** Iterate over all filters. */ +#define FOR_EACH_FILTER(j) for (j = 1; lls_cmd(j, filter_cmd_suite); j++) /** * Obtain a reference to a filter structure. @@ -37,115 +35,110 @@ static struct filter filters[NUM_SUPPORTED_FILTERS] = {FILTER_ARRAY}; */ const struct filter *filter_get(int filter_num) { - assert(filter_num >= 0); - assert(filter_num < NUM_SUPPORTED_FILTERS); - return filters + filter_num; + assert(filter_num >= 1); + assert(filter_num <= LSG_NUM_FILTER_CMD_SUBCOMMANDS); + return lls_user_data(FILTER_CMD(filter_num)); } -/** - * Call the init function of each supported filter. - * \sa filter::init - */ -void filter_init(void) +static inline bool filter_supported(int filter_num) { - int i; - - FOR_EACH_SUPPORTED_FILTER(i) - filter_get(i)->init((struct filter *)filter_get(i)); + return lls_user_data(FILTER_CMD(filter_num)); } -/* - * If the filter has a command line parser and options is not NULL, run it. - * Returns filter_num on success, negative on errors - */ -static int parse_filter_args(int filter_num, const char *options, void **conf) +const char *filter_name(int filter_num) { - const struct filter *f = filter_get(filter_num); - int ret, argc; - char **argv; - - if (!f->parse_config) - return strlen(options)? -E_BAD_FILTER_OPTIONS : filter_num; - argc = create_shifted_argv(options, " \t", &argv); - if (argc < 0) - return -E_BAD_FILTER_OPTIONS; - argv[0] = para_strdup(f->name); - ret = f->parse_config(argc, argv, conf); - free_argv(argv); - return ret < 0? ret : filter_num; + return lls_command_name(FILTER_CMD(filter_num)); } /** - * Check the filter command line options. + * Parse a filter command line and call the corresponding ->setup method. * * \param fa The filter argument. - * \param conf Points to the filter configuration upon successful return. + * \param conf Points to filter-specific setup upon successful return. + * \param lprp Parsed command line options are returned here. * * Check if the given filter argument starts with the name of a supported * filter, optionally followed by options for this filter. If yes, call the - * command line parser of that filter. - * - * \return On success, the number of the filter is returned and \a conf - * is initialized to point to the filter configuration determined by \a fa. - * On errors, a negative value is returned. + * command line parser of that filter and its ->setup method. * - * Note: If \a fa specifies a filter that has no command line parser success is - * returned, and \a conf is initialized to \p NULL. - * - * \sa filter::parse_config + * \return This function either succeeds or does not return. On success, the + * number of the filter is returned and conf is initialized to point to the + * filter configuration as returned by the filter's ->setup() method, if any. + * Moreover, *lprp is initialized to contain the parsed command line options. + * On errors, the function calls exit(EXIT_FAILURE). */ -int check_filter_arg(const char *fa, void **conf) +int filter_setup(const char *fa, void **conf, struct lls_parse_result **lprp) { - int j; - - *conf = NULL; -// PARA_DEBUG_LOG("arg: %s\n", fa); - FOR_EACH_SUPPORTED_FILTER(j) { - const char *name = filter_get(j)->name; - size_t len = strlen(name); - char c; - if (strlen(fa) < len) - continue; - if (strncmp(name, fa, len)) - continue; - c = fa[len]; - if (c && c != ' ') - continue; - if (c && !filter_get(j)->parse_config) - return -E_BAD_FILTER_OPTIONS; - return parse_filter_args(j, c? fa + len + 1 : - fa + strlen(fa), conf); + int ret, filter_num, argc; + char *errctx = NULL, **argv; + const struct lls_command *cmd; + const struct filter *f; + + ret = create_argv(fa, " \t\n", &argv); + if (ret < 0) + goto fail; + argc = ret; + ret = lls(lls_lookup_subcmd(argv[0], filter_cmd_suite, &errctx)); + if (ret < 0) + goto free_argv; + filter_num = ret; + cmd = FILTER_CMD(filter_num); + if (!filter_supported(filter_num)) { + ret = -E_UNSUPPORTED_FILTER; + errctx = make_message("bad filter name: %s", + lls_command_name(cmd)); + goto free_argv; } - return -E_UNSUPPORTED_FILTER; + ret = lls(lls_parse(argc, argv, cmd, lprp, &errctx)); + if (ret < 0) + goto free_argv; + f = filter_get(filter_num); + *conf = f->setup? f->setup(*lprp) : NULL; + ret = filter_num; +free_argv: + free_argv(argv); + if (ret >= 0) + return ret; +fail: + if (errctx) + PARA_ERROR_LOG("%s\n", errctx); + free(errctx); + PARA_EMERG_LOG("%s\n", para_strerror(-ret)); + exit(EXIT_FAILURE); } /** * Print help text of each filter to stdout. * - * \param flags Passed to \ref ggo_print_help(). + * \param detailed Whether to print short or long help. */ -void print_filter_helps(unsigned flags) +void print_filter_helps(bool detailed) { int i, num = 0; - printf_or_die("\nAvailable filters: "); - FOR_EACH_SUPPORTED_FILTER(i) { + printf("\nAvailable filters: "); + FOR_EACH_FILTER(i) { + if (!filter_supported(i)) + continue; if (num > 50) { - printf_or_die("\n "); + printf("\n "); num = 0; } - num += printf_or_die("%s%s", i? " " : "", filter_get(i)->name); + num += printf("%s%s", i? " " : "", filter_name(i)); } - printf_or_die("\n"); + printf("\n"); - FOR_EACH_SUPPORTED_FILTER(i) { - struct filter *f = (struct filter *)filter_get(i); + FOR_EACH_FILTER(i) { + const struct lls_command *cmd = FILTER_CMD(i); + char *help; - if (!f->help.short_help) + if (!filter_supported(i)) + continue; + help = detailed? lls_long_help(cmd) : lls_short_help(cmd); + if (!help) continue; - printf_or_die("\nOptions for %s (%s):", f->name, - f->help.purpose); - ggo_print_help(&f->help, flags); + printf("%s\n", help); + free(help); } } diff --git a/flacdec_filter.c b/flacdec_filter.c index bbacb3da..16237acb 100644 --- a/flacdec_filter.c +++ b/flacdec_filter.c @@ -12,7 +12,6 @@ #include "para.h" #include "list.h" #include "sched.h" -#include "ggo.h" #include "buffer_tree.h" #include "filter.h" #include "error.h" @@ -296,18 +295,10 @@ static void flacdec_open(struct filter_node *fn) fn->min_iqs = 0; } -/** - * The init function of the flacdec filter. - * - * \param f Pointer to the filter struct to initialize. - * - * \sa filter::init. - */ -void flacdec_filter_init(struct filter *f) -{ - f->open = flacdec_open; - f->close = flacdec_close; - f->pre_select = flacdec_pre_select; - f->post_select = flacdec_post_select; - f->execute = flacdec_execute; -} +const struct filter lsg_filter_cmd_com_flacdec_user_data = { + .open = flacdec_open, + .close = flacdec_close, + .pre_select = flacdec_pre_select, + .post_select = flacdec_post_select, + .execute = flacdec_execute, +}; diff --git a/m4/gengetopt/amp_filter.m4 b/m4/gengetopt/amp_filter.m4 deleted file mode 100644 index a02cc5b7..00000000 --- a/m4/gengetopt/amp_filter.m4 +++ /dev/null @@ -1,21 +0,0 @@ -args "--no-version --no-help" - -purpose "Amplify the decoded audio stream" - -option "amp" a -#~~~~~~~~~~~~~ -"amplification value" -int typestr="number" -default="32" -optional -details=" - The amplification value determines the scaling factor by - which the amplitude of the audio stream is multiplied. The - formula for the scaling factor is - - factor = 1 + amp / 64. - - For example, an amplifiction value of zero results in a - scaling factor of one while an amplification value of 64 - means to double the volume. -" diff --git a/m4/gengetopt/compress_filter.m4 b/m4/gengetopt/compress_filter.m4 deleted file mode 100644 index 501f46cc..00000000 --- a/m4/gengetopt/compress_filter.m4 +++ /dev/null @@ -1,41 +0,0 @@ -args "--no-version --no-help" - -purpose "Dynamically adjust the volume of an audio stream" - -option "blocksize" b -#~~~~~~~~~~~~~~~~~~~ -"adjust block size" -int typestr="number" -default="15" -optional -details = " - Larger blocksize means fewer volume adjustments per time unit. -" - -option "aggressiveness" a -#~~~~~~~~~~~~~~~~~~~~~~~~ - "controls the maximum amount to amplify by" -int typestr="number" -default="4" -optional - -option "inertia" i -#~~~~~~~~~~~~~~~~~ - "how much inertia ramping has" - int typestr="number" -default="6" -optional - -option "target-level" t -#~~~~~~~~~~~~~~~~~~~~~~ -"target signal level (0-32768)" -int typestr="number" -default="20000" -optional - -option "damp" d -#~~~~~~~~~~~~~~ -"if non-zero, scale down after normalizing" -int typestr="number" -default="0" -optional diff --git a/m4/gengetopt/mp3dec_filter.m4 b/m4/gengetopt/mp3dec_filter.m4 deleted file mode 100644 index 8b187835..00000000 --- a/m4/gengetopt/mp3dec_filter.m4 +++ /dev/null @@ -1,18 +0,0 @@ -args "--no-version --no-help" - -purpose "Decode an mp3 stream" - -include(header.m4) - - -option "ignore-crc" i -#~~~~~~~~~~~~~~~~~~~~ -"ignore CRC information in the audio stream." -flag off -details=" - This causes frames with CRC errors to be decoded and played - anyway. This option is not recommended, but since some encoders - have been known to generate bad CRC information, this option - is a work-around to play streams from such encoders. -" - diff --git a/m4/gengetopt/prebuffer_filter.m4 b/m4/gengetopt/prebuffer_filter.m4 deleted file mode 100644 index 20ff86fe..00000000 --- a/m4/gengetopt/prebuffer_filter.m4 +++ /dev/null @@ -1,28 +0,0 @@ -args "--no-version --no-help" - -purpose "Delay processing of an audio stream" - -option "duration" d -#~~~~~~~~~~~~~~~~~~ -"prebuffer time" -int typestr="milliseconds" -default="200" -optional -details=" - Wait that many milliseconds before letting data go through. - The time interval starts when the first data byte is seen by - this filter. -" - -option "size" s -#~~~~~~~~~~~~~~ -"amount of data to prebuffer" -int typestr="bytes" -default="0" -optional -details=" - Wait until that many data bytes are available in the input buffer. - The default value of zero means to not prebuffer by size at all. - If both --duration and --size options are given and non-zero, the - filter waits until both conditions are met. -" diff --git a/m4/gengetopt/resample_filter.m4 b/m4/gengetopt/resample_filter.m4 deleted file mode 100644 index 2e5f4005..00000000 --- a/m4/gengetopt/resample_filter.m4 +++ /dev/null @@ -1,45 +0,0 @@ -args "--no-version --no-help" - -purpose "Transform raw audio to a different sample rate" - -include(header.m4) - -option "converter" C -#~~~~~~~~~~~~~~~~~~~ -"choose converter type" -enum typestr = "type" -values = "best", "medium", "fastest", "zero_order_hold", "linear" -default = "medium" -optional - -details = " - best: This is a bandlimited interpolator derived from the - mathematical sinc function and this is the highest quality - sinc based converter, providing a worst case Signal-to-Noise - Ratio (SNR) of 97 decibels (dB) at a bandwidth of 97%. - - medium: This is another bandlimited interpolator much like the - previous one. It has an SNR of 97dB and a bandwidth of 90%. The - speed of the conversion is much faster than the previous one. - - fastest: This is the fastest bandlimited interpolator and - has an SNR of 97dB and a bandwidth of 80%. - - zero_order_hold: A Zero Order Hold converter (interpolated - value is equal to the last value). The quality is poor but - the conversion speed is blindlingly fast. - - linear: A linear converter. Again the quality is poor, but - the conversion speed is blindingly fast. -" - -include(channels.m4) -include(sample_rate.m4) -include(sample_format.m4) - -option "dest-sample-rate" d -#~~~~~~~~~~~~~~~~~~~~~~~~~~ -"sample rate to convert to" -int typestr = "rate" -default = "44100" -optional diff --git a/m4/gengetopt/sync_filter.m4 b/m4/gengetopt/sync_filter.m4 deleted file mode 100644 index 1e6f5f82..00000000 --- a/m4/gengetopt/sync_filter.m4 +++ /dev/null @@ -1,45 +0,0 @@ -args "--no-version --no-help" - -purpose "Synchronize playback between multiple clients." - -option "buddy" b -#~~~~~~~~~~~~~~~ -"host to synchronize with" -multiple -string typestr = "url" -optional -details = " - This option may be given multiple times, one per buddy. Each - value may be given as a host, port pair in either IPv4 or - IPv6 form, with port being optional. If no port was specified - the listening port (as specified with --port, see below) - is used to send the synchronization packet to this buddy. -" - -option "port" p -#~~~~~~~~~~~~~~ -"UDP port for incoming synchronization packets" -int typestr = "portnumber" -default = "29900" -optional -details = " - The sync filter receives incoming synchronization packets on - this UDP port. -" - -option "timeout" t -#~~~~~~~~~~~~~~~~~ -"how long to wait for other clients" -int typestr = "milliseconds" -default = "2000" -optional -details = " - Once the sync filter receives its first chunk of input, a - synchronization period of the given number of milliseconds - begins. Playback is deferred until a synchronization packet - has been received from each defined buddy, or until the end - of the period. Buddies which did not send a synchronization - packet in time are temporarily disabled and are not waited for - during subsequent synchronization periods. They are re-enabled - automatically when another synchronization packet arrives. -" diff --git a/m4/lls/filter_cmd.suite.m4 b/m4/lls/filter_cmd.suite.m4 new file mode 100644 index 00000000..4dda0bf9 --- /dev/null +++ b/m4/lls/filter_cmd.suite.m4 @@ -0,0 +1,197 @@ +[suite filter_cmd] +caption = filters +[subcommand aacdec] + purpose = decode an aac stream +[subcommand amp] + purpose = amplify (scale) a raw audio stream + [option amp] + short_opt = a + summary = amplification value + typestr = number + arg_info = required_arg + arg_type = uint32 + default_val = 32 + [help] + The amplification value determines the scaling factor by which the + amplitude of the audio stream is multiplied. The formula for the + scaling factor is + + factor = 1 + amp / 64. + + For example, an amplification value of zero results in a scaling factor + of one while an amplification value of 64 means to double the volume. + [/help] +[subcommand compress] + purpose = dynamically adjust the volume of an audio stream + [option blocksize] + short_opt = b + summary = use blocks of size 2**bits + typestr = bits + arg_info = required_arg + arg_type = uint32 + default_val = 15 + [help] + Larger blocksize means fewer volume adjustments per time unit. + [/help] + [option aggressiveness] + short_opt = a + summary = controls the maximum amount to amplify by + typestr = bits + arg_info = required_arg + arg_type = uint32 + default_val = 4 + [option inertia] + short_opt = i + summary = how much inertia ramping has + typestr = bits + arg_info = required_arg + arg_type = uint32 + default_val = 6 + [option target-level] + short_opt = t + summary = target signal level (0-32768) + typestr = level + arg_info = required_arg + arg_type = uint32 + default_val = 20000 + [option damp] + short_opt = d + summary = if non-zero, scale down after normalizing + typestr = bits + arg_info = required_arg + arg_type = uint32 + default_val = 0 +[subcommand fecdec] + purpose = decode a (lossy) input stream using forward error correction +[subcommand flacdec] + purpose = decode a flac stream +[subcommand mp3dec] + purpose = decode an mp3 stream + [option ignore-crc] + short_opt = i + summary = ignore CRC information in the audio stream + [help] + This causes frames with CRC errors to be decoded and played + anyway. This option is not recommended, but since some encoders + have been known to generate bad CRC information, this option is a + work-around to play streams from such encoders. + [/help] +[subcommand oggdec] + purpose = decode an ogg/vorbis stream +[subcommand opusdec] + purpose = decode an ogg/opus stream +[subcommand prebuffer] + purpose = delay processing of an audio stream + [option duration] + short_opt = d + summary = length of the prebuffer period + typestr = milliseconds + arg_info = required_arg + arg_type = uint32 + default_val = 200 + [help] + Wait this many milliseconds before letting data go through. The time + interval starts when the first data byte is seen in the input queue. + [/help] + [option size] + short_opt = s + summary = amount of data to prebuffer + typestr = bytes + arg_info = required_arg + arg_type = uint32 + default_val = 0 + [help] + Wait until this many data bytes are available in the input queue. The + default value of zero means to not prebuffer by size. If both + --duration and --size are given and non-zero, the prebuffer filter + waits until both conditions are met. + [/help] +[subcommand resample] + purpose = transform raw audio to a different sample rate + [option converter] + short_opt = C + summary = set conversion algorithm + typestr = type + arg_info = required_arg + arg_type = string + default_val = medium + [help] + best: This is a bandlimited interpolator derived from the mathematical + sinc function and this is the highest quality sinc based converter, + providing a worst case Signal-to-Noise Ratio (SNR) of 97 decibels + (dB) at a bandwidth of 97%. + + medium: This is another bandlimited interpolator much like the previous + one. It has an SNR of 97dB and a bandwidth of 90%. The speed of the + conversion is much faster than the previous one. + + fastest: This is the fastest bandlimited interpolator and has an SNR + of 97dB and a bandwidth of 80%. + + zero_order_hold: A Zero Order Hold converter (interpolated value + is equal to the last value). The quality is poor but the conversion + speed is blindlingly fast. + + linear: A linear converter. Again the quality is poor, but the + conversion speed is blindingly fast. + [/help] + [option dest-sample-rate] + short_opt = d + summary = sample rate to convert to + typestr = rate + arg_info = required_arg + arg_type = uint32 + default_val = 44100 + m4_include(channels.m4) + m4_include(sample-rate.m4) + m4_include(sample-format.m4) +[subcommand spxdec] + purpose = decode an ogg/speex stream +[subcommand sync] + purpose = synchronize playback between multiple clients + [option buddy] + short_opt = b + summary = client to synchronize with + typestr = url + arg_info = required_arg + arg_type = string + flag multiple + [help] + This option may be given multiple times, one per buddy. Each value + may be given as a host, port pair in either IPv4 or IPv6 form, with + port being optional. If no port was specified the listening port (as + specified with --port, see below) is used to send the synchronization + packet to this buddy. + [/help] + [option port] + short_opt = p + summary = UDP port for incoming synchronization packets + typestr = portnumber + arg_info = required_arg + arg_type = uint32 + default_val = 29900 + [help] + The sync filter expects incoming synchronization packets on this + UDP port. + [/help] + [option timeout] + short_opt = t + summary = how long to wait for other clients + typestr = milliseconds + arg_info = required_arg + arg_type = uint32 + default_val = 2000 + [help] + Once the sync filter receives its first chunk of input, + a synchronization period of the given number of milliseconds + begins. Playback is deferred until a synchronization packet has + been received from each defined buddy, or until the end of the + period. Buddies which did not send a synchronization packet in time + are temporarily disabled and are not waited for during subsequent + synchronization periods. They are re-enabled automatically when + another synchronization packet arrives. + [/help] +[subcommand wav] + purpose = insert a Microsoft wave header into a raw audio stream +[subcommand wmadec] + purpose = decode a wma stream diff --git a/m4/lls/include/channels.m4 b/m4/lls/include/channels.m4 new file mode 100644 index 00000000..ae26b735 --- /dev/null +++ b/m4/lls/include/channels.m4 @@ -0,0 +1,13 @@ +[option channels] + short_opt = c + summary = specify number of channels + typestr = num + arg_info = required_arg + arg_type = uint32 + default_val = 2 + [help] + It is only necessary to specify this option for raw audio. If it is + not given, the channel count is queried from the parent buffer tree + nodes (e.g. the decoder) or the wav header. Only if this query fails, + the default value applies. + [/help] diff --git a/m4/lls/include/sample-format.m4 b/m4/lls/include/sample-format.m4 new file mode 100644 index 00000000..0daad192 --- /dev/null +++ b/m4/lls/include/sample-format.m4 @@ -0,0 +1,20 @@ +[option sample-format] + short_opt = f + summary = specify sample format + typestr = format + arg_info = required_arg + arg_type = string + # TODO: dedup this from enum in para.h. + values = { + SAMPLE_FORMAT_S8 = "S8", + SAMPLE_FORMAT_U8 = "U8", + SAMPLE_FORMAT_S16_LE = "S16_LE", + SAMPLE_FORMAT_S16_BE = "S16_BE", + SAMPLE_FORMAT_U16_LE = "U16_LE", + SAMPLE_FORMAT_U16_BE = "U16_BE" + } + default_val = S16_LE + [help] + It is only necessary to specify this for raw audio. See the discussion + of the --channels option. + [/help] diff --git a/m4/lls/include/sample-rate.m4 b/m4/lls/include/sample-rate.m4 new file mode 100644 index 00000000..1433521b --- /dev/null +++ b/m4/lls/include/sample-rate.m4 @@ -0,0 +1,11 @@ +[option sample-rate] + short_opt = s + summary = do not guess the input sample rate + typestr = rate + arg_info = required_arg + arg_type = uint32 + default_val = 44100 + [help] + It is only necessary to specify this for raw audio. See the discussion + of the --channels option. + [/help] diff --git a/man_util.bash b/man_util.bash index 38ed86c8..2a12737e 100755 --- a/man_util.bash +++ b/man_util.bash @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# filters, writers are called "modules" in this script +# writers are called "modules" in this script print_modhelp() { local ggo="$1" @@ -39,14 +39,6 @@ make_help() --set-package "para_$1" \ < "$ggo" - if [[ "$target" == 'filter' || "$target" == 'audiod' ]]; then - for module in $FILTERS; do - ggo="$GGO_DIR/${module}_filter.ggo" - [[ ! -f "$ggo" ]] && continue - printf "\nOptions for the $module filter" - print_modhelp "$ggo" - done - fi if [[ "$target" == 'write' || "$target" == 'audiod' ]]; then for module in $WRITERS; do ggo="$GGO_DIR/${module}_write.ggo" diff --git a/mp3dec_filter.c b/mp3dec_filter.c index 5f6935d1..b053aaa8 100644 --- a/mp3dec_filter.c +++ b/mp3dec_filter.c @@ -8,12 +8,12 @@ #include #include +#include +#include "filter_cmd.lsg.h" #include "para.h" -#include "mp3dec_filter.cmdline.h" #include "list.h" #include "sched.h" -#include "ggo.h" #include "buffer_tree.h" #include "filter.h" #include "error.h" @@ -171,25 +171,15 @@ err: static void mp3dec_open(struct filter_node *fn) { struct private_mp3dec_data *pmd = para_calloc(sizeof(*pmd)); - struct mp3dec_filter_args_info *mp3_conf = fn->conf; fn->private_data = pmd; mad_stream_init(&pmd->stream); mad_frame_init(&pmd->frame); mad_synth_init(&pmd->synth); - if (mp3_conf->ignore_crc_given) + if (FILTER_CMD_OPT_GIVEN(MP3DEC, IGNORE_CRC, fn->lpr)) mad_stream_options(&pmd->stream, MAD_OPTION_IGNORECRC); } -static int mp3dec_parse_config(int argc, char **argv, void **config) -{ - struct mp3dec_filter_args_info *conf = para_calloc(sizeof(*conf)); - - mp3dec_filter_cmdline_parser(argc, argv, conf); - *config = conf; - return 1; -} - static int mp3dec_execute(struct btr_node *btrn, const char *cmd, char **result) { struct filter_node *fn = btr_context(btrn); @@ -198,28 +188,10 @@ static int mp3dec_execute(struct btr_node *btrn, const char *cmd, char **result) return decoder_execute(cmd, pmd->sample_rate, pmd->channels, result); } -static void mp3dec_free_config(void *conf) -{ - mp3dec_filter_cmdline_parser_free(conf); -} -/** - * The init function of the mp3dec filter. - * - * \param f Pointer to the filter struct to initialize. - * - * \sa filter::init. - */ -void mp3dec_filter_init(struct filter *f) -{ - struct mp3dec_filter_args_info dummy; - - mp3dec_filter_cmdline_parser_init(&dummy); - f->open = mp3dec_open; - f->close = mp3dec_close; - f->parse_config = mp3dec_parse_config; - f->free_config = mp3dec_free_config; - f->pre_select = generic_filter_pre_select; - f->post_select = mp3dec_post_select; - f->execute = mp3dec_execute; - f->help = (struct ggo_help)DEFINE_GGO_HELP(mp3dec_filter); -} +const struct filter lsg_filter_cmd_com_mp3dec_user_data = { + .open = mp3dec_open, + .close = mp3dec_close, + .pre_select = generic_filter_pre_select, + .post_select = mp3dec_post_select, + .execute = mp3dec_execute, +}; diff --git a/oggdec_filter.c b/oggdec_filter.c index 04be7020..9fa80c55 100644 --- a/oggdec_filter.c +++ b/oggdec_filter.c @@ -12,7 +12,6 @@ #include "para.h" #include "list.h" #include "sched.h" -#include "ggo.h" #include "buffer_tree.h" #include "filter.h" #include "error.h" @@ -264,16 +263,10 @@ out: return ret; } -/** - * The init function of the ogg vorbis decoder. - * - * \param f Its fields are filled in by the function. - */ -void oggdec_filter_init(struct filter *f) -{ - f->open = ogg_open; - f->close = ogg_close; - f->pre_select = ogg_pre_select; - f->post_select = ogg_post_select; - f->execute = oggdec_execute; -} +const struct filter lsg_filter_cmd_com_oggdec_user_data = { + .open = ogg_open, + .close = ogg_close, + .pre_select = ogg_pre_select, + .post_select = ogg_post_select, + .execute = oggdec_execute +}; diff --git a/opusdec_filter.c b/opusdec_filter.c index 28222985..6f9d526c 100644 --- a/opusdec_filter.c +++ b/opusdec_filter.c @@ -50,7 +50,6 @@ #include "para.h" #include "list.h" #include "sched.h" -#include "ggo.h" #include "buffer_tree.h" #include "filter.h" #include "error.h" @@ -284,18 +283,10 @@ static void opusdec_pre_select(struct sched *s, void *context) return sched_min_delay(s); } -/** - * The init function of the opusdec filter. - * - * \param f Pointer to the filter struct to initialize. - * - * \sa filter::init. - */ -void opusdec_filter_init(struct filter *f) -{ - f->open = opusdec_open; - f->close = opusdec_close; - f->pre_select = opusdec_pre_select; - f->post_select = opusdec_post_select; - f->execute = opusdec_execute; -} +const struct filter lsg_filter_cmd_com_opusdec_user_data = { + .open = opusdec_open, + .close = opusdec_close, + .pre_select = opusdec_pre_select, + .post_select = opusdec_post_select, + .execute = opusdec_execute, +}; diff --git a/play.c b/play.c index 330c8b21..ca584b99 100644 --- a/play.c +++ b/play.c @@ -296,7 +296,7 @@ static int get_playback_error(struct play_task *pt) static int eof_cleanup(struct play_task *pt) { struct writer *w = writers + DEFAULT_WRITER; - const struct filter *decoder = filter_get(pt->fn.filter_num); + const struct filter *decoder; int ret; ret = get_playback_error(pt); @@ -309,6 +309,7 @@ static int eof_cleanup(struct play_task *pt) w->free_config(pt->wn.conf); memset(&pt->wn, 0, sizeof(struct writer_node)); + decoder = filter_get(pt->fn.filter_num); task_reap(&pt->fn.task); if (decoder->close) decoder->close(&pt->fn); @@ -410,6 +411,7 @@ static int load_file(struct play_task *pt) char *tmp, buf[20]; int ret; const struct filter *decoder; + static struct lls_parse_result *filter_lpr; btr_remove_node(&pt->rn.btrn); if (!pt->rn.receiver || pt->next_file != pt->current_file) { @@ -429,16 +431,17 @@ static int load_file(struct play_task *pt) /* set up decoding filter */ af = audio_format_name(pt->audio_format_num); tmp = make_message("%sdec", af); - PARA_INFO_LOG("decoder: %s\n", tmp); - ret = check_filter_arg(tmp, &pt->fn.conf); + ret = filter_setup(tmp, &pt->fn.conf, &filter_lpr); freep(&tmp); if (ret < 0) goto fail; pt->fn.filter_num = ret; + pt->fn.lpr = filter_lpr; decoder = filter_get(ret); pt->fn.btrn = btr_new_node(&(struct btr_node_description) - EMBRACE(.name = decoder->name, .parent = pt->rn.btrn, - .handler = decoder->execute, .context = &pt->fn)); + EMBRACE(.name = filter_name(pt->fn.filter_num), + .parent = pt->rn.btrn, .handler = decoder->execute, + .context = &pt->fn)); if (decoder->open) decoder->open(&pt->fn); PARA_INFO_LOG("buffer tree:\n"); @@ -1313,7 +1316,6 @@ int main(int argc, char *argv[]) /* needed this early to make help work */ recv_init(); - filter_init(); writer_init(); sched.default_timeout.tv_sec = 5; diff --git a/prebuffer_filter.c b/prebuffer_filter.c index 6078da07..8ca1630d 100644 --- a/prebuffer_filter.c +++ b/prebuffer_filter.c @@ -7,12 +7,12 @@ /** \file prebuffer_filter.c Paraslash's prebuffering filter. */ #include +#include #include "para.h" -#include "prebuffer_filter.cmdline.h" +#include "filter_cmd.lsg.h" #include "list.h" #include "sched.h" -#include "ggo.h" #include "buffer_tree.h" #include "filter.h" #include "string.h" @@ -20,8 +20,6 @@ /** Data specific to the prebuffer filter. */ struct private_prebuffer_data { - /** The configuration data for this instance of the filter. */ - struct prebuffer_filter_args_info *conf; /** Number of bytes prebuffered or -1 if no longer prebuffering. */ int prebuffered; /** End of prebuffering period. */ @@ -34,16 +32,16 @@ static void prebuffer_pre_select(struct sched *s, void *context) struct btr_node *btrn = fn->btrn; size_t iqs = btr_get_input_queue_size(btrn); struct private_prebuffer_data *ppd = fn->private_data; - struct prebuffer_filter_args_info *conf = ppd->conf; struct timeval diff; if (iqs == 0) return; if (ppd->barrier.tv_sec == 0) { + uint32_t duration = FILTER_CMD_OPT_UINT32_VAL(PREBUFFER, + DURATION, fn->lpr); struct timeval tv; - PARA_INFO_LOG("prebuffer period %dms\n", - conf->duration_arg); - ms2tv(conf->duration_arg, &tv); + PARA_INFO_LOG("prebuffer period %" PRIu32 "ms\n", duration); + ms2tv(duration, &tv); tv_add(&tv, now, &ppd->barrier); } if (tv_diff(&ppd->barrier, now, &diff) < 0) @@ -62,66 +60,27 @@ static int prebuffer_post_select(__a_unused struct sched *s, void *context) struct btr_node *btrn = fn->btrn; size_t iqs = btr_get_input_queue_size(btrn); struct private_prebuffer_data *ppd = fn->private_data; - struct prebuffer_filter_args_info *conf = ppd->conf; + uint32_t size = FILTER_CMD_OPT_UINT32_VAL(PREBUFFER, SIZE, fn->lpr); if (ppd->barrier.tv_sec == 0) return 0; if (tv_diff(now, &ppd->barrier, NULL) < 0) return 0; - if (iqs < conf->size_arg) + if (iqs < size) return 0; btr_splice_out_node(&fn->btrn); return -E_PREBUFFER_SUCCESS; } -static int prebuffer_parse_config(int argc, char **argv, void **config) -{ - struct prebuffer_filter_args_info *conf = para_calloc(sizeof(*conf)); - int ret; - - prebuffer_filter_cmdline_parser(argc, argv, conf); - ret = -ERRNO_TO_PARA_ERROR(EINVAL); - if (conf->duration_arg < 0) - goto err; - if (conf->size_arg < 0) - goto err; - PARA_NOTICE_LOG("prebuffering %ims, %i bytes\n", conf->duration_arg, - conf->size_arg); - *config = conf; - return 1; -err: - free(conf); - return ret; -} - static void prebuffer_open(struct filter_node *fn) { struct private_prebuffer_data *ppd = para_calloc(sizeof(*ppd)); - - ppd->conf = fn->conf; fn->private_data = ppd; } -static void prebuffer_free_config(void *conf) -{ - prebuffer_filter_cmdline_parser_free(conf); -} - -/** - * The init function of the prebuffer filter. - * - * \param f Pointer to the struct to initialize. - */ -void prebuffer_filter_init(struct filter *f) -{ - struct prebuffer_filter_args_info dummy; - - prebuffer_filter_cmdline_parser_init(&dummy); - f->open = prebuffer_open; - f->close = prebuffer_close; - f->parse_config = prebuffer_parse_config; - f->free_config = prebuffer_free_config; - f->pre_select = prebuffer_pre_select; - f->post_select = prebuffer_post_select; - f->help = (struct ggo_help)DEFINE_GGO_HELP(prebuffer_filter); -} +const struct filter lsg_filter_cmd_com_prebuffer_user_data = { + .open = prebuffer_open, + .close = prebuffer_close, + .pre_select = prebuffer_pre_select, + .post_select = prebuffer_post_select, +}; diff --git a/resample_filter.c b/resample_filter.c index 1699ed2c..e630596a 100644 --- a/resample_filter.c +++ b/resample_filter.c @@ -8,20 +8,24 @@ #include #include +#include -#include "resample_filter.cmdline.h" +#include "filter_cmd.lsg.h" #include "para.h" #include "error.h" #include "list.h" #include "sched.h" -#include "ggo.h" #include "buffer_tree.h" #include "filter.h" #include "string.h" #include "check_wav.h" +#define U32_OPTVAL(_opt, _lpr) (FILTER_CMD_OPT_UINT32_VAL(RESAMPLE, _opt, _lpr)) +#define OPT_GIVEN(_opt, _lpr) (FILTER_CMD_OPT_GIVEN(RESAMPLE, _opt, _lpr)) + +/* effective values, may differ from config arg */ struct resample_context { - int channels; + uint32_t channels; int source_sample_rate; float ratio; SRC_STATE *src_state; @@ -32,10 +36,8 @@ static int resample_execute(struct btr_node *btrn, const char *cmd, char **resul { struct filter_node *fn = btr_context(btrn); struct resample_context *ctx = fn->private_data; - struct resample_filter_args_info *conf = fn->conf; - - return decoder_execute(cmd, conf->dest_sample_rate_arg, ctx->channels, - result); + uint32_t dsr = U32_OPTVAL(DEST_SAMPLE_RATE, fn->lpr); + return decoder_execute(cmd, dsr, ctx->channels, result); } static void resample_close(struct filter_node *fn) @@ -54,13 +56,12 @@ static void resample_close(struct filter_node *fn) static void resample_open(struct filter_node *fn) { struct resample_context *ctx = para_calloc(sizeof(*ctx)); - struct resample_filter_args_info *conf = fn->conf; struct btr_node *btrn = fn->btrn; struct wav_params wp; fn->private_data = ctx; fn->min_iqs = 2; - COPY_WAV_PARMS(&wp, conf); + LLS_COPY_WAV_PARMS(&wp, LSG_FILTER_CMD_RESAMPLE, fn->lpr); ctx->cwc = check_wav_init(btr_parent(btrn), btrn, &wp, NULL); btr_log_tree(btr_parent(btr_parent(btrn)), LL_INFO); } @@ -95,18 +96,17 @@ static int resample_set_params(struct filter_node *fn) { int ret; struct resample_context *ctx = fn->private_data; - struct resample_filter_args_info *conf = fn->conf; struct btr_node *btrn = fn->btrn; + struct lls_parse_result *lpr = fn->lpr; - ctx->channels = conf->channels_arg; - if (!conf->channels_given) { + ctx->channels = U32_OPTVAL(CHANNELS, lpr); + if (!OPT_GIVEN(CHANNELS, lpr)) { ret = get_btr_val("channels", btrn); if (ret >= 0) ctx->channels = ret; } - - ctx->source_sample_rate = conf->sample_rate_arg; - if (!conf->sample_rate_given) { + ctx->source_sample_rate = U32_OPTVAL(SAMPLE_RATE, lpr); + if (!OPT_GIVEN(SAMPLE_RATE, lpr)) { ret = get_btr_val("sample_rate", btrn); if (ret >= 0) ctx->source_sample_rate = ret; @@ -114,44 +114,25 @@ static int resample_set_params(struct filter_node *fn) /* reject all sample formats except 16 bit signed, little endian */ ret = get_btr_val("sample_format", btrn); if (ret >= 0 && ret != SF_S16_LE) { - const char *sample_formats[] = {SAMPLE_FORMATS}; + const char * const sample_formats[] = {SAMPLE_FORMATS}; PARA_ERROR_LOG("unsupported sample format: %s\n", sample_formats[ret]); return -ERRNO_TO_PARA_ERROR(EINVAL); } - ctx->ratio = (float)conf->dest_sample_rate_arg / ctx->source_sample_rate; + ctx->ratio = U32_OPTVAL(DEST_SAMPLE_RATE, lpr) + / (float)ctx->source_sample_rate; return 1; } static int resample_init(struct filter_node *fn) { - int ret, converter; + int ret, converter = *(int *)fn->conf; struct resample_context *ctx = fn->private_data; - struct resample_filter_args_info *conf = fn->conf; ret = resample_set_params(fn); if (ret < 0) return ret; - switch (conf->converter_arg) { - case converter_arg_best: - converter = SRC_SINC_BEST_QUALITY; - break; - case converter_arg_medium: - converter = SRC_SINC_MEDIUM_QUALITY; - break; - case converter_arg_fastest: - converter = SRC_SINC_FASTEST; - break; - case converter_arg_zero_order_hold: - converter = SRC_ZERO_ORDER_HOLD; - break; - case converter_arg_linear: - converter = SRC_LINEAR; - break; - default: - assert(0); - } - ctx->src_state = src_new(converter, conf->channels_arg, &ret); + ctx->src_state = src_new(converter, U32_OPTVAL(CHANNELS, fn->lpr), &ret); if (!ctx->src_state) { PARA_ERROR_LOG("%s\n", src_strerror(ret)); return -E_LIBSAMPLERATE; @@ -201,7 +182,6 @@ static int resample_post_select(__a_unused struct sched *s, void *context) int ret; struct filter_node *fn = context; struct resample_context *ctx = fn->private_data; - struct resample_filter_args_info *conf = fn->conf; struct btr_node *btrn = fn->btrn; int16_t *in, *out; size_t in_bytes, num_frames; @@ -218,7 +198,7 @@ static int resample_post_select(__a_unused struct sched *s, void *context) if (ret <= 0) goto out; } - if (ctx->source_sample_rate == conf->dest_sample_rate_arg) { + if (ctx->source_sample_rate == U32_OPTVAL(DEST_SAMPLE_RATE, fn->lpr)) { /* * No resampling necessary. We do not splice ourselves out * though, since our children might want to ask us through the @@ -251,58 +231,62 @@ out: return ret; } -static int resample_parse_config(int argc, char **argv, void **config) +static void *resample_setup(const struct lls_parse_result *lpr) { - int ret, val, given; - struct resample_filter_args_info *conf = para_calloc(sizeof(*conf)); - - resample_filter_cmdline_parser(argc, argv, conf); + int given, *converter; + const char *converter_arg; + uint32_t u32; /* sanity checks */ - ret = -ERRNO_TO_PARA_ERROR(EINVAL); - val = conf->channels_arg; - given = conf->channels_given; - if (val < 0 || (val == 0 && given)) - goto err; - val = conf->sample_rate_arg; - given = conf->sample_rate_given; - if (val < 0 || (val == 0 && given)) - goto err; - val = conf->dest_sample_rate_arg; - given = conf->dest_sample_rate_given; - if (val < 0 || (val == 0 && given)) - goto err; - *config = conf; - return 1; -err: - free(conf); - return ret; + u32 = U32_OPTVAL(CHANNELS, lpr); + given = OPT_GIVEN(CHANNELS, lpr); + if (u32 == 0 && given) { + PARA_EMERG_LOG("fatal: zero channels?!\n"); + exit(EXIT_FAILURE); + } + u32 = U32_OPTVAL(SAMPLE_RATE, lpr); + given = OPT_GIVEN(SAMPLE_RATE, lpr); + if (u32 == 0 && given) { + PARA_EMERG_LOG("fatal: input sample rate can not be 0\n"); + exit(EXIT_FAILURE); + } + u32 = U32_OPTVAL(DEST_SAMPLE_RATE, lpr); + given = OPT_GIVEN(DEST_SAMPLE_RATE, lpr); + if (u32 == 0 && given) { + PARA_EMERG_LOG("fatal: destination sample rate can not be 0\n"); + exit(EXIT_FAILURE); + } + converter = para_malloc(sizeof(int)); + converter_arg = FILTER_CMD_OPT_STRING_VAL(RESAMPLE, CONVERTER, lpr); + if (!strcmp(converter_arg, "best")) + *converter = SRC_SINC_BEST_QUALITY; + else if (!strcmp(converter_arg, "medium")) + *converter = SRC_SINC_MEDIUM_QUALITY; + else if (!strcmp(converter_arg, "fastest")) + *converter = SRC_SINC_FASTEST; + else if (!strcmp(converter_arg, "zero_order_hold")) + *converter = SRC_ZERO_ORDER_HOLD; + else if (!strcmp(converter_arg, "linear")) + *converter = SRC_LINEAR; + else { + PARA_EMERG_LOG("invalid converter type: %s\n", converter_arg); + exit(EXIT_FAILURE); + } + return converter; } -static void resample_free_config(void *conf) +static void resample_teardown(__a_unused const struct lls_parse_result *lpr, + void *conf) { - if (!conf) - return; - resample_filter_cmdline_parser_free(conf); free(conf); } -/** - * The init function of the resample filter. - * - * \param f Structure to initialize. - */ -void resample_filter_init(struct filter *f) -{ - struct resample_filter_args_info dummy; - - resample_filter_cmdline_parser_init(&dummy); - f->close = resample_close; - f->open = resample_open; - f->pre_select = resample_pre_select; - f->post_select = resample_post_select; - f->parse_config = resample_parse_config; - f->free_config = resample_free_config; - f->execute = resample_execute; - f->help = (struct ggo_help)DEFINE_GGO_HELP(resample_filter); -} +const struct filter lsg_filter_cmd_com_resample_user_data = { + .setup = resample_setup, + .open = resample_open, + .pre_select = resample_pre_select, + .post_select = resample_post_select, + .close = resample_close, + .teardown = resample_teardown, + .execute = resample_execute +}; diff --git a/spxdec_filter.c b/spxdec_filter.c index 644d287a..3266d090 100644 --- a/spxdec_filter.c +++ b/spxdec_filter.c @@ -50,7 +50,6 @@ #include "para.h" #include "list.h" #include "sched.h" -#include "ggo.h" #include "buffer_tree.h" #include "filter.h" #include "error.h" @@ -308,16 +307,10 @@ fail: return ret; } -/** - * The init function of the ogg/speex decoder. - * - * \param f Its fields are filled in by the function. - */ -void spxdec_filter_init(struct filter *f) -{ - f->open = spxdec_open; - f->close = speexdec_close; - f->pre_select = generic_filter_pre_select; - f->post_select = speexdec_post_select; - f->execute = speexdec_execute; -} +const struct filter lsg_filter_cmd_com_spxdec_user_data = { + .open = spxdec_open, + .close = speexdec_close, + .pre_select = generic_filter_pre_select, + .post_select = speexdec_post_select, + .execute = speexdec_execute, +}; diff --git a/sync_filter.c b/sync_filter.c index 82e86e90..efc10116 100644 --- a/sync_filter.c +++ b/sync_filter.c @@ -13,13 +13,13 @@ #include #include #include +#include +#include "filter_cmd.lsg.h" #include "para.h" -#include "sync_filter.cmdline.h" #include "list.h" #include "net.h" #include "sched.h" -#include "ggo.h" #include "buffer_tree.h" #include "filter.h" #include "string.h" @@ -42,7 +42,7 @@ struct sync_buddy { struct list_head node; }; -/* Allocated in ->open() */ +/* Allocated in ->open(), stored in fn->private_data */ struct sync_filter_context { int listen_fd; struct list_head buddies; @@ -50,12 +50,6 @@ struct sync_filter_context { bool ping_sent; }; -/* Allocated and freed in ->parse_config() and ->free_config(). */ -struct sync_filter_config { - struct sync_filter_args_info *conf; - struct sync_buddy_info *buddy_info; -}; - #define FOR_EACH_BUDDY(_buddy, _list) \ list_for_each_entry(_buddy, _list, node) #define FOR_EACH_BUDDY_SAFE(_buddy, _tmp_buddy, _list) \ @@ -90,59 +84,59 @@ static void sync_close(struct filter_node *fn) fn->private_data = NULL; } -static void sync_free_config(void *conf) +static void sync_teardown(const struct lls_parse_result *lpr, void *conf) { - struct sync_filter_config *sfc = conf; - int i; + struct sync_buddy_info *sbi = conf; + int i, num_buddies = FILTER_CMD_OPT_GIVEN(SYNC, BUDDY, lpr); - for (i = 0; i < sfc->conf->buddy_given; i++) { - free(sfc->buddy_info[i].host); - freeaddrinfo(sfc->buddy_info[i].ai); + for (i = 0; i < num_buddies; i++) { + free(sbi[i].host); + freeaddrinfo(sbi[i].ai); } - sync_filter_cmdline_parser_free(sfc->conf); - free(sfc); + free(sbi); } static void sync_open(struct filter_node *fn) { int i, ret; - struct sync_filter_config *sfc = fn->conf; struct sync_buddy *buddy; struct sync_filter_context *ctx; - - assert(sfc); + struct sync_buddy_info *sbi = fn->conf; + uint32_t port = FILTER_CMD_OPT_UINT32_VAL(SYNC, PORT, fn->lpr); + unsigned buddy_given; + const struct lls_opt_result *r_b; ctx = fn->private_data = para_calloc(sizeof(*ctx)); INIT_LIST_HEAD(&ctx->buddies); - ctx->listen_fd = -1; /* create socket to listen for incoming packets */ ret = makesock( IPPROTO_UDP, true /* passive */, NULL /* no host required */, - sfc->conf->port_arg, + port, NULL /* no flowopts */ ); if (ret < 0) { - PARA_ERROR_LOG("could not create UDP listening socket %d\n", - sfc->conf->port_arg); + PARA_ERROR_LOG("could not create UDP listening socket %u\n", + port); return; } ctx->listen_fd = ret; PARA_INFO_LOG("listening on fd %d\n", ctx->listen_fd); - for (i = 0; i < sfc->conf->buddy_given; i++) { - struct sync_buddy_info *sbi = sfc->buddy_info + i; - const char *url = sfc->conf->buddy_arg[i]; + r_b = FILTER_CMD_OPT_RESULT(SYNC, BUDDY, fn->lpr); + buddy_given = lls_opt_given(r_b); + for (i = 0; i < buddy_given; i++) { int fd; + const char *url = lls_string_val(i, r_b); /* make buddy udp socket from address info */ assert(sbi->ai); ret = makesock_addrinfo( IPPROTO_UDP, false /* not passive */, - sbi->ai, + sbi[i].ai, NULL /* no flowopts */ ); if (ret < 0) { @@ -160,7 +154,7 @@ static void sync_open(struct filter_node *fn) } buddy = para_malloc(sizeof(*buddy)); buddy->fd = fd; - buddy->sbi = sbi; + buddy->sbi = sbi + i; buddy->ping_received = false; para_list_add(&buddy->node, &ctx->buddies); @@ -172,61 +166,50 @@ fail: } /* - * At parse config time, we build an array of struct sync_buddy_info with one - * entry for each buddy given in the arguments. This array is not affected by - * sync_close(), so information stored there can be used for multiple instances - * (para_audiod). We store the resolved url and the ->disabled bit in this - * array. + * Build an array of struct sync_buddy_info with one entry for each buddy given + * in the arguments. This array is not affected by sync_close(), so information + * stored there can be used for multiple instances (para_audiod). We store the + * resolved url and the ->disabled bit in this array. */ -static int sync_parse_config(int argc, char **argv, void **result) +static void *sync_setup(const struct lls_parse_result *lpr) { - int i, ret, n; - struct sync_filter_config *sfc; - struct sync_filter_args_info *conf = para_malloc(sizeof(*conf)); - - sync_filter_cmdline_parser(argc, argv, conf); /* exits on error */ - sfc = para_calloc(sizeof(*sfc)); - sfc->conf = conf; - n = conf->buddy_given; - sfc->buddy_info = para_malloc((n + 1) * sizeof(*sfc->buddy_info)); - PARA_INFO_LOG("initializing buddy info array of length %d\n", n); + int i, ret; + unsigned n; + struct sync_buddy_info *sbi; + const struct lls_opt_result *r_b; + + r_b = FILTER_CMD_OPT_RESULT(SYNC, BUDDY, lpr); + n = lls_opt_given(r_b); + sbi = para_malloc(n * sizeof(*sbi)); + PARA_INFO_LOG("initializing buddy info array of length %u\n", n); for (i = 0; i < n; i++) { - const char *url = conf->buddy_arg[i]; + const char *url = lls_string_val(i, r_b); size_t len = strlen(url); char *host = para_malloc(len + 1); int port; struct addrinfo *ai; - struct sync_buddy_info *sbi = sfc->buddy_info + i; if (!parse_url(url, host, len, &port)) { - free(host); PARA_ERROR_LOG("could not parse url %s\n", url); - ret = -ERRNO_TO_PARA_ERROR(EINVAL); - goto fail; + exit(EXIT_FAILURE); } if (port < 0) - port = conf->port_arg; + port = FILTER_CMD_OPT_UINT32_VAL(SYNC, PORT, lpr); ret = lookup_address(IPPROTO_UDP, false /* not passive */, host, port, &ai); if (ret < 0) { - PARA_ERROR_LOG("host lookup failure for %s\n", url); - free(host); - goto fail; + PARA_ERROR_LOG("host lookup failure for %s: %s\n", + url, para_strerror(-ret)); + exit(EXIT_FAILURE); } - sbi->url = url; - sbi->host = host; - sbi->port = port; - sbi->ai = ai; - sbi->disabled = false; + sbi[i].url = url; + sbi[i].host = host; + sbi[i].port = port; + sbi[i].ai = ai; + sbi[i].disabled = false; PARA_DEBUG_LOG("buddy #%d: %s\n", i, url); } - *result = sfc; - return 1; -fail: - assert(ret < 0); - PARA_ERROR_LOG("%s\n", para_strerror(-ret)); - sync_free_config(sfc); - return ret; + return sbi; } /* @@ -260,11 +243,12 @@ static void sync_disable_active_buddies(struct sync_filter_context *ctx) } static void sync_set_timeout(struct sync_filter_context *ctx, - struct sync_filter_config *sfc) + struct lls_parse_result *lpr) { + uint32_t ms = FILTER_CMD_OPT_UINT32_VAL(SYNC, TIMEOUT, lpr); struct timeval to; - ms2tv(sfc->conf->timeout_arg, &to); + ms2tv(ms, &to); tv_add(now, &to, &ctx->timeout); } @@ -273,7 +257,6 @@ static void sync_pre_select(struct sched *s, void *context) int ret; struct filter_node *fn = context; struct sync_filter_context *ctx = fn->private_data; - struct sync_filter_config *sfc = fn->conf; if (list_empty(&ctx->buddies)) return sched_min_delay(s); @@ -286,7 +269,7 @@ static void sync_pre_select(struct sched *s, void *context) if (ret == 0) return; if (ctx->timeout.tv_sec == 0) { /* must ping buddies */ - sync_set_timeout(ctx, sfc); + sync_set_timeout(ctx, fn->lpr); return sched_min_delay(s); } if (sync_complete(ctx)) /* push down what we have */ @@ -310,7 +293,6 @@ static int sync_post_select(__a_unused struct sched *s, void *context) int ret; struct filter_node *fn = context; struct sync_filter_context *ctx = fn->private_data; - struct sync_filter_config *sfc = fn->conf; struct sync_buddy *buddy, *tmp; if (list_empty(&ctx->buddies)) @@ -324,7 +306,7 @@ static int sync_post_select(__a_unused struct sched *s, void *context) if (ret == 0) return 0; if (ctx->timeout.tv_sec == 0) - sync_set_timeout(ctx, sfc); + sync_set_timeout(ctx, fn->lpr); else { if (tv_diff(&ctx->timeout, now, NULL) < 0) { sync_disable_active_buddies(ctx); @@ -395,21 +377,11 @@ out: return ret; } -/** - * The synchronization filter. - * - * \param f Pointer to the struct to initialize. - */ -void sync_filter_init(struct filter *f) -{ - struct sync_filter_args_info dummy; - - sync_filter_cmdline_parser_init(&dummy); - f->open = sync_open; - f->close = sync_close; - f->pre_select = sync_pre_select; - f->post_select = sync_post_select; - f->parse_config = sync_parse_config; - f->free_config = sync_free_config; - f->help = (struct ggo_help)DEFINE_GGO_HELP(sync_filter); -} +const struct filter lsg_filter_cmd_com_sync_user_data = { + .setup = sync_setup, + .open = sync_open, + .pre_select = sync_pre_select, + .post_select = sync_post_select, + .close = sync_close, + .teardown = sync_teardown +}; diff --git a/t/t0005-man.sh b/t/t0005-man.sh index ad9fa4c7..de3a81df 100755 --- a/t/t0005-man.sh +++ b/t/t0005-man.sh @@ -26,7 +26,7 @@ grep_man() regex="$rfw_regex" test_expect_success 'para_recv: receiver options' "grep_man 'RECEIVERS' recv" -test_expect_success 'para_filter: filter options' "grep_man '$regex' filter" +test_expect_success 'para_filter: filter options' "grep_man 'FILTERS' filter" test_expect_success 'para_write: writer options' "grep_man '$regex' write" test_require_objects "audiod" if [[ -n "$result" ]]; then @@ -35,7 +35,7 @@ else test_expect_success 'para_audiod: receivers' \ "grep_man 'RECEIVERS' audiod" test_expect_success 'para_audiod: filters' \ - "grep_man 'Options for the compress filter' audiod" + "grep_man 'FILTERS' audiod" test_expect_success 'para_audiod: writers' \ "grep_man 'Options for the file writer' audiod" fi diff --git a/wav_filter.c b/wav_filter.c index 88047adb..eb38cd7d 100644 --- a/wav_filter.c +++ b/wav_filter.c @@ -12,7 +12,6 @@ #include "error.h" #include "list.h" #include "sched.h" -#include "ggo.h" #include "buffer_tree.h" #include "filter.h" #include "string.h" @@ -120,15 +119,9 @@ err: return ret; } -/** - * The init function of the wav filter. - * - * \param f Structure to initialize. - */ -void wav_filter_init(struct filter *f) -{ - f->close = wav_close; - f->open = wav_open; - f->pre_select = wav_pre_select; - f->post_select = wav_post_select; -} +const struct filter lsg_filter_cmd_com_wav_user_data = { + .close = wav_close, + .open = wav_open, + .pre_select = wav_pre_select, + .post_select = wav_post_select, +}; diff --git a/wmadec_filter.c b/wmadec_filter.c index 4c7c047a..18997aec 100644 --- a/wmadec_filter.c +++ b/wmadec_filter.c @@ -24,7 +24,6 @@ #include "para.h" #include "error.h" #include "list.h" -#include "ggo.h" #include "string.h" #include "sched.h" #include "buffer_tree.h" @@ -1259,16 +1258,10 @@ static void wmadec_open(struct filter_node *fn) fn->min_iqs = 4096; } -/** - * The init function of the wma decoder. - * - * \param f Its fields are filled in by the function. - */ -void wmadec_filter_init(struct filter *f) -{ - f->open = wmadec_open; - f->close = wmadec_close; - f->execute = wmadec_execute; - f->pre_select = generic_filter_pre_select; - f->post_select = wmadec_post_select; -} +const struct filter lsg_filter_cmd_com_wmadec_user_data = { + .open = wmadec_open, + .close = wmadec_close, + .execute = wmadec_execute, + .pre_select = generic_filter_pre_select, + .post_select = wmadec_post_select, +};