Class: HeadMusic::Style::Guidelines::AllowFifthSpeciesRhythmicValues

Inherits:
HeadMusic::Style::Guideline show all
Defined in:
lib/head_music/style/guidelines/allow_fifth_species_rhythmic_values.rb

Overview

Validates that counterpoint notes use only permitted rhythmic values for fifth species. Whole notes (final bar only), half notes, quarter notes, and paired stepwise eighth notes on weak beats are allowed. No dotted rhythms.

Constant Summary collapse

PERMITTED_UNIT_NAMES =
%w[whole half quarter eighth].freeze

Instance Method Summary collapse

Constructor Details

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

Instance Method Details

#bars_with_excess_eighth_pairsObject (private)



80
81
82
83
84
85
# File 'lib/head_music/style/guidelines/allow_fifth_species_rhythmic_values.rb', line 80

def bars_with_excess_eighth_pairs
  eighth_notes
    .group_by { |note| note.position.bar_number }
    .select { |_bar, bar_eighths| bar_eighths.length > 2 }
    .keys
end

#disallowed_unit_marksObject (private)



23
24
25
# File 'lib/head_music/style/guidelines/allow_fifth_species_rhythmic_values.rb', line 23

def disallowed_unit_marks
  mark_each(notes.reject { |note| PERMITTED_UNIT_NAMES.include?(note.rhythmic_value.unit_name) })
end

#dotted_rhythm_marksObject (private)



27
28
29
# File 'lib/head_music/style/guidelines/allow_fifth_species_rhythmic_values.rb', line 27

def dotted_rhythm_marks
  mark_each(notes.select { |note| note.rhythmic_value.dots > 0 })
end

#eighth_notesObject (private)



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

def eighth_notes
  @eighth_notes ||= notes.select { |note| note.rhythmic_value.unit_name == "eighth" }
end

#eighth_notes_in_bar(bar_number) ⇒ Object (private)



87
88
89
# File 'lib/head_music/style/guidelines/allow_fifth_species_rhythmic_values.rb', line 87

def eighth_notes_in_bar(bar_number)
  eighth_notes.select { |note| note.position.bar_number == bar_number }
end

#excess_eighth_note_pair_marksObject (private)



51
52
53
54
55
# File 'lib/head_music/style/guidelines/allow_fifth_species_rhythmic_values.rb', line 51

def excess_eighth_note_pair_marks
  bars_with_excess_eighth_pairs.flat_map do |bar_number|
    mark_each(eighth_notes_in_bar(bar_number)[2..])
  end
end

#final_bar_numberObject (private)



91
92
93
# File 'lib/head_music/style/guidelines/allow_fifth_species_rhythmic_values.rb', line 91

def final_bar_number
  last_note&.position&.bar_number
end

#mark_each(violating_notes) ⇒ Object (private)



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

def mark_each(violating_notes)
  violating_notes.map { |note| HeadMusic::Style::Mark.for(note) }
end

#marksObject



10
11
12
13
14
15
16
17
18
19
# File 'lib/head_music/style/guidelines/allow_fifth_species_rhythmic_values.rb', line 10

def marks
  [
    *disallowed_unit_marks,
    *dotted_rhythm_marks,
    *whole_note_not_in_final_bar_marks,
    *unpaired_eighth_note_marks,
    *non_stepwise_eighth_note_marks,
    *excess_eighth_note_pair_marks
  ]
end

#non_stepwise_eighth_note_marksObject (private)



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

def non_stepwise_eighth_note_marks
  mark_each(
    eighth_notes
      .select { |note| paired_eighth?(note) }
      .reject { |note| stepwise_eighth?(note) }
  )
end

#paired_eighth?(note) ⇒ Boolean (private)

Returns:

  • (Boolean)


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

def paired_eighth?(note)
  prev = preceding_note(note)
  foll = following_note(note)
  (prev && prev.rhythmic_value.unit_name == "eighth") ||
    (foll && foll.rhythmic_value.unit_name == "eighth")
end

#stepwise_eighth?(note) ⇒ Boolean (private)

Returns:

  • (Boolean)


72
73
74
75
76
77
78
# File 'lib/head_music/style/guidelines/allow_fifth_species_rhythmic_values.rb', line 72

def stepwise_eighth?(note)
  prev = preceding_note(note)
  foll = following_note(note)
  stepwise_from_prev = prev && HeadMusic::Analysis::MelodicInterval.new(prev, note).step?
  stepwise_to_foll = foll && HeadMusic::Analysis::MelodicInterval.new(note, foll).step?
  stepwise_from_prev || stepwise_to_foll
end

#unpaired_eighth_note_marksObject (private)



39
40
41
# File 'lib/head_music/style/guidelines/allow_fifth_species_rhythmic_values.rb', line 39

def unpaired_eighth_note_marks
  mark_each(eighth_notes.reject { |note| paired_eighth?(note) })
end

#whole_note_not_in_final_bar_marksObject (private)



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

def whole_note_not_in_final_bar_marks
  mark_each(
    notes
      .select { |note| note.rhythmic_value.unit_name == "whole" }
      .reject { |note| note.position.bar_number == final_bar_number }
  )
end