Class: HeadMusic::Style::Annotation
- Inherits:
-
Object
- Object
- HeadMusic::Style::Annotation
show all
- Defined in:
- lib/head_music/style/annotation.rb
Overview
An Annotation encapsulates an issue with or comment on a voice
Direct Known Subclasses
Guidelines::AllowedRhythmicValuesForCombined123, Guidelines::AllowedRhythmicValuesForFifthSpecies, Guidelines::AlwaysMove, Guidelines::ApproachPerfectionContrarily, Guidelines::AvoidCrossingVoices, Guidelines::AvoidOverlappingVoices, Guidelines::ConsonantClimax, Guidelines::ConsonantDownbeats, Guidelines::Contoured, Guidelines::Diatonic, Guidelines::DirectionChanges, Guidelines::DirectionalStepToFinalNote, Guidelines::EndOnPerfectConsonance, Guidelines::EndOnTonic, Guidelines::FirstBarEntry, Guidelines::FloridDissonanceTreatment, Guidelines::LargeLeaps, Guidelines::LimitOctaveLeaps, Guidelines::MaximumNotes, Guidelines::MinimumMelodicIntervals, Guidelines::MinimumNotes, Guidelines::MixedRhythmicValues, Guidelines::MostlyConjunct, Guidelines::NoParallelPerfectAcrossBarline, Guidelines::NoParallelPerfectOnDownbeats, Guidelines::NoParallelPerfectWithSyncopation, Guidelines::NoRests, Guidelines::NoRestsAfterNote, Guidelines::NoStrongBeatUnisons, Guidelines::NoUnisonsInMiddle, Guidelines::NoteCountPerBar, Guidelines::NoteFillsFinalBar, Guidelines::NotesSameLength, Guidelines::OneToOne, Guidelines::OneToOneWithTies, Guidelines::PreferContraryMotion, Guidelines::PreferImperfect, Guidelines::PrepareOctaveLeaps, Guidelines::SingableIntervals, Guidelines::SingableRange, Guidelines::StartOnPerfectConsonance, Guidelines::StartOnTonic, Guidelines::StepOutOfUnison, Guidelines::StepToFinalNote, Guidelines::SuspensionTreatment, Guidelines::WeakBeatDissonanceTreatment
Defined Under Namespace
Classes: Configured
Constant Summary
collapse
- MESSAGE =
"Write music."
- DEFAULT_WEIGHT =
1.0
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
Constructor Details
#initialize(voice, **options) ⇒ Annotation
Returns a new instance of Annotation.
28
29
30
31
|
# File 'lib/head_music/style/annotation.rb', line 28
def initialize(voice, **options)
@voice = voice
@options = options
end
|
Instance Attribute Details
#options ⇒ Object
Returns the value of attribute options.
126
127
128
|
# File 'lib/head_music/style/annotation.rb', line 126
def options
@options
end
|
#voice ⇒ Object
Returns the value of attribute voice.
7
8
9
|
# File 'lib/head_music/style/annotation.rb', line 7
def voice
@voice
end
|
Class Method Details
.default_gate? ⇒ Boolean
43
44
45
|
# File 'lib/head_music/style/annotation.rb', line 43
def self.default_gate?
false
end
|
.default_weight ⇒ Object
39
40
41
|
# File 'lib/head_music/style/annotation.rb', line 39
def self.default_weight
DEFAULT_WEIGHT
end
|
.with(**options) ⇒ Object
Wraps a guideline class with preset options so it can live in a RULESET
and still be instantiated with just a voice, e.g. MinimumNotes.with(minimum: 5).
35
36
37
|
# File 'lib/head_music/style/annotation.rb', line 35
def self.with(**options)
Configured.new(self, options)
end
|
Instance Method Details
#adherent? ⇒ Boolean
88
89
90
|
# File 'lib/head_music/style/annotation.rb', line 88
def adherent?
fitness == 1
end
|
#bass_voice? ⇒ Boolean
161
162
163
|
# File 'lib/head_music/style/annotation.rb', line 161
def bass_voice?
lower_voices.empty?
end
|
#cantus_firmus ⇒ Object
143
144
145
|
# File 'lib/head_music/style/annotation.rb', line 143
def cantus_firmus
@cantus_firmus ||= other_voices.detect(&:cantus_firmus?) || other_voices.first
end
|
#diatonic_interval_from_tonic(note) ⇒ Object
#downbeat_harmonic_intervals ⇒ Object
175
176
177
178
179
180
|
# File 'lib/head_music/style/annotation.rb', line 175
def downbeat_harmonic_intervals
@downbeat_harmonic_intervals ||=
cantus_firmus.notes
.map { |note| HeadMusic::Analysis::HarmonicInterval.new(note.voice, voice, note.position) }
.reject { |interval| interval.notes.length < 2 }
end
|
#end_position ⇒ Object
108
109
110
|
# File 'lib/head_music/style/annotation.rb', line 108
def end_position
[marks].flatten.compact.map(&:end_position).max
end
|
#first_note ⇒ Object
116
117
118
|
# File 'lib/head_music/style/annotation.rb', line 116
def first_note
notes.first
end
|
#fitness ⇒ Object
81
82
83
84
85
86
|
# File 'lib/head_music/style/annotation.rb', line 81
def fitness
mark_fitnesses = [marks].flatten.compact.map(&:fitness)
return 1.0 if mark_fitnesses.empty?
mark_fitnesses.reduce(1, :*)**(1.0 / [fitness_denominator, 1].max)
end
|
#fitness_denominator ⇒ Object
Normalization rate for the product of mark fitnesses. Subclasses override
(e.g. with an opportunity count) to score by violation rate rather than
raw violation count. The default of 1 preserves the raw product.
131
132
133
|
# File 'lib/head_music/style/annotation.rb', line 131
def fitness_denominator
1
end
|
#gate? ⇒ Boolean
96
97
98
|
# File 'lib/head_music/style/annotation.rb', line 96
def gate?
options.fetch(:gate, self.class.default_gate?)
end
|
#harmonic_intervals ⇒ Object
182
183
184
185
186
187
|
# File 'lib/head_music/style/annotation.rb', line 182
def harmonic_intervals
@harmonic_intervals ||=
positions
.map { |position| HeadMusic::Analysis::HarmonicInterval.new(cantus_firmus, voice, position) }
.reject { |harmonic_interval| harmonic_interval.notes.length < 2 }
end
|
#has_notes? ⇒ Boolean
100
101
102
|
# File 'lib/head_music/style/annotation.rb', line 100
def has_notes?
!!first_note
end
|
#higher_voices ⇒ Object
147
148
149
|
# File 'lib/head_music/style/annotation.rb', line 147
def higher_voices
@higher_voices ||= unsorted_higher_voices.sort_by(&:highest_pitch).reverse
end
|
#last_note ⇒ Object
120
121
122
|
# File 'lib/head_music/style/annotation.rb', line 120
def last_note
notes.last
end
|
#lower_voices ⇒ Object
151
152
153
|
# File 'lib/head_music/style/annotation.rb', line 151
def lower_voices
@lower_voices ||= unsorted_lower_voices.sort_by(&:lowest_pitch).reverse
end
|
#message ⇒ Object
112
113
114
|
# File 'lib/head_music/style/annotation.rb', line 112
def message
self.class::MESSAGE
end
|
#motions ⇒ Object
169
170
171
172
173
|
# File 'lib/head_music/style/annotation.rb', line 169
def motions
downbeat_harmonic_intervals.each_cons(2).map do |harmonic_interval_pair|
HeadMusic::Analysis::Motion.new(*harmonic_interval_pair)
end
end
|
#other_voices ⇒ Object
139
140
141
|
# File 'lib/head_music/style/annotation.rb', line 139
def other_voices
@other_voices ||= voices.reject { |part| part == voice }
end
|
#positions ⇒ Object
189
190
191
192
|
# File 'lib/head_music/style/annotation.rb', line 189
def positions
@positions ||=
voices.map(&:notes).flatten.map(&:position).sort.uniq(&:to_s)
end
|
#start_position ⇒ Object
104
105
106
|
# File 'lib/head_music/style/annotation.rb', line 104
def start_position
[marks].flatten.compact.map(&:start_position).min
end
|
#starts_on_tonic? ⇒ Boolean
165
166
167
|
# File 'lib/head_music/style/annotation.rb', line 165
def starts_on_tonic?
tonic_spelling == first_note.spelling
end
|
#tonic_pitch ⇒ Object
202
203
204
|
# File 'lib/head_music/style/annotation.rb', line 202
def tonic_pitch
@tonic_pitch ||= HeadMusic::Rudiment::Pitch.get(tonic_spelling)
end
|
#unsorted_higher_voices ⇒ Object
194
195
196
|
# File 'lib/head_music/style/annotation.rb', line 194
def unsorted_higher_voices
other_voices.select { |part| part.highest_pitch && highest_pitch && part.highest_pitch > highest_pitch }
end
|
#unsorted_lower_voices ⇒ Object
198
199
200
|
# File 'lib/head_music/style/annotation.rb', line 198
def unsorted_lower_voices
other_voices.select { |part| part.lowest_pitch && lowest_pitch && part.lowest_pitch < lowest_pitch }
end
|
#voices ⇒ Object
135
136
137
|
# File 'lib/head_music/style/annotation.rb', line 135
def voices
@voices ||= voice.composition.voices
end
|
#weight ⇒ Object
92
93
94
|
# File 'lib/head_music/style/annotation.rb', line 92
def weight
options.fetch(:weight, self.class.default_weight)
end
|