musa-dsl 0.22.3 → 0.23.1

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -1
  3. data/lib/musa-dsl.rb +14 -8
  4. data/lib/musa-dsl/core-ext/deep-copy.rb +12 -1
  5. data/lib/musa-dsl/core-ext/inspect-nice.rb +1 -2
  6. data/lib/musa-dsl/core-ext/smart-proc-binder.rb +13 -11
  7. data/lib/musa-dsl/datasets/p.rb +41 -16
  8. data/lib/musa-dsl/datasets/score/to-mxml/process-pdv.rb +14 -12
  9. data/lib/musa-dsl/datasets/score/to-mxml/process-ps.rb +32 -6
  10. data/lib/musa-dsl/datasets/score/to-mxml/to-mxml.rb +24 -10
  11. data/lib/musa-dsl/generative/backboner.rb +6 -11
  12. data/lib/musa-dsl/generative/generative-grammar.rb +1 -3
  13. data/lib/musa-dsl/generative/markov.rb +10 -6
  14. data/lib/musa-dsl/logger/logger.rb +6 -1
  15. data/lib/musa-dsl/matrix/matrix.rb +9 -7
  16. data/lib/musa-dsl/midi/midi-voices.rb +8 -7
  17. data/lib/musa-dsl/music/scales.rb +1 -1
  18. data/lib/musa-dsl/neumalang/neumalang.rb +1 -1
  19. data/lib/musa-dsl/neumas/array-to-neumas.rb +1 -1
  20. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +9 -4
  21. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-timed.rb +30 -129
  22. data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +10 -24
  23. data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +9 -9
  24. data/lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb +3 -5
  25. data/lib/musa-dsl/sequencer/base-sequencer.rb +14 -23
  26. data/lib/musa-dsl/sequencer/sequencer-dsl.rb +9 -7
  27. data/lib/musa-dsl/sequencer/sequencer.rb +7 -0
  28. data/lib/musa-dsl/series/base-series.rb +293 -144
  29. data/lib/musa-dsl/series/buffer-serie.rb +237 -0
  30. data/lib/musa-dsl/series/hash-or-array-serie-splitter.rb +136 -105
  31. data/lib/musa-dsl/series/main-serie-constructors.rb +251 -156
  32. data/lib/musa-dsl/series/main-serie-operations.rb +308 -303
  33. data/lib/musa-dsl/series/proxy-serie.rb +21 -41
  34. data/lib/musa-dsl/series/quantizer-serie.rb +44 -46
  35. data/lib/musa-dsl/series/queue-serie.rb +39 -43
  36. data/lib/musa-dsl/series/series-composer.rb +149 -0
  37. data/lib/musa-dsl/series/series.rb +6 -3
  38. data/lib/musa-dsl/series/timed-serie.rb +343 -0
  39. data/musa-dsl.gemspec +13 -3
  40. metadata +10 -11
  41. data/lib/musa-dsl/series/flattener-timed-serie.rb +0 -61
  42. data/lib/musa-dsl/series/holder-serie.rb +0 -87
  43. data/lib/musa-dsl/series/union-timed-series.rb +0 -109
@@ -5,12 +5,15 @@ require_relative 'main-serie-operations'
5
5
 
6
6
  require_relative 'array-to-serie'
7
7
 
8
- require_relative 'holder-serie'
9
8
  require_relative 'proxy-serie'
10
9
  require_relative 'queue-serie'
11
10
 
11
+ require_relative 'buffer-serie'
12
+
13
+ require_relative 'series-composer'
14
+
12
15
  require_relative 'hash-or-array-serie-splitter'
13
16
 
14
17
  require_relative 'quantizer-serie'
