Module: Alda::EventList

Included in:
Chord, Cram, Score, Sequence, SetVariable
Defined in:
lib/alda-rb/event_list.rb

Overview

Including this module can make your class have the ability to have an event list. See docs below to get an overview of its functions.

Instance Attribute Summary collapse

Instance Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &block) ⇒ Object

:call-seq:

(some sugar) -> Alda::EventContainer

Make the object have the ability to append its #events conveniently.

Here is a list of sugar. When the name of a method meets certain condition, the method is regarded as an event appended to #events.

  1. Ending with 2 underlines: set variable. See Alda::SetVariable.

  2. Starting with 2 lowercase letters and ending with underline character: instrument. See Alda::Part. This will trigger a warning if we are using Alda 2 because parts inside a sequence are not allowed in Alda 2 (alda-lang/alda#441).

  3. Starting with 2 lowercase letters: inline lisp code, set variable, or get variable. One of the above three is chosen intelligently. See Alda::InlineLisp, Alda::SetVariable, Alda::GetVariable.

  4. Starting with “t”: CRAM. See Alda::Cram.

  5. Starting with one of “a”, “b”, …, “g”: note. See Alda::Note.

  6. Starting with “r”: rest. See Alda::Rest.

  7. “x”: chord. See Alda::Chord.

  8. “s”: sequence. See Alda::Sequence.

  9. Starting with “o”: octave. See Alda::Octave.

  10. Starting with “v”: voice. See Alda::Voice.

  11. Starting with “__” (2 underlines): at marker. See Alda::AtMarker.

  12. Starting with “_” (underline) and ending with “_” (underline): lisp identifier. See Alda::LispIdentifier.

  13. Starting with “_” (underline): marker. See Alda::Marker.

All the appended events are contained in an Alda::EventContainer object, which is to be returned.

These sugars forms a DSL. See ::new for examples.



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/alda-rb/event_list.rb', line 75

def method_missing name, *args, &block
	if @parent&.respond_to? name, true
		return @parent.__send__ name, *args, &block
	end
	sequence_sugar = ->event do
		if args.size == 1
			Alda::Sequence.join event, args.first.tap(&:detach_from_parent)
		else
			event
		end
	end
	case
	when /\A(?<head>[a-z][a-z].*)__\z/        =~ name
		Alda::SetVariable.new head, *args, &block
	when /\A(?<part>[a-z][a-z].*)_\z/         =~ name
		if args.first.is_a? String
			Alda::Part.new [part], args.first
		else
			Alda::Utils.warn 'parts in sequence not allowed in v2' if Alda.v2? && !args.empty?
			sequence_sugar.(Alda::Part.new [part])
		end
	when /\A[a-z][a-z].*\z/                   =~ name
		arg = args.first
		if block || !has_variable?(name) && args.size == 1 && arg.is_a?(Alda::Event) &&
				!arg.is_event_of?(Alda::InlineLisp) && !arg.is_event_of?(Alda::LispIdentifier)
			Alda::SetVariable.new name, *args, &block
		elsif has_variable?(name) && (args.empty? || args.size == 1 && arg.is_a?(Alda::Event))
			sequence_sugar.(Alda::GetVariable.new name)
		else
			Alda::InlineLisp.new name, *args
		end
	when /\At(?<duration>.*)\z/               =~ name
		Alda::Cram.new duration, &block
	when /\A(?<pitch>[a-g])(?<duration>.*)\z/ =~ name
		sequence_sugar.(Alda::Note.new pitch, duration)
	when /\Ar(?<duration>.*)\z/               =~ name
		sequence_sugar.(Alda::Rest.new duration)
	when /\Ax\z/                              =~ name
		Alda::Chord.new &block
	when /\As\z/                              =~ name
		Alda::Sequence.new *args, &block
	when /\Ao!\z/                             =~ name
		sequence_sugar.(Alda::Octave.new('').tap { _1.up_or_down = 1})
	when /\Ao\?\z/                            =~ name
		sequence_sugar.(Alda::Octave.new('').tap { _1.up_or_down = -1})
	when /\Ao(?<num>\d*)\z/                   =~ name
		sequence_sugar.(Alda::Octave.new num)
	when /\Av(?<num>\d+)\z/                   =~ name
		sequence_sugar.(Alda::Voice.new num)
	when /\A__(?<head>.+)\z/                  =~ name
		sequence_sugar.(Alda::AtMarker.new head)
	when /\A_(?<head>.+)_\z/                  =~ name
		sequence_sugar.(Alda::LispIdentifier.new head)
	when /\A_(?<head>.+)\z/                   =~ name
		sequence_sugar.(Alda::Marker.new head)
	else
		super
	end.then do |event|
		Alda::EventContainer.new event, self
	end.tap { @events.push _1 }
end

Instance Attribute Details

#eventsObject

The array containing the events (Alda::Event objects), most of which are Alda::EventContainer objects.



12
13
14
# File 'lib/alda-rb/event_list.rb', line 12

def events
  @events
end

#variablesObject

The set containing the available variable names.



16
17
18
# File 'lib/alda-rb/event_list.rb', line 16

def variables
  @variables
end

Instance Method Details

#==(other) ⇒ Object

:call-seq:

event_list == other -> true or false

Returns true if other is of the same class as event_list and they have the same (in the sense of ==) #events and #variables.



215
216
217
# File 'lib/alda-rb/event_list.rb', line 215

def == other
	super || self.class == other.class && @events == other.events && @variables == other.variables
end

#events_alda_codes(delimiter = ' ') ⇒ Object

:call-seq:

events_alda_codes(delimiter=" ") -> String

Join the alda codes of #events with a specified delimiter. Returns a string representing the result.



205
206
207
# File 'lib/alda-rb/event_list.rb', line 205

def events_alda_codes delimiter = ' '
	@events.map(&:to_alda_code).join delimiter
end

#has_variable?(name) ⇒ Boolean

:call-seq:

has_variable?(name) -> true or false

Whether there is a previously declared alda variable whose name is specified by name.

Searches variables in #parent.

Returns:

  • (Boolean)


145
146
147
# File 'lib/alda-rb/event_list.rb', line 145

def has_variable? name
	@variables.include?(name) || !!@parent&.has_variable?(name)
end

#import(event_list) ⇒ Object

:call-seq:

import(event_list) -> nil

Append the events of another Alda::EventList object here. This method covers the disadvantage of alda’s being unable to import scores from other files (alda-lang/alda-core#8).



157
158
159
160
# File 'lib/alda-rb/event_list.rb', line 157

def import event_list
	@events.concat event_list.events
	nil
end

#initialize(&block) ⇒ Object

:call-seq:

new(&block) -> Alda::EventList

The parameter block is to be passed with the Alda::EventList object as self.

Note that block is not called immediately. It is instead called in #on_contained. Specially, Alda::Score::new calls #on_contained.

Alda::Score.new do
  tempo! 108           # inline lisp
  piano_               # piano part
  o4                   # octave 4
  c8; d; e; f          # notes
  g4 g a f g e f d e c # a sequence
  d4_8                 # cannot have '~', use '_' instead
  o3 b8 o4 c2          # a sequence
end
# => #<Alda::Score:0x... @events=[...]>

For a list of sugars, see #method_missing.



184
185
186
187
188
# File 'lib/alda-rb/event_list.rb', line 184

def initialize &block
	@events ||= []
	@variables ||= Set.new
	@block ||= block
end

#on_containedObject

When the module is included by a subclass of Alda::Event, this method overrides Alda::Event#on_contained. When invoked, calls the overridden method (if any) and then evaluates the block given when ::new was called.



23
24
25
26
# File 'lib/alda-rb/event_list.rb', line 23

def on_contained
	super if defined? super
	instance_eval &@block if @block
end

#to_aObject

:call-seq:

to_a -> Array

Same as #events.



195
196
197
# File 'lib/alda-rb/event_list.rb', line 195

def to_a
	@events
end