head_music 0.19.0 → 0.19.1

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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/lib/head_music/chord.rb +29 -3
  4. data/lib/head_music/functional_interval.rb +175 -94
  5. data/lib/head_music/octave.rb +8 -0
  6. data/lib/head_music/style/analysis.rb +7 -7
  7. data/lib/head_music/style/{annotations → guidelines}/always_move.rb +3 -3
  8. data/lib/head_music/style/{annotations → guidelines}/approach_perfection_contrarily.rb +3 -3
  9. data/lib/head_music/style/{annotations → guidelines}/at_least_eight_notes.rb +3 -3
  10. data/lib/head_music/style/{annotations → guidelines}/avoid_crossing_voices.rb +3 -3
  11. data/lib/head_music/style/{annotations → guidelines}/avoid_overlapping_voices.rb +3 -3
  12. data/lib/head_music/style/{annotations → guidelines}/consonant_climax.rb +3 -3
  13. data/lib/head_music/style/{annotations → guidelines}/consonant_downbeats.rb +3 -3
  14. data/lib/head_music/style/{annotations → guidelines}/diatonic.rb +3 -3
  15. data/lib/head_music/style/{annotations → guidelines}/direction_changes.rb +3 -3
  16. data/lib/head_music/style/{annotations → guidelines}/end_on_perfect_consonance.rb +3 -3
  17. data/lib/head_music/style/{annotations → guidelines}/end_on_tonic.rb +3 -3
  18. data/lib/head_music/style/guidelines/frequent_direction_changes.rb +13 -0
  19. data/lib/head_music/style/{annotations → guidelines}/limit_octave_leaps.rb +3 -3
  20. data/lib/head_music/style/guidelines/moderate_direction_changes.rb +13 -0
  21. data/lib/head_music/style/{annotations → guidelines}/mostly_conjunct.rb +3 -3
  22. data/lib/head_music/style/{annotations → guidelines}/no_rests.rb +3 -3
  23. data/lib/head_music/style/{annotations → guidelines}/no_unisons_in_middle.rb +3 -3
  24. data/lib/head_music/style/{annotations → guidelines}/notes_same_length.rb +3 -3
  25. data/lib/head_music/style/{annotations → guidelines}/one_to_one.rb +3 -3
  26. data/lib/head_music/style/{annotations → guidelines}/prefer_contrary_motion.rb +3 -3
  27. data/lib/head_music/style/{annotations → guidelines}/prefer_imperfect.rb +3 -3
  28. data/lib/head_music/style/{annotations → guidelines}/prepare_octave_leaps.rb +3 -3
  29. data/lib/head_music/style/{annotations → guidelines}/recover_large_leaps.rb +3 -3
  30. data/lib/head_music/style/{annotations → guidelines}/singable_intervals.rb +3 -3
  31. data/lib/head_music/style/{annotations → guidelines}/singable_range.rb +3 -3
  32. data/lib/head_music/style/{annotations → guidelines}/single_large_leaps.rb +3 -3
  33. data/lib/head_music/style/{annotations → guidelines}/start_on_perfect_consonance.rb +3 -3
  34. data/lib/head_music/style/{annotations → guidelines}/start_on_tonic.rb +3 -3
  35. data/lib/head_music/style/{annotations → guidelines}/step_down_to_final_note.rb +3 -3
  36. data/lib/head_music/style/{annotations → guidelines}/step_out_of_unison.rb +3 -3
  37. data/lib/head_music/style/{annotations → guidelines}/step_to_final_note.rb +3 -3
  38. data/lib/head_music/style/{annotations → guidelines}/step_up_to_final_note.rb +3 -3
  39. data/lib/head_music/style/{annotations → guidelines}/up_to_fourteen_notes.rb +3 -3
  40. data/lib/head_music/style/guides/first_species_harmony.rb +22 -0
  41. data/lib/head_music/style/guides/first_species_melody.rb +28 -0
  42. data/lib/head_music/style/guides/fux_cantus_firmus.rb +30 -0
  43. data/lib/head_music/style/guides/modern_cantus_firmus.rb +31 -0
  44. data/lib/head_music/style/mark.rb +1 -1
  45. data/lib/head_music/version.rb +1 -1
  46. data/lib/head_music.rb +39 -39
  47. metadata +39 -39
  48. data/lib/head_music/style/annotations/frequent_direction_changes.rb +0 -13
  49. data/lib/head_music/style/annotations/moderate_direction_changes.rb +0 -13
  50. data/lib/head_music/style/rulesets/first_species_harmony.rb +0 -22
  51. data/lib/head_music/style/rulesets/first_species_melody.rb +0 -28
  52. data/lib/head_music/style/rulesets/fux_cantus_firmus.rb +0 -30
  53. data/lib/head_music/style/rulesets/modern_cantus_firmus.rb +0 -31
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 17a6cfc4f189a661c7f095056b429e32ce17468a
4
- data.tar.gz: d3a3cc4b18e87fa0f7af9ba584b5f28946329784
3
+ metadata.gz: 838a7ebfc5a6873d62ac2d484b655bcc5fe37302
4
+ data.tar.gz: 669f4f3f9ab721dc93208b39ef22ac7170951ba9
5
5
  SHA512:
