musa-dsl 0.21.5 → 0.22.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/musa-dsl.rb +1 -1
  3. data/lib/musa-dsl/core-ext.rb +1 -0
  4. data/lib/musa-dsl/core-ext/arrayfy.rb +9 -9
  5. data/lib/musa-dsl/core-ext/hashify.rb +42 -0
  6. data/lib/musa-dsl/core-ext/inspect-nice.rb +6 -1
  7. data/lib/musa-dsl/datasets/e.rb +22 -5
  8. data/lib/musa-dsl/datasets/gdv.rb +0 -1
  9. data/lib/musa-dsl/datasets/p.rb +29 -36
  10. data/lib/musa-dsl/datasets/pdv.rb +0 -1
  11. data/lib/musa-dsl/datasets/ps.rb +10 -78
  12. data/lib/musa-dsl/logger/logger.rb +2 -2
  13. data/lib/musa-dsl/matrix/matrix.rb +9 -64
  14. data/lib/musa-dsl/sequencer/base-sequencer-implementation-every.rb +87 -0
  15. data/lib/musa-dsl/sequencer/base-sequencer-implementation-move.rb +439 -0
  16. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +2 -2
  17. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-timed.rb +219 -0
  18. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb +178 -0
  19. data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +150 -597
  20. data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +6 -8
  21. data/lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb +1 -5
  22. data/lib/musa-dsl/sequencer/{base-sequencer-public.rb → base-sequencer.rb} +63 -5
  23. data/lib/musa-dsl/sequencer/sequencer-dsl.rb +11 -2
  24. data/lib/musa-dsl/sequencer/sequencer.rb +1 -1
  25. data/lib/musa-dsl/series/base-series.rb +43 -78
  26. data/lib/musa-dsl/series/flattener-timed-serie.rb +61 -0
  27. data/lib/musa-dsl/series/hash-or-array-serie-splitter.rb +143 -0
  28. data/lib/musa-dsl/series/holder-serie.rb +1 -1
  29. data/lib/musa-dsl/series/main-serie-constructors.rb +32 -92
  30. data/lib/musa-dsl/series/main-serie-operations.rb +60 -215
  31. data/lib/musa-dsl/series/proxy-serie.rb +1 -1
  32. data/lib/musa-dsl/series/quantizer-serie.rb +558 -0
  33. data/lib/musa-dsl/series/queue-serie.rb +1 -1
  34. data/lib/musa-dsl/series/series.rb +8 -2
  35. data/lib/musa-dsl/series/union-timed-series.rb +109 -0
  36. data/musa-dsl.gemspec +2 -2
  37. metadata +12 -5
  38. data/lib/musa-dsl/sequencer/base-sequencer-implementation-control.rb +0 -216
  39. data/lib/musa-dsl/series/hash-serie-splitter.rb +0 -196
@@ -117,7 +117,7 @@ module Musa
117
117
 
118
118
  @@id = 0
119
119
 
120
- attr_reader :nl_context
120
+ attr_reader :neumalang_context
121
121
  attr_reader :block_procedure_binder
122
122
 
123
123
  def initialize(block_procedure_binder, decoder, nl_context, parent: nil)
@@ -166,7 +166,7 @@ module Musa
166
166
  end
167
167
 
168
168
  def eval_p(p)
169
- p.to_ps_serie(@decoder.base_duration).instance
169
+ p.to_ps_serie(base_duration: @decoder.base_duration).instance
170
170
  end
171
171
 
172
172
  def eval_serie(serie)
