1 /* Implements a libSoX internal interface for implementing effects.
2 * All public functions & data are prefixed with lsx_ .
4 * Copyright (c) 2005-2012 Chris Bagwell and SoX contributors
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.
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.
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
26 int lsx_usage(sox_effect_t
* effp
)
28 if (effp
->handler
.usage
)
29 lsx_fail("usage: %s", effp
->handler
.usage
);
31 lsx_fail("this effect takes no parameters");
35 char * lsx_usage_lines(char * * usage
, char const * const * lines
, size_t n
)
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
) {
44 strcat(*usage
, lines
[i
]);
50 static lsx_enum_item
const s_lsx_wave_enum
[] = {
51 LSX_ENUM_ITEM(SOX_WAVE_
,SINE
)
52 LSX_ENUM_ITEM(SOX_WAVE_
,TRIANGLE
)
55 lsx_enum_item
const * lsx_get_wave_enum(void)
57 return s_lsx_wave_enum
;
60 void lsx_generate_wave_table(
70 uint32_t phase_offset
= phase
/ M_PI
/ 2 * table_size
+ 0.5;
72 for (t
= 0; t
< table_size
; t
++)
74 uint32_t point
= (t
+ phase_offset
) % table_size
;
79 d
= (sin((double)point
/ table_size
* 2 * M_PI
) + 1) / 2;
82 case SOX_WAVE_TRIANGLE
:
83 d
= (double)point
* 2 / table_size
;
84 switch (4 * point
/ table_size
)
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;
92 default: /* Oops! FIXME */
93 d
= 0.0; /* Make sure we have a value */
96 d
= d
* (max
- min
) + min
;
101 float *fp
= (float *)table
;
108 double *dp
= (double *)table
;
115 d
+= d
< 0? -0.5 : +0.5;
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.
153 static char const * parsesamples(sox_rate_t rate
, const char *str0
, uint64_t *samples
, int def
, int combine
);
155 char const * lsx_parsesamples(sox_rate_t rate
, const char *str0
, uint64_t *samples
, int def
)
158 return parsesamples(rate
, str0
, samples
, def
, '+');
161 static char const * parsesamples(sox_rate_t rate
, const char *str0
, uint64_t *samples
, int def
, int combine
)
163 char * str
= (char *)str0
;
166 uint64_t samples_part
;
167 sox_bool found_samples
= sox_false
, found_time
= sox_false
;
170 sox_bool found_colon
, found_dot
, found_e
;
172 for (;*str
== ' '; ++str
);
173 for (end
= str
; *end
&& strchr("0123456789:.ets", *end
); ++end
);
175 return NULL
; /* error: empty input */
177 pos
= strchr(str
, ':');
178 found_colon
= pos
&& pos
< end
;
180 pos
= strchr(str
, '.');
181 found_dot
= pos
&& pos
< end
;
183 pos
= strchr(str
, 'e');
184 found_e
= pos
&& pos
< end
;
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
;
191 if (found_time
|| (def
== 't' && !found_samples
)) {
194 return NULL
; /* error: e notation in time */
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
;
210 char * last_str
= str
;
211 double part
= strtod(str
, &str
);
213 return NULL
; /* error: empty fractional part */
214 samples_part
+= rate
* part
+ .5;
219 char * last_str
= str
;
220 double part
= strtod(str
, &str
);
222 return NULL
; /* error: no sample count */
223 samples_part
= part
+ .5;
228 return NULL
; /* error: trailing characters */
231 case '+': *samples
+= samples_part
; break;
232 case '-': *samples
= samples_part
<= *samples
?
233 *samples
- samples_part
: 0;
237 if (*str
&& strchr("+-", *str
))
247 #define TEST(st, samp, len) \
249 next = lsx_parsesamples(10000, str, &samples, 't'); \
250 assert(samples == samp && next == str + len);
252 int main(int argc
, char * * argv
)
254 char const * str
, * next
;
275 TEST("1e6s" , 1000000, 4)
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'));
290 TEST("0:0:0.0", 0, 7)
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)
304 TEST("0.444444", 4444, 8)
305 TEST("0.555555", 5556, 8)
307 assert(!lsx_parsesamples(10000, "x", &samples
, 't'));
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)
315 TEST("1\0" "2", 10000, 1)
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.
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.
338 char const * lsx_parseposition(sox_rate_t rate
, const char *str0
, uint64_t *samples
, uint64_t latest
, uint64_t end
, int def
)
340 char *str
= (char *)str0
;
341 char anchor
, combine
;
343 if (!strchr("+-=", def
))
344 return NULL
; /* error: invalid default anchor */
346 if (*str
&& strchr("+-=", *str
))
350 if (strchr("+-", anchor
)) {
352 if (*str
&& strchr("+-", *str
))
357 /* dummy parse, syntax checking only */
359 return parsesamples(0., str
, &dummy
, 't', '+');
363 case '=': *samples
= 0; break;
364 case '+': *samples
= latest
; break;
365 case '-': *samples
= end
; break;
368 if (anchor
== '-' && end
== SOX_UNKNOWN_LEN
) {
369 /* "-0" only valid input here */
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 */
376 return NULL
; /* error: end-relative position, but end unknown */
379 return parsesamples(rate
, str
, samples
, 't', combine
);
382 /* a note is given as an int,
384 * >0 => number of half notes 'up',
385 * <0 => number of half notes down,
386 * example 12 => A of next octave, 880Hz
388 * calculated by freq = 440Hz * 2**(note/12)
390 static double calc_note_freq(double note
, int key
)
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
);
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
];
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
);
404 return 440 * pow(2., note
/ 12);
407 int lsx_parse_note(char const * text
, char * * end_ptr
)
409 int result
= INT_MAX
;
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');
418 *end_ptr
= (char *)text
;
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.
428 double lsx_parse_frequency_k(char const * text
, char * * end_ptr
, int key
)
433 result
= strtod(text
+ 1, end_ptr
);
434 if (*end_ptr
== text
+ 1)
436 return calc_note_freq(result
, key
);
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
);
442 result
= strtod(text
, end_ptr
);
444 if (*end_ptr
== text
)
446 if (**end_ptr
== 'k') {
451 return result
< 0 ? -1 : result
;
454 FILE * lsx_open_input_file(sox_effect_t
* effp
, char const * filename
, sox_bool text_mode
)
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
);
463 effp
->global_info
->global_info
->stdin_in_use_by
= effp
->handler
.name
;
466 else if (!(file
= fopen(filename
, text_mode
? "r" : "rb"))) {
467 lsx_fail("couldn't open file %s: %s", filename
, strerror(errno
));
473 int lsx_effects_init(void)
479 int lsx_effects_quit(void)