musa-dsl 0.22.1 → 0.22.6

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.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/lib/musa-dsl.rb +1 -1
  3. data/lib/musa-dsl/core-ext/inspect-nice.rb +1 -2
  4. data/lib/musa-dsl/datasets/p.rb +36 -15
  5. data/lib/musa-dsl/datasets/score/to-mxml/process-pdv.rb +14 -12
  6. data/lib/musa-dsl/datasets/score/to-mxml/process-ps.rb +32 -6
  7. data/lib/musa-dsl/datasets/score/to-mxml/to-mxml.rb +24 -10
  8. data/lib/musa-dsl/logger/logger.rb +6 -1
  9. data/lib/musa-dsl/matrix/matrix.rb +9 -7
  10. data/lib/musa-dsl/midi/midi-voices.rb +1 -0
  11. data/lib/musa-dsl/music/scales.rb +1 -1
  12. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +7 -2
  13. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-timed.rb +30 -129
  14. data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +10 -24
  15. data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +9 -9
  16. data/lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb +3 -5
  17. data/lib/musa-dsl/sequencer/{base-sequencer-public.rb → base-sequencer.rb} +15 -23
  18. data/lib/musa-dsl/sequencer/sequencer-dsl.rb +7 -4
  19. data/lib/musa-dsl/sequencer/sequencer.rb +8 -1
  20. data/lib/musa-dsl/series/base-series.rb +3 -3
  21. data/lib/musa-dsl/series/hash-or-array-serie-splitter.rb +90 -10
  22. data/lib/musa-dsl/series/main-serie-constructors.rb +8 -12
  23. data/lib/musa-dsl/series/main-serie-operations.rb +45 -5
  24. data/lib/musa-dsl/series/quantizer-serie.rb +25 -15
  25. data/lib/musa-dsl/series/series.rb +1 -1
  26. data/lib/musa-dsl/series/timed-serie.rb +356 -0
  27. data/musa-dsl.gemspec +3 -3
  28. metadata +6 -6
  29. data/lib/musa-dsl/series/flattener-timed-serie.rb +0 -61
