[Too many FX?] Notes get behind, crackling sound, pc grinds to a halt

I’m playing around with a simple track and when I let it run for a while (5-10 minutes while playing around with it) at some point notes start falling behind, the sounds starts to stutter and slow down and eventually it stops with errors about “running to far behind”.
At that point my machine is having trouble recovering as everything becomes very slow. Any application reacts sluggish, I’m getting the impression the OS is suffering somehow.

CPU usage remains low while the tune is running, memory consumption isn’t going up either. I don’t really see what’s going on.
I have to stop Sonic Pi and then wait until my machine recovers.

Maybe I made some beginners mistake. I know you have to watch out with fx and samples that run longer than they should, but I don’t see what’s wrong here. I’m posting the code below, perhaps someone can spot a problem with it.

I’m running on Win10, using ASIO4ALL (had to tweak that ruby file to get it running).
Any tips are welcome to debug the problem (any logs I don’t know about?).

# Acid jam
set :bpm, 120


live_loop :drum do
  with_fx :reverb, room: 0.9 do
    use_bpm get[:bpm]
    
    sample :bd_boom
    sample :bd_tek, amp: 0.9
    sleep 0.25
    sample :drum_cymbal_pedal, amp: 0.5
    sleep 0.25
    sample :drum_cymbal_closed
    sleep 0.25
    if rrand(1, 10) <= 8
      sample :drum_cymbal_pedal, amp: 0.5
    end
    
    sleep 0.25    
  end
end

use_synth :tb303

live_loop :acid do
  with_fx :reverb, room: 0.9 do
    
    use_bpm get[:bpm]
    
    4.times do
      play [:e2, :es2].choose, amp: 0.20, release: 0.1, cutoff: rrand(45, 120), attack: 0.03, pitch: 0.5
      sleep 0.25
    end
    
  end
end

Hi there, welcome to the Sonic Pi community! It’s lovely to have you here.

Looking at your problem, it’s very much likely that your CPU is chewing too hard over the real-time requirements of running many simultaneous reverb FX.

Short answer, try adding , reps: 32 before the do of each with_fx :reverb such as:

with_fx :reverb, room: 0.9, reps: 32 do
.
.

end

Longer answer - when you’re experiencing bad performance (with glitchy sounds) you might benefit from throttling the number of FX synths that are created.

Sonic Pi uses a synthesis engine called SuperCollider to generate its sounds. SuperCollider in turn has a notion of synth nodes which are bits of code which are running and represent a live sound or bit of sound processing. Sonic Pi manages the full life cycle of these synth nodes for you - constantly creating and terminating them as new sounds are triggered and manipulated. Each sound comes from a synth node and each FX is implemented using synth nodes. Normally you don’t have to think about them (unless you want to control them). However, occasionally you do and this appears to be such a situation. Let’s explore this in a little more detail…

Each time the code executes with_fx it creates a brand new FX synth just for that code block. It also ensures that the synth it created for the FX is terminated when it’s no longer needed.

This is kind of like a garbage collector for synths if that helps in any way as an analogy.

This therefore means that as a user of Sonic Pi you don’t have to worry about either creating the synths that power FX or correctly terminating them when they’re no longer needed. You just need to describe the scope of the code (within the do/end block). In my experience this has made working with FX way easier and more fluent given it reduces the amount of code you have to write and the things you have to think about.

However, one caveat of this approach is that it’s very possible to get into situations where you generate more FX synths than you can process in real time. This is likely to be what’s causing your issues right now.

Looking at the code, I know that the reverb synth with a room size of 0.9 is held open for around 10s to allow for the the long tail of the reverberation to fully fade out. You are also generating 4 reverbs a second in the :drum live loop alone. This means that after 10s you’ll have ~40 reverb synths running at the same time just to serve the audio requirements of the :drum live loop. This is likely to be clogging things up somewhat.

One approach to fix this is to move the with_fx block outside of the live loop. This will result in only 1 FX reverb synth being created. If you plan to never change the reverb FX (remove it, change its opts etc.) then this is probably the best option to take.

with_fx :reverb, room: 0.9 do
  live_loop :drums do
   .
   . 
   .
  end
end

However, if you do want to be able to change or remove the :reverb, at least as of v3.1 you can only change with_fx blocks that are within a live loop not outside it.

Unfortunately, the fact that the live loop spins round quickly, putting the FX within them can mean that they get triggered a lot. One way to reduce this effect is to use iteration within the live loop:

live_loop :drums do
  with_fx :reverb do
   16.times do
     .
     .
     sleep 0.25
   end
 end
end

This means that we only get a new reverb synth every 16 times rather than every time. The live loop spins round 16 times slower as a result (although the inner code is spinning round just as fast). So in this case, even though the code is being repeated 4 times a second, it does 16 repetitions before spinning round the live loop again and triggering a new reverb FX.

This pattern is so useful that it’s been integrated into all FX as an opt called reps:. The following code is equivalent to the example above:

live_loop :drums do
  with_fx :reverb, reps: 16 do
    .
    .
    sleep 0.25
  end
end

This approach will drastically reduce the number of FX synths are created and hopefully give your CPU a little bit more room to breath.

BTW, the crunch sounds you were hearing are called x-runs. They points in the soundwave where the CPU couldn’t feed the right numbers in time, so just got a default value.

Hope that this helps :slight_smile:

6 Likes

Thanks for the detailed reply! That seems to have fixed the problem.
The loops have been running for a while now and no glitches so far. :slight_smile:

I thought it had something to do with the FX inside the loop, but all the samples I found online did it that way too, so I thought it was the way to go.
I tried putting the with_fix statement outside of the loop before, but that didn’t seem to work. I simply didn’t hear the effect.

After playing with it for a bit longer now I found out that is because I didn’t stop and restart the buffer from scratch. I tend to hit ALT-R to trigger the changes I made, but that doesn’t seem to work for things outside the loop. Hitting Stop & then Play does.
I guess that’s also why the BMP change has to be inside the loop, instead of in the main body of the code outside the loops.

I’ll have to remember the reps option. I saw that in the tutorial but didn’t quite see the point at that time. :slight_smile:

2 Likes