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.move(root: -1, seventh: 1)
# Root down one octave, seventh up one octave
Duplicate - Add copies of chord tones in other octaves:
chord.duplicate(root: -2, third: [-1, 1])
# Add root 2 octaves down, third 1 octave down and 1 up
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(**octaves) ⇒ Chord
readonly
Creates new chord with positions duplicated in other octaves.
-
#move(**octaves) ⇒ Chord
readonly
Creates new chord with positions moved to different octaves.
-
#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.
-
#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.
Instance Attribute Details
#chord_definition ⇒ ChordDefinition (readonly)
Chord definition template.
321 322 323 |
# File 'lib/musa-dsl/music/chords.rb', line 321 def chord_definition @chord_definition end |
#duplicate(**octaves) ⇒ Chord (readonly)
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.
329 330 331 |
# File 'lib/musa-dsl/music/chords.rb', line 329 def duplicate @duplicate end |
#move(**octaves) ⇒ Chord (readonly)
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.
325 326 327 |
# File 'lib/musa-dsl/music/chords.rb', line 325 def move @move end |
#scale ⇒ Scale? (readonly)
Scale context (nil if chord contains non-diatonic notes).
317 318 319 |
# File 'lib/musa-dsl/music/chords.rb', line 317 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.
142 143 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 |
# File 'lib/musa-dsl/music/chords.rb', line 142 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.
477 478 479 480 481 |
# File 'lib/musa-dsl/music/chords.rb', line 477 def ==(other) self.class == other.class && @sorted_notes == other.notes && @chord_definition == other.chord_definition end |
#features ⇒ Hash{Symbol => Symbol}
Returns chord features.
367 368 369 |
# File 'lib/musa-dsl/music/chords.rb', line 367 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.
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 |
# File 'lib/musa-dsl/music/chords.rb', line 391 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.
486 487 488 |
# File 'lib/musa-dsl/music/chords.rb', line 486 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.
339 340 341 |
# File 'lib/musa-dsl/music/chords.rb', line 339 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).
424 425 426 427 428 429 430 |
# File 'lib/musa-dsl/music/chords.rb', line 424 def octave(octave) source_notes_map = @source_notes_map.transform_values do |notes| notes.collect { |note| note.octave(octave) }.freeze end.freeze Chord.new(@root.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.
356 357 358 359 |
# File 'lib/musa-dsl/music/chords.rb', line 356 def pitches(*grades) grades = @notes_map.keys if grades.empty? @sorted_notes.select { |_| grades.include?(_.grade) }.collect { |_| _.note.pitch } end |