Chord extensions with degree function

#1

I have been working on creating some shortcuts for myself that include rhythm patterns, chord progressions, and arpeggiator patterns. In doing so I’ve run into a limitation in the “degree” function.

My idea was to store my arpeggiator patterns as rings of scale degrees, then use play degree() to play them, thereby being able to easily translate them to different scales and different chords within the scale. However, I’d like to be able to extend arpeggiator patterns to 9ths, 11ths, and 13ths and “degree” doesn’t currently do that. It’ll accept up to 8, but anything above that and it puts out a rest.

I feel like there could plenty of reasons why one might want to be able to plug in degrees beyond 8 into this function. In the meantime, however, here’s my workaround:

define :playdegree do |deg, tonic, scale|
  if deg > 7
    deg = deg - 7
    tonic = tonic + 12
  end
  play degree(deg, tonic, scale)
  sleep 1
end
1 Like
Sonification of Climate Data
#2

Hi @lwalchuk,

just as a side note (because it does not solve your problem but rather attempts to provide a different view on it):

I think the concept of degrees (or functional harmonics) is limited to 7 degrees simply because the 9th (and respectively the 11th and the 13th) is functionally equivalent to the II. degree. In its functionality this theory does not need to extend beyond the octave. With respect to that the degree function is in accordance with the musical theory - as far as I see.

Apart from that: I’d be interested in what you are working on. Are you planning to ‘release’ your shortcuts or code using it?

#3

I don’t think that limiting the system to 7 degrees is a good thing. In some occasions, one might need to extend it to include degrees such as IIb, VIb, etc… It should be at least chromatic to be complete. However, I agree that such a system should not consider going further than one octave (just because pitch /= degree).

#4

Hi @Martin!

I understand that the 9 is functionally the ii, etc. My concern comes from a practical standpoint, rather than theoretical. In lots of music, jazz in particular, the 9, 11, and 13 are regularly notated in chords, etc. When I’m creating my own “notation” for arpeggios, I want to be able to outline a pattern that is, say, [1, 3, 5, 7, 9], or something like that. It makes sense in my head and it’s the way I’d like to be able to notate things.

The other concern is that the “degree” function is limited to an octave, so even if I were to use 2 in place of 9, or 4 in place of 11, I have to change the octave as well to achieve the note I want. It adds an extra step.

To respond to @Bubo’s point – making the “degree” function chromatic would defeat its purpose. The reason I want to use it is so that I can make sure to stay within the given scale by calling out degrees. However, it does raise a good point - could there be a way to force a chromatic change, like adding a flat or a sharp to a degree, if the situation called for it?

Here are some of the functions I’m building. They’re essentially a list of patterns that I can call up by index, as well as specifying the number of steps in the pattern.

define :arp do |idx, deg = 1, num_notes = 0| #arpeggiator patterns
  num_patterns = 3
  assert idx >= 0 && idx < num_patterns, "First argument must be between 0 and #{num_patterns - 1}"
  ptrn = [1, 3, 5, 7] if idx == 0
  ptrn = [1, 5, 3, 7, 5] if idx == 1
  ptrn = [1, 3, 5, 6] if idx == 2
 # off = (deg - 2).to_i
  if num_notes == 0
    steps = ptrn.length
  else
    steps = num_notes
  end
  arpeg = ptrn.ring.take(steps)
#  arpplus = arpeg + off
#  arp = arpplus.map {|n| n.to_i}
  return arpeg
end

define :rtm do |idx, num_steps = 0| #rhythm patterns
  num_patterns = 13
  assert idx >= 0 && idx < num_patterns, "First argument must be between 0 and #{num_patterns - 1}"
  ptrn = (bools 1, 0, 0, 0) if idx == 0
  ptrn = (bools 1, 0, 1, 0) if idx == 1
  ptrn = (bools 1, 1, 1, 1) if idx == 2
  ptrn = (bools 1, 1, 1, 0) if idx == 3
  ptrn = (bools 1, 1, 0, 1) if idx == 4
  ptrn = (bools 1, 0, 1, 1) if idx == 5
  ptrn = (bools 1, 1, 0, 0) if idx == 6
  ptrn = (bools 1, 0, 0, 1) if idx == 7
  ptrn = (bools 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0) if idx == 8 #Son Clave 2/3
  ptrn = (bools 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0) if idx == 9 #Son Clave 3/2
  ptrn = (bools 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1) if idx == 10 #Rhumba Clave 2/3
  ptrn = (bools 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0) if idx == 11 #Rhumba Clave 3/2
  ptrn = (bools 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0) if idx == 12 #Chitlins Con Carne
  if num_steps == 0
    steps = ptrn.length
  else
    steps = num_steps
  end
  patrn = ptrn.take(steps)
  return patrn
end

