musa-dsl 0.22.3 → 0.23.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -1
  3. data/lib/musa-dsl.rb +14 -8
  4. data/lib/musa-dsl/core-ext/deep-copy.rb +12 -1
  5. data/lib/musa-dsl/core-ext/inspect-nice.rb +1 -2
  6. data/lib/musa-dsl/core-ext/smart-proc-binder.rb +13 -11
  7. data/lib/musa-dsl/datasets/p.rb +41 -16
  8. data/lib/musa-dsl/datasets/score/to-mxml/process-pdv.rb +14 -12
  9. data/lib/musa-dsl/datasets/score/to-mxml/process-ps.rb +32 -6
  10. data/lib/musa-dsl/datasets/score/to-mxml/to-mxml.rb +24 -10
  11. data/lib/musa-dsl/generative/backboner.rb +6 -11
  12. data/lib/musa-dsl/generative/generative-grammar.rb +1 -3
  13. data/lib/musa-dsl/generative/markov.rb +10 -6
  14. data/lib/musa-dsl/logger/logger.rb +6 -1
  15. data/lib/musa-dsl/matrix/matrix.rb +9 -7
  16. data/lib/musa-dsl/midi/midi-voices.rb +8 -7
  17. data/lib/musa-dsl/music/scales.rb +1 -1
  18. data/lib/musa-dsl/neumalang/neumalang.rb +1 -1
  19. data/lib/musa-dsl/neumas/array-to-neumas.rb +1 -1
  20. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +9 -4
  21. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-timed.rb +30 -129
  22. data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +10 -24
  23. data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +9 -9
  24. data/lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb +3 -5
  25. data/lib/musa-dsl/sequencer/base-sequencer.rb +14 -23
  26. data/lib/musa-dsl/sequencer/sequencer-dsl.rb +9 -7
  27. data/lib/musa-dsl/sequencer/sequencer.rb +7 -0
  28. data/lib/musa-dsl/series/base-series.rb +293 -144
  29. data/lib/musa-dsl/series/buffer-serie.rb +237 -0
  30. data/lib/musa-dsl/series/hash-or-array-serie-splitter.rb +136 -105
  31. data/lib/musa-dsl/series/main-serie-constructors.rb +251 -156
  32. data/lib/musa-dsl/series/main-serie-operations.rb +308 -303
  33. data/lib/musa-dsl/series/proxy-serie.rb +21 -41
  34. data/lib/musa-dsl/series/quantizer-serie.rb +44 -46
  35. data/lib/musa-dsl/series/queue-serie.rb +39 -43
  36. data/lib/musa-dsl/series/series-composer.rb +149 -0
  37. data/lib/musa-dsl/series/series.rb +6 -3
  38. data/lib/musa-dsl/series/timed-serie.rb +343 -0
  39. data/musa-dsl.gemspec +13 -3
  40. metadata +10 -11
  41. data/lib/musa-dsl/series/flattener-timed-serie.rb +0 -61
  42. data/lib/musa-dsl/series/holder-serie.rb +0 -87
  43. data/lib/musa-dsl/series/union-timed-series.rb +0 -109
@@ -1,69 +1,49 @@
1
- module Musa
2
- module Series
3
- # TODO: adapt to series prototyping
1
+ require_relative 'base-series'
4
2
 
3
+ module Musa
4
+ module Series::Constructors
5
5
  def PROXY(serie = nil)
6
6
  ProxySerie.new(serie)
7
7
  end
8
8
 
9
9
  class ProxySerie
10
- include Serie
11
-
12
- attr_reader :target
10
+ include Series::Serie.with(source: true)
13
11
 
14
12
  def initialize(serie)
15
- @target = serie.instance if serie
16
- mark_as_instance!
17
- end
18
-
19
- def target=(target)
20
- @target = target.instance
21
- end
22
-
23
- def _prototype!
24
- raise PrototypingSerieError, 'Cannot get prototype of a proxy serie'
13
+ self.source = serie
14
+ init
25
15
  end
26
16
 
27
- def restart
28
- @target.restart if @target
17
+ private def _restart
18
+ @source.restart if @source
29
19
  end
30
20
 
