head_music 11.5.0 → 11.6.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: cac99dc343591a77ac4160fc00bf209bb5ef9182372248f8d8f5f15728cb419f
4
- data.tar.gz: 5c97aba2f05dff3350864bf7c34b41dcb08e0fb9e87f487fd90647cc2019522a
3
+ metadata.gz: 8f3b2f457b2bc3baf47891681fdc15638176f5d63275a5d10dfb72ebdeb09d4a
4
+ data.tar.gz: 2bc6e91ec28518b7160d9f9ae3e35cf4cbfe3d160e645ad2f3fee108328ec93e
5
5
  SHA512:
6
- metadata.gz: fff6cedb936923db750c8725d2f8d2a486dbf5be0f4dced45c345fc1d0fe585b8f39b46f983d56e8561427e8fab52298168b583beae5a5b94aa9d27645165624
7
- data.tar.gz: cd06cb5715a6767849a9f6f327647ec3fb2ded81d450a280b2c58e06b6e55c2a2505b31353f7c0b5f2aef6340ffeeea4f819528d62be37ccff3a21196b90bd10
6
+ metadata.gz: 6d6d3352bf212394174b700c4ba45dfe5ff6f980508b2b50fc9e02e8d163ba747a281d73702c6bb9c636b7784682784e3bfc3725f103a14f5f55d41d5f3b75b7
7
+ data.tar.gz: d086a69554fea7d19830a82ebafe3c1482281a9c317aacb0407ff7aae24152b665685d6d1a8f38ad67c9375fbee0d71e4a0b389ff47dc52caead30e2795835df
data/CHANGELOG.md CHANGED
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [11.6.0] - 2026-02-10
11
+
12
+ ### Added
13
+ - Second-species counterpoint style guides: `SecondSpeciesMelody` and `SecondSpeciesHarmony`
14
+ - New guidelines for second-species counterpoint:
15
+ - `TwoToOne` — enforces two half notes per cantus firmus whole note (with optional half-rest opening)
16
+ - `WeakBeatDissonanceTreatment` — dissonant weak beats must be passing tones
17
+ - `NoParallelPerfectOnDownbeats` — forbids parallel perfect consonances on consecutive downbeats
18
+ - `NoParallelPerfectAcrossBarline` — forbids parallel perfect consonances from weak beat to following downbeat
19
+ - `NoStrongBeatUnisons` — forbids unisons on interior downbeats
20
+ - Pedagogical reference document for second-species counterpoint (`references/second-species-counterpoint.md`)
21
+
10
22
  ## [11.0.0] - 2026-01-05
11
23
 
12
24
  ### Changed
data/CLAUDE.md CHANGED
@@ -166,6 +166,13 @@ This project deliberately deprioritizes formal documentation in favor of clear,
166
166
  2. Run tests for the specific module: `bundle exec rspec spec/head_music/[module_name]`
167
167
  3. Ensure translations are updated if names change
168
168
 
169
+ ## Reference Documents
170
+
171
+ Domain reference materials live in `references/`.
172
+ Consult these when implementing or modifying style guidelines:
173
+
174
+ - `references/second-species-counterpoint.md` — Pedagogical survey of second-species counterpoint rules from Fux through contemporary sources, with cross-source comparison and mapping to head_music's style guideline architecture.
175
+
169
176
  ## Music theory and concepts
170
177
 
171
178
  Please refer to MUSIC_THEORY.md for music theory domain knowledge.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- head_music (11.5.0)
4
+ head_music (11.6.0)
5
5
  activesupport (>= 7.0, < 10)
6
6
  humanize (~> 2.0)
7
7
  i18n (~> 1.8)
@@ -45,8 +45,7 @@ class HeadMusic::Analysis::DiatonicInterval::Naming
45
45
  end
46
46
 
47
47
  def shorthand
48
- step_shorthand = (number == 1) ? "U" : number
49
- [quality.shorthand, step_shorthand].join
48
+ [quality.shorthand, number].join
50
49
  end
51
50
 
52
51
  private
@@ -32,10 +32,10 @@ module HeadMusic
32
32
  # e.g., "Fux" -> "fux", "Clendinning & Marvin" -> "clendinning_and_marvin"
33
33
  def normalize_key(identifier)
34
34
  identifier.to_s
35
- .downcase
36
- .gsub(/\s*&\s*/, "_and_")
37
- .gsub(/\s+/, "_")
38
- .to_sym
35
+ .downcase
36
+ .gsub(/\s*&\s*/, "_and_")
37
+ .gsub(/\s+/, "_")
38
+ .to_sym
39
39
  end
40
40
  end
41
41
 