6
- metadata.gz: 2e9b674c4bde48f3ce71ef0768b60461f3af1ebfcfc501ab9500af24b9b61cbc071f176a43d064fba5e5d9e83f0bc53ee80ffd8d34551ea2bd2777926437f1dc
7
- data.tar.gz: 04fb64922f6c47d6df0f0cb637ad68b188fffe448dbefe02992aa85169c6ae8993b83383bdf59118391e94c3651ea76ddb7c52c9dd7b93d266d9a0aa8a9d28f0
6
+ metadata.gz: 0ca1b44616d4aed6f76e5838b99a546b28889fed30802fec1c8e5ee713af89352e1326e89bf9e5195104722a574c7f8db47755fa6b561685c1b18b190fc38525
7
+ data.tar.gz: cc2a14f96a3ec7e7590c59e2b81023ed41a5e9b2f7fb8fd5d25a860c929ece23fa8e2df32adf15faa508b1ed21856a774642d3e4ba40ec4299ce3d2d640df6dd
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2017 Rob Head
3
+ Copyright (c) 2018 Robert Emerson Head
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -11,7 +11,7 @@ class HeadMusic::Chord
11
11
 
12
12
  def consonant_triad?
13
13
  return false unless three_pitches?
14
- root_triad? || first_inversion_triad? || second_inversion_triad?
14
+ reduction.root_triad? || reduction.first_inversion_triad? || reduction.second_inversion_triad?
15
15
  end
16
16
 
17
17
  def root_triad?
@@ -26,8 +26,9 @@ class HeadMusic::Chord
26
26
  invert.intervals.map(&:shorthand).sort == %w[M3 m3]
27
27
  end
28
28
 
29
- # TODO
30
- def reduction; end
29
+ def reduction
30
+ @reduction ||= HeadMusic::Chord.new(reduction_pitches)
31
+ end
31
32
 
32
33
  def three_pitches?
33
34
  pitches.length == 3
@@ -44,4 +45,29 @@ class HeadMusic::Chord
44
45
  new_pitches = pitches.drop(1) + [inverted_pitch]
45
46
  HeadMusic::Chord.new(new_pitches)
46
47
  end
48
+
49
+ def bass_pitch
50
+ @bass_pitch ||= pitches.first
51
+ end
52
+
53
+ def inspect
54
+ pitches.map(&:to_s).join(' ')
55
+ end
56
+
57
+ def to_s
58
+ pitches.map(&:to_s).join(' ')
59
+ end
60
+
61
+ def ==(other)
62
+ pitches & other.pitches == pitches
63
+ end
64
+
65
+ private
66
+
67
+ def reduction_pitches
68
+ pitches.map do |pitch|
69
+ pitch = HeadMusic::Pitch.fetch_or_create(pitch.spelling, pitch.octave - 1) while pitch > bass_pitch + 12
70
+ pitch
71
+ end.sort
72
+ end
47
73
  end
@@ -36,8 +36,8 @@ class HeadMusic::FunctionalInterval
36
36
  delegate :to_s, to: :name
37
37
  delegate :perfect?, :major?, :minor?, :diminished?, :augmented?, :doubly_diminished?, :doubly_augmented?, to: :quality
