«fraction2basimal» by eli.rosenkim

on 20 Apr'22 13:28 in formantjust intonationhordijkbasimaldecimalpartchintegers

function 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;
)
raw 7848 chars (focus & ctrl+a+c to copy)
reception
comments