wav: simplify extended fmt chunk handling [bug #198]
[sox.git] / src / remix.c
blobffef0e8acfa48f27b5742d80073c84a97a845d0c
1 /* libSoX effect: remix Copyright (c) 2008-9 robs@users.sourceforge.net
3 * This library is free software; you can redistribute it and/or modify it
4 * under the terms of the GNU Lesser General Public License as published by
5 * the Free Software Foundation; either version 2.1 of the License, or (at
6 * your option) any later version.
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
11 * General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library; if not, write to the Free Software Foundation,
15 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 #include "sox_i.h"
19 #include <string.h>
21 typedef struct {
22 enum {semi, automatic, manual} mode;
23 sox_bool mix_power;
24 unsigned num_out_channels, min_in_channels;
25 struct {
26 char * str; /* Command-line argument to parse for this out_spec */
27 unsigned num_in_channels;
28 struct in_spec {
29 unsigned channel_num;
30 double multiplier;
31 } * in_specs;
32 } * out_specs;
33 } priv_t;
35 #define PARSE(SEP, SCAN, VAR, MIN, SEPARATORS) do {\
36 end = strpbrk(text, SEPARATORS); \
37 if (end == text) \
38 SEP = *text++; \
39 else { \
40 SEP = (SEPARATORS)[strlen(SEPARATORS) - 1]; \
41 n = sscanf(text, SCAN"%c", &VAR, &SEP); \
42 if (n == 0 || VAR < MIN || (n == 2 && !strchr(SEPARATORS, SEP))) \
43 return lsx_usage(effp); \
44 text = end? end + 1 : text + strlen(text); \
45 } \
46 } while (0)
48 static int parse(sox_effect_t * effp, char * * argv, unsigned channels)
50 priv_t * p = (priv_t *)effp->priv;
51 unsigned i, j;
52 double mult;
54 p->min_in_channels = 0;
55 for (i = 0; i < p->num_out_channels; ++i) {
56 sox_bool mul_spec = sox_false;
57 char * text, * end;
58 if (argv) /* 1st parse only */
59 p->out_specs[i].str = lsx_strdup(argv[i]);
60 for (j = 0, text = p->out_specs[i].str; *text;) {
61 static char const separators[] = "-vpi,";
62 char sep1, sep2;
63 int chan1 = 1, chan2 = channels, n;
64 double multiplier = HUGE_VAL;
66 PARSE(sep1, "%i", chan1, 0, separators);
67 if (!chan1) {
68 if (j || *text)
69 return lsx_usage(effp);
70 continue;
72 if (sep1 == '-')
73 PARSE(sep1, "%i", chan2, 0, separators + 1);
74 else chan2 = chan1;
75 if (sep1 != ',') {
76 multiplier = sep1 == 'v' ? 1 : 0;
77 PARSE(sep2, "%lf", multiplier, -HUGE_VAL, separators + 4);
78 if (sep1 != 'v')
79 multiplier = (sep1 == 'p'? 1 : -1) * dB_to_linear(multiplier);
80 mul_spec = sox_true;
82 if (chan2 < chan1) {int t = chan1; chan1 = chan2; chan2 = t;}
83 p->out_specs[i].in_specs = lsx_realloc(p->out_specs[i].in_specs,
84 (j + chan2 - chan1 + 1) * sizeof(*p->out_specs[i].in_specs));
85 while (chan1 <= chan2) {
86 p->out_specs[i].in_specs[j].channel_num = chan1++ - 1;
87 p->out_specs[i].in_specs[j++].multiplier = multiplier;
89 p->min_in_channels = max(p->min_in_channels, (unsigned)chan2);
91 p->out_specs[i].num_in_channels = j;
92 mult = 1. / (p->mix_power? sqrt((double)j) : j);
93 for (j = 0; j < p->out_specs[i].num_in_channels; ++j)
94 if (p->out_specs[i].in_specs[j].multiplier == HUGE_VAL)
95 p->out_specs[i].in_specs[j].multiplier = (p->mode == automatic || (p->mode == semi && !mul_spec)) ? mult : 1;
97 effp->out_signal.channels = p->num_out_channels;
98 return SOX_SUCCESS;
101 static int show(priv_t *p)
103 unsigned i, j;
105 for (j = 0; j < p->num_out_channels; j++) {
106 lsx_debug("%i: ", j);
107 for (i = 0; i < p->out_specs[j].num_in_channels; i++)
108 lsx_debug("\t%i %g", p->out_specs[j].in_specs[i].channel_num, p->out_specs[j].in_specs[i].multiplier);
110 return SOX_SUCCESS;
113 static int create(sox_effect_t * effp, int argc, char * * argv)
115 priv_t * p = (priv_t *)effp->priv;
116 --argc, ++argv;
117 if (argc && !strcmp(*argv, "-m")) p->mode = manual , ++argv, --argc;
118 if (argc && !strcmp(*argv, "-a")) p->mode = automatic, ++argv, --argc;
119 if (argc && !strcmp(*argv, "-p")) p->mix_power = sox_true, ++argv, --argc;
120 if (!argc) {
121 lsx_fail("must specify at least one output channel");
122 return SOX_EOF;
124 p->num_out_channels = argc;
125 p->out_specs = lsx_calloc(p->num_out_channels, sizeof(*p->out_specs));
126 return parse(effp, argv, 1); /* No channels yet; parse with dummy */
129 static int start(sox_effect_t * effp)
131 priv_t * p = (priv_t *)effp->priv;
132 double max_sum = 0;
133 unsigned i, j;
134 int non_integer = 0;
136 parse(effp, NULL, effp->in_signal.channels);
137 if (effp->in_signal.channels < p->min_in_channels) {
138 lsx_fail("too few input channels");
139 return SOX_EOF;
142 for (j = 0; j < effp->out_signal.channels; j++) {
143 double sum = 0;
144 for (i = 0; i < p->out_specs[j].num_in_channels; i++) {
145 double mult = p->out_specs[j].in_specs[i].multiplier;
146 sum += fabs(mult);
147 non_integer += floor(mult) != mult;
149 max_sum = max(max_sum, sum);
151 if (effp->in_signal.mult && max_sum > 1)
152 *effp->in_signal.mult /= max_sum;
153 if (!non_integer)
154 effp->out_signal.precision = effp->in_signal.precision;
155 else
156 effp->out_signal.precision = SOX_SAMPLE_PRECISION;
157 show(p);
158 return SOX_SUCCESS;
161 static int flow(sox_effect_t * effp, const sox_sample_t * ibuf,
162 sox_sample_t * obuf, size_t * isamp, size_t * osamp)
164 priv_t * p = (priv_t *)effp->priv;
165 unsigned i, j, len;
166 len = min(*isamp / effp->in_signal.channels, *osamp / effp->out_signal.channels);
167 *isamp = len * effp->in_signal.channels;
168 *osamp = len * effp->out_signal.channels;
170 for (; len--; ibuf += effp->in_signal.channels) for (j = 0; j < effp->out_signal.channels; j++) {
171 double out = 0;
172 for (i = 0; i < p->out_specs[j].num_in_channels; i++)
173 out += ibuf[p->out_specs[j].in_specs[i].channel_num] * p->out_specs[j].in_specs[i].multiplier;
174 *obuf++ = SOX_ROUND_CLIP_COUNT(out, effp->clips);
176 return SOX_SUCCESS;
179 static int closedown(sox_effect_t * effp)
181 priv_t * p = (priv_t *)effp->priv;
182 unsigned i;
183 for (i = 0; i < p->num_out_channels; ++i) {
184 free(p->out_specs[i].str);
185 free(p->out_specs[i].in_specs);
187 free(p->out_specs);
188 return SOX_SUCCESS;
191 sox_effect_handler_t const * lsx_remix_effect_fn(void)
193 static sox_effect_handler_t handler = {
194 "remix", "[-m|-a] [-p] <0|in-chan[v|p|i volume]{,in-chan[v|p|i volume]}>",
195 SOX_EFF_MCHAN | SOX_EFF_CHAN | SOX_EFF_GAIN | SOX_EFF_PREC,
196 create, start, flow, NULL, NULL, closedown, sizeof(priv_t)
198 return &handler;
201 /*----------------------- The `channels' effect alias ------------------------*/
203 static int channels_create(sox_effect_t * effp, int argc, char * * argv)
205 priv_t * p = (priv_t *)effp->priv;
206 char dummy; /* To check for extraneous chars. */
208 if (argc == 2) {
209 if (sscanf(argv[1], "%d %c", (int *)&p->num_out_channels,
210 &dummy) != 1 || (int)p->num_out_channels <= 0)
211 return lsx_usage(effp);
212 effp->out_signal.channels = p->num_out_channels;
214 else if (argc != 1)
215 return lsx_usage(effp);
216 return SOX_SUCCESS;
219 static int channels_start(sox_effect_t * effp)
221 priv_t * p = (priv_t *)effp->priv;
222 unsigned num_out_channels = p->num_out_channels != 0 ?
223 p->num_out_channels : effp->out_signal.channels;
224 unsigned i, j;
226 p->out_specs = lsx_calloc(num_out_channels, sizeof(*p->out_specs));
227 if (effp->in_signal.channels == num_out_channels)
228 return SOX_EFF_NULL;
230 if (effp->in_signal.channels > num_out_channels) {
231 for (j = 0; j < num_out_channels; j++) {
232 unsigned in_per_out = (effp->in_signal.channels +
233 num_out_channels - 1 - j) / num_out_channels;
234 lsx_valloc(p->out_specs[j].in_specs, in_per_out);
235 p->out_specs[j].num_in_channels = in_per_out;
236 for (i = 0; i < in_per_out; ++i) {
237 p->out_specs[j].in_specs[i].channel_num = i * num_out_channels + j;
238 p->out_specs[j].in_specs[i].multiplier = 1. / in_per_out;
242 else for (j = 0; j < num_out_channels; j++) {
243 lsx_valloc(p->out_specs[j].in_specs, 1);
244 p->out_specs[j].num_in_channels = 1;
245 p->out_specs[j].in_specs[0].channel_num = j % effp->in_signal.channels;
246 p->out_specs[j].in_specs[0].multiplier = 1;
248 effp->out_signal.channels = p->num_out_channels = num_out_channels;
249 effp->out_signal.precision = (effp->in_signal.channels > num_out_channels) ?
250 SOX_SAMPLE_PRECISION : effp->in_signal.precision;
251 show(p);
252 return SOX_SUCCESS;
255 sox_effect_handler_t const * lsx_channels_effect_fn(void)
257 static sox_effect_handler_t handler;
258 handler = *lsx_remix_effect_fn();
259 handler.name = "channels";
260 handler.usage = "number";
261 handler.flags &= ~SOX_EFF_GAIN;
262 handler.getopts = channels_create;
263 handler.start = channels_start;
264 return &handler;
267 /*------------------------- The `oops' effect alias --------------------------*/
269 static int oops_getopts(sox_effect_t *effp, int argc, char **argv)
271 char *args[] = {0, "1,2i", "1,2i"};
272 args[0] = argv[0];
273 return --argc? lsx_usage(effp) : create(effp, 3, args);
276 sox_effect_handler_t const * lsx_oops_effect_fn(void)
278 static sox_effect_handler_t handler;
279 handler = *lsx_remix_effect_fn();
280 handler.name = "oops";
281 handler.usage = NULL;
282 handler.getopts = oops_getopts;
283 return &handler;