class Coltrane::Representation::Guitar::Chord

This class represents a group of guitar notes, strummed at the same time

Constants

MAX_FRET_SPAN

Attributes

barre[RW]
free_fingers[R]
guitar[R]
guitar_notes[R]
target_chord[R]

Public Class Methods

find(chord, guitar:) click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 13
def self.find(chord, guitar:)
  new(chord, guitar: guitar)
  .fetch_descendant_chords
  .sort
  .reverse
end
find_by_notation(guitar, chord_notation) click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 20
def self.find_by_notation(guitar, chord_notation)
  chord_notation
  .split('-')
  .map
  .with_index do |n, i|
    next if n == 'x'
    n = Guitar::Note.new(guitar.strings[i], n.to_i).pitch.pitch_class
  end
  .yield_self do |notes|
    notes
    .map
    .with_index do |note, index|
      begin
        Theory::Chord.new(notes: notes.rotate(index))
      rescue Theory::ChordNotFoundError
        next
      end
    end
    .compact
    .yield_self do |chords|
      raise(Theory::ChordNotFoundError) if chords.empty?
      chords.compact.uniq &:name
    end
  end
end
new(target_chord, guitar_notes: [], free_fingers: 4, barre: nil, guitar:) click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 46
def initialize(target_chord,
               guitar_notes: [],
               free_fingers: 4,
               barre: nil,
               guitar:)

  @target_chord = target_chord
  @guitar_notes = guitar_notes
  @free_fingers = free_fingers
  @guitar       = guitar
  @barre        = barre
end

Public Instance Methods

<=>(other) click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 63
def <=>(other)
  rank <=> other.rank
end
analysis() click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 75
def analysis
  %i[completeness discontinuity fullness spreadness easyness]
  .reduce({}) do |output, criteria|
    output.merge(criteria => send(criteria).round(2))
  end
  .merge(rank: rank.round(4))
end
barre?() click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 103
def barre?
  !@barre.nil?
end
completeness() click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 87
def completeness
  (target_chord.notes.size.to_f - notes_left.size) / target_chord.notes.size
end
discontinuity() click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 99
def discontinuity
  voicing.discontinuity
end
easyness() click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 91
def easyness
  frets.count(0).to_f / guitar_notes.size
end
fetch_descendant_chords() click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 107
def fetch_descendant_chords
  return [self] if guitar_notes.size >= guitar.strings.size
  possible_new_notes(notes_available.positive?).reduce([]) do |memo, n|
    barre = n.fret if guitar_notes.last == n.fret
    fingers_change = n.fret == barre || n.fret.zero? ? 0 : 1
    next memo if (free_fingers - fingers_change).negative?
    self.class.new(target_chord,
      guitar_notes: guitar_notes + [n],
      free_fingers: free_fingers - fingers_change,
      guitar: guitar,
      barre: barre
    ).fetch_descendant_chords + memo
  end
end
fret_expansion_range() click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 181
def fret_expansion_range
  (lowest_possible_fret..highest_possible_fret).to_a +
    [(0 unless barre?)].compact
end
fret_range() click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 165
def fret_range
  (lowest_fret..highest_fret)
end
frets() click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 149
def frets
  @frets ||= guitar_notes.map(&:fret)
end
fullness() click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 95
def fullness
  (guitar.strings.size.to_f - frets.count(nil)) / guitar.strings.size
end
highest_fret() click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 161
def highest_fret
  non_zero_frets.max || 0
end
highest_possible_fret() click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 173
def highest_possible_fret
  [(possible_span + (highest_fret == 0 ? guitar.frets : highest_fret)), guitar.frets].min
end
lowest_fret() click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 157
def lowest_fret
  non_zero_frets.any? ? non_zero_frets.min : 0
end
lowest_possible_fret() click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 169
def lowest_possible_fret
  lowest_fret.zero? ? 0 : [(lowest_fret - possible_span), 0].max
end
max_fret_span() click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 59
def max_fret_span
  MAX_FRET_SPAN
end
non_zero_frets() click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 153
def non_zero_frets
  frets.reject { |f| f.nil? || f.zero? }
end
notes() click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 145
def notes
  guitar_notes.map(&:pitch)
end
notes_available() click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 122
def notes_available
  strings_available - notes_left.size
end
notes_left() click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 130
def notes_left
  @notes_left ||= begin
                    target_chord.notes - Theory::NoteSet[
                    *guitar_notes.map do |n|
                      next if n.pitch.nil?
                      n.pitch.pitch_class
                    end
                  ]
                  end
end
possible_span() click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 177
def possible_span
  MAX_FRET_SPAN - fret_range.size
end
rank() click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 67
def rank
  +completeness  * 10_000 +
  +fullness      * 1_000 +
  -spreadness    * 10 +
  -discontinuity * 1 +
  +easyness      * 1
end
spreadness() click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 83
def spreadness
  fret_range.size.to_f / MAX_FRET_SPAN
end
strings_available() click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 126
def strings_available
  guitar.strings.size - guitar_notes.size
end
target_notes() click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 141
def target_notes
  notes_left.any? ? notes_left : target_chord.notes
end
to_s(debug = false) click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 186
def to_s(debug = false)
  guitar_notes.map {
    |n| n.fret.nil? ? 'x' : n.fret
  }.join('-') + (debug ? ' ' + analysis.to_s : '')
end
voicing() click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 192
def voicing
  Theory::Voicing.new(pitches: guitar_notes.map(&:pitch).compact)
end

Private Instance Methods

next_string() click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 200
def next_string
  guitar.strings[guitar_notes.size]
end
possible_new_notes(include_mute_note = false) click to toggle source
# File lib/coltrane/representation/guitar/chord.rb, line 204
def possible_new_notes(include_mute_note = false)
  target_notes.notes.map do |note|
    next_string.find(note, possible_frets: fret_expansion_range)
  end.flatten + [(Guitar::Note.new(next_string, nil) if include_mute_note)].compact
end