@@ -0,0 +1,61 @@
1
+ # Module for style guidelines.
2
+ module HeadMusic::Style::Guidelines; end
3
+
4
+ # A counterpoint guideline
5
+ class HeadMusic::Style::Guidelines::NoParallelPerfectAcrossBarline < HeadMusic::Style::Annotation
6
+ MESSAGE = "Avoid parallel perfect consonances from weak beat to the following downbeat."
7
+
8
+ def marks
9
+ parallel_perfect_across_barline_pairs.map do |pair|
10
+ HeadMusic::Style::Mark.for_all(pair.flat_map(&:notes))
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def parallel_perfect_across_barline_pairs
17
+ weak_strong_interval_pairs.select do |weak, strong|
18
+ weak.perfect_consonance?(:two_part_harmony) &&
19
+ strong.perfect_consonance?(:two_part_harmony) &&
20
+ same_simple_type?(weak, strong)
21
+ end
22
+ end
23
+
24
+ def weak_strong_interval_pairs
25
+ weak_beat_harmonic_intervals.filter_map do |weak_interval|
26
+ next_downbeat = next_downbeat_interval(weak_interval)
27
+ [weak_interval, next_downbeat] if next_downbeat
28
+ end
29
+ end
30
+
31
+ def weak_beat_harmonic_intervals
32
+ weak_beat_positions.filter_map do |position|
33
+ interval = HeadMusic::Analysis::HarmonicInterval.new(cantus_firmus, voice, position)
34
+ interval if interval.notes.length == 2
35
+ end
36
+ end
37
+
38
+ def weak_beat_positions
39
+ notes.map(&:position).reject { |pos| downbeat_position?(pos) }
40
+ end
41
+
42
+ def downbeat_position?(position)
43
+ cantus_firmus_positions.include?(position.to_s)
44
+ end
45
+
46
+ def cantus_firmus_positions
47
+ @cantus_firmus_positions ||= Set.new(cantus_firmus.notes.map { |n| n.position.to_s })
48
+ end
49
+
50
+ def next_downbeat_interval(weak_interval)
51
+ next_cf_note = cantus_firmus.notes.detect { |n| n.position > weak_interval.position }
52
+ return unless next_cf_note
53
+
54
+ interval = HeadMusic::Analysis::HarmonicInterval.new(cantus_firmus, voice, next_cf_note.position)
55
+ interval if interval.notes.length == 2
56
+ end
57
+
58
+ def same_simple_type?(first, second)
59
+ first.simple_number == second.simple_number
60
+ end
61
+ end
@@ -0,0 +1,27 @@
1
+ # Module for style guidelines.
2
+ module HeadMusic::Style::Guidelines; end
3
+
4
+ # A counterpoint guideline
5
+ class HeadMusic::Style::Guidelines::NoParallelPerfectOnDownbeats < HeadMusic::Style::Annotation
6
+ MESSAGE = "Avoid parallel perfect consonances on consecutive downbeats."
7
+
8
+ def marks
9
+ parallel_perfect_downbeat_pairs.map do |pair|
10
+ HeadMusic::Style::Mark.for_all(pair.flat_map(&:notes))
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def parallel_perfect_downbeat_pairs
17
+ downbeat_harmonic_intervals.each_cons(2).select do |first, second|
18
+ first.perfect_consonance?(:two_part_harmony) &&
19
+ second.perfect_consonance?(:two_part_harmony) &&
20
+ same_simple_type?(first, second)
21
+ end
22
+ end
23
+
24
+ def same_simple_type?(first, second)
25
+ first.simple_number == second.simple_number
26
+ end
27
+ end
@@ -0,0 +1,25 @@
1
+ # Module for style guidelines.
2
+ module HeadMusic::Style::Guidelines; end
3
+
4
+ # A counterpoint guideline
5
+ class HeadMusic::Style::Guidelines::NoStrongBeatUnisons < HeadMusic::Style::Annotation
6
+ MESSAGE = "Avoid unisons on strong beats except at the beginning and end."
7
+
8
+ def marks
9
+ interior_downbeat_unisons.map do |interval|
10
+ HeadMusic::Style::Mark.for_all(interval.notes)
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def interior_downbeat_unisons
17
+ interior_downbeat_intervals.select { |interval| interval.perfect_consonance? && interval.unison? }
18
+ end
19
+
20
+ def interior_downbeat_intervals
21
+ return [] if downbeat_harmonic_intervals.length < 3
22
+
23
+ downbeat_harmonic_intervals[1..-2]
24
+ end
25
+ end
@@ -3,10 +3,10 @@ module HeadMusic::Style::Guidelines; end
3
3
 
4
4
  # A counterpoint guideline
5
5
  class HeadMusic::Style::Guidelines::SingableIntervals < HeadMusic::Style::Annotation
6
- PERMITTED_ASCENDING = %w[PU m2 M2 m3 M3 P4 P5 m6 P8].freeze
7
- PERMITTED_DESCENDING = %w[PU m2 M2 m3 M3 P4 P5 P8].freeze
6
+ PERMITTED_ASCENDING = %w[P1 m2 M2 m3 M3 P4 P5 m6 P8].freeze
7
+ PERMITTED_DESCENDING = %w[P1 m2 M2 m3 M3 P4 P5 P8].freeze
8
8
 
9
- MESSAGE = "Use only PU, m2, M2, m3, M3, P4, P5, m6 (ascending), P8 in the melodic line."
9
+ MESSAGE = "Use only P1, m2, M2, m3, M3, P4, P5, m6 (ascending), P8 in the melodic line."
10
10
 
11
11
  def marks
12
12
  melodic_note_pairs.reject { |note_pair| permitted?(note_pair) }.map do |pair_with_unpermitted_interval|