@@ -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,356 @@
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
+ TimedUnionOfArrayOfTimedSeries.new(array_of_timed_series)
12
+ elsif hash_of_timed_series.any?
13
+ TimedUnionOfHashOfTimedSeries.new(hash_of_timed_series)
14
+ else
15
+ raise ArgumentError, 'Missing argument series'
16
+ end
17
+ end
18
+
19
+ class TimedUnionOfArrayOfTimedSeries
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(&: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.extend(Musa::Datasets::AbsTimed)
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 :TimedUnionOfArrayOfTimedSeries
150
+
151
+ class TimedUnionOfHashOfTimedSeries
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(&: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 = {}
178
+
179
+ @components.each do |key|
180
+ sources_values[key] = @sources_next_values[key] || (@sources_next_values[key] = @sources[key].next_value)
181
+ end
182
+
183
+ @other_attributes = infer_other_attributes(sources_values) unless @other_attributes
184
+
185
+ time = sources_values.values.collect { |_| _&.[](:time) }.compact.min
186
+
187
+ if time
188
+ selected_values = sources_values.transform_values { |_| _ if _&.[](:time) == time }
189
+
190
+ @sources_next_values.each_key do |key|
191
+ if @sources_next_values[key]&.[](:time) == time
192
+ @sources_next_values[key] = nil
193
+ end
194
+ end
195
+
196
+ result = { time: time, value: {} }
197
+
198
+ @other_attributes.each do |attribute_name|
199
+ result[attribute_name] = {}
200
+ end
201
+
202
+ @components.each do |component|
203
+ result[:value][component] = selected_values[component]&.[](:value)
204
+
205
+ @other_attributes.each do |attribute_name|
206
+ result[attribute_name][component] = selected_values[component]&.[](attribute_name)
207
+ end
208
+ end
209
+
210
+ result.extend(Musa::Datasets::AbsTimed)
211
+ else
212
+ nil
213
+ end
214
+ end
215
+
216
+ def infinite?
217
+ !!@sources.find(&:infinite?)
218
+ end
219
+
220
+ private def infer_other_attributes(sources_values)
221
+ other_attributes = Set[]
222
+
223
+ sources_values.each_value do |source_value|
224
+ (source_value.keys - [:time, :value]).each do |attribute_name|
225
+ other_attributes << attribute_name
226
+ end
227
+ end
228
+
229
+ other_attributes
230
+ end
231
+ end
232
+
233
+ private_constant :TimedUnionOfHashOfTimedSeries
234
+
235
+ module SerieOperations
236
+ def flatten_timed
237
+ TimedFlattener.new(self)
238
+ end
239
+
240
+ def compact_timed
241
+ TimedCompacter.new(self)
242
+ end
243
+
244
+ def union_timed(*other_timed_series, key: nil, **other_key_timed_series)
245
+ if key && other_key_timed_series.any?
246
+ Series::TIMED_UNION(key => self, **other_key_timed_series)
247
+
248
+ elsif other_timed_series.any? && other_key_timed_series.empty?
249
+ Series::TIMED_UNION(self, *other_timed_series)
250
+
251
+ else
252
+ raise ArgumentError, 'Can\'t union an array of series with a hash of series'
253
+ end
254
+ end
255
+
256
+ class TimedFlattener
257
+ include Serie
258
+
259
+ attr_reader :source
260
+
261
+ def initialize(serie)
262
+ @source = serie
263
+ mark_regarding! @source
264
+ end
265
+
266
+ def _restart
267
+ @source.restart
268
+ end
269
+
270
+ def _next_value
271
+ source_value = @source.next_value
272
+
273
+ if !source_value.nil?
274
+ time = source_value[:time]
275
+ source_value_value = source_value[:value]
276
+
277
+ source_value_extra = (source_value.keys - [:time, :value]).collect do |attribute_name|
278
+ [attribute_name, source_value[attribute_name]]
279
+ end.to_h
280
+
281
+ case source_value_value
282
+ when Hash
283
+ result = {}
284
+ source_value_value.each_pair do |key, value|
285
+ result[key] = { time: time, value: value }.extend(Musa::Datasets::AbsTimed)
286
+
287
+ source_value_extra.each do |attribute_name, attribute_value|
288
+ result[key][attribute_name] = attribute_value[key]
289
+ end
290
+ end
291
+
292
+ when Array
293
+ result = []
294
+ source_value_value.each_index do |index|
295
+ result[index] = { time: time, value: source_value_value[index] }.extend(Musa::Datasets::AbsTimed)
296
+
297
+ source_value_extra.each do |attribute_name, attribute_value|
298
+ result[index][attribute_name] = attribute_value[index]
299
+ end
300
+ end
301
+ else
302
+ result = source_value.clone.extend(Musa::Datasets::AbsTimed)
303
+ end
304
+
305
+ result.extend(Musa::Datasets::AbsTimed)
306
+ else
307
+ nil
308
+ end
309
+ end
310
+
311
+ def infinite?
312
+ @source.infinite?
313
+ end
314
+ end
315
+
316
+ private_constant :TimedFlattener
317
+ end
318
+
319
+ class TimedCompacter
320
+ include Serie
321
+
322
+ attr_reader :source
323
+
324
+ def initialize(serie)
325
+ @source = serie
326
+ mark_regarding! @source
327
+ end
328
+
329
+ def _restart
330
+ @source.restart
331
+ end
332
+
333
+ def _next_value
334
+ while (source_value = @source.next_value) && skip_value?(source_value[:value]); end
335
+ source_value
336
+ end
337
+
338
+ def infinite?
339
+ @source.infinite?
340
+ end
341
+
342
+ private def skip_value?(timed_value)
343
+ case timed_value
344
+ when Hash
345
+ timed_value.all? { |_, v| v.nil? }
346
+ when Array
347
+ timed_value.all?(&:nil?)
348
+ else
349
+ timed_value.nil?
350
+ end
351
+ end
352
+ end
353
+
354
+ private_constant :TimedCompacter
355
+ end
356
+ end
@@ -1,9 +1,9 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'musa-dsl'
3
- s.version = '0.22.1'
4
- s.date = '2020-11-11'
3
+ s.version = '0.22.6'
4
+ s.date = '2020-11-25'
5
5
  s.summary = 'A simple Ruby DSL for making complex music'
6
- s.description = 'Musa-DSL: A Ruby DSL for algorithmic music composition, device language neutral (MIDI, OSC, etc)'
6
+ s.description = 'Musa-DSL: A Ruby framework and DSL for algorithmic sound and musical thinking and composition'
7
7
  s.authors = ['Javier Sánchez Yeste']
8
8
  s.email = 'javier.sy@gmail.com'
9
9
  s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|samples)/}) }
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.1
4
+ version: 0.22.6
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 00:00:00.000000000 Z
11
+ date: 2020-11-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: citrus
@@ -70,8 +70,8 @@ dependencies:
70
70
  - - ">="
71
71
  - !ruby/object:Gem::Version
72
72
  version: 0.2.4
73
- description: 'Musa-DSL: A Ruby DSL for algorithmic music composition, device language
74
- neutral (MIDI, OSC, etc)'
73
+ description: 'Musa-DSL: A Ruby framework and DSL for algorithmic sound and musical
74
+ thinking and composition'
75
75
  email: javier.sy@gmail.com
76
76
  executables: []
77
77
  extensions: []
@@ -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(Musa::Datasets::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(Musa::Datasets::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