musa-dsl 0.14.32 → 0.21.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/Gemfile +0 -1
  4. data/README.md +5 -1
  5. data/lib/musa-dsl.rb +54 -11
  6. data/lib/musa-dsl/core-ext.rb +7 -13
  7. data/lib/musa-dsl/core-ext/array-explode-ranges.rb +15 -23
  8. data/lib/musa-dsl/core-ext/arrayfy.rb +30 -12
  9. data/lib/musa-dsl/core-ext/attribute-builder.rb +194 -0
  10. data/lib/musa-dsl/core-ext/deep-copy.rb +185 -0
  11. data/lib/musa-dsl/core-ext/dynamic-proxy.rb +44 -40
  12. data/lib/musa-dsl/core-ext/inspect-nice.rb +40 -22
  13. data/lib/musa-dsl/core-ext/smart-proc-binder.rb +108 -0
  14. data/lib/musa-dsl/core-ext/with.rb +26 -0
  15. data/lib/musa-dsl/datasets.rb +8 -3
  16. data/lib/musa-dsl/datasets/dataset.rb +3 -0
  17. data/lib/musa-dsl/datasets/delta-d.rb +12 -0
  18. data/lib/musa-dsl/datasets/e.rb +61 -0
  19. data/lib/musa-dsl/datasets/gdv.rb +51 -411
  20. data/lib/musa-dsl/datasets/gdvd.rb +179 -0
  21. data/lib/musa-dsl/datasets/helper.rb +41 -0
  22. data/lib/musa-dsl/datasets/p.rb +68 -0
  23. data/lib/musa-dsl/datasets/packed-v.rb +19 -0
  24. data/lib/musa-dsl/datasets/pdv.rb +22 -15
  25. data/lib/musa-dsl/datasets/ps.rb +113 -0
  26. data/lib/musa-dsl/datasets/score.rb +210 -0
  27. data/lib/musa-dsl/datasets/score/queriable.rb +48 -0
  28. data/lib/musa-dsl/datasets/score/render.rb +31 -0
  29. data/lib/musa-dsl/datasets/score/to-mxml/process-pdv.rb +160 -0
  30. data/lib/musa-dsl/datasets/score/to-mxml/process-ps.rb +51 -0
  31. data/lib/musa-dsl/datasets/score/to-mxml/process-time.rb +153 -0
  32. data/lib/musa-dsl/datasets/score/to-mxml/to-mxml.rb +158 -0
  33. data/lib/musa-dsl/datasets/v.rb +23 -0
  34. data/lib/musa-dsl/generative.rb +5 -5
  35. data/lib/musa-dsl/generative/backboner.rb +274 -0
  36. data/lib/musa-dsl/generative/darwin.rb +102 -96
  37. data/lib/musa-dsl/generative/generative-grammar.rb +182 -187
  38. data/lib/musa-dsl/generative/markov.rb +56 -53
  39. data/lib/musa-dsl/generative/variatio.rb +234 -222
  40. data/lib/musa-dsl/logger.rb +1 -0
  41. data/lib/musa-dsl/logger/logger.rb +32 -0
  42. data/lib/musa-dsl/matrix.rb +1 -0
  43. data/lib/musa-dsl/matrix/matrix.rb +210 -0
  44. data/lib/musa-dsl/midi.rb +2 -2
  45. data/lib/musa-dsl/midi/midi-recorder.rb +54 -52
  46. data/lib/musa-dsl/midi/midi-voices.rb +187 -182
  47. data/lib/musa-dsl/music.rb +5 -5
  48. data/lib/musa-dsl/music/chord-definition.rb +54 -50
  49. data/lib/musa-dsl/music/chord-definitions.rb +13 -9
  50. data/lib/musa-dsl/music/chords.rb +236 -238
  51. data/lib/musa-dsl/music/equally-tempered-12-tone-scale-system.rb +187 -183
  52. data/lib/musa-dsl/music/scales.rb +331 -332
  53. data/lib/musa-dsl/musicxml.rb +1 -0
  54. data/lib/musa-dsl/musicxml/builder/attributes.rb +155 -0
  55. data/lib/musa-dsl/musicxml/builder/backup-forward.rb +45 -0
  56. data/lib/musa-dsl/musicxml/builder/direction.rb +322 -0
  57. data/lib/musa-dsl/musicxml/builder/helper.rb +90 -0
  58. data/lib/musa-dsl/musicxml/builder/measure.rb +137 -0
  59. data/lib/musa-dsl/musicxml/builder/note-complexities.rb +152 -0
  60. data/lib/musa-dsl/musicxml/builder/note.rb +577 -0
  61. data/lib/musa-dsl/musicxml/builder/part-group.rb +44 -0
  62. data/lib/musa-dsl/musicxml/builder/part.rb +67 -0
  63. data/lib/musa-dsl/musicxml/builder/pitched-note.rb +126 -0
  64. data/lib/musa-dsl/musicxml/builder/rest.rb +117 -0
  65. data/lib/musa-dsl/musicxml/builder/score-partwise.rb +120 -0
  66. data/lib/musa-dsl/musicxml/builder/typed-text.rb +43 -0
  67. data/lib/musa-dsl/musicxml/builder/unpitched-note.rb +112 -0
  68. data/lib/musa-dsl/neumalang.rb +1 -1
  69. data/lib/musa-dsl/neumalang/datatypes.citrus +79 -0
  70. data/lib/musa-dsl/neumalang/neuma.citrus +165 -0
  71. data/lib/musa-dsl/neumalang/neumalang.citrus +32 -242
  72. data/lib/musa-dsl/neumalang/neumalang.rb +373 -142
  73. data/lib/musa-dsl/neumalang/process.citrus +21 -0
  74. data/lib/musa-dsl/neumalang/terminals.citrus +67 -0
  75. data/lib/musa-dsl/neumalang/vectors.citrus +23 -0
  76. data/lib/musa-dsl/neumas.rb +5 -0
  77. data/lib/musa-dsl/neumas/array-to-neumas.rb +34 -0
  78. data/lib/musa-dsl/neumas/neuma-decoder.rb +63 -0
  79. data/lib/musa-dsl/neumas/neuma-gdv-decoder.rb +57 -0
  80. data/lib/musa-dsl/neumas/neuma-gdvd-decoder.rb +15 -0
  81. data/lib/musa-dsl/neumas/neumas.rb +37 -0
  82. data/lib/musa-dsl/neumas/string-to-neumas.rb +34 -0
  83. data/lib/musa-dsl/repl.rb +1 -1
  84. data/lib/musa-dsl/repl/repl.rb +122 -110
  85. data/lib/musa-dsl/sequencer.rb +1 -1
  86. data/lib/musa-dsl/sequencer/base-sequencer-implementation-control.rb +163 -136
  87. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +301 -286
  88. data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +550 -321
  89. data/lib/musa-dsl/sequencer/base-sequencer-public.rb +198 -176
  90. data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +75 -0
  91. data/lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb +75 -0
  92. data/lib/musa-dsl/sequencer/sequencer-dsl.rb +105 -85
  93. data/lib/musa-dsl/sequencer/timeslots.rb +34 -0
  94. data/lib/musa-dsl/series.rb +1 -1
  95. data/lib/musa-dsl/{core-ext → series}/array-to-serie.rb +1 -1
  96. data/lib/musa-dsl/series/base-series.rb +171 -168
  97. data/lib/musa-dsl/series/hash-serie-splitter.rb +134 -132
  98. data/lib/musa-dsl/series/holder-serie.rb +1 -1
  99. data/lib/musa-dsl/series/main-serie-constructors.rb +6 -1
  100. data/lib/musa-dsl/series/main-serie-operations.rb +807 -797
  101. data/lib/musa-dsl/series/proxy-serie.rb +6 -6
  102. data/lib/musa-dsl/series/queue-serie.rb +5 -5
  103. data/lib/musa-dsl/series/series.rb +2 -0
  104. data/lib/musa-dsl/transcription.rb +4 -0
  105. data/lib/musa-dsl/transcription/from-gdv-to-midi.rb +227 -0
  106. data/lib/musa-dsl/transcription/from-gdv-to-musicxml.rb +36 -0
  107. data/lib/musa-dsl/transcription/from-gdv.rb +17 -0
  108. data/lib/musa-dsl/transcription/transcription.rb +55 -0
  109. data/lib/musa-dsl/transport.rb +6 -6
  110. data/lib/musa-dsl/transport/clock.rb +26 -26
  111. data/lib/musa-dsl/transport/dummy-clock.rb +32 -30
  112. data/lib/musa-dsl/transport/external-tick-clock.rb +21 -20
  113. data/lib/musa-dsl/transport/input-midi-clock.rb +89 -80
  114. data/lib/musa-dsl/transport/timer-clock.rb +72 -71
  115. data/lib/musa-dsl/transport/timer.rb +28 -26
  116. data/lib/musa-dsl/transport/transport.rb +111 -93
  117. data/musa-dsl.gemspec +3 -3
  118. metadata +73 -24
  119. data/lib/musa-dsl/core-ext/array-apply-get.rb +0 -18
  120. data/lib/musa-dsl/core-ext/array-to-neumas.rb +0 -28
  121. data/lib/musa-dsl/core-ext/as-context-run.rb +0 -44
  122. data/lib/musa-dsl/core-ext/duplicate.rb +0 -134
  123. data/lib/musa-dsl/core-ext/key-parameters-procedure-binder.rb +0 -85
  124. data/lib/musa-dsl/core-ext/proc-nice.rb +0 -13
  125. data/lib/musa-dsl/core-ext/send-nice.rb +0 -21
  126. data/lib/musa-dsl/core-ext/string-to-neumas.rb +0 -27
  127. data/lib/musa-dsl/datasets/gdv-decorators.rb +0 -221
  128. data/lib/musa-dsl/generative/rules.rb +0 -282
  129. data/lib/musa-dsl/neuma.rb +0 -1
  130. data/lib/musa-dsl/neuma/neuma.rb +0 -181