31
- def current_value
32
- @target.current_value if @target
33
- end
34
-
35
- def next_value
36
- @target.next_value if @target
37
- end
38
-
39
- def peek_next_value
40
- @target.peek_next_value if @target
21
+ private def _next_value
22
+ @source.next_value if @source
41
23
  end
42
24
 
43
25
  def infinite?
44
- @target.infinite? if @target
26
+ @source.infinite? if @source
45
27
  end
46
28
 
47
- private
48
-
49
- def method_missing(method_name, *args, **key_args, &block)
50
- if @target && @target.respond_to?(method_name)
51
- @target.send method_name, *args, **key_args, &block
29
+ private def method_missing(method_name, *args, **key_args, &block)
30
+ if @source && @source.respond_to?(method_name)
31
+ @source.send method_name, *args, **key_args, &block
52
32
  else
53
33
  super
54
34
  end
55
35
  end
56
36
 
57
- def respond_to_missing?(method_name, include_private)
58
- @target && @target.respond_to?(method_name, include_private) # || super
37
+ private def respond_to_missing?(method_name, include_private)
38
+ @source && @source.respond_to?(method_name, include_private) # || super
59
39
  end
60
40
  end
41
+ end
61
42
 
62
- module SerieOperations
63
- # TODO add test case
64
- def proxied
65
- Series::ProxySerie.new self
66
- end
43
+ module Series::Operations
44
+ # TODO add test case
45
+ def proxy
46
+ Series::ProxySerie.new(self)
67
47
  end
68
48
  end
69
49
  end
@@ -1,34 +1,32 @@
1
1
  require_relative '../datasets/e'
2
2
  require_relative '../core-ext/inspect-nice'
3
3
 
4
+ require_relative 'base-series'
5
+
4
6
  # TODO remove debugging puts, intermediate hash comments on :info and InspectNice
5
7
  using Musa::Extension::InspectNice
6
8
 
7
9
  module Musa
8
- module Series
9
- module SerieOperations
10
- def quantize(reference: nil, step: nil,
11
- value_attribute: nil,
12
- stops: nil,
13
- predictive: nil,
14
- left_open: nil,
15
- right_open: nil)
16
-
17
- Series.QUANTIZE(self,
18
- reference: reference,
19
- step: step,
20
- value_attribute: value_attribute,
21
- stops: stops,
22
- predictive: predictive,
23
- left_open: left_open,
24
- right_open: right_open)
25
- end
10
+ module Series::Operations
11
+ def quantize(reference: nil, step: nil,
12
+ value_attribute: nil,
13
+ stops: nil,
14
+ predictive: nil,
15
+ left_open: nil,
16
+ right_open: nil)
17
+
18
+ Series.QUANTIZE(self,
19
+ reference: reference,
20
+ step: step,
21
+ value_attribute: value_attribute,
22
+ stops: stops,
23
+ predictive: predictive,
24
+ left_open: left_open,
25
+ right_open: right_open)
26
26
  end
27
27
  end
28
28
 
29
- module Series
30
- extend self
31
-
29
+ module Series::Constructors
32
30
  def QUANTIZE(time_value_serie,
33
31
  reference: nil, step: nil,
34
32
  value_attribute: nil,
@@ -82,30 +80,27 @@ module Musa
82
80
  private_constant :QuantizerTools
83
81
 
84
82
  class RawQuantizer
85
- include Serie
83
+ include Series::Serie.with(source: true)
86
84
  include QuantizerTools
87
85
 
88
86
  attr_reader :source
89
87
 
90
- attr_reader :points_history
91
-
92
88
  def initialize(reference, step, source, value_attribute, stops, left_open, right_open)
89
+ self.source = source
90
+
93
91
  @reference = reference
94
92
  @step_size = step.abs
95
93
 
96
- @source = source
97
94
  @value_attribute = value_attribute
98
95
 
99
96
  @stops = stops
100
97
  @left_open = left_open
101
98
  @right_open = right_open
102
99
 
103
- _restart false
104
-
105
- mark_regarding! source
100
+ init
106
101
  end
107
102
 
108
- def _restart(restart_sources = true)
103
+ private def _init
109
104
  @last_processed_q_value = nil
110
105
  @last_processed_time = nil
111
106
 
@@ -113,11 +108,13 @@ module Musa
113
108
 
114
109
  @points = []
115
110
  @segments = []
111
+ end
116
112
 
117
- @source.restart if restart_sources
113
+ private def _restart
114
+ @source.restart
118
115
  end
119
116
 
120
- def _next_value
117
+ private def _next_value
121
118
  if @stops
122
119
  i = 2
123
120
 
@@ -218,13 +215,13 @@ module Musa
218
215
  if @segments.last && @segments.last[:time] == from_time
219
216
 
220
217
  @segments.last[:duration] = to_time - from_time
221
- @segments.last[:info] += "; edited on a as start"
218
+ @segments.last[:info] += '; edited on a as start'
222
219
 
223
220
  else
224
221
  @segments << { time: from_time,
225
222
  value: from_value,
226
223
  duration: to_time - from_time,
227
- info: "added on a as start" }
224
+ info: 'added on a as start' }
228
225
 
