Class: HeadMusic::Content::Voice

Inherits:
Object
  • Object
show all
Defined in:
lib/head_music/content/voice.rb,
lib/head_music/content/voice/continuity.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: Continuity, MelodicLine, MelodicNotePair

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(composition: nil, role: nil) ⇒ Voice

Returns a new instance of Voice.



16
17
18
19
20
# File 'lib/head_music/content/voice.rb', line 16

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.



7
8
9
# File 'lib/head_music/content/voice.rb', line 7

def composition
  @composition
end

#placementsObject (readonly)

Returns the value of attribute placements.



7
8
9
# File 'lib/head_music/content/voice.rb', line 7

def placements
  @placements
end

#roleObject (readonly)

Returns the value of attribute role.



7
8
9
# File 'lib/head_music/content/voice.rb', line 7

def role
  @role
end

Instance Method Details

#bar_number_of(placement) ⇒ Object (private)



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

def bar_number_of(placement)
  placement ? placement.position.bar_number : 1
end

#cantus_firmus?Boolean

Returns:

  • (Boolean)


52
53
54
# File 'lib/head_music/content/voice.rb', line 52

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

#earliest_bar_numberObject



56
57
58
# File 'lib/head_music/content/voice.rb', line 56

def earliest_bar_number
  bar_number_of(placements.first)
end

#first_gapObject

Returns nil if placements are contiguous, or [expected_position, found_placement] for the first gap.



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

def first_gap
  Continuity.new(composition, placements).first_gap
end

#insert_into_placements(placement) ⇒ Object (private)

Positions are unique within a voice (place merges same-position placements), so insertion order is simply position order.



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

def insert_into_placements(placement)
  index = placements.index { |existing| existing > placement } || placements.length
  placements.insert(index, placement)
end

#last_placementObject



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

def last_placement
  placements.last
end

#latest_bar_numberObject



60
61
62
# File 'lib/head_music/content/voice.rb', line 60

def latest_bar_number
  bar_number_of(placements.last)
end

#melodic_lineObject



48
49
50
# File 'lib/head_music/content/voice.rb', line 48

def melodic_line
  @melodic_line ||= MelodicLine.new(notes)
end

#next_positionObject



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

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

#notesObject

Placements are kept in position order, so the notes and rests drawn from them are already ordered.



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

def notes
  placements.select(&:pitched?)
end

#notes_not_in_keyObject



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

def notes_not_in_key
  key_spellings = key_signature.spellings
  notes.reject { |note| key_spellings.include?(note.pitch.spelling) }
end

#pitches_stringObject (private)



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

def pitches_string
  pitches.first(10).join(" ")
end

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



22
23
24
25
26
27
28
29
30
31
# File 'lib/head_music/content/voice.rb', line 22

def place(position, rhythmic_value, sound_or_sounds = nil)
  # The melodic line is a snapshot of the notes, so any placement invalidates it.
  @melodic_line = 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 (private)



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

def placement_at(position)
  placements.find { |placement| placement.position == position }
end

#restsObject



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

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

#to_hObject



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

def to_h
  {
    "role" => role&.to_s,
    "placements" => placements.map(&:to_h)
  }
end

#to_sObject



78
79
80
81
82
# File 'lib/head_music/content/voice.rb', line 78

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

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