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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 94643e9fad9404dd51c148dd74a9deba4f0100b5
4
- data.tar.gz: 5509506687c478db25e7c38d64ea69994d46f1b2
3
+ metadata.gz: ff284893d21767573fa4d994b8500e229e067aa6
4
+ data.tar.gz: 5dc30f493cfd2b0774c6fb87399b7d237f164166
5
5
  SHA512:
6
- metadata.gz: 5e704efcbffd9738263488cebaaf3478dc8bdf7cae22f55875d681cdb7017d6bea510620f9db77a2e012947caab0677077853e0af818ea5d760302706a4c31d6
7
- data.tar.gz: 6d16156270a86e137083ba7376e4bbc486e9635dc11fd628cef10d453f84ea1280c8b3a2251e1d4efe6c484781295bc39c2db29f1b0e3d976d994a1e67ce6eaa
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
@@ -92,4 +92,8 @@ class HeadMusic::ScaleType
92
92
  def parent
93
93
  @parent ||= self.class.get(parent_name) if parent_name
94
94
  end
95
+
96
+ def diatonic?
97
+ intervals.length == 7
98
+ end
95
99
  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 ||= voices.map(&:notes).flatten.map(&:position).sort.uniq(&:to_s)
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 notes
9
- improper_climaxes = highest_notes.select.with_index do |note, i|
10
- tonic_pitch = HeadMusic::Pitch.get(tonic_spelling)
11
- interval = HeadMusic::FunctionalInterval.new(tonic_pitch, note.pitch)
12
- interval.consonance(:melodic).dissonant? || i > 0
13
- end
14
- HeadMusic::Style::Mark.for_each(improper_climaxes)
15
- end
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(notes_not_in_key)
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::AlwaysMove,
6
+ HeadMusic::Style::Annotations::ConsonantClimax,
7
7
  HeadMusic::Style::Annotations::Diatonic,
8
8
  HeadMusic::Style::Annotations::DirectionChanges,
9
9
  HeadMusic::Style::Annotations::EndOnTonic,
@@ -1,3 +1,3 @@
1
1
  module HeadMusic
2
- VERSION = "0.14.2"
2
+ VERSION = "0.14.4"
3
3
  end
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.2
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 00:00:00.000000000 Z
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