Mixing several simultaneous sample loops

I have a project that is playing several samples of the same length in sync and I want to control the volume of each “track” via OSC. I have a small project working now (see abbreviated version below) that has OSC selecting between different sets of tracks, but the changes don’t take effect until the samples loop, whereas I’d like the mix to happen in real time.

Is there a way to control the volume of individual simultaneous sample loops asynchronously?

Edit: to clarify, my problem is that I can change the volume of any sample, but that only takes effect at the loop point. I want the volume to change when the OSC message is received

# test program, loops below replacing my own external loops, all the same length
live_loop :main do
  sample :loop_breakbeat, amp: get(:trackVol)
  sleep sample_duration :loop_breakbeat
end

set :trackVol, 1.0

live_loop :vol do
  use_real_time
  ##| note,duration,value = sync "/osc*/pers/purpose"
  value = 1 # normally value from OSC
  set :trackVol, get(:trackVol) - 0.1
  if get(:trackVol) < 0
    set :trackVol, 1.0
  end
  
  puts get(:trackVol)
  sleep 0.5
end

You can wrap the playing sample in an fx :level and control the amp: setting in the fx.

Code below illustrates.

There is one gotcha. Each time you run the program a new fx :level is gnereated. However the sample is always controlled by the initial one. The bodge solution is to comment out the second line so that the control always refers to the first fx :level set up.

Of course if you leave the program running and dont re-run without pressing stop first then this doesn’t matter.

with_fx :level,amp: 0 do |v1|
  set :v1,v1 #nb comment after first run if you want to rerun with same fx level:
  
  live_loop :s1 do #sample being played
    sample :loop_breakbeat
    sleep sample_duration :loop_breakbeat
  end
end

live_loop :trig do #wait for incoming osc and control level: amp:
  use_real_time
  val = sync "/osc*/s1vol"
  puts val[0]
  control get(:v1),amp: val[0]
end


#generate osc calls to send new required amp value
use_osc "localhost",4560
live_loop :ov do
  osc "/s1vol",rand(2)
  sleep [0.25,1,2].choose
end

I use this technique a lot usually in conjuction with TouchOSC to generate the osc calls.



EDIT you can add an amp_slide: value too if you want smooth volume changes

2 Likes

Hi Robin - Thanks for the response, it looks promising. But when I run your example, I get the below error. Do I need to set |v1| in a variable somewhere?

Runtime Error: [workspace_nine] - Thread death ±-> :live_loop_trig
Unable to normalise argument with key nil and value {amp: 0.169677734375} (RuntimeError)

@pvadbx - that is commented out on the first line inside the level fx block at the top of Robin’s example :slight_smile: - there, v1 is intended to be stored in the state system with set(…) to access later. It’s important to note the point Robin mentioned above about v1 continually being overwritten unless you use a workaround such as those he suggests, in order to successfully modify the volume of that initial level fx without losing it in a later run…

You need to have the set :v1,v1 uncommented on the first run. You only need to THEN comment it if you want to be able to rerun the program wiht other values changed WITHOUT first stopping it.

Each time you press stop everything resets and it is the fx :level created on the FIRST run that you want to control.

Ethan’s answer gives furthert explanation of this.

I have edited the program in my orginal psot as it should be for the first run.

Ah, okay. My bad on missing the “set” comment. That works! Thanks again.

Hi

and welcome! This may be a little naive for your needs, but it’s how I do real time fx control:

use_real_time

s = :loop_breakbeat
t = sample_duration s
with_fx :level, amp: 0 do |vol|
  
  live_loop :test do
    sample s
    10.times do
      tick
      control vol, amp: line(0,1.0, steps: 10).mirror.look
      # amp (or any other fx) control in here 
      sleep t/10.0
    end 
  end

end

PD-Pi

Hi Robin. Sorry, but how do I mark your post as resolving my question?

you just say here happy with solutions proposed. There isn’t a specific button as on github issues.

People often pick up threads like this at a later date, either discussing alternatives, or linking to a similar problem.