@@ -1,449 +1,678 @@
1
- require 'musa-dsl/core-ext/arrayfy'
2
- require 'musa-dsl/core-ext/key-parameters-procedure-binder'
1
+ require_relative '../core-ext/arrayfy'
2
+ require_relative '../core-ext/smart-proc-binder'
3
3
 
4
4
  require_relative 'base-sequencer-implementation-control'
5
5
  require_relative 'base-sequencer-implementation-play-helper'
6
6
 
7
- class Musa::BaseSequencer
8
- private
7
+ using Musa::Extension::Arrayfy
8
+ using Musa::Extension::DeepCopy
9
9
 
10
- def _tick
11
- position_to_run = @position_mutex.synchronize { @position += 1 }
10
+ module Musa
11
+ module Sequencer
12
+ class BaseSequencer
13
+ include Musa::Extension::SmartProcBinder
14
+ include Musa::Extension::DeepCopy
12
15
 
13
- @before_tick.each { |block| block.call Rational(position_to_run, @ticks_per_bar) }
16
+ private
14
17
 
15
- if @score[position_to_run]
16
- @score[position_to_run].each do |command|
18
+ def _tick(position_to_run)
17
19
 
18
- if command.key?(:parent_control) && !command[:parent_control].stopped?
19
- @event_handlers.push command[:parent_control]
20
+ @before_tick.each { |block| block.call position_to_run }
20
21
 
