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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6be1575228b7a6ea778c74be5e1f0422732fa7e0
4
- data.tar.gz: 5761e7ae91487307331c9f73487d6b91a1fdd8d9
3
+ metadata.gz: 3a0baef52f11f8aab0c4becbe31e70928b358499
4
+ data.tar.gz: 47ae1ef06c1e25a2abd0559eb40a20494c47eed3
5
5
  SHA512:
6
- metadata.gz: b754a036d18c02225ed9bef0e9a778f870adf265a39df3d612dbc67544631496883fe2cda40c5088bfaac58fe46df309e38e43037eb43c4b96258650ca82d49e
7
- data.tar.gz: 4e7bb9461fbf78a833773d1cabcbbe0574e48087fde520f4f181aeafdb4ade491adcc463b79c2b384e50e437bf3e31a50d3ea2885314dea845e75f130075c843
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::PermittedIntervals < HeadMusic::Style::Annotation
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::PermittedIntervals,
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
@@ -1,3 +1,3 @@
1
1
  module HeadMusic
2
- VERSION = "0.11.8"
2
+ VERSION = "0.11.9"
3
3
  end
@@ -63,6 +63,10 @@ class HeadMusic::Voice
63
63
  melodic_intervals.select(&:leap?)
64
64
  end
65
65
 
66
+ def cantus_firmus?
67
+ role && role =~ /cantus.?firmus/i
68
+ end
69
+
66
70
  private
67
71
 
68
72
  def insert_into_placements(placement)
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/permitted_intervals'
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.8
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-05-12 00:00:00.000000000 Z
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/permitted_intervals.rb
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