Audio loops and drift?

Hi all - have been in and out of here a couple times but really would like to start using Sonic Pi more seriously. Previous experiments were a little less than satisfying because audio loops seemed to drift out of sync after multiple play throughs. I’ve tried heartbeat, I’ve tried in_thread, and I’m just not sure what I’m doing wrong. I’ll explain my use case super quick:

I spend a lot more time writing music on iOS. It’s cool and all, but my day job involves more coding and I’d like to marry coding with music (I don’t do a ton of coding during the work week so I am scared I’m gonna get rusty, I want to stay on top of logic and code structure etc - but while having fun lol).

Ideally, I’d be cutting loops out of various iOS apps and then using Sonic Pi as the “glue” to hold it all together and manipulate them, plus add built in stuff on top. Just kind of disheartening even doing a simple test like a two bar drum loop and a four bar synth loop (and I know they are correct length and tempo from the source).

Any pointers? Is SP just not really great for working with longer audio loops? Many thanks in advance and I really am in awe of how cool this project is, even if my use case doesn’t really fit it super well.

Hi Matt
Welcome to Sonic Pi.

From your thread, I guess that you are using recordings of audio loops as samples and are trying to get them to play together. In fact Sonic Pi is incredibly accurate with timing and it matters a great deal the accuracy with which samples last if you want to be able to play them continuously and sync them together.

Lets look at three of the build-in samples
try this program

puts "bd_haus lasts ",sample_duration :bd_haus
puts"loop_amen lasts "sample_duration :loop_amen
puts loop_compus lasts ",sampleUdration :loop_compus

This produces

 ├─ "bd_haus lasts " 0.21993197278911564
 ├─ "loop_amen lasts " 1.753310657596372
 └─ "loop_compus lasts " 6.486485260770975

I order to get these to play nicely together and continuously in loops we need to determine the loop duration accurately and force the samples to play for accurate times that we can handle.

If we just want a sample to repeat continously we can set the duration of the loop to be equal to that of the sample

live_loop :amen1 do
  sample :loop_amen
  sleep sample_duration :loop_amen

This works nicely, but the duration of the loop will be 1.753310657596372 beats or 1.753310657596372 seconds assuming we are using the default bpm of 60.

It is difficult to sync other looping samples to this time directly. The trick is to change the sample time to something much easer to handle by stretching the time that the sample takes to play, either by speeding it up or by slowing it down. This is done using the opt beat_stretch:

Here is a programs that plays all three samples synced together in loops

puts "bd_haus lasts ",(sample_duration :bd_haus)
puts"loop_amen lasts ",(sample_duration :loop_amen)
puts "loop_compus lasts ",(sample_duration :loop_compus)

live_loop :dr,delay: 0.001 do #see delay note at the end
  sample :bd_haus #percussive sample lasts less that 1 beat
  sleep 1

live_loop :amen,sync: :dr do
  sample :loop_amen,beat_stretch: 2 #slow down to 2 beats
  sleep 2
live_loop :compus,sync: :dr do
  sample :loop_compus,beat_stretch: 8 #slow down to 8 beats
  sleep 8

These will all play nicely together. The delay parameter in the :dr loop ensures that the two other loops are waiting to go before the :dr loop starts and sends a sync cue to start the other two loops at the same time. Without it (try missing it out) you will get a single ;bd_haus drum and then the other loops will start synced on the second pass of the :dr loop.

Hope this gives you a starting point from which to experiment. See section 3.3 of the Tutorial for more.


Robin thanks! I gotta sit down with maybe two easy loops I cut out of blocs wave and try this out, kinda hard with the kids running around haha…

I’ve read through the tutorial but I have a strong feeling I wasn’t using sync commands correctly. I’m gonna give it a roll again tomorrow morning and use your example as a template.

Hoping I can get over this hump cause it seems like such a powerful and flexible way to arrange audio, plus it’s sooo easy to get cool polyrhythm loops going where on iOS you end up needing to use something like Rozeta, which is fine, but being able to apply maths and logic on the fly makes for some really compelling tricks :slight_smile:


Hey @oceansequence - welcome to our community!

I hope Robin’s comments help. If not, don’t worry - I’m sure we can get you up and running with well-timed loops. Just share the code you’re running and that should help us figure out how to go forwards…


@robin.newman ok cool - had a bit of a play this morning, I think it’s sorted. I’ve got this really simple example up and running:

use_bpm 82

live_loop :s, delay: 0.001 do
  sample "/Users/matt/Desktop/pad.wav", beat_stretch: 32
  puts "pad lasts ",(sample_duration "/Users/matt/Desktop/pad.wav")
  sleep 32

live_loop :y, sync: :s do
  sample "/Users/matt/Desktop/beat.wav", beat_stretch: 8
  puts "beat lasts ",(sample_duration "/Users/matt/Desktop/beat.wav")
  sleep 8

I used puts just to see and got this in the log pane:

{run: 37, time: 228.2934, thread: :live_loop_y}
 ├─ sample "~/Desktop",
 │           "beat.wav", {beat_stretch: 8, rate: 1.0}
 └─ "beat lasts " 8.000020408163266
{run: 37, time: 234.1471, thread: :live_loop_s}
 ├─ sample "~/Desktop",
 │           "pad.wav", {beat_stretch: 32, rate: 1.0}
 └─ "pad lasts " 32.00001965230537

Let it fly for about ten minutes and everything seems in good timing :slight_smile: last time I tried this I had four different loops going, but I think I can chalk this up to me not understanding in_thread vs sync vs whatever other options there are lol…

Thanks for the help! I think I have a lot more reading to do and even more experimenting :stuck_out_tongue: and darn it, trying the triple quote code markup and I think my mac keeps flipping one of the quotes into a fancy looking one so that’s not working :expressionless: sorry…

Another thing to look into is use_sample_bpm

This allows you to set the bpm based on the sample you are using and the number of beats in the loop. This way, you don’t have to alter the actual sample with beat_stretch and affect the rate.

use_sample_bpm :loop_amen_full, num_beats: 16

live_loop :a do
  sample :loop_amen_full
  sleep 16

You can set the num_beats to whatever you prefer, but it is preferable to stay true to the sample so that if/when you start to add other sound to go with it, it is easier to understand what sleep values to use.

Also, once you start trying to coordinate other loops to go with it, you will likely have to start using beat_stretch for them, but this is a good method if you have a main loop that you want to work off of so you don’t have to alter the sound of the audio.

Hope this helps.

1 Like