Move wav detection code to a separate file.
authorAndre Noll <maan@systemlinux.org>
Sun, 28 Oct 2012 13:01:01 +0000 (14:01 +0100)
committerAndre Noll <maan@systemlinux.org>
Sun, 25 Nov 2012 13:08:55 +0000 (14:08 +0100)
The logic to detect a wav header  and delete it from an input stream
is useful also for the upcoming resample filter, so let's make this
code reusable.

The first step for doing so is to remove the relevant parts from
write.c to check_wav.c and exports those functions which are called
from write.c to check_wav.h.

There is one problem though: The wav detector code needs to know
the arguments for the --channels, --sample_rate and --sample_format
options given to para_write, but the code would not be reusable if
check_wav.c depended on the gengetop args info struct of para_write.

Hence we introduce the public structure wav_params in check_wav.h
for this information, which is independent of gengetopt. A simple
convenience helper for copying the command line arguments from an
(arbitrary) args info structure to a struct wav_params is provided
as well.

check_wav.c [new file with mode: 0644]
check_wav.h [new file with mode: 0644]
configure.ac
error.h
write.c

diff --git a/check_wav.c b/check_wav.c
new file mode 100644 (file)
index 0000000..0ed79e4
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2005-2012 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file check_wav.c Detect and delete a wav header. */
+
+#include <regex.h>
+
+#include "para.h"
+#include "string.h"
+#include "list.h"
+#include "sched.h"
+#include "buffer_tree.h"
+#include "error.h"
+#include "check_wav.h"
+
+/** Length of a standard wav header. */
+#define WAV_HEADER_LEN 44
+
+enum check_wav_state {
+       CWS_NEED_HEADER,
+       CWS_HAVE_HEADER,
+       CWS_NO_HEADER,
+};
+
+struct check_wav_task {
+       enum check_wav_state state;
+       struct task task;
+       struct btr_node *btrn;
+       size_t min_iqs;
+       /* Command line args. */
+       struct wav_params params;
+       /* Extracted from the wav header.*/
+       unsigned channels;
+       unsigned sample_format;
+       unsigned sample_rate;
+};
+
+static void check_wav_pre_select(struct sched *s, struct task *t)
+{
+       struct check_wav_task *cwt = container_of(t, struct check_wav_task, task);
+       int ret;
+
+       ret = btr_node_status(cwt->btrn, cwt->min_iqs, BTR_NT_INTERNAL);
+       if (ret != 0)
+               sched_min_delay(s);
+}
+
+static int check_wav_exec(struct btr_node *btrn, const char *cmd, char **result)
+{
+       struct check_wav_task *cwt = btr_context(btrn);
+       int val, header_val, given, arg;
+
+       header_val = cwt->channels;
+       arg = cwt->params.channels_arg;
+       given = cwt->params.channels_given;
+       if (!strcmp(cmd, "channels"))
+               goto out;
+
+       header_val = cwt->sample_rate;
+       arg = cwt->params.sample_rate_arg;
+       given = cwt->params.sample_rate_given;
+       if (!strcmp(cmd, "sample_rate"))
+               goto out;
+
+       header_val = cwt->sample_format;
+       arg = cwt->params.sample_format_arg;
+       given = cwt->params.sample_format_given;
+       if (!strcmp(cmd, "sample_format"))
+               goto out;
+
+       return -ERRNO_TO_PARA_ERROR(ENOTSUP);
+out:
+       if (given)
+               val = arg;
+       else {
+               switch (cwt->state) {
+               case CWS_HAVE_HEADER:
+                       val = header_val;
+                       break;
+               case CWS_NO_HEADER:
+                       /* Use default value */
+                       val = arg;
+                       break;
+               default:
+                       return -E_BTR_NAVAIL;
+               }
+       }
+       *result = make_message("%d", val);
+       return 1;
+}
+
+static void check_wav_post_select(__a_unused struct sched *s, struct task *t)
+{
+       struct check_wav_task *cwt = container_of(t, struct check_wav_task, task);
+       struct btr_node *btrn = cwt->btrn;
+       unsigned char *a;
+       size_t sz;
+       int ret;
+       uint16_t bps; /* bits per sample */
+       const char *sample_formats[] = {SAMPLE_FORMATS};
+
+       t->error = 0;
+       ret = btr_node_status(btrn, cwt->min_iqs, BTR_NT_INTERNAL);
+       if (ret <= 0)
+               goto out;
+       if (cwt->state != CWS_NEED_HEADER)
+               goto pushdown;
+       btr_merge(btrn, cwt->min_iqs);
+       sz = btr_next_buffer(btrn, (char **)&a);
+       if (sz < cwt->min_iqs) /* file size less than WAV_HEADER_SIZE */
+               goto pushdown;
+       cwt->min_iqs = 0;
+       /*
+        * The default byte ordering assumed for WAVE data files is
+        * little-endian. Files written using the big-endian byte ordering
+        * scheme have the identifier RIFX instead of RIFF.
+        */
+       if (a[0] != 'R' || a[1] != 'I' || a[2] != 'F' ||
+                       (a[3] != 'F' && a[3] != 'X')) {
+               PARA_NOTICE_LOG("wav header not found\n");
+               cwt->state = CWS_NO_HEADER;
+               sprintf(t->status, "check wav: no header");
+               goto out;
+       }
+       PARA_INFO_LOG("found wav header\n");
+       cwt->state = CWS_HAVE_HEADER;
+       sprintf(t->status, "check wav: have header");
+       /* Only set those values which have not already been set. */
+       cwt->channels = (unsigned)a[22];
+       cwt->sample_rate = a[24] + (a[25] << 8) + (a[26] << 16) + (a[27] << 24);
+       bps = a[34] + ((unsigned)a[35] << 8);
+       if (bps != 8 && bps != 16) {
+               PARA_WARNING_LOG("%u bps not supported, assuming 16\n",
+                       bps);
+               bps = 16;
+       }
+       /*
+        * 8-bit samples are stored as unsigned bytes, ranging from 0
+        * to 255.  16-bit samples are stored as 2's-complement signed
+        * integers, ranging from -32768 to 32767.
+        */
+       if (bps == 8)
+               cwt->sample_format = SF_U8;
+       else
+               cwt->sample_format = (a[3] == 'F')?
+                       SF_S16_LE : SF_S16_BE;
+       PARA_NOTICE_LOG("%dHz, %s, %s\n", cwt->sample_rate,
+               cwt->channels == 1? "mono" : "stereo",
+               sample_formats[cwt->sample_format]);
+       btr_consume(btrn, WAV_HEADER_LEN);
+pushdown:
+       btr_pushdown(btrn);
+out:
+       t->error = ret;
+       if (ret < 0)
+               btr_remove_node(&cwt->btrn);
+}
+
+struct check_wav_task *check_wav_init(struct sched *s, struct btr_node *parent,
+               struct wav_params *params, struct btr_node **cwt_btrn)
+{
+       struct check_wav_task *cwt = para_calloc(sizeof(*cwt));
+
+       cwt->state = CWS_NEED_HEADER;
+       cwt->min_iqs = WAV_HEADER_LEN;
+       cwt->params = *params;
+       cwt->btrn = btr_new_node(&(struct btr_node_description)
+               EMBRACE(.name = "check_wav", .parent = parent,
+               .handler = check_wav_exec, .context = cwt));
+       sprintf(cwt->task.status, "check_wav");
+       cwt->task.pre_select = check_wav_pre_select;
+       cwt->task.post_select = check_wav_post_select;
+       if (cwt_btrn)
+               *cwt_btrn = cwt->btrn;
+       register_task(s, &cwt->task);
+       return cwt;
+}
+
+void check_wav_shutdown(struct check_wav_task *cwt)
+{
+       free(cwt);
+}
diff --git a/check_wav.h b/check_wav.h
new file mode 100644 (file)
index 0000000..102e951
--- /dev/null
@@ -0,0 +1,43 @@
+struct check_wav_task;
+
+/**
+ * These come from the command line arguments.
+ *
+ * Different users of the check_wav API have different arg_info structs,
+ * so we need a universal variant for these.
+ */
+struct wav_params {
+       /** Number of channels, or the default value. */
+       int channels_arg;
+       /** Whether the channel count was given. */
+       int channels_given;
+       /** Same semantics as \a channels_count. */
+       int sample_rate_arg;
+       /** Whether the sample rate was given. */
+       int sample_rate_given;
+       /** Same semantics as \a sample_rate. */
+       int sample_format_arg;
+       /** Whether the sample format was given. */
+       int sample_format_given;
+};
+
+/**
+ * Copy the wav parameters.
+ *
+ * \param dst Usually a pointer to struct wav_params.
+ * \param src Usually a pointer to some args_info struct.
+ *
+ * This can not be implemented as a function since the type of the structure
+ * pointed to by \a src depends on the application.
+ */
+#define COPY_WAV_PARMS(dst, src) \
+       (dst)->channels_arg = (src)->channels_arg; \
+       (dst)->channels_given = (src)->channels_given; \
+       (dst)->sample_rate_arg = (src)->sample_rate_arg; \
+       (dst)->sample_rate_given = (src)->sample_rate_given; \
+       (dst)->sample_format_arg = (src)->sample_format_arg; \
+       (dst)->sample_format_given = (src)->sample_format_given;
+
+struct check_wav_task *check_wav_init(struct sched *s, struct btr_node *parent,
+               struct wav_params *params, struct btr_node **cwt_btrn);
+void check_wav_shutdown(struct check_wav_task *cwt);
index 32ad846..b724696 100644 (file)
@@ -100,7 +100,7 @@ all_errlist_objs="mp3_afh afh_common net string signal time daemon
        dccp_recv recv_common write_common file_write audiod_command
        client_common recv stdout filter stdin audioc write client
        exec send_common ggo udp_recv color fec fecdec_filter
        dccp_recv recv_common write_common file_write audiod_command
        client_common recv stdout filter stdin audioc write client
        exec send_common ggo udp_recv color fec fecdec_filter
