musa-dsl 0.21.3 → 0.22.2

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