musa-dsl 0.21.0 → 0.22.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +0 -1
  3. data/lib/musa-dsl.rb +1 -1
  4. data/lib/musa-dsl/core-ext.rb +1 -0
  5. data/lib/musa-dsl/core-ext/arrayfy.rb +9 -9
  6. data/lib/musa-dsl/core-ext/hashify.rb +42 -0
  7. data/lib/musa-dsl/core-ext/inspect-nice.rb +6 -1
  8. data/lib/musa-dsl/datasets/e.rb +22 -5
  9. data/lib/musa-dsl/datasets/gdv.rb +0 -1
  10. data/lib/musa-dsl/datasets/p.rb +28 -37
  11. data/lib/musa-dsl/datasets/pdv.rb +0 -1
  12. data/lib/musa-dsl/datasets/ps.rb +10 -78
  13. data/lib/musa-dsl/generative/markov.rb +1 -1
  14. data/lib/musa-dsl/logger/logger.rb +4 -3
  15. data/lib/musa-dsl/matrix/matrix.rb +0 -57
  16. data/lib/musa-dsl/midi/midi-voices.rb +4 -0
  17. data/lib/musa-dsl/neumas/string-to-neumas.rb +1 -0
  18. data/lib/musa-dsl/repl/repl.rb +30 -11
  19. data/lib/musa-dsl/sequencer/base-sequencer-implementation-every.rb +87 -0
  20. data/lib/musa-dsl/sequencer/base-sequencer-implementation-move.rb +439 -0
  21. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +3 -3
  22. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-timed.rb +210 -0
  23. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb +178 -0
  24. data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +150 -595
  25. data/lib/musa-dsl/sequencer/base-sequencer-public.rb +58 -5
  26. data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +5 -9
  27. data/lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb +1 -5
  28. data/lib/musa-dsl/sequencer/sequencer-dsl.rb +8 -0
  29. data/lib/musa-dsl/series/base-series.rb +43 -78
  30. data/lib/musa-dsl/series/flattener-timed-serie.rb +61 -0
  31. data/lib/musa-dsl/series/hash-or-array-serie-splitter.rb +95 -0
  32. data/lib/musa-dsl/series/holder-serie.rb +1 -1
  33. data/lib/musa-dsl/series/main-serie-constructors.rb +29 -83
  34. data/lib/musa-dsl/series/main-serie-operations.rb +60 -215
  35. data/lib/musa-dsl/series/proxy-serie.rb +1 -1
  36. data/lib/musa-dsl/series/quantizer-serie.rb +546 -0
  37. data/lib/musa-dsl/series/queue-serie.rb +1 -1
  38. data/lib/musa-dsl/series/series.rb +7 -2
  39. data/lib/musa-dsl/transport/input-midi-clock.rb +19 -12
  40. data/lib/musa-dsl/transport/transport.rb +25 -12
  41. data/musa-dsl.gemspec +2 -2
  42. metadata +11 -5
  43. data/lib/musa-dsl/sequencer/base-sequencer-implementation-control.rb +0 -216
  44. data/lib/musa-dsl/series/hash-serie-splitter.rb +0 -196
@@ -17,7 +17,9 @@ module Musa
17
17
  attr_reader :everying, :playing, :moving
18
18
  attr_reader :logger
19
19
 
20
- def initialize(beats_per_bar = nil, ticks_per_beat = nil, logger: nil, do_log: nil, do_error_log: nil, log_position_format: nil)
20
+ def initialize(beats_per_bar = nil, ticks_per_beat = nil,
21
+ logger: nil,
22
+ do_log: nil, do_error_log: nil, log_position_format: nil)
21
23
 
22
24
  raise ArgumentError,
23
25
  "'beats_per_bar' and 'ticks_per_beat' parameters should be both nil or both have values" \
@@ -175,10 +177,18 @@ module Musa
175
177
  control
176
178
  end
177
179
 
178
- def play(serie, mode: nil, parameter: nil, after: nil, context: nil, **mode_args, &block)
180
+ def play(serie,
181
+ mode: nil,
182
+ parameter: nil,
183
+ after_bars: nil,
184
+ after: nil,
185
+ context: nil,
186
+ **mode_args,
187
+ &block)
188
+
179
189
  mode ||= :wait
