arbol 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/arbol +25 -0
- data/lib/arbol.rb +45 -0
- data/lib/base.rb +397 -0
- data/lib/builder.rb +102 -0
- data/lib/documentation.rb +19 -0
- data/lib/dsl.rb +168 -0
- data/lib/functions/add.rb +70 -0
- data/lib/functions/add_constrain.rb +70 -0
- data/lib/functions/add_modulo.rb +70 -0
- data/lib/functions/analog_pin.rb +316 -0
- data/lib/functions/choose.rb +74 -0
- data/lib/functions/const.rb +63 -0
- data/lib/functions/create_lookup.rb +63 -0
- data/lib/functions/create_ref.rb +48 -0
- data/lib/functions/crossfade.rb +73 -0
- data/lib/functions/divide.rb +70 -0
- data/lib/functions/feedback.rb +65 -0
- data/lib/functions/feedback_offset.rb +69 -0
- data/lib/functions/gamma.rb +61 -0
- data/lib/functions/greater_than.rb +70 -0
- data/lib/functions/greater_than_equals.rb +70 -0
- data/lib/functions/lamp_phase.rb +39 -0
- data/lib/functions/less_than.rb +70 -0
- data/lib/functions/less_than_equals.rb +70 -0
- data/lib/functions/lfo_square.rb +68 -0
- data/lib/functions/lfo_triangle.rb +72 -0
- data/lib/functions/lookup.rb +86 -0
- data/lib/functions/max.rb +70 -0
- data/lib/functions/min.rb +70 -0
- data/lib/functions/minus.rb +70 -0
- data/lib/functions/modulo.rb +70 -0
- data/lib/functions/noise.rb +49 -0
- data/lib/functions/noise_pixel.rb +49 -0
- data/lib/functions/phasor.rb +96 -0
- data/lib/functions/ref.rb +34 -0
- data/lib/functions/scale.rb +71 -0
- data/lib/functions/table.rb +16 -0
- data/lib/functions/times.rb +70 -0
- data/lib/functions/triangle.rb +69 -0
- data/lib/templates/arduino_library.ino.erb +75 -0
- metadata +84 -0
@@ -0,0 +1,316 @@
|
|
1
|
+
class AnalogPin < Base
|
2
|
+
Arbol.add_mapped_class(
|
3
|
+
'analog_pin',
|
4
|
+
AnalogPin,
|
5
|
+
%{void analog_pin(
|
6
|
+
int pin,
|
7
|
+
byte scale,
|
8
|
+
long in_lo,
|
9
|
+
long in_hi,
|
10
|
+
long out_lo,
|
11
|
+
long out_hi,
|
12
|
+
long threshold,
|
13
|
+
long window[],
|
14
|
+
long& running_total,
|
15
|
+
int& current_index,
|
16
|
+
long window_size,
|
17
|
+
long feedback,
|
18
|
+
long& storage,
|
19
|
+
long out[3]) {
|
20
|
+
long reading = long_mult(long_div(analogRead(pin), 4095), INTEGER_SCALE);
|
21
|
+
|
22
|
+
// scaling
|
23
|
+
if(scale == 1) {
|
24
|
+
reading = constrain(
|
25
|
+
map(
|
26
|
+
reading,
|
27
|
+
in_lo,
|
28
|
+
in_hi,
|
29
|
+
out_lo,
|
30
|
+
out_hi
|
31
|
+
),
|
32
|
+
out_lo,
|
33
|
+
out_hi
|
34
|
+
);
|
35
|
+
}
|
36
|
+
|
37
|
+
// apply a threshold
|
38
|
+
if(threshold >= 0) {
|
39
|
+
if(reading >= threshold) {
|
40
|
+
reading = INTEGER_SCALE;
|
41
|
+
}
|
42
|
+
else {
|
43
|
+
reading = 0;
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
// averaging
|
48
|
+
if(window_size > 1) {
|
49
|
+
// remove the element at current_index from the running total
|
50
|
+
running_total -= window[current_index];
|
51
|
+
|
52
|
+
// store reading to current index for deletion in a later frame
|
53
|
+
window[current_index] = reading;
|
54
|
+
|
55
|
+
// add current reading to running total
|
56
|
+
running_total += window[current_index];
|
57
|
+
|
58
|
+
// calculate the average
|
59
|
+
reading = long(float(running_total) / (float(INTEGER_SCALE) * window_size) * INTEGER_SCALE);
|
60
|
+
|
61
|
+
// increment current_index
|
62
|
+
current_index += 1;
|
63
|
+
|
64
|
+
//set it to zero if we reached window_size
|
65
|
+
if(current_index == window_size) { current_index = 0; }
|
66
|
+
}
|
67
|
+
|
68
|
+
if(feedback > 0) {
|
69
|
+
reading = max(reading, storage);
|
70
|
+
storage = constrain(long_mult(reading, feedback), 0, INTEGER_SCALE);
|
71
|
+
}
|
72
|
+
|
73
|
+
out[0] = reading;
|
74
|
+
out[1] = reading;
|
75
|
+
out[2] = reading;
|
76
|
+
}}
|
77
|
+
)
|
78
|
+
|
79
|
+
def initialize(params)
|
80
|
+
@frame_optimized = true
|
81
|
+
@name = "#{self.class}_#{SecureRandom.uuid.to_s.gsub('-','')}"
|
82
|
+
@pin = params[:pin]
|
83
|
+
|
84
|
+
@scale = params[:scale]
|
85
|
+
@in_lo = params[:in_lo]
|
86
|
+
@in_hi = params[:in_hi]
|
87
|
+
@out_lo = params[:out_lo]
|
88
|
+
@out_hi = params[:out_hi]
|
89
|
+
|
90
|
+
@threshold = params[:threshold]
|
91
|
+
@window = params[:window]
|
92
|
+
@feedback = params[:feedback]
|
93
|
+
# buildit
|
94
|
+
end
|
95
|
+
|
96
|
+
def depends_on
|
97
|
+
[]
|
98
|
+
end
|
99
|
+
|
100
|
+
attr_accessor :pin
|
101
|
+
|
102
|
+
attr_accessor :scale
|
103
|
+
attr_accessor :in_lo
|
104
|
+
attr_accessor :in_hi
|
105
|
+
attr_accessor :out_lo
|
106
|
+
attr_accessor :out_hi
|
107
|
+
|
108
|
+
attr_accessor :threshold
|
109
|
+
attr_accessor :window
|
110
|
+
attr_accessor :feedback
|
111
|
+
|
112
|
+
def arduino_code
|
113
|
+
unless @frame_optimized
|
114
|
+
[
|
115
|
+
%{analog_pin(
|
116
|
+
#{@pin},
|
117
|
+
#{@name}_scale,
|
118
|
+
#{@name}_in_lo,
|
119
|
+
#{@name}_in_hi,
|
120
|
+
#{@name}_out_lo,
|
121
|
+
#{@name}_out_hi,
|
122
|
+
#{@name}_threshold,
|
123
|
+
#{@name}_window,
|
124
|
+
#{@name}_running_total,
|
125
|
+
#{@name}_current_index,
|
126
|
+
#{seconds_to_frames(@window)},
|
127
|
+
#{@name}_feedback,
|
128
|
+
#{@name}_storage,
|
129
|
+
#{@name});}
|
130
|
+
]
|
131
|
+
else
|
132
|
+
[]
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def cycle_level_arduino_code
|
137
|
+
if @frame_optimized
|
138
|
+
[
|
139
|
+
%{analog_pin(
|
140
|
+
#{@pin},
|
141
|
+
#{@name}_scale,
|
142
|
+
#{@name}_in_lo,
|
143
|
+
#{@name}_in_hi,
|
144
|
+
#{@name}_out_lo,
|
145
|
+
#{@name}_out_hi,
|
146
|
+
#{@name}_threshold,
|
147
|
+
#{@name}_window,
|
148
|
+
#{@name}_running_total,
|
149
|
+
#{@name}_current_index,
|
150
|
+
#{seconds_to_frames(@window)},
|
151
|
+
#{@name}_feedback,
|
152
|
+
#{@name}_storage,
|
153
|
+
#{@name});}
|
154
|
+
]
|
155
|
+
else
|
156
|
+
[]
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def top_level_scope_code
|
161
|
+
[
|
162
|
+
"// pinMode(#{@pin}, INPUT);",
|
163
|
+
"long #{@name}[3];",
|
164
|
+
|
165
|
+
"byte #{@name}_scale = #{@scale};",
|
166
|
+
"long #{@name}_in_lo = #{@in_lo};",
|
167
|
+
"long #{@name}_in_hi = #{@in_hi};",
|
168
|
+
"long #{@name}_out_lo = #{@out_lo};",
|
169
|
+
"long #{@name}_out_hi = #{@out_hi};",
|
170
|
+
|
171
|
+
"long #{@name}_threshold = #{@threshold};",
|
172
|
+
|
173
|
+
"long #{@name}_window[#{seconds_to_frames(@window)}];",
|
174
|
+
"long #{@name}_running_total;",
|
175
|
+
"int #{@name}_current_index;",
|
176
|
+
|
177
|
+
"long #{@name}_feedback = #{@feedback};",
|
178
|
+
"long #{@name}_storage;"
|
179
|
+
]
|
180
|
+
end
|
181
|
+
|
182
|
+
def seconds_to_frames(seconds)
|
183
|
+
(seconds * 30.0).to_i
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
module Arbol
|
188
|
+
class Documentation
|
189
|
+
|
190
|
+
def analog_pin
|
191
|
+
%{--
|
192
|
+
### analog\\_pin(pin, in\\_lo, in\\_hi, out\\_lo, out\\_hi, threshold, window, feedback)
|
193
|
+
|
194
|
+
* **pin** - arduino pin to use as input.. either a number or A0, A1, etc...
|
195
|
+
* **in\\_lo** - scale and constrain.. lo value for input range
|
196
|
+
* **in\\_hi** - scale and constrain.. hi value for input range
|
197
|
+
* **out\\_lo** - scale and constrain.. lo value for output range
|
198
|
+
* **out\\_hi** - scale and constrain.. hi value for output range
|
199
|
+
* **threshold** - edge detection.. outputs INTEGER_SCALE if >= threshold.. otherwise zero
|
200
|
+
* **window** - input will be averaged by frame across this many seconds
|
201
|
+
* **feedback** - feeback amount applied to the input
|
202
|
+
|
203
|
+
Once per frame the specified pin is sampled, then the data is run through a chain of optional processing to add usefulness to signals that can be noisy, incorrectly offset, or too fast or squirrely.
|
204
|
+
|
205
|
+
The chain of processing is as follows:
|
206
|
+
|
207
|
+
* Pin is sampled once per frame.
|
208
|
+
* Scale and Constrain - Sampled value is scaled and constrained to the values specified by `in_lo`, `in_hi`, `out_lo`, `out_hi`. This functionality is enabled by specifying any one of these parameters, which default to 0.0, 1.0, 0.0, and 1.0 respectively.
|
209
|
+
* Edge detection - If `threshold` is specified, the value outputs 1.0 if >= `threshold` and 0.0 otherwise.
|
210
|
+
* Averaging window - Specifying a `window` value (in seconds, as a float) allows for the input to be averaged across the time period. The averages are calculated using a buffer that stores 30 frames for each second specified. Averaging provides a less jerky, slower control signal.
|
211
|
+
* Feedback - Specifying `feedback` (float between 0-1) applies a simple feedback calculation. This creates a longer "tail" on the control signal.
|
212
|
+
|
213
|
+
The above processes are calculated in the order specified above, with each stage feeding into the next.. unless the process is disabled, meaning that no parameter value is specified to enable it.
|
214
|
+
|
215
|
+
`analog_pin` uses named parameters as shown in the various examples below.
|
216
|
+
|
217
|
+
```
|
218
|
+
sensor = analog_pin(
|
219
|
+
pin: 'A1'
|
220
|
+
);
|
221
|
+
|
222
|
+
sensor = analog_pin(
|
223
|
+
pin: 'A1',
|
224
|
+
in_lo: 0.1,
|
225
|
+
threshold: 0.4
|
226
|
+
);
|
227
|
+
|
228
|
+
sensor = analog_pint(
|
229
|
+
pin: 'A1',
|
230
|
+
window: 0.5,
|
231
|
+
feedback: 0.95
|
232
|
+
);
|
233
|
+
```
|
234
|
+
}
|
235
|
+
end
|
236
|
+
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def analog_pin(
|
241
|
+
pin:,
|
242
|
+
in_lo: 0,
|
243
|
+
in_hi: 1.0,
|
244
|
+
out_lo: 0,
|
245
|
+
out_hi: 1.0,
|
246
|
+
threshold: nil,
|
247
|
+
window: nil,
|
248
|
+
feedback: nil
|
249
|
+
)
|
250
|
+
|
251
|
+
h = ArbolHash.new
|
252
|
+
h[:type] = 'analog_pin'
|
253
|
+
h[:pin] = resolve_pin_reference(pin)
|
254
|
+
|
255
|
+
# set scaling if any of the scale parameters are changed
|
256
|
+
h[:scale] = 0
|
257
|
+
h[:scale] = 1 if in_lo != 0
|
258
|
+
h[:scale] = 1 if in_hi != 1.0
|
259
|
+
h[:scale] = 1 if out_lo != 0
|
260
|
+
h[:scale] = 1 if out_hi != 1.0
|
261
|
+
|
262
|
+
h[:in_lo] = resolve_positive_scalar(in_lo)
|
263
|
+
h[:in_hi] = resolve_positive_scalar(in_hi)
|
264
|
+
h[:out_lo] = resolve_positive_scalar(out_lo)
|
265
|
+
h[:out_hi] = resolve_positive_scalar(out_hi)
|
266
|
+
|
267
|
+
if threshold
|
268
|
+
h[:threshold] = resolve_positive_scalar(threshold)
|
269
|
+
else
|
270
|
+
h[:threshold] = -1
|
271
|
+
end
|
272
|
+
|
273
|
+
if window
|
274
|
+
h[:window] = resolve_float(window)
|
275
|
+
else
|
276
|
+
h[:window] = 0.0
|
277
|
+
end
|
278
|
+
|
279
|
+
if feedback
|
280
|
+
h[:feedback] = resolve_positive_scalar(feedback)
|
281
|
+
else
|
282
|
+
h[:feedback] = 0
|
283
|
+
end
|
284
|
+
puts(h)
|
285
|
+
h
|
286
|
+
end
|
287
|
+
|
288
|
+
=begin
|
289
|
+
steps:
|
290
|
+
|
291
|
+
1. read pin
|
292
|
+
|
293
|
+
pin
|
294
|
+
|
295
|
+
2. optionally map/constrain
|
296
|
+
|
297
|
+
scale
|
298
|
+
in_low
|
299
|
+
in_hi
|
300
|
+
out_low
|
301
|
+
out_hi
|
302
|
+
|
303
|
+
|
304
|
+
3. optional threshold
|
305
|
+
|
306
|
+
threshold
|
307
|
+
|
308
|
+
4. optional averaging window
|
309
|
+
|
310
|
+
window
|
311
|
+
|
312
|
+
5. optional feedback
|
313
|
+
|
314
|
+
feedback
|
315
|
+
|
316
|
+
=end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
class Choose < Base
|
2
|
+
Arbol.add_mapped_class(
|
3
|
+
'choose',
|
4
|
+
Choose,
|
5
|
+
%{void choose(long choice[3], long op1[3], long op2[3], long out[3], long threshold) {
|
6
|
+
if(choice[0] <= threshold) { out[0] = op1[0];} else { out[0] = op2[0]; }
|
7
|
+
if(choice[1] <= threshold) { out[1] = op1[1];} else { out[1] = op2[1]; }
|
8
|
+
if(choice[2] <= threshold) { out[2] = op1[2];} else { out[2] = op2[2]; }
|
9
|
+
}}
|
10
|
+
)
|
11
|
+
|
12
|
+
attr_accessor :choice
|
13
|
+
attr_accessor :op1
|
14
|
+
attr_accessor :op2
|
15
|
+
|
16
|
+
def param_keys
|
17
|
+
[:choice, :op1, :op2]
|
18
|
+
end
|
19
|
+
|
20
|
+
def arduino_code
|
21
|
+
unless @frame_optimized
|
22
|
+
[
|
23
|
+
"choose(#{@choice.name}, #{@op1.name}, #{@op2.name}, #{@name}, choose_half_phase);"
|
24
|
+
]
|
25
|
+
else
|
26
|
+
[]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def cycle_level_arduino_code
|
31
|
+
if @frame_optimized
|
32
|
+
[
|
33
|
+
"choose(#{@choice.name}, #{@op1.name}, #{@op2.name}, #{@name}, choose_half_phase);"
|
34
|
+
]
|
35
|
+
else
|
36
|
+
[]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def top_level_scope_code
|
41
|
+
[
|
42
|
+
"long choose_half_phase = INTEGER_SCALE / 2 - 1;",
|
43
|
+
"long #{@name}[3];"
|
44
|
+
]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
module Arbol
|
49
|
+
class Documentation
|
50
|
+
|
51
|
+
def choose
|
52
|
+
%{--
|
53
|
+
### choose(choice, op1, op2)
|
54
|
+
|
55
|
+
* **choice** - selects the operator to be returned
|
56
|
+
* **op1** - operator1
|
57
|
+
* **op2** - operator2
|
58
|
+
|
59
|
+
Returns operator1 if choice < 0.5.. or operator2 if choice >= 0.5.
|
60
|
+
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def choose(choice, op1, op2)
|
68
|
+
h = ArbolHash.new
|
69
|
+
h[:type] = 'choose'
|
70
|
+
h[:choice] = resolve(choice)
|
71
|
+
h[:op1] = resolve(op1)
|
72
|
+
h[:op2] = resolve(op2)
|
73
|
+
h
|
74
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
class Const < Base
|
2
|
+
Arbol.add_mapped_class('const', Const, nil)
|
3
|
+
attr_accessor :value
|
4
|
+
|
5
|
+
def initialize(params)
|
6
|
+
super
|
7
|
+
@value = params[:value]
|
8
|
+
end
|
9
|
+
|
10
|
+
def arduino_code
|
11
|
+
[]
|
12
|
+
end
|
13
|
+
|
14
|
+
def top_level_scope_code
|
15
|
+
[
|
16
|
+
"long #{@name}[3] = {#{@value.join(',')}};"
|
17
|
+
]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module Arbol
|
22
|
+
class Documentation
|
23
|
+
|
24
|
+
def const
|
25
|
+
%{--
|
26
|
+
### const(value)
|
27
|
+
|
28
|
+
* **value** - input value to be used as a constant.
|
29
|
+
|
30
|
+
You can specify a constant explicity:
|
31
|
+
|
32
|
+
```
|
33
|
+
const(0.4)
|
34
|
+
|
35
|
+
or
|
36
|
+
|
37
|
+
const([0.1, 0.2, 0.3])
|
38
|
+
```
|
39
|
+
|
40
|
+
..but generally you specify constants directly as values. There is no advantage in using the function.
|
41
|
+
|
42
|
+
|
43
|
+
```
|
44
|
+
0.4
|
45
|
+
|
46
|
+
or
|
47
|
+
|
48
|
+
[0.1, 0.2, 0.3]
|
49
|
+
```
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
# given a slightly different name to avoid
|
58
|
+
def my_const(value)
|
59
|
+
{
|
60
|
+
type: "const",
|
61
|
+
value: coerce_array(value)
|
62
|
+
}
|
63
|
+
end
|