1 /* libSoX effect: remix   Copyright (c) 2008-9 robs@users.sourceforge.net
2  *
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.
7  *
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.
12  *
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
16  */
17 
18 #include "sox_i.h"
19 #include <string.h>
20 
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;
34 
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)
47 
parse(sox_effect_t * effp,char ** argv,unsigned channels)48 static int parse(sox_effect_t * effp, char * * argv, unsigned channels)
49 {
50   priv_t * p = (priv_t *)effp->priv;
51   unsigned i, j;
52   double mult;
53 
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;
65 
66       PARSE(sep1, "%i", chan1, 0, separators);
67       if (!chan1) {
68        if (j || *text)
69          return lsx_usage(effp);
70        continue;
71       }
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;
81       }
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;
88       }
89       p->min_in_channels = max(p->min_in_channels, (unsigned)chan2);
90     }
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;
96   }
97   effp->out_signal.channels = p->num_out_channels;
98   return SOX_SUCCESS;
99 }
100 
show(priv_t * p)101 static int show(priv_t *p)
102 {
103   unsigned i, j;
104 
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);
109   }
110   return SOX_SUCCESS;
111 }
112 
create(sox_effect_t * effp,int argc,char ** argv)113 static int create(sox_effect_t * effp, int argc, char * * argv)
114 {
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;
123   }
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 */
127 }
128 
start(sox_effect_t * effp)129 static int start(sox_effect_t * effp)
130 {
131   priv_t * p = (priv_t *)effp->priv;
132   double max_sum = 0;
133   unsigned i, j;
134   int non_integer = 0;
135 
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;
140   }
141 
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;
148     }
149     max_sum = max(max_sum, sum);
150   }
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;
159 }
160 
flow(sox_effect_t * effp,const sox_sample_t * ibuf,sox_sample_t * obuf,size_t * isamp,size_t * osamp)161 static int flow(sox_effect_t * effp, const sox_sample_t * ibuf,
162     sox_sample_t * obuf, size_t * isamp, size_t * osamp)
163 {
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;
169 
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);
175   }
176   return SOX_SUCCESS;
177 }
178 
closedown(sox_effect_t * effp)179 static int closedown(sox_effect_t * effp)
180 {
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);
186   }
187   free(p->out_specs);
188   return SOX_SUCCESS;
189 }
190 
lsx_remix_effect_fn(void)191 sox_effect_handler_t const * lsx_remix_effect_fn(void)
192 {
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)
197   };
198   return &handler;
199 }
200 
201 /*----------------------- The `channels' effect alias ------------------------*/
202 
channels_create(sox_effect_t * effp,int argc,char ** argv)203 static int channels_create(sox_effect_t * effp, int argc, char * * argv)
204 {
205   priv_t * p = (priv_t *)effp->priv;
206   char dummy;     /* To check for extraneous chars. */
207 
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;
213   }
214   else if (argc != 1)
215     return lsx_usage(effp);
216   return SOX_SUCCESS;
217 }
218 
channels_start(sox_effect_t * effp)219 static int channels_start(sox_effect_t * effp)
220 {
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;
225 
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;
229 
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;
239       }
240     }
241   }
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;
247   }
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;
253 }
254 
lsx_channels_effect_fn(void)255 sox_effect_handler_t const * lsx_channels_effect_fn(void)
256 {
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;
265 }
266 
267 /*------------------------- The `oops' effect alias --------------------------*/
268 
oops_getopts(sox_effect_t * effp,int argc,char ** argv)269 static int oops_getopts(sox_effect_t *effp, int argc, char **argv)
270 {
271   char *args[] = {0, "1,2i", "1,2i"};
272   args[0] = argv[0];
273   return --argc? lsx_usage(effp) : create(effp, 3, args);
274 }
275 
lsx_oops_effect_fn(void)276 sox_effect_handler_t const * lsx_oops_effect_fn(void)
277 {
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;
284 }
285