180
190
 
181
- control = PlayControl.new @event_handlers.last, after: after
191
+ control = PlayControl.new @event_handlers.last, after_bars: after_bars, after: after
182
192
  @event_handlers.push control
183
193
 
184
194
  _play serie.instance, control, context, mode: mode, parameter: parameter, **mode_args, &block
@@ -197,14 +207,57 @@ module Musa
197
207
  def continuation_play(parameters)
198
208
  _play parameters[:serie],
199
209
  parameters[:control],
200
- parameters[:nl_context],
210
+ parameters[:neumalang_context],
201
211
  mode: parameters[:mode],
202
212
  decoder: parameters[:decoder],
203
213
  __play_eval: parameters[:play_eval],
204
214
  **parameters[:mode_args]
205
215
  end
206
216
 
207
- def every(interval, duration: nil, till: nil, condition: nil, on_stop: nil, after_bars: nil, after: nil, &block)
217
+ def play_timed(timed_serie,
218
+ reference: nil,
219
+ step: nil,
220
+ right_open: nil,
221
+ on_stop: nil,
222
+ after_bars: nil, after: nil,
223
+ &block)
224
+
225
+ control = PlayTimedControl.new(@event_handlers.last,
226
+ on_stop: on_stop, after_bars: after_bars, after: after)
227
+
228
+ control.on_stop do
229
+ control.do_after.each do |do_after|
230
+ _numeric_at position + do_after[:bars], control, &do_after[:block]
231
+ end
232
+ end
233
+
234
+ @event_handlers.push control
235
+
236
+ _play_timed(timed_serie.instance,
237
+ control,
238
+ reference: reference,
239
+ step: step,
240
+ right_open: right_open,
241
+ &block)
242
+
243
+ @event_handlers.pop
244
+
245
+ @playing << control
246
+
247
+ control.after do
248
+ @playing.delete control
249
+ end
250
+
251
+ control
252
+ end
253
+
254
+ def every(interval,
255
+ duration: nil, till: nil,
256
+ condition: nil,
257
+ on_stop: nil,
258
+ after_bars: nil, after: nil,
259
+ &block)
260
+
208
261
  # nil interval means 'only once'
209
262
  interval = interval.rationalize unless interval.nil?
210
263
 
@@ -41,27 +41,23 @@ module Musa
41
41
  @position = @position_mutex.synchronize { 1r - @tick_duration }
42
42
  end
43
43
 
44
- def _check_position(position)
44
+ def _quantize_position(position, warn: true)
45
45
  ticks_position = position / @tick_duration
46
46
 
47
47
  if ticks_position.round != ticks_position
48
48
  original_position = position
49
49
  position = ticks_position.round * @tick_duration
50
50
 
51
- if @do_log
52
- _log "BaseSequencer._numeric_at: warning: rounding "\
53
- "position #{position} (#{original_position.to_f.round(5)}) "\
54
- "to tick precision: #{position} (#{position.to_f.round(5)})"
51
+ if warn
52
+ @logger.warn('BaseSequencer') { "_check_position: rounding "\
53
+ "position #{original_position} (#{original_position.to_f.round(5)}) "\
54
+ "to tick precision: #{position} (#{position.to_f.round(5)})" }
55
55
  end
56
56
  end
57
57
 
58
58
  position
59
59
  end
60
60
 
61
- def _quantize(position)
62
- (position / @tick_duration).round * @tick_duration
63
- end
64
-
65
61
  def _hold_public_ticks
66
62
  @hold_public_ticks = true
67
63
  end
@@ -62,11 +62,7 @@ module Musa
62
62
  @position = nil
63
63
  end
64
64
 
65
- def _check_position(position)
66
- position
67
- end
68
-
69
- def _quantize(position)
65
+ def _quantize_position(position, warn: false)
70
66
  position
71
67
  end
72
68
  end
@@ -89,6 +89,14 @@ module Musa
89
89
  end
90
90
  end
91
91
 
