Go Back   Cockos Incorporated Forums > REAPER Forums > ReaScript, JSFX, REAPER Plug-in Extensions, Developer Forum

Reply
 
Thread Tools Display Modes
Old 04-23-2017, 12:20 PM   #1
SonicAxiom
Human being with feelings
 
SonicAxiom's Avatar
 
Join Date: Dec 2012
Location: Germany
Posts: 1,921
Default JSFX that can split audio signal to diff. out_pins/busses depending on level

I'm looking for a plugin that is capable of splitting audio to three different pairs of its out_pins (three stereo busses) depending on the momentary level of the incoming audio. For example, for audio levels between -inf and -32 dB, the plugin would send the audio to its out_pins 1+2. While the incoming audio level is between -32 and -14 dB the plugin should send the audio to its out_pins 3+4 and for audio with a level above -14 dB it should route it to its out_pins 5+6.

Hope somebody of you folks has an idea how to code this plugin. Any help is very much appreciated.
SonicAxiom is offline   Reply With Quote
Old 04-23-2017, 01:08 PM   #2
bezusheist
Human being with feelings
 
bezusheist's Avatar
 
Join Date: Nov 2010
Location: USA
Posts: 701
Default

what scale do you want for the dB values ? Peak, RMS, LUFS, etc...
__________________
trahpyzzup
bezusheist is offline   Reply With Quote
Old 04-23-2017, 01:38 PM   #3
SonicAxiom
Human being with feelings
 
SonicAxiom's Avatar
 
Join Date: Dec 2012
Location: Germany
Posts: 1,921
Default

Quote:
Originally Posted by bezusheist View Post
what scale do you want for the dB values ? Peak, RMS, LUFS, etc...
thanks for your reply, bezusheist.

well, I haven't even thought about this really . Let's try peak, might be the "easiest". But feel free to implement another scale. I don't know what impact the scale has on the audio being split.

Only requirement I can think of at the moment is that switching the audio to another pair of out_pins should not introduce audible clicks, thus should preferably be more like a very quick cross-fade from one "bus" to the other (20-40 ms? maybe user configurable?).

Last edited by SonicAxiom; 04-23-2017 at 03:44 PM.
SonicAxiom is offline   Reply With Quote
Old 04-23-2017, 03:25 PM   #4
matthewbarnhart
Human being with feelings
 
Join Date: Sep 2014
Posts: 50
Default

I haven't tested this and it's just off the top of my head, but how about three instances of ReaGate, using parameter modulation on each to open the gate based on level.
matthewbarnhart is offline   Reply With Quote
Old 04-23-2017, 03:52 PM   #5
TryingToMakeMusic
Banned
 
Join Date: Nov 2016
Posts: 416
Default

Quote:
Originally Posted by SonicAxiom View Post
Only requirement I can think of at the moment is that switching the audio to another pair of out_pins should not introduce audible clicks, thus should preferably be more like a very quick cross-fade from one "bus" to the other (20-40 ms? maybe user configurable?).
A 500 Hz tone would change to a different dynamic-zone 4 times per millisecond. If your crossfades are 20-40 ms, it's just going to output everything to all pins constantly. Design needs refinement.
TryingToMakeMusic is offline   Reply With Quote
Old 04-23-2017, 03:53 PM   #6
Robert Randolph
Human being with feelings
 
Robert Randolph's Avatar
 
Join Date: Apr 2017
Location: St. Petersburg, FL
Posts: 842
Default

Quote:
Originally Posted by TryingToMakeMusic View Post
A 500 Hz tone would change to a different dynamic-zone 4 times per millisecond. If your crossfades are 20-40 ms, it's just going to output everything to all pins constantly. Design needs refinement.
Eh, you can just use an RMS window of the appropriate size.

If the OP wants, this exists as a plug-in already http://x42-plugins.com/x42/x42-mixtrix
__________________
http://www.admiralbumblebee.com
Audio/DAW stuff, Woodworking, Programming and more...
Robert Randolph is online now   Reply With Quote
Old 04-23-2017, 03:56 PM   #7
TryingToMakeMusic
Banned
 
Join Date: Nov 2016
Posts: 416
Default

Quote:
Originally Posted by Robert Randolph View Post
Eh, you can just use an RMS window of the appropriate size.
What is the appropriate size?
TryingToMakeMusic is offline   Reply With Quote
Old 04-23-2017, 04:06 PM   #8
Robert Randolph
Human being with feelings
 
Robert Randolph's Avatar
 
Join Date: Apr 2017
Location: St. Petersburg, FL
Posts: 842
Default

Quote:
Originally Posted by TryingToMakeMusic View Post
What is the appropriate size?
That'd be entirely dependent on the source and the routing required.

I was indicating that the windowing can be a parameter, both in type and size, so that the necessary results can be obtained.
__________________
http://www.admiralbumblebee.com
Audio/DAW stuff, Woodworking, Programming and more...
Robert Randolph is online now   Reply With Quote
Old 04-23-2017, 04:13 PM   #9
TryingToMakeMusic
Banned
 
Join Date: Nov 2016
Posts: 416
Default

Quote:
Originally Posted by Robert Randolph View Post
That'd be entirely dependent on the source and the routing required.

I was indicating that the windowing can be a parameter, both in type and size, so that the necessary results can be obtained.
Okay. I see how this could work for routing long-term changes to different outputs. I didn't realize that's what OP wants, but maybe it is.
TryingToMakeMusic is offline   Reply With Quote
Old 04-23-2017, 05:26 PM   #10
SonicAxiom
Human being with feelings
 
SonicAxiom's Avatar
 
Join Date: Dec 2012
Location: Germany
Posts: 1,921
Default

thanks for your input, guys!

@Robert: I admit that I don't get what the plugin you suggest can actually do and if that's what I'm looking for
It seems to be a standalone application. I'd prefer a JSFX of VST plugin for easy integration into Reaper's track fx chain making use of the pin routing feature.