@@ -0,0 +1,83 @@
1
+ # Module for style guidelines.
2
+ module HeadMusic::Style::Guidelines; end
3
+
4
+ # A counterpoint guideline
5
+ class HeadMusic::Style::Guidelines::TwoToOne < HeadMusic::Style::Annotation
6
+ MESSAGE = "Use two half notes against each whole note in the cantus firmus."
7
+
8
+ HALF = HeadMusic::Rudiment::RhythmicValue.get(:half)
9
+ WHOLE = HeadMusic::Rudiment::RhythmicValue.get(:whole)
10
+
11
+ def marks
12
+ return [] unless cantus_firmus&.notes&.any?
13
+
14
+ cantus_firmus.notes.each_with_index.filter_map do |cf_note, index|
15
+ bar_number = cf_note.position.bar_number
16
+ if index == cantus_firmus.notes.length - 1
17
+ check_final_bar(bar_number)
18
+ elsif index == 0
19
+ check_first_bar(bar_number)
20
+ else
21
+ check_middle_bar(bar_number)
22
+ end
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def check_first_bar(bar_number)
29
+ bar_notes = notes_in_bar(bar_number)
30
+ bar_rests = rests_in_bar(bar_number)
31
+ return if two_half_notes?(bar_notes)
32
+ return if rest_then_half_note?(bar_notes, bar_rests)
33
+
34
+ mark_bar(bar_number)
35
+ end
36
+
37
+ def check_middle_bar(bar_number)
38
+ bar_notes = notes_in_bar(bar_number)
39
+ return if two_half_notes?(bar_notes)
40
+
41
+ mark_bar(bar_number)
42
+ end
43
+
44
+ def check_final_bar(bar_number)
45
+ bar_notes = notes_in_bar(bar_number)
46
+ return if one_whole_note?(bar_notes)
47
+
48
+ mark_bar(bar_number)
49
+ end
50
+
51
+ def two_half_notes?(bar_notes)
52
+ bar_notes.length == 2 && bar_notes.all? { |n| n.rhythmic_value == HALF }
53
+ end
54
+
55
+ def one_whole_note?(bar_notes)
56
+ bar_notes.length == 1 && bar_notes.first.rhythmic_value == WHOLE
57
+ end
58
+
59
+ def rest_then_half_note?(bar_notes, bar_rests)
60
+ bar_notes.length == 1 &&
61
+ bar_notes.first.rhythmic_value == HALF &&
62
+ bar_rests.length == 1 &&
63
+ bar_rests.first.rhythmic_value == HALF
64
+ end
65
+
66
+ def notes_in_bar(bar_number)
67
+ notes.select { |n| n.position.bar_number == bar_number }
68
+ end
69
+
70
+ def rests_in_bar(bar_number)
71
+ rests.select { |r| r.position.bar_number == bar_number }
72
+ end
73
+
74
+ def mark_bar(bar_number)
75
+ bar_placements = notes_in_bar(bar_number)
76
+ if bar_placements.any?
77
+ HeadMusic::Style::Mark.for_all(bar_placements)
78
+ else
79
+ cf_note = cantus_firmus.notes.detect { |n| n.position.bar_number == bar_number }
80
+ HeadMusic::Style::Mark.for(cf_note) if cf_note
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,59 @@
1
+ # Module for style guidelines.
2
+ module HeadMusic::Style::Guidelines; end
3
+
4
+ # A counterpoint guideline
5
+ class HeadMusic::Style::Guidelines::WeakBeatDissonanceTreatment < HeadMusic::Style::Annotation
6
+ MESSAGE = "Use only passing tones for dissonances on the weak beat."
7
+
8
+ def marks
9
+ return [] unless cantus_firmus&.notes&.any?
10
+
11
+ dissonant_weak_beat_notes.reject { |note| passing_tone?(note) }.map do |note|
12
+ HeadMusic::Style::Mark.for(note)
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def dissonant_weak_beat_notes
19
+ weak_beat_notes.select { |note| dissonant_with_cantus?(note) }
20
+ end
21
+
22
+ def weak_beat_notes
23
+ notes.reject { |note| downbeat_position?(note.position) }
24
+ end
25
+
26
+ def downbeat_position?(position)
27
+ cantus_firmus_positions.include?(position.to_s)
28
+ end
29
+
30
+ def cantus_firmus_positions
31
+ @cantus_firmus_positions ||= Set.new(cantus_firmus.notes.map { |n| n.position.to_s })
32
+ end
33
+
34
+ def dissonant_with_cantus?(note)
35
+ interval = HeadMusic::Analysis::HarmonicInterval.new(cantus_firmus, voice, note.position)
36
+ interval.notes.length == 2 && interval.dissonance?(:two_part_harmony)
37
+ end
38
+
39
+ def passing_tone?(note)
40
+ prev = preceding_note(note)
41
+ foll = following_note(note)
42
+ return false unless prev && foll
43
+
44
+ approach = HeadMusic::Analysis::MelodicInterval.new(prev, note)
45
+ departure = HeadMusic::Analysis::MelodicInterval.new(note, foll)
46
+
47
+ approach.step? && departure.step? && approach.direction == departure.direction
48
+ end
49
+
50
+ def preceding_note(note)
51
+ index = notes.index(note)
52
+ notes[index - 1] if index && index > 0
53
+ end
54
+
55
+ def following_note(note)
56
+ index = notes.index(note)
57
+ notes[index + 1] if index && index < notes.length - 1
58
+ end
59
+ end
@@ -0,0 +1,22 @@
1
+ # Module for guides
2
+ module HeadMusic::Style::Guides; end
3
+
4
+ # Rules for second species harmony
5
+ class HeadMusic::Style::Guides::SecondSpeciesHarmony
6
+ RULESET = [
7
+ HeadMusic::Style::Guidelines::ApproachPerfectionContrarily,
8
+ HeadMusic::Style::Guidelines::AvoidCrossingVoices,
9
+ HeadMusic::Style::Guidelines::AvoidOverlappingVoices,
10
+ HeadMusic::Style::Guidelines::ConsonantDownbeats,
11
+ HeadMusic::Style::Guidelines::NoParallelPerfectAcrossBarline,
12
+ HeadMusic::Style::Guidelines::NoParallelPerfectOnDownbeats,
13
+ HeadMusic::Style::Guidelines::NoStrongBeatUnisons,
14
+ HeadMusic::Style::Guidelines::PreferContraryMotion,
15
+ HeadMusic::Style::Guidelines::PreferImperfect,
16
+ HeadMusic::Style::Guidelines::WeakBeatDissonanceTreatment
17
+ ].freeze
18
+
19
+ def self.analyze(voice)
20
+ RULESET.map { |rule| rule.new(voice) }
21
+ end
22
+ end
@@ -0,0 +1,26 @@
1
+ # Module for guides
2
+ module HeadMusic::Style::Guides; end
3
+
4
+ # Rules for second species melodies
5
+ class HeadMusic::Style::Guides::SecondSpeciesMelody
6
+ RULESET = [
7
+ HeadMusic::Style::Guidelines::AlwaysMove,
8
+ HeadMusic::Style::Guidelines::ConsonantClimax,
9
+ HeadMusic::Style::Guidelines::Diatonic,
10
+ HeadMusic::Style::Guidelines::EndOnTonic,
11
+ HeadMusic::Style::Guidelines::FrequentDirectionChanges,
12
+ HeadMusic::Style::Guidelines::LimitOctaveLeaps,
13
+ HeadMusic::Style::Guidelines::MostlyConjunct,
14
+ HeadMusic::Style::Guidelines::PrepareOctaveLeaps,
15
+ HeadMusic::Style::Guidelines::SingableIntervals,
16
+ HeadMusic::Style::Guidelines::SingableRange,
17
+ HeadMusic::Style::Guidelines::StartOnPerfectConsonance,
18
+ HeadMusic::Style::Guidelines::StepOutOfUnison,
19
+ HeadMusic::Style::Guidelines::StepUpToFinalNote,
20
+ HeadMusic::Style::Guidelines::TwoToOne
21
+ ].freeze
22
+
23
+ def self.analyze(voice)
24
+ RULESET.map { |rule| rule.new(voice) }
25
+ end
26
+ end
@@ -1,3 +1,3 @@
1
1
  module HeadMusic
