1 /* libSoX effect: splice audio 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
20 static double difference(
21 const sox_sample_t
* a
, const sox_sample_t
* b
, size_t length
)
26 #define _ diff += sqr((double)a[i] - b[i]), ++i; /* Loop optimisation */
27 do {_ _ _ _ _ _ _ _
} while (i
< length
); /* N.B. length ≡ 0 (mod 8) */
32 /* Find where the two segments are most alike over the overlap period. */
33 static size_t best_overlap_position(sox_sample_t
const * f1
,
34 sox_sample_t
const * f2
, uint64_t overlap
, uint64_t search
, size_t channels
)
36 size_t i
, best_pos
= 0;
37 double diff
, least_diff
= difference(f2
, f1
, (size_t) (channels
* overlap
));
39 for (i
= 1; i
< search
; ++i
) { /* linear search */
40 diff
= difference(f2
+ channels
* i
, f1
, (size_t) (channels
* overlap
));
41 if (diff
< least_diff
)
42 least_diff
= diff
, best_pos
= i
;
49 enum {Cosine_2
, Cosine_4
, Triangular
} fade_type
;
50 unsigned nsplices
; /* Number of splices requested */
52 char * str
; /* Command-line argument to parse for this splice */
53 uint64_t overlap
; /* Number of samples to overlap */
54 uint64_t search
; /* Number of samples to search */
55 uint64_t start
; /* Start splicing when in_pos equals this */
58 uint64_t in_pos
; /* Number of samples read from the input stream */
59 unsigned splices_pos
; /* Number of splices completed so far */
60 size_t buffer_pos
; /* Number of samples through the current splice */
61 size_t max_buffer_size
;
62 sox_sample_t
* buffer
;
66 static void splice(sox_effect_t
* effp
, const sox_sample_t
* in1
, const
67 sox_sample_t
* in2
, sox_sample_t
* output
, uint64_t overlap
, size_t channels
)
69 priv_t
* p
= (priv_t
*)effp
->priv
;
72 if (p
->fade_type
== Cosine_4
) {
73 double fade_step
= M_PI_2
/ overlap
;
74 for (i
= 0; i
< overlap
; ++i
) {
75 double fade_in
= sin(i
* fade_step
);
76 double fade_out
= cos(i
* fade_step
); /* constant RMS level (`power') */
77 for (j
= 0; j
< channels
; ++j
, ++k
) {
78 double d
= in1
[k
] * fade_out
+ in2
[k
] * fade_in
;
79 output
[k
] = SOX_ROUND_CLIP_COUNT(d
, effp
->clips
); /* Might clip */
83 else if (p
->fade_type
== Cosine_2
) {
84 double fade_step
= M_PI
/ overlap
;
85 for (i
= 0; i
< overlap
; ++i
) {
86 double fade_in
= .5 - .5 * cos(i
* fade_step
);
87 double fade_out
= 1 - fade_in
; /* constant peak level (`gain') */
88 for (j
= 0; j
< channels
; ++j
, ++k
) {
89 double d
= in1
[k
] * fade_out
+ in2
[k
] * fade_in
;
90 output
[k
] = SOX_ROUND_CLIP_COUNT(d
, effp
->clips
); /* Should not clip */
94 else /* Triangular */ {
95 double fade_step
= 1. / overlap
;
96 for (i
= 0; i
< overlap
; ++i
) {
97 double fade_in
= fade_step
* i
;
98 double fade_out
= 1 - fade_in
; /* constant peak level (`gain') */
99 for (j
= 0; j
< channels
; ++j
, ++k
) {
100 double d
= in1
[k
] * fade_out
+ in2
[k
] * fade_in
;
101 output
[k
] = SOX_ROUND_CLIP_COUNT(d
, effp
->clips
); /* Should not clip */
107 static uint64_t do_splice(sox_effect_t
* effp
,
108 sox_sample_t
* f
, uint64_t overlap
, uint64_t search
, size_t channels
)
110 uint64_t offset
= search
? best_overlap_position(
111 f
, f
+ overlap
* channels
, overlap
, search
, channels
) : 0;
112 splice(effp
, f
, f
+ (overlap
+ offset
) * channels
,
113 f
+ (overlap
+ offset
) * channels
, overlap
, channels
);
114 return overlap
+ offset
;
117 static int parse(sox_effect_t
* effp
, char * * argv
, sox_rate_t rate
)
119 priv_t
* p
= (priv_t
*)effp
->priv
;
121 size_t i
, buffer_size
;
122 uint64_t last_seen
= 0;
123 const uint64_t in_length
= argv
? 0 :
124 (effp
->in_signal
.length
!= SOX_UNKNOWN_LEN
?
125 effp
->in_signal
.length
/ effp
->in_signal
.channels
: SOX_UNKNOWN_LEN
);
127 p
->max_buffer_size
= 0;
128 for (i
= 0; i
< p
->nsplices
; ++i
) {
129 if (argv
) /* 1st parse only */
130 p
->splices
[i
].str
= lsx_strdup(argv
[i
]);
132 p
->splices
[i
].overlap
= rate
* 0.01 + .5;
133 p
->splices
[i
].search
= p
->fade_type
== Cosine_4
? 0 : p
->splices
[i
].overlap
;
135 next
= lsx_parseposition(rate
, p
->splices
[i
].str
,
136 argv
? NULL
: &p
->splices
[i
].start
, last_seen
, in_length
, '=');
137 if (next
== NULL
) break;
138 last_seen
= p
->splices
[i
].start
;
141 next
= lsx_parsesamples(rate
, next
+ 1, &p
->splices
[i
].overlap
, 't');
142 if (next
== NULL
) break;
143 p
->splices
[i
].overlap
*= 2;
145 next
= lsx_parsesamples(rate
, next
+ 1, &p
->splices
[i
].search
, 't');
146 if (next
== NULL
) break;
147 p
->splices
[i
].search
*= 2;
150 if (*next
!= '\0') break;
151 p
->splices
[i
].overlap
= max(p
->splices
[i
].overlap
+ 4, 16);
152 p
->splices
[i
].overlap
&= ~7; /* Make divisible by 8 for loop optimisation */
155 if (i
> 0 && p
->splices
[i
].start
<= p
->splices
[i
-1].start
) break;
156 if (p
->splices
[i
].start
< p
->splices
[i
].overlap
) break;
157 p
->splices
[i
].start
-= p
->splices
[i
].overlap
;
158 buffer_size
= 2 * p
->splices
[i
].overlap
+ p
->splices
[i
].search
;
159 p
->max_buffer_size
= max(p
->max_buffer_size
, buffer_size
);
163 return lsx_usage(effp
);
167 static int create(sox_effect_t
* effp
, int argc
, char * * argv
)
169 priv_t
* p
= (priv_t
*)effp
->priv
;
172 if (!strcmp(*argv
, "-t")) p
->fade_type
= Triangular
, --argc
, ++argv
;
173 else if (!strcmp(*argv
, "-q")) p
->fade_type
= Cosine_4
, --argc
, ++argv
;
174 else if (!strcmp(*argv
, "-h")) p
->fade_type
= Cosine_2
, --argc
, ++argv
;
177 p
->splices
= lsx_calloc(p
->nsplices
, sizeof(*p
->splices
));
178 return parse(effp
, argv
, 1e5
); /* No rate yet; parse with dummy */
181 static int start(sox_effect_t
* effp
)
183 priv_t
* p
= (priv_t
*)effp
->priv
;
186 parse(effp
, 0, effp
->in_signal
.rate
); /* Re-parse now rate is known */
187 p
->buffer
= lsx_calloc(p
->max_buffer_size
* effp
->in_signal
.channels
, sizeof(*p
->buffer
));
188 p
->in_pos
= p
->buffer_pos
= p
->splices_pos
= 0;
189 p
->state
= p
->splices_pos
!= p
->nsplices
&& p
->in_pos
== p
->splices
[p
->splices_pos
].start
;
190 effp
->out_signal
.length
= SOX_UNKNOWN_LEN
; /* depends on input data */
191 for (i
= 0; i
< p
->nsplices
; ++i
)
192 if (p
->splices
[i
].overlap
) {
193 if (p
->fade_type
== Cosine_4
&& effp
->in_signal
.mult
)
194 *effp
->in_signal
.mult
*= pow(.5, .5);
200 static int flow(sox_effect_t
* effp
, const sox_sample_t
* ibuf
,
201 sox_sample_t
* obuf
, size_t * isamp
, size_t * osamp
)
203 priv_t
* p
= (priv_t
*)effp
->priv
;
204 size_t c
, idone
= 0, odone
= 0;
205 *isamp
/= effp
->in_signal
.channels
;
206 *osamp
/= effp
->in_signal
.channels
;
211 for (; idone
< *isamp
&& odone
< *osamp
; ++idone
, ++odone
, ++p
->in_pos
) {
212 if (p
->splices_pos
!= p
->nsplices
&& p
->in_pos
== p
->splices
[p
->splices_pos
].start
) {
216 for (c
= 0; c
< effp
->in_signal
.channels
; ++c
)
224 size_t buffer_size
= (2 * p
->splices
[p
->splices_pos
].overlap
+ p
->splices
[p
->splices_pos
].search
) * effp
->in_signal
.channels
;
225 for (; idone
< *isamp
; ++idone
, ++p
->in_pos
) {
226 if (p
->buffer_pos
== buffer_size
) {
227 p
->buffer_pos
= do_splice(effp
, p
->buffer
,
228 p
->splices
[p
->splices_pos
].overlap
,
229 p
->splices
[p
->splices_pos
].search
,
230 (size_t)effp
->in_signal
.channels
) * effp
->in_signal
.channels
;
235 for (c
= 0; c
< effp
->in_signal
.channels
; ++c
)
236 p
->buffer
[p
->buffer_pos
++] = *ibuf
++;
243 size_t buffer_size
= (2 * p
->splices
[p
->splices_pos
].overlap
+ p
->splices
[p
->splices_pos
].search
) * effp
->in_signal
.channels
;
244 for (; odone
< *osamp
; ++odone
) {
245 if (p
->buffer_pos
== buffer_size
) {
248 p
->state
= p
->splices_pos
!= p
->nsplices
&& p
->in_pos
== p
->splices
[p
->splices_pos
].start
;
251 for (c
= 0; c
< effp
->in_signal
.channels
; ++c
)
252 *obuf
++ = p
->buffer
[p
->buffer_pos
++];
258 *isamp
= idone
* effp
->in_signal
.channels
;
259 *osamp
= odone
* effp
->in_signal
.channels
;
263 static int drain(sox_effect_t
* effp
, sox_sample_t
* obuf
, size_t * osamp
)
266 return flow(effp
, 0, obuf
, &isamp
, osamp
);
269 static int stop(sox_effect_t
* effp
)
271 priv_t
* p
= (priv_t
*)effp
->priv
;
272 if (p
->splices_pos
!= p
->nsplices
)
273 lsx_warn("Input audio too short; splices not made: %u", p
->nsplices
- p
->splices_pos
);
278 static int lsx_kill(sox_effect_t
* effp
)
280 priv_t
* p
= (priv_t
*)effp
->priv
;
282 for (i
= 0; i
< p
->nsplices
; ++i
)
283 free(p
->splices
[i
].str
);
288 sox_effect_handler_t
const * lsx_splice_effect_fn(void)
290 static sox_effect_handler_t handler
= {
291 "splice", "[-h|-t|-q] {position[,excess[,leeway]]}"
292 "\n -h Half sine fade (default); constant gain (for correlated audio)"
293 "\n -t Triangular (linear) fade; constant gain (for correlated audio)"
294 "\n -q Quarter sine fade; constant power (for correlated audio e.g. x-fade)"
295 "\n position The length of part 1 (including the excess)"
296 "\n excess At the end of part 1 & the start of part2 (default 0.005)"
297 "\n leeway Before part2 (default 0.005; set to 0 for cross-fade)",
298 SOX_EFF_MCHAN
| SOX_EFF_LENGTH
,
299 create
, start
, flow
, drain
, stop
, lsx_kill
, sizeof(priv_t
)