/* SPDX-License-Identifier: GPL-2.0 */ /** \file amp_filter.c Paraslash's amplify filter. */ #include #include "filter_cmd.lsg.h" #include "para.h" #include "list.h" #include "sched.h" #include "buffer_tree.h" #include "filter.h" #include "string.h" #include "error.h" extern const char *stat_item_values[NUM_STAT_ITEMS]; /** Data specific to the amplify filter. */ struct private_amp_data { /** Amplification factor. */ unsigned amp; }; static void amp_close(struct filter_node *fn) { free(fn->private_data); } static void amp_open(struct filter_node *fn) { struct private_amp_data *pad = zalloc(sizeof(*pad)); unsigned given = FILTER_CMD_OPT_GIVEN(AMP, AMP, fn->lpr); uint32_t amp_arg = FILTER_CMD_OPT_UINT32_VAL(AMP, AMP, fn->lpr); fn->private_data = pad; fn->min_iqs = 2; if (!given && stat_item_values[SI_amplification]) sscanf(stat_item_values[SI_amplification], "%u", &pad->amp); else pad->amp = amp_arg; PARA_INFO_LOG("amplification: %u (scaling factor: %1.2f)\n", pad->amp, pad->amp / 64.0 + 1.0); } static int amp_post_monitor(__a_unused struct sched *s, void *context) { struct filter_node *fn = context; struct private_amp_data *pad = fn->private_data; struct btr_node *btrn = fn->btrn; int ret, factor = 64 + pad->amp; size_t i, in_bytes, len; int16_t *in, *out; bool inplace = btr_inplace_ok(btrn); if (pad->amp == 0) { /* no amplification */ btr_splice_out_node(&fn->btrn); return -E_AMP_ZERO_AMP; } next_buffer: ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL); if (ret < 0) goto err; if (ret == 0) return 0; btr_merge(btrn, fn->min_iqs); in_bytes = btr_next_buffer(btrn, (char **)&in); len = in_bytes / 2; if (len == 0) { /* eof and in_bytes == 1 */ ret = -E_EOF; goto err; } if (inplace) out = in; else out = alloc(len * 2); for (i = 0; i < len; i++) { int x = (in[i] * factor) >> 6; out[i] = x; if (out[i] != x) /* overflow, clip */ out[i] = (x >= 0)? 32767 : -32768; } if (inplace) btr_pushdown_one(btrn); else { btr_consume(btrn, len * 2); btr_add_output((char *)out, len * 2, btrn); } goto next_buffer; err: assert(ret < 0); btr_remove_node(&fn->btrn); return ret; } /** \cond doxygen_ignore */ const struct filter lsg_filter_cmd_com_amp_user_data = { .open = amp_open, .close = amp_close, .pre_monitor = generic_filter_pre_monitor, .post_monitor = amp_post_monitor, }; /** \endcond */