@@ -0,0 +1,219 @@
1
+ using Musa::Extension::Hashify
2
+ using Musa::Extension::Arrayfy
3
+
4
+ using Musa::Extension::InspectNice
5
+
6
+ module Musa; module Sequencer
7
+ class BaseSequencer
8
+ private def _play_timed(timed_serie,
9
+ control,
10
+ reference: nil,
11
+ step: nil,
12
+ predictive: nil,
13
+ right_open: nil,
14
+ stops: nil,
15
+ &block)
16
+
17
+ reference ||= 0r
18
+ step ||= 1r
19
+ predictive ||= predictive
20
+ stops = stops.nil? ? true : stops
21
+
22
+ if first_value_sample = timed_serie.peek_next_value
23
+
24
+ debug "_play_timed: first_value_sample #{first_value_sample}"
25
+
26
+ hash_mode = first_value_sample[:value].is_a?(Hash)
27
+
28
+ if hash_mode
29
+ components = first_value_sample[:value].keys
30
+
31
+ reference = reference.hashify(keys: components)
32
+ step = step.hashify(keys: components)
33
+ predictive = predictive.hashify(keys:components)
34
+ stops = stops.hashify(keys:components)
35
+ right_open = right_open.hashify(keys:components)
36
+ else
37
+ size = first_value_sample[:value].size
38
+ components = (0 .. size-1).to_a
39
+
40
+ reference = reference.arrayfy(size: size)
41
+ step = step.arrayfy(size: size)
42
+ predictive = predictive.arrayfy(size: size)
43
+ stops = stops.arrayfy(size: size)
44
+ right_open = right_open.arrayfy(size: size)
45
+ end
46
+
47
+ split = timed_serie.flatten_timed.split
48
+ quantized_series = hash_mode ? {} : []
49
+
50
+ components.each do |component|
51
+ quantized_series[component] =
52
+ QUANTIZE(split[component],
53
+ reference: reference[component],
54
+ step: step[component],
55
+ predictive: predictive[component],
56
+ right_open: right_open[component],
57
+ stops: stops[component]).instance
58
+ end
59
+
60
+ last_positions = hash_mode ? {} : []
61
+ end
62
+
63
+ binder = SmartProcBinder.new(block)
64
+
65
+ _play_timed_step(hash_mode, components, quantized_series, position, last_positions, binder, control)
66
+ end
67
+
68
+
69
+ private def _play_timed_step(hash_mode, components, quantized_series, start_position, last_positions,
70
+ binder, control)
71
+
72
+ affected_components_by_time = {}
73
+
74
+ components.each do |component|
75
+ if v = quantized_series[component].peek_next_value
76
+
77
+ debug "_play_timed_step: quantized_series[#{component}].peek_next_value #{v}"
78
+ time = v[:time]
79
+
80
+ affected_components_by_time[time] ||= []
81
+ affected_components_by_time[time] << component
82
+ end
83
+ end
84
+
85
+ if affected_components_by_time.any?
86
+ time = affected_components_by_time.keys.sort.first
87
+
88
+ values = hash_mode ? {} : []
89
+ next_values = hash_mode ? {} : []
90
+ durations = hash_mode ? {} : []
91
+ q_durations = hash_mode ? {} : []
92
+ started_ago = hash_mode ? {} : []
93
+
94
+ affected_components_by_time[time].each do |component|
95
+ value = quantized_series[component].next_value
96
+
97
+ values[component] = value[:value]
98
+ durations[component] = value[:duration]
99
+
100
+ q_durations[component] =
101
+ _quantize_position(time + durations[component], warn: false) -
102
+ _quantize_position(time, warn: false)
103
+
104
+ nv = quantized_series[component].peek_next_value
105
+ next_values[component] = (nv && nv[:value] != values[component]) ? nv[:value] : nil
106
+
107
+ last_positions[component] = _quantize_position(time, warn: false)
108
+ end
109
+
110
+ components.each do |component|
111
+ if last_positions[component] && last_positions[component] != time
112
+ sa = _quantize_position(time, warn: false) - last_positions[component]
113
+ started_ago[component] = (sa == 0) ? nil : sa
114
+ end
115
+ end
116
+
117
+ _numeric_at start_position + _quantize_position(time, warn: false), control do
118
+ debug "_play_timed_step: before binder.call: durations #{durations} q_durations #{q_durations}"
119
+ binder.call(values, next_values,
120
+ duration: durations,
121
+ quantized_duration: q_durations,
122
+ started_ago: started_ago,
123
+ control: control)
124
+
125
+ _play_timed_step(hash_mode, components, quantized_series, start_position, last_positions,
126
+ binder, control)
127
+ end
128
+ end
129
+ end
130
+
131
+ # TODO implement this alternative play method as another mode
132
+ # Este es un modo muy interesante pero que implica un procesamiento diferente en el yield_block que no me
133
+ # sirve para el código de samples/multidim_sample, puesto que en este el next_values es literal,
134
+ # mientras que samples/multidim_sample necesita que el next_value sea nil si el valor no cambia durante el periodo.
135
+ #
136
+ private def _play_timed_step_b(hash_mode, components, quantized_series, start_position, last_positions,
137
+ binder, control)
138
+
139
+ affected_components_by_time = {}
140
+
141
+ components.each do |component|
142
+ if v = quantized_series[component].peek_next_value
143
+ time = v[:time]
144
+
145
+ affected_components_by_time[time] ||= []
146
+ affected_components_by_time[time] << component
147
+ end
148
+ end
149
+
150
+ if !affected_components_by_time.empty?
151
+ time = affected_components_by_time.keys.sort.first
152
+
153
+ values = hash_mode ? {} : []
154
+ next_values = hash_mode ? {} : []
155
+ durations = hash_mode ? {} : []
156
+ q_durations = hash_mode ? {} : []
157
+ started_ago = hash_mode ? {} : []
158
+
159
+ affected_components_by_time[time].each do |component|
160
+ value = quantized_series[component].next_value
161
+
162
+ values[component] = value[:value]
163
+ durations[component] = value[:duration]
164
+
165
+ q_durations[component] =
166
+ _quantize_position(time + durations[component], warn: false) -
167
+ _quantize_position(time, warn: false)
168
+
169
+ last_positions[component] = _quantize_position(time, warn: false)
170
+ end
171
+
172
+ components.each do |component|
173
+ nv = quantized_series[component].peek_next_value
174
+ next_values[component] = nv[:value] if nv
175
+
176
+ if last_positions[component] && last_positions[component] != time
177
+ started_ago[component] = _quantize_position(time, warn: false) - last_positions[component]
178
+ end
179
+ end
180
+
181
+ _numeric_at start_position + _quantize_position(time, warn: false), control do
182
+ binder.call(values, next_values,
183
+ duration: durations,
184
+ quantized_duration: q_durations,
185
+ started_ago: started_ago,
186
+ control: control)
187
+
188
+ _play_timed_step_b(hash_mode, components, quantized_series, start_position, last_positions,
189
+ binder, control)
190
+ end
191
+ end
192
+ end
193
+
194
+ class PlayTimedControl < EventHandler
195
+ attr_reader :do_on_stop, :do_after
196
+
197
+ def initialize(parent, on_stop: nil, after_bars: nil, after: nil)
198
+ super parent
199
+ @do_on_stop = []
200
+ @do_after = []
201
+
202
+ @do_on_stop << on_stop if on_stop
203
+ self.after after_bars, &after if after
204
+ end
205
+
206
+ def on_stop(&block)
207
+ @do_on_stop << block
208
+ end
209
+
210
+ def after(bars = nil, &block)
211
+ bars ||= 0
212
+ @do_after << { bars: bars.rationalize, block: block }
213
+ end
214
+ end
215
+
216
+ private_constant :PlayTimedControl
217
+ end
218
+ end; end
219
+
@@ -0,0 +1,178 @@
1
+ require_relative 'base-sequencer-implementation-play-helper'
2
+
3
+ using Musa::Extension::Hashify
4
+ using Musa::Extension::Arrayfy
5
+
6
+ using Musa::Extension::InspectNice
7
+
8
+ module Musa; module Sequencer
9
+ class BaseSequencer
10
+ private def _play(serie,
11
+ control,
12
+ neumalang_context = nil,
13
+ mode: nil,
14
+ decoder: nil,
15
+ __play_eval: nil,
16
+ **mode_args,
17
+ &block)
18
+
19
+ block ||= proc {}
20
+
21
+ __play_eval ||= PlayEval.create \
22
+ mode,
23
+ SmartProcBinder.new(block,
24
+ on_rescue: proc { |e| _rescue_error(e) }),
25
+ decoder,
26
+ neumalang_context
27
+
28
+ element = nil
29
+
30
+ if control.stopped?
31
+ # nothing to do
32
+ elsif control.paused?
33
+ control.store_continuation sequencer: self,
34
+ serie: serie,
35
+ neumalang_context: neumalang_context,
36
+ mode: mode,
37
+ decoder: decoder,
38
+ play_eval: __play_eval,
39
+ mode_args: mode_args
40
+ else
41
+ element = serie.next_value
42
+ end
43
+
44
+ if element
45
+ operation = __play_eval.run_operation element
46
+
47
+ case operation[:current_operation]
48
+ when :none
49
+ when :block
50
+ __play_eval.block_procedure_binder.call operation[:current_parameter], control: control
51
+
52
+ when :event
53
+ control._launch operation[:current_event],
54
+ operation[:current_value_parameters],
55
+ operation[:current_key_parameters]
56
+
57
+ when :play
58
+ control2 = PlayControl.new control
59
+ control3 = PlayControl.new control2
60
+ control3.after { control3.launch :sync }
61
+
62
+ _play operation[:current_parameter].instance,
63
+ control3,
64
+ __play_eval: __play_eval.subcontext,
65
+ **mode_args
66
+
67
+ control2.on :sync do
68
+ _play serie, control, __play_eval: __play_eval, **mode_args
69
+ end
70
+
71
+ when :no_eval_play
72
+ control2 = PlayControl.new control
73
+ control3 = PlayControl.new control2
74
+ control3.after { control3.launch :sync }
75
+
76
+ _play operation[:current_parameter].instance,
77
+ control3,
78
+ __play_eval: WaitModePlayEval.new(__play_eval.block_procedure_binder),
79
+ **mode_args
80
+
81
+ control2.on :sync do
82
+ _play serie, control, __play_eval: __play_eval, **mode_args
83
+ end
84
+
85
+ when :parallel_play
86
+ control2 = PlayControl.new control
87
+
88
+ operation[:current_parameter].each do |current_parameter|
89
+ control3 = PlayControl.new control2
90
+ control3.after { control3.launch :sync }
91
+
92
+ _play current_parameter.instance,
93
+ control3,
94
+ __play_eval: __play_eval.subcontext,
95
+ **mode_args
96
+ end
97
+
98
+ counter = operation[:current_parameter].size
99
+
100
+ control2.on :sync do
101
+ counter -= 1
102
+ _play serie, control, __play_eval: __play_eval, **mode_args if counter == 0
103
+ end
104
+ end
105
+
106
+ case operation[:continue_operation]
107
+ when :now
108
+ _numeric_at position, control do
109
+ _play serie, control, __play_eval: __play_eval, **mode_args
110
+ end
111
+
112
+ when :at
113
+ _numeric_at operation[:continue_parameter], control do
114
+ _play serie, control, __play_eval: __play_eval, **mode_args
115
+ end
116
+
117
+ when :wait
118
+ _numeric_at position + operation[:continue_parameter].rationalize, control do
119
+ _play serie, control, __play_eval: __play_eval, **mode_args
120
+ end
121
+
122
+ when :on
123
+ control.on operation[:continue_parameter], only_once: true do
124
+ _play serie, control, __play_eval: __play_eval, **mode_args
125
+ end
126
+ end
127
+ else
128
+ control2 = EventHandler.new control
129
+
130
+ control.do_after.each do |do_after|
131
+ _numeric_at position + do_after[:bars], control2, &do_after[:block]
132
+ end
133
+ end
134
+
135
+ nil
136
+ end
137
+
138
+ class PlayControl < EventHandler
139
+ attr_reader :do_after
140
+
141
+ def initialize(parent, after_bars: nil, after: nil)
142
+ super parent
143
+
144
+ @do_after = []
145
+
146
+ after(after_bars, &after) if after
147
+ end
148
+
149
+ def pause
150
+ @paused = true
151
+ end
152
+
153
+ def store_continuation(sequencer:, serie:, neumalang_context:, mode:, decoder:, play_eval:, mode_args:)
154
+ @continuation_sequencer = sequencer
155
+ @continuation_parameters = {
156
+ serie: serie,
157
+ control: self,
158
+ neumalang_context: neumalang_context,
159
+ mode: mode,
160
+ decoder: decoder,
161
+ play_eval: play_eval,
162
+ mode_args: mode_args }
163
+ end
164
+
165
+ def continue
166
+ super
167
+ @continuation_sequencer.continuation_play(@continuation_parameters) if @continuation_sequencer
168
+ end
169
+
170
+ def after(bars = nil, &block)
171
+ bars ||= 0
172
+ @do_after << { bars: bars.rationalize, block: block }
173
+ end
174
+ end
175
+
176
+ private_constant :PlayControl
177
+ end
178
+ end; end
@@ -1,678 +1,231 @@
1
1
  require_relative '../core-ext/arrayfy'
