class MultimediaParadise::Note

Constants

ARRAY_NOTES
#

ARRAY_NOTES

#
CHORD_ALIASES
#

CHORD_ALIASES

#
CHORD_INTERVALS
#

CHORD_INTERVALS

#
NOTE_STRINGS
#

NOTE_STRINGS

MultimediaParadise::NOTE_STRINGS

#

Attributes

frequency[RW]

Public Class Methods

calculate_frequency(*args) click to toggle source
#

calculate_frequency

#
# File lib/multimedia_paradise/multimedia/note.rb, line 435
def calculate_frequency(*args)
  case args.size
  when 1
    letter, accidental, octave = parse_note_string(args[0])
  when 2
    letter, accidental, octave = parse_note_string(args[0], args[1])
  when 3
    letter, accidental, octave = args
  else
    raise ArgumentError, "Invalid octave of arguments"
  end
  distance = note_distance('A4', "#{letter}#{accidental}#{octave}")
  frequency_adjustment(440.0, distance)
end
calculate_note( frequency, give_flat = false ) click to toggle source
#

calculate_note

#
# File lib/multimedia_paradise/multimedia/note.rb, line 453
def calculate_note(
    frequency, give_flat = false
  )
  # ===================================================================== #
  # We could use #log(frequency / 440.0, 2), but then this is
  # possibly not supported in Ruby 1.8 anymore.
  # ===================================================================== #
  frequency_log_base_2 = Math.log(frequency / 440.0) / Math.log(2)
  distance = (ARRAY_NOTES.size * frequency_log_base_2).round
  index  = 9 + distance # 9 is index for A
  octave = 4 + (index / ARRAY_NOTES.size) # 4 is because we're using A4
  index  = (index % ARRAY_NOTES.size)

  parts = ARRAY_NOTES[index].split('/')
  note = if give_flat
    parts.last
  else
    parts.first
  end
  # "#{note}#{octave}"
  note_parts = note.split('')
  note_parts + (note_parts.size == 1 ? [nil] : []) + [octave.to_i]
end
frequency_adjustment(start_frequency, distance) click to toggle source
#

frequency_adjustment

#
# File lib/multimedia_paradise/multimedia/note.rb, line 426
def frequency_adjustment(start_frequency, distance)
  result = (start_frequency * (2.0 ** (distance.to_f / ARRAY_NOTES.size)))
  # Would like to use #round(2), but want to support Ruby 1.8
  (result * 100.0).round / 100.0
end
nearest_note_frequency(frequency) click to toggle source
#

nearest_note_frequency

#
# File lib/multimedia_paradise/multimedia/note.rb, line 480
def nearest_note_frequency(frequency)
  Note.calculate_frequency(
    Note.calculate_note(frequency).join
  )
end
new(descriptor, assumed_octave = nil) click to toggle source
#

initialize

This class will create a new note.

Usage examples:

note = MultimediaParadise::Note.new(698.46) # Creates a note object
with the frequency 698.46
note = MultimediaParadise::Note.new('F#6') # Creates an F sharp note
in the 6th octave

