Onset Percussion

Utilising my listOnsets function, this piece plays Sonic Pi :loop samples processed at the fractional onset level giving some interesting effects.


#Onset Percussion by Robin Newman, May 2018
#utilises use of onset values whilst playing loop samples
#two streams are played together, one handling the first section of each onset, and the other
#handling gthe second section of each onset.The streams hav a slight delay between them
#so that they match up at the correct times. Each segment has its pan and ampitude settings varied.
#giving and interesting end effect.
#Each loop is pl;ayed four times, on the fourth pass, the order of the onsets are shuffled.
#This is just one example of what can be done using the function listOnsets

use_debug false
use_cue_logging false
define :listOnsets do |s|
  set :onsetsMaps,[] #initialise a blank list
  l = lambda {|c| set :onsetsMaps,c.to_a; c[0]} #set the info obtained in :listonsets
  sample s, onset: l,amp: 0,finish: 0 #trigger the lambda function played sample at 0 volume and finish=0
  return get(:onsetsMaps)
end
load_samples "loop_"
sleep 2



with_fx :reverb,room: 0.8 do
  live_loop :funky do
    onsets=[] #set list empty to ensure pass of while loop
    while onsets.length<2 #process onsets, until number of onsets >1
      sm=(sample_names :loop).tick #get next sample name
      onsets=(listOnsets sm)
      sleep 0.01
    end
    stop if look==20
    puts sm,look #current sample
    onsets=listOnsets(sm)
    p=range(0,onsets.length) #range to choose offsets
    pp=4.0/onsets.length #pp used for panning increments
    4.times do |z|
      p=p.shuffle if z==3 #scramble last time
      puts p #current range
      pos=1 #start pan value
      fraction=rrand(0.1,0.9) #fractional split for two onset streams
      onsets.length.times do |i| #cycle through onset values playing them
        pos+=pp #increment pan position
        pp=-pp if pos>1-pp.abs #check when limits reached and reverse
        pp=-pp if pos< pp.abs
        #puts fraction,pos  #print fraction and pos values for debug checks
        st=onsets[p[i]][:start];fi=onsets[p[i]][:finish] #start and stop for current onset
        #stream1 plays first section of each onset
        sample sm,rate: 1,pan: pos,start: st,finish: st+(fi-st)*(1-fraction),amp: (ring 1,2,3,2).tick(:vol)
        #stream 2 is delayed then plays second section of each onset in a concurrent thread
        in_thread do
          sleep (fi-st)*(1-fraction)*(sample_duration sm)
          sample sm,rate: 1,pan: -pos,start: st+(fi-st)*(1-fraction),finish: fi,amp: (ring 3,2,1,2).look(:vol)
        end
        sleep sample_duration sm,start: st,finish: fi #sleep for duration of current onset
        pos+=pp #sounded nicer with second increment of pos here
      end
    end
  end
end
3 Likes

To make it even more funky, I’ve added occasional density functions to the replay.
This version plays the loop normally, then twice with the onsets in seuqeunce but effects added, and finally with the onsets in shuffled order,

#Onset Percussion2 by Robin Newman, May 2018
#utilises use of onset values whilst playing loop samples
#two streams are played together, one handling the first section of each onset, and the other
#handling gthe second section of each onset.The streams hav a slight delay between them
#so that they match up at the correct times. Each segment has its pan and ampitude settings varied.
#giving and interesting end effect.
#Each loop is pl;ayed four times, on the fourth pass, the order of the onsets are shuffled.
#This is just one example of what can be done using the function listOnsets

use_debug false
use_cue_logging false
define :listOnsets do |s|
  set :onsetsMaps,[] #initialise a blank list
  l = lambda {|c| set :onsetsMaps,c.to_a; c[0]} #set the info obtained in :listonsets
  sample s, onset: l,amp: 0,finish: 0 #trigger the lambda function played sample at 0 volume and finish=0
  return get(:onsetsMaps)
end
load_samples "loop_"
sleep 2



with_fx :reverb,room: 0.8 do
  live_loop :funky do
    onsets=[] #set list empty to ensure pass of while loop
    while onsets.length<2 #process onsets, until number of onsets >1
      sm=(sample_names :loop).tick #get next sample name
      onsets=(listOnsets sm)
      sleep 0.01
    end
    stop if look==20
    puts sm,look #current sample
    onsets=listOnsets(sm)
    p=range(0,onsets.length) #range to choose offsets
    pp=4.0/onsets.length #pp used for panning increments
    puts "First play normal"
    sample sm
    sleep sample_duration sm
    puts "Now play three times using onsets, wtih occasional density function, last time randomised"
    3.times do |z|
      p=p.shuffle if z==2 #scramble last time
      puts p #current range
      pos=1 #start pan value
      fraction=rrand(0.1,0.9) #fractional split for two onset streams
      onsets.length.times do |i| #cycle through onset values playing them
        pos+=pp #increment pan position
        pp=-pp if pos>1-pp.abs #check when limits reached and reverse
        pp=-pp if pos< pp.abs
        if one_in 5
          dv=[2,2,3].choose
        else
          dv=1
        end
        density dv do
          #puts fraction,pos  #print fraction and pos values for debug checks
          st=onsets[p[i]][:start];fi=onsets[p[i]][:finish] #start and stop for current onset
          #stream1 plays first section of each onset
          sample sm,rate: 1,pan: pos,start: st,finish: st+(fi-st)*(1-fraction),amp: (ring 1,2,3,2).tick(:vol)
          #stream 2 is delayed then plays second section of each onset in a concurrent thread
          in_thread do
            sleep (fi-st)*(1-fraction)*(sample_duration sm)
            sample sm,rate: 1,pan: -pos,start: st+(fi-st)*(1-fraction),finish: fi,amp: (ring 3,2,1,2).look(:vol)
          end
          sleep sample_duration sm,start: st,finish: fi #sleep for duration of current onset
        end
        pos+=pp #sounded nicer with second increment of pos here
      end
    end
  end