38
38
 
39
- # Representation of the name of the functional interval
40
- class Name
39
+ # Interprets a string or symbol
40
+ class Parser
41
41
  attr_reader :identifier
42
42
 
43
43
  def initialize(identifier)
@@ -65,114 +65,197 @@ class HeadMusic::FunctionalInterval
65
65
  end
66
66
  end
67
67
 
68
- def self.get(identifier)
69
- name = Name.new(identifier)
70
- semitones = _degree_quality_semitones.dig(name.degree_name.to_sym, name.quality_name)
71
- higher_pitch = HeadMusic::Pitch.from_number_and_letter(HeadMusic::Pitch.middle_c + semitones, name.higher_letter)
72
- new(HeadMusic::Pitch.middle_c, higher_pitch)
73
- end
68
+ # Accepts a name and a quality and returns the number of semitones
69
+ class Semitones
70
+ attr_reader :count
71
+
72
+ def initialize(name, quality_name)
73
+ @count ||= Semitones.degree_quality_semitones.dig(name, quality_name)
74
+ end
74
75
 
75
- def self._degree_quality_semitones
76
- @_degree_quality_semitones ||= begin
77
- {}.tap do |degree_quality_semitones|
78
- QUALITY_SEMITONES.each do |degree_name, qualities|
79
- default_quality = qualities.keys.first
80
- default_semitones = qualities[default_quality]
81
- degree_quality_semitones[degree_name] = _semitones_for_degree(default_quality, default_semitones)
76
+ def self.degree_quality_semitones
77
+ @_degree_quality_semitones ||= begin
78
+ {}.tap do |degree_quality_semitones|
79
+ QUALITY_SEMITONES.each do |degree_name, qualities|
80
+ default_quality = qualities.keys.first
81
+ default_semitones = qualities[default_quality]
82
+ degree_quality_semitones[degree_name] = _semitones_for_degree(default_quality, default_semitones)
83
+ end
82
84
  end
83
85
  end
84
86
  end
85
- end
86
87
 
87
- def self._semitones_for_degree(quality, default_semitones)
88
- {}.tap do |semitones|
89
- _degree_quality_modifications(quality).each do |current_quality, delta|
90
- semitones[current_quality] = default_semitones + delta
88
+ def self._semitones_for_degree(quality, default_semitones)
89
+ {}.tap do |semitones|
90
+ _degree_quality_modifications(quality).each do |current_quality, delta|
91
+ semitones[current_quality] = default_semitones + delta
92
+ end
91
93
  end
92
94
  end
93
- end
94
95
 
95
- def self._degree_quality_modifications(quality)
96
- if quality == :perfect
97
- HeadMusic::Quality::PERFECT_INTERVAL_MODIFICATION.invert
98
- else
99
- HeadMusic::Quality::MAJOR_INTERVAL_MODIFICATION.invert
96
+ def self._degree_quality_modifications(quality)
97
+ if quality == :perfect
98
+ HeadMusic::Quality::PERFECT_INTERVAL_MODIFICATION.invert
99
+ else
100
+ HeadMusic::Quality::MAJOR_INTERVAL_MODIFICATION.invert
101
+ end
100
102
  end
101
103
  end
102
104
 
103
- def initialize(pitch1, pitch2)
104
- pitch1 = HeadMusic::Pitch.get(pitch1)
105
- pitch2 = HeadMusic::Pitch.get(pitch2)
106
- @lower_pitch, @higher_pitch = [pitch1, pitch2].sort
107
- end
105
+ # Accepts the letter name count between two notes and categorizes the interval
106
+ class Category
107
+ attr_reader :number
108
108
 
109
- def number
110
- simple_number + octaves * 7
111
- end
109
+ def initialize(number)
110
+ @number = number
111
+ end
112
112
 
113
- def steps
114
- number - 1
115
- end
113
+ def step?
114
+ number == 2
115
+ end
116
116
 
117
- def simple_number
118
- @simple_number ||= @lower_pitch.letter_name.steps_to(@higher_pitch.letter_name) + 1
119
- end
117
+ def skip?
118
+ number == 3
119
+ end
120
120
 
