Class: Alda::EventContainer

Inherits:
Event
  • Object
show all
Defined in:
lib/alda-rb/event.rb

Overview

The class for objects containing an event.

Alda::EventContainer objects are literally everywhere if you are a heavy user of event list sugars. See Alda::EventList#method_missing.

Instance Attribute Summary collapse

Attributes inherited from Event

#container, #parent

Instance Method Summary collapse

Methods inherited from Event

#detach_from_parent, #on_contained

Constructor Details

#initialize(event, parent) ⇒ EventContainer

:call-seq:

new(event, parent) -> Alda::EventContainer

Creates a new Alda::EventContainer. Invokes #on_containing.

event is the Alda::Event object to be contained.

parent is the Alda::EventList object containing the event.



147
148
149
150
151
152
153
154
# File 'lib/alda-rb/event.rb', line 147

def initialize event, parent
	super()
	@event = event
	@labels = []
	@count = 1
	self.parent = parent
	on_containing
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missingObject

:call-seq:

(missing method) -> obj

Calls method on #event.

Note that if the method of #event returns #event itself, the method here returns the container itself.

Alda::Score.new do
  container = c
  p container.class              # => Alda::EventContainer
  p container.respond_to? :pitch # => false
  p container.pitch              # => "c"
  p container.respond_to? :+@    # => false
  p((+container).class)          # => Alda::EventContainer
  p to_s                         # => "c+"
end


335
336
337
338
# File 'lib/alda-rb/event.rb', line 335

def method_missing(...)
	result = @event.__send__(...)
	result == @event ? self : result
end

Instance Attribute Details

#countObject

The repetition counts. nil if none.

Alda::Score.new do
  p((c*2).count)   # => 2
  p((d*3*5).count) # => 15
end


126
127
128
# File 'lib/alda-rb/event.rb', line 126

def count
  @count
end

#eventObject

The contained Alda::Event object.

 Alda::Score.new do
   p c.event.class      # => Alda::Note
   p((e/g).event.class) # => Alda::Chord
   p((a b).event.class) # => Alda::Sequence
end

When setting this attribute, #on_containing is invoked.



117
118
119
# File 'lib/alda-rb/event.rb', line 117

def event
  @event
end

#labelsObject

The repetition labels. Empty if none.

Alda::Score.new do
  p((c%2).labels)        # => [2]
  p((c%[2,4..6]).labels) # => [2, 4..6]
end


135
136
137
# File 'lib/alda-rb/event.rb', line 135

def labels
  @labels
end

Instance Method Details

#%(labels) ⇒ Object

:call-seq:

container % labels -> container

Marks alternative endings.

Alda::Score.new { (b a%1)*2 }.to_s
# => "[b a'1]*2"


232
233
234
235
236
237
# File 'lib/alda-rb/event.rb', line 232

def % labels
	labels = [labels] unless labels.is_a? Array
	@labels.replace labels.to_a
	check_in_chord
	self
end

#*(num) ⇒ Object

:call-seq:

container * num -> container

Marks repetition.

For examples, see #%.



218
219
220
221
222
# File 'lib/alda-rb/event.rb', line 218

def * num
	@count = (@count || 1) * num
	check_in_chord
	self
end

#/(other) ⇒ Object

:call-seq:

container / other -> container

If at first #event is not an Alda::Part, makes #event an Alda::Chord object.

Alda::Score.new { piano_; c/-e/g }.play
# (plays the chord Cm)

This usage assumes that other is an Alda::EventContainer and will extract the contained event out from other. This will lose some information about other, such as #count and #labels, and potentially lead to confusing results.

Because the #labels information about other is lost, the label on d disappears in the following example:

Alda::Score.new { c/(d%1) }.to_s # => "c/d"

The following example shows that the two ways of writing a chord with a label are equivalent: adding the label and then using slash, or using slash and then adding the label. This is because #labels and #count are retained while the #event is updated when #/ is called.

Alda::Score.new { p c%1/d == c/d%1 }.to_s # (prints "true") => "c/d'1 c/d'1"

If at first #event is an Alda::Part object, makes #event a new Alda::Part object. The meaning is to play the two parts simultaneously.

Alda::Score.new { violin_/viola_/cello_; e; f; g}.play
# (plays notes E, F, G with three instruments simultaneously)


188
189
190
191
192
193
194
195
196
197
# File 'lib/alda-rb/event.rb', line 188

def / other
	other.detach_from_parent
	@event =
			if @event.is_a? Alda::Part
				Alda::Part.new @event.names + other.event.names, other.event.arg
			else
				Alda::Chord.new @event, other.event
			end
	self
end

#==(other) ⇒ Object

:call-seq:

container == other -> true or false

Overrides Alda::Event#==. Returns true if other is an Alda::EventContainer object and #event, #count and #labels are all equal (using ==).



312
313
314
315
# File 'lib/alda-rb/event.rb', line 312

def == other
	super || other.is_a?(Alda::EventContainer) &&
			@event == other.event && @count == other.count && @labels == other.labels
end

#check_in_chordObject

:call-seq:

check_in_chord() -> true or false

This method is called in #%, #*, and #parent=. It checks if #parent is an Alda::Chord and warns about potential dangers. Returns true if there is no danger, and false otherwise.

Because Alda 2 does not support specifying alternative endings inside a chord (something like a'1/b) (alda-lang/alda#383), this method will warn about this if such thing happens and we are using Alda 2.

Alda.v2!
Alda::Score.new { x{a%1;b} }.to_s # (warns) => "a'1/b"

This method will warn about repetitions inside a chord in both generations because the resultant Alda code is not valid.

Alda::Score.new { x{a*2;b} }.to_s # (warns) => "a*2/b"


263
264
265
266
267
268
269
270
271
# File 'lib/alda-rb/event.rb', line 263

def check_in_chord
	if @parent.is_a?(Alda::Event) && @parent.is_event_of?(Alda::Chord)
		Alda::Utils.warn 'alternative endings in chord not allowed in v2' if Alda.v2? && !@labels&.empty?
		Alda::Utils.warn 'repetitions in chord not allowed' if @count && @count != 1
		false
	else
		true
	end
end

#is_event_of?(klass) ⇒ Boolean

:call-seq:

is_event_of?(klass) -> true or false

Overrides Alda::Event#is_event_of?. Whether it is an event of the given class (klass) or the contained event is.

Returns:

  • (Boolean)


301
302
303
# File 'lib/alda-rb/event.rb', line 301

def is_event_of? klass
	super || @event.is_event_of?(klass)
end

#on_containingObject

A callback invoked in #event= and ::new.



287
288
289
290
291
292
# File 'lib/alda-rb/event.rb', line 287

def on_containing
	return unless @event
	@event.container = self
	@event.parent = @parent
	@event.on_contained
end

#parent=(event) ⇒ Object

:call-seq:

parent=(event) -> event

Overrides Alda::Event#parent=. Sets the Alda::Event#parent of the container as well as that of #event.



279
280
281
282
283
# File 'lib/alda-rb/event.rb', line 279

def parent= event
	@parent = event
	check_in_chord
	@event.parent = event
end

#to_alda_codeObject

:call-seq:

to_alda_code() -> String

Overrides Alda::Event#to_alda_code.



204
205
206
207
208
209
# File 'lib/alda-rb/event.rb', line 204

def to_alda_code
	result = @event.to_alda_code
	result.concat ?', @labels.map(&:to_alda_code).join(?,) unless @labels.empty?
	result.concat ?*, @count.to_alda_code if @count != 1
	result
end