2
- VERSION = "11.5.0"
2
+ VERSION = "11.6.0"
3
3
  end
data/lib/head_music.rb CHANGED
@@ -166,7 +166,10 @@ require "head_music/style/guidelines/limit_octave_leaps"
166
166
  require "head_music/style/guidelines/moderate_direction_changes"
167
167
  require "head_music/style/guidelines/mostly_conjunct"
168
168
  require "head_music/style/guidelines/notes_same_length"
169
+ require "head_music/style/guidelines/no_parallel_perfect_across_barline"
170
+ require "head_music/style/guidelines/no_parallel_perfect_on_downbeats"
169
171
  require "head_music/style/guidelines/no_rests"
172
+ require "head_music/style/guidelines/no_strong_beat_unisons"
170
173
  require "head_music/style/guidelines/no_unisons_in_middle"
171
174
  require "head_music/style/guidelines/one_to_one"
172
175
  require "head_music/style/guidelines/prefer_contrary_motion"
@@ -182,10 +185,14 @@ require "head_music/style/guidelines/step_down_to_final_note"
182
185
  require "head_music/style/guidelines/step_out_of_unison"
183
186
  require "head_music/style/guidelines/step_to_final_note"
184
187
  require "head_music/style/guidelines/step_up_to_final_note"
188
+ require "head_music/style/guidelines/two_to_one"
185
189
  require "head_music/style/guidelines/up_to_fourteen_notes"
190
+ require "head_music/style/guidelines/weak_beat_dissonance_treatment"
186
191
 
187
192
  # style guides
188
193
  require "head_music/style/guides/fux_cantus_firmus"
189
194
  require "head_music/style/guides/modern_cantus_firmus"
190
195
  require "head_music/style/guides/first_species_melody"
191
196
  require "head_music/style/guides/first_species_harmony"