229
226
  end
230
227
 
@@ -233,7 +230,7 @@ module Musa
233
230
  value: from_value,
234
231
  duration: 0,
235
232
  stop: true,
236
- info: "added on a as end stop" }
233
+ info: 'added on a as end stop' }
237
234
  end
238
235
  else
239
236
  time_increment = to_time - from_time
@@ -268,7 +265,7 @@ module Musa
268
265
  @segments.last[:value] == value
269
266
 
270
267
  @segments.last[:duration] = step_time_increment
271
- @segments.last[:info] += "; edited on b"
268
+ @segments.last[:info] += '; edited on b'
272
269
 
273
270
  # puts "process2: editing #{@segments.last}"
274
271
 
@@ -276,7 +273,7 @@ module Musa
276
273
  @segments << v = { time: intermediate_point_time,
277
274
  value: value,
278
275
  duration: step_time_increment,
279
- info: "added on b" }
276
+ info: 'added on b' }
280
277
 
281
278
  # puts "process2: adding #{v.inspect}"
282
279
  end
@@ -297,7 +294,7 @@ module Musa
297
294
 
298
295
  private def process(time, value, last_time_value)
299
296
  if time && value
300
- raise RuntimeError, "time only can go forward" if @last_processed_time && time <= @last_processed_time
297
+ raise RuntimeError, 'time only can go forward' if @last_processed_time && time <= @last_processed_time
301
298
 
302
299
  q_value = round_quantize(value)
303
300
 
@@ -350,16 +347,17 @@ module Musa
350
347
  private_constant :RawQuantizer
351
348
 
352
349
  class PredictiveQuantizer
353
- include Serie
350
+ include Series::Serie.with(source: true)
354
351
  include QuantizerTools
355
352
 
356
353
  attr_reader :source
357
354
 
358
355
  def initialize(reference, step, source, value_attribute, include_stops)
356
+ self.source = source
357
+
359
358
  @reference = reference
360
359
  @step_size = step
361
360
 
362
- @source = source
363
361
  @value_attribute = value_attribute
364
362
 
365
363
  @include_stops = include_stops
@@ -367,25 +365,25 @@ module Musa
367
365
  @halfway_offset = step / 2r
368
366
  @crossing_reference = reference - @halfway_offset
369
367
 
370
- _restart false
371
-
372
- mark_regarding! source
368
+ init
373
369
  end
374
370
 
375
- def _restart(restart_sources = true)
376
- @source.restart if restart_sources
377
-
371
+ private def _init
378
372
  @last_time = nil
379
373
  @crossings = []
380
374
 
381
375
  @first = true
382
376
  end
383
377
 
378
+ private def _restart
379
+ @source.restart
380
+ end
381
+
384
382
  def infinite?
385
383
  !!@source.infinite?
386
384
  end
387
385
 
388
- def _next_value
386
+ private def _next_value
389
387
  result = nil
390
388
 
391
389
  first_time, first_value = get_time_value(@source.peek_next_value) if @first
@@ -1,51 +1,53 @@
1
- module Musa
2
- module Series
3
- # TODO: adapt to series prototyping
1
+ require_relative 'base-series'
4
2
 
3
+ module Musa
4
+ module Series::Constructors
5
5
  def QUEUE(*series)
6
6
  QueueSerie.new(series)
7
7
  end
8
8
 
9
9
  class QueueSerie
10
- include Serie
11
-
12
- attr_reader :targets, :target
10
+ include Series::Serie.with(sources: true)
13
11
 
