shithub: sox

Download patch

ref: b4a55cb0b7a58897f31baa4ba0d91f7be7322abc
parent: d37de773a37d22dfe25dbc99632a2dba7f133c6b
author: robs <robs>
date: Wed Apr 22 13:28:10 EDT 2009

[2778142] just intonation

--- a/ChangeLog
+++ b/ChangeLog
@@ -85,6 +85,7 @@
   o New `biquad' filter effect using external coefficients.  (robs)
   o New `overdrive' effect.  (robs)
   o New `pluck' and `tpdf' types for `synth'.  (robs)
+  o [2778142] just intonation for `synth'.  (robs)
   o Can now set common parameters for multiple `synth' channels.  (robs)
   o Richer gain/normalise options.  (robs)
   o [2704442] Slight change to `riaa' gain: now norm'd to 0dB @ 1k
--- a/NEWS
+++ b/NEWS
@@ -22,7 +22,7 @@
   o Can now auto-detect file-type even when inputing from a pipe.
 
 For the complete list of changes, see the ChangeLog at
-  http://sox.cvs.sourceforge.net/sox/sox/ChangeLog?revision=1.xxx&view=markup
+  http://sox.cvs.sourceforge.net/sox/sox/ChangeLog?revision=1.223&view=markup
 
 Thanks to all who contributed to this release.
 
--- a/sox.1
+++ b/sox.1
@@ -1736,7 +1736,7 @@
 The following (one long) command plays a chime sound:
 .EX
 .ne 3
-   play -n synth sin %3 sin %-2 sin %-5 sin %-9 \\
+   play -n synth -j 3 sin %3 sin %-2 sin %-5 sin %-9 \\
 	sin %-14 sin %-21 fade h .01 2 1.5 delay \\
 	1.3 1 .76 .54 .27 remix - fade h 0 2.7 2.5 norm -1
 .EE
@@ -1743,8 +1743,8 @@
 and this plays a guitar chord:
 .EX
 .ne 2
-   play -n synth pl %-26 pl %-22 pl %-19 pl %-14 pl %-7 pl \\
-	%-2 delay 0 .05 .1 .15 .2 .25 remix - fade 0 4 .1 norm -1
+   play -n synth -j -2  pl %-26 pl %-22 pl %-19 pl %-14 pl %-7 \\
+	pl %-2 delay 0 .05 .1 .15 .2 .25 remix - fade 0 4 .1 norm -1
 .EE
 .TP
 \fBdither\fR [\fB\-a\fR] [\fB\-S\fR\^|\^\fB\-s\fR\^|\^\fB\-f \fIfilter\fR]
@@ -3455,7 +3455,7 @@
 .B tempo
 effect.
 .TP
-\fBsynth\fR [\fB\-n\fR] [\fIlen\fR] {[\fItype\fR] [\fIcombine\fR] [[\fB%\fR]\fIfreq\fR[\fBk\fR][\fB:\fR\^|\^\fB+\fR\^|\^\fB/\fR\^|\^\fB\-\fR[\fB%\fR]\fIfreq2\fR[\fBk\fR]]] [\fIoff\fR] [\fIph\fR] [\fIp1\fR] [\fIp2\fR] [\fIp3\fR]}
+\fBsynth\fR [\fB\-j \fIKEY\fR] [\fB\-n\fR] [\fIlen\fR] {[\fItype\fR] [\fIcombine\fR] [[\fB%\fR]\fIfreq\fR[\fBk\fR][\fB:\fR\^|\^\fB+\fR\^|\^\fB/\fR\^|\^\fB\-\fR[\fB%\fR]\fIfreq2\fR[\fBk\fR]]] [\fIoff\fR] [\fIph\fR] [\fIp1\fR] [\fIp2\fR] [\fIp3\fR]}
 This effect can be used to generate fixed or swept frequency audio tones
 with various wave shapes, or to generate wide-band noise of various
 `colours'.
@@ -3497,7 +3497,7 @@
    sox -n output.wav synth 0.5 sine 200-500 \\
 	synth 0.5 sine fmod 700-100
 .EE
-Frequencies can also be given as a number of equal temperament semitones
+Frequencies can also be given as a number of semitones
 relative to `middle A' (440\ Hz) by prefixing a `%' character;  for
 example, the following could be used to help tune a guitar's low `E'
 string:
@@ -3510,9 +3510,6 @@
    for s in -29 -24 -19 -14 -10 -5; do \\
 	play -n synth 4 pluck %$s repeat 2; done
 .EE
-Note that, at this time, temperaments other than equal can only be
-obtained by using the frequency notation.
-.SP
 .B N.B.
 This effect generates audio at maximum volume (0dBFS), which means that there
 is a high chance of clipping when using the audio subsequently, so
@@ -3548,8 +3545,16 @@
 (frequency modulation); default=create
 .SP
 \fIfreq\fR/\fIfreq2\fR are the frequencies at the beginning/end of
-synthesis in Hz or, if preceded with `%', equal temperament semitones
-relative to A (440\ Hz); for both, default=%0.  If
+synthesis in Hz or, if preceded with `%', semitones relative to A
+(440\ Hz); for both, default=%0.  By default, the tuning used for the
+semitone notation is equal temperament; the
+.B \-j
+.I KEY
+option selects just intonation, where
+.I KEY
+is an integer number of semitones relative to A.
+So for example, \-8 or 4 selects the key of C.
+If
 .I freq2
 is given, then
 .I len