2
2
  require_relative '../core-ext/smart-proc-binder'
3
3
 
4
- require_relative 'base-sequencer-implementation-control'
5
- require_relative 'base-sequencer-implementation-play-helper'
6
-
7
4
  using Musa::Extension::Arrayfy
8
5
  using Musa::Extension::DeepCopy
9
6
 
10
- module Musa
11
- module Sequencer
12
- class BaseSequencer
13
- include Musa::Extension::SmartProcBinder
14
- include Musa::Extension::DeepCopy
15
-
16
- private
17
-
18
- def _tick(position_to_run)
19
-
20
- @before_tick.each { |block| block.call position_to_run }
7
+ module Musa; module Sequencer
8
+ class BaseSequencer
9
+ include Musa::Extension::SmartProcBinder
10
+ include Musa::Extension::DeepCopy
21
11
 
22
- queue = @timeslots[position_to_run]
12
+ private def _tick(position_to_run)
13
+ @before_tick.each { |block| block.call position_to_run }
14
+ queue = @timeslots[position_to_run]
23
15
 
24
- if queue
25
- until queue.empty?
16
+ if queue
17
+ until queue.empty?
18
+ command = queue.shift
19
+ @timeslots.delete position_to_run if queue.empty?
26
20
 
27
- command = queue.shift
28
- @timeslots.delete position_to_run if queue.empty?
21
+ if command.key?(:parent_control) && !command[:parent_control].stopped?
22
+ @event_handlers.push command[:parent_control]
29
23
 
