]> git.tuebingen.mpg.de Git - paraslash.git/blobdiff - check_wav.c
Move wav detection code to a separate file.
[paraslash.git] / check_wav.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);
+}