To clarify: The plugin should route a stereo audio signal to three busses (but to only one bus at a time!) depending on the current level of the source audio. While levels are low, audio is output via bus 1, medium levels are output via bus 2 and high levels are output via bus 3. Switching should be smoothed out (regarding time) to avoid too quick and chaotic "jumps" from one bus to the other. I think my proposal of 20-40 ms was way too quick. 1-2 sec. may be much more appropriate. Maybe having an option to tweak this smoothing parameter from the gui would be a good idea, too.
SonicAxiom is offline   Reply With Quote
Old 04-24-2017, 07:05 AM   #11
jcjr
Human being with feelings
 
Join Date: Dec 2015
Location: SE TN USA
Posts: 77
Default

Depending on what you want-- If it needs to be fairly easy to code and smooth sounding, perhaps split up the time smoothing vs the level smoothing between the outputs.

For instance the time sensitivity could be adjusted with an envelope follower. If you want "lots of tweakability" you could use an envelope follower as used in compressors, with independently adjustable attack and release knobs.

Or you could just use a lowpass filter for rms or averaging envelope smoothing with the attack "about the same" as the release, controlled by one knob. Set the knob to low values and both attack and release would be fast, or use big knob values for both attack and release to be slower. Some such filters, the "time constant" of both attack and release is the same, but the actual audible response isn't always exactly the same. For instance, a simple first order lowpass smoother would reach 67% of target attack value in 1 time constant, but would fall to 37% of release value in one time constant. So the time constants are the same but the attack vs release behavior isn't exactly the same. But it might work better if they are not exactly the same. Just try and see.

After taking care of time smoothing with an envelope follower, one could try to minimize switch clicking on the outputs by doing crossfades between the outputs based on the time-smoothed envelope.

For instance, rather than "If envelope < -30 dB send to out 1 otherwise send to out 2" (Which would click rather bad)--

Try something like "If envelope < -36 dB send to out 1. If envelope > -24 dB send to out 2. Otherwise pan between out 1 and out 2 according to the envelope level."

Maybe you would decide to linear fade so that at -30 dB (the center point), that both out 1 and out 2 are equally loud, and both out 1 and out 2 added together == -30 dB. Or other schemes. Essentially use the envelope level to enforce "pan rules" between the outputs. There are various pan rules you could choose.

The "envelope modulated panning" would sometimes have audio shared between more than one output, but could more easily be made smoother and less clicky sounding.

All the parms you like could be made adjustable-- Envelope times, switching threshold levels, panning width in dB, even gain adjustment of the different outputs, in case maybe you want to boost the quiet outputs and cut the loud outputs or whatever.

If you'd want more sudden switching at greater risk of artifacts, maybe pan over a band of 1 dB, for instance doing the entire pan from full out 1 at -30.5 dB, equal mix at -30 dB, and full out 2 at -29.5 dB. Or maybe pan over a wider amplitude window for less risk of artifacts and clicks.

Similarly, longer envelope times would have less risk of glitches, better chance of smooth sound. Faster envelopes responding faster with higher risk of glitches. Let the user pick how much glitches vs response speed he wants.
jcjr is offline   Reply With Quote
Old 04-24-2017, 07:24 AM   #12
SonicAxiom
Human being with feelings
 
SonicAxiom's Avatar
 
Join Date: Dec 2012
Location: Germany
Posts: 1,921
Default

Quote:
Originally Posted by jcjr View Post
Depending on what you want-- If it needs to be fairly easy to code and smooth sounding, perhaps split up the time smoothing vs the level smoothing between the outputs.

For instance the time sensitivity could be adjusted with an envelope follower. If you want "lots of tweakability" you could use an envelope follower as used in compressors, with independently adjustable attack and release knobs.

Or you could just use a lowpass filter for rms or averaging envelope smoothing with the attack "about the same" as the release, controlled by one knob. Set the knob to low values and both attack and release would be fast, or use big knob values for both attack and release to be slower. Some such filters, the "time constant" of both attack and release is the same, but the actual audible response isn't always exactly the same. For instance, a simple first order lowpass smoother would reach 67% of target attack value in 1 time constant, but would fall to 37% of release value in one time constant. So the time constants are the same but the attack vs release behavior isn't exactly the same. But it might work better if they are not exactly the same. Just try and see.

After taking care of time smoothing with an envelope follower, one could try to minimize switch clicking on the outputs by doing crossfades between the outputs based on the time-smoothed envelope.

For instance, rather than "If envelope < -30 dB send to out 1 otherwise send to out 2" (Which would click rather bad)--

Try something like "If envelope < -36 dB send to out 1. If envelope > -24 dB send to out 2. Otherwise pan between out 1 and out 2 according to the envelope level."

Maybe you would decide to linear fade so that at -30 dB (the center point), that both out 1 and out 2 are equally loud, and both out 1 and out 2 added together == -30 dB. Or other schemes. Essentially use the envelope level to enforce "pan rules" between the outputs. There are various pan rules you could choose.

The "envelope modulated panning" would sometimes have audio shared between more than one output, but could more easily be made smoother and less clicky sounding.

All the parms you like could be made adjustable-- Envelope times, switching threshold levels, panning width in dB, even gain adjustment of the different outputs, in case maybe you want to boost the quiet outputs and cut the loud outputs or whatever.

If you'd want more sudden switching at greater risk of artifacts, maybe pan over a band of 1 dB, for instance doing the entire pan from full out 1 at -30.5 dB, equal mix at -30 dB, and full out 2 at -29.5 dB. Or maybe pan over a wider amplitude window for less risk of artifacts and clicks.

Similarly, longer envelope times would have less risk of glitches, better chance of smooth sound. Faster envelopes responding faster with higher risk of glitches. Let the user pick how much glitches vs response speed he wants.
great contribution, jcjr! Thank you!

I'm very new to JSFX coding and I fear that this task is waaayyy too complex for my skills . I can follow your logics, though. Makes sense to me. I'll have to be digging more into JSFX coding. In fact I hoped that a similar plugin already existed and could be used as a starting point.

