head_music 12.0.1 → 12.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7c54b609a69ea91a3d669a6e56f4e0000fb1f03de18554da8c4db7c01ce30655
4
- data.tar.gz: 6629c915d4ad4ee0dc66c75247c96687d9d5382e7577a471c7837b47e9c0ebfb
3
+ metadata.gz: c8f4f677c163723dfa35c168e4094f6dfb883b2817808e0f15fcef4753a2d003
4
+ data.tar.gz: a9f85adca12a56dbba4f28da4cb44790a793ec9e27de78c3c63900f9b461bac3
5
5
  SHA512:
6
- metadata.gz: 56f406bb2b9c3320f901929f1783b7a6c9ed6402b2904ae21cefafb9e711126d18720c2ac4c062d3ada15173d0c2889af8afc8c8602d6e74ba7cfb866c04e9f2
7
- data.tar.gz: ba7e142a8b553018ddc5a0752572a58e83b89da4ce080eecb353c72e34332ccdb03b3b7d288f81df8e4713f1584770c9777576434ed1f1f03ddce8a731c4d31f
6
+ metadata.gz: 2ac3464d2b14802c85b7779a11cbd95d8b849ea03b4992501eab1c40b2406e9411c1667de276ae1e95bb996b60f2d0a4e9d3f3b04973b539ba6df4c2462a302e
7
+ data.tar.gz: 3b6afff57cdcd7fcd343d5da25aad78fa7cbb0a05a74f86197e80b41dee8cf7585170af37ec71aa2da4dfa3ade9e3bcc708e6e6a4d3565507be9ea62d9e9ee79
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- head_music (12.0.1)
4
+ head_music (12.1.0)
5
5
  activesupport (>= 7.0, < 10)
6
6
  humanize (~> 2.0)
7
7
  i18n (~> 1.8)
