OSC, dance, microplastics and... help?

Hi everyone !
I’m currently working on a dance project on the subject of microplastic pollution.

I will use Touch OSC on stage to control my sounds, and I borrowed some code here from chris.krakou for my “control” script. I also have some scripts and a soundbank of samples I created in Reaper. I already have one working with my Touch OSC but I noticed some latency in the behaviour of my loops.
I guess I have to dig a bit deeper in the forum, I have no training in code so the learning is a bit slow for me ^^ But if by chance you can see somtehing I can improve, here are my first two main scripts :

the “control” script:


#OSC script control for Blind Dreamers.
#the first attempt corresponds to the "Simple" Layout in touch osc
#based on this framework: https://in-thread.sonic-pi.net/t/a-basic-framework-for-using-touchosc-with-sonic-pi/2293
#

#multiples toggles
live_loop :toggle1 do
  
  use_real_time
  
  on = sync "/osc*/1/toggle1"
  set :tog1, on[0]
  
end


live_loop :toggle2 do
  
  use_real_time
  
  on = sync "/osc*/1/toggle2"
  set :tog2, on[0]
  
end


live_loop :toggle3 do
  
  use_real_time
  
  on = sync "/osc*/1/toggle3"
  set :tog3, on[0]
  
end


live_loop :toggle4 do
  
  use_real_time
  
  on = sync "/osc*/1/toggle4"
  set :tog4, on[0]
  
end


live_loop :toggle5 do
  
  use_real_time
  
  on = sync "/osc*/1/toggle5"
  set :tog5, on[0]
  
end

live_loop :toggle6 do
  
  use_real_time
  
  on = sync "/osc*/1/toggle6"
  set :tog6, on[0]
  
end

#multiple faders

live_loop :fader1 do
  use_real_time
  f = sync "/osc*/1/fader1"
  set :fader1, f[0]
  
  if (get(:fader1)<=0.5) then
    
    set :vol1, f[0]*2
    print :vol1, f[0]*2
  else
    
    set :vol1, f[0]+0.5
    print :vol1, f[0]+0.5
  end
end

live_loop :fader1 do
  use_real_time
  f = sync "/osc*/1/fader1"
  set :fader1, f[0]
  
  if (get(:fader1)<=0.5) then
    
    set :vol1, f[0]*2
    print :vol1, f[0]*2
  else
    
    set :vol1, f[0]+0.5
    print :vol1, f[0]+0.5
  end
end

live_loop :fader2 do
  use_real_time
  f = sync "/osc*/1/fader2"
  set :fader2, f[0]
  
  if (get(:fader2)<=0.5) then
    
    set :vol2, f[0]*2
    print :vol2, f[0]*2
  else
    
    set :vol2, f[0]+0.5
    print :vol2, f[0]+0.5
  end
end

live_loop :fader3 do
  use_real_time
  f = sync "/osc*/1/fader3"
  set :fader3, f[0]
  
  if (get(:fader3)<=0.5) then
    
    set :vol3, f[0]*2
    print :vol3, f[0]*2
  else
    
    set :vol3, f[0]+0.5
    print :vol3, f[0]+0.5
  end
end


live_loop :fader4 do
  use_real_time
  f = sync "/osc*/1/fader4"
  set :fader4, f[0]
  
  if (get(:fader4)<=0.5) then
    
    set :vol4, f[0]*2
    print :vol4, f[0]*2
  else
    
    set :vol4, f[0]+0.5
    print :vol4, f[0]+0.5
  end
end


live_loop :fader5 do
  use_real_time
  f = sync "/osc*/1/fader5"
  set :fader5, f[0]
  
  if (get(:fader5)<=0.5) then
    
    set :vol5, f[0]*2
    print :vol5, f[0]*2
  else
    
    set :vol5, f[0]+0.5
    print :vol5, f[0]+0.5
  end
end









and one of the “sound” scripts (don’t mind the silly phrases in french)

BD = "/Users/ltir/Documents/Projets en cours MAC/Blind Dreamers/BD SonicPi/Blind Dreamers Sonic Pi Samples/"

#sample pack, selection de choses enregistrees sur reaper

#note de sandra : le couple est un rubik's, le rubik's couple

