Class: Musa::Chords::Chord
Overview
Instantiated chord with specific root and scale context.
Chord represents an actual chord instance with a root note, scale context, and chord definition. It provides access to chord tones, voicing modifications, and navigation between related chords.
Creation
Chords are typically created from scale notes rather than directly:
scale = Scales::Scales.default_system.default_tuning.major[60]
chord = scale.tonic.chord # C major triad
chord = scale.tonic.chord :seventh # C major seventh
chord = scale.dominant.chord :ninth # G ninth chord
Accessing Chord Tones
Chord tones are accessed by their position name (root, third, fifth, etc.):
chord.root # Returns NoteInScale for root
chord.third # Returns NoteInScale for third
chord.fifth # Returns NoteInScale for fifth
chord.seventh # Returns NoteInScale for seventh (if exists)
When notes are duplicated, use all: true to get all instances:
chord.root(all: true) # Returns array of all root notes
Features and Navigation
Chords have features (quality, size) and can navigate to related chords:
chord.features # => { quality: :major, size: :triad }
chord.quality # => :major (dynamic method)
chord.size # => :triad (dynamic method)
chord.with_quality(:minor) # Change to minor
chord.with_size(:seventh) # Add seventh
chord.featuring(size: :ninth) # Change multiple features
Voicing Modifications
Move - Relocate specific chord tones to different octaves:
chord.with_move(root: -1, seventh: 1)
# Root down one octave, seventh up one octave
chord.move # => { root: -1, seventh: 1 } (current settings)
Duplicate - Add copies of chord tones in other octaves:
chord.with_duplicate(root: -2, third: [-1, 1])
# Add root 2 octaves down, third 1 octave down and 1 up
chord.duplicate # => { root: -2, third: [-1, 1] } (current settings)
Octave - Transpose entire chord:
chord.octave(-1) # Move entire chord down one octave
Pitch Extraction
chord.pitches # All pitches sorted by pitch
chord.pitches(:root, :third) # Only specified chord tones
chord.notes # Sorted ChordGradeNote structs
Scale Context
Chords maintain their scale context. When navigating to chords with non-diatonic notes (e.g., major to minor), the scale may become nil:
major_chord = c_major.tonic.chord
major_chord.scale # => C major scale
minor_chord = major_chord.with_quality(:minor)
minor_chord.scale # => nil (Eb not in C major)
Instance Attribute Summary collapse
-
#chord_definition ⇒ ChordDefinition
readonly
Chord definition template.
-
#duplicate ⇒ Hash{Symbol => Integer, Array<Integer>}
readonly
Octave duplications applied to positions.
-
#move ⇒ Hash{Symbol => Integer}
readonly
Octave moves applied to positions.
-
#scale ⇒ Scale?
readonly
Scale context (nil if chord contains non-diatonic notes).
Class Method Summary collapse
-
.with_root(root_note_or_pitch_or_symbol, scale: nil, allow_chromatic: false, name: nil, move: nil, duplicate: nil, **features) ⇒ Chord
Creates a chord with specified root.
Instance Method Summary collapse
-
#==(other) ⇒ Boolean
Checks chord equality.
-
#as_chord_in_scale(scale) ⇒ Chord?
Creates an equivalent chord with the given scale as its context.
-
#features ⇒ Hash{Symbol => Symbol}
Returns chord features.
-
#featuring(*values, allow_chromatic: false, **hash) ⇒ Chord
Creates new chord with modified features.
-
#inspect ⇒ String
(also: #to_s)
Returns string representation.
-
#notes ⇒ Array<ChordGradeNote>
Returns chord notes sorted by pitch.
-
#octave(octave) ⇒ Chord
Transposes entire chord to a different octave.
-
#pitches(*grades) ⇒ Array<Integer>
Returns MIDI pitches of chord notes.
-
#search_in_scales(roots: nil, **metadata) ⇒ Array<Chord>
Finds this chord in other scales.
-
#with_duplicate(**octaves) ⇒ Chord
Creates new chord with positions duplicated in other octaves.
-
#with_move(**octaves) ⇒ Chord
Creates new chord with positions moved to different octaves.
Instance Attribute Details
#chord_definition ⇒ ChordDefinition (readonly)
Chord definition template.
323 324 325 |
# File 'lib/musa-dsl/music/chords.rb', line 323 def chord_definition @chord_definition end |
#duplicate ⇒ Hash{Symbol => Integer, Array<Integer>} (readonly)
Octave duplications applied to positions.
331 332 333 |
# File 'lib/musa-dsl/music/chords.rb', line 331 def duplicate @duplicate end |
#move ⇒ Hash{Symbol => Integer} (readonly)
Octave moves applied to positions.
327 328 329 |
# File 'lib/musa-dsl/music/chords.rb', line 327 def move @move end |
#scale ⇒ Scale? (readonly)
Scale context (nil if chord contains non-diatonic notes).
319 320 321 |
# File 'lib/musa-dsl/music/chords.rb', line 319 def scale @scale end |
Class Method Details
.with_root(root_note_or_pitch_or_symbol, scale: nil, allow_chromatic: false, name: nil, move: nil, duplicate: nil, **features) ⇒ Chord
Creates a chord with specified root.
Factory method for creating chords by specifying the root note and either a chord definition name or features. The root can be a NoteInScale, pitch number, or scale degree symbol.
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/musa-dsl/music/chords.rb', line 144 def self.with_root(root_note_or_pitch_or_symbol, scale: nil, allow_chromatic: false, name: nil, move: nil, duplicate: nil, **features) root = case root_note_or_pitch_or_symbol when Scales::NoteInScale root_note_or_pitch_or_symbol when Numeric if scale scale.note_of_pitch(root_note_or_pitch_or_symbol, allow_chromatic: allow_chromatic) else scale = Musa::Scales::Scales.default_system.default_tuning[root_note_or_pitch_or_symbol].major scale.note_of_pitch(root_note_or_pitch_or_symbol) end when Symbol raise ArgumentError, "Missing scale parameter to calculate root note for #{root_note_or_pitch_or_symbol}" unless scale scale[root_note_or_pitch_or_symbol] else raise ArgumentError, "Unexpected #{root_note_or_pitch_or_symbol}" end scale ||= root.scale if name raise ArgumentError, "Received name parameter with value #{name}: features parameter is not allowed" if features.any? chord_definition = ChordDefinition[name] elsif features.any? chord_definition = Helper.find_definition_by_features(root.pitch, features, scale, allow_chromatic: allow_chromatic) else raise ArgumentError, "Don't know how to find a chord definition without name or features parameters" end unless chord_definition raise ArgumentError, "Unable to find chord definition for root #{root}" \ "#{" with name #{name}" if name}" \ "#{" with features #{features}" if features.any?}" end source_notes_map = Helper.compute_source_notes_map(root, chord_definition, scale) Chord.new(root, scale, chord_definition, move, duplicate, source_notes_map) end |
Instance Method Details
#==(other) ⇒ Boolean
Checks chord equality.
Chords are equal if they have the same notes and chord definition.
544 545 546 547 548 |
# File 'lib/musa-dsl/music/chords.rb', line 544 def ==(other) self.class == other.class && @sorted_notes == other.notes && @chord_definition == other.chord_definition end |
#as_chord_in_scale(scale) ⇒ Chord?
Creates an equivalent chord with the given scale as its context.
Returns a new Chord object representing the same chord but with the specified scale as its harmonic context. Returns nil if the chord is not contained in the scale.
523 524 525 526 527 528 529 530 531 532 533 534 535 536 |
# File 'lib/musa-dsl/music/chords.rb', line 523 def as_chord_in_scale(scale) return nil unless scale.contains_chord?(self) root_note = scale.note_of_pitch(@root.pitch, allow_chromatic: false) return nil unless root_note Chord.with_root( root_note, scale: scale, name: @chord_definition.name, move: @move.empty? ? nil : @move, duplicate: @duplicate.empty? ? nil : @duplicate ) end |
#features ⇒ Hash{Symbol => Symbol}
Returns chord features.
369 370 371 |
# File 'lib/musa-dsl/music/chords.rb', line 369 def features @chord_definition.features end |
#featuring(*values, allow_chromatic: false, **hash) ⇒ Chord
Creates new chord with modified features.
Returns a new chord with the same root but different features. Features can be specified as values (converted to feature hash) or as keyword arguments.
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 |
# File 'lib/musa-dsl/music/chords.rb', line 393 def featuring(*values, allow_chromatic: false, **hash) # create a new list of features based on current features but # replacing the values for the new ones and adding the new features # features = @chord_definition.features.dup ChordDefinition.features_from(values, hash).each { |k, v| features[k] = v } chord_definition = Helper.find_definition_by_features(@root.pitch, features, @scale, allow_chromatic: allow_chromatic) raise ArgumentError, "Unable to find a chord definition for #{features}" unless chord_definition source_notes_map = Helper.compute_source_notes_map(@root, chord_definition, @scale) Chord.new(@root, (@scale if chord_definition.in_scale?(@scale, chord_root_pitch: @root.pitch)), chord_definition, @move, @duplicate, source_notes_map) end |
#inspect ⇒ String Also known as: to_s
Returns string representation.
553 554 555 |
# File 'lib/musa-dsl/music/chords.rb', line 553 def inspect "<Chord #{@name} root #{@root} notes #{@sorted_notes.collect { |_| "#{_.grade}=#{_.note.grade}|#{_.note.pitch} "} }>" end |
#notes ⇒ Array<ChordGradeNote>
Returns chord notes sorted by pitch.
341 342 343 |
# File 'lib/musa-dsl/music/chords.rb', line 341 def notes @sorted_notes end |
#octave(octave) ⇒ Chord
Transposes entire chord to a different octave.
Moves all chord notes by the specified octave offset, preserving internal voicing structure (moves and duplications).
426 427 428 429 430 431 432 |
# File 'lib/musa-dsl/music/chords.rb', line 426 def octave(octave) source_notes_map = @source_notes_map.transform_values do |notes| notes.collect { |note| note.at_octave(octave) }.freeze end.freeze Chord.new(@root.at_octave(octave), @scale, chord_definition, @move, @duplicate, source_notes_map) end |
#pitches(*grades) ⇒ Array<Integer>
Returns MIDI pitches of chord notes.
Without arguments, returns all pitches sorted from low to high. With grade arguments, returns only pitches for those positions.
358 359 360 361 |
# File 'lib/musa-dsl/music/chords.rb', line 358 def pitches(*grades) grades = @notes_map.keys if grades.empty? @sorted_notes.select { |_| grades.include?(_.grade) }.collect { |_| _.note.pitch } end |
#search_in_scales(roots: nil, **metadata) ⇒ Array<Chord>
Finds this chord in other scales.
Searches through scale kinds matching the given metadata criteria to find all scales that contain this chord. Returns new chord instances, each with its containing scale as context.
499 500 501 502 |
# File 'lib/musa-dsl/music/chords.rb', line 499 def search_in_scales(roots: nil, **) tuning = @scale&.kind&.tuning || @root.scale.kind.tuning tuning.search_chord_in_scales(self, roots: roots, **) end |
#with_duplicate(**octaves) ⇒ Chord
Creates new chord with positions duplicated in other octaves.
Adds copies of specific chord positions in different octaves. Original positions remain at their current octave. Merges with existing duplications.
469 470 471 |
# File 'lib/musa-dsl/music/chords.rb', line 469 def with_duplicate(**octaves) Chord.new(@root, @scale, @chord_definition, @move, @duplicate.merge(octaves), @source_notes_map) end |
#with_move(**octaves) ⇒ Chord
Creates new chord with positions moved to different octaves.
Relocates specific chord positions to different octaves while keeping other positions unchanged. Multiple positions can be moved at once. Merges with existing moves.
448 449 450 |
# File 'lib/musa-dsl/music/chords.rb', line 448 def with_move(**octaves) Chord.new(@root, @scale, @chord_definition, @move.merge(octaves), @duplicate, @source_notes_map) end |