@@ -0,0 +1,48 @@
1
+ # Module for style guidelines.
2
+ module HeadMusic::Style::Guidelines; end
3
+
4
+ # Base class for first-bar guidelines.
5
+ # Rules: (a) at least one note, (b) each note is the correct beat unit,
6
+ # (c) at most one rest, and only on the first beat.
7
+ class HeadMusic::Style::Guidelines::FirstBarEntry < HeadMusic::Style::Annotation
8
+ def marks
9
+ return unless notes.any?
10
+
11
+ bar_notes = notes_in_first_bar
12
+ bar_rests = rests_in_first_bar
13
+ return if valid_first_bar?(bar_notes, bar_rests)
14
+
15
+ HeadMusic::Style::Mark.for_all(bar_notes.any? ? bar_notes : [first_note])
16
+ end
17
+
18
+ private
19
+
20
+ def expected_rhythmic_value
21
+ raise NotImplementedError
22
+ end
23
+
24
+ def valid_first_bar?(bar_notes, bar_rests)
25
+ bar_notes.any? &&
26
+ all_correct_beat_unit?(bar_notes) &&
27
+ valid_rests?(bar_rests)
28
+ end
29
+
30
+ def all_correct_beat_unit?(bar_notes)
31
+ bar_notes.all? { |note| note.rhythmic_value == expected_rhythmic_value }
32
+ end
33
+
34
+ def valid_rests?(bar_rests)
35
+ bar_rests.empty? ||
36
+ (bar_rests.length == 1 &&
37
+ bar_rests.first.position.count == 1 &&
38
+ bar_rests.first.rhythmic_value == expected_rhythmic_value)
39
+ end
40
+
41
+ def notes_in_first_bar
42
+ notes.select { |note| note.position.bar_number == 1 }
43
+ end
44
+
45
+ def rests_in_first_bar
46
+ rests.select { |rest| rest.position.bar_number == 1 }
47
+ end
48
+ end
@@ -0,0 +1,13 @@
1
+ # Module for style guidelines.
2
+ module HeadMusic::Style::Guidelines; end
3
+
4
+ # Checks that the first bar contains half notes, with an optional half rest on beat one.
5
+ class HeadMusic::Style::Guidelines::FirstBarHalfNotes < HeadMusic::Style::Guidelines::FirstBarEntry
6
+ MESSAGE = "Begin the first bar with half notes, or enter after a half rest."
7
+
8
+ private
9
+
10
+ def expected_rhythmic_value
11
+ HeadMusic::Rudiment::RhythmicValue.get(:half)
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # Module for style guidelines.
2
+ module HeadMusic::Style::Guidelines; end
3
+
4
+ # Checks that the first bar contains quarter notes, with an optional quarter rest on beat one.
5
+ class HeadMusic::Style::Guidelines::FirstBarQuarterNotes < HeadMusic::Style::Guidelines::FirstBarEntry
6
+ MESSAGE = "Begin the first bar with quarter notes, or enter after a quarter rest."
7
+
8
+ private
9
+
10
+ def expected_rhythmic_value
11
+ HeadMusic::Rudiment::RhythmicValue.get(:quarter)
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # Module for style guidelines.
2
+ module HeadMusic::Style::Guidelines; end
3
+
4
+ # Checks that the first bar contains a whole note.
5
+ class HeadMusic::Style::Guidelines::FirstBarWholeNote < HeadMusic::Style::Guidelines::FirstBarEntry
6
+ MESSAGE = "Begin with a whole note in the first bar."
7
+
8
+ private
9
+
10
+ def expected_rhythmic_value
11
+ HeadMusic::Rudiment::RhythmicValue.get(:whole)
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ # Module for style guidelines.
2
+ module HeadMusic::Style::Guidelines; end
3
+
4
+ # Checks that each middle bar contains exactly four quarter notes.
5
+ class HeadMusic::Style::Guidelines::FourPerBar < HeadMusic::Style::Guidelines::NoteCountPerBar
6
+ MESSAGE = "Use four quarter notes in each middle bar."
7
+
8
+ QUARTER = HeadMusic::Rudiment::RhythmicValue.get(:quarter)
9
+
10
+ private
11
+
12
+ def check_middle_bar(bar_number)
13
+ bar_notes = notes_in_bar(bar_number)
14
+ return if bar_notes.length == 4 && bar_notes.all? { |note| note.rhythmic_value == QUARTER }
15
+
16
+ mark_bar(bar_number)
17
+ end
18
+ end
@@ -1,47 +1,27 @@
1
1
  # Module for style guidelines.
2
2
  module HeadMusic::Style::Guidelines; end
3
3
 
4
- # Base class for guidelines that check note counts per bar against the cantus firmus.
4
+ # Base class for guidelines that check note counts in middle bars (not first or last).
5
5
  class HeadMusic::Style::Guidelines::NoteCountPerBar < HeadMusic::Style::Annotation
6
- WHOLE = HeadMusic::Rudiment::RhythmicValue.get(:whole)
7
-
8
6
  def marks
9
7
  return [] unless cantus_firmus&.notes&.any?
10
8
 
11
- cf_notes = cantus_firmus.notes
12
- cf_notes.each_with_index.filter_map do |cf_note, index|
13
- bar_number = cf_note.position.bar_number
14
- if index == cf_notes.length - 1
15
- check_final_bar(bar_number)
16
- elsif index == 0
17
- check_first_bar(bar_number)
18
- else
19
- check_middle_bar(bar_number)
20
- end
21
- end
9
+ middle_bars.filter_map { |bar_number| check_middle_bar(bar_number) }
22
10
  end
23
11
 
24
12
  private
25
13
 
26
- def check_final_bar(bar_number)
27
- bar_notes = notes_in_bar(bar_number)
28
- return if one_whole_note?(bar_notes)
29
-
30
- mark_bar(bar_number)
31
- end
14
+ def middle_bars
15
+ cf_notes = cantus_firmus.notes
16
+ return [] if cf_notes.length <= 2
32
17
 
33
- def one_whole_note?(bar_notes)
34
- bar_notes.length == 1 && bar_notes.first.rhythmic_value == WHOLE
18
+ cf_notes[1..-2].map { |note| note.position.bar_number }
35
19
  end
36
20
 
37
21
  def notes_in_bar(bar_number)
