Module: Webmidi::Music::Note

Defined in:
lib/webmidi/music/note.rb

Constant Summary collapse

NOTE_NAMES =
{
  "C" => 0, "D" => 2, "E" => 4, "F" => 5,
  "G" => 7, "A" => 9, "B" => 11
}.freeze
MIDI_TO_NAME =
%w[C Cs D Ds E F Fs G Gs A As B].freeze
MIDI_RANGE =
0..127

Class Method Summary collapse

Class Method Details

.from_frequency(freq, a4: 440.0) ⇒ Object



49
50
51
52
53
# File 'lib/webmidi/music/note.rb', line 49

def from_frequency(freq, a4: 440.0)
  validate_frequency!(freq)
  validate_frequency_reference!(a4)
  (12 * Math.log2(freq / a4) + 69).round
end

.to_frequency(midi_number, a4: 440.0) ⇒ Object



43
44
45
46
47
# File 'lib/webmidi/music/note.rb', line 43

def to_frequency(midi_number, a4: 440.0)
  validate_midi!(midi_number)
  validate_frequency_reference!(a4)
  a4 * (2.0**((midi_number - 69) / 12.0))
end

.to_midi(input, validate: true) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/webmidi/music/note.rb', line 16

def to_midi(input, validate: true)
  midi = case input
  when Integer
    input
  when Symbol
    parse_note_name(input.to_s)
  when String
    parse_note_name(input)
  else
    raise InvalidMessageError, "Cannot convert #{input.class} to MIDI note"
  end
  validate_midi!(midi) if validate
  midi
end

.to_name(midi_number, sharps: true) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
# File 'lib/webmidi/music/note.rb', line 31

def to_name(midi_number, sharps: true)
  validate_midi!(midi_number)
  octave = (midi_number / 12) - 1
  note_index = midi_number % 12
  name = if sharps
    MIDI_TO_NAME[note_index]
  else
    %w[C Db D Eb E F Gb G Ab A Bb B][note_index]
  end
  "#{name}#{octave}"
end

.validate_midi!(midi_number) ⇒ Object



81
82
83
84
85
# File 'lib/webmidi/music/note.rb', line 81

def validate_midi!(midi_number)
  return if midi_number.is_a?(Integer) && MIDI_RANGE.cover?(midi_number)

  raise InvalidMessageError, "MIDI note must be between 0 and 127, got #{midi_number.inspect}"
end