lib/coltrane_instruments/guitar/chord.rb in coltrane-2.0.0 vs lib/coltrane_instruments/guitar/chord.rb in coltrane-2.1.0
- old
+ new
@@ -2,40 +2,167 @@
module ColtraneInstruments
module Guitar
# This class represents a group of guitar notes, strummed at the same time
class Chord
- def initialize(target_chord, guitar_notes: [],
+ include Comparable
+ attr_reader :guitar_notes, :guitar, :free_fingers, :target_chord, :barre
+
+ MAX_FRET_SPAN = 3
+
+ def self.find(chord)
+ new(chord).fetch_descendant_chords
+ end
+
+ def initialize(target_chord,
+ guitar_notes: [],
free_fingers: 4,
barre: nil,
guitar:)
+
@target_chord = target_chord
@guitar_notes = guitar_notes
@free_fingers = free_fingers
- @barre = barre
+ @guitar = guitar
+ @barre = barre
end
+ def <=>(other)
+ rank <=> other.rank
+ end
+
+ def rank
+ +completeness * 10000 +
+ +fullness * 1000 +
+ -spreadness * 100 +
+ +easyness * 1
+ end
+
+ def analysis
+ {
+ completeness: completeness,
+ fullness: fullness,
+ easyness: easyness,
+ spreadness: spreadness
+ }
+ end
+
+ def spreadness
+ fret_range.size.to_f / MAX_FRET_SPAN
+ end
+
+ def completeness
+ (target_chord.notes.size.to_f - notes_left.size) / target_chord.notes.size
+ end
+
+ def easyness
+ frets.count(0).to_f / guitar_notes.size
+ end
+
+ def fullness
+ (guitar.strings.size.to_f - frets.count(nil)) / guitar.strings.size
+ end
+
def barre?
!@barre.nil?
end
def fetch_descendant_chords
- return self if guitar_notes.size < guitar.strings
- possible_new_notes.reduce([]) do |memo, n|
- fingers_change = n.fret == @barre ? 0 : 1
- return self unless (@free_fingers - fingers_change).negative?
- guitar.create_chord target_chord,
- guitar_notes: @guitar_notes + n,
- free_fingers: @free_fingers - fingers_change,
- barre: @barre
- .fetch_descendant_chords + memo
+ 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?
+ Chord.new(target_chord, guitar_notes: guitar_notes + [n],
+ free_fingers: free_fingers - fingers_change,
+ guitar: guitar,
+ barre: barre)
+ .fetch_descendant_chords + memo
end
end
- def possible_new_notes; end
+ def notes_available
+ strings_available - notes_left.size
+ end
+ def strings_available
+ guitar.strings.size - guitar_notes.size
+ end
+
+ def notes_left
+ @notes_left ||= (target_chord.notes - Coltrane::NoteSet[
+ *guitar_notes.map do |n|
+ next if n.pitch.nil?
+ n.pitch.pitch_class
+ end
+ ])
+ end
+
+ def target_notes
+ notes_left.any? ? notes_left : target_chord.notes
+ end
+
+ def notes
+ guitar_notes.map(&:pitch)
+ end
+
+ def frets
+ @frets ||= guitar_notes.map(&:fret)
+ end
+
+ def non_zero_frets
+ frets.reject {|f| f.nil? || f.zero? }
+ end
+
+ def lowest_fret
+ non_zero_frets.any? ? non_zero_frets.min : 0
+ end
+
+ def highest_fret
+ non_zero_frets.max || 0
+ end
+
+ def fret_range
+ (lowest_fret..highest_fret)
+ end
+
+ def lowest_possible_fret
+ lowest_fret.zero? ? 0 : [(lowest_fret - possible_span), 0].max
+ end
+
+ def highest_possible_fret
+ [(possible_span + (highest_fret == 0 ? guitar.frets : highest_fret)), guitar.frets].min
+ end
+
+ def possible_span
+ MAX_FRET_SPAN - fret_range.size
+ end
+
+ def fret_expansion_range
+ (lowest_possible_fret..highest_possible_fret).to_a +
+ [(0 unless barre?)].compact
+ end
+
+ def to_s
+ guitar_notes.map { |n| n.fret.nil? ? 'x' : n.fret }.join('-')
+ end
+
+ def voicing
+ Coltrane::Voicing.new(pitches: guitar_notes.map(&:pitch).compact)
+ end
+
private
attr_writer :barre
+
+ def next_string
+ guitar.strings[guitar_notes.size]
+ end
+
+ 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
end
end
end