38
22
  notes.select { |note| note.position.bar_number == bar_number }
39
23
  end
40
24
 
41
- def rests_in_bar(bar_number)
42
- rests.select { |rest| rest.position.bar_number == bar_number }
43
- end
44
-
45
25
  def mark_bar(bar_number)
46
26
  bar_placements = notes_in_bar(bar_number)
47
27
  if bar_placements.any?
@@ -0,0 +1,35 @@
1
+ # Module for style guidelines.
2
+ module HeadMusic::Style::Guidelines; end
3
+
4
+ # Checks that the final bar contains a single note that fills the entire measure.
5
+ class HeadMusic::Style::Guidelines::NoteFillsFinalBar < HeadMusic::Style::Annotation
6
+ MESSAGE = "End with a single note that fills the final bar."
7
+
8
+ def marks
9
+ return unless notes.any?
10
+
11
+ bar_notes = notes_in_final_bar
12
+ return if bar_notes.length == 1 && fills_bar?(bar_notes.first)
13
+
14
+ HeadMusic::Style::Mark.for_all(bar_notes.any? ? bar_notes : [last_note])
15
+ end
16
+
17
+ private
18
+
19
+ def notes_in_final_bar
20
+ notes.select { |note| note.position.bar_number == final_bar_number }
21
+ end
22
+
23
+ def final_bar_number
24
+ last_note.position.bar_number
25
+ end
26
+
27
+ def fills_bar?(note)
28
+ note.rhythmic_value.total_value == bar_duration
29
+ end
30
+
31
+ def bar_duration
32
+ meter = composition.meter_at(final_bar_number)
33
+ meter.count_unit.relative_value * meter.counts_per_bar
34
+ end
35
+ end
@@ -0,0 +1,18 @@
1
+ # Module for style guidelines.
2
+ module HeadMusic::Style::Guidelines; end
3
+
4
+ # Checks that each middle bar contains exactly one whole note.
5
+ class HeadMusic::Style::Guidelines::OnePerBar < HeadMusic::Style::Guidelines::NoteCountPerBar
6
+ MESSAGE = "Use one whole note in each middle bar."
7
+
8
+ WHOLE = HeadMusic::Rudiment::RhythmicValue.get(:whole)
9
+
10
+ private
11
+
12
+ def check_middle_bar(bar_number)
13
+ bar_notes = notes_in_bar(bar_number)
14
+ return if bar_notes.length == 1 && bar_notes.first.rhythmic_value == WHOLE
15
+
16
+ mark_bar(bar_number)
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # Module for style guidelines.
2
+ module HeadMusic::Style::Guidelines; end
3
+
4
+ # Checks that each middle bar contains exactly three quarter notes.
5
+ class HeadMusic::Style::Guidelines::ThreePerBar < HeadMusic::Style::Guidelines::NoteCountPerBar
6
+ MESSAGE = "Use three quarter notes in each middle bar."
7
+
8
+ QUARTER = HeadMusic::Rudiment::RhythmicValue.get(:quarter)
9
+
10
+ private
11
+
12
+ def check_middle_bar(bar_number)
13
+ bar_notes = notes_in_bar(bar_number)
14
+ return if bar_notes.length == 3 && bar_notes.all? { |note| note.rhythmic_value == QUARTER }
15
+
16
+ mark_bar(bar_number)
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # Module for style guidelines.
2
+ module HeadMusic::Style::Guidelines; end
3
+
4
+ # Checks that each middle bar contains exactly two half notes.
5
+ class HeadMusic::Style::Guidelines::TwoPerBar < HeadMusic::Style::Guidelines::NoteCountPerBar
6
+ MESSAGE = "Use two half notes in each middle bar."
7
+
8
+ HALF = HeadMusic::Rudiment::RhythmicValue.get(:half)
9
+
10
+ private
11
+
12
+ def check_middle_bar(bar_number)
13
+ bar_notes = notes_in_bar(bar_number)
14
+ return if bar_notes.length == 2 && bar_notes.all? { |note| note.rhythmic_value == HALF }
15
+
16
+ mark_bar(bar_number)
17
+ end
18
+ end
@@ -7,11 +7,12 @@ class HeadMusic::Style::Guides::FirstSpeciesMelody < HeadMusic::Style::Guides::S
7
7
  HeadMusic::Style::Guidelines::ConsonantClimax,
