head_music 0.19.0 → 0.19.1

Sign up to get free protection for your applications and to get access to all the features.
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