musa-dsl 0.22.4 → 0.22.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fe8d0a64e4ba5518c7ed0fe4b75fffbf059217ef65e83025889a21d1c830a6e7
4
- data.tar.gz: a78c43004243d81a9411c42e34338ecfe4ca7ef17322417dc2e8eb9a953b9abd
3
+ metadata.gz: 77d95cd844364ae8dce08779712a34fdb0e8e349336717ffc7e49580c293b647
4
+ data.tar.gz: 16b7d33203bdd273acd5f235189025fa15797e7d09a1bee498151d521c0109d3
5
5
  SHA512:
6
- metadata.gz: 6bf51f55471c9a753ec648cb1c223ef0564307d2807ec0c569a144856ef330ca7567e8ed4f8f0f651e1808ea42712c02c686ac123424d0feaa90955e2f6d77fe
7
- data.tar.gz: 2bd503000a0d7cd2bd0e74f9790cbf5533335bc7d74cc6846cb60ea3bad4f1fd3fb24e74ed0bd1c6955b476d45274f51047202c89e12582e5fd642753d34144e
6
+ metadata.gz: bad2f753e7c62c93276dd5a019f57ff8eb2b5c23c38a20989e4dcc5f7a3a4365dfdd1532750da0cbb8dbe0a08c459fb02f8f62b0fd1246f1a5f8da8c755c49c5
7
+ data.tar.gz: a7a7e3d32b7a723c206ec098585cbbad76d2ef377ad55007a722537ef0b1070a9a43a4a18740758db82167193b55e1b524e7bb7f3bdf5251e7beba1c2effd6d5
@@ -1,5 +1,5 @@
1
1
  module Musa
2
- VERSION = '0.22.4'
2
+ VERSION = '0.22.5'
3
3
  end
4
4
 
5
5
  require_relative 'musa-dsl/core-ext'
@@ -324,8 +324,13 @@ module Musa
324
324
  run_operation eval_use_variable(element[:use_variable])
325
325
 
326
326
  when :event
327
- value_parameters = element[:value_parameters] ? element[:value_parameters].collect { |e| subcontext.eval_element(e) } : []
328
- key_parameters = element[:key_parameters] ? element[:key_parameters].collect { |k, e| [k, subcontext.eval_element(e)] }.to_h : {}
327
+ value_parameters = element[:value_parameters] ?
328
+ element[:value_parameters].collect { |e| subcontext.eval_element(e) } :
329
+ []
330
+
331
+ key_parameters = element[:key_parameters] ?
332
+ element[:key_parameters].collect { |k, e| [k, subcontext.eval_element(e)] }.to_h :
333
+ {}
329
334
 
