-/*
- * Copyright (C) 2012 Andre Noll <maan@systemlinux.org>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
+/* Copyright (C) 2012 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
/** \file resample_filter.c A sample rate converter based on libsamplerate. */
#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 {
+ uint32_t channels;
+ int source_sample_rate;
+ float ratio;
+ SRC_STATE *src_state;
+ struct check_wav_context *cwc;
+};
+
+static int resample_execute(struct btr_node *btrn, const char *cmd, char **result)
+{
+ struct filter_node *fn = btr_context(btrn);
+ struct resample_context *ctx = fn->private_data;
+ 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)
{
- free(fn->private_data);
+ struct resample_context *ctx = fn->private_data;
+
+ if (!ctx)
+ return;
+ check_wav_shutdown(ctx->cwc);
+ if (ctx->src_state)
+ src_delete(ctx->src_state);
+ free(ctx);
fn->private_data = NULL;
}
static void resample_open(struct filter_node *fn)
{
+ struct resample_context *ctx = para_calloc(sizeof(*ctx));
+ struct btr_node *btrn = fn->btrn;
+ struct wav_params wp;
+
+ fn->private_data = ctx;
+ fn->min_iqs = 2;
+ 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);
+}
+
+static void resample_pre_select(struct sched *s, void *context)
+{
+ struct filter_node *fn = context;
+ struct resample_context *ctx = fn->private_data;
+ int ret = btr_node_status(fn->btrn, fn->min_iqs, BTR_NT_INTERNAL);
+
+ if (ret != 0)
+ return sched_min_delay(s);
+ check_wav_pre_select(s, ctx->cwc);
+}
+
+static int get_btr_val(const char *what, struct btr_node *btrn)
+{
+ char *buf;
+ int32_t val;
+ int ret = btr_exec_up(btr_parent(btrn), what, &buf);
+
+ if (ret < 0) {
+ PARA_NOTICE_LOG("btr exec for \"%s\" failed\n", what);
+ return ret;
+ }
+ ret = para_atoi32(buf, &val);
+ free(buf);
+ return ret < 0? ret : val;
}
-static void resample_pre_select(struct sched *s, struct task *t)
+static int resample_set_params(struct filter_node *fn)
{
- struct filter_node *fn = container_of(t, struct filter_node, task);
+ int ret;
+ struct resample_context *ctx = fn->private_data;
+ struct btr_node *btrn = fn->btrn;
+ struct lls_parse_result *lpr = fn->lpr;
+
+ 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 = 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;
+ }
+ /* 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 * const sample_formats[] = {SAMPLE_FORMATS};
+ PARA_ERROR_LOG("unsupported sample format: %s\n",
+ sample_formats[ret]);
+ return -ERRNO_TO_PARA_ERROR(EINVAL);
+ }
+ 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;
+ const uint32_t trafo[] = {
+ [RCT_BEST] = SRC_SINC_BEST_QUALITY,
+ [RCT_MEDIUM] = SRC_SINC_MEDIUM_QUALITY,
+ [RCT_FASTEST] = SRC_SINC_FASTEST,
+ [RCT_ZERO_ORDER_HOLD] = SRC_ZERO_ORDER_HOLD,
+ [RCT_LINEAR] = SRC_LINEAR
+ };
+ struct resample_context *ctx = fn->private_data;
+ const struct lls_option *o_c = FILTER_CMD_OPT(RESAMPLE, CONVERTER);
+ uint32_t converter = U32_OPTVAL(CONVERTER, fn->lpr);
+
+ PARA_INFO_LOG("converter type: %s\n",
+ lls_enum_string_val(converter, o_c));
+ ret = resample_set_params(fn);
+ if (ret < 0)
+ return ret;
+ ctx->src_state = src_new(trafo[converter],
+ U32_OPTVAL(CHANNELS, fn->lpr), &ret);
+ if (!ctx->src_state) {
+ PARA_ERROR_LOG("%s\n", src_strerror(ret));
+ return -E_LIBSAMPLERATE;
+ }
+ fn->min_iqs = 2 * ctx->channels;
+ return 1;
}
-static void resample_post_select(__a_unused struct sched *s, struct task *t)
+/* returns number of input frames used */
+static int resample_frames(int16_t *in, size_t num_frames, bool have_more,
+ struct resample_context *ctx, int16_t **result,
+ size_t *result_frames)
{
- struct filter_node *fn = container_of(t, struct filter_node, task);
+ int ret, num_samples, out_samples;
+ float *in_float;
+ int16_t *out;
+ SRC_DATA data;
+
+ data.src_ratio = ctx->ratio;
+ data.end_of_input = !have_more;
+
+ data.input_frames = num_frames;
+ num_samples = num_frames * ctx->channels;
+ data.output_frames = num_frames * ctx->ratio + 1;
+ out_samples = data.output_frames * ctx->channels;
+
+ in_float = para_malloc(num_samples * sizeof(float));
+ src_short_to_float_array(in, in_float, num_samples);
+ data.data_in = in_float;
+ data.data_out = para_malloc(out_samples * sizeof(float));
+ ret = src_process(ctx->src_state, &data);
+ free(in_float);
+ if (ret != 0) {
+ PARA_ERROR_LOG("%s\n", src_strerror(ret));
+ free(data.data_out);
+ return -E_LIBSAMPLERATE;
+ }
+ out_samples = data.output_frames_gen * ctx->channels;
+ out = para_malloc(out_samples * sizeof(short));
+ src_float_to_short_array(data.data_out, out, out_samples);
+ free(data.data_out);
+ *result = out;
+ *result_frames = data.output_frames_gen;
+ return data.input_frames_used;
}
-static int resample_parse_config(int argc, char **argv, void **config)
+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 btr_node *btrn = fn->btrn;
+ int16_t *in, *out;
+ size_t in_bytes, num_frames;
+ bool have_more;
+
+ ret = check_wav_post_select(ctx->cwc);
+ if (ret < 0)
+ goto out;
+ ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
+ if (ret <= 0)
+ goto out;
+ if (!ctx->src_state) {
+ ret = resample_init(fn);
+ if (ret <= 0)
+ goto out;
+ }
+ 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
+ * btr exec mechanism for the destination samplerate and the
+ * channel count.
+ */
+ btr_pushdown(btrn);
+ return 0;
+ }
+ btr_merge(btrn, fn->min_iqs);
+ in_bytes = btr_next_buffer(btrn, (char **)&in);
+ ret = -E_RESAMPLE_EOF;
+ num_frames = in_bytes / 2 / ctx->channels;
+ if (num_frames == 0)
+ goto out;
+ have_more = !btr_no_parent(btrn) ||
+ btr_next_buffer_omit(btrn, in_bytes, NULL) > 0;
+ ret = resample_frames(in, num_frames, have_more, ctx, &out, &num_frames);
+ if (ret < 0)
+ goto out;
+ btr_consume(btrn, ret * 2 * ctx->channels);
+ btr_add_output((char *)out, num_frames * 2 * ctx->channels, btrn);
return 0;
+out:
+ if (ret < 0) {
+ btr_remove_node(&fn->btrn);
+ /* This releases the check_wav btr node */
+ check_wav_post_select(ctx->cwc);
+ }
+ return ret;
}
-static void resample_free_config(void *conf)
+static void *resample_setup(const struct lls_parse_result *lpr)
{
- resample_filter_cmdline_parser_free(conf);
+ int given;
+ uint32_t u32;
+
+ /* sanity checks */
+ 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);
+ }
+ return NULL;
}
-/**
- * The init function of the resample filter.
- *
- * \param f Structure to initialize.
- */
-void resample_filter_init(struct filter *f)
+static void resample_teardown(__a_unused const struct lls_parse_result *lpr,
+ void *conf)
{
- 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->help = (struct ggo_help) {
- .short_help = resample_filter_args_info_help,
- .detailed_help = resample_filter_args_info_detailed_help
- };
+ free(conf);
}
+
+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
+};