summaryrefslogtreecommitdiff
path: root/amp_filter.c
blob: 1970a45ae2b4a47e334268787dc57efda724f1f8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/* SPDX-License-Identifier: GPL-2.0 */

/** \file amp_filter.c Paraslash's amplify filter. */

#include <lopsub.h>

#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 */