330
335
  { current_operation: :event,
331
336
  current_event: element[:event],
@@ -7,107 +7,59 @@ module Musa; module Sequencer
7
7
  class BaseSequencer
8
8
  private def _play_timed(timed_serie,
9
9
  control,
10
- reference: nil,
11
- step: nil,
12
- predictive: nil,
13
- right_open: nil,
14
- stops: nil,
15
10
  &block)
16
11
 
17
- reference ||= 0r
18
- step ||= 1r
19
- predictive ||= predictive
20
- stops = stops.nil? ? true : stops
21
-
22
12
  if first_value_sample = timed_serie.peek_next_value
23
-
24
13
  debug "_play_timed: first_value_sample #{first_value_sample}"
25
14
 
26
15
  hash_mode = first_value_sample[:value].is_a?(Hash)
27
16
 
28
17
  if hash_mode
29
- components = first_value_sample[:value].keys
30
-
31
- reference = reference.hashify(keys: components)
32
- step = step.hashify(keys: components)
33
- predictive = predictive.hashify(keys:components)
34
- stops = stops.hashify(keys:components)
35
- right_open = right_open.hashify(keys:components)
18
+ component_ids = first_value_sample[:value].keys
36
19
  else
37
20
  size = first_value_sample[:value].size
38
- components = (0 .. size-1).to_a
39
-
40
- reference = reference.arrayfy(size: size)
41
- step = step.arrayfy(size: size)
42
- predictive = predictive.arrayfy(size: size)
43
- stops = stops.arrayfy(size: size)
44
- right_open = right_open.arrayfy(size: size)
45
- end
46
-
47
- split = timed_serie.flatten_timed.split
48
- quantized_series = hash_mode ? {} : []
49
-
50
- components.each do |component|
51
- quantized_series[component] =
52
- QUANTIZE(split[component],
53
- reference: reference[component],
54
- step: step[component],
55
- predictive: predictive[component],
56
- right_open: right_open[component],
57
- stops: stops[component]).instance
21
+ component_ids = (0 .. size-1).to_a
58
22
  end
23
+ extra_attribute_names = Set[*(first_value_sample.keys - [:time, :value])]
59
24
 
60
25
  last_positions = hash_mode ? {} : []
61
26
  end
62
27
 
63
28
  binder = SmartProcBinder.new(block)
64
29
 
65
- _play_timed_step(hash_mode, components, quantized_series, position, last_positions, binder, control)
30
+ _play_timed_step(hash_mode, component_ids, extra_attribute_names, timed_serie,
31
+ position, last_positions, binder, control)
66
32
  end
67
33
 
68
-
69
- private def _play_timed_step(hash_mode, components, quantized_series, start_position, last_positions,
34
+ private def _play_timed_step(hash_mode,
35
+ component_ids, extra_attribute_names,
36
+ timed_serie,
37
+ start_position,
38
+ last_positions,
70
39
  binder, control)
71
40
 
72
- affected_components_by_time = {}
73
-
74
- components.each do |component|
75
- if v = quantized_series[component].peek_next_value
76
-
77
- debug "_play_timed_step: quantized_series[#{component}].peek_next_value #{v}"
78
- time = v[:time]
41
+ source_next_value = timed_serie.next_value
79
42
 
80
- affected_components_by_time[time] ||= []
81
- affected_components_by_time[time] << component
82
- end
83
- end
43
+ affected_components = component_ids.select { |_| !source_next_value[:value][_].nil? } if source_next_value
84
44
 
85
- if affected_components_by_time.any?
86
- time = affected_components_by_time.keys.sort.first
45
+ if affected_components && affected_components.any?
46
+ time = source_next_value[:time]
87
47
 
88
48
  values = hash_mode ? {} : []
89
- next_values = hash_mode ? {} : []
90
- durations = hash_mode ? {} : []
91
- q_durations = hash_mode ? {} : []
49
+ extra_attributes = extra_attribute_names.collect { |_| [_, hash_mode ? {} : []] }.to_h
92
50
  started_ago = hash_mode ? {} : []
93
51
 
94
- affected_components_by_time[time].each do |component|
95
- value = quantized_series[component].next_value
52
+ affected_components.each do |component|
53
+ values[component] = source_next_value[:value][component]
96
54
 
97
- values[component] = value[:value]
98
- durations[component] = value[:duration]
99
-
100
- q_durations[component] =
101
- _quantize_position(time + durations[component], warn: false) -
102
- _quantize_position(time, warn: false)
103
-
104
- nv = quantized_series[component].peek_next_value
105
- next_values[component] = (nv && nv[:value] != values[component]) ? nv[:value] : nil
55
+ extra_attribute_names.each do |attribute_name|
56
+ extra_attributes[attribute_name][component] = source_next_value[attribute_name][component]
57
+ end
106
58
 
107
59
  last_positions[component] = _quantize_position(time, warn: false)
108
60
  end
109
61
 
110
- components.each do |component|
62
+ component_ids.each do |component|
111
63
  if last_positions[component] && last_positions[component] != time
112
64
  sa = _quantize_position(time, warn: false) - last_positions[component]
113
65
  started_ago[component] = (sa == 0) ? nil : sa
@@ -115,82 +67,21 @@ module Musa; module Sequencer
115
67
  end
116
68
 
117
69
  _numeric_at start_position + _quantize_position(time, warn: false), control do
118
- debug "_play_timed_step: before binder.call: durations #{durations} q_durations #{q_durations}"
119
- binder.call(values, next_values,
120
- duration: durations,
121
- quantized_duration: q_durations,
70
+ binder.call(values,
71
+ **extra_attributes,
122
72
  started_ago: started_ago,
123
73
  control: control)
124
74
 
125
- _play_timed_step(hash_mode, components, quantized_series, start_position, last_positions,
75
+ _play_timed_step(hash_mode,
76
+ component_ids, extra_attribute_names,
77
+ timed_serie,
78
+ start_position,
79
+ last_positions,
126
80
  binder, control)
127
81
  end
128
82
  end
129
83
  end
130
84
 
131
- # TODO implement this alternative play method as another mode
132
- # Este es un modo muy interesante pero que implica un procesamiento diferente en el yield_block que no me
133
- # sirve para el código de samples/multidim_sample, puesto que en este el next_values es literal,
134
- # mientras que samples/multidim_sample necesita que el next_value sea nil si el valor no cambia durante el periodo.
135
- #
136
- private def _play_timed_step_b(hash_mode, components, quantized_series, start_position, last_positions,
137
- binder, control)
138
-
139
- affected_components_by_time = {}
140
-
141
- components.each do |component|
142
- if v = quantized_series[component].peek_next_value
143
- time = v[:time]
144
-
145
- affected_components_by_time[time] ||= []
146
- affected_components_by_time[time] << component
147
- end
148
- end
149
-
150
- if !affected_components_by_time.empty?
151
- time = affected_components_by_time.keys.sort.first
152
-
153
- values = hash_mode ? {} : []
154
- next_values = hash_mode ? {} : []
155
- durations = hash_mode ? {} : []
156
- q_durations = hash_mode ? {} : []
157
- started_ago = hash_mode ? {} : []
158
-
159
- affected_components_by_time[time].each do |component|
160
- value = quantized_series[component].next_value
161
-
162
- values[component] = value[:value]
163
- durations[component] = value[:duration]
164
-
165
- q_durations[component] =
166
- _quantize_position(time + durations[component], warn: false) -
167
- _quantize_position(time, warn: false)
168
-
169
- last_positions[component] = _quantize_position(time, warn: false)
170
- end
171
-
172
- components.each do |component|
173
- nv = quantized_series[component].peek_next_value
174
- next_values[component] = nv[:value] if nv
175
-
176
- if last_positions[component] && last_positions[component] != time
177
- started_ago[component] = _quantize_position(time, warn: false) - last_positions[component]
178
- end
179
- end
180
-
181
- _numeric_at start_position + _quantize_position(time, warn: false), control do
182
- binder.call(values, next_values,
183
- duration: durations,
184
- quantized_duration: q_durations,
185
- started_ago: started_ago,
186
- control: control)
187
-
188
- _play_timed_step_b(hash_mode, components, quantized_series, start_position, last_positions,
189
- binder, control)
190
- end
191
- end
192
- end
193
-
194
85
  class PlayTimedControl < EventHandler
195
86
  attr_reader :do_on_stop, :do_after
196
87
 
@@ -63,21 +63,16 @@ module Musa; module Sequencer
63
63
  nil
64
64
  end
65
65
 
66
- private def _numeric_at(at_position, control, with: nil, debug: nil, &block)
66
+ private def _numeric_at(at_position, control, debug: nil, &block)
67
67
  raise ArgumentError, "'at_position' parameter cannot be nil" if at_position.nil?
68
68
  raise ArgumentError, 'Yield block is mandatory' unless block
69
69
 
70
70
  at_position = _quantize_position(at_position)
71
71
 
72
- value_parameters = []
73
- value_parameters << with if !with.nil? && !with.is_a?(Hash)
74
-
75
72
  block_key_parameters_binder =
76
73
  SmartProcBinder.new block, on_rescue: proc { |e| _rescue_error(e) }
77
74
 
78
75
  key_parameters = {}
79
- key_parameters.merge! block_key_parameters_binder._apply(nil, with).last if with.is_a?(Hash)
80
-
81
76
  key_parameters[:control] = control if block_key_parameters_binder.key?(:control)
82
77
 
83
78
  if at_position == @position
@@ -85,7 +80,7 @@ module Musa; module Sequencer
85
80
 
86
81
  begin
87
82
  locked = @tick_mutex.try_lock
88
- block_key_parameters_binder._call(value_parameters, key_parameters)
83
+ block_key_parameters_binder._call(nil, key_parameters)
89
84
  ensure
90
85
  @tick_mutex.unlock if locked
91
86
  end
@@ -100,8 +95,8 @@ module Musa; module Sequencer
100
95
  end
101
96
  end
102
97
 
103
- @timeslots[at_position] << { parent_control: control, block: block_key_parameters_binder,
104
- value_parameters: value_parameters,
98
+ @timeslots[at_position] << { parent_control: control,
99
+ block: block_key_parameters_binder,
105
100
  key_parameters: key_parameters }
106
101
  else
107
102
  @logger.warn('BaseSequencer') { "._numeric_at: ignoring past 'at' command for #{at_position}" }
@@ -110,20 +105,14 @@ module Musa; module Sequencer
110
105
  nil
111
106
  end
112
107
 
113
- private def _serie_at(bar_position_serie, control, with: nil, debug: nil, &block)
114
- bar_position = bar_position_serie.next_value
115
-
116
- with_value = if with.respond_to? :next_value
117
- with.next_value
118
- else
119
- with
120
- end
108
+ private def _serie_at(position_or_serie, control, debug: nil, &block)
109
+ bar_position = position_or_serie.next_value
121
110
 
122
111
  if bar_position
123
- _numeric_at bar_position, control, with: with_value, debug: debug, &block
112
+ _numeric_at bar_position, control, debug: debug, &block
124
113
 
125
114
  _numeric_at bar_position, control, debug: false do
126
- _serie_at bar_position_serie, control, with: with, debug: debug, &block
115
+ _serie_at position_or_serie, control, debug: debug, &block
127
116
  end
128
117
  else
129
118
  # serie finalizada
@@ -224,8 +213,3 @@ module Musa; module Sequencer
224
213
  private_constant :EventHandler
225
214
  end
226
215
  end; end
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'
@@ -27,9 +27,7 @@ module Musa
27
27
  _release_public_ticks
28
28
  end
29
29
 
30
- private
31
-
32
- def _init_timing
30
+ private def _init_timing
33
31
  @ticks_per_bar = Rational(beats_per_bar * ticks_per_beat)
34
32
  @tick_duration = Rational(1, @ticks_per_bar)
35
33
 
@@ -37,11 +35,11 @@ module Musa
37
35
  @hold_ticks = 0
38
36
  end
39
37
 
40
- def _reset_timing
38
+ private def _reset_timing
41
39
  @position = @position_mutex.synchronize { 1r - @tick_duration }
42
40
  end
43
41
 
44
- def _quantize_position(position, warn: true)
42
+ private def _quantize_position(position, warn: true)
45
43
  ticks_position = position / @tick_duration
46
44
 
47
45
  if ticks_position.round != ticks_position
@@ -58,11 +56,11 @@ module Musa
58
56
  position
59
57
  end
60
58
 
61
- def _hold_public_ticks
59
+ private def _hold_public_ticks
62
60
  @hold_public_ticks = true
63
61
  end
64
62
 
65
- def _release_public_ticks
63
+ private def _release_public_ticks
66
64
  @hold_ticks.times { _tick(@position_mutex.synchronize { @position += @tick_duration }) }
67
65
  @hold_ticks = 0
68
66
  @hold_public_ticks = false
@@ -53,16 +53,14 @@ module Musa
53
53
  @on_fast_forward.each { |block| block.call(false) }
54
54
  end
55
55
 
56
- private
57
-
58
- def _init_timing
56
+ private def _init_timing
59
57
  end
60
58
 
61
- def _reset_timing
59
+ private def _reset_timing
62
60
  @position = nil
63
61
  end
64
62
 
65
- def _quantize_position(position, warn: false)
63
+ private def _quantize_position(position, warn: false)
66
64
  position
67
65
  end
68
66
  end
@@ -83,6 +83,11 @@ module Musa
83
83
  @timeslots.empty?
84
84
  end
85
85
 
86
+ def quantize_position(position, warn: nil)
87
+ warn ||= false
88
+ _quantize_position(position, warn: warn)
89
+ end
90
+
86
91
  def run
87
92
  tick until empty?
88
93
  end
@@ -115,22 +120,19 @@ module Musa
115
120
  @event_handlers.last.launch event, *value_parameters, **key_parameters
116
121
  end
117
122
 
118
- def wait(bars_delay, with: nil, debug: nil, &block)
123
+ def wait(bars_delay, debug: nil, &block)
119
124
  debug ||= false
120
125
 
121
126
  control = EventHandler.new @event_handlers.last
122
127
  @event_handlers.push control
123
128
 
124
129
  if bars_delay.is_a? Numeric
125
- _numeric_at position + bars_delay.rationalize, control, with: with, debug: debug, &block
130
+ _numeric_at position + bars_delay.rationalize, control, debug: debug, &block
126
131
  else
127
132
  bars_delay = Series::S(*bars_delay) if bars_delay.is_a?(Array)
128
133
  bars_delay = bars_delay.instance if bars_delay
129
134
 
130
- with = Series::S(*with).repeat if with.is_a?(Array)
131
- with = with.instance if with
132
-
133
- _serie_at bars_delay.eval { |delay| position + delay }, control, with: with, debug: debug, &block
135
+ _serie_at bars_delay.eval { |delay| position + delay }, control, debug: debug, &block
134
136
  end
135
137
 
136
138
  @event_handlers.pop
@@ -138,11 +140,11 @@ module Musa
138
140
  control
139
141
  end
140
142
 
141
- def now(with: nil, &block)
143
+ def now(&block)
142
144
  control = EventHandler.new @event_handlers.last
143
145
  @event_handlers.push control
144
146
 
145
- _numeric_at position, control, with: with, &block
147
+ _numeric_at position, control, &block
146
148
 
147
149
  @event_handlers.pop
148
150
 
@@ -155,22 +157,19 @@ module Musa
155
157
  nil
156
158
  end
157
159
 
158
- def at(bar_position, with: nil, debug: nil, &block)
160
+ def at(bar_position, debug: nil, &block)
159
161
  debug ||= false
160
162
 
161
163
  control = EventHandler.new @event_handlers.last
162
164
  @event_handlers.push control
163
165
 
164
166
  if bar_position.is_a? Numeric
165
- _numeric_at bar_position.rationalize, control, with: with, debug: debug, &block
167
+ _numeric_at bar_position.rationalize, control, debug: debug, &block
166
168
  else
167
169
  bar_position = Series::S(*bar_position) if bar_position.is_a? Array
168
170
  bar_position = bar_position.instance if bar_position
169
171
 
170
- with = Series::S(*with).repeat if with.is_a? Array
171
- with = with.instance if with
172
-
173
- _serie_at bar_position, control, with: with, debug: debug, &block
172
+ _serie_at bar_position, control, debug: debug, &block
174
173
  end
175
174
 
176
175
  @event_handlers.pop
@@ -216,11 +215,6 @@ module Musa
216
215
  end
217
216
 
218
217
  def play_timed(timed_serie,
219
- reference: nil,
220
- step: nil,
221
- predictive: nil,
222
- stops: nil,
223
- right_open: nil,
224
218
  on_stop: nil,
225
219
  after_bars: nil, after: nil,
226
220
  &block)
@@ -236,14 +230,7 @@ module Musa
236
230
 
237
231
  @event_handlers.push control
238
232
 
239
- _play_timed(timed_serie.instance,
240
- control,
241
- reference: reference,
242
- step: step,
243
- predictive: predictive,
244
- stops: stops,
245
- right_open: right_open,
246
- &block)
233
+ _play_timed(timed_serie.instance, control, &block)
247
234
 
248
235
  @event_handlers.pop
249
236
 
@@ -17,7 +17,7 @@ module Musa
17
17
  :position=,
18
18
  :event_handler
19
19
 
20
- def_delegators :@context, :position, :logger, :debug
20
+ def_delegators :@context, :position, :quantize_position, :logger, :debug
21
21
  def_delegators :@context, :with, :now, :at, :wait, :play, :play_timed, :every, :move
22
22
  def_delegators :@context, :everying, :playing, :moving
23
23
  def_delegators :@context, :launch, :on
@@ -51,7 +51,9 @@ module Musa
51
51
 
52
52
  def_delegators :@sequencer,
53
53
  :launch, :on,
54
- :position, :size, :everying, :playing, :moving,
54
+ :position, :quantize_position,
55
+ :size,
56
+ :everying, :playing, :moving,
55
57
  :ticks_per_bar, :logger, :debug, :inspect,
56
58
  :run
57
59
 
@@ -1,3 +1,10 @@
1
1
  require_relative 'base-sequencer'
2
+
2
3
  require_relative 'base-sequencer-implementation'
4
+
5
+ require_relative 'base-sequencer-implementation-every'
6
+ require_relative 'base-sequencer-implementation-move'
7
+ require_relative 'base-sequencer-implementation-play'
8
+ require_relative 'base-sequencer-implementation-play-timed'
9
+
3
10
  require_relative 'sequencer-dsl'
@@ -30,7 +30,7 @@ module Musa
30
30
  if @is_instance
31
31
  self
32
32
  else
33
- clone(freeze: false).tap(&:_instance!).mark_as_instance!(self)
33
+ clone(freeze: false).tap(&:_instance!).mark_as_instance!(self).tap(&:restart)
34
34
  end
35
35
  end
36
36
 
@@ -40,7 +40,7 @@ module Musa
40
40
  # handle prototyping/instancing automatically.
41
41
  # If there is a @sources attribute with the eventual several sources, SeriePrototyping will handle them by
42
42
  # default.
43
- # If needed the subclasses can override this behaviour to accomodate to real subclass specificities.
43
+ # If needed the subclasses can override this behaviour to accommodate to real subclass specificities.
44
44
  #
45
45
  protected def _prototype!
46
46
  @source = @source.prototype if @source
@@ -45,6 +45,22 @@ module Musa
45
45
  end
46
46
  end
47
47
 
48
+ def to_hash
49
+ if @proxy.hash_mode?
50
+ @proxy.components.collect { |key| [key, self[key]] }.to_h
51
+ else
52
+ raise RuntimeError, 'Splitter is not based on Hash: can\'t convert to Hash'
53
+ end
54
+ end
55
+
56
+ def to_array
57
+ if @proxy.array_mode?
58
+ [].tap { |_| @proxy.components.each { |i| _[i] = self[i] } }
59
+ else
60
+ raise RuntimeError, 'Splitter is not based on Array: can\'t convert to Array'
61
+ end
62
+ end
63
+
48
64
  class BufferedProxy
49
65
  include SeriePrototyping
50
66
 
@@ -192,6 +192,8 @@ module Musa
192
192
  private_constant :Sequence
193
193
 
194
194
  class FromEvalBlockWithParameters
195
+ using Musa::Extension::DeepCopy
196
+
195
197
  include Serie
196
198
  include Musa::Extension::SmartProcBinder
197
199
 
@@ -211,8 +213,8 @@ module Musa
211
213
  end
212
214
 
213
215
  def _restart
214
- @value_parameters = @original_value_parameters.collect(&:clone)
215
- @key_parameters = @original_key_parameters.transform_values(&:clone)
216
+ @value_parameters = @original_value_parameters.clone(deep: true)
217
+ @key_parameters = @original_key_parameters.clone(deep: true)
216
218
 
217
219
  @first = true
218
220
  @value = nil
@@ -112,6 +112,10 @@ module Musa
112
112
  ProcessWith.new self, &yield_block
113
113
  end
114
114
 
115
+ def anticipate(&yield_block)
116
+ Anticipate.new self, &yield_block
117
+ end
118
+
115
119
  ###
116
120
  ### Implementation
117
121
  ###
@@ -130,7 +134,7 @@ module Musa
130
134
  @block = SmartProcBinder.new(block) if block_given?
131
135
 
132
136
  if @source.prototype?
133
- @sources = @sources.transform_values { |s| s.prototype }
137
+ @sources = @sources.transform_values { |s| s.prototype }.freeze
134
138
  else
135
139
  @sources = @sources.transform_values { |s| s.instance }
136
140
  end
@@ -168,6 +172,37 @@ module Musa
168
172
 
169
173
  private_constant :ProcessWith
170
174
 
175
+ class Anticipate
176
+ include Musa::Extension::SmartProcBinder
177
+ include Serie
178
+
179
+ attr_reader :source, :block
180
+
181
+ def initialize(serie, &block)
182
+ @source = serie
183
+ @block = block
184
+
185
+ mark_regarding! @source
186
+ end
187
+
188
+ def _restart
189
+ @source.restart
190
+ end
191
+
192
+ def _next_value
193
+ value = @source.next_value
194
+ peek_next_value = @source.peek_next_value
195
+
196
+ @block.call(value, peek_next_value)
197
+ end
198
+
199
+ def infinite?
200
+ @source.infinite?
201
+ end
202
+ end
203
+
204
+ private_constant :Anticipate
205
+
171
206
  class Switcher
172
207
  include Serie
173
208
 
@@ -185,7 +220,7 @@ module Musa
185
220
  @sources = hash_series.clone.transform_values(&get)
186
221
  end
187
222
 
188
- if get == :_prototype!
223
+ if get == :prototype!
189
224
  @sources.freeze
190
225
  end
191
226
 
@@ -236,7 +271,7 @@ module Musa
236
271
 
237
272
  _restart false
238
273
 
239
- if get == :_prototype!
274
+ if get == :prototype
240
275
  @sources.freeze
241
276
  end
242
277
 
@@ -293,7 +328,7 @@ module Musa
293
328
  @sources = hash_series.clone.transform_values(&get)
294
329
  end
295
330
 
296
- if get == :_prototype!
331
+ if get == :prototype
297
332
  @sources.freeze
298
333
  end
299
334
 
@@ -87,8 +87,6 @@ module Musa
87
87
 
88
88
  attr_reader :source
89
89
 
90
- attr_reader :points_history
91
-
92
90
  def initialize(reference, step, source, value_attribute, stops, left_open, right_open)
93
91
  @reference = reference
94
92
  @step_size = step.abs
@@ -218,13 +216,13 @@ module Musa
218
216
  if @segments.last && @segments.last[:time] == from_time
219
217
 
220
218
  @segments.last[:duration] = to_time - from_time
221
- @segments.last[:info] += "; edited on a as start"
219
+ @segments.last[:info] += '; edited on a as start'
222
220
 
223
221
  else
224
222
  @segments << { time: from_time,
225
223
  value: from_value,
226
224
  duration: to_time - from_time,
227
- info: "added on a as start" }
225
+ info: 'added on a as start' }
228
226
 
229
227
  end
230
228
 
@@ -233,7 +231,7 @@ module Musa
233
231
  value: from_value,
234
232
  duration: 0,
235
233
  stop: true,
236
- info: "added on a as end stop" }
234
+ info: 'added on a as end stop' }
237
235
  end
238
236
  else
239
237
  time_increment = to_time - from_time
@@ -268,7 +266,7 @@ module Musa
268
266
  @segments.last[:value] == value
269
267
 
270
268
  @segments.last[:duration] = step_time_increment
271
- @segments.last[:info] += "; edited on b"
269
+ @segments.last[:info] += '; edited on b'
272
270
 
273
271
  # puts "process2: editing #{@segments.last}"
274
272
 
@@ -276,7 +274,7 @@ module Musa
276
274
  @segments << v = { time: intermediate_point_time,
277
275
  value: value,
278
276
  duration: step_time_increment,
279
- info: "added on b" }
277
+ info: 'added on b' }
280
278
 
281
279
  # puts "process2: adding #{v.inspect}"
282
280
  end
@@ -297,7 +295,7 @@ module Musa
297
295
 
298
296
  private def process(time, value, last_time_value)
299
297
  if time && value
300
- raise RuntimeError, "time only can go forward" if @last_processed_time && time <= @last_processed_time
298
+ raise RuntimeError, 'time only can go forward' if @last_processed_time && time <= @last_processed_time
301
299
 
302
300
  q_value = round_quantize(value)
303
301
 
@@ -12,5 +12,4 @@ require_relative 'queue-serie'
12
12
  require_relative 'hash-or-array-serie-splitter'
13
13
 
14
14
  require_relative 'quantizer-serie'
15
- require_relative 'flattener-timed-serie'
16
- require_relative 'union-timed-series'
15
+ require_relative 'timed-serie'
@@ -0,0 +1,354 @@
1
+ require_relative '../datasets/e'
2
+
3
+ module Musa
4
+ module Series
5
+ extend self
6
+
7
+ def TIMED_UNION(*array_of_timed_series, **hash_of_timed_series)
8
+ raise ArgumentError, 'Can\'t union an array of series with a hash of series' if array_of_timed_series.any? && hash_of_timed_series.any?
9
+
10
+ if array_of_timed_series.any?
11
+ TimedUnionOfArrayOfSeries.new(array_of_timed_series)
12
+ elsif hash_of_timed_series.any?
13
+ TimedUnionOfHashOfSeries.new(hash_of_timed_series)
14
+ else
15
+ raise ArgumentError, 'Missing argument series'
16
+ end
17
+ end
18
+
19
+ class TimedUnionOfArrayOfSeries
20
+ include Serie
21
+
22
+ attr_reader :sources
23
+
24
+ def initialize(series)
25
+ @sources = if series[0].prototype?
26
+ series.collect(&:prototype).freeze
27
+ else
28
+ series.collect(&:instance)
29
+ end
30
+
31
+ _restart false
32
+
33
+ mark_regarding! series[0]
34
+ end
35
+
36
+ private def _restart(restart_sources = true)
37
+ @sources.each { |serie| serie.restart } if restart_sources
38
+ @sources_next_values = Array.new(@sources.size)
39
+
40
+ @components = nil
41
+ end
42
+
43
+ private def _next_value
44
+ sources_values = @sources_next_values.each_index.collect do |i|
45
+ @sources_next_values[i] || (@sources_next_values[i] = @sources[i].next_value)
46
+ end
47
+
48
+ @components, @hash_mode, @array_mode = infer_components(sources_values) unless @components
49
+
50
+ time = sources_values.collect { |_| _&.[](:time) }.compact.min
51
+
52
+ if time
53
+ selected_values = sources_values.collect { |_| _ if _&.[](:time) == time }
54
+
55
+ @sources_next_values.each_index do |i|
56
+ if @sources_next_values[i]&.[](:time) == time
57
+ @sources_next_values[i] = nil
58
+ end
59
+ end
60
+
61
+ result = { time: time }
62
+
63
+ @components.each do |attribute_name, components|
64
+ if @hash_mode
65
+ result[attribute_name] = {}
66
+ elsif @array_mode
67
+ result[attribute_name] = []
68
+ else # value mode
69
+ result[attribute_name] = []
70
+ end
71
+
72
+ components.each do |target_key_or_index, source_placement|
73
+ result[attribute_name][target_key_or_index] = selected_values.dig(*source_placement)
74
+ end
75
+ end
76
+
77
+ result
78
+ else
79
+ nil
80
+ end
81
+ end
82
+
83
+ def infinite?
84
+ !!@sources.find(&:infinite?)
85
+ end
86
+ end
87
+
88
+ private def infer_components(sources_values)
89
+ other_attributes = Set[]
90
+
91
+ sources_values.each do |source_value|
92
+ (source_value.keys - [:time, :value]).each { |_| other_attributes << _ }
93
+ end
94
+
95
+ components = {}
96
+ components[:value] = {}
97
+
98
+ hash_mode = array_mode = nil
99
+
100
+ other_attributes.each do |attribute_name|
101
+ components[attribute_name] = {}
102
+ end
103
+
104
+ target_index = 0
105
+
106
+ sources_values.each_with_index do |source_value, i|
107
+ case source_value[:value]
108
+ when Hash
109
+ hash_mode = true
110
+
111
+ source_value[:value].keys.each do |key|
112
+ raise RuntimeError, "Value: key #{key} already used" unless components[:value][key].nil?
113
+
114
+ components[:value][key] = [i, :value, key]
115
+
116
+ other_attributes.each do |attribute_name|
117
+ raise RuntimeError, "Attribute #{attribute_name}: key #{key} already used" unless components[attribute_name][key].nil?
118
+ components[attribute_name][key] = [i, attribute_name, key]
119
+ end
120
+ end
121
+ when Array
122
+ array_mode = true
123
+
124
+ (0..source_value[:value].size - 1).each do |index|
125
+ components[:value][target_index] = [i, :value, index]
126
+
127
+ other_attributes.each do |attribute_name|
128
+ components[attribute_name][target_index] = [i, attribute_name, index]
129
+ end
130
+
131
+ target_index += 1
132
+ end
133
+ else
134
+ components[:value][target_index] = [i, :value]
135
+
136
+ other_attributes.each do |attribute_name|
137
+ components[attribute_name][target_index] = [i, attribute_name]
138
+ end
139
+
140
+ target_index += 1
141
+ end
142
+ end
143
+
144
+ raise RuntimeError, "source series values are of incompatible type (can't combine Hash and Array values)" if array_mode && hash_mode
145
+
146
+ return components, hash_mode, array_mode
147
+ end
148
+
149
+ private_constant :TimedUnionOfArrayOfSeries
150
+
151
+ class TimedUnionOfHashOfSeries
152
+ include Serie
153
+
154
+ attr_reader :sources
155
+
156
+ def initialize(series)
157
+ @components = series.keys
158
+
159
+ @sources = if series.values.first.prototype?
160
+ series.transform_values(&:prototype).freeze
161
+ else
162
+ series.transform_values(&:instance)
163
+ end
164
+
165
+ _restart false
166
+
167
+ mark_regarding! series.values.first
168
+ end
169
+
170
+ private def _restart(restart_sources = true)
171
+ @sources.each_value { |serie| serie.restart } if restart_sources
172
+ @sources_next_values = @components.collect { |k| [k, nil] }.to_h
173
+ @other_attributes = nil
174
+ end
175
+
176
+ private def _next_value
177
+ sources_values = @sources_next_values.collect do |key, _|
178
+ [key, @sources_next_values[key] || (@sources_next_values[key] = @sources[key].next_value)]
179
+ end.to_h
180
+
181
+ @other_attributes = infer_other_attributes(sources_values) unless @other_attributes
182
+
183
+ time = sources_values.values.collect { |_| _&.[](:time) }.compact.min
184
+
185
+ if time
186
+ selected_values = sources_values.transform_values { |_| _ if _&.[](:time) == time }
187
+
188
+ @sources_next_values.each_key do |key|
189
+ if @sources_next_values[key]&.[](:time) == time
190
+ @sources_next_values[key] = nil
191
+ end
192
+ end
193
+
194
+ result = { time: time, value: {} }
195
+
196
+ @other_attributes.each do |attribute_name|
197
+ result[attribute_name] = {}
198
+ end
199
+
200
+ @components.each do |component|
201
+ result[:value][component] = selected_values[component]&.[](:value)
202
+
203
+ @other_attributes.each do |attribute_name|
204
+ result[attribute_name][component] = selected_values[component]&.[](attribute_name)
205
+ end
206
+ end
207
+
208
+ result
209
+ else
210
+ nil
211
+ end
212
+ end
213
+
214
+ def infinite?
215
+ !!@sources.find(&:infinite?)
216
+ end
217
+
218
+ private def infer_other_attributes(sources_values)
219
+ other_attributes = Set[]
220
+
221
+ sources_values.each_value do |source_value|
222
+ (source_value.keys - [:time, :value]).each do |attribute_name|
223
+ other_attributes << attribute_name
224
+ end
225
+ end
226
+
227
+ other_attributes
228
+ end
229
+ end
230
+
231
+ private_constant :TimedUnionOfHashOfSeries
232
+
233
+ module SerieOperations
234
+ def flatten_timed
235
+ TimedFlattener.new(self)
236
+ end
237
+
238
+ def compact_timed
239
+ TimedCompacter.new(self)
240
+ end
241
+
242
+ def union_timed(*other_timed_series, key: nil, **other_key_timed_series)
243
+ if key && other_key_timed_series.any?
244
+ Series::TIMED_UNION(key => self, **other_key_timed_series)
245
+
246
+ elsif other_timed_series.any? && other_key_timed_series.empty?
247
+ Series::TIMED_UNION(self, *other_timed_series)
248
+
249
+ else
250
+ raise ArgumentError, 'Can\'t union an array of series with a hash of series'
251
+ end
252
+ end
253
+
254
+ class TimedFlattener
255
+ include Serie
256
+
257
+ attr_reader :source
258
+
259
+ def initialize(serie)
260
+ @source = serie
261
+ mark_regarding! @source
262
+ end
263
+
264
+ def _restart
265
+ @source.restart
266
+ end
267
+
268
+ def _next_value
269
+ source_value = @source.next_value
270
+
271
+ if !source_value.nil?
272
+ time = source_value[:time]
273
+ source_value_value = source_value[:value]
274
+
275
+ source_value_extra = (source_value.keys - [:time, :value]).collect do |attribute_name|
276
+ [attribute_name, source_value[attribute_name]]
277
+ end.to_h
278
+
279
+ case source_value_value
280
+ when Hash
281
+ result = {}
282
+ source_value_value.each_pair do |key, value|
283
+ result[key] = { time: time, value: value }.extend(Musa::Datasets::AbsTimed)
284
+
285
+ source_value_extra.each do |attribute_name, attribute_value|
286
+ result[key][attribute_name] = attribute_value[key]
287
+ end
288
+ end
289
+
290
+ when Array
291
+ result = []
292
+ source_value_value.each_index do |index|
293
+ result[index] = { time: time, value: source_value_value[index] }.extend(Musa::Datasets::AbsTimed)
294
+
295
+ source_value_extra.each do |attribute_name, attribute_value|
296
+ result[index][attribute_name] = attribute_value[index]
297
+ end
298
+ end
299
+ else
300
+ result = source_value.clone.extend(Musa::Datasets::AbsTimed)
301
+ end
302
+
303
+ result
304
+ else
305
+ nil
306
+ end
307
+ end
308
+
309
+ def infinite?
310
+ @source.infinite?
311
+ end
312
+ end
313
+
314
+ private_constant :TimedFlattener
315
+ end
316
+
317
+ class TimedCompacter
318
+ include Serie
319
+
320
+ attr_reader :source
321
+
322
+ def initialize(serie)
323
+ @source = serie
324
+ mark_regarding! @source
325
+ end
326
+
327
+ def _restart
328
+ @source.restart
329
+ end
330
+
331
+ def _next_value
332
+ while (source_value = @source.next_value) && skip_value?(source_value[:value]); end
333
+ source_value
334
+ end
335
+
336
+ def infinite?
337
+ @source.infinite?
338
+ end
339
+
340
+ private def skip_value?(timed_value)
341
+ case timed_value
342
+ when Hash
343
+ timed_value.all? { |_, v| v.nil? }
344
+ when Array
345
+ timed_value.all?(&:nil?)
346
+ else
347
+ timed_value.nil?
348
+ end
349
+ end
350
+ end
351
+
352
+ private_constant :TimedCompacter
353
+ end
354
+ end
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'musa-dsl'
3
- s.version = '0.22.4'
4
- s.date = '2020-11-14'
3
+ s.version = '0.22.5'
4
+ s.date = '2020-11-18'
5
5
  s.summary = 'A simple Ruby DSL for making complex music'
6
6
  s.description = 'Musa-DSL: A Ruby DSL for algorithmic music composition, device language neutral (MIDI, OSC, etc)'
7
7
  s.authors = ['Javier Sánchez Yeste']
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: musa-dsl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.22.4
4
+ version: 0.22.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Javier Sánchez Yeste
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-14 00:00:00.000000000 Z
11
+ date: 2020-11-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: citrus
@@ -179,7 +179,6 @@ files:
179
179
  - lib/musa-dsl/series.rb
180
180
  - lib/musa-dsl/series/array-to-serie.rb
181
181
  - lib/musa-dsl/series/base-series.rb
182
- - lib/musa-dsl/series/flattener-timed-serie.rb
183
182
  - lib/musa-dsl/series/hash-or-array-serie-splitter.rb
184
183
  - lib/musa-dsl/series/holder-serie.rb
185
184
  - lib/musa-dsl/series/main-serie-constructors.rb
@@ -188,7 +187,7 @@ files:
188
187
  - lib/musa-dsl/series/quantizer-serie.rb
189
188
  - lib/musa-dsl/series/queue-serie.rb
190
189
  - lib/musa-dsl/series/series.rb
191
- - lib/musa-dsl/series/union-timed-series.rb
190
+ - lib/musa-dsl/series/timed-serie.rb
192
191
  - lib/musa-dsl/transcription.rb
193
192
  - lib/musa-dsl/transcription/from-gdv-to-midi.rb
194
193
  - lib/musa-dsl/transcription/from-gdv-to-musicxml.rb
@@ -1,61 +0,0 @@
1
- require_relative '../datasets/e'
2
-
3
- module Musa
4
- module Series
5
-
6
- module SerieOperations
7
- def flatten_timed
8
- TimedFlattener.new(self)
9
- end
10
-
11
- class TimedFlattener
12
- include Serie
13
-
14
- attr_reader :source
15
-
16
- def initialize(serie)
17
- @source = serie
18
- mark_regarding! @source
19
- end
20
-
21
- def _restart
22
- @source.restart
23
- end
24
-
25
- def _next_value
26
- source_value = @source.next_value
27
-
28
- if !source_value.nil?
29
- time = source_value[:time]
30
- source_value_value = source_value[:value]
31
-
32
- case source_value_value
33
- when Hash
34
- result = {}
35
- source_value_value.each_pair do |key, value|
36
- result[key] = { time: time, value: value }.extend(Musa::Datasets::AbsTimed)
37
- end
38
- when Array
39
- result = []
40
- source_value_value.each_index do |index|
41
- result[index] = { time: time, value: source_value_value[index] }.extend(Musa::Datasets::AbsTimed)
42
- end
43
- else
44
- raise RuntimeError, "Don't know how to handle #{source_value_value}"
45
- end
46
-
47
- result
48
- else
49
- nil
50
- end
51
- end
52
-
53
- def infinite?
54
- @source.infinite?
55
- end
56
- end
57
-
58
- private_constant :TimedFlattener
59
- end
60
- end
61
- end
@@ -1,109 +0,0 @@
1
- require_relative '../datasets/e'
2
-
3
- module Musa
4
- module Series
5
-
6
- extend self
7
-
8
- def TIMED_UNION(*timed_series)
9
- TimedUnion.new(timed_series)
10
- end
11
-
12
- class TimedUnion
13
- include Serie
14
-
15
- attr_reader :sources
16
-
17
- def initialize(series)
18
- @sources = if series[0].prototype?
19
- series.collect(&:prototype).freeze
20
- else
21
- series.collect(&:instance)
22
- end
23
-
24
- _restart false
25
-
26
- mark_regarding! series[0]
27
- end
28
-
29
- private def _restart(restart_sources = true)
30
- @sources.each { |serie| serie.restart } if restart_sources
31
- @sources_next_values = Array.new(@sources.size)
32
-
33
- @components = nil
34
- @hash_mode = @array_mode = nil
35
- end
36
-
37
- private def _next_value
38
- sources_values = @sources_next_values.each_index.collect do |i|
39
- @sources_next_values[i] || (@sources_next_values[i] = @sources[i].next_value)
40
- end
41
-
42
- infer_components(sources_values) if !@components
43
-
44
- time = sources_values.collect { |_| _&.[](:time) }.compact.min
45
-
46
- if time
47
- selected_values = sources_values.collect { |_| _ if _&.[](:time) == time }
48
-
49
- @sources_next_values.each_index do |i|
50
- if @sources_next_values[i]&.[](:time) == time
51
- @sources_next_values[i] = nil
52
- end
53
- end
54
-
55
- if @hash_mode
56
- result = {}
57
- elsif @array_mode
58
- result = []
59
- else # value mode
60
- result = []
61
- end
62
-
63
- @components.each do |target_key_or_index, source_placement|
64
- result[target_key_or_index] = selected_values.dig(*source_placement)
65
- end
66
-
67
- { time: time,
68
- value: result }
69
- else
70
- nil
71
- end
72
- end
73
-
74
- def infinite?
75
- !!@sources.find(&:infinite?)
76
- end
77
- end
78
-
79
- private def infer_components(sources_values)
80
- @components = {}
81
- target_index = 0
82
-
83
- sources_values.each_with_index do |source_value, i|
84
- case source_value[:value]
85
- when Hash
86
- @hash_mode = true
87
-
88
- source_value[:value].keys.each do |key|
89
- @components[key] = [i, :value, key]
90
- end
91
- when Array
92
- @array_mode = true
93
-
94
- (0..source_value[:value].size - 1).each do |index|
95
- @components[target_index] = [i, :value, index]
96
- target_index += 1
97
- end
98
- else
99
- @components[target_index] = [i, :value]
100
- target_index += 1
101
- end
102
- end
103
-
104
- raise RuntimeError, "source series values are of incompatible type (can't combine Hash and Array values)" if @array_mode && @hash_mode
105
- end
106
-
107
- private_constant :TimedUnion
108
- end
109
- end