15
- require_relative 'flattener-timed-serie'
16
- require_relative 'union-timed-series'
18
+ require_relative 'timed-serie'
19
+
@@ -0,0 +1,343 @@
1
+ require_relative '../datasets/e'
2
+
3
+ require_relative 'base-series'
4
+
5
+ module Musa
6
+ module Series::Constructors
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 Series::Serie.with(sources: true)
21
+
22
+ def initialize(series)
23
+ self.sources = series
24
+ init
25
+ end
26
+
27
+ private def _init
28
+ @sources_next_values = Array.new(@sources.size)
29
+ @components = nil
30
+ end
31
+
32
+ private def _restart
33
+ @sources.each(&:restart)
34
+ end
35
+
36
+ private def _next_value
37
+ sources_values = @sources_next_values.each_index.collect do |i|
38
+ @sources_next_values[i] || (@sources_next_values[i] = @sources[i].next_value)
39
+ end
40
+
41
+ @components, @hash_mode, @array_mode = infer_components(sources_values) unless @components
42
+
43
+ time = sources_values.collect { |_| _&.[](:time) }.compact.min
44
+
45
+ if time
46
+ selected_values = sources_values.collect { |_| _ if _&.[](:time) == time }
47
+
48
+ @sources_next_values.each_index do |i|
49
+ if @sources_next_values[i]&.[](:time) == time
50
+ @sources_next_values[i] = nil
51
+ end
52
+ end
53
+
54
+ result = { time: time }
55
+
56
+ @components.each do |attribute_name, components|
57
+ if @hash_mode
58
+ result[attribute_name] = {}
59
+ elsif @array_mode
60
+ result[attribute_name] = []
61
+ else # value mode
62
+ result[attribute_name] = []
63
+ end
64
+
65
+ components.each do |target_key_or_index, source_placement|
66
+ result[attribute_name][target_key_or_index] = selected_values.dig(*source_placement)
67
+ end
68
+ end
69
+
70
+ result.extend(Musa::Datasets::AbsTimed)
71
+ else
72
+ nil
73
+ end
74
+ end
75
+
76
+ def infinite?
77
+ !!@sources.find(&:infinite?)
78
+ end
79
+ end
80
+
81
+ private def infer_components(sources_values)
82
+ other_attributes = Set[]
83
+
84
+ sources_values.each do |source_value|
85
+ (source_value.keys - [:time, :value]).each { |_| other_attributes << _ }
86
+ end
87
+
88
+ components = {}
89
+ components[:value] = {}
90
+
91
+ hash_mode = array_mode = nil
92
+
93
+ other_attributes.each do |attribute_name|
94
+ components[attribute_name] = {}
95
+ end
96
+
97
+ target_index = 0
98
+
99
+ sources_values.each_with_index do |source_value, i|
100
+ case source_value[:value]
101
+ when Hash
102
+ hash_mode = true
103
+
104
+ source_value[:value].keys.each do |key|
105
+ raise RuntimeError, "Value: key #{key} already used" unless components[:value][key].nil?
106
+
107
+ components[:value][key] = [i, :value, key]
108
+
109
+ other_attributes.each do |attribute_name|
110
+ raise RuntimeError, "Attribute #{attribute_name}: key #{key} already used" unless components[attribute_name][key].nil?
111
+ components[attribute_name][key] = [i, attribute_name, key]
112
+ end
113
+ end
114
+ when Array
115
+ array_mode = true
116
+
117
+ (0..source_value[:value].size - 1).each do |index|
118
+ components[:value][target_index] = [i, :value, index]
119
+
120
+ other_attributes.each do |attribute_name|
121
+ components[attribute_name][target_index] = [i, attribute_name, index]
122
+ end
123
+
124
+ target_index += 1
125
+ end
126
+ else
127
+ components[:value][target_index] = [i, :value]
128
+
129
+ other_attributes.each do |attribute_name|
130
+ components[attribute_name][target_index] = [i, attribute_name]
131
+ end
132
+
133
+ target_index += 1
134
+ end
135
+ end
136
+
137
+ raise RuntimeError, "source series values are of incompatible type (can't combine Hash and Array values)" if array_mode && hash_mode
138
+
139
+ return components, hash_mode, array_mode
140
+ end
141
+
142
+ private_constant :TimedUnionOfArrayOfTimedSeries
143
+
144
+ class TimedUnionOfHashOfTimedSeries
145
+ include Series::Serie.with(sources: true)
146
+
147
+ def initialize(series)
148
+ self.sources = series
149
+ init
150
+ end
151
+
152
+ def sources=(series)
153
+ super
154
+ @components = series.keys
155
+ end
156
+
157
+ private def _init
158
+ @sources_next_values = @components.collect { |k| [k, nil] }.to_h
159
+ @other_attributes = nil
160
+ end
161
+
162
+ private def _restart
163
+ @sources.each_value(&:restart)
164
+ end
165
+
166
+ private def _next_value
167
+ sources_values = {}
168
+
169
+ @components.each do |key|
170
+ sources_values[key] = @sources_next_values[key] || (@sources_next_values[key] = @sources[key].next_value)
171
+ end
172
+
173
+ @other_attributes = infer_other_attributes(sources_values) unless @other_attributes
174
+
175
+ time = sources_values.values.collect { |_| _&.[](:time) }.compact.min
176
+
177
+ if time
178
+ selected_values = sources_values.transform_values { |_| _ if _&.[](:time) == time }
179
+
180
+ @sources_next_values.each_key do |key|
181
+ if @sources_next_values[key]&.[](:time) == time
182
+ @sources_next_values[key] = nil
183
+ end
184
+ end
185
+
186
+ result = { time: time, value: {} }
187
+
188
+ @other_attributes.each do |attribute_name|
189
+ result[attribute_name] = {}
190
+ end
191
+
192
+ @components.each do |component|
193
+ result[:value][component] = selected_values[component]&.[](:value)
194
+
195
+ @other_attributes.each do |attribute_name|
196
+ result[attribute_name][component] = selected_values[component]&.[](attribute_name)
197
+ end
198
+ end
199
+
200
+ result.extend(Musa::Datasets::AbsTimed)
201
+ else
202
+ nil
203
+ end
204
+ end
205
+
206
+ def infinite?
207
+ !!@sources.find(&:infinite?)
208
+ end
209
+
210
+ private def infer_other_attributes(sources_values)
211
+ other_attributes = Set[]
212
+
213
+ sources_values.each_value do |source_value|
214
+ (source_value.keys - [:time, :value]).each do |attribute_name|
215
+ other_attributes << attribute_name
216
+ end
217
+ end
218
+
219
+ other_attributes
220
+ end
221
+ end
222
+
223
+ private_constant :TimedUnionOfHashOfTimedSeries
224
+ end
225
+
226
+ module Series::Operations
227
+ def flatten_timed
228
+ TimedFlattener.new(self)
229
+ end
230
+
231
+ def compact_timed
232
+ TimedCompacter.new(self)
233
+ end
234
+
235
+ def union_timed(*other_timed_series, key: nil, **other_key_timed_series)
236
+ if key && other_key_timed_series.any?
237
+ Series::Constructors.TIMED_UNION(key => self, **other_key_timed_series)
238
+
239
+ elsif other_timed_series.any? && other_key_timed_series.empty?
240
+ Series::Constructors.TIMED_UNION(self, *other_timed_series)
241
+
242
+ else
243
+ raise ArgumentError, 'Can\'t union an array of series with a hash of series'
244
+ end
245
+ end
246
+
247
+ class TimedFlattener
248
+ include Series::Serie.with(source: true)
249
+
250
+ def initialize(serie)
251
+ self.source = serie
252
+ init
253
+ end
254
+
255
+ private def _restart
256
+ @source.restart
257
+ end
258
+
259
+ private def _next_value
260
+ source_value = @source.next_value
261
+
262
+ if !source_value.nil?
263
+ time = source_value[:time]
264
+ source_value_value = source_value[:value]
265
+
266
+ source_value_extra = (source_value.keys - [:time, :value]).collect do |attribute_name|
267
+ [attribute_name, source_value[attribute_name]]
268
+ end.to_h
269
+
270
+ case source_value_value
271
+ when Hash
272
+ result = {}
273
+ source_value_value.each_pair do |key, value|
274
+ result[key] = { time: time, value: value }.extend(Musa::Datasets::AbsTimed)
275
+
276
+ source_value_extra.each do |attribute_name, attribute_value|
277
+ result[key][attribute_name] = attribute_value[key]
278
+ end
279
+ end
280
+
281
+ when Array
282
+ result = []
283
+ source_value_value.each_index do |index|
284
+ result[index] = { time: time, value: source_value_value[index] }.extend(Musa::Datasets::AbsTimed)
285
+
286
+ source_value_extra.each do |attribute_name, attribute_value|
287
+ result[index][attribute_name] = attribute_value[index]
288
+ end
289
+ end
290
+ else
291
+ result = source_value.clone.extend(Musa::Datasets::AbsTimed)
292
+ end
293
+
294
+ result.extend(Musa::Datasets::AbsTimed)
295
+ else
296
+ nil
297
+ end
298
+ end
299
+
300
+ def infinite?
301
+ @source.infinite?
302
+ end
303
+ end
304
+
305
+ private_constant :TimedFlattener
306
+ end
307
+
308
+ class TimedCompacter
309
+ include Series::Serie.with(source: true)
310
+
311
+ def initialize(serie)
312
+ self.source = serie
313
+ init
314
+ end
315
+
316
+ private def _restart
317
+ @source.restart
318
+ end
319
+
320
+ private def _next_value
321
+ while (source_value = @source.next_value) && skip_value?(source_value[:value]); end
322
+ source_value
323
+ end
324
+
325
+ def infinite?
326
+ @source.infinite?
327
+ end
328
+
329
+ private def skip_value?(timed_value)
330
+ case timed_value
331
+ when Hash
332
+ timed_value.all? { |_, v| v.nil? }
333
+ when Array
334
+ timed_value.all?(&:nil?)
335
+ else
336
+ timed_value.nil?
337
+ end
338
+ end
339
+ end
340
+
341
+ private_constant :TimedCompacter
342
+ end
343
+
data/musa-dsl.gemspec CHANGED
@@ -1,15 +1,25 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'musa-dsl'
3
- s.version = '0.22.3'
4
- s.date = '2020-11-12'
3
+ s.version = '0.23.1'
4
+ s.date = '2021-07-02'
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)/}) }
10
10
  s.homepage = 'https://github.com/javier-sy/musa-dsl'