-       prebuffer_filter bitstream imdct
+       prebuffer_filter bitstream imdct check_wav
        wma_afh wma_common wmadec_filter buffer_tree crypt_common
        gui gui_theme sideband"
 
        wma_afh wma_common wmadec_filter buffer_tree crypt_common
        gui gui_theme sideband"
 
@@ -138,7 +138,7 @@ afh_ldflags=""
 
 write_cmdline_objs="add_cmdline(write file_write)"
 write_errlist_objs="write write_common file_write time fd string sched stdin
 
 write_cmdline_objs="add_cmdline(write file_write)"
 write_errlist_objs="write write_common file_write time fd string sched stdin
-       buffer_tree ggo"
+       buffer_tree ggo check_wav"
 write_ldflags=""
 writers=" file"
 default_writer="FILE_WRITE"
 write_ldflags=""
 writers=" file"
 default_writer="FILE_WRITE"
diff --git a/error.h b/error.h
index 8579bd8..5308eb9 100644 (file)
--- a/error.h
+++ b/error.h
@@ -33,6 +33,7 @@ DEFINE_ERRLIST_OBJECT_ENUM;
 #define FILE_WRITE_ERRORS
 #define STDIN_ERRORS
 #define WRITE_ERRORS
 #define FILE_WRITE_ERRORS
 #define STDIN_ERRORS
 #define WRITE_ERRORS
