musa-dsl 0.22.2 → 0.23.0
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 +1 -0
- 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-public.rb → base-sequencer.rb} +15 -23
- data/lib/musa-dsl/sequencer/sequencer-dsl.rb +9 -7
- data/lib/musa-dsl/sequencer/sequencer.rb +8 -1
- 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 +139 -60
- data/lib/musa-dsl/series/main-serie-constructors.rb +254 -165
- 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 -2
- data/lib/musa-dsl/series/timed-serie.rb +343 -0
- data/musa-dsl.gemspec +13 -3
- metadata +11 -11
- data/lib/musa-dsl/series/flattener-timed-serie.rb +0 -61
- data/lib/musa-dsl/series/holder-serie.rb +0 -87
@@ -5,11 +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 '
|
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.0'
|
4
|
+
s.date = '2021-06-29'
|
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.0
|
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-06-29 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
|
@@ -170,24 +169,25 @@ files:
|
|
170
169
|
- lib/musa-dsl/sequencer/base-sequencer-implementation-play-timed.rb
|
171
170
|
- lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb
|
172
171
|
- lib/musa-dsl/sequencer/base-sequencer-implementation.rb
|
173
|
-
- lib/musa-dsl/sequencer/base-sequencer-public.rb
|
174
172
|
- lib/musa-dsl/sequencer/base-sequencer-tick-based.rb
|
175
173
|
- lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb
|
174
|
+
- lib/musa-dsl/sequencer/base-sequencer.rb
|
176
175
|
- lib/musa-dsl/sequencer/sequencer-dsl.rb
|
177
176
|
- lib/musa-dsl/sequencer/sequencer.rb
|
178
177
|
- lib/musa-dsl/sequencer/timeslots.rb
|
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
|
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
|
@@ -212,16 +212,16 @@ require_paths:
|
|
212
212
|
- lib
|
213
213
|
required_ruby_version: !ruby/object:Gem::Requirement
|
214
214
|
requirements:
|
215
|
-
- - "
|
215
|
+
- - "~>"
|
216
216
|
- !ruby/object:Gem::Version
|
217
|
-
version: '
|
217
|
+
version: '2.7'
|
218
218
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
219
219
|
requirements:
|
220
220
|
- - ">="
|
221
221
|
- !ruby/object:Gem::Version
|
222
222
|
version: '0'
|
223
223
|
requirements: []
|
224
|
-
rubygems_version: 3.1.
|
224
|
+
rubygems_version: 3.1.6
|
225
225
|
signing_key:
|
226
226
|
specification_version: 4
|
227
227
|
summary: A simple Ruby DSL for making complex music
|