14
12
  def initialize(series)
15
- @targets = series.collect(&:instance)
16
- @targets ||= []
17
-
18
- mark_as_instance!
19
-
20
- _restart
13
+ self.sources = series
14
+ init
21
15
  end
22
16
 
23
17
  def <<(serie)
24
- @targets << serie.instance
25
- check_current
18
+ # when queue is a prototype it is also frozen so no serie can be added (it would raise an Exception if tried).
19
+ # when queue is an instance the added serie should also be an instance (raise an Exception otherwise)
20
+ #
21
+ raise ArgumentError, "Only an instance serie can be queued" unless serie.instance?
22
+
23
+ @sources << serie
24
+ @current ||= @sources[@index]
25
+
26
26
  self
27
27
  end
28
28
 
29
29
  def clear
30
- @targets.clear
31
- restart
30
+ @sources.clear
31
+ init
32
32
  self
33
33
  end
34
34
 
35
- def _prototype!
36
- raise PrototypingSerieError, 'Cannot get prototype of a proxy serie'
35
+ private def _init
36
+ @index = 0
37
+ @current = @sources[@index]
38
+ @restart_sources = false
37
39
  end
38
40
 
39
- def _restart
40
- @index = -1
41
- forward
41
+ private def _restart
42
+ @current.restart
43
+ @restart_sources = true
42
44
  end
43
45
 
44
- def _next_value
46
+ private def _next_value
45
47
  value = nil
46
48
 
47
- if @target
48
- value = @target.next_value
49
+ if @current
50
+ value = @current.next_value
49
51
 
50
52
  if value.nil?
51
53
  forward
@@ -57,38 +59,32 @@ module Musa
57
59
  end
58
60
 
59
61
  def infinite?
60
- !!@targets.find(&:infinite?)
62
+ !!@sources.find(&:infinite?)
61
63
  end
62
64
 
63
- private
64
-
65
- def forward
65
+ private def forward
66
66
  @index += 1
67
- @target = nil
68
- @target = @targets[@index].restart if @index < @targets.size
69
- end
70
-
71
- def check_current
72
- @target = @targets[@index].restart unless @target
67
+ @current = @sources[@index]
68
+ @current&.restart if @restart_sources
73
69
  end
74
70
 
75
- def method_missing(method_name, *args, **key_args, &block)
76
- if @target && @target.respond_to?(method_name)
77
- @target.send method_name, *args, **key_args, &block
71
+ private def method_missing(method_name, *args, **key_args, &block)
72
+ if @current&.respond_to?(method_name)
73
+ @current.send method_name, *args, **key_args, &block
78
74
  else
79
75
  super
80
76
  end
81
77
  end
82
78
 
83
- def respond_to_missing?(method_name, include_private)
84
- @target && @target.respond_to?(method_name, include_private) # || super
79
+ private def respond_to_missing?(method_name, include_private)
80
+ @current&.respond_to?(method_name, include_private) # || super
85
81
  end
86
82
  end
83
+ end
87
84
 
88
- module SerieOperations
89
- def queued
90
- Series::QueueSerie.new [self]
91
- end
85
+ module Series::Operations
86
+ def queued
87
+ Series::Constructors.QUEUE(self)
92
88
  end
93
89
  end
94
90
  end