21
- @@tick_mutex.synchronize do
22
- original_stdout = $stdout
23
- original_stderr = $stderr
22
+ queue = @timeslots[position_to_run]
24
23
 
25
- $stdout = command[:parent_control].stdout
26
- $stderr = command[:parent_control].stderr
24
+ if queue
25
+ until queue.empty?
27
26
 
28
- command[:block]._call command[:value_parameters], command[:key_parameters] if command[:block]
27
+ command = queue.shift
28
+ @timeslots.delete position_to_run if queue.empty?
29
29
 
30
- $stdout = original_stdout
31
- $stderr = original_stderr
32
- end
30
+ if command.key?(:parent_control) && !command[:parent_control].stopped?
31
+ @event_handlers.push command[:parent_control]
33
32
 
34
- @event_handlers.pop
35
- else
36
- @@tick_mutex.synchronize do
37
- command[:block]._call command[:value_parameters], command[:key_parameters] if command[:block]
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
42
+ end
38
43
  end
39
44
  end
45
+
46
+ Thread.pass
40
47
  end
41
48
 
42
- @score.delete position_to_run
43
- end
49
+ def _raw_numeric_at(at_position, force_first: nil, &block)
50
+ force_first ||= false
44
51
 
45
- Thread.pass
46
- end
52
+ if at_position == @position
53
+ begin
54
+ yield
55
+ rescue StandardError, ScriptError => e
56
+ _rescue_error e
57
+ end
47
58
 
48
- def _hold_public_ticks
49
- @hold_public_ticks = true
50
- end
59
+ elsif at_position > @position
60
+ @timeslots[at_position] ||= []
51
61
 
52
- def _release_public_ticks
53
- @hold_ticks.times { _tick }
54
- @hold_ticks = 0
55
- @hold_public_ticks = false
56
- end
62
+ value = { block: block, value_parameters: [], key_parameters: {} }
63
+ if force_first
64
+ @timeslots[at_position].insert 0, value
65
+ else
66
+ @timeslots[at_position] << value
67
+ end
68
+ else
69
+ @logger.warn('BaseSequencer') { "._raw_numeric_at: ignoring past at command for #{at_position}" }
70
+ end
57
71
 
