formats: clarify setting of reverse_bytes
[sox.git] / src / trim.c
blob5c28a360edb31aeab00a8894878a5d1dda82919c
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
23 #include "sox_i.h"
25 typedef struct {
26 /* parameters */
27 unsigned int num_pos;
28 struct {
29 uint64_t sample; /* NB: wide samples */
30 char *argstr;
31 } *pos;
32 /* state */
33 unsigned int current_pos;
34 uint64_t samples_read; /* NB: wide samples */
35 sox_bool copying;
36 } priv_t;
38 static int parse(sox_effect_t *effp, int argc, char **argv)
40 priv_t *p = (priv_t*) effp->priv;
41 unsigned int i;
42 --argc, ++argv;
43 p->num_pos = argc;
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, '+');
50 if (!arg || *arg) {
51 lsx_fail("Error parsing position %u", i+1);
52 return lsx_usage(effp);
55 return SOX_SUCCESS;
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;
64 sox_bool open_end;
65 unsigned int i;
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);
73 return SOX_EOF;
75 last_seen = p->pos[i].sample;
76 lsx_debug_more("position %u at %" PRIu64, i+1, last_seen);
79 /* sanity checks */
80 last_seen = 0;
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);
84 return SOX_EOF;
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");
98 p->num_pos--;
99 free(p->pos[p->num_pos].argstr);
101 if (p->num_pos == 1 && !p->pos[0].sample)
102 return SOX_EFF_NULL;
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;
108 else {
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);
113 if (open_end)
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;
119 return SOX_SUCCESS;
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;
128 len /= channels;
129 *isamp = *osamp = 0;
131 while (len) {
132 size_t chunk;
134 if (p->current_pos < p->num_pos &&
135 p->samples_read == p->pos[p->current_pos].sample) {
136 p->copying = !p->copying;
137 p->current_pos++;
140 if (p->current_pos >= p->num_pos && !p->copying)
141 return SOX_EOF;
143 chunk = p->current_pos < p->num_pos ?
144 min(len, p->pos[p->current_pos].sample - p->samples_read) : len;
145 if (p->copying) {
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;
153 return SOX_SUCCESS;
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 */
164 p->current_pos++;
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)"
173 return SOX_EOF;
176 static int lsx_kill(sox_effect_t *effp)
178 unsigned int i;
179 priv_t *p = (priv_t*) effp->priv;
180 for (i = 0; i < p->num_pos; i++)
181 free(p->pos[i].argstr);
182 free(p->pos);
183 return SOX_SUCCESS;
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,
192 sizeof(priv_t)
194 return &handler;
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
204 * samples. */
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;