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.
@@ -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