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 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'