musa-dsl 0.22.0 → 0.22.5
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/lib/musa-dsl.rb +1 -1
- data/lib/musa-dsl/datasets/p.rb +3 -1
- data/lib/musa-dsl/matrix/matrix.rb +9 -7
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +7 -2
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-timed.rb +28 -128
- data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +8 -24
- data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +5 -7
- 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 +7 -4
- data/lib/musa-dsl/sequencer/sequencer.rb +8 -1
- data/lib/musa-dsl/series/base-series.rb +2 -2
- data/lib/musa-dsl/series/hash-or-array-serie-splitter.rb +68 -4
- data/lib/musa-dsl/series/main-serie-constructors.rb +7 -11
- data/lib/musa-dsl/series/main-serie-operations.rb +39 -4
- data/lib/musa-dsl/series/quantizer-serie.rb +25 -15
- data/lib/musa-dsl/series/series.rb +1 -1
- data/lib/musa-dsl/series/timed-serie.rb +354 -0
- data/musa-dsl.gemspec +2 -2
- metadata +4 -4
- data/lib/musa-dsl/series/flattener-timed-serie.rb +0 -61
@@ -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
|
data/musa-dsl.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'musa-dsl'
|
3
|
-
s.version = '0.22.
|
4
|
-
s.date = '2020-11-
|
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
|
+
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-
|
11
|
+
date: 2020-11-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: citrus
|
@@ -170,16 +170,15 @@ files:
|
|
170
170
|
- lib/musa-dsl/sequencer/base-sequencer-implementation-play-timed.rb
|
171
171
|
- lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb
|
172
172
|
- lib/musa-dsl/sequencer/base-sequencer-implementation.rb
|
173
|
-
- lib/musa-dsl/sequencer/base-sequencer-public.rb
|
174
173
|
- lib/musa-dsl/sequencer/base-sequencer-tick-based.rb
|
175
174
|
- lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb
|
175
|
+
- lib/musa-dsl/sequencer/base-sequencer.rb
|
176
176
|
- lib/musa-dsl/sequencer/sequencer-dsl.rb
|
177
177
|
- lib/musa-dsl/sequencer/sequencer.rb
|
178
178
|
- lib/musa-dsl/sequencer/timeslots.rb
|
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,6 +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
|
190
|
+
- lib/musa-dsl/series/timed-serie.rb
|
191
191
|
- lib/musa-dsl/transcription.rb
|
192
192
|
- lib/musa-dsl/transcription/from-gdv-to-midi.rb
|
193
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(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
|