I also need to try the VST Robert mentioned. However, I haven't managed to get it up and running yet.
SonicAxiom is offline   Reply With Quote
Old 04-24-2017, 08:54 AM   #13
Robert Randolph
Human being with feelings
 
Robert Randolph's Avatar
 
Join Date: Apr 2017
Location: St. Petersburg, FL
Posts: 842
Default



So, you want something like this right?

__________________
http://www.admiralbumblebee.com
Audio/DAW stuff, Woodworking, Programming and more...
Robert Randolph is online now   Reply With Quote
Old 04-24-2017, 09:10 AM   #14
SonicAxiom
Human being with feelings
 
SonicAxiom's Avatar
 
Join Date: Dec 2012
Location: Germany
Posts: 1,921
Default

EXACTLY, Robert!

Can you accomplish the same with three output busses, too? That would be it! Are you using the VST you mentioned? I didn't have the time testing it yet.
SonicAxiom is offline   Reply With Quote
Old 04-24-2017, 10:04 AM   #15
Robert Randolph
Human being with feelings
 
Robert Randolph's Avatar
 
Join Date: Apr 2017
Location: St. Petersburg, FL
Posts: 842
Default

Quote:
Originally Posted by SonicAxiom View Post
EXACTLY, Robert!

Can you accomplish the same with three output busses, too? That would be it! Are you using the VST you mentioned? I didn't have the time testing it yet.
Nope, just 1 plug-in built in to Reaper. (ReaSurround + parameter modulation)

I'll write a little tutorial later today, it's actually pretty easy and no JS required.
__________________
http://www.admiralbumblebee.com
Audio/DAW stuff, Woodworking, Programming and more...
Robert Randolph is online now   Reply With Quote
Old 04-24-2017, 04:12 PM   #16
Robert Randolph
Human being with feelings
 
Robert Randolph's Avatar
 
Join Date: Apr 2017
Location: St. Petersburg, FL
Posts: 842
Default

As promised: http://admiralbumblebee.com/music/20...in-Reaper.html

If this is confusing, then I can do a video on it as well.

I personally prefer text explanations, so that's what I primarily write. Video isn't too much more difficult if that'd help you out though.

Crossfading and transition speed is built-in with this method. I've had it setup with some 5 layers of level-based routing and it works great. If you set it up on all on 1 channel, then you can save it as an FX chain and just toss-it-on-and-go.
__________________
http://www.admiralbumblebee.com
Audio/DAW stuff, Woodworking, Programming and more...
Robert Randolph is online now   Reply With Quote
Old 04-24-2017, 04:47 PM   #17
TryingToMakeMusic
Banned
 
Join Date: Nov 2016
Posts: 416
Default