#samples :
#00_respirations
#01_chantgorge
#02_grosseimpro (un jeu de nappe de plastique pichtées et avec reverb)
#03_respisJY
#04_rythme (rythme composé dans reaper 8 temps)
#05_submarine
#06_supericeberg
#07_testimpro
#08_bruit blanc
#09_rythme02 (plus long, 32 temps)
#10_reverse sac_01
#11_reverse sac_02
#12_reverse sac_03
#13_respi 01
#14_respi 02
#15_respi 03
#16_respi 04

with_fx :level, amp: 0, amp_slide: 1 do |v|
  
  live_loop :crackscontinuous do
    
    control v, amp: (get (:vol5))
    
    # stop
    #sample_duration BD, 6 = 18.56734693877551
    if (get(:tog6)>0) then
      
      
      
      
      with_fx :level, amp: 0, amp_slide: [9.25, 18.5].choose do |l|
        with_fx :pan, pan: 0, pan_slide: 18.5 do |p|
          
          sample BD, 6, amp: 1
          control p, pan: [-1, 1].choose
          control l, amp: 3
          sleep sample_duration BD, 6
        end
      end
    else
      
      sleep 1
      
      
    end
    
  end
end

with_fx :level, amp: 0, amp_slide: 1 do |v|
  
  live_loop :cracks do
    
    control v, amp: (get (:vol5))
    
    # stop
    #sample_duration BD, 6 = 18.56734693877551
    if (get(:tog5)>0) then
      
      
      
      if one_in (4)
        with_fx :level, amp: 0, amp_slide: [9.25, 18.5].choose do |l|
          with_fx :pan, pan: 0, pan_slide: 18.5 do |p|
            
            sample BD, 6, amp: 1
            control p, pan: [-1, 1].choose
            control l, amp: 3
            sleep sample_duration BD, 6
          end
        end
      else
        
        sample BD, 6, amp: 2, onset: pick, pan: rrand(-1, 1)
        
        sleep rrand(5, 7)
        
        
      end
      
    else
      
      sleep 1
    end
  end
  
end


with_fx :level, amp: 0, amp_slide: 1 do |v|
  
  with_fx :lpf, cutoff: 25, mix: 1 do
    
    live_loop :deepnoise do
      #stop
      control v, amp: (get (:vol4))
      
      if (get(:tog4)>0) then
        
        use_synth :bnoise
        
        play rrand(50, 70), amp: 2, attack: 2, decay: 11
        
        sleep 10
        
      else
        sleep 1
      end
      
      
    end
  end
end

with_fx :level, amp: 0, amp_slide: 1 do |v|
  with_fx :hpf, cutoff: 55, mix: 1 do
    
    live_loop :nicenote do
      control v, amp: (get (:vol3))
      
      
      if (get(:tog3)>0) then
        
        
        #stop
        
        use_synth :hollow
        
        play [:C5, :Cs5, :C4, :B4, :C3].choose, amp: rrand(0.1, 0.15), attack: 4, decay: 12, pan: rrand(-1, 1)
        sleep 8
        
      else
        
        
        sleep 1
        
      end
      
      
    end
    
  end
end



with_fx :level, amp: 0, amp_slide: 1 do |v|
  
  with_fx :reverb, room: 1, mix: 1 do
    with_fx :bpf, centre: 80, res: 0.9, mix: 0.5 do
      with_fx :hpf, cutoff: 55, mix: 1 do
        
        
        
        live_loop :highsine do
          control v, amp: (get (:vol2))
          
          if (get(:tog2)>0) then
            
            #stop
            use_synth :sine
            
            
            play 48, decay: 8, amp: rrand(0.15, 0.30), attack: 1, pan: -0.1
            play 48.5, decay: 9, amp: rrand(0.15, 0.30), attack: 3, pan: 0.1
            play 70, decay: 16, amp: rrand(0.01, 0.005), attack: 5, pan: -0.35
            play 71, decay: 17, amp: rrand(0.005, 0.010), attack: 7, pan: -0.25
            play 87, decay: 32, amp: rrand(0.005, 0.0055), attack: 9, pan: -1
            play rrand(100, 200), decay: 33, amp: rrand(0.0005, 0.001), attack: 1, pan: 0.75
            
            
            sleep 23
          else
            
            sleep 1
            
          end
        end
      end
    end
  end
end


