Sonic Pi quicksort My Take

Earlier today I came across a great tweet from Damian Mooney @damionmooney in which he posed the question
what rhythm does an algorithm have? and gave the answer in code for quicksort in sonic-pi. I liked the idea a lot and delveloped a piece for Sonic Pi using it, which I share below with his permission.
First his original code which I copied from the video in the tweet.

# What rhythm does an algorithm have?
# quicksort in sonic-pi
def sort(to_sort)
  #stop the recursion if nothing to sort
  play_pattern_timed (to_sort),0.2
  if to_sort.length <= 1 then return to_sort end
  #pick the pivot (I chose the last element)
  pivot = to_sort.pop
  # partition operation
  smaller_array = []
  larger_array = []
  to_sort.each do |element|
    if element <= pivot
      smaller_array.push(element)
    else
      larger_array.push(element)
    end
  end
  # recursively sort the sub arrays and concatenate the results
  return sort(smaller_array).push(pivot) + sort(larger_array)
end
use_synth :pluck
mynotes = [61,51,49,73,63,65,55,67,57,53,47,59,69,71,45]
play_pattern_timed sort(mynotes), 0.2
puts("Sorted")

He wrote this for Sonic Pi 2.10 but it works as is on Sonic Pi 3.0.1
However it uses a Ruby def function to define the sort code, and it is preferential to add function code to Sonic Pi using the define keyword, so the first thing I did was to make the minor alteration to acheive this, changing
def sort(to_sort)
into
define :sort do |to_sort|
I then went on to play with the idea and ended up with the code below. I used the notes from a 5 octave minor_pentatonic scale for my source, which I then scrambled using the Ruby .shuffle method, and I also added an extra paramter to the sort function to allow a pan value to be passed into it.
I setup two simultaneous sorts using different scrambled versions of the scale, playing them with different synths :plck and :blade and alternating their pan positions on subsequent passes from -1 to +1
I put a pause in the sort function after the note string supplied on each recursive call had been played, which gave some interesting rhythmic breaks, and I also added some printed output of these strings to give something visual to look at.
At the end of each completed sort, I waited until each parallel sort had ended (they might differ in time) and then played the sorted strings (which would be the same) with the two differnt synths together at a slower tempo, but with one of them reversed.
The code was arranged so it could play continously (reuse of shuffle would mean that the initial strings would be different for each new iteration. A mechansims was also added so that they process could be stopped after a given number of passes, and below it is set to 4.
Finally the whole was played in a gverb fx loop to give an ethereal feel to the piece.
I hope you enjoy the end result. Again thanks to Damian for a great idea.

#This program is based on a tweet from @damianmooney sent to @sonic_pi
#all credit for using the quicksort routine to play in sonic pi is his
#I have modified the routing slightly to add some printout and to
#add a delay after each of the play_pattern_timed sections
#I have then used it twice with two related lists of notes to
#produce
define :sort do |to_sort,p|
  #stop the recursion if nothing to sort
  puts to_sort #rbn added printout of to_sort
  play_pattern_timed (to_sort),0.2,pan: p
  sleep 0.4 #rbn added delay after each play pattern
  if to_sort.length <= 1 then return to_sort end
  #pick the pivot (I chose the last element)
  pivot = to_sort.pop
  # partition operation
  smaller_array = []
  larger_array = []
  to_sort.each do |element|
    if element <= pivot
      smaller_array.push(element)
    else
      larger_array.push(element)
    end
  end
  # recursively sort the sub arrays and concatenate the results
  return sort(smaller_array,p).push(pivot) + sort(larger_array,p)
end

with_fx :gverb, room: 20 do #play everything with gverb
  ppos=1
  loop do
    f1=0;f2=0
    ##| mynotes = [61,51,49,73,63,65,55,67,57,53,47,59,69,71,45]
    #generate mynotes to sort from minor_pentatonic scale
    #convert it to an array rather than a ring
    mynotes=scale(:c2,:minor_pentatonic,num_octaves: 5).shuffle.to_a
    mynotes2=mynotes.shuffle #second mynotes reshuffle mynotes
    
    #process mynotes in a thread, using :pluck synth
    in_thread do
      use_synth :pluck
      play_pattern_timed sort(mynotes,-ppos),0.2 #plays partial arrays
      sleep 0.8 #delay at end then set finish flag :f1
      f1=1
    end
    #process mynotes2 in a second thread, using :blad synth
    in_thread do
      use_synth :blade
      play_pattern_timed sort(mynotes2,ppos),0.2 #plays partial arrays
      sleep 0.8 #delay at end then set finish flag :f2
      f2=1
    end
    #poll and wait until BOTH finish flags are set
    while f1==0 and f2==0;sleep 0.05;end
    #play sorted array from mynotes => :sarray1 in a thread
    
    in_thread do
      use_synth :pluck
      puts mynotes.sort
      play_pattern_timed mynotes.sort,0.3,pan: -ppos
    end
    #play reverse sorted thread :sarray2 from mynotes2
    #if the sort has worked properly this should be :sarray1 reversed
    use_synth :blade
    puts mynotes2.sort.reverse
    play_pattern_timed mynotes2.sort.reverse,0.3,pan: ppos
    
    puts "Finished pass #{tick+1}"
    ppos=ppos*-1 #reverse pan positions for next pass
    sleep 0.8
    
    stop if look >= 3 #remove this line to run continously or alter stop limit
  end #loop
end #gverb

You can hear it here

PS I see Damian has now tweeted a Bubble Sort version too. I’ll take a look at that too!