8
8
  HeadMusic::Style::Guidelines::Diatonic,
9
9
  HeadMusic::Style::Guidelines::EndOnTonic,
10
+ HeadMusic::Style::Guidelines::NoteFillsFinalBar,
11
+ HeadMusic::Style::Guidelines::FirstBarWholeNote,
10
12
  HeadMusic::Style::Guidelines::FrequentDirectionChanges,
11
13
  HeadMusic::Style::Guidelines::LimitOctaveLeaps,
12
14
  HeadMusic::Style::Guidelines::MostlyConjunct,
13
- HeadMusic::Style::Guidelines::NoRests,
14
- HeadMusic::Style::Guidelines::NotesSameLength,
15
+ HeadMusic::Style::Guidelines::OnePerBar,
15
16
  HeadMusic::Style::Guidelines::PrepareOctaveLeaps,
16
17
  HeadMusic::Style::Guidelines::SingableIntervals,
17
18
  HeadMusic::Style::Guidelines::SingableRange,
@@ -8,6 +8,8 @@ class HeadMusic::Style::Guides::SecondSpeciesMelody < HeadMusic::Style::Guides::
8
8
  HeadMusic::Style::Guidelines::ConsonantClimax,
9
9
  HeadMusic::Style::Guidelines::Diatonic,
10
10
  HeadMusic::Style::Guidelines::EndOnTonic,
11
+ HeadMusic::Style::Guidelines::NoteFillsFinalBar,
12
+ HeadMusic::Style::Guidelines::FirstBarHalfNotes,
11
13
  HeadMusic::Style::Guidelines::FrequentDirectionChanges,
12
14
  HeadMusic::Style::Guidelines::LimitOctaveLeaps,
13
15
  HeadMusic::Style::Guidelines::MostlyConjunct,
@@ -17,6 +19,6 @@ class HeadMusic::Style::Guides::SecondSpeciesMelody < HeadMusic::Style::Guides::
17
19
  HeadMusic::Style::Guidelines::StartOnPerfectConsonance,
18
20
  HeadMusic::Style::Guidelines::StepOutOfUnison,
19
21
  HeadMusic::Style::Guidelines::StepUpToFinalNote,
20
- HeadMusic::Style::Guidelines::TwoToOne
22
+ HeadMusic::Style::Guidelines::TwoPerBar
21
23
  ].freeze
22
24
  end
@@ -8,7 +8,9 @@ class HeadMusic::Style::Guides::ThirdSpeciesMelody < HeadMusic::Style::Guides::S
8
8
  HeadMusic::Style::Guidelines::ConsonantClimax,
9
9
  HeadMusic::Style::Guidelines::Diatonic,
10
10
  HeadMusic::Style::Guidelines::EndOnTonic,
11
- HeadMusic::Style::Guidelines::FourToOne,
11
+ HeadMusic::Style::Guidelines::NoteFillsFinalBar,
12
+ HeadMusic::Style::Guidelines::FirstBarQuarterNotes,
13
+ HeadMusic::Style::Guidelines::FourPerBar,
12
14
  HeadMusic::Style::Guidelines::FrequentDirectionChanges,
13
15
  HeadMusic::Style::Guidelines::LimitOctaveLeaps,
14
16
  HeadMusic::Style::Guidelines::MostlyConjunct,
@@ -8,6 +8,8 @@ class HeadMusic::Style::Guides::ThirdSpeciesTripleMeterMelody < HeadMusic::Style
8
8
  HeadMusic::Style::Guidelines::ConsonantClimax,
9
9
  HeadMusic::Style::Guidelines::Diatonic,
10
10
  HeadMusic::Style::Guidelines::EndOnTonic,
11
+ HeadMusic::Style::Guidelines::NoteFillsFinalBar,
12
+ HeadMusic::Style::Guidelines::FirstBarQuarterNotes,
11
13
  HeadMusic::Style::Guidelines::FrequentDirectionChanges,
