An explanation is here (in interlingua).
/* , by Ruud Harmsen
Idea: take 12 steps in an octave, give them Zarlino-like
ratios consisting of rather low numerators and denominators
containing factors 2, 3 and 5 (optionally more). Then
calculate the ratios (expressed as whole numbers) between
those steps. In other words, shift the scale, start it at
different points. Would the derived scales be usable
musically, and would they contain bad, dissonant intervals,
like "wolf fifths"?
*/
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <ctype.h>
typedef short int factor_t;
typedef short int numfac_t;
static const factor_t factors[] = {1, 2, 3, 5, 7, 11, 13, 17, -1};
#define SizeFactors (sizeof factors / sizeof factors[0])
struct ratio
{
numfac_t *num_factors_numerator; /* Points to foot of malloc'ed array, ends in -1 */
numfac_t *num_factors_denominator; /* Points to foot of malloc'ed array, ends in -1 */
};
static struct ratio notes[12];
#define SizeNotes (sizeof notes / sizeof notes[0])
static void free_all (void);
static int read_infile (FILE *inf);
static int fillfactor (char *cp, int note_index, int *p_allocd, int is_numerator);
static int report (void);
static void report_one (int i, int j);
static int report_width = 4;
#ifdef DEBUG
static void show_table (void);
static void ChkIntegr (int linenumber);
#endif
int main (int argc, char **argv)
{
FILE *inf = NULL;
if (argc < 2)
{
fprintf(stderr, "Usage: %s infile\n", argv[0]);
exit(1);
}
if (!(inf = fopen(argv[1], "r")))
{
fprintf(stderr, "%s: Can't open input file %s\n", argv[0], argv[1]);
exit(2);
}
read_infile(inf);
#ifdef DEBUG
ChkIntegr (__LINE__);
#endif
report();
free_all();
return 0;
}
static void free_all (void)
{
int note_index;
for (note_index = 0; note_index < SizeNotes; note_index++)
{
if (notes[note_index].num_factors_numerator)
{
free(notes[note_index].num_factors_numerator);
notes[note_index].num_factors_numerator = NULL;
}
if (notes[note_index].num_factors_denominator)
{
free(notes[note_index].num_factors_denominator);
notes[note_index].num_factors_denominator = NULL;
}
}
}
/* See examples of such a file in this same directory, e.g. inzar1. */
static int read_infile (FILE *inf)
{
char line[81];
char *cp;
char is_numerator; /* else denominator */
int note_index;
int factors_found;
for (note_index = 0;
note_index < SizeNotes && fgets(line, sizeof line, inf);
note_index += factors_found)
{
int allocd = 0;
is_numerator = 1;
factors_found = 0;
for (cp = line; cp < line + sizeof line && *cp && *cp != '#'; cp++)
{
if (*cp == '/' || *cp == ':')
{
is_numerator = !is_numerator;
allocd = 0;
}
else if (isdigit(*cp))
{
cp += fillfactor (cp, note_index, &allocd, is_numerator);
factors_found = 1;
}
/* Whitespace, and anything else: all ignored */
}
}
fclose(inf);
return 0;
}
/* Build up a table of prime factors for the ratios, in memory,
for later reference. */
static int fillfactor (char *cp, int note_index, int *p_allocd, int is_numerator)
{
int i;
int wherenewcount = -1;
int factor;
int consumed;
sscanf(cp, "%3d%n", &factor, &consumed);
for (i = 0; factors[i] > 0; i++)
{
if (factors[i] == factor)
{
wherenewcount = i;
break;
}
}
if (wherenewcount < 0)
/* Factor not found. Ignore, do nothing. */
return 2;
if (wherenewcount + 2 > *p_allocd)
{
numfac_t *p;
if (is_numerator)
{
/* Make room for more factor counters, as necessary. */
p = realloc(notes[note_index].num_factors_numerator,
(wherenewcount + 2) * sizeof (numfac_t));
if (p)
{
int zerofrom = *p_allocd - 1;
if (zerofrom < 0) zerofrom = 0;
notes[note_index].num_factors_numerator = p;
for (i = zerofrom; i <= wherenewcount; i++)
{
notes[note_index].num_factors_numerator[i] = 0;
}
notes[note_index].num_factors_numerator[wherenewcount + 1] = -1;
*p_allocd = wherenewcount + 2;
}
else
{
/* A failed realloc is so unlikely, seeing the minute
amounts of memory required, that we simply abort the
program then. */
fprintf(stderr, "%s line %d: cannot realloc %d bytes\n",
__FILE__, __LINE__, (int)((wherenewcount + 2) * sizeof (numfac_t)));
exit(2);
}
}
else
{
p = realloc(notes[note_index].num_factors_denominator,
(wherenewcount + 2) * sizeof (numfac_t));
if (p)
{
int zerofrom = *p_allocd - 1;
if (zerofrom < 0) zerofrom = 0;
notes[note_index].num_factors_denominator = p;
for (i = zerofrom; i <= wherenewcount; i++)
notes[note_index].num_factors_denominator[i] = 0;
notes[note_index].num_factors_denominator[wherenewcount + 1] = -1;
*p_allocd = wherenewcount + 2;
}
else
{
/* A failed realloc is so unlikely, seeing the minute
amounts of memory required, that we simply abort the
program then. */
fprintf(stderr, "%s line %d: cannot realloc %d bytes\n",
__FILE__, __LINE__, (int)((wherenewcount + 2) * sizeof (numfac_t)));
exit(2);
}
}
}
if (is_numerator)
notes[note_index].num_factors_numerator[wherenewcount] += 1;
else
notes[note_index].num_factors_denominator[wherenewcount] += 1;
return consumed - 1; /* Minus one to compensate for cp++ in calling function */
}
/* Print the result to stdout, in two different ways. */
static int report (void)
{
int i, j;
#ifdef DEBUG
show_table();
#endif
for (i = 0; i <= SizeNotes; i++)
{
fprintf(stdout, "\n%2d ", i);
for (j = 0; j < SizeNotes; j++)
report_one(j, i);
}
fprintf(stdout, "\n ");
for (j = 0; j <= SizeNotes; j++)
fprintf(stdout, "%*s%2d%*s", report_width, "", j, report_width - 1, "");
for (i = 0; i < SizeNotes; i++)
{
fprintf(stdout, "\n%2d ", i);
for (j = i; j <= SizeNotes + i; j++)
report_one(i, j);
}
fprintf(stdout, "\n\n");
return 0;
}
static void report_one (int i, int j)
{
int jj;
numfac_t *numer_factors = NULL;
numfac_t *denom_factors = NULL;
numfac_t *n;
int index;
int numerator, denominator;
if (!(numer_factors = calloc((SizeFactors-1), sizeof (numfac_t))))
{
/* A failed calloc is so unlikely, seeing the minute
amounts of memory required, that we simply abort the
program then. */
fprintf(stdout, "%s line %d: cannot calloc %d bytes\n",
__FILE__, __LINE__, (int)((SizeFactors-1) * sizeof (numfac_t)));
exit(3);
}
if (!(denom_factors = calloc((SizeFactors-1), sizeof (numfac_t))))
{
fprintf(stdout, "%s line %d: cannot calloc %d bytes\n",
__FILE__, __LINE__, (int)((SizeFactors-1) * sizeof (numfac_t)));
exit(3);
}
jj = j;
while (jj >= SizeNotes)
{
jj -= SizeNotes;
/* Add a factor two in the numerator */
numer_factors[1]++;
}
for (index = 0, n = notes[jj].num_factors_numerator ;
n && *n >= 0; n++, index++)
numer_factors[index] += *n;
for (index = 0, n = notes[i].num_factors_numerator ;
n && *n >= 0; n++, index++)
denom_factors[index] += *n;
for (index = 0, n = notes[jj].num_factors_denominator;
n && *n >= 0; n++, index++)
denom_factors[index] += *n;
for (index = 0, n = notes[i].num_factors_denominator;
n && *n >= 0; n++, index++)
numer_factors[index] += *n;
/* Simplify ratios, reducing common factors in numerator and denominator */
for (index = 0; index < SizeFactors - 1; index++)
{
/* Number of factors is non-zero above and below? */
if (numer_factors[index] && denom_factors[index])
{
numfac_t diff = numer_factors[index] - denom_factors[index];
if (diff > 0)
{
numer_factors[index] -= denom_factors[index];
denom_factors[index] = 0;
}
else
{
denom_factors[index] -= numer_factors[index];
numer_factors[index] = 0;
}
}
}
/* Now calculate the resulting ratio, by multiplying all the factors,
for both numerator and denominator. */
numerator = 1;
for (index = 0; index < SizeFactors - 1; index++)
numerator *= pow(factors[index], numer_factors[index]);
denominator = 1;
for (index = 0; index < SizeFactors - 1; index++)
denominator *= pow(factors[index], denom_factors[index]);
if (1 || numerator >= denominator)
fprintf(stdout, "%*d:%-*d", report_width, numerator,
report_width, denominator);
else
fprintf(stdout, "%*s-:-%*s", report_width - 1, "",
report_width - 1, "");
free(numer_factors);
free(denom_factors);
}
#ifdef DEBUG
static void show_table (void)
{
int j;
numfac_t *n;
for (j = 0; j < SizeNotes; j++)
{
fprintf(stdout, "\nj=%2d\t", j);
for (n = notes[j].num_factors_numerator; n && *n > -1; n++)
{
int index = n - notes[j].num_factors_numerator;
fprintf(stdout, "Numb.of factors %2d: %2d ",
(int)factors[index],
(int)notes[j].num_factors_numerator[index]);
}
fprintf(stdout, "\n\t");
for (n = notes[j].num_factors_denominator; n && *n > -1; n++)
{
int index = n - notes[j].num_factors_denominator;
fprintf(stdout, "Numb.of factors %2d: %2d ",
(int)factors[index],
(int)notes[j].num_factors_denominator[index]);
}
fprintf(stdout, "\n");
}
fprintf(stdout, "\n");
}
#endif
#ifdef DEBUG
static void ChkIntegr (int linenumber)
{
int j;
numfac_t *n;
int index;
static int NumErrors = 0;
for (j = 0; j < SizeNotes; j++)
{
for (n = notes[j].num_factors_numerator, index = 0;
n && *n != -1 && index < SizeFactors;
n++, index++)
{
if (*n < -1 || *n > 10)
{
fprintf(stdout,
"Checked from line %4d: Note=%d, factor %d %5d times in numerator\n",
linenumber, j, index, (int)*n);
if (++NumErrors > 150)
{
fprintf(stdout, "Too many errors\n\n");
exit(10);
}
}
}
for (n = notes[j].num_factors_denominator, index = 0;
n && *n != -1 && index < SizeFactors;
n++, index++)
{
if (*n < -1 || *n > 10)
{
fprintf(stdout,
"\nChecked from line %4d: Note=%d, factor %d %5d times in denominator\n",
linenumber, j, index, (int)*n);
if (++NumErrors > 150)
{
fprintf(stdout, "Too many errors\n\n");
exit(11);
}
}
}
}
}
#endif