121
- def simple_semitones
122
- semitones % 12
123
- end
121
+ def leap?
122
+ number >= 3
123
+ end
124
124
 
125
- def semitones
126
- (@higher_pitch - @lower_pitch).to_i
125
+ def large_leap?
126
+ number > 3
127
+ end
127
128
  end
128
129
 
129
- def octaves
130
- (higher_pitch.number - lower_pitch.number) / 12
131
- end
130
+ # Encapsulate the distance methods of the interval
131
+ class Size
132
+ attr_reader :low_pitch, :high_pitch
132
133
 
133
- def compound?
134
- !simple?
135
- end
134
+ def initialize(pitch1, pitch2)
135
+ @low_pitch, @high_pitch = *[pitch1, pitch2].sort
136
+ end
136
137
 
137
- def simple?
138
- octaves.zero?
139
- end
138
+ def simple_number
139
+ @simple_number ||= @low_pitch.letter_name.steps_to(@high_pitch.letter_name) + 1
140
+ end
140
141
 
141
- def simple_name
142
- [quality_name, simple_number_name].join(' ')
143
- end
142
+ def octaves
143
+ @octaves ||= (high_pitch.number - low_pitch.number) / 12
144
+ end
144
145
 
145
- def name
146
- if named_number?
147
- [quality_name, number_name].join(' ')
148
- elsif simple_name == 'perfect unison'
149
- "#{octaves.humanize} octaves"
150
- else
151
- "#{octaves.humanize} octaves and #{quality.article} #{simple_name}"
146
+ # returns the ordinality of the interval
147
+ def number
148
+ simple_number + octaves * 7
149
+ end
150
+
151
+ def simple?
152
+ octaves.zero?
153
+ end
154
+
155
+ def compound?
156
+ !simple?
152
157
  end
153
- end
154
158
 
155
- def shorthand
156
- step_shorthand = number == 1 ? 'U' : number
157
- [quality.shorthand, step_shorthand].join
159
+ def simple_semitones
160
+ @simple_semitones ||= semitones % 12
161
+ end
162
+
163
+ def semitones
164
+ (high_pitch - low_pitch).to_i
165
+ end
166
+
167
+ def steps
168
+ number - 1
169
+ end
158
170
  end
159
171
 
160
- def quality
161
- HeadMusic::Quality.get(quality_name)
172
+ # Accepts a number and number of semitones and privides the naming methods.
173
+ class Naming
174
+ attr_reader :number, :semitones
175
+
176
+ def initialize(number:, semitones:)
177
+ @number = number
178
+ @semitones = semitones
179
+ end
180
+
181
+ def simple_semitones
182
+ @simple_semitones ||= semitones % 12
183
+ end
184
+
185
+ def simple_number
186
+ @simple_number ||= (number - 1) % 7 + 1
187
+ end
188
+
189
+ def simple_name
190
+ [quality_name, simple_number_name].join(' ')
191
+ end
192
+
193
+ def quality_name
194
+ starting_quality = QUALITY_SEMITONES[simple_number_name.to_sym].keys.first
195
+ delta = simple_semitones - QUALITY_SEMITONES[simple_number_name.to_sym][starting_quality]
196
+ HeadMusic::Quality.from(starting_quality, delta)
197
+ end
198
+
199
+ def simple_number_name
200
+ NUMBER_NAMES[simple_number - 1]
201
+ end
202
+
203
+ def number_name
204
+ NUMBER_NAMES[number - 1] || (number.to_s + NAME_SUFFIXES[number % 10])
205
+ end
206
+
207
+ def name
208
+ if named_number?
209
+ [quality_name, number_name].join(' ')
210
+ elsif simple_name == 'perfect unison'
211
+ "#{octaves.humanize} octaves"
212
+ else
213
+ "#{octaves.humanize} octaves and #{quality.article} #{simple_name}"
214
+ end
215
+ end
216
+
217
+ def shorthand
218
+ step_shorthand = number == 1 ? 'U' : number
219
+ [quality.shorthand, step_shorthand].join
220
+ end
221
+
222
+ private
223
+
224
+ def named_number?
225
+ number < NUMBER_NAMES.length
226
+ end
227
+
228
+ def quality
229
+ @quality ||= HeadMusic::Quality.get(quality_name)
230
+ end
231
+
232
+ def octaves
233
+ @octaves ||= semitones / 12
234
+ end
162
235
  end
