musa-dsl 0.22.3 → 0.23.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +3 -1
- data/lib/musa-dsl.rb +14 -8
- data/lib/musa-dsl/core-ext/deep-copy.rb +12 -1
- data/lib/musa-dsl/core-ext/inspect-nice.rb +1 -2
- data/lib/musa-dsl/core-ext/smart-proc-binder.rb +13 -11
- data/lib/musa-dsl/datasets/p.rb +41 -16
- data/lib/musa-dsl/datasets/score/to-mxml/process-pdv.rb +14 -12
- data/lib/musa-dsl/datasets/score/to-mxml/process-ps.rb +32 -6
- data/lib/musa-dsl/datasets/score/to-mxml/to-mxml.rb +24 -10
- data/lib/musa-dsl/generative/backboner.rb +6 -11
- data/lib/musa-dsl/generative/generative-grammar.rb +1 -3
- data/lib/musa-dsl/generative/markov.rb +10 -6
- data/lib/musa-dsl/logger/logger.rb +6 -1
- data/lib/musa-dsl/matrix/matrix.rb +9 -7
- data/lib/musa-dsl/midi/midi-voices.rb +8 -7
- data/lib/musa-dsl/music/scales.rb +1 -1
- data/lib/musa-dsl/neumalang/neumalang.rb +1 -1
- data/lib/musa-dsl/neumas/array-to-neumas.rb +1 -1
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +9 -4
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-timed.rb +30 -129
- data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +10 -24
- data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +9 -9
- data/lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb +3 -5
- data/lib/musa-dsl/sequencer/base-sequencer.rb +14 -23
- data/lib/musa-dsl/sequencer/sequencer-dsl.rb +9 -7
- data/lib/musa-dsl/sequencer/sequencer.rb +7 -0
- data/lib/musa-dsl/series/base-series.rb +293 -144
- data/lib/musa-dsl/series/buffer-serie.rb +237 -0
- data/lib/musa-dsl/series/hash-or-array-serie-splitter.rb +136 -105
- data/lib/musa-dsl/series/main-serie-constructors.rb +251 -156
- data/lib/musa-dsl/series/main-serie-operations.rb +308 -303
- data/lib/musa-dsl/series/proxy-serie.rb +21 -41
- data/lib/musa-dsl/series/quantizer-serie.rb +44 -46
- data/lib/musa-dsl/series/queue-serie.rb +39 -43
- data/lib/musa-dsl/series/series-composer.rb +149 -0
- data/lib/musa-dsl/series/series.rb +6 -3
- data/lib/musa-dsl/series/timed-serie.rb +343 -0
- data/musa-dsl.gemspec +13 -3
- metadata +10 -11
- data/lib/musa-dsl/series/flattener-timed-serie.rb +0 -61
- data/lib/musa-dsl/series/holder-serie.rb +0 -87
- data/lib/musa-dsl/series/union-timed-series.rb +0 -109
@@ -3,13 +3,8 @@ require_relative '../core-ext/with'
|
|
3
3
|
|
4
4
|
using Musa::Extension::Arrayfy
|
5
5
|
|
6
|
-
#
|
7
|
-
#
|
8
|
-
# crear rama tb debe recibir la serie de la history -> ya lo hace
|
9
|
-
# crear rama puede repetirse (hasta terminar según ended_when) -> no
|
10
|
-
#
|
11
|
-
# hacer que pueda funcionar en tiempo real? le vas suministrando seeds y le vas diciendo qué opción has elegido (p.ej. para hacer un armonizador en tiempo real)
|
12
|
-
# esto mismo sería aplicable en otros generadores? variatio/darwin? generative-grammar? markov?
|
6
|
+
# TODO hacer que pueda funcionar en tiempo real? le vas suministrando seeds y le vas diciendo qué opción has elegido (p.ej. para hacer un armonizador en tiempo real)
|
7
|
+
# TODO esto mismo sería aplicable en otros generadores? variatio/darwin? generative-grammar? markov?
|
13
8
|
|
14
9
|
module Musa
|
15
10
|
module Backboner
|
@@ -17,12 +12,12 @@ module Musa
|
|
17
12
|
include Musa::Extension::With
|
18
13
|
|
19
14
|
def initialize(&block)
|
20
|
-
@
|
15
|
+
@dsl = RulesEvalContext.new(&block)
|
21
16
|
end
|
22
17
|
|
23
18
|
def generate_possibilities(object, confirmed_node = nil, node = nil, grow_rules = nil)
|
24
19
|
node ||= Node.new
|
25
|
-
grow_rules ||= @
|
20
|
+
grow_rules ||= @dsl._grow_rules
|
26
21
|
|
27
22
|
history = confirmed_node.history if confirmed_node
|
28
23
|
history ||= []
|
@@ -33,9 +28,9 @@ module Musa
|
|
33
28
|
if grow_rule
|
34
29
|
grow_rule.generate_possibilities(object, history).each do |new_object|
|
35
30
|
new_node = Node.new new_object, node
|
36
|
-
new_node.mark_as_ended! if @
|
31
|
+
new_node.mark_as_ended! if @dsl._ended? new_object
|
37
32
|
|
38
|
-
rejection = @
|
33
|
+
rejection = @dsl._cut_rules.find { |cut_rule| cut_rule.rejects?(new_object, history) }
|
39
34
|
# TODO: include rejection secondary reasons in rejection message
|
40
35
|
|
41
36
|
new_node.reject! rejection if rejection
|
@@ -100,9 +100,7 @@ module Musa
|
|
100
100
|
options[index].to_serie.to_node
|
101
101
|
end
|
102
102
|
|
103
|
-
def to_serie(flatten:
|
104
|
-
flatten ||= true
|
105
|
-
|
103
|
+
def to_serie(flatten: true, &condition)
|
106
104
|
serie = _options(&condition).collect { |o| o.collect(&:content) }.to_serie(of_series: true).merge
|
107
105
|
serie = serie.flatten if flatten
|
108
106
|
|
@@ -7,9 +7,8 @@ module Musa
|
|
7
7
|
module Markov
|
8
8
|
class Markov
|
9
9
|
include Musa::Extension::SmartProcBinder
|
10
|
-
include Musa::Series::Serie
|
10
|
+
include Musa::Series::Serie.base
|
11
11
|
|
12
|
-
attr_accessor :start, :finish, :random, :transitions
|
13
12
|
|
14
13
|
def initialize(transitions:, start:, finish: nil, random: nil)
|
15
14
|
@transitions = transitions.clone.freeze
|
@@ -22,23 +21,28 @@ module Musa
|
|
22
21
|
|
23
22
|
@procedure_binders = {}
|
24
23
|
|
25
|
-
|
24
|
+
init
|
26
25
|
end
|
27
26
|
|
28
|
-
|
27
|
+
attr_accessor :start
|
28
|
+
attr_accessor :finish
|
29
|
+
attr_accessor :random
|
30
|
+
attr_accessor :transitions
|
31
|
+
|
32
|
+
private def _init
|
29
33
|
@current = nil
|
30
34
|
@finished = false
|
31
35
|
@history = []
|
32
36
|
end
|
33
37
|
|
34
|
-
def _next_value
|
38
|
+
private def _next_value
|
35
39
|
if @finished
|
36
40
|
@current = nil
|
37
41
|
else
|
38
42
|
if @current.nil?
|
39
43
|
@current = @start
|
40
44
|
else
|
41
|
-
if @transitions.has_key?
|
45
|
+
if @transitions.has_key?(@current)
|
42
46
|
options = @transitions[@current]
|
43
47
|
|
44
48
|
case options
|
@@ -1,7 +1,12 @@
|
|
1
1
|
require 'logger'
|
2
2
|
|
3
|
+
require_relative '../core-ext/inspect-nice'
|
4
|
+
|
5
|
+
|
3
6
|
module Musa; module Logger
|
4
7
|
class Logger < ::Logger
|
8
|
+
using Musa::Extension::InspectNice
|
9
|
+
|
5
10
|
def initialize(sequencer: nil, position_format: nil)
|
6
11
|
super STDERR, level: WARN
|
7
12
|
|
@@ -22,7 +27,7 @@ module Musa; module Logger
|
|
22
27
|
|
23
28
|
progname = "[#{progname}]" if progname
|
24
29
|
|
25
|
-
"#{position}#{level}#{progname} #{msg}\n"
|
30
|
+
"#{position}#{level}#{progname}#{' ' if position || level || progname}#{msg}\n"
|
26
31
|
else
|
27
32
|
"\n"
|
28
33
|
end
|
@@ -22,8 +22,8 @@ module Musa
|
|
22
22
|
indexes
|
23
23
|
end
|
24
24
|
|
25
|
-
def to_p(time_dimension
|
26
|
-
condensed_matrices.collect { |m| m.to_p(time_dimension, keep_time: keep_time) }
|
25
|
+
def to_p(time_dimension:, keep_time: nil)
|
26
|
+
condensed_matrices.collect { |m| m.to_p(time_dimension: time_dimension, keep_time: keep_time) }
|
27
27
|
end
|
28
28
|
|
29
29
|
def condensed_matrices
|
@@ -69,14 +69,14 @@ module Musa
|
|
69
69
|
refine ::Matrix do
|
70
70
|
include Musa::Datasets
|
71
71
|
|
72
|
-
def to_p(time_dimension
|
72
|
+
def to_p(time_dimension:, keep_time: nil)
|
73
73
|
decompose(self.to_a, time_dimension).collect do |points|
|
74
74
|
line = []
|
75
75
|
|
76
76
|
start_point = points[0]
|
77
77
|
start_time = start_point[time_dimension]
|
78
78
|
|
79
|
-
line << start_point.tap { |_| _.delete_at(time_dimension) unless keep_time; _ }.extend(Datasets::V)
|
79
|
+
line << start_point.clone.tap { |_| _.delete_at(time_dimension) unless keep_time; _ }.extend(Datasets::V)
|
80
80
|
|
81
81
|
(1..points.size-1).each do |i|
|
82
82
|
end_point = points[i]
|
@@ -84,7 +84,7 @@ module Musa
|
|
84
84
|
end_time = end_point[time_dimension]
|
85
85
|
|
86
86
|
line << end_time - start_time
|
87
|
-
line << end_point.tap { |_| _.delete_at(time_dimension) unless keep_time; _ }.extend(Datasets::V)
|
87
|
+
line << end_point.clone.tap { |_| _.delete_at(time_dimension) unless keep_time; _ }.extend(Datasets::V)
|
88
88
|
|
89
89
|
start_time = end_time
|
90
90
|
end
|
@@ -107,8 +107,9 @@ module Musa
|
|
107
107
|
|
108
108
|
x_dim_values_indexes.keys.sort.each do |value|
|
109
109
|
x_dim_values_indexes[value].each do |index|
|
110
|
+
#
|
110
111
|
# hacia un lado
|
111
|
-
|
112
|
+
#
|
112
113
|
unless used_indexes.include?(index)
|
113
114
|
i = index
|
114
115
|
xx = array[i][time_dimension]
|
@@ -125,8 +126,9 @@ module Musa
|
|
125
126
|
|
126
127
|
directional_segments << a if a.size > 1
|
127
128
|
|
129
|
+
#
|
128
130
|
# y hacia el otro
|
129
|
-
|
131
|
+
#
|
130
132
|
i = index
|
131
133
|
xx = array[i][time_dimension]
|
132
134
|
|
@@ -10,7 +10,7 @@ using Musa::Extension::ExplodeRanges
|
|
10
10
|
module Musa
|
11
11
|
module MIDIVoices
|
12
12
|
class MIDIVoices
|
13
|
-
attr_accessor :
|
13
|
+
attr_accessor :do_log
|
14
14
|
|
15
15
|
def initialize(sequencer:, output:, channels:, do_log: nil)
|
16
16
|
do_log ||= false
|
@@ -24,7 +24,7 @@ module Musa
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def reset
|
27
|
-
@voices = @channels.collect { |channel| MIDIVoice.new
|
27
|
+
@voices = @channels.collect { |channel| MIDIVoice.new(sequencer: @sequencer, output: @output, channel: channel, do_log: @do_log) }.freeze
|
28
28
|
end
|
29
29
|
|
30
30
|
attr_reader :voices
|
@@ -48,14 +48,14 @@ module Musa
|
|
48
48
|
attr_accessor :name, :do_log
|
49
49
|
attr_reader :sequencer, :output, :channel, :active_pitches, :tick_duration
|
50
50
|
|
51
|
-
def initialize(sequencer:, output:, channel:, name: nil,
|
52
|
-
|
51
|
+
def initialize(sequencer:, output:, channel:, name: nil, do_log: nil)
|
52
|
+
do_log ||= false
|
53
53
|
|
54
54
|
@sequencer = sequencer
|
55
55
|
@output = output
|
56
56
|
@channel = channel
|
57
57
|
@name = name
|
58
|
-
@do_log =
|
58
|
+
@do_log = do_log
|
59
59
|
|
60
60
|
@tick_duration = Rational(1, @sequencer.ticks_per_bar)
|
61
61
|
|
@@ -64,7 +64,7 @@ module Musa
|
|
64
64
|
@active_pitches = []
|
65
65
|
fill_active_pitches @active_pitches
|
66
66
|
|
67
|
-
|
67
|
+
@sequencer.logger.warn 'voice without output' unless @output
|
68
68
|
|
69
69
|
self
|
70
70
|
end
|
@@ -118,7 +118,7 @@ module Musa
|
|
118
118
|
end
|
119
119
|
|
120
120
|
def log(msg)
|
121
|
-
@sequencer.
|
121
|
+
@sequencer.logger.info('MIDIVoice') { "voice #{name || @channel}: #{msg}" } if @do_log
|
122
122
|
end
|
123
123
|
|
124
124
|
def to_s
|
@@ -169,6 +169,7 @@ module Musa
|
|
169
169
|
private_constant :ControllersControl
|
170
170
|
|
171
171
|
class NoteControl
|
172
|
+
attr_reader :voice, :pitch, :velocity, :velocity_off, :duration
|
172
173
|
attr_reader :start_position, :end_position
|
173
174
|
|
174
175
|
def initialize(voice, pitch:, velocity: nil, duration: nil, velocity_off: nil)
|
@@ -303,7 +303,7 @@ module Musa
|
|
303
303
|
|
304
304
|
def [](grade_or_symbol)
|
305
305
|
|
306
|
-
raise ArgumentError, "grade_or_symbol '#{grade_or_symbol}' should be a
|
306
|
+
raise ArgumentError, "grade_or_symbol '#{grade_or_symbol}' should be a Integer, String or Symbol" unless grade_or_symbol.is_a?(Symbol) || grade_or_symbol.is_a?(String) || grade_or_symbol.is_a?(Integer)
|
307
307
|
|
308
308
|
wide_grade, sharps = grade_of(grade_or_symbol)
|
309
309
|
|
@@ -21,7 +21,7 @@ module Musa
|
|
21
21
|
def convert_to_neumas(e)
|
22
22
|
case e
|
23
23
|
when Musa::Neumas::Neuma::Serie then e
|
24
|
-
when Musa::Neumas::Neuma::Parallel then
|
24
|
+
when Musa::Neumas::Neuma::Parallel then S(e).extend(Musa::Neumas::Neuma::Serie)
|
25
25
|
when String then e.to_neumas
|
26
26
|
else
|
27
27
|
raise ArgumentError, "Don't know how to convert to neumas #{e}"
|
@@ -262,11 +262,11 @@ module Musa
|
|
262
262
|
|
263
263
|
when Musa::Series::Serie
|
264
264
|
{ current_operation: :play,
|
265
|
-
current_parameter: element.restart }
|
265
|
+
current_parameter: element.instance.restart }
|
266
266
|
|
267
267
|
when Parallel
|
268
268
|
{ current_operation: :parallel_play,
|
269
|
-
current_parameter: element.tap { |e| e.each(&:restart) } }
|
269
|
+
current_parameter: element.instance.tap { |e| e.each(&:restart) } }
|
270
270
|
|
271
271
|
when Array
|
272
272
|
{ current_operation: :no_eval_play,
|
@@ -324,8 +324,13 @@ module Musa
|
|
324
324
|
run_operation eval_use_variable(element[:use_variable])
|
325
325
|
|
326
326
|
when :event
|
327
|
-
value_parameters = element[:value_parameters] ?
|
328
|
-
|
327
|
+
value_parameters = element[:value_parameters] ?
|
328
|
+
element[:value_parameters].collect { |e| subcontext.eval_element(e) } :
|
329
|
+
[]
|
330
|
+
|
331
|
+
key_parameters = element[:key_parameters] ?
|
332
|
+
element[:key_parameters].collect { |k, e| [k, subcontext.eval_element(e)] }.to_h :
|
333
|
+
{}
|
329
334
|
|
330
335
|
{ current_operation: :event,
|
331
336
|
current_event: element[:event],
|
@@ -7,181 +7,82 @@ module Musa; module Sequencer
|
|
7
7
|
class BaseSequencer
|
8
8
|
private def _play_timed(timed_serie,
|
9
9
|
control,
|
10
|
-
reference: nil,
|
11
|
-
step: nil,
|
12
|
-
right_open: nil,
|
13
10
|
&block)
|
14
11
|
|
15
|
-
reference ||= 0r
|
16
|
-
step ||= 1r
|
17
|
-
|
18
12
|
if first_value_sample = timed_serie.peek_next_value
|
19
|
-
|
20
13
|
debug "_play_timed: first_value_sample #{first_value_sample}"
|
21
14
|
|
22
15
|
hash_mode = first_value_sample[:value].is_a?(Hash)
|
23
16
|
|
24
17
|
if hash_mode
|
25
|
-
|
26
|
-
|
27
|
-
reference = reference.hashify(keys: components)
|
28
|
-
step = step.hashify(keys: components)
|
29
|
-
right_open = right_open.hashify(keys:components)
|
18
|
+
component_ids = first_value_sample[:value].keys
|
30
19
|
else
|
31
20
|
size = first_value_sample[:value].size
|
32
|
-
|
33
|
-
|
34
|
-
reference = reference.arrayfy(size: size)
|
35
|
-
step = step.arrayfy(size: size)
|
36
|
-
right_open = right_open.arrayfy(size: size)
|
37
|
-
end
|
38
|
-
|
39
|
-
split = timed_serie.flatten_timed.split
|
40
|
-
quantized_series = hash_mode ? {} : []
|
41
|
-
|
42
|
-
components.each do |component|
|
43
|
-
quantized_series[component] =
|
44
|
-
QUANTIZE(split[component],
|
45
|
-
reference: reference[component],
|
46
|
-
step: step[component],
|
47
|
-
right_open: right_open[component],
|
48
|
-
stops: true).instance
|
21
|
+
component_ids = (0 .. size-1).to_a
|
49
22
|
end
|
23
|
+
extra_attribute_names = Set[*(first_value_sample.keys - [:time, :value])]
|
50
24
|
|
51
25
|
last_positions = hash_mode ? {} : []
|
52
26
|
end
|
53
27
|
|
54
28
|
binder = SmartProcBinder.new(block)
|
55
29
|
|
56
|
-
_play_timed_step(hash_mode,
|
30
|
+
_play_timed_step(hash_mode, component_ids, extra_attribute_names, timed_serie,
|
31
|
+
position, last_positions, binder, control)
|
57
32
|
end
|
58
33
|
|
59
|
-
|
60
|
-
|
34
|
+
private def _play_timed_step(hash_mode,
|
35
|
+
component_ids, extra_attribute_names,
|
36
|
+
timed_serie,
|
37
|
+
start_position,
|
38
|
+
last_positions,
|
61
39
|
binder, control)
|
62
40
|
|
63
|
-
|
64
|
-
|
65
|
-
components.each do |component|
|
66
|
-
if v = quantized_series[component].peek_next_value
|
41
|
+
source_next_value = timed_serie.next_value
|
67
42
|
|
68
|
-
|
69
|
-
time = v[:time]
|
70
|
-
|
71
|
-
affected_components_by_time[time] ||= []
|
72
|
-
affected_components_by_time[time] << component
|
73
|
-
end
|
74
|
-
end
|
43
|
+
affected_components = component_ids.select { |_| !source_next_value[:value][_].nil? } if source_next_value
|
75
44
|
|
76
|
-
if
|
77
|
-
time =
|
45
|
+
if affected_components && affected_components.any?
|
46
|
+
time = source_next_value[:time]
|
78
47
|
|
79
48
|
values = hash_mode ? {} : []
|
80
|
-
|
81
|
-
durations = hash_mode ? {} : []
|
82
|
-
q_durations = hash_mode ? {} : []
|
49
|
+
extra_attributes = extra_attribute_names.collect { |_| [_, hash_mode ? {} : []] }.to_h
|
83
50
|
started_ago = hash_mode ? {} : []
|
84
51
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
values[component] = value[:value]
|
89
|
-
durations[component] = value[:duration]
|
90
|
-
|
91
|
-
q_durations[component] =
|
92
|
-
_quantize_position(time + durations[component], warn: false) -
|
93
|
-
_quantize_position(time, warn: false)
|
52
|
+
affected_components.each do |component|
|
53
|
+
values[component] = source_next_value[:value][component]
|
94
54
|
|
95
|
-
|
96
|
-
|
55
|
+
extra_attribute_names.each do |attribute_name|
|
56
|
+
extra_attributes[attribute_name][component] = source_next_value[attribute_name][component]
|
57
|
+
end
|
97
58
|
|
98
59
|
last_positions[component] = _quantize_position(time, warn: false)
|
99
60
|
end
|
100
61
|
|
101
|
-
|
62
|
+
component_ids.each do |component|
|
102
63
|
if last_positions[component] && last_positions[component] != time
|
103
64
|
sa = _quantize_position(time, warn: false) - last_positions[component]
|
104
65
|
started_ago[component] = (sa == 0) ? nil : sa
|
105
66
|
end
|
106
67
|
end
|
107
68
|
|
108
|
-
_numeric_at start_position +
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
quantized_duration: q_durations,
|
69
|
+
_numeric_at _quantize_position(start_position + time, warn: true), control do
|
70
|
+
binder.call(values,
|
71
|
+
**extra_attributes,
|
72
|
+
time: start_position + time,
|
113
73
|
started_ago: started_ago,
|
114
74
|
control: control)
|
115
75
|
|
116
|
-
_play_timed_step(hash_mode,
|
76
|
+
_play_timed_step(hash_mode,
|
77
|
+
component_ids, extra_attribute_names,
|
78
|
+
timed_serie,
|
79
|
+
start_position,
|
80
|
+
last_positions,
|
117
81
|
binder, control)
|
118
82
|
end
|
119
83
|
end
|
120
84
|
end
|
121
85
|
|
122
|
-
# TODO implement this alternative play method as another mode
|
123
|
-
# Este es un modo muy interesante pero que implica un procesamiento diferente en el yield_block que no me
|
124
|
-
# sirve para el código de samples/multidim_sample, puesto que en este el next_values es literal,
|
125
|
-
# mientras que samples/multidim_sample necesita que el next_value sea nil si el valor no cambia durante el periodo.
|
126
|
-
#
|
127
|
-
private def _play_timed_step_b(hash_mode, components, quantized_series, start_position, last_positions,
|
128
|
-
binder, control)
|
129
|
-
|
130
|
-
affected_components_by_time = {}
|
131
|
-
|
132
|
-
components.each do |component|
|
133
|
-
if v = quantized_series[component].peek_next_value
|
134
|
-
time = v[:time]
|
135
|
-
|
136
|
-
affected_components_by_time[time] ||= []
|
137
|
-
affected_components_by_time[time] << component
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
if !affected_components_by_time.empty?
|
142
|
-
time = affected_components_by_time.keys.sort.first
|
143
|
-
|
144
|
-
values = hash_mode ? {} : []
|
145
|
-
next_values = hash_mode ? {} : []
|
146
|
-
durations = hash_mode ? {} : []
|
147
|
-
q_durations = hash_mode ? {} : []
|
148
|
-
started_ago = hash_mode ? {} : []
|
149
|
-
|
150
|
-
affected_components_by_time[time].each do |component|
|
151
|
-
value = quantized_series[component].next_value
|
152
|
-
|
153
|
-
values[component] = value[:value]
|
154
|
-
durations[component] = value[:duration]
|
155
|
-
|
156
|
-
q_durations[component] =
|
157
|
-
_quantize_position(time + durations[component], warn: false) -
|
158
|
-
_quantize_position(time, warn: false)
|
159
|
-
|
160
|
-
last_positions[component] = _quantize_position(time, warn: false)
|
161
|
-
end
|
162
|
-
|
163
|
-
components.each do |component|
|
164
|
-
nv = quantized_series[component].peek_next_value
|
165
|
-
next_values[component] = nv[:value] if nv
|
166
|
-
|
167
|
-
if last_positions[component] && last_positions[component] != time
|
168
|
-
started_ago[component] = _quantize_position(time, warn: false) - last_positions[component]
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
_numeric_at start_position + _quantize_position(time, warn: false), control do
|
173
|
-
binder.call(values, next_values,
|
174
|
-
duration: durations,
|
175
|
-
quantized_duration: q_durations,
|
176
|
-
started_ago: started_ago,
|
177
|
-
control: control)
|
178
|
-
|
179
|
-
_play_timed_step_b(hash_mode, components, quantized_series, start_position, last_positions,
|
180
|
-
binder, control)
|
181
|
-
end
|
182
|
-
end
|
183
|
-
end
|
184
|
-
|
185
86
|
class PlayTimedControl < EventHandler
|
186
87
|
attr_reader :do_on_stop, :do_after
|
187
88
|
|