1 /* Implements a libSoX internal interface for implementing effects.
2  * All public functions & data are prefixed with lsx_ .
3  *
4  * Copyright (c) 2005-2012 Chris Bagwell and SoX contributors
5  *
6  * This library is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1 of the License, or (at
9  * your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this library; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20 
21 #define LSX_EFF_ALIAS
22 #include "sox_i.h"
23 #include <string.h>
24 #include <ctype.h>
25 
lsx_usage(sox_effect_t * effp)26 int lsx_usage(sox_effect_t * effp)
27 {
28   if (effp->handler.usage)
29     lsx_fail("usage: %s", effp->handler.usage);
30   else
31     lsx_fail("this effect takes no parameters");
32   return SOX_EOF;
33 }
34 
lsx_usage_lines(char ** usage,char const * const * lines,size_t n)35 char * lsx_usage_lines(char * * usage, char const * const * lines, size_t n)
36 {
37   if (!*usage) {
38     size_t i, len;
39     for (len = i = 0; i < n; len += strlen(lines[i++]) + 1);
40     *usage = lsx_malloc(len); /* FIXME: this memory will never be freed */
41     strcpy(*usage, lines[0]);
42     for (i = 1; i < n; ++i) {
43       strcat(*usage, "\n");
44       strcat(*usage, lines[i]);
45     }
46   }
47   return *usage;
48 }
49 
50 static lsx_enum_item const s_lsx_wave_enum[] = {
51   LSX_ENUM_ITEM(SOX_WAVE_,SINE)
52   LSX_ENUM_ITEM(SOX_WAVE_,TRIANGLE)
53   {0, 0}};
54 
lsx_get_wave_enum(void)55 lsx_enum_item const * lsx_get_wave_enum(void)
56 {
57   return s_lsx_wave_enum;
58 }
59 
lsx_generate_wave_table(lsx_wave_t wave_type,sox_data_t data_type,void * table,size_t table_size,double min,double max,double phase)60 void lsx_generate_wave_table(
61     lsx_wave_t wave_type,
62     sox_data_t data_type,
63     void *table,
64     size_t table_size,
65     double min,
66     double max,
67     double phase)
68 {
69   uint32_t t;
70   uint32_t phase_offset = phase / M_PI / 2 * table_size + 0.5;
71 
72   for (t = 0; t < table_size; t++)
73   {
74     uint32_t point = (t + phase_offset) % table_size;
75     double d;
76     switch (wave_type)
77     {
78       case SOX_WAVE_SINE:
79       d = (sin((double)point / table_size * 2 * M_PI) + 1) / 2;
80       break;
81 
82       case SOX_WAVE_TRIANGLE:
83       d = (double)point * 2 / table_size;
84       switch (4 * point / table_size)
85       {
86         case 0:         d = d + 0.5; break;
87         case 1: case 2: d = 1.5 - d; break;
88         case 3:         d = d - 1.5; break;
89       }
90       break;
91 
92       default: /* Oops! FIXME */
93         d = 0.0; /* Make sure we have a value */
94       break;
95     }
96     d  = d * (max - min) + min;
97     switch (data_type)
98     {
99       case SOX_FLOAT:
100         {
101           float *fp = (float *)table;
102           *fp++ = (float)d;
103           table = fp;
104           continue;
105         }
106       case SOX_DOUBLE:
107         {
108           double *dp = (double *)table;
109           *dp++ = d;
110           table = dp;
111           continue;
112         }
113       default: break;
114     }
115     d += d < 0? -0.5 : +0.5;
116     switch (data_type)
117     {
118       case SOX_SHORT:
119         {
120           short *sp = table;
121           *sp++ = (short)d;
122           table = sp;
123           continue;
124         }
125       case SOX_INT:
126         {
127           int *ip = table;
128           *ip++ = (int)d;
129           table = ip;
130           continue;
131         }
132       default: break;
133     }
134   }
135 }
136 
137 /*
138  * lsx_parsesamples
139  *
140  * Parse a string for # of samples.  The input consists of one or more
141  * parts, with '+' or '-' between them indicating if the sample count
142  * should be added to or subtracted from the previous value.
143  * If a part ends with a 's' then it is interpreted as a
144  * user-calculated # of samples.
145  * If a part contains ':' or '.' but no 'e' or if it ends with a 't'
146  * then it is treated as an amount of time.  This is converted into
147  * seconds and fraction of seconds, then the sample rate is used to
148  * calculate # of samples.
149  * Parameter def specifies which interpretation should be the default
150  * for a bare number like "123".  It can either be 't' or 's'.
151  * Returns NULL on error, pointer to next char to parse otherwise.
152  */
153 static char const * parsesamples(sox_rate_t rate, const char *str0, uint64_t *samples, int def, int combine);
154 
lsx_parsesamples(sox_rate_t rate,const char * str0,uint64_t * samples,int def)155 char const * lsx_parsesamples(sox_rate_t rate, const char *str0, uint64_t *samples, int def)
156 {
157   *samples = 0;
158   return parsesamples(rate, str0, samples, def, '+');
159 }
160 
parsesamples(sox_rate_t rate,const char * str0,uint64_t * samples,int def,int combine)161 static char const * parsesamples(sox_rate_t rate, const char *str0, uint64_t *samples, int def, int combine)
162 {
163   char * str = (char *)str0;
164 
165   do {
166     uint64_t samples_part;
167     sox_bool found_samples = sox_false, found_time = sox_false;
168     char const * end;
169     char const * pos;
170     sox_bool found_colon, found_dot, found_e;
171 
172     for (;*str == ' '; ++str);
173     for (end = str; *end && strchr("0123456789:.ets", *end); ++end);
174     if (end == str)
175       return NULL; /* error: empty input */
176 
177     pos = strchr(str, ':');
178     found_colon = pos && pos < end;
179 
180     pos = strchr(str, '.');
181     found_dot = pos && pos < end;
182 
183     pos = strchr(str, 'e');
184     found_e = pos && pos < end;
185 
186     if (found_colon || (found_dot && !found_e) || *(end-1) == 't')
187       found_time = sox_true;
188     else if (*(end-1) == 's')
189       found_samples = sox_true;
190 
191     if (found_time || (def == 't' && !found_samples)) {
192       int i;
193       if (found_e)
194         return NULL; /* error: e notation in time */
195 
196       for (samples_part = 0, i = 0; *str != '.' && i < 3; ++i) {
197         char * last_str = str;
198         long part = strtol(str, &str, 10);
199         if (!i && str == last_str)
200           return NULL; /* error: empty first component */
201         samples_part += rate * part;
202         if (i < 2) {
203           if (*str != ':')
204             break;
205           ++str;
206           samples_part *= 60;
207         }
208       }
209       if (*str == '.') {
210         char * last_str = str;
211         double part = strtod(str, &str);
212         if (str == last_str)
213           return NULL; /* error: empty fractional part */
214         samples_part += rate * part + .5;
215       }
216       if (*str == 't')
217         str++;
218     } else {
219       char * last_str = str;
220       double part = strtod(str, &str);
221       if (str == last_str)
222         return NULL; /* error: no sample count */
223       samples_part = part + .5;
224       if (*str == 's')
225         str++;
226     }
227     if (str != end)
228       return NULL; /* error: trailing characters */
229 
230     switch (combine) {
231       case '+': *samples += samples_part; break;
232       case '-': *samples = samples_part <= *samples ?
233                            *samples - samples_part : 0;
234         break;
235     }
236     combine = '\0';
237     if (*str && strchr("+-", *str))
238       combine = *str++;
239   } while (combine);
240   return str;
241 }
242 
243 #if 0
244 
245 #include <assert.h>
246 
247 #define TEST(st, samp, len) \
248   str = st; \
249   next = lsx_parsesamples(10000, str, &samples, 't'); \
250   assert(samples == samp && next == str + len);
251 
252 int main(int argc, char * * argv)
253 {
254   char const * str, * next;
255   uint64_t samples;
256 
257   TEST("0"  , 0, 1)
258   TEST("1" , 10000, 1)
259 
260   TEST("0s" , 0, 2)
261   TEST("0s,", 0, 2)
262   TEST("0s/", 0, 2)
263   TEST("0s@", 0, 2)
264 
265   TEST("0t" , 0, 2)
266   TEST("0t,", 0, 2)
267   TEST("0t/", 0, 2)
268   TEST("0t@", 0, 2)
269 
270   TEST("1s" , 1, 2)
271   TEST("1s,", 1, 2)
272   TEST("1s/", 1, 2)
273   TEST("1s@", 1, 2)
274   TEST(" 01s" , 1, 4)
275   TEST("1e6s" , 1000000, 4)
276 
277   TEST("1t" , 10000, 2)
278   TEST("1t,", 10000, 2)
279   TEST("1t/", 10000, 2)
280   TEST("1t@", 10000, 2)
281   TEST("1.1t" , 11000, 4)
282   TEST("1.1t,", 11000, 4)
283   TEST("1.1t/", 11000, 4)
284   TEST("1.1t@", 11000, 4)
285   assert(!lsx_parsesamples(10000, "1e6t", &samples, 't'));
286 
287   TEST(".0", 0, 2)
288   TEST("0.0", 0, 3)
289   TEST("0:0.0", 0, 5)
290   TEST("0:0:0.0", 0, 7)
291 
292   TEST(".1", 1000, 2)
293   TEST(".10", 1000, 3)
294   TEST("0.1", 1000, 3)
295   TEST("1.1", 11000, 3)
296   TEST("1:1.1", 611000, 5)
297   TEST("1:1:1.1", 36611000, 7)
298   TEST("1:1", 610000, 3)
299   TEST("1:01", 610000, 4)
300   TEST("1:1:1", 36610000, 5)
301   TEST("1:", 600000, 2)
302   TEST("1::", 36000000, 3)
303 
304   TEST("0.444444", 4444, 8)
305   TEST("0.555555", 5556, 8)
306 
307   assert(!lsx_parsesamples(10000, "x", &samples, 't'));
308 
309   TEST("1:23+37", 1200000, 7)
310   TEST("12t+12s",  120012, 7)
311   TEST("1e6s-10",  900000, 7)
312   TEST("10-2:00",       0, 7)
313   TEST("123-45+12s+2:00-3e3s@foo", 1977012, 20)
314 
315   TEST("1\0" "2", 10000, 1)
316 
317   return 0;
318 }
319 #endif
320 
321 /*
322  * lsx_parseposition
323  *
324  * Parse a string for an audio position.  Similar to lsx_parsesamples
325  * above, but an initial '=', '+' or '-' indicates that the specified time
326  * is relative to the start of audio, last used position or end of audio,
327  * respectively.  Parameter def states which of these is the default.
328  * Parameters latest and end are the positions to which '+' and '-' relate;
329  * end may be SOX_UNKNOWN_LEN, in which case "-0" is the only valid
330  * end-relative input and will result in a position of SOX_UNKNOWN_LEN.
331  * Other parameters and return value are the same as for lsx_parsesamples.
332  *
333  * A test parse that only checks for valid syntax can be done by
334  * specifying samples = NULL.  If this passes, a later reparse of the same
335  * input will only fail if it is relative to the end ("-"), not "-0", and
336  * the end position is unknown.
337  */
lsx_parseposition(sox_rate_t rate,const char * str0,uint64_t * samples,uint64_t latest,uint64_t end,int def)338 char const * lsx_parseposition(sox_rate_t rate, const char *str0, uint64_t *samples, uint64_t latest, uint64_t end, int def)
339 {
340   char *str = (char *)str0;
341   char anchor, combine;
342 
343   if (!strchr("+-=", def))
344     return NULL; /* error: invalid default anchor */
345   anchor = def;
346   if (*str && strchr("+-=", *str))
347     anchor = *str++;
348 
349   combine = '+';
350   if (strchr("+-", anchor)) {
351     combine = anchor;
352     if (*str && strchr("+-", *str))
353       combine = *str++;
354   }
355 
356   if (!samples) {
357     /* dummy parse, syntax checking only */
358     uint64_t dummy = 0;
359     return parsesamples(0., str, &dummy, 't', '+');
360   }
361 
362   switch (anchor) {
363     case '=': *samples = 0; break;
364     case '+': *samples = latest; break;
365     case '-': *samples = end; break;
366   }
367 
368   if (anchor == '-' && end == SOX_UNKNOWN_LEN) {
369     /* "-0" only valid input here */
370     char const *l;
371     for (l = str; *l && strchr("0123456789:.ets+-", *l); ++l);
372     if (l == str+1 && *str == '0') {
373       /* *samples already set to SOX_UNKNOWN_LEN */
374       return l;
375     }
376     return NULL; /* error: end-relative position, but end unknown */
377   }
378 
379   return parsesamples(rate, str, samples, 't', combine);
380 }
381 
382 /* a note is given as an int,
383  * 0   => 440 Hz = A
384  * >0  => number of half notes 'up',
385  * <0  => number of half notes down,
386  * example 12 => A of next octave, 880Hz
387  *
388  * calculated by freq = 440Hz * 2**(note/12)
389  */
calc_note_freq(double note,int key)390 static double calc_note_freq(double note, int key)
391 {
392   if (key != INT_MAX) {                         /* Just intonation. */
393     static const int n[] = {16, 9, 6, 5, 4, 7}; /* Numerator. */
394     static const int d[] = {15, 8, 5, 4, 3, 5}; /* Denominator. */
395     static double j[13];                        /* Just semitones */
396     int i, m = floor(note);
397 
398     if (!j[1]) for (i = 1; i <= 12; ++i)
399       j[i] = i <= 6? log((double)n[i - 1] / d[i - 1]) / log(2.) : 1 - j[12 - i];
400     note -= m;
401     m -= key = m - ((INT_MAX / 2 - ((INT_MAX / 2) % 12) + m - key) % 12);
402     return 440 * pow(2., key / 12. + j[m] + (j[m + 1] - j[m]) * note);
403   }
404   return 440 * pow(2., note / 12);
405 }
406 
lsx_parse_note(char const * text,char ** end_ptr)407 int lsx_parse_note(char const * text, char * * end_ptr)
408 {
409   int result = INT_MAX;
410 
411   if (*text >= 'A' && *text <= 'G') {
412     result = (int)(5/3. * (*text++ - 'A') + 9.5) % 12 - 9;
413     if (*text == 'b') {--result; ++text;}
414     else if (*text == '#') {++result; ++text;}
415     if (isdigit((unsigned char)*text))
416       result += 12 * (*text++ - '4');
417   }
418   *end_ptr = (char *)text;
419   return result;
420 }
421 
422 /* Read string 'text' and convert to frequency.
423  * 'text' can be a positive number which is the frequency in Hz.
424  * If 'text' starts with a '%' and a following number the corresponding
425  * note is calculated.
426  * Return -1 on error.
427  */
lsx_parse_frequency_k(char const * text,char ** end_ptr,int key)428 double lsx_parse_frequency_k(char const * text, char * * end_ptr, int key)
429 {
430   double result;
431 
432   if (*text == '%') {
433     result = strtod(text + 1, end_ptr);
434     if (*end_ptr == text + 1)
435       return -1;
436     return calc_note_freq(result, key);
437   }
438   if (*text >= 'A' && *text <= 'G') {
439     int result2 = lsx_parse_note(text, end_ptr);
440     return result2 == INT_MAX? - 1 : calc_note_freq((double)result2, key);
441   }
442   result = strtod(text, end_ptr);
443   if (end_ptr) {
444     if (*end_ptr == text)
445       return -1;
446     if (**end_ptr == 'k') {
447       result *= 1000;
448       ++*end_ptr;
449     }
450   }
451   return result < 0 ? -1 : result;
452 }
453 
lsx_open_input_file(sox_effect_t * effp,char const * filename,sox_bool text_mode)454 FILE * lsx_open_input_file(sox_effect_t * effp, char const * filename, sox_bool text_mode)
455 {
456   FILE * file;
457 
458   if (!filename || !strcmp(filename, "-")) {
459     if (effp->global_info->global_info->stdin_in_use_by) {
460       lsx_fail("stdin already in use by `%s'", effp->global_info->global_info->stdin_in_use_by);
461       return NULL;
462     }
463     effp->global_info->global_info->stdin_in_use_by = effp->handler.name;
464     file = stdin;
465   }
466   else if (!(file = fopen(filename, text_mode ? "r" : "rb"))) {
467     lsx_fail("couldn't open file %s: %s", filename, strerror(errno));
468     return NULL;
469   }
470   return file;
471 }
472 
lsx_effects_init(void)473 int lsx_effects_init(void)
474 {
475   init_fft_cache();
476   return SOX_SUCCESS;
477 }
478 
lsx_effects_quit(void)479 int lsx_effects_quit(void)
480 {
481   clear_fft_cache();
482   return SOX_SUCCESS;
483 }
484