musa-dsl 0.22.3 → 0.23.1

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