Class: HeadMusic::Analysis::Dyad::ChordImplication
- Inherits:
-
Object
- Object
- HeadMusic::Analysis::Dyad::ChordImplication
- Defined in:
- lib/head_music/analysis/dyad/chord_implication.rb
Overview
Enumerates the chords (as PitchCollections) that contain a given dyad. Trichords and seventh chords are built at every chromatic root from a fixed table of interval stacks, then kept only when they actually span the dyad. An optional key narrows the results to diatonic chords and ranks the survivors by how many of their pitches the key contains.
Constant Summary collapse
- TRICHORD_INTERVALS =
[ %w[M3 P5], # major triad %w[m3 P5], # minor triad %w[m3 d5], # diminished triad %w[M3 A5], # augmented triad %w[P4 P5], # sus4 (not a triad) %w[M2 P5] # sus2 (not a triad) ].freeze
- SEVENTH_CHORD_INTERVALS =
[ %w[M3 P5 M7], # major seventh %w[M3 P5 m7], # dominant seventh (major-minor) %w[m3 P5 m7], # minor seventh %w[m3 P5 M7], # minor-major seventh %w[m3 d5 m7], # half-diminished seventh %w[m3 d5 d7], # diminished seventh %w[M2 M3 P5 m7], # dominant ninth %w[m2 M3 P5 m7], # dominant minor ninth %w[M2 m3 P5 m7], # minor ninth %w[M2 M3 P5 M7] # major ninth ].freeze
Instance Attribute Summary collapse
-
#dyad_pitch_classes ⇒ Object
readonly
Returns the value of attribute dyad_pitch_classes.
-
#key ⇒ Object
readonly
Returns the value of attribute key.
Instance Method Summary collapse
-
#build_chord(root_pitch, intervals) ⇒ Object
private
-
#candidate_chords(interval_sets) ⇒ Object
private
-
#candidate_roots ⇒ Object
private
-
#chords_from(interval_sets) ⇒ Object
private
-
#diatonic_spellings ⇒ Object
private
-
#filter_by_key(pitch_collections) ⇒ Object
private
-
#includes_dyad?(pitch_collection) ⇒ Boolean
private
-
#initialize(dyad_pitch_classes, key) ⇒ ChordImplication
constructor
A new instance of ChordImplication.
-
#ranked(chords) ⇒ Object
private
Without a key every chord stands; with one, keep the diatonic chords and order them by how much they agree with the key.
-
#seventh_chords ⇒ Object
-
#sort_by_diatonic_agreement(pitch_collections) ⇒ Object
private
-
#trichords ⇒ Object
Constructor Details
#initialize(dyad_pitch_classes, key) ⇒ ChordImplication
Returns a new instance of ChordImplication.
32 33 34 35 |
# File 'lib/head_music/analysis/dyad/chord_implication.rb', line 32 def initialize(dyad_pitch_classes, key) @dyad_pitch_classes = dyad_pitch_classes @key = key end |
Instance Attribute Details
#dyad_pitch_classes ⇒ Object (readonly)
Returns the value of attribute dyad_pitch_classes.
30 31 32 |
# File 'lib/head_music/analysis/dyad/chord_implication.rb', line 30 def dyad_pitch_classes @dyad_pitch_classes end |
#key ⇒ Object (readonly)
Returns the value of attribute key.
30 31 32 |
# File 'lib/head_music/analysis/dyad/chord_implication.rb', line 30 def key @key end |
Instance Method Details
#build_chord(root_pitch, intervals) ⇒ Object (private)
74 75 76 77 78 79 |
# File 'lib/head_music/analysis/dyad/chord_implication.rb', line 74 def build_chord(root_pitch, intervals) chord_pitches = [root_pitch] + intervals.map do |name| HeadMusic::Analysis::DiatonicInterval.get(name).above(root_pitch) end HeadMusic::Analysis::PitchCollection.new(chord_pitches) end |
#candidate_chords(interval_sets) ⇒ Object (private)
62 63 64 65 66 |
# File 'lib/head_music/analysis/dyad/chord_implication.rb', line 62 def candidate_chords(interval_sets) candidate_roots.flat_map do |root_pitch| interval_sets.map { |intervals| build_chord(root_pitch, intervals) } end end |
#candidate_roots ⇒ Object (private)
68 69 70 71 72 |
# File 'lib/head_music/analysis/dyad/chord_implication.rb', line 68 def candidate_roots HeadMusic::Rudiment::Spelling::CHROMATIC_SPELLINGS.map do |root_spelling| HeadMusic::Rudiment::Pitch.get("#{root_spelling}4") end end |
#chords_from(interval_sets) ⇒ Object (private)
56 57 58 59 60 |
# File 'lib/head_music/analysis/dyad/chord_implication.rb', line 56 def chords_from(interval_sets) candidate_chords(interval_sets) .select { |chord| includes_dyad?(chord) } .uniq { |chord| chord.pitch_classes.sort.map(&:to_i) } end |
#diatonic_spellings ⇒ Object (private)
97 98 99 |
# File 'lib/head_music/analysis/dyad/chord_implication.rb', line 97 def diatonic_spellings @diatonic_spellings ||= key.scale.spellings end |
#filter_by_key(pitch_collections) ⇒ Object (private)
85 86 87 88 89 |
# File 'lib/head_music/analysis/dyad/chord_implication.rb', line 85 def filter_by_key(pitch_collections) pitch_collections.select do |pitch_collection| pitch_collection.pitches.all? { |pitch| diatonic_spellings.include?(pitch.spelling) } end end |
#includes_dyad?(pitch_collection) ⇒ Boolean (private)
81 82 83 |
# File 'lib/head_music/analysis/dyad/chord_implication.rb', line 81 def includes_dyad?(pitch_collection) dyad_pitch_classes.all? { |pitch_class| pitch_collection.pitch_classes.include?(pitch_class) } end |
#ranked(chords) ⇒ Object (private)
Without a key every chord stands; with one, keep the diatonic chords and order them by how much they agree with the key. Concentrating the key check here keeps filter and sort free of their own guards.
50 51 52 53 54 |
# File 'lib/head_music/analysis/dyad/chord_implication.rb', line 50 def ranked(chords) return chords unless key sort_by_diatonic_agreement(filter_by_key(chords)) end |
#seventh_chords ⇒ Object
41 42 43 |
# File 'lib/head_music/analysis/dyad/chord_implication.rb', line 41 def seventh_chords @seventh_chords ||= ranked(chords_from(SEVENTH_CHORD_INTERVALS)) end |
#sort_by_diatonic_agreement(pitch_collections) ⇒ Object (private)
91 92 93 94 95 |
# File 'lib/head_music/analysis/dyad/chord_implication.rb', line 91 def sort_by_diatonic_agreement(pitch_collections) pitch_collections.sort_by do |pitch_collection| -pitch_collection.pitches.count { |pitch| diatonic_spellings.include?(pitch.spelling) } end end |
#trichords ⇒ Object
37 38 39 |
# File 'lib/head_music/analysis/dyad/chord_implication.rb', line 37 def trichords @trichords ||= ranked(chords_from(TRICHORD_INTERVALS)) end |