30
- if command.key?(:parent_control) && !command[:parent_control].stopped?
31
- @event_handlers.push command[:parent_control]
32
-
33
- @tick_mutex.synchronize do
34
- command[:block].call *command[:value_parameters], **command[:key_parameters] if command[:block]
35
- end
36
-
37
- @event_handlers.pop
38
- else
39
- @tick_mutex.synchronize do
40
- command[:block].call *command[:value_parameters], **command[:key_parameters] if command[:block]
41
- end
24
+ @tick_mutex.synchronize do
25
+ command[:block].call *command[:value_parameters], **command[:key_parameters] if command[:block]
42
26
  end
43
- end
44
- end
45
-
46
- Thread.pass
47
- end
48
-
49
- def _raw_numeric_at(at_position, force_first: nil, &block)
50
- force_first ||= false
51
-
52
- if at_position == @position
53
- begin
54
- yield
55
- rescue StandardError, ScriptError => e
56
- _rescue_error e
57
- end
58
-
59
- elsif at_position > @position
60
- @timeslots[at_position] ||= []
61
27
 
62
- value = { block: block, value_parameters: [], key_parameters: {} }
63
- if force_first
64
- @timeslots[at_position].insert 0, value
28
+ @event_handlers.pop
65
29
  else
66
- @timeslots[at_position] << value
67
- end
68
- else
69
- @logger.warn('BaseSequencer') { "._raw_numeric_at: ignoring past at command for #{at_position}" }
70
- end
71
-
72
- nil
73
- end
74
-
75
- def _numeric_at(at_position, control, with: nil, debug: nil, &block)
76
- raise ArgumentError, "'at_position' parameter cannot be nil" if at_position.nil?
77
- raise ArgumentError, 'Yield block is mandatory' unless block
78
-
79
- at_position = _check_position(at_position)
80
-
81
- value_parameters = []
82
- value_parameters << with if !with.nil? && !with.is_a?(Hash)
83
-
84
- block_key_parameters_binder =
85
- SmartProcBinder.new block, on_rescue: proc { |e| _rescue_error(e) }
86
-
87
- key_parameters = {}
88
- key_parameters.merge! block_key_parameters_binder._apply(nil, with).last if with.is_a?(Hash)
89
-
90
- key_parameters[:control] = control if block_key_parameters_binder.key?(:control)
91
-
92
- if at_position == @position
93
- @on_debug_at.each { |c| c.call } if @logger.sev_threshold >= ::Logger::Severity::DEBUG
94
-
95
- begin
96
- locked = @tick_mutex.try_lock
97
- block_key_parameters_binder._call(value_parameters, key_parameters)
98
- ensure
99
- @tick_mutex.unlock if locked
100
- end
101
-
102
- elsif @position.nil? || at_position > @position
103
-
104
- @timeslots[at_position] ||= []
105
-
106
- if @logger.sev_threshold <= ::Logger::Severity::DEBUG
107
- @on_debug_at.each do |block|
108
- @timeslots[at_position] << { parent_control: control, block: block }
30
+ @tick_mutex.synchronize do
31
+ command[:block].call *command[:value_parameters], **command[:key_parameters] if command[:block]
109
32
  end
110
33
  end
111
-
112
- @timeslots[at_position] << { parent_control: control, block: block_key_parameters_binder,
113
- value_parameters: value_parameters,
114
- key_parameters: key_parameters }
115
- else
116
- @logger.warn('BaseSequencer') { "._numeric_at: ignoring past 'at' command for #{at_position}" }
117
34
  end
118
-
119
- nil
120
35
  end
121
36
 
122
- def _serie_at(bar_position_serie, control, with: nil, debug: nil, &block)
123
-
124
- bar_position = bar_position_serie.next_value
125
-
126
- with_value = if with.respond_to? :next_value
127
- with.next_value
128
- else
129
- with
130
- end
37
+ Thread.pass
38
+ end
131
39
 
132
- if bar_position
133
- _numeric_at bar_position, control, with: with_value, debug: debug, &block
40
+ private def _raw_numeric_at(at_position, force_first: nil, &block)
41
+ force_first ||= false
134
42
 
135
- _numeric_at bar_position, control, debug: false do
136
- _serie_at bar_position_serie, control, with: with, debug: debug, &block
137
- end
138
- else
139
- # serie finalizada
43
+ if at_position == @position
44
+ begin
45
+ yield
46
+ rescue StandardError, ScriptError => e
47
+ _rescue_error e
140
48
  end
141
49
 
142
- nil
143
- end
50
+ elsif at_position > @position
51
+ @timeslots[at_position] ||= []
144
52
 