58
- def _raw_numeric_at(bar_position, force_first: nil, &block)
59
- force_first ||= false
72
+ nil
73
+ end
60
74
 
61
- position = bar_position.rationalize * @ticks_per_bar
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
62
78
 
63
- if position == @position
64
- begin
65
- yield
66
- rescue StandardError, ScriptError => e
67
- _rescue_block_error e
68
- end
79
+ at_position = _check_position(at_position)
69
80
 
70
- elsif position > @position
71
- @score[position] = [] unless @score[position]
81
+ value_parameters = []
82
+ value_parameters << with if !with.nil? && !with.is_a?(Hash)
72
83
 
73
- value = { block: block, value_parameters: [], key_parameters: {} }
74
- if force_first
75
- @score[position].insert 0, value
76
- else
77
- @score[position] << value
78
- end
79
- else
80
- _log "BaseSequencer._raw_numeric_at: warning: ignoring past at command for #{Rational(position, @ticks_per_bar)}" if @do_log
81
- end
84
+ block_key_parameters_binder =
85
+ SmartProcBinder.new block, on_rescue: proc { |e| _rescue_error(e) }
82
86
 
83
- nil
84
- end
87
+ key_parameters = {}
88
+ key_parameters.merge! block_key_parameters_binder._apply(nil, with).last if with.is_a?(Hash)
85
89
 
86
- def _numeric_at(bar_position, control, with: nil, debug: nil, &block)
87
- raise ArgumentError, 'Block is mandatory' unless block
90
+ key_parameters[:control] = control if block_key_parameters_binder.key?(:control)
88
91
 
89
- position = bar_position.rationalize * @ticks_per_bar
92
+ if at_position == @position
93
+ @on_debug_at.each { |c| c.call } if @logger.sev_threshold >= ::Logger::Severity::DEBUG
90
94
 
91
- if position != position.round
92
- original_position = position
93
- position = position.round.rationalize
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
94
101
 
95
- if @do_log
96
- _log "BaseSequencer._numeric_at: warning: rounding position #{bar_position} (#{original_position}) "\
97
- "to tick precision: #{position / @ticks_per_bar} (#{position})"
98
- end
99
- end
102
+ elsif @position.nil? || at_position > @position
100
103
 
101
- value_parameters = []
102
- value_parameters << with if !with.nil? && !with.is_a?(Hash)
104
+ @timeslots[at_position] ||= []
103
105
 
104
- if block_given?
105
- block_key_parameters_binder =
106
- KeyParametersProcedureBinder.new block, on_rescue: proc { |e| _rescue_block_error(e) }
106
+ if @logger.sev_threshold <= ::Logger::Severity::DEBUG
107
+ @on_debug_at.each do |block|
108
+ @timeslots[at_position] << { parent_control: control, block: block }
109
+ end
110
+ end
107
111
 
108
- key_parameters = {}
109
- key_parameters.merge! block_key_parameters_binder.apply with if with.is_a? Hash
112
+ @timeslots[at_position] << { parent_control: control, block: block_key_parameters_binder,
113
+ value_parameters: value_parameters,
114
+ key_parameters: key_parameters }
115
+ else
116
+ @logger.warn('BaseSequencer') { "._numeric_at: ignoring past 'at' command for #{at_position}" }
117
+ end
110
118
 
111
- key_parameters[:control] = control if block_key_parameters_binder.key?(:control)
119
+ nil
120
+ end
112
121
 
113
- if position == @position
114
- @debug_at.call if debug && @debug_at
122
+ def _serie_at(bar_position_serie, control, with: nil, debug: nil, &block)
115
123
 
116
- begin
117
- locked = @@tick_mutex.try_lock
124
+ bar_position = bar_position_serie.next_value
118
125
 
119
- if locked
120
- original_stdout = $stdout
121
- original_stderr = $stderr
126
+ with_value = if with.respond_to? :next_value
127
+ with.next_value
128
+ else
129
+ with
130
+ end
122
131
 
123
- $stdout = control.stdout
124
- $stderr = control.stderr
125
- end
132
+ if bar_position
133
+ _numeric_at bar_position, control, with: with_value, debug: debug, &block
126
134
 
127
- block_key_parameters_binder._call value_parameters, key_parameters
128
- ensure
129
- if locked
130
- $stdout = original_stdout
131
- $stderr = original_stderr
135
+ _numeric_at bar_position, control, debug: false do
136
+ _serie_at bar_position_serie, control, with: with, debug: debug, &block
132
137
  end
