musa-dsl 0.22.0 → 0.22.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -12,4 +12,4 @@ require_relative 'queue-serie'
12
12
  require_relative 'hash-or-array-serie-splitter'
13
13
 
14
14
  require_relative 'quantizer-serie'
15
- require_relative 'flattener-timed-serie'
15
+ require_relative 'timed-serie'
@@ -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
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'musa-dsl'
3
- s.version = '0.22.0'
4
- s.date = '2020-11-10'
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.0
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-10 00:00:00.000000000 Z
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