From 1af65c31171b35e5a5e931e3a4467786e932e145 Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Sun, 28 Oct 2012 14:01:01 +0100 Subject: [PATCH] Move wav detection code to a separate file. 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 | 185 +++++++++++++++++++++++++++++++++++++++++++++++++++ check_wav.h | 43 ++++++++++++ configure.ac | 4 +- error.h | 1 + write.c | 141 +++------------------------------------ 5 files changed, 240 insertions(+), 134 deletions(-) create mode 100644 check_wav.c create mode 100644 check_wav.h diff --git a/check_wav.c b/check_wav.c new file mode 100644 index 00000000..0ed79e43 --- /dev/null +++ b/check_wav.c @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2005-2012 Andre Noll + * + * Licensed under the GPL v2. For licencing details see COPYING. + */ + +/** \file check_wav.c Detect and delete a wav header. */ + +#include + +#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 index 00000000..102e9514 --- /dev/null +++ b/check_wav.h @@ -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); diff --git a/configure.ac b/configure.ac index 32ad846d..b724696a 100644 --- a/configure.ac +++ b/configure.ac @@ -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 - prebuffer_filter bitstream imdct + prebuffer_filter bitstream imdct check_wav 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 - buffer_tree ggo" + buffer_tree ggo check_wav" write_ldflags="" writers=" file" default_writer="FILE_WRITE" diff --git a/error.h b/error.h index 8579bd8d..5308eb91 100644 --- 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 CHECK_WAV_ERRORS extern const char **para_errlist[]; diff --git a/write.c b/write.c index df37d7ec..453824ac 100644 --- a/write.c +++ b/write.c @@ -22,130 +22,14 @@ #include "fd.h" #include "error.h" #include "version.h" +#include "check_wav.h" 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; -/** 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) @@ -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; - 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 wav_params wp; 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); - 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)); - 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++) - setup_writer_node(conf.writer_arg[i], cwt->btrn, + setup_writer_node(conf.writer_arg[i], cwt_btrn, wns + i, &s); } @@ -253,7 +130,7 @@ static int setup_and_schedule(void) free(wn->conf); } free(wns); - btr_remove_node(&cwt->btrn); + check_wav_shutdown(cwt); return ret; } -- 2.39.2