musa-dsl 0.21.3 → 0.22.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/musa-dsl.rb +1 -1
- data/lib/musa-dsl/core-ext.rb +1 -0
- data/lib/musa-dsl/core-ext/arrayfy.rb +9 -9
- data/lib/musa-dsl/core-ext/hashify.rb +42 -0
- data/lib/musa-dsl/core-ext/inspect-nice.rb +6 -1
- data/lib/musa-dsl/datasets/e.rb +22 -5
- data/lib/musa-dsl/datasets/gdv.rb +0 -1
- data/lib/musa-dsl/datasets/p.rb +28 -37
- data/lib/musa-dsl/datasets/pdv.rb +0 -1
- data/lib/musa-dsl/datasets/ps.rb +10 -78
- data/lib/musa-dsl/logger/logger.rb +4 -3
- data/lib/musa-dsl/matrix/matrix.rb +0 -57
- data/lib/musa-dsl/repl/repl.rb +4 -8
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-every.rb +87 -0
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-move.rb +439 -0
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +3 -3
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-timed.rb +210 -0
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb +178 -0
- data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +150 -595
- data/lib/musa-dsl/sequencer/base-sequencer-public.rb +58 -5
- data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +5 -9
- data/lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb +1 -5
- data/lib/musa-dsl/sequencer/sequencer-dsl.rb +11 -2
- data/lib/musa-dsl/series/base-series.rb +43 -78
- data/lib/musa-dsl/series/flattener-timed-serie.rb +61 -0
- data/lib/musa-dsl/series/hash-or-array-serie-splitter.rb +95 -0
- data/lib/musa-dsl/series/holder-serie.rb +1 -1
- data/lib/musa-dsl/series/main-serie-constructors.rb +29 -83
- data/lib/musa-dsl/series/main-serie-operations.rb +60 -215
- data/lib/musa-dsl/series/proxy-serie.rb +1 -1
- data/lib/musa-dsl/series/quantizer-serie.rb +558 -0
- data/lib/musa-dsl/series/queue-serie.rb +1 -1
- data/lib/musa-dsl/series/series.rb +7 -2
- data/lib/musa-dsl/transport/input-midi-clock.rb +19 -12
- data/lib/musa-dsl/transport/transport.rb +6 -6
- data/musa-dsl.gemspec +2 -2
- metadata +10 -4
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-control.rb +0 -216
- data/lib/musa-dsl/series/hash-serie-splitter.rb +0 -196
@@ -0,0 +1,558 @@
|
|
1
|
+
require_relative '../datasets/e'
|
2
|
+
require_relative '../core-ext/inspect-nice'
|
3
|
+
|
4
|
+
# TODO remove debugging puts, intermediate hash comments on :info and InspectNice
|
5
|
+
using Musa::Extension::InspectNice
|
6
|
+
|
7
|
+
module Musa
|
8
|
+
module Series
|
9
|
+
module SerieOperations
|
10
|
+
def quantize(reference: nil, step: nil,
|
11
|
+
value_attribute: nil,
|
12
|
+
stops: nil,
|
13
|
+
predictive: nil,
|
14
|
+
left_open: nil,
|
15
|
+
right_open: nil)
|
16
|
+
|
17
|
+
Series.QUANTIZE(self,
|
18
|
+
reference: reference,
|
19
|
+
step: step,
|
20
|
+
value_attribute: value_attribute,
|
21
|
+
stops: stops,
|
22
|
+
predictive: predictive,
|
23
|
+
left_open: left_open,
|
24
|
+
right_open: right_open)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
module Series
|
30
|
+
extend self
|
31
|
+
|
32
|
+
def QUANTIZE(time_value_serie,
|
33
|
+
reference: nil, step: nil,
|
34
|
+
value_attribute: nil,
|
35
|
+
stops: nil,
|
36
|
+
predictive: nil,
|
37
|
+
left_open: nil,
|
38
|
+
right_open: nil)
|
39
|
+
|
40
|
+
reference ||= 0r
|
41
|
+
step ||= 1r
|
42
|
+
value_attribute ||= :value
|
43
|
+
stops ||= false
|
44
|
+
predictive ||= false
|
45
|
+
|
46
|
+
if predictive
|
47
|
+
raise ArgumentError, "Predictive quantization doesn't allow parameters 'left_open' or 'right_open'" if left_open || right_open
|
48
|
+
|
49
|
+
PredictiveQuantizer.new(reference, step, time_value_serie, value_attribute, stops)
|
50
|
+
else
|
51
|
+
# By default: left closed and right_open
|
52
|
+
# By default 2:
|
53
|
+
# if right_open is true and left_open is nil, left_open will be false
|
54
|
+
# if left_open is true and right_open is nil, right_open will be false
|
55
|
+
|
56
|
+
right_open = right_open.nil? ? !left_open : right_open
|
57
|
+
left_open = left_open.nil? ? !right_open : left_open
|
58
|
+
|
59
|
+
RawQuantizer.new(reference, step, time_value_serie, value_attribute, stops, left_open, right_open)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
module QuantizerTools
|
64
|
+
private def get_time_value(n)
|
65
|
+
case n
|
66
|
+
when nil
|
67
|
+
time = value = nil
|
68
|
+
when Musa::Datasets::AbsTimed
|
69
|
+
time = n[:time].rationalize
|
70
|
+
value = n[@value_attribute].rationalize
|
71
|
+
when Array
|
72
|
+
time = n[0].rationalize
|
73
|
+
value = n[1].rationalize
|
74
|
+
else
|
75
|
+
raise RuntimeError, "Don't know how to process #{n}"
|
76
|
+
end
|
77
|
+
|
78
|
+
return time, value
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private_constant :QuantizerTools
|
83
|
+
|
84
|
+
class RawQuantizer
|
85
|
+
include Serie
|
86
|
+
include QuantizerTools
|
87
|
+
|
88
|
+
attr_reader :source
|
89
|
+
|
90
|
+
attr_reader :points_history
|
91
|
+
|
92
|
+
def initialize(reference, step, source, value_attribute, stops, left_open, right_open)
|
93
|
+
@reference = reference
|
94
|
+
@step_size = step.abs
|
95
|
+
|
96
|
+
@source = source
|
97
|
+
@value_attribute = value_attribute
|
98
|
+
|
99
|
+
@stops = stops
|
100
|
+
@left_open = left_open
|
101
|
+
@right_open = right_open
|
102
|
+
|
103
|
+
_restart false
|
104
|
+
|
105
|
+
mark_regarding! source
|
106
|
+
end
|
107
|
+
|
108
|
+
def _restart(restart_sources = true)
|
109
|
+
@last_processed_q_value = nil
|
110
|
+
@last_processed_time = nil
|
111
|
+
|
112
|
+
@before_last_processed_q_value = nil
|
113
|
+
|
114
|
+
@points = []
|
115
|
+
@segments = []
|
116
|
+
|
117
|
+
@source.restart if restart_sources
|
118
|
+
end
|
119
|
+
|
120
|
+
def _next_value
|
121
|
+
if @stops
|
122
|
+
i = 2
|
123
|
+
|
124
|
+
loop do
|
125
|
+
while @segments.size < i && process2; end
|
126
|
+
|
127
|
+
first = @segments[0]
|
128
|
+
last = @segments[i - 1]
|
129
|
+
|
130
|
+
# puts "_next_value: first #{first || 'nil'} last #{last || 'nil'}"
|
131
|
+
|
132
|
+
break if first.nil?
|
133
|
+
|
134
|
+
if last.nil? || last[:stop] || first[:value] != last[:value]
|
135
|
+
# puts "_next_value: found segments:"
|
136
|
+
|
137
|
+
durations_to_sum = @segments.shift(i-1)
|
138
|
+
|
139
|
+
# durations_to_sum.each { |i| puts i.inspect }
|
140
|
+
#
|
141
|
+
# puts "_next_value: result #{{ time: first[:time],
|
142
|
+
# @value_attribute => first[:value],
|
143
|
+
# duration: durations_to_sum.sum { |_| _[:duration] } }}"
|
144
|
+
|
145
|
+
return { time: first[:time],
|
146
|
+
@value_attribute => first[:value],
|
147
|
+
duration: durations_to_sum.sum { |_| _[:duration] } }
|
148
|
+
.extend(Musa::Datasets::AbsTimed)
|
149
|
+
.extend(Musa::Datasets::AbsD)
|
150
|
+
else
|
151
|
+
i += 1
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
return nil
|
156
|
+
|
157
|
+
else
|
158
|
+
i = 2
|
159
|
+
# puts "\n\n"
|
160
|
+
loop do
|
161
|
+
while @segments.size < i && process2; end
|
162
|
+
|
163
|
+
first = @segments[0]
|
164
|
+
last = @segments[i - 1]
|
165
|
+
|
166
|
+
# puts "_next_value: first #{first || 'nil'} last #{last || 'nil'}"
|
167
|
+
|
168
|
+
break if first.nil?
|
169
|
+
|
170
|
+
if last.nil? || first[:value] != last[:value]
|
171
|
+
|
172
|
+
# puts "_next_value: found segments:"
|
173
|
+
|
174
|
+
durations_to_sum = @segments.shift(i-1)
|
175
|
+
|
176
|
+
# durations_to_sum.each { |i| puts i.inspect }
|
177
|
+
#
|
178
|
+
# puts "_next_value: result #{{ time: first[:time],
|
179
|
+
# @value_attribute => first[:value],
|
180
|
+
# duration: durations_to_sum.sum { |_| _[:duration] } }}"
|
181
|
+
|
182
|
+
return { time: first[:time],
|
183
|
+
@value_attribute => first[:value],
|
184
|
+
duration: durations_to_sum.sum { |_| _[:duration] } }
|
185
|
+
.extend(Musa::Datasets::AbsTimed)
|
186
|
+
.extend(Musa::Datasets::AbsD)
|
187
|
+
else
|
188
|
+
i += 1
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
return nil
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
private def process2
|
197
|
+
while (ready_count = count_ready_points) <= 2 &&
|
198
|
+
process(*get_time_value(@source.next_value), !@source.peek_next_value)
|
199
|
+
end
|
200
|
+
|
201
|
+
if ready_count >= 2
|
202
|
+
point = @points.shift
|
203
|
+
|
204
|
+
from_time = point[:time]
|
205
|
+
from_value = point[:value]
|
206
|
+
|
207
|
+
next_point = @points.first
|
208
|
+
|
209
|
+
to_time = next_point[:time]
|
210
|
+
to_value = next_point[:value]
|
211
|
+
to_point_is_last = next_point[:last]
|
212
|
+
|
213
|
+
sign = to_value <=> from_value # to_value > from_value => +1
|
214
|
+
|
215
|
+
# puts "process2: from_time #{from_time} from_value #{from_value} to_time #{to_time} to_value #{to_value} to_last #{to_point_is_last || 'nil'} sign #{sign}"
|
216
|
+
|
217
|
+
if sign == 0
|
218
|
+
if @segments.last && @segments.last[:time] == from_time
|
219
|
+
|
220
|
+
@segments.last[:duration] = to_time - from_time
|
221
|
+
@segments.last[:info] += "; edited on a as start"
|
222
|
+
|
223
|
+
else
|
224
|
+
@segments << { time: from_time,
|
225
|
+
value: from_value,
|
226
|
+
duration: to_time - from_time,
|
227
|
+
info: "added on a as start" }
|
228
|
+
|
229
|
+
end
|
230
|
+
|
231
|
+
if !to_point_is_last
|
232
|
+
@segments << { time: to_time,
|
233
|
+
value: from_value,
|
234
|
+
duration: 0,
|
235
|
+
stop: true,
|
236
|
+
info: "added on a as end stop" }
|
237
|
+
end
|
238
|
+
else
|
239
|
+
time_increment = to_time - from_time
|
240
|
+
|
241
|
+
step_value_increment = @step_size * sign
|
242
|
+
|
243
|
+
extra_steps = 0
|
244
|
+
|
245
|
+
if @right_open
|
246
|
+
loop_to_value = to_value - step_value_increment
|
247
|
+
else
|
248
|
+
loop_to_value = to_value
|
249
|
+
extra_steps += 1
|
250
|
+
end
|
251
|
+
|
252
|
+
if @left_open
|
253
|
+
loop_from_value = from_value + step_value_increment
|
254
|
+
extra_steps -= 1
|
255
|
+
else
|
256
|
+
loop_from_value = from_value
|
257
|
+
end
|
258
|
+
|
259
|
+
step_time_increment = time_increment / ((to_value - from_value).abs + extra_steps)
|
260
|
+
|
261
|
+
intermediate_point_time = from_time
|
262
|
+
|
263
|
+
# puts "process2: loop_from_value #{loop_from_value} loop_to_value #{loop_to_value} step_value_increment #{step_value_increment} step_time_increment #{step_time_increment}"
|
264
|
+
|
265
|
+
loop_from_value.step(loop_to_value, step_value_increment) do |value|
|
266
|
+
if @segments.last &&
|
267
|
+
@segments.last[:time] == intermediate_point_time &&
|
268
|
+
@segments.last[:value] == value
|
269
|
+
|
270
|
+
@segments.last[:duration] = step_time_increment
|
271
|
+
@segments.last[:info] += "; edited on b"
|
272
|
+
|
273
|
+
# puts "process2: editing #{@segments.last}"
|
274
|
+
|
275
|
+
else
|
276
|
+
@segments << v = { time: intermediate_point_time,
|
277
|
+
value: value,
|
278
|
+
duration: step_time_increment,
|
279
|
+
info: "added on b" }
|
280
|
+
|
281
|
+
# puts "process2: adding #{v.inspect}"
|
282
|
+
end
|
283
|
+
|
284
|
+
intermediate_point_time += step_time_increment
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
true
|
289
|
+
else
|
290
|
+
false
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
private def count_ready_points
|
295
|
+
@points.select { |_| _[:ready] }.size
|
296
|
+
end
|
297
|
+
|
298
|
+
private def process(time, value, last_time_value)
|
299
|
+
if time && value
|
300
|
+
raise RuntimeError, "time only can go forward" if @last_processed_time && time <= @last_processed_time
|
301
|
+
|
302
|
+
q_value = round_quantize(value)
|
303
|
+
|
304
|
+
# A continuation point time will be changed if new points of equal value arrive.
|
305
|
+
# For this reason this point is not ready to consume.
|
306
|
+
# A ready point is a well determined point that can be consumed.
|
307
|
+
# When we arrive to a well determined point all the previous points become also determined (ready).
|
308
|
+
|
309
|
+
if q_value == @last_processed_q_value && !last_time_value
|
310
|
+
if @points.size == 1 || @points.last[:ready]
|
311
|
+
# If @points.last is the first point of a segment the new point is a continuation point.
|
312
|
+
# The continuation point is used as a stop point.
|
313
|
+
@points << { time: time, value: q_value }
|
314
|
+
else
|
315
|
+
# @points.last is NOT the first point of a segment but a continuation point.
|
316
|
+
@points.last[:time] = time
|
317
|
+
end
|
318
|
+
else
|
319
|
+
@points.reverse_each do |point|
|
320
|
+
break if point[:ready]
|
321
|
+
point[:ready] = true
|
322
|
+
end
|
323
|
+
|
324
|
+
@points << { time: time, value: q_value, ready: true, last: last_time_value }
|
325
|
+
end
|
326
|
+
|
327
|
+
@last_processed_q_value = q_value
|
328
|
+
@last_processed_time = time
|
329
|
+
|
330
|
+
true
|
331
|
+
else
|
332
|
+
false
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
private def round_quantize(value)
|
337
|
+
round((value - @reference) / @step_size) * @step_size + @reference
|
338
|
+
end
|
339
|
+
|
340
|
+
private def round(value)
|
341
|
+
i = value.floor
|
342
|
+
value > (i + 1/2r) ? i + 1r : i.rationalize
|
343
|
+
end
|
344
|
+
|
345
|
+
def infinite?
|
346
|
+
!!@source.infinite?
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
private_constant :RawQuantizer
|
351
|
+
|
352
|
+
class PredictiveQuantizer
|
353
|
+
include Serie
|
354
|
+
include QuantizerTools
|
355
|
+
|
356
|
+
attr_reader :source
|
357
|
+
|
358
|
+
def initialize(reference, step, source, value_attribute, include_stops)
|
359
|
+
@reference = reference
|
360
|
+
@step_size = step
|
361
|
+
|
362
|
+
@source = source
|
363
|
+
@value_attribute = value_attribute
|
364
|
+
|
365
|
+
@include_stops = include_stops
|
366
|
+
|
367
|
+
@halfway_offset = step / 2r
|
368
|
+
@crossing_reference = reference - @halfway_offset
|
369
|
+
|
370
|
+
_restart false
|
371
|
+
|
372
|
+
mark_regarding! source
|
373
|
+
end
|
374
|
+
|
375
|
+
def _restart(restart_sources = true)
|
376
|
+
@source.restart if restart_sources
|
377
|
+
|
378
|
+
@last_time = nil
|
379
|
+
@crossings = []
|
380
|
+
|
381
|
+
@first = true
|
382
|
+
end
|
383
|
+
|
384
|
+
def infinite?
|
385
|
+
!!@source.infinite?
|
386
|
+
end
|
387
|
+
|
388
|
+
def _next_value
|
389
|
+
result = nil
|
390
|
+
|
391
|
+
first_time, first_value = get_time_value(@source.peek_next_value) if @first
|
392
|
+
|
393
|
+
while @crossings.size <= 2 && process_more; end
|
394
|
+
|
395
|
+
@crossings.delete_if { |c| c[:stop] && c[:stops].nil? }
|
396
|
+
|
397
|
+
if @crossings[0]
|
398
|
+
time = @crossings[0][:time]
|
399
|
+
value = @crossings[0][@value_attribute]
|
400
|
+
|
401
|
+
if @first
|
402
|
+
@first = false
|
403
|
+
|
404
|
+
if time > first_time
|
405
|
+
result = { time: first_time,
|
406
|
+
@value_attribute => round_to_nearest_quantize(first_value, value),
|
407
|
+
duration: time - first_time }
|
408
|
+
.extend(Musa::Datasets::AbsD)
|
409
|
+
.extend(Musa::Datasets::AbsTimed)
|
410
|
+
else
|
411
|
+
result = _next_value
|
412
|
+
end
|
413
|
+
else
|
414
|
+
if @crossings[1]
|
415
|
+
next_time = @crossings[1][:time]
|
416
|
+
result = { time: time,
|
417
|
+
@value_attribute => value,
|
418
|
+
duration: next_time - time }
|
419
|
+
.extend(Musa::Datasets::AbsD)
|
420
|
+
.extend(Musa::Datasets::AbsTimed)
|
421
|
+
|
422
|
+
@crossings.shift
|
423
|
+
|
424
|
+
else
|
425
|
+
if @last_time && @last_time > @crossings[0][:time]
|
426
|
+
result = { time: @crossings[0][:time],
|
427
|
+
@value_attribute => @crossings[0][@value_attribute],
|
428
|
+
duration: @last_time - @crossings[0][:time] }
|
429
|
+
.extend(Musa::Datasets::AbsD)
|
430
|
+
.extend(Musa::Datasets::AbsTimed)
|
431
|
+
|
432
|
+
@last_time = nil
|
433
|
+
end
|
434
|
+
end
|
435
|
+
end
|
436
|
+
else
|
437
|
+
if @first && @last_time && @last_time > first_time
|
438
|
+
result = { time: first_time,
|
439
|
+
value: round_to_nearest_quantize(first_value),
|
440
|
+
duration: @last_time - first_time }
|
441
|
+
.extend(Musa::Datasets::AbsD)
|
442
|
+
.extend(Musa::Datasets::AbsTimed)
|
443
|
+
|
444
|
+
@first = false
|
445
|
+
@last_time = false
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
449
|
+
return result
|
450
|
+
end
|
451
|
+
|
452
|
+
private def process_more
|
453
|
+
while (@crossings.size <= 2 || @crossings[-1][:stop]) && new_crossings = next_crossings
|
454
|
+
new_crossings.each do |c|
|
455
|
+
if @last_time.nil? || c[:time] > @last_time
|
456
|
+
|
457
|
+
if c[:stop] &&
|
458
|
+
@crossings.dig(-1, :stop) &&
|
459
|
+
@crossings.dig(-1, @value_attribute) == c[@value_attribute]
|
460
|
+
|
461
|
+
c[:stops] = (@crossings[-1][:stops] || 0) + 1
|
462
|
+
|
463
|
+
@crossings[-1] = c
|
464
|
+
else
|
465
|
+
@crossings << c
|
466
|
+
end
|
467
|
+
end
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
!!new_crossings
|
472
|
+
end
|
473
|
+
|
474
|
+
private def next_crossings
|
475
|
+
from_time, from_value = get_time_value(@source.next_value)
|
476
|
+
|
477
|
+
if from_time && from_value
|
478
|
+
raise RuntimeError, "time only can go forward" if @last_time && from_time <= @last_time
|
479
|
+
|
480
|
+
@last_time = from_time
|
481
|
+
|
482
|
+
to_time, to_value = get_time_value(@source.peek_next_value)
|
483
|
+
|
484
|
+
if to_time && to_value
|
485
|
+
crossings(from_time, from_value, to_time, to_value)
|
486
|
+
end
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
490
|
+
private def crossings(from_time, from_value, to_time, to_value)
|
491
|
+
sign = to_value >= from_value ? 1r : -1r
|
492
|
+
|
493
|
+
if sign == 1
|
494
|
+
from_step = ((from_value - @crossing_reference) / @step_size).ceil
|
495
|
+
last_step = ((to_value - @crossing_reference) / @step_size).floor
|
496
|
+
else
|
497
|
+
from_step = ((from_value - @crossing_reference) / @step_size).floor
|
498
|
+
last_step = ((to_value - @crossing_reference) / @step_size).ceil
|
499
|
+
end
|
500
|
+
|
501
|
+
delta_value = to_value - from_value
|
502
|
+
delta_time = to_time - from_time
|
503
|
+
|
504
|
+
crossings =
|
505
|
+
from_step.step(last_step, sign).collect do |i|
|
506
|
+
value = @crossing_reference + i * @step_size
|
507
|
+
|
508
|
+
{ time: from_time + (delta_time / delta_value) * (value - from_value),
|
509
|
+
@value_attribute => value + sign * @halfway_offset }
|
510
|
+
end
|
511
|
+
|
512
|
+
if @include_stops
|
513
|
+
first_crossing_time = crossings.dig(0, :time)
|
514
|
+
last_crossing_time = crossings.dig(-1, :time)
|
515
|
+
|
516
|
+
if first_crossing_time.nil? || from_time < first_crossing_time
|
517
|
+
stop_before = [ { time: from_time,
|
518
|
+
@value_attribute =>
|
519
|
+
round_to_nearest_quantize(from_value,
|
520
|
+
crossings.dig(0, @value_attribute)),
|
521
|
+
stop: true } ]
|
522
|
+
else
|
523
|
+
stop_before = []
|
524
|
+
end
|
525
|
+
|
526
|
+
if last_crossing_time.nil? || to_time > last_crossing_time
|
527
|
+
stop_after = [ { time: to_time,
|
528
|
+
@value_attribute =>
|
529
|
+
round_to_nearest_quantize(to_value,
|
530
|
+
crossings.dig(-1, @value_attribute)),
|
531
|
+
stop: true } ]
|
532
|
+
else
|
533
|
+
stop_after = []
|
534
|
+
end
|
535
|
+
|
536
|
+
stop_before + crossings + stop_after
|
537
|
+
else
|
538
|
+
crossings
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
private def round_to_nearest_quantize(value, nearest = nil)
|
543
|
+
v = (value - @reference) / @step_size
|
544
|
+
|
545
|
+
if nearest
|
546
|
+
a = v.floor * @step_size + @reference
|
547
|
+
b = v.ceil * @step_size + @reference
|
548
|
+
|
549
|
+
(nearest - a).abs < (nearest - b).abs ? a : b
|
550
|
+
else
|
551
|
+
v.round * @step_size + @reference
|
552
|
+
end
|
553
|
+
end
|
554
|
+
end
|
555
|
+
|
556
|
+
private_constant :PredictiveQuantizer
|
557
|
+
end
|
558
|
+
end
|