163
236
 
164
- def quality_name
165
- starting_quality = QUALITY_SEMITONES[simple_number_name.to_sym].keys.first
166
- delta = simple_semitones - QUALITY_SEMITONES[simple_number_name.to_sym][starting_quality]
167
- HeadMusic::Quality.from(starting_quality, delta)
237
+ delegate :step?, :skip?, :leap?, :large_leap?, to: :category
238
+ delegate :simple_number, :octaves, :number, :simple?, :compound?, :semitones, :simple_semitones, :steps, to: :size
239
+ delegate(
240
+ :simple_semitones, :simple_name, :quality_name, :simple_number_name, :number_name, :name, :shorthand, to: :naming
241
+ )
242
+
243
+ # Accepts a name and returns the interval with middle c on the bottom
244
+ def self.get(identifier)
245
+ name = Parser.new(identifier)
246
+ semitones = Semitones.new(name.degree_name.to_sym, name.quality_name).count
247
+ higher_pitch = HeadMusic::Pitch.from_number_and_letter(HeadMusic::Pitch.middle_c + semitones, name.higher_letter)
248
+ new(HeadMusic::Pitch.middle_c, higher_pitch)
168
249
  end
169
250
 
170
- def simple_number_name
171
- NUMBER_NAMES[simple_number - 1]
251
+ def initialize(pitch1, pitch2)
252
+ pitch1 = HeadMusic::Pitch.get(pitch1)
253
+ pitch2 = HeadMusic::Pitch.get(pitch2)
254
+ @lower_pitch, @higher_pitch = [pitch1, pitch2].sort
172
255
  end
173
256
 
174
- def number_name
175
- NUMBER_NAMES[number - 1] || (number.to_s + NAME_SUFFIXES[number % 10])
257
+ def quality
258
+ HeadMusic::Quality.get(quality_name)
176
259
  end
177
260
 
178
261
  def inversion
@@ -180,6 +263,7 @@ class HeadMusic::FunctionalInterval
180
263
  inverted_low_pitch += 12 while inverted_low_pitch < higher_pitch
181
264
  HeadMusic::FunctionalInterval.new(higher_pitch, inverted_low_pitch)
182
265
  end
266
+ alias invert inversion
183
267
 
184
268
  def consonance(style = :standard_practice)
185
269
  consonance_for_perfect(style) ||
@@ -190,6 +274,7 @@ class HeadMusic::FunctionalInterval
190
274
  def consonance?(style = :standard_practice)
191
275
  consonance(style).perfect? || consonance(style).imperfect?
192
276
  end
277
+ alias consonant? consonance?
193
278
 
194
279
  def perfect_consonance?(style = :standard_practice)
195
280
  consonance(style).perfect?
@@ -203,22 +288,6 @@ class HeadMusic::FunctionalInterval
203
288
  consonance(style).dissonant?
204
289
  end
205
290
 
206
- def step?
207
- number == 2
208
- end
209
-
210
- def skip?
211
- number == 3
212
- end
213
-
214
- def leap?
215
- number >= 3
216
- end
217
-
218
- def large_leap?
219
- number > 3
220
- end
221
-
222
291
  def <=>(other)
223
292
  other = self.class.get(other) unless other.is_a?(HeadMusic::FunctionalInterval)
224
293
  semitones <=> other.semitones
@@ -234,6 +303,18 @@ class HeadMusic::FunctionalInterval
234
303
 
235
304
  private
236
305
 
306
+ def size
307
+ @size ||= Size.new(@lower_pitch, @higher_pitch)
308
+ end
309
+
310
+ def category
311
+ @category ||= Category.new(number)
312
+ end
313
+
314
+ def naming
315
+ @naming ||= Naming.new(number: number, semitones: semitones)
316
+ end
317
+
237
318
  def named_number?
238
319
  number < NUMBER_NAMES.length
239
320
  end
@@ -39,5 +39,13 @@ class HeadMusic::Octave
39
39
  to_i <=> other.to_i