133
-
134
- @@tick_mutex.unlock if locked
138
+ else
139
+ # serie finalizada
135
140
  end
136
141
 
137
- elsif position > @position
138
- @score[position] = [] unless @score[position]
139
-
140
- @score[position] << { parent_control: control, block: @on_debug_at } if debug && @on_debug_at
141
- @score[position] << { parent_control: control, block: block_key_parameters_binder, value_parameters: value_parameters, key_parameters: key_parameters }
142
- else
143
- _log "BaseSequencer._numeric_at: warning: ignoring past at command for #{Rational(position, @ticks_per_bar)}" if @do_log
142
+ nil
144
143
  end
145
- end
146
144
 
147
- nil
148
- end
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
168
+ else
169
+ element = serie.next_value
170
+ end
149
171
 
150
- def _serie_at(bar_position_serie, control, with: nil, debug: nil, &block)
172
+ if element
173
+ operation = __play_eval.run_operation element
151
174
 
152
- bar_position = bar_position_serie.next_value
175
+ case operation[:current_operation]
153
176
 
154
- with_value = if with.respond_to? :next_value
155
- with.next_value
156
- else
157
- with
158
- end
177
+ when :none
159
178
 
160
- if bar_position
161
- _numeric_at bar_position, control, with: with_value, debug: debug, &block
179
+ when :block
162
180
 
163
- _numeric_at bar_position, control, debug: false do
164
- _serie_at bar_position_serie, control, with: with, debug: debug, &block
165
- end
166
- else
167
- # serie finalizada
168
- end
181
+ __play_eval.block_procedure_binder.call operation[:current_parameter], control: control
169
182
 
170
- nil
171
- end
183
+ when :event
172
184
 
173
- def _play(serie, control, nl_context = nil, mode: nil, decoder: nil, __play_eval: nil, **mode_args, &block)
174
-
175
- block ||= proc {}
176
-
177
- __play_eval ||= PlayEval.create \
178
- mode,
179
- KeyParametersProcedureBinder.new(block,
180
- on_rescue: proc { |e| _rescue_block_error(e) }),
181
- decoder,
182
- nl_context
183
-
184
- element = nil
185
-
186
- if control.stopped?
187
- # nothing to do
188
- elsif control.paused?
189
- control.store_continuation sequencer: self,
190
- serie: serie,
191
- nl_context: nl_context,
192
- mode: mode,
193
- decoder: decoder,
194
- play_eval: __play_eval,
195
- mode_args: mode_args
196
- else
197
- element = serie.next_value
198
- end
185
+ control._launch operation[:current_event],
186
+ operation[:current_value_parameters],
187
+ operation[:current_key_parameters]
199
188
 
200
- if element
201
- operation = __play_eval.run_operation element
189
+ when :play
202
190
 
203
- case operation[:current_operation]
191
+ control2 = PlayControl.new control
192
+ control3 = PlayControl.new control2
193
+ control3.after { control3.launch :sync }
204
194
 
205
- when :none
195
+ _play operation[:current_parameter].instance,
196
+ control3,
197
+ __play_eval: __play_eval.subcontext,
198
+ **mode_args
206
199
 
207
- when :block
200
+ control2.on :sync do
201
+ _play serie, control, __play_eval: __play_eval, **mode_args
202
+ end
208
203
 
209
- __play_eval.block_procedure_binder.call operation[:current_parameter], control: control
204
+ when :no_eval_play
210
205
 
211
- when :event
206
+ control2 = PlayControl.new control
207
+ control3 = PlayControl.new control2
208
+ control3.after { control3.launch :sync }
212
209
 
213
- control._launch operation[:current_event],
214
- operation[:current_value_parameters],
215
- operation[:current_key_parameters]
210
+ _play operation[:current_parameter].instance,
211
+ control3,
212
+ __play_eval: WaitModePlayEval.new(__play_eval.block_procedure_binder),
213
+ **mode_args
216
214
 
217
- when :play
215
+ control2.on :sync do
216
+ _play serie, control, __play_eval: __play_eval, **mode_args
217
+ end
218
218
 
219
- control2 = PlayControl.new control
220
- control3 = PlayControl.new control2
221
- control3.after { control3.launch :sync }
219
+ when :parallel_play
222
220
 
223
- _play operation[:current_parameter].instance,
224
- control3,
225
- __play_eval: __play_eval.subcontext,
226
- **mode_args
221
+ control2 = PlayControl.new control
227
222
 
