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
22 enum {semi
, automatic
, manual
} mode
;
24 unsigned num_out_channels
, min_in_channels
;
26 char * str
; /* Command-line argument to parse for this out_spec */
27 unsigned num_in_channels
;
35 #define PARSE(SEP, SCAN, VAR, MIN, SEPARATORS) do {\
36 end = strpbrk(text, SEPARATORS); \
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); \
48 static int parse(sox_effect_t
* effp
, char * * argv
, unsigned channels
)
50 priv_t
* p
= (priv_t
*)effp
->priv
;
54 p
->min_in_channels
= 0;
55 for (i
= 0; i
< p
->num_out_channels
; ++i
) {
56 sox_bool mul_spec
= sox_false
;
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,";
63 int chan1
= 1, chan2
= channels
, n
;
64 double multiplier
= HUGE_VAL
;
66 PARSE(sep1
, "%i", chan1
, 0, separators
);
69 return lsx_usage(effp
);
73 PARSE(sep1
, "%i", chan2
, 0, separators
+ 1);
76 multiplier
= sep1
== 'v' ? 1 : 0;
77 PARSE(sep2
, "%lf", multiplier
, -HUGE_VAL
, separators
+ 4);
79 multiplier
= (sep1
== 'p'? 1 : -1) * dB_to_linear(multiplier
);
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
;
101 static int show(priv_t
*p
)
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
);
113 static int create(sox_effect_t
* effp
, int argc
, char * * argv
)
115 priv_t
* p
= (priv_t
*)effp
->priv
;
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
;
121 lsx_fail("must specify at least one output channel");
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
;
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");
142 for (j
= 0; j
< effp
->out_signal
.channels
; j
++) {
144 for (i
= 0; i
< p
->out_specs
[j
].num_in_channels
; i
++) {
145 double mult
= p
->out_specs
[j
].in_specs
[i
].multiplier
;
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
;
154 effp
->out_signal
.precision
= effp
->in_signal
.precision
;
156 effp
->out_signal
.precision
= SOX_SAMPLE_PRECISION
;
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
;
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
++) {
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
);
179 static int closedown(sox_effect_t
* effp
)
181 priv_t
* p
= (priv_t
*)effp
->priv
;
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
);
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
)
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. */
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
;
215 return lsx_usage(effp
);
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
;
226 p
->out_specs
= lsx_calloc(num_out_channels
, sizeof(*p
->out_specs
));
227 if (effp
->in_signal
.channels
== num_out_channels
)
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
;
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
;
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"};
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
;