92
+ def play_timed(*value_parameters, **key_parameters, &block)
93
+ block ||= proc {}
94
+
95
+ @sequencer.play_timed *value_parameters, **key_parameters do |*value_args, **key_args|
96
+ with *value_args, **key_args, &block
97
+ end
98
+ end
99
+
92
100
  def every(*value_parameters, **key_parameters, &block)
93
101
  block ||= proc {}
94
102
 
@@ -18,57 +18,73 @@ module Musa
18
18
 
19
19
  def prototype
20
20
  if @is_instance
21
- @instance_of || (@instance_of = clone.tap(&:_prototype).mark_as_prototype!)
21
+ @instance_of || (@instance_of = self.clone.tap(&:_prototype!).mark_as_prototype!)
22
22
  else
23
23
  self
24
24
  end
25
25
  end
26
26
 
27
- def _prototype
28
- nil
29
- end
30
-
31
27
  alias_method :p, :prototype
32
28
 
33
- def mark_as_prototype!
34
- @is_instance = nil
35
- freeze
29
+ def instance
30
+ if @is_instance
31
+ self
32
+ else
33
+ clone(freeze: false).tap(&:_instance!).mark_as_instance!(self)
34
+ end
36
35
  end
37
36
 
38
- protected :_prototype, :mark_as_prototype!
37
+ alias_method :i, :instance
39
38
 
40
- def mark_regarding!(source)
41
- if source.prototype?
42
- mark_as_prototype!
43
- else
44
- mark_as_instance!
39
+ # By default, if there is a @source attribute that contains the source of the serie, SeriePrototyping will
40
+ # handle prototyping/instancing automatically.
41
+ # If there is a @sources attribute with the eventual several sources, SeriePrototyping will handle them by
42
+ # default.
43
+ # If needed the subclasses can override this behaviour to accomodate to real subclass specificities.
44
+ #
45
+ protected def _prototype!
46
+ @source = @source.prototype if @source
47
+
48
+ if @sources
49
+ if @sources.is_a?(Array)
50
+ @sources = @sources.collect(&:prototype).freeze
51
+ elsif @sources.is_a?(Hash)
52
+ @sources = @sources.transform_values(&:prototype).freeze
53
+ end
45
54
  end
46
55
  end
47
56
 
48
- protected :mark_regarding!
57
+ protected def _instance!
58
+ @source = @source.instance if @source
49
59
 
50
- def instance
51
- if @is_instance
52
- self
53
- else
54
- clone(freeze: false).tap(&:_instance).mark_as_instance!(self)
60
+ if @sources
61
+ if @sources.is_a?(Array)
62
+ @sources = @sources.collect(&:instance)
63
+ elsif @sources.is_a?(Hash)
64
+ @sources = @sources.transform_values(&:instance)
65
+ end
55
66
  end
56
67
  end
57
68
 
58
- alias_method :i, :instance
69
+ protected def mark_regarding!(source)
70
+ if source.prototype?
71
+ mark_as_prototype!
72
+ else
73
+ mark_as_instance!
74
+ end
75
+ end
59
76
 
60
- def _instance
61
- nil
77
+ protected def mark_as_prototype!
78
+ @is_instance = nil
79
+ freeze
62
80
  end
63
81
 
64
- def mark_as_instance!(prototype = nil)
82
+ protected def mark_as_instance!(prototype = nil)
65
83
  @instance_of = prototype
66
84
  @is_instance = true
67
85
  self
68
86
  end
69
87
 
70
- protected :_instance, :mark_as_instance!
71
-
72
88
  class PrototypingSerieError < RuntimeError
73
89
  def initialize(message = nil)
74
90
  message ||= 'This serie is a prototype serie: cannot be consumed. To consume the serie use an instance serie via .instance method'
@@ -106,8 +122,6 @@ module Musa
106
122
  end
107
123
  end
108
124
 
109
- propagate_value @_current_value
110
-
111
125
  @_current_value
112
126
  end
113
127
 
@@ -180,15 +194,7 @@ module Musa
180
194
 
181
195
  private_constant :Nodificator
182
196
 