228
- control2.on :sync do
229
- _play serie, control, __play_eval: __play_eval, **mode_args
230
- end
223
+ operation[:current_parameter].each do |current_parameter|
224
+ control3 = PlayControl.new control2
225
+ control3.after { control3.launch :sync }
231
226
 
232
- when :no_eval_play
227
+ _play current_parameter.instance,
228
+ control3,
229
+ __play_eval: __play_eval.subcontext,
230
+ **mode_args
231
+ end
233
232
 
234
- control2 = PlayControl.new control
235
- control3 = PlayControl.new control2
236
- control3.after { control3.launch :sync }
233
+ counter = operation[:current_parameter].size
237
234
 
238
- _play operation[:current_parameter].instance,
239
- control3,
240
- __play_eval: WaitModePlayEval.new(__play_eval.block_procedure_binder),
241
- **mode_args
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
242
264
 
243
- control2.on :sync do
244
- _play serie, control, __play_eval: __play_eval, **mode_args
265
+ control.do_after.each do |do_after|
266
+ _numeric_at position, control2, &do_after
267
+ end
245
268
  end
246
269
 
247
- when :parallel_play
270
+ nil
271
+ end
248
272
 
249
- control2 = PlayControl.new control
273
+ def _every(interval, control, block_procedure_binder: nil, &block)
274
+ block ||= proc {}
250
275
 
251
- operation[:current_parameter].each do |current_parameter|
252
- control3 = PlayControl.new control2
253
- control3.after { control3.launch :sync }
276
+ block_procedure_binder ||= SmartProcBinder.new block, on_rescue: proc { |e| _rescue_error(e) }
254
277
 
255
- _play current_parameter.instance,
256
- control3,
257
- __play_eval: __play_eval.subcontext,
258
- **mode_args
259
- end
278
+ _numeric_at position, control do
279
+ control._start_position ||= position
280
+ control._execution_counter ||= 0
260
281
 
261
- counter = operation[:current_parameter].size
282
+ duration_exceeded =
283
+ (control._start_position + control.duration_value - interval) <= position if interval && control.duration_value
262
284
 
263
- control2.on :sync do
264
- counter -= 1
265
- _play serie, control, __play_eval: __play_eval, **mode_args if counter == 0
266
- end
267
- end
285
+ till_exceeded = control.till_value - interval <= position if interval && control.till_value
268
286
 
269
- case operation[:continue_operation]
270
- when :now
271
- #now do
272
- _numeric_at position, control do
273
- _play serie, control, __play_eval: __play_eval, **mode_args
274
- end
287
+ condition_failed = !control.condition_block.call if control.condition_block
275
288
 
276
- when :at
277
- #at operation[:continue_parameter] do
278
- _numeric_at operation[:continue_parameter], control do
279
- _play serie, control, __play_eval: __play_eval, **mode_args
280
- end
289
+ unless control.stopped? || condition_failed || till_exceeded
290
+ block_procedure_binder.call(control: control)
291
+ control._execution_counter += 1
292
+ end
281
293
 
282
- when :wait
283
- #wait operation[:continue_parameter] do
284
- _numeric_at position + operation[:continue_parameter].rationalize, control do
285
- _play serie, control, __play_eval: __play_eval, **mode_args
286
- end
287
294
 
288
- when :on
289
- control.on operation[:continue_parameter], only_once: true do
290
- _play serie, control, __play_eval: __play_eval, **mode_args
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
299
+
300
+ else
301
+ control.do_on_stop.each(&:call)
302
+
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
291
307
  end
292
- end
293
- else
294
- control2 = EventHandler.new control
295
308
 
296
- control.do_after.each do |do_after|
297
- _numeric_at position, control2, &do_after
309
+ nil
298
310
  end
299
- end
300
311
 
301
- nil
302
- end
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
303
348
 
304
- def _every(binterval, control, block_procedure_binder: nil, &block)
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
305
354
 
306
- block ||= proc {}
355
+ if step.is_a?(Hash)
356
+ step = hash_keys.collect { |k| step[k] }
357
+ end
307
358
 
308
- block_procedure_binder ||= KeyParametersProcedureBinder.new block, on_rescue: proc { |e| _rescue_block_error(e) }
359
+ if right_open.is_a?(Hash)
360
+ right_open = hash_keys.collect { |k| right_open[k] }
361
+ end
309
362
 
310
- _numeric_at position, control do
311
- control._start ||= position
363
+ else
364
+ from = from.arrayfy
365
+ size = from.size
366
+ end
312
367
 
