alda-rb 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,313 @@
1
+ require 'set'
2
+
3
+ ##
4
+ # Including this module can make your class have the ability
5
+ # to have an event list.
6
+ # See docs below to get an overview of its functions.
7
+ module Alda::EventList
8
+
9
+ ##
10
+ # The array containing the events (Alda::Event objects),
11
+ # most of which are Alda::EventContainer objects.
12
+ attr_accessor :events
13
+
14
+ ##
15
+ # The set containing the available variable names.
16
+ attr_accessor :variables
17
+
18
+ def on_contained
19
+ super if defined? super
20
+ instance_eval &@block if @block
21
+ end
22
+
23
+ ##
24
+ # :call-seq:
25
+ # (some sugar) -> Alda::EventContainer
26
+ #
27
+ # Make the object have the ability to append its #events conveniently.
28
+ #
29
+ # Here is a list of sugar. When the name of a method meets certain
30
+ # condition, the method is regarded as an event appended to #events.
31
+ #
32
+ # 1. Ending with 2 underlines: set variable. See Alda::SetVariable.
33
+ #
34
+ # 2. Starting with 2 lowercase letters and
35
+ # ending with underline character: instrument. See Alda::Part.
36
+ #
37
+ # 3. Starting with 2 lowercase letters: inline lisp code,
38
+ # set variable, or get variable.
39
+ # One of the above three is chosen intelligently.
40
+ # See Alda::InlineLisp, Alda::SetVariable, Alda::GetVariable.
41
+ #
42
+ # 4. Starting with "t": CRAM. See Alda::Cram.
43
+ #
44
+ # 5. Starting with one of "a", "b", ..., "g": note. See Alda::Note.
45
+ #
46
+ # 6. Starting with "r": rest. See Alda::Rest.
47
+ #
48
+ # 7. "x": chord. See Alda::Chord.
49
+ #
50
+ # 8. "s": sequence. See Alda::Sequence.
51
+ #
52
+ # 9. Starting with "o": octave. See Alda::Octave.
53
+ #
54
+ # 10. Starting with "v": voice. See Alda::Voice.
55
+ #
56
+ # 11. Starting with "__" (2 underlines): at marker. See Alda::AtMarker.
57
+ #
58
+ # 12. Starting with "_" (underline) and ending with "_" (underline):
59
+ # lisp identifier. See Alda::LispIdentifier.
60
+ #
61
+ # 13. Starting with "_" (underline): marker. See Alda::Marker.
62
+ #
63
+ # All the appended events are contained in an Alda::EventContainer object,
64
+ # which is to be returned.
65
+ #
66
+ # These sugars forms a DSL. See ::new for examples.
67
+ def method_missing name, *args, &block
68
+ if @parent&.respond_to? name, true
69
+ return @parent.__send__ name, *args, &block
70
+ end
71
+ sequence_sugar = ->event do
72
+ if args.size == 1
73
+ Alda::Sequence.join event, args.first.tap(&:detach_from_parent)
74
+ else
75
+ event
76
+ end
77
+ end
78
+ case
79
+ when /\A(?<head>[a-z][a-z].*)__\z/ =~ name
80
+ Alda::SetVariable.new head, *args, &block
81
+ when /\A(?<part>[a-z][a-z].*)_\z/ =~ name
82
+ if args.first.is_a? String
83
+ Alda::Part.new [part], args.first
84
+ else
85
+ sequence_sugar.(Alda::Part.new [part])
86
+ end
87
+ when /\A[a-z][a-z].*\z/ =~ name
88
+ arg = args.first
89
+ if block || !has_variable?(name) && args.size == 1 && arg.is_a?(Alda::Event) &&
90
+ !arg.is_a?(Alda::InlineLisp) && !arg.is_a?(Alda::LispIdentifier) &&
91
+ !(arg.is_a?(Alda::EventContainer) &&
92
+ (arg.event.is_a?(Alda::InlineLisp) || arg.event.is_a?(Alda::LispIdentifier)))
93
+ Alda::SetVariable.new name, *args, &block
94
+ elsif has_variable?(name) && (args.empty? || args.size == 1 && arg.is_a?(Alda::Event))
95
+ sequence_sugar.(Alda::GetVariable.new name)
96
+ else
97
+ Alda::InlineLisp.new name, *args
98
+ end
99
+ when /\At(?<duration>.*)\z/ =~ name
100
+ Alda::Cram.new duration, &block
101
+ when /\A(?<pitch>[a-g])(?<duration>.*)\z/ =~ name
102
+ sequence_sugar.(Alda::Note.new pitch, duration)
103
+ when /\Ar(?<duration>.*)\z/ =~ name
104
+ sequence_sugar.(Alda::Rest.new duration)
105
+ when /\Ax\z/ =~ name
106
+ Alda::Chord.new &block
107
+ when /\As\z/ =~ name
108
+ Alda::Sequence.new *args, &block
109
+ when /\Ao!\z/ =~ name
110
+ sequence_sugar.(Alda::Octave.new('').tap { _1.up_or_down = 1})
111
+ when /\Ao\?\z/ =~ name
112
+ sequence_sugar.(Alda::Octave.new('').tap { _1.up_or_down = -1})
113
+ when /\Ao(?<num>\d*)\z/ =~ name
114
+ sequence_sugar.(Alda::Octave.new num)
115
+ when /\Av(?<num>\d+)\z/ =~ name
116
+ sequence_sugar.(Alda::Voice.new num)
117
+ when /\A__(?<head>.+)\z/ =~ name
118
+ sequence_sugar.(Alda::AtMarker.new head)
119
+ when /\A_(?<head>.+)_\z/ =~ name
120
+ sequence_sugar.(Alda::LispIdentifier.new head)
121
+ when /\A_(?<head>.+)\z/ =~ name
122
+ sequence_sugar.(Alda::Marker.new head)
123
+ else
124
+ super
125
+ end.then do |event|
126
+ Alda::EventContainer.new event, self
127
+ end.tap { @events.push _1 }
128
+ end
129
+
130
+ ##
131
+ # :call-seq:
132
+ # has_variable?(name) -> true or false
133
+ #
134
+ # Whether there is a previously declared alda variable
135
+ # whose name is specified by +name+.
136
+ #
137
+ # Searches variables in #parent.
138
+ def has_variable? name
139
+ @variables.include?(name) || !!@parent&.has_variable?(name)
140
+ end
141
+
142
+ ##
143
+ # :call-seq:
144
+ # import(event_list) -> nil
145
+ #
146
+ # Append the events of another Alda::EventList object here.
147
+ # This method covers the disadvantage of alda's being unable to
148
+ # import scores from other files.
149
+ # See https://github.com/alda-lang/alda-core/issues/8.
150
+ def import event_list
151
+ @events.concat event_list.events
152
+ nil
153
+ end
154
+
155
+ ##
156
+ # :call-seq:
157
+ # new(&block) -> Alda::EventList
158
+ #
159
+ # +block+ is to be passed with the Alda::EventList object as +self+.
160
+ #
161
+ # Note that +block+ is not called immediately.
162
+ # It is instead called in #on_contained.
163
+ # Specially, Alda::Score::new calls #on_contained.
164
+ #
165
+ # Alda::Score.new do
166
+ # tempo! 108 # inline lisp
167
+ # piano_ # piano part
168
+ # o4 # octave 4
169
+ # c8; d; e; f # notes
170
+ # g4 g a f g e f d e c # a sequence
171
+ # d4_8 # cannot have '~', use '_' instead
172
+ # o3 b8 o4 c2 # a sequence
173
+ # end
174
+ # # => #<Alda::Score:0x... @events=[...]>
175
+ #
176
+ # For a list of sugars, see #method_missing.
177
+ def initialize &block
178
+ @events ||= []
179
+ @variables ||= Set.new
180
+ @block ||= block
181
+ end
182
+
183
+ ##
184
+ # :call-seq:
185
+ # to_a -> Array
186
+ #
187
+ # Same as #events.
188
+ def to_a
189
+ @events
190
+ end
191
+
192
+ ##
193
+ # :call-seq:
194
+ # events_alda_codes(delimiter=" ") -> String
195
+ #
196
+ # Join the alda codes of #events with a specified delimiter.
197
+ # Returns a string representing the result.
198
+ def events_alda_codes delimiter = ' '
199
+ @events.map(&:to_alda_code).join delimiter
200
+ end
201
+ end
202
+
203
+ ##
204
+ # Includes Alda::EventList and provides methods to #play, #parse, or #export.
205
+ class Alda::Score
206
+ include Alda::EventList
207
+
208
+ ##
209
+ # :call-seq:
210
+ # play(**opts) -> String
211
+ #
212
+ # Plays the score.
213
+ #
214
+ # Returns the command line output of the +alda+ command.
215
+ #
216
+ # Run command <tt>alda help</tt> to see available options
217
+ # that can be specified in +opts+.
218
+ #
219
+ # Alda::Score.new { piano_; c; d; e }.play
220
+ # # => "[27713] Parsing/evaluating...\n[27713] Playing...\n"
221
+ # # (and plays the sound)
222
+ # Alda::Score.new { piano_; c; d; e }.play from: 1
223
+ # # (plays only an E note)
224
+ def play **opts
225
+ Alda.play code: self, **opts
226
+ end
227
+
228
+ ##
229
+ # :call-seq:
230
+ # parse(**opts) -> String
231
+ #
232
+ # Parses the score.
233
+ #
234
+ # Returns the JSON string of the parse result.
235
+ #
236
+ # Run command <tt>alda help</tt> to see available options
237
+ # that can be specified in +opts+.
238
+ #
239
+ # Alda::Score.new { piano_; c }.parse output: :events
240
+ # # => "[{\"event-type\":...}]\n"
241
+ def parse **opts
242
+ Alda.parse code: self, **opts
243
+ end
244
+
245
+ ##
246
+ # :call-seq:
247
+ # export(**opts) -> String
248
+ #
249
+ # Exports the score.
250
+ #
251
+ # Returns the command line output of the +alda+ command.
252
+ #
253
+ # Run command <tt>alda help</tt> to see available options
254
+ # that can be specified in +opts+.
255
+ #
256
+ # Alda::Score.new { piano_; c }.export output: 'temp.mid'
257
+ # # (outputs a midi file called temp.mid)
258
+ def export **opts
259
+ Alda.export code: self, **opts
260
+ end
261
+
262
+ ##
263
+ # :call-seq:
264
+ # save(filename) -> nil
265
+ #
266
+ # Saves the alda codes into a file.
267
+ def save filename
268
+ File.open(filename, 'w') { _1.puts to_s }
269
+ end
270
+
271
+ ##
272
+ # :call-seq:
273
+ # load(filename) -> Alda::InlineLisp
274
+ #
275
+ # Loads alda codes from a file.
276
+ #
277
+ # Actually appends a Alda::InlineLisp event of +alda-code+ lisp call.
278
+ def load filename
279
+ event = Alda::InlineLisp.new :alda_code, File.read(filename)
280
+ @events.push event
281
+ event
282
+ end
283
+
284
+ ##
285
+ # :call-seq:
286
+ # to_s() -> String
287
+ #
288
+ # Returns a String containing the alda codes representing the score.
289
+ def to_s
290
+ events_alda_codes
291
+ end
292
+
293
+ ##
294
+ # :call-seq:
295
+ # new(&block) -> Alda::Score
296
+ #
297
+ # Creates an Alda::Score.
298
+ def initialize(...)
299
+ super
300
+ on_contained
301
+ end
302
+
303
+ ##
304
+ # :call-seq:
305
+ # clear() -> nil
306
+ #
307
+ # Clears all the events and variables.
308
+ def clear
309
+ @events.clear
310
+ @variables.clear
311
+ nil
312
+ end
313
+ end
@@ -0,0 +1,168 @@
1
+ require 'stringio'
2
+
3
+ ##
4
+ class Array
5
+
6
+ ##
7
+ # See Alda::Event#to_alda_code.
8
+ def to_alda_code
9
+ "[#{map(&:to_alda_code).join ' '}]"
10
+ end
11
+
12
+ ##
13
+ # See Alda::Event#detach_from_parent.
14
+ def detach_from_parent
15
+ reverse_each &:detach_from_parent
16
+ end
17
+ end
18
+
19
+ ##
20
+ class Hash
21
+
22
+ ##
23
+ # See Alda::Event#to_alda_code.
24
+ def to_alda_code
25
+ "[#{map(&:to_alda_code).join ' '}]"
26
+ end
27
+
28
+ ##
29
+ # See Alda::Event#detach_from_parent.
30
+ def detach_from_parent
31
+ each.reverse_each &:detach_from_parent
32
+ end
33
+ end
34
+
35
+ ##
36
+ class String
37
+
38
+ ##
39
+ # See Alda::Event#to_alda_code.
40
+ def to_alda_code
41
+ dump
42
+ end
43
+
44
+ ##
45
+ # See Alda::Event#detach_from_parent.
46
+ def detach_from_parent
47
+ end
48
+ end
49
+
50
+ ##
51
+ class Symbol
52
+
53
+ ##
54
+ # See Alda::Event#to_alda_code.
55
+ def to_alda_code
56
+ ?: + to_s
57
+ end
58
+
59
+ ##
60
+ # See Alda::Event#detach_from_parent.
61
+ def detach_from_parent
62
+ end
63
+ end
64
+
65
+ ##
66
+ class Numeric
67
+
68
+ ##
69
+ # See Alda::Event#to_alda_code.
70
+ def to_alda_code
71
+ inspect
72
+ end
73
+
74
+ ##
75
+ # See Alda::Event#detach_from_parent.
76
+ def detach_from_parent
77
+ end
78
+ end
79
+
80
+ ##
81
+ class Range
82
+
83
+ ##
84
+ # See Alda::Event#to_alda_code.
85
+ def to_alda_code
86
+ "#{first}-#{last}"
87
+ end
88
+
89
+ ##
90
+ # See Alda::Event#detach_from_parent.
91
+ def detach_from_parent
92
+ end
93
+ end
94
+
95
+ ##
96
+ class TrueClass
97
+
98
+ ##
99
+ # See Alda::Event#to_alda_code.
100
+ def to_alda_code
101
+ 'true'
102
+ end
103
+
104
+ ##
105
+ # See Alda::Event#detach_from_parent.
106
+ def detach_from_parent
107
+ end
108
+ end
109
+
110
+ ##
111
+ class FalseClass
112
+
113
+ ##
114
+ # See Alda::Event#to_alda_code.
115
+ def to_alda_code
116
+ 'false'
117
+ end
118
+
119
+ ##
120
+ # See Alda::Event#detach_from_parent.
121
+ def detach_from_parent
122
+ end
123
+ end
124
+
125
+ ##
126
+ class NilClass
127
+
128
+ ##
129
+ # See Alda::Event#to_alda_code.
130
+ def to_alda_code
131
+ 'nil'
132
+ end
133
+
134
+ ##
135
+ # See Alda::Event#detach_from_parent.
136
+ def detach_from_parent
137
+ end
138
+ end
139
+
140
+ ##
141
+ class Proc
142
+
143
+ ##
144
+ # :call-seq:
145
+ # proc * n -> n
146
+ #
147
+ # Runs +self+ for +n+ times.
148
+ def * n
149
+ if !lambda? || arity == 1
150
+ n.times &self
151
+ else
152
+ n.times { self.() }
153
+ end
154
+ end
155
+ end
156
+
157
+ ##
158
+ class StringIO
159
+
160
+ ##
161
+ # :call-seq:
162
+ # to_s() -> String
163
+ #
164
+ # Equivalent to <tt>string</tt>.
165
+ def to_s
166
+ string
167
+ end
168
+ end