Module: OutroRails::Instruments::Guitar::ChordShapes
- Defined in:
- lib/outro_rails/instruments/guitar/chord_shapes.rb
Overview
The library of guitar chord shapes (CAGED and friends) in standard tuning, keyed by [quality, extension].
Each shape is written relative to its open position: frets are the frets you'd play for the shape's namesake chord (the :c shape is spelled as open C). A shape becomes any other root by shifting every fret up by the same offset. Shapes carry:
root_string – the string whose note names the chord
frets – { string => fret }, string 6 (low) .. 1 (high)
movable_omit – strings dropped when the shape is barred up the
neck (e.g. the awkward pinky notes of the G and A
shapes); open-position voicings keep every string.
Chords without an entry here (9/11/13, altered dominants, added tones, ...) fall back to stub generation - add shapes below to bring them into the generator.
Defined Under Namespace
Classes: Shape
Constant Summary collapse
- CAGED_LETTERS =
The five CAGED families, in their conventional order.
%w[C A G E D].freeze
- OPEN_BASS_MAX_FRET =
Open-chord bass extensions (slash chords for Travis picking etc.) may only reach this far up the neck.
3- SHAPES =
The shape table itself: { [quality, extension] => [Shape, ...] }. A missing key means the generator falls back to stubs.
{ # Full CAGED, ordered C A G E D (order is cosmetic - voicings are # sorted by neck position at generation time). [ :major, nil ] => [ shape(:c, 5, { 5 => 3, 4 => 2, 3 => 0, 2 => 1, 1 => 0 }), shape(:a, 5, { 5 => 0, 4 => 2, 3 => 2, 2 => 2, 1 => 0 }), shape(:g, 6, { 6 => 3, 5 => 2, 4 => 0, 3 => 0, 2 => 0, 1 => 3 }), shape(:e, 6, { 6 => 0, 5 => 2, 4 => 2, 3 => 1, 2 => 0, 1 => 0 }), shape(:d, 4, { 4 => 0, 3 => 2, 2 => 3, 1 => 2 }) ], # The practical minor shapes. The Cm and Gm forms exist in theory # but are unplayable as grips; add them here if you ever want them. [ :minor, nil ] => [ shape(:a_minor, 5, { 5 => 0, 4 => 2, 3 => 2, 2 => 1, 1 => 0 }), shape(:e_minor, 6, { 6 => 0, 5 => 2, 4 => 2, 3 => 0, 2 => 0, 1 => 0 }), shape(:d_minor, 4, { 4 => 0, 3 => 2, 2 => 3, 1 => 1 }) ], # Power chords: root on string 6 and string 5 only. [ :power, nil ] => [ shape(:e_power, 6, { 6 => 0, 5 => 2, 4 => 2 }), shape(:a_power, 5, { 5 => 0, 4 => 2, 3 => 2 }) ], # Dominant sevenths get the full CAGED treatment. The G7 shape # keeps string 1 - it carries the seventh. [ :major, :'7' ] => [ shape(:c7, 5, { 5 => 3, 4 => 2, 3 => 3, 2 => 1, 1 => 0 }), shape(:a7, 5, { 5 => 0, 4 => 2, 3 => 0, 2 => 2, 1 => 0 }), shape(:g7, 6, { 6 => 3, 5 => 2, 4 => 0, 3 => 0, 2 => 0, 1 => 1 }), shape(:e7, 6, { 6 => 0, 5 => 2, 4 => 0, 3 => 1, 2 => 0, 1 => 0 }), shape(:d7, 4, { 4 => 0, 3 => 2, 2 => 1, 1 => 2 }) ], [ :major, :maj7 ] => [ shape(:cmaj7, 5, { 5 => 3, 4 => 2, 3 => 0, 2 => 0, 1 => 0 }), shape(:amaj7, 5, { 5 => 0, 4 => 2, 3 => 1, 2 => 2, 1 => 0 }), shape(:gmaj7, 6, { 6 => 3, 5 => 2, 4 => 0, 3 => 0, 2 => 0, 1 => 2 }), shape(:emaj7, 6, { 6 => 0, 5 => 2, 4 => 1, 3 => 1, 2 => 0, 1 => 0 }), shape(:dmaj7, 4, { 4 => 0, 3 => 2, 2 => 2, 1 => 2 }) ], [ :major, :'6' ] => [ shape(:c6, 5, { 5 => 3, 4 => 2, 3 => 2, 2 => 1, 1 => 0 }), shape(:a6, 5, { 5 => 0, 4 => 2, 3 => 2, 2 => 2, 1 => 2 }), shape(:g6, 6, { 6 => 3, 5 => 2, 4 => 0, 3 => 0, 2 => 0, 1 => 0 }), shape(:e6, 6, { 6 => 0, 5 => 2, 4 => 2, 3 => 1, 2 => 2, 1 => 0 }), shape(:d6, 4, { 4 => 0, 3 => 2, 2 => 0, 1 => 2 }) ], [ :minor, :'7' ] => [ shape(:am7, 5, { 5 => 0, 4 => 2, 3 => 0, 2 => 1, 1 => 0 }), shape(:em7, 6, { 6 => 0, 5 => 2, 4 => 0, 3 => 0, 2 => 0, 1 => 0 }), shape(:dm7, 4, { 4 => 0, 3 => 2, 2 => 1, 1 => 1 }) ], [ :minor, :maj7 ] => [ shape(:am_maj7, 5, { 5 => 0, 4 => 2, 3 => 1, 2 => 1, 1 => 0 }), shape(:em_maj7, 6, { 6 => 0, 5 => 2, 4 => 1, 3 => 0, 2 => 0, 1 => 0 }), shape(:dm_maj7, 4, { 4 => 0, 3 => 2, 2 => 2, 1 => 1 }) ], [ :minor, :'6' ] => [ shape(:am6, 5, { 5 => 0, 4 => 2, 3 => 2, 2 => 1, 1 => 2 }), shape(:em6, 6, { 6 => 0, 5 => 2, 4 => 2, 3 => 0, 2 => 2, 1 => 0 }), shape(:dm6, 4, { 4 => 0, 3 => 2, 2 => 0, 1 => 1 }) ], [ :suspended_2, nil ] => [ shape(:a_sus2, 5, { 5 => 0, 4 => 2, 3 => 2, 2 => 0, 1 => 0 }), shape(:d_sus2, 4, { 4 => 0, 3 => 2, 2 => 3, 1 => 0 }) ], [ :suspended_2, :'7' ] => [ shape(:a7sus2, 5, { 5 => 0, 4 => 2, 3 => 0, 2 => 0, 1 => 0 }), shape(:d7sus2, 4, { 4 => 0, 3 => 2, 2 => 1, 1 => 0 }) ], [ :suspended_4, nil ] => [ shape(:a_sus4, 5, { 5 => 0, 4 => 2, 3 => 2, 2 => 3, 1 => 0 }), shape(:e_sus4, 6, { 6 => 0, 5 => 2, 4 => 2, 3 => 2, 2 => 0, 1 => 0 }), shape(:d_sus4, 4, { 4 => 0, 3 => 2, 2 => 3, 1 => 3 }) ], [ :suspended_4, :'7' ] => [ shape(:a7sus4, 5, { 5 => 0, 4 => 2, 3 => 0, 2 => 3, 1 => 0 }), shape(:e7sus4, 6, { 6 => 0, 5 => 2, 4 => 0, 3 => 2, 2 => 0, 1 => 0 }), shape(:d7sus4, 4, { 4 => 0, 3 => 2, 2 => 1, 1 => 3 }) ], [ :diminished, nil ] => [ shape(:a_dim, 5, { 5 => 0, 4 => 1, 3 => 2, 2 => 1 }), shape(:d_dim, 4, { 4 => 0, 3 => 1, 2 => 3, 1 => 1 }) ], # quality :diminished + extension '7' is symbolized m7b5. [ :diminished, :'7' ] => [ shape(:am7b5, 5, { 5 => 0, 4 => 1, 3 => 0, 2 => 1 }), shape(:dm7b5, 4, { 4 => 0, 3 => 1, 2 => 1, 1 => 1 }) ], [ :diminished, :dim7 ] => [ shape(:adim7, 5, { 5 => 1, 4 => 2, 3 => 0, 2 => 2 }), shape(:ddim7, 4, { 4 => 0, 3 => 1, 2 => 0, 1 => 1 }) ], [ :augmented, nil ] => [ shape(:a_aug, 5, { 5 => 0, 4 => 3, 3 => 2, 2 => 2, 1 => 1 }), shape(:e_aug, 6, { 6 => 0, 5 => 3, 4 => 2, 3 => 1, 2 => 1, 1 => 0 }) ], [ :augmented, :maj7 ] => [ shape(:amaj7sharp5, 5, { 5 => 0, 4 => 3, 3 => 1, 2 => 2 }), shape(:emaj7sharp5, 6, { 6 => 0, 5 => 3, 4 => 1, 3 => 1 }) ] }.freeze
Class Method Summary collapse
-
.chords_for_letter(letter) ⇒ Object
Every chord type with a shape in a CAGED family, in vocabulary order: [[quality, extension, shape], ...].
-
.for(quality:, extension: nil) ⇒ Object
The shapes for a chord type, or nil when none are defined.
-
.namesake_pitch_class(shape, guitar: Guitar.new) ⇒ Object
Pitch class of the note naming the shape's open-position chord (the E shape's namesake root is the open low E).
-
.positions(shape, root, guitar: Guitar.new) ⇒ Object
[string, fret] positions playing
shaperooted onroot: the whole grip shifts up by the semitone distance from the shape's namesake root. -
.root_offset(shape, root, guitar: Guitar.new) ⇒ Object
Semitones the shape shifts up the neck to sound
root(0..11). -
.supports?(quality:, extension: nil) ⇒ Boolean
Whether the library has shapes for a chord type.
Class Method Details
.chords_for_letter(letter) ⇒ Object
Every chord type with a shape in a CAGED family, in vocabulary order: [[quality, extension, shape], ...]. Not every type has every letter (minor has no C or G grip, sus2 only A and D).
205 206 207 208 209 210 211 212 |
# File 'lib/outro_rails/instruments/guitar/chord_shapes.rb', line 205 def chords_for_letter(letter) target = letter.to_s.upcase SHAPES.filter_map do |(quality, extension), shapes| shape = shapes.find { |candidate| candidate.caged_letter == target } [ quality, extension, shape ] if shape end end |
.for(quality:, extension: nil) ⇒ Object
The shapes for a chord type, or nil when none are defined.
193 194 195 |
# File 'lib/outro_rails/instruments/guitar/chord_shapes.rb', line 193 def for(quality:, extension: nil) SHAPES[[ quality&.to_sym, extension&.to_sym ]] end |
.namesake_pitch_class(shape, guitar: Guitar.new) ⇒ Object
Pitch class of the note naming the shape's open-position chord (the E shape's namesake root is the open low E). Capo charts build on this too.
237 238 239 |
# File 'lib/outro_rails/instruments/guitar/chord_shapes.rb', line 237 def namesake_pitch_class(shape, guitar: Guitar.new) guitar.pitch_class_at(shape.root_string, shape.root_fret) end |
.positions(shape, root, guitar: Guitar.new) ⇒ Object
[string, fret] positions playing shape rooted on root: the
whole grip shifts up by the semitone distance from the shape's
namesake root. Open position (offset 0) keeps every string;
barred positions drop the shape's movable_omit strings.
218 219 220 221 222 223 |
# File 'lib/outro_rails/instruments/guitar/chord_shapes.rb', line 218 def positions(shape, root, guitar: Guitar.new) offset = root_offset(shape, root, guitar: guitar) frets = offset.zero? ? shape.frets : shape.movable_frets frets.map { |string, fret| [ string, fret + offset ] } end |
.root_offset(shape, root, guitar: Guitar.new) ⇒ Object
Semitones the shape shifts up the neck to sound root (0..11).
226 227 228 229 230 231 232 |
# File 'lib/outro_rails/instruments/guitar/chord_shapes.rb', line 226 def root_offset(shape, root, guitar: Guitar.new) root_pitch_class = Theory::NoteName.parse(root).pitch_class namesake_pitch_class = namesake_pitch_class(shape, guitar: guitar) offset = root_pitch_class - namesake_pitch_class offset % Theory::Interval::SEMITONES_PER_OCTAVE end |
.supports?(quality:, extension: nil) ⇒ Boolean
Whether the library has shapes for a chord type.
198 199 200 |
# File 'lib/outro_rails/instruments/guitar/chord_shapes.rb', line 198 def supports?(quality:, extension: nil) SHAPES.key?([ quality&.to_sym, extension&.to_sym ]) end |