183
- protected
184
-
185
- def propagate_value(value)
186
- @_slaves.each { |s| s.push_next_value value } if @_slaves
187
- end
188
-
189
- private
190
-
191
- def process_for_to_a(value)
197
+ private def process_for_to_a(value)
192
198
  case value
193
199
  when Serie
194
200
  value.to_a(recursive: true, restart: false, duplicate: false)
@@ -203,46 +209,5 @@ module Musa
203
209
  end
204
210
  end
205
211
  end
206
-
207
- class Slave
208
- include Serie
209
-
210
- attr_reader :master
211
-
212
- def initialize(master)
213
- @master = master
214
- @next_value = []
215
- end
216
-
217
- def _restart
218
- throw OperationNotAllowedError, "SlaveSerie #{self}: slave series cannot be restarted"
219
- end
220
-
221
- def next_value
222
- value = @next_value.shift
223
-
224
- raise "Warning: slave serie #{self} has lost sync with his master serie #{@master}" if value.nil? && !@master.peek_next_value.nil?
225
-
226
- propagate_value value
227
-
228
- value
229
- end
230
-
231
- def peek_next_value
232
- value = @next_value.first
233
-
234
- raise "Warning: slave serie #{self} has lost sync with his master serie #{@master}" if value.nil? && !@master.peek_next_value.nil?
235
-
236
- value
237
- end
238
-
239
- def infinite?
240
- @master.infinite?
241
- end
242
-
243
- def push_next_value(value)
244
- @next_value << value
245
- end
246
- end
247
212
  end
248
213
  end
@@ -0,0 +1,61 @@
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(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(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
@@ -0,0 +1,95 @@
1
+ module Musa
2
+ module Series
3
+ module SerieOperations
4
+ def split
5
+ Splitter.new(Splitter::BufferedProxy.new(self))
6
+ end
7
+
8
+ class Splitter
9
+ def initialize(proxy)
10
+ @proxy = proxy
11
+ @series = {}
12
+ end
13
+
14
+ def [](key_or_index)
15
+ if @series.has_key?(key_or_index)
16
+ @series[key_or_index]
17
+ else
18
+ @series[key_or_index] = Split.new(@proxy, key_or_index)
19
+ end
20
+ end
21
+
22
+ class BufferedProxy
23
+ include SeriePrototyping
24
+
25
+ def initialize(hash_or_array_serie)
26
+ @source = hash_or_array_serie
27
+ restart restart_source: false
28
+
29
+ mark_regarding! @source
30
+ end
31
+
32
+ protected def _instance!
33
+ super
34
+ restart
35
+ end
36
+
37
+ def restart(restart_source: true)
38
+ @source.restart if restart_source
39
+ @values = nil
40
+ end
41
+
42
+ def next_value(key_or_index)
43
+ if @values.nil? || @values[key_or_index].nil? || @values[key_or_index].empty?
44
+ hash_or_array_value = @source.next_value
45
+
46
+ case hash_or_array_value
47
+ when Hash
48
+ @values ||= {}
49
+ hash_or_array_value.each do |k, v|
50
+ @values[k] ||= []
51
+ @values[k] << v
52
+ end
53
+ when Array
54
+ @values ||= []
55
+ hash_or_array_value.each_index do |i|
56
+ @values[i] ||= []
57
+ @values[i] << hash_or_array_value[i]
58
+ end
59
+ end
60
+ end
61
+
62
+ if @values && !@values[key_or_index].nil?
63
+ @values[key_or_index].shift
64
+ else
65
+ nil
66
+ end
67
+ end
68
+ end
69
+
70
+ class Split
71
+ include Serie
72
+
73
+ def initialize(proxy, key_or_index)
74
+ @source = proxy
75
+ @key_or_index = key_or_index
76
+
77
+ mark_regarding! @source
78
+ end
79
+
80
+ def _restart
81
+ @source.restart
82
+ end
83
+
84
+ def _next_value
85
+ @source.next_value(@key_or_index)
86
+ end
87
+ end
88
+
89
+ private_constant :Split
90
+ end
91
+
92
+ private_constant :Splitter
93
+ end
94
+ end
95
+ end