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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fa068d3caf9d89e4c0bfb90b2bc0800f5fe2824cbd9d3674be7a4853a55e5b25
4
- data.tar.gz: ec0c8aa26fd9f789d2bbcaef2502890a21223036779e29704eb8c30ebf08b27f
3
+ metadata.gz: dda95434d70128902d3d1ab96628c079ea8716123768d2e93b1ed429df5571b2
4
+ data.tar.gz: 8886cd7c861469aa980c18b56142e939c1727086c4d14e15b55fad3eb9fad1b0
5
5
  SHA512:
6
- metadata.gz: 98860e1ec9598d9935bb59371d8621be062dcab44f524ada4ae1a1a5460f4c85bd440429b38bbdc74032fc19f18cfc0787f575f60593f1e1abceedc5d2ad06c1
7
- data.tar.gz: 83a071c40c8e82a5d57a609c668d5f5886d02be45b1656f2a89946208eb590af940e292b550cefbcf4ed930000c1a46b518ec2a204d7f0bca8cb07293911727c
6
+ metadata.gz: 4cc9a4cd860643fdd69d152d77b13dac9c9d730d119b55d2799c0ffef328ccbeb91c9e618bf706652306210bdd298d1055f3f0a88ead557cc5f550d9f644823f
7
+ data.tar.gz: 0c3654c453bf4d66fc6eed1bdcda53abe712ad625799d6687d64d29222c77d6bb7fc86e9190cb96d09052f7191eb43bfbcafc8dea8a0e26bf11a2a1bbb99dfee
data/.gitignore CHANGED
@@ -9,3 +9,4 @@
9
9
 
10
10
  /.idea/
11
11
  Gemfile.lock
12
+ /html/
@@ -2,5 +2,5 @@
2
2
  language: ruby
3
3
  cache: bundler
4
4
  rvm:
5
- - 2.7.0
5
+ - 2.7.1
6
6
  before_install: gem install bundler -v 2.1.2
data/Gemfile CHANGED
@@ -5,3 +5,7 @@ gemspec
5
5
  gem "rake", "~> 12.0"
6
6
  gem "minitest", "~> 5.0"
7
7
  gem "colorize"
8
+ group :develop do
9
+ gem 'rubocop'
10
+ gem 'rdoc'
11
+ end
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
- # Alda-rb
1
+ # alda-rb
2
2
 
