typo
[paraslash.git] / compress.c
1 /*
2 * Copyright (C) 2005-2006 Andre Noll <maan@systemlinux.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
17 */
18
19 /** \file compress.c paraslash's dynamic audio range compressor */
20
21 /*
22 * Based on AudioCompress, (C) 2002-2004 M. Hari Nezumi <magenta@trikuare.cx>
23 */
24
25 #include "gcc-compat.h"
26 #include "para.h"
27 #include "compress_filter.cmdline.h"
28 #include "list.h"
29 #include "filter.h"
30 #include "string.h"
31
32 /** how fine-grained the gain is */
33 #define GAINSHIFT 10
34 /** the size of the output data buffer */
35 #define COMPRESS_CHUNK_SIZE 40960
36
37 /** data specific to the compress filter */
38 struct private_compress_data {
39 /** an array holding the previous peak values */
40 int *peaks;
41 /** current bucket number to be modified */
42 unsigned pn;
43 /** number of times clipping occured */
44 unsigned clip;
45 /** the current multiplier */
46 int current_gain;
47 /** the target multiplier */
48 int target_gain;
49 /** pointer to the configuration data for this instance of the compress filter */
50 struct gengetopt_args_info *conf;
51 };
52
53 static ssize_t compress(char *inbuf, size_t inbuf_len, struct filter_node *fn)
54 {
55 int16_t *audio = (int16_t *) inbuf, *ip = audio, *op;
56 int peak = 1, pos = 0, i, gr, gf, gn;
57 size_t length = MIN((inbuf_len / 2) * 2, (fn->bufsize - fn->loaded) / 2 * 2);
58 struct private_compress_data *pcd = fn->private_data;
59
60 if (!length)
61 return 0;
62 /* determine peak's value and position */
63 for (i = 0; i < length / 2; i++, ip++) {
64 int val = ABS(*ip);
65 if (val > peak) {
66 peak = val;
67 pos = i;
68 }
69 }
70 pcd->peaks[pcd->pn] = peak;
71 for (i = 0; i < pcd->conf->buckets_arg; i++) {
72 if (pcd->peaks[i] > peak) {
73 peak = pcd->peaks[i];
74 pos = 0;
75 }
76 }
77 /* determine target gain */
78 gn = (1 << GAINSHIFT) * pcd->conf->target_level_arg / peak;
79 if (gn < (1 << GAINSHIFT))
80 gn = 1 << GAINSHIFT;
81 pcd->target_gain = (pcd->target_gain * ((1 << pcd->conf->gain_smooth_arg) - 1) + gn)
82 >> pcd->conf->gain_smooth_arg;
83 /* give it an extra insignificant nudge to counteract possible
84 * rounding error
85 */
86 if (gn < pcd->target_gain)
87 pcd->target_gain--;
88 else if (gn > pcd->target_gain)
89 pcd->target_gain++;
90 if (pcd->target_gain > pcd->conf->gain_max_arg << GAINSHIFT)
91 pcd->target_gain = pcd->conf->gain_max_arg << GAINSHIFT;
92 /* see if a peak is going to clip */
93 gn = (1 << GAINSHIFT) * 32768 / peak;
94 if (gn < pcd->target_gain) {
95 pcd->target_gain = gn;
96 if (pcd->conf->anticlip_given)
97 pos = 0;
98 } else
99 /* we're ramping up, so draw it out over the whole frame */
100 pos = length;
101 /* determine gain rate necessary to make target */
102 if (!pos)
103 pos = 1;
104 gr = ((pcd->target_gain - pcd->current_gain) << 16) / pos;
105 gf = pcd->current_gain << 16;
106 ip = audio;
107 op = (int16_t *)(fn->buf + fn->loaded);
108 for (i = 0; i < length / 2; i++) {
109 int sample;
110 /* interpolate the gain */
111 pcd->current_gain = gf >> 16;
112 if (i < pos)
113 gf += gr;
114 else if (i == pos)
115 gf = pcd->target_gain << 16;
116 /* amplify */
117 sample = (*ip++) * pcd->current_gain * pcd->conf->volume_arg / 10 >> GAINSHIFT;
118 if (sample < -32768) {
119 pcd->clip++;
120 sample = -32768;
121 } else if (sample > 32767) {
122 pcd->clip++;
123 sample = 32767;
124 }
125 *op++ = sample;
126 }
127 pcd->pn = (pcd->pn + 1) % pcd->conf->buckets_arg;
128 PARA_DEBUG_LOG("bucket: %03i, input len: %i, length: %i, peak: %05i, "
129 "current gain: %03i, clipped: %d\n", pcd->pn, inbuf_len,
130 length, peak, pcd->current_gain, pcd->clip);
131 fn->loaded = length;
132 return length;
133 }
134
135 static void close_compress(struct filter_node *fn)
136 {
137 struct private_compress_data *pcd = fn->private_data;
138 free(pcd->peaks);
139 free(fn->private_data);
140 free(fn->buf);
141 }
142
143 static void *compress_parse_config(int argc, char **argv)
144 {
145 struct gengetopt_args_info *ret = para_calloc(sizeof(struct gengetopt_args_info));
146 if (!compress_cmdline_parser(argc, argv, ret))
147 return ret;
148 free(ret);
149 return NULL;
150 }
151
152 static void open_compress(struct filter_node *fn)
153 {
154 struct private_compress_data *pcd = para_calloc(
155 sizeof(struct private_compress_data));
156 // compress_cmdline_parser(fn->argc, fn->argv, &pcd->conf);
157 pcd->conf = fn->conf;
158 pcd->peaks = para_calloc(pcd->conf->buckets_arg * sizeof(int));
159 fn->private_data = pcd;
160 fn->bufsize = COMPRESS_CHUNK_SIZE;
161 fn->buf = para_malloc(fn->bufsize);
162 fn->loaded = 0;
163 }
164
165 /** the init function of the compress filter */
166 void compress_init(struct filter *f)
167 {
168 f->open = open_compress;
169 f->close = close_compress;
170 f->convert = compress;
171 f->print_help = compress_cmdline_parser_print_help;
172 f->parse_config = compress_parse_config;
173 }