Sha256: f531d4b0270d5c8e87e584baf79629cdd0dc2dd50b2306fb53c906cb9a871577

Contents?: true

Size: 1.68 KB

Versions: 1

Compression:

Stored size: 1.68 KB

Contents

# frozen_string_literal: true

# A Chord is a collection of three or more pitches
class HeadMusic::Chord
  attr_reader :pitches

  def initialize(pitches)
    raise ArgumentError if pitches.length < 3

    @pitches = pitches.map { |pitch| HeadMusic::Pitch.get(pitch) }.sort
  end

  def consonant_triad?
    reduction.root_triad? || reduction.first_inversion_triad? || reduction.second_inversion_triad?
  end

  def root_triad?
    return false unless triad?

    intervals.map(&:shorthand).sort == %w[M3 m3]
  end

  # TODO: look up definition of first and second inversion triads. Can they be spread?
  def first_inversion_triad?
    return false unless triad?

    invert.invert.intervals.map(&:shorthand).sort == %w[M3 m3]
  end

  def second_inversion_triad?
    return false unless triad?

    invert.intervals.map(&:shorthand).sort == %w[M3 m3]
  end

  def reduction
    @reduction ||= HeadMusic::Chord.new(reduction_pitches)
  end

  def triad?
    pitches.length == 3
  end

  def intervals
    pitches.each_cons(2).map do |pitch_pair|
      HeadMusic::FunctionalInterval.new(*pitch_pair)
    end
  end

  def invert
    inverted_pitch = pitches[0] + HeadMusic::Interval.get(12)
    new_pitches = pitches.drop(1) + [inverted_pitch]
    HeadMusic::Chord.new(new_pitches)
  end

  def bass_pitch
    @bass_pitch ||= pitches.first
  end

  def inspect
    pitches.map(&:to_s).join(' ')
  end

  def to_s
    pitches.map(&:to_s).join(' ')
  end

  def ==(other)
    pitches & other.pitches == pitches
  end

  private

  def reduction_pitches
    pitches.map do |pitch|
      pitch = HeadMusic::Pitch.fetch_or_create(pitch.spelling, pitch.octave - 1) while pitch > bass_pitch + 12
      pitch
    end.sort
  end
end

Version data entries

1 entries across 1 versions & 1 rubygems

Version Path
head_music-0.22.0 lib/head_music/chord.rb