3
3
  A Ruby library for live-coding music with [Alda](https://alda.io/).
4
- Also provides an Alda DSL in Ruby.
4
+ Also provides an alda DSL in Ruby.
5
5
 
6
6
  ## Installation
7
7
 
data/Rakefile CHANGED
@@ -1,10 +1,19 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+ require 'rdoc/task'
3
4
 
4
- Rake::TestTask.new(:test) do |t|
5
- t.libs << "test"
6
- t.libs << "lib"
7
- t.test_files = FileList["test/**/*_test.rb"]
5
+ RDoc::Task.new do |t|
6
+ t.main = 'README.md'
7
+ t.rdoc_files.include 'README.md', 'CODE_OF_CONDUCT.md',
8
+ 'LICENSE.txt', 'lib/**/*.rb'
9
+ t.options << '--tab-width'
10
+ t.options << '2'
11
+ end
12
+
13
+ Rake::TestTask.new :test do |t|
14
+ t.libs << 'test'
15
+ t.libs << 'lib'
16
+ t.test_files = FileList['test/**/*_test.rb']
8
17
  end
9
18
 
10
19
  task :default => :test
data/bin/setup CHANGED
@@ -5,4 +5,4 @@ set -vx
5
5
 
6
6
  bundle install
7
7
 
8
- # Do any other automated setup that you need to do here
8
+ rake rdoc
@@ -15,7 +15,7 @@ class Alda::Sequence
15
15
  /(?<letter>[a-g][-+_]*)(?<octave>\d*)/ =~ pitch
16
16
  octave = @@last_octave ||= '4' if octave.empty?
17
17
  result = Alda::Note.new "o#{@@last_octave = octave} #{letter}", duration
18
- @events.push result
18
+ @events.push result
19
19
  result
20
20
  end
21
21
  end
@@ -1,1075 +1,9 @@
1
- require 'readline'
2
- require 'set'
3
- require 'stringio'
4
- require 'irb/ruby-lex'
5
- require 'alda-rb/version'
6
- require 'colorize'
7
-
8
- {
9
- Array => -> { "[#{map(&:to_alda_code).join ' '}]" },
10
- Hash => -> { "{#{to_a.reduce(:+).map(&:to_alda_code).join ' '}}" },
11
- String => -> { dump },
12
- Symbol => -> { ?: + to_s },
13
- Numeric => -> { inspect },
14
- Range => -> { "#{first}-#{last}" },
15
- TrueClass => -> { 'true' },
16
- FalseClass => -> { 'false' },
17
- NilClass => -> { 'nil' }
18
- }.each { |klass, block| klass.define_method :to_alda_code, &block }
19
-
20
- class Proc
21
- # Runs +self+ for +n+ times.
22
- def * n
23
- if !lambda? || arity == 1
24
- n.times &self
25
- else
26
- n.times { self.() }
27
- end
28
- end
29
- end
1
+ # frozen_string_literal: true
30
2
 
31
- class StringIO
32
- # Equivalent to #string.
33
- def to_s
34
- string
35
- end
36
- end
37
-
38
- module Kernel
39
- # Runs the alda command.
40
- # Does not capture output.
41
- # @example
42
- # alda 'version'
43
- # alda 'play', '-c', 'piano: a'
44
- # alda 'repl'
45
- def alda *args
46
- system Alda.executable, *args
47
- end
48
- end
49
-
50
- # The module serving as a namespace.
51
- module Alda
52
-
53
- # The array of available subcommands of alda executable.
54
- #
55
- # Alda# is able to invoke +alda+ at the command line.
56
- # The subcommand is the name of the method invoked upon Alda#.
57
- #
58
- # The first argument (a hash) is interpreted as the options.
59
- # The keyword arguments are interpreted as the subcommand options.
60
- #
61
- # The return value is the string output by the command in STDOUT.
62
- #
63
- # If the exit code is nonzero, a CommandLineError# is raised.
64
- # @example
65
- # Alda.version
66
- # # => "Client version: 1.4.0\nServer version: [27713] 1.4.0\n"
67
- # Alda.parse code: 'bassoon: o3 c'
68
- # # => "{\"chord-mode\":false,\"current-instruments\":...}\n"
69
- COMMANDS = %i[
70
- help update repl up start_server init down stop_server
71
- downup restart_server list status version play stop parse
72
- instruments export
73
- ].freeze
74
-
75
- COMMANDS.each do |command|
76
- define_method command do |*args, **opts|
77
- block = ->key, val do
78
- next unless val
79
- args.push "--#{key.to_s.tr ?_, ?-}"
80
- args.push val.to_s unless val == true
81
- end
82
- # executable
83
- args.unshift Alda.executable
84
- args.map! &:to_s
85
- # options
86
- Alda.options.each &block
87
- # subcommand
88
- args.push command.to_s
89
- # subcommand options
90
- opts.each &block
91
- # subprocess
92
- IO.popen(args, &:read).tap do
93
- raise CommandLineError.new $?, _1 if $?.exitstatus.nonzero?
94
- end
95
- end
96
- end
97
-
98
- # The path to the +alda+ executable.
99
- #
100
- # The default value is <tt>"alda"</tt>,
101
- # which will depend on your PATH.
102
- singleton_class.attr_accessor :executable
103
- @executable = 'alda'
104
-
105
- singleton_class.attr_reader :options
106
- @options = {}
107
-
108
- # @return Whether the alda server is up.
109
- def up?
110
- status.include? 'up'
111
- end
112
-
113
- # @return Whether the alda server is down.
114
- def down?
115
- status.include? 'down'
116
- end
117
-
118
- module_function :up?, :down?, *COMMANDS
119
-
120
- # Start a REPL session.
121
- def self.repl
122
- REPL.new.run
123
- end
124
-
125
- # Sets the options of alda command.
126
- # Not the subcommand options.
127
- def self.[] **opts
128
- @options.merge! opts
129
- self
130
- end
131
-
132
- # Clears the command line options.
133
- def self.clear_options
134
- @options.clear
135
- end
136
-
137
- # Including this module can make your class have the ability
138
- # to have an event list.
139
- # See docs below to get an overview of its functions.
140
- module EventList
141
-
142
- # The array containing the events (Event# objects),
143
- # most of which are EventContainer# objects.
144
- attr_accessor :events
145
-
146
- # The set containing the available variable names.
147
- attr_accessor :variables
148
-
149
- def on_contained
150
- instance_eval &@block if @block
151
- end
152
-
153
- # Make the object have the ability to appending its +events+
154
- # conveniently.
155
- #
156
- # Here is a list of sugar. When the name of a method meets certain
157
- # condition, the method is regarded as an event appended to +events+.
158
- #
159
- # 1. Ending with 2 underlines: set variable. See SetVariable#.
160
- #
161
- # 2. Starting with 2 lowercase letters and
162
- # ending with underline character: instrument. See Part#.
163
- #
164
- # 3. Starting with 2 lowercase letters: inline lisp code,
165
- # set variable, or get variable.
166
- # One of the above three is chosen intelligently.
167
- # See InlineLisp#, SetVariable#, GetVariable#.
168
- #
169
- # 4. Starting with "t": CRAM. See Cram#.
170
- #
171
- # 5. Starting with one of "a", "b", ..., "g": note. See Note#.
172
- #
173
- # 6. Starting with "r": rest. See Rest#.
174
- #
175
- # 7. "x": chord. See Chord#.
176
- #
177
- # 8. "s": sequence. See Sequence#.
178
- #
179
- # 9. Starting with "o": octave. See Octave#.
180
- #
181
- # 10. Starting with "v": voice. See Voice#.
182
- #
183
- # 11. Starting with "__" (2 underlines): at marker. See AtMarker#.
184
- #
185
- # 12. Starting with "_" (underline): marker. See Marker#.
186
- #
187
- # Notes cannot have dots.
188
- # To tie multiple durations, +_+ is used instead of +~+.
189
- #
190
- # All the appended events are contained in an EventContainer# object,
191
- # which is to be returned.
192
- #
193
- # These sugars forms a DSL.
194
- # @see #initialize.
195
- # @return an EventContainer# object.
196
- def method_missing name, *args, &block
197
- if @parent&.respond_to? name, true
198
- return @parent.__send__ name, *args, &block
199
- end
200
- sequence_sugar = ->event do
201
- if args.size == 1
202
- joined = args.first
203
- unless (got = @events.pop) == (expected = joined)
204
- raise OrderError.new expected, got
205
- end
206
- Sequence.join event, joined
207
- else
208
- event
209
- end
210
- end
211
- case
212
- when /\A(?<head>[a-z][a-z].*)__\z/ =~ name
213
- SetVariable.new head, *args, &block
214
- when /\A(?<part>[a-z][a-z].*)_\z/ =~ name
215
- if args.first.is_a? String
216
- Part.new [part], args.first
217
- else
218
- sequence_sugar.(Part.new [part])
219
- end
220
- when /\A[a-z][a-z].*\z/ =~ name
221
- if block
222
- SetVariable.new name, *args, &block
223
- elsif has_variable?(name) && (args.empty? || args.size == 1 && args.first.is_a?(Event))
224
- sequence_sugar.(GetVariable.new name)
225
- else
226
- InlineLisp.new name, *args
227
- end
228
- when /\At(?<duration>.*)\z/ =~ name
229
- Cram.new duration, &block
230
- when /\A(?<pitch>[a-g])(?<duration>.*)\z/ =~ name
231
- sequence_sugar.(Note.new pitch, duration)
232
- when /\Ar(?<duration>.*)\z/ =~ name
233
- sequence_sugar.(Rest.new duration)
234
- when /\Ax\z/ =~ name
235
- Chord.new &block
236
- when /\As\z/ =~ name
237
- Sequence.new *args, &block
238
- when /\Ao!\z/ =~ name
239
- sequence_sugar.(Octave.new('').tap { _1.up_or_down = 1})
240
- when /\Ao\?\z/ =~ name
241
- sequence_sugar.(Octave.new('').tap { _1.up_or_down = -1})
242
- when /\Ao(?<num>\d*)\z/ =~ name
243
- sequence_sugar.(Octave.new num)
244
- when /\Av(?<num>\d+)\z/ =~ name
245
- sequence_sugar.(Voice.new num)
246
- when /\A__(?<head>.+)\z/ =~ name
247
- sequence_sugar.(AtMarker.new head)
248
- when /\A_(?<head>.+)\z/ =~ name
249
- sequence_sugar.(Marker.new head)
250
- else
251
- super
252
- end.then do |event|
253
- EventContainer.new event, self
254
- end.tap &@events.method(:push)
255
- end
256
-
257
- def has_variable? name
258
- @variables.include?(name) || !!@parent&.has_variable?(name)
259
- end
260
-
261
- # Append the events of another EventList# object here.
262
- # This method covers the disadvantage of alda's being unable to
263
- # import scores from other files.
264
- # See https://github.com/alda-lang/alda-core/issues/8.
265
- def import event_list
266
- @events.concat event_list.events
267
- end
268
-
269
- # @param block to be passed with the EventList# object as +self+.
270
- # @example
271
- # Alda::Score.new do
272
- # tempo! 108 # inline lisp
273
- # piano_ # piano part
274
- # o4 # octave 4
275
- # c8; d; e; f # notes
276
- # g4 g a f g e f d e c # a sequence
277
- # d4_8 # cannot have '~', use '_' instead
278
- # o3 b8 o4 c2 # a sequence
279
- # end
280
- # # => #<Alda::Score:0x... @events=[...]>
281
- def initialize &block
282
- @events ||= []
283
- @variables ||= Set.new
284
- @block ||= block
285
- end
286
-
287
- # Same as #events
288
- def to_a
289
- @events
290
- end
291
-
292
- # Join the alda codes of #events with a specified delimiter.
293
- # Returns a string representing the result.
294
- def events_alda_codes delimiter = ' '
295
- @events.map(&:to_alda_code).join delimiter
296
- end
297
- end
298
-
299
- # The class mixes in EventList# and provides methods to play or parse.
300
- class Score
301
- include EventList
302
-
303
- # Plays the score.
304
- # @return The command line output of the +alda+ command.
305
- # @example
306
- # Alda::Score.new { piano_; c; d; e }.play
307
- # # => "[27713] Parsing/evaluating...\n[27713] Playing...\n"
308
- # # (and plays the sound)
309
- # Alda::Score.new { piano_; c; d; e }.play from: 1
310
- # # (plays only an E note)
311
- def play **opts
312
- Alda.play code: self, **opts
313
- end
314
-
315
- # Parses the score.
316
- # @return The JSON string of the parse result.
317
- # @example
318
- # Alda::Score.new { piano_; c }.parse output: :events
319
- # # => "[{\"event-type\":...}]\n"
320
- def parse **opts
321
- Alda.parse code: self, **opts
322
- end
323
-
324
- # Exports the score.
325
- # @return The command line output of the +alda+ command.
326
- # @example
327
- # Alda::Score.new { piano_; c }.export output: 'temp.mid'
328
- # # (outputs a midi file called temp.mid)
329
- def export **opts
330
- Alda.export code: self, **opts
331
- end
332
-
333
- # Saves the alda codes into a file.
334
- def save filename
335
- File.open(filename, 'w') { _1.puts to_s }
336
- end
337
-
338
- # Loads alda codes from a file.
339
- def load filename
340
- event = InlineLisp.new :alda_code, File.read(filename)
341
- @events.push event
342
- event
343
- end
344
-
345
- # @return Alda codes.
346
- def to_s
347
- events_alda_codes
348
- end
349
-
350
- # The initialization.
351
- def initialize(...)
352
- super
353
- on_contained
354
- end
355
-
356
- # Clears all the events and variables.
357
- def clear
358
- @events.clear
359
- @variables.clear
360
- self
361
- end
362
- end
363
-
364
- # An encapsulation for the REPL session for alda-rb.
365
- class REPL
366
-
367
- # The score object used in REPL.
368
- # Includes Alda#, so it can refer to alda commandline.
369
- class TempScore < Score
370
- include Alda
371
-
372
- Score.instance_methods(false).each do |meth|
373
- define_method meth, Score.instance_method(meth)
374
- end
375
-
376
- def initialize session
377
- super()
378
- @session = session
379
- end
380
-
381
- def to_s
382
- history
383
- end
384
-
385
- def history
386
- @session.history.to_s
387
- end
388
-
389
- def clear_history
390
- @session.clear_history
391
- end
392
-
393
- def get_binding
394
- binding
395
- end
396
-
397
- alias quit exit
398
- end
399
-
400
- # The history.
401
- attr_reader :history
402
-
403
- # Initialization.
404
- def initialize
405
- @score = TempScore.new self
406
- @binding = @score.get_binding
407
- @lex = RubyLex.new
408
- @history = StringIO.new
409
- end
410
-
411
- # Runs the session. Includes the start, the main loop, and the termination.
412
- def run
413
- start
414
- while code = rb_code
415
- break unless process_rb_code code
416
- end
417
- terminate
418
- end
419
-
420
- # Starts the session.
421
- def start
422
- end
423
-
424
- # Reads the next Ruby codes input in the REPL session.
425
- # It can intelligently continue reading if the code is not complete yet.
426
- def rb_code
427
- result = ''
428
- begin
429
- buf = Readline.readline '> '.green, true
430
- return unless buf
431
- result.concat buf, ?\n
432
- ltype, indent, continue, block_open = @lex.check_state result
433
- rescue Interrupt
434
- $stdout.puts
435
- retry
436
- end while ltype || indent.nonzero? || continue || block_open
437
- result
438
- end
439
-
440
- # Processes the Ruby codes read.
441
- # Sending it to a score and sending the result to alda.
442
- # @return +true+ for continue looping, +false+ for breaking the loop.
443
- def process_rb_code code
444
- @score.clear
445
- begin
446
- @binding.eval code
447
- rescue StandardError, ScriptError => e
448
- $stderr.print e.full_message
449
- return true
450
- rescue Interrupt
451
- return true
452
- rescue SystemExit
453
- return false
454
- end
455
- code = @score.events_alda_codes
456
- unless code.empty?
457
- $stdout.puts code.yellow
458
- play_score code
459
- end
460
- true
461
- end
462
-
463
- # Tries to run the block and rescue CommandLineError#.
464
- def try_command # :block:
465
- begin
466
- yield
467
- rescue CommandLineError => e
468
- puts e.message.red
469
- end
470
- end
471
-
472
- # Plays the score.
473
- def play_score code
474
- try_command do
475
- Alda.play code: code, history: @history
476
- @history.puts code
477
- end
478
- end
479
-
480
- # Terminates the REPL session.
481
- def terminate
482
- clear_history
483
- end
484
-
485
- # Clears the history.
486
- def clear_history
487
- @history = StringIO.new
488
- end
489
- end
490
-
491
- # The error is raised when one tries to
492
- # run a non-existing subcommand of +alda+.
493
- class CommandLineError < StandardError
494
-
495
- # The <tt>Process::Status</tt> object representing the status of
496
- # the process that runs +alda+ command.
497
- attr_reader :status
498
-
499
- # The port on which the problematic Alda server runs.
500
- # @example
501
- # begin
502
- # Alda.play({port: 1108}, code: "y")
503
- # rescue CommandLineError => e
504
- # e.port # => 1108
505
- # end
506
- attr_reader :port
507
-
508
- # Create a CommandLineError# object.
509
- # @param status The status of the process running +alda+ command.
510
- # @param msg The exception message.
511
- def initialize status, msg = nil
512
- if match = msg&.match(/^\[(?<port>\d+)\]\sERROR\s(?<message>.*)$/)
513
- super match[:message]
514
- @port = match[:port].to_i
515
- else
516
- super msg
517
- @port = nil
518
- end
519
- @status = status
520
- end
521
- end
522
-
523
- # This error is raised when one tries to
524
- # append events in an EventList# in a wrong order.
525
- # @example
526
- # Alda::Score.new do
527
- # motif = f4 f e e d d c2
528
- # g4 f e d c2 # It commented out, error will not occur
529
- # c4 c g g a a g2 motif # (OrderError)
530
- # end
531
- class OrderError < StandardError
532
-
533
- # The expected element gotten if it is of the correct order.
534
- # @see #got
535
- # @example
536
- # Alda::Score.new do
537
- # motif = f4 f e e d d c2
538
- # g4 f e d c2
539
- # p @events.size # => 2
540
- # c4 c g g a a g2 motif
541
- # rescue OrderError => e
542
- # p @events.size # => 1
543
- # p e.expected # => #<Alda::EventContainer:...>
544
- # p e.got # => #<Alda::EventContainer:...>
545
- # end
546
- attr_reader :expected
547
-
548
- # The actually gotten element.
549
- # For an example, see #expected.
550
- # @see #expected
551
- attr_reader :got
552
-
553
- def initialize expected, got
554
- super 'events are out of order'
555
- @expected = expected
556
- @got = got
557
- end
558
- end
559
-
560
- # The class of elements of EventList#events.
561
- class Event
562
-
563
- # The EventList# object that contains it.
564
- # Note that it may not be directly contained, but with an EventContainer#
565
- # object in the middle.
566
- attr_accessor :parent
567
-
568
- # The EventContainer# object that contains it.
569
- # It may be +nil+, especially probably when
570
- # it itself is an EventContainer#.
571
- attr_accessor :container
572
-
573
- # The callback invoked when it is contained in an EventContainer#.
574
- # It is overridden in InlineLisp# and EventList#.
575
- # @example
576
- # class Alda::Note
577
- # def on_contained
578
- # puts 'a note contained'
579
- # end
580
- # end
581
- # Alda::Score.new { c } # => outputs "a note contained"
582
- def on_contained
583
- end
584
-
585
- # Converts to alda code. To be overridden.
586
- def to_alda_code
587
- ''
588
- end
589
- end
590
-
591
- # The class for objects containing an event.
592
- class EventContainer < Event
593
-
594
- # The contained Event# object.
595
- attr_accessor :event
596
-
597
- # The repetition counts. +nil+ if none.
598
- attr_accessor :count
599
-
600
- # The repetition labels. Empty if none.
601
- attr_accessor :labels
602
-
603
- # @param event The Event# object to be contained.
604
- # @param parent The EventList# object containing the event.
605
- def initialize event, parent
606
- @event = event
607
- @parent = parent
608
- @labels = []
609
- on_containing
610
- end
611
-
612
- # Make #event a Chord# object.
613
- # @example
614
- # Alda::Score.new { piano_; c/-e/g }.play
615
- # # (plays the chord Cm)
616
- #
617
- # If the contained event is a Part# object,
618
- # make #event a new Part# object.
619
- # @example
620
- # Alda::Score.new { violin_/viola_/cello_; e; f; g}.play
621
- # # (plays notes E, F, G with three instruments simultaneously)
622
- def / other
623
- unless (expected = other) == (got = @parent.events.pop)
624
- raise OrderError.new expected, got
625
- end
626
- @event =
627
- if @event.is_a? Part
628
- Part.new @event.names + other.event.names, other.event.arg
629
- else
630
- Chord.new @event, other.event
631
- end
632
- self
633
- end
634
-
635
- def to_alda_code
636
- result = @event.to_alda_code
637
- unless @labels.empty?
638
- result.concat ?', @labels.map(&:to_alda_code).join(?,)
639
- end
640
- result.concat ?*, @count.to_alda_code if @count
641
- result
642
- end
643
-
644
- # Marks repetition.
645
- def * num
646
- @count = (@count || 1) * num
647
- self
648
- end
649
-
650
- # Marks alternative repetition.
651
- def % labels
652
- labels = [labels] unless labels.is_a? Array
653
- @labels.replace labels.to_a
654
- self
655
- end
656
-
657
- def event= event
658
- @event = event
659
- on_containing
660
- @event
661
- end
662
-
663
- def on_containing
664
- if @event
665
- @event.container = self
666
- @event.parent = @parent
667
- @event.on_contained
668
- end
669
- end
670
-
671
- def method_missing name, *args
672
- result = @event.__send__ name, *args
673
- result = self if result == @event
674
- result
675
- end
676
- end
677
-
678
- # Inline lisp event.
679
- class InlineLisp < Event
680
-
681
- # The function name of the lisp function
682
- attr_accessor :head
683
-
684
- # The arguments passed to the lisp function.
685
- # Its elements can be
686
- # Array#, Hash#, Numeric#, String#, Symbol#, or Event#.
687
- attr_accessor :args
688
-
689
- # The underlines in +head+ will be converted to hyphens.
690
- def initialize head, *args
691
- @head = head.to_s.gsub ?_, ?-
692
- @args = args
693
- end
694
-
695
- def to_alda_code
696
- "(#{head} #{args.map(&:to_alda_code).join ' '})"
697
- end
698
-
699
- def on_contained
700
- super
701
- @args.reverse_each do |event|
702
- if event.is_a?(Event) && (expected = event) != (got = @parent.events.pop)
703
- raise OrderError.new expected, got
704
- end
705
- end
706
- end
707
- end
708
-
709
- # A note event.
710
- class Note < Event
711
-
712
- # The string representing the pitch
713
- attr_accessor :pitch
714
-
715
- # The string representing the duration.
716
- # It ends with +~+ if the note slurs.
717
- attr_accessor :duration
718
-
719
- # The underlines in +duration+ will be converted to +~+.
720
- # Exclamation mark and question mark in +duration+
721
- # will be interpreted as accidentals in #pitch.
722
- #
723
- # The number of underlines at the end of +duration+ means:
724
- # neither natural nor slur if 0,
725
- # natural if 1,
726
- # slur if 2,
727
- # both natural and slur if 3.
728
- def initialize pitch, duration
729
- @pitch = pitch.to_s
730
- @duration = duration.to_s.tr ?_, ?~
731
- case @duration[-1]
732
- when ?! # sharp
733
- @pitch.concat ?+
734
- @duration[-1] = ''
735
- when ?? # flat
736
- @pitch.concat ?-
737
- @duration[-1] = ''
738
- end
739
- waves = /(?<str>~+)\z/ =~ @duration ? str.size : return
740
- @duration[@duration.length - waves..] = ''
741
- if waves >= 2
742
- waves -= 2
743
- @duration.concat ?~
744
- end
745
- @pitch.concat ?_ * waves
746
- end
747
-
748
- # Append a sharp sign after #pitch.
749
- # @example
750
- # Alda::Score.new { piano_; +c }.play
751
- # # (plays a C\# note)
752
- def +@
753
- @pitch.concat ?+
754
- self
755
- end
756
-
757
- # Append a flat sign after #pitch.
758
- # @example
759
- # Alda::Score.new { piano_; -d }.play
760
- # # (plays a Db note)
761
- def -@
762
- @pitch.concat ?-
763
- self
764
- end
765
-
766
- # Append a natural sign after #pitch
767
- # @example
768
- # Alda::Score.new { piano_; key_sig 'f+'; ~f }.play
769
- # # (plays a F note)
770
- def ~
771
- @pitch.concat ?_
772
- self
773
- end
774
-
775
- def to_alda_code
776
- result = @pitch + @duration
777
- result.concat ?*, @count.to_alda_code if @count
778
- result
779
- end
780
- end
781
-
782
- # A rest event.
783
- class Rest < Event
784
-
785
- # The string representing a duration.
786
- attr_accessor :duration
787
-
788
- # Underlines in +duration+ will be converted to +~+.
789
- def initialize duration
790
- @duration = duration.to_s.tr ?_, ?~
791
- end
792
-
793
- def to_alda_code
794
- ?r + @duration
795
- end
796
- end
797
-
798
- # An octave event.
799
- class Octave < Event
800
-
801
- # The string representing the octave's number.
802
- # It can be empty, serving for #+@ and #-@.
803
- attr_accessor :num
804
-
805
- # Positive for up, negative for down, and 0 as default.
806
- attr_accessor :up_or_down
807
-
808
- def initialize num
809
- @num = num.to_s
810
- @up_or_down = 0
811
- end
812
-
813
- # Octave up.
814
- # @example
815
- # Alda::Score.new { piano_; c; +o; c }.play
816
- # # (plays C4, then C5)
817
- # @see #-@
818
- def +@
819
- @up_or_down += 1
820
- self
821
- end
822
-
823
- # Octave down.
824
- # @see #+@.
825
- def -@
826
- @up_or_down -= 1
827
- self
828
- end
829
-
830
- def to_alda_code
831
- case @up_or_down <=> 0
832
- when 0
833
- ?o + @num
834
- when 1
835
- ?> * @up_or_down
836
- when -1
837
- ?< * -@up_or_down
838
- end
839
- end
840
- end
841
-
842
- # A chord event.
843
- # Includes EventList#.
844
- class Chord < Event
845
- include EventList
846
-
847
- # EventList#x invokes this method.
848
- # @see EventList#method_missing
849
- # @param events In most cases, should not be used.
850
- # @param block To be passed with the Chord# object as +self+.
851
- # @example
852
- # Alda::Score.new { piano_; x { c; -e; g } }.play
853
- # # (plays chord Cm)
854
- def initialize *events, &block
855
- @events = events
856
- super &block
857
- end
858
-
859
- def to_alda_code
860
- events_alda_codes ?/
861
- end
862
- end
863
-
864
- # A part event.
865
- class Part < Event
866
-
867
- # The names of the part. To be joined with +/+ as delimiter.
868
- attr_accessor :names
869
-
870
- # The nickname of the part. +nil+ if none.
871
- attr_accessor :arg
872
-
873
- def initialize names, arg = nil
874
- @names = names.map { |name| name.to_s.tr ?_, ?- }
875
- @arg = arg
876
- end
877
-
878
- def to_alda_code
879
- result = @names.join ?/
880
- result.concat " \"#{@arg}\"" if @arg
881
- result.concat ?:
882
- end
883
-
884
- # Enables dot accessor.
885
- # @example
886
- # Alda::Score.new do
887
- # violin_/viola_/cello_('strings'); g1_1_1
888
- # strings_.cello_; -o; c1_1_1
889
- # end.play
890
- def method_missing name, *args
891
- str = name.to_s
892
- return super unless str[-1] == ?_
893
- str[-1] = ''
894
- @names.last.concat ?., str
895
- if args.size == 1
896
- joined = args.first
897
- unless (got = @parent.events.pop) == (expected = joined)
898
- raise OrderError.new expected, got
899
- end
900
- unless @container
901
- @container = EventContainer.new nil, @parent
902
- @parent.events.delete self
903
- @parent.push @container
904
- end
905
- @container.event = Sequence.join self, joined
906
- end
907
- end
908
- end
909
-
910
- # A voice event.
911
- class Voice < Event
912
-
913
- # The string representing the voice's number.
914
- attr_accessor :num
915
-
916
- def initialize num
917
- @num = num
918
- end
919
-
920
- def to_alda_code
921
- ?V + num + ?:
922
- end
923
- end
924
-
925
- # A CRAM event. Includes EventList#.
926
- class Cram < Event
927
- include EventList
928
-
929
- # The string representing the duration of the CRAM.
930
- attr_accessor :duration
931
-
932
- # EventList#t invokes this method.
933
- # @see EventList#method_missing
934
- # @param block To be passed with the CRAM as +self+.
935
- # @example
936
- # Alda::Score.new { piano_; t8 { x; y; }}
937
- def initialize duration, &block
938
- @duration = duration
939
- super &block
940
- end
941
-
942
- def to_alda_code
943
- "{#{events_alda_codes}}#@duration"
944
- end
945
- end
946
-
947
- # A marker event.
948
- # @see AtMarker#
949
- class Marker < Event
950
-
951
- # The marker's name
952
- attr_accessor :name
953
-
954
- # Underlines in +name+ is converted to hyphens.
955
- def initialize name
956
- @name = name.to_s.tr ?_, ?-
957
- end
958
-
959
- def to_alda_code
960
- ?% + @name
961
- end
962
- end
963
-
964
- # An at-marker event.
965
- # @see Marker#
966
- class AtMarker < Event
967
-
968
- # The corresponding marker's name
969
- attr_accessor :name
970
-
971
- # Underlines in +name+ is converted to hyphens.
972
- def initialize name
973
- @name = name.to_s.tr ?_, ?-
974
- end
975
-
976
- def to_alda_code
977
- ?@ + @name
978
- end
979
- end
980
-
981
- # A sequence event. Includes EventList#.
982
- class Sequence < Event
983
- include EventList
984
-
985
- # Using this module can fix a bug of Array#flatten.
986
- # @example
987
- # def (a = Object.new).method_missing(...)
988
- # Object.new
989
- # end
990
- # [a].flatten rescue $! # => #<TypeError:...>
991
- # using Alda::Sequence::RefineFlatten
992
- # [a].flatten # => [#<Object:...>]
993
- module RefineFlatten
994
- refine Array do
995
- def flatten
996
- each_with_object [] do |element, result|
997
- if element.is_a? Array
998
- result.push *element.flatten
999
- else
1000
- result.push element
1001
- end
1002
- end
1003
- end
1004
- end
1005
- end
1006
- using RefineFlatten
1007
-
1008
- def to_alda_code
1009
- @events.to_alda_code
1010
- end
1011
-
1012
- # Creates a Sequence# object by joining +events+.
1013
- # The EventContainer# objects are extracted,
1014
- # and the Sequence# objects are flattened.
1015
- def self.join *events
1016
- new do
1017
- @events = events.map do |event|
1018
- while event.is_a?(EventContainer) && !event.count && event.labels.empty?
1019
- event = event.event
1020
- end
1021
- event.is_a?(Sequence) ? event.events : event
1022
- end.flatten
1023
- end
1024
- end
1025
- end
1026
-
1027
- # A set-variable event.
1028
- # Includes EventList#.
1029
- class SetVariable < Event
1030
- include EventList
1031
-
1032
- # The name of the variable.
1033
- attr_accessor :name
1034
-
1035
- # The events passed to it using arguments instead of a block.
1036
- attr_reader :original_events
1037
-
1038
- def initialize name, *events, &block
1039
- @name = name.to_sym
1040
- @original_events = events
1041
- @events = events.clone
1042
- super &block
1043
- end
1044
-
1045
- # Specially, the result ends with a newline.
1046
- def to_alda_code
1047
- "#@name = #{events_alda_codes}\n"
1048
- end
1049
-
1050
- def on_contained
1051
- super
1052
- @parent.variables.add @name
1053
- @original_events.reverse_each do |event|
1054
- unless (expected = event) == (got = @parent.events.pop)
1055
- raise OrderError.new expected, got
1056
- end
1057
- end
1058
- end
1059
- end
1060
-
1061
- # A get-variable event
1062
- class GetVariable < Event
1063
-
1064
- # The name of the variable
1065
- attr_accessor :name
1066
-
1067
- def initialize name
1068
- @name = name
1069
- end
1070
-
1071
- def to_alda_code
1072
- @name.to_s
1073
- end
1074
- end
1075
- end
3
+ require 'alda-rb/version'
4
+ require 'alda-rb/patches'
5
+ require 'alda-rb/error'
6
+ require 'alda-rb/commandline'
7
+ require 'alda-rb/event_list'
8
+ require 'alda-rb/repl'
9
+ require 'alda-rb/event'