Class: HeadMusic::Style::Guidelines::AllowedRhythmicValuesForFifthSpecies

Inherits:
Annotation
  • Object
show all
Defined in:
lib/head_music/style/guidelines/allowed_rhythmic_values_for_fifth_species.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

MESSAGE =
"Use only permitted rhythmic values: whole (final bar only), half, quarter, " \
"or paired stepwise eighth notes on weak beats."
PERMITTED_UNIT_NAMES =
%w[whole half quarter eighth].freeze

Instance Method Summary collapse

Constructor Details

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

Instance Method Details

#bars_with_excess_eighth_pairsObject (private)



83
84
85
86
87
88
# File 'lib/head_music/style/guidelines/allowed_rhythmic_values_for_fifth_species.rb', line 83

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)



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

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

#dotted_rhythm_marksObject (private)



30
31
32
# File 'lib/head_music/style/guidelines/allowed_rhythmic_values_for_fifth_species.rb', line 30

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

#eighth_notesObject (private)



64
65
66
# File 'lib/head_music/style/guidelines/allowed_rhythmic_values_for_fifth_species.rb', line 64

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

#eighth_notes_in_bar(bar_number) ⇒ Object (private)



90
91
92
# File 'lib/head_music/style/guidelines/allowed_rhythmic_values_for_fifth_species.rb', line 90

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

#excess_eighth_note_pair_marksObject (private)



54
55
56
57
58
# File 'lib/head_music/style/guidelines/allowed_rhythmic_values_for_fifth_species.rb', line 54

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)



94
95
96
# File 'lib/head_music/style/guidelines/allowed_rhythmic_values_for_fifth_species.rb', line 94

def final_bar_number
  last_note&.position&.bar_number
end

#following_note(note) ⇒ Object (private)



103
104
105
106
# File 'lib/head_music/style/guidelines/allowed_rhythmic_values_for_fifth_species.rb', line 103

def following_note(note)
  index = notes.index(note)
  notes[index + 1] if index && index < notes.length - 1
end

#mark_each(violating_notes) ⇒ Object (private)



60
61
62
# File 'lib/head_music/style/guidelines/allowed_rhythmic_values_for_fifth_species.rb', line 60

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

#marksObject



13
14
15
16
17
18
19
20
21
22
# File 'lib/head_music/style/guidelines/allowed_rhythmic_values_for_fifth_species.rb', line 13

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)



46
47
48
49
50
51
52
# File 'lib/head_music/style/guidelines/allowed_rhythmic_values_for_fifth_species.rb', line 46

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)


68
69
70
71
72
73
# File 'lib/head_music/style/guidelines/allowed_rhythmic_values_for_fifth_species.rb', line 68

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

#preceding_note(note) ⇒ Object (private)



98
99
100
101
# File 'lib/head_music/style/guidelines/allowed_rhythmic_values_for_fifth_species.rb', line 98

def preceding_note(note)
  index = notes.index(note)
  notes[index - 1] if index && index > 0
end

#stepwise_eighth?(note) ⇒ Boolean (private)

Returns:

  • (Boolean)


75
76
77
78
79
80
81
# File 'lib/head_music/style/guidelines/allowed_rhythmic_values_for_fifth_species.rb', line 75

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)



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

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

#whole_note_not_in_final_bar_marksObject (private)



34
35
36
37
38
39
40
# File 'lib/head_music/style/guidelines/allowed_rhythmic_values_for_fifth_species.rb', line 34

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