Melody improvisation

I am having quite a hard time improvising melodies with Sonic Pi.

A popular approach to improvisation (in jazz music) is to build “phrases” over chord progressions, using only the notes contained in the scale of every chord the harmony plays.

I am trying to code something like this, but the result turns out to be quite disappointing:

use_bpm 120

note_duration = ring(
  2.0,    # double
  1.0,    # whole
  0.5,    # half
  0.75    # dot half
)

scl = :major
chord_progression = ring(:ii, :v, :i, :i)

define :play_note do |note|
  play note \
    ,release: note_duration.choose() \
    ,attack:  [0,0,0,note_duration.tick/4].choose() \
    ,decay:   [0,0,0,note_duration.tick/4].choose() \
    ,sustain: [0,0,0,note_duration.tick/4].choose() \
    ,amp:     [1.0,0.75,0.25].choose()
end

live_loop :melody do
  chord_progression.each do |crd|
    
    phrase_finished = false
    
    8.times do
      print "=== ", crd, " ==="
      if one_in(10) and not phrase_finished then
        phrase_finished = true
      else
        if one_in(6) then
          # Silence
          sleep [0.125,0.5,0.250].choose()
        else
          play_note chord_degree(crd, :C4, scl).choose()
        end
      end
      
      sleep 1
    end
  end
end

Even though the code plays the right notes for every chord in the ii-V-I progression, it seems that I am missing something for the most important part: the rythm

Suggestion and ideas will be greatly appreciated. Cheers!

2 Likes

Nice program. By conicidence I was playing yesterday with some chord progressions doing something similar, but a little less elegenatly.

#commented section produced the offsets from the list of notes n
comment do
  n=[:c4,:e4,:g4,:c5,:a4,:c5,:e5,:a5,:f4,:a4,:c5,:f5,:g4,:b4,:d5,:g5,:e4,:g4,:c5,:e5,:f4,:a4,:c5,:f5,:d4,:f4,:c5,:f5,:d4,:f4,:b4,:d5,:e4,:g4,:b4,:e5,:c4,:e4,:a4,:c5,:d4,:f4,:a4,:d5,:b3,:d4,:g4,:b4,:c4,:e4,:g4,:c5]
  l=[]
  n.length.times do |i|
    l.concat [note(n[i])-note(:c4)]
  end
  puts l
end
##| l was transferrred to the ring x
use_bpm 60
set :x, (ring [0, 4, 7, 12],
         [9, 12, 16, 21],
         [5, 9, 12, 17],
         [7, 11, 14, 19],
         [4, 7, 12, 16],
         [5, 9, 12, 17],
         [2, 5, 12, 17],
         [2, 5, 11, 14],
         [4, 7, 11, 16],
         [0, 4, 9, 12],
         [2, 5, 9, 14],
         [-1, 2, 7, 11] )


set :dur, (ring 0.25,0.25,0.25,0.125,0.375,0.25,0.25,0.25,0.25)

live_loop :metro do
  sleep 0.125
end

with_fx :gverb,room: 20,mix: 0.6 do
  
  live_loop :pl,sync: :metro do
    use_synth :fm
    n=get(:x).tick(:ch)*2
    tick_set :tr,look(:ch)/12
    tr=(ring 60,65,67,56).look(:tr)
    d=get(:dur).shuffle
    tick_reset(:pl)
    play tr+n[0],release: 2,amp: 4
    use_synth :tb303
    8.times do
      tick(:pl)
      play tr+ n.look(:pl),release: d.look(:pl),cutoff: rrand(70,100) if (spread,7,8).look(:pl)
      sleep d.look(:pl)
    end
  end


  live_loop :dr,sync: :metro do
    sample :bd_haus,amp: 4
    sleep 0.5
  end

end #gverb
3 Likes

