1 /* libSoX effect: Pad With Silence   (c) 2006 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 
20 typedef struct {
21   unsigned npads;     /* Number of pads requested */
22   struct {
23     char * str;       /* Command-line argument to parse for this pad */
24     uint64_t start; /* Start padding when in_pos equals this */
25     uint64_t pad;   /* Number of samples to pad */
26   } * pads;
27 
28   uint64_t in_pos;  /* Number of samples read from the input stream */
29   unsigned pads_pos;  /* Number of pads completed so far */
30   uint64_t pad_pos; /* Number of samples through the current pad */
31 } priv_t;
32 
parse(sox_effect_t * effp,char ** argv,sox_rate_t rate)33 static int parse(sox_effect_t * effp, char * * argv, sox_rate_t rate)
34 {
35   priv_t * p = (priv_t *)effp->priv;
36   char const * next;
37   unsigned i;
38   uint64_t last_seen = 0;
39   const uint64_t in_length = argv ? 0 :
40     (effp->in_signal.length != SOX_UNKNOWN_LEN ?
41      effp->in_signal.length / effp->in_signal.channels : SOX_UNKNOWN_LEN);
42 
43   for (i = 0; i < p->npads; ++i) {
44     if (argv) /* 1st parse only */
45       p->pads[i].str = lsx_strdup(argv[i]);
46     next = lsx_parsesamples(rate, p->pads[i].str, &p->pads[i].pad, 't');
47     if (next == NULL) break;
48     if (*next == '\0')
49       p->pads[i].start = i? UINT64_MAX : 0;
50     else {
51       if (*next != '@') break;
52       next = lsx_parseposition(rate, next+1, argv ? NULL : &p->pads[i].start,
53                last_seen, in_length, '=');
54       if (next == NULL || *next != '\0') break;
55       last_seen = p->pads[i].start;
56       if (p->pads[i].start == SOX_UNKNOWN_LEN)
57         p->pads[i].start = UINT64_MAX; /* currently the same value, but ... */
58     }
59     if (!argv) {
60       /* Do this check only during the second pass when the actual
61          sample rate is known, otherwise it might fail on legal
62          commands like
63            pad 1@0.5 1@30000s
64          if the rate is, e.g., 48k. */
65       if (i > 0 && p->pads[i].start <= p->pads[i-1].start) break;
66     }
67   }
68   if (i < p->npads)
69     return lsx_usage(effp);
70   return SOX_SUCCESS;
71 }
72 
create(sox_effect_t * effp,int argc,char ** argv)73 static int create(sox_effect_t * effp, int argc, char * * argv)
74 {
75   priv_t * p = (priv_t *)effp->priv;
76   --argc, ++argv;
77   p->npads = argc;
78   p->pads = lsx_calloc(p->npads, sizeof(*p->pads));
79   return parse(effp, argv, 1e5); /* No rate yet; parse with dummy */
80 }
81 
start(sox_effect_t * effp)82 static int start(sox_effect_t * effp)
83 {
84   priv_t * p = (priv_t *)effp->priv;
85   unsigned i;
86 
87   /* Re-parse now rate is known */
88   if (parse(effp, 0, effp->in_signal.rate) != SOX_SUCCESS)
89     return SOX_EOF;
90 
91   if ((effp->out_signal.length = effp->in_signal.length) != SOX_UNKNOWN_LEN) {
92     for (i = 0; i < p->npads; ++i)
93       effp->out_signal.length +=
94         p->pads[i].pad * effp->in_signal.channels;
95 
96     /* Check that the last pad position (except for "at the end")
97        is within bounds. */
98     i = p->npads;
99     if (i > 0 && p->pads[i-1].start == UINT64_MAX)
100       i--;
101     if (i > 0 &&
102         p->pads[i-1].start * effp->in_signal.channels
103           > effp->in_signal.length)
104     {
105       lsx_fail("pad position after end of audio");
106       return SOX_EOF;
107     }
108   }
109 
110   p->in_pos = p->pad_pos = p->pads_pos = 0;
111   for (i = 0; i < p->npads; ++i)
112     if (p->pads[i].pad)
113       return SOX_SUCCESS;
114   return SOX_EFF_NULL;
115 }
116 
flow(sox_effect_t * effp,const sox_sample_t * ibuf,sox_sample_t * obuf,size_t * isamp,size_t * osamp)117 static int flow(sox_effect_t * effp, const sox_sample_t * ibuf,
118     sox_sample_t * obuf, size_t * isamp, size_t * osamp)
119 {
120   priv_t * p = (priv_t *)effp->priv;
121   size_t c, idone = 0, odone = 0;
122   *isamp /= effp->in_signal.channels;
123   *osamp /= effp->in_signal.channels;
124 
125   do {
126     /* Copying: */
127     for (; idone < *isamp && odone < *osamp && !(p->pads_pos != p->npads && p->in_pos == p->pads[p->pads_pos].start); ++idone, ++odone, ++p->in_pos)
128       for (c = 0; c < effp->in_signal.channels; ++c) *obuf++ = *ibuf++;
129 
130     /* Padding: */
131     if (p->pads_pos != p->npads && p->in_pos == p->pads[p->pads_pos].start) {
132       for (; odone < *osamp && p->pad_pos < p->pads[p->pads_pos].pad; ++odone, ++p->pad_pos)
133         for (c = 0; c < effp->in_signal.channels; ++c) *obuf++ = 0;
134       if (p->pad_pos == p->pads[p->pads_pos].pad) { /* Move to next pad? */
135         ++p->pads_pos;
136         p->pad_pos = 0;
137       }
138     }
139   } while (idone < *isamp && odone < *osamp);
140 
141   *isamp = idone * effp->in_signal.channels;
142   *osamp = odone * effp->in_signal.channels;
143   return SOX_SUCCESS;
144 }
145 
drain(sox_effect_t * effp,sox_sample_t * obuf,size_t * osamp)146 static int drain(sox_effect_t * effp, sox_sample_t * obuf, size_t * osamp)
147 {
148   priv_t * p = (priv_t *)effp->priv;
149   static size_t isamp = 0;
150   if (p->pads_pos != p->npads && p->in_pos != p->pads[p->pads_pos].start)
151     p->in_pos = UINT64_MAX;  /* Invoke the final pad (with no given start) */
152   return flow(effp, 0, obuf, &isamp, osamp);
153 }
154 
stop(sox_effect_t * effp)155 static int stop(sox_effect_t * effp)
156 {
157   priv_t * p = (priv_t *)effp->priv;
158   if (p->pads_pos != p->npads)
159     lsx_warn("Input audio too short; pads not applied: %u", p->npads-p->pads_pos);
160   return SOX_SUCCESS;
161 }
162 
lsx_kill(sox_effect_t * effp)163 static int lsx_kill(sox_effect_t * effp)
164 {
165   priv_t * p = (priv_t *)effp->priv;
166   unsigned i;
167   for (i = 0; i < p->npads; ++i)
168     free(p->pads[i].str);
169   free(p->pads);
170   return SOX_SUCCESS;
171 }
172 
lsx_pad_effect_fn(void)173 sox_effect_handler_t const * lsx_pad_effect_fn(void)
174 {
175   static sox_effect_handler_t handler = {
176     "pad", "{length[@position]}", SOX_EFF_MCHAN|SOX_EFF_LENGTH|SOX_EFF_MODIFY,
177     create, start, flow, drain, stop, lsx_kill, sizeof(priv_t)
178   };
179   return &handler;
180 }
181