12
14
  HeadMusic::Style::Guidelines::LimitOctaveLeaps,
13
15
  HeadMusic::Style::Guidelines::MostlyConjunct,
@@ -17,6 +19,6 @@ class HeadMusic::Style::Guides::ThirdSpeciesTripleMeterMelody < HeadMusic::Style
17
19
  HeadMusic::Style::Guidelines::StartOnPerfectConsonance,
18
20
  HeadMusic::Style::Guidelines::StepOutOfUnison,
19
21
  HeadMusic::Style::Guidelines::StepUpToFinalNote,
20
- HeadMusic::Style::Guidelines::ThreeToOne
22
+ HeadMusic::Style::Guidelines::ThreePerBar
21
23
  ].freeze
22
24
  end
@@ -1,3 +1,3 @@
1
1
  module HeadMusic
2
- VERSION = "12.0.1"
2
+ VERSION = "12.1.0"
3
3
  end
data/lib/head_music.rb CHANGED
@@ -161,8 +161,14 @@ require "head_music/style/guidelines/diatonic"
161
161
  require "head_music/style/guidelines/direction_changes"
162
162
  require "head_music/style/guidelines/end_on_perfect_consonance"
163
163
  require "head_music/style/guidelines/end_on_tonic"
164
+ require "head_music/style/guidelines/note_fills_final_bar"
165
+ require "head_music/style/guidelines/first_bar_entry"
166
+ require "head_music/style/guidelines/first_bar_half_notes"
167
+ require "head_music/style/guidelines/first_bar_quarter_notes"
168
+ require "head_music/style/guidelines/first_bar_whole_note"
164
169
  require "head_music/style/guidelines/note_count_per_bar"
165
- require "head_music/style/guidelines/four_to_one"
170
+ require "head_music/style/guidelines/one_per_bar"
171
+ require "head_music/style/guidelines/four_per_bar"
166
172
  require "head_music/style/guidelines/frequent_direction_changes"
167
173
  require "head_music/style/guidelines/limit_octave_leaps"
168
174
  require "head_music/style/guidelines/moderate_direction_changes"
@@ -188,8 +194,8 @@ require "head_music/style/guidelines/step_down_to_final_note"
188
194
  require "head_music/style/guidelines/step_out_of_unison"
189
195
  require "head_music/style/guidelines/step_to_final_note"
190
196
  require "head_music/style/guidelines/step_up_to_final_note"
191
- require "head_music/style/guidelines/three_to_one"
192
- require "head_music/style/guidelines/two_to_one"
197
+ require "head_music/style/guidelines/three_per_bar"
198
+ require "head_music/style/guidelines/two_per_bar"
193
199
  require "head_music/style/guidelines/up_to_fourteen_notes"
194
200
  require "head_music/style/guidelines/weak_beat_dissonance_treatment"
195
201
  require "head_music/style/guidelines/third_species_dissonance_treatment"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: head_music
3
3
  version: !ruby/object:Gem::Version
4
- version: 12.0.1
4
+ version: 12.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rob Head
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-02-23 00:00:00.000000000 Z
11
+ date: 2026-02-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -280,7 +280,11 @@ files:
280
280
  - lib/head_music/style/guidelines/directional_step_to_final_note.rb
281
281
  - lib/head_music/style/guidelines/end_on_perfect_consonance.rb
282
282
  - lib/head_music/style/guidelines/end_on_tonic.rb
283
- - lib/head_music/style/guidelines/four_to_one.rb
283
+ - lib/head_music/style/guidelines/first_bar_entry.rb
284
+ - lib/head_music/style/guidelines/first_bar_half_notes.rb
285
+ - lib/head_music/style/guidelines/first_bar_quarter_notes.rb
286
+ - lib/head_music/style/guidelines/first_bar_whole_note.rb
287
+ - lib/head_music/style/guidelines/four_per_bar.rb
284
288
  - lib/head_music/style/guidelines/frequent_direction_changes.rb
285
289
  - lib/head_music/style/guidelines/limit_octave_leaps.rb
