musa-dsl 0.26.5 → 0.26.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/musa-dsl/generative/{backboner.rb → rules.rb} +34 -21
- data/lib/musa-dsl/generative.rb +1 -1
- data/lib/musa-dsl/music/chord-definition.rb +20 -6
- data/lib/musa-dsl/music/chord-definitions.rb +10 -2
- data/lib/musa-dsl/music/chords.rb +135 -241
- data/lib/musa-dsl/music/equally-tempered-12-tone-scale-system.rb +28 -28
- data/lib/musa-dsl/music/scales.rb +21 -11
- data/lib/musa-dsl/repl/repl.rb +4 -2
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb +6 -1
- data/lib/musa-dsl/sequencer/sequencer-dsl.rb +1 -1
- data/lib/musa-dsl/transport/input-midi-clock.rb +12 -1
- data/lib/musa-dsl.rb +2 -2
- data/musa-dsl.gemspec +2 -2
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 33fe7c69b6b470b2563a5bb631744c7d39c7adb83c21fa612d630b212e63d99b
|
4
|
+
data.tar.gz: 18bd3611f80fcb47af81c4afc377f827bb18aa60702b491eb8e2d5edb4f70c16
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 35c3e1131b02666ee0f7db1dc596946a9b472738499b50139aea90fa1ecd468ff900378ce862fe6e9b31958dd94fbd8e1209cb23894a800ec8a1c888acb1cdd0
|
7
|
+
data.tar.gz: 6b2c952e9388f77ceb296b008ed572420f0621a0d05ebb617cc7254d18b4b5e3cbd9c81dee87b22deecf864d953a99f1cf4353e40b0ce3fb072a67ef93baada1
|
@@ -1,21 +1,22 @@
|
|
1
1
|
require_relative '../core-ext/smart-proc-binder'
|
2
2
|
require_relative '../core-ext/with'
|
3
3
|
|
4
|
-
# TODO hacer que pueda funcionar en tiempo real? le vas suministrando seeds y le vas diciendo qué opción has elegido (p.ej. para hacer un armonizador en tiempo real)
|
5
|
-
# TODO esto mismo sería aplicable en otros generadores? variatio/darwin? generative-grammar? markov?
|
4
|
+
# TODO: hacer que pueda funcionar en tiempo real? le vas suministrando seeds y le vas diciendo qué opción has elegido (p.ej. para hacer un armonizador en tiempo real)
|
5
|
+
# TODO: esto mismo sería aplicable en otros generadores? variatio/darwin? generative-grammar? markov?
|
6
|
+
# TODO: optimizar la llamada a .with que internamente genera cada vez un SmartProcBinder; podría generarse sólo una vez por cada &block
|
6
7
|
|
7
8
|
module Musa
|
8
|
-
module
|
9
|
+
module Rules
|
9
10
|
using Musa::Extension::Arrayfy
|
10
11
|
|
11
|
-
class
|
12
|
+
class Rules
|
12
13
|
include Musa::Extension::With
|
13
14
|
|
14
15
|
def initialize(&block)
|
15
16
|
@dsl = RulesEvalContext.new(&block)
|
16
17
|
end
|
17
18
|
|
18
|
-
def generate_possibilities(object, confirmed_node = nil, node = nil, grow_rules = nil)
|
19
|
+
def generate_possibilities(object, confirmed_node = nil, node = nil, grow_rules = nil, **parameters)
|
19
20
|
node ||= Node.new
|
20
21
|
grow_rules ||= @dsl._grow_rules
|
21
22
|
|
@@ -26,11 +27,15 @@ module Musa
|
|
26
27
|
grow_rule = grow_rules.shift
|
27
28
|
|
28
29
|
if grow_rule
|
29
|
-
grow_rule.generate_possibilities(object, history).each do |new_object|
|
30
|
+
grow_rule.generate_possibilities(object, history, **parameters).each do |new_object|
|
30
31
|
new_node = Node.new new_object, node
|
31
|
-
|
32
|
+
if @dsl._has_ending? && @dsl._ended?(new_object, history, **parameters) ||
|
33
|
+
!@dsl._has_ending? && grow_rules.empty?
|
32
34
|
|
33
|
-
|
35
|
+
new_node.mark_as_ended!
|
36
|
+
end
|
37
|
+
|
38
|
+
rejection = @dsl._cut_rules.find { |cut_rule| cut_rule.rejects?(new_object, history, **parameters) }
|
34
39
|
# TODO: include rejection secondary reasons in rejection message
|
35
40
|
|
36
41
|
new_node.reject! rejection if rejection
|
@@ -41,14 +46,14 @@ module Musa
|
|
41
46
|
|
42
47
|
unless grow_rules.empty?
|
43
48
|
node.children.each do |node|
|
44
|
-
generate_possibilities node.object, confirmed_node, node, grow_rules unless node.rejected || node.ended?
|
49
|
+
generate_possibilities node.object, confirmed_node, node, grow_rules, **parameters unless node.rejected || node.ended?
|
45
50
|
end
|
46
51
|
end
|
47
52
|
|
48
53
|
node
|
49
54
|
end
|
50
55
|
|
51
|
-
def apply(object_or_list, node = nil)
|
56
|
+
def apply(object_or_list, node = nil, **parameters)
|
52
57
|
list = object_or_list.arrayfy.clone
|
53
58
|
|
54
59
|
node ||= Node.new
|
@@ -56,7 +61,7 @@ module Musa
|
|
56
61
|
seed = list.shift
|
57
62
|
|
58
63
|
if seed
|
59
|
-
result = generate_possibilities seed, node
|
64
|
+
result = generate_possibilities seed, node, **parameters
|
60
65
|
|
61
66
|
fished = result.fish
|
62
67
|
|
@@ -64,7 +69,7 @@ module Musa
|
|
64
69
|
|
65
70
|
fished.each do |object|
|
66
71
|
subnode = node.add(object).mark_as_ended!
|
67
|
-
apply list, subnode
|
72
|
+
apply list, subnode, **parameters
|
68
73
|
end
|
69
74
|
end
|
70
75
|
|
@@ -77,11 +82,12 @@ module Musa
|
|
77
82
|
attr_reader :_grow_rules, :_ended_when, :_cut_rules
|
78
83
|
|
79
84
|
def initialize(&block)
|
85
|
+
@_grow_rules = []
|
86
|
+
@_cut_rules = []
|
80
87
|
with &block
|
81
88
|
end
|
82
89
|
|
83
90
|
def grow(name, &block)
|
84
|
-
@_grow_rules ||= []
|
85
91
|
@_grow_rules << GrowRule.new(name, &block)
|
86
92
|
self
|
87
93
|
end
|
@@ -92,13 +98,20 @@ module Musa
|
|
92
98
|
end
|
93
99
|
|
94
100
|
def cut(reason, &block)
|
95
|
-
@_cut_rules ||= []
|
96
101
|
@_cut_rules << CutRule.new(reason, &block)
|
97
102
|
self
|
98
103
|
end
|
99
104
|
|
100
|
-
def
|
101
|
-
|
105
|
+
def _has_ending?
|
106
|
+
!@_ended_when.nil?
|
107
|
+
end
|
108
|
+
|
109
|
+
def _ended?(object, history, **parameters)
|
110
|
+
if @_ended_when
|
111
|
+
with object, history, **parameters, &@_ended_when
|
112
|
+
else
|
113
|
+
false
|
114
|
+
end
|
102
115
|
end
|
103
116
|
|
104
117
|
class GrowRule
|
@@ -109,10 +122,10 @@ module Musa
|
|
109
122
|
@block = block
|
110
123
|
end
|
111
124
|
|
112
|
-
def generate_possibilities(object, history)
|
125
|
+
def generate_possibilities(object, history, **parameters)
|
113
126
|
# TODO: optimize context using only one instance for all genereate_possibilities calls
|
114
127
|
context = GrowRuleEvalContext.new
|
115
|
-
context.with object, history, &@block
|
128
|
+
context.with object, history, **parameters, &@block
|
116
129
|
|
117
130
|
context._branches
|
118
131
|
end
|
@@ -145,10 +158,10 @@ module Musa
|
|
145
158
|
@block = block
|
146
159
|
end
|
147
160
|
|
148
|
-
def rejects?(object, history)
|
161
|
+
def rejects?(object, history, **parameters)
|
149
162
|
# TODO: optimize context using only one instance for all rejects? checks
|
150
163
|
context = CutRuleEvalContext.new
|
151
|
-
context.with object, history, &@block
|
164
|
+
context.with object, history, **parameters, &@block
|
152
165
|
|
153
166
|
reasons = context._secondary_reasons.collect { |_| ("#{@reason} (#{_})" if _) || @reason }
|
154
167
|
|
@@ -203,7 +216,7 @@ module Musa
|
|
203
216
|
@children.each(&:update_rejection_by_children!)
|
204
217
|
|
205
218
|
if !@children.empty? && !@children.find { |n| !n.rejected }
|
206
|
-
reject!
|
219
|
+
reject! 'Node rejected because all children are rejected'
|
207
220
|
end
|
208
221
|
|
209
222
|
@ended = true
|
data/lib/musa-dsl/generative.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
1
3
|
module Musa
|
2
4
|
module Chords
|
3
5
|
class ChordDefinition
|
@@ -6,7 +8,7 @@ module Musa
|
|
6
8
|
end
|
7
9
|
|
8
10
|
def self.register(name, offsets:, **features)
|
9
|
-
definition = ChordDefinition.new(name, offsets: offsets, **features)
|
11
|
+
definition = ChordDefinition.new(name, offsets: offsets, **features)
|
10
12
|
|
11
13
|
@definitions ||= {}
|
12
14
|
@definitions[definition.name] = definition
|
@@ -14,6 +16,9 @@ module Musa
|
|
14
16
|
@features_by_value ||= {}
|
15
17
|
definition.features.each { |k, v| @features_by_value[v] = k }
|
16
18
|
|
19
|
+
@feature_keys ||= Set[]
|
20
|
+
features.keys.each { |feature_name| @feature_keys << feature_name }
|
21
|
+
|
17
22
|
self
|
18
23
|
end
|
19
24
|
|
@@ -44,11 +49,16 @@ module Musa
|
|
44
49
|
@features_by_value.keys
|
45
50
|
end
|
46
51
|
|
52
|
+
def self.feature_keys
|
53
|
+
@feature_keys
|
54
|
+
end
|
55
|
+
|
47
56
|
def initialize(name, offsets:, **features)
|
48
|
-
@name = name
|
49
|
-
@features = features.
|
50
|
-
@pitch_offsets = offsets.
|
51
|
-
@pitch_names = offsets.collect { |k, v| [v, k] }.to_h
|
57
|
+
@name = name.freeze
|
58
|
+
@features = features.transform_values(&:dup).transform_values(&:freeze).freeze
|
59
|
+
@pitch_offsets = offsets.dup.freeze
|
60
|
+
@pitch_names = offsets.collect { |k, v| [v, k] }.to_h.freeze
|
61
|
+
freeze
|
52
62
|
end
|
53
63
|
|
54
64
|
attr_reader :name, :features, :pitch_offsets, :pitch_names
|
@@ -57,6 +67,10 @@ module Musa
|
|
57
67
|
@pitch_offsets.values.collect { |offset| root_pitch + offset }
|
58
68
|
end
|
59
69
|
|
70
|
+
def in_scale?(scale, chord_root_pitch:)
|
71
|
+
!pitches(chord_root_pitch).find { |chord_pitch| scale.note_of_pitch(chord_pitch).nil? }
|
72
|
+
end
|
73
|
+
|
60
74
|
def named_pitches(elements_or_pitches, &block)
|
61
75
|
pitches = elements_or_pitches.collect do |element_or_pitch|
|
62
76
|
[if block_given?
|
@@ -93,7 +107,7 @@ module Musa
|
|
93
107
|
|
94
108
|
alias to_s inspect
|
95
109
|
|
96
|
-
|
110
|
+
private
|
97
111
|
|
98
112
|
def octave_reduce(pitches)
|
99
113
|
pitches.collect { |p| p % 12 }
|
@@ -1,15 +1,23 @@
|
|
1
1
|
require_relative 'chord-definition'
|
2
2
|
|
3
|
+
# TODO trasladar los acordes de https://en.wikipedia.org/wiki/Chord_notation
|
4
|
+
|
3
5
|
Musa::Chords::ChordDefinition.register :maj, quality: :major, size: :triad, offsets: { root: 0, third: 4, fifth: 7 }
|
4
6
|
Musa::Chords::ChordDefinition.register :min, quality: :minor, size: :triad, offsets: { root: 0, third: 3, fifth: 7 }
|
7
|
+
Musa::Chords::ChordDefinition.register :dim, quality: :diminished, size: :triad, offsets: { root: 0, third: 3, fifth: 3 }
|
8
|
+
Musa::Chords::ChordDefinition.register :aug, quality: :augmented, size: :triad, offsets: { root: 0, third: 4, fifth: 8 }
|
5
9
|
|
6
10
|
Musa::Chords::ChordDefinition.register :maj7, quality: :major, size: :seventh, offsets: { root: 0, third: 4, fifth: 7, seventh: 11 }
|
7
|
-
Musa::Chords::ChordDefinition.register :maj7, quality: :major, size: :seventh, dominant: :dominant , offsets: { root: 0, third: 4, fifth: 7, seventh: 10 }
|
8
|
-
|
9
11
|
Musa::Chords::ChordDefinition.register :min7, quality: :minor, size: :seventh, offsets: { root: 0, third: 3, fifth: 7, seventh: 11 }
|
10
12
|
|
13
|
+
Musa::Chords::ChordDefinition.register :dom7, quality: :dominant, size: :seventh, offsets: { root: 0, third: 4, fifth: 7, seventh: 10 }
|
14
|
+
|
11
15
|
Musa::Chords::ChordDefinition.register :maj9, quality: :major, size: :ninth, offsets: { root: 0, third: 4, fifth: 7, seventh: 11, ninth: 14 }
|
12
16
|
Musa::Chords::ChordDefinition.register :min9, quality: :minor, size: :ninth, offsets: { root: 0, third: 3, fifth: 7, seventh: 11, ninth: 14 }
|
17
|
+
Musa::Chords::ChordDefinition.register :dom9, quality: :dominant, size: :ninth, offsets: { root: 0, third: 4, fifth: 7, seventh: 10, ninth: 14 }
|
13
18
|
|
14
19
|
Musa::Chords::ChordDefinition.register :maj11, quality: :major, size: :eleventh, offsets: { root: 0, third: 4, fifth: 7, seventh: 11, ninth: 14, eleventh: 17 }
|
15
20
|
Musa::Chords::ChordDefinition.register :min11, quality: :minor, size: :eleventh, offsets: { root: 0, third: 3, fifth: 7, seventh: 11, ninth: 14, eleventh: 17 }
|
21
|
+
|
22
|
+
Musa::Chords::ChordDefinition.register :maj13, quality: :major, size: :eleventh, offsets: { root: 0, third: 4, fifth: 7, seventh: 11, ninth: 14, eleventh: 17 }
|
23
|
+
Musa::Chords::ChordDefinition.register :min13, quality: :minor, size: :eleventh, offsets: { root: 0, third: 3, fifth: 7, seventh: 11, ninth: 14, eleventh: 17 }
|
@@ -3,319 +3,213 @@ require_relative 'chord-definition'
|
|
3
3
|
|
4
4
|
module Musa
|
5
5
|
module Chords
|
6
|
-
using Musa::Extension::Arrayfy
|
7
|
-
|
8
6
|
class Chord
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
position: nil,
|
21
|
-
move: nil,
|
22
|
-
duplicate: nil,
|
23
|
-
add: nil,
|
24
|
-
drop: nil,
|
25
|
-
#
|
26
|
-
_source: nil)
|
27
|
-
|
28
|
-
# Preparing notes and pitches Arrays: they will we used to collect further notes and pitches
|
29
|
-
#
|
30
|
-
if notes
|
31
|
-
notes = notes.collect do |n|
|
32
|
-
case n
|
33
|
-
when Scales::NoteInScale
|
34
|
-
n
|
35
|
-
when Numeric, Symbol
|
36
|
-
scale[n]
|
7
|
+
|
8
|
+
using Musa::Extension::Arrayfy
|
9
|
+
|
10
|
+
def self.with_root(root_note_or_pitch_or_symbol, scale: nil, allow_chromatic: false, name: nil, move: nil, duplicate: nil, **features)
|
11
|
+
root =
|
12
|
+
case root_note_or_pitch_or_symbol
|
13
|
+
when Scales::NoteInScale
|
14
|
+
root_note_or_pitch_or_symbol
|
15
|
+
when Numeric
|
16
|
+
if scale
|
17
|
+
scale.note_of_pitch(root_note_or_pitch_or_symbol, allow_chromatic: allow_chromatic)
|
37
18
|
else
|
38
|
-
|
19
|
+
scale = Musa::Scales::Scales.default_system.default_tuning[root_note_or_pitch_or_symbol].major
|
20
|
+
scale.note_of_pitch(root_note_or_pitch_or_symbol)
|
39
21
|
end
|
22
|
+
when Symbol
|
23
|
+
raise ArgumentError, "Missing scale parameter to calculate root note for #{root_note_or_pitch_or_symbol}" unless scale
|
24
|
+
|
25
|
+
scale[root_note_or_pitch_or_symbol]
|
26
|
+
else
|
27
|
+
raise ArgumentError, "Unexpected #{root_note_or_pitch_or_symbol}"
|
40
28
|
end
|
41
|
-
end
|
42
29
|
|
43
|
-
|
30
|
+
scale ||= root.scale
|
44
31
|
|
45
|
-
|
46
|
-
|
32
|
+
if name
|
33
|
+
raise ArgumentError, "Received name parameter with value #{name}: features parameter is not allowed" if features.any?
|
47
34
|
|
48
|
-
|
35
|
+
chord_definition = ChordDefinition[name]
|
49
36
|
|
50
|
-
|
37
|
+
elsif features.any?
|
38
|
+
chord_definition = Helper.find_definition_by_features(root.pitch, features, scale, allow_chromatic: allow_chromatic)
|
51
39
|
|
52
|
-
|
40
|
+
else
|
41
|
+
raise ArgumentError, "Don't know how to find a chord definition without name or features parameters"
|
42
|
+
end
|
53
43
|
|
54
|
-
|
55
|
-
|
56
|
-
|
44
|
+
unless chord_definition
|
45
|
+
raise ArgumentError,
|
46
|
+
"Unable to find chord definition for root #{root}" \
|
47
|
+
"#{" with name #{name}" if name}" \
|
48
|
+
"#{" with features #{features}" if features.any?}"
|
57
49
|
end
|
58
50
|
|
59
|
-
|
51
|
+
source_notes_map = Helper.compute_source_notes_map(root, chord_definition, scale)
|
52
|
+
|
53
|
+
Chord.new(root, scale, chord_definition, move, duplicate, source_notes_map)
|
54
|
+
end
|
60
55
|
|
61
|
-
|
56
|
+
class Helper
|
57
|
+
def self.compute_source_notes_map(root, chord_definition, scale)
|
58
|
+
chord_definition.pitch_offsets.transform_values do |offset|
|
59
|
+
pitch = root.pitch + offset
|
60
|
+
[scale.note_of_pitch(pitch) || scale.chromatic.note_of_pitch(pitch)]
|
61
|
+
end.tap { |_| _.values.each(&:freeze) }.freeze
|
62
|
+
end
|
62
63
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
name = name_or_notes_or_pitches
|
71
|
-
|
72
|
-
when Array
|
73
|
-
name_or_notes_or_pitches.each do |note_or_pitch|
|
74
|
-
case note_or_pitch
|
75
|
-
when Scales::NoteInScale
|
76
|
-
notes ||= [] << note_or_pitch
|
77
|
-
when Numeric
|
78
|
-
if scale
|
79
|
-
notes ||= [] << scale[note_or_pitch]
|
80
|
-
else
|
81
|
-
pitches ||= [] << note_or_pitch
|
82
|
-
end
|
83
|
-
when Symbol
|
84
|
-
raise ArgumentError, "Don't know how to recognize #{note_or_pitch} in parameter list #{name_or_notes_or_pitches}: it's a symbol but the scale is not provided" unless scale
|
85
|
-
|
86
|
-
notes ||= [] << scale[note_or_pitch]
|
87
|
-
else
|
88
|
-
raise ArgumentError, "Can't recognize #{note_or_pitch} in parameter list #{name_or_notes_or_pitches}"
|
64
|
+
def self.find_definition_by_features(root_pitch, features, scale, allow_chromatic:)
|
65
|
+
featured_chord_definitions = ChordDefinition.find_by_features(**features)
|
66
|
+
|
67
|
+
unless allow_chromatic
|
68
|
+
featured_chord_definitions.reject! do |chord_definition|
|
69
|
+
chord_definition.pitches(root_pitch).find { |chord_pitch| scale.note_of_pitch(chord_pitch).nil? }
|
89
70
|
end
|
90
71
|
end
|
91
72
|
|
92
|
-
|
93
|
-
# nothing happens
|
94
|
-
else
|
95
|
-
raise ArgumentError, "Can't recognize #{name_or_notes_or_pitches}"
|
73
|
+
featured_chord_definitions.first
|
96
74
|
end
|
75
|
+
end
|
97
76
|
|
98
|
-
|
99
|
-
#
|
100
|
-
|
101
|
-
@notes = if _source.nil?
|
102
|
-
compute_notes(name, root_pitch, scale, notes, pitches, features, allow_chromatic)
|
103
|
-
else
|
104
|
-
compute_notes_from_source(_source, name, root_pitch, scale, notes, pitches, features, allow_chromatic)
|
105
|
-
end
|
77
|
+
private_constant :Helper
|
106
78
|
|
107
|
-
|
108
|
-
#
|
79
|
+
ChordGradeNote = Struct.new(:grade, :note, keyword_init: true)
|
109
80
|
|
110
|
-
|
111
|
-
case to_add
|
112
|
-
when NoteInScale
|
113
|
-
@notes << to_add
|
114
|
-
when Numeric # pitch increment
|
115
|
-
pitch = root_pitch + to_add
|
116
|
-
@notes << scale.note_of_pitch(pitch) || scale.chromatic.note_of_pitch(pitch)
|
117
|
-
when Symbol # interval name
|
118
|
-
pitch = root_pitch + scale.offset_of_interval(to_add)
|
119
|
-
@notes << scale.note_of_pitch(pitch)
|
120
|
-
else
|
121
|
-
raise ArgumentError, "Can't recognize element to add #{to_add}"
|
122
|
-
end
|
123
|
-
end
|
81
|
+
private_constant :ChordGradeNote
|
124
82
|
|
125
|
-
|
126
|
-
|
127
|
-
|
83
|
+
private def initialize(root, scale, chord_definition, move, duplicate, source_notes_map)
|
84
|
+
@root = root
|
85
|
+
@scale = scale
|
86
|
+
@chord_definition = chord_definition
|
87
|
+
@move = move.dup.freeze || {}
|
88
|
+
# TODO: ojo esto implica que sólo se puede duplicar una vez cada grado! permitir múltiples?
|
89
|
+
@duplicate = duplicate.dup.freeze || {}
|
90
|
+
@source_notes_map = source_notes_map.dup.freeze
|
91
|
+
@notes_map = compute_moved_and_duplicated(source_notes_map, move, duplicate)
|
128
92
|
|
129
|
-
#
|
93
|
+
# Calculate sorted notes: from lower to higher notes
|
130
94
|
#
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
move.each do |position, octave|
|
136
|
-
@notes[position][0] = @notes[position][0].octave(octave)
|
95
|
+
@sorted_notes = []
|
96
|
+
@notes_map.each_pair do |name, array_of_notes|
|
97
|
+
array_of_notes.each do |note|
|
98
|
+
@sorted_notes << ChordGradeNote.new(grade: name, note: note).freeze
|
137
99
|
end
|
138
100
|
end
|
139
101
|
|
140
|
-
|
141
|
-
|
102
|
+
@sorted_notes.sort_by! { |chord_grade_note| chord_grade_note.note.pitch }
|
103
|
+
@sorted_notes.freeze
|
142
104
|
|
143
|
-
|
144
|
-
|
145
|
-
|
105
|
+
# Add getters for grades
|
106
|
+
#
|
107
|
+
@notes_map.each_key do |chord_grade_name|
|
108
|
+
define_singleton_method chord_grade_name do |all: false|
|
109
|
+
if all
|
110
|
+
@notes_map[chord_grade_name]
|
111
|
+
else
|
112
|
+
@notes_map[chord_grade_name].first
|
146
113
|
end
|
147
114
|
end
|
148
115
|
end
|
149
116
|
|
150
|
-
#
|
117
|
+
# Add getters for the features values
|
151
118
|
#
|
119
|
+
@chord_definition.features.each_key do |feature_name|
|
120
|
+
define_singleton_method feature_name do
|
121
|
+
@chord_definition.features[feature_name]
|
122
|
+
end
|
123
|
+
end
|
152
124
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
define_singleton_method name do
|
159
|
-
featuring(name)
|
125
|
+
# Add navigation methods to other chords based on changing a feature
|
126
|
+
#
|
127
|
+
ChordDefinition.feature_keys.each do |feature_name|
|
128
|
+
define_singleton_method "with_#{feature_name}".to_sym do |feature_value, allow_chromatic: true|
|
129
|
+
featuring(allow_chromatic: allow_chromatic, **{ feature_name => feature_value })
|
160
130
|
end
|
161
131
|
end
|
162
132
|
end
|
163
133
|
|
164
|
-
attr_reader :
|
134
|
+
attr_reader :scale, :chord_definition, :move, :duplicate
|
165
135
|
|
166
|
-
def
|
167
|
-
|
168
|
-
@chord_definition&.name
|
169
|
-
else
|
170
|
-
Chord.new(_source: self, name: name)
|
171
|
-
end
|
136
|
+
def notes
|
137
|
+
@sorted_notes
|
172
138
|
end
|
173
139
|
|
174
|
-
def
|
175
|
-
@
|
140
|
+
def pitches(*grades)
|
141
|
+
grades = @notes_map.keys if grades.empty?
|
142
|
+
@sorted_notes.select { |_| grades.include?(_.grade) }.collect { |_| _.note.pitch }
|
176
143
|
end
|
177
144
|
|
178
|
-
def
|
179
|
-
|
180
|
-
|
145
|
+
def features
|
146
|
+
@chord_definition.features
|
147
|
+
end
|
181
148
|
|
149
|
+
def featuring(*values, allow_chromatic: false, **hash)
|
150
|
+
# create a new list of features based on current features but
|
151
|
+
# replacing the values for the new ones and adding the new features
|
152
|
+
#
|
153
|
+
features = @chord_definition.features.dup
|
182
154
|
ChordDefinition.features_from(values, hash).each { |k, v| features[k] = v }
|
183
155
|
|
184
|
-
|
185
|
-
end
|
156
|
+
chord_definition = Helper.find_definition_by_features(@root.pitch, features, @scale, allow_chromatic: allow_chromatic)
|
186
157
|
|
187
|
-
|
188
|
-
if root.nil?
|
189
|
-
@notes[:root]
|
190
|
-
else
|
191
|
-
Chord.new(_source: self, root: root)
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
def [](position)
|
196
|
-
case position
|
197
|
-
when Numeric
|
198
|
-
@notes.values[position]
|
199
|
-
when Symbol
|
200
|
-
@notes[position]
|
201
|
-
end
|
202
|
-
end
|
158
|
+
raise ArgumentError, "Unable to find a chord definition for #{features}" unless chord_definition
|
203
159
|
|
204
|
-
|
205
|
-
Chord.new(_source: self, move: octaves)
|
206
|
-
end
|
160
|
+
source_notes_map = Helper.compute_source_notes_map(@root, chord_definition, @scale)
|
207
161
|
|
208
|
-
|
209
|
-
|
162
|
+
Chord.new(@root,
|
163
|
+
(@scale if chord_definition.in_scale?(@scale, chord_root_pitch: @root.pitch)),
|
164
|
+
chord_definition,
|
165
|
+
@move, @duplicate,
|
166
|
+
source_notes_map)
|
210
167
|
end
|
211
168
|
|
212
|
-
def
|
213
|
-
|
214
|
-
|
215
|
-
|
169
|
+
def octave(octave)
|
170
|
+
source_notes_map = @source_notes_map.transform_values do |notes|
|
171
|
+
notes.collect { |note| note.octave(octave) }.freeze
|
172
|
+
end.freeze
|
216
173
|
|
217
|
-
|
218
|
-
def as_scale
|
174
|
+
Chord.new(@root.octave(octave), @scale, chord_definition, @move, @duplicate, source_notes_map)
|
219
175
|
end
|
220
176
|
|
221
|
-
def
|
222
|
-
|
223
|
-
allow_chromatic ||= false
|
224
|
-
|
225
|
-
note_sets = {}
|
226
|
-
scales.each do |scale|
|
227
|
-
note_sets[scale] = if allow_chromatic
|
228
|
-
@notes.values.flatten(1).collect { |n| n.on(scale) || n.on(scale.chromatic) }
|
229
|
-
else
|
230
|
-
@notes.values.flatten(1).collect { |n| n.on(scale) }
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
|
-
note_sets_in_scale = note_sets.values.reject { |notes| notes.include?(nil) }
|
235
|
-
note_sets_in_scale.collect { |notes| Chord.new(notes: notes) }
|
177
|
+
def move(**octaves)
|
178
|
+
Chord.new(@root, @scale, @chord_definition, @move.merge(octaves), @duplicate, @source_notes_map)
|
236
179
|
end
|
237
180
|
|
238
|
-
def
|
239
|
-
|
240
|
-
project_on_all(*scales, allow_chromatic: allow_chromatic).first
|
181
|
+
def duplicate(**octaves)
|
182
|
+
Chord.new(@root, @scale, @chord_definition, @move, @duplicate.merge(octaves), @source_notes_map)
|
241
183
|
end
|
242
184
|
|
243
185
|
def ==(other)
|
244
|
-
self.class == other.class &&
|
186
|
+
self.class == other.class &&
|
187
|
+
@sorted_notes == other.notes &&
|
188
|
+
@chord_definition == other.chord_definition
|
245
189
|
end
|
246
190
|
|
247
191
|
def inspect
|
248
|
-
"<Chord
|
192
|
+
"<Chord #{@name} root #{@root} notes #{@sorted_notes.collect { |_| "#{_.grade}=#{_.note.grade}|#{_.note.pitch} "} }>"
|
249
193
|
end
|
250
194
|
|
251
195
|
alias to_s inspect
|
252
196
|
|
253
|
-
private
|
254
|
-
|
255
|
-
def compute_notes(name, root_pitch, scale, notes, pitches, features, allow_chromatic)
|
256
|
-
if name && root_pitch && scale && !(notes || pitches || features)
|
257
|
-
|
258
|
-
chord_definition = ChordDefinition[name]
|
259
|
-
|
260
|
-
raise ArgumentError, "Unrecognized #{name} chord" unless chord_definition
|
261
|
-
|
262
|
-
chord_definition.pitch_offsets.transform_values do |offset|
|
263
|
-
pitch = root_pitch + offset
|
264
|
-
[scale.note_of_pitch(pitch) || scale.chromatic.note_of_pitch(pitch)]
|
265
|
-
end
|
266
|
-
|
267
|
-
elsif root_pitch && features && scale && !(name || notes || pitches)
|
197
|
+
private def compute_moved_and_duplicated(notes_map, moved, duplicated)
|
198
|
+
notes_map = notes_map.transform_values(&:dup)
|
268
199
|
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
chord_definitions.reject! do |chord_definition|
|
273
|
-
chord_definition.pitches(root_pitch).find { |chord_pitch| scale.note_of_pitch(chord_pitch).nil? }
|
274
|
-
end
|
275
|
-
end
|
276
|
-
|
277
|
-
selected = chord_definitions.first
|
278
|
-
|
279
|
-
unless selected
|
280
|
-
raise ArgumentError, "Don't know how to create a chord with root pitch #{root_pitch}"\
|
281
|
-
" and features #{features} based on scale #{scale.kind.class} with root on #{scale.root}: "\
|
282
|
-
" no suitable definition found (allow_chromatic is #{allow_chromatic})"
|
283
|
-
end
|
200
|
+
moved&.each do |position, octave|
|
201
|
+
notes_map[position][0] = notes_map[position][0].octave(octave)
|
202
|
+
end
|
284
203
|
|
285
|
-
|
286
|
-
|
287
|
-
[
|
204
|
+
duplicated&.each do |position, octave|
|
205
|
+
octave.arrayfy.each do |octave|
|
206
|
+
notes_map[position] << notes_map[position][0].octave(octave)
|
288
207
|
end
|
289
|
-
|
290
|
-
elsif (notes || pitches && scale) && !(name || root_pitch || features)
|
291
|
-
|
292
|
-
notes ||= []
|
293
|
-
|
294
|
-
notes += pitches.collect { |p| scale.note_of_pitch(p) } if pitches
|
295
|
-
|
296
|
-
chord_definition = ChordDefinition.find_by_pitches(notes.collect(&:pitch))
|
297
|
-
|
298
|
-
raise "Can't find a chord definition for pitches #{pitches} on scale #{scale.kind.id} based on #{scale.root}" unless chord_definition
|
299
|
-
|
300
|
-
chord_definition.named_pitches(notes, &:pitch)
|
301
|
-
else
|
302
|
-
pattern = { name: name, root: root_pitch, scale: scale, notes: notes, pitches: pitches, features: features, allow_chromatic: allow_chromatic }
|
303
|
-
raise ArgumentError, "Can't understand chord definition pattern #{pattern}"
|
304
208
|
end
|
305
|
-
end
|
306
209
|
|
307
|
-
|
308
|
-
if !(name || root_pitch || scale || notes || pitches || features)
|
309
|
-
source.notes
|
310
|
-
|
311
|
-
elsif features && !(name || root_pitch || scale || notes || pitches)
|
312
|
-
compute_notes(nil, source.root.first.pitch, source.root.first.scale, nil, nil, features, allow_chromatic)
|
313
|
-
|
314
|
-
else
|
315
|
-
pattern = { name: name, root: root_pitch, scale: scale, notes: notes, pitches: pitches, features: features, allow_chromatic: allow_chromatic }
|
316
|
-
raise ArgumentError, "Can't understand chord definition pattern #{pattern}"
|
317
|
-
end
|
210
|
+
notes_map.tap { |_| _.values.each(&:freeze) }.freeze
|
318
211
|
end
|
319
212
|
end
|
320
213
|
end
|
321
214
|
end
|
215
|
+
|
@@ -70,31 +70,31 @@ module Musa
|
|
70
70
|
class MajorScaleKind < ScaleKind
|
71
71
|
class << self
|
72
72
|
@@pitches =
|
73
|
-
[{ functions: %i[I _1 tonic],
|
73
|
+
[{ functions: %i[I _1 tonic first],
|
74
74
|
pitch: 0 },
|
75
|
-
{ functions: %i[II _2 supertonic],
|
75
|
+
{ functions: %i[II _2 supertonic second],
|
76
76
|
pitch: 2 },
|
77
|
-
{ functions: %i[III _3 mediant],
|
77
|
+
{ functions: %i[III _3 mediant third],
|
78
78
|
pitch: 4 },
|
79
|
-
{ functions: %i[IV _4 subdominant],
|
79
|
+
{ functions: %i[IV _4 subdominant fourth],
|
80
80
|
pitch: 5 },
|
81
|
-
{ functions: %i[V _5 dominant],
|
81
|
+
{ functions: %i[V _5 dominant fifth],
|
82
82
|
pitch: 7 },
|
83
|
-
{ functions: %i[VI _6 submediant relative relative_minor],
|
83
|
+
{ functions: %i[VI _6 submediant relative relative_minor sixth],
|
84
84
|
pitch: 9 },
|
85
|
-
{ functions: %i[VII _7 leading],
|
85
|
+
{ functions: %i[VII _7 leading seventh],
|
86
86
|
pitch: 11 },
|
87
|
-
{ functions: %i[VIII _8],
|
87
|
+
{ functions: %i[VIII _8 eighth],
|
88
88
|
pitch: 12 },
|
89
|
-
{ functions: %i[IX _9],
|
89
|
+
{ functions: %i[IX _9 ninth],
|
90
90
|
pitch: 12 + 2 },
|
91
|
-
{ functions: %i[X _10],
|
91
|
+
{ functions: %i[X _10 tenth],
|
92
92
|
pitch: 12 + 4 },
|
93
|
-
{ functions: %i[XI _11],
|
93
|
+
{ functions: %i[XI _11 eleventh],
|
94
94
|
pitch: 12 + 5 },
|
95
|
-
{ functions: %i[XII _12],
|
95
|
+
{ functions: %i[XII _12 twelfth],
|
96
96
|
pitch: 12 + 7 },
|
97
|
-
{ functions: %i[XIII _13],
|
97
|
+
{ functions: %i[XIII _13 thirteenth],
|
98
98
|
pitch: 12 + 9 }].freeze
|
99
99
|
|
100
100
|
def pitches
|
@@ -113,34 +113,34 @@ module Musa
|
|
113
113
|
EquallyTempered12ToneScaleSystem.register MajorScaleKind
|
114
114
|
end
|
115
115
|
|
116
|
-
class
|
116
|
+
class MinorNaturalScaleKind < ScaleKind
|
117
117
|
class << self
|
118
118
|
@@pitches =
|
119
|
-
[{ functions: %i[i _1 tonic],
|
119
|
+
[{ functions: %i[i _1 tonic first],
|
120
120
|
pitch: 0 },
|
121
|
-
{ functions: %i[ii _2 supertonic],
|
121
|
+
{ functions: %i[ii _2 supertonic second],
|
122
122
|
pitch: 2 },
|
123
|
-
{ functions: %i[iii _3 mediant relative relative_major],
|
123
|
+
{ functions: %i[iii _3 mediant relative relative_major third],
|
124
124
|
pitch: 3 },
|
125
|
-
{ functions: %i[iv _4 subdominant],
|
125
|
+
{ functions: %i[iv _4 subdominant fourth],
|
126
126
|
pitch: 5 },
|
127
|
-
{ functions: %i[v _5 dominant],
|
127
|
+
{ functions: %i[v _5 dominant fifth],
|
128
128
|
pitch: 7 },
|
129
|
-
{ functions: %i[vi _6 submediant],
|
129
|
+
{ functions: %i[vi _6 submediant sixth],
|
130
130
|
pitch: 8 },
|
131
|
-
{ functions: %i[vii _7],
|
131
|
+
{ functions: %i[vii _7 seventh],
|
132
132
|
pitch: 10 },
|
133
|
-
{ functions: %i[viii _8],
|
133
|
+
{ functions: %i[viii _8 eighth],
|
134
134
|
pitch: 12 },
|
135
|
-
{ functions: %i[ix _9],
|
135
|
+
{ functions: %i[ix _9 ninth],
|
136
136
|
pitch: 12 + 2 },
|
137
|
-
{ functions: %i[x _10],
|
137
|
+
{ functions: %i[x _10 tenth],
|
138
138
|
pitch: 12 + 3 },
|
139
|
-
{ functions: %i[xi _11],
|
139
|
+
{ functions: %i[xi _11 eleventh],
|
140
140
|
pitch: 12 + 5 },
|
141
|
-
{ functions: %i[xii _12],
|
141
|
+
{ functions: %i[xii _12 twelfth],
|
142
142
|
pitch: 12 + 7 },
|
143
|
-
{ functions: %i[xiii _13],
|
143
|
+
{ functions: %i[xiii _13 thirteenth],
|
144
144
|
pitch: 12 + 8 }].freeze
|
145
145
|
|
146
146
|
def pitches
|
@@ -156,7 +156,7 @@ module Musa
|
|
156
156
|
end
|
157
157
|
end
|
158
158
|
|
159
|
-
EquallyTempered12ToneScaleSystem.register
|
159
|
+
EquallyTempered12ToneScaleSystem.register MinorNaturalScaleKind
|
160
160
|
end
|
161
161
|
|
162
162
|
class MinorHarmonicScaleKind < ScaleKind
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require_relative 'chords'
|
2
|
+
|
1
3
|
module Musa
|
2
4
|
module Scales
|
3
5
|
module Scales
|
@@ -180,8 +182,6 @@ module Musa
|
|
180
182
|
end
|
181
183
|
|
182
184
|
class ScaleKind
|
183
|
-
extend Forwardable
|
184
|
-
|
185
185
|
def initialize(tuning)
|
186
186
|
@tuning = tuning
|
187
187
|
@scales = {}
|
@@ -194,6 +194,10 @@ module Musa
|
|
194
194
|
@scales[root_pitch]
|
195
195
|
end
|
196
196
|
|
197
|
+
def default_root
|
198
|
+
self[60]
|
199
|
+
end
|
200
|
+
|
197
201
|
def absolut
|
198
202
|
self[0]
|
199
203
|
end
|
@@ -277,6 +281,7 @@ module Musa
|
|
277
281
|
end
|
278
282
|
end
|
279
283
|
|
284
|
+
freeze
|
280
285
|
end
|
281
286
|
|
282
287
|
def_delegators :@kind, :a_tuning
|
@@ -406,10 +411,6 @@ module Musa
|
|
406
411
|
@kind.tuning.offset_of_interval(interval_name)
|
407
412
|
end
|
408
413
|
|
409
|
-
def chord_of(*grades_or_symbols)
|
410
|
-
Chord.new(notes: grades_or_symbols.collect { |g| self[g] })
|
411
|
-
end
|
412
|
-
|
413
414
|
def ==(other)
|
414
415
|
self.class == other.class &&
|
415
416
|
@kind == other.kind &&
|
@@ -454,13 +455,13 @@ module Musa
|
|
454
455
|
@scale.kind.class.pitches[grade][:functions]
|
455
456
|
end
|
456
457
|
|
457
|
-
def octave(octave = nil)
|
458
|
+
def octave(octave = nil, absolute: false)
|
458
459
|
if octave.nil?
|
459
460
|
@octave
|
460
461
|
else
|
461
462
|
raise ArgumentError, "#{octave} is not integer" unless octave == octave.to_i
|
462
463
|
|
463
|
-
@scale[@grade + (@octave + octave) * @scale.kind.class.grades]
|
464
|
+
@scale[@grade + ((absolute ? 0 : @octave) + octave) * @scale.kind.class.grades]
|
464
465
|
end
|
465
466
|
end
|
466
467
|
|
@@ -558,11 +559,20 @@ module Musa
|
|
558
559
|
scale.note_of_pitch @pitch
|
559
560
|
end
|
560
561
|
|
561
|
-
def chord(*feature_values,
|
562
|
+
def chord(*feature_values,
|
563
|
+
allow_chromatic: nil,
|
564
|
+
move: nil,
|
565
|
+
duplicate: nil,
|
566
|
+
**features_hash)
|
567
|
+
|
562
568
|
features = { size: :triad } if feature_values.empty? && features_hash.empty?
|
563
|
-
features ||= ChordDefinition.features_from(feature_values, features_hash)
|
569
|
+
features ||= Musa::Chords::ChordDefinition.features_from(feature_values, features_hash)
|
564
570
|
|
565
|
-
Musa::Chords::Chord.
|
571
|
+
Musa::Chords::Chord.with_root(self,
|
572
|
+
allow_chromatic: allow_chromatic,
|
573
|
+
move: move,
|
574
|
+
duplicate: duplicate,
|
575
|
+
**features)
|
566
576
|
end
|
567
577
|
|
568
578
|
def ==(other)
|
data/lib/musa-dsl/repl/repl.rb
CHANGED
@@ -113,9 +113,11 @@ module Musa
|
|
113
113
|
@client_threads.clear
|
114
114
|
end
|
115
115
|
|
116
|
-
def puts(
|
116
|
+
def puts(*messages)
|
117
117
|
if @connection
|
118
|
-
|
118
|
+
messages.each do |message|
|
119
|
+
send output: @connection, content: message&.to_s
|
120
|
+
end
|
119
121
|
else
|
120
122
|
@logger.warn('REPL') { "trying to print a message in Atom client but the client is not connected. Ignoring message \'#{message} \'." }
|
121
123
|
end
|
@@ -45,7 +45,12 @@ module Musa::Sequencer
|
|
45
45
|
case operation[:current_operation]
|
46
46
|
when :none
|
47
47
|
when :block
|
48
|
-
|
48
|
+
# duplicating parameters as direct object value (operation[:current_parameter])
|
49
|
+
# and key_passed parameters (**operation[:current_parameter])
|
50
|
+
#
|
51
|
+
__play_eval.block_procedure_binder.call operation[:current_parameter],
|
52
|
+
**operation[:current_parameter],
|
53
|
+
control: control
|
49
54
|
|
50
55
|
when :event
|
51
56
|
control._launch operation[:current_event],
|
@@ -21,10 +21,12 @@ module Musa
|
|
21
21
|
@logger.debug! if do_log
|
22
22
|
end
|
23
23
|
|
24
|
+
@time_table = []
|
24
25
|
@midi_parser = MIDIParser.new
|
25
26
|
end
|
26
27
|
|
27
28
|
attr_reader :input
|
29
|
+
attr_reader :time_table
|
28
30
|
|
29
31
|
def input=(input_midi_port)
|
30
32
|
@input = input_midi_port
|
@@ -121,6 +123,7 @@ module Musa
|
|
121
123
|
case m.name
|
122
124
|
when 'Start'
|
123
125
|
process_start
|
126
|
+
@time_table.clear
|
124
127
|
|
125
128
|
when 'Stop'
|
126
129
|
@logger.debug('InputMidiClock') { 'processing Stop...' }
|
@@ -135,7 +138,15 @@ module Musa
|
|
135
138
|
@logger.debug('InputMidiClock') { 'processing Continue... done' }
|
136
139
|
|
137
140
|
when 'Clock'
|
138
|
-
|
141
|
+
if block_given? && @started
|
142
|
+
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
|
143
|
+
yield
|
144
|
+
finish_time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
|
145
|
+
|
146
|
+
duration = finish_time - start_time
|
147
|
+
@time_table[duration] ||= 0
|
148
|
+
@time_table[duration] += 1
|
149
|
+
end
|
139
150
|
|
140
151
|
when 'Song Position Pointer'
|
141
152
|
new_position_in_midi_beats = m.data[0] & 0x7F | ((m.data[1] & 0x7F) << 7)
|
data/lib/musa-dsl.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module Musa
|
2
|
-
VERSION = '0.26.
|
2
|
+
VERSION = '0.26.6.wip'.freeze
|
3
3
|
end
|
4
4
|
|
5
5
|
require_relative 'musa-dsl/core-ext'
|
@@ -54,7 +54,7 @@ module Musa::All
|
|
54
54
|
|
55
55
|
include Musa::Darwin
|
56
56
|
include Musa::Markov
|
57
|
-
include Musa::
|
57
|
+
include Musa::Rules
|
58
58
|
include Musa::Variatio
|
59
59
|
|
60
60
|
include Musa::MIDIRecorder
|
data/musa-dsl.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'musa-dsl'
|
3
|
-
s.version = '0.26.
|
4
|
-
s.date = '2022-02-
|
3
|
+
s.version = '0.26.6'
|
4
|
+
s.date = '2022-02-26'
|
5
5
|
s.summary = 'A simple Ruby DSL for making complex music'
|
6
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']
|
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.26.
|
4
|
+
version: 0.26.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: 2022-02-
|
11
|
+
date: 2022-02-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: logger
|
@@ -161,10 +161,10 @@ files:
|
|
161
161
|
- lib/musa-dsl/datasets/score/to-mxml/to-mxml.rb
|
162
162
|
- lib/musa-dsl/datasets/v.rb
|
163
163
|
- lib/musa-dsl/generative.rb
|
164
|
-
- lib/musa-dsl/generative/backboner.rb
|
165
164
|
- lib/musa-dsl/generative/darwin.rb
|
166
165
|
- lib/musa-dsl/generative/generative-grammar.rb
|
167
166
|
- lib/musa-dsl/generative/markov.rb
|
167
|
+
- lib/musa-dsl/generative/rules.rb
|
168
168
|
- lib/musa-dsl/generative/variatio.rb
|
169
169
|
- lib/musa-dsl/logger.rb
|
170
170
|
- lib/musa-dsl/logger/logger.rb
|