musa-dsl 0.14.16

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.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/Gemfile +20 -0
  4. data/LICENSE.md +157 -0
  5. data/README.md +8 -0
  6. data/lib/musa-dsl/core-ext/array-apply-get.rb +18 -0
  7. data/lib/musa-dsl/core-ext/array-explode-ranges.rb +29 -0
  8. data/lib/musa-dsl/core-ext/array-to-neumas.rb +28 -0
  9. data/lib/musa-dsl/core-ext/array-to-serie.rb +20 -0
  10. data/lib/musa-dsl/core-ext/arrayfy.rb +15 -0
  11. data/lib/musa-dsl/core-ext/as-context-run.rb +44 -0
  12. data/lib/musa-dsl/core-ext/duplicate.rb +134 -0
  13. data/lib/musa-dsl/core-ext/dynamic-proxy.rb +55 -0
  14. data/lib/musa-dsl/core-ext/inspect-nice.rb +28 -0
  15. data/lib/musa-dsl/core-ext/key-parameters-procedure-binder.rb +85 -0
  16. data/lib/musa-dsl/core-ext/proc-nice.rb +13 -0
  17. data/lib/musa-dsl/core-ext/send-nice.rb +21 -0
  18. data/lib/musa-dsl/core-ext/string-to-neumas.rb +27 -0
  19. data/lib/musa-dsl/core-ext.rb +13 -0
  20. data/lib/musa-dsl/datasets/gdv-decorators.rb +221 -0
  21. data/lib/musa-dsl/datasets/gdv.rb +499 -0
  22. data/lib/musa-dsl/datasets/pdv.rb +44 -0
  23. data/lib/musa-dsl/datasets.rb +5 -0
  24. data/lib/musa-dsl/generative/darwin.rb +145 -0
  25. data/lib/musa-dsl/generative/generative-grammar.rb +294 -0
  26. data/lib/musa-dsl/generative/markov.rb +78 -0
  27. data/lib/musa-dsl/generative/rules.rb +282 -0
  28. data/lib/musa-dsl/generative/variatio.rb +331 -0
  29. data/lib/musa-dsl/generative.rb +5 -0
  30. data/lib/musa-dsl/midi/midi-recorder.rb +83 -0
  31. data/lib/musa-dsl/midi/midi-voices.rb +274 -0
  32. data/lib/musa-dsl/midi.rb +2 -0
  33. data/lib/musa-dsl/music/chord-definition.rb +99 -0
  34. data/lib/musa-dsl/music/chord-definitions.rb +13 -0
  35. data/lib/musa-dsl/music/chords.rb +326 -0
  36. data/lib/musa-dsl/music/equally-tempered-12-tone-scale-system.rb +204 -0
  37. data/lib/musa-dsl/music/scales.rb +584 -0
  38. data/lib/musa-dsl/music.rb +6 -0
  39. data/lib/musa-dsl/neuma/neuma.rb +181 -0
  40. data/lib/musa-dsl/neuma.rb +1 -0
  41. data/lib/musa-dsl/neumalang/neumalang.citrus +294 -0
  42. data/lib/musa-dsl/neumalang/neumalang.rb +179 -0
  43. data/lib/musa-dsl/neumalang.rb +3 -0
  44. data/lib/musa-dsl/repl/repl.rb +143 -0
  45. data/lib/musa-dsl/repl.rb +1 -0
  46. data/lib/musa-dsl/sequencer/base-sequencer-implementation-control.rb +189 -0
  47. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +354 -0
  48. data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +382 -0
  49. data/lib/musa-dsl/sequencer/base-sequencer-public.rb +261 -0
  50. data/lib/musa-dsl/sequencer/sequencer-dsl.rb +94 -0
  51. data/lib/musa-dsl/sequencer/sequencer.rb +3 -0
  52. data/lib/musa-dsl/sequencer.rb +1 -0
  53. data/lib/musa-dsl/series/base-series.rb +245 -0
  54. data/lib/musa-dsl/series/hash-serie-splitter.rb +194 -0
  55. data/lib/musa-dsl/series/holder-serie.rb +87 -0
  56. data/lib/musa-dsl/series/main-serie-constructors.rb +726 -0
  57. data/lib/musa-dsl/series/main-serie-operations.rb +1151 -0
  58. data/lib/musa-dsl/series/proxy-serie.rb +69 -0
  59. data/lib/musa-dsl/series/queue-serie.rb +94 -0
  60. data/lib/musa-dsl/series/series.rb +8 -0
  61. data/lib/musa-dsl/series.rb +1 -0
  62. data/lib/musa-dsl/transport/clock.rb +36 -0
  63. data/lib/musa-dsl/transport/dummy-clock.rb +47 -0
  64. data/lib/musa-dsl/transport/external-tick-clock.rb +31 -0
  65. data/lib/musa-dsl/transport/input-midi-clock.rb +124 -0
  66. data/lib/musa-dsl/transport/timer-clock.rb +102 -0
  67. data/lib/musa-dsl/transport/timer.rb +40 -0
  68. data/lib/musa-dsl/transport/transport.rb +137 -0
  69. data/lib/musa-dsl/transport.rb +9 -0
  70. data/lib/musa-dsl.rb +17 -0
  71. data/musa-dsl.gemspec +17 -0
  72. metadata +174 -0
