Class: Coltrane::Representation::Guitar::Chord

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/coltrane/representation/guitar/chord.rb

Overview

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

Constant Summary collapse

MAX_FRET_SPAN =
3

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(target_chord, guitar_notes: [], free_fingers: 4, barre: nil, guitar:) ⇒ Chord

Returns a new instance of Chord.



46
47
48
49
50
51
52
53
54
55
56
57
# 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

Instance Attribute Details

#barreObject

Returns the value of attribute barre.



9
10
11
# File 'lib/coltrane/representation/guitar/chord.rb', line 9

def barre
  @barre
end

#free_fingersObject (readonly)

Returns the value of attribute free_fingers.



9
10
11
# File 'lib/coltrane/representation/guitar/chord.rb', line 9

def free_fingers
  @free_fingers
end

#guitarObject (readonly)

Returns the value of attribute guitar.



9
10
11
# File 'lib/coltrane/representation/guitar/chord.rb', line 9

def guitar
  @guitar
end

#guitar_notesObject (readonly)

Returns the value of attribute guitar_notes.



9
10
11
# File 'lib/coltrane/representation/guitar/chord.rb', line 9

def guitar_notes
  @guitar_notes
end

#target_chordObject (readonly)

Returns the value of attribute target_chord.



9
10
11
# File 'lib/coltrane/representation/guitar/chord.rb', line 9

def target_chord
  @target_chord
end

Class Method Details

.find(chord, guitar:) ⇒ Object



13
14
15
16
17
18
# 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) ⇒ Object



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 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

Instance Method Details

#<=>(other) ⇒ Object



63
64
65
# File 'lib/coltrane/representation/guitar/chord.rb', line 63

def <=>(other)
  rank <=> other.rank
end

#analysisObject



75
76
77
78
79
80
81
# 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?Boolean

Returns:

  • (Boolean)


103
104
105
# File 'lib/coltrane/representation/guitar/chord.rb', line 103

def barre?
  !@barre.nil?
end

#completenessObject



87
88
89
# 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

#discontinuityObject



99
100
101
# File 'lib/coltrane/representation/guitar/chord.rb', line 99

def discontinuity
  voicing.discontinuity
end

#easynessObject



91
92
93
# File 'lib/coltrane/representation/guitar/chord.rb', line 91

def easyness
  frets.count(0).to_f / guitar_notes.size
end

#fetch_descendant_chordsObject



107
108
109
110
111
112
113
114
115
116
117
118
119
120
# 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_rangeObject



181
182
183
184
# 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_rangeObject



165
166
167
# File 'lib/coltrane/representation/guitar/chord.rb', line 165

def fret_range
  (lowest_fret..highest_fret)
end

#fretsObject



149
150
151
# File 'lib/coltrane/representation/guitar/chord.rb', line 149

def frets
  @frets ||= guitar_notes.map(&:fret)
end

#fullnessObject



95
96
97
# File 'lib/coltrane/representation/guitar/chord.rb', line 95

def fullness
  (guitar.strings.size.to_f - frets.count(nil)) / guitar.strings.size
end

#highest_fretObject



161
162
163
# File 'lib/coltrane/representation/guitar/chord.rb', line 161

def highest_fret
  non_zero_frets.max || 0
end

#highest_possible_fretObject



173
174
175
# 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_fretObject



157
158
159
# File 'lib/coltrane/representation/guitar/chord.rb', line 157

def lowest_fret
  non_zero_frets.any? ? non_zero_frets.min : 0
end

#lowest_possible_fretObject



169
170
171
# 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_spanObject



59
60
61
# File 'lib/coltrane/representation/guitar/chord.rb', line 59

def max_fret_span
  MAX_FRET_SPAN
end

#non_zero_fretsObject



153
154
155
# File 'lib/coltrane/representation/guitar/chord.rb', line 153

def non_zero_frets
  frets.reject { |f| f.nil? || f.zero? }
end

#notesObject



145
146
147
# File 'lib/coltrane/representation/guitar/chord.rb', line 145

def notes
  guitar_notes.map(&:pitch)
end

#notes_availableObject



122
123
124
# File 'lib/coltrane/representation/guitar/chord.rb', line 122

def notes_available
  strings_available - notes_left.size
end

#notes_leftObject



130
131
132
133
134
135
136
137
138
139
# 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_spanObject



177
178
179
# File 'lib/coltrane/representation/guitar/chord.rb', line 177

def possible_span
  MAX_FRET_SPAN - fret_range.size
end

#rankObject



67
68
69
70
71
72
73
# File 'lib/coltrane/representation/guitar/chord.rb', line 67

def rank
  +completeness  * 10_000 +
  +fullness      * 1_000 +
  -spreadness    * 10 +
  -discontinuity * 1 +
  +easyness      * 1
end

#spreadnessObject



83
84
85
# File 'lib/coltrane/representation/guitar/chord.rb', line 83

def spreadness
  fret_range.size.to_f / MAX_FRET_SPAN
end

#strings_availableObject



126
127
128
# File 'lib/coltrane/representation/guitar/chord.rb', line 126

def strings_available
  guitar.strings.size - guitar_notes.size
end

#target_notesObject



141
142
143
# 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) ⇒ Object



186
187
188
189
190
# 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

#voicingObject



192
193
194
# File 'lib/coltrane/representation/guitar/chord.rb', line 192

def voicing
  Theory::Voicing.new(pitches: guitar_notes.map(&:pitch).compact)
end