head_music 0.13.2 → 0.14.1

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: f1c8d179b57fdcf25212aa045104c235981b7618
4
- data.tar.gz: 324f02d328e7f94a35bcce3edf754d186f6f127b
3
+ metadata.gz: 1f987ca8cb9cdf11cc5f3f44385480ff343c899b
4
+ data.tar.gz: 261effd060be6679e7c9c766c49c16f1d5fa3c43
5
5
  SHA512:
6
- metadata.gz: 6d59e0eee2a58b5126bed466801d27bb2f64b93b736057a2802b9b17f133e67b9b60fdbc19aefbd974e5ea6872a9a50095f760b4677e5f31c5631734c47dd977
7
- data.tar.gz: 92548ec40646df28b54230aba80dba41bd9470024397b627658e8d27c2026ea6909992effc98d8959fd49e715f1b1750c532e8950edc06b363f4d692f1cb2339
6
+ metadata.gz: 6b0cd2013873788e4e520d8f8cc0495658903e8eefe54560d27ee7edc2bf0243f70745fe73c288dddd5fd54819e75594723bde4f683d22e73a7cd101fe29b5a7
7
+ data.tar.gz: 916deb897a49e6dd40c6abdcc9fab8f1bd8cc0f1f06d5cb8bcd8227a1c84d443ab70ef42bc5d46712a5419cee05706821c5f6f0629a512c1856483cbfdb70cee
@@ -11,6 +11,6 @@ class HeadMusic::Bar
11
11
  end
12
12
 
13
13
  def to_s
14
- ['Bar', key_signature, meter].reject(&:nil?).join(' ')
14
+ ['Bar', key_signature, meter].compact.join(' ')
15
15
  end
16
16
  end
@@ -24,7 +24,7 @@ class HeadMusic::Composition
24
24
  def bars(last = latest_bar_number)
25
25
  @bars ||= []
26
26
  (earliest_bar_number..last).each do |bar_number|
27
- @bars[bar_number] ||= Bar.new(self)
27
+ @bars[bar_number] ||= HeadMusic::Bar.new(self)
28
28
  end
29
29
  @bars[earliest_bar_number..last]
30
30
  end
@@ -12,11 +12,11 @@ class HeadMusic::HarmonicInterval
12
12
  end
13
13
 
14
14
  def voices
15
- [voice1, voice2].reject(&:nil?)
15
+ [voice1, voice2].compact
16
16
  end
17
17
 
18
18
  def notes
19
- @notes ||= voices.map { |voice| voice.note_at(position) }.reject(&:nil?).sort_by(&:pitch)
19
+ @notes ||= voices.map { |voice| voice.note_at(position) }.compact.sort_by(&:pitch)
20
20
  end
21
21
 
22
22
  def lower_note
@@ -39,6 +39,16 @@ class HeadMusic::HarmonicInterval
39
39
  pitches.last
40
40
  end
41
41
 
42
+ def pitch_orientation
43
+ if lower_pitch < upper_pitch
44
+ if lower_note.voice == voice1
45
+ :up
46
+ elsif lower_note.voice == voice2
47
+ :down
48
+ end
49
+ end
50
+ end
51
+
42
52
  def to_s
43
53
  "#{functional_interval} at #{position}"
44
54
  end
@@ -8,7 +8,7 @@ class HeadMusic::MelodicInterval
8
8
  end
9
9
 
10
10
  def functional_interval
11
- @functional_interval ||= HeadMusic::FunctionalInterval.new(first_note.pitch, second_note.pitch)
11
+ @functional_interval ||= HeadMusic::FunctionalInterval.new(first_pitch, second_pitch)
12
12
  end
13
13
 
14
14
  def position_start
@@ -79,12 +79,12 @@ class HeadMusic::RhythmicValue
79
79
  end
80
80
 
81
81
  def single_value_name
82
- [name_modifier_prefix, unit_name].reject(&:nil?).join(' ')
82
+ [name_modifier_prefix, unit_name].compact.join(' ')
83
83
  end
