Class: HeadMusic::Rudiment::Scale
- Inherits:
-
Object
- Object
- HeadMusic::Rudiment::Scale
- 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
-
#root_pitch ⇒ Object
readonly
Returns the value of attribute root_pitch.
-
#scale_type ⇒ Object
readonly
Returns the value of attribute scale_type.
Class Method Summary collapse
Instance Method Summary collapse
-
#child_scale_letter_for_step(semitones_from_root) ⇒ Object
private
-
#degree(degree_number) ⇒ Object
-
#determine_scale_pitches(direction, octaves) ⇒ Object
private
-
#diatonic_letter_for_step(direction, step) ⇒ Object
private
-
#direction_intervals(direction) ⇒ Object
private
-
#direction_sign(direction) ⇒ Object
private
-
#flat_letter_for_step(semitones_from_root) ⇒ Object
private
-
#initialize(root_pitch, scale_type) ⇒ Scale
constructor
A new instance of Scale.
-
#letter_for_step(step, semitones_from_root, direction) ⇒ Object
private
-
#octave_scale_pitches(direction, semitones_from_root) ⇒ Object
private
-
#parent_scale_pitch_for(semitones_from_root) ⇒ Object
private
-
#parent_scale_pitches ⇒ Object
private
-
#pitch_class_number(semitones_from_root) ⇒ Object
private
-
#pitch_classes ⇒ Object
-
#pitch_for_step(step, semitones_from_root, direction) ⇒ Object
private
-
#pitch_names(direction: :ascending, octaves: 1) ⇒ Object
-
#pitches(direction: :ascending, octaves: 1) ⇒ Object
-
#root_pitch_number ⇒ Object
-
#sharp_letter_for_step(semitones_from_root) ⇒ Object
private
-
#spellings(direction: :ascending, octaves: 1) ⇒ Object
Constructor Details
#initialize(root_pitch, scale_type) ⇒ Scale
Returns a new instance of Scale.
23 24 25 26 |
# File 'lib/head_music/rudiment/scale.rb', line 23 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_pitch ⇒ Object (readonly)
Returns the value of attribute root_pitch.
21 22 23 |
# File 'lib/head_music/rudiment/scale.rb', line 21 def root_pitch @root_pitch end |
#scale_type ⇒ Object (readonly)
Returns the value of attribute scale_type.
21 22 23 |
# File 'lib/head_music/rudiment/scale.rb', line 21 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 17 |
# File 'lib/head_music/rudiment/scale.rb', line 8 def self.get(root_pitch, scale_type = nil) root_pitch, scale_type = root_pitch.split(/\s+/) if root_pitch.is_a?(String) && scale_type =~ SCALE_REGEX root_pitch = HeadMusic::Rudiment::Pitch.get(root_pitch) scale_type = HeadMusic::Rudiment::ScaleType.get(scale_type || :major) @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)
114 115 116 117 118 |
# File 'lib/head_music/rudiment/scale.rb', line 114 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
50 51 52 |
# File 'lib/head_music/rudiment/scale.rb', line 50 def degree(degree_number) pitches[degree_number - 1] end |
#determine_scale_pitches(direction, octaves) ⇒ Object (private)
56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/head_music/rudiment/scale.rb', line 56 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)
108 109 110 111 112 |
# File 'lib/head_music/rudiment/scale.rb', line 108 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)
81 82 83 |
# File 'lib/head_music/rudiment/scale.rb', line 81 def direction_intervals(direction) scale_type.send("#{direction}_intervals") end |
#direction_sign(direction) ⇒ Object (private)
77 78 79 |
# File 'lib/head_music/rudiment/scale.rb', line 77 def direction_sign(direction) (direction == :descending) ? -1 : 1 end |
#flat_letter_for_step(semitones_from_root) ⇒ Object (private)
120 121 122 123 124 |
# File 'lib/head_music/rudiment/scale.rb', line 120 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)
101 102 103 104 105 106 |
# File 'lib/head_music/rudiment/scale.rb', line 101 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)
70 71 72 73 74 75 |
# File 'lib/head_music/rudiment/scale.rb', line 70 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)
89 90 91 92 93 |
# File 'lib/head_music/rudiment/scale.rb', line 89 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_pitches ⇒ Object (private)
85 86 87 |
# File 'lib/head_music/rudiment/scale.rb', line 85 def parent_scale_pitches HeadMusic::Rudiment::Scale.get(root_pitch, scale_type.parent_name).pitches if scale_type.parent end |
#pitch_class_number(semitones_from_root) ⇒ Object (private)
130 131 132 |
# File 'lib/head_music/rudiment/scale.rb', line 130 def pitch_class_number(semitones_from_root) (root_pitch.pitch_class.to_i + semitones_from_root) % 12 end |
#pitch_classes ⇒ Object
34 35 36 |
# File 'lib/head_music/rudiment/scale.rb', line 34 def pitch_classes pitches.map(&:pitch_class).uniq end |
#pitch_for_step(step, semitones_from_root, direction) ⇒ Object (private)
95 96 97 98 99 |
# File 'lib/head_music/rudiment/scale.rb', line 95 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
42 43 44 |
# File 'lib/head_music/rudiment/scale.rb', line 42 def pitch_names(direction: :ascending, octaves: 1) pitches(direction: direction, octaves: octaves).map(&:name) end |
#pitches(direction: :ascending, octaves: 1) ⇒ Object
28 29 30 31 32 |
# File 'lib/head_music/rudiment/scale.rb', line 28 def pitches(direction: :ascending, octaves: 1) @pitches ||= {} @pitches[direction] ||= {} @pitches[direction][octaves] ||= determine_scale_pitches(direction, octaves) end |
#root_pitch_number ⇒ Object
46 47 48 |
# File 'lib/head_music/rudiment/scale.rb', line 46 def root_pitch_number @root_pitch_number ||= root_pitch.number end |
#sharp_letter_for_step(semitones_from_root) ⇒ Object (private)
126 127 128 |
# File 'lib/head_music/rudiment/scale.rb', line 126 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
38 39 40 |
# File 'lib/head_music/rudiment/scale.rb', line 38 def spellings(direction: :ascending, octaves: 1) pitches(direction: direction, octaves: octaves).map(&:spelling).map(&:to_s) end |