musa-dsl 0.26.5 → 0.26.6
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/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
|