musa-dsl 0.21.1 → 0.22.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/midi/midi-voices.rb +4 -0
- data/lib/musa-dsl/repl/repl.rb +30 -11
- 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 +8 -0
- 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 +546 -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 +25 -12
- 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,546 @@
|
|
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 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] } }.extend(AbsTimed).extend(AbsD)
|
148
|
+
else
|
149
|
+
i += 1
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
return nil
|
154
|
+
|
155
|
+
else
|
156
|
+
i = 2
|
157
|
+
# puts "\n\n"
|
158
|
+
loop do
|
159
|
+
while @segments.size < i && process2; end
|
160
|
+
|
161
|
+
first = @segments[0]
|
162
|
+
last = @segments[i - 1]
|
163
|
+
|
164
|
+
# puts "_next_value: first #{first || 'nil'} last #{last || 'nil'}"
|
165
|
+
|
166
|
+
break if first.nil?
|
167
|
+
|
168
|
+
if last.nil? || first[:value] != last[:value]
|
169
|
+
|
170
|
+
# puts "_next_value: found segments:"
|
171
|
+
|
172
|
+
durations_to_sum = @segments.shift(i-1)
|
173
|
+
|
174
|
+
# durations_to_sum.each { |i| puts i.inspect }
|
175
|
+
#
|
176
|
+
# puts "_next_value: result #{{ time: first[:time],
|
177
|
+
# @value_attribute => first[:value],
|
178
|
+
# duration: durations_to_sum.sum { |_| _[:duration] } }}"
|
179
|
+
|
180
|
+
return { time: first[:time],
|
181
|
+
@value_attribute => first[:value],
|
182
|
+
duration: durations_to_sum.sum { |_| _[:duration] } }.extend(AbsTimed).extend(AbsD)
|
183
|
+
else
|
184
|
+
i += 1
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
return nil
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
private def process2
|
193
|
+
while (ready_count = count_ready_points) <= 2 &&
|
194
|
+
process(*get_time_value(@source.next_value), !@source.peek_next_value)
|
195
|
+
end
|
196
|
+
|
197
|
+
if ready_count >= 2
|
198
|
+
point = @points.shift
|
199
|
+
|
200
|
+
from_time = point[:time]
|
201
|
+
from_value = point[:value]
|
202
|
+
|
203
|
+
next_point = @points.first
|
204
|
+
|
205
|
+
to_time = next_point[:time]
|
206
|
+
to_value = next_point[:value]
|
207
|
+
to_point_is_last = next_point[:last]
|
208
|
+
|
209
|
+
sign = to_value <=> from_value # to_value > from_value => +1
|
210
|
+
|
211
|
+
# 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}"
|
212
|
+
|
213
|
+
if sign == 0
|
214
|
+
if @segments.last && @segments.last[:time] == from_time
|
215
|
+
|
216
|
+
@segments.last[:duration] = to_time - from_time
|
217
|
+
@segments.last[:info] += "; edited on a as start"
|
218
|
+
|
219
|
+
else
|
220
|
+
@segments << { time: from_time,
|
221
|
+
value: from_value,
|
222
|
+
duration: to_time - from_time,
|
223
|
+
info: "added on a as start" }
|
224
|
+
|
225
|
+
end
|
226
|
+
|
227
|
+
if !to_point_is_last
|
228
|
+
@segments << { time: to_time,
|
229
|
+
value: from_value,
|
230
|
+
duration: 0,
|
231
|
+
stop: true,
|
232
|
+
info: "added on a as end stop" }
|
233
|
+
end
|
234
|
+
else
|
235
|
+
time_increment = to_time - from_time
|
236
|
+
|
237
|
+
step_value_increment = @step_size * sign
|
238
|
+
|
239
|
+
extra_steps = 0
|
240
|
+
|
241
|
+
if @right_open
|
242
|
+
loop_to_value = to_value - step_value_increment
|
243
|
+
else
|
244
|
+
loop_to_value = to_value
|
245
|
+
extra_steps += 1
|
246
|
+
end
|
247
|
+
|
248
|
+
if @left_open
|
249
|
+
loop_from_value = from_value + step_value_increment
|
250
|
+
extra_steps -= 1
|
251
|
+
else
|
252
|
+
loop_from_value = from_value
|
253
|
+
end
|
254
|
+
|
255
|
+
step_time_increment = time_increment / ((to_value - from_value).abs + extra_steps)
|
256
|
+
|
257
|
+
intermediate_point_time = from_time
|
258
|
+
|
259
|
+
# 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}"
|
260
|
+
|
261
|
+
loop_from_value.step(loop_to_value, step_value_increment) do |value|
|
262
|
+
if @segments.last &&
|
263
|
+
@segments.last[:time] == intermediate_point_time &&
|
264
|
+
@segments.last[:value] == value
|
265
|
+
|
266
|
+
@segments.last[:duration] = step_time_increment
|
267
|
+
@segments.last[:info] += "; edited on b"
|
268
|
+
|
269
|
+
# puts "process2: editing #{@segments.last}"
|
270
|
+
|
271
|
+
else
|
272
|
+
@segments << v = { time: intermediate_point_time,
|
273
|
+
value: value,
|
274
|
+
duration: step_time_increment,
|
275
|
+
info: "added on b" }
|
276
|
+
|
277
|
+
# puts "process2: adding #{v.inspect}"
|
278
|
+
end
|
279
|
+
|
280
|
+
intermediate_point_time += step_time_increment
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
true
|
285
|
+
else
|
286
|
+
false
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
private def count_ready_points
|
291
|
+
@points.select { |_| _[:ready] }.size
|
292
|
+
end
|
293
|
+
|
294
|
+
private def process(time, value, last_time_value)
|
295
|
+
if time && value
|
296
|
+
raise RuntimeError, "time only can go forward" if @last_processed_time && time <= @last_processed_time
|
297
|
+
|
298
|
+
q_value = round_quantize(value)
|
299
|
+
|
300
|
+
# A continuation point time will be changed if new points of equal value arrive.
|
301
|
+
# For this reason this point is not ready to consume.
|
302
|
+
# A ready point is a well determined point that can be consumed.
|
303
|
+
# When we arrive to a well determined point all the previous points become also determined (ready).
|
304
|
+
|
305
|
+
if q_value == @last_processed_q_value && !last_time_value
|
306
|
+
if @points.size == 1 || @points.last[:ready]
|
307
|
+
# If @points.last is the first point of a segment the new point is a continuation point.
|
308
|
+
# The continuation point is used as a stop point.
|
309
|
+
@points << { time: time, value: q_value }
|
310
|
+
else
|
311
|
+
# @points.last is NOT the first point of a segment but a continuation point.
|
312
|
+
@points.last[:time] = time
|
313
|
+
end
|
314
|
+
else
|
315
|
+
@points.reverse_each do |point|
|
316
|
+
break if point[:ready]
|
317
|
+
point[:ready] = true
|
318
|
+
end
|
319
|
+
|
320
|
+
@points << { time: time, value: q_value, ready: true, last: last_time_value }
|
321
|
+
end
|
322
|
+
|
323
|
+
@last_processed_q_value = q_value
|
324
|
+
@last_processed_time = time
|
325
|
+
|
326
|
+
true
|
327
|
+
else
|
328
|
+
false
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
private def round_quantize(value)
|
333
|
+
round((value - @reference) / @step_size) * @step_size + @reference
|
334
|
+
end
|
335
|
+
|
336
|
+
private def round(value)
|
337
|
+
i = value.floor
|
338
|
+
value > (i + 1/2r) ? i + 1r : i.rationalize
|
339
|
+
end
|
340
|
+
|
341
|
+
def infinite?
|
342
|
+
!!@source.infinite?
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
private_constant :RawQuantizer
|
347
|
+
|
348
|
+
class PredictiveQuantizer
|
349
|
+
include Serie
|
350
|
+
include QuantizerTools
|
351
|
+
|
352
|
+
attr_reader :source
|
353
|
+
|
354
|
+
def initialize(reference, step, source, value_attribute, include_stops)
|
355
|
+
@reference = reference
|
356
|
+
@step_size = step
|
357
|
+
|
358
|
+
@source = source
|
359
|
+
@value_attribute = value_attribute
|
360
|
+
|
361
|
+
@include_stops = include_stops
|
362
|
+
|
363
|
+
@halfway_offset = step / 2r
|
364
|
+
@crossing_reference = reference - @halfway_offset
|
365
|
+
|
366
|
+
_restart false
|
367
|
+
|
368
|
+
mark_regarding! source
|
369
|
+
end
|
370
|
+
|
371
|
+
def _restart(restart_sources = true)
|
372
|
+
@source.restart if restart_sources
|
373
|
+
|
374
|
+
@last_time = nil
|
375
|
+
@crossings = []
|
376
|
+
|
377
|
+
@first = true
|
378
|
+
end
|
379
|
+
|
380
|
+
def infinite?
|
381
|
+
!!@source.infinite?
|
382
|
+
end
|
383
|
+
|
384
|
+
def _next_value
|
385
|
+
result = nil
|
386
|
+
|
387
|
+
first_time, first_value = get_time_value(@source.peek_next_value) if @first
|
388
|
+
|
389
|
+
while @crossings.size <= 2 && process_more; end
|
390
|
+
|
391
|
+
@crossings.delete_if { |c| c[:stop] && c[:stops].nil? }
|
392
|
+
|
393
|
+
if @crossings[0]
|
394
|
+
time = @crossings[0][:time]
|
395
|
+
value = @crossings[0][@value_attribute]
|
396
|
+
|
397
|
+
if @first
|
398
|
+
@first = false
|
399
|
+
|
400
|
+
if time > first_time
|
401
|
+
result = { time: first_time,
|
402
|
+
@value_attribute => round_to_nearest_quantize(first_value, value),
|
403
|
+
duration: time - first_time }.extend(AbsD).extend(AbsTimed)
|
404
|
+
else
|
405
|
+
result = _next_value
|
406
|
+
end
|
407
|
+
else
|
408
|
+
if @crossings[1]
|
409
|
+
next_time = @crossings[1][:time]
|
410
|
+
result = { time: time,
|
411
|
+
@value_attribute => value,
|
412
|
+
duration: next_time - time }.extend(AbsD).extend(AbsTimed)
|
413
|
+
|
414
|
+
@crossings.shift
|
415
|
+
|
416
|
+
else
|
417
|
+
if @last_time && @last_time > @crossings[0][:time]
|
418
|
+
result = { time: @crossings[0][:time],
|
419
|
+
@value_attribute => @crossings[0][@value_attribute],
|
420
|
+
duration: @last_time - @crossings[0][:time] }.extend(AbsD).extend(AbsTimed)
|
421
|
+
|
422
|
+
@last_time = nil
|
423
|
+
end
|
424
|
+
end
|
425
|
+
end
|
426
|
+
else
|
427
|
+
if @first && @last_time && @last_time > first_time
|
428
|
+
result = { time: first_time,
|
429
|
+
value: round_to_nearest_quantize(first_value),
|
430
|
+
duration: @last_time - first_time }.extend(AbsD).extend(AbsTimed)
|
431
|
+
|
432
|
+
@first = false
|
433
|
+
@last_time = false
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
return result
|
438
|
+
end
|
439
|
+
|
440
|
+
private def process_more
|
441
|
+
while (@crossings.size <= 2 || @crossings[-1][:stop]) && new_crossings = next_crossings
|
442
|
+
new_crossings.each do |c|
|
443
|
+
if @last_time.nil? || c[:time] > @last_time
|
444
|
+
|
445
|
+
if c[:stop] &&
|
446
|
+
@crossings.dig(-1, :stop) &&
|
447
|
+
@crossings.dig(-1, @value_attribute) == c[@value_attribute]
|
448
|
+
|
449
|
+
c[:stops] = (@crossings[-1][:stops] || 0) + 1
|
450
|
+
|
451
|
+
@crossings[-1] = c
|
452
|
+
else
|
453
|
+
@crossings << c
|
454
|
+
end
|
455
|
+
end
|
456
|
+
end
|
457
|
+
end
|
458
|
+
|
459
|
+
!!new_crossings
|
460
|
+
end
|
461
|
+
|
462
|
+
private def next_crossings
|
463
|
+
from_time, from_value = get_time_value(@source.next_value)
|
464
|
+
|
465
|
+
if from_time && from_value
|
466
|
+
raise RuntimeError, "time only can go forward" if @last_time && from_time <= @last_time
|
467
|
+
|
468
|
+
@last_time = from_time
|
469
|
+
|
470
|
+
to_time, to_value = get_time_value(@source.peek_next_value)
|
471
|
+
|
472
|
+
if to_time && to_value
|
473
|
+
crossings(from_time, from_value, to_time, to_value)
|
474
|
+
end
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
private def crossings(from_time, from_value, to_time, to_value)
|
479
|
+
sign = to_value >= from_value ? 1r : -1r
|
480
|
+
|
481
|
+
if sign == 1
|
482
|
+
from_step = ((from_value - @crossing_reference) / @step_size).ceil
|
483
|
+
last_step = ((to_value - @crossing_reference) / @step_size).floor
|
484
|
+
else
|
485
|
+
from_step = ((from_value - @crossing_reference) / @step_size).floor
|
486
|
+
last_step = ((to_value - @crossing_reference) / @step_size).ceil
|
487
|
+
end
|
488
|
+
|
489
|
+
delta_value = to_value - from_value
|
490
|
+
delta_time = to_time - from_time
|
491
|
+
|
492
|
+
crossings =
|
493
|
+
from_step.step(last_step, sign).collect do |i|
|
494
|
+
value = @crossing_reference + i * @step_size
|
495
|
+
|
496
|
+
{ time: from_time + (delta_time / delta_value) * (value - from_value),
|
497
|
+
@value_attribute => value + sign * @halfway_offset }
|
498
|
+
end
|
499
|
+
|
500
|
+
if @include_stops
|
501
|
+
first_crossing_time = crossings.dig(0, :time)
|
502
|
+
last_crossing_time = crossings.dig(-1, :time)
|
503
|
+
|
504
|
+
if first_crossing_time.nil? || from_time < first_crossing_time
|
505
|
+
stop_before = [ { time: from_time,
|
506
|
+
@value_attribute =>
|
507
|
+
round_to_nearest_quantize(from_value,
|
508
|
+
crossings.dig(0, @value_attribute)),
|
509
|
+
stop: true } ]
|
510
|
+
else
|
511
|
+
stop_before = []
|
512
|
+
end
|
513
|
+
|
514
|
+
if last_crossing_time.nil? || to_time > last_crossing_time
|
515
|
+
stop_after = [ { time: to_time,
|
516
|
+
@value_attribute =>
|
517
|
+
round_to_nearest_quantize(to_value,
|
518
|
+
crossings.dig(-1, @value_attribute)),
|
519
|
+
stop: true } ]
|
520
|
+
else
|
521
|
+
stop_after = []
|
522
|
+
end
|
523
|
+
|
524
|
+
stop_before + crossings + stop_after
|
525
|
+
else
|
526
|
+
crossings
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
private def round_to_nearest_quantize(value, nearest = nil)
|
531
|
+
v = (value - @reference) / @step_size
|
532
|
+
|
533
|
+
if nearest
|
534
|
+
a = v.floor * @step_size + @reference
|
535
|
+
b = v.ceil * @step_size + @reference
|
536
|
+
|
537
|
+
(nearest - a).abs < (nearest - b).abs ? a : b
|
538
|
+
else
|
539
|
+
v.round * @step_size + @reference
|
540
|
+
end
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
544
|
+
private_constant :PredictiveQuantizer
|
545
|
+
end
|
546
|
+
end
|