Hi @robin.newman,
yeah, sure. While editing most of the example got lost… I edited again and now it should be complete.
Hi @robin.newman,
yeah, sure. While editing most of the example got lost… I edited again and now it should be complete.
I am definitely interested in this topic. I have been playing around with sending OSC messages from p5.js to adjust parameters and was considering this very question. I’ve also run into it when playing around with micro-controllers to adjust parameters. I was curious if I could adjust the rate of a looped sample without having to wait until the loop had completed before changing, but making any real time adjustments without waiting out the completion of the loop is of interest to me. However, I’m looking at your code and trying to make some sense of how it is all working, so I don’t really feel like I have much to contribute at this point. but I am following this thread closely!
Here’s a video of what I have been playing with:
Hi @mrbombmusic
that’s actually not too complicated: I started with looking for a way to make fading of any paramater in a smooth way. Which means: make the fade somehow independant of the length of the live_loop
it runs in. If I have a loop that runs for 8 beats I don’t want to wait that long until the parameter change and goes to the next value. So it was clear that I had to use control
. My initial attempt goes like this (in the code above this is example 2):
ring
of values and step through it using tick
tick
again to advance within the ring
ring
like (ring 0, 0.25, 0.5, 0.75, 1)
the initial value for (let’s say) amp
should be 0
; then the control
takes over and sets the amp
to 0.25
. And here comes, what’s wrong about this: Now, in the next loop run, the 3rd tick
does not start at 0.25
but with 0.5
and goes to 0.75
with the next control
.ring
for that (and I got input from Sam about that). The ring
must look like: (ring 0, 0.25, 0.25, 0.5, 0.5, 0.75, 0.75, 1)
. That is, what the expression: (line 60, 100, steps: 10, inclusive: true).stretch(2).drop(1).butlast
in example 1 is about. See the discussion in Manipualation of Rings to get further details about that.tick
(to stay with the example of the amp
value) takes the 0
, the following control
will take 0.25
with the 2nd tick
, and then the 3rd tick
will also have the 0.25
because the ring
provides every value except the first one (.drop(1)
) and the last one (.butlast
) twice. (The .reflect
in example 1 just let’s the filter value go back, when it has reached the maximum value.)Hope this makes the background of my solution search more understandable
Now I would like 1. to have a function that provides me with a simple way to construct rings
of that kind (which I sketched out here) and furthermore a way to be able to fade in/out a live_loop
at any time, which is a bit more complicated because you will have to deal with the tick
value to set it back. One way is to a) set the filter, amp or whatever value to the last reached position and then b) reset
tick
. After that you can use you ring
again with the desired values (e. g. first you fade in the loop and then, after a while, you want to fade it out). So I know how, but I was wondering if it is somehow possible to cover that with one conveniant function …
Martin
Hi Martin
Had a play this afternoon and came up with this:
define :fade do |min, max, len, type|
case type
when :up
b = (line min, max, steps: len, inclusive: true).stretch(2).drop(1).butlast.ramp
when :down
b = (line min, max, steps: len, inclusive: true).stretch(2).drop(1).butlast.reverse.ramp
when :wave
b = (line min, max, steps: len, inclusive: true).stretch(2).drop(1).butlast.mirror
end
end
define :fadeControl do |min,max,duration,type|
l=fade min,max,11,type #11 ensures 20 steps for each up/down, 40 for wave
if type==:wave
dt=duration/40.0 #adjust step interval to give correct total time
else
dt=duration/20.0
end
#puts l.length,type #uncomment for debugging to check number of steps
puts "fadeControl #{min} #{max} #{duration} #{type}"
in_thread do
t=vt
tick_reset
l.length.times do
control get(:lv),amp: l.tick,amp_slide: dt
sleep dt
end
end
end
live_loop :test do
with_fx :level,amp: 0 do |lv|
set :lv,lv
sample :loop_amen_full,beat_stretch: 8
fadeControl(0,1,2,:up) #fade up from 0 to 1 in 2 seconds
sleep 2
fadeControl(0.3,1,1,:down) #fade down from 1 to 0.3 in 1 second
sleep 2
fadeControl(0.3,1,1,:wave) #fade up and down from 0.3->1-> 0 in 1 second
sleep 2
fadeControl(0,0.3,1,:down) #fade down from 0.3 to 0 in 1 second
sleep 2
sample :loop_amen_full,beat_stretch: 8 #start another sample to give 16 second loop
fadeControl(0,0.4,8,:wave) #fade up and down from 0->0.4->0 in 8 seconds
sleep 8
end
end
I put the fx within the live_loop :test so that you could alter and rerun the loop without stopping.
Basically I put the control section in a function inside a thread. I saved the control pointer lv using set :lv and retrieved it where necessary.
The function has 4 parameters:
the min and max amplitude values, to type of fade and the overall time. I set the len parameter internally passed on to your fade function as 11 which gives 20 steps for :up and :down and 40 for :wave. I adjust the step time dt appropriately to give the correct total time required for the fade. If you only want a 10 step face the vaule to use is 6 in which case adjust the divides for the dt calculation to 20.0 and 10.0 rather than 40.0 and 20.0
By interspersing calls to fadeControl (which doesn’t consume time in the live loop, as it runs in a thread) with suitable sleep commands you can place fades where you want them in the live_loop with whatever duration time you wish making sure they don’t overlap subsequent fades. for subsequent fades set the range so that they follow on seamlessly from the previous one. This doesn;t ahve to be so, but you will get a sharp transition if not.
If you want to start the live_loop at full volume adjust the initial amp setting in the fx_level line to amp: 1
Finally if you are not going to reurn the program once started you can then put the with_fx :level outside the loop, plus the line set :lv,lv
EDIT ADDENDUM you can put these two lines outside the live loop, and then comment them out (plus the associated end
after the live_loop) once the program is running. You can then re-run and the fx call will still be in force until you actually stop the whole program. Then of course you will have to uncomment the lines so that a new fx is set up again when you next start the program once more.
I improved the parameter list for fadeControl to use start and finish, which makes it more logical when entering the data. So you get
fadeControl(1,0.5,2,:down)
for example, which fades down from 1 to 0.5 in 2 seconds
and fadeControl(0.3,0.8,4,:up)
which fades up from 0.3 to 0.8 in 4 seconds
The adjusted programs are below
define :fade do |min, max, len, type|
case type
when :up
b = (line min, max, steps: len, inclusive: true).stretch(2).drop(1).butlast.ramp
when :down
b = (line min, max, steps: len, inclusive: true).stretch(2).drop(1).butlast.reverse.ramp
when :wave
b = (line min, max, steps: len, inclusive: true).stretch(2).drop(1).butlast.mirror
end
end
define :fadeControl do |start,finish,duration,type|
if type==:down
min=finish
max=start
else
min=start
max=finish
end
l=fade min,max,11,type #11 ensures 20 steps for each up/down, 40 for wave
if type==:wave
dt=duration/40.0 #adjust step interval to give correct total time
else
dt=duration/20.0
end
#puts l.length,type #uncomment for debugging to check number of steps
puts "fadeControl #{start} #{finish} #{duration} #{type}"
in_thread do
t=vt
tick_reset
l.length.times do
control get(:lv),amp: l.tick,amp_slide: dt
sleep dt
end
end
end
with_fx :level,amp: 0 do |lv|
set :lv,lv
live_loop :test do
sample :loop_amen_full,beat_stretch: 8
fadeControl(0,1,2,:up) #fade up from 0 to 1 in 2 seconds
sleep 2
fadeControl(1,0.3,1,:down) #fade down from 1 to 0.3 in 1 second
sleep 2
fadeControl(0.3,1,1,:wave) #fade up and down from 0.3->1-> 0in 1 second
sleep 2
fadeControl(0.3,0,1,:down) #fade down from 0.3 to 0 in 1 second
sleep 2 #there will be 1 second of silence here
sample :loop_amen_full,beat_stretch: 8 #start another sample to give 16 second loop
fadeControl(0,0.4,8,:wave) #fade up and down from 0->0.4->0 in 8 seconds
sleep 8
end
end
ADDENDUM EDIT
Finally (!!)
here is a much more compact way of writing the live loop above, making use of the at command. It is shown below with the fx wrapper.
The remainder of the program in my previous post remains the same.
Here is a much more compact version of the live loop using the at command. This replaces the live_loop :test and fx wrapper above. The remainder remains as it is.
with_fx :level,amp: 0 do |lv|
set :lv,lv
live_loop :test do
at [0,2,4,6,8],[[0,1,2,:up],[1,0.3,1,:down],[0.3,1,1,:wave],[0.3,0,1,:down],[0,0.4,8,:wave]]do |param|
fadeControl param[0],param[1],param[2],param[3]
end
sample :loop_amen_full,beat_stretch: 8
sleep 8
sample :loop_amen_full,beat_stretch: 8
sleep 8
end
end
Hi Robin,
this looks very promissing. I might have the opportunity at the weekend to have a closer look. Please give me some time for a feedback which I am eager to give. For now, thanks a lot that you took the time to look into it!
Martin
Fine Martin
I’m having fun playing with this. I’ve now changed things slightly adding the pointer :lv as a parameter inside the fadeControl function. This means that it is possible to have instances fo the function running in differnt live loops at the same time, each with their own fx :level wrapper.
So fade control is now
define :fadeControl do |start,finish,duration,type,pointer|
if type==:down
min=finish
max=start
else
min=start
max=finish
end
l=fade min,max,11,type #11 ensures 20 steps for each up/down, 40 for wave
if type==:wave
dt=duration/40.0 #adjust step interval to give correct total time
else
dt=duration/20.0
end
#puts l.length,type #uncomment for debugging to check number of steps
puts "fadeControl #{start} #{finish} #{duration} #{type} #{pointer}"
in_thread do
t=vt
tick_reset
l.length.times do
control get(pointer),amp: l.tick,amp_slide: dt
sleep dt
end
end
end
and a typical call would be
fadeControl(0.3,0.8,2,:up,:lv2)
which would control the fx :level reference by :lv2 fading it up from 0.3 to 0.8 over 2 seconds
So if you had three controlled loops one would be on :lv1 a second on :lv2 and the third on:lv3
Hi Robin, this sounds exactly like what I need. Will get back to you …
Great. I’ve Made further progress. Can now choose which type of parameter to alter so can deal for example with with_fx :pan
or with_fx ;reverb
. I now have an example with three nested fx for pan, level and reverb and all are controlled independently within the loop at different times.
EDIT ADD
I’m also working on a touchOSC input controller for this. Initial experiments working OK, and so far I’m controlling the amp of a live_loop with fades up and down and “wave” remotely. Now working to allow several controllers together.
I now have a complete working system with two large scale examples.
which I will post separately. One controls five with_fx wrappers associated with two live loops, with sliding sequences built into the code. Each slide starts from the end point of a previous one.
The second uses four wrappers, but this time they are controlled by aTouchOSC screen.
The two fundamental routines fadeSteps
(essentially your fade
function) and fadeControl
are shown below, in a simple example program.
Hi Robin, you are very industrious (hope that’s the correct word, never heard or used it but the dictionary told me …). I still need some time to process what you’ve written but will definitely get back to you. I have some questions and some remarks …
The TouchOSC version has a video
code is available at
Hi @robin.newman,
I finally had some time to look at your code in more detail. This is nice work! Thanks for looking into that and thanks for the inspiration!
There are however two obstacles which I find using this in the context of live coding:
fx
only very rarely outside of a live_loop
because I almost always want it to be adjustable by reevaluation.live_loop
starts again (which is obvious, because the sample’s amp
is not set to the finish value and tick_reset
is called every new run). Am I right? See this example:define :fade do |min, max, len, type|
case type
when :up
b = (line min, max, steps: len, inclusive: true).stretch(2).drop(1).butlast.ramp
when :down
b = (line min, max, steps: len, inclusive: true).stretch(2).drop(1).butlast.reverse.ramp
when :wave
b = (line min, max, steps: len, inclusive: true).stretch(2).drop(1).butlast.mirror
end
end
define :fadeControl do |start,finish,duration,type|
if type==:down
min=finish
max=start
else
min=start
max=finish
end
l=fade min,max,11,type #11 ensures 20 steps for each up/down, 40 for wave
if type==:wave
dt=duration/40.0 #adjust step interval to give correct total time
else
dt=duration/20.0
end
#puts l.length,type #uncomment for debugging to check number of steps
puts "fadeControl #{start} #{finish} #{duration} #{type}"
in_thread do
t=vt
tick_reset
l.length.times do
control get(:lv),amp: l.tick,amp_slide: dt
sleep dt
end
end
end
with_fx :level,amp: 0 do |lv|
set :lv,lv
live_loop :test do
sample :loop_amen_full,beat_stretch: 8
fadeControl(0,1,2,:up) #fade up from 0 to 1 in 2 seconds
sleep 8
sample :elec_beep
sleep 2
end
end
Now I messed with your code, firstly to understand how it is working and secondly to try to build on it and find a solution to the outlined issues. I did not succeed yet (due to lack of knowledge and experience) but I have at least a better grasp of what’s going on as well as some ideas.
Here are some notes on my ideas (please read this with respect to the following code sketch):
:wave
is not necessary or better: this should better be coded as a separate function (actually it could be serving as a sort of LFO).amp
value within the live_loop
and tried something like: amp: (set :vol, 1)
but this does not work.faceControl
else set the [current volume of s] to finish (which then would be the starting point for the next fade):s
's amp
because if it has reached the desired fading finish value fadeControl
does not have to get called. Only if the current :amp
differs from the finish value …It might well be that I am thinking to complicated or into a very wrong direction. It’ll be nice if you had a look at that. I am definitely stuck.
Here is my unfinished framing which although not working might show what I am aming at:
define :fadeControl do | start, finish, duration, pointer |
l = (line start, finish, steps: 11, inclusive: true).stretch(2).drop(1).butlast.ramp
dt = duration / 20.0
in_thread do
tick_reset
l.length.times do
control get(pointer), amp: l.tick, amp_slide: dt
sleep dt
end
end
end
set :vol, 0
live_loop :test2 do
set :s, (sample :loop_amen, beat_stretch: 4, amp: get(:vol))
fadeControl(get(:vol), 1, 2, :s) # if current_vol of :s != finish go on
sleep 4
end
Hi Martin
I am very busy this weekend so haven’t time to look at your answer in detail till Monday. However you can use it with the fx inside the loop. I have done do with three nested loops. Also you can do the control using at function or manually so it can happen independently of the loop and shouldn’t keep resetting.
Will give fuller answer later.
Hi Martin
I have had another look at things and I think the code below does what you want. The fadeControl function can be used to control other opts, but here is set to control the volume (:amp) of the sample in loop :test
The fade is controlled by the function setVol which needs two parameters: the final amp level (normal range 0->1 (although you can go higher) It always starts from the current volume level (held in :vol) so the fade is smooth without any jumps. It runs independently of the live_loop :test duration. (8 beats) and can for example last for 16 beats, or 2 beats. The initial volume the first time the program runs is set to 0 in the defonce :setup code, although this could be set to 1 if you wish. Note for such a change to take place, you would have to quit and restart SP or use the override option for defonce.
To test, just run the program. (The initial fade setup 0->0.5 will happen. When it has finished, just set a new fade eg setVol 1,6 and press run again, and so on.
#function returns step values for the fade
define :fade do |start, finish, len|
b = (line start, finish, steps: len, inclusive: true).stretch(2).drop(1).butlast.ramp
return b #cam be omitted but makes explicit what happens
end
define :fadeControl do |start,finish,duration,pointer,opt|
return if start==finish
l=fade start,finish,11 #11 ensures 20 steps for each up/down
dt=duration/20.0
puts "fadeControl #{start} #{finish} #{duration} #{pointer} #{opt}"
in_thread do
t=vt
tick_reset
l.length.times do
#note that amp: is equivalent to :amp=> This enables use of :amp stored in a variable
# similarly for pan: or phase: or any other similar
# note also how the corresponding _slide is created.
control get(pointer),opt=> l.tick,(opt.to_s+"_slide").to_sym => dt
sleep dt
set :vol,l.look #update current volume at end of each step
end
end
end
#set inital volume value on first run
defonce :setup do
set :vol,0
end
live_loop :test do
lv=sample :loop_amen_full,beat_stretch: 8,amp: get(:vol) #sample volume will be controlled
set :lv,lv
sleep 8
end
#defines setvol so that only final vol and duration need to be specified for each fade
define :setVol do |value,duration|
fadeControl get(:vol),value,duration,:lv,:amp
end
setVol 0.5,4 #This sets and carries out the fade
EDIT I also have a version that will control volume and pan independently at the same time if you want that.
Hi Robin, again thanks, did a quick check. Something seems to be wrong (might be my installation). I will need some time to check again an give some usefull feedback but will definitely look into this because it is on my live coding todo list.
Martin
Hi @robin.newman,
yes! It does work. I am very greatful for this intelligent piece of code! As soon as possible I will try out some of my uses cases. This will most probably go into my startup library to be available as a standard extension.
To explain my earlier posting: Currently I have 3 SP versions, which I can start (3.1 and two 3.2x dev versions). There are some irregularities, which I am not surprised of that being developement versions. F. e. the midi fader control was not running stable (besides my stupid coding error). I also had some not expected issues with control
when I first started building a fader function. In effect the same code ran one day and failed a few months later.
But this can be due to a lot of reasons. I did not have the time to investigate in a systematic way… Actually it was kind of the same with your example. So I still don’t know if it is just me, the SP version I am using or some reason rooted in my system environement or current state. Anyhow… time will show.
Martin
Glad it’s working for you. I’ve enjoyed playing with this. I’ve tried it with more complex setups using both fx calls (inside a live loop) and other parameters such as pan and echo. Can get some quite nice setups.
I just started using SP and was looking for a way to fade in a bass line. This seems to do the trick! I’ll def be playing around with it. Thanks