head_music 0.11.8 → 0.11.9
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/lib/head_music/composition.rb +2 -1
- data/lib/head_music/functional_interval.rb +16 -0
- data/lib/head_music/style/annotations/end_on_perfect_consonance.rb +29 -0
- data/lib/head_music/style/annotations/one_to_one.rb +32 -0
- data/lib/head_music/style/annotations/{permitted_intervals.rb → singable_intervals.rb} +1 -1
- data/lib/head_music/style/annotations/start_on_perfect_consonance.rb +29 -0
- data/lib/head_music/style/annotations/step_up_to_final_note.rb +33 -0
- data/lib/head_music/style/mark.rb +2 -1
- data/lib/head_music/style/rulesets/cantus_firmus.rb +1 -1
- data/lib/head_music/style/rulesets/first_species_harmony.rb +12 -0
- data/lib/head_music/style/rulesets/first_species_melody.rb +17 -0
- data/lib/head_music/version.rb +1 -1
- data/lib/head_music/voice.rb +4 -0
- data/lib/head_music.rb +7 -1
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a0baef52f11f8aab0c4becbe31e70928b358499
|
4
|
+
data.tar.gz: 47ae1ef06c1e25a2abd0559eb40a20494c47eed3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 07e0d608df0d6e5901e652c8796b84ef99c2987c94a346347efff6c73d0b159352d0813eaa2a932530fe7f5e1613548c96ca9c2dbcc2b486ef4978d4e6509235
|
7
|
+
data.tar.gz: 1dd90a0197c26138b56b109de9dae548c5b9d4b6f4f382b13da80b2fa1093d006c197e26523ff4f2bacb5863bee878d36037049857e8c2655bb3fd61125ec7ae
|
@@ -3,6 +3,7 @@ class HeadMusic::Composition
|
|
3
3
|
|
4
4
|
def initialize(name: nil, key_signature: nil, meter: nil)
|
5
5
|
ensure_attributes(name, key_signature, meter)
|
6
|
+
@voices = []
|
6
7
|
end
|
7
8
|
|
8
9
|
def add_bar
|
@@ -17,8 +18,8 @@ class HeadMusic::Composition
|
|
17
18
|
end
|
18
19
|
|
19
20
|
def add_voice(role: nil)
|
20
|
-
@voices ||= []
|
21
21
|
@voices << HeadMusic::Voice.new(composition: self, role: role)
|
22
|
+
@voices.last
|
22
23
|
end
|
23
24
|
|
24
25
|
private
|
@@ -178,6 +178,22 @@ class HeadMusic::FunctionalInterval
|
|
178
178
|
end
|
179
179
|
end
|
180
180
|
|
181
|
+
def consonance?(style = :standard_practice)
|
182
|
+
consonance(style).perfect? || consonance(style).imperfect?
|
183
|
+
end
|
184
|
+
|
185
|
+
def perfect_consonance?(style = :standard_practice)
|
186
|
+
consonance(style).perfect?
|
187
|
+
end
|
188
|
+
|
189
|
+
def imperfect_consonance?(style = :standard_practice)
|
190
|
+
consonance(style).imperfect?
|
191
|
+
end
|
192
|
+
|
193
|
+
def dissonance?(style = :standard_practice)
|
194
|
+
consonance(style).dissonant?
|
195
|
+
end
|
196
|
+
|
181
197
|
def step?
|
182
198
|
number <= 2
|
183
199
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module HeadMusic::Style::Annotations
|
2
|
+
end
|
3
|
+
|
4
|
+
# marks the voice if the first note is not the first or fifth scale degree of the key.
|
5
|
+
class HeadMusic::Style::Annotations::EndOnPerfectConsonance < HeadMusic::Style::Annotation
|
6
|
+
def message
|
7
|
+
'End on the tonic or a perfect consonance above the tonic.'
|
8
|
+
end
|
9
|
+
|
10
|
+
def marks
|
11
|
+
if last_note && !ends_on_perfect_consonance?
|
12
|
+
HeadMusic::Style::Mark.for(last_note)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def ends_on_perfect_consonance?
|
19
|
+
functional_interval.perfect_consonance?(:two_part_harmony)
|
20
|
+
end
|
21
|
+
|
22
|
+
def functional_interval
|
23
|
+
HeadMusic::FunctionalInterval.new(composition.key_signature.tonic_spelling, last_note.spelling)
|
24
|
+
end
|
25
|
+
|
26
|
+
def last_note
|
27
|
+
notes.last
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module HeadMusic::Style::Annotations
|
2
|
+
end
|
3
|
+
|
4
|
+
class HeadMusic::Style::Annotations::OneToOne < HeadMusic::Style::Annotation
|
5
|
+
def message
|
6
|
+
'Place a note for each note in the other voice.'
|
7
|
+
end
|
8
|
+
|
9
|
+
def marks
|
10
|
+
if other_voice && other_voice.notes.length > 0
|
11
|
+
HeadMusic::Style::Mark.for_each(
|
12
|
+
notes_without_match(voice, other_voice) + notes_without_match(other_voice, voice)
|
13
|
+
)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def notes_without_match(voice1, voice2)
|
20
|
+
voice1.notes.reject do |voice1_note|
|
21
|
+
voice2.notes.map(&:position).include?(voice1_note.position)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def other_voice
|
26
|
+
other_voices.detect(&:cantus_firmus?) || other_voices.first
|
27
|
+
end
|
28
|
+
|
29
|
+
def other_voices
|
30
|
+
@other_voices ||= voice.composition.voices.select { |part| part != voice }
|
31
|
+
end
|
32
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module HeadMusic::Style::Annotations
|
2
2
|
end
|
3
3
|
|
4
|
-
class HeadMusic::Style::Annotations::
|
4
|
+
class HeadMusic::Style::Annotations::SingableIntervals < HeadMusic::Style::Annotation
|
5
5
|
PERMITTED_ASCENDING = %w[m2 M2 m3 M3 P4 P5 m6 P8]
|
6
6
|
PERMITTED_DESCENDING = %w[m2 M2 m3 M3 P4 P5 P8]
|
7
7
|
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module HeadMusic::Style::Annotations
|
2
|
+
end
|
3
|
+
|
4
|
+
# marks the voice if the first note is not the first or fifth scale degree of the key.
|
5
|
+
class HeadMusic::Style::Annotations::StartOnPerfectConsonance < HeadMusic::Style::Annotation
|
6
|
+
def message
|
7
|
+
'Start on the tonic or a perfect consonance above the tonic.'
|
8
|
+
end
|
9
|
+
|
10
|
+
def marks
|
11
|
+
if first_note && !starts_on_perfect_consonance?
|
12
|
+
HeadMusic::Style::Mark.for(first_note)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def starts_on_perfect_consonance?
|
19
|
+
functional_interval.perfect_consonance?(:two_part_harmony)
|
20
|
+
end
|
21
|
+
|
22
|
+
def functional_interval
|
23
|
+
HeadMusic::FunctionalInterval.new(composition.key_signature.tonic_spelling, first_note.spelling)
|
24
|
+
end
|
25
|
+
|
26
|
+
def first_note
|
27
|
+
notes.first
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module HeadMusic::Style::Annotations
|
2
|
+
end
|
3
|
+
|
4
|
+
class HeadMusic::Style::Annotations::StepUpToFinalNote < HeadMusic::Style::Annotation
|
5
|
+
def message
|
6
|
+
'Step up to final note.'
|
7
|
+
end
|
8
|
+
|
9
|
+
def marks
|
10
|
+
if !last_melodic_interval.nil?
|
11
|
+
fitness = 1
|
12
|
+
fitness *= HeadMusic::PENALTY_FACTOR unless step?
|
13
|
+
fitness *= HeadMusic::PENALTY_FACTOR unless ascending?
|
14
|
+
if fitness < 1
|
15
|
+
HeadMusic::Style::Mark.for_all(notes[-2..-1], fitness: fitness)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def ascending?
|
23
|
+
last_melodic_interval && last_melodic_interval.ascending?
|
24
|
+
end
|
25
|
+
|
26
|
+
def step?
|
27
|
+
last_melodic_interval && last_melodic_interval.step?
|
28
|
+
end
|
29
|
+
|
30
|
+
def last_melodic_interval
|
31
|
+
@last_melodic_interval ||= melodic_intervals.last
|
32
|
+
end
|
33
|
+
end
|
@@ -6,7 +6,8 @@ class HeadMusic::Style::Mark
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def self.for_all(placements, fitness: nil)
|
9
|
-
placements = [placements].flatten
|
9
|
+
placements = [placements].flatten.compact
|
10
|
+
return [] if placements.length == 0
|
10
11
|
start_position = placements.map { |placement| placement.position }.sort.first
|
11
12
|
end_position = placements.map { |placement| placement.next_position }.sort.last
|
12
13
|
new(start_position, end_position, placements: placements, fitness: fitness)
|
@@ -13,7 +13,7 @@ class HeadMusic::Style::Rulesets::CantusFirmus
|
|
13
13
|
HeadMusic::Style::Annotations::MostlyConjunct,
|
14
14
|
HeadMusic::Style::Annotations::NoRests,
|
15
15
|
HeadMusic::Style::Annotations::NotesSameLength,
|
16
|
-
HeadMusic::Style::Annotations::
|
16
|
+
HeadMusic::Style::Annotations::SingableIntervals,
|
17
17
|
HeadMusic::Style::Annotations::RecoverLargeLeaps,
|
18
18
|
HeadMusic::Style::Annotations::StartOnTonic,
|
19
19
|
HeadMusic::Style::Annotations::StepDownToFinalNote,
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module HeadMusic::Style::Rulesets
|
2
|
+
end
|
3
|
+
|
4
|
+
class HeadMusic::Style::Rulesets::FirstSpeciesHarmony
|
5
|
+
RULESET = [
|
6
|
+
# HeadMusic::Style::Annotations::ConsonantOnStrongBeats,
|
7
|
+
]
|
8
|
+
|
9
|
+
def self.analyze(voice)
|
10
|
+
RULESET.map { |rule| rule.new(voice) }
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module HeadMusic::Style::Rulesets
|
2
|
+
end
|
3
|
+
|
4
|
+
class HeadMusic::Style::Rulesets::FirstSpeciesMelody
|
5
|
+
RULESET = [
|
6
|
+
HeadMusic::Style::Annotations::OneToOne,
|
7
|
+
HeadMusic::Style::Annotations::NotesSameLength,
|
8
|
+
HeadMusic::Style::Annotations::SingableIntervals,
|
9
|
+
HeadMusic::Style::Annotations::StartOnPerfectConsonance,
|
10
|
+
HeadMusic::Style::Annotations::EndOnPerfectConsonance,
|
11
|
+
HeadMusic::Style::Annotations::StepUpToFinalNote,
|
12
|
+
]
|
13
|
+
|
14
|
+
def self.analyze(voice)
|
15
|
+
RULESET.map { |rule| rule.new(voice) }
|
16
|
+
end
|
17
|
+
end
|
data/lib/head_music/version.rb
CHANGED
data/lib/head_music/voice.rb
CHANGED
data/lib/head_music.rb
CHANGED
@@ -43,18 +43,24 @@ require 'head_music/style/annotations/at_least_eight_notes'
|
|
43
43
|
require 'head_music/style/annotations/consonant_climax'
|
44
44
|
require 'head_music/style/annotations/diatonic'
|
45
45
|
require 'head_music/style/annotations/direction_changes'
|
46
|
+
require 'head_music/style/annotations/end_on_perfect_consonance'
|
46
47
|
require 'head_music/style/annotations/end_on_tonic'
|
47
48
|
require 'head_music/style/annotations/limit_range'
|
48
49
|
require 'head_music/style/annotations/mostly_conjunct'
|
49
50
|
require 'head_music/style/annotations/no_rests'
|
50
51
|
require 'head_music/style/annotations/notes_same_length'
|
51
|
-
require 'head_music/style/annotations/
|
52
|
+
require 'head_music/style/annotations/one_to_one'
|
52
53
|
require 'head_music/style/annotations/recover_large_leaps'
|
54
|
+
require 'head_music/style/annotations/singable_intervals'
|
55
|
+
require 'head_music/style/annotations/start_on_perfect_consonance'
|
53
56
|
require 'head_music/style/annotations/start_on_tonic'
|
54
57
|
require 'head_music/style/annotations/step_down_to_final_note'
|
58
|
+
require 'head_music/style/annotations/step_up_to_final_note'
|
55
59
|
require 'head_music/style/annotations/up_to_thirteen_notes'
|
56
60
|
|
57
61
|
require 'head_music/style/rulesets/cantus_firmus'
|
62
|
+
require 'head_music/style/rulesets/first_species_melody'
|
63
|
+
require 'head_music/style/rulesets/first_species_harmony'
|
58
64
|
|
59
65
|
require 'head_music/utilities/hash_key'
|
60
66
|
require 'head_music/voice'
|
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: 0.11.
|
4
|
+
version: 0.11.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rob Head
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-06-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -152,18 +152,24 @@ files:
|
|
152
152
|
- lib/head_music/style/annotations/consonant_climax.rb
|
153
153
|
- lib/head_music/style/annotations/diatonic.rb
|
154
154
|
- lib/head_music/style/annotations/direction_changes.rb
|
155
|
+
- lib/head_music/style/annotations/end_on_perfect_consonance.rb
|
155
156
|
- lib/head_music/style/annotations/end_on_tonic.rb
|
156
157
|
- lib/head_music/style/annotations/limit_range.rb
|
157
158
|
- lib/head_music/style/annotations/mostly_conjunct.rb
|
158
159
|
- lib/head_music/style/annotations/no_rests.rb
|
159
160
|
- lib/head_music/style/annotations/notes_same_length.rb
|
160
|
-
- lib/head_music/style/annotations/
|
161
|
+
- lib/head_music/style/annotations/one_to_one.rb
|
161
162
|
- lib/head_music/style/annotations/recover_large_leaps.rb
|
163
|
+
- lib/head_music/style/annotations/singable_intervals.rb
|
164
|
+
- lib/head_music/style/annotations/start_on_perfect_consonance.rb
|
162
165
|
- lib/head_music/style/annotations/start_on_tonic.rb
|
163
166
|
- lib/head_music/style/annotations/step_down_to_final_note.rb
|
167
|
+
- lib/head_music/style/annotations/step_up_to_final_note.rb
|
164
168
|
- lib/head_music/style/annotations/up_to_thirteen_notes.rb
|
165
169
|
- lib/head_music/style/mark.rb
|
166
170
|
- lib/head_music/style/rulesets/cantus_firmus.rb
|
171
|
+
- lib/head_music/style/rulesets/first_species_harmony.rb
|
172
|
+
- lib/head_music/style/rulesets/first_species_melody.rb
|
167
173
|
- lib/head_music/utilities/hash_key.rb
|
168
174
|
- lib/head_music/version.rb
|
169
175
|
- lib/head_music/voice.rb
|