shithub: sox

Download patch

ref: 05180cc1749e52dd0faf65c1a07df4e78089c6f5
parent: 6ced231547b24200f27c211038fb1647d5ee8f01
author: robs <robs>
date: Sun Jun 15 12:18:03 EDT 2008

Further linearise linear sweep.
Speed optimisations.
Parser and error-reporting improvements.
Allow `k' suffix on frequencies.

--- a/src/synth.c
+++ b/src/synth.c
@@ -149,8 +149,8 @@
   /* options */
   type_t type;
   combine_t combine;
-  double freq, freq2;
-  sox_bool linear_sweep;
+  double freq, freq2, mult;
+  sox_bool log_sweep;
   double offset, phase;
   double p1, p2, p3; /* Use depends on synth type */
 
@@ -167,7 +167,7 @@
   char *        length_str;
   channel_t     getopts_channels;
   sox_size_t    getopts_nchannels;
-  sox_sample_t max;
+  sox_sample_t  max;
   sox_size_t    samples_done;
   sox_size_t    samples_to_do;
   channel_t     channels;
@@ -176,48 +176,6 @@
 
 
 
-/* a note is given as an int,
- * 0   => 440 Hz = A
- * >0  => number of half notes 'up',
- * <0  => number of half notes down,
- * example 12 => A of next octave, 880Hz
- *
- * calculated by freq = 440Hz * 2**(note/12)
- */
-static double calc_note_freq(double note)
-{
-  return 440.0 * pow(2.0, note / 12.0);
-}
-
-
-
-/* Read string 's' and convert to frequency.
- * 's' can be a positive number which is the frequency in Hz.
- * If 's' starts with a hash '%' and a following number the corresponding
- * note is calculated.
- * Return -1 on error.
- */
-static double StringToFreq(char *s, char **h)
-{
-  double f;
-
-  if (*s == '%') {
-    f = strtod(s + 1, h);
-    if (*h == s + 1)
-      return -1;
-    f = calc_note_freq(f);
-  } else {
-    f = strtod(s, h);
-    if (*h == s)
-      return -1;
-  }
-  if (f < 0)
-    return -1;
-  return f;
-}
-
-
-
 static void create_channel(channel_t chan)
 {
   memset(chan, 0, sizeof(*chan));
@@ -290,7 +248,7 @@
     synth->length_str = lsx_malloc(strlen(argv[argn]) + 1);
     strcpy(synth->length_str, argv[argn]);
     /* Do a dummy parse of to see if it will fail */
-    if (lsx_parsesamples(0., synth->length_str, &synth->samples_to_do, 't') == NULL)
+    if (lsx_parsesamples(9e9, synth->length_str, &synth->samples_to_do, 't') == NULL || !synth->samples_to_do)
       return lsx_usage(effp);
     argn++;
   }
@@ -297,7 +255,7 @@
 
   while (argn < argc) { /* type [combine] [f1[-f2] [p1 [p2 [p3 [p3 [p4]]]]]] */
     channel_t chan;
-    char * char_ptr;
+    char * end_ptr;
     enum_item const *p = find_enum_text(argv[argn], synth_type);
 
     if (p == NULL) {
@@ -320,17 +278,16 @@
     }
 
     /* read frequencies if given */
-    if (isdigit((int) argv[argn][0]) || argv[argn][0] == '%') {
-      chan->freq2 = chan->freq = StringToFreq(argv[argn], &char_ptr);
+    if (isdigit((int) argv[argn][0]) ||
+        argv[argn][0] == '.' || argv[argn][0] == '%') {
+      chan->freq2 = chan->freq = lsx_parse_frequency(argv[argn], &end_ptr);
       if (chan->freq < 0) {
         sox_fail("invalid freq");
         return SOX_EOF;
       }
-      if (*char_ptr == '-' || *char_ptr == '~') {        /* freq2 given? */
-        char *hlp2;
-
-        chan->linear_sweep = *char_ptr == '~';
-        chan->freq2 = StringToFreq(char_ptr + 1, &hlp2);
+      if (*end_ptr == '-' || *end_ptr == '~') {        /* freq2 given? */
+        chan->log_sweep = *end_ptr == '-';
+        chan->freq2 = lsx_parse_frequency(end_ptr + 1, &end_ptr);
         if (chan->freq2 < 0) {
           sox_fail("invalid freq2");
           return SOX_EOF;
@@ -340,6 +297,15 @@
           return SOX_EOF;
         }
       }
+      if (*end_ptr) {
+        sox_fail("frequency: invalid trailing character");
+        return SOX_EOF;
+      }
+      if (chan->log_sweep && chan->freq * chan->freq2 == 0) {
+        sox_fail("invalid frequency for logarithmic sweep");
+        return SOX_EOF;
+      }
+
       if (++argn == argc)
         break;
     }
@@ -391,7 +357,7 @@
   synth->samples_done = 0;
 
   if (synth->length_str)
-    if (lsx_parsesamples(effp->in_signal.rate, synth->length_str, &synth->samples_to_do, 't') == NULL)
+    if (lsx_parsesamples(effp->in_signal.rate, synth->length_str, &synth->samples_to_do, 't') == NULL || !synth->samples_to_do)
       return lsx_usage(effp);
 
   synth->number_of_channels = effp->in_signal.channels;
@@ -400,12 +366,15 @@
     channel_t chan = &synth->channels[i];
     *chan = synth->getopts_channels[i % synth->getopts_nchannels];
     set_default_parameters(chan, i);
+    chan->mult = chan->log_sweep ?
+        synth->samples_to_do? (log(chan->freq2) - log(chan->freq)) / synth->samples_to_do : 1 :
+        synth->samples_to_do? (chan->freq2 - chan->freq)  / synth->samples_to_do / 2 : 0;
     sox_debug("type=%s, combine=%s, samples_to_do=%u, f1=%g, f2=%g, "
-              "offset=%g, phase=%g, p1=%g, p2=%g, p3=%g",
+              "offset=%g, phase=%g, p1=%g, p2=%g, p3=%g mult=%g",
         find_enum_value(chan->type, synth_type)->text,
         find_enum_value(chan->combine, combine_type)->text,
         synth->samples_to_do, chan->freq, chan->freq2,
-        chan->offset, chan->phase, chan->p1, chan->p2, chan->p3);
+        chan->offset, chan->phase, chan->p1, chan->p2, chan->p3, chan->mult);
   }
   return SOX_SUCCESS;
 }
@@ -412,30 +381,27 @@
 
 
 
-static sox_sample_t do_synth(sox_sample_t synth_input, priv_t * synth, unsigned c, double rate)
+static sox_sample_t do_synth(sox_sample_t synth_input, priv_t * synth, unsigned c, double elapsed_time_s)
 {
   channel_t chan = &synth->channels[c];
   double synth_out;              /* [-1, 1] */
 
   if (chan->type < synth_noise) { /* Need to calculate phase: */
-    double f;              /* Current frequency; variable if sweeping */
-    double cycle_period_s; /* Current period in seconds */
-    double total_elapsed_time_s, cycle_elapsed_time_s;
     double phase;            /* [0, 1) */
 
-    if (synth->samples_to_do <= 0)
-      f = chan->freq;      /* Can't sweep if synth duration is unknown */
-    else if (chan->linear_sweep)
-      f = chan->freq + (chan->freq2 - chan->freq) * synth->samples_done / synth->samples_to_do;
-    else f = chan->freq * exp((log(chan->freq2) - log(chan->freq)) * synth->samples_done / synth->samples_to_do);
-    cycle_period_s = 1 / f;
-    total_elapsed_time_s = synth->samples_done / rate;
-    cycle_elapsed_time_s = total_elapsed_time_s - chan->cycle_start_time_s;
-    if (cycle_elapsed_time_s >= cycle_period_s) {  /* move to next cycle */
-      chan->cycle_start_time_s += cycle_period_s;
-      cycle_elapsed_time_s = total_elapsed_time_s - chan->cycle_start_time_s;
+    if (!chan->log_sweep) {
+      double f = chan->freq + synth->samples_done * chan->mult;
+      phase = f * elapsed_time_s;
     }
-    phase = cycle_elapsed_time_s / cycle_period_s;
+    else {
+      double f = chan->freq * exp(synth->samples_done * chan->mult);
+      double cycle_elapsed_time_s = elapsed_time_s - chan->cycle_start_time_s;
+      if (f * cycle_elapsed_time_s >= 1) {  /* move to next cycle */
+        chan->cycle_start_time_s += 1 / f;
+        cycle_elapsed_time_s = elapsed_time_s - chan->cycle_start_time_s;
+      }
+      phase = f * cycle_elapsed_time_s;
+    }
     phase = fmod(phase + chan->phase, 1.0);
 
     switch (chan->type) {
@@ -560,11 +526,13 @@
   priv_t * synth = (priv_t *) effp->priv;
   unsigned len = min(*isamp, *osamp) / effp->in_signal.channels;
   unsigned c, done;
+  double sample_period = 1 / effp->in_signal.rate;
   int result = SOX_SUCCESS;
 
   for (done = 0; done < len && result == SOX_SUCCESS; ++done) {
+    double elapsed_time_s = synth->samples_done * sample_period;
     for (c = 0; c < effp->in_signal.channels; c++)
-      *obuf++ = do_synth(*ibuf++, synth, c, effp->in_signal.rate);
+      *obuf++ = do_synth(*ibuf++, synth, c, elapsed_time_s);
     if (++synth->samples_done == synth->samples_to_do)
       result = SOX_EOF;
   }
@@ -596,7 +564,7 @@
 const sox_effect_handler_t *sox_synth_effect_fn(void)
 {
   static sox_effect_handler_t handler = {
-    "synth", "[len] {type [combine] [freq[-freq2|~freq2] [off [ph [p1 [p2 [p3]]]]]]}",
+    "synth", "[len] {type [combine] [freq[k][-freq2[k]|~freq2[k]] [off [ph [p1 [p2 [p3]]]]]]}",
     SOX_EFF_MCHAN | SOX_EFF_PREC |SOX_EFF_LENGTH,
     getopts, start, flow, 0, stop, kill, sizeof(priv_t)
   };