Source code belongs to this explanation. Also available for download.
Codice fonte pertine a iste explanation. Anque disponibile pro discargar.
/*
Written 16 May 1993, Ruud Harmsen
Examine musical intervals made by frequency ratios between the
first natural numbers, and from 12-, 24-, 31-, 53-, and 72-tone
equidistant tuning systems.
3 October 2020:
Added Pythagorean intervals like 81/64, 32/27, 256/243 etc., by
permuting factors 2 and 3.
4 October 2020:
More reliable algorithm, no longer assuming qsort will leave
equal entries in the original order. A sequence number now
guarantees that later entries (e.g. 6:4, 12:8) appear later
than the basic ratio (e.g. 3:2), and so can easily and reliably
be removed afterwards.
:
Published source and outputs to https://rudhar.com/musica/intonat/ .
5 October 2020:
Used realloc() instead of a fixed size array.
6 October 2020:
More identical code in a function, and add ratios 135:128,
64:45 and 45:32.
16 October 2020:
Can now also generate C code (option -g), for use elsewhere
in a display function (showintv.c). Also added some more
intervals.
24 October 2020:
Added some more intervals, after examination of shifted Zarlino
and Pythagoras scales.
22 August 2023:
Added 72-tone equidistant.
25 August 2023:
Added 54-tone equidistant. It seems this is used in Turkish music:
multiples of a 'koma', which is 1/9 of a tone (two semitones).
That makes (12/2)*9 = 54 such distances in the octave.
13 July 2025:
Added Pythagorean and quarter-comma meantone fifth stacking scales.
14 July 2025:
And third-comma meantone too. (Guillaume Costeley / Francisco de Salinas)
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
/* 14 September 2023: 25 becomes 28 */
#define MAXFACT 28
/* Added 3 October 2020 */
#define MAXPYTH 13
struct interval
{
int seqnum;
double cents;
char text[35];
};
struct interval *interv = NULL;
enum MEANTONE_TYPE
{
MEANTONE_PYTHAGORAS,
MEANTONE_FOURTH_COMMA,
MEANTONE_THIRD_COMMA,
};
/* Number of filled rows, how many are already in the table. */
int fild_rows = 0;
static int cmp (const void *, const void *);
static void fill_octave_division (int div);
static void fill_pythagorean_ratios (void);
static void fill_pythagorean_ratios_2_3 (int factorA, int factorB);
static void pile_fifths (enum MEANTONE_TYPE meantone_type);
static int addratio (
int fild_rows_arg,
int numerator, int denominator,
struct interval **pp_interv);
static int addrow (
int fild_rows_arg,
struct interval *p_newrow,
struct interval **pp_interv);
static void write_C_array (void);
int main (int argc, char **argv)
{
int i, j;
int only_53 = 0;
int generate_C = 0;
while (argc > 1)
{
if (strcmp(argv[1], "-53") == 0)
only_53 = 1;
else if (strcmp(argv[1], "-g") == 0)
generate_C = 1;
argc--, argv++;
}
for (i = 1; i <= MAXFACT; i++)
for (j = i + 1; j <= MAXFACT; j++)
{
fild_rows = addratio(fild_rows, j, i, &interv);
}
fill_octave_division(12);
fill_octave_division(24);
if (!only_53)
{
fill_octave_division(19);
fill_octave_division(31);
fill_octave_division(43);
fill_octave_division(54);
fill_octave_division(72);
}
fill_octave_division(53);
fill_pythagorean_ratios();
/* Some extra ratios, not covered by previous algorithms */
fild_rows = addratio(fild_rows, 135, 128, &interv);
fild_rows = addratio(fild_rows, 64, 45, &interv);
fild_rows = addratio(fild_rows, 45, 32, &interv);
fild_rows = addratio(fild_rows, 36, 25, &interv);
fild_rows = addratio(fild_rows, 48, 25, &interv);
pile_fifths(MEANTONE_PYTHAGORAS);
pile_fifths(MEANTONE_FOURTH_COMMA);
pile_fifths(MEANTONE_THIRD_COMMA);
/* Some additions as per
https://en.wikipedia.org/wiki/Interval_(music)#Size_of_intervals_used_in_different_tuning_systems
https://en.wikipedia.org/wiki/Five-limit_tuning#The_just_ratios
https://en.wikipedia.org/wiki/Pythagorean_interval
https://nl.wikipedia.org/wiki/Didymisch_komma
https://en.wikipedia.org/wiki/Syntonic_comma
etc. */
fild_rows = addratio(fild_rows, 27, 25, &interv);
fild_rows = addratio(fild_rows, 27, 20, &interv);
fild_rows = addratio(fild_rows, 40, 27, &interv);
fild_rows = addratio(fild_rows, 50, 27, &interv);
fild_rows = addratio(fild_rows, 81, 80, &interv);
/* 14 September 2023 */
fild_rows = addratio(fild_rows, 88, 81, &interv);
/* 5 November 2023 */
fild_rows = addratio(fild_rows, 33, 20, &interv);
/* Some additions after analysis of shifted Zarlino and
Pythagoras scales, from self-written program zarlino.c */
fild_rows = addratio(fild_rows, 32, 25, &interv);
fild_rows = addratio(fild_rows, 75, 64, &interv);
fild_rows = addratio(fild_rows, 225, 128, &interv);
fild_rows = addratio(fild_rows, 256, 225, &interv);
fild_rows = addratio(fild_rows, 256, 243, &interv);
fild_rows = addratio(fild_rows, 512, 405, &interv);
fild_rows = addratio(fild_rows, 675, 512, &interv);
fild_rows = addratio(fild_rows, 2187, 2048, &interv);
fild_rows = addratio(fild_rows, 6561, 4096, &interv);
fild_rows = addratio(fild_rows, 8192, 6561, &interv);
fild_rows = addratio(fild_rows, 19683, 16384, &interv);
fild_rows = addratio(fild_rows, 59049, 32768, &interv);
fild_rows = addratio(fild_rows, 65536, 59049, &interv);
fild_rows = addratio(fild_rows, 177147, 131072, &interv);
fild_rows = addratio(fild_rows, 262144, 177147, &interv);
/* Sort the raw table on ascending number of cents,
and sequence number */
qsort((void *)interv, fild_rows, sizeof interv[0], cmp);
/* Remove doublets, e.g. 3/2 and 6/4 both are a fifth,
only the first one needs to be kept. */
for (i = 1, j = 0; i < fild_rows; i++)
{
/* Round difference to 5 decimals, so spurious last bits
cannot determine the sorting order. */
long int centsdiff = 0.5 + (interv[i].cents - interv[j].cents) * 100000L;
if (centsdiff == 0 && (
strstr(interv[i].text, "octave") ||
strstr(interv[i].text, "ratio ")))
{
/* Mark as doublet (first row is kept alive */
interv[i].text[0] = '\0';
}
else
{
j = i;
}
}
/* Write the results to standard output */
if (generate_C)
write_C_array();
else for (i = 0; i < fild_rows; i++)
{
/* Skip marked doublets, write everything else */
if (interv[i].text[0])
{
fprintf(stdout, "%8.6f %8.3f %s\n",
pow(2.0, interv[i].cents / 1200.0),
interv[i].cents,
interv[i].text);
}
}
free(interv);
return 0;
}
static int cmp (const void *a, const void *b)
{
const struct interval *aa = (const struct interval *)a;
const struct interval *bb = (const struct interval *)b;
/* Round difference to 5 decimals, so spurious last bits
cannot determine the sorting order. */
long int centsdiff = 0.5 + (aa->cents - bb->cents) * 100000L;
if (centsdiff)
{
if (aa->cents > bb->cents) return 1;
if (aa->cents < bb->cents) return -1;
}
/* Equal intervals should remain in the original order
of entry, so all but the first can be wiped. */
return aa->seqnum - bb->seqnum;
}
static void fill_octave_division (int div)
{
int i;
for (i = 0; i <= div; i++)
{
struct interval newrow;
newrow.cents = 1200.0 * i / (double)div;
sprintf(newrow.text, "%6d/%-6d octave", i, div);
/* Set sequence number, to guarantee that later additions
stay after earlier ones, even after sorting. */
newrow.seqnum = fild_rows;
fild_rows = addrow(fild_rows, &newrow, &interv);
}
}
/* Added the extra Pythagorean ratios on 3 October 2020. Yes,
that's more than 27 years later, and still by the same
programmer. I am Ruud Harmsen. */
static void fill_pythagorean_ratios (void)
{
fill_pythagorean_ratios_2_3(3, 2);
fill_pythagorean_ratios_2_3(2, 3);
}
static void fill_pythagorean_ratios_2_3 (int factorA, int factorB)
{
int i , j;
int numerator, denominator;
double ratio;
/* This adds things like 32/27, 256/243 etc. which would otherwise
not be included, as the maximum factor MAXFACT is 25. */
for (i = 1; i <= MAXPYTH; i++)
for (j = 1; j <= MAXPYTH; j++)
{
struct interval newrow;
numerator = pow(factorA, i);
denominator = pow(factorB, j);
fild_rows = addratio(fild_rows, numerator, denominator, &interv);
}
}
/* This function added 13 July 2025, RH. Enhanced 14 July. */
static void pile_fifths (enum MEANTONE_TYPE meantone_type)
{
int i;
double fifth_cents = log(3.0 / 2) / log(2.0) * 1200;
struct interval newrow;
switch (meantone_type)
{
case MEANTONE_FOURTH_COMMA:
fifth_cents -= log(81.0 / 80) / log(2.0) * 1200 / 4;
/* Deliberate fall-through */
case MEANTONE_PYTHAGORAS:
for (i = -6; i <= 6; i++)
{
newrow.cents = fmod(i * fifth_cents, 1200);
while (newrow.cents < 0) newrow.cents += 1200;
sprintf(newrow.text, " semitone %2d %s", ((i+12) * 7) % 12,
meantone_type == MEANTONE_FOURTH_COMMA ?
"4th comma mean" : "pythagoras");
/* Set sequence number, to guarantee that later additions
stay after earlier ones, even after sorting. */
newrow.seqnum = fild_rows;
fild_rows = addrow(fild_rows, &newrow, &interv);
}
break;
case MEANTONE_THIRD_COMMA:
fifth_cents -= log(81.0 / 80) / log(2.0) * 1200 / 3;
for (i = 0; i < 19; i++)
{
newrow.cents = fmod(i * fifth_cents, 1200);
sprintf(newrow.text, " step no. %2.0f %s",
newrow.cents * 19.0 / 1200.0,
"3rd comma mean");
/* Set sequence number, to guarantee that later additions
stay after earlier ones, even after sorting. */
newrow.seqnum = fild_rows;
fild_rows = addrow(fild_rows, &newrow, &interv);
}
break;
default:
break;
}
}
static int addratio (
int fild_rows_arg,
int numerator, int denominator,
struct interval **pp_interv)
{
double ratio = (double)numerator / (double)denominator;
struct interval newrow;
if (ratio < 1.0 || ratio > 2.0)
return fild_rows;
newrow.cents = log(ratio) / log(2.0) * 1200;
sprintf(newrow.text, "%6d:%-6d ratio ", numerator, denominator);
/* Set sequence number, to guarantee that later additions
stay after earlier ones, even after sorting. */
newrow.seqnum = fild_rows;
return fild_rows_arg = addrow(fild_rows, &newrow, pp_interv);
}
static int addrow (
int fild_rows_arg,
struct interval *p_newrow,
struct interval **pp_interv)
{
struct interval *realloked =
realloc(*pp_interv, (fild_rows_arg + 1) * sizeof (struct interval));
if (realloked)
{
*pp_interv = realloked;
/* Structure assign */
(*pp_interv)[fild_rows_arg++] = *p_newrow;
}
else
{
/* Realloc failed. Silently ignore. Just don't put in
that new row, and keep pointer and counter unchanged.
Failure is so unlikely in practice, having only a few
hundred entries, that error handling would be overkill.
*/ ;
}
return fild_rows_arg;
}
static void write_C_array (void)
{
int i;
fprintf(stdout, "/* Generated from intonat.c */\n");
fprintf(stdout, "struct interv_struct interv[] = {\n");
for (i = 0; i < fild_rows; i++)
{
/* Skip marked doublets, write everything else */
if (interv[i].text[0])
{
fprintf(stdout, " { %8.6f, %11.6f, \"%s\" },\n",
pow(2.0, interv[i].cents / 1200.0),
interv[i].cents,
interv[i].text);
}
}
fprintf(stdout, "};\n");
fprintf(stdout, "/* End of generated code from intonat.c */\n\n");
}