I also played around with some jazz improv code a while back. I wrote a program that I could trade solos with while playing along on guitar. My thought was to have a few different rhythms to choose from, push them into an array as they played and then once the value of all those rhythms reached a certain number, the improvising would stop until the next time around.

Here’s a video of me playing along with it. I am intentionally playing the rhythms in a way to mimic what Sonic Pi does.

Here’s the code:

live_loop :prog do
  8.times do
    use_synth :piano
    use_synth_defaults release: 1.5
    play (ring, chord(:c3, :M7), chord(:a3, :m7), chord(:d3, :m7), chord(:g3, :dom7)).tick, sustain: 1.25
    sleep 2
  end
  
  with_fx :reverb, room: 0.7 do
    
    use_synth :piano
    use_synth_defaults  release: 0
    s = scale(:d4, :dorian, num_octaves: 2)
    d = [0.125, 0.25, 0.375, 0.5]
    l = [0]
    use_random_seed rrand_i(30000, 40000)
    100.times do
      if l.inject(:+) < 15.5
        play s.choose, sustain: 2
        dur = d.choose
        sleep dur
        l.push(dur)
        puts l.inject(:+)
      end
    end
    sync :upright
    
  end
end



live_loop :upright do
  with_fx :lpf, cutoff: 60 do
    use_synth :chipbass
    use_synth_defaults release: 0.3, amp: 0.6
    play_pattern_timed chord(:c1, :M7), 0.5
    play_pattern_timed chord(:a1, :m7), 0.5
    play_pattern_timed chord(:d1, :m7), 0.5
    play_pattern_timed chord(:g1, :dom7), 0.5
    
  end
end

7 Likes

I only wish I could < heart > more than once.

Looks like great fun, must learn to play something one day…

Eli…

1 Like

@mrbombmusic that is really cool!

But I’m sure there should be a way in Sonic Pi to generate solos/melodies with richer rhythm patterns. In the pre-iRealB era, I used to play along with a software called band-in-a-box. It contains a totally awesome melody generator:

If only I could create melodies half as great as BIAB with sonic pi :wink:

I’ve been struggling with this, but finally I created a piece of code that seems to improvise a bit better than original version :slight_smile:

As a bonus it comes with a cool walking bass and some funky drums (borrowed from here)

Let me know your thoughts!

use_bpm 220

key = note(:C4)
mode = :major

chord_progression = [ :ii, :v, :i, :i] # Jazz - ii-V-I

chords=[]
chord_progression.each do |chord_from_progression|
  chords.append(chord_degree chord_from_progression, key, mode, 3)
end
chords = chords.ring

c = chords[0] # take the first chord of the ring and save it to a variable
# this is going to be used in all the live_loops. It will be ticked by the :bass loop

live_loop :melody do
  use_synth :beep
  use_octave +1
  r = [1, 1, 1, 2, 2, 4].choose
  play c.choose, \
    amp: rrand(0.4,1), \
    attack: rrand(0, 0.2), \
    pan: 0.5, \
    release: r if not one_in(8)
  sleep r
end

live_loop :keys do
  use_synth :beep
  r = [1, 1, 2, 4].choose
  play c, \
    attack: 0, \
    release: r, \
    amp: rrand(0.8,1.2), \
    pan: -0.5
  sleep 2
end

live_loop :bass do
  use_synth :fm
  use_synth_defaults attack:0, \
    release:0.2, \
    pan: -0.5, \
    amp: rrand(0.6,1.0)
  use_octave -2
  
  play_pattern_timed [c[0], c[1], c[2], c[1]], [2,2,2,2]
  c = chords.tick
end

# FUNKY DRUMS
ghost = -> { rrand(0.1, 0.2) }
normal = -> { rrand(0.5, 0.6) }
accent = -> { rrand(0.8, 0.9) }

swing_time = 0.1
puts swing_time

define :play_kick do
  sample :elec_hollow_kick, amp: normal.call
  play :A1, amp: normal.call
end

