2 * Copyright (C) 2005-2006 Andre Noll <maan@systemlinux.org>
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.
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.
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.
19 /** \file compress.c paraslash's dynamic audio range compressor */
22 * Based on AudioCompress, (C) 2002-2004 M. Hari Nezumi <magenta@trikuare.cx>
25 #include "gcc-compat.h"
27 #include "compress_filter.cmdline.h"
32 /** how fine-grained the gain is */
34 /** the size of the output data buffer */
35 #define COMPRESS_CHUNK_SIZE 40960
37 /** data specific to the compress filter */
38 struct private_compress_data {
39 /** an array holding the previous peak values */
41 /** current bucket number to be modified */
43 /** number of times clipping occured */
45 /** the current multiplier */
47 /** the target multiplier */
49 /** pointer to the configuration data for this instance of the compress filter */
50 struct gengetopt_args_info *conf;
53 static ssize_t compress(char *inbuf, size_t inbuf_len, struct filter_node *fn)
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;
62 /* determine peak's value and position */
63 for (i = 0; i < length / 2; i++, ip++) {
70 pcd->peaks[pcd->pn] = peak;
71 for (i = 0; i < pcd->conf->buckets_arg; i++) {
72 if (pcd->peaks[i] > peak) {
77 /* determine target gain */
78 gn = (1 << GAINSHIFT) * pcd->conf->target_level_arg / peak;
79 if (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
86 if (gn < pcd->target_gain)
88 else if (gn > 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)
99 /* we're ramping up, so draw it out over the whole frame */
101 /* determine gain rate necessary to make target */
104 gr = ((pcd->target_gain - pcd->current_gain) << 16) / pos;
105 gf = pcd->current_gain << 16;
107 op = (int16_t *)(fn->buf + fn->loaded);
108 for (i = 0; i < length / 2; i++) {
110 /* interpolate the gain */
111 pcd->current_gain = gf >> 16;
115 gf = pcd->target_gain << 16;
117 sample = (*ip++) * pcd->current_gain * pcd->conf->volume_arg / 10 >> GAINSHIFT;
118 if (sample < -32768) {
121 } else if (sample > 32767) {
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);
135 static void close_compress(struct filter_node *fn)
137 struct private_compress_data *pcd = fn->private_data;
139 free(fn->private_data);
143 static void *compress_parse_config(int argc, char **argv)
145 struct gengetopt_args_info *ret = para_calloc(sizeof(struct gengetopt_args_info));
146 if (!compress_cmdline_parser(argc, argv, ret))
152 static void open_compress(struct filter_node *fn)
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);
165 /** the init function of the compress filter */
166 void compress_init(struct filter *f)
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;