Long form ambient - trying to build long live_loops that crossfade between each other

Hey! I am working on building a long form generative ambient piece that will be a 24/7 live stream. So far I have built 2 samplers that select random clips from a folder and loop them a few times, and then pick another random sample from their respective folder.

What I am trying to figure out is what would be the most elegant way to crossfade between the incoming clips so that it isn’t as jarring. The elegant approach would be to figure out how to have sonic pi recognize that the live_loop was nearing the end of its playback of x amount of repititions, and then fade the amplitude out over 20 seconds or so while triggering and fading in the next sample simultaneously. Not sure if that’s possible within a single live_loop, or if it would need to be 2 instances.

The less elegant approach would be to have 4 samplers instead of 2, and sampler 1A and 1B fade in and out of each other, so that no crossfade has to be built just a fade in and out. same for 2A and 2B

Another way would be that each looper would just be looping indefinitely and then there was a global time which would trigger the fade in and fade out of the 2 loopers over a 20 second period at the same time.

Any ideas for how to achieve these long fades in and out between loopers? Would love some help figuring this out!

Here is the code so far:

f1 = Dir.glob(“C:/Users/…/f1/.wav")
f2 = Dir.glob("C:/Users/…/f2/
.wav”)

in_thread do
live_loop :f1 do
use_random_seed dice(244)
sample_file = choose(f1)
with_fx :reverb, room: 1, mix: rand do

  repetitions = rand_i(1..4)
  repetitions.times do
    sample sample_file, amp: 1
    sleep sample_duration sample_file
    puts "f1 repetitions: #{repetitions}"
  end
  
  
end

end
end

in_thread do
live_loop :f2 do
use_random_seed dice(244)
sample_file = choose(f2)
with_fx :reverb, room: 1, mix: rand do
# with_fx :echo, phase: 0.5, decay: 8 do

  repetitions = rand_i(1..4)
  repetitions.times do
    sample sample_file, amp: 1
    sleep sample_duration sample_file
    puts "f2 repetitions: #{repetitions}"
  end
  
  
end

end
end

live_loop :up do
  for fade in 0..5
    volume = (5 - fade)/5.0
    for x in 0..12
      midinumber = 48 + x
      play midinumber, amp: volume
      sleep 0.5
end; end; end

live_loop :down do
  for fade in 0..5
    volume = fade / 5.0
    for x in 0..12
      midinumber = 60 - x
      play midinumber, amp: volume
      sleep 0.5
end; end; end

Thanks for the response, the fade in in this example seems to be effecting individual midi notes amplitude which would work with a synth arpeggio, but I’m trying to fade in and out looping .wav file, even if it is halfway through its playback. Wondering if that is best achieved through ADSR or through a variable trigger that ramps down the amplitude at a certain time that corresponds with a ramping up of amplitude of another audio file

amp:
cannot be used with .wav ?

Hi Stewart
Quite a nice problem. I spent some time playing with this and came up with a single function which can be used to play n repeats of an input sample, fading in the first play and fading out the last one. The function “lasts” for the time from the beginning of the first play to the initiation of the fadeout, and this can be called repeatedly starting a subsequent sample to fade in as the first one fades out. In order to test this I used the built in :loop samples in Sonic Pi, choosing ones which lasted at least 4 seconds (at 60bpm) and I set the fadein/fadeout times each to be 2 seconds or beats. You can easily adjust this up to 20 if you have long enough samples. In order to make it more obivious at the merged changeovers I added the ability to pan the sample outputs, and also to limit the max amplitude at which they play. If these paramteres are omitted they default to 0 and 1 respectively.
The logic in the function is a bit involved, but basically it determines first if your sample is only to be played once, in which case it fades it in and fades it out again at the end.
If there are two repetitions it fades the first one in by controlling its amplitude, and then starts the second repetition at the specified maxAmp and fades that one out.
If it determines there are more than two repetitions it applies the fade in to the first repetition, then plays the required number of “middle” repetitions at maxAmp, and then starts the final repetition and fades that out at the end.
The sample playing and fading is done inside an in-thread structure, and in parallel with that a single sleep equal to the duration of the number of repetitions specified minus the final fade time is applied. Thus if the function is called twice with different samples the second sample starts fading in as the first one fades out giving smooth transitions.

#exsmple script by Robin Newamn, May 2023
#repetetive sample merger

use_debug false #remove clutter from the log

set :del,2 #fade up or down time Adjust as required eg maybe 20 for longer samples

define :sPlay do |sname,repeats,pan=0,maxAmp=1|
  puts"##############################################"
  puts"#{sname} #{repeats} times at pan #{pan} amp #{maxAmp}"
  puts"##############################################"
  use_sample_defaults pan: pan #set the pan value
  in_thread do
    del=get(:del)
    sd = sample_duration sname
    repeats.times do |n| #number of times sample to be played
      if n == 0 #start of samples playing
        v =  sample sname,amp: 0 #initial sample fades in
        control v, amp: maxAmp,amp_slide: del
        if repeats == 1 #if only one play then set up fade out
          sleep sd - del
          control v, amp: 0,amp_slide: del
          sleep del
        else
          sleep sd # sleep duration of sample
        end
      end #case n==0
      if n > 0 # on second or subsequent time through repeats loop
        if repeats > 2 and n < repeats -1 #on a complete middle play of sample
          sample sname,amp: maxAmp #play sample in full maxAmp
          sleep sd
        elsif n == repeats - 1 #last repeat through loop
          vend = sample sname,amp: maxAmp #start playing and set up fade out
          sleep sd -del
          control vend, amp: 0,amp_slide: del #fade out
          sleep del #duration of fade out
        end
      end #of subsequent pass through repeats loop
    end #repeats loop
  end #thread
  #set overall sleep duration of function = total duration minus fade out time
  sleep (sample_duration sname) * repeats - get(:del) #delay before next sample
end


#samples lists chosen from built-in loop samples >= 4 beats duration
#samples allocagted arbitrarily to the two lists
slist1 = [:loop_3d_printer, :loop_amen_full, :loop_compus, :loop_drone_g_97, :loop_garzul]
slist2 = [:loop_mika, :loop_safari, :loop_tabla, :loop_weirdo]

live_loop :testPlay do #shows the function in operation
  #choose and play sample from first list
#choose random repeats 1-3, pan 1 or -1 and random maxAmp
  sPlay slist1.choose, rrand_i(1,3),1,[0.2,0.4,0.6,1.0].choose #play samples at pan -1
  #choose and play sample from second list
  sPlay slist2.choose, rrand_i(1,3),-1,[0.2,0.4,0.6,1.0].choose #play samples at pan 1
end