145
- def _play(serie, control, nl_context = nil, mode: nil, decoder: nil, __play_eval: nil, **mode_args, &block)
146
-
147
- block ||= proc {}
148
-
149
- __play_eval ||= PlayEval.create \
150
- mode,
151
- SmartProcBinder.new(block,
152
- on_rescue: proc { |e| _rescue_error(e) }),
153
- decoder,
154
- nl_context
155
-
156
- element = nil
157
-
158
- if control.stopped?
159
- # nothing to do
160
- elsif control.paused?
161
- control.store_continuation sequencer: self,
162
- serie: serie,
163
- nl_context: nl_context,
164
- mode: mode,
165
- decoder: decoder,
166
- play_eval: __play_eval,
167
- mode_args: mode_args
53
+ value = { block: block, value_parameters: [], key_parameters: {} }
54
+ if force_first
55
+ @timeslots[at_position].insert 0, value
168
56
  else
169
- element = serie.next_value
57
+ @timeslots[at_position] << value
170
58
  end
171
-
172
- if element
173
- operation = __play_eval.run_operation element
174
-
175
- case operation[:current_operation]
176
-
177
- when :none
178
-
179
- when :block
180
-
181
- __play_eval.block_procedure_binder.call operation[:current_parameter], control: control
182
-
183
- when :event
184
-
185
- control._launch operation[:current_event],
186
- operation[:current_value_parameters],
187
- operation[:current_key_parameters]
188
-
189
- when :play
190
-
191
- control2 = PlayControl.new control
192
- control3 = PlayControl.new control2
193
- control3.after { control3.launch :sync }
194
-
195
- _play operation[:current_parameter].instance,
196
- control3,
197
- __play_eval: __play_eval.subcontext,
198
- **mode_args
199
-
200
- control2.on :sync do
201
- _play serie, control, __play_eval: __play_eval, **mode_args
202
- end
203
-
204
- when :no_eval_play
205
-
206
- control2 = PlayControl.new control
207
- control3 = PlayControl.new control2
208
- control3.after { control3.launch :sync }
209
-
210
- _play operation[:current_parameter].instance,
211
- control3,
212
- __play_eval: WaitModePlayEval.new(__play_eval.block_procedure_binder),
213
- **mode_args
214
-
215
- control2.on :sync do
216
- _play serie, control, __play_eval: __play_eval, **mode_args
217
- end
218
-
219
- when :parallel_play
220
-
221
- control2 = PlayControl.new control
222
-
223
- operation[:current_parameter].each do |current_parameter|
224
- control3 = PlayControl.new control2
225
- control3.after { control3.launch :sync }
226
-
227
- _play current_parameter.instance,
228
- control3,
229
- __play_eval: __play_eval.subcontext,
230
- **mode_args
231
- end
232
-
233
- counter = operation[:current_parameter].size
234
-
235
- control2.on :sync do
236
- counter -= 1
237
- _play serie, control, __play_eval: __play_eval, **mode_args if counter == 0
238
- end
239
- end
240
-
241
- case operation[:continue_operation]
242
- when :now
243
- _numeric_at position, control do
244
- _play serie, control, __play_eval: __play_eval, **mode_args
245
- end
246
-
247
- when :at
248
- _numeric_at operation[:continue_parameter], control do
249
- _play serie, control, __play_eval: __play_eval, **mode_args
250
- end
251
-
252
- when :wait
253
- _numeric_at position + operation[:continue_parameter].rationalize, control do
254
- _play serie, control, __play_eval: __play_eval, **mode_args
255
- end
256
-
257
- when :on
258
- control.on operation[:continue_parameter], only_once: true do
259
- _play serie, control, __play_eval: __play_eval, **mode_args
260
- end
261
- end
262
- else
263
- control2 = EventHandler.new control
264
-
265
- control.do_after.each do |do_after|
266
- _numeric_at position, control2, &do_after
267
- end
268
- end
269
-
270
- nil
59
+ else
60
+ @logger.warn('BaseSequencer') { "._raw_numeric_at: ignoring past at command for #{at_position}" }
271
61
  end
272
62
 
273
- def _every(interval, control, block_procedure_binder: nil, &block)
274
- block ||= proc {}
275
-
276
- block_procedure_binder ||= SmartProcBinder.new block, on_rescue: proc { |e| _rescue_error(e) }
277
-
278
- _numeric_at position, control do
279
- control._start_position ||= position
280
- control._execution_counter ||= 0
63
+ nil
64
+ end
281
65
 
282
- duration_exceeded =
283
- (control._start_position + control.duration_value - interval) <= position if interval && control.duration_value
66
+ private def _numeric_at(at_position, control, with: nil, debug: nil, &block)
67
+ raise ArgumentError, "'at_position' parameter cannot be nil" if at_position.nil?
68
+ raise ArgumentError, 'Yield block is mandatory' unless block
284
69
 
285
- till_exceeded = control.till_value - interval <= position if interval && control.till_value
70
+ at_position = _quantize_position(at_position)
286
71
 
287
- condition_failed = !control.condition_block.call if control.condition_block
72
+ value_parameters = []
73
+ value_parameters << with if !with.nil? && !with.is_a?(Hash)
288
74
 
289
- unless control.stopped? || condition_failed || till_exceeded
290
- block_procedure_binder.call(control: control)
291
- control._execution_counter += 1
292
- end
75
+ block_key_parameters_binder =
76
+ SmartProcBinder.new block, on_rescue: proc { |e| _rescue_error(e) }
293
77
 
78
+ key_parameters = {}
79
+ key_parameters.merge! block_key_parameters_binder._apply(nil, with).last if with.is_a?(Hash)
294
80
 
