Sha256: 416efa1b535df7d32adf535dcfb842cc7805f8df023555b5a6a27e48c7669a3c

Contents?: true

Size: 1.66 KB

Versions: 1

Compression:

Stored size: 1.66 KB

Contents

# A Spelling is a pitch class with a letter and possibly an accidental

class HeadMusic::Spelling
  MATCHER = /^\s*([A-G])([b#]*)(\-?\d+)?\s*$/

  attr_reader :pitch_class
  attr_reader :letter
  attr_reader :accidental

  delegate :number, to: :pitch_class, prefix: true

  def self.get(identifier)
    @spellings ||= {}
    from_name(identifier) || from_number(identifier)
  end

  def self.match(string)
    string.to_s.match(MATCHER)
  end

  def self.from_name(name)
    if match(name)
      letter_name, accidental_string, _octave = match(name).captures
      letter = HeadMusic::Letter.get(letter_name)
      return nil unless letter
      accidental = HeadMusic::Accidental.get(accidental_string)
      fetch_or_create(letter, accidental)
    end
  end

  def self.from_number(number)
    return nil unless number == number.to_i
    pitch_class_number = number.to_i % 12
    letter = HeadMusic::Letter.from_pitch_class(pitch_class_number)
    accidental = HeadMusic::Accidental.for_interval(pitch_class_number - letter.pitch_class.to_i)
    fetch_or_create(letter, accidental)
  end

  def self.fetch_or_create(letter, accidental)
    key = [letter, accidental].join
    @spellings[key] ||= new(letter, accidental)
  end

  def initialize(letter, accidental = nil)
    @letter = HeadMusic::Letter.get(letter.to_s)
    @accidental = HeadMusic::Accidental.get(accidental.to_s)
    accidental_semitones = @accidental ? @accidental.semitones : 0
    @pitch_class = HeadMusic::PitchClass.get(letter.pitch_class + accidental_semitones)
  end

  def name
    [letter, accidental].join
  end

  def to_s
    name
  end

  def ==(value)
    to_s == value.to_s
  end

  private_class_method :new
end

Version data entries

1 entries across 1 versions & 1 rubygems

Version Path
head_music-0.1.5 lib/head_music/spelling.rb