11
11
  s.license = 'LGPL-3.0'
12
12
 
13
+ s.required_ruby_version = '~> 2.7'
14
+
15
+ # TODO
16
+ #s.metadata = {
17
+ # "source_code_uri" => "https://",
18
+ # "homepage_uri" => "",
19
+ # "documentation_uri" => "",
20
+ # "changelog_uri" => ""
21
+ #}
22
+
13
23
  s.add_runtime_dependency 'citrus', '~> 3.0.0', '>= 3.0.0'
14
24
 
15
25
  s.add_runtime_dependency 'midi-message', '~> 0.4', '>= 0.4.9'
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.3
4
+ version: 0.23.1
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-12 00:00:00.000000000 Z
11
+ date: 2021-07-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: citrus
@@ -70,15 +70,14 @@ 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: []
78
78
  extra_rdoc_files: []
79
79
  files:
80
80
  - ".gitignore"
81
- - ".ruby-version"
82
81
  - Gemfile
83
82
  - LICENSE.md
84
83
  - README.md
@@ -179,16 +178,16 @@ files:
179
178
  - lib/musa-dsl/series.rb
180
179
  - lib/musa-dsl/series/array-to-serie.rb
181
180
  - lib/musa-dsl/series/base-series.rb
182
- - lib/musa-dsl/series/flattener-timed-serie.rb
181
+ - lib/musa-dsl/series/buffer-serie.rb
183
182
  - lib/musa-dsl/series/hash-or-array-serie-splitter.rb