with_fx :level, amp: 0, amp_slide: 1 do |v|
  
  with_fx :bpf, centre: rrand(40, 90), res: 0.9, mix: 1 do
    with_fx :reverb, room: 1, mix: 1 do
      with_fx :bitcrusher do
        
        live_loop :abstracttime do
          control v, amp: (get (:vol1))
          
          
          if (get(:tog1)>0) then
            
            #stop
            
            #valeur max de amp : vers 3 ou 4
            sample BD, 8, amp: rrand(1, 4), attack: 2, sustain: 10 ,release: 2, pan: rrand(-1, 1), rate: rrand(-1, 3)
            sleep rrand_i(1, 10)
            
            
            #valeur max de amp : 0.5 voir moins ! 0.25  ?
            #use_synth :bnoise
            sample BD, 8, amp: rrand(0, 3), attack: 2, sustain: 5, pan: rrand(-1, 1), rate: rrand(1, 4)
            sleep rrand_i(1, 10)
            
          else
            
            sleep 1
          end
        end
      end
      
    end
  end
end




The Touch OSC layout looks like that :

For example, I have a doubt about use_real_time : do I need to use it also in my “sound” scripts ?

for the french readers, here is some context on my patreon page :slight_smile: https://www.patreon.com/posts/de-lethique-du-123449464

1 Like

I had a play with your code and set up a TouchOSC panel and used some of my own samples. I’m not sure exactly what you are expecting to hear, as your patreon page requires a subscription.
SP runs a scheduled delay time which you can see with
puts default_sched_ahead_time
This allows strict timing to be kept at the expense of cdelaying sound output
With Mac and Windows it is 0.5 an this is the delay betweeen intialing a command and generating the sound. This can be reduced using
set sched_ahead_time! x
where x is the time you want, or eliminated using use_real_time
The disadvantage is that you may run into timing problems if your machines is not fast enough.
I think you need it in the sound generating live_loops as well as the loops detecting OSC calls for quickest response time.

You could just put a single use_real_time at the start odf the program

If youir samples are very large, you may wish to add a
load_samples BD
to load them at the start of the program and avoid any first use delay for each sample.

1 Like

Thank you ! I’ll look into it. For the moment everything seems ok, I’m thinking about creating some visual feedback on my Touch OSC interface. We are still working on creating some sound material, the show is due in the Fall of 2026, so we still have time :slight_smile:

little update: I got an “too far behind time” error on one of my scripts. Here is the log:

Runtime Error: [buffer 4, line 51] - SonicPi::Lang::Core::TimingError

Thread death +--> :live_loop_plasticafx

Timing Exception: thread got too far behind time

/Applications/Sonic Pi.app/Contents/Resources/app/server/ruby/lib/sonicpi/lang/core.rb:4164:in `sleep'

workspace_four:51:in `block (5 levels) in __spider_eval'

/Applications/Sonic Pi.app/Contents/Resources/app/server/ruby/lib/sonicpi/lang/sound.rb:1836:in `block in with_fx'

/Applications/Sonic Pi.app/Contents/Resources/app/server/ruby/lib/sonicpi/lang/sound.rb:1835:in `times'

/Applications/Sonic Pi.app/Contents/Resources/app/server/ruby/lib/sonicpi/lang/sound.rb:1835:in `with_fx'

workspace_four:12:in `block (4 levels) in __spider_eval'

/Applications/Sonic Pi.app/Contents/Resources/app/server/ruby/lib/sonicpi/lang/core.rb:2196:in `block in live_loop'

/Applications/Sonic Pi.app/Contents/Resources/app/server/ruby/lib/sonicpi/lang/core.rb:2216:in `block (2 levels) in live_loop'

/Applications/Sonic Pi.app/Contents/Resources/app/server/ruby/lib/sonicpi/lang/core.rb:2136:in `block (2 levels) in loop'

/Applications/Sonic Pi.app/Contents/Resources/app/server/ruby/lib/sonicpi/lang/core.rb:2357:in `block_duration'

/Applications/Sonic Pi.app/Contents/Resources/app/server/ruby/lib/sonicpi/lang/core.rb:2394:in `block_slept?'

/Applications/Sonic Pi.app/Contents/Resources/app/server/ruby/lib/sonicpi/lang/core.rb:2135:in `block in loop'

