Using QuickSort to produce randomized melody

I got a pretty cool idea: use quick sort to generate an arrpegio:

def quicksort(array)
  if (array.empty?)
    return array
  end
  note = array.choose
  do_play(note)
  return quicksort(array.select {|x| x < note}) +
    [note] +
    quicksort(array.select {|x| x > note})
end

def do_play(number)
  amp = ((tick(:amp) % 4 == 0) ? 0.4 : 0.2) + rand(0.1)
  if (get[:chord][number] > 40)
    amp = amp / 4
  end
  synth :fm, note: get[:chord][number], amp: amp, release: 0.2 + rand(0.1), depth: 5, pan: 0.2 - rand(0.4)
  sleep 0.125
end

live_loop :quicksort, delay: -1.0 / 32 do
  with_fx :level, amp: 0.4 do
    with_fx :nrlpf, res: 0.1  do
      set :chord, [chord(:a2, :minor7, num_octaves: 4), chord(:C2, :m9, num_octaves: 4)].ring.tick
      array = (1..64).to_a.shuffle
      array = quicksort(array)
      print array
    end
  end
end

Here is how it sounds with arrangement: https://soundcloud.com/artem-onuchin/quicksort

1 Like

Interesting idea. I’m doing something simpler. I have a function that automatically inverts a chord to constrain it within a range. In the piece I’m currently developing, I’m using chords inverted in this way as the basis for melody generation. It produces something that is highly musical, but not obviously arpeggios. Particularly for more interesting chord types.

That’s a good idea! Could you show a small example?

1 Like

Here’s a quick example I whisked up. Note that I use ‘limit’ both to automatically invert the chord chords, and for the bass I automatically inverted then took the lowest note in the inverted chord as the bass note to get altered bass. I’ve commented out an alternative ‘limiting’ of the chord in the bass live_loop, which gives a different bass line.

Note that I only started writing Sonic Pi programs a few days ago, and I am far from confident that I am doing things by the best ways. However, I find these automatic inversions interesting, and easy to use. E.g. it automatically transposes as well leading to fewer with_transpose statements in my code.


chords = (ring chord( :c3, 'm7' ), chord( :ab3, 'major' ),
          chord( :f3, 'm7'  ), chord( :g3, '7' ))

use_bpm 120

define :limit do |notes, low=36, high=72|
  ret = (ring)
  
  notes.each do |x|
    while x < low
      x = x + 12
    end
    
    while x > high
      x = x - 12
    end
    
    ret = ret + (ring x)
    
  end
  
  return ret
end

live_loop :chords do
  tick
  use_synth :dsaw
  use_synth_defaults sustain: 3.5, decay: 0.5, release: 0.5
  
  with_fx :ixi_techno, cutoff_min: 60, cutoff_max: 90, res: 0.3 do
    
    play limit( chords.look, 60, 71 )
    
    sleep 4
  end
end

live_loop :drums do
  at [ 0,  1, 2, 3 ] do
    sample :bd_haus
  end
  
  at [ 1, 3 ] do
    sample :sn_dolf
  end
  
  at [ 0.5, 1.5, 2.5, 3.5, 3.75  ] do
    sample :drum_cymbal_closed
  end
  
  sleep 4
end

live_loop :bass do
  tick
  
  #achord = limit( chords.look, 36, 47 ).sort
  achord = limit( chords.look, 40, 51 ).sort
  lownote = achord[0]
  
  print "chord: ", chords.look, " lownote: ", lownote
  print "achord: ", achord
  
  use_synth :dpulse
  use_synth_defaults sustain: 0.5, release: 0.2, cutoff: 70
  
  at [ 0, 0.75, 1.5 ], [ 1.0, 0.6, 0.8] do |a|
    with_fx :eq, low_shelf: 1.3 do
      
      play lownote, amp: a
      sleep 4
    end
  end
  
  
  sleep 4
end
1 Like