Convert filters to lopsub.
authorAndre Noll <maan@tuebingen.mpg.de>
Sun, 10 Apr 2016 20:44:30 +0000 (22:44 +0200)
committerAndre Noll <maan@tuebingen.mpg.de>
Sun, 26 Mar 2017 09:02:28 +0000 (11:02 +0200)
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.

36 files changed:
Makefile.in
Makefile.real
aacdec_filter.c
amp_filter.c
audiod.c
check_wav.h
compress_filter.c
configure.ac
error.h
fecdec_filter.c
filter.c
filter.h
filter_common.c
flacdec_filter.c
m4/gengetopt/amp_filter.m4 [deleted file]
m4/gengetopt/compress_filter.m4 [deleted file]
m4/gengetopt/mp3dec_filter.m4 [deleted file]
m4/gengetopt/prebuffer_filter.m4 [deleted file]
m4/gengetopt/resample_filter.m4 [deleted file]
m4/gengetopt/sync_filter.m4 [deleted file]
m4/lls/filter_cmd.suite.m4 [new file with mode: 0644]
m4/lls/include/channels.m4 [new file with mode: 0644]
m4/lls/include/sample-format.m4 [new file with mode: 0644]
m4/lls/include/sample-rate.m4 [new file with mode: 0644]
man_util.bash
mp3dec_filter.c
oggdec_filter.c
opusdec_filter.c
play.c
prebuffer_filter.c
resample_filter.c
spxdec_filter.c
sync_filter.c
t/t0005-man.sh
wav_filter.c
wmadec_filter.c

index 2a2bdaf..e72b518 100644 (file)
@@ -16,7 +16,6 @@ HELP2MAN := @HELP2MAN@
 ggo_descriptions_declared := @ggo_descriptions_declared@
 
 executables := @executables@
-filters := @filters@
 writers := @writers@
 
 recv_objs := @recv_objs@
index e119919..2dec984 100644 (file)
@@ -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 \
index 5725ce0..bbb756a 100644 (file)
@@ -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
+};
index 5193d7c..f3d0d87 100644 (file)
@@ -7,12 +7,12 @@
 /** \file amp_filter.c Paraslash's amplify filter. */
 
 #include <regex.h>
+#include <lopsub.h>
 
+#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,
+};
index 56ab0c9..5f4acb4 100644 (file)
--- 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();
index 0957fe0..31417de 100644 (file)
@@ -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);
index d0d96fd..6589764 100644 (file)
  */
 
 #include <regex.h>
+#include <lopsub.h>
 
+#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,
+};
index d592fba..62aa16d 100644 (file)
@@ -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 404bdf4..9c8d7d3 100644 (file)
--- 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"), \
index 1c3a378..1b95ea4 100644 (file)
@@ -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,
+};
index 8190189..1deaf6d 100644 (file)
--- 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);
index 0bd5469..03e79d9 100644 (file)
--- 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);
index e9b97e5..5a5e9d0 100644 (file)
@@ -8,22 +8,20 @@
 
 #include <regex.h>
 #include <sys/types.h>
+#include <lopsub.h>
 
+#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);
        }
 }
 
index bbacb3d..16237ac 100644 (file)
@@ -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 (file)
index a02cc5b..0000000
+++ /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 (file)
index 501f46c..0000000
+++ /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 (file)
index 8b18783..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Decode an mp3 stream"
-
-include(header.m4)
-
-<qu>
-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.
-"
-</qu>
diff --git a/m4/gengetopt/prebuffer_filter.m4 b/m4/gengetopt/prebuffer_filter.m4
deleted file mode 100644 (file)
index 20ff86f..0000000
+++ /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 (file)
index 2e5f400..0000000
+++ /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 (file)
index 1e6f5f8..0000000
+++ /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 (file)
index 0000000..4dda0bf
--- /dev/null
@@ -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 (file)
index 0000000..ae26b73
--- /dev/null
@@ -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 (file)
index 0000000..0daad19
--- /dev/null
@@ -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 (file)
index 0000000..1433521
--- /dev/null
@@ -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]
index 38ed86c..2a12737 100755 (executable)
@@ -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"
index 5f6935d..b053aaa 100644 (file)
@@ -8,12 +8,12 @@
 
 #include <mad.h>
 #include <regex.h>
+#include <lopsub.h>
 
+#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,
+};
index 04be702..9fa80c5 100644 (file)
@@ -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
+};
index 2822298..6f9d526 100644 (file)
@@ -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 330c8b2..ca584b9 100644 (file)
--- 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;
index 6078da0..8ca1630 100644 (file)
@@ -7,12 +7,12 @@
 /** \file prebuffer_filter.c Paraslash's prebuffering filter. */
 
 #include <regex.h>
+#include <lopsub.h>
 
 #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,
+};
index 1699ed2..e630596 100644 (file)
@@ -8,20 +8,24 @@
 
 #include <regex.h>
 #include <samplerate.h>
+#include <lopsub.h>
 
-#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
+};
index 644d287..3266d09 100644 (file)
@@ -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,
+};
index 82e86e9..efc1011 100644 (file)
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
+#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
+};
index ad9fa4c..de3a81d 100755 (executable)
@@ -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
index 88047ad..eb38cd7 100644 (file)
@@ -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,
+};
index 4c7c047..18997ae 100644 (file)
@@ -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,
+};