@@ -0,0 +1,294 @@
1
+ module Musa
2
+ module GenerativeGrammar
3
+
4
+ def N(content = nil, **attributes, &block)
5
+ if block_given? && content.nil?
6
+ BlockNode.new(attributes, &block)
7
+ else
8
+ FinalNode.new(content, attributes)
9
+ end
10
+ end
11
+
12
+ class OptionElement
13
+ attr_reader :content, :attributes
14
+
15
+ def initialize(content, attributes = nil)
16
+ @content = content
17
+ @attributes = attributes || {}
18
+ end
19
+ end
20
+
21
+ class Node
22
+ def or(other)
23
+ OrNode.new(self, other)
24
+ end
25
+
26
+ alias_method :|, :or
27
+
28
+ def repeat(exactly = nil, min: nil, max: nil)
29
+ raise ArgumentError, 'Only exactly value or min/max values are allowed' if exactly && (min || max)
30
+
31
+ min = max = exactly if exactly
32
+
33
+ if min && min > 0
34
+ pre = self
35
+
36
+ (min - 1).times do
37
+ pre += self
38
+ end
39
+ end
40
+
41
+ if pre && max == min
42
+ pre
43
+ elsif pre && max > min
44
+ pre + RepeatNode.new(self, max - min)
45
+ else
46
+ RepeatNode.new(self, max)
47
+ end
48
+ end
49
+
50
+ def limit(attribute = nil, after_collect_operation = nil, comparison_method = nil, comparison_value = nil, &block)
51
+ raise ArgumentError, 'Cannot use simplified arguments and yield block at the same time' if (attribute || after_collect_operation || comparison_method || comparison_value) && @block
52
+
53
+ block ||= generate_simple_condition_block(attribute, after_collect_operation, comparison_method, comparison_value)
54
+
55
+ ConditionNode.new(self, &block)
56
+ end
57
+
58
+ def next(other)
59
+ NextNode.new(self, other)
60
+ end
61
+
62
+ alias_method :+, :next
63
+
64
+ def options(attribute = nil,
65
+ after_collect_operation = nil,
66
+ comparison_method = nil,
67
+ comparison_value = nil,
68
+ raw: nil,
69
+ content: nil,
70
+ &condition)
71
+
72
+ raise ArgumentError, 'Cannot use simplified arguments and yield block at the same time' if (attribute || after_collect_operation || comparison_method || comparison_value) && @condition
73
+ raise ArgumentError, 'Cannot use raw: true and content: option at the same time' if raw && content
74
+
75
+ raw ||= false
76
+ content ||= :itself
77
+
78
+ condition ||= generate_simple_condition_block(attribute, after_collect_operation, comparison_method, comparison_value)
79
+
80
+ if raw
81
+ _options(&condition)
82
+ else
83
+ _options(&condition).collect { |o| o.collect(&:content).send(content) }
84
+ end
85
+ end
86
+
87
+ def size
88
+ options.size
89
+ end
90
+
91
+ alias_method :length, :size
92
+
93
+ def [](index)
94
+ options[index].to_serie.to_node
95
+ end
96
+
97
+ def to_serie(flatten: nil, &condition)
98
+ flatten ||= true
99
+
100
+ serie = _options(&condition).collect { |o| o.collect(&:content) }.to_serie(of_series: true).merge
101
+ serie = serie.flatten if flatten
102
+
103
+ serie.prototype
104
+ end
105
+
106
+ alias_method :s, :to_serie
107
+
108
+ def _options(parent: nil, &condition)
109
+ raise NotImplementedError
110
+ end
111
+
112
+ protected
113
+
114
+ def generate_simple_condition_block(attribute = nil,
115
+ after_collect_operation = nil,
116
+ comparison_method = nil,
117
+ comparison_value = nil)
118
+
119
+ if attribute && after_collect_operation && comparison_method && comparison_value
120
+ proc do |o|
121
+ o.collect { |_| _.attributes[attribute] }
122
+ .send(after_collect_operation)
123
+ .send(comparison_method, comparison_value)
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ private_constant :Node
130
+
131
+ class ProxyNode < Node
132
+ attr_accessor :node
133
+
134
+ def _options(parent: nil, &condition)
135
+ @node._options parent: parent, &condition
136
+ end
137
+ end
138
+
139
+ class FinalNode < Node
140
+ attr_reader :content
141
+ attr_reader :attributes
142
+
143
+ def initialize(content, attributes)
144
+ super()
145
+ @element = OptionElement.new(content, attributes)
146
+ end
147
+
148
+ def _options(parent: nil, &condition)
149
+ parent ||= []
150
+
151
+ if block_given?
152
+ if yield(parent + [@element])
153
+ [[@element]]
154
+ else
155
+ []
156
+ end
157
+ else
158
+ [[@element]]
159
+ end
160
+ end
161
+ end
162
+
163
+ private_constant :FinalNode
164
+
165
+ class BlockNode < Node
166
+ def initialize(attributes, &block)
167
+ @attributes = attributes
168
+ @block = block
169
+ end
170
+
171
+ def _options(parent: nil, &condition)
172
+ parent ||= []
173
+
174
+ element = @block.call(parent, @attributes)
175
+ element = OptionElement.new(element, @attributes) unless element.is_a?(OptionElement)
176
+
177
+ if block_given?
178
+ if yield(parent + [element], @attributes)
179
+ [[element]]
180
+ else
181
+ []
182
+ end
183
+ else
184
+ [[element]]
185
+ end
186
+ end
187
+ end
188
+
189
+ private_constant :BlockNode
190
+
191
+ class ConditionNode < Node
192
+ def initialize(node, &block)
193
+ @node = node
194
+ @block = block
195
+ end
196
+
197
+ def _options(parent: nil, &condition)
198
+ parent ||= []
199
+
200
+ r = []
201
+
202
+ @node._options(parent: parent, &condition).each do |node_option|
203
+ r << node_option if (!block_given? || yield(parent + node_option)) && @block.call(parent + node_option)
204
+ end
205
+
206
+ r
207
+ end
208
+ end
209
+
210
+ private_constant :ConditionNode
211
+
212
+ class OrNode < Node
213
+ def initialize(node1, node2)
214
+ @node1 = node1
215
+ @node2 = node2
216
+ super()
217
+ end
218
+
219
+ def _options(parent: nil, &condition)
220
+ parent ||= []
221
+
222
+ r = []
223
+
224
+ @node1._options(parent: parent, &condition).each do |node_option|
225
+ r << node_option if !block_given? || yield(parent + node_option)
226
+ end
227
+
228
+ @node2._options(parent: parent, &condition).each do |node_option|
229
+ r << node_option if !block_given? || yield(parent + node_option)
230
+ end
231
+
232
+ r
233
+ end
234
+ end
235
+
236
+ private_constant :OrNode
237
+
238
+ class NextNode < Node
239
+ def initialize(node, after)
240
+ @node = node
241
+ @after = after
242
+ super()
243
+ end
244
+
245
+ def _options(parent: nil, &condition)
246
+ parent ||= []
247
+
248
+ r = []
249
+ @node._options(parent: parent, &condition).each do |node_option|
250
+ @after._options(parent: parent + node_option, &condition).each do |after_option|
251
+ r << node_option + after_option unless after_option.empty?
252
+ end
253
+ end
254
+ r
255
+ end
256
+ end
257
+
258
+ private_constant :NextNode
259
+
260
+ class RepeatNode < Node
261
+ def initialize(node, max = nil)
262
+ @node = node
263
+ @max = max
264
+
265
+ super()
266
+ end
267
+
268
+ def _options(parent: nil, depth: nil, &condition)
269
+ parent ||= []
270
+ depth ||= 0
271
+
272
+ r = []
273
+
274
+ if @max.nil? || depth < @max
275
+ node_options = @node._options(parent: parent, &condition)
276
+
277
+ node_options.each do |node_option|
278
+ r << node_option
279
+
280
+ node_suboptions = _options(parent: parent + node_option, depth: depth + 1, &condition)
281
+
282
+ node_suboptions.each do |node_suboption|
283
+ r << node_option + node_suboption
284
+ end
285
+ end
286
+ end
287
+
288
+ r
289
+ end
290
+ end
291
+
292
+ private_constant :RepeatNode
293
+ end
294
+ end
@@ -0,0 +1,78 @@
1
+ require 'musa-dsl/series'
2
+
3
+ module Musa
4
+
5
+ # TODO: adapt to series prototyping
6
+
7
+ class Markov
8
+ include Serie
9
+
10
+ attr_accessor :start, :finish, :random, :transitions
11
+
12
+ def initialize(transitions:, start:, finish: nil, random: nil)
13
+ @transitions = transitions.clone.freeze
14
+
15
+ @start = start
16
+ @finish = finish
17
+
18
+ @random = Random.new random if random.is_a?(Integer)
19
+ @random ||= Random.new
20
+
21
+ @procedure_binders = {}
22
+
23
+ _restart
24
+ end
25
+
26
+ def _restart
27
+ @current = nil
28
+ @finished = false
29
+ @history = []
30
+ end
31
+
32
+ def _next_value
33
+ if @finished
34
+ @current = nil
35
+ else
36
+ if @current.nil?
37
+ @current = @start
38
+ else
39
+ if @transitions.has_key? @current
40
+ options = @transitions[@current]
41
+
42
+ case options
43
+ when Array
44
+ @current = options[@random.rand(0...options.size)]
45
+
46
+ when Hash
47
+ total = accumulated = 0.0
48
+ options.each_value { |probability| total += probability.abs }
49
+ r = @random.rand total
50
+
51
+ @current = options.find { |key, probability|
52
+ accumulated += probability;
53
+ r >= accumulated - probability && r < accumulated }[0]
54
+
55
+ when Proc
56
+ procedure_binder = @procedure_binders[options] ||= KeyParametersProcedureBinder.new options
57
+ @current = procedure_binder.call @history
58
+ else
59
+ raise ArgumentError, "Option #{option} is not allowed. Only Array, Hash or Proc are allowed."
60
+ end
61
+ else
62
+ raise RuntimeError, "No transition defined for #{@current}"
63
+ end
64
+ end
65
+
66
+ @history << @current
67
+ @finished = true if !@finish.nil? && (@current == @finish)
68
+ end
69
+
70
+ @current
71
+ end
72
+
73
+ def infinite?
74
+ @finish.nil?
75
+ end
76
+ end
77
+ end
78
+
@@ -0,0 +1,282 @@
1
+ require 'musa-dsl/core-ext/as-context-run'
2
+ require 'musa-dsl/core-ext/key-parameters-procedure-binder'
3
+
4
+ module Musa
5
+ class Rules
6
+ def initialize(&block)
7
+ @context = RulesEvalContext.new.tap { |_| _._as_context_run block }
8
+ end
9
+
10
+ def generate_possibilities(object, confirmed_node = nil, node = nil, rules = nil)
11
+ node ||= Node.new
12
+ rules ||= @context._rules
13
+
14
+ history = confirmed_node.history if confirmed_node
15
+ history ||= []
16
+
17
+ rules = rules.clone
18
+ rule = rules.shift
19
+
20
+ if rule
21
+ rule.generate_possibilities(object, history).each do |new_object|
22
+ new_node = Node.new new_object, node
23
+ new_node.mark_as_ended! if @context._ended? new_object
24
+
25
+ rejection = @context._rejections.find { |rejection| rejection.rejects?(new_object, history) }
26
+ # TODO: include rejection secondary reasons in rejection message
27
+
28
+ new_node.reject! rejection if rejection
29
+
30
+ node.children << new_node
31
+ end
32
+ end
33
+
34
+ unless rules.empty?
35
+ node.children.each do |node|
36
+ generate_possibilities node.object, confirmed_node, node, rules unless node.rejected || node.ended?
37
+ end
38
+ end
39
+
40
+ node
41
+ end
42
+
43
+ def apply(object_or_list, node = nil)
44
+ list = object_or_list.arrayfy.clone
45
+
46
+ node ||= Node.new
47
+
48
+ seed = list.shift
49
+
50
+ if seed
51
+ result = generate_possibilities seed, node
52
+
53
+ fished = result.fish
54
+
55
+ node.reject! 'All children are rejected' if fished.empty?
56
+
57
+ fished.each do |object|
58
+ subnode = node.add(object).mark_as_ended!
59
+ apply list, subnode
60
+ end
61
+ end
62
+
63
+ node
64
+ end
65
+
66
+ class RulesEvalContext
67
+ attr_reader :_rules, :_ended_when, :_rejections
68
+
69
+ def rule(name, &block)
70
+ @_rules ||= []
71
+ @_rules << Rule.new(name, self, block)
72
+ self
73
+ end
74
+
75
+ def ended_when(&block)
76
+ @_ended_when = block
77
+ self
78
+ end
79
+
80
+ def rejection(reason, &block)
81
+ @_rejections ||= []
82
+ @_rejections << Rejection.new(reason, self, block)
83
+ self
84
+ end
85
+
86
+ def _ended?(object)
87
+ as_context_run @_ended_when, object
88
+ end
89
+
90
+ class Rule
91
+ attr_reader :name
92
+
93
+ def initialize(name, context, block)
94
+ @name = name
95
+ @context = context
96
+ @block = block
97
+ end
98
+
99
+ def generate_possibilities(object, history)
100
+ # TODO: optimize context using only one instance for all genereate_possibilities calls
101
+ context = RuleEvalContext.new @context
102
+ context.as_context_run @block, object, history
103
+
104
+ context._possibilities
105
+ end
106
+
107
+ class RuleEvalContext
108
+ attr_reader :_possibilities
109
+
110
+ def initialize(parent_context)
111
+ @_parent_context = parent_context
112
+ @_possibilities = []
113
+ end
114
+
115
+ def possibility(object)
116
+ @_possibilities << object
117
+ self
118
+ end
119
+
120
+ private
121
+
122
+ def method_missing(method_name, *args, **key_args, &block)
123
+ if @_parent_context.respond_to? method_name
124
+ @_parent_context.send_nice method_name, *args, **key_args, &block
125
+ else
126
+ super
127
+ end
128
+ end
129
+
130
+ def respond_to_missing?(method_name, include_private)
131
+ @_parent_context.respond_to?(method_name, include_private) || super
132
+ end
133
+ end
134
+
135
+ private_constant :RuleEvalContext
136
+ end
137
+
138
+ private_constant :Rule
139
+
140
+ class Rejection
141
+ attr_reader :reason
142
+
143
+ def initialize(reason, context = nil, block = nil)
144
+ @reason = reason
145
+ @context = context
146
+ @block = block
147
+ end
148
+
149
+ def rejects?(object, history)
150
+ # TODO: optimize context using only one instance for all rejects? checks
151
+ context = RejectionEvalContext.new @context
152
+ context.as_context_run @block, object, history
153
+
154
+ reasons = context._secondary_reasons.collect { |_| ("#{@reason} (#{_})" if _) || @reason }
155
+
156
+ reasons.empty? ? nil : reasons
157
+ end
158
+
159
+ class RejectionEvalContext
160
+ attr_reader :_secondary_reasons
161
+
162
+ def initialize(parent_context)
163
+ @_parent_context = parent_context
164
+ @_secondary_reasons = []
165
+ end
166
+
167
+ def reject(secondary_reason = nil)
168
+ @_secondary_reasons << secondary_reason
169
+ self
170
+ end
171
+
172
+ private
173
+
174
+ def method_missing(method_name, *args, **key_args, &block)
175
+ if @_parent_context.respond_to? method_name
176
+ @_parent_context.send_nice method_name, *args, **key_args, &block
177
+ else
178
+ super
179
+ end
180
+ end
181
+
182
+ def respond_to_missing?(method_name, include_private)
183
+ @_parent_context.respond_to?(method_name, include_private) || super
184
+ end
185
+ end
186
+
187
+ private_constant :RejectionEvalContext
188
+ end
189
+
190
+ private_constant :Rejection
191
+ end
192
+
193
+ private_constant :RulesEvalContext
194
+
195
+ class Node
196
+ attr_reader :parent, :children, :object, :rejected
197
+
198
+ def initialize(object = nil, parent = nil)
199
+ @parent = parent
200
+ @children = []
201
+ @object = object
202
+
203
+ @ended = false
204
+ @rejected = nil
205
+ end
206
+
207
+ def add(object)
208
+ Node.new(object, self).tap { |n| @children << n }
209
+ end
210
+
211
+ def reject!(rejection)
212
+ @rejected = rejection
213
+ self
214
+ end
215
+
216
+ def mark_as_ended!
217
+ @children.each(&:update_rejection_by_children!)
218
+
219
+ if !@children.empty? && !@children.find { |n| !n.rejected }
220
+ reject! Rejection::AllChildrenRejected
221
+ end
222
+
223
+ @ended = true
224
+
225
+ self
226
+ end
227
+
228
+ def ended?
229
+ @ended
230
+ end
231
+
232
+ def history
233
+ objects = []
234
+ n = self
235
+ while n && n.object
236
+ objects << n.object
237
+ n = n.parent
238
+ end
239
+
240
+ objects.reverse
241
+ end
242
+
243
+ def fish
244
+ fished = []
245
+
246
+ @children.each do |node|
247
+ unless node.rejected
248
+ if node.ended?
249
+ fished << node.object
250
+ else
251
+ fished += node.fish
252
+ end
253
+ end
254
+ end
255
+
256
+ fished
257
+ end
258
+
259
+ def combinations(parent_combination = nil)
260
+ parent_combination ||= []
261
+
262
+ combinations = []
263
+
264
+ unless rejected
265
+ if @children.empty?
266
+ combinations << parent_combination
267
+ else
268
+ @children.each do |node|
269
+ node.combinations(parent_combination + [node.object]).each do |object|
270
+ combinations << object
271
+ end
272
+ end
273
+ end
274
+ end
275
+
276
+ combinations
277
+ end
278
+ end
279
+
280
+ private_constant :Node
281
+ end
282
+ end