|
|
TITLE:: FluidAmpSlice
|
|
|
SUMMARY:: Amplitude-based Slicer
|
|
|
CATEGORIES:: Libraries>FluidDecomposition
|
|
|
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition
|
|
|
|
|
|
DESCRIPTION::
|
|
|
This class implements an amplitude-based slicer, with various customisable options and conditions to detect relative amplitude changes as onsets. It is part of the Fluid Decomposition Toolkit of the FluCoMa project.footnote::This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899).::
|
|
|
|
|
|
FluidAmpSlice is based on two envelop followers on a highpassed version of the signal: one slow that gives the trend, and one fast. Each have features that will interact. The example code below is unfolding the various possibilites in order of complexity.
|
|
|
|
|
|
The process will return an audio steam with sample-long impulses at estimated starting points of the different slices.
|
|
|
|
|
|
CLASSMETHODS::
|
|
|
|
|
|
METHOD:: ar
|
|
|
The audio rate version of the object.
|
|
|
|
|
|
ARGUMENT:: in
|
|
|
The audio to be processed.
|
|
|
|
|
|
ARGUMENT:: fastRampUp
|
|
|
The number of samples the fast envelope follower will take to reach the next value when raising. Typically, this will be faster than slowRampUp.
|
|
|
|
|
|
ARGUMENT:: fastRampDown
|
|
|
The number of samples the fast envelope follower will take to reach the next value when falling. Typically, this will be faster than slowRampDown.
|
|
|
|
|
|
ARGUMENT:: slowRampUp
|
|
|
The number of samples the absolute envelope follower will take to reach the next value when raising.
|
|
|
|
|
|
ARGUMENT:: slowRampDown
|
|
|
The number of samples the absolute envelope follower will take to reach the next value when falling.
|
|
|
|
|
|
ARGUMENT:: onThreshold
|
|
|
The threshold in dB of the relative envelope follower to trigger an onset, aka to go ON when in OFF state. It is computed on the difference between the two envelope followers.
|
|
|
|
|
|
ARGUMENT:: offThreshold
|
|
|
The threshold in dB of the relative envelope follower to reset, aka to allow the differential envelop to trigger again.
|
|
|
|
|
|
ARGUMENT:: floor
|
|
|
The level in dB the slowRamp needs to be above to consider a detected difference valid, allowing to ignore the slices in the noise floor.
|
|
|
|
|
|
ARGUMENT:: highPassFreq
|
|
|
The frequency of the fourth-order Linkwitz–Riley high-pass filter (https://en.wikipedia.org/wiki/Linkwitz%E2%80%93Riley_filter). This is done first on the signal to minimise low frequency intermodulation with very fast ramp lengths.
|
|
|
|
|
|
ARGUMENT:: minSliceLength
|
|
|
The length in samples that the Slice will stay ON. Changes of states during that period will be ignored.
|
|
|
|
|
|
RETURNS::
|
|
|
An audio stream with square envelopes around the slices. The latency between the input and the output is dependant on the relation between the two envelope followers.
|
|
|
|
|
|
EXAMPLES::
|
|
|
|
|
|
code::
|
|
|
//basic tests: absThresh sanity
|
|
|
(
|
|
|
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
|
|
|
env = FluidAmpSlice.ar(source,absRampUp:5, absRampDown:50, absThreshOn:-12, absThreshOff: -12);
|
|
|
[source, env]
|
|
|
}.plot(0.1);
|
|
|
)
|
|
|
//basic tests: absThresh hysteresis
|
|
|
(
|
|
|
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
|
|
|
env = FluidAmpSlice.ar(source,absRampUp:5, absRampDown:50, absThreshOn:-12, absThreshOff: -16);
|
|
|
[source, env]
|
|
|
}.plot(0.1);
|
|
|
)
|
|
|
//basic tests: absThresh min slice
|
|
|
(
|
|
|
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
|
|
|
env = FluidAmpSlice.ar(source,absRampUp:5, absRampDown:50, absThreshOn:-12, absThreshOff: -12, minSliceLength:441);
|
|
|
[source, env]
|
|
|
}.plot(0.1);
|
|
|
)
|
|
|
//basic tests: absThresh min silence
|
|
|
(
|
|
|
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
|
|
|
env = FluidAmpSlice.ar(source,absRampUp:5, absRampDown:50, absThreshOn:-12, absThreshOff: -12, minSilenceLength:441);
|
|
|
[source, env]
|
|
|
}.plot(0.1);
|
|
|
)
|
|
|
//mid tests: absThresh time hysteresis on
|
|
|
(
|
|
|
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
|
|
|
env = FluidAmpSlice.ar(source,absRampUp:5, absRampDown:50, absThreshOn:-12, absThreshOff: -12, minLengthAbove:441);
|
|
|
[DelayN.ar(source,0.1,441/44100), env]
|
|
|
}.plot(0.1);
|
|
|
)
|
|
|
//mid tests: absThresh time hysteresis off
|
|
|
(
|
|
|
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
|
|
|
env = FluidAmpSlice.ar(source,absRampUp:5, absRampDown:50, absThreshOn:-12, absThreshOff: -12, minLengthBelow:441);
|
|
|
[DelayN.ar(source,0.1,441/44100), env]
|
|
|
}.plot(0.1);
|
|
|
)
|
|
|
//mid tests: absThresh with lookBack
|
|
|
(
|
|
|
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
|
|
|
env = FluidAmpSlice.ar(source,absRampUp:5, absRampDown:50, absThreshOn:-12, absThreshOff: -12,lookBack:441);
|
|
|
[DelayN.ar(source,0.1,441/44100), env]
|
|
|
}.plot(0.1);
|
|
|
)
|
|
|
//mid tests: absThresh with lookAhead
|
|
|
(
|
|
|
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
|
|
|
env = FluidAmpSlice.ar(source,absRampUp:5, absRampDown:50, absThreshOn:-12, absThreshOff: -12,lookAhead:441);
|
|
|
[DelayN.ar(source,0.1,441/44100), env]
|
|
|
}.plot(0.1);
|
|
|
)
|
|
|
//mid tests: absThresh with asymetrical lookBack and lookAhead
|
|
|
(
|
|
|
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
|
|
|
env = FluidAmpSlice.ar(source,absRampUp:5, absRampDown:50, absThreshOn:-12, absThreshOff: -12,lookBack:221, lookAhead:441);
|
|
|
[DelayN.ar(source,0.1,441/44100), env]
|
|
|
}.plot(0.1);
|
|
|
)
|
|
|
//advanced tests: absThresh hysteresis, long tail
|
|
|
(
|
|
|
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
|
|
|
env = FluidAmpSlice.ar(source,absRampUp:220, absRampDown:220, absThreshOn:-60, absThreshOff: -70);
|
|
|
[source, env]
|
|
|
}.plot(0.1);
|
|
|
)
|
|
|
//solution: have to recut with relThresh
|
|
|
(
|
|
|
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
|
|
|
env = FluidAmpSlice.ar(source,absRampUp:220, absRampDown:220, absThreshOn:-60, absThreshOff: -70, relRampUp:5, relRampDown:200, relThreshOn:1, relThreshOff:0);
|
|
|
[source, env]
|
|
|
}.plot(0.08);
|
|
|
)
|
|
|
//beware of double trig
|
|
|
(
|
|
|
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
|
|
|
env = FluidAmpSlice.ar(source,absRampUp:220, absRampDown:220, absThreshOn:-60, absThreshOff: -70, relRampUp:5, relRampDown:200, relThreshOn:1, relThreshOff:0);
|
|
|
[source, env]
|
|
|
}.plot(0.005);
|
|
|
)
|
|
|
//a solution: minSliceLength
|
|
|
(
|
|
|
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
|
|
|
env = FluidAmpSlice.ar(source,absRampUp:220, absRampDown:220, absThreshOn:-60, absThreshOff: -70, relRampUp:5, relRampDown:200, relThreshOn:1, relThreshOff:0, minSliceLength:2205);
|
|
|
[source, env]
|
|
|
}.plot(0.005);
|
|
|
)
|
|
|
//drum slicing, many ways
|
|
|
//load a buffer
|
|
|
b = Buffer.read(s,File.realpath(FluidAmpSlice.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav");
|
|
|
//have fun with a gate (explore lookahead and lookback, but correct for latency)
|
|
|
(
|
|
|
{var env, source = PlayBuf.ar(1,b);
|
|
|
env = FluidAmpSlice.ar(source,absRampUp:1103, absRampDown:2205, absThreshOn:-27, absThreshOff: -31, minSilenceLength:1100, lookBack:441, highPassFreq:40);
|
|
|
[DelayN.ar(source,delaytime:441/44100), env]
|
|
|
}.plot(2,separately:true);
|
|
|
)
|
|
|
|
|
|
(
|
|
|
{var env, source = PlayBuf.ar(1,b, loop:1);
|
|
|
env = FluidAmpSlice.ar(source, absRampUp:4410, absRampDown:4410, absThreshOn:-60, absThreshOff: -70, relRampUp:10, relRampDown:2205, relThreshOn:12, relThreshOff:9, minSilenceLength:4410, highPassFreq:20);
|
|
|
[source, Trig.ar(env,0)]
|
|
|
}.play;
|
|
|
)
|
|
|
::
|