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>
26 #include "compress_filter.cmdline.h"
31 /** how fine-grained the gain is */
33 /** the size of the output data buffer */
34 #define COMPRESS_CHUNK_SIZE 40960
36 /** data specific to the compress filter */
37 struct private_compress_data {
38 /** an array holding the previous peak values */
40 /** current bucket number to be modified */
42 /** number of times clipping occured */
44 /** the current multiplier */
46 /** the target multiplier */
48 /** pointer to the configuration data for this instance of the compress filter */
49 struct gengetopt_args_info *conf;
52 static ssize_t compress(char *inbuf, size_t inbuf_len, struct filter_node *fn)
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;
61 /* determine peak's value and position */
62 for (i = 0; i < length / 2; i++, ip++) {
69 pcd->peaks[pcd->pn] = peak;
70 for (i = 0; i < pcd->conf->buckets_arg; i++) {
71 if (pcd->peaks[i] > peak) {
76 /* determine target gain */
77 gn = (1 << GAINSHIFT) * pcd->conf->target_level_arg / peak;
78 if (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
85 if (gn < pcd->target_gain)
87 else if (gn > 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)
98 /* we're ramping up, so draw it out over the whole frame */
100 /* determine gain rate necessary to make target */
103 gr = ((pcd->target_gain - pcd->current_gain) << 16) / pos;
104 gf = pcd->current_gain << 16;
106 op = (int16_t *)(fn->buf + fn->loaded);
107 for (i = 0; i < length / 2; i++) {
109 /* interpolate the gain */
110 pcd->current_gain = gf >> 16;
114 gf = pcd->target_gain << 16;
116 sample = (*ip++) * pcd->current_gain * pcd->conf->volume_arg / 10 >> GAINSHIFT;
117 if (sample < -32768) {
120 } else if (sample > 32767) {
126 pcd->pn = (pcd->pn + 1) % pcd->conf->buckets_arg;
127 PARA_DEBUG_LOG("bucket: %03i, input len: %zd, length: %zd, peak: %05i, "
128 "current gain: %03i, clipped: %d\n", pcd->pn, inbuf_len,
129 length, peak, pcd->current_gain, pcd->clip);
134 static void close_compress(struct filter_node *fn)
136 struct private_compress_data *pcd = fn->private_data;
138 free(fn->private_data);
142 static void *compress_parse_config(int argc, char **argv)
144 struct gengetopt_args_info *ret = para_calloc(sizeof(struct gengetopt_args_info));
145 if (!compress_cmdline_parser(argc, argv, ret))
151 static void open_compress(struct filter_node *fn)
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);
164 /** the init function of the compress filter */
165 void compress_init(struct filter *f)
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;