/Applications/Sonic Pi.app/Contents/Resources/app/server/ruby/lib/sonicpi/lang/core.rb:2133:in `loop'

/Applications/Sonic Pi.app/Contents/Resources/app/server/ruby/lib/sonicpi/lang/core.rb:2133:in `loop'

/Applications/Sonic Pi.app/Contents/Resources/app/server/ruby/lib/sonicpi/lang/core.rb:2214:in `block in live_loop'

/Applications/Sonic Pi.app/Contents/Resources/app/server/ruby/lib/sonicpi/runtime.rb:1093:in `block (2 levels) in __in_thread'

and here is the script


BD = "/Users/ltir/Documents/Projets en cours MAC/Blind Dreamers/BD SonicPi/Blind Dreamers Sonic Pi Samples/"
load_sample BD


with_fx :level, amp: 0, amp_slide: 1 do |v|
  
  print sample_duration BD, 9
  
  live_loop :plasticafx do
    use_real_time
    with_fx :bpf, centre: rrand(50, 100), mix: 0.9 do
      control v, amp: (get (:vol13))
      
      if (get(:tog20)>0) then
        
        use_bpm 9.375
        
      else
        
        if (get(:tog21)>0) then
          
          
          
          use_bpm 75
          
        else
          
          use_bpm 300
        end
      end
      
      if (get(:tog18)>0) then
        
        
        with_fx :distortion do
          if one_in (8)
            with_fx :distortion, amp: 0.5 do
              sample BD, 9, onset: pick, release: [2,4,8,16,32].choose, beat_stretch: [0.5, 1, 2, 4, 8].choose, pan: [-1, -0.5, 0, 0.5, 1].choose
              sleep [1, 2].choose
            end
          else
            sample BD, 9, onset: pick
            sleep 1
          end
          
        end
        
      else
        
        sleep 1
      end
    end
    
  end
end


The interesting thing for me is that sometimes I have no problems, and sometimes it seems to just happen ^^ I was running other programs at the same time, maybe this could be a cause ? I am reading other topics on the forum since I saw that error seems to be a common one. I am testing this code live on tuesday, at the end of our residency, I’ll let you know how it’s going until then :slight_smile:

I just noticed that the incriminated line is a “sleep 1”, I’ll put a bigger number, maybe it’ll do the trick

Having the with_fx inside the loop recreates the fx each time around, and you have two (sometimes 3) fx being created each time. At 300 bpm that’s more than 10 fx being created (and destroyed) per second, which is quite a lot.
If possible, try putting some more of the with_fx blocks outside the live_loop, and I think that should help. You’ll need to keep a reference to the :bpf fx to control the centre: parameter (similar to what you do already for the :level fx).

1 Like

Thank you ! I’ll review my other scripts to see if I did that mistake in them too ! Thanks a lot !

I’m not sure I’d call it a mistake, as having the fx inside the loop is simpler when any of the parameters will change over time, so there’s a trade-off between simplicity and efficiency.

There are also things to watch out for with the fx outside the loop (especially if you will be live coding), due to the way they interact that cause changes to the fx parameters to not have an effect of the sound generated.
If you run into that, there are some workarounds if you search for them in these forums.

1 Like

So, three days ago we did a show for the end of our artistic residency and it went well ! No bugs, I was on stage with the performers and controlling my scripts with touch OSC, while performing a bit myself. A lot of stress but a lot of joy also, I really want to thank everyone here since you continuously help making this wonderful tool grow and evolve. I feel a bit emotional since it was the first time I went onstage since 2020, and I’m really happy to have done it using Sonic Pi ! I’ll keep you updated on the progress of this project, there are still things that I want to do and things that are waiting improvement in my code !

1 Like

here are some infos about the night of the show (in french) Sandra Geco - Blind Dreamers | L'étoile du nord

1 Like

Don’t suppose you have the audio / video available to share with us?
Congratulations on a successful performance!

1 Like

I should have a “teaser” recorded on that night soon, but I think we will do the soundtrack in post, there was no proper recording of the end of the residency (only “work” recording with bad audio, that I actually cannot share publicly, orders of my choreographer!) but the more we will approach from the actual release of the show (2026) the more I will be able to share things !

And I’ll share the version of the code from that night very soon.