Tunes Improvisations Bird Song New Fractal Tunes Recordings Test my Midi player

# Find the closest ratios for a scale <!-- // To change width of all the text fields, do a search and replace // of size="59" by some other value for the width. // // To make more text fields see the section // "this generates the code for fields for any number of degrees" // to make the extra code needed to handle them. // // Then change the number in the next line var max_degrees_to_show_as_separate_fields=32; var primes=new Array(); var max_powers=new Array(); var ratio_strings=new Array(); var ratios_as_cents_strings=new Array(); var cents_diffs_strings=new Array(); function round(x,precision) { if(precision<0||precision>30) return x; val_10_to_precision=Math.round(Math.exp(precision*Math.log(10))); value=Math.round(x*val_10_to_precision)/val_10_to_precision; return value; } function find_cents_for(x) { return 1200*Math.log(x)/Math.log(2); } function find_decimal_for_cents(x) { return (Math.pow(2,x/1200)); } //http://tech.irt.org/articles/js038/index.htm function split(string,text) { // splits string at text // set splitIndex = 0; before calling from outside this function. // splits the array splitArray[] which has to be made // before calling it // recursive method. // (n.b. if text repeats, splits // between each copy of the text, returning zero length // trings if there is nothing else in between var strLength = string.length, txtLength = text.length; if ((strLength == 0) || (txtLength == 0)) return; var i = string.indexOf(text); if ((!i) && (text != string.substring(0,txtLength))) return; if (i == -1) { splitArray[splitIndex++] = string; return; } splitArray[splitIndex++] = string.substring(0,i); if (i+txtLength < strLength) split(string.substring(i+txtLength,strLength),text); return; } // only need two levels of splitting (for the optional p^n tokens in the primes field) // so perhaps easiest to just duplicate that function // - the url above has method for double level splitting as well // - function split2(string,text) { //same, uses splitIndex2 and splitArray2 - for nested splitting. var strLength = string.length, txtLength = text.length; if ((strLength == 0) || (txtLength == 0)) return; var i = string.indexOf(text); if ((!i) && (text != string.substring(0,txtLength))) return; if (i == -1) { splitArray2[splitIndex2++] = string; return; } splitArray2[splitIndex2++] = string.substring(0,i); if (i+txtLength < strLength) split2(string.substring(i+txtLength,strLength),text); return; } function find_gcd(m,n) // find g.c.d of m and n { min=Math.min(m,n); max=Math.max(m,n); diff=max-min; if(diff==1) return 1; if(max==min) return 1; for(ired=0;;ired++) { nmins=Math.floor(max/min); max-=min*nmins; min2=Math.min(min,max); max2=Math.max(min,max); min=min2; max=max2; if(min==0) { return max; } diff=max-min; if(diff==1) return 1; if(ired>1000) return 1;// shouldn't happen, but it's a good habit to add a little // check for runaway loops } } function OkFactors(n,max_primes,try_excl,try_incl) { power=0; //if no primes given to test, defaults to true if(max_primes==0) return true; if(try_excl) for(ip=0;ip<max_primes;ip++) { p=primes[ip]; //-ve values for primes = ones to exclude. //= return false if n is divisible by it. if(p<0) { s=-p; if(Math.round(n/s)*s==n) return false; else continue; } } if(try_incl) { for(ip=0;ip<max_primes;) { p=primes[ip]; if(p<=0) {ip++;continue;} //-ve values for primes = ones to exclude. //= return false if n is divisible by it. //positive values, keep looping round, and //divide by them, return true if successful. if(Math.round(n/p)*p==n) { power++; if(max_powers[ip]>0) if(power>max_powers[ip]) return false; n/=p; } else {ip++;power=0;} if(n==1||p>n) break; } if(n==1) return true; else return false; } else return true; } cents_or_ratios=1; cents_or_net=2; decimal=3; herz=4; scale_notation=cents_or_ratios; function find_ratios_for_cents(what_to_do) // what_to_do > 0 converts it to another notation e.g herz { upper=0;// upper, lower, both and closest are going to be used as // constants - faster than comparing strings to find what // to do within the loop. lower=1; both=2; closest=3; show=closest; if (eval(document.cents_to_ratio.convergents.checked) == true) show=upper; if (eval(document.cents_to_ratio.convergents.checked) == true) show=lower; if (eval(document.cents_to_ratio.convergents.checked) == true) show=both; if (eval(document.cents_to_ratio.convergents.checked) == true) show=closest; if(what_to_do==0) { scale_notation=cents_or_ratios; if (eval(document.cents_to_ratio.notation.checked) == true) scale_notation=cents_or_ratios; if (eval(document.cents_to_ratio.notation.checked) == true) scale_notation=cents_or_net; if (eval(document.cents_to_ratio.notation.checked) == true) scale_notation=decimal; if (eval(document.cents_to_ratio.notation.checked) == true) scale_notation=herz; } try_incl=false; try_excl=false; ratios_as_cents=""; ratios=""; cents_diffs=""; scale_in_cents=new Array(); szcents =document.cents_to_ratio.cents.value; herz_for_1o1 =eval(document.cents_to_ratio.herz_for_1o1.value); splitIndex = 0; splitArray = new Array(); split(szcents ,' '); nscale = 1 ; for(i=0;i<splitIndex ;i++) { if(splitArray[i].length>0) { val=eval(splitArray[i]); scale_in_cents[nscale]=val; if(scale_notation==decimal) { scale_in_cents[nscale]=find_cents_for(val); } if(scale_notation==herz) { scale_in_cents[nscale]=find_cents_for(val/herz_for_1o1); } splitArray2 = new Array(); splitIndex2 = 0; split2(splitArray[i],"/"); if(splitIndex2>1) { val=eval(splitArray2)/eval(splitArray2); scale_in_cents[nscale]=find_cents_for(val); if(scale_notation==herz) { scale_in_cents[nscale]=find_cents_for(val/herz_for_1o1); } if(scale_notation==cents_or_net) { scale_in_cents[nscale]=1200*val; } } nscale+=1; } } nscale-=1; precision=eval(document.cents_to_ratio.precision.value); tolerance=eval(document.cents_to_ratio.tolerance.value); if(what_to_do>0) { precision_for_source_decimal=precision+4; precision_for_source=precision+2; precision_for_source_cents=precision; scale_notation=cents_or_ratios; if (eval(document.cents_to_ratio.notation.checked) == true) scale_notation=cents_or_ratios; if (eval(document.cents_to_ratio.notation.checked) == true) scale_notation=cents_or_net; if (eval(document.cents_to_ratio.notation.checked) == true) scale_notation=decimal; if (eval(document.cents_to_ratio.notation.checked) == true) scale_notation=herz; new_scale_string=""; for(degree=1;degree<=nscale;degree++) { decimal_val=find_decimal_for_cents(scale_in_cents[degree]); if(what_to_do==herz) { new_scale_string+=round(herz_for_1o1*decimal_val,precision_for_source)+" "; } else if(what_to_do==decimal) { new_scale_string+=round(decimal_val,precision_for_source_decimal)+" "; } else if(what_to_do==cents_or_net) { // see if it happens to be close to an n-et value, up to the tolerance { val=scale_in_cents[degree]; found_net_degree=false; for(net=2;net<1200;net++) { net_degree=net*val/1200; if(Math.abs(net_degree-Math.round(net_degree))<tolerance/12000) // tenth of the tolerance for the scale results { found_net_degree=true; new_scale_string+=Math.round(net_degree)+"/"+net+" "; break; } } if(!found_net_degree) new_scale_string+=round(scale_in_cents[degree],precision_for_source_cents)+" "; } } else new_scale_string+=round(scale_in_cents[degree],precision_for_source_cents)+" "; } document.cents_to_ratio.cents.value=new_scale_string; return; } cents=scale_in_cents; max_quotient=eval(document.cents_to_ratio.max_quotient.value); tol=1000000;//anything large szprimes =document.cents_to_ratio.primes.value; splitIndex = 0; splitArray = new Array(); split(szprimes,' '); max_primes = 0 ; for(i=0;i<splitIndex ;i++) { if(splitArray[i].length>0) { max_powers[max_primes]=0; splitArray2 = new Array(); splitIndex2 = 0; split2(splitArray[i],"^"); primes[max_primes]=p=eval(splitArray2); if(splitIndex2>1) max_powers[max_primes]=eval(splitArray2); if(p>0) try_incl=true;//ones that one wants to have as factors if(p<0) try_excl=true;//ones one doesn't want to have as a factor. max_primes++; } } do_first_test=show!=upper; do_second_test=show!=lower; for(i=0;i<=max_degrees_to_show_as_separate_fields;i++) { ratio_strings[i]=""; ratios_as_cents_strings[i]=""; cents_diffs_strings[i]=""; } if(nscale>0) for(degree=1;degree<=nscale;degree++) { ok_denom=false; denum=1.0;//force floats best_denum=1.0; best_denom=1.0; cents=1.0; //to force use of float rather than integer cents=scale_in_cents[degree]; x=Math.exp((cents/1200)*Math.log(2)); upper_tol=lower_tol=tol=1000000.0;//anything large upper_diff=1000000.0;//ditto lower_diff=1000000.0;//ditto diff=1000000.0;//ditto invert_ratios=false; if(x<1) { invert_ratios=true; x=1/x;// makes case testing easier if x>1 } for(denom=1;denom<=max_quotient;denom++) { // some optimisation here - prob. less than a factor of two, but why not! // // note that for many loops, only five of these lines are used // - object is to do as little as possible for each loop, particularly as javascript // is interpreted, and rather slow. // since it is interpreted, main objective is to reduce the number of statements to be // executed, and the strategy of saving re-usable intermediate results of calc. // in local variables is prob. of no value if it requires an extra statement to do it. denom_tested=false; if(do_first_test) { // want x ~= denum/denom denum=Math.floor(denom*x); // could have an inner for loop, to do the two cases as one, but unrolling the two cases // like this will make the code a bit faster. // do fast test first using subtraction,instead of exp and logs // here approx <= x // Note that if x >1, then x/approx-1 < x-approx < 1-approx/x, // So if we test for x/approx-1 < diff, and it fails, // we can be sure that 1-approx/x > diff as well if(1-denum/(denom*x)<lower_diff)// no need for Math.abs(..) as it should be the right way round // or if it isn't because of floating point rounding error, diff // is tiny, like fifteenth decimal place { val=x*denom/denum;// here denum/denom<x, so val > 1 new_tol=find_cents_for(val); if(new_tol<lower_tol) { // OkFactors(..) is slow, so do it last, and so // only if the ratio is closer than previous ones. ok_denom=OkFactors(denom,max_primes,try_excl,try_incl); denom_tested=true; if(ok_denom&&OkFactors(denum,max_primes,try_excl,try_incl)) { approx=denum/denom; gcd=find_gcd(denum,denom); denom/=gcd; denum/=gcd; if(invert_ratios) { if(nscale==1) ratios+=denom+"/"+denum+", "; ratio_strings[degree]+=denom+"/"+denum+", "; } else { if(nscale==1) ratios+=denum+"/"+denom+", "; ratio_strings[degree]+=denum+"/"+denom+", "; } if(nscale==1) ratios_as_cents+=round(find_cents_for(approx),precision)+", "; ratios_as_cents_strings[degree]+=round(find_cents_for(approx),precision)+", "; if(nscale==1) cents_diffs+=round(-new_tol,precision)+", ";//cents for approx/x cents_diffs_strings[degree]+=round(-new_tol,precision)+", ";//cents for approx/x if(nscale>1) {best_denum=denum;best_denom=denom;} lower_tol=new_tol; lower_diff=val-1; if(show==closest) { upper_diff=lower_diff; upper_tol=lower_tol; } if(new_tol<tolerance) break; } } } } if(do_second_test) { denum=Math.ceil(denom*x); // here approx >= x // Note that if x >1, then x/approx -1 < x-approx < 1-approx/x, // if(1-x*denom/denum<upper_diff)// no need for Math.abs(..) as it should be the right way round { val=denum/(denom*x);// x < denum/denom>, so val > 1 new_tol=find_cents_for(val); if(new_tol<upper_tol) { if(!denom_tested) ok_denom=OkFactors(denom,max_primes,try_excl,try_incl); if(ok_denom&&OkFactors(denum,max_primes,try_excl,try_incl)) { approx=denum/denom; gcd=find_gcd(denum,denom); denom/=gcd; denum/=gcd; if(invert_ratios) { if(nscale==1) ratios+=denom+"/"+denum+", "; ratio_strings[degree]+=denom+"/"+denum+", "; } else { if(nscale==1) ratios+=denum+"/"+denom+", "; ratio_strings[degree]+=denum+"/"+denom+", "; } if(nscale==1) ratios_as_cents+=round(find_cents_for(approx),precision)+", "; ratios_as_cents_strings[degree]+=round(find_cents_for(approx),precision)+", "; if(nscale==1) cents_diffs+=round(new_tol,precision)+", ";//cents for approx/x cents_diffs_strings[degree]+=round(new_tol,precision)+", ";//cents for approx/x if(nscale>1) {best_denum=denum;best_denom=denom;} upper_tol=new_tol; upper_diff=val-1; if(show==closest) { lower_diff=upper_diff; lower_tol=upper_tol; } if(new_tol<tolerance) break; } } } } } if(nscale>1) { approx=best_denum/best_denom; ratios_as_cents+=round(find_cents_for(approx),precision)+", "; ratios+=best_denum+"/"+best_denom+", "; cents_diffs+=round(find_cents_for(approx/x),precision)+", "; } } document.cents_to_ratio.cents_diffs.value=cents_diffs; document.cents_to_ratio.ratios.value=ratios; document.cents_to_ratio.ratios_as_cents.value=ratios_as_cents; /**this generates the html form fields for all the degrees of the scale *uncomment this section of code, run (with quotient == 0), and the code will *appear all as a single line in the Ratios as cents field, *Copy / paste it from there into the html and replace the @s by returns. ** for(i=1;i<=max_degrees_to_show_as_separate_fields;i++) { ratios_as_cents_strings+= " document.cents_to_ratio_scale.cents_diffs"+ i +".value=cents_diffs_strings["+ i +"];"+"@" +" document.cents_to_ratio_scale.ratios"+ i +".value=ratio_strings["+ i +"];"+"@" +" document.cents_to_ratio_scale.ratios_as_cents"+ i +".value=ratios_as_cents_strings["+ i +"];@@"; ratio_strings+= " <H4 align=\"left\">" +"Ratios for degree "+ i +"@" +" <INPUT type=\"text\" size=\"98\" name=\"ratios"+ i +"\">@" +" </H4>@" +" <H4 align=\"left\">" +"As cents@" +" <INPUT type=\"text\" size=\"98\" name=\"ratios_as_cents"+ i +"\">@" +" </H4>@" +" <H4 align=\"left\">" +"Cents diffs@" +" <INPUT type=\"text\" size=\"98\" name=\"cents_diffs"+ i +"\">@" +" </H4>@@"; } document.cents_to_ratio.cents_diffs.value=cents_diffs_strings; document.cents_to_ratio.ratios.value=ratio_strings; document.cents_to_ratio.ratios_as_cents.value=ratios_as_cents_strings; **/ document.cents_to_ratio_scale.cents_diffs1.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios1.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents1.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs2.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios2.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents2.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs3.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios3.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents3.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs4.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios4.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents4.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs5.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios5.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents5.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs6.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios6.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents6.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs7.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios7.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents7.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs8.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios8.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents8.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs9.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios9.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents9.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs10.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios10.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents10.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs11.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios11.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents11.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs12.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios12.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents12.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs13.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios13.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents13.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs14.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios14.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents14.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs15.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios15.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents15.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs16.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios16.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents16.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs17.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios17.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents17.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs18.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios18.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents18.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs19.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios19.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents19.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs20.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios20.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents20.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs21.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios21.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents21.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs22.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios22.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents22.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs23.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios23.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents23.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs24.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios24.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents24.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs25.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios25.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents25.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs26.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios26.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents26.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs27.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios27.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents27.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs28.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios28.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents28.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs29.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios29.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents29.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs30.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios30.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents30.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs31.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios31.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents31.value=ratios_as_cents_strings; document.cents_to_ratio_scale.cents_diffs31.value=cents_diffs_strings; document.cents_to_ratio_scale.ratios31.value=ratio_strings; document.cents_to_ratio_scale.ratios_as_cents31.value=ratios_as_cents_strings; } // -->

