Class: HeadMusic::Content::Voice
- Inherits:
-
Object
- Object
- HeadMusic::Content::Voice
show all
- Includes:
- Comparable
- Defined in:
- lib/head_music/content/voice.rb,
lib/head_music/content/voice/melodic_line.rb,
lib/head_music/content/voice/melodic_note_pair.rb
Overview
A Voice is a stream of music with some indepedence that is conceptually one part or for one performer.
The melodic lines in counterpoint are each a voice.
Defined Under Namespace
Classes: MelodicLine, MelodicNotePair
Instance Attribute Summary collapse
Instance Method Summary
collapse
Constructor Details
#initialize(composition: nil, role: nil) ⇒ Voice
Returns a new instance of Voice.
13
14
15
16
17
|
# File 'lib/head_music/content/voice.rb', line 13
def initialize(composition: nil, role: nil)
@composition = composition || HeadMusic::Content::Composition.new
@role = role
@placements = []
end
|
Instance Attribute Details
#composition ⇒ Object
Returns the value of attribute composition.
9
10
11
|
# File 'lib/head_music/content/voice.rb', line 9
def composition
@composition
end
|
#placements ⇒ Object
Returns the value of attribute placements.
9
10
11
|
# File 'lib/head_music/content/voice.rb', line 9
def placements
@placements
end
|
#role ⇒ Object
Returns the value of attribute role.
9
10
11
|
# File 'lib/head_music/content/voice.rb', line 9
def role
@role
end
|
Instance Method Details
#bar_start_position(placement) ⇒ Object
144
145
146
|
# File 'lib/head_music/content/voice.rb', line 144
def bar_start_position(placement)
HeadMusic::Content::Position.new(composition, placement.position.bar_number, 1, 0)
end
|
#cantus_firmus? ⇒ Boolean
71
72
73
|
# File 'lib/head_music/content/voice.rb', line 71
def cantus_firmus?
role.to_s =~ /cantus.?firmus/i
end
|
#earliest_bar_number ⇒ Object
91
92
93
94
95
|
# File 'lib/head_music/content/voice.rb', line 91
def earliest_bar_number
return 1 if notes.empty?
placements.first.position.bar_number
end
|
#first_gap ⇒ Object
Returns nil if placements are contiguous, or [expected_position, found_placement]
for the first gap: either the first placement not starting its bar, or the
first pair of consecutive placements where the second doesn’t begin where
the first one ends.
115
116
117
118
119
120
121
122
123
124
125
126
127
|
# File 'lib/head_music/content/voice.rb', line 115
def first_gap
first = placements.first
return unless first
first_position = first.position
return [bar_start_position(first), first] unless first_position.count == 1 && first_position.tick.zero?
placements.each_cons(2) do |previous, current|
expected_position = previous.next_position
return [expected_position, current] unless current.position == expected_position
end
nil
end
|
#highest_notes ⇒ Object
53
54
55
|
# File 'lib/head_music/content/voice.rb', line 53
def highest_notes
notes.select { |note| note.pitch == highest_pitch }
end
|
#highest_pitch ⇒ Object
45
46
47
|
# File 'lib/head_music/content/voice.rb', line 45
def highest_pitch
pitches.max
end
|
#insert_into_placements(placement) ⇒ Object
Positions are unique within a voice (place merges same-position
placements), so insertion order is simply position order.
154
155
156
157
|
# File 'lib/head_music/content/voice.rb', line 154
def insert_into_placements(placement)
index = @placements.index { |existing| existing > placement } || @placements.length
@placements.insert(index, placement)
end
|
#last_placement ⇒ Object
103
104
105
|
# File 'lib/head_music/content/voice.rb', line 103
def last_placement
placements.last
end
|
#latest_bar_number ⇒ Object
97
98
99
100
101
|
# File 'lib/head_music/content/voice.rb', line 97
def latest_bar_number
return 1 if notes.empty?
placements.last.position.bar_number
end
|
#lowest_notes ⇒ Object
57
58
59
|
# File 'lib/head_music/content/voice.rb', line 57
def lowest_notes
notes.select { |note| note.pitch == lowest_pitch }
end
|
#lowest_pitch ⇒ Object
49
50
51
|
# File 'lib/head_music/content/voice.rb', line 49
def lowest_pitch
pitches.min
end
|
#melodic_line ⇒ Object
65
66
67
|
# File 'lib/head_music/content/voice.rb', line 65
def melodic_line
@melodic_line ||= MelodicLine.new(notes)
end
|
#next_position ⇒ Object
107
108
109
|
# File 'lib/head_music/content/voice.rb', line 107
def next_position
last_placement ? last_placement.next_position : HeadMusic::Content::Position.new(composition, 1, 1, 0)
end
|
#note_at(position) ⇒ Object
75
76
77
|
# File 'lib/head_music/content/voice.rb', line 75
def note_at(position)
notes.detect { |note| position.within_placement?(note) }
end
|
#note_following(position) ⇒ Object
87
88
89
|
# File 'lib/head_music/content/voice.rb', line 87
def note_following(position)
notes.detect { |note| note.position > position }
end
|
#note_preceding(position) ⇒ Object
83
84
85
|
# File 'lib/head_music/content/voice.rb', line 83
def note_preceding(position)
notes.reverse.find { |note| note.position < position }
end
|
#notes ⇒ Object
28
29
30
|
# File 'lib/head_music/content/voice.rb', line 28
def notes
@placements.select(&:pitched?).sort_by(&:position)
end
|
#notes_during(placement) ⇒ Object
79
80
81
|
# File 'lib/head_music/content/voice.rb', line 79
def notes_during(placement)
notes.select { |note| note.during?(placement) }
end
|
#notes_not_in_key ⇒ Object
32
33
34
35
|
# File 'lib/head_music/content/voice.rb', line 32
def notes_not_in_key
key_spellings = key_signature.pitches.map(&:spelling).uniq
notes.reject { |note| key_spellings.include? note.pitch.spelling }
end
|
#pitches ⇒ Object
37
38
39
|
# File 'lib/head_music/content/voice.rb', line 37
def pitches
notes.map(&:pitch)
end
|
#pitches_string ⇒ Object
159
160
161
|
# File 'lib/head_music/content/voice.rb', line 159
def pitches_string
pitches.first(10).map(&:to_s).join(" ")
end
|
#place(position, rhythmic_value, sound_or_sounds = nil) ⇒ Object
19
20
21
22
23
24
25
26
|
# File 'lib/head_music/content/voice.rb', line 19
def place(position, rhythmic_value, sound_or_sounds = nil)
placement = HeadMusic::Content::Placement.new(self, position, rhythmic_value, sound_or_sounds)
existing = placement_at(placement.position)
return existing.merge(placement) if existing
insert_into_placements(placement)
placement
end
|
#placement_at(position) ⇒ Object
148
149
150
|
# File 'lib/head_music/content/voice.rb', line 148
def placement_at(position)
@placements.find { |placement| placement.position == position }
end
|
#rests ⇒ Object
41
42
43
|
# File 'lib/head_music/content/voice.rb', line 41
def rests
@placements.select(&:rest?)
end
|
#to_h ⇒ Object
135
136
137
138
139
140
|
# File 'lib/head_music/content/voice.rb', line 135
def to_h
{
"role" => role&.to_s,
"placements" => placements.map(&:to_h)
}
end
|
#to_s ⇒ Object
129
130
131
132
133
|
# File 'lib/head_music/content/voice.rb', line 129
def to_s
return pitches_string if role.to_s.strip == ""
[role, pitches_string].join(": ")
end
|