define :play_charles do
  sample :drum_cymbal_pedal, attack:0.02, amp:normal.call, rate:1.1
end


live_loop :drums do
  # Two ways of modelling beats - with 0s and 1s OR with indexes
  [1, 0, 0, 0, 1, 0, 0, 0].each.with_index(1) do |kick, index|
    play_kick if kick == 1 and one_in(3)
    
    if index == 5
      play_charles
    end
    
    if [1,3,4,5,7,8].include? index
      # Rand here can be really nice
      sample :drum_cymbal_soft, amp: ((index == 3 or index == 7) ? accent.call*1.5 : normal.call ) #if rand < 0.8
    end
    
    
    # snare on 2 and 4
    if index == 5
      if one_in(4)
        sample :drum_snare_hard, amp: normal.call*0.8, pan: 0.4
      end
    end
    
    
    # And sometimes on the and of 4
    if (index % 2) == 0
      sample :drum_snare_soft, amp: normal.call if rand < 0.05
    end
    
    
    if(index % 2) == 0
      # offbeats need to be slightly late for swing
      sleep (0.5 - swing_time)
    else
      sleep (0.5 + swing_time)
    end
  end
end
7 Likes

Nice! I especially like the hint of calypso music. Made me think of Jamaica Farewell.

Hi everyone! I was also experimenting with trying to create melodic improvisations with sonicPi that have somewhat a human feel to them. Right now I’m using a super simple technique to do this and I’m using it more in the context of live coding (not writing a full program before hand but modeling the improvisation as I go…). I’d like to try and create a SonicPi program that can kind of randomize the same thing that I’m currently doing manually.

I created a video where I have fun experimenting with this, first part of the video is creating the beat and harmony and second part is where I play around with the improvisation:

https://www.youtube.com/watch?v=0Ueg0nOyXec

Would love to have some feedback on this if anyone has a look at it :slight_smile:

4 Likes

I don’t know that I have any particularly helpful feedback, but I quite enjoyed that! The improvisation in the second half was great. It’s nice how a simple technique like having several blocks playing slightly different amounts of notes of the same scales, and changing a few note/rest lengths etc can make it sound ‘human’.

Thanks @ethancrawford I’m really glad you liked it! I have more of a musical background than a programming one so it’s interesting for me to try and replicate a human ‘feel’ with numbers :grinning: I plan to explore the idea more and see how much I could automate this process. Getting loads of inspiration by some of the code I see in this forum :smiley:

I sat listening to the video piece about 10 mins (or more, not sure). Absolutely mesmerised.

I dont think it’s perfect, but I’d hate to try and criticise it, constructively or not.

Eli…

1 Like

Hi,

I do appreciate your work a lot. It is inspiring. I also very much liked your Jungle Rhythms-Session. Your code to me seems very efficient yet mainly driven by musical thinking.

Pentatonics are probably a good start. In Jazz it is one way to gradually go from inside to outside playing which basically means e. g. to shift the melodic content from playing faithfully along with the chords (such as A minor pentatonic over a minor 7 chord) to more interesting combinations (such as E minor pentatonic over a minor 7 chord or B flat minor pentatonic over an altered dominant such as G7/b13 chord). (Really ‘outside’ would be C minor pentatonic over a minor 7 which works well depending on the context. I can almost not imagine how that can be mimiced without making it sound just like wrong notes. Would require some AI I guess.)

By the way, Simon Cossar did some really nice SP experiments which also have to do with automatically generated melodic sequences but probably a different style, e. g. Frac Progression (on Github).

one thing that might help with choice of pitch and temporal intervals might be to use l-systems, or otherwise some other biologically motivated function, rather than straight-ahead randomness over a uniform distribution (i didn’t look too carefully over the posted code, but i’m guessing the ones that are working out are carving out contours that conform to historically meaningful distributions). i think there are a few papers about it from a while back… though i’m not as familiar with algorithmic music literature.

