Class: HeadMusic::Content::Voice

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/head_music/content/voice.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: 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

#compositionObject (readonly)

Returns the value of attribute composition.



9
10
11
# File 'lib/head_music/content/voice.rb', line 9

def composition
  @composition
end

#placementsObject (readonly)

Returns the value of attribute placements.



9
10
11
# File 'lib/head_music/content/voice.rb', line 9

def placements
  @placements
end

#roleObject (readonly)

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 (private)



145
146
147
# File 'lib/head_music/content/voice.rb', line 145

def bar_start_position(placement)
  HeadMusic::Content::Position.new(composition, placement.position.bar_number, 1, 0)
end

#cantus_firmus?Boolean

Returns:

  • (Boolean)


81
82
83
# File 'lib/head_music/content/voice.rb', line 81

def cantus_firmus?
  role.to_s =~ /cantus.?firmus/i
end

#earliest_bar_numberObject



101
102
103
104
105
# File 'lib/head_music/content/voice.rb', line 101

def earliest_bar_number
  return 1 if notes.empty?

  placements.first.position.bar_number
end

#first_gapObject

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.



125
126
127
128
129
130
131
132
133
134
135
# File 'lib/head_music/content/voice.rb', line 125

def first_gap
  first = placements.first
  return unless first

  return [bar_start_position(first), first] unless first.position.count == 1 && first.position.tick.zero?

  placements.each_cons(2) do |previous, current|
    return [previous.next_position, current] unless current.position == previous.next_position
  end
  nil
end

#highest_notesObject



50
51
52
# File 'lib/head_music/content/voice.rb', line 50

def highest_notes
  notes.select { |note| note.pitch == highest_pitch }
end

#highest_pitchObject



42
43
44
# File 'lib/head_music/content/voice.rb', line 42

def highest_pitch
  pitches.max
end

#insert_into_placements(placement) ⇒ Object (private)



149
150
151
152
# File 'lib/head_music/content/voice.rb', line 149

def insert_into_placements(placement)
  @placements << placement
  @placements = @placements.sort
end

#large_leapsObject



77
78
79
# File 'lib/head_music/content/voice.rb', line 77

def large_leaps
  melodic_note_pairs.select(&:large_leap?)
end

#last_placementObject



113
114
115
# File 'lib/head_music/content/voice.rb', line 113

def last_placement
  placements.last
end

#latest_bar_numberObject



107
108
109
110
111
# File 'lib/head_music/content/voice.rb', line 107

def latest_bar_number
  return 1 if notes.empty?

  placements.last.position.bar_number
end

#leapsObject



73
74
75
# File 'lib/head_music/content/voice.rb', line 73

def leaps
  melodic_note_pairs.select(&:leap?)
end

#lowest_notesObject



54
55
56
# File 'lib/head_music/content/voice.rb', line 54

def lowest_notes
  notes.select { |note| note.pitch == lowest_pitch }
end

#lowest_pitchObject



46
47
48
# File 'lib/head_music/content/voice.rb', line 46

def lowest_pitch
  pitches.min
end

#melodic_intervalsObject



68
69
70
71
# File 'lib/head_music/content/voice.rb', line 68

def melodic_intervals
  @melodic_intervals ||=
    melodic_note_pairs.map { |note_pair| HeadMusic::Analysis::MelodicInterval.new(*note_pair.notes) }
end

#melodic_note_pairsObject



62
63
64
65
66
# File 'lib/head_music/content/voice.rb', line 62

def melodic_note_pairs
  @melodic_note_pairs ||= notes.each_cons(2).map do |first_note, second_note|
    HeadMusic::Content::Voice::MelodicNotePair.new(first_note, second_note)
  end
end

#next_positionObject



117
118
119
# File 'lib/head_music/content/voice.rb', line 117

def next_position
  last_placement ? last_placement.next_position : HeadMusic::Content::Position.new(composition, 1, 1, 0)
end

#note_at(position) ⇒ Object



85
86
87
# File 'lib/head_music/content/voice.rb', line 85

def note_at(position)
  notes.detect { |note| position.within_placement?(note) }
end

#note_following(position) ⇒ Object



97
98
99
# File 'lib/head_music/content/voice.rb', line 97

def note_following(position)
  notes.detect { |note| note.position > position }
end

#note_preceding(position) ⇒ Object



93
94
95
# File 'lib/head_music/content/voice.rb', line 93

def note_preceding(position)
  notes.reverse.find { |note| note.position < position }
end

#notesObject



25
26
27
# File 'lib/head_music/content/voice.rb', line 25

def notes
  @placements.select(&:note?).sort_by(&:position)
end

#notes_during(placement) ⇒ Object



89
90
91
# File 'lib/head_music/content/voice.rb', line 89

def notes_during(placement)
  notes.select { |note| note.during?(placement) }
end

#notes_not_in_keyObject



29
30
31
32
# File 'lib/head_music/content/voice.rb', line 29

def notes_not_in_key
  key_spellings = key_signature.pitches.map(&:spelling).uniq
  notes.reject { |note| key_spellings.include? note.pitch.spelling }
end

#pitchesObject



34
35
36
# File 'lib/head_music/content/voice.rb', line 34

def pitches
  notes.map(&:pitch)
end

#pitches_stringObject (private)



154
155
156
# File 'lib/head_music/content/voice.rb', line 154

def pitches_string
  pitches.first(10).map(&:to_s).join(" ")
end

#place(position, rhythmic_value, pitch = nil) ⇒ Object



19
20
21
22
23
# File 'lib/head_music/content/voice.rb', line 19

def place(position, rhythmic_value, pitch = nil)
  HeadMusic::Content::Placement.new(self, position, rhythmic_value, pitch).tap do |placement|
    insert_into_placements(placement)
  end
end

#rangeObject



58
59
60
# File 'lib/head_music/content/voice.rb', line 58

def range
  HeadMusic::Analysis::DiatonicInterval.new(lowest_pitch, highest_pitch)
end

#restsObject



38
39
40
# File 'lib/head_music/content/voice.rb', line 38

def rests
  @placements.select(&:rest?)
end

#to_sObject



137
138
139
140
141
# File 'lib/head_music/content/voice.rb', line 137

def to_s
  return pitches_string if role.to_s.strip == ""

  [role, pitches_string].join(": ")
end