+#define CHECK_WAV_ERRORS
 
 extern const char **para_errlist[];
 
 
 extern const char **para_errlist[];
 
diff --git a/write.c b/write.c
index df37d7e..453824a 100644 (file)
--- a/write.c
+++ b/write.c
 #include "fd.h"
 #include "error.h"
 #include "version.h"
 #include "fd.h"
 #include "error.h"
 #include "version.h"
+#include "check_wav.h"
 
 INIT_WRITE_ERRLISTS;
 
 
 INIT_WRITE_ERRLISTS;
 
-enum check_wav_state {
-       CWS_NEED_HEADER,
-       CWS_HAVE_HEADER,
-       CWS_NO_HEADER,
-};
-
-/* Information extracted from the wav header. */
-struct check_wav_task {
-       int state;
-       /** Number of channels. */
-       unsigned channels;
-       unsigned sample_format;
-       /** Sample rate specified in wav header given by \a buf. */
-       unsigned sample_rate;
-       /** The task structure used by the scheduler. */
-       struct task task;
-       struct btr_node *btrn;
-       size_t min_iqs;
-};
-
 static struct write_args_info conf;
 
 static struct stdin_task sit;
 
 static struct write_args_info conf;
 
 static struct stdin_task sit;
 
-/** Length of a standard wav header. */
-#define WAV_HEADER_LEN 44
-
-static void check_wav_pre_select(struct sched *s, struct task *t)
-{
-       struct check_wav_task *cwt = container_of(t, struct check_wav_task, task);
-       int ret;
-
-       ret = btr_node_status(cwt->btrn, cwt->min_iqs, BTR_NT_INTERNAL);
-       if (ret != 0)
-               sched_min_delay(s);
-}
-
-#define HANDLE_EXEC(_cmd) \
-       if (!strcmp(cmd, #_cmd)) { \
-               if (!conf._cmd ## _given && cwt->state == CWS_NEED_HEADER) \
-                       return -E_BTR_NAVAIL; \
-               *result = make_message("%d", cwt->state == CWS_NO_HEADER || conf._cmd ## _given? \
-                       conf._cmd ## _arg : cwt->_cmd); \
-               return 1; \
-       } \
-
-
-static int check_wav_exec(struct btr_node *btrn, const char *cmd, char **result)
-{
-       struct check_wav_task *cwt = btr_context(btrn);
-
-       HANDLE_EXEC(sample_rate);
-       HANDLE_EXEC(channels);
-       HANDLE_EXEC(sample_format);
-       return -ERRNO_TO_PARA_ERROR(ENOTSUP);
-}
-
-static void check_wav_post_select(__a_unused struct sched *s, struct task *t)
-{
-       struct check_wav_task *cwt = container_of(t, struct check_wav_task, task);
-       struct btr_node *btrn = cwt->btrn;
-       unsigned char *a;
-       size_t sz;
-       int ret;
-       uint16_t bps; /* bits per sample */
-       const char *sample_formats[] = {SAMPLE_FORMATS};
-
-       t->error = 0;
-       ret = btr_node_status(btrn, cwt->min_iqs, BTR_NT_INTERNAL);
-       if (ret <= 0)
-               goto out;
-       if (cwt->state != CWS_NEED_HEADER)
-               goto pushdown;
-       btr_merge(btrn, cwt->min_iqs);
-       sz = btr_next_buffer(btrn, (char **)&a);
-       if (sz < cwt->min_iqs) /* file size less than WAV_HEADER_SIZE */
-               goto pushdown;
-       cwt->min_iqs = 0;
-       /*
-        * The default byte ordering assumed for WAVE data files is
-        * little-endian. Files written using the big-endian byte ordering
-        * scheme have the identifier RIFX instead of RIFF.
-        */
-       if (a[0] != 'R' || a[1] != 'I' || a[2] != 'F' ||
-                       (a[3] != 'F' && a[3] != 'X')) {
-               PARA_NOTICE_LOG("wav header not found\n");
-               cwt->state = CWS_NO_HEADER;
-               sprintf(t->status, "check wav: no header");
-               goto out;
-       }
-       PARA_INFO_LOG("found wav header\n");
-       cwt->state = CWS_HAVE_HEADER;
-       sprintf(t->status, "check wav: have header");
-       cwt->channels = (unsigned) a[22];
-       cwt->sample_rate = a[24] + (a[25] << 8) + (a[26] << 16) + (a[27] << 24);
-       bps = a[34] + ((unsigned)a[35] << 8);
-       if (bps != 8 && bps != 16) {
-               PARA_WARNING_LOG("%u bps not supported, assuming 16\n", bps);
-               bps = 16;
-       }
-       /*
-        * 8-bit samples are stored as unsigned bytes, ranging from 0 to 255.
-        * 16-bit samples are stored as 2's-complement signed integers, ranging
-        * from -32768 to 32767.
-        */
-       if (bps == 8)
-               cwt->sample_format = SF_U8;
-       else
-               cwt->sample_format = (a[3] == 'F')? SF_S16_LE : SF_S16_BE;
-       PARA_NOTICE_LOG("%dHz, %s, %s\n", cwt->sample_rate,
-               cwt->channels == 1? "mono" : "stereo",
-               sample_formats[cwt->sample_format]);
-       btr_consume(btrn, WAV_HEADER_LEN);
-pushdown:
-       btr_pushdown(btrn);
-out:
-       t->error = ret;
-       if (ret < 0)
-               btr_remove_node(&cwt->btrn);
-}
-
 static int loglevel;
 INIT_STDERR_LOGGING(loglevel)
 
 static int loglevel;
 INIT_STDERR_LOGGING(loglevel)
 
@@ -194,9 +78,11 @@ static void setup_writer_node(const char *arg, struct btr_node *parent,
 static int setup_and_schedule(void)
 {
        int i, ret;
 static int setup_and_schedule(void)
 {
        int i, ret;
-       struct check_wav_task _cwt, *cwt = &_cwt;
+       struct check_wav_task *cwt;
+       struct btr_node *cwt_btrn;
        struct writer_node *wns;
        static struct sched s;
        struct writer_node *wns;
        static struct sched s;
+       struct wav_params wp;
 
        loglevel = get_loglevel_by_name(conf.loglevel_arg);
        sit.btrn = btr_new_node(&(struct btr_node_description)
 
        loglevel = get_loglevel_by_name(conf.loglevel_arg);
        sit.btrn = btr_new_node(&(struct btr_node_description)
@@ -204,25 +90,16 @@ static int setup_and_schedule(void)
        stdin_set_defaults(&sit);
        register_task(&s, &sit.task);
 
        stdin_set_defaults(&sit);
        register_task(&s, &sit.task);
 
-       cwt->state = CWS_NEED_HEADER;
-       cwt->min_iqs = WAV_HEADER_LEN;
-       cwt->btrn = btr_new_node(&(struct btr_node_description)
-               EMBRACE(.name = "check_wav", .parent = sit.btrn,
-               .handler = check_wav_exec, .context = cwt));
-       sprintf(cwt->task.status, "check_wav");
-       cwt->task.pre_select = check_wav_pre_select;
-       cwt->task.post_select = check_wav_post_select;
-       cwt->task.error = 0;
-       register_task(&s, &cwt->task);
-
+       COPY_WAV_PARMS(&wp, &conf);
+       cwt = check_wav_init(&s, sit.btrn, &wp, &cwt_btrn);
        if (!conf.writer_given) {
                wns = para_calloc(sizeof(*wns));
        if (!conf.writer_given) {
                wns = para_calloc(sizeof(*wns));
-               setup_writer_node(NULL, cwt->btrn, wns, &s);
+               setup_writer_node(NULL, cwt_btrn, wns, &s);
                i = 1;
        } else {
                wns = para_calloc(conf.writer_given * sizeof(*wns));
                for (i = 0; i < conf.writer_given; i++)
                i = 1;
        } else {
                wns = para_calloc(conf.writer_given * sizeof(*wns));
                for (i = 0; i < conf.writer_given; i++)
-                       setup_writer_node(conf.writer_arg[i], cwt->btrn,
+                       setup_writer_node(conf.writer_arg[i], cwt_btrn,
                                wns + i, &s);
        }
 
                                wns + i, &s);
        }
 
@@ -253,7 +130,7 @@ static int setup_and_schedule(void)
                free(wn->conf);
        }
        free(wns);
                free(wn->conf);
        }
        free(wns);
-       btr_remove_node(&cwt->btrn);
+       check_wav_shutdown(cwt);
        return ret;
 }
 
        return ret;
 }