a4d881f5afedb6057761f2aabcf789db8b4815e1
[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 "para.h"
26 #include "compress_filter.cmdline.h"
27 #include "list.h"
28 #include "filter.h"
29 #include "string.h"
30
31 /** how fine-grained the gain is */
32 #define GAINSHIFT 10
33 /** the size of the output data buffer */
34 #define COMPRESS_CHUNK_SIZE 40960
35
36 /** data specific to the compress filter */
37 struct private_compress_data {
38 /** an array holding the previous peak values */
39 int *peaks;
40 /** current bucket number to be modified */
41 unsigned pn;
42 /** number of times clipping occured */
43 unsigned clip;
44 /** the current multiplier */
45 int current_gain;
46 /** the target multiplier */
47 int target_gain;
48 /** pointer to the configuration data for this instance of the compress filter */
49 struct gengetopt_args_info *conf;
50 };
51
52 static ssize_t compress(char *inbuf, size_t inbuf_len, struct filter_node *fn)
53 {
54 int16_t *audio = (int16_t *) inbuf, *ip = audio, *op;
55 int peak = 1, pos = 0, i, gr, gf, gn;
56 size_t length = MIN((inbuf_len / 2) * 2, (fn->bufsize - fn->loaded) / 2 * 2);
57 struct private_compress_data *pcd = fn->private_data;
58
59 if (!length)
60 return 0;
61 /* determine peak's value and position */
62 for (i = 0; i < length / 2; i++, ip++) {
63 int val = ABS(*ip);
64 if (val > peak) {
65 peak = val;
66 pos = i;
67 }
68 }
69 pcd->peaks[pcd->pn] = peak;
70 for (i = 0; i < pcd->conf->buckets_arg; i++) {
71 if (pcd->peaks[i] > peak) {
72 peak = pcd->peaks[i];
73 pos = 0;
74 }
75 }
76 /* determine target gain */
77 gn = (1 << GAINSHIFT) * pcd->conf->target_level_arg / peak;
78 if (gn < (1 << GAINSHIFT))
79 gn = 1 << GAINSHIFT;
80 pcd->target_gain = (pcd->target_gain * ((1 << pcd->conf->gain_smooth_arg) - 1) + gn)
81 >> pcd->conf->gain_smooth_arg;
82 /* give it an extra insignificant nudge to counteract possible
83 * rounding error
84 */
85 if (gn < pcd->target_gain)
86 pcd->target_gain--;
87 else if (gn > pcd->target_gain)
88 pcd->target_gain++;
89 if (pcd->target_gain > pcd->conf->gain_max_arg << GAINSHIFT)
90 pcd->target_gain = pcd->conf->gain_max_arg << GAINSHIFT;
91 /* see if a peak is going to clip */
92 gn = (1 << GAINSHIFT) * 32768 / peak;
93 if (gn < pcd->target_gain) {
94 pcd->target_gain = gn;
95 if (pcd->conf->anticlip_given)
96 pos = 0;
97 } else
98 /* we're ramping up, so draw it out over the whole frame */
99 pos = length;
100 /* determine gain rate necessary to make target */
101 if (!pos)
102 pos = 1;
103 gr = ((pcd->target_gain - pcd->current_gain) << 16) / pos;
104 gf = pcd->current_gain << 16;
105 ip = audio;
106 op = (int16_t *)(fn->buf + fn->loaded);
107 for (i = 0; i < length / 2; i++) {
108 int sample;
109 /* interpolate the gain */
110 pcd->current_gain = gf >> 16;
111 if (i < pos)
112 gf += gr;
113 else if (i == pos)
114 gf = pcd->target_gain << 16;
115 /* amplify */
116 sample = (*ip++) * pcd->current_gain * pcd->conf->volume_arg / 10 >> GAINSHIFT;
117 if (sample < -32768) {
118 pcd->clip++;
119 sample = -32768;
120 } else if (sample > 32767) {
121 pcd->clip++;
122 sample = 32767;
123 }
124 *op++ = sample;
125 }
126 pcd->pn = (pcd->pn + 1) % pcd->conf->buckets_arg;
127 PARA_DEBUG_LOG("bucket: %03i, input len: %i, length: %i, peak: %05i, "
128 "current gain: %03i, clipped: %d\n", pcd->pn, inbuf_len,
129 length, peak, pcd->current_gain, pcd->clip);
130 fn->loaded = length;
131 return length;
132 }
133
134 static void close_compress(struct filter_node *fn)
135 {
136 struct private_compress_data *pcd = fn->private_data;
137 free(pcd->peaks);
138 free(fn->private_data);
139 free(fn->buf);
140 }
141
142 static void *compress_parse_config(int argc, char **argv)
143 {
144 struct gengetopt_args_info *ret = para_calloc(sizeof(struct gengetopt_args_info));
145 if (!compress_cmdline_parser(argc, argv, ret))
146 return ret;
147 free(ret);
148 return NULL;
149 }
150
151 static void open_compress(struct filter_node *fn)
152 {
153 struct private_compress_data *pcd = para_calloc(
154 sizeof(struct private_compress_data));
155 // compress_cmdline_parser(fn->argc, fn->argv, &pcd->conf);
156 pcd->conf = fn->conf;
157 pcd->peaks = para_calloc(pcd->conf->buckets_arg * sizeof(int));
158 fn->private_data = pcd;
159 fn->bufsize = COMPRESS_CHUNK_SIZE;
160 fn->buf = para_malloc(fn->bufsize);
161 fn->loaded = 0;
162 }
163
164 /** the init function of the compress filter */
165 void compress_init(struct filter *f)
166 {
167 f->open = open_compress;
168 f->close = close_compress;
169 f->convert = compress;
170 f->print_help = compress_cmdline_parser_print_help;
171 f->parse_config = compress_parse_config;
172 }