musa-dsl 0.21.4 → 0.22.3

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.
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 +28 -37
  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 +4 -3
  13. data/lib/musa-dsl/matrix/matrix.rb +0 -57
  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 +210 -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} +59 -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,210 @@
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
+ right_open: nil,
13
+ &block)
14
+
15
+ reference ||= 0r
16
+ step ||= 1r
17
+
18
+ if first_value_sample = timed_serie.peek_next_value
19
+
20
+ debug "_play_timed: first_value_sample #{first_value_sample}"
21
+
22
+ hash_mode = first_value_sample[:value].is_a?(Hash)
23
+
24
+ if hash_mode
25
+ components = first_value_sample[:value].keys
26
+
27
+ reference = reference.hashify(keys: components)
28
+ step = step.hashify(keys: components)
29
+ right_open = right_open.hashify(keys:components)
30
+ else
31
+ size = first_value_sample[:value].size
32
+ components = (0 .. size-1).to_a
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
49
+ end
50
+
51
+ last_positions = hash_mode ? {} : []
52
+ end
53
+
54
+ binder = SmartProcBinder.new(block)
55
+
56
+ _play_timed_step(hash_mode, components, quantized_series, position, last_positions, binder, control)
57
+ end
58
+
59
+
60
+ private def _play_timed_step(hash_mode, components, quantized_series, start_position, last_positions,
61
+ binder, control)
62
+
63
+ affected_components_by_time = {}
64
+
65
+ components.each do |component|
66
+ if v = quantized_series[component].peek_next_value
67
+
68
+ debug "_play_timed_step: quantized_series[#{component}].peek_next_value #{v}"
69
+ time = v[:time]
70
+
71
+ affected_components_by_time[time] ||= []
72
+ affected_components_by_time[time] << component
73
+ end
74
+ end
75
+
76
+ if affected_components_by_time.any?
77
+ time = affected_components_by_time.keys.sort.first
78
+
79
+ values = hash_mode ? {} : []
80
+ next_values = hash_mode ? {} : []
81
+ durations = hash_mode ? {} : []
82
+ q_durations = hash_mode ? {} : []
83
+ started_ago = hash_mode ? {} : []
84
+
85
+ affected_components_by_time[time].each do |component|
86
+ value = quantized_series[component].next_value
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)
94
+
95
+ nv = quantized_series[component].peek_next_value
96
+ next_values[component] = (nv && nv[:value] != values[component]) ? nv[:value] : nil
97
+
98
+ last_positions[component] = _quantize_position(time, warn: false)
99
+ end
100
+
101
+ components.each do |component|
102
+ if last_positions[component] && last_positions[component] != time
103
+ sa = _quantize_position(time, warn: false) - last_positions[component]
104
+ started_ago[component] = (sa == 0) ? nil : sa
105
+ end
106
+ end
107
+
108
+ _numeric_at start_position + _quantize_position(time, warn: false), control do
109
+ debug "_play_timed_step: before binder.call: durations #{durations} q_durations #{q_durations}"
110
+ binder.call(values, next_values,
111
+ duration: durations,
112
+ quantized_duration: q_durations,
113
+ started_ago: started_ago,
114
+ control: control)
115
+
116
+ _play_timed_step(hash_mode, components, quantized_series, start_position, last_positions,
117
+ binder, control)
118
+ end
119
+ end
120
+ end
121
+
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
+ class PlayTimedControl < EventHandler
186
+ attr_reader :do_on_stop, :do_after
187
+
188
+ def initialize(parent, on_stop: nil, after_bars: nil, after: nil)
189
+ super parent
190
+ @do_on_stop = []
191
+ @do_after = []
192
+
193
+ @do_on_stop << on_stop if on_stop
194
+ self.after after_bars, &after if after
195
+ end
196
+
197
+ def on_stop(&block)
198
+ @do_on_stop << block
199
+ end
200
+
201
+ def after(bars = nil, &block)
202
+ bars ||= 0
203
+ @do_after << { bars: bars.rationalize, block: block }
204
+ end
205
+ end
206
+
207
+ private_constant :PlayTimedControl
208
+ end
209
+ end; end
210
+
@@ -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: warning: 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: warning: 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'