musa-dsl 0.14.16

Sign up to get free protection for your applications and to get access to all the features.
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