--- a/src/effects_i.c
+++ b/src/effects_i.c
@@ -273,9 +273,21 @@
  *
  * calculated by freq = 440Hz * 2**(note/12)
  */
-static double calc_note_freq(double note)
+static double calc_note_freq(double note, int key)
 {
-  return 440.0 * pow(2.0, note / 12.0);
+  if (key != INT_MAX) {                         /* Just intonation. */
+    static const int n[] = {16, 9, 6, 5, 4, 7}; /* Numerator. */
+    static const int d[] = {15, 8, 5, 4, 3, 5}; /* Denominator. */
+    static double j[13];                        /* Just semitones */
+    int i, m = floor(note);
+
+    if (!j[1]) for (i = 1; i <= 12; ++i)
+      j[i] = i <= 6? log((double)n[i - 1] / d[i - 1]) / log(2.) : 1 - j[12 - i];
+    note -= m;
+    m -= key = m - ((INT_MAX / 2 - ((INT_MAX / 2) % 12) + m - key) % 12);
+    return 440 * pow(2., key / 12. + j[m] + (j[m + 1] - j[m]) * note);
+  }
+  return 440 * pow(2., note / 12);
 }
 
 /* Read string 'text' and convert to frequency.
@@ -284,7 +296,7 @@
  * note is calculated.
  * Return -1 on error.
  */
-double lsx_parse_frequency(char const * text, char * * end_ptr)
+double lsx_parse_frequency_k(char const * text, char * * end_ptr, int key)
 {
   double result;
 
@@ -292,7 +304,7 @@
     result = strtod(text + 1, end_ptr);
     if (*end_ptr == text + 1)
       return -1;
-    return calc_note_freq(result);
+    return calc_note_freq(result, key);
   }
   result = strtod(text, end_ptr);
   if (end_ptr) {
--- a/src/sox_i.h
+++ b/src/sox_i.h
@@ -82,7 +82,8 @@
     double max,         /* Maximum value on the y-axis. (e.g. +1) */
     double phase);      /* Phase at 1st point; 0..2pi. (e.g. pi/2 for cosine) */
 char const * lsx_parsesamples(sox_rate_t rate, const char *str, size_t *samples, int def);
-double lsx_parse_frequency(char const * text, char * * end_ptr);
+double lsx_parse_frequency_k(char const * text, char * * end_ptr, int key);
+#define lsx_parse_frequency(a, b) lsx_parse_frequency_k(a, b, INT_MAX)
 FILE * lsx_open_input_file(sox_effect_t * effp, char const * filename);
 
 void lsx_prepare_spline3(double const * x, double const * y, int n,
--- a/src/synth.c
+++ b/src/synth.c
@@ -266,11 +266,17 @@
 {
   priv_t * p = (priv_t *) effp->priv;
   channel_t master, * chan = &master;
-  int argn = 0;
+  int key = INT_MAX, argn = 0;
+  char dummy;
   --argc, ++argv;
 
   if (argc && !strcmp(*argv, "-n")) p->no_headroom = sox_true, ++argv, --argc;
 
+  if (argc > 1 && !strcmp(*argv, "-j") && sscanf(argv[1], "%i %c", &key, &dummy) == 1) {
+    argc -= 2;
+    argv += 2;
+  }
+
   /* Get duration if given (if first arg starts with digit) */
   if (argc && (isdigit((int)argv[argn][0]) || argv[argn][0] == '.')) {
     p->length_str = lsx_malloc(strlen(argv[argn]) + 1);
@@ -320,7 +326,7 @@
         argv[argn][0] == '.' || argv[argn][0] == '%') {
       static const char sweeps[] = ":+/-";
 
-      chan->freq2 = chan->freq = lsx_parse_frequency(argv[argn], &end_ptr);
+      chan->freq2 = chan->freq = lsx_parse_frequency_k(argv[argn], &end_ptr, key);
       if (chan->freq < 0) {
         lsx_fail("invalid freq");
         return SOX_EOF;
@@ -327,7 +333,7 @@
       }
       if (*end_ptr && strchr(sweeps, *end_ptr)) {         /* freq2 given? */
         chan->sweep = strchr(sweeps, *end_ptr) - sweeps;
-        chan->freq2 = lsx_parse_frequency(end_ptr + 1, &end_ptr);
+        chan->freq2 = lsx_parse_frequency_k(end_ptr + 1, &end_ptr, key);
         if (chan->freq2 < 0) {
           lsx_fail("invalid freq2");
           return SOX_EOF;
@@ -649,7 +655,7 @@
 const sox_effect_handler_t *lsx_synth_effect_fn(void)
 {
   static sox_effect_handler_t handler = {
-    "synth", "[-n] [len] {type [combine] [[%]freq[k][:|+|/|-[%]freq2[k]] [off [ph [p1 [p2 [p3]]]]]]}",
+    "synth", "[-j KEY] [-n] [len] {type [combine] [[%]freq[k][:|+|/|-[%]freq2[k]] [off [ph [p1 [p2 [p3]]]]]]}",
     SOX_EFF_MCHAN | SOX_EFF_LENGTH | SOX_EFF_GAIN,
     getopts, start, flow, 0, stop, kill, sizeof(priv_t)
   };