40
40
  end
41
41
 
42
+ def +(other)
43
+ self.class.get(to_i + other.to_i)
44
+ end
45
+
46
+ def -(other)
47
+ self.class.get(to_i - other.to_i)
48
+ end
49
+
42
50
  private_class_method :new
43
51
  end
@@ -1,15 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # A module for style analysis and rules.
3
+ # A module for style analysis and guidelines.
4
4
  module HeadMusic::Style; end
5
5
 
6
- # An analysis of music according to a ruleset.
6
+ # An analysis of music according to a style guide.
7
7
  class HeadMusic::Style::Analysis
8
- attr_reader :ruleset, :subject
8
+ attr_reader :guide, :voice
9
9
 
10
- def initialize(ruleset, subject)
11
- @ruleset = ruleset
12
- @subject = subject
10
+ def initialize(guide, voice)
11
+ @guide = guide
12
+ @voice = voice
13
13
  end
14
14
 
15
15
  def messages
@@ -18,7 +18,7 @@ class HeadMusic::Style::Analysis
18
18
  alias annotation_messages messages
19
19
 
20
20
  def annotations
21
- @annotations ||= @ruleset.analyze(subject)
21
+ @annotations ||= @guide.analyze(voice)
22
22
  end
23
23
 
24
24
  def fitness
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Module for Annotations.
4
- module HeadMusic::Style::Annotations; end
3
+ # Module for style guidelines.
4
+ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # A counterpoint guideline
7
- class HeadMusic::Style::Annotations::AlwaysMove < HeadMusic::Style::Annotation
7
+ class HeadMusic::Style::Guidelines::AlwaysMove < HeadMusic::Style::Annotation
8
8
  MESSAGE = 'Always move to a different note.'
9
9
 
10
10
  def marks
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Module for Annotations.
4
- module HeadMusic::Style::Annotations; end
3
+ # Module for style guidelines.
4
+ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # A counterpoint guideline
7
- class HeadMusic::Style::Annotations::ApproachPerfectionContrarily < HeadMusic::Style::Annotation
7
+ class HeadMusic::Style::Guidelines::ApproachPerfectionContrarily < HeadMusic::Style::Annotation
8
8
  MESSAGE = 'Approach perfect consonances by contrary motion.'
9
9
 
10
10
  def marks
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Module for Annotations.
4
- module HeadMusic::Style::Annotations; end
3
+ # Module for style guidelines.
4
+ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # A counterpoint guideline
7
- class HeadMusic::Style::Annotations::AtLeastEightNotes < HeadMusic::Style::Annotation
7
+ class HeadMusic::Style::Guidelines::AtLeastEightNotes < HeadMusic::Style::Annotation
8
8
  MINIMUM_NOTES = 8
9
9
 
10
10
  MESSAGE = 'Write at least eight notes.'
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Module for Annotations.
4
- module HeadMusic::Style::Annotations; end
3
+ # Module for style guidelines.
4
+ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # A counterpoint guideline
7
- class HeadMusic::Style::Annotations::AvoidCrossingVoices < HeadMusic::Style::Annotation
7
+ class HeadMusic::Style::Guidelines::AvoidCrossingVoices < HeadMusic::Style::Annotation
8
8
  MESSAGE = 'Avoid crossing voices. Maintain the high-low relationship between voices.'
9
9
 
10
10
  def marks
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Module for Annotations.
4
- module HeadMusic::Style::Annotations; end
3
+ # Module for style guidelines.
4
+ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # A counterpoint guideline
7
- class HeadMusic::Style::Annotations::AvoidOverlappingVoices < HeadMusic::Style::Annotation
7
+ class HeadMusic::Style::Guidelines::AvoidOverlappingVoices < HeadMusic::Style::Annotation
8
8
  MESSAGE = 'Avoid overlapping voices. Maintain the high-low relationship between voices even for adjacent notes.'
9
9
 
10
10
  def marks
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Module for Annotations.
4
- module HeadMusic::Style::Annotations; end
3
+ # Module for style guidelines.
4
+ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # A counterpoint guideline
7
- class HeadMusic::Style::Annotations::ConsonantClimax < HeadMusic::Style::Annotation
7
+ class HeadMusic::Style::Guidelines::ConsonantClimax < HeadMusic::Style::Annotation
8
8
  MESSAGE = 'Peak on a consonant high or low note one time or twice with a step between.'