295
- unless control.stopped? || duration_exceeded || till_exceeded || condition_failed || interval.nil?
296
- _numeric_at control._start_position + control._execution_counter * interval, control do
297
- _every interval, control, block_procedure_binder: block_procedure_binder
298
- end
81
+ key_parameters[:control] = control if block_key_parameters_binder.key?(:control)
299
82
 
300
- else
301
- control.do_on_stop.each(&:call)
83
+ if at_position == @position
84
+ @on_debug_at.each { |c| c.call } if @logger.sev_threshold >= ::Logger::Severity::DEBUG
302
85
 
303
- control.do_after.each do |do_after|
304
- _numeric_at position + (interval || 0) + do_after[:bars], control, &do_after[:block]
305
- end
306
- end
86
+ begin
87
+ locked = @tick_mutex.try_lock
88
+ block_key_parameters_binder._call(value_parameters, key_parameters)
89
+ ensure
90
+ @tick_mutex.unlock if locked
307
91
  end
308
92
 
309
- nil
310
- end
311
-
312
- def _move(every: nil,
313
- from:, to: nil, step: nil,
314
- duration: nil, till: nil,
315
- function: nil,
316
- right_open: nil,
317
- on_stop: nil,
318
- after_bars: nil, after: nil,
319
- &block)
320
-
321
- raise ArgumentError,
322
- "Cannot use duration: #{duration} and till: #{till} parameters at the same time. " \
323
- "Use only one of them." if till && duration
324
-
325
- raise ArgumentError,
326
- "Invalid use: 'function:' parameter is incompatible with 'step:' parameter" if function && step
327
- raise ArgumentError,
328
- "Invalid use: 'function:' parameter needs 'to:' parameter to be not nil" if function && !to
329
-
330
- array_mode = from.is_a?(Array)
331
- hash_mode = from.is_a?(Hash)
332
-
333
- if array_mode
334
- from = from.arrayfy
335
- size = from.size
336
-
337
- elsif hash_mode
338
- hash_keys = from.keys
339
- from = from.values
340
- size = from.size
341
-
342
- if every.is_a?(Hash)
343
- every = hash_keys.collect { |k| every[k] }
344
- raise ArgumentError,
345
- "Invalid use: 'every:' parameter should contain the same keys as 'from:' Hash" \
346
- unless every.all? { |_| _ }
347
- end
348
-
349
- if to.is_a?(Hash)
350
- to = hash_keys.collect { |k| to[k] }
351
- raise ArgumentError,
352
- "Invalid use: 'to:' parameter should contain the same keys as 'from:' Hash" unless to.all? { |_| _ }
353
- end
93
+ elsif @position.nil? || at_position > @position
354
94
 
355
- if step.is_a?(Hash)
356
- step = hash_keys.collect { |k| step[k] }
357
- end
95
+ @timeslots[at_position] ||= []
358
96
 
359
- if right_open.is_a?(Hash)
360
- right_open = hash_keys.collect { |k| right_open[k] }
97
+ if @logger.sev_threshold <= ::Logger::Severity::DEBUG
98
+ @on_debug_at.each do |block|
99
+ @timeslots[at_position] << { parent_control: control, block: block }
361
100
  end
362
-
363
- else
364
- from = from.arrayfy
365
- size = from.size
366
101
  end
367
102
 
368
- every = every.arrayfy(size: size)
369
- to = to.arrayfy(size: size)
370
- step = step.arrayfy(size: size)
371
- right_open = right_open.arrayfy(size: size)
372
-
373
- # from, to, step, every
374
- # from, to, step, (duration | till)
375
- # from, to, every, (duration | till)
376
- # from, step, every, (duration | till)
377
-
378
- block ||= proc {}
379
-
380
- step.map!.with_index do |s, i|
381
- (s && to[i] && ((s > 0 && to[i] < from[i]) || (s < 0 && from[i] < to[i]))) ? -s : s
382
- end
383
-
384
- right_open.map! { |v| v || false }
385
-
386
- function ||= proc { |ratio| ratio }
387
- function = function.arrayfy(size: size)
388
-
389
- function_range = 1r.arrayfy(size: size)
390
- function_offset = 0r.arrayfy(size: size)
391
-
392
- start_position = position
393
-
394
- if duration || till
395
- effective_duration = duration || till - start_position
396
-
397
- # Add 1 tick to arrive to final value in duration time (no need to add an extra tick)
398
- right_open_offset = right_open.collect { |ro| ro ? 0 : 1 }
399
-
400
- size.times do |i|
401
- if to[i] && step[i] && !every[i]
402
-
403
- steps = (to[i] - from[i]) / step[i]
404
-
405
- # When to == from don't need to do any iteration with every
406
- if steps + right_open_offset[i] > 0
407
- every[i] = Rational(effective_duration, steps + right_open_offset[i])
408
- else
409
- every[i] = nil
410
- end
411
-
412
- elsif to[i] && !step[i] && !every[i]
413
-
414
- if tick_duration > 0
415
- function_range[i] = to[i] - from[i]
416
- function_offset[i] = from[i]
417
-
418
- from[i] = 0r
419
- to[i] = 1r
420
-
421
- step[i] = 1r / (effective_duration * ticks_per_bar - right_open_offset[i])
422
- every[i] = tick_duration
423
- else
424
- raise ArgumentError, "Cannot use sequencer tickless mode without 'step' or 'every' parameter values"
425
- end
426
-
427
- elsif to[i] && !step[i] && every[i]
428
- function_range[i] = to[i] - from[i]
429
- function_offset[i] = from[i]
430
-
431
- from[i] = 0r
432
- to[i] = 1r
433
-
434
- steps = effective_duration / every[i]
435
- step[i] = 1r / (steps - right_open_offset[i])
436
-
437
- elsif !to[i] && step[i] && every[i]
438
- # ok
439
- elsif !to[i] && !step[i] && every[i]
440
- step[i] = 1r
103
+ @timeslots[at_position] << { parent_control: control, block: block_key_parameters_binder,
104
+ value_parameters: value_parameters,
105
+ key_parameters: key_parameters }
106
+ else
107
+ @logger.warn('BaseSequencer') { "._numeric_at: ignoring past 'at' command for #{at_position}" }
108
+ end
441
109
 
