Class: Deftones::Music::Note

Inherits:
Object
  • Object
show all
Defined in:
lib/deftones/music/note.rb

Constant Summary collapse

NOTE_NAMES =
%w[C C# D D# E F F# G G# A A# B].freeze
FLAT_MAP =
{
  "Cb" => "B",
  "Db" => "C#",
  "Eb" => "D#",
  "Fb" => "E",
  "Gb" => "F#",
  "Ab" => "G#",
  "Bb" => "A#"
}.freeze

Class Method Summary collapse

Class Method Details

.from_frequency(frequency) ⇒ Object



34
35
36
37
38
39
40
# File 'lib/deftones/music/note.rb', line 34

def from_frequency(frequency)
  normalized_frequency = frequency.to_f
  raise Deftones::InvalidFrequencyError, "Frequency must be positive" unless normalized_frequency.positive? && normalized_frequency.finite?

  midi_number = (12 * Math.log2(normalized_frequency / 440.0) + 69).round
  from_midi(midi_number)
end

.from_midi(midi_number) ⇒ Object



28
29
30
31
32
# File 'lib/deftones/music/note.rb', line 28

def from_midi(midi_number)
  integer = midi_number.to_i
  octave = (integer / 12) - 1
  "#{NOTE_NAMES[integer % 12]}#{octave}"
end

.normalize_name(token) ⇒ Object (private)



61
62
63
64
# File 'lib/deftones/music/note.rb', line 61

def normalize_name(token)
  canonical = token[0].upcase + token[1..]
  FLAT_MAP.fetch(canonical, canonical.upcase)
end

.parse_note_name(note_name) ⇒ Object (private)



44
45
46
47
48
49
50
51
52
53
# File 'lib/deftones/music/note.rb', line 44

def parse_note_name(note_name)
  validate_accidentals!(note_name)
  match = note_name.to_s.match(/\A([A-Ga-g][#b]?)(-?\d+)\z/)
  raise Deftones::InvalidNoteError, "Invalid note: #{note_name}" unless match

  normalized_name = normalize_name(match[1])
  raise Deftones::InvalidNoteError, "Unsupported note name: #{note_name}" unless NOTE_NAMES.include?(normalized_name)

  [normalized_name, match[2].to_i]
end

.to_frequency(note_name) ⇒ Object



18
19
20
21
# File 'lib/deftones/music/note.rb', line 18

def to_frequency(note_name)
  midi_number = to_midi(note_name)
  440.0 * (2.0**((midi_number - 69) / 12.0))
end

.to_midi(note_name) ⇒ Object



23
24
25
26
# File 'lib/deftones/music/note.rb', line 23

def to_midi(note_name)
  name, octave = parse_note_name(note_name)
  NOTE_NAMES.index(name) + ((octave + 1) * 12)
end

.validate_accidentals!(note_name) ⇒ Object (private)



55
56
57
58
59
# File 'lib/deftones/music/note.rb', line 55

def validate_accidentals!(note_name)
  return unless note_name.to_s.match?(/\A[A-Ga-g](?:##|bb)/)

  raise Deftones::InvalidNoteError, "Double accidentals are unsupported: #{note_name}"
end