head_music 0.14.2 → 0.14.4
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/scale_degree.rb +51 -0
- data/lib/head_music/scale_type.rb +4 -0
- data/lib/head_music/style/annotation.rb +6 -1
- data/lib/head_music/style/annotations/consonant_climax.rb +49 -9
- data/lib/head_music/style/annotations/diatonic.rb +15 -1
- data/lib/head_music/style/annotations/singable_intervals.rb +3 -3
- data/lib/head_music/style/rulesets/cantus_firmus.rb +1 -0
- data/lib/head_music/style/rulesets/first_species_melody.rb +1 -1
- data/lib/head_music/version.rb +1 -1
- data/lib/head_music.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ff284893d21767573fa4d994b8500e229e067aa6
|
4
|
+
data.tar.gz: 5dc30f493cfd2b0774c6fb87399b7d237f164166
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9aca4f3ab9e889f03a33074f110c1fafc48dc4606e1b8c89ff2d4e8be54b9879cac792f3825c0c3edd0e7c805ae55866d10988e47afbb9ed9b8a1b91b43659cb
|
7
|
+
data.tar.gz: e0303cd214d229ad6d20988e2d98e988471f0494c0a56acd2c183b2709b509516b428e839c6580b43d2e699e89f13e3a0a3870f277562da1d3643390103b09f9
|
@@ -0,0 +1,51 @@
|
|
1
|
+
class HeadMusic::ScaleDegree
|
2
|
+
include Comparable
|
3
|
+
|
4
|
+
NAME_FOR_DIATONIC_DEGREE = [nil, 'tonic', 'supertonic', 'mediant', 'subdominant', 'dominant', 'submediant']
|
5
|
+
|
6
|
+
attr_reader :key_signature, :spelling
|
7
|
+
delegate :scale, to: :key_signature
|
8
|
+
delegate :scale_type, to: :scale
|
9
|
+
|
10
|
+
def initialize(key_signature, spelling)
|
11
|
+
@key_signature = key_signature
|
12
|
+
@spelling = Spelling.get(spelling)
|
13
|
+
end
|
14
|
+
|
15
|
+
def degree
|
16
|
+
scale.letter_name_cycle.index(spelling.letter_name.to_s) + 1
|
17
|
+
end
|
18
|
+
|
19
|
+
def accidental
|
20
|
+
spelling.accidental
|
21
|
+
scale_degree_usual_spelling.accidental
|
22
|
+
accidental_semitones = spelling.accidental && spelling.accidental.semitones || 0
|
23
|
+
usual_accidental_semitones = scale_degree_usual_spelling.accidental && scale_degree_usual_spelling.accidental.semitones || 0
|
24
|
+
Accidental.for_interval(accidental_semitones - usual_accidental_semitones)
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
"#{accidental}#{degree}"
|
29
|
+
end
|
30
|
+
|
31
|
+
def <=>(other)
|
32
|
+
if other.is_a?(HeadMusic::ScaleDegree)
|
33
|
+
[degree, accidental.semitones] <=> [other.degree, other.accidental.semitones]
|
34
|
+
else
|
35
|
+
to_s <=> other.to_s
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def name_for_degree
|
40
|
+
if scale_type.diatonic?
|
41
|
+
NAME_FOR_DIATONIC_DEGREE[degree] ||
|
42
|
+
(scale_type.intervals.last == 1 || accidental == '#' ? 'leading tone' : 'subtonic')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def scale_degree_usual_spelling
|
49
|
+
Spelling.get(scale.spellings[degree - 1])
|
50
|
+
end
|
51
|
+
end
|
@@ -33,6 +33,10 @@ class HeadMusic::Style::Annotation
|
|
33
33
|
fitness == 1
|
34
34
|
end
|
35
35
|
|
36
|
+
def notes?
|
37
|
+
first_note
|
38
|
+
end
|
39
|
+
|
36
40
|
def start_position
|
37
41
|
[marks].flatten.compact.map(&:start_position).sort.first
|
38
42
|
end
|
@@ -113,7 +117,8 @@ class HeadMusic::Style::Annotation
|
|
113
117
|
end
|
114
118
|
|
115
119
|
def positions
|
116
|
-
@positions ||=
|
120
|
+
@positions ||=
|
121
|
+
voices.map(&:notes).flatten.map(&:position).sort.uniq(&:to_s)
|
117
122
|
end
|
118
123
|
|
119
124
|
def unsorted_higher_voices
|
@@ -2,16 +2,56 @@ module HeadMusic::Style::Annotations
|
|
2
2
|
end
|
3
3
|
|
4
4
|
class HeadMusic::Style::Annotations::ConsonantClimax < HeadMusic::Style::Annotation
|
5
|
-
MESSAGE = "Peak on a consonant high note one time."
|
5
|
+
MESSAGE = "Peak on a consonant high note one time or twice with a step between."
|
6
6
|
|
7
7
|
def marks
|
8
|
-
if
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
8
|
+
HeadMusic::Style::Mark.for_each(highest_notes) if !adherent_climax?
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def adherent_climax?
|
14
|
+
notes? && highest_pitch_consonant_with_tonic? &&
|
15
|
+
( highest_pitch_appears_once? || highest_pitch_appears_twice_with_step_between? )
|
16
|
+
end
|
17
|
+
|
18
|
+
def highest_pitch_consonant_with_tonic?
|
19
|
+
functional_interval_to_highest_pitch.consonance?(:melodic)
|
20
|
+
end
|
21
|
+
|
22
|
+
def functional_interval_to_highest_pitch
|
23
|
+
@functional_interval_to_highest_pitch ||=
|
24
|
+
HeadMusic::FunctionalInterval.new(tonic_pitch, highest_pitch)
|
25
|
+
end
|
26
|
+
|
27
|
+
def tonic_pitch
|
28
|
+
@tonic_pitch ||= HeadMusic::Pitch.get(tonic_spelling)
|
29
|
+
end
|
30
|
+
|
31
|
+
def highest_pitch_appears_once?
|
32
|
+
highest_notes.length == 1
|
33
|
+
end
|
34
|
+
|
35
|
+
def highest_pitch_appears_twice_with_step_between?
|
36
|
+
highest_pitch_appears_twice? &&
|
37
|
+
single_note_between_highest_notes? &&
|
38
|
+
step_between_highest_notes?
|
39
|
+
end
|
40
|
+
|
41
|
+
def highest_pitch_appears_twice?
|
42
|
+
highest_notes.length == 2
|
43
|
+
end
|
44
|
+
|
45
|
+
def step_between_highest_notes?
|
46
|
+
MelodicInterval.new(voice, highest_notes.first, notes_between_highest_notes.first).step?
|
47
|
+
end
|
48
|
+
|
49
|
+
def single_note_between_highest_notes?
|
50
|
+
notes_between_highest_notes.length == 1
|
51
|
+
end
|
52
|
+
|
53
|
+
def notes_between_highest_notes
|
54
|
+
indexes = highest_notes.map { |note| notes.index(note) }
|
55
|
+
notes[(indexes.first + 1)..(indexes.last - 1)] || []
|
16
56
|
end
|
17
57
|
end
|
@@ -5,6 +5,20 @@ class HeadMusic::Style::Annotations::Diatonic < HeadMusic::Style::Annotation
|
|
5
5
|
MESSAGE = "Use only notes in the key signature."
|
6
6
|
|
7
7
|
def marks
|
8
|
-
HeadMusic::Style::Mark.for_each(
|
8
|
+
HeadMusic::Style::Mark.for_each(notes_not_in_key_excluding_penultimate_leading_tone)
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def notes_not_in_key_excluding_penultimate_leading_tone
|
14
|
+
notes_not_in_key.reject do |note|
|
15
|
+
penultimate_note &&
|
16
|
+
note == penultimate_note &&
|
17
|
+
HeadMusic::ScaleDegree.new(key_signature, note.pitch.spelling).accidental == '#'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def penultimate_note
|
22
|
+
voice.note_preceding(positions.last) if positions.last
|
9
23
|
end
|
10
24
|
end
|
@@ -2,10 +2,10 @@ module HeadMusic::Style::Annotations
|
|
2
2
|
end
|
3
3
|
|
4
4
|
class HeadMusic::Style::Annotations::SingableIntervals < HeadMusic::Style::Annotation
|
5
|
-
PERMITTED_ASCENDING = %w[m2 M2 m3 M3 P4 P5 m6 P8]
|
6
|
-
PERMITTED_DESCENDING = %w[m2 M2 m3 M3 P4 P5 P8]
|
5
|
+
PERMITTED_ASCENDING = %w[PU m2 M2 m3 M3 P4 P5 m6 P8]
|
6
|
+
PERMITTED_DESCENDING = %w[PU m2 M2 m3 M3 P4 P5 P8]
|
7
7
|
|
8
|
-
MESSAGE = "Use only m2, M2, m3, M3, P4, P5, m6 (ascending), P8."
|
8
|
+
MESSAGE = "Use only PU, m2, M2, m3, M3, P4, P5, m6 (ascending), P8 in the melodic line."
|
9
9
|
|
10
10
|
def marks
|
11
11
|
melodic_intervals.reject { |interval| permitted?(interval) }.map do |unpermitted_interval|
|
@@ -9,6 +9,7 @@ class HeadMusic::Style::Rulesets::CantusFirmus
|
|
9
9
|
HeadMusic::Style::Annotations::Diatonic,
|
10
10
|
HeadMusic::Style::Annotations::DirectionChanges,
|
11
11
|
HeadMusic::Style::Annotations::EndOnTonic,
|
12
|
+
HeadMusic::Style::Annotations::LimitOctaveLeaps,
|
12
13
|
HeadMusic::Style::Annotations::MostlyConjunct,
|
13
14
|
HeadMusic::Style::Annotations::NoRests,
|
14
15
|
HeadMusic::Style::Annotations::NotesSameLength,
|
@@ -3,7 +3,7 @@ end
|
|
3
3
|
|
4
4
|
class HeadMusic::Style::Rulesets::FirstSpeciesMelody
|
5
5
|
RULESET = [
|
6
|
-
HeadMusic::Style::Annotations::
|
6
|
+
HeadMusic::Style::Annotations::ConsonantClimax,
|
7
7
|
HeadMusic::Style::Annotations::Diatonic,
|
8
8
|
HeadMusic::Style::Annotations::DirectionChanges,
|
9
9
|
HeadMusic::Style::Annotations::EndOnTonic,
|
data/lib/head_music/version.rb
CHANGED
data/lib/head_music.rb
CHANGED
@@ -34,6 +34,7 @@ require 'head_music/rhythm'
|
|
34
34
|
require 'head_music/rhythmic_unit'
|
35
35
|
require 'head_music/rhythmic_value'
|
36
36
|
require 'head_music/scale'
|
37
|
+
require 'head_music/scale_degree'
|
37
38
|
require 'head_music/scale_type'
|
38
39
|
require 'head_music/spelling'
|
39
40
|
require 'head_music/staff'
|
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.14.
|
4
|
+
version: 0.14.4
|
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-06-
|
11
|
+
date: 2017-06-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -145,6 +145,7 @@ files:
|
|
145
145
|
- lib/head_music/rhythmic_unit.rb
|
146
146
|
- lib/head_music/rhythmic_value.rb
|
147
147
|
- lib/head_music/scale.rb
|
148
|
+
- lib/head_music/scale_degree.rb
|
148
149
|
- lib/head_music/scale_type.rb
|
149
150
|
- lib/head_music/spelling.rb
|
150
151
|
- lib/head_music/staff.rb
|