Module: OutroRails::Instruments::Piano

Defined in:
lib/outro_rails/instruments/piano.rb

Overview

Piano keyboard logic.

Constant Summary collapse

WHITE_PITCH_CLASSES =

Pitch classes of the naturals, i.e. the white keys of an octave.

Theory::NoteName::NATURAL_PITCH_CLASSES.values.freeze

Class Method Summary collapse

Class Method Details

.black_key?(midi) ⇒ Boolean

Whether a MIDI number lands on a black key

Returns:

  • (Boolean)


21
22
23
# File 'lib/outro_rails/instruments/piano.rb', line 21

def black_key?(midi)
  !white_key?(midi)
end

.display_range(pitches, prefer: :sharps) ⇒ Object

Display range for a collection of pitches: the first white key strictly below the lowest note through the first white key strictly above the highest, so the keyboard always has one free white key of margin on each side (and never starts or ends on a black key):

display_range(c_major.pitched_notes)       => [B3, D5] (as Pitches)
display_range(f_sharp_major.pitched_notes) => [F4, G5]


46
47
48
49
50
51
52
53
54
55
# File 'lib/outro_rails/instruments/piano.rb', line 46

def display_range(pitches, prefer: :sharps)
  midis = pitches.map { |pitch| Theory::Pitch.parse(pitch).midi }

  [
    Theory::Pitch.from_midi(next_white_key(midis.min - 1, -1),
                                           prefer: prefer),
    Theory::Pitch.from_midi(next_white_key(midis.max + 1, 1),
                                           prefer: prefer)
  ]
end

.keys_between(start_note, end_note, prefer: :sharps) ⇒ Object

Inclusive list of key hashes between two note names, low to high:

[{ midi: 48, note: 'C3', black: false }, ...]


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

def keys_between(start_note, end_note, prefer: :sharps)
  low  = Theory::Pitch.parse(start_note).midi
  high = Theory::Pitch.parse(end_note).midi
  low, high = high, low if low > high

  (low..high).map do |midi|
    { midi: midi,
      note: Theory::Pitch.from_midi(midi, prefer: prefer).to_s,
      black: black_key?(midi)
    }
  end
end

.white_key?(midi) ⇒ Boolean

Whether a MIDI number lands on a white key

Returns:

  • (Boolean)


14
15
16
17
18
# File 'lib/outro_rails/instruments/piano.rb', line 14

def white_key?(midi)
  pitch_class = midi % Theory::Interval::SEMITONES_PER_OCTAVE

  WHITE_PITCH_CLASSES.include?(pitch_class)
end