head_music 0.11.8 → 0.11.9
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/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
|