Class: Musa::Chords::ChordDefinition
- Defined in:
- lib/musa-dsl/music/chord-definition.rb
Overview
Chord template defining structure and features.
ChordDefinition is a template that specifies the intervals and characteristics of a chord type. It's defined once and used to create many chord instances.
Components
- Name: Unique identifier (:maj, :min, :dom7, etc.)
- Offsets: Semitone intervals from root ({ root: 0, third: 4, fifth: 7 })
- Features: Characteristics (quality: :major, size: :triad)
Registration
Chord definitions are registered globally:
ChordDefinition.register :maj,
quality: :major,
size: :triad,
offsets: { root: 0, third: 4, fifth: 7 }
Finding Definitions
By name:
ChordDefinition[:maj] # => <ChordDefinition :maj>
By features:
ChordDefinition.find_by_features(quality: :major, size: :triad)
# => [<ChordDefinition :maj>]
By pitches:
ChordDefinition.find_by_pitches([60, 64, 67]) # C E G
# => <ChordDefinition :maj>
Instance Attribute Summary collapse
-
#features ⇒ Hash{Symbol => Symbol}
readonly
Chord features (quality, size, etc.).
-
#name ⇒ Symbol
readonly
Chord name.
-
#pitch_names ⇒ Hash{Integer => Symbol}
readonly
Position names by semitone offset.
-
#pitch_offsets ⇒ Hash{Symbol => Integer}
readonly
Semitone offsets by position name.
Class Method Summary collapse
-
.feature_key_of(feature_value) ⇒ Symbol
Returns feature key for a feature value.
-
.feature_keys ⇒ Set<Symbol>
Returns all registered feature keys.
-
.feature_values ⇒ Array<Symbol>
Returns all registered feature values.
-
.features_from(values = nil, hash = nil) ⇒ Hash
Converts feature values to feature hash.
-
.find_by_features(*values, **hash) ⇒ Array<ChordDefinition>
Finds definitions matching specified features.
-
.find_by_pitches(pitches) ⇒ ChordDefinition?
Finds chord definition matching a set of pitches.
-
.get(name) ⇒ ChordDefinition?
(also: [])
Retrieves a registered chord definition by name.
-
.register(name, offsets:, **features) ⇒ self
Registers a new chord definition.
Instance Method Summary collapse
-
#in_scale?(scale, chord_root_pitch:) ⇒ Boolean
Checks if chord fits within a scale.
-
#initialize(name, offsets:, **features) ⇒ ChordDefinition
constructor
private
Creates a chord definition.
-
#inspect ⇒ String
(also: #to_s)
Returns string representation.
-
#matches(pitches) ⇒ Boolean
Checks if pitches match this chord definition.
-
#named_pitches(elements_or_pitches) {|element| ... } ⇒ Hash{Symbol => Array}
private
Maps elements to named chord positions.
-
#pitches(root_pitch) ⇒ Array<Integer>
Calculates chord pitches from root pitch.
Constructor Details
#initialize(name, offsets:, **features) ⇒ ChordDefinition
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Creates a chord definition.
273 274 275 276 277 278 279 |
# File 'lib/musa-dsl/music/chord-definition.rb', line 273 def initialize(name, offsets:, **features) @name = name.freeze @features = features.transform_values(&:dup).transform_values(&:freeze).freeze @pitch_offsets = offsets.dup.freeze @pitch_names = offsets.collect { |k, v| [v, k] }.to_h.freeze freeze end |
Instance Attribute Details
#features ⇒ Hash{Symbol => Symbol} (readonly)
Chord features (quality, size, etc.).
287 288 289 |
# File 'lib/musa-dsl/music/chord-definition.rb', line 287 def features @features end |
#name ⇒ Symbol (readonly)
Chord name.
283 284 285 |
# File 'lib/musa-dsl/music/chord-definition.rb', line 283 def name @name end |
#pitch_names ⇒ Hash{Integer => Symbol} (readonly)
Position names by semitone offset.
295 296 297 |
# File 'lib/musa-dsl/music/chord-definition.rb', line 295 def pitch_names @pitch_names end |
#pitch_offsets ⇒ Hash{Symbol => Integer} (readonly)
Semitone offsets by position name.
291 292 293 |
# File 'lib/musa-dsl/music/chord-definition.rb', line 291 def pitch_offsets @pitch_offsets end |
Class Method Details
.feature_key_of(feature_value) ⇒ Symbol
Returns feature key for a feature value.
248 249 250 |
# File 'lib/musa-dsl/music/chord-definition.rb', line 248 def self.feature_key_of(feature_value) @features_by_value[feature_value] end |
.feature_keys ⇒ Set<Symbol>
Returns all registered feature keys.
262 263 264 |
# File 'lib/musa-dsl/music/chord-definition.rb', line 262 def self.feature_keys @feature_keys end |
.feature_values ⇒ Array<Symbol>
Returns all registered feature values.
255 256 257 |
# File 'lib/musa-dsl/music/chord-definition.rb', line 255 def self.feature_values @features_by_value.keys end |
.features_from(values = nil, hash = nil) ⇒ Hash
Converts feature values to feature hash.
220 221 222 223 224 225 226 227 228 |
# File 'lib/musa-dsl/music/chord-definition.rb', line 220 def self.features_from(values = nil, hash = nil) values ||= [] hash ||= {} features = hash.dup values.each { |v| features[@features_by_value[v]] = v } features end |
.find_by_features(*values, **hash) ⇒ Array<ChordDefinition>
Finds definitions matching specified features.
239 240 241 242 |
# File 'lib/musa-dsl/music/chord-definition.rb', line 239 def self.find_by_features(*values, **hash) features = features_from(values, hash) @definitions.values.select { |d| features <= d.features } end |
.find_by_pitches(pitches) ⇒ ChordDefinition?
Finds chord definition matching a set of pitches.
Identifies chord by comparing pitch intervals, accounting for octave reduction.
207 208 209 |
# File 'lib/musa-dsl/music/chord-definition.rb', line 207 def self.find_by_pitches(pitches) @definitions.values.find { |d| d.matches(pitches) } end |
.get(name) ⇒ ChordDefinition? Also known as: []
Retrieves a registered chord definition by name.
132 133 134 |
# File 'lib/musa-dsl/music/chord-definition.rb', line 132 def self.get(name) @definitions[name] end |
.register(name, offsets:, **features) ⇒ self
Registers a new chord definition.
Creates and registers a chord definition with specified intervals and features. The definition becomes available globally for chord creation.
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/musa-dsl/music/chord-definition.rb', line 168 def self.register(name, offsets:, **features) definition = ChordDefinition.new(name, offsets: offsets, **features) @definitions ||= {} # Registering over an existing name used to replace it silently, and a # chord definition is global: the piece that registered first goes on # asking for its own chord and gets somebody else's. Refuse instead, and # say what is already there. if (existing = @definitions[definition.name]) raise ArgumentError, "chord definition #{definition.name.inspect} is already registered " \ "as #{existing.features.inspect}; unregister it first if you mean to replace it" end @definitions[definition.name] = definition @features_by_value ||= {} definition.features.each { |k, v| @features_by_value[v] = k } @feature_keys ||= Set[] features.each_key { |feature_name| @feature_keys << feature_name } self end |
Instance Method Details
#in_scale?(scale, chord_root_pitch:) ⇒ Boolean
Checks if chord fits within a scale.
316 317 318 |
# File 'lib/musa-dsl/music/chord-definition.rb', line 316 def in_scale?(scale, chord_root_pitch:) !pitches(chord_root_pitch).find { |chord_pitch| scale.note_of_pitch(chord_pitch).nil? } end |
#inspect ⇒ String Also known as: to_s
Returns string representation.
370 371 372 |
# File 'lib/musa-dsl/music/chord-definition.rb', line 370 def inspect "<ChordDefinition: name = #{@name} features = #{@features} pitch_offsets = #{@pitch_offsets}>" end |
#matches(pitches) ⇒ Boolean
Checks if pitches match this chord definition.
Compares octave-reduced pitch sets to determine if they form this chord.
359 360 361 362 363 364 365 |
# File 'lib/musa-dsl/music/chord-definition.rb', line 359 def matches(pitches) reduced_pitches = octave_reduce(pitches).uniq !!reduced_pitches.find do |candidate_root_pitch| reduced_pitches.sort == octave_reduce(pitches(candidate_root_pitch)).uniq.sort end end |
#named_pitches(elements_or_pitches) {|element| ... } ⇒ Hash{Symbol => Array}
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Maps elements to named chord positions.
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 |
# File 'lib/musa-dsl/music/chord-definition.rb', line 327 def named_pitches(elements_or_pitches, &block) pitches = elements_or_pitches.collect do |element_or_pitch| [if block_given? yield element_or_pitch else element_or_pitch end, element_or_pitch] end.to_h root_pitch = pitches.keys.find do |candidate_root_pitch| candidate_pitches = pitches.keys.collect { |p| p - candidate_root_pitch } octave_reduce(candidate_pitches).uniq == octave_reduce(@pitch_offsets.values).uniq end # TODO: OJO: problema con las notas duplicadas, con la identificación de inversiones y con las notas a distancias de más de una octava pitches.collect do |pitch, element| [@pitch_names[pitch - root_pitch], [element]] end.to_h end |
#pitches(root_pitch) ⇒ Array<Integer>
Calculates chord pitches from root pitch.
304 305 306 |
# File 'lib/musa-dsl/music/chord-definition.rb', line 304 def pitches(root_pitch) @pitch_offsets.values.collect { |offset| root_pitch + offset } end |