lib/coltrane/theory/progression.rb
# frozen_string_literal: true
module Coltrane
module Theory
# Allows the creation of chord progressions using standard notations.
# Ex: Progression.new('I-IV-V', key: 'Am')
class Progression
extend NotableProgressions
include Changes
attr_reader :scale, :chords, :notation
def self.find(*chords)
chords
.yield_self { |chords|
next chords if chords[0].is_a?(Chord)
chords.map {|c| Chord.new(name: c) }
}
.yield_self { |chords|
NoteSet[*chords.map(&:root_note)]
.yield_self { |root_notes|
Scale.having_notes(*root_notes).strict_scales
}
.reduce([]) { |memo, scale|
memo + [Progression.new(chords: chords, scale: scale)]
}
}
.yield_self { |progressions| ProgressionSet.new(*progressions) }
end
def initialize(notation = nil, chords: nil, roman_chords: nil, key: nil, scale: nil)
if notation.nil? && chords.nil? && roman_chords.nil? || key.nil? && scale.nil?
raise WrongKeywordsError,
'[chords:, [scale: || key:]] '\
'[roman_chords:, [scale: || key:]] '\
'[notation:, [scale: || key:]] '\
end
@scale = scale || Key[key]
@chords =
if !chords.nil?
chords
elsif !roman_chords.nil?
roman_chords.map(&:chord)
elsif !notation.nil?
@notation = notation
notation.split('-').map { |c| RomanChord.new(c, scale: @scale).chord }
end
end
def interval_sequence
@interval_sequence ||= IntervalSequence(notes: @root_notes)
end
def root_notes
@root_notes ||= @chords.map(&:root_note)
end
def roman_chords
@roman_chords ||= chords.map do |c|
RomanChord.new(chord: c, scale: scale)
end
end
def notation
roman_chords.map(&:notation).join('-')
end
def notes
NoteSet[*chords.map(&:notes).map(&:notes).flatten]
end
def notes_out
notes - scale.notes
end
def notes_out_size
notes_out.size
end
end
end
end