442
- else
443
- raise ArgumentError, 'Cannot use this parameters combination (with \'duration\' or \'till\')'
444
- end
445
- end
446
- else
447
- size.times do |i|
448
- if to[i] && step[i] && every[i]
449
- # ok
450
- elsif to[i] && !step[i] && every[i]
451
- size.times do |i|
452
- step[i] = (to[i] <=> from[i]).to_r
453
- end
454
- else
455
- raise ArgumentError, 'Cannot use this parameters combination'
456
- end
457
- end
458
- end
110
+ nil
111
+ end
459
112
 
460
- binder = SmartProcBinder.new(block)
113
+ private def _serie_at(bar_position_serie, control, with: nil, debug: nil, &block)
114
+ bar_position = bar_position_serie.next_value
461
115
 
462
- every_groups = {}
463
- group_counter = {}
116
+ with_value = if with.respond_to? :next_value
117
+ with.next_value
118
+ else
119
+ with
120
+ end
464
121
 
465
- positions = Array.new(size)
466
- q_durations = Array.new(size)
467
- position_jitters = Array.new(size)
468
- duration_jitters = Array.new(size)
122
+ if bar_position
123
+ _numeric_at bar_position, control, with: with_value, debug: debug, &block
469
124
 
470
- size.times.each do |i|
471
- every_groups[every[i]] ||= []
472
- every_groups[every[i]] << i
473
- group_counter[every[i]] = 0
125
+ _numeric_at bar_position, control, debug: false do
126
+ _serie_at bar_position_serie, control, with: with, debug: debug, &block
474
127
  end
128
+ else
129
+ # serie finalizada
130
+ end
475
131
 
476
- control = MoveControl.new(@event_handlers.last,
477
- duration: duration, till: till,
478
- on_stop: on_stop, after_bars: after_bars, after: after)
132
+ nil
133
+ end
479
134
 
480
- control.on_stop do
481
- control.do_after.each do |do_after|
482
- _numeric_at position + do_after[:bars], control, &do_after[:block]
483
- end
484
- end
135
+ def _rescue_error(e)
136
+ @logger.error('BaseSequencer') { e.to_s }
137
+ @logger.error('BaseSequencer') { e.full_message(highlight: true, order: :top) }
485
138
 
486
- @event_handlers.push control
139
+ @on_error.each do |block|
140
+ block.call e
141
+ end
142
+ end
487
143
 
488
- _numeric_at start_position, control do
489
- next_values = from.dup
144
+ class EventHandler
145
+ include Musa::Extension::SmartProcBinder
490
146
 
491
- values = Array.new(size)
492
- stop = Array.new(size, false)
493
- last_position = Array.new(size)
147
+ attr_accessor :continue_parameters
494
148
 
495
- _every _common_interval(every_groups.keys), control.every_control do
496
- process_indexes = []
149
+ @@counter = 0
497
150
 
498
- every_groups.each_pair do |group_interval, affected_indexes|
499
- group_position = start_position + ((group_interval || 0) * group_counter[group_interval])
151
+ def initialize(parent = nil)
152
+ @id = (@@counter += 1)
500
153
 
501
- # We consider a position to be on current tick position when it is inside the interval of one tick
502
- # centered on the current tick (current tick +- 1/2 tick duration).
503
- # This allow to round the irregularly timed positions due to every intervals not integer
504
- # multiples of the tick_duration.
505
- #
506
- if tick_duration == 0 && group_position == position ||
507
- group_position >= position - tick_duration && group_position < position + tick_duration
154
+ @parent = parent
155
+ @handlers = {}
508
156
 
509
- process_indexes << affected_indexes
157
+ @stop = false
158
+ end
510
159
 
511
- group_counter[group_interval] += 1
160
+ def stop
161
+ @stop = true
162
+ end
512
163
 
513
- next_group_position = start_position +
514
- if group_interval
515
- (group_interval * group_counter[group_interval])
516
- else
517
- effective_duration
518
- end
164
+ def stopped?
165
+ @stop
166
+ end
519
167
 
520
- next_group_q_position = _quantize(next_group_position)
168
+ def pause
169
+ raise NotImplementedError
170
+ end
521
171
 
522
- affected_indexes.each do |i|
523
- positions[i] = group_position
524
- q_durations[i] = next_group_q_position - position
172
+ def continue
173
+ @paused = false
174
+ end
525
175
 
526
- position_jitters[i] = group_position - position
527
- duration_jitters[i] = next_group_position - next_group_q_position
528
- end
529
- end
530
- end
176
+ def paused?
177
+ @paused
178
+ end
531
179
 