84
84
 
85
85
  def name
86
86
  if tied_value
87
- [single_value_name, tied_value.name].reject(&:nil?).join(' tied to ')
87
+ [single_value_name, tied_value.name].compact.join(' tied to ')
88
88
  else
89
89
  single_value_name
90
90
  end
@@ -49,6 +49,8 @@ class HeadMusic::Style::Annotation
49
49
  self.class::MESSAGE
50
50
  end
51
51
 
52
+ protected
53
+
52
54
  def first_note
53
55
  notes && notes.first
54
56
  end
@@ -93,17 +95,26 @@ class HeadMusic::Style::Annotation
93
95
  downbeat_harmonic_intervals.map.with_index do |harmonic_interval, i|
94
96
  next_harmonic_interval = downbeat_harmonic_intervals[i+1]
95
97
  HeadMusic::Motion.new(harmonic_interval, next_harmonic_interval) if next_harmonic_interval
96
- end.reject(&:nil?)
98
+ end.compact
97
99
  end
98
100
 
99
101
  def downbeat_harmonic_intervals
100
- @downbeat_harmonic_intervals ||= cantus_firmus.notes.map do |cantus_firmus_note|
101
- interval = HeadMusic::HarmonicInterval.new(cantus_firmus_note.voice, voice, cantus_firmus_note.position)
102
- interval.notes.length == 2 ? interval : nil
103
- end.reject(&:nil?)
102
+ @downbeat_harmonic_intervals ||=
103
+ cantus_firmus.notes.map { |cantus_firmus_note|
104
+ HeadMusic::HarmonicInterval.new(cantus_firmus_note.voice, voice, cantus_firmus_note.position)
105
+ }.reject { |interval| interval.notes.length < 2 }
106
+ end
107
+
108
+ def harmonic_intervals
109
+ @harmonic_intervals ||=
110
+ positions.map { |position|
111
+ HeadMusic::HarmonicInterval.new(cantus_firmus, voice, position)
112
+ }.reject { |harmonic_interval| harmonic_interval.notes.length < 2 }
104
113
  end
105
114
 
106
- private
115
+ def positions
116
+ @positions ||= voices.map(&:notes).flatten.map(&:position).sort.uniq(&:to_s)
117
+ end
107
118
 
108
119
  def unsorted_higher_voices
109
120
  other_voices.select { |part| part.highest_pitch > highest_pitch }
@@ -9,6 +9,6 @@ class HeadMusic::Style::Annotations::AlwaysMove < HeadMusic::Style::Annotation
9
9
  if interval.shorthand == 'PU'
10
10
  HeadMusic::Style::Mark.for_all(notes[i..i+1])
11
11
  end
12
- end.reject(&:nil?)
12
+ end.compact
13
13
  end
14
14
  end
@@ -2,7 +2,7 @@ module HeadMusic::Style::Annotations
2
2
  end
3
3
 
4
4
  class HeadMusic::Style::Annotations::ApproachPerfectionContrarily < HeadMusic::Style::Annotation
5
- MESSAGE = 'Step down to final note.'
5
+ MESSAGE = 'Approach perfect consonances by contrary motion.'
6
6
 
7
7
  def marks
8
8
  motions_to_perfect_consonance_approached_directly.map do |bad_motion|
@@ -5,36 +5,28 @@ class HeadMusic::Style::Annotations::AvoidCrossingVoices < HeadMusic::Style::Ann
5
5
  MESSAGE = "Avoid crossing voices."
6
6
 
7
7
  def marks
8
- crossings
8
+ crossings.map do |crossing|
9
+ HeadMusic::Style::Mark.for_all(crossing.notes)
10
+ end
9
11
  end
10
12
 
11
13
  private
12
14
 
13
15
  def crossings