Extract from help for Fractal Tune Smithy

#### Cents diffs

Enter a single value to see successive ratio approximations to it.

Enter several values, e.g a scale, and the most accurate ratio found will be shown for each one. Scroll down this page to see the successive approximations for each scale degree

To convert the scale from cents to herz etc, change the selection for the scale notation. Values in herz are shown to two extra decimal places, and decimals are shown to four extra decimal places. When converting to n-et, the tolerance is used a tenth of the tolerance selected for the scale results, and all n-ets are checked up to 1200-et.

The n-et notation works like this: 7/17 means the 7th degree of seventeen equal temperament - i.e. 7*1200/17 cents. So it looks like a ratio but is a quick way of entering n-ets

With the cents or ratios and cents or n-et options, enter ratios or n-et notation with a '/': 5/4, and for values in cents notation, enter values without a '/': 250. I.e it treats it as a ratio or n-et notation if it looks like one.

The tolerance is the minimum distance you want the ratio to be from the cents value. If you set the max quotient high, and the tolerance low, then it will be slow, because this script goes through the quotients one at a time testing them all.

To halt the calculation, and try again, use your browser STOP button.

List all the primes you want to use as factors, or if you want to see all successive approximations, leave the primes field blank.

To set a maximum power for a prime, do it like this: "2^8 3^5" to set max powers of 2^8 and 3^5.

To exclude a prime, show it as a negative number. E.g. use -7 to search for all numbers except those divisible by 7. This can be combined with the positive primes, e.g. use 3 -9 to allow any multiple of 3, except for those that are a multiple of 9 (not sure why one would want to do it, but it comes for free!).

The entries in the factors field can also be composite. So for instance, if you enter 6 as a value, you will find ratios with denumerator or denumerator a multiple of 6 .