Playing live_loops in a given order

Hey!

I’m still working on having a nice setup for live-coding with Sonic Pi. I have yet to come up with a system to play several live_loops trigerred by their order in an array. Here is an example and details about what I’m trying to achieve:

# -- IDEAL PERFORMANCE SETUP
use_bpm = 130 # global tempo

track1 = [1, 2, 3, 1, 2, 1] # my drums
track2 = [X, X, 4, 5, 6, 1] # my synthesizer n°1
track3 = [7, X, 7, X, 7, 7] # my sound-effect
# Tracks are live_loops of same duration. The number is a reference to one specific 
# live_loop that I would like to trigger. 
# X = void live_loop of same duration.

free1 = [1, 3, 2, 1, 4, 1]
# Free could be a live_loop of undefined duration, syncing to tracks every now and then.

The idea behind all this would be to modify or create some live_loops before actually inserting them in a larger musical structure. For instance, when verse I is playing, I’m preparing verse II, but I’m still able to come back to verse I after some time if I want to, just by inserting the reference number again in the array.

Having several tracks layered could be interesting to prepare some special effects, like the typical rising synth before a strong musical episode or a variation on something already heard by the audience.

I know that some of you have come up with something similar, but mostly aimed at non-real-time composition. Do you have any ideas of how it could be possible to make this idea come true ?

Thanks for your help. Reading this forum has helped me a lot so far to use Sonic Pi with all the possibilities it offers.

I’ve been having a look at this, and came up with the ideas below.
The program controls 4 percussion liveloops and 4 tune liveloops The content of these is fairly rubbishy, but they are there to show how the setup works, and could have nicer contents substituted.

The idea is that each class has a list of triggers, one for each loop. So for the percussions loops these are
:p1,:p2,:p3 and :p4
For the tune loops they are :t1,:t2,:t3 and :t4
Each class has a playing list which repeats. This can also include a symbol :x (for the percussion loops) and :tx for the tune loops which “substitutes” a silent loop fo the same duration.

So the play list for percussion might be
pl=[:p1,:x,:p2,:x,:p3,:x,:p4,:x,:p1,:p2,:p3,:p4,:x,:x,:x,:x]

and for the tune loops
tl=[:tx,:t1,:tx,:t2,:tx,:t3,:tx,:t4,:tx,:tx,:tx,:tx,:t1,:t2,:t3,:t4]

All of the loops have the same duration (I choose 2 beats, but they could be 4 or 8)
Each live loop is synced to a cue. :goP for percussion and :goT for tune loops
These are triggered by two enable functions, which in turn are launched in a liveloop called conduct.
This is delayed by 0.01 compared to all the other loops, so that it will only issue cues (via the enable functions) when the recipient liveloops are waiting to receive them, so no cues are missed.
Each enable function moves through the relevant play list each time it is triggered, and sets the trigger for the relevant loop to true, setting the others to false. So when the cue is received by ALL the tune of percussion loops, only the one with a true trigger key will sound. The others are set to sleep for the 2 beat duration. Actually they sleep for 1 beat, to make sure that they are ready to receive the next cue.
Including an :x or :tx symbol in the relevant play list means that no liveloop triggers will match and a silent period ensues for 2 beats.

The code is shown below. It can in principle be developed to allow for further classes of liveloops to be added, each with its own enable function. If you want 4 or 8 beat liveloops the conduct liveloop can be modified to issue cues every 4 or 8 beats by changing its sleep time.

I would be interested in any comments on the setup.

#experimental live_loop sequencer by Robin Newman, December 2018
#The program controls 4 percussion and 4 tune liveloops.
#Each has a duration of 2 beats (or less)
#each group has a series of 2 flags. The first list contains an identifier for each loop
#the second lists the sequence playing order. An x signifies a silent loop
#an enable function for each group, enables the loop trigger flat
#matching the current sequence position, and then issues a start cue :goT or :goP.
#These enable functions are called in the conduct live loop.
#This is delayed to start last, and so only send cues when the liveloops are waiting.
#Inside each loop, the rleevant trigger flag enables the content, or else a short sleep
#The sleep is less than 2 beats, ensuring the live_loop is ready for the next cue
#The "playing" liveloops are carefully set up so that they are completed in time
#for the next :goT or :goP cue, so all loops remain in sync.

use_bpm 60
#percussion loop control
pl=[:p1,:x,:p2,:x,:p3,:x,:p4,:x,:p1,:p2,:p3,:p4,:x,:x,:x,:x] #play list for percussion liveloops :x is blank
p=[:p1,:p2,:p3,:p4] #available percussion liveloops triggers
p.each do |x| #disable all initially
  set x,:false