14
- crossings_of_lower_voices + crossings_of_higher_voices
16
+ harmonic_intervals.select do |harmonic_interval|
17
+ harmonic_interval.pitch_orientation && harmonic_interval.pitch_orientation != predominant_pitch_orientation
18
+ end
15
19
  end
16
20
 
17
- def crossings_of_lower_voices
18
- [].tap do |marks|
19
- lower_voices.each do |lower_voice|
20
- lower_voice.notes.each do |lower_voice_note|
21
- notes_during = voice.notes_during(lower_voice_note)
22
- crossed_notes = notes_during.select { |note| note.pitch < lower_voice_note.pitch }
23
- marks << HeadMusic::Style::Mark.for_all(crossed_notes)
24
- end
25
- end
26
- end
21
+ def predominant_pitch_orientation
22
+ pitch_orientations
23
+ .compact
24
+ .group_by { |orientation| orientation }
25
+ .max { |a, b| a[1].length <=> b[1].length }
26
+ .first
27
27
  end
28
28
 
29
- def crossings_of_higher_voices
30
- [].tap do |marks|
31
- higher_voices.each do |higher_voice|
32
- higher_voice.notes.each do |higher_voice_note|
33
- notes_during = voice.notes_during(higher_voice_note)
34
- crossed_notes = notes_during.select { |note| note.pitch > lower_voice_note.pitch }
35
- marks << HeadMusic::Style::Mark.for_all(crossed_notes)
36
- end
37
- end
38
- end
29
+ def pitch_orientations
30
+ harmonic_intervals.map(&:pitch_orientation).compact.uniq
39
31
  end
40
32
  end
@@ -24,7 +24,7 @@ class HeadMusic::Style::Annotations::AvoidOverlappingVoices < HeadMusic::Style::
24
24
  end
25
25
  marks << HeadMusic::Style::Mark.for_each(overlapped_notes)
26
26
  end
27
- end
27
+ end.flatten
28
28
  end
29
29
 
30
30
  def overlappings_of_higher_voices
@@ -32,11 +32,11 @@ class HeadMusic::Style::Annotations::AvoidOverlappingVoices < HeadMusic::Style::
32
32
  higher_voices.each do |higher_voice|
33
33
  overlapped_notes = voice.notes.select do |note|
34
34
  preceding_note = higher_voice.note_preceding(note.position)
35
- following_note = lower_voice.note_following(note.position)
35
+ following_note = higher_voice.note_following(note.position)
36
36
  (preceding_note && preceding_note.pitch < note.pitch) || (following_note && following_note.pitch < note.pitch)
37
37
  end
38
38
  marks << HeadMusic::Style::Mark.for_each(overlapped_notes)
39
39
  end
40
- end
40
+ end.flatten
41
41
  end
42
42
  end
@@ -13,7 +13,7 @@ class HeadMusic::Style::Annotations::ConsonantDownbeats < HeadMusic::Style::Anno
13
13
  private
14
14
 
15
15
  def dissonant_pairs
16
- dissonant_intervals.map(&:notes).reject(&:nil?)
16
+ dissonant_intervals.map(&:notes).compact
17
17
  end
18
18
 
19
19
  def dissonant_intervals
@@ -13,7 +13,7 @@ class HeadMusic::Style::Annotations::MostlyConjunct < HeadMusic::Style::Annotati
13
13
  def marks_for_skips_and_leaps
14
14
  melodic_intervals.map.with_index do |interval, i|
15
15
  HeadMusic::Style::Mark.for_all(notes[i..i+1], fitness: HeadMusic::SMALL_PENALTY_FACTOR) unless interval.step?
16
- end.reject(&:nil?)
16
+ end.compact
17
17
  end
18
18
 
19
19
  def conjunct_ratio
@@ -13,7 +13,7 @@ class HeadMusic::Style::Annotations::NoUnisonsInMiddle < HeadMusic::Style::Annot
13
13
  private
14
14
 
15
15
  def unison_pairs
