Class: HeadMusic::PitchSet

Inherits:
Object
  • Object
show all
Defined in:
lib/head_music/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

Returns a new instance of PitchSet.



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

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

Instance Attribute Details

#pitchesObject (readonly)

Returns the value of attribute pitches.



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

def pitches
  @pitches
end

Instance Method Details

#==(other) ⇒ Object



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

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

#augmented_triad?Boolean

Returns:

  • (Boolean)


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

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

#bass_pitchObject



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

def bass_pitch
  @bass_pitch ||= pitches.first
end

#consonant_triad?Boolean

Returns:

  • (Boolean)


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

def consonant_triad?
  major_triad? || minor_triad?
end

#diatonic_intervalsObject



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

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

#diatonic_intervals_above_bass_pitchObject



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

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

#diminished_triad?Boolean

Returns:

  • (Boolean)


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

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

#eleventh_chord?Boolean

Returns:

  • (Boolean)


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

def eleventh_chord?
  hexachord? && tertian?
end

#equivalent?(other) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#first_inversion_seventh_chord?Boolean

Returns:

  • (Boolean)


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

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

#first_inversion_triad?Boolean

Returns:

  • (Boolean)


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

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

#inspectObject



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

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

#integer_notationObject



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

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



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

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

#major_triad?Boolean

Returns:

  • (Boolean)


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

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

#minor_triad?Boolean

Returns:

  • (Boolean)


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

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

#ninth_chord?Boolean

Returns:

  • (Boolean)


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

def ninth_chord?
  pentachord? && tertian?
end

#pitch_class_setObject



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

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

#pitch_classesObject



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

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

#pitches_above_bass_pitchObject



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

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

#reductionObject



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

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

#root_position_seventh_chord?Boolean

Returns:

  • (Boolean)


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

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

#root_position_triad?Boolean

Returns:

  • (Boolean)


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

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

#scale_degreesObject



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

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

#scale_degrees_above_bass_pitchObject



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

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

Returns:

  • (Boolean)


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

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

#second_inversion_triad?Boolean

Returns:

  • (Boolean)


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

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

#seventh_chord?Boolean

Returns:

  • (Boolean)


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

def seventh_chord?
  tetrachord? && tertian?
end

#sizeObject



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

def size
  pitches.length
end

#tertian?Boolean

Returns:

  • (Boolean)


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

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

Returns:

  • (Boolean)


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

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

#thirteenth_chord?Boolean

Returns:

  • (Boolean)


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

def thirteenth_chord?
  heptachord? && tertian?
end

#to_sObject



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

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

#triad?Boolean

Returns:

  • (Boolean)


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

def triad?
  trichord? && tertian?
end

#uninvertObject



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

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