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