Class: HeadMusic::Style::Guidelines::SecondSpeciesBreak

Inherits:
WeakBeatDissonanceTreatment show all
Defined in:
lib/head_music/style/guidelines/second_species_break.rb

Overview

A counterpoint guideline for fourth species counterpoint. The syncopated texture is occasionally broken: the counterpoint moves on the downbeat instead of sustaining. When this happens, a dissonant off-beat note is permitted only if it is a passing tone. Breaks should be infrequent.

Constant Summary collapse

MESSAGE =
"Use only passing tones when breaking the syncopated texture. Breaks should be infrequent."
MAX_BREAK_RATIO =
0.25

Instance Method Summary collapse

Constructor Details

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

Instance Method Details

#break_bar?(bar) ⇒ Boolean (private)

Returns:

  • (Boolean)


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

def break_bar?(bar)
  downbeats, off_beats = notes_in_bar(bar).partition { |note| downbeat_position?(note.position) }
  downbeats.any? && off_beats.any?
end

#break_bar_off_beat_notesObject (private)



49
50
51
# File 'lib/head_music/style/guidelines/second_species_break.rb', line 49

def break_bar_off_beat_notes
  break_bars.flat_map { |bar| notes_in_bar(bar).reject { |note| downbeat_position?(note.position) } }
end

#break_barsObject (private)



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

def break_bars
  @break_bars ||= (first_bar..last_bar).select { |bar| break_bar?(bar) }
end

#dissonance_marksObject (private)



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

def dissonance_marks
  break_bar_off_beat_notes
    .select { |note| dissonant_with_cantus?(note) }
    .reject { |note| passing_tone?(note) }
    .map { |note| HeadMusic::Style::Mark.for(note) }
end

#first_barObject (private)



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

def first_bar
  cantus_firmus.notes.first.position.bar_number
end

#frequency_marksObject (private)



32
33
34
35
36
37
38
# File 'lib/head_music/style/guidelines/second_species_break.rb', line 32

def frequency_marks
  return [] if total_bars <= 0
  return [] if break_bars.length <= total_bars * max_break_ratio

  break_bar_notes = break_bars.flat_map { |bar| notes_in_bar(bar) }
  [HeadMusic::Style::Mark.for_all(break_bar_notes, fitness: HeadMusic::SMALL_PENALTY_FACTOR)]
end

#last_barObject (private)



65
66
67
# File 'lib/head_music/style/guidelines/second_species_break.rb', line 65

def last_bar
  cantus_firmus.notes.last.position.bar_number
end

#marksObject



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

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

  dissonance_marks + frequency_marks
end

#max_break_ratioObject (private)



28
29
30
# File 'lib/head_music/style/guidelines/second_species_break.rb', line 28

def max_break_ratio
  options.fetch(:max_break_ratio) { self.class::MAX_BREAK_RATIO }
end

#notes_in_bar(bar) ⇒ Object (private)



53
54
55
# File 'lib/head_music/style/guidelines/second_species_break.rb', line 53

def notes_in_bar(bar)
  notes.select { |note| note.position.bar_number == bar }
end

#total_barsObject (private)



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

def total_bars
  last_bar - first_bar + 1
end