Class: HeadMusic::Style::Guidelines::SuspensionTreatment

Inherits:
Annotation
  • Object
show all
Defined in:
lib/head_music/style/guidelines/suspension_treatment.rb

Overview

A counterpoint guideline for fourth species suspension treatment. A suspension has three parts: 1. Preparation: The note is consonant with the current cantus firmus note. 2. Suspension: The cantus firmus moves; the counterpoint sustains, becoming dissonant. 3. Resolution: The counterpoint resolves by step down to a consonance.

Constant Summary collapse

MESSAGE =
"Treat suspensions with proper preparation and downward stepwise resolution."

Instance Method Summary collapse

Constructor Details

This class inherits a constructor from HeadMusic::Style::Annotation

Instance Method Details

#dissonant_suspensionsObject (private)



26
27
28
29
30
31
32
33
34
35
36
# File 'lib/head_music/style/guidelines/suspension_treatment.rb', line 26

def dissonant_suspensions
  cantus_firmus.notes[1..].filter_map do |cf_note|
    cp_note = voice.note_at(cf_note.position)
    next unless cp_note && cp_note.position < cf_note.position

    interval = HeadMusic::Analysis::HarmonicInterval.new(cantus_firmus, voice, cf_note.position)
    next unless interval.notes.length == 2 && interval.dissonance?(:two_part_harmony)

    [cp_note, cf_note]
  end
end

#improperly_treated_suspensionsObject (private)



22
23
24
# File 'lib/head_music/style/guidelines/suspension_treatment.rb', line 22

def improperly_treated_suspensions
  dissonant_suspensions.reject { |note, cf_note| properly_treated?(note, cf_note) }.map(&:first)
end

#marksObject



12
13
14
15
16
17
18
# File 'lib/head_music/style/guidelines/suspension_treatment.rb', line 12

def marks
  return [] unless cantus_firmus&.notes&.any?

  improperly_treated_suspensions.map do |note|
    HeadMusic::Style::Mark.for(note)
  end
end

#prepared?(cp_note, cf_note) ⇒ Boolean (private)

Returns:

  • (Boolean)


42
43
44
45
46
47
48
# File 'lib/head_music/style/guidelines/suspension_treatment.rb', line 42

def prepared?(cp_note, cf_note)
  prev_cf = previous_cf_note(cf_note)
  return false unless prev_cf

  interval = HeadMusic::Analysis::HarmonicInterval.new(cantus_firmus, voice, cp_note.position)
  interval.notes.length == 2 && interval.consonance?(:two_part_harmony)
end

#previous_cf_note(cf_note) ⇒ Object (private)



61
62
63
64
# File 'lib/head_music/style/guidelines/suspension_treatment.rb', line 61

def previous_cf_note(cf_note)
  index = cantus_firmus.notes.index(cf_note)
  cantus_firmus.notes[index - 1] if index && index > 0
end

#properly_treated?(cp_note, cf_note) ⇒ Boolean (private)

Returns:

  • (Boolean)


38
39
40
# File 'lib/head_music/style/guidelines/suspension_treatment.rb', line 38

def properly_treated?(cp_note, cf_note)
  prepared?(cp_note, cf_note) && resolved?(cp_note, cf_note)
end

#resolved?(cp_note, cf_note) ⇒ Boolean (private)

Returns:

  • (Boolean)


50
51
52
53
54
55
56
57
58
59
# File 'lib/head_music/style/guidelines/suspension_treatment.rb', line 50

def resolved?(cp_note, cf_note)
  next_cp = voice.note_following(cp_note.position)
  return false unless next_cp

  melodic = HeadMusic::Analysis::MelodicInterval.new(cp_note, next_cp)
  return false unless melodic.step? && melodic.descending?

  resolution_interval = HeadMusic::Analysis::HarmonicInterval.new(cantus_firmus, voice, next_cp.position)
  resolution_interval.notes.length == 2 && resolution_interval.consonance?(:two_part_harmony)
end