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

Inherits:
HeadMusic::Style::Guideline 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.

Direct Known Subclasses

EmbellishedSuspensionTreatment

Instance Method Summary collapse

Constructor Details

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

Instance Method Details

#consonant_at?(position) ⇒ Boolean (private)

Returns:

  • (Boolean)


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

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)


57
58
59
60
# File 'lib/head_music/style/guidelines/suspension_treatment.rb', line 57

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

#dissonant_suspensions ⇒ Object (private)



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

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)



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

def harmonic_interval_at(position)
  HeadMusic::Analysis::HarmonicInterval.new(cantus_firmus, voice, position)
end

#improperly_treated_suspensions ⇒ Object (private)



18
19
20
# File 'lib/head_music/style/guidelines/suspension_treatment.rb', line 18

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

#marks ⇒ Object



10
11
12
13
14
# File 'lib/head_music/style/guidelines/suspension_treatment.rb', line 10

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

  HeadMusic::Style::Mark.for_each(improperly_treated_suspensions)
end

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

Returns:

  • (Boolean)


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

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)



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

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)


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

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)


47
48
49
50
51
52
53
54
55
# File 'lib/head_music/style/guidelines/suspension_treatment.rb', line 47

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).



31
32
33
34
# File 'lib/head_music/style/guidelines/suspension_treatment.rb', line 31

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)


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

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