end
set :x,false #blank percussion loop flag

define :enablep do |t|
  cv=pl[t % (pl.length)] #next flag to be checked
  p.length.times do |i| #cycle through all available percussion triggers and enable match
    if cv==p[i]
      set p[i],true
    else
      set p[i],false
    end
  end
  cue :goP #cue percussion live_loops
end

#uncomment next loop to get metronome for debugging
##| live_loop :test do
##|   sync :goT
##|   play 84,release: 0.05
##| end

#tune loop control
tl=[:tx,:t1,:tx,:t2,:tx,:t3,:tx,:t4,:tx,:tx,:tx,:tx,:t1,:t2,:t3,:t4]#[:tx,:t1,:tx,:t2,:tx,:t1] #play list for tune liveloops :tx is blank
tn=[:t1,:t2,:t3,:t4] #available tune liveloop triggers
tn.each do |x| #disable all intially
  set x,false
end
set :tx,false #blank tune loop flag
define :enablet do |t|
  cv=tl[t % (tl.length)] #next flag to be checked
  tn.length.times do |i| #cycle through all available tune triggers and enable match
    if cv==tn[i]
      set tn[i],true
    else
      set tn[i],false
    end
  end
  cue :goT #cue tune live_loops
end



live_loop :conduct,delay: 0.01 do
  tick
  enablep(look) #can comment out to stop all percussion loops
  enablet(look) #can comment out to stop all tune loops
  sleep 2
  
end

live_loop :percussion1 do
  sync :goP
  puts "p1"
  if get(:p1)==true
    sample :loop_amen,beat_stretch: 2
    #no sleep so will be ready for next cue
  end
  sleep 1 #ready for next cue in plenty of time
end

live_loop :percussion2 do
  sync :goP
  #puts"p2"
  if get(:p2)==true
    sample :loop_garzul,beat_stretch: 4,finish: 0.5 #adjusted for 2 beats
    #no sleep so will be ready for next cue
  end
  sleep 1 #ready for next cue in plenty of time
end


live_loop :percussion3 do
  sync :goP
  #puts "p3"
  if get(:p3)==true
    sample  :loop_compus,beat_stretch: 4,finish: 0.5 #adjusted for 2 beats
    #no sleep so will be ready for next cue
  end
  sleep 1
end

live_loop :percussion4 do
  sync :goP
  #puts "p4"
  if get(:p4)==true
    2.times do |i|
      sample :bd_haus
      sleep 0.5
      sample :drum_cymbal_soft
      sleep 0.25
      sample :drum_cymbal_soft
      sleep 0.25 if i==0 #miss last sleep so ready for next cue
    end
  else
    sleep 1 #ready for next cue in plenty of time
  end
end

live_loop :tune1 do
  use_synth :pluck
  sync :goT
  if get(:t1)==true
    19.times do
      play scale(:c4,:major,num_octaves: 2).choose,release: 0.1,amp: 0.4
      sleep 0.1
    end
    play scale(:c4,:major,num_octaves: 2).choose,release: 0.1,amp: 0.4
    #no sleep so will be ready for next cue
  else
    sleep 1 #ready for next cue in plenty of time
  end
end

live_loop :tune2 do
  use_synth :tri
  sync :goT
  if get(:t2)==true
    19.times do
      play scale(:c2,:minor,num_octaves: 2).choose,release: 0.1,amp: 0.4
      sleep 0.1
    end
    play scale(:c2,:minor,num_octaves: 2).choose,release: 0.1,amp: 0.4
    #no sleep so will be ready for next cue
  else
    sleep 1 #read for next cue in plenty of time
  end
end

live_loop :tune3 do
  use_synth :saw
  sync :goT
  if get(:t3)==true
    9.times do
      play scale(:c3,:minor,num_octaves:2).tick,release: 0.2,amp: 0.5
      sleep 0.2
    end
    play [:c3,:c4,:c5],release: 0.2,amp: 0.8
    #no sleep so will be ready for next cue
  else
    sleep 1 #readn for nexrt cue in plenty of time
  end
end

live_loop :tune4 do
  use_synth :piano
  sync :goT
  if get(:t4)==true
    3.times do
      play [:c3,:e3,:g3,:c4],release: 0.5
      sleep 0.5
    end
    play [:c4,:e4,:g4,:c5],release: 0.5
    #no sleep so will be ready for next cue
  else
    sleep 1 #readn for nexrt cue in plenty of time
  end
end

You can alter the play lists and rerun and it will pick up the changes

Wow. It works really well. Sorry for the late reply, I was unable to connect for a few days. I’ll try to give it an in depth look tomorrow but thanks for helping me out on this problem!