«fraction2basimal» by eli.rosenkim
on 20 Apr'22 19:28 infunction that extracts repeating digits in "basimal" representations of ratios
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
//I wanted some interesting periodic sets of integers for just intonation music. //Rational fractions often yield periodic decimals in base 10 if they can't be formed using multiples of 2s and 5s. Prime base number systems have even greater potential for these periodic sequences. Choosing the base also acts to constrain the maximum magnitude of the numbers, useful to stay roughly within a certain "limit" in the Partch sense. //Conversion of decimal fraction to basimal algorithim from https://mnmathleague.org/wp-content/uploads/2013/06/Basimal-Fractions-Part-A-only.pdf //it doesn't work perfectly due to floating point errors, I can't find an arbitrary precision float for SC ~fraction2basimal.(1/3, 10); //without rounding in the equals comparision, this returns a bunch of threes (should only return two) and then after a few digits turns to rando errors ~fraction2basimal.(pi, 10); //pi and phi are both "terminating" after less than 50 digits with or without rounding in the equals comparison //function ( ~fraction2basimal = { arg x, base; var message = "max iterations computed", messageList = List.newClear(size: 0), a = 0, d=x, c, maxIterations=15, i=0, cList = List.newClear(size: 0), dList = List.newClear(size: 0); d = d - d.floor.asInteger; //removes integer component to avoid double digit numbers in the array when there shouldn't be while( ////conditions for stopping section//// {i < maxIterations} //limit on maximum numbers of iterations for safety && {d != 0}//if d=0 the basimal repetition is done and non-repeating &&{//if d at i is equal to any previous d, then the basimal repetition is the previous digits repeating var result = true; dList.do{ arg item, i; //dList.postln; d.postln; //for testing //************* if( ({i > 0}.value;) && //if i = 0 then i-1.max(0) is 0, and d will be compared with itself! ({dList[(i-1).max(0)].equalWithPrecision(d, 0.0000001)}.value;), //equalWithPrecision nessescarry to avoid floating point errors, I wish it wasn't! Comment out to test //************* //({dList[(i-1).max(0)] == d}.value;), //broken version without rounding to showcase issue, comment in for testing //************* {message = "repeating"; result = false;}, ); if(result == false, {messageList.add(cList[i])}); }; result; } , ////body of the algo section//// { i = i+1; a = d*base; c = a.floor.asInteger; //the integer part of a d = a - c; //the fractional part of a cList.add(c); dList.add(d); } ); //post and return section if(d == 0, {message = "terminating"}); if((message == "repeating"), {postf("%",messageList.asArray);(" repeating").postln;}, {message.postln;}); cList.asArray; }; ) //examples ~fraction2basimal.(1/3, 10); ~fraction2basimal.(4/5, 7); ~fraction2basimal.(6/7, 7); //results from the paper ~fraction2basimal.(3/8, 4); ~fraction2basimal.(3/8, 3); //supports bases over 10. Note that all digits in the output are represented in base 10 even if the computation is in a different base. ~fraction2basimal.(6/7, 3); //decimals to basimals also works ~fraction2basimal.((1+(5.sqrt))/2, 10);//golden ratio and other irrationals will yield infinite series that should always hit the max iterations. A precision error might at some point cause it to mistakenly terminate if you set the max iterations high enough though. ~fraction2basimal.(pi, 10);//floating point poops out at about 47 iterations ~fraction2basimal.(pi, 6); //note that one should always imagine a decimal before the first place, the function discards everything to the left of the decimal ~fraction2basimal.(1/3, 10); ~fraction2basimal.(4/3, 10); //same thing //fun formant sound, disperser taken from https://scsynth.org/t/phase-rotation-fos/5575 ( SynthDef(\Harrison3, { arg root = 100, oTone = 1, uTone = 1, fb1 = 0, formant = 1760, tube = 0, amp = 1, pan = 0, gate = 0.5, ratio = 1, k= 0.5; var finalFreq, sig1, sig, sigL, sigR, sigM, sigS, hornbostelwertheimerkonstante = 0.00063; finalFreq = (oTone/uTone) * root; sig1 = (0.2*Formant.ar(finalFreq, formfreq: formant)) + //osc1 (0.2*SinOscFB.ar(finalFreq, feedback: fb1)); //osc2 sig = sig1; sig = Limiter.ar(LeakDC.ar(NTube.ar(sig,`[0.97,1.0,1.0,1.0,0.97],`[0.5, tube.range(-1, 1),0.2],`([0.01,0.02,0.01,0.005]*0.01)))*3);//vocal tract filter sig = 0.1 * sig; sigL = DelayL.ar(sig, 1, LFNoise1.ar(1, finalFreq * hornbostelwertheimerkonstante * 0.1)); sigR = DelayL.ar(sig, 1, LFNoise1.ar(1.3, finalFreq * hornbostelwertheimerkonstante * 0.1)); sigM = sigL + sigR; sigS = sigL - sigR; 100.do {sigS = FOS.ar(sigS, k.neg, 1, k);}; //allpass disperser sigL = (sigM + sigS); sigR = (sigM - sigS); sig = [sigL, sigR]; sig = Pan2.ar(Limiter.ar(sig), pan, amp); sig = DFM1.ar(sig, 22000 - (500 * ((finalFreq/20 + 1).log2)), 0.3, 2, 0, 0);//filter sig = sig*EnvGen.kr(Env.adsr(0.04, 0.2, 0.6, 0.1), gate, doneAction: Done.freeSelf);//env sig = Limiter.ar(sig); //sig = Friction.ar(sig, friction: MouseX.kr(0.00001, 0.03, 1), spring: MouseY.kr(0.01, 0.1, 1), beltmass: MouseY.kr(2, 0.01, 1)); OffsetOut.ar(0, sig); }).add; ) //patern using 8/9 in base 11 for musical purposes ( ~long.stop; ~short1.stop; ~short2.stop; ~basimal = ~fraction2basimal.(8/9, 11); // 8/9 in base 11 is .986124 repeating ~basimal.pop; //this results in ~basimal = [9,8,6,1,2,4] //~basimal = ~basimal.rotate(0); ~antiTempo = 6/16; ~long = PmonoArtic( \Harrison2, \formant, Pseq(~basimal++~basimal, inf), \fb1, Pseq(~basimal++~basimal++[3], inf)*1/4, \tube, Pseq([0, 0, 0, 1/2, -1/2, 1/2], inf);, \root, 144* Pstep([9/10, 1, 1], ~antiTempo*4/5, inf), \uTone, Pstep(~basimal.rotate(-1), ~antiTempo*8/9, inf), \oTone, Pstep(~basimal, ~antiTempo, inf), \dur, Pseq(~basimal, inf)*(~antiTempo/10), \legato, Pseq(~basimal.mirror2, inf)*2/19 ).play; ~short = PmonoArtic( \Harrison2, \formant, Pseq(~basimal, inf), \fb1, Pseq([10, 1, 1], inf)*1/4, \tube, Pseq([-0.9, -0.8, 1, 1/2, -1/2, -0.9], inf), \root, 288* Pstep([9/10, 1, 1], ~antiTempo*3/5, inf), \uTone, Pstep(~basimal.rotate(1), ~antiTempo*8/9, inf), \oTone, Pstep(~basimal, ~antiTempo, inf), \dur, Pseq(~basimal, inf)*(~antiTempo/10), \legato, Pseq(~basimal, inf)*1/20 ).play; ) //another pattern ( ~long.stop; ~short.stop; ~short1.stop; ~short2.stop; ~basimal = ~fraction2basimal.(4/5, 13); ~basimal.pop; ~basimal = ~basimal.rotate(5); ~antiTempo = 6/32; ~root = 288; //~ratiolong = 25/24; ~ratiolong = 1; ~long = PmonoArtic( \Harrison2, \formant, Pseq(~basimal, inf), \fb1, Pseq(~basimal, inf)*0.1, \tube, Pseq([0, 0, 0, 1/2, -1/2, 1/2], inf);, \root, ~root *(~ratiolong)* Pstep([3/4, 1, 1], ~antiTempo*5/4, inf), \uTone, Pstep(~basimal, ~antiTempo*4/5, inf), \oTone, Pstep(~basimal, ~antiTempo, inf), \dur, Pseq(~basimal, inf)*(~antiTempo/10), \legato, Pseq(~basimal.mirror2, inf)*3/19 ).play; ~short1 = PmonoArtic( \Harrison2, \formant, Pseq(~basimal, inf), \fb1, Pseq([10, 1, 1], inf)*1/4, \tube, Pseq([-0.9, -0.8, 1, 1/2, -1/2, -0.9], inf), \root, ~root* Pstep([9/10, 1, 1], ~antiTempo*3/5, inf), \uTone, Pstep(~basimal.rotate(1), ~antiTempo*4/5, inf), \oTone, Pstep(~basimal, ~antiTempo, inf), \dur, Pseq(~basimal, inf)*(~antiTempo/10), \legato, Pseq(~basimal, inf)*1/20).play; ~short2 = PmonoArtic( \Harrison2, \formant, Pseq(~basimal, inf), \fb1, Pseq([10, 1, 1], inf)*1/4, \tube, Pseq([-0.9, -0.8, 1, 1/2, -1/2, -0.9], inf), \root, ~root* Pstep([9/10, 1, 1], ~antiTempo*3/5, inf), \uTone, Pstep(~ basimal.rotate(3), ~antiTempo*4/5, inf), \oTone, Pstep(~basimal, ~antiTempo, inf), \dur, Pseq(~basimal, inf)*(~antiTempo/10), \legato, Pseq(~basimal, inf)*1/20 ).play; )
reception
comments