532
- process_indexes.flatten!
533
-
534
- if process_indexes.any?
535
-
536
- process_indexes.each do |i|
537
- unless stop[i]
538
- values[i] = next_values[i]
539
- next_values[i] += step[i]
540
-
541
- if to[i]
542
- stop[i] = if right_open[i]
543
- step[i].positive? ? next_values[i] >= to[i] : next_values[i] <= to[i]
544
- else
545
- step[i].positive? ? next_values[i] > to[i] : next_values[i] < to[i]
546
- end
547
-
548
- if stop[i]
549
- if right_open[i]
550
- next_values[i] = nil if values[i] == to[i]
551
- else
552
- next_values[i] = nil
553
- end
554
- end
555
- end
556
- end
557
- end
558
-
559
- control.stop if stop.all?
560
-
561
- effective_values = from.clone(freeze: false).map!.with_index do |_, i|
562
- function[i].call(values[i]) * function_range[i] + function_offset[i] unless values[i].nil?
563
- end
564
-
565
- effective_next_values = from.clone(freeze: false).map!.with_index do |_, i|
566
- function[i].call(next_values[i]) * function_range[i] +
567
- function_offset[i] unless next_values[i].nil?
568
- end
569
-
570
- # TODO add to values and next_values the modules of the original from and/or to objects.
571
-
572
- value_parameters, key_parameters =
573
- if array_mode
574
- binder.apply(effective_values, effective_next_values,
575
- control: control,
576
- duration: _durations(every_groups, effective_duration),
577
- quantized_duration: q_durations.dup,
578
- started_ago: _started_ago(last_position, position, process_indexes),
579
- position_jitter: position_jitters.dup,
580
- duration_jitter: duration_jitters.dup,
581
- right_open: right_open.dup)
582
- elsif hash_mode
583
- binder.apply(_hash_from_keys_and_values(hash_keys, effective_values),
584
- _hash_from_keys_and_values(hash_keys, effective_next_values),
585
- control: control,
586
- duration: _hash_from_keys_and_values(
587
- hash_keys,
588
- _durations(every_groups, effective_duration)),
589
- quantized_duration: _hash_from_keys_and_values(
590
- hash_keys,
591
- q_durations),
592
- started_ago: _hash_from_keys_and_values(
593
- hash_keys,
594
- _started_ago(last_position, position, process_indexes)),
595
- position_jitter: _hash_from_keys_and_values(
596
- hash_keys,
597
- position_jitters),
598
- duration_jitter: _hash_from_keys_and_values(
599
- hash_keys,
600
- duration_jitters),
601
- right_open: _hash_from_keys_and_values(hash_keys, right_open))
602
- else
603
- binder.apply(effective_values.first,
604
- effective_next_values.first,
605
- control: control,
606
- duration: _durations(every_groups, effective_duration).first,
607
- quantized_duration: q_durations.first,
608
- position_jitter: position_jitters.first,
609
- duration_jitter: duration_jitters.first,
610
- started_ago: nil,
611
- right_open: right_open.first)
612
- end
613
-
614
- yield *value_parameters, **key_parameters
615
-
616
- process_indexes.each { |i| last_position[i] = position }
617
- end
618
- end
619
- end
180
+ def on(event, name: nil, only_once: nil, &block)
181
+ only_once ||= false
620
182
 
621
- @event_handlers.pop
183
+ @handlers[event] ||= {}
622
184
 
623
- control
185
+ # TODO: add on_rescue: proc { |e| _rescue_block_error(e) } [this method is on Sequencer, not in EventHandler]
186
+ @handlers[event][name] = { block: SmartProcBinder.new(block), only_once: only_once }
624
187
  end
625
188
 
626
- def _started_ago(last_positions, position, affected_indexes)
627
- Array.new(last_positions.size).tap do |a|
628
- last_positions.each_index do |i|
629
- if last_positions[i] && !affected_indexes.include?(i)
630
- a[i] = position - last_positions[i]
631
- end
632
- end
633
- end
189
+ def launch(event, *value_parameters, **key_parameters)
190
+ _launch event, value_parameters, key_parameters
634
191
  end
635
192
 
636
- def _durations(every_groups, largest_duration)
637
- [].tap do |a|
638
- if every_groups.any?
639
- every_groups.each_pair do |every_group, affected_indexes|
640
- affected_indexes.each do |i|
641
- a[i] = every_group || largest_duration
642
- end
643
- end
644
- else
645
- a << largest_duration
193
+ def _launch(event, value_parameters = nil, key_parameters = nil)
194
+ value_parameters ||= []
195
+ key_parameters ||= {}
196
+ processed = false
197
+
198
+ if @handlers.key? event
199
+ @handlers[event].each do |name, handler|
200
+ handler[:block].call *value_parameters, **key_parameters
201
+ @handlers[event].delete name if handler[:only_once]
202
+ processed = true
646
203
  end
647
204
  end
648
- end
649
205
 
650
- def _hash_from_keys_and_values(keys, values)
651
- {}.tap { |h| keys.each_index { |i| h[keys[i]] = values[i] } }
206
+ @parent._launch event, value_parameters, key_parameters if @parent && !processed
652
207
  end
653
208
 
654
- def _common_interval(intervals)
655
- intervals = intervals.compact
656
- return nil if intervals.empty?
657
-
658
- lcm_denominators = intervals.collect(&:denominator).reduce(1, :lcm)
659
- numerators = intervals.collect { |i| i.numerator * lcm_denominators / i.denominator }
660
- gcd_numerators = numerators.reduce(numerators.first, :gcd)
661
-
662
- #intervals.reduce(1r, :*)
663
-
664
- Rational(gcd_numerators, lcm_denominators)
209
+ def inspect
210
+ "EventHandler #{id}"
665
211
  end
666
212
 
667
- def _rescue_error(e)
668
- @logger.error('BaseSequencer') { e.to_s }
669
- @logger.error('BaseSequencer') { e.full_message(highlight: true, order: :top) }
670
-
671
- @on_error.each do |block|
672
- block.call e
213
+ def id
214
+ if @parent
215
+ "#{@parent.id}.#{self.class.name.split('::').last}-#{@id}"
216
+ else
217
+ "#{self.class.name.split('::').last}-#{@id.to_s}"
673
218
  end
674
219
  end
220
+
221
+ alias to_s inspect
675
222
  end
223
+
224
+ private_constant :EventHandler
676
225
  end
677
- end
226
+ end; end
678
227
 
228
+ require_relative 'base-sequencer-implementation-every'
229
+ require_relative 'base-sequencer-implementation-move'
230
+ require_relative 'base-sequencer-implementation-play'
231
+ require_relative 'base-sequencer-implementation-play-timed'