286
290
  - lib/head_music/style/guidelines/moderate_direction_changes.rb
@@ -291,7 +295,9 @@ files:
291
295
  - lib/head_music/style/guidelines/no_strong_beat_unisons.rb
292
296
  - lib/head_music/style/guidelines/no_unisons_in_middle.rb
293
297
  - lib/head_music/style/guidelines/note_count_per_bar.rb
298
+ - lib/head_music/style/guidelines/note_fills_final_bar.rb
294
299
  - lib/head_music/style/guidelines/notes_same_length.rb
300
+ - lib/head_music/style/guidelines/one_per_bar.rb
295
301
  - lib/head_music/style/guidelines/one_to_one.rb
296
302
  - lib/head_music/style/guidelines/prefer_contrary_motion.rb
297
303
  - lib/head_music/style/guidelines/prefer_imperfect.rb
@@ -307,9 +313,9 @@ files:
307
313
  - lib/head_music/style/guidelines/step_to_final_note.rb
308
314
  - lib/head_music/style/guidelines/step_up_to_final_note.rb
309
315
  - lib/head_music/style/guidelines/third_species_dissonance_treatment.rb
310
- - lib/head_music/style/guidelines/three_to_one.rb
316
+ - lib/head_music/style/guidelines/three_per_bar.rb
311
317
  - lib/head_music/style/guidelines/triple_meter_dissonance_treatment.rb
312
- - lib/head_music/style/guidelines/two_to_one.rb
318
+ - lib/head_music/style/guidelines/two_per_bar.rb
313
319
  - lib/head_music/style/guidelines/up_to_fourteen_notes.rb
314
320
  - lib/head_music/style/guidelines/weak_beat_dissonance_treatment.rb
315
321
  - lib/head_music/style/guides/first_species_harmony.rb
