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!