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)
};