Class: HeadMusic::Analysis::PitchSet

Inherits:
Object
  • Object
show all
Defined in:
lib/head_music/analysis/pitch_set.rb

Overview

A PitchSet is a collection of one or more pitches. See also: PitchClassSet

Constant Summary collapse

TERTIAN_SONORITIES =
{
  implied_triad: [3],
  triad: [3, 5],
  seventh_chord: [3, 5, 7],
  ninth_chord: [2, 3, 5, 7],
  eleventh_chord: [2, 3, 4, 5, 7],
  thirteenth_chord: [2, 3, 4, 5, 6, 7] # a.k.a. diatonic scale
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(pitches) ⇒ PitchSet



25
26
27
# File 'lib/head_music/analysis/pitch_set.rb', line 25

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

Instance Attribute Details

#pitchesObject (readonly)

Returns the value of attribute pitches.



16
17
18
# File 'lib/head_music/analysis/pitch_set.rb', line 16

def pitches
  @pitches
end

Instance Method Details

#==(other) ⇒ Object



91
92
93
# File 'lib/head_music/analysis/pitch_set.rb', line 91

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

#augmented_triad?Boolean



123
124
125
# File 'lib/head_music/analysis/pitch_set.rb', line 123

def augmented_triad?
  [%w[M3 M3], %w[M3 d4], %w[d4 M3]].include? reduction_diatonic_intervals.map(&:shorthand)
end

#bass_pitchObject



79
80
81
# File 'lib/head_music/analysis/pitch_set.rb', line 79

def bass_pitch
  @bass_pitch ||= pitches.first
end

#consonant_triad?Boolean



107
108
109
# File 'lib/head_music/analysis/pitch_set.rb', line 107

def consonant_triad?
  major_triad? || minor_triad?
end

#diatonic_intervalsObject



41
42
43
44
45
# File 'lib/head_music/analysis/pitch_set.rb', line 41

def diatonic_intervals
  @diatonic_intervals ||= pitches.each_cons(2).map do |pitch_pair|
    HeadMusic::Analysis::DiatonicInterval.new(*pitch_pair)
  end
end

#diatonic_intervals_above_bass_pitchObject



47
48
49
50
51
# File 'lib/head_music/analysis/pitch_set.rb', line 47

def diatonic_intervals_above_bass_pitch
  @diatonic_intervals_above_bass_pitch ||= pitches_above_bass_pitch.map do |pitch|
    HeadMusic::Analysis::DiatonicInterval.new(bass_pitch, pitch)
  end
end

#diminished_triad?Boolean



119
120
121
# File 'lib/head_music/analysis/pitch_set.rb', line 119

def diminished_triad?
  [%w[m3 m3], %w[m3 A4], %w[A4 m3]].include? reduction_diatonic_intervals.map(&:shorthand)
end

#eleventh_chord?Boolean



163
164
165
# File 'lib/head_music/analysis/pitch_set.rb', line 163

def eleventh_chord?
  hexachord? && tertian?
end

#equivalent?(other) ⇒ Boolean



95
96
97
# File 'lib/head_music/analysis/pitch_set.rb', line 95

def equivalent?(other)
  pitch_classes.sort == other.pitch_classes.sort
end

#first_inversion_seventh_chord?Boolean



147
148
149
# File 'lib/head_music/analysis/pitch_set.rb', line 147

def first_inversion_seventh_chord?
  tetrachord? && reduction.uninvert.diatonic_intervals.all?(&:third?)
end

#first_inversion_triad?Boolean



131
132
133
# File 'lib/head_music/analysis/pitch_set.rb', line 131

def first_inversion_triad?
  trichord? && reduction.uninvert.diatonic_intervals.all?(&:third?)
end

#inspectObject



83
84
85
# File 'lib/head_music/analysis/pitch_set.rb', line 83

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

#integer_notationObject



57
58
59
60
61
62
63
64
65
# File 'lib/head_music/analysis/pitch_set.rb', line 57

def integer_notation
  # questions:
  # - should this be absolute? 0 = C?
  # - should there be both absolute and relative versions?
  @integer_notation ||= begin
    return [] if pitches.empty?
    diatonic_intervals_above_bass_pitch.map { |interval| interval.semitones % 12 }.flatten.sort.unshift(0).uniq
  end
end

#invertObject



67
68
69
70
71
# File 'lib/head_music/analysis/pitch_set.rb', line 67

def invert
  inverted_pitch = pitches[0] + HeadMusic::Analysis::DiatonicInterval.get("perfect octave")
  new_pitches = pitches.drop(1) + [inverted_pitch]
  HeadMusic::Analysis::PitchSet.new(new_pitches)
end

#major_triad?Boolean



111
112
113
# File 'lib/head_music/analysis/pitch_set.rb', line 111

def major_triad?
  [%w[M3 m3], %w[m3 P4], %w[P4 M3]].include? reduction_diatonic_intervals.map(&:shorthand)
end

#minor_triad?Boolean



115
116
117
# File 'lib/head_music/analysis/pitch_set.rb', line 115

def minor_triad?
  [%w[m3 M3], %w[M3 P4], %w[P4 m3]].include? reduction_diatonic_intervals.map(&:shorthand)
end

#ninth_chord?Boolean



159
160
161
# File 'lib/head_music/analysis/pitch_set.rb', line 159

def ninth_chord?
  pentachord? && tertian?
end

#pitch_class_setObject



33
34
35
# File 'lib/head_music/analysis/pitch_set.rb', line 33

def pitch_class_set
  @pitch_class_set ||= HeadMusic::Analysis::PitchClassSet.new(pitch_classes)
end

#pitch_classesObject



29
30
31
# File 'lib/head_music/analysis/pitch_set.rb', line 29

def pitch_classes
  @pitch_classes ||= reduction_pitches.map(&:pitch_class).uniq
end

#pitches_above_bass_pitchObject



53
54
55
# File 'lib/head_music/analysis/pitch_set.rb', line 53

def pitches_above_bass_pitch
  @pitches_above_bass_pitch ||= pitches.drop(1)
end

#reductionObject



37
38
39
# File 'lib/head_music/analysis/pitch_set.rb', line 37

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

#reduction_pitchesObject (private)



192
193
194
195
196
197
# File 'lib/head_music/analysis/pitch_set.rb', line 192

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

#root_position_seventh_chord?Boolean



143
144
145
# File 'lib/head_music/analysis/pitch_set.rb', line 143

def root_position_seventh_chord?
  tetrachord? && reduction_diatonic_intervals.all?(&:third?)
end

#root_position_triad?Boolean



127
128
129
# File 'lib/head_music/analysis/pitch_set.rb', line 127

def root_position_triad?
  trichord? && reduction_diatonic_intervals.all?(&:third?)
end

#scale_degreesObject



182
183
184
# File 'lib/head_music/analysis/pitch_set.rb', line 182

def scale_degrees
  @scale_degrees ||= pitches.empty? ? [] : scale_degrees_above_bass_pitch.unshift(1)
end

#scale_degrees_above_bass_pitchObject



186
187
188
# File 'lib/head_music/analysis/pitch_set.rb', line 186

def scale_degrees_above_bass_pitch
  @scale_degrees_above_bass_pitch ||= diatonic_intervals_above_bass_pitch.map(&:simple_number).sort - [8]
end

#second_inversion_seventh_chord?Boolean



151
152
153
# File 'lib/head_music/analysis/pitch_set.rb', line 151

def second_inversion_seventh_chord?
  tetrachord? && reduction.uninvert.uninvert.diatonic_intervals.all?(&:third?)
end

#second_inversion_triad?Boolean



135
136
137
# File 'lib/head_music/analysis/pitch_set.rb', line 135

def second_inversion_triad?
  trichord? && reduction.invert.diatonic_intervals.all?(&:third?)
end

#seventh_chord?Boolean



139
140
141
# File 'lib/head_music/analysis/pitch_set.rb', line 139

def seventh_chord?
  tetrachord? && tertian?
end

#sizeObject



99
100
101
# File 'lib/head_music/analysis/pitch_set.rb', line 99

def size
  pitches.length
end

#tertian?Boolean



171
172
173
174
175
176
177
178
179
180
# File 'lib/head_music/analysis/pitch_set.rb', line 171

def tertian?
  return false unless diatonic_intervals.any?

  inversion = reduction
  pitches.length.times do
    return true if TERTIAN_SONORITIES.value?(inversion.scale_degrees_above_bass_pitch)
    inversion = inversion.invert
  end
  false
end

#third_inversion_seventh_chord?Boolean



155
156
157
# File 'lib/head_music/analysis/pitch_set.rb', line 155

def third_inversion_seventh_chord?
  tetrachord? && reduction.invert.diatonic_intervals.all?(&:third?)
end

#thirteenth_chord?Boolean



167
168
169
# File 'lib/head_music/analysis/pitch_set.rb', line 167

def thirteenth_chord?
  heptachord? && tertian?
end

#to_sObject



87
88
89
# File 'lib/head_music/analysis/pitch_set.rb', line 87

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

#triad?Boolean



103
104
105
# File 'lib/head_music/analysis/pitch_set.rb', line 103

def triad?
  trichord? && tertian?
end

#uninvertObject



73
74
75
76
77
# File 'lib/head_music/analysis/pitch_set.rb', line 73

def uninvert
  inverted_pitch = pitches[-1] - HeadMusic::Analysis::DiatonicInterval.get("perfect octave")
  new_pitches = [inverted_pitch] + pitches[0..-2]
  HeadMusic::Analysis::PitchSet.new(new_pitches)
end