X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=resample_filter.c;h=bbdda51c525630c6d1411dfa547193f8ccdae9ce;hp=5bdfe03755d41d822a17cbc117b40f56ae6193f6;hb=0c7c4d80673854527ce3c3786dd581169565b5e6;hpb=cad2842e228ab3e42702a05af759ad292b89bed9 diff --git a/resample_filter.c b/resample_filter.c index 5bdfe037..bbdda51c 100644 --- a/resample_filter.c +++ b/resample_filter.c @@ -1,71 +1,285 @@ -/* - * Copyright (C) 2012 Andre Noll - * - * Licensed under the GPL v2. For licencing details see COPYING. - */ +/* Copyright (C) 2012 Andre Noll , see file COPYING. */ /** \file resample_filter.c A sample rate converter based on libsamplerate. */ #include +#include +#include -#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 +};