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

#consonant_at?(position) ⇒ Boolean (private)

Returns:

  • (Boolean)


66
67
68
69
# File 'lib/head_music/style/guidelines/suspension_treatment.rb', line 66

def consonant_at?(position)
  interval = harmonic_interval_at(position)
  two_part?(interval) && interval.consonance?(:two_part_harmony)
end

#dissonant_at?(position) ⇒ Boolean (private)

Returns:

  • (Boolean)


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

def dissonant_at?(position)
  interval = harmonic_interval_at(position)
  two_part?(interval) && interval.dissonance?(:two_part_harmony)
end

#dissonant_suspensionsObject (private)



26
27
28
29
30
31
# 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 = suspended_cp_note(cf_note)
    [cp_note, cf_note] if cp_note && dissonant_at?(cf_note.position)
  end
end

#harmonic_interval_at(position) ⇒ Object (private)



71
72
73
# File 'lib/head_music/style/guidelines/suspension_treatment.rb', line 71

def harmonic_interval_at(position)
  HeadMusic::Analysis::HarmonicInterval.new(cantus_firmus, voice, position)
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)


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

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

  consonant_at?(cp_note.position)
end

#previous_cf_note(cf_note) ⇒ Object (private)



79
80
81
82
# File 'lib/head_music/style/guidelines/suspension_treatment.rb', line 79

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)


40
41
42
# File 'lib/head_music/style/guidelines/suspension_treatment.rb', line 40

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)


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

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?

  consonant_at?(next_cp.position)
end

#suspended_cp_note(cf_note) ⇒ Object (private)

The counterpoint note sounding under the cantus firmus note, but only when it began earlier (a note held over, i.e. a suspension).



35
36
37
38
# File 'lib/head_music/style/guidelines/suspension_treatment.rb', line 35

def suspended_cp_note(cf_note)
  cp_note = voice.note_at(cf_note.position)
  cp_note if cp_note && cp_note.position < cf_note.position
end

#two_part?(interval) ⇒ Boolean (private)

Returns:

  • (Boolean)


75
76
77
# File 'lib/head_music/style/guidelines/suspension_treatment.rb', line 75

def two_part?(interval)
  interval.notes.length == 2
end