head_music 0.13.2 → 0.14.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/head_music/bar.rb +1 -1
- data/lib/head_music/composition.rb +1 -1
- data/lib/head_music/harmonic_interval.rb +12 -2
- data/lib/head_music/melodic_interval.rb +1 -1
- data/lib/head_music/rhythmic_value.rb +2 -2
- data/lib/head_music/style/annotation.rb +17 -6
- data/lib/head_music/style/annotations/always_move.rb +1 -1
- data/lib/head_music/style/annotations/approach_perfection_contrarily.rb +1 -1
- data/lib/head_music/style/annotations/avoid_crossing_voices.rb +14 -22
- data/lib/head_music/style/annotations/avoid_overlapping_voices.rb +3 -3
- data/lib/head_music/style/annotations/consonant_downbeats.rb +1 -1
- data/lib/head_music/style/annotations/mostly_conjunct.rb +1 -1
- data/lib/head_music/style/annotations/no_unisons_in_middle.rb +2 -15
- data/lib/head_music/style/annotations/prefer_imperfect.rb +1 -1
- data/lib/head_music/style/annotations/recover_large_leaps.rb +1 -1
- data/lib/head_music/style/annotations/step_out_of_unison.rb +8 -15
- data/lib/head_music/version.rb +1 -1
- data/lib/head_music/voice.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1f987ca8cb9cdf11cc5f3f44385480ff343c899b
|
4
|
+
data.tar.gz: 261effd060be6679e7c9c766c49c16f1d5fa3c43
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b0cd2013873788e4e520d8f8cc0495658903e8eefe54560d27ee7edc2bf0243f70745fe73c288dddd5fd54819e75594723bde4f683d22e73a7cd101fe29b5a7
|
7
|
+
data.tar.gz: 916deb897a49e6dd40c6abdcc9fab8f1bd8cc0f1f06d5cb8bcd8227a1c84d443ab70ef42bc5d46712a5419cee05706821c5f6f0629a512c1856483cbfdb70cee
|
data/lib/head_music/bar.rb
CHANGED
@@ -24,7 +24,7 @@ class HeadMusic::Composition
|
|
24
24
|
def bars(last = latest_bar_number)
|
25
25
|
@bars ||= []
|
26
26
|
(earliest_bar_number..last).each do |bar_number|
|
27
|
-
@bars[bar_number] ||= Bar.new(self)
|
27
|
+
@bars[bar_number] ||= HeadMusic::Bar.new(self)
|
28
28
|
end
|
29
29
|
@bars[earliest_bar_number..last]
|
30
30
|
end
|
@@ -12,11 +12,11 @@ class HeadMusic::HarmonicInterval
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def voices
|
15
|
-
[voice1, voice2].
|
15
|
+
[voice1, voice2].compact
|
16
16
|
end
|
17
17
|
|
18
18
|
def notes
|
19
|
-
@notes ||= voices.map { |voice| voice.note_at(position) }.
|
19
|
+
@notes ||= voices.map { |voice| voice.note_at(position) }.compact.sort_by(&:pitch)
|
20
20
|
end
|
21
21
|
|
22
22
|
def lower_note
|
@@ -39,6 +39,16 @@ class HeadMusic::HarmonicInterval
|
|
39
39
|
pitches.last
|
40
40
|
end
|
41
41
|
|
42
|
+
def pitch_orientation
|
43
|
+
if lower_pitch < upper_pitch
|
44
|
+
if lower_note.voice == voice1
|
45
|
+
:up
|
46
|
+
elsif lower_note.voice == voice2
|
47
|
+
:down
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
42
52
|
def to_s
|
43
53
|
"#{functional_interval} at #{position}"
|
44
54
|
end
|
@@ -8,7 +8,7 @@ class HeadMusic::MelodicInterval
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def functional_interval
|
11
|
-
@functional_interval ||= HeadMusic::FunctionalInterval.new(
|
11
|
+
@functional_interval ||= HeadMusic::FunctionalInterval.new(first_pitch, second_pitch)
|
12
12
|
end
|
13
13
|
|
14
14
|
def position_start
|
@@ -79,12 +79,12 @@ class HeadMusic::RhythmicValue
|
|
79
79
|
end
|
80
80
|
|
81
81
|
def single_value_name
|
82
|
-
[name_modifier_prefix, unit_name].
|
82
|
+
[name_modifier_prefix, unit_name].compact.join(' ')
|
83
83
|
end
|
84
84
|
|
85
85
|
def name
|
86
86
|
if tied_value
|
87
|
-
[single_value_name, tied_value.name].
|
87
|
+
[single_value_name, tied_value.name].compact.join(' tied to ')
|
88
88
|
else
|
89
89
|
single_value_name
|
90
90
|
end
|
@@ -49,6 +49,8 @@ class HeadMusic::Style::Annotation
|
|
49
49
|
self.class::MESSAGE
|
50
50
|
end
|
51
51
|
|
52
|
+
protected
|
53
|
+
|
52
54
|
def first_note
|
53
55
|
notes && notes.first
|
54
56
|
end
|
@@ -93,17 +95,26 @@ class HeadMusic::Style::Annotation
|
|
93
95
|
downbeat_harmonic_intervals.map.with_index do |harmonic_interval, i|
|
94
96
|
next_harmonic_interval = downbeat_harmonic_intervals[i+1]
|
95
97
|
HeadMusic::Motion.new(harmonic_interval, next_harmonic_interval) if next_harmonic_interval
|
96
|
-
end.
|
98
|
+
end.compact
|
97
99
|
end
|
98
100
|
|
99
101
|
def downbeat_harmonic_intervals
|
100
|
-
@downbeat_harmonic_intervals ||=
|
101
|
-
|
102
|
-
|
103
|
-
|
102
|
+
@downbeat_harmonic_intervals ||=
|
103
|
+
cantus_firmus.notes.map { |cantus_firmus_note|
|
104
|
+
HeadMusic::HarmonicInterval.new(cantus_firmus_note.voice, voice, cantus_firmus_note.position)
|
105
|
+
}.reject { |interval| interval.notes.length < 2 }
|
106
|
+
end
|
107
|
+
|
108
|
+
def harmonic_intervals
|
109
|
+
@harmonic_intervals ||=
|
110
|
+
positions.map { |position|
|
111
|
+
HeadMusic::HarmonicInterval.new(cantus_firmus, voice, position)
|
112
|
+
}.reject { |harmonic_interval| harmonic_interval.notes.length < 2 }
|
104
113
|
end
|
105
114
|
|
106
|
-
|
115
|
+
def positions
|
116
|
+
@positions ||= voices.map(&:notes).flatten.map(&:position).sort.uniq(&:to_s)
|
117
|
+
end
|
107
118
|
|
108
119
|
def unsorted_higher_voices
|
109
120
|
other_voices.select { |part| part.highest_pitch > highest_pitch }
|
@@ -2,7 +2,7 @@ module HeadMusic::Style::Annotations
|
|
2
2
|
end
|
3
3
|
|
4
4
|
class HeadMusic::Style::Annotations::ApproachPerfectionContrarily < HeadMusic::Style::Annotation
|
5
|
-
MESSAGE = '
|
5
|
+
MESSAGE = 'Approach perfect consonances by contrary motion.'
|
6
6
|
|
7
7
|
def marks
|
8
8
|
motions_to_perfect_consonance_approached_directly.map do |bad_motion|
|
@@ -5,36 +5,28 @@ class HeadMusic::Style::Annotations::AvoidCrossingVoices < HeadMusic::Style::Ann
|
|
5
5
|
MESSAGE = "Avoid crossing voices."
|
6
6
|
|
7
7
|
def marks
|
8
|
-
crossings
|
8
|
+
crossings.map do |crossing|
|
9
|
+
HeadMusic::Style::Mark.for_all(crossing.notes)
|
10
|
+
end
|
9
11
|
end
|
10
12
|
|
11
13
|
private
|
12
14
|
|
13
15
|
def crossings
|
14
|
-
|
16
|
+
harmonic_intervals.select do |harmonic_interval|
|
17
|
+
harmonic_interval.pitch_orientation && harmonic_interval.pitch_orientation != predominant_pitch_orientation
|
18
|
+
end
|
15
19
|
end
|
16
20
|
|
17
|
-
def
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
marks << HeadMusic::Style::Mark.for_all(crossed_notes)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
21
|
+
def predominant_pitch_orientation
|
22
|
+
pitch_orientations
|
23
|
+
.compact
|
24
|
+
.group_by { |orientation| orientation }
|
25
|
+
.max { |a, b| a[1].length <=> b[1].length }
|
26
|
+
.first
|
27
27
|
end
|
28
28
|
|
29
|
-
def
|
30
|
-
|
31
|
-
higher_voices.each do |higher_voice|
|
32
|
-
higher_voice.notes.each do |higher_voice_note|
|
33
|
-
notes_during = voice.notes_during(higher_voice_note)
|
34
|
-
crossed_notes = notes_during.select { |note| note.pitch > lower_voice_note.pitch }
|
35
|
-
marks << HeadMusic::Style::Mark.for_all(crossed_notes)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
29
|
+
def pitch_orientations
|
30
|
+
harmonic_intervals.map(&:pitch_orientation).compact.uniq
|
39
31
|
end
|
40
32
|
end
|
@@ -24,7 +24,7 @@ class HeadMusic::Style::Annotations::AvoidOverlappingVoices < HeadMusic::Style::
|
|
24
24
|
end
|
25
25
|
marks << HeadMusic::Style::Mark.for_each(overlapped_notes)
|
26
26
|
end
|
27
|
-
end
|
27
|
+
end.flatten
|
28
28
|
end
|
29
29
|
|
30
30
|
def overlappings_of_higher_voices
|
@@ -32,11 +32,11 @@ class HeadMusic::Style::Annotations::AvoidOverlappingVoices < HeadMusic::Style::
|
|
32
32
|
higher_voices.each do |higher_voice|
|
33
33
|
overlapped_notes = voice.notes.select do |note|
|
34
34
|
preceding_note = higher_voice.note_preceding(note.position)
|
35
|
-
following_note =
|
35
|
+
following_note = higher_voice.note_following(note.position)
|
36
36
|
(preceding_note && preceding_note.pitch < note.pitch) || (following_note && following_note.pitch < note.pitch)
|
37
37
|
end
|
38
38
|
marks << HeadMusic::Style::Mark.for_each(overlapped_notes)
|
39
39
|
end
|
40
|
-
end
|
40
|
+
end.flatten
|
41
41
|
end
|
42
42
|
end
|
@@ -13,7 +13,7 @@ class HeadMusic::Style::Annotations::MostlyConjunct < HeadMusic::Style::Annotati
|
|
13
13
|
def marks_for_skips_and_leaps
|
14
14
|
melodic_intervals.map.with_index do |interval, i|
|
15
15
|
HeadMusic::Style::Mark.for_all(notes[i..i+1], fitness: HeadMusic::SMALL_PENALTY_FACTOR) unless interval.step?
|
16
|
-
end.
|
16
|
+
end.compact
|
17
17
|
end
|
18
18
|
|
19
19
|
def conjunct_ratio
|
@@ -13,7 +13,7 @@ class HeadMusic::Style::Annotations::NoUnisonsInMiddle < HeadMusic::Style::Annot
|
|
13
13
|
private
|
14
14
|
|
15
15
|
def unison_pairs
|
16
|
-
middle_unisons.map(&:notes).
|
16
|
+
middle_unisons.map(&:notes).compact
|
17
17
|
end
|
18
18
|
|
19
19
|
def middle_unisons
|
@@ -21,19 +21,6 @@ class HeadMusic::Style::Annotations::NoUnisonsInMiddle < HeadMusic::Style::Annot
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def middle_intervals
|
24
|
-
[harmonic_intervals[1..-2]].flatten.
|
25
|
-
end
|
26
|
-
|
27
|
-
def harmonic_intervals
|
28
|
-
cantus_firmus.notes.map do |cantus_firmus_note|
|
29
|
-
counterpoint_notes = voice.notes_during(cantus_firmus_note)
|
30
|
-
counterpoint_notes.map { |note|
|
31
|
-
HarmonicInterval.new(cantus_firmus_note.voice, voice, note.position)
|
32
|
-
}
|
33
|
-
end.flatten
|
34
|
-
end
|
35
|
-
|
36
|
-
def positions
|
37
|
-
voices.map(:notes).flatten.map(&:position).sort.uniq
|
24
|
+
[harmonic_intervals[1..-2]].flatten.compact
|
38
25
|
end
|
39
26
|
end
|
@@ -2,7 +2,7 @@ module HeadMusic::Style::Annotations
|
|
2
2
|
end
|
3
3
|
|
4
4
|
class HeadMusic::Style::Annotations::PreferImperfect < HeadMusic::Style::Annotation
|
5
|
-
MESSAGE = "
|
5
|
+
MESSAGE = "Prefer imperfect harmonic intervals."
|
6
6
|
|
7
7
|
def marks
|
8
8
|
if ratio_of_perfect_intervals > 0.5
|
@@ -14,7 +14,7 @@ class HeadMusic::Style::Annotations::RecoverLargeLeaps < HeadMusic::Style::Annot
|
|
14
14
|
if unrecovered_leap?(previous_interval, interval)
|
15
15
|
HeadMusic::Style::Mark.for_all((previous_interval.notes + interval.notes).uniq)
|
16
16
|
end
|
17
|
-
end.
|
17
|
+
end.compact
|
18
18
|
end
|
19
19
|
|
20
20
|
private
|
@@ -17,22 +17,15 @@ class HeadMusic::Style::Annotations::StepOutOfUnison < HeadMusic::Style::Annotat
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def melodic_intervals_following_unisons
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
@melodic_intervals_following_unisons ||=
|
21
|
+
perfect_unisons.map do |unison|
|
22
|
+
note1 = voice.note_at(unison.position)
|
23
|
+
note2 = voice.note_following(unison.position)
|
24
|
+
HeadMusic::MelodicInterval.new(voice, note1, note2) if note1 && note2
|
25
|
+
end.compact
|
25
26
|
end
|
26
27
|
|
27
|
-
def
|
28
|
-
harmonic_intervals.select
|
29
|
-
end
|
30
|
-
|
31
|
-
def harmonic_intervals
|
32
|
-
positions.map { |position| HarmonicInterval.new(cantus_firmus, voice, position) }
|
33
|
-
end
|
34
|
-
|
35
|
-
def positions
|
36
|
-
voices.map(&:notes).flatten.map(&:position).sort.uniq(&:to_s)
|
28
|
+
def perfect_unisons
|
29
|
+
@unisons ||= harmonic_intervals.select(&:perfect_consonance?).select(&:unison?)
|
37
30
|
end
|
38
31
|
end
|
data/lib/head_music/version.rb
CHANGED
data/lib/head_music/voice.rb
CHANGED