@@ -0,0 +1,149 @@
1
+ require_relative 'base-series'
2
+
3
+ require_relative '../core-ext/with'
4
+
5
+ module Musa
6
+ module Series
7
+ module Composer
8
+ class Composer
9
+ using Musa::Extension::Arrayfy
10
+
11
+ attr_reader :inputs, :outputs
12
+
13
+ def initialize(inputs: [:input], outputs: [:output], &block)
14
+ @pipelines = {}
15
+
16
+ @links = Set[]
17
+ @links_from = {}
18
+ @links_to = {}
19
+
20
+ @dsl = DSLContext.new(@pipelines, @links, @links_from, @links_to)
21
+ @inputs = {}
22
+ @outputs = {}
23
+
24
+ inputs&.each do |input|
25
+ @inputs[input] = Series::Constructors.PROXY
26
+ @pipelines[input] = { input: nil, output: @inputs[input].buffered }
27
+
28
+ @dsl.define_singleton_method(input) { input }
29
+ end
30
+
31
+ outputs&.each do |output|
32
+ @outputs[output] = Series::Constructors.PROXY
33
+ @pipelines[output] = { input: @outputs[output], output: nil }
34
+
35
+ @dsl.define_singleton_method(output) { output }
36
+ end
37
+
38
+ @dsl.with &block if block
39
+ end
40
+
41
+ def route(from, to:, on: nil, as: nil)
42
+ @dsl.route(from, to: to, on: on, as: as)
43
+ end
44
+
45
+ def pipeline(name, *elements)
46
+ @dsl.pipeline(name, elements)
47
+ end
48
+
49
+ def update(&block)
50
+ @dsl.with &block
51
+ end
52
+
53
+ class DSLContext
54
+ include Musa::Extension::With
55
+
56
+ def initialize(pipelines, links, links_from, links_to)
57
+ @pipelines = pipelines
58
+
59
+ @links = links
60
+ @links_from = links_from
61
+ @links_to = links_to
62
+ end
63
+
64
+ def route(from, to:, on: nil, as: nil)
65
+ from_pipeline = @pipelines[from]
66
+ to_pipeline = @pipelines[to]
67
+
68
+ raise ArgumentError, "Pipeline '#{from}' not found." unless from_pipeline
69
+ raise ArgumentError, "Pipeline '#{to}' not found." unless to_pipeline
70
+
71
+ @links_from[from] ||= Set[]
72
+
73
+ on ||= as ? :sources : :source
74
+
75
+ raise ArgumentError, "Pipeline #{@links_to[[to, on, as]]} already connected to pipeline #{to} on #{on} as #{as}" if @links_to[[to, on, as]]
76
+
77
+ if as
78
+ to_pipeline[:input].send(on)[as] = from_pipeline[:output].buffer
79
+ else
80
+ to_pipeline[:input].send("#{on.to_s}=".to_sym, from_pipeline[:output].buffer)
81
+ end
82
+
83
+ @links_from[from] << [to, on, as]
84
+ @links_to[[to, on, as]] = from
85
+ @links << [from, to, on, as]
86
+ end
87
+
88
+ def pipeline(name, elements)
89
+ first = last = nil
90
+
91
+ elements.each do |e|
92
+ case e
93
+ when Hash
94
+ if e.size == 1
95
+ operation = e.keys.first
96
+ parameters = e.values.first
97
+
98
+ if Musa::Series::Constructors.instance_methods.include?(operation)
99
+ raise ArgumentError, "Called constructor '#{operation}' ignoring previous elements" unless last.nil?
100
+
101
+ last = Musa::Series::Constructors.method(operation).call(*parameters)
102
+
103
+ elsif Musa::Series::Operations.instance_methods.include?(operation)
104
+ first = last = Musa::Series::Constructors.PROXY if last.nil?
105
+ last = last.send(operation, *parameters)
106
+
107
+ end
108
+ else
109
+ raise ArgumentError, "Don't know how to handle #{e}"
110
+ end
111
+ when Symbol
112
+ first = last = Musa::Series::Constructors.PROXY if last.nil?
113
+ # operation == e
114
+ last = last.send(e) if Musa::Series::Operations.instance_methods.include?(e)
115
+ end
116
+
117
+ first ||= last
118
+ end
119
+
120
+ @pipelines[name] = { input: first, output: last.buffered }
121
+
122
+ define_singleton_method(name) { name }
123
+ end
124
+
125
+ private def method_missing(symbol, *args, &block)
126
+ if Musa::Series::Operations.instance_methods.include?(symbol)
127
+ symbol
128
+ elsif Musa::Series::Constructors.instance_methods.include?(symbol)
129
+ symbol
130
+ else
131
+ raise ArgumentError, "Pipeline '#{symbol}' is undefined" if args.empty?
132
+
133
+ pipeline(symbol, args)
134
+ end
135
+ end
136
+
137
+ private def respond_to_missing?(method_name, include_private = false)
138
+ Musa::Series::Operations.instance_methods.include?(method_name) ||
139
+ Musa::Series::Constructors.instance_methods.include?(method_name) ||
140
+ @pipelines.key?(method_name) ||
141
+ super
142
+ end
143
+ end
144
+
145
+ private_constant :DSLContext
146
+ end
147
+ end
148
+ end
149
+ end