313
- duration_exceeded = (control._start + control.duration_value - binterval) <= position if control.duration_value
314
- till_exceeded = control.till_value - binterval <= position if control.till_value
315
- condition_failed = !instance_eval(&control.condition) if control.condition
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)
316
372
 
317
- block_procedure_binder.call(control: control) unless control.stopped?
373
+ # from, to, step, every
374
+ # from, to, step, (duration | till)
375
+ # from, to, every, (duration | till)
376
+ # from, step, every, (duration | till)
318
377
 
319
- if !control.stopped? && !duration_exceeded && !till_exceeded && !condition_failed
378
+ block ||= proc {}
320
379
 
321
- _numeric_at position + binterval, control do
322
- _every binterval, control, block_procedure_binder: block_procedure_binder
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
323
382
  end
324
- else
325
- control.do_on_stop.each(&:call)
326
383
 
327
- control.do_after.each do |do_after|
328
- _numeric_at position + binterval + do_after[:bars], control, &do_after[:block]
329
- end
330
- end
331
- end
384
+ right_open.map! { |v| v || false }
332
385
 
333
- nil
334
- end
386
+ function ||= proc { |ratio| ratio }
387
+ function = function.arrayfy(size: size)
335
388
 
336
- def _move(every: nil, from:, to: nil, step: nil, duration: nil, till: nil, function: nil, right_open: nil, on_stop: nil, after_bars: nil, after: nil, &block)
389
+ function_range = 1r.arrayfy(size: size)
390
+ function_offset = 0r.arrayfy(size: size)
337
391
 
338
- raise ArgumentError, "Cannot use duration: #{duration} and till: #{till} parameters at the same time. Use only one of them." if till && duration
339
- raise ArgumentError, "Invalid use: 'function:' parameter is incompatible with 'step:' parameter" if function && step
340
- raise ArgumentError, "Invalid use: 'function:' parameter needs 'to:' parameter not nil" if function && !to
392
+ start_position = position
341
393
 
342
- # from, to, step, every
343
- # from, to, step, (duration | till)
344
- # from, to, every, (duration | till)
345
- # from, step, every, (duration | till)
394
+ if duration || till
395
+ effective_duration = duration || till - start_position
346
396
 
347
- block ||= proc {}
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 }
348
399
 
349
- step = -step if step && to && ((step > 0 && to < from) || (step < 0 && from < to))
350
- right_open ||= false
400
+ size.times do |i|
401
+ if to[i] && step[i] && !every[i]
351
402
 
352
- function ||= proc { |ratio| ratio }
353
- function_range = 1r
354
- function_offset = 0r
403
+ steps = (to[i] - from[i]) / step[i]
355
404
 
356
- start_position = position
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
357
411
 
358
- if duration || till
359
- effective_duration = duration || till - start_position
360
- right_open_offset = right_open ? 0 : 1 # Add 1 tick to arrive to final value in duration time (no need to add an extra tick)
412
+ elsif to[i] && !step[i] && !every[i]
361
413
 
362
- if to && step && !every
363
- steps = (to - from) / step
364
- every = Rational(effective_duration, steps + right_open_offset)
414
+ if tick_duration > 0
415
+ function_range[i] = to[i] - from[i]
416
+ function_offset[i] = from[i]
365
417
 
366
- elsif to && !step && !every
367
- function_range = to - from
368
- function_offset = from
418
+ from[i] = 0r
419
+ to[i] = 1r
369
420
 
370
- from = 0r
371
- to = 1r
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
372
426
 
373
- step = 1r / (effective_duration * @ticks_per_bar - right_open_offset)
374
- every = @tick_duration
427
+ elsif to[i] && !step[i] && every[i]
428
+ function_range[i] = to[i] - from[i]
429
+ function_offset[i] = from[i]
375
430
 
376
- elsif to && !step && every
377
- function_range = to - from
378
- function_offset = from
431
+ from[i] = 0r
432
+ to[i] = 1r
379
433
 
380
- from = 0r
381
- to = 1r
434
+ steps = effective_duration / every[i]
435
+ step[i] = 1r / (steps - right_open_offset[i])
382
436
 
383
- steps = effective_duration / every
384
- step = 1r / (steps - right_open_offset)
437
+ elsif !to[i] && step[i] && every[i]
438
+ # ok
439
+ elsif !to[i] && !step[i] && every[i]
440
+ step[i] = 1r
385
441
 
