Class: HeadMusic::Rudiment::Scale

Inherits:
Base
  • Object
show all
Defined in:
lib/head_music/rudiment/scale.rb

Overview

A scale contains ordered pitches starting at a tonal center.

Constant Summary collapse

SCALE_REGEX =
/^[A-G][#b]?\s+\w+$/

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(root_pitch, scale_type) ⇒ Scale

Returns a new instance of Scale.



22
23
24
25
# File 'lib/head_music/rudiment/scale.rb', line 22

def initialize(root_pitch, scale_type)
  @root_pitch = HeadMusic::Rudiment::Pitch.get(root_pitch)
  @scale_type = HeadMusic::Rudiment::ScaleType.get(scale_type)
end

Instance Attribute Details

#root_pitchObject (readonly)

Returns the value of attribute root_pitch.



20
21
22
# File 'lib/head_music/rudiment/scale.rb', line 20

def root_pitch
  @root_pitch
end

#scale_typeObject (readonly)

Returns the value of attribute scale_type.



20
21
22
# File 'lib/head_music/rudiment/scale.rb', line 20

def scale_type
  @scale_type
end

Class Method Details

.get(root_pitch, scale_type = nil) ⇒ Object



8
9
10
11
12
13
14
15
16
# File 'lib/head_music/rudiment/scale.rb', line 8

def self.get(root_pitch, scale_type = nil)
  root_pitch = HeadMusic::Rudiment::Pitch.get(root_pitch)
  scale_type = HeadMusic::Rudiment::ScaleType.get(scale_type || HeadMusic::Rudiment::ScaleType::DEFAULT)
  @scales ||= {}
  hash_key = HeadMusic::Utilities::HashKey.for(
    [root_pitch, scale_type].join(" ").gsub(/#|♯/, "sharp").gsub(/(\w)[b♭]/, '\\1flat')
  )
  @scales[hash_key] ||= new(root_pitch, scale_type)
end

Instance Method Details

#child_scale_letter_for_step(semitones_from_root) ⇒ Object (private)



113
114
115
116
117
# File 'lib/head_music/rudiment/scale.rb', line 113

def child_scale_letter_for_step(semitones_from_root)
  return unless scale_type.parent

  parent_scale_pitch_for(semitones_from_root).letter_name
end

#degree(degree_number) ⇒ Object



49
50
51
# File 'lib/head_music/rudiment/scale.rb', line 49

def degree(degree_number)
  pitches[degree_number - 1]
end

#determine_scale_pitches(direction, octaves) ⇒ Object (private)



55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/head_music/rudiment/scale.rb', line 55

def determine_scale_pitches(direction, octaves)
  semitones_from_root = 0
  pitches = [root_pitch]
  %i[ascending descending].each do |single_direction|
    next unless [single_direction, :both].include?(direction)

    (1..octaves).each do
      pitches += octave_scale_pitches(single_direction, semitones_from_root)
      semitones_from_root += 12 * direction_sign(single_direction)
    end
  end
  pitches
end

#diatonic_letter_for_step(direction, step) ⇒ Object (private)



107
108
109
110
111
# File 'lib/head_music/rudiment/scale.rb', line 107

def diatonic_letter_for_step(direction, step)
  return unless scale_type.diatonic?

  (direction == :ascending) ? letter_name_series_ascending[step % 7] : letter_name_series_descending[step % 7]
end

#direction_intervals(direction) ⇒ Object (private)



80
81
82
# File 'lib/head_music/rudiment/scale.rb', line 80

def direction_intervals(direction)
  scale_type.send("#{direction}_intervals")
end

#direction_sign(direction) ⇒ Object (private)



76
77
78
# File 'lib/head_music/rudiment/scale.rb', line 76

def direction_sign(direction)
  (direction == :descending) ? -1 : 1
end

#flat_letter_for_step(semitones_from_root) ⇒ Object (private)



119
120
121
122
123
# File 'lib/head_music/rudiment/scale.rb', line 119

def flat_letter_for_step(semitones_from_root)
  return unless root_pitch.flat?

  HeadMusic::Rudiment::PitchClass::FLAT_SPELLINGS[pitch_class_number(semitones_from_root)]
end

#letter_for_step(step, semitones_from_root, direction) ⇒ Object (private)



100
101
102
103
104
105
# File 'lib/head_music/rudiment/scale.rb', line 100

def letter_for_step(step, semitones_from_root, direction)
  diatonic_letter_for_step(direction, step) ||
    child_scale_letter_for_step(semitones_from_root) ||
    flat_letter_for_step(semitones_from_root) ||
    sharp_letter_for_step(semitones_from_root)
end

#octave_scale_pitches(direction, semitones_from_root) ⇒ Object (private)



69
70
71
72
73
74
# File 'lib/head_music/rudiment/scale.rb', line 69

def octave_scale_pitches(direction, semitones_from_root)
  direction_intervals(direction).map.with_index(1) do |semitones, i|
    semitones_from_root += semitones * direction_sign(direction)
    pitch_for_step(i, semitones_from_root, direction)
  end
end

#parent_scale_pitch_for(semitones_from_root) ⇒ Object (private)



88
89
90
91
92
# File 'lib/head_music/rudiment/scale.rb', line 88

def parent_scale_pitch_for(semitones_from_root)
  parent_scale_pitches.detect do |parent_scale_pitch|
    parent_scale_pitch.pitch_class == (root_pitch + semitones_from_root).to_i % 12
  end
end

#parent_scale_pitchesObject (private)



84
85
86
# File 'lib/head_music/rudiment/scale.rb', line 84

def parent_scale_pitches
  HeadMusic::Rudiment::Scale.get(root_pitch, scale_type.parent_name).pitches
end

#pitch_class_number(semitones_from_root) ⇒ Object (private)



129
130
131
# File 'lib/head_music/rudiment/scale.rb', line 129

def pitch_class_number(semitones_from_root)
  (root_pitch.pitch_class.to_i + semitones_from_root) % 12
end

#pitch_classesObject



33
34
35
# File 'lib/head_music/rudiment/scale.rb', line 33

def pitch_classes
  pitches.map(&:pitch_class).uniq
end

#pitch_for_step(step, semitones_from_root, direction) ⇒ Object (private)



94
95
96
97
98
# File 'lib/head_music/rudiment/scale.rb', line 94

def pitch_for_step(step, semitones_from_root, direction)
  pitch_number = root_pitch_number + semitones_from_root
  letter_name = letter_for_step(step, semitones_from_root, direction)
  HeadMusic::Rudiment::Pitch.from_number_and_letter(pitch_number, letter_name)
end

#pitch_names(direction: :ascending, octaves: 1) ⇒ Object



41
42
43
# File 'lib/head_music/rudiment/scale.rb', line 41

def pitch_names(direction: :ascending, octaves: 1)
  pitches(direction: direction, octaves: octaves).map(&:name)
end

#pitches(direction: :ascending, octaves: 1) ⇒ Object



27
28
29
30
31
# File 'lib/head_music/rudiment/scale.rb', line 27

def pitches(direction: :ascending, octaves: 1)
  @pitches ||= {}
  @pitches[direction] ||= {}
  @pitches[direction][octaves] ||= determine_scale_pitches(direction, octaves)
end

#root_pitch_numberObject



45
46
47
# File 'lib/head_music/rudiment/scale.rb', line 45

def root_pitch_number
  @root_pitch_number ||= root_pitch.number
end

#sharp_letter_for_step(semitones_from_root) ⇒ Object (private)



125
126
127
# File 'lib/head_music/rudiment/scale.rb', line 125

def sharp_letter_for_step(semitones_from_root)
  HeadMusic::Rudiment::PitchClass::SHARP_SPELLINGS[pitch_class_number(semitones_from_root)]
end

#spellings(direction: :ascending, octaves: 1) ⇒ Object



37
38
39
# File 'lib/head_music/rudiment/scale.rb', line 37

def spellings(direction: :ascending, octaves: 1)
  pitches(direction: direction, octaves: octaves).map(&:spelling)
end