musa-dsl 0.22.1 → 0.22.6

Sign up to get free protection for your applications and to get access to all the features.
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