Merge branch 'refs/heads/t/i9e'
[paraslash.git] / check_wav.c
index 0ed79e4..d1d2787 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 /** Length of a standard wav header. */
 #define WAV_HEADER_LEN 44
 
+/** The possible states of a check_wav instance. */
 enum check_wav_state {
+       /** Initial state, less than \p WAV_HEADER_LEN bytes available. */
        CWS_NEED_HEADER,
+       /** Wav hader was detected. */
        CWS_HAVE_HEADER,
+       /** First part of the stream did not look like a wav header. */
        CWS_NO_HEADER,
 };
 
-struct check_wav_task {
+struct check_wav_context {
        enum check_wav_state state;
-       struct task task;
        struct btr_node *btrn;
        size_t min_iqs;
        /* Command line args. */
@@ -38,36 +41,42 @@ struct check_wav_task {
        unsigned sample_rate;
 };
 
-static void check_wav_pre_select(struct sched *s, struct task *t)
+/**
+ * Set select timeout according to the given context.
+ *
+ * \param s Contains the timeval that should be set.
+ * \param cwc Contains a pointer to the buffer tree node.
+ *
+ * This requests a minimal timeout from the scheduler if btrn of \a cwc is not
+ * idle.
+ */
+void check_wav_pre_select(struct sched *s, struct check_wav_context *cwc)
 {
-       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);
+       int ret = btr_node_status(cwc->btrn, cwc->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);
+       struct check_wav_context *cwc = btr_context(btrn);
        int val, header_val, given, arg;
 
-       header_val = cwt->channels;
-       arg = cwt->params.channels_arg;
-       given = cwt->params.channels_given;
+       header_val = cwc->channels;
+       arg = cwc->params.channels_arg;
+       given = cwc->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;
+       header_val = cwc->sample_rate;
+       arg = cwc->params.sample_rate_arg;
+       given = cwc->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;
+       header_val = cwc->sample_format;
+       arg = cwc->params.sample_format_arg;
+       given = cwc->params.sample_format_given;
        if (!strcmp(cmd, "sample_format"))
                goto out;
 
@@ -76,11 +85,18 @@ out:
        if (given)
                val = arg;
        else {
-               switch (cwt->state) {
+               switch (cwc->state) {
                case CWS_HAVE_HEADER:
                        val = header_val;
                        break;
                case CWS_NO_HEADER:
+                       /*
+                        * No wav header available and no value specified at
+                        * the command line. Maybe one of our parent nodes
+                        * knows.
+                        */
+                       if (btr_exec_up(btr_parent(cwc->btrn), cmd, result) >= 0)
+                               return 1;
                        /* Use default value */
                        val = arg;
                        break;
@@ -92,27 +108,43 @@ out:
        return 1;
 }
 
-static void check_wav_post_select(__a_unused struct sched *s, struct task *t)
+/**
+ * Filter out the wav header, pushdown everything else.
+ *
+ * \param cwc The context of this instance.
+ *
+ * This function looks at the first \p WAV_HEADER_SIZE bytes of the input queue
+ * of the btrn of \a cwc. If they look like a wav header, the function extracts
+ * the information of interest and swallows this part of the stream. Otherwise
+ * it is pushed down to all children. In either case the rest of the input is
+ * pushed down as well.
+ *
+ * Once the first part has been processed this way, the state of the instance
+ * changes from \p CWS_NEED_HEADER to \p CWS_HAVE_HEADER or \p CWS_NO_HEADER.
+ *
+ * \return Standard.
+ */
+int check_wav_post_select(struct check_wav_context *cwc)
 {
-       struct check_wav_task *cwt = container_of(t, struct check_wav_task, task);
-       struct btr_node *btrn = cwt->btrn;
+       struct btr_node *btrn = cwc->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 (!btrn)
+               return 0;
+       ret = btr_node_status(btrn, cwc->min_iqs, BTR_NT_INTERNAL);
        if (ret <= 0)
                goto out;
-       if (cwt->state != CWS_NEED_HEADER)
+       if (cwc->state != CWS_NEED_HEADER)
                goto pushdown;
-       btr_merge(btrn, cwt->min_iqs);
+       btr_merge(btrn, cwc->min_iqs);
        sz = btr_next_buffer(btrn, (char **)&a);
-       if (sz < cwt->min_iqs) /* file size less than WAV_HEADER_SIZE */
+       if (sz < cwc->min_iqs) /* file size less than WAV_HEADER_SIZE */
                goto pushdown;
-       cwt->min_iqs = 0;
+       cwc->min_iqs = 0;
        /*
         * The default byte ordering assumed for WAVE data files is
         * little-endian. Files written using the big-endian byte ordering
@@ -121,16 +153,14 @@ static void check_wav_post_select(__a_unused struct sched *s, struct task *t)
        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");
+               cwc->state = CWS_NO_HEADER;
                goto out;
        }
        PARA_INFO_LOG("found wav header\n");
-       cwt->state = CWS_HAVE_HEADER;
-       sprintf(t->status, "check wav: have header");
+       cwc->state = CWS_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);
+       cwc->channels = (unsigned)a[22];
+       cwc->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",
@@ -143,43 +173,65 @@ static void check_wav_post_select(__a_unused struct sched *s, struct task *t)
         * integers, ranging from -32768 to 32767.
         */
        if (bps == 8)
-               cwt->sample_format = SF_U8;
+               cwc->sample_format = SF_U8;
        else
-               cwt->sample_format = (a[3] == 'F')?
+               cwc->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]);
+       PARA_NOTICE_LOG("%dHz, %s, %s\n", cwc->sample_rate,
+               cwc->channels == 1? "mono" : "stereo",
+               sample_formats[cwc->sample_format]);
        btr_consume(btrn, WAV_HEADER_LEN);
 pushdown:
        btr_pushdown(btrn);
 out:
-       t->error = ret;
        if (ret < 0)
-               btr_remove_node(&cwt->btrn);
+               btr_remove_node(&cwc->btrn);
+       return ret;
 }
 
-struct check_wav_task *check_wav_init(struct sched *s, struct btr_node *parent,
-               struct wav_params *params, struct btr_node **cwt_btrn)
+/**
+ * Allocate and set up a new check_wav instance.
+ *
+ * \param parent This buffer tree node will be the parent of the new node.
+ * \param child The child of the new node.
+ * \param params Default values and options.
+ * \param cw_btrn A pointer to the check wav node is returned here.
+ *
+ * This function also sets up the ->execute handler of the btrn so that all
+ * children of this node can figure out channel count, sample rate, etc.
+ *
+ * \return The (opaque) handle of the newly created check_wav instance. It is
+ * supposed to be passed to \ref check_wav_pre_select() and \ref
+ * check_wav_post_select().
+ *
+ * \sa \ref btr_new_node.
+ */
+struct check_wav_context *check_wav_init(struct btr_node *parent,
+               struct btr_node *child, struct wav_params *params,
+               struct btr_node **cw_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;
+       struct check_wav_context *cwc = para_calloc(sizeof(*cwc));
+
+       cwc->state = CWS_NEED_HEADER;
+       cwc->min_iqs = WAV_HEADER_LEN;
+       cwc->params = *params;
+       cwc->btrn = btr_new_node(&(struct btr_node_description)
+               EMBRACE(.name = "check_wav", .parent = parent, .child = child,
+               .handler = check_wav_exec, .context = cwc));
+       if (cw_btrn)
+               *cw_btrn = cwc->btrn;
+       return cwc;
 }
 
-void check_wav_shutdown(struct check_wav_task *cwt)
+/**
+ * Dellocate all ressources of a check_wav instance.
+ *
+ * \param cwc Determines the instance to shut down.
+ *
+ * This function may only be called after check_wav_post_select() has returned
+ * negative.
+ */
+void check_wav_shutdown(struct check_wav_context *cwc)
 {
-       free(cwt);
+       free(cwc);
 }