head_music 12.2.0 → 12.3.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 +4 -4
- data/Gemfile.lock +1 -1
- data/lib/head_music/style/guidelines/no_parallel_perfect_with_syncopation.rb +27 -0
- data/lib/head_music/style/guidelines/one_to_one_with_ties.rb +27 -0
- data/lib/head_music/style/guidelines/second_species_break.rb +64 -0
- data/lib/head_music/style/guidelines/suspension_treatment.rb +65 -0
- data/lib/head_music/style/guidelines/third_species_dissonance_treatment.rb +1 -8
- data/lib/head_music/style/guidelines/weak_beat_dissonance_treatment.rb +17 -9
- data/lib/head_music/style/guides/fourth_species_harmony.rb +16 -0
- data/lib/head_music/style/guides/fourth_species_melody.rb +23 -0
- data/lib/head_music/version.rb +1 -1
- data/lib/head_music.rb +6 -0
- data/references/fourth-species-counterpoint.md +251 -0
- metadata +8 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 20155b40b92a030588ea4b1c791efcc37c70aa40e5e2008b0d3a282cb72391a1
|
|
4
|
+
data.tar.gz: e717940c964c2859dcf7181a5ae7997f99cabaf741f6d5ab73dc374977ae2f3f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9b97712e65e7ecb1df4dc400854aedfb1ed9fbf0654fdda348dc72d552c6e3f061eaa1dec9b02034d867c7279fe1ad5a6305dd9724cf4c47f8389463486f1f4b
|
|
7
|
+
data.tar.gz: c3bd7b7c5bc5ea85790a7e15998eb25f9a32e6a249ffcbea26b4e420fdde5fad236e8e2ebf7a844405414e97a1104792f8ad0aae85862ed11712aa1103794490
|
data/Gemfile.lock
CHANGED
|
@@ -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::NoParallelPerfectWithSyncopation < HeadMusic::Style::Annotation
|
|
6
|
+
MESSAGE = "Avoid parallel perfect consonances between syncopated notes."
|
|
7
|
+
|
|
8
|
+
def marks
|
|
9
|
+
parallel_perfect_syncopation_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_syncopation_pairs
|
|
17
|
+
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,27 @@
|
|
|
1
|
+
# Module for style guidelines.
|
|
2
|
+
module HeadMusic::Style::Guidelines; end
|
|
3
|
+
|
|
4
|
+
# A counterpoint guideline for fourth species counterpoint.
|
|
5
|
+
# For each cantus firmus note, verifies that one or two counterpoint notes
|
|
6
|
+
# are sounding at that position. A note may sustain across the barline
|
|
7
|
+
# (syncopation) rather than starting at the CF note position.
|
|
8
|
+
# Two notes sounding against one CF note is permitted as a "second species break."
|
|
9
|
+
class HeadMusic::Style::Guidelines::OneToOneWithTies < HeadMusic::Style::Annotation
|
|
10
|
+
MESSAGE = "Place one note per cantus firmus note. Notes may sustain across the barline."
|
|
11
|
+
|
|
12
|
+
def marks
|
|
13
|
+
return unless cantus_firmus&.notes
|
|
14
|
+
return if cantus_firmus.notes.empty?
|
|
15
|
+
|
|
16
|
+
HeadMusic::Style::Mark.for_each(uncovered_cantus_firmus_notes)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def uncovered_cantus_firmus_notes
|
|
22
|
+
cantus_firmus.notes.select do |cf_note|
|
|
23
|
+
notes_sounding = voice.notes_during(cf_note)
|
|
24
|
+
notes_sounding.empty? || notes_sounding.length > 2
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Module for style guidelines.
|
|
2
|
+
module HeadMusic::Style::Guidelines; end
|
|
3
|
+
|
|
4
|
+
# A counterpoint guideline for fourth species counterpoint.
|
|
5
|
+
# The syncopated texture is occasionally broken: the counterpoint moves on the
|
|
6
|
+
# downbeat instead of sustaining. When this happens, a dissonant off-beat note
|
|
7
|
+
# is permitted only if it is a passing tone. Breaks should be infrequent.
|
|
8
|
+
class HeadMusic::Style::Guidelines::SecondSpeciesBreak < HeadMusic::Style::Guidelines::WeakBeatDissonanceTreatment
|
|
9
|
+
MESSAGE = "Use only passing tones when breaking the syncopated texture. Breaks should be infrequent."
|
|
10
|
+
|
|
11
|
+
MAX_BREAK_RATIO = 0.25
|
|
12
|
+
|
|
13
|
+
def marks
|
|
14
|
+
return [] unless cantus_firmus&.notes&.any?
|
|
15
|
+
|
|
16
|
+
dissonance_marks + frequency_marks
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def dissonance_marks
|
|
22
|
+
break_bar_off_beat_notes
|
|
23
|
+
.select { |note| dissonant_with_cantus?(note) }
|
|
24
|
+
.reject { |note| passing_tone?(note) }
|
|
25
|
+
.map { |note| HeadMusic::Style::Mark.for(note) }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def frequency_marks
|
|
29
|
+
return [] if total_bars <= 0
|
|
30
|
+
return [] if break_bars.length <= total_bars * MAX_BREAK_RATIO
|
|
31
|
+
|
|
32
|
+
break_bar_notes = break_bars.flat_map { |bar| notes_in_bar(bar) }
|
|
33
|
+
[HeadMusic::Style::Mark.for_all(break_bar_notes, fitness: HeadMusic::SMALL_PENALTY_FACTOR)]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def break_bars
|
|
37
|
+
@break_bars ||= (first_bar..last_bar).select { |bar| break_bar?(bar) }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def break_bar?(bar)
|
|
41
|
+
downbeats, off_beats = notes_in_bar(bar).partition { |note| downbeat_position?(note.position) }
|
|
42
|
+
downbeats.any? && off_beats.any?
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def break_bar_off_beat_notes
|
|
46
|
+
break_bars.flat_map { |bar| notes_in_bar(bar).reject { |note| downbeat_position?(note.position) } }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def notes_in_bar(bar)
|
|
50
|
+
notes.select { |note| note.position.bar_number == bar }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def total_bars
|
|
54
|
+
last_bar - first_bar + 1
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def first_bar
|
|
58
|
+
cantus_firmus.notes.first.position.bar_number
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def last_bar
|
|
62
|
+
cantus_firmus.notes.last.position.bar_number
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Module for style guidelines.
|
|
2
|
+
module HeadMusic::Style::Guidelines; end
|
|
3
|
+
|
|
4
|
+
# A counterpoint guideline for fourth species suspension treatment.
|
|
5
|
+
# A suspension has three parts:
|
|
6
|
+
# 1. Preparation: The note is consonant with the current cantus firmus note.
|
|
7
|
+
# 2. Suspension: The cantus firmus moves; the counterpoint sustains, becoming dissonant.
|
|
8
|
+
# 3. Resolution: The counterpoint resolves by step (usually down) to a consonance.
|
|
9
|
+
class HeadMusic::Style::Guidelines::SuspensionTreatment < HeadMusic::Style::Annotation
|
|
10
|
+
MESSAGE = "Treat suspensions with proper preparation and stepwise resolution."
|
|
11
|
+
|
|
12
|
+
def marks
|
|
13
|
+
return [] unless cantus_firmus&.notes&.any?
|
|
14
|
+
|
|
15
|
+
improperly_treated_suspensions.map do |note|
|
|
16
|
+
HeadMusic::Style::Mark.for(note)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def improperly_treated_suspensions
|
|
23
|
+
dissonant_suspensions.reject { |note, cf_note| properly_treated?(note, cf_note) }.map(&:first)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def dissonant_suspensions
|
|
27
|
+
cantus_firmus.notes[1..].filter_map do |cf_note|
|
|
28
|
+
cp_note = voice.note_at(cf_note.position)
|
|
29
|
+
next unless cp_note && cp_note.position < cf_note.position
|
|
30
|
+
|
|
31
|
+
interval = HeadMusic::Analysis::HarmonicInterval.new(cantus_firmus, voice, cf_note.position)
|
|
32
|
+
next unless interval.notes.length == 2 && interval.dissonance?(:two_part_harmony)
|
|
33
|
+
|
|
34
|
+
[cp_note, cf_note]
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def properly_treated?(cp_note, cf_note)
|
|
39
|
+
prepared?(cp_note, cf_note) && resolved?(cp_note, cf_note)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def prepared?(cp_note, cf_note)
|
|
43
|
+
prev_cf = previous_cf_note(cf_note)
|
|
44
|
+
return false unless prev_cf
|
|
45
|
+
|
|
46
|
+
interval = HeadMusic::Analysis::HarmonicInterval.new(cantus_firmus, voice, cp_note.position)
|
|
47
|
+
interval.notes.length == 2 && interval.consonance?(:two_part_harmony)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def resolved?(cp_note, cf_note)
|
|
51
|
+
next_cp = voice.note_following(cp_note.position)
|
|
52
|
+
return false unless next_cp
|
|
53
|
+
|
|
54
|
+
melodic = HeadMusic::Analysis::MelodicInterval.new(cp_note, next_cp)
|
|
55
|
+
return false unless melodic.step?
|
|
56
|
+
|
|
57
|
+
resolution_interval = HeadMusic::Analysis::HarmonicInterval.new(cantus_firmus, voice, next_cp.position)
|
|
58
|
+
resolution_interval.notes.length == 2 && resolution_interval.consonance?(:two_part_harmony)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def previous_cf_note(cf_note)
|
|
62
|
+
index = cantus_firmus.notes.index(cf_note)
|
|
63
|
+
cantus_firmus.notes[index - 1] if index && index > 0
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -15,14 +15,7 @@ class HeadMusic::Style::Guidelines::ThirdSpeciesDissonanceTreatment < HeadMusic:
|
|
|
15
15
|
|
|
16
16
|
# Neighbor tone: approached by step, left by step in the opposite direction.
|
|
17
17
|
def neighbor_tone?(note)
|
|
18
|
-
|
|
19
|
-
foll = following_note(note)
|
|
20
|
-
return false unless prev && foll
|
|
21
|
-
|
|
22
|
-
approach = melodic_interval_between(prev, note)
|
|
23
|
-
departure = melodic_interval_between(note, foll)
|
|
24
|
-
|
|
25
|
-
approach.step? && departure.step? && approach.direction != departure.direction
|
|
18
|
+
stepwise_figure?(note, same_direction: false)
|
|
26
19
|
end
|
|
27
20
|
|
|
28
21
|
# Nota cambiata: a five-note figure where note 2 is dissonant,
|
|
@@ -32,7 +32,7 @@ class HeadMusic::Style::Guidelines::WeakBeatDissonanceTreatment < HeadMusic::Sty
|
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def cantus_firmus_positions
|
|
35
|
-
@cantus_firmus_positions ||= Set.new(cantus_firmus.notes.map { |
|
|
35
|
+
@cantus_firmus_positions ||= Set.new(cantus_firmus.notes.map { |note| note.position.to_s })
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def dissonant_with_cantus?(note)
|
|
@@ -41,18 +41,26 @@ class HeadMusic::Style::Guidelines::WeakBeatDissonanceTreatment < HeadMusic::Sty
|
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
def passing_tone?(note)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return false unless prev && foll
|
|
44
|
+
stepwise_figure?(note, same_direction: true)
|
|
45
|
+
end
|
|
47
46
|
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
def stepwise_figure?(note, same_direction:)
|
|
48
|
+
surrounding = surrounding_notes(note)
|
|
49
|
+
return false unless surrounding
|
|
50
50
|
|
|
51
|
-
approach
|
|
51
|
+
approach = melodic_interval_between(surrounding.first, note)
|
|
52
|
+
departure = melodic_interval_between(note, surrounding.last)
|
|
53
|
+
approach.step? && departure.step? && (approach.direction == departure.direction) == same_direction
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def surrounding_notes(note)
|
|
57
|
+
prev = preceding_note(note)
|
|
58
|
+
foll = following_note(note)
|
|
59
|
+
[prev, foll] if prev && foll
|
|
52
60
|
end
|
|
53
61
|
|
|
54
|
-
def melodic_interval_between(
|
|
55
|
-
HeadMusic::Analysis::MelodicInterval.new(
|
|
62
|
+
def melodic_interval_between(first_note, second_note)
|
|
63
|
+
HeadMusic::Analysis::MelodicInterval.new(first_note, second_note)
|
|
56
64
|
end
|
|
57
65
|
|
|
58
66
|
def preceding_note(note)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Rules for fourth species harmony
|
|
2
|
+
class HeadMusic::Style::Guides::FourthSpeciesHarmony < HeadMusic::Style::Guides::SpeciesHarmony
|
|
3
|
+
RULESET = [
|
|
4
|
+
HeadMusic::Style::Guidelines::ApproachPerfectionContrarily,
|
|
5
|
+
HeadMusic::Style::Guidelines::AvoidCrossingVoices,
|
|
6
|
+
HeadMusic::Style::Guidelines::AvoidOverlappingVoices,
|
|
7
|
+
HeadMusic::Style::Guidelines::ConsonantDownbeats,
|
|
8
|
+
HeadMusic::Style::Guidelines::NoParallelPerfectOnDownbeats,
|
|
9
|
+
HeadMusic::Style::Guidelines::NoParallelPerfectWithSyncopation,
|
|
10
|
+
HeadMusic::Style::Guidelines::NoStrongBeatUnisons,
|
|
11
|
+
HeadMusic::Style::Guidelines::PreferContraryMotion,
|
|
12
|
+
HeadMusic::Style::Guidelines::PreferImperfect,
|
|
13
|
+
HeadMusic::Style::Guidelines::SecondSpeciesBreak,
|
|
14
|
+
HeadMusic::Style::Guidelines::SuspensionTreatment
|
|
15
|
+
].freeze
|
|
16
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Module for guides
|
|
2
|
+
module HeadMusic::Style::Guides; end
|
|
3
|
+
|
|
4
|
+
# Rules for fourth species melodies
|
|
5
|
+
class HeadMusic::Style::Guides::FourthSpeciesMelody < HeadMusic::Style::Guides::SpeciesMelody
|
|
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::NoteFillsFinalBar,
|
|
12
|
+
HeadMusic::Style::Guidelines::FrequentDirectionChanges,
|
|
13
|
+
HeadMusic::Style::Guidelines::LimitOctaveLeaps,
|
|
14
|
+
HeadMusic::Style::Guidelines::MostlyConjunct,
|
|
15
|
+
HeadMusic::Style::Guidelines::OneToOneWithTies,
|
|
16
|
+
HeadMusic::Style::Guidelines::PrepareOctaveLeaps,
|
|
17
|
+
HeadMusic::Style::Guidelines::SingableIntervals,
|
|
18
|
+
HeadMusic::Style::Guidelines::SingableRange,
|
|
19
|
+
HeadMusic::Style::Guidelines::StartOnPerfectConsonance,
|
|
20
|
+
HeadMusic::Style::Guidelines::StepOutOfUnison,
|
|
21
|
+
HeadMusic::Style::Guidelines::StepUpToFinalNote
|
|
22
|
+
].freeze
|
|
23
|
+
end
|
data/lib/head_music/version.rb
CHANGED
data/lib/head_music.rb
CHANGED
|
@@ -176,10 +176,12 @@ require "head_music/style/guidelines/mostly_conjunct"
|
|
|
176
176
|
require "head_music/style/guidelines/notes_same_length"
|
|
177
177
|
require "head_music/style/guidelines/no_parallel_perfect_across_barline"
|
|
178
178
|
require "head_music/style/guidelines/no_parallel_perfect_on_downbeats"
|
|
179
|
+
require "head_music/style/guidelines/no_parallel_perfect_with_syncopation"
|
|
179
180
|
require "head_music/style/guidelines/no_rests"
|
|
180
181
|
require "head_music/style/guidelines/no_strong_beat_unisons"
|
|
181
182
|
require "head_music/style/guidelines/no_unisons_in_middle"
|
|
182
183
|
require "head_music/style/guidelines/one_to_one"
|
|
184
|
+
require "head_music/style/guidelines/one_to_one_with_ties"
|
|
183
185
|
require "head_music/style/guidelines/prefer_contrary_motion"
|
|
184
186
|
require "head_music/style/guidelines/prefer_imperfect"
|
|
185
187
|
require "head_music/style/guidelines/prepare_octave_leaps"
|
|
@@ -194,12 +196,14 @@ require "head_music/style/guidelines/step_down_to_final_note"
|
|
|
194
196
|
require "head_music/style/guidelines/step_out_of_unison"
|
|
195
197
|
require "head_music/style/guidelines/step_to_final_note"
|
|
196
198
|
require "head_music/style/guidelines/step_up_to_final_note"
|
|
199
|
+
require "head_music/style/guidelines/suspension_treatment"
|
|
197
200
|
require "head_music/style/guidelines/three_per_bar"
|
|
198
201
|
require "head_music/style/guidelines/two_per_bar"
|
|
199
202
|
require "head_music/style/guidelines/up_to_fourteen_notes"
|
|
200
203
|
require "head_music/style/guidelines/weak_beat_dissonance_treatment"
|
|
201
204
|
require "head_music/style/guidelines/third_species_dissonance_treatment"
|
|
202
205
|
require "head_music/style/guidelines/triple_meter_dissonance_treatment"
|
|
206
|
+
require "head_music/style/guidelines/second_species_break"
|
|
203
207
|
|
|
204
208
|
# style guides
|
|
205
209
|
require "head_music/style/guides/species_melody"
|
|
@@ -214,3 +218,5 @@ require "head_music/style/guides/third_species_triple_meter_melody"
|
|
|
214
218
|
require "head_music/style/guides/third_species_triple_meter_harmony"
|
|
215
219
|
require "head_music/style/guides/third_species_melody"
|
|
216
220
|
require "head_music/style/guides/third_species_harmony"
|
|
221
|
+
require "head_music/style/guides/fourth_species_melody"
|
|
222
|
+
require "head_music/style/guides/fourth_species_harmony"
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# Fourth-Species Counterpoint: Developer Reference
|
|
2
|
+
|
|
3
|
+
A concise reference for implementing fourth-species counterpoint guidelines in the `head_music` gem. Covers pedagogical foundations, music theory rules, and architectural mapping.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Historical Context
|
|
8
|
+
|
|
9
|
+
### Fux (1725)
|
|
10
|
+
|
|
11
|
+
Johann Joseph Fux's *Gradus ad Parnassum* establishes the species framework that remains canonical. Fourth species introduces the **suspension**: the counterpoint voice ties across the barline, causing the previously consonant pitch to become dissonant against the cantus firmus on the downbeat, then resolving by step downward on the upbeat. Fux treats suspensions as the defining idiom of Renaissance vocal counterpoint.
|
|
12
|
+
|
|
13
|
+
### Schenker (1910/1922)
|
|
14
|
+
|
|
15
|
+
Schenker reframes fourth species as a study in **voice-leading prolongation**. The suspension is not merely an ornament but a structural event: the tied note delays (prolongs) the resolution, creating tension that drives forward motion. He emphasizes the three-phase structure (preparation, suspension, resolution) as fundamental to understanding tonal prolongation at all structural levels.
|
|
16
|
+
|
|
17
|
+
### Contemporary Pedagogy (Salzer & Schachter, Schubert, OMT)
|
|
18
|
+
|
|
19
|
+
Modern treatments preserve the Fuxian suspension taxonomy while clarifying edge cases: when ties break (the "second species break"), how to handle the final cadence, and the treatment of the 2-3 suspension in the bass. Open Music Theory and Schubert's *Modal Counterpoint* both codify the rule that when the syncopated texture is interrupted, the off-beat note reverts to second-species behavior and may be a dissonant passing tone.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 2. The Syncopated Texture
|
|
24
|
+
|
|
25
|
+
Fourth species is built on **rhythmic syncopation**: notes begin on the weak beat of one bar and sustain through the downbeat of the next.
|
|
26
|
+
|
|
27
|
+
In 4/4 or cut time (the typical cantus firmus meter):
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
Beat: 1 2 3 4
|
|
31
|
+
CF: o--------- (whole note)
|
|
32
|
+
CPT: o---------o---------
|
|
33
|
+
^weak ^weak
|
|
34
|
+
preparation resolution
|
|
35
|
+
^strong
|
|
36
|
+
suspension (may be dissonant)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The **sounding duration** is what matters for analysis. A pitch that begins on beat 3 and sustains through beat 1 of the next bar is sounding on that downbeat regardless of how it is notated. See section 6 on notation.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 3. Suspension Types
|
|
44
|
+
|
|
45
|
+
Suspensions are named by the interval sequence: **dissonant interval on the downbeat -- resolved interval on the upbeat**.
|
|
46
|
+
|
|
47
|
+
### Upper Voice Suspensions (counterpoint above cantus firmus)
|
|
48
|
+
|
|
49
|
+
| Name | Suspension | Resolution | Notes |
|
|
50
|
+
|------|-----------|------------|-------|
|
|
51
|
+
| 7-6 | 7th (dissonant) | 6th | Most common; smooth resolution to imperfect consonance |
|
|
52
|
+
| 4-3 | 4th (dissonant) | 3rd | The 4th is dissonant in two-voice counterpoint |
|
|
53
|
+
| 9-8 | 9th (dissonant) | Octave | Resolves to perfect consonance; use with care |
|
|
54
|
+
| 2-1 | 2nd (dissonant) | Unison | Rare; treated as a form of 9-8 at the octave |
|
|
55
|
+
|
|
56
|
+
### Lower Voice Suspensions (counterpoint below cantus firmus)
|
|
57
|
+
|
|
58
|
+
| Name | Suspension | Resolution | Notes |
|
|
59
|
+
|------|-----------|------------|-------|
|
|
60
|
+
| 2-3 | 2nd (dissonant) | 3rd | The "bass suspension"; resolution moves downward by step |
|
|
61
|
+
| 4-5 | 4th (dissonant) | 5th | Less common than 2-3 |
|
|
62
|
+
| 9-10 | 9th (dissonant) | 10th (3rd) | Compound form of 2-3 |
|
|
63
|
+
|
|
64
|
+
**Key constraint for all suspensions:** The resolution must move **by step downward** (in the resolving voice) from the suspended pitch. Upward resolution is not permitted in strict species counterpoint.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 4. Suspension Structure: Preparation, Suspension, Resolution
|
|
69
|
+
|
|
70
|
+
Every suspension consists of three phases:
|
|
71
|
+
|
|
72
|
+
1. **Preparation** -- The pitch to be suspended is heard on a **weak beat** as a **consonance** with the cantus firmus. This establishes the pitch as stable before it becomes dissonant.
|
|
73
|
+
|
|
74
|
+
2. **Suspension** -- The same pitch is held (or re-articulated as tied) into the **strong beat** (downbeat) of the next bar, where it is now **dissonant** with the cantus firmus.
|
|
75
|
+
|
|
76
|
+
3. **Resolution** -- On the following **weak beat**, the suspended pitch moves **by step downward** to a consonance.
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
Phase: PREPARATION SUSPENSION RESOLUTION
|
|
80
|
+
Beat: weak strong weak
|
|
81
|
+
Harmony: consonant dissonant consonant
|
|
82
|
+
Motion: (arrival) (sustained) step down
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Preparation Requirements
|
|
86
|
+
|
|
87
|
+
- The preparation must be a consonance (not a dissonance, not a rest).
|
|
88
|
+
- The preparation pitch and the suspended pitch are the same pitch -- this is the defining feature of a suspension (vs. an appoggiatura, which arrives unprepared).
|
|
89
|
+
- In head_music terms: the `Placement` on the weak beat must produce a consonant `HarmonicInterval` with the cantus firmus at that position.
|
|
90
|
+
|
|
91
|
+
### Resolution Requirements
|
|
92
|
+
|
|
93
|
+
- Resolution is **always** by **step downward** in the voice containing the suspension.
|
|
94
|
+
- Resolution arrives on a **consonance** with the cantus firmus.
|
|
95
|
+
- The cantus firmus does not move during the suspension (it is a whole note): the resolution step belongs to the counterpoint voice alone.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## 5. The "Second Species Break"
|
|
100
|
+
|
|
101
|
+
The syncopated texture is sometimes interrupted -- the tie does not occur and the voice articulates a new pitch on the weak beat without sustaining into the next downbeat. This moment is called a **second species break** (some sources call it a "cambiata" context or simply describe it as reverting to second-species behavior).
|
|
102
|
+
|
|
103
|
+
**Rule:** When the texture breaks and the off-beat note does not tie into the next downbeat, that off-beat note may be a **dissonant passing tone**, subject to the same conditions as second species:
|
|
104
|
+
- Approached by step.
|
|
105
|
+
- Left by step **in the same direction**.
|
|
106
|
+
- The dissonance falls on the weak beat only.
|
|
107
|
+
|
|
108
|
+
This is the only context in fourth species where a dissonance may appear without being a suspension. The break allows the voice to redirect melodically when a suspension would produce forbidden parallels or other violations.
|
|
109
|
+
|
|
110
|
+
**Implementation note:** Detecting a second species break requires knowing whether a `Placement` at a weak beat position ties into the next downbeat. If `placement.next_position` falls on a downbeat and `voice.note_at(next_downbeat)` returns a different pitch (or no pitch), the tie has broken.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## 6. Notation-Agnostic Analysis Principle
|
|
115
|
+
|
|
116
|
+
**Ties are a display concern only.** For analytical purposes, a pitch that sounds continuously from position A to position B is a single sounding event, regardless of how many notated note heads or tie symbols represent it.
|
|
117
|
+
|
|
118
|
+
These two notations are analytically identical in fourth species:
|
|
119
|
+
|
|
120
|
+
- A whole note C4 beginning at beat 3 of bar 1, sustained through beat 1 of bar 2.
|
|
121
|
+
- A half note C4 at beat 3 of bar 1, tied to a half note C4 at beat 1 of bar 2.
|
|
122
|
+
|
|
123
|
+
**Guidelines operate on sounding durations, not on notated note heads.**
|
|
124
|
+
|
|
125
|
+
In head_music, this principle is already encoded in the data model:
|
|
126
|
+
|
|
127
|
+
- A `Placement` represents a single sounding event with a `position` (when it starts) and a `rhythmic_value` (how long it lasts).
|
|
128
|
+
- `Placement#next_position` computes when the sound ends: `position + rhythmic_value`.
|
|
129
|
+
- The display layer (ties, beaming, notation symbols) is a separate concern handled by the `HeadMusic::Notation` module.
|
|
130
|
+
|
|
131
|
+
Analytical guidelines should never count note heads or detect ties in the notated score. Instead, they should query `voice.note_at(position)` for any given position to determine what is sounding.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## 7. Mapping to head_music Architecture
|
|
136
|
+
|
|
137
|
+
### Voice and Placement Model
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
Voice
|
|
141
|
+
#placements -> [Placement, ...] all sounding events (notes and rests)
|
|
142
|
+
#notes -> [Placement, ...] only pitched placements
|
|
143
|
+
#note_at(pos) -> Placement | nil what is sounding at a given Position
|
|
144
|
+
#note_preceding(pos) -> Placement | nil the note whose position is before pos
|
|
145
|
+
#note_following(pos) -> Placement | nil the note whose position is after pos
|
|
146
|
+
|
|
147
|
+
Placement
|
|
148
|
+
#position -> Position when the event begins
|
|
149
|
+
#rhythmic_value -> RhythmicValue how long it lasts
|
|
150
|
+
#next_position -> Position position + rhythmic_value (when it ends)
|
|
151
|
+
#pitch -> Pitch | nil nil for rests
|
|
152
|
+
#note? -> bool
|
|
153
|
+
#rest? -> bool
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Position
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
Position
|
|
160
|
+
#within_placement?(placement) -> bool true if self >= placement.position
|
|
161
|
+
AND self < placement.next_position
|
|
162
|
+
#strong? -> bool downbeat (strength >= 80)
|
|
163
|
+
#weak? -> bool not strong
|
|
164
|
+
#bar_number -> Integer
|
|
165
|
+
#count -> Integer beat within bar
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
`Position#within_placement?` is the key predicate for "is this pitch sounding at this moment?" It returns true for any position that falls during the placement's duration -- including the middle of a long note. This is the mechanism that makes sustained notes invisible to position-based queries.
|
|
169
|
+
|
|
170
|
+
### HarmonicInterval
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
HarmonicInterval.new(voice1, voice2, position)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Internally calls `voice.note_at(position)` for each voice. Because `note_at` uses `position.within_placement?`, it correctly finds notes that **began earlier and are still sounding**, not only notes that **start at** the given position.
|
|
177
|
+
|
|
178
|
+
This means `HarmonicInterval` already handles sustained pitches correctly. A suspended note that began on beat 3 and is still sounding on beat 1 of the next bar will be found and evaluated as a harmonic interval at beat 1 without any special casing in the guideline code.
|
|
179
|
+
|
|
180
|
+
### Identifying Suspension Phases in Guidelines
|
|
181
|
+
|
|
182
|
+
To analyze a suspension at a given downbeat position:
|
|
183
|
+
|
|
184
|
+
```ruby
|
|
185
|
+
# The suspended note: sounding at the downbeat but started earlier
|
|
186
|
+
suspended_note = counterpoint_voice.note_at(downbeat_position)
|
|
187
|
+
|
|
188
|
+
# It is a suspension only if it started before the downbeat
|
|
189
|
+
is_suspension = suspended_note && suspended_note.position < downbeat_position
|
|
190
|
+
|
|
191
|
+
# Preparation: the same placement, evaluated at the previous weak beat
|
|
192
|
+
# (suspended_note itself IS the preparation placement)
|
|
193
|
+
preparation_harmonic_interval = HarmonicInterval.new(
|
|
194
|
+
cantus_firmus, counterpoint_voice, suspended_note.position
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
# Suspension: harmonic interval at the downbeat
|
|
198
|
+
suspension_harmonic_interval = HarmonicInterval.new(
|
|
199
|
+
cantus_firmus, counterpoint_voice, downbeat_position
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
# Resolution: the note that follows the sustained note
|
|
203
|
+
resolution_note = counterpoint_voice.note_following(downbeat_position)
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Guidelines: Annotation and Mark
|
|
207
|
+
|
|
208
|
+
Guidelines inherit from `HeadMusic::Style::Annotation` and override the `marks` method, returning an array of `HeadMusic::Style::Mark` objects.
|
|
209
|
+
|
|
210
|
+
```ruby
|
|
211
|
+
class MyGuideline < HeadMusic::Style::Annotation
|
|
212
|
+
MESSAGE = "Description of the rule."
|
|
213
|
+
|
|
214
|
+
def marks
|
|
215
|
+
# Collect placements that violate the rule.
|
|
216
|
+
# Return Mark objects for each violation.
|
|
217
|
+
violating_placements.map do |placement|
|
|
218
|
+
HeadMusic::Style::Mark.for(placement, fitness: 0)
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
`Mark.for(placement)` creates a mark spanning `placement.position` to `placement.next_position`. `Mark.for_all(placements)` creates a single mark spanning a group. `Mark.for_each(placements)` creates one mark per placement.
|
|
225
|
+
|
|
226
|
+
Fitness of `0` signals a hard violation (forbidden). The default `HeadMusic::PENALTY_FACTOR` is used for soft violations (discouraged but not forbidden).
|
|
227
|
+
|
|
228
|
+
### Guideline Classification for Fourth Species
|
|
229
|
+
|
|
230
|
+
**Hard violations (fitness: 0):**
|
|
231
|
+
- Suspension not prepared as a consonance (preparation must be consonant).
|
|
232
|
+
- Suspension resolution not by step downward.
|
|
233
|
+
- Suspension resolution not to a consonance.
|
|
234
|
+
- Dissonance on a downbeat that is not a valid suspension.
|
|
235
|
+
- Off-beat dissonance in a second-species break that is not a passing tone (approached and left by step in same direction).
|
|
236
|
+
|
|
237
|
+
**Soft penalties (PENALTY_FACTOR):**
|
|
238
|
+
- Failure to use suspensions where they are available (too many direct consonances reduces interest).
|
|
239
|
+
- Resolution to a perfect consonance (prefer imperfect).
|
|
240
|
+
- Parallel perfect consonances on successive downbeats.
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## 8. Sources
|
|
245
|
+
|
|
246
|
+
- Fux, J.J. *Gradus ad Parnassum* (1725). Trans. Alfred Mann, *The Study of Counterpoint* (W.W. Norton, 1965).
|
|
247
|
+
- Schenker, H. *Kontrapunkt* (1910/1922). Trans. Rothgeb & Thym (Musicalia Press, 2001).
|
|
248
|
+
- Salzer, F. & Schachter, C. *Counterpoint in Composition* (Columbia UP, 1969).
|
|
249
|
+
- Schubert, P. *Modal Counterpoint: Renaissance Style*, 2nd ed. (Oxford UP, 2008).
|
|
250
|
+
- [Open Music Theory -- Fourth-Species Counterpoint](https://viva.pressbooks.pub/openmusictheory/chapter/fourth-species-counterpoint/)
|
|
251
|
+
- [Puget Sound Music Theory -- Fourth Species](https://musictheory.pugetsound.edu/mt21c/FourthSpecies.html)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: head_music
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 12.
|
|
4
|
+
version: 12.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Rob Head
|
|
@@ -291,6 +291,7 @@ files:
|
|
|
291
291
|
- lib/head_music/style/guidelines/mostly_conjunct.rb
|
|
292
292
|
- lib/head_music/style/guidelines/no_parallel_perfect_across_barline.rb
|
|
293
293
|
- lib/head_music/style/guidelines/no_parallel_perfect_on_downbeats.rb
|
|
294
|
+
- lib/head_music/style/guidelines/no_parallel_perfect_with_syncopation.rb
|
|
294
295
|
- lib/head_music/style/guidelines/no_rests.rb
|
|
295
296
|
- lib/head_music/style/guidelines/no_strong_beat_unisons.rb
|
|
296
297
|
- lib/head_music/style/guidelines/no_unisons_in_middle.rb
|
|
@@ -299,10 +300,12 @@ files:
|
|
|
299
300
|
- lib/head_music/style/guidelines/notes_same_length.rb
|
|
300
301
|
- lib/head_music/style/guidelines/one_per_bar.rb
|
|
301
302
|
- lib/head_music/style/guidelines/one_to_one.rb
|
|
303
|
+
- lib/head_music/style/guidelines/one_to_one_with_ties.rb
|
|
302
304
|
- lib/head_music/style/guidelines/prefer_contrary_motion.rb
|
|
303
305
|
- lib/head_music/style/guidelines/prefer_imperfect.rb
|
|
304
306
|
- lib/head_music/style/guidelines/prepare_octave_leaps.rb
|
|
305
307
|
- lib/head_music/style/guidelines/recover_large_leaps.rb
|
|
308
|
+
- lib/head_music/style/guidelines/second_species_break.rb
|
|
306
309
|
- lib/head_music/style/guidelines/singable_intervals.rb
|
|
307
310
|
- lib/head_music/style/guidelines/singable_range.rb
|
|
308
311
|
- lib/head_music/style/guidelines/single_large_leaps.rb
|
|
@@ -312,6 +315,7 @@ files:
|
|
|
312
315
|
- lib/head_music/style/guidelines/step_out_of_unison.rb
|
|
313
316
|
- lib/head_music/style/guidelines/step_to_final_note.rb
|
|
314
317
|
- lib/head_music/style/guidelines/step_up_to_final_note.rb
|
|
318
|
+
- lib/head_music/style/guidelines/suspension_treatment.rb
|
|
315
319
|
- lib/head_music/style/guidelines/third_species_dissonance_treatment.rb
|
|
316
320
|
- lib/head_music/style/guidelines/three_per_bar.rb
|
|
317
321
|
- lib/head_music/style/guidelines/triple_meter_dissonance_treatment.rb
|
|
@@ -320,6 +324,8 @@ files:
|
|
|
320
324
|
- lib/head_music/style/guidelines/weak_beat_dissonance_treatment.rb
|
|
321
325
|
- lib/head_music/style/guides/first_species_harmony.rb
|
|
322
326
|
- lib/head_music/style/guides/first_species_melody.rb
|
|
327
|
+
- lib/head_music/style/guides/fourth_species_harmony.rb
|
|
328
|
+
- lib/head_music/style/guides/fourth_species_melody.rb
|
|
323
329
|
- lib/head_music/style/guides/fux_cantus_firmus.rb
|
|
324
330
|
- lib/head_music/style/guides/modern_cantus_firmus.rb
|
|
325
331
|
- lib/head_music/style/guides/second_species_harmony.rb
|
|
@@ -348,6 +354,7 @@ files:
|
|
|
348
354
|
- lib/head_music/utilities/case.rb
|
|
349
355
|
- lib/head_music/utilities/hash_key.rb
|
|
350
356
|
- lib/head_music/version.rb
|
|
357
|
+
- references/fourth-species-counterpoint.md
|
|
351
358
|
- references/second-species-counterpoint.md
|
|
352
359
|
- references/third-species-counterpoint.md
|
|
353
360
|
- user_stories/backlog/notation-style.md
|