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.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/lib/head_music/chord.rb +29 -3
- data/lib/head_music/functional_interval.rb +175 -94
- data/lib/head_music/octave.rb +8 -0
- data/lib/head_music/style/analysis.rb +7 -7
- data/lib/head_music/style/{annotations → guidelines}/always_move.rb +3 -3
- data/lib/head_music/style/{annotations → guidelines}/approach_perfection_contrarily.rb +3 -3
- data/lib/head_music/style/{annotations → guidelines}/at_least_eight_notes.rb +3 -3
- data/lib/head_music/style/{annotations → guidelines}/avoid_crossing_voices.rb +3 -3
- data/lib/head_music/style/{annotations → guidelines}/avoid_overlapping_voices.rb +3 -3
- data/lib/head_music/style/{annotations → guidelines}/consonant_climax.rb +3 -3
- data/lib/head_music/style/{annotations → guidelines}/consonant_downbeats.rb +3 -3
- data/lib/head_music/style/{annotations → guidelines}/diatonic.rb +3 -3
- data/lib/head_music/style/{annotations → guidelines}/direction_changes.rb +3 -3
- data/lib/head_music/style/{annotations → guidelines}/end_on_perfect_consonance.rb +3 -3
- data/lib/head_music/style/{annotations → guidelines}/end_on_tonic.rb +3 -3
- data/lib/head_music/style/guidelines/frequent_direction_changes.rb +13 -0
- data/lib/head_music/style/{annotations → guidelines}/limit_octave_leaps.rb +3 -3
- data/lib/head_music/style/guidelines/moderate_direction_changes.rb +13 -0
- data/lib/head_music/style/{annotations → guidelines}/mostly_conjunct.rb +3 -3
- data/lib/head_music/style/{annotations → guidelines}/no_rests.rb +3 -3
- data/lib/head_music/style/{annotations → guidelines}/no_unisons_in_middle.rb +3 -3
- data/lib/head_music/style/{annotations → guidelines}/notes_same_length.rb +3 -3
- data/lib/head_music/style/{annotations → guidelines}/one_to_one.rb +3 -3
- data/lib/head_music/style/{annotations → guidelines}/prefer_contrary_motion.rb +3 -3
- data/lib/head_music/style/{annotations → guidelines}/prefer_imperfect.rb +3 -3
- data/lib/head_music/style/{annotations → guidelines}/prepare_octave_leaps.rb +3 -3
- data/lib/head_music/style/{annotations → guidelines}/recover_large_leaps.rb +3 -3
- data/lib/head_music/style/{annotations → guidelines}/singable_intervals.rb +3 -3
- data/lib/head_music/style/{annotations → guidelines}/singable_range.rb +3 -3
- data/lib/head_music/style/{annotations → guidelines}/single_large_leaps.rb +3 -3
- data/lib/head_music/style/{annotations → guidelines}/start_on_perfect_consonance.rb +3 -3
- data/lib/head_music/style/{annotations → guidelines}/start_on_tonic.rb +3 -3
- data/lib/head_music/style/{annotations → guidelines}/step_down_to_final_note.rb +3 -3
- data/lib/head_music/style/{annotations → guidelines}/step_out_of_unison.rb +3 -3
- data/lib/head_music/style/{annotations → guidelines}/step_to_final_note.rb +3 -3
- data/lib/head_music/style/{annotations → guidelines}/step_up_to_final_note.rb +3 -3
- data/lib/head_music/style/{annotations → guidelines}/up_to_fourteen_notes.rb +3 -3
- data/lib/head_music/style/guides/first_species_harmony.rb +22 -0
- data/lib/head_music/style/guides/first_species_melody.rb +28 -0
- data/lib/head_music/style/guides/fux_cantus_firmus.rb +30 -0
- data/lib/head_music/style/guides/modern_cantus_firmus.rb +31 -0
- data/lib/head_music/style/mark.rb +1 -1
- data/lib/head_music/version.rb +1 -1
- data/lib/head_music.rb +39 -39
- metadata +39 -39
- data/lib/head_music/style/annotations/frequent_direction_changes.rb +0 -13
- data/lib/head_music/style/annotations/moderate_direction_changes.rb +0 -13
- data/lib/head_music/style/rulesets/first_species_harmony.rb +0 -22
- data/lib/head_music/style/rulesets/first_species_melody.rb +0 -28
- data/lib/head_music/style/rulesets/fux_cantus_firmus.rb +0 -30
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 838a7ebfc5a6873d62ac2d484b655bcc5fe37302
|
4
|
+
data.tar.gz: 669f4f3f9ab721dc93208b39ef22ac7170951ba9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0ca1b44616d4aed6f76e5838b99a546b28889fed30802fec1c8e5ee713af89352e1326e89bf9e5195104722a574c7f8db47755fa6b561685c1b18b190fc38525
|
7
|
+
data.tar.gz: cc2a14f96a3ec7e7590c59e2b81023ed41a5e9b2f7fb8fd5d25a860c929ece23fa8e2df32adf15faa508b1ed21856a774642d3e4ba40ec4299ce3d2d640df6dd
|
data/LICENSE.txt
CHANGED
data/lib/head_music/chord.rb
CHANGED
@@ -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
|
-
|
30
|
-
|
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
|
-
#
|
40
|
-
class
|
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
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
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
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
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
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
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
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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
|
-
|
104
|
-
|
105
|
-
|
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
|
-
|
110
|
-
|
111
|
-
|
109
|
+
def initialize(number)
|
110
|
+
@number = number
|
111
|
+
end
|
112
112
|
|
113
|
-
|
114
|
-
|
115
|
-
|
113
|
+
def step?
|
114
|
+
number == 2
|
115
|
+
end
|
116
116
|
|
117
|
-
|
118
|
-
|
119
|
-
|
117
|
+
def skip?
|
118
|
+
number == 3
|
119
|
+
end
|
120
120
|
|
121
|
-
|
122
|
-
|
123
|
-
|
121
|
+
def leap?
|
122
|
+
number >= 3
|
123
|
+
end
|
124
124
|
|
125
|
-
|
126
|
-
|
125
|
+
def large_leap?
|
126
|
+
number > 3
|
127
|
+
end
|
127
128
|
end
|
128
129
|
|
129
|
-
|
130
|
-
|
131
|
-
|
130
|
+
# Encapsulate the distance methods of the interval
|
131
|
+
class Size
|
132
|
+
attr_reader :low_pitch, :high_pitch
|
132
133
|
|
133
|
-
|
134
|
-
|
135
|
-
|
134
|
+
def initialize(pitch1, pitch2)
|
135
|
+
@low_pitch, @high_pitch = *[pitch1, pitch2].sort
|
136
|
+
end
|
136
137
|
|
137
|
-
|
138
|
-
|
139
|
-
|
138
|
+
def simple_number
|
139
|
+
@simple_number ||= @low_pitch.letter_name.steps_to(@high_pitch.letter_name) + 1
|
140
|
+
end
|
140
141
|
|
141
|
-
|
142
|
-
|
143
|
-
|
142
|
+
def octaves
|
143
|
+
@octaves ||= (high_pitch.number - low_pitch.number) / 12
|
144
|
+
end
|
144
145
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
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
|
-
|
156
|
-
|
157
|
-
|
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
|
-
|
161
|
-
|
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
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
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
|
171
|
-
|
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
|
175
|
-
|
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
|
data/lib/head_music/octave.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# A module for style analysis and
|
3
|
+
# A module for style analysis and guidelines.
|
4
4
|
module HeadMusic::Style; end
|
5
5
|
|
6
|
-
# An analysis of music according to a
|
6
|
+
# An analysis of music according to a style guide.
|
7
7
|
class HeadMusic::Style::Analysis
|
8
|
-
attr_reader :
|
8
|
+
attr_reader :guide, :voice
|
9
9
|
|
10
|
-
def initialize(
|
11
|
-
@
|
12
|
-
@
|
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 ||= @
|
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
|
4
|
-
module HeadMusic::Style::
|
3
|
+
# Module for style guidelines.
|
4
|
+
module HeadMusic::Style::Guidelines; end
|
5
5
|
|
6
6
|
# A counterpoint guideline
|
7
|
-
class HeadMusic::Style::
|
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
|
4
|
-
module HeadMusic::Style::
|
3
|
+
# Module for style guidelines.
|
4
|
+
module HeadMusic::Style::Guidelines; end
|
5
5
|
|
6
6
|
# A counterpoint guideline
|
7
|
-
class HeadMusic::Style::
|
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
|
4
|
-
module HeadMusic::Style::
|
3
|
+
# Module for style guidelines.
|
4
|
+
module HeadMusic::Style::Guidelines; end
|
5
5
|
|
6
6
|
# A counterpoint guideline
|
7
|
-
class HeadMusic::Style::
|
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
|
4
|
-
module HeadMusic::Style::
|
3
|
+
# Module for style guidelines.
|
4
|
+
module HeadMusic::Style::Guidelines; end
|
5
5
|
|
6
6
|
# A counterpoint guideline
|
7
|
-
class HeadMusic::Style::
|
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
|
4
|
-
module HeadMusic::Style::
|
3
|
+
# Module for style guidelines.
|
4
|
+
module HeadMusic::Style::Guidelines; end
|
5
5
|
|
6
6
|
# A counterpoint guideline
|
7
|
-
class HeadMusic::Style::
|
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
|
4
|
-
module HeadMusic::Style::
|
3
|
+
# Module for style guidelines.
|
4
|
+
module HeadMusic::Style::Guidelines; end
|
5
5
|
|
6
6
|
# A counterpoint guideline
|
7
|
-
class HeadMusic::Style::
|
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
|
4
|
-
module HeadMusic::Style::
|
3
|
+
# Module for style guidelines.
|
4
|
+
module HeadMusic::Style::Guidelines; end
|
5
5
|
|
6
6
|
# A counterpoint guideline
|
7
|
-
class HeadMusic::Style::
|
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
|
4
|
-
module HeadMusic::Style::
|
3
|
+
# Module for style guidelines.
|
4
|
+
module HeadMusic::Style::Guidelines; end
|
5
5
|
|
6
6
|
# A counterpoint guideline
|
7
|
-
class HeadMusic::Style::
|
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
|
4
|
-
module HeadMusic::Style::
|
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::
|
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
|
4
|
-
module HeadMusic::Style::
|
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::
|
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
|
4
|
-
module HeadMusic::Style::
|
3
|
+
# Module for style guidelines.
|
4
|
+
module HeadMusic::Style::Guidelines; end
|
5
5
|
|
6
6
|
# A counterpoint guideline
|
7
|
-
class HeadMusic::Style::
|
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
|
4
|
-
module HeadMusic::Style::
|
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::
|
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
|