whitespace cleanup
[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: %zd, length: %zd, 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 }