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. */ #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[28]; }; struct interval *interv = NULL; /* 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 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); /* 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) /* 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); } } 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\n"); fprintf(stdout, "{\n"); fprintf(stdout, " double ratio;\n"); fprintf(stdout, " double cents;\n"); fprintf(stdout, " char text[24];\n"); fprintf(stdout, "} 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"); }