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.
- checksums.yaml +4 -4
- data/Gemfile +3 -1
- data/lib/musa-dsl.rb +14 -8
- data/lib/musa-dsl/core-ext/deep-copy.rb +12 -1
- data/lib/musa-dsl/core-ext/inspect-nice.rb +1 -2
- data/lib/musa-dsl/core-ext/smart-proc-binder.rb +13 -11
- data/lib/musa-dsl/datasets/p.rb +41 -16
- data/lib/musa-dsl/datasets/score/to-mxml/process-pdv.rb +14 -12
- data/lib/musa-dsl/datasets/score/to-mxml/process-ps.rb +32 -6
- data/lib/musa-dsl/datasets/score/to-mxml/to-mxml.rb +24 -10
- data/lib/musa-dsl/generative/backboner.rb +6 -11
- data/lib/musa-dsl/generative/generative-grammar.rb +1 -3
- data/lib/musa-dsl/generative/markov.rb +10 -6
- data/lib/musa-dsl/logger/logger.rb +6 -1
- data/lib/musa-dsl/matrix/matrix.rb +9 -7
- data/lib/musa-dsl/midi/midi-voices.rb +8 -7
- data/lib/musa-dsl/music/scales.rb +1 -1
- data/lib/musa-dsl/neumalang/neumalang.rb +1 -1
- data/lib/musa-dsl/neumas/array-to-neumas.rb +1 -1
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +9 -4
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-timed.rb +30 -129
- data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +10 -24
- data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +9 -9
- data/lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb +3 -5
- data/lib/musa-dsl/sequencer/base-sequencer.rb +14 -23
- data/lib/musa-dsl/sequencer/sequencer-dsl.rb +9 -7
- data/lib/musa-dsl/sequencer/sequencer.rb +7 -0
- data/lib/musa-dsl/series/base-series.rb +293 -144
- data/lib/musa-dsl/series/buffer-serie.rb +237 -0
- data/lib/musa-dsl/series/hash-or-array-serie-splitter.rb +136 -105
- data/lib/musa-dsl/series/main-serie-constructors.rb +251 -156
- data/lib/musa-dsl/series/main-serie-operations.rb +308 -303
- data/lib/musa-dsl/series/proxy-serie.rb +21 -41
- data/lib/musa-dsl/series/quantizer-serie.rb +44 -46
- data/lib/musa-dsl/series/queue-serie.rb +39 -43
- data/lib/musa-dsl/series/series-composer.rb +149 -0
- data/lib/musa-dsl/series/series.rb +6 -3
- data/lib/musa-dsl/series/timed-serie.rb +343 -0
- data/musa-dsl.gemspec +13 -3
- metadata +10 -11
- data/lib/musa-dsl/series/flattener-timed-serie.rb +0 -61
- data/lib/musa-dsl/series/holder-serie.rb +0 -87
- 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 '
|
16
|
-
|
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.
|
4
|
-
s.date = '
|
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
|
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.
|
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:
|
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
|
74
|
-
|
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/
|
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/
|
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: '
|
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.
|
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
|