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

MAX_BREAK_RATIO =
0.25

Instance Method Summary collapse

Constructor Details

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

Instance Method Details

#break_bar?(bar) ⇒ Boolean (private)

Returns:

  • (Boolean)


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

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)



47
48
49
# File 'lib/head_music/style/guidelines/second_species_break.rb', line 47

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)



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

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

#dissonance_marksObject (private)



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

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)



59
60
61
# File 'lib/head_music/style/guidelines/second_species_break.rb', line 59

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

#frequency_marksObject (private)



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

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)



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

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

#marksObject



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

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

  dissonance_marks + frequency_marks
end

#max_break_ratioObject (private)



26
27
28
# File 'lib/head_music/style/guidelines/second_species_break.rb', line 26

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

#notes_in_bar(bar) ⇒ Object (private)



51
52
53
# File 'lib/head_music/style/guidelines/second_species_break.rb', line 51

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

#total_barsObject (private)



55
56
57
# File 'lib/head_music/style/guidelines/second_species_break.rb', line 55

def total_bars
  last_bar - first_bar + 1
end