184
- - lib/musa-dsl/series/holder-serie.rb
185
183
  - lib/musa-dsl/series/main-serie-constructors.rb
186
184
  - lib/musa-dsl/series/main-serie-operations.rb
187
185
  - lib/musa-dsl/series/proxy-serie.rb
188
186
  - lib/musa-dsl/series/quantizer-serie.rb
189
187
  - lib/musa-dsl/series/queue-serie.rb
188
+ - lib/musa-dsl/series/series-composer.rb
190
189
  - lib/musa-dsl/series/series.rb
191
- - lib/musa-dsl/series/union-timed-series.rb
190
+ - lib/musa-dsl/series/timed-serie.rb
192
191
  - lib/musa-dsl/transcription.rb
193
192
  - lib/musa-dsl/transcription/from-gdv-to-midi.rb
194
193
  - lib/musa-dsl/transcription/from-gdv-to-musicxml.rb
@@ -213,16 +212,16 @@ require_paths:
213
212
  - lib
214
213
  required_ruby_version: !ruby/object:Gem::Requirement
215
214
  requirements:
216
- - - ">="
215
+ - - "~>"
217
216
  - !ruby/object:Gem::Version
218
- version: '0'
217
+ version: '2.7'
219
218
  required_rubygems_version: !ruby/object:Gem::Requirement
220
219
  requirements:
221
220
  - - ">="
222
221
  - !ruby/object:Gem::Version
223
222
  version: '0'
224
223
  requirements: []
225
- rubygems_version: 3.1.4
224
+ rubygems_version: 3.1.6
226
225
  signing_key:
227
226
  specification_version: 4
228
227
  summary: A simple Ruby DSL for making complex music