head_music 0.14.2 → 0.14.4
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/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
|