Scale degrees and modes

Hi, I was looking at some videos on degrees, and building chords in scales.

There’s a video that talks about the modes, and applying this to different scales, which got me thinking how this could be done in SP.

The 7 Strange Scales Nobody Talks About - YouTube

It looks like sp’s degree function is for note resolution.

I tried to get at the known interval sequences in the Scale class, but not sure if that’s even possible…

SCALE = lambda{
      ionian_sequence     = [2, 2, 1, 2, 2, 2, 1]
      hex_sequence        = [2, 2, 1, 2, 2, 3]
      pentatonic_sequence = [3, 2, 2, 3, 2]

then I reviewed a few related topics. Whilst I didn’t see anything that seemed identical to this, I’d be surprised if this is the first!

First version simply produces the notes for a given scale and mode by rotating.


define :stm do |s = :major, t = 0, m = 1|
  scale_steps = []
  (Array scale t, s).to_a.each_cons(2) { |a, b| scale_steps << b - a }
  scale_steps.rotate!(m-1)  
  notes = [t] #v1
  
  scale_steps.each_with_index {|s,i| notes << notes[i]+s}  
  return notes
end

puts stm :major, 60, 2 
puts scale :c, :dorian

To make the method return the scale at given mode I added a start_pos variable, which I’m decrementing like with the mode indexer m

I thought it seemed to work a bit before “breaking” where the tonic stops climbing…

I tried to compare notes, but don’t think I’m quite there yet, with developing dorian functions and stuff like this

#WIP
define :stm do |s = :major, t = 0, m = 1, start_pos = 1|
  scale_steps = []
  
  (Array scale t, s).to_a.each_cons(2) { |a, b| scale_steps << b - a }
  scale_steps.rotate!(m-1)
  
  n = start_pos - 1
  scale_steps.first(n).each {|s| t += s}
  notes = [t] #v1
  
  scale_steps.each_with_index {|s,i| notes << notes[i]+s}
  return notes
end

a test-loop

uncomment do
  live_loop :scaling, init: 1 do |i|
    use_synth :piano
    tonic = :d
    ##| scale :c,  :melodic_minor
    scl = :major
    current_scale = stm scl, tonic, i, i
    puts current_scale, i
    play_pattern_timed current_scale, 0.25
    wait 1
    i+=1
  end
end

Optional-tags: this doesn't work yet! buggy!! scales idea! pre-creation WIP

Just putting this out there for feedback and stuff :palms_up_together:t2:

1 Like

I thought it seemed to work a bit before “breaking” where the tonic stops climbing…

I played with rings first, suspecting indexed array items was the issue, eg you can’t “get 6” from an array of 3, and expect repetition, like a ring…

I retested and found puts [0,1,2].ring.take(6) yields the expected, so revised the line that instantiates first “modal-tonic” and think I fixed that :boom:

original - I tried to ring, then get first(n)
scale_steps.first(n).each {|s| t += s}

fix
scale_steps.ring.take(n).each {|s| t += s}

:question: Originally I’d attempted to make notes a ring, but then I can’t add to it with the array.add operator << - is there a way to “push” to a ring? Just curious!

Latest version

define :stm do |s = :major, t = 0, m = 1, start_pos = 1|
  scale_steps = []
  
  (Array scale t, s).to_a.each_cons(2) { |a, b| scale_steps << b - a }
  scale_steps.rotate!(m-1)
  
  n = start_pos - 1
  scale_steps.ring.take(n).each {|s| t += s}
  notes = [t] #v1
  
  scale_steps.each_with_index {|s,i| notes << notes[i]+s}
  return notes
end

don’t think I’m quite there yet, with developing dorian functions and stuff like this

That GPT 5 is able to comprehend me is astonishing enough. I got a bit of help for the dorian transform function bit…

Mode Transformation masks…

Don’t ask me how these will be used yet, or why!

define :stm do |scale_type, tonic, mode|
  scale_steps = []
  (Array scale tonic, scale_type).to_a.each_cons(2) { |a, b| scale_steps << b - a }
  scale_steps.rotate!(mode.zero? ? 0 : mode - 1)
  
  current_note = tonic
  notes = [current_note]
  scale_steps.each { |step| notes << (current_note += step) }
  
  return notes
end

define :generate_modes do |scale_type, tonic|
  (1..7).map { |mode| stm(scale_type, tonic, mode) }
end

define :compare_scales do |original, transformed|
  original.zip(transformed).map { |o, t| t - o }
end

# Example usage
scale_type = :major
tonic = :c
original_scale = stm(scale_type, tonic, 1) # Mode 1 (original major scale)

modes = generate_modes(scale_type, tonic)
modes.each_with_index do |mode_scale, index|
  transformation_mask = compare_scales(original_scale, mode_scale)
  puts "Mode #{index + 1}: #{mode_scale}"
  puts "Transformation mask: #{transformation_mask}"
end
1 Like

You can “push” elements onto a ring with:

a = (ring 2, 4, 6)
puts a  # => (ring 2, 4, 6)
a += [8]
puts a  # => (ring 2, 4, 6, 8)
1 Like