musa-dsl 0.22.2 → 0.23.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) 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 +1 -0
  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-public.rb → base-sequencer.rb} +15 -23
  26. data/lib/musa-dsl/sequencer/sequencer-dsl.rb +9 -7
  27. data/lib/musa-dsl/sequencer/sequencer.rb +8 -1
  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 +139 -60
  31. data/lib/musa-dsl/series/main-serie-constructors.rb +254 -165
  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 -2
  38. data/lib/musa-dsl/series/timed-serie.rb +343 -0
  39. data/musa-dsl.gemspec +13 -3
  40. metadata +11 -11
  41. data/lib/musa-dsl/series/flattener-timed-serie.rb +0 -61
  42. data/lib/musa-dsl/series/holder-serie.rb +0 -87
@@ -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