musa-dsl 0.21.3 → 0.22.2

Sign up to get free protection for your applications and to get access to all the features.
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'