197
+ require "head_music/style/guides/second_species_melody"
198
+ require "head_music/style/guides/second_species_harmony"
@@ -0,0 +1,278 @@
1
+ # Second-Species Counterpoint: Pedagogical Reference
2
+
3
+ A survey of second-species counterpoint guidelines from Fux (1725) through contemporary pedagogy, noting points of agreement and disagreement across sources. Intended as a reference for implementing style guidelines in the `head_music` gem.
4
+
5
+ ---
6
+
7
+ ## 1. Fundamental Premise
8
+
9
+ Second-species counterpoint sets **two half notes** in the counterpoint voice against each **whole note** in the cantus firmus, creating a 2:1 rhythmic ratio. This species introduces two concepts absent from first species:
10
+
11
+ 1. **Metric differentiation** -- strong beats (downbeats) vs. weak beats (upbeats)
12
+ 2. **Dissonance** -- the passing tone, in a highly controlled context
13
+
14
+ ---
15
+
16
+ ## 2. Sources Surveyed
17
+
18
+ | Abbreviation | Source | Orientation |
19
+ |---|---|---|
20
+ | **Fux** | Johann Joseph Fux, *Gradus ad Parnassum* (1725); trans. Alfred Mann | Ostensibly Palestrina style; actually reflects 18th-century practice |
21
+ | **Schenker** | Heinrich Schenker, *Kontrapunkt* (1910/1922); trans. Rothgeb & Thym | Abstract voice-leading principles underlying tonal music |
22
+ | **Jeppesen** | Knud Jeppesen, *Counterpoint: The Polyphonic Vocal Style of the 16th Century* (1931) | Closest to actual Palestrina/Renaissance practice |
23
+ | **S&S** | Felix Salzer & Carl Schachter, *Counterpoint in Composition* (1969) | Schenkerian -- species as foundation for prolongational voice-leading |
24
+ | **Kennan** | Kent Kennan, *Counterpoint*, 4th ed. (1999) | 18th-century tonal counterpoint (Bach-oriented) |
25
+ | **Gauldin** | Robert Gauldin, *A Practical Approach to 16th-Century Counterpoint* / *18th-Century Counterpoint* | Separate treatments for Renaissance and Baroque styles |
26
+ | **Schubert** | Peter Schubert, *Modal Counterpoint: Renaissance Style*, 2nd ed. (2008) | Renaissance modal; draws directly on Renaissance treatises |
27
+ | **OMT** | Open Music Theory (Shaffer, Hughes, et al.) | Contemporary online pedagogy |
28
+
29
+ ---
30
+
31
+ ## 3. Guideline Synthesis
32
+
33
+ ### 3.1 Rhythmic Structure
34
+
35
+ **Universal agreement.** Two half notes against each whole note. The final measure is a whole note.
36
+
37
+ ### 3.2 Beginning
38
+
39
+ **Pitch (universal):**
40
+ - Above the cantus firmus: begin on the tonic (unison/octave) or the fifth.
41
+ - Below the cantus firmus: begin on the tonic (unison/octave).
42
+
43
+ **Rhythm (two options):**
44
+ 1. **Half rest followed by a half note** -- the standard Fuxian approach; preferred by most modern sources because it establishes the 2:1 rhythmic profile immediately.
45
+ 2. **Two half notes** -- permitted by most sources.
46
+
47
+ ### 3.3 Strong Beats (Downbeats)
48
+
49
+ **Universal agreement.** Every downbeat must be **consonant** with the cantus firmus. Permissible consonances: unison, third, fifth, sixth, octave, and compound equivalents. The perfect fourth is a dissonance in two-voice counterpoint (all sources agree).
50
+
51
+ **Preference.** Imperfect consonances (thirds, sixths) are preferred over perfect consonances (fifths, octaves, unisons) -- carried forward from first species.
52
+
53
+ ### 3.4 Weak Beats (Upbeats)
54
+
55
+ **Core agreement.** The weak beat may be consonant or dissonant.
56
+
57
+ **If dissonant:** Must be a **passing tone** -- approached by step and left by step **in the same direction**. This is the only dissonance type permitted in second species. All sources agree.
58
+
59
+ **If consonant:** May be reached by step or by leap. Open Music Theory names specific weak-beat consonant patterns:
60
+
61
+ | Pattern | Description |
62
+ |---|---|
63
+ | **Consonant passing tone** | Two steps in the same direction, outlining a third |
64
+ | **Substitution** | Leap a fourth, then step in the opposite direction |
65
+ | **Skipped passing tone** | A third and a step in the same direction, outlining a fourth |
66
+ | **Interval subdivision** | Two leaps in the same direction dividing a larger interval |
67
+ | **Change of register** | A large consonant leap (P5, 6th, or octave) followed by a step opposite |
68
+ | **Melodic delay** | Leap a third, then step in the opposite direction |
69
+ | **Consonant neighbor tone** | Step in one direction, then step back (see disagreement below) |
70
+
71
+ #### Neighbor Tone Disagreement (the largest area of divergence)
72
+
73
+ | Source | Dissonant Neighbor | Consonant Neighbor |
74
+ |---|---|---|
75
+ | Fux | Not discussed; implicitly forbidden | Not discussed |
76
+ | Schenker | **Explicitly forbidden** | **Forbidden** ("highlights a single pitch") |
77
+ | Jeppesen | Forbidden | Permitted occasionally |
78
+ | S&S | Generally forbidden | Permitted with care |
79
+ | OMT | Forbidden | **Explicitly permitted** (named pattern) |
80
+
81
+ No source permits **dissonant** neighbor tones in second species. The disagreement is over **consonant** neighbor tones -- Schenker's ban is the strictest; contemporary pedagogy is the most permissive.
82
+
83
+ ### 3.5 Ending / Cadence
84
+
85
+ **Universal agreement.** End with a clausula vera: stepwise contrary motion into a perfect consonance (unison or octave).
86
+
87
+ **Final bar:** Always a whole note on the tonic, forming a unison or octave with the cantus firmus.
88
+
89
+ **Penultimate bar** may contain two half notes or one whole note.
90
+
91
+ **Specific cadential formulas:**
92
+
93
+ | Cantus firmus position | Penultimate intervals | Scale degrees in counterpoint |
94
+ |---|---|---|
95
+ | CF below, counterpoint above | Fifth then major sixth resolving to octave | 6-7-8 against CF scale degree 2 |
96
+ | CF above, counterpoint below | Fifth then minor third resolving to unison/octave | 5-3-1 against CF scale degree 2 |
97
+
98
+ **Phrygian exception (Puget Sound):** When the CF is above in Phrygian mode, use a sixth to a third (scale degrees 4-b7 against b2).
99
+
100
+ ### 3.6 Melodic Guidelines
101
+
102
+ These carry forward from first species with modifications:
103
+
104
+ - **Primarily stepwise motion** -- even more so than first species, since the doubled note count and passing tones make conjunct motion easier.
105
+ - **Single overall climax** -- with one or two secondary climaxes permitted given the greater number of notes.
106
+ - **Singable range** -- generally not exceeding a tenth.
107
+ - **Diatonic** -- stay within the mode/key.
108
+ - **Recover large leaps** -- follow leaps with stepwise motion in the opposite direction.
109
+ - **Prefer leaping within the bar** (strong to weak) rather than across the barline (weak to strong), as the metric position diminishes the perceptual weight of the leap.
110
+ - **No repeated notes** (see 3.10 below).
111
+
112
+ ### 3.7 Parallel Perfect Consonances
113
+
114
+ **Strict rule (universal).** Parallel fifths or octaves between adjacent attacks (strong-to-weak or weak-to-strong) are absolutely forbidden.
115
+
116
+ **Downbeat-to-downbeat parallels:**
117
+
118
+ | Situation | Treatment |
119
+ |---|---|
120
+ | P5 on downbeat, P5 on next downbeat | **Forbidden** by virtually all sources |
121
+ | P8 on downbeat, P8 on next downbeat | **Forbidden** similarly |
122
+ | P5 on weak beat, P5 on next downbeat | **Forbidden** (equivalent to first-species parallels across the barline) |
123
+
124
+ **Exception (minority).** Some sources tolerate downbeat-to-downbeat parallels if the intervening note leaps by a fourth or more, arguing the large leap masks the effect. Not universally accepted.
125
+
126
+ **Weak-beat-to-weak-beat parallels** are acceptable (not perceptually audible).
127
+
128
+ ### 3.8 Direct (Hidden) Fifths and Octaves
129
+
130
+ | Context | Treatment |
131
+ |---|---|
132
+ | Weak-to-strong (across barline) | Treated as in first species: **approach perfect consonances by contrary or oblique motion** (universal) |
133
+ | Strong-to-weak (within the bar) | More lenient; some sources permit direct motion to perfect consonances |
134
+ | Downbeat-to-downbeat | **Permitted** by most modern sources ("the effect is weakened by the intervening note") |
135
+
136
+ This is a significant relaxation from first species. Hidden fifths/octaves between successive downbeats are generally acceptable; hidden fifths/octaves across the barline (weak to strong) remain forbidden.
137
+
138
+ ### 3.9 Unison Treatment
139
+
140
+ | Source | Interior Unisons |
141
+ |---|---|
142
+ | Fux | Forbidden in middle (though his own examples occasionally show them) |
143
+ | Schenker | Strictly limited |
144
+ | Jeppesen | More permissive |
145
+ | Modern consensus | Permitted on **weak beats only**, with proper voice-leading |
146
+
147
+ **Generally agreed:**
148
+ - Permitted at first and last dyads.
149
+ - Interior unisons (if allowed) occur only on weak beats.
150
+ - Do not leap to or from a unison.
151
+ - Leave a unison by step in the opposite direction from approach.
152
+
153
+ ### 3.10 Repeated Notes
154
+
155
+ **Universal agreement.** Repeating the same pitch on consecutive half notes is **forbidden**, both within the bar and across the barline. Repetition undermines the forward motion that second species is designed to cultivate.
156
+
157
+ ### 3.11 Permitted and Forbidden Leaps
158
+
159
+ **Permitted leaps:**
160
+ - Minor third, major third
161
+ - Perfect fourth, perfect fifth
162
+ - Minor sixth (ascending only in 16th-century style)
163
+ - Octave
164
+
165
+ **Forbidden leaps:**
166
+ - Tritone (diminished fifth, augmented fourth)
167
+ - Major sixth (in 16th-century style; some 18th-century treatments permit it)
168
+ - Seventh
169
+ - Any augmented or diminished interval
170
+
171
+ **Leap treatment:**
172
+ - All leaps must be to and from consonances. Cannot leap to or from a dissonant note.
173
+ - Recover large leaps by stepwise motion in the opposite direction.
174
+ - Limit consecutive leaps in the same direction.
175
+
176
+ ### 3.12 Voice Crossing and Overlap
177
+
178
+ | Source | Voice Crossing |
179
+ |---|---|
180
+ | Fux | Occasionally permits |
181
+ | Schenker | Strictly forbidden |
182
+ | S&S | Strictly forbidden ("weakens polarity between voices") |
183
+ | Modern pedagogy | Generally forbidden; rarely tolerated |
184
+
185
+ Voice overlap (approaching beyond the other voice's previous pitch without crossing) is universally discouraged.
186
+
187
+ ### 3.13 Contrary Motion Preference
188
+
189
+ **Universal.** Contrary motion is preferred throughout, especially when approaching perfect consonances. Perfect consonances on the downbeat must be approached by contrary or oblique motion from the preceding note across the barline.
190
+
191
+ ---
192
+
193
+ ## 4. Mapping to head_music Architecture
194
+
195
+ The existing `head_music` style system groups guidelines into melody guides and harmony guides. Second species will follow the same pattern. Below maps each guideline area to whether it is a **melody** concern (single-voice) or **harmony** concern (two-voice interaction), and notes where existing first-species guidelines can be reused vs. where new guidelines are needed.
196
+
197
+ ### Melody Guidelines (SecondSpeciesMelody)
198
+
199
+ | Guideline | Status | Notes |
200
+ |---|---|---|
201
+ | Diatonic | **Reuse** from first species | |
202
+ | SingableRange | **Reuse** | |
203
+ | SingableIntervals | **Reuse** | |
204
+ | MostlyConjunct | **Reuse** (may need threshold adjustment) | Even more stepwise than first species |
205
+ | ConsonantClimax | **Reuse** | Secondary climaxes may need consideration |
206
+ | FrequentDirectionChanges | **Reuse** | |
207
+ | LimitOctaveLeaps | **Reuse** | |
208
+ | PrepareOctaveLeaps | **Reuse** | |
209
+ | NoRests | **Reuse** | |
210
+ | EndOnTonic | **Reuse** | |
211
+ | NotesSameLength | **Remove/Replace** | Not applicable; replaced by TwoToOne ratio |
212
+ | **TwoToOne** | **New** | Two half notes per whole note in CF (except final bar) |
213
+ | **NoRepeatedNotes** | **New** (or reuse AlwaysMove) | No consecutive identical pitches |
214
+ | **BeginWithRestOrConsonance** | **New** | Half rest + half note, or two half notes starting on P1/P5/P8 |
215
+ | **StepUpToFinalNote** | **Reuse** or adapt | Penultimate note approaches tonic by step |
216
+ | **PreferLeapsWithinBar** | **New** (soft) | Leap from strong to weak preferred over weak to strong |
217
+
218
+ ### Harmony Guidelines (SecondSpeciesHarmony)
219
+
220
+ | Guideline | Status | Notes |
221
+ |---|---|---|
222
+ | ConsonantDownbeats | **Reuse** | |
223
+ | PreferImperfect | **Reuse** | |
224
+ | PreferContraryMotion | **Reuse** | |
225
+ | ApproachPerfectionContrarily | **Reuse** (across barline) | Weak-to-strong motion into perfect consonance |
226
+ | AvoidCrossingVoices | **Reuse** | |
227
+ | AvoidOverlappingVoices | **Reuse** | |
228
+ | NoUnisonsInMiddle | **Adapt** | Allow unisons on weak beats; forbid on strong beats |
229
+ | OneToOne | **Remove** | Not applicable; replaced by TwoToOne |
230
+ | **WeakBeatDissonanceTreatment** | **New** | Dissonant weak beats must be passing tones (step in, step out, same direction) |
231
+ | **NoParallelPerfectOnDownbeats** | **New** | No P5-P5 or P8-P8 on consecutive downbeats |
232
+ | **NoParallelPerfectAcrossBarline** | **New** | No parallel perfect consonances from weak beat to next strong beat |
233
+ | **CadentialFormula** | **New** | Proper clausula vera approach (5-6-8 above; 5-3-1 below) |
234
+
235
+ ### Hard vs. Soft Classification (after Schubert)
236
+
237
+ **Hard rules** (fitness near 0 for violations):
238
+ - Consonant downbeats
239
+ - Passing tone as the only dissonance type
240
+ - No parallel perfect consonances on adjacent attacks
241
+ - No repeated notes
242
+ - Diatonic
243
+ - Singable intervals
244
+
245
+ **Soft rules** (penalty but not zero fitness):
246
+ - Prefer contrary motion
247
+ - Prefer imperfect consonances
248
+ - Mostly conjunct
249
+ - Prefer leaps within the bar
250
+ - Avoid consecutive downbeat perfect consonances
251
+ - Avoid interior unisons on strong beats
252
+
253
+ ---
254
+
255
+ ## 5. Sources
256
+
257
+ ### Primary Textbooks
258
+
259
+ - Fux, J.J. *Gradus ad Parnassum* (1725). Trans. Alfred Mann, *The Study of Counterpoint* (W.W. Norton, 1965).
260
+ - Schenker, H. *Kontrapunkt* (1910/1922). Trans. Rothgeb & Thym, *Counterpoint* (Musicalia Press, 2001).
261
+ - Jeppesen, K. *Counterpoint: The Polyphonic Vocal Style of the Sixteenth Century* (1931; Dover, 1992).
262
+ - Salzer, F. & Schachter, C. *Counterpoint in Composition* (Columbia UP, 1969).
263
+ - Kennan, K. *Counterpoint*, 4th ed. (Prentice Hall, 1999).
264
+ - Gauldin, R. *A Practical Approach to 16th-Century Counterpoint* (Waveland Press).
265
+ - Gauldin, R. *A Practical Approach to 18th-Century Counterpoint* (Waveland Press).
266
+ - Schubert, P. *Modal Counterpoint: Renaissance Style*, 2nd ed. (Oxford UP, 2008).
267
+ - Roig-Francoli, M. *Harmony in Context*, 3rd ed. (McGraw-Hill, 2020).
268
+
269
+ ### Online Pedagogy
270
+
271
+ - [Open Music Theory -- Second-Species Counterpoint](https://viva.pressbooks.pub/openmusictheory/chapter/second-species-counterpoint/)
272
+ - [Kris Shaffer -- Composing a Second-Species Counterpoint](http://kshaffer.github.io/musicianshipResources/secondSpecies.html)
273
+ - [Puget Sound Music Theory -- Second Species](https://musictheory.pugetsound.edu/mt21c/SecondSpecies.html)
274
+ - [Rothfarb (UCSB) -- The Second Species of Counterpoint](https://rothfarb.faculty.music.ucsb.edu/courses/103/Second_Species(2v).html)
275
+ - [Ars Nova -- Second Species Counterpoint](https://www.ars-nova.com/cpmanual/secondspecies.htm)
276
+ - [Todd Tarantino -- Basic Rules for Second Species Counterpoint (PDF)](http://www.toddtarantino.com/fundamentals/SecondSpeciesRules.pdf)
277
+ - [Clark Ross -- 16th Century Counterpoint Rules (PDF, based on Jeppesen)](https://www.clarkross.ca/16thC-100-SpeciesRules.3.pdf)
278
+ - [Hansen Media -- Second Species Guidelines 2:1](https://hansenmedia.net/courses/counterpoint/lessons/second-species-guidelines-21/)
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: 11.5.0
4
+ version: 11.6.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-05 00:00:00.000000000 Z
11
+ date: 2026-02-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -283,7 +283,10 @@ files:
283
283
  - lib/head_music/style/guidelines/limit_octave_leaps.rb
284
284
  - lib/head_music/style/guidelines/moderate_direction_changes.rb
285
285
  - lib/head_music/style/guidelines/mostly_conjunct.rb
286
+ - lib/head_music/style/guidelines/no_parallel_perfect_across_barline.rb
287
+ - lib/head_music/style/guidelines/no_parallel_perfect_on_downbeats.rb
286
288
  - lib/head_music/style/guidelines/no_rests.rb
289
+ - lib/head_music/style/guidelines/no_strong_beat_unisons.rb
287
290
  - lib/head_music/style/guidelines/no_unisons_in_middle.rb
288
291
  - lib/head_music/style/guidelines/notes_same_length.rb
289
292
  - lib/head_music/style/guidelines/one_to_one.rb
@@ -300,11 +303,15 @@ files:
300
303
  - lib/head_music/style/guidelines/step_out_of_unison.rb
301
304
  - lib/head_music/style/guidelines/step_to_final_note.rb
302
305
  - lib/head_music/style/guidelines/step_up_to_final_note.rb
306
+ - lib/head_music/style/guidelines/two_to_one.rb
303
307
  - lib/head_music/style/guidelines/up_to_fourteen_notes.rb
308
+ - lib/head_music/style/guidelines/weak_beat_dissonance_treatment.rb
304
309
  - lib/head_music/style/guides/first_species_harmony.rb
305
310
  - lib/head_music/style/guides/first_species_melody.rb
306
311
  - lib/head_music/style/guides/fux_cantus_firmus.rb
307
312
  - lib/head_music/style/guides/modern_cantus_firmus.rb
313
+ - lib/head_music/style/guides/second_species_harmony.rb
314
+ - lib/head_music/style/guides/second_species_melody.rb
308
315
  - lib/head_music/style/mark.rb
309
316
  - lib/head_music/style/medieval_tradition.rb
310
317
  - lib/head_music/style/modern_tradition.rb
@@ -322,6 +329,7 @@ files:
322
329
  - lib/head_music/utilities/case.rb
323
330
  - lib/head_music/utilities/hash_key.rb
324
331
  - lib/head_music/version.rb
332
+ - references/second-species-counterpoint.md
325
333
  - user_stories/backlog/notation-style.md
326
334
  - user_stories/backlog/organizing-content.md
327
335
  - user_stories/done/consonance-dissonance-classification.md