Class: OutroRails::Instruments::Guitar

Inherits:
Object
  • Object
show all
Defined in:
lib/outro_rails/instruments/guitar.rb,
lib/outro_rails/instruments/guitar/capo.rb,
lib/outro_rails/instruments/guitar/tunings.rb,
lib/outro_rails/instruments/guitar/chord_shapes.rb

Overview

A guitar with a particular tuning. Strings are numbered 1 (highest pitch) to N (lowest).

Defined Under Namespace

Modules: Capo, ChordShapes, Tunings

Constant Summary collapse

STANDARD_TUNING =

Open strings low to high, i.e. string 6 first.

%w[E2 A2 D3 G3 B3 E4].freeze
MIN_FRET =

Playable fret range; 0 is the open string.

0
MAX_FRET =
24
SINGLE_MARKER_FRETS =

Fret inlay markers.

[ 3, 5, 7, 9, 15, 17, 19, 21 ].freeze
DOUBLE_MARKER_FRETS =
[ 12, 24 ].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tuning: STANDARD_TUNING) ⇒ Guitar

Parses the tuning into Pitches. Instances are immutable.



50
51
52
53
# File 'lib/outro_rails/instruments/guitar.rb', line 50

def initialize(tuning: STANDARD_TUNING)
  @tuning = tuning.map { |note| Theory::Pitch.parse(note) }.freeze
  freeze
end

Instance Attribute Details

#tuningObject (readonly)

Open-string Pitches, low to high, as given to the constructor



24
25
26
# File 'lib/outro_rails/instruments/guitar.rb', line 24

def tuning
  @tuning
end

Class Method Details

.in_tuning(tuning) ⇒ Object

Builds a Guitar from a named tuning: a Tunings::Tuning, a slug ("drop_d"), or nil/"" for standard. Guitar.in_tuning("open_g") => #



44
45
46
47
# File 'lib/outro_rails/instruments/guitar.rb', line 44

def self.in_tuning(tuning)
  tuning = Tunings.fetch(tuning) unless tuning.is_a?(Tunings::Tuning)
  new(tuning: tuning.notes)
end

.normalize_positions(positions) ⇒ Object

Normalizes highlight positions to [string, fret] integer pairs. Accepts [string, fret] pairs or "string:fret" strings:

normalize_positions([[5, 3], '4:2']) => [[5, 3], [4, 2]]


29
30
31
32
33
34
35
36
37
38
39
# File 'lib/outro_rails/instruments/guitar.rb', line 29

def self.normalize_positions(positions)
  Array(positions).map do |position|
    string, fret =
      if position.is_a?(String)
        position.split(":", 2)
      else
        position.to_a
      end
    [ Integer(string), Integer(fret) ]
  end
end

Instance Method Details

#midi_at(string, fret) ⇒ Object

MIDI number sounded at a string and fret - one semitone per fret



73
74
75
# File 'lib/outro_rails/instruments/guitar.rb', line 73

def midi_at(string, fret)
  open_pitch(string).midi + fret
end

#open_midisObject

Open-string MIDI numbers keyed by string number:

{ 1 => 64, 2 => 59, 3 => 55, 4 => 50, 5 => 45, 6 => 40 }


68
69
70
# File 'lib/outro_rails/instruments/guitar.rb', line 68

def open_midis
  (1..string_count).to_h { |string| [ string, midi_at(string, MIN_FRET) ] }
end

#open_pitch(string) ⇒ Object

Open Pitch of a string, counting 1 as the highest. The tuning is stored low to high, so the index is inverted.



62
63
64
# File 'lib/outro_rails/instruments/guitar.rb', line 62

def open_pitch(string)
  tuning.fetch(string_count - string)
end

#pitch_at(string, fret, prefer: :sharps) ⇒ Object

That note as a Pitch, spelled with sharps or flats as preferred



78
79
80
# File 'lib/outro_rails/instruments/guitar.rb', line 78

def pitch_at(string, fret, prefer: :sharps)
  Theory::Pitch.from_midi(midi_at(string, fret), prefer: prefer)
end

#pitch_class_at(string, fret) ⇒ Object

Pitch class (0-11) sounded at a string and fret, ignoring octave



83
84
85
# File 'lib/outro_rails/instruments/guitar.rb', line 83

def pitch_class_at(string, fret)
  midi_at(string, fret) % Theory::Interval::SEMITONES_PER_OCTAVE
end

#positions_in(scale, min_fret: MIN_FRET, max_fret: MAX_FRET) ⇒ Object

Every [string, fret] position whose pitch class belongs to the scale, covering open strings through max_fret on every string:

guitar.positions_in(Theory::Scale.new('C')) => [[1, 0], [1, 1], ...]


90
91
92
93
94
# File 'lib/outro_rails/instruments/guitar.rb', line 90

def positions_in(scale, min_fret: MIN_FRET, max_fret: MAX_FRET)
  positions_with_pitch_classes(scale.pitch_classes,
                               min_fret: min_fret,
                               max_fret: max_fret)
end

#positions_of(note, min_fret: MIN_FRET, max_fret: MAX_FRET) ⇒ Object

Every [string, fret] position sounding a note (any octave):

guitar.positions_of('C') => [[1, 8], [1, 20], [2, 1], ...]


98
99
100
101
102
103
# File 'lib/outro_rails/instruments/guitar.rb', line 98

def positions_of(note, min_fret: MIN_FRET, max_fret: MAX_FRET)
  pitch_class = Theory::NoteName.parse(note).pitch_class
  positions_with_pitch_classes([ pitch_class ],
                               min_fret: min_fret,
                               max_fret: max_fret)
end

#string_countObject

Number of strings, taken from the tuning



56
57
58
# File 'lib/outro_rails/instruments/guitar.rb', line 56

def string_count
  tuning.size
end