@@ -1,45 +0,0 @@
1
- # Module for style guidelines.
2
- module HeadMusic::Style::Guidelines; end
3
-
4
- # A counterpoint guideline
5
- class HeadMusic::Style::Guidelines::FourToOne < HeadMusic::Style::Guidelines::NoteCountPerBar
6
- MESSAGE = "Use four quarter notes against each whole note in the cantus firmus."
7
-
8
- QUARTER = HeadMusic::Rudiment::RhythmicValue.get(:quarter)
9
-
10
- private
11
-
12
- def check_first_bar(bar_number)
13
- bar_notes = notes_in_bar(bar_number)
14
- bar_rests = rests_in_bar(bar_number)
15
- return if four_quarter_notes?(bar_notes)
16
- return if rest_then_three_quarter_notes?(bar_notes, bar_rests)
17
- return if three_quarter_notes_after_downbeat?(bar_notes)
18
-
19
- mark_bar(bar_number)
20
- end
21
-
22
- def check_middle_bar(bar_number)
23
- bar_notes = notes_in_bar(bar_number)
24
- return if four_quarter_notes?(bar_notes)
25
-
26
- mark_bar(bar_number)
27
- end
28
-
29
- def four_quarter_notes?(bar_notes)
30
- bar_notes.length == 4 && bar_notes.all? { |note| note.rhythmic_value == QUARTER }
31
- end
32
-
33
- def rest_then_three_quarter_notes?(bar_notes, bar_rests)
34
- bar_notes.length == 3 &&
35
- bar_notes.all? { |note| note.rhythmic_value == QUARTER } &&
36
- bar_rests.length == 1 &&
37
- bar_rests.first.rhythmic_value == QUARTER
38
- end
39
-
40
- def three_quarter_notes_after_downbeat?(bar_notes)
41
- bar_notes.length == 3 &&
42
- bar_notes.all? { |note| note.rhythmic_value == QUARTER } &&
43
- bar_notes.first.position.count > 1
44
- end
45
- end
@@ -1,57 +0,0 @@
1
- # Module for style guidelines.
2
- module HeadMusic::Style::Guidelines; end
3
-
4
- # A counterpoint guideline
5
- class HeadMusic::Style::Guidelines::ThreeToOne < HeadMusic::Style::Guidelines::NoteCountPerBar
6
- MESSAGE = "Use three quarter notes against each dotted half note in the cantus firmus."
7
-
8
- QUARTER = HeadMusic::Rudiment::RhythmicValue.get(:quarter)
9
- DOTTED_HALF = HeadMusic::Rudiment::RhythmicValue.get(:dotted_half)
10
-
11
- private
12
-
13
- def check_first_bar(bar_number)
14
- bar_notes = notes_in_bar(bar_number)
15
- bar_rests = rests_in_bar(bar_number)
16
- return if three_quarter_notes?(bar_notes)
17
- return if rest_then_two_quarter_notes?(bar_notes, bar_rests)
18
- return if two_quarter_notes_after_downbeat?(bar_notes)
19
-
20
- mark_bar(bar_number)
21
- end
22
-
23
- def check_middle_bar(bar_number)
24
- bar_notes = notes_in_bar(bar_number)
25
- return if three_quarter_notes?(bar_notes)
26
-
27
- mark_bar(bar_number)
28
- end
29
-
30
- def check_final_bar(bar_number)
31
- bar_notes = notes_in_bar(bar_number)
32
- return if one_dotted_half_note?(bar_notes)
33
-
34
- mark_bar(bar_number)
35
- end
36
-
37
- def three_quarter_notes?(bar_notes)
38
- bar_notes.length == 3 && bar_notes.all? { |note| note.rhythmic_value == QUARTER }
39
- end
40
-
41
- def rest_then_two_quarter_notes?(bar_notes, bar_rests)
42
- bar_notes.length == 2 &&
43
- bar_notes.all? { |note| note.rhythmic_value == QUARTER } &&
44
- bar_rests.length == 1 &&
45
- bar_rests.first.rhythmic_value == QUARTER
46
- end
47
-
48
- def two_quarter_notes_after_downbeat?(bar_notes)
49
- bar_notes.length == 2 &&
50
- bar_notes.all? { |note| note.rhythmic_value == QUARTER } &&
51
- bar_notes.first.position.count > 1
52
- end
53
-
54
- def one_dotted_half_note?(bar_notes)
55
- bar_notes.length == 1 && bar_notes.first.rhythmic_value == DOTTED_HALF
56
- end
57
- end
@@ -1,46 +0,0 @@
1
- # Module for style guidelines.
2
- module HeadMusic::Style::Guidelines; end
3
-
4
- # A counterpoint guideline
5
- class HeadMusic::Style::Guidelines::TwoToOne < HeadMusic::Style::Guidelines::NoteCountPerBar
6
- MESSAGE = "Use two half notes against each whole note in the cantus firmus."
7
-
8
- HALF = HeadMusic::Rudiment::RhythmicValue.get(:half)
9
-
10
- private
11
-
12
- def check_first_bar(bar_number)
13
- bar_notes = notes_in_bar(bar_number)
14
- bar_rests = rests_in_bar(bar_number)
15
- return if two_half_notes?(bar_notes)
16
- return if rest_then_half_note?(bar_notes, bar_rests)
17
- return if single_half_note_after_downbeat?(bar_notes)
18
-
19
- mark_bar(bar_number)
20
- end
21
-
22
- def check_middle_bar(bar_number)
23
- bar_notes = notes_in_bar(bar_number)
24
- return if two_half_notes?(bar_notes)
25
-
26
- mark_bar(bar_number)
27
- end
28
-
29
- def two_half_notes?(bar_notes)
30
- bar_notes.length == 2 && bar_notes.all? { |note| note.rhythmic_value == HALF }
31
- end
32
-
33
- def rest_then_half_note?(bar_notes, bar_rests)
34
- bar_notes.length == 1 &&
35
- bar_notes.first.rhythmic_value == HALF &&
36
- bar_rests.length == 1 &&
37
- bar_rests.first.rhythmic_value == HALF
38
- end
39
-
40
- def single_half_note_after_downbeat?(bar_notes)
41
- first_note = bar_notes.first
42
- bar_notes.length == 1 &&
43
- first_note.rhythmic_value == HALF &&
44
- first_note.position.count > 1
45
- end
46
- end