class Coltrane::Theory::Interval

Attributes

cents[R]
letter_distance[R]

Public Class Methods

[](arg) click to toggle source
# File lib/coltrane/theory/interval.rb, line 80
def self.[](arg)
  new(arg)
end
all() click to toggle source
Calls superclass method
# File lib/coltrane/theory/interval.rb, line 10
def all
  @all ||= super.map(&:interval)
end
all_augmented() click to toggle source
# File lib/coltrane/theory/interval.rb, line 22
def all_augmented
  @all_augmented ||= all_including_compound.select(&:has_augmented?)
                                           .map(&:augmented)
end
all_compound() click to toggle source
# File lib/coltrane/theory/interval.rb, line 14
def all_compound
  @all_compound ||= all.map(&:compound)
end
all_diminished() click to toggle source
# File lib/coltrane/theory/interval.rb, line 27
def all_diminished
  @all_diminished ||= all_including_compound.select(&:has_diminished?)
                                            .map(&:diminished)
end
all_including_compound() click to toggle source
# File lib/coltrane/theory/interval.rb, line 18
def all_including_compound
  @all_including_compound ||= all + all_compound
end
all_including_compound_and_altered() click to toggle source
# File lib/coltrane/theory/interval.rb, line 32
def all_including_compound_and_altered
  @all_including_compound_and_altered ||=
    all_including_compound +
    all_diminished +
    all_augmented
end
new(arg_1 = nil, arg_2 = nil, ascending: true, letter_distance: nil, semitones: nil, compound: false) click to toggle source
# File lib/coltrane/theory/interval.rb, line 40
def initialize(arg_1 = nil, arg_2 = nil, ascending: true,
               letter_distance: nil,
               semitones: nil,
               compound: false)
  if arg_1 && !arg_2 # assumes arg_1 is a letter
    @compound = compound
    IntervalClass[arg_1].interval.yield_self do |interval|
      @letter_distance = interval.letter_distance
      @cents = interval.cents
    end
  elsif arg_1 && arg_2 # assumes those are notes
    if ascending
      @compound = compound
      @cents =
        (arg_1.frequency / arg_2.frequency)
        .interval_class
        .cents

      @letter_distance = calculate_letter_distance arg_1.letter,
                                                   arg_2.letter,
                                                   ascending
    else
      self.class.new(arg_1, arg_2).descending.yield_self do |base_interval|
        @compound        = base_interval.compound?
        @cents           = base_interval.cents
        @letter_distance = base_interval.letter_distance
      end
    end
  elsif letter_distance && semitones
    @compound        = compound || letter_distance > 8
    @cents           = semitones * 100
    @letter_distance = letter_distance
  else
    raise WrongKeywordsError,
          '[interval_class_name]' \
          'Provide: [first_note, second_note] || ' \
          '[letter_distance:, semitones:]'
  end
end

Public Instance Methods

accidentals() click to toggle source
# File lib/coltrane/theory/interval.rb, line 100
def accidentals
  if distance_to_starting.positive? then 'A' * distance_to_starting.abs
  elsif distance_to_starting.negative? then 'd' * distance_to_starting.abs
  else ''
  end
end
as(n) click to toggle source
# File lib/coltrane/theory/interval.rb, line 117
def as(n)
  i = clone(letter_distance: n)
  i if i.name.match?(n.to_s)
end
as!(n) click to toggle source
# File lib/coltrane/theory/interval.rb, line 122
def as!(n)
  i = as(n)
  i unless i&.name&.match? /d|A/
end
as_augmented(n = 1) click to toggle source
# File lib/coltrane/theory/interval.rb, line 131
def as_augmented(n = 1)
  as(letter_distance - n)
end
as_diminished(n = 1) click to toggle source
# File lib/coltrane/theory/interval.rb, line 127
def as_diminished(n = 1)
  as(letter_distance + n)
end
ascending() click to toggle source
# File lib/coltrane/theory/interval.rb, line 159
def ascending
  ascending? ? self : opposite
end
augment(n = 1) click to toggle source
# File lib/coltrane/theory/interval.rb, line 149
def augment(n = 1)
  clone(semitones: semitones + n)
end
Also aliased as: augmented
augmented(n = 1)
Alias for: augment
clone(override_args = {}) click to toggle source
# File lib/coltrane/theory/interval.rb, line 135
def clone(override_args = {})
  self.class.new(**{
    semitones: semitones,
    letter_distance: letter_distance,
    compound: compound?
  }.merge(override_args))
end
compound?() click to toggle source
# File lib/coltrane/theory/interval.rb, line 88
def compound?
  @compound
end
descending() click to toggle source
# File lib/coltrane/theory/interval.rb, line 163
def descending
  descending? ? self : opposite
end
diminish(n = 1) click to toggle source
# File lib/coltrane/theory/interval.rb, line 143
def diminish(n = 1)
  clone(semitones: semitones - n)
end
Also aliased as: diminished
diminished(n = 1)
Alias for: diminish
has_augmented?() click to toggle source
# File lib/coltrane/theory/interval.rb, line 92
def has_augmented?
  name.match? /M|P|A/
end
has_diminished?() click to toggle source
# File lib/coltrane/theory/interval.rb, line 96
def has_diminished?
  name.match? /m|P|d/
end
interval_class() click to toggle source
# File lib/coltrane/theory/interval.rb, line 84
def interval_class
  FrequencyInterval[cents].interval_class
end
name() click to toggle source
# File lib/coltrane/theory/interval.rb, line 107
def name
  @name ||= begin
    if distance_to_starting.zero? || distance_to_starting.abs > 2
      compound? ? interval_class.compound_name : interval_class.name
    else
      "#{accidentals}#{starting_interval.distance + (compound? ? 7 : 0)}"
    end
  end
end
opposite() click to toggle source
# File lib/coltrane/theory/interval.rb, line 155
def opposite
  clone(semitones: -semitones, letter_distance: (-letter_distance % 8) + 1)
end

Private Instance Methods

all_letters() click to toggle source
# File lib/coltrane/theory/interval.rb, line 192
def all_letters
  PitchClass.all_letters
end
calculate_letter_distance(a, b, asc) click to toggle source
# File lib/coltrane/theory/interval.rb, line 196
def calculate_letter_distance(a, b, asc)
  all_letters
    .rotate(all_letters.index(asc ? a : b))
    .index(b) + 1
end
distance_to_starting() click to toggle source
# File lib/coltrane/theory/interval.rb, line 187
def distance_to_starting # calculate the closts distance to it
  d = semitones - starting_interval.semitones
  [(d % 12), (d % -12)].min_by(&:abs)
end
normalized_letter_distance() click to toggle source
# File lib/coltrane/theory/interval.rb, line 182
def normalized_letter_distance
  return letter_distance if letter_distance < 8
  (letter_distance % 8) + 1
end
starting_interval() click to toggle source
# File lib/coltrane/theory/interval.rb, line 169
def starting_interval # select the closest interval possible to start from
  @starting_interval ||= begin
    IntervalClass.all
                 .select { |i| i.distance == normalized_letter_distance }
                 .sort_by do |i|
      (cents - i.cents)
        .yield_self { |d| [(d % 1200), (d % -1200)].min_by(&:abs) }
        .abs
    end
                 .first
  end
end