386
- elsif !to && step && every
387
- # ok
388
- elsif !to && !step && every
389
- step = 1r
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
390
459
 
391
- else
392
- raise ArgumentError, 'Cannot use this parameters combination'
393
- end
394
- else
395
- if to && step && every
396
- # ok
397
- elsif to && !step && every
398
- step = (to <=> from).to_r
399
- else
400
- raise ArgumentError, 'Cannot use this parameters combination'
401
- end
402
- end
460
+ binder = SmartProcBinder.new(block)
403
461
 
404
- binder = KeyParametersProcedureBinder.new(block)
462
+ every_groups = {}
463
+ group_counter = {}
405
464
 
406
- every_control = EveryControl.new(@event_handlers.last, capture_stdout: true, duration: duration, till: till, on_stop: on_stop, after_bars: after_bars, after: after)
465
+ positions = Array.new(size)
466
+ q_durations = Array.new(size)
467
+ position_jitters = Array.new(size)
468
+ duration_jitters = Array.new(size)
407
469
 
408
- control = MoveControl.new(every_control)
470
+ size.times.each do |i|
471
+ every_groups[every[i]] ||= []
472
+ every_groups[every[i]] << i
473
+ group_counter[every[i]] = 0
474
+ end
409
475
 
410
- @event_handlers.push control
476
+ control = MoveControl.new(@event_handlers.last,
477
+ duration: duration, till: till,
478
+ on_stop: on_stop, after_bars: after_bars, after: after)
411
479
 
412
- _numeric_at start_position, control do
413
- value = from
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
414
485
 
415
- _every every, every_control do
486
+ @event_handlers.push control
487
+
488
+ _numeric_at start_position, control do
489
+ next_values = from.dup
490
+
491
+ values = Array.new(size)
492
+ stop = Array.new(size, false)
493
+ last_position = Array.new(size)
494
+
495
+ _every _common_interval(every_groups.keys), control.every_control do
496
+ process_indexes = []
497
+
498
+ every_groups.each_pair do |group_interval, affected_indexes|
499
+ group_position = start_position + ((group_interval || 0) * group_counter[group_interval])
500
+
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
508
+
509
+ process_indexes << affected_indexes
510
+
511
+ group_counter[group_interval] += 1
512
+
513
+ next_group_position = start_position +
514
+ if group_interval
515
+ (group_interval * group_counter[group_interval])
516
+ else
517
+ effective_duration
518
+ end
519
+
520
+ next_group_q_position = _quantize(next_group_position)
521
+
522
+ affected_indexes.each do |i|
523
+ positions[i] = group_position
524
+ q_durations[i] = next_group_q_position - position
525
+
526
+ position_jitters[i] = group_position - position
527
+ duration_jitters[i] = next_group_position - next_group_q_position
528
+ end
529
+ end
530
+ end
531
+
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
416
620
 
417
- parameters = binder.apply(control: control)
621
+ @event_handlers.pop
418
622
 
419
- yield function.call(value) * function_range + function_offset, **parameters
623
+ control
624
+ end
420
625
 
421
- if to && (value >= to && step.positive? || value <= to && step.negative?)
422
- control.stop
423
- else
424
- value += step
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
634
+ end
635
+
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
646
+ end
425
647
  end
426
648
  end
427
- end
428
649
 
429
- @event_handlers.pop
650
+ def _hash_from_keys_and_values(keys, values)
651
+ {}.tap { |h| keys.each_index { |i| h[keys[i]] = values[i] } }
652
+ end
430
653
 
431
- control
432
- end
654
+ def _common_interval(intervals)
655
+ intervals = intervals.compact
656
+ return nil if intervals.empty?
433
657
 
434
- def _rescue_block_error(e)
435
- _log e
436
- _log e.full_message(order: :top)
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)
437
661
 
438
- @on_block_error.each do |block|
439
- block.call e
440
- end
441
- end
662
+ #intervals.reduce(1r, :*)
442
663
 
443
- def _log(msg = nil)
444
- m = '...' unless msg
445
- m = ": #{msg}" if msg
664
+ Rational(gcd_numerators, lcm_denominators)
665
+ end
666
+
667
+ def _rescue_error(e)
668
+ @logger.error('BaseSequencer') { e.to_s }
669
+ @logger.error('BaseSequencer') { e.full_message(highlight: true, order: :top) }
446
670
 
447
- warn "#{position.to_f.round(3)} [#{position}]#{m}"
671
+ @on_error.each do |block|
672
+ block.call e
673
+ end
674
+ end
675
+ end
448
676
  end
449
677
  end
678
+