musa-dsl 0.22.2 → 0.23.0
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.
- checksums.yaml +4 -4
- data/Gemfile +3 -1
- data/lib/musa-dsl.rb +14 -8
- data/lib/musa-dsl/core-ext/deep-copy.rb +12 -1
- data/lib/musa-dsl/core-ext/inspect-nice.rb +1 -2
- data/lib/musa-dsl/core-ext/smart-proc-binder.rb +13 -11
- data/lib/musa-dsl/datasets/p.rb +41 -16
- data/lib/musa-dsl/datasets/score/to-mxml/process-pdv.rb +14 -12
- data/lib/musa-dsl/datasets/score/to-mxml/process-ps.rb +32 -6
- data/lib/musa-dsl/datasets/score/to-mxml/to-mxml.rb +24 -10
- data/lib/musa-dsl/generative/backboner.rb +6 -11
- data/lib/musa-dsl/generative/generative-grammar.rb +1 -3
- data/lib/musa-dsl/generative/markov.rb +10 -6
- data/lib/musa-dsl/logger/logger.rb +6 -1
- data/lib/musa-dsl/matrix/matrix.rb +9 -7
- data/lib/musa-dsl/midi/midi-voices.rb +1 -0
- data/lib/musa-dsl/music/scales.rb +1 -1
- data/lib/musa-dsl/neumalang/neumalang.rb +1 -1
- data/lib/musa-dsl/neumas/array-to-neumas.rb +1 -1
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +9 -4
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-timed.rb +30 -129
- data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +10 -24
- data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +9 -9
- data/lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb +3 -5
- data/lib/musa-dsl/sequencer/{base-sequencer-public.rb → base-sequencer.rb} +15 -23
- data/lib/musa-dsl/sequencer/sequencer-dsl.rb +9 -7
- data/lib/musa-dsl/sequencer/sequencer.rb +8 -1
- data/lib/musa-dsl/series/base-series.rb +293 -144
- data/lib/musa-dsl/series/buffer-serie.rb +237 -0
- data/lib/musa-dsl/series/hash-or-array-serie-splitter.rb +139 -60
- data/lib/musa-dsl/series/main-serie-constructors.rb +254 -165
- data/lib/musa-dsl/series/main-serie-operations.rb +308 -303
- data/lib/musa-dsl/series/proxy-serie.rb +21 -41
- data/lib/musa-dsl/series/quantizer-serie.rb +44 -46
- data/lib/musa-dsl/series/queue-serie.rb +39 -43
- data/lib/musa-dsl/series/series-composer.rb +149 -0
- data/lib/musa-dsl/series/series.rb +6 -2
- data/lib/musa-dsl/series/timed-serie.rb +343 -0
- data/musa-dsl.gemspec +13 -3
- metadata +11 -11
- data/lib/musa-dsl/series/flattener-timed-serie.rb +0 -61
- data/lib/musa-dsl/series/holder-serie.rb +0 -87
@@ -1,69 +1,49 @@
|
|
1
|
-
|
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
|
-
|
16
|
-
|
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
|
28
|
-
@
|
17
|
+
private def _restart
|
18
|
+
@source.restart if @source
|
29
19
|
end
|
30
20
|
|
31
|
-
def
|
32
|
-
@
|
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
|
-
@
|
26
|
+
@source.infinite? if @source
|
45
27
|
end
|
46
28
|
|
47
|
-
private
|
48
|
-
|
49
|
-
|
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
|
-
@
|
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
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
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
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
-
|
104
|
-
|
105
|
-
mark_regarding! source
|
100
|
+
init
|
106
101
|
end
|
107
102
|
|
108
|
-
def
|
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
|
-
|
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] +=
|
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:
|
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:
|
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] +=
|
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:
|
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,
|
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
|
-
|
371
|
-
|
372
|
-
mark_regarding! source
|
368
|
+
init
|
373
369
|
end
|
374
370
|
|
375
|
-
def
|
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
|
-
|
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
|
-
|
16
|
-
|
17
|
-
|
18
|
-
mark_as_instance!
|
19
|
-
|
20
|
-
_restart
|
13
|
+
self.sources = series
|
14
|
+
init
|
21
15
|
end
|
22
16
|
|
23
17
|
def <<(serie)
|
24
|
-
|
25
|
-
|
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
|
-
@
|
31
|
-
|
30
|
+
@sources.clear
|
31
|
+
init
|
32
32
|
self
|
33
33
|
end
|
34
34
|
|
35
|
-
def
|
36
|
-
|
35
|
+
private def _init
|
36
|
+
@index = 0
|
37
|
+
@current = @sources[@index]
|
38
|
+
@restart_sources = false
|
37
39
|
end
|
38
40
|
|
39
|
-
def _restart
|
40
|
-
@
|
41
|
-
|
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 @
|
48
|
-
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
|
-
!!@
|
62
|
+
!!@sources.find(&:infinite?)
|
61
63
|
end
|
62
64
|
|
63
|
-
private
|
64
|
-
|
65
|
-
def forward
|
65
|
+
private def forward
|
66
66
|
@index += 1
|
67
|
-
@
|
68
|
-
@
|
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 @
|
77
|
-
@
|
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
|
-
@
|
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
|
-
|
89
|
-
|
90
|
-
|
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
|