Pausing looping samples


#1

Hey guys, I am a very beginner in music composition and as a seasoned programmer I find Sonic Pi absolutely wonderful, because I always wanted to compose music.

My ear tells me that most modern music is made of looping samples running in different layers. A track usually starts with a single sample, then others come into play overlaping each other and making the music more complex. That’s easy, I can already do that in Sonic Pi after a couple of hours in the tutorials.

What I am struggling with is that sometimes during a song you want to pause a looping sample for say 4/8/16 beats and then restart it. How do I pause a sample in a loop for a specific amount of beats? Or even better, is there a way to modify properties of a looping sample on the go?

I tried a few approaches, I would expect something like this:

in_thread do
  loop do
    amb = sample :ambi_choir, rate: 0.5, attack: 5, release: 5, amp: 0
    sleep sample_duration(amb)
  end
end

in_thread do
  loop do
    beats = sample :loop_safari, rate: 1, amp: 0
    sleep sample_duration(beats)
  end
end

# start the first sample / layer (ambient background)
contol amb, amp: 1
sleep 8

# add the second sample / layer (beats)
control beats, amp: 1
sleep 16

# pause the second sample for 8 beats
control beats, amp: 0
sleep 8

# restart the second sample for 16 beats
control beats, amp: 1
sleep 16

# end the beats
control beats, amp: 0
sleep 8

# end the song
control amb, amp: 0

At the moment, I get:

Runtime Error: [buffer 3, line 11] - RuntimeError

Thread death!

Unknown sample filter type: SonicPi::SynthNode - got: #<SonicPi::SynthNode @id=3399, @name=sonic-pi-basic_stereo_player @state=pending>

But overall I’ve got a feeling that I’m doing it completely wrong, something tells me this is something many people would get across in the past and that there has to be solution.

Any hints are welcome. Thank you. :slight_smile:


#2

Hi @Kaspi,

welcome to Sonic Pi and welcome to in_thread.

Your question is kind of tricky (allthough musically a very common thing).

Some remarks:

  • I guess you get the error because sleep sample_durations([sample name]) is expecting just a sample name. (Also there’s a typo in your first ‘cont_r_ol’)
  • I am almost sure you will want to use the beat_stretch option for your drums because otherwise you can’t be sure if your drum loop matches your beat counting (e. g. in the sleeps); beat_stretch will stretch (or compress) the loop to the specified number of beats (which is of cause depending on your overall bpm, which is 60 if you don’t specifiy it otherwise)
  • As far as I see the code never makes it to the control section because you have 2 loops running. So I am pretty sure that the control sections also needs an in_thread; nevertheless, I tried the in_thread approach (which probably will also be possible) but:
  • For ‘loop-based’ music the construct live_loop is ideal. Besides, it is sort of a combination of in_thread do and loop do with the advantage, that anytime you’ll evaluate the code it’ll get updated without creating further threads (meaning in terms of sound: copies of already running loops)

So here are two possible solutions, which might help to find what you want to achieve:

live_loop :metro do # helps to count the beats
  sample :elec_blip, amp: 0.5
  sleep 1
end

live_loop :ambience do
  s = sample :ambi_choir, rate: 0.5, attack: 5, release: 5, amp: 1
  set :amb, s
  sleep sample_duration(:ambi_choir)
end

live_loop :drums do
  s = sample :loop_safari, rate: 1, amp: 0, beat_stretch: 8
  set :beats, s
  sleep 8
end

live_loop :controller do
  sleep 4
  
  control get(:beats), amp: (set :beats_amp, 1)
  sleep 4
  
  control get(:beats), amp: (set :beats_amp, 0)
  sleep 4
  
  control get(:beats), amp: (set :beats_amp, 1)
  sleep 4
end

Or:

live_loop :metro do
  sample :elec_blip, amp: 0.5
  sleep 1
end

live_loop :ambience, sync: :ambi_sync do
  sample :ambi_choir, rate: 0.5, attack: 5, release: 5, amp: 1
  sleep sample_duration(:ambi_choir)
end

live_loop :drums do
  sync :drum_sync
  sample :loop_safari, rate: 1, amp: 1, beat_stretch: 8
  sleep 8
end

live_loop :controller do
  cue :ambi_sync
  sleep 4
  
  cue :drum_sync
  sleep 8
  
  # pause drums for 8 beats
  sleep 8
  
  cue :drum_sync
  sleep 8
end

There are probably lots of other ways, so please do regard my propositions as provisional. I did shorten the sleep-times to better be able to check what’s going on.

Martin


#3

@Martin Thank you very much for taking your time to show me these examples.

I chose the first option as it seems to allow not only to pause the loops but also to set any parameters with any values (e.g. making the loop less out).

It seems clear to me except one thing, why would you set the amp as

amp: (set :beats_amp, 1)

instead of simply

amp: 1

Could you please explain this part? :slight_smile:


#4

Hi @Kaspi,

oh, I guess that is a leftover from me being experimenting with your code. Forget about it. Obviously not necessary :wink:

PS.: It is worthwhile to look into the sync-thing. You will need it, if you are going to tweak things live, get an error (with the effect of one of your loops stopping) and have to make sure everything runs in sync.


#5

Hi @Martin

Ok, no probs. :slight_smile: I will also have a look on sync and cue.

There’s one more thing I’m curious about. Your first example works fine with 4 beats length, but if I try to stretch the loop to 8 beats, it still plays for only 4 beats for some reason. I tried to completely isolate the loop as:

live_loop :drums do
  s = sample :loop_safari, rate: 1, amp: 1, beat_stretch: 8
  set :beats, s
  sleep 4
end

… and this just keep looping infinitely as expected. If you try to add some control though, like:

live_loop :drums do
  s = sample :loop_safari, rate: 1, amp: 0, beat_stretch: 8
  set :beats, s
  sleep 4
end

sleep 4

control get(:beats), amp: 1

sleep 8

control get(:beats), amp: 0

The beats seems to play for only 4 beats sinced turned on (4-8, not 4-12 as expected).

Any idea why is that?

Btw: it seems to be some problem related just to this loop_safari sample, other samples seems to work just fine. :-/


#6

Hi @Kaspi,

I am still looking into that because it is interesting. Don’t have much time right now but will return as soon as I have something worthwhile to contribute.


#7

Hi @Martin Thank you very much for caring. :slight_smile:


#8

Hi @Kaspi

have a look, if that works better for you:

use_bpm 120

live_loop :metro do
  sample :elec_ping, start: 0.125, finish: 0.5
  sleep 1
  15.times do
    sample :elec_ping, start: 0.125, finish: 0.5, rate: 2
    sleep 1
  end
end

live_loop :safari do
  d = sample :loop_safari, beat_stretch: 16
  set :drums, d
  sleep 16
end

#live_loop :ctrl do # run just once or loop the control statements
1.times do # 16
  control get(:drums), amp: 1
  sleep 12
  control get(:drums), amp: 0, amp_slide: 1
  sleep 4
end

2.times do # 8
  control get(:drums), amp: 1, amp_slide: 0.25
  sleep 4
  control get(:drums), amp: 0, amp_slide: 0.5
  sleep 4
end

4.times do # 8
  control get(:drums), amp: 1, amp_slide: 0.125
  sleep 2
  control get(:drums), amp: 0, amp_slide: 0.125
  sleep 2
end

#end