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.

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.



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

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.



17
18
19
# File 'lib/head_music/rudiment/scale.rb', line 17

def root_pitch
  @root_pitch
end

#scale_typeObject (readonly)

Returns the value of attribute scale_type.



17
18
19
# File 'lib/head_music/rudiment/scale.rb', line 17

def scale_type
  @scale_type
end

Class Method Details

.get(root_pitch, scale_type = nil) ⇒ Object



6
7
8
9
10
11
12
13
# File 'lib/head_music/rudiment/scale.rb', line 6

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)
  # Pitch#to_s already emits unicode accidentals and HashKey already desymbolizes
  # them, so no accidental normalization is needed here.
  hash_key = HeadMusic::Utilities::HashKey.for([root_pitch, scale_type].join(" "))
  fetch_or_register(hash_key, root_pitch, scale_type)
end

Instance Method Details

#child_scale_letter_for_step(semitones_from_root) ⇒ Object (private)



110
111
112
113
114
# File 'lib/head_music/rudiment/scale.rb', line 110

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



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

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

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



52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/head_music/rudiment/scale.rb', line 52

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)



104
105
106
107
108
# File 'lib/head_music/rudiment/scale.rb', line 104

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)



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

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

#direction_sign(direction) ⇒ Object (private)



73
74
75
# File 'lib/head_music/rudiment/scale.rb', line 73

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

#flat_letter_for_step(semitones_from_root) ⇒ Object (private)



116
117
118
119
120
# File 'lib/head_music/rudiment/scale.rb', line 116

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)



97
98
99
100
101
102
# File 'lib/head_music/rudiment/scale.rb', line 97

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)



66
67
68
69
70
71
# File 'lib/head_music/rudiment/scale.rb', line 66

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)



85
86
87
88
89
# File 'lib/head_music/rudiment/scale.rb', line 85

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)



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

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

#pitch_class_number(semitones_from_root) ⇒ Object (private)



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

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

#pitch_classesObject



30
31
32
# File 'lib/head_music/rudiment/scale.rb', line 30

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

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



91
92
93
94
95
# File 'lib/head_music/rudiment/scale.rb', line 91

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



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

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

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



24
25
26
27
28
# File 'lib/head_music/rudiment/scale.rb', line 24

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

#root_pitch_numberObject



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

def root_pitch_number
  @root_pitch_number ||= root_pitch.number
end

#sharp_letter_for_step(semitones_from_root) ⇒ Object (private)



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

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



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

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