alda-rb 0.1.4 → 0.2.0
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/.gitignore +1 -0
- data/.travis.yml +1 -1
- data/Gemfile +4 -0
- data/README.md +2 -2
- data/Rakefile +15 -6
- data/bin/setup +1 -1
- data/examples/bwv846_prelude.rb +1 -1
- data/lib/alda-rb.rb +8 -1074
- data/lib/alda-rb/commandline.rb +139 -0
- data/lib/alda-rb/error.rb +80 -0
- data/lib/alda-rb/event.rb +983 -0
- data/lib/alda-rb/event_list.rb +313 -0
- data/lib/alda-rb/patches.rb +168 -0
- data/lib/alda-rb/repl.rb +245 -0
- data/lib/alda-rb/version.rb +7 -1
- metadata +8 -2
@@ -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
|