Quote:
Originally Posted by SonicAxiom View Post
I'm very new to JSFX coding and I fear that this task is waaayyy too complex for my skills . I can follow your logics, though. Makes sense to me. I'll have to be digging more into JSFX coding.
If you study the first couple of sections of the docs, you can skip most of the other sections in order to learn the parts you need for this. (Browse the other parts, but you don't need graphics, printing, and many other features for this.)
TryingToMakeMusic is offline   Reply With Quote
Old 04-24-2017, 06:19 PM   #18
SonicAxiom
Human being with feelings
 
SonicAxiom's Avatar
 
Join Date: Dec 2012
Location: Germany
Posts: 1,921
Default

Quote:
Originally Posted by Robert Randolph View Post
As promised: http://admiralbumblebee.com/music/20...in-Reaper.html

If this is confusing, then I can do a video on it as well.

I personally prefer text explanations, so that's what I primarily write. Video isn't too much more difficult if that'd help you out though.

Crossfading and transition speed is built-in with this method. I've had it setup with some 5 layers of level-based routing and it works great. If you set it up on all on 1 channel, then you can save it as an FX chain and just toss-it-on-and-go.
Hey Robert, congratulations. Fairly unconventional solution. I got it working more or less (with two stereo output signals), however, I had to setup parameter modulation completely different than what you suggested to make it work. Never mind. Though, for the third stereo split, I can't seem to figure it out right. Easiest would be if you could provide a working track template. I could then study all the routing and modulation stuff.

As a side note: I've read that you had avoided Reaper for a long time and then had a hard time getting started only recently. I think the Reaper community has gained a great soul with lots of crazy ideas! This is the right place for somebody like you! In my perception your solution for my problem is definitely the least obvious! But who cares, it works thanks to your funnily connected brain cells I mean this with all respect and gratitude! You are able to think outside the box!

THANK YOU!

You might have a quick look at JSFX coding. Maybe you can come up with an equivalent solution in a few hours . A JSFX will surely be much smoother to use. In the meantime, I'll try to use your current remedy. Cool stuff!
SonicAxiom is offline   Reply With Quote
Old 04-24-2017, 07:43 PM   #19
ericzang
Human being with feelings
 
ericzang's Avatar
 
Join Date: Mar 2014
Location: Phoenix, AZ
Posts: 364
Default

Robert, that is really cool and ingenious! Thanks very much!
ericzang is online now   Reply With Quote
Old 04-25-2017, 02:38 AM   #20
bezusheist
Human being with feelings
 
bezusheist's Avatar
 
Join Date: Nov 2010
Location: USA
Posts: 701
Default

i haven't tested this thoroughly, but it seems to work...
basically the code uses 2 gates and subtraction to split the stereo input signal into 3 stereo channels based on level.
(i just copied Witti's JS gate code for a quick example, but obviously you could customize/code however you like.)
it may be a little hacked ? (my coffee to weed ratio is a little off this A.M.), but it's just a "proof of concept" (...as i've heard some say round here...)
it's based on peak level, but i think it would be easy enough to add a filter to the detector for RMS.
edit: i suppose another good option would be a "lookahead" and/or "hold" parameter...

Code:
desc:level based signal splitter

slider1:-60<-100,0,0.01>Threshold 1 (dBFS)
slider2:50<1,1000,1>Attack 1 (ms)
slider3:50<1,1000,1>Release 1 (ms)
slider4:50<1,1000,1>Env Decay 1 (ms)
slider6:-30<-100,0,0.01>Threshold 2 (dBFS)
slider7:50<1,1000,1>Attack 2 (ms)
slider8:50<1,1000,1>Release 2(ms)
slider9:50<1,1000,1>Env Decay 2 (ms)

in_pin:left input
in_pin:right input
out_pin:left output
out_pin:right output
out_pin:left output
out_pin:right output
out_pin:left output
out_pin:right output
out_pin:left output
out_pin:right output

@init

gain = gain2 = 1;
env = env2 = 0;

@slider

threshold = 10^(slider1/20);
attack = exp(-1/(slider2/1000*srate));
release = exp(-1/(slider3/1000*srate));
envelope_decay = exp(-1/(slider4/1000*srate));

threshold2 = 10^(slider6/20);
attack2 = exp(-1/(slider7/1000*srate));
release2 = exp(-1/(slider8/1000*srate));
envelope_decay2 = exp(-1/(slider9/1000*srate));

@sample

inL = spl0;
inR = spl1;


det = max(abs(inL),abs(inR));
//det += 0.000000000001;
env = det >= env ? det : det+envelope_decay*(env-det);
transfer_gain = env > threshold ? pow(env,0):0;
gain = transfer_gain > gain ? transfer_gain+attack*(gain-transfer_gain) : 
transfer_gain+release*(gain-transfer_gain);

env2 = det >= env2 ? det : det+envelope_decay2*(env2-det);
transfer_gain2 = env2 > threshold2 ? pow(env2,0):0;
gain2 = transfer_gain2 > gain2 ? transfer_gain2+attack2*(gain2-transfer_gain2) : 
transfer_gain2+release2*(gain2-transfer_gain2);

a = inL * gain;
b = inR * gain;
aa = inL * gain2;
bb = inR * gain2;

spl0 = inL-a; 
spl1 = inR-b;

spl4 = aa;
spl5 = aa;

spl2 = inL-(spl0+spl4); 
spl3 = inR-(spl1+spl5);
__________________
trahpyzzup

Last edited by bezusheist; 04-25-2017 at 02:02 PM.
bezusheist is offline   Reply With Quote
Old 04-25-2017, 06:25 AM   #21
Robert Randolph
Human being with feelings
 
Robert Randolph's Avatar
 
Join Date: Apr 2017
Location: St. Petersburg, FL
Posts: 842
Default

Quote:
Originally Posted by SonicAxiom View Post
Hey Robert, congratulations. Fairly unconventional solution. I got it working more or less (with two stereo output signals), however, I had to setup parameter modulation completely different than what you suggested to make it work. Never mind. Though, for the third stereo split, I can't seem to figure it out right. Easiest would be if you could provide a working track template. I could then study all the routing and modulation stuff.

As a side note: I've read that you had avoided Reaper for a long time and then had a hard time getting started only recently. I think the Reaper community has gained a great soul with lots of crazy ideas! This is the right place for somebody like you! In my perception your solution for my problem is definitely the least obvious! But who cares, it works thanks to your funnily connected brain cells I mean this with all respect and gratitude! You are able to think outside the box!

THANK YOU!

You might have a quick look at JSFX coding. Maybe you can come up with an equivalent solution in a few hours . A JSFX will surely be much smoother to use. In the meantime, I'll try to use your current remedy. Cool stuff!
Thank you

Could you show me what parameter modulation settings you had to use? I want to make sure that my tutorial is correct.

I'll try to get you a track template later today after this session is done.
__________________
http://www.admiralbumblebee.com
Audio/DAW stuff, Woodworking, Programming and more...
Robert Randolph is online now   Reply With Quote
Old 04-25-2017, 07:50 AM   #22
bezusheist
Human being with feelings
 
bezusheist's Avatar
 
Join Date: Nov 2010
Location: USA
Posts: 701
Default

something like this works as well...(based on "RMS" type levels)
Code:
desc: thingy

slider1:50<1,1000,1>smoothing (Hz)
slider2:-60<-100,0,0.01>threshold 1
slider3:-20<-100,0,0.01>threshold 2

@slider

fc = slider1;
k = exp(-2*$pi*fc/srate);
t1 = 10^(slider2/20);
t2 = 10^(slider3/20);

@sample

inl = spl0*spl0;
inr = spl1*spl1;
in = (inl+inr)*0.5;
out = (1-k)*in+k*out;
rms = sqrt(out);

rms > t1 ?
(
a = spl0;
aa = spl1;
):(
a = aa = 0;
);
rms > t2 ?
(
b = spl0;
bb = spl1;
):(
b = bb = 0;
);

spl0 = spl0-a; 
spl1 = spl1-aa;
spl2 = a-b;
spl3 = aa-bb;
spl4 = b; 
spl5 = bb;
...and it might be good to have an envelope follower or something else to help with "smoothing", but this is the raw concept.
__________________
trahpyzzup

Last edited by bezusheist; 04-25-2017 at 08:04 AM.
bezusheist is offline   Reply With Quote
Old 04-25-2017, 09:46 PM   #23
SaulT
Human being with feelings
 
Join Date: Oct 2013
Location: Seattle, WA
Posts: 769
Default

Here's one way to do it.

Selectable detector RMS, two different detector modes (average of the stereo buss or the higher peak value of the two), adjustable attack/release, can select input/outputs from the first four stereo busses. The attack might sound a little slower than the time indicates, so use your ears.


Code:
desc:audio bus splitter (sault)

slider1:0<0,3,1{1+2,3+4,5+6,7+8}>Input channel
slider2:500<100,1000,1>RMS detector (ms)
slider3:20<1,100,1>Attack (ms)
slider4:20<1,100,1>Release (ms)
slider5:0<0,1,0{avg,peak}>Detect mode
slider10:0<0,3,1{1+2,3+4,5+6,7+8}>Output channel < thresh 1
slider11:-32<-60,15,0.1>Threshold 1
slider15:1<0,3,1{1+2,3+4,5+6,7+8}>Output channel < thresh 2
slider16:-18<-60,15,0.1>Threshold 2
slider20:2<0,3,1{1+2,3+4,5+6,7+8}>Output channel > thresh 2



@init

function RMS_set(ms) ( this.coeff = exp(-1/(ms/1000*srate)); );

function RMS(in)
  instance(rms_s,coeff) 
(
  rms_s = (rms_s * coeff) + ((1-coeff)*in*in);
  sqrt(rms_s);
);

function channel_selector(ch) (
  ch == 0 ? ( global.s0 = spl0; global.s1 = spl1; );
  ch == 1 ? ( global.s0 = spl2; global.s1 = spl3; );
  ch == 2 ? ( global.s0 = spl4; global.s1 = spl5; );
  ch == 3 ? ( global.s0 = spl6; global.s1 = spl7; );
  global.as0 = abs(global.s0);
  global.as1 = abs(global.s1);
  );

function channel_output(ch,s0,s1) (
  ch == 0 ? ( spl0 = s0; spl1 = s1; );
  ch == 1 ? ( spl2 = s0; spl3 = s1; );
  ch == 2 ? ( spl4 = s0; spl5 = s1; );
  ch == 3 ? ( spl6 = s0; spl7 = s1; );
  );

env0 = env1 = env2 = 1;

@slider

input_channel = slider1;
output_ch0 = slider10;
output_ch1 = slider15;
output_ch2 = slider20;

(slider20 < slider15) ? slider20 = slider15;

thresh1 = 10^(slider11/20);
thresh2 = 10^(slider16/20);

rms0.RMS_set(slider2);
attack  = exp(-1/(0.001 * srate * slider3));
release = exp(-1/(0.001 * srate * slider4));

mode = slider5; // 0 is sum

@sample

channel_selector(input_channel);

mode ? (
  detector = rms0.RMS(max(global.as0,global.as1));
  ) : (
  detector = rms0.RMS(0.5*(global.as0 + global.as1));
  );

(detector < thresh1) ? (
  env0 /= attack;
  env1 *= release;
  env2 *= release;
  );

(detector >= thresh1 && detector < thresh2 ) ? (
  env0 *= release;
  env1 /= attack;
  env2 *= release;
  );

(detector >= thresh2) ? (
  env0 *= release;
  env1 *= release;
  env2 /= attack;
  );

env0 = max(min(env0,1),0.001);
env1 = max(min(env1,1),0.001);
env2 = max(min(env2,1),0.001);


channel_output(output_ch0,global.s0*env0,global.s1*env0);
channel_output(output_ch1,global.s0*env1,global.s1*env1);
channel_output(output_ch2,global.s0*env2,global.s1*env2);
SaulT is offline   Reply With Quote
Old 04-26-2017, 04:57 AM   #24
IXix
Human being with feelings
 
Join Date: Jan 2007
Location: mcr:uk
Posts: 3,386
Default

Nice
IXix is offline   Reply With Quote
Old 04-28-2017, 08:28 AM   #25
SonicAxiom
Human being with feelings
 
SonicAxiom's Avatar
 
Join Date: Dec 2012
Location: Germany
Posts: 1,921
Default

oddly, I didn't get any more notification on new replies in this thread since 3 days. Came here right now by accident and am overwhelmed by the input from all of you guys!!! Thanks a lot!

I'm going to check out all your suggestions and will try report back soon. Thanks again! As usual, Reaper proves to allow for a whole variety of solutions for one problem.
SonicAxiom is offline   Reply With Quote
Old 04-28-2017, 08:48 AM   #26
SonicAxiom
Human being with feelings
 
SonicAxiom's Avatar
 
Join Date: Dec 2012
Location: Germany
Posts: 1,921
Default

Quote:
Originally Posted by SaulT View Post
Here's one way to do it.

Selectable detector RMS, two different detector modes (average of the stereo buss or the higher peak value of the two), adjustable attack/release, can select input/outputs from the first four stereo busses. The attack might sound a little slower than the time indicates, so use your ears.


Code:
desc:audio bus splitter (sault)

slider1:0<0,3,1{1+2,3+4,5+6,7+8}>Input channel
slider2:500<100,1000,1>RMS detector (ms)
slider3:20<1,100,1>Attack (ms)
slider4:20<1,100,1>Release (ms)
slider5:0<0,1,0{avg,peak}>Detect mode
slider10:0<0,3,1{1+2,3+4,5+6,7+8}>Output channel < thresh 1
slider11:-32<-60,15,0.1>Threshold 1
slider15:1<0,3,1{1+2,3+4,5+6,7+8}>Output channel < thresh 2
slider16:-18<-60,15,0.1>Threshold 2
slider20:2<0,3,1{1+2,3+4,5+6,7+8}>Output channel > thresh 2



@init

function RMS_set(ms) ( this.coeff = exp(-1/(ms/1000*srate)); );

function RMS(in)
  instance(rms_s,coeff) 
(
  rms_s = (rms_s * coeff) + ((1-coeff)*in*in);
  sqrt(rms_s);
);

function channel_selector(ch) (
  ch == 0 ? ( global.s0 = spl0; global.s1 = spl1; );
  ch == 1 ? ( global.s0 = spl2; global.s1 = spl3; );
  ch == 2 ? ( global.s0 = spl4; global.s1 = spl5; );
  ch == 3 ? ( global.s0 = spl6; global.s1 = spl7; );
  global.as0 = abs(global.s0);
  global.as1 = abs(global.s1);
  );

function channel_output(ch,s0,s1) (
  ch == 0 ? ( spl0 = s0; spl1 = s1; );
  ch == 1 ? ( spl2 = s0; spl3 = s1; );
  ch == 2 ? ( spl4 = s0; spl5 = s1; );
  ch == 3 ? ( spl6 = s0; spl7 = s1; );
  );

env0 = env1 = env2 = 1;

@slider

input_channel = slider1;
output_ch0 = slider10;
output_ch1 = slider15;
output_ch2 = slider20;

(slider20 < slider15) ? slider20 = slider15;

thresh1 = 10^(slider11/20);
thresh2 = 10^(slider16/20);

rms0.RMS_set(slider2);
attack  = exp(-1/(0.001 * srate * slider3));
release = exp(-1/(0.001 * srate * slider4));

mode = slider5; // 0 is sum

@sample

channel_selector(input_channel);

mode ? (
  detector = rms0.RMS(max(global.as0,global.as1));
  ) : (
  detector = rms0.RMS(0.5*(global.as0 + global.as1));
  );

(detector < thresh1) ? (
  env0 /= attack;
  env1 *= release;
  env2 *= release;
  );

(detector >= thresh1 && detector < thresh2 ) ? (
  env0 *= release;
  env1 /= attack;
  env2 *= release;
  );

(detector >= thresh2) ? (
  env0 *= release;
  env1 *= release;
  env2 /= attack;
  );

env0 = max(min(env0,1),0.001);
env1 = max(min(env1,1),0.001);
env2 = max(min(env2,1),0.001);


channel_output(output_ch0,global.s0*env0,global.s1*env0);
channel_output(output_ch1,global.s0*env1,global.s1*env1);
channel_output(output_ch2,global.s0*env2,global.s1*env2);
Great work, Saul! A huge thank you! Without having tested other solutions, your approach comes very close to what I imagined.

I noticed that there seems to be some signal left on "non-active" busses. Can this "sonic residue" be eliminated?

Also, I don't quite succeed in getting seemless transitioning between busses. Maybe I'm using the wrong settings, though. I've been testing the plugin with a mastered mp3.
SonicAxiom is offline   Reply With Quote
Old 04-28-2017, 09:00 AM   #27
SonicAxiom
Human being with feelings
 
SonicAxiom's Avatar
 
Join Date: Dec 2012
Location: Germany
Posts: 1,921
Default

Quote:
Originally Posted by bezusheist View Post
something like this works as well...(based on "RMS" type levels)
Code:
desc: thingy

slider1:50<1,1000,1>smoothing (Hz)
slider2:-60<-100,0,0.01>threshold 1
slider3:-20<-100,0,0.01>threshold 2

@slider

fc = slider1;
k = exp(-2*$pi*fc/srate);
t1 = 10^(slider2/20);
t2 = 10^(slider3/20);

@sample

inl = spl0*spl0;
inr = spl1*spl1;
in = (inl+inr)*0.5;
out = (1-k)*in+k*out;
rms = sqrt(out);

rms > t1 ?
(
a = spl0;
aa = spl1;
):(
a = aa = 0;
);
rms > t2 ?
(
b = spl0;
bb = spl1;
):(
b = bb = 0;
);

spl0 = spl0-a; 
spl1 = spl1-aa;
spl2 = a-b;
spl3 = aa-bb;
spl4 = b; 
spl5 = bb;
...and it might be good to have an envelope follower or something else to help with "smoothing", but this is the raw concept.
Excellent solution, too, bezusheist, many thanks! Even more simplistic but notably smoothing works extremely well. If we could combine your smoothing approach (and maybe other favorable properties with) Sauls routing options that would be bliss!
SonicAxiom is offline   Reply With Quote
Old 04-28-2017, 09:23 AM   #28
SonicAxiom
Human being with feelings
 
SonicAxiom's Avatar
 
Join Date: Dec 2012
Location: Germany
Posts: 1,921
Default

Quote:
Originally Posted by Robert Randolph View Post
Thank you

Could you show me what parameter modulation settings you had to use? I want to make sure that my tutorial is correct.

I'll try to get you a track template later today after this session is done.
Hey Robert, while your approach seems to be way more exceptional and fascinating in terms "out-of-the-box-thinking" and you have my greatest respect for this solution, I think the small piece of JS code is far more efficient and a fair bit more user friendly to work with

I can't stop thinking that your imagination will find a great space to have a fling with JSFX coding!

Thanks again for taking your precious time and knead your brain! Much appreciated!
SonicAxiom is offline   Reply With Quote
Old 04-28-2017, 09:53 AM   #29
Robert Randolph
Human being with feelings
 
Robert Randolph's Avatar
 
Join Date: Apr 2017
Location: St. Petersburg, FL
Posts: 842
Default

Quote:
Originally Posted by SonicAxiom View Post
Hey Robert, while your approach seems to be way more exceptional and fascinating in terms "out-of-the-box-thinking" and you have my greatest respect for this solution, I think the small piece of JS code is far more efficient and a fair bit more user friendly to work with

I can't stop thinking that your imagination will find a great space to have a fling with JSFX coding!

Thanks again for taking your precious time and knead your brain! Much appreciated!
For your usage, definitely...

But there's a lot more you can do with what I outlined above
__________________
http://www.admiralbumblebee.com
Audio/DAW stuff, Woodworking, Programming and more...
Robert Randolph is online now   Reply With Quote
Old 04-28-2017, 10:05 AM   #30
SonicAxiom
Human being with feelings
 
SonicAxiom's Avatar
 
Join Date: Dec 2012
Location: Germany
Posts: 1,921
Default

Quote:
Originally Posted by bezusheist View Post
...and it might be good to have an envelope follower or something else to help with "smoothing", but this is the raw concept.
Unfortunately, I'm getting audio glitches as soon as I put plugins into the busses that receive the split audio signals. The more I raise the smoothing value the more crackles I'm getting. Any idea, bezusheist?
SonicAxiom is offline   Reply With Quote
Old 04-28-2017, 10:17 AM   #31
SonicAxiom
Human being with feelings
 
SonicAxiom's Avatar
 
Join Date: Dec 2012
Location: Germany
Posts: 1,921
Default

Quote:
Originally Posted by Robert Randolph View Post
For your usage, definitely...

But there's a lot more you can do with what I outlined above
I admit that it went above what I'm capable of grasping, Robert. My fault. Keep up!
SonicAxiom is offline   Reply With Quote
Old 04-28-2017, 11:06 AM   #32
SonicAxiom
Human being with feelings
 
SonicAxiom's Avatar
 
Join Date: Dec 2012
Location: Germany
Posts: 1,921
Default

Quote:
Originally Posted by bezusheist View Post

Code:
desc:level based signal splitter

slider1:-60<-100,0,0.01>Threshold 1 (dBFS)
slider2:50<1,1000,1>Attack 1 (ms)
slider3:50<1,1000,1>Release 1 (ms)
slider4:50<1,1000,1>Env Decay 1 (ms)
slider6:-30<-100,0,0.01>Threshold 2 (dBFS)
slider7:50<1,1000,1>Attack 2 (ms)
slider8:50<1,1000,1>Release 2(ms)
slider9:50<1,1000,1>Env Decay 2 (ms)

in_pin:left input
in_pin:right input
out_pin:left output
out_pin:right output
out_pin:left output
out_pin:right output
out_pin:left output
out_pin:right output
out_pin:left output
out_pin:right output

@init

gain = gain2 = 1;
env = env2 = 0;

@slider

threshold = 10^(slider1/20);
attack = exp(-1/(slider2/1000*srate));
release = exp(-1/(slider3/1000*srate));
envelope_decay = exp(-1/(slider4/1000*srate));

threshold2 = 10^(slider6/20);
attack2 = exp(-1/(slider7/1000*srate));
release2 = exp(-1/(slider8/1000*srate));
envelope_decay2 = exp(-1/(slider9/1000*srate));

@sample

inL = spl0;
inR = spl1;


det = max(abs(inL),abs(inR));
//det += 0.000000000001;
env = det >= env ? det : det+envelope_decay*(env-det);
transfer_gain = env > threshold ? pow(env,0):0;
gain = transfer_gain > gain ? transfer_gain+attack*(gain-transfer_gain) : 
transfer_gain+release*(gain-transfer_gain);

env2 = det >= env2 ? det : det+envelope_decay2*(env2-det);
transfer_gain2 = env2 > threshold2 ? pow(env2,0):0;
gain2 = transfer_gain2 > gain2 ? transfer_gain2+attack2*(gain2-transfer_gain2) : 
transfer_gain2+release2*(gain2-transfer_gain2);

a = inL * gain;
b = inR * gain;
aa = inL * gain2;
bb = inR * gain2;

spl0 = inL-a; 
spl1 = inR-b;

spl4 = aa;
spl5 = aa; /* error! must read: spl5 = bb; */

spl2 = inL-(spl0+spl4); 
spl3 = inR-(spl1+spl5);
This one works great! It also doesn't produce the audio glitches that your other plugin introduces.

Note, that the code had a little bug:

replace

spl5 = aa;

with

spl5 = bb;

and it works brillantly!

Thanks a bunch, bezusheist!!
SonicAxiom is offline   Reply With Quote
Old 04-28-2017, 07:13 PM   #33
SaulT
Human being with feelings
 
Join Date: Oct 2013
Location: Seattle, WA
Posts: 769
Default

Quote:
I noticed that there seems to be some signal left on "non-active" busses. Can this "sonic residue" be eliminated?
So basically I chose this envelope method because it was the first one I thought of and it isn't super hard to implement. The downside of it is that there must be a non-zero minimum value - there will always be some audio sent to all channels. I figured that 60 dB of attenuation is normally considered pretty close to "silent", so that's why I chose 0.001 as a minimum.

Normal "analog style" controls are calibrated so that it takes about 5 time constants to reach 99% of your target value. In other words, if your attack time is 20 ms, then you should reach your full attack value by about 100 ms. To go from 0.001 to 1 takes about 6 time constants, so I figured that was close enough.

So what if 60 dB of attenuation isn't enough? The lower the value you start with the greater the number of time constants you would need unless you tweak the time constant formula. Which we of course can do.

After a little bit of experimentation, this has 120 dB of attenuation when the envelope is at minimum and is much snappier with about a 4.5 time constant rise time. I would expect that this would work for you? These would be the changes needed -


Code:
env0 = max(min(env0,1),0.000001);
env1 = max(min(env1,1),0.000001);
env2 = max(min(env2,1),0.000001);

attack  = exp(-3/(0.001 * srate * slider3));
release = exp(-3/(0.001 * srate * slider4));
SaulT is offline   Reply With Quote
Old 05-01-2017, 10:21 AM   #34
SonicAxiom
Human being with feelings
 
SonicAxiom's Avatar
 
Join Date: Dec 2012
Location: Germany
Posts: 1,921
Default

Quote:
Originally Posted by SaulT View Post
So basically I chose this envelope method because it was the first one I thought of and it isn't super hard to implement. The downside of it is that there must be a non-zero minimum value - there will always be some audio sent to all channels. I figured that 60 dB of attenuation is normally considered pretty close to "silent", so that's why I chose 0.001 as a minimum.

Normal "analog style" controls are calibrated so that it takes about 5 time constants to reach 99% of your target value. In other words, if your attack time is 20 ms, then you should reach your full attack value by about 100 ms. To go from 0.001 to 1 takes about 6 time constants, so I figured that was close enough.

So what if 60 dB of attenuation isn't enough? The lower the value you start with the greater the number of time constants you would need unless you tweak the time constant formula. Which we of course can do.

After a little bit of experimentation, this has 120 dB of attenuation when the envelope is at minimum and is much snappier with about a 4.5 time constant rise time. I would expect that this would work for you? These would be the changes needed -


Code:
env0 = max(min(env0,1),0.000001);
env1 = max(min(env1,1),0.000001);
env2 = max(min(env2,1),0.000001);

attack  = exp(-3/(0.001 * srate * slider3));
release = exp(-3/(0.001 * srate * slider4));
Many thanks, Saul! Your code works quite well also. What I found is that the transitioning between busses is very quick, in fact a bit too quick. If I don't set attack and release sliders to minimum I get a brief silence gap while transitioning occurs. Could the code be changed in a way that transitioning length (=crossfading between busses) could be adjusted from instantly to let's say 500 ms?
SonicAxiom is offline   Reply With Quote
Old 05-02-2017, 03:53 PM   #35
SaulT
Human being with feelings
 
Join Date: Oct 2013
Location: Seattle, WA
Posts: 769
Default

So I did some testing and I see what you mean. It's really hard to get a seamless transition using this method. I was so focused on making the math work that I didn't quite get that.

I'll have to go back and think about it a little more, I don't know if this method is the best if you want to avoid even brief audio cut-outs, just about every setting I try has at least a bit of cut-out. Until I can think of a better way to do it, here's a tweaked version. Coefficients are changed around, times are changed, etc.

Code:
desc:audio bus splitter (sault)

slider1:0<0,3,1{1+2,3+4,5+6,7+8}>Input channel
slider2:500<100,1000,1>RMS detector (ms)
slider3:150<1,1000,1>Attack (ms)
slider4:400<1,1000,1>Release (ms)
slider5:0<0,1,0{avg,peak}>Detect mode
slider7:-90<-90,0>RMS meter (dB)
slider10:0<0,3,1{1+2,3+4,5+6,7+8}>Output channel < thresh 1
slider11:-32<-60,15,0.1>Threshold 1
slider15:1<0,3,1{1+2,3+4,5+6,7+8}>Output channel < thresh 2
slider16:-18<-60,15,0.1>Threshold 2
slider20:2<0,3,1{1+2,3+4,5+6,7+8}>Output channel > thresh 2
//slider30:10<0.1,100,0.1>Envelope coefficient

@init

function singlepole(in,target,coeff) ( in*coeff + target*(1-coeff); );
function RMS_set(ms) ( this.coeff = exp(-1/(ms/1000*srate)); );

function RMS(in)
  instance(rms_s,coeff) 
(
  rms_s = (rms_s * coeff) + ((1-coeff)*in*in);
  sqrt(rms_s);
);

function channel_selector(ch) (
  ch == 0 ? ( global.s0 = spl0; global.s1 = spl1; );
  ch == 1 ? ( global.s0 = spl2; global.s1 = spl3; );
  ch == 2 ? ( global.s0 = spl4; global.s1 = spl5; );
  ch == 3 ? ( global.s0 = spl6; global.s1 = spl7; );
  global.as0 = abs(global.s0);
  global.as1 = abs(global.s1);
  );

function channel_output(ch,s0,s1) (
  ch == 0 ? ( spl0 = s0; spl1 = s1; );
  ch == 1 ? ( spl2 = s0; spl3 = s1; );
  ch == 2 ? ( spl4 = s0; spl5 = s1; );
  ch == 3 ? ( spl6 = s0; spl7 = s1; );
  );

env0 = env1 = env2 = 0.000001;

dbc = 20/log(10);

@slider

input_channel = slider1;
output_ch0 = slider10;
output_ch1 = slider15;
output_ch2 = slider20;

(slider20 < slider15) ? slider20 = slider15;

thresh1 = 10^(slider11/20);
thresh2 = 10^(slider16/20);

rms0.RMS_set(slider2);
attack  = exp(3/(0.001 * srate * slider3));
release = exp(-1/(0.001 * srate * slider4));
env_coeff = exp(-1/(0.001 * 0.01 * srate));

mode = slider5; // 0 is sum

@sample

channel_selector(input_channel);

mode ? (
  detector = rms0.RMS(max(global.as0,global.as1));
  ) : (
  detector = rms0.RMS(0.5*(global.as0 + global.as1));
  );

slider7 = log(detector)*dbc;

(detector < thresh1) ? (
  tenv0 = attack;
  tenv1 = release;
  tenv2 = release;
  );

(detector >= thresh1 && detector < thresh2 ) ? (
  tenv0 = release;
  tenv1 = attack;
  tenv2 = release;
  );

(detector >= thresh2) ? (
  tenv0 = release;
  tenv1 = release;
  tenv2 = attack;
  );

cenv0 = singlepole(cenv0,tenv0,env_coeff);
cenv1 = singlepole(cenv1,tenv1,env_coeff);
cenv2 = singlepole(cenv2,tenv2,env_coeff);

env0 *= cenv0;
env1 *= cenv1;
env2 *= cenv2;

env0 = max(min(env0,1),0.000001);
env1 = max(min(env1,1),0.000001);
env2 = max(min(env2,1),0.000001);

channel_output(output_ch0,global.s0*env0,global.s1*env0);
channel_output(output_ch1,global.s0*env1,global.s1*env1);
channel_output(output_ch2,global.s0*env2,global.s1*env2);
SaulT is offline   Reply With Quote
Old 05-05-2017, 06:40 AM   #36
SonicAxiom
Human being with feelings
 
SonicAxiom's Avatar
 
Join Date: Dec 2012
Location: Germany
Posts: 1,921
Default

Hey Saul, thanks you so much for your effort! Your new version seems to have the same little issue when transitionig between busses. For my use, this is a problem. Maybe this is due to the whole design of your plugin which would be sad. In the meantime I'm using bezusheist's plugin and I'm getting very good results. Don't worry and don't put more effort into looking for a better solution if you have something more important to do though I'm always stunned by different people's ideas and approaches to solve the same problem.
SonicAxiom is offline   Reply With Quote
Old Today, 07:56 AM   #37
kodebode2
Human being with feelings
 
Join Date: Oct 2009
Posts: 148
Default

This so reminds me of the initial processing that may be required for dynamic convolution, like Acustica Audio's and all you need is a different impulse for each level of audio, and some way to smoothly merge them all back again without glitching.

Maybe this is the true reason why it is impossible to have true zero latency in AA's technology. (considering they have been around for at least a decade! and no true zero latency version of their technology)
kodebode2 is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 04:46 PM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2019, vBulletin Solutions Inc.