16
- middle_unisons.map(&:notes).reject(&:nil?)
16
+ middle_unisons.map(&:notes).compact
17
17
  end
18
18
 
19
19
  def middle_unisons
@@ -21,19 +21,6 @@ class HeadMusic::Style::Annotations::NoUnisonsInMiddle < HeadMusic::Style::Annot
21
21
  end
22
22
 
23
23
  def middle_intervals
24
- [harmonic_intervals[1..-2]].flatten.reject(&:nil?)
25
- end
26
-
27
- def harmonic_intervals
28
- cantus_firmus.notes.map do |cantus_firmus_note|
29
- counterpoint_notes = voice.notes_during(cantus_firmus_note)
30
- counterpoint_notes.map { |note|
31
- HarmonicInterval.new(cantus_firmus_note.voice, voice, note.position)
32
- }
33
- end.flatten
34
- end
35
-
36
- def positions
37
- voices.map(:notes).flatten.map(&:position).sort.uniq
24
+ [harmonic_intervals[1..-2]].flatten.compact
38
25
  end
39
26
  end
@@ -2,7 +2,7 @@ module HeadMusic::Style::Annotations
2
2
  end
3
3
 
4
4
  class HeadMusic::Style::Annotations::PreferImperfect < HeadMusic::Style::Annotation
5
- MESSAGE = "Use consonant harmonic intervals."
5
+ MESSAGE = "Prefer imperfect harmonic intervals."
6
6
 
7
7
  def marks
8
8
  if ratio_of_perfect_intervals > 0.5
@@ -14,7 +14,7 @@ class HeadMusic::Style::Annotations::RecoverLargeLeaps < HeadMusic::Style::Annot
14
14
  if unrecovered_leap?(previous_interval, interval)
15
15
  HeadMusic::Style::Mark.for_all((previous_interval.notes + interval.notes).uniq)
16
16
  end
17
- end.reject(&:nil?)
17
+ end.compact
18
18
  end
19
19
 
20
20
  private
@@ -17,22 +17,15 @@ class HeadMusic::Style::Annotations::StepOutOfUnison < HeadMusic::Style::Annotat
17
17
  end
18
18
 
19
19
  def melodic_intervals_following_unisons
20
- unisons.map do |unison|
21
- note1 = voice.note_at(unison.position)
22
- note2 = voice.note_following(unison.position)
23
- MelodicInterval.new(voice, note1, note2) if note2
24
- end.reject(&:nil?)
20
+ @melodic_intervals_following_unisons ||=
21
+ perfect_unisons.map do |unison|
22
+ note1 = voice.note_at(unison.position)
23
+ note2 = voice.note_following(unison.position)
24
+ HeadMusic::MelodicInterval.new(voice, note1, note2) if note1 && note2
25
+ end.compact
25
26
  end
26
27
 
27
- def unisons
28
- harmonic_intervals.select { |interval| interval.perfect_consonance? && interval.unison? }
29
- end
30
-
31
- def harmonic_intervals
32
- positions.map { |position| HarmonicInterval.new(cantus_firmus, voice, position) }
33
- end
34
-
35
- def positions
36
- voices.map(&:notes).flatten.map(&:position).sort.uniq(&:to_s)
28
+ def perfect_unisons
29
+ @unisons ||= harmonic_intervals.select(&:perfect_consonance?).select(&:unison?)
37
30
  end
38
31
  end
@@ -1,3 +1,3 @@
1
1
  module HeadMusic
2
- VERSION = "0.13.2"
2
+ VERSION = "0.14.1"
3
3
  end
@@ -56,7 +56,7 @@ class HeadMusic::Voice
56
56
  def melodic_intervals
57
57
  notes.map.with_index do |note, i|
58
58
  HeadMusic::MelodicInterval.new(self, notes[i-1], note) if i > 0
59
- end.reject(&:nil?)
59
+ end.compact
60
60
  end
61
61
 
62
62
  def leaps
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: head_music
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.2
4
+ version: 0.14.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rob Head