i’ve been meaning to give it a shot for a while now, since it’s pretty fundamental in vfx work for generating biologically plausible structures. (i hadn’t found a good test-bed environment for working with music, but now sonic pi and ruby seem good for the job) but if anyone is interested in this direction, there’s a book called Texturing and Modeling: A Procedural Approach, that goes through a lot of the procedural techniques used in film production, for shader writing for example.

i find algorithmic composition language a little daunting, but at least have a sense for what works in this context. so, maybe someone may get something out of it as well

1 Like

@Martin Thanks for the feedback and for the reference to Simon Cossar’s work, it’s really helpful to look at the code and see how melodic progressions can be generated. I’m guessing combining something like this with rhythmic sequences as well could give interesting results. I agree that generating something that can play inside to outside and vice versa without having it sound like mistakes would be quite challenging. Maybe by breaking down the scales into tetrachords and matching chords to tetrachords (instead of the whole scale) would help eliminate some of the notes that could sound “wrong”. I guess it would need quite alot of setup (defining all the patterns and matching them to chords…) but it’s an interesting option to maybe experiment with :slight_smile:

Another idea I had was to try and write something that would use middle eastern scales (using quarter notes). I love the fact that you can kind of “fine tune” SP and create your own scales, always wanted to play middle eastern music but was limited by the fact that I play keyborad instruments and don’t have access to quarter tones. Maybe SP will break me free from this constraint :smile: Would love to know if anyone has tried something similar…

By the way, I visited your blog and had a listen to your piece Hommage To Detroit and I really loved it, love the way it develops and the funky references :slight_smile:

Hi @EarthToAbigail,

yes (concerning the tretrachords), the limitation is probably a good choice, that’s why I also favoured the pentatonics. And truly it is not only about the note material: rhythm and (perhaps even more) phrasing are very important.

Actually I am not into the ‘automatic’ improvisation of lead melodies (like e. g. in Jazz); it is a very interesting issue, but I’d rather don’t leave that to the machine. Nevertheless I did some experiments to generate more or less interesting (and at the same time easy to code) basslines, which of course touches this issue. But it might not be that complex to create - as @ds604 put it - ‘biologically plausible structures’. A simple example:

use_bpm 120

live_loop :bass do
  use_synth :fm
  use_synth_defaults depth: 1, divisor: 1, release: 3, amp: 3
  play :g1, release: 3
  sleep 4
end

live_loop :bass_support do
  use_synth :fm
  use_synth_defaults depth: 1, divisor: 1, release: (ring 0.2,0.25,0.3).choose
  if spread((ring 3,5,9).choose,8).drop(1).tick
    play (scale :g1, :minor_pentatonic).drop(1).choose, release: 0.125, amp: rrand(0.5,2)
  end
  sleep (ring 0.25,0.25,0.5).choose
end

I am curious about what you and others come up with next. Let us know. I do learn a lot by studying the code and the ideas others within this growing Sonic Pi-community!

1 Like

hehe, ‘biologically plausible structure’… i guess i kind of lapse into testing ideas against my original field of atmospheric science. so, when i’m like, ‘dj, play that funky methanogenic cyanobacteria!’ that’s what that’s about

but, more concretely, musical structure and time-series analysis are pretty closely related, so when looking for historical ‘event onset’ information and laying out ‘plausible outcomes,’ thinking in musical terms helps to keep things tangible

Hi @ds604,

yes, I think I see what you mean. For me (coming not from the hard-boiled science but from the arts) in the end its about patterns, which have been established in certain genres/forms/styles/periods (including the option to establish new patterns, which break/develop the old ones). These are probably something similar to what you were refering to as ‘biologically plausible structure’ (to put it simple: things we are culturally used to).

Of course things are much more complicated when you look at it more closely… :wink:

SampleRadar has a whole pack of ‘Eastern’ drums and instruments, which slice nicely into your own
home grown Eastern beats.