@param [String, Numeric] descriptor Either a string describing the note (e.g. ‘C#4’) or a number giving the note’s frequency (e.g. 440) @param [Numeric, nil] assumed_octave If no octive is given in the descriptor, use this @returns [Note] Note specified

#
# File lib/multimedia_paradise/multimedia/note.rb, line 151
def initialize(descriptor, assumed_octave = nil)
  reset
  self.frequency = if descriptor.is_a? Numeric
    Note.nearest_note_frequency(descriptor)
  else
    Note.calculate_frequency(descriptor, assumed_octave)
  end
end
note_distance(note_string1, note_string2) click to toggle source
#

note_distance

#
# File lib/multimedia_paradise/multimedia/note.rb, line 401
def note_distance(note_string1, note_string2)
  letter1, accidental1, octave1 = parse_note_string(note_string1)
  letter2, accidental2, octave2 = parse_note_string(note_string2)

  get_index = Proc.new { |letter, accidental|
    ARRAY_NOTES.index { |note|
      regex = case accidental
      when '#' then
        /^#{letter}#/
      when 'b' then
        /#{letter}b$/
      else
        /^#{letter}$/
      end
      note.match(regex)
    }
  }
  index1 = get_index.call(letter1, accidental1)
  index2 = get_index.call(letter2, accidental2)
  (index2 - index1) + ((octave2.to_i - octave1.to_i) * ARRAY_NOTES.size)
end
parse_note_string(note_string, assumed_octave = nil) click to toggle source
# File lib/multimedia_paradise/multimedia/note.rb, line 382
def parse_note_string(note_string, assumed_octave = nil)
  match = note_string.match(/^([A-Ga-g])([#b]?)([0-8]?)$/)

  unless match
    raise ArgumentError, "Could not recognize the note string: #{note_string}. It may be invalid."
  end
  if match[3].empty? && assumed_octave.nil?
    raise ArgumentError, "No octave found or specified"
  end
  if match[3].to_i > 8 || (assumed_octave && !(0..8).include?(assumed_octave))
    raise ArgumentError
  end
  octave = match[3].empty? ? assumed_octave : match[3]
  [match[1].upcase, match[2] == '' ? nil : match[2], octave.to_i]
end

Public Instance Methods

<=>(other_note) click to toggle source
#

<=>

#
# File lib/multimedia_paradise/multimedia/note.rb, line 177
def <=>(other_note)
  self.frequency <=> other_note.frequency
end
accidental(give_flat = false) click to toggle source
#

accidental

Returns the accidental portion of the note e.g. ‘#’ or ‘b’

@param [boolean] give_flat Should the result give a flat? (defaults

to giving a sharp)

@return [String] The resulting accidental.

#
# File lib/multimedia_paradise/multimedia/note.rb, line 238
def accidental(give_flat = false)
  Note.calculate_note(self.frequency, give_flat)[1]
end
adjust_by_semitones(interval) click to toggle source
#

adjust_by_semitones

Return another note adjusted by a given interval

@param [Fixnum] interval Number of semitones to adjust by @return [Note] Resulting note after adjustment.

#
# File lib/multimedia_paradise/multimedia/note.rb, line 294
def adjust_by_semitones(interval)
  Note.new(
    Note.frequency_adjustment(self.frequency, interval)
  )
end
chord(description) click to toggle source
#

chord

The first argument is the chord description.

#
# File lib/multimedia_paradise/multimedia/note.rb, line 359
def chord(description)
  description = :major if description.to_s.empty?

  description = description.to_s
  description.downcase! unless %w( M M7 ).include? description
  description.gsub!(/[\s\-]+/, '_')
  description = description.to_sym
  intervals = CHORD_INTERVALS[description] || CHORD_INTERVALS[CHORD_ALIASES[description]]
  if intervals
    Chord.new([self] + intervals.collect {|interval| self.send(interval) })
  end
end
distance_to(note) click to toggle source
#

distance_to

Return the distance (in semitones) to a note

@return [Fixnum] Number of semitones

#
# File lib/multimedia_paradise/multimedia/note.rb, line 282
def distance_to(note)
  Note.note_distance(self.note_string, note.note_string)
end
eql?(other_note) click to toggle source
#

eql?

#
# File lib/multimedia_paradise/multimedia/note.rb, line 191
def eql?(other_note)
  self.frequency == other_note.frequency
end
frequency?() click to toggle source
#

frequency?

#
# File lib/multimedia_paradise/multimedia/note.rb, line 170
def frequency?
  @frequency
end
hash() click to toggle source
#

hash

#
# File lib/multimedia_paradise/multimedia/note.rb, line 184
def hash
  self.frequency.hash
end
letter(give_flat = false) click to toggle source
#

letter

Returns the letter portion of the note e.g. ‘C’

@param [boolean] give_flat Should the result be based on

giving a flat? (defaults to giving a sharp)

@return [String] The resulting note letter

#
# File lib/multimedia_paradise/multimedia/note.rb, line 225
def letter(give_flat = false)
  Note.calculate_note(self.frequency, give_flat).first
end
Also aliased as: letter?
letter?(give_flat = false)
Alias for: letter
major_scale() click to toggle source
#

major_scale

Uses note as key to give major scale

@returns [Array<Note>] Notes in major scale.

#
# File lib/multimedia_paradise/multimedia/note.rb, line 325
def major_scale
  [self,
    self.major_second,
    self.major_third,
    self.perfect_fourth,
    self.perfect_fifth,
    self.major_sixth,
    self.major_seventh,
  ]
end
minor_scale() click to toggle source
#

minor_scale

Uses note as key to give minor scale

@returns [Array<Note>] Notes in minor scale.

#
# File lib/multimedia_paradise/multimedia/note.rb, line 343
def minor_scale
  [self,
    self.major_second,
    self.minor_third,
    self.perfect_fourth,
    self.perfect_fifth,
    self.minor_sixth,
    self.minor_seventh,
  ]
end
next()
Alias for: succ
note_string(give_flat = false) click to toggle source
#

note_string

Returns string representing note with letter, accidental, and octave number e.g. ‘C#5’

@param [boolean] give_flat Should the result give a flat? (defaults to giving a sharp) @return [String] The resulting note string.

#
# File lib/multimedia_paradise/multimedia/note.rb, line 212
def note_string(give_flat = false)
  Note.calculate_note(self.frequency, give_flat).join
end
octave() click to toggle source
#

octave

Returns the octive number of the note e.g. 4.

@return [Fixnum] The resulting octive number

#
# File lib/multimedia_paradise/multimedia/note.rb, line 249
def octave
  Note.calculate_note(self.frequency)[2]
end
prev() click to toggle source
#

prev

Return the previous note (adjusted by one semitone down)

@return [Note] The previous note

#
# File lib/multimedia_paradise/multimedia/note.rb, line 260
def prev
  Note.new(Note.frequency_adjustment(self.frequency, -1))
end
reset() click to toggle source
#

reset

#
Calls superclass method MultimediaParadise::Base#reset
# File lib/multimedia_paradise/multimedia/note.rb, line 163
def reset
  super()
end
succ() click to toggle source
#

succ

Return the next note (adjusted by one semitone up)

@return [Note] The next note

#
# File lib/multimedia_paradise/multimedia/note.rb, line 271
def succ
  Note.new(Note.frequency_adjustment(self.frequency, 1))
end
Also aliased as: next
to_s() click to toggle source
#

to_s

#
# File lib/multimedia_paradise/multimedia/note.rb, line 198
def to_s
  self.note_string
end