1 /* libSoX effect: trim - cut portions out of the audio
3 * First version written 01/2012 by Ulrich Klauer.
4 * Replaces an older trim effect originally written by Curt Zirzow in 2000.
6 * Copyright 2012 Chris Bagwell and SoX Contributors
8 * This library is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or (at
11 * your option) any later version.
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
16 * General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this library; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
29 uint64_t sample
; /* NB: wide samples */
33 unsigned int current_pos
;
34 uint64_t samples_read
; /* NB: wide samples */
38 static int parse(sox_effect_t
*effp
, int argc
, char **argv
)
40 priv_t
*p
= (priv_t
*) effp
->priv
;
44 lsx_Calloc(p
->pos
, p
->num_pos
);
45 for (i
= 0; i
< p
->num_pos
; i
++) {
46 const char *arg
= argv
[i
];
47 p
->pos
[i
].argstr
= lsx_strdup(arg
);
48 /* dummy parse to check for syntax errors */
49 arg
= lsx_parseposition(0., arg
, NULL
, (uint64_t)0, (uint64_t)0, '+');
51 lsx_fail("Error parsing position %u", i
+1);
52 return lsx_usage(effp
);
58 static int start(sox_effect_t
*effp
)
60 priv_t
*p
= (priv_t
*) effp
->priv
;
61 uint64_t in_length
= effp
->in_signal
.length
!= SOX_UNKNOWN_LEN
?
62 effp
->in_signal
.length
/ effp
->in_signal
.channels
: SOX_UNKNOWN_LEN
;
63 uint64_t last_seen
= 0;
67 p
->copying
= sox_false
;
69 /* calculate absolute positions */
70 for (i
= 0; i
< p
->num_pos
; i
++) {
71 if (!lsx_parseposition(effp
->in_signal
.rate
, p
->pos
[i
].argstr
, &p
->pos
[i
].sample
, last_seen
, in_length
, '+')) {
72 lsx_fail("Position %u is relative to end of audio, but audio length is unknown", i
+1);
75 last_seen
= p
->pos
[i
].sample
;
76 lsx_debug_more("position %u at %" PRIu64
, i
+1, last_seen
);
81 for (i
= 0; i
< p
->num_pos
; i
++) {
82 if (p
->pos
[i
].sample
< last_seen
) {
83 lsx_fail("Position %u is behind the following position.", i
);
86 last_seen
= p
->pos
[i
].sample
;
88 if (p
->num_pos
&& in_length
!= SOX_UNKNOWN_LEN
)
89 if (p
->pos
[0].sample
> in_length
||
90 p
->pos
[p
->num_pos
-1].sample
> in_length
)
91 lsx_warn("%s position is after expected end of audio.",
92 p
->pos
[0].sample
> in_length
? "Start" : "End");
94 /* avoid unnecessary work */
95 if (in_length
== SOX_UNKNOWN_LEN
)
96 while (p
->num_pos
&& p
->pos
[p
->num_pos
-1].sample
== SOX_UNKNOWN_LEN
) {
97 lsx_debug_more("removing `-0' position");
99 free(p
->pos
[p
->num_pos
].argstr
);
101 if (p
->num_pos
== 1 && !p
->pos
[0].sample
)
104 /* calculate output length */
105 open_end
= p
->num_pos
% 2;
106 if (open_end
&& in_length
== SOX_UNKNOWN_LEN
)
107 effp
->out_signal
.length
= SOX_UNKNOWN_LEN
;
109 effp
->out_signal
.length
= 0;
110 for (i
= 0; i
+1 < p
->num_pos
; i
+= 2)
111 effp
->out_signal
.length
+=
112 min(p
->pos
[i
+1].sample
, in_length
) - min(p
->pos
[i
].sample
, in_length
);
114 effp
->out_signal
.length
+=
115 in_length
- min(p
->pos
[p
->num_pos
-1].sample
, in_length
);
116 effp
->out_signal
.length
*= effp
->in_signal
.channels
;
122 static int flow(sox_effect_t
*effp
, const sox_sample_t
*ibuf
,
123 sox_sample_t
*obuf
, size_t *isamp
, size_t *osamp
)
125 priv_t
*p
= (priv_t
*) effp
->priv
;
126 size_t len
= min(*isamp
, *osamp
);
127 size_t channels
= effp
->in_signal
.channels
;
134 if (p
->current_pos
< p
->num_pos
&&
135 p
->samples_read
== p
->pos
[p
->current_pos
].sample
) {
136 p
->copying
= !p
->copying
;
140 if (p
->current_pos
>= p
->num_pos
&& !p
->copying
)
143 chunk
= p
->current_pos
< p
->num_pos
?
144 min(len
, p
->pos
[p
->current_pos
].sample
- p
->samples_read
) : len
;
146 memcpy(obuf
, ibuf
, chunk
* channels
* sizeof(*obuf
));
147 obuf
+= chunk
* channels
, *osamp
+= chunk
* channels
;
149 ibuf
+= chunk
* channels
; *isamp
+= chunk
* channels
;
150 p
->samples_read
+= chunk
, len
-= chunk
;
156 static int drain(sox_effect_t
*effp
, sox_sample_t
*obuf UNUSED
, size_t *osamp
)
158 priv_t
*p
= (priv_t
*) effp
->priv
;
159 *osamp
= 0; /* only checking for errors */
161 if (p
->current_pos
+ 1 == p
->num_pos
&&
162 p
->pos
[p
->current_pos
].sample
== p
->samples_read
&&
163 p
->copying
) /* would stop here anyway */
165 if (p
->current_pos
< p
->num_pos
)
166 lsx_warn("Last %u position(s) not reached%s.",
167 p
->num_pos
- p
->current_pos
,
168 (effp
->in_signal
.length
== SOX_UNKNOWN_LEN
||
169 effp
->in_signal
.length
/effp
->in_signal
.channels
== p
->samples_read
) ?
170 "" /* unknown length, or did already warn during start() */ :
171 " (audio shorter than expected)"
176 static int lsx_kill(sox_effect_t
*effp
)
179 priv_t
*p
= (priv_t
*) effp
->priv
;
180 for (i
= 0; i
< p
->num_pos
; i
++)
181 free(p
->pos
[i
].argstr
);
186 sox_effect_handler_t
const *lsx_trim_effect_fn(void)
188 static sox_effect_handler_t handler
= {
189 "trim", "{position}",
190 SOX_EFF_MCHAN
| SOX_EFF_LENGTH
| SOX_EFF_MODIFY
,
191 parse
, start
, flow
, drain
, NULL
, lsx_kill
,
197 /* The following functions allow a libSoX client to do a speed
198 * optimization, by asking for the number of samples to be skipped
199 * at the beginning of the audio with sox_trim_get_start(), skipping
200 * that many samples in an efficient way such as seeking within the
201 * input file, then telling us it has been done by calling
202 * sox_trim_clear_start() (the name is historical).
203 * Note that sox_trim_get_start() returns the number of non-wide
206 sox_uint64_t
sox_trim_get_start(sox_effect_t
*effp
)
208 priv_t
*p
= (priv_t
*) effp
->priv
;
209 return p
->num_pos
? p
->pos
[0].sample
* effp
->in_signal
.channels
: 0;
212 void sox_trim_clear_start(sox_effect_t
*effp
)
214 priv_t
*p
= (priv_t
*) effp
->priv
;
215 p
->samples_read
= p
->num_pos
? p
->pos
[0].sample
: 0;