Or just with the built-in samples…

## Eastern pluck
## Coded by Nanomancer
## Modifications by Eli. April 2018.

set_volume! 0.55
set_mixer_control! amp: 0.1, amp_slide: 0.1
sleep 1
set_mixer_control! amp: 5, amp_slide: 12
use_bpm 90

seeds = [1024,2048,3072,4096,5120,6144,7168,8192,9212,512].ring
use_random_seed 999
vol=0.3

live_loop :low_boom, delay: 8 do
  sample :bd_boom, rate: 0.75, amp: 1
  sleep [7,1,8].ring.tick
end

live_loop :drums do
  use_bpm 45
  this_sample = [:loop_compus, :loop_tabla, :loop_safari].ring
  start = [ 0.0 , 0.125 , 0.25 , 0.375 , 0.5 , 0.625 , 0.75 , 0.875 ].ring
  sample this_sample.look , beat_stretch: 4, start: start.look, rate: 0.5
  sleep 1
  tick
end

live_loop :eastern_twang do |idx|
  use_synth :pluck
  if rand() > 0.75 then
    seeds = [1024,2048,3072,4096,5120,6144,7168,8192,9212,512].ring
  else
    seeds =seeds.shuffle
  end
  with_random_seed seeds[idx] do
    notes = scale(:a3, :harmonic_minor, num_octaves: dice(2)).pick(4)
    multi = [0.5,1,1.5,2].choose
    cut = range(50, 130, step: 2.5).mirror.ring
    slp = [1,1.5,0.5,1].ring
    
    if multi == 0.5 || multi == 1 then
      reps = 16
    else
      reps = 8
    end
    
    with_fx :echo, mix: 0.4, phase: 2, max_phase: 4 do
      with_fx :reverb, mix: 0.65, room: 0.85 do
        reps.times do
          play notes.tick, amp: vol*rdist(0.09, 1),
            cutoff: rdist(15, 115), pitch: rdist(0.1, 0)
          sleep slp.look*multi
        end
      end
    end
    
    if one_in(3) then
      sleep 8
    end
    
    idx+=1
  end
end

Eli…

3 Likes

I really enjoyed this. Definitely a creative way of generating more human like improvisations. I started playing around with making a function to imitate what you are doing to make the code a little more concise. It doesn’t afford all the options you have, but you can make quick changes to the repetition values and/or sleep values and the function will automatically figure out how much additional sleep time is needed at the end to make an even 4 beats. I also included parameters to decide if the scale will tick or choose notes.

I didn’t include any accompaniment but feel free to try it out over your pre-existing code.

define :bar do |t1, r1, t2, r2, tc1, tc2|
  s = 4-((t1*r1)+(t2*r2))
  use_synth :piano
  with_fx :gverb do
    if tc1
      t1.times do
        play scale(:e4, :minor_pentatonic, num_octaves: 2).tick
        sleep r1
      end
    else
      t1.times do
        play scale(:e4, :minor_pentatonic, num_octaves: 2).choose
        sleep r1
      end
    end
    
    if tc2
      t2.times do
        play scale(:e4, :minor_pentatonic, num_octaves: 2).tick
        sleep r2
      end
    else
      t2.times do
        play scale(:e4, :minor_pentatonic, num_octaves: 2).choose
        sleep r2
      end
    end
    sleep s
  end
end
use_bpm 110

#Change values for different patterns.
#Changing the last two arguments between true and false will change from tick to choose
live_loop :solo do
  bar 4, 0.25, 6, 0.667, true, false
  bar 4, 0.5, 2, 0.75, false, true
  bar 3, 0.1667, 6, 0.25, false, true
  bar 9, 0.25, 4, 0.5, true, false
end

3 Likes

Somehow, this reminds me of traditional Chinese/Japanese tonal music played in various
ceremonies and in temples in both countries. Exploring it could be interesting…

Eli…