end

I ran this code and got this error:

Haven’t really combed through it all yet, but wanted to bring it to your attention.

Love the onset function, btw. Glad to see someone else’s take on it. Sam had recommended it to me a while back and said “it might blow your mind”

I was not disappointed.

Hmm Not sure what’s going wrong. I copied the code from the post back to my Sonic Pi and it ran fine. The pan value is increasing up to 1 (where it should start decreasing again down to -1) and so on. Maybe my end conditions for switching direction were not safe enough. You could adjust it for closer limits.

@robin.newman the recording sounds great!

I also tried to run this on my local machine and got the same error as @mrbombmusic after about 1 second. I got it to run a bit longer than a second by initializing pos to pos=0.5, though. Though eventually that also errored out with a similar error:

Thread death +--> :live_loop_funky
 Value of opt :pan must be a value between -1 and 1 inclusively, got 1.106060606060606.

I had another look at this, and there were erros in the pan values. Strangely my Mac accommodated values < -1 without throwing an error.
I’ve now revamped the calculations using .min and .max to check the limiting conditions and I think it is now OK. Try it and let me know.

#Onset Percussion3 by Robin Newman, May 2018
#utilises use of onset values whilst playing loop samples
#two streams are played together, one handling the first section of each onset, and the other
#handling gthe second section of each onset.The streams hav a slight delay between them
#so that they match up at the correct times. Each segment has its pan and ampitude settings varied.
#giving and interesting end effect.
#Each loop is pl;ayed four times, on the fourth pass, the order of the onsets are shuffled.
#This is just one example of what can be done using the function listOnsets
#NEW VERSION WITH CORRECT PAN VALUES

use_debug false
use_cue_logging false
define :listOnsets do |s|
  set :onsetsMaps,[] #initialise a blank list
  l = lambda {|c| set :onsetsMaps,c.to_a; c[0]} #set the info obtained in :listonsets
  sample s, onset: l,amp: 0,finish: 0 #trigger the lambda function played sample at 0 volume and finish=0
  return get(:onsetsMaps)
end
load_samples "loop_"
sleep 2

with_fx :reverb,room: 0.8 do
  live_loop :funky do
    onsets=[] #set list empty to ensure pass of while loop
    while onsets.length<2 #process onsets, until number of onsets >1
      sm=(sample_names :loop).tick #get next sample name
      onsets=(listOnsets sm)
      sleep 0.01
    end
    stop if look==20
    puts sm,look #current sample
    onsets=listOnsets(sm)
    p=range(0,onsets.length) #range to choose offsets
    pp=4.0/onsets.length #pp used for panning increments
    #puts "pp",pp
    puts "First play normal"
    sample sm
    sleep sample_duration sm
    puts "Now play three times using onsets, wtih occasional density function, last time randomised"
    3.times do |z|
      p=p.shuffle if z==2 #scramble last time
      puts p #current range
      pos=-1 #start pan value
      fraction=rrand(0.1,0.9) #fractional split for two onset streams
      onsets.length.times do |i| #cycle through onset values playing them
        #puts "pan pos",pos
        if one_in 5
          dv=[2,2,3].choose
        else
          dv=1
        end
        density dv do
          #puts fraction,pos  #print fraction and pos values for debug checks
          st=onsets[p[i]][:start];fi=onsets[p[i]][:finish] #start and stop for current onset
          #stream1 plays first section of each onset
          sample sm,rate: 1,pan: pos,start: st,finish: st+(fi-st)*(1-fraction),amp: (ring 1,2,3,2).tick(:vol)
          #stream 2 is delayed then plays second section of each onset in a concurrent thread
          in_thread do
            sleep (fi-st)*(1-fraction)*(sample_duration sm)
            sample sm,rate: 1,pan: -pos,start: st+(fi-st)*(1-fraction),finish: fi,amp: (ring 3,2,1,2).look(:vol)
          end
          sleep sample_duration sm,start: st,finish: fi #sleep for duration of current onset
        end
        if pp>0
          pos=[pos+pp,1].min
          pp=-pp if pos==1
        end
        if pp<0
          pos=[pos+pp,0].max
          pp=-pp if pos==0
        end
      end
    end
  end
end
2 Likes