define :prg do |idx, num_chords = 0| #chord progressions
  num_patterns = 14
  assert idx >= 0 && idx < num_patterns, "First argument must be between 0 and #{num_patterns - 1}"
  ptrn = [1, 4, 5] if idx == 0
  ptrn = [1, 5, 6, 4] if idx == 1
  ptrn = [1, 6, 4, 5] if idx == 2
  ptrn = [6, 4, 1, 5] if idx == 3
  ptrn = [1, 4, 6, 5] if idx == 4
  ptrn = [1, 4, 5, 4] if idx == 5
  ptrn = [1, 4, 1, 5] if idx == 6
  ptrn = [1, 5, 6, 3, 4, 1, 4, 5] if idx == 7 #pachelbel
  ptrn = [4, 5, 6, 1] if idx == 8
  ptrn = [4, 5, 6] if idx == 9
  ptrn = [1, 5, 4] if idx == 10
  ptrn = [6, 1, 4, 5] if idx == 11
  ptrn = [6, 2, 4, 5] if idx == 12
  ptrn = [4, 3, 2, 3] if idx == 13
  if num_chords == 0
    steps = ptrn.length
  else
    steps = num_chords
  end
  prog = ptrn.ring.take(steps)
  return prog
end
1 Like
#5

Yes, sure. That makes totally sense! Inspired by your idea I did some coding. I am not sure whether this is the direction you want to go in but you might want to have a look:

# @params:
# base = tonic
# mode = scale
# degree = notated as integer, 1..7
# num = number of notes to play
# returns ring with arpeggiated scale
define :arpeggio_from_degree do |base, mode, degree, num|
  notes = (scale base, mode, num_octaves: 3)
  i = degree - 1
  arp = []
  num.times do
    arp.push(notes[i])
    i += 2
  end
  return arp.ring
end

# Testing
notes = 6 # adjust to how many arpeggio notes you want
notes.times do
  play (arpeggio_from_degree :c4, :major, 7, notes).tick
  sleep 0.25
end

This is just a sketch and has some obvious limitations.

Nevertheless, it can already produce some nice sounds; I have just noticed that there is a scale called shang :wink:

live_loop :arpeggios do
  use_synth :fm
  use_synth_defaults release: 0.25, cutoff: rrand(70, 130)
  degrees = (range 1, 8)
  # Just learned about the pleasures of random_seed in Sam's last patreon video session 
  use_random_seed 20 # change to change mood
  d = degrees.shuffle.tick
  with_fx :reverb, room: 0.75, mix: 1 do
    7.times do
      play (arpeggio_from_degree [:c3, :c4].choose, :shang, d.to_i, 7).tick(:n), amp: rrand(0.25,1)
      sleep 0.125
    end
  end
end

And thanks for posting your other code. This looks interesting and I am looking foward to listen to some music where you are using this!

#6

The Sonic Pi scale definition actually includes degree symbols up to :xii, but then only looks them up in a single-octave scale, so maybe this is an oversight in the code? Maybe @samaaron would consider a PR to extend it in the next release.

EDIT: I just realised that the symbols up to :xii are needed for a single octave of the chromatic scale, so maybe it wasn’t an oversight after all. But the point remains that Sam might be open to extending this in a future release.

1 Like
#7

Happy to consider extensions to the behaviour. What’s your suggestion? :slight_smile:

#8

I think it would make sense to support degrees larger than an octave (such as a thirteenth).

It might also be useful to support augmented and diminished intervals (maybe by prefixing the symbol with an a or a d?) to add or subtract a semitone from the note (obviously that wouldn’t work with numeric degrees, but we could allow strings like "d5" as well as :dv for diminished 5th).

I’d be happy to put together a pull request if that sounds reasonable.

#9

How does this look @samaaron?

#10

Have you tried the chord_degree function? If you use this with .tick, .choose and other ring chains, you can arpeggiate through different chords. This function does include an argument which allows you to add extensions.

c = (chord_degree, 1, :a3, :major, 5) # The last argument determines how many notes in the chord. 
                                      # 5 includes the 9th, 6 includes 9th & 11th, 7 includes 9th, 1tth & 13th
                                      # I supposed you could use chains to drop a value or play diff patterns

live_loop :cord do
  c.length.times do
    play c.tick # arppegiates a major chord 1, 3, 5, 7, 9
    sleep 1
  end
end

Admittedly, I haven’t dug too deep into the code examples posted in this thread so I could be way off. But based on the code in your first post, you could do something similar in terms of changing the tonic, scale and degree with the chord_degree function as well as having the option for extensions.

1 Like
#11

I originally approached this using the chord_degree function. The problem is, I like to make arpeggiator patterns that don’t necessarily stick within the chord, like 1, 3, 5, 6, 5, 3 or something like that. That’s what led me to using degree instead.

#12

My PR was just merged :smiley: so the next Sonic Pi release will have an expanded degree function that supports intervals over one octave, as well as augmented/diminished intervals.

2 Likes
#13

@emlyn - thank-you again for your fab work :slight_smile:

#14

@samaaron thank YOU for making it possible! Sonic Pi is an awesome piece of software and it feels great to be able to do a little bit to make it even better :smiley:

2 Likes
#15

Just trying this out. Very nice and useful for doing chord progressions.
I also like your improved piano synth also in 3.2dev. Playing with a honky tonk piano generated using its micro tuning capability . Thanks a lot.

3 Likes