class Music::Note

Constants

ACCIDENTALS
CHROMATIC_SCALE
DIATONIC_SCALE
REGEXP

Attributes

accidental[R]

# or ♭.

@return [String]

letter[R]

C, D, E, F, G, A or B.

@return [String]

octave[R]

An integer.

@return [Integer]

Public Class Methods

new(name) click to toggle source

@param name [String] Consists of a letter (C, D, E, F, G, A, B),

accidental (# or ♭) and octave (any integer).

@example

Note.new("C")
Note.new("C#")
Note.new("C♭") #=> or Note.new("Cb")

Note.new("C1")
Note.new("B-1")
# File lib/music/note.rb, line 51
def initialize(name)
  unless match = name.match(/^#{REGEXP}$/)
    raise ArgumentError, "invalid note name: #{name} (example: C#1)"
  end

  @letter     = match[:letter]
  @accidental = match[:accidental].sub("b", "♭") unless match[:accidental].empty?
  @octave     = match[:octave].to_i              unless match[:octave].empty?
end

Public Instance Methods

-(other) click to toggle source

@param other [Music::Note] @return [Music::Interval] @example

Note.new("E") - Note.new("C") #=> #<Music::Interval @number=3, @quality=:major>
# File lib/music/note.rb, line 130
def -(other)
  number = (self.diatonic_idx - other.diatonic_idx).abs + 1
  number += (self.octave - other.octave).abs * DIATONIC_SCALE.size unless octave.nil?

  distance = (self.pitch - other.pitch).abs
  quality = Interval::QUALITIES.find do |quality|
    begin; Interval.new(number, quality).size == distance; rescue ArgumentError; end
  end

  interval = Interval.new(number, quality)
  self >= other ? interval : -interval
end
<=>(other) click to toggle source

Compares notes by their pitch.

@param other [Music::Note] @example

Note.new("C#") == Note.new("D♭") #=> true
Note.new("C")  <  Note.new("D")  #=> true
Note.new("C2") >  Note.new("C1") #=> true
# File lib/music/note.rb, line 75
def <=>(other)
  self.pitch <=> other.pitch
end
name() click to toggle source
# File lib/music/note.rb, line 61
def name
  [letter, accidental, octave].join
end
Also aliased as: to_s
to_s()
Alias for: name
transpose_by(interval) click to toggle source

@param interval [Music::Interval] @return [Music::Note] @example

major_third = Interval.new(3, :major)
Note.new("C").transpose_by(major_third)  == Note.new("E") #=> true
Note.new("E").transpose_by(-major_third) == Note.new("C") #=> true
# File lib/music/note.rb, line 87
def transpose_by(interval)
  transposed_pitch = pitch + interval.size
  transposed_pitch %= CHROMATIC_SCALE.size if octave.nil?

  transposed_diatonic_idx = (diatonic_idx + interval.diff) % DIATONIC_SCALE.size
  transposed_letter = DIATONIC_SCALE.fetch(transposed_diatonic_idx)

  transposed_octave = octave + (diatonic_idx + interval.diff) / DIATONIC_SCALE.size if octave

  transposed_accidental = ACCIDENTALS.find do |accidental|
    note = Note.new [transposed_letter, accidental, transposed_octave].join
    note.pitch == transposed_pitch
  end

  Note.new [transposed_letter, transposed_accidental, transposed_octave].join
end
transpose_down(interval) click to toggle source

@param (see transpose_by) @return (see transpose_by)

@see transpose_by

# File lib/music/note.rb, line 120
def transpose_down(interval)
  transpose_by(-interval)
end
transpose_up(interval) click to toggle source

@param (see transpose_by) @return (see transpose_by)

@see transpose_by

# File lib/music/note.rb, line 110
def transpose_up(interval)
  transpose_by(interval)
end

Protected Instance Methods

chromatic_idx() click to toggle source

@return [Integer] An integer from 0 to 11

# File lib/music/note.rb, line 166
def chromatic_idx
  accidental_effect = {"#" => +1, "♭" => -1}.fetch(accidental, 0)
  CHROMATIC_SCALE.index(letter) + accidental_effect
end
diatonic_idx() click to toggle source

@return [Integer] An integer from 0 to 6

# File lib/music/note.rb, line 159
def diatonic_idx
  DIATONIC_SCALE.index(letter)
end
pitch() click to toggle source

Internal integer representation on the note.

@return [Integer]

# File lib/music/note.rb, line 150
def pitch
  result = chromatic_idx + (octave.to_i * CHROMATIC_SCALE.size)
  result %= CHROMATIC_SCALE.size if octave.nil?
  result
end