9
9
 
10
10
  def marks
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Module for Annotations.
4
- module HeadMusic::Style::Annotations; end
3
+ # Module for style guidelines.
4
+ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # A counterpoint guideline
7
- class HeadMusic::Style::Annotations::ConsonantDownbeats < HeadMusic::Style::Annotation
7
+ class HeadMusic::Style::Guidelines::ConsonantDownbeats < HeadMusic::Style::Annotation
8
8
  MESSAGE = 'Use consonant harmonic intervals on every downbeat.'
9
9
 
10
10
  def marks
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Module for Annotations.
4
- module HeadMusic::Style::Annotations; end
3
+ # Module for style guidelines.
4
+ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # A counterpoint guideline
7
- class HeadMusic::Style::Annotations::Diatonic < HeadMusic::Style::Annotation
7
+ class HeadMusic::Style::Guidelines::Diatonic < HeadMusic::Style::Annotation
8
8
  MESSAGE = 'Use only notes in the key signature.'
9
9
 
10
10
  def marks
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Module for Annotations.
4
- module HeadMusic::Style::Annotations; end
3
+ # Module for style guidelines.
4
+ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # A melodic line should have direction changes.
7
- class HeadMusic::Style::Annotations::DirectionChanges < HeadMusic::Style::Annotation
7
+ class HeadMusic::Style::Guidelines::DirectionChanges < HeadMusic::Style::Annotation
8
8
  def marks
9
9
  return unless overage.positive?
10
10
  penalty_exponent = overage**0.5
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Module for Annotations.
4
- module HeadMusic::Style::Annotations; end
3
+ # Module for style guidelines.
4
+ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # marks the voice if the first note is not the first or fifth scale degree of the key.
7
- class HeadMusic::Style::Annotations::EndOnPerfectConsonance < HeadMusic::Style::Annotation
7
+ class HeadMusic::Style::Guidelines::EndOnPerfectConsonance < HeadMusic::Style::Annotation
8
8
  MESSAGE = 'End on the first or the fifth scale degree.'
9
9
 
10
10
  def marks
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Module for Annotations.
4
- module HeadMusic::Style::Annotations; end
3
+ # Module for style guidelines.
4
+ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # A counterpoint guideline
7
- class HeadMusic::Style::Annotations::EndOnTonic < HeadMusic::Style::Annotation
7
+ class HeadMusic::Style::Guidelines::EndOnTonic < HeadMusic::Style::Annotation
8
8
  MESSAGE = 'End on the first scale degree.'
9
9
 
10
10
  def marks
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Module for style guidelines.
4
+ module HeadMusic::Style::Guidelines; end
5
+
6
+ # A counterpoint guideline
7
+ class HeadMusic::Style::Guidelines::FrequentDirectionChanges < HeadMusic::Style::Guidelines::DirectionChanges
8
+ MESSAGE = 'Change melodic direction frequently.'
9
+
10
+ def self.maximum_notes_per_direction
11
+ 3
12
+ end
13
+ end
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Module for Annotations.
4
- module HeadMusic::Style::Annotations; end
3
+ # Module for style guidelines.
4
+ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # A counterpoint guideline: Use a maximum of one octave leap.
7
- class HeadMusic::Style::Annotations::LimitOctaveLeaps < HeadMusic::Style::Annotation
7
+ class HeadMusic::Style::Guidelines::LimitOctaveLeaps < HeadMusic::Style::Annotation
8
8
  MESSAGE = 'Use a maximum of one octave leap.'
9
9
 
10
10
  def marks
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Module for style guidelines.
4
+ module HeadMusic::Style::Guidelines; end
5
+
6
+ # A counterpoint guideline
7
+ class HeadMusic::Style::Guidelines::ModerateDirectionChanges < HeadMusic::Style::Guidelines::DirectionChanges
8
+ MESSAGE = 'Change melodic direction occasionally.'
9
+
10
+ def self.maximum_notes_per_direction
11
+ 5
12
+ end
13
+ end