Merge pull request #136 from flucoma/dev

[release] 1.0.3
nix
tremblap 3 years ago committed by GitHub
commit 39860d14d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -5,7 +5,7 @@ FluidAmpGate : FluidRTUGen {
checkInputs {
if(inputs.at(12).rate != 'scalar') {
^(": maxSize cannot be modulated.");
};
};
^this.checkValidInputs;
}
}

@ -1,11 +1,11 @@
FluidAudioTransport : FluidRTUGen {
init { |...theInputs|
theInputs;
inputs = theInputs;
this.specialIndex = 1; //two audio inputs
// ^this.initOutputs(1,rate);
}
init { |...theInputs|
theInputs;
inputs = theInputs;
this.specialIndex = 1; //two audio inputs
// ^this.initOutputs(1,rate);
}
*ar { arg in = 0, in2 = 0, interpolation = 0.0, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = -1;
^this.multiNew('audio', in.asAudioRateInput, in2, interpolation, windowSize, hopSize, fftSize, maxFFTSize)

@ -2,35 +2,35 @@ FluidBufAmpFeature : FluidBufProcessor {
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, floor = -144, highPassFreq = 85, trig = 1, blocking = 0|
source = source.asUGenInput;
features = features.asUGenInput;
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufAmpFeature: Invalid source buffer".throw};
features.isNil.if {"FluidBufAmpFeature: Invalid features buffer".throw};
source.isNil.if {"FluidBufAmpFeature: Invalid source buffer".throw};
features.isNil.if {"FluidBufAmpFeature: Invalid features buffer".throw};
^FluidProxyUgen.kr(\FluidBufAmpFeatureTrigger, -1, source, startFrame, numFrames, startChan, numChans, features, fastRampUp, fastRampDown, slowRampUp, slowRampDown, floor, highPassFreq, trig, blocking);
}
*process { |server,source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, floor = -144, highPassFreq = 85, freeWhenDone = true, action |
*process { |server,source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, floor = -144, highPassFreq = 85, freeWhenDone = true, action |
source = source.asUGenInput;
features = features.asUGenInput;
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufAmpFeature: Invalid source buffer".throw};
features.isNil.if {"FluidBufAmpFeature: Invalid features buffer".throw};
source.isNil.if {"FluidBufAmpFeature: Invalid source buffer".throw};
features.isNil.if {"FluidBufAmpFeature: Invalid features buffer".throw};
^this.new(server, nil, [features]).processList(
[source, startFrame, numFrames, startChan, numChans, features, fastRampUp, fastRampDown, slowRampUp, slowRampDown, floor, highPassFreq,0],freeWhenDone, action
);
}
*processBlocking { |server,source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, floor = -144, highPassFreq = 85, freeWhenDone = true, action |
*processBlocking { |server,source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, floor = -144, highPassFreq = 85, freeWhenDone = true, action |
source = source.asUGenInput;
features = features.asUGenInput;
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufAmpFeature: Invalid source buffer".throw};
features.isNil.if {"FluidBufAmpFeature: Invalid features buffer".throw};
source.isNil.if {"FluidBufAmpFeature: Invalid source buffer".throw};
features.isNil.if {"FluidBufAmpFeature: Invalid features buffer".throw};
^this.new(server, nil, [features]).processList(
[source, startFrame, numFrames, startChan, numChans, features, fastRampUp, fastRampDown, slowRampUp, slowRampDown, floor, highPassFreq,1],freeWhenDone, action

@ -2,18 +2,18 @@ FluidBufAmpGate : FluidBufProcessor {
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, trig = 1, blocking = 0|
var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead));
var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead));
source = source.asUGenInput;
indices = indices.asUGenInput;
source = source.asUGenInput;
indices = indices.asUGenInput;
^FluidProxyUgen.kr(\FluidBufAmpGateTrigger,-1, source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq,maxSize, trig, blocking);
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, freeWhenDone = true, action |
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, freeWhenDone = true, action |
var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead));
var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead));
source = source ? -1;
indices = indices ? -1;
@ -22,23 +22,23 @@ FluidBufAmpGate : FluidBufProcessor {
server, nil, [indices]
).processList(
[source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq, maxSize, 0],freeWhenDone,action
);
);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, freeWhenDone = true, action |
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, freeWhenDone = true, action |
var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead));
var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead));
source = source ? -1;
indices = indices ? -1;
source = source ? -1;
indices = indices ? -1;
^this.new(
server, nil, [indices]
).processList(
[source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq, maxSize, 1],freeWhenDone,action
);
}
^this.new(
server, nil, [indices]
).processList(
[source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq, maxSize, 1],freeWhenDone,action
);
}
}
FluidBufAmpGateTrigger : FluidProxyUgen {}

@ -2,35 +2,35 @@ FluidBufAmpSlice : FluidBufProcessor {
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85, trig = 1, blocking = 0|
source = source.asUGenInput;
indices = indices.asUGenInput;
source = source.asUGenInput;
indices = indices.asUGenInput;
source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw};
source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw};
^FluidProxyUgen.kr(\FluidBufAmpSliceTrigger, -1, source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq, trig, blocking);
}
*process { |server,source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85, freeWhenDone = true, action |
*process { |server,source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85, freeWhenDone = true, action |
source = source.asUGenInput;
indices = indices.asUGenInput;
source = source.asUGenInput;
indices = indices.asUGenInput;
source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw};
source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw};
^this.new(server, nil, [indices]).processList(
[source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq,0],freeWhenDone, action
);
}
*processBlocking { |server,source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85, freeWhenDone = true, action |
*processBlocking { |server,source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85, freeWhenDone = true, action |
source = source.asUGenInput;
indices = indices.asUGenInput;
source = source.asUGenInput;
indices = indices.asUGenInput;
source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw};
source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw};
^this.new(server, nil, [indices]).processList(
[source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq,1],freeWhenDone, action

@ -2,14 +2,14 @@ FluidBufAudioTransport : FluidBufProcessor {
*kr { |sourceA, startFrameA = 0, numFramesA = -1, startChanA = 0, numChansA = -1, sourceB, startFrameB = 0, numFramesB = -1, startChanB = 0, numChansB = -1, destination, interpolation = 0.0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
sourceA.isNil.if {"FluidAudioTransport: Invalid source 1 buffer".throw};
sourceB.isNil.if {"FluidAudioTransport: Invalid source 2 buffer".throw};
sourceA = sourceA.asUGenInput;
sourceB = sourceB.asUGenInput;
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
sourceA.isNil.if {"FluidAudioTransport: Invalid source 1 buffer".throw};
sourceB.isNil.if {"FluidAudioTransport: Invalid source 2 buffer".throw};
sourceA = sourceA.asUGenInput;
sourceB = sourceB.asUGenInput;
destination.isNil.if {"FluidAudioTransport: Invalid destination buffer".throw};
destination = destination.asUGenInput;
destination.isNil.if {"FluidAudioTransport: Invalid destination buffer".throw};
destination = destination.asUGenInput;
^FluidProxyUgen.kr(this.objectClassName++\Trigger,-1, sourceA, startFrameA, numFramesA, startChanA, numChansA, sourceB, startFrameA, numFramesA, startChanB, numChansB, destination, interpolation, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking);
@ -18,14 +18,14 @@ FluidBufAudioTransport : FluidBufProcessor {
*process { |server, sourceA, startFrameA = 0, numFramesA = -1, startChanA = 0, numChansA = -1, sourceB, startFrameB = 0, numFramesB = -1, startChanB = 0, numChansB = -1, destination, interpolation=0.0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
sourceA.isNil.if {"FluidAudioTransport: Invalid source 1 buffer".throw};
sourceB.isNil.if {"FluidAudioTransport: Invalid source 2 buffer".throw};
sourceA = sourceA.asUGenInput;
sourceB = sourceB.asUGenInput;
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
sourceA.isNil.if {"FluidAudioTransport: Invalid source 1 buffer".throw};
sourceB.isNil.if {"FluidAudioTransport: Invalid source 2 buffer".throw};
sourceA = sourceA.asUGenInput;
sourceB = sourceB.asUGenInput;
destination.isNil.if {"FluidAudioTransport: Invalid destination buffer".throw};
destination = destination.asUGenInput;
destination.isNil.if {"FluidAudioTransport: Invalid destination buffer".throw};
destination = destination.asUGenInput;
^this.new(
server, nil, [destination]
@ -34,16 +34,16 @@ FluidBufAudioTransport : FluidBufProcessor {
)
}
*processBlocking { |server, sourceA, startFrameA = 0, numFramesA = -1, startChanA = 0, numChansA = -1, sourceB, startFrameB = 0, numFramesB = -1, startChanB = 0, numChansB = -1, destination, interpolation=0.0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
*processBlocking { |server, sourceA, startFrameA = 0, numFramesA = -1, startChanA = 0, numChansA = -1, sourceB, startFrameB = 0, numFramesB = -1, startChanB = 0, numChansB = -1, destination, interpolation=0.0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
sourceA.isNil.if {"FluidAudioTransport: Invalid source 1 buffer".throw};
sourceB.isNil.if {"FluidAudioTransport: Invalid source 2 buffer".throw};
sourceA = sourceA.asUGenInput;
sourceB = sourceB.asUGenInput;
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
sourceA.isNil.if {"FluidAudioTransport: Invalid source 1 buffer".throw};
sourceB.isNil.if {"FluidAudioTransport: Invalid source 2 buffer".throw};
sourceA = sourceA.asUGenInput;
sourceB = sourceB.asUGenInput;
destination.isNil.if {"FluidAudioTransport: Invalid destination buffer".throw};
destination = destination.asUGenInput;
destination.isNil.if {"FluidAudioTransport: Invalid destination buffer".throw};
destination = destination.asUGenInput;
^this.new(
server, nil, [destination]

@ -1,49 +1,49 @@
FluidBufChroma : FluidBufProcessor {
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numChroma = 12, ref = 440, normalize = 0,minFreq = 0,maxFreq = -1, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 0|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numChroma = 12, ref = 440, normalize = 0,minFreq = 0,maxFreq = -1, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 0|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
features = features.asUGenInput;
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufChroma: Invalid source buffer".throw};
features.isNil.if {"FluidBufChroma: Invalid features buffer".throw};
source.isNil.if {"FluidBufChroma: Invalid source buffer".throw};
features.isNil.if {"FluidBufChroma: Invalid features buffer".throw};
^FluidProxyUgen.kr(\FluidBufChromaTrigger,-1, source, startFrame, numFrames, startChan, numChans, features, padding, numChroma, numChroma, ref, normalize, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking);
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numChroma = 12, ref = 440, normalize = 0,minFreq = 0,maxFreq = -1, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numChroma = 12, ref = 440, normalize = 0,minFreq = 0,maxFreq = -1, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
features = features.asUGenInput;
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufChroma: Invalid source buffer".throw};
features.isNil.if {"FluidBufChroma: Invalid features buffer".throw};
source.isNil.if {"FluidBufChroma: Invalid source buffer".throw};
features.isNil.if {"FluidBufChroma: Invalid features buffer".throw};
^this.new(
server, nil, [features]
).processList(
[source, startFrame, numFrames, startChan, numChans, features, padding, numChroma, numChroma, ref, normalize, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize, 0],freeWhenDone,action
);
);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numChroma = 12, ref = 440, normalize = 0,minFreq = 0,maxFreq = -1, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numChroma = 12, ref = 440, normalize = 0,minFreq = 0,maxFreq = -1, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
features = features.asUGenInput;
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufChroma: Invalid source buffer".throw};
features.isNil.if {"FluidBufChroma: Invalid features buffer".throw};
source.isNil.if {"FluidBufChroma: Invalid source buffer".throw};
features.isNil.if {"FluidBufChroma: Invalid features buffer".throw};
^this.new(
server, nil, [features]
).processList(
[source, startFrame, numFrames, startChan, numChans, features, padding, numChroma, numChroma, ref, normalize, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize, 1],freeWhenDone,action
);
);
}
}
FluidBufChromaTrigger : FluidProxyUgen {}

@ -1,39 +1,39 @@
FluidBufCompose : FluidBufProcessor {
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, trig = 1, blocking = 1|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, trig = 1, blocking = 1|
source = source.asUGenInput;
destination = destination.asUGenInput;
source = source.asUGenInput;
destination = destination.asUGenInput;
source.isNil.if {"FluidBufCompose: Invalid source buffer".throw};
destination.isNil.if {"FluidBufCompose: Invalid destination buffer".throw};
source.isNil.if {"FluidBufCompose: Invalid source buffer".throw};
destination.isNil.if {"FluidBufCompose: Invalid destination buffer".throw};
^FluidProxyUgen.kr(\FluidBufComposeTrigger,-1, source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, trig, blocking);
^FluidProxyUgen.kr(\FluidBufComposeTrigger,-1, source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, trig, blocking);
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, freeWhenDone = true, action|
source = source.asUGenInput;
destination = destination.asUGenInput;
source = source.asUGenInput;
destination = destination.asUGenInput;
source.isNil.if {"FluidBufCompose: Invalid source buffer".throw};
destination.isNil.if {"FluidBufCompose: Invalid destination buffer".throw};
source.isNil.if {"FluidBufCompose: Invalid source buffer".throw};
destination.isNil.if {"FluidBufCompose: Invalid destination buffer".throw};
^this.new( server, nil, [destination]).processList([source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, 1], freeWhenDone, action);//NB always blocking
^this.new( server, nil, [destination]).processList([source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, 1], freeWhenDone, action);//NB always blocking
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, freeWhenDone = true, action|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, freeWhenDone = true, action|
source = source.asUGenInput;
destination = destination.asUGenInput;
source = source.asUGenInput;
destination = destination.asUGenInput;
source.isNil.if {"FluidBufCompose: Invalid source buffer".throw};
destination.isNil.if {"FluidBufCompose: Invalid destination buffer".throw};
source.isNil.if {"FluidBufCompose: Invalid source buffer".throw};
destination.isNil.if {"FluidBufCompose: Invalid destination buffer".throw};
^this.new(
server, nil, [destination]
).processList([source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, 1], freeWhenDone, action);
^this.new(
server, nil, [destination]
).processList([source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, 1], freeWhenDone, action);
}
}
FluidBufComposeTrigger : FluidProxyUgen {}

@ -1,47 +1,47 @@
FluidBufFlatten : FluidBufProcessor {
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, axis = 1, trig = 1, blocking = 1|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, axis = 1, trig = 1, blocking = 1|
source = source.asUGenInput;
destination = destination.asUGenInput;
source = source.asUGenInput;
destination = destination.asUGenInput;
source.isNil.if {"FluidBufFlatten: Invalid source buffer".throw};
destination.isNil.if {"FluidBufFlatten: Invalid destination buffer".throw};
source.isNil.if {"FluidBufFlatten: Invalid source buffer".throw};
destination.isNil.if {"FluidBufFlatten: Invalid destination buffer".throw};
^FluidProxyUgen.kr(\FluidBufFlattenTrigger,-1, source, startFrame, numFrames, startChan, numChans, destination, axis, trig, blocking);
^FluidProxyUgen.kr(\FluidBufFlattenTrigger,-1, source, startFrame, numFrames, startChan, numChans, destination, axis, trig, blocking);
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, axis = 1, freeWhenDone = true, action|
source = source.asUGenInput;
destination = destination.asUGenInput;
source = source.asUGenInput;
destination = destination.asUGenInput;
source.isNil.if {"FluidBufFlatten: Invalid source buffer".throw};
destination.isNil.if {"FluidBufFlatten: Invalid destination buffer".throw};
source.isNil.if {"FluidBufFlatten: Invalid source buffer".throw};
destination.isNil.if {"FluidBufFlatten: Invalid destination buffer".throw};
^this.new(
server, nil, [destination],
^this.new(
server, nil, [destination],
).processList(
[source, startFrame, numFrames, startChan, numChans, destination, axis,0],freeWhenDone,action
);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, axis = 1, freeWhenDone = true, action|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, axis = 1, freeWhenDone = true, action|
source = source.asUGenInput;
destination = destination.asUGenInput;
source = source.asUGenInput;
destination = destination.asUGenInput;
source.isNil.if {"FluidBufFlatten: Invalid source buffer".throw};
destination.isNil.if {"FluidBufFlatten: Invalid destination buffer".throw};
source.isNil.if {"FluidBufFlatten: Invalid source buffer".throw};
destination.isNil.if {"FluidBufFlatten: Invalid destination buffer".throw};
^this.new(
server, nil, [destination],
).processList(
[source, startFrame, numFrames, startChan, numChans, destination, axis,1],freeWhenDone,action
);
^this.new(
server, nil, [destination],
).processList(
[source, startFrame, numFrames, startChan, numChans, destination, axis,1],freeWhenDone,action
);
}
}
}
FluidBufFlattenTrigger : FluidProxyUgen {}

@ -2,25 +2,25 @@ FluidBufHPSS : FluidBufProcessor {
*kr {|source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, harmonic = -1, percussive = -1, residual = -1, harmFilterSize = 17, percFilterSize = 31, maskingMode = 0, harmThreshFreq1 = 0.1, harmThreshAmp1 = 0, harmThreshFreq2 = 0.5, harmThreshAmp2 = 0, percThreshFreq1 = 0.1, percThreshAmp1 = 0, percThreshFreq2 = 0.5, percThreshAmp2 = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
harmonic = harmonic ? -1;
percussive = percussive ? -1;
residual = residual ? -1;
source.isNil.if {"FluidBufHPSS: Invalid source buffer".throw};
harmonic = harmonic ? -1;
percussive = percussive ? -1;
residual = residual ? -1;
source.isNil.if {"FluidBufHPSS: Invalid source buffer".throw};
^FluidProxyUgen.kr(\FluidBufHPSSTrigger, -1, source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, harmFilterSize, percFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking
);
^FluidProxyUgen.kr(\FluidBufHPSSTrigger, -1, source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, harmFilterSize, percFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking
);
}
*process {|server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, harmonic = -1, percussive = -1, residual = -1, harmFilterSize = 17, percFilterSize = 31, maskingMode = 0, harmThreshFreq1 = 0.1, harmThreshAmp1 = 0, harmThreshFreq2 = 0.5, harmThreshAmp2 = 0, percThreshFreq1 = 0.1, percThreshAmp1 = 0, percThreshFreq2 = 0.5, percThreshAmp2 = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone=true, action|
*process {|server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, harmonic = -1, percussive = -1, residual = -1, harmFilterSize = 17, percFilterSize = 31, maskingMode = 0, harmThreshFreq1 = 0.1, harmThreshAmp1 = 0, harmThreshFreq2 = 0.5, harmThreshAmp2 = 0, percThreshFreq1 = 0.1, percThreshAmp1 = 0, percThreshFreq2 = 0.5, percThreshAmp2 = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone=true, action|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
harmonic = harmonic ? -1;
percussive = percussive ? -1;
residual = residual ? -1;
source.isNil.if {"FluidBufHPSS: Invalid source buffer".throw};
harmonic = harmonic ? -1;
percussive = percussive ? -1;
residual = residual ? -1;
source.isNil.if {"FluidBufHPSS: Invalid source buffer".throw};
^this.new(
@ -31,22 +31,22 @@ FluidBufHPSS : FluidBufProcessor {
}
*processBlocking {|server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, harmonic = -1, percussive = -1, residual = -1, harmFilterSize = 17, percFilterSize = 31, maskingMode = 0, harmThreshFreq1 = 0.1, harmThreshAmp1 = 0, harmThreshFreq2 = 0.5, harmThreshAmp2 = 0, percThreshFreq1 = 0.1, percThreshAmp1 = 0, percThreshFreq2 = 0.5, percThreshAmp2 = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone=true, action|
*processBlocking {|server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, harmonic = -1, percussive = -1, residual = -1, harmFilterSize = 17, percFilterSize = 31, maskingMode = 0, harmThreshFreq1 = 0.1, harmThreshAmp1 = 0, harmThreshFreq2 = 0.5, harmThreshAmp2 = 0, percThreshFreq1 = 0.1, percThreshAmp1 = 0, percThreshFreq2 = 0.5, percThreshAmp2 = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone=true, action|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
harmonic = harmonic ? -1;
percussive = percussive ? -1;
residual = residual ? -1;
source.isNil.if {"FluidBufHPSS: Invalid source buffer".throw};
harmonic = harmonic ? -1;
percussive = percussive ? -1;
residual = residual ? -1;
source.isNil.if {"FluidBufHPSS: Invalid source buffer".throw};
^this.new(
server, nil, [harmonic, percussive, residual].select{|x| x!= -1}
).processList(
[source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, harmFilterSize, percFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize, maxFFTSize, 1], freeWhenDone,action
);
^this.new(
server, nil, [harmonic, percussive, residual].select{|x| x!= -1}
).processList(
[source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, harmFilterSize, percFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize, maxFFTSize, 1], freeWhenDone,action
);
}
}
}
FluidBufHPSSTrigger : FluidProxyUgen {}

@ -1,76 +1,76 @@
FluidBufLoudness : FluidBufProcessor{
const <features=#[\loudness, \peak];
classvar featuresLookup;
const <features=#[\loudness, \peak];
classvar featuresLookup;
*initClass {
featuresLookup = Dictionary.with(*this.features.collect{|x,i| x->(1<<i)});
}
*initClass {
featuresLookup = Dictionary.with(*this.features.collect{|x,i| x->(1<<i)});
}
*prWarnUnrecognised {|sym| ("WARNING: FluidLoudness -" + sym + "is not a recognised option").postln}
*prWarnUnrecognised {|sym| ("WARNING: FluidLoudness -" + sym + "is not a recognised option").postln}
*prProcessSelect {|a|
var bits;
a.asBag.countsDo{|item,count,i|
if(count > 1) { ("Option '" ++ item ++ "' is repeated").warn};
};
bits = a.collect{ |sym|
(featuresLookup[sym.asSymbol] !? {|x| x} ?? {this.prWarnUnrecognised(sym); 0})
}.reduce{|x,y| x | y};
^bits
}
*prProcessSelect {|a|
var bits;
a.asBag.countsDo{|item,count,i|
if(count > 1) { ("Option '" ++ item ++ "' is repeated").warn};
};
bits = a.collect{ |sym|
(featuresLookup[sym.asSymbol] !? {|x| x} ?? {this.prWarnUnrecognised(sym); 0})
}.reduce{|x,y| x | y};
^bits
}
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, padding = 1, trig = 1, blocking = 0|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, padding = 1, trig = 1, blocking = 0|
var maxwindowSize = windowSize.nextPowerOfTwo;
var maxwindowSize = windowSize.nextPowerOfTwo;
var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)};
var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)};
source = source.asUGenInput;
features = features.asUGenInput;
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufPitch: Invalid source buffer".throw};
features.isNil.if {"FluidBufPitch: Invalid features buffer".throw};
source.isNil.if {"%: Invalid source buffer".format(this.class.name).throw};
features.isNil.if {"%: Invalid features buffer".format(this.class.name).throw};
^FluidProxyUgen.kr(\FluidBufLoudnessTrigger, -1, source, startFrame, numFrames, startChan, numChans, features, padding, selectbits, kWeighting, truePeak, windowSize, hopSize, maxwindowSize, trig, blocking);
}
^FluidProxyUgen.kr(\FluidBufLoudnessTrigger, -1, source, startFrame, numFrames, startChan, numChans, features, padding, selectbits, kWeighting, truePeak, windowSize, hopSize, maxwindowSize, trig, blocking);
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, padding = 1, freeWhenDone = true, action|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, padding = 1, freeWhenDone = true, action|
var maxwindowSize = windowSize.nextPowerOfTwo;
var maxwindowSize = windowSize.nextPowerOfTwo;
var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)};
var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)};
source = source.asUGenInput;
features = features.asUGenInput;
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufPitch: Invalid source buffer".throw};
features.isNil.if {"FluidBufPitch: Invalid features buffer".throw};
source.isNil.if {"%: Invalid source buffer".format(this.class.name).throw};
features.isNil.if {"%: Invalid features buffer".format(this.class.name).throw};
^this.new(
server, nil, [features]
).processList(
[source, startFrame, numFrames, startChan, numChans, features, padding, selectbits, kWeighting, truePeak, windowSize, hopSize, maxwindowSize,0],freeWhenDone,action
);
}
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, padding = 1, freeWhenDone = true, action|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, padding = 1, freeWhenDone = true, action|
var maxwindowSize = windowSize.nextPowerOfTwo;
var maxwindowSize = windowSize.nextPowerOfTwo;
var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)};
var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)};
source = source.asUGenInput;
features = features.asUGenInput;
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufPitch: Invalid source buffer".throw};
features.isNil.if {"FluidBufPitch: Invalid features buffer".throw};
source.isNil.if {"%: Invalid source buffer".format(this.class.name).throw};
features.isNil.if {"%: Invalid features buffer".format(this.class.name).throw};
^this.new(
server, nil, [features]
).processList(
[source, startFrame, numFrames, startChan, numChans, features,padding, selectbits, kWeighting, truePeak, windowSize, hopSize, maxwindowSize,1],freeWhenDone,action
);
}
}
}
FluidBufLoudnessTrigger : FluidProxyUgen {}

@ -1,46 +1,46 @@
FluidBufMFCC : FluidBufProcessor{
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, startCoeff = 0, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 0|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, startCoeff = 0, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 0|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
features = features.asUGenInput;
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufMFCC: Invalid source buffer".throw};
features.isNil.if {"FluidBufMFCC: Invalid features buffer".throw};
source.isNil.if {"FluidBufMFCC: Invalid source buffer".throw};
features.isNil.if {"FluidBufMFCC: Invalid features buffer".throw};
^FluidProxyUgen.kr(\FluidBufMFCCTrigger, -1, source, startFrame, numFrames, startChan, numChans, features, padding, numCoeffs, numCoeffs, numBands, startCoeff, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize,trig, blocking);
}
^FluidProxyUgen.kr(\FluidBufMFCCTrigger, -1, source, startFrame, numFrames, startChan, numChans, features, padding, numCoeffs, numCoeffs, numBands, numBands, startCoeff, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize,trig, blocking);
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, startCoeff = 0, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone=true, action |
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, startCoeff = 0, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone=true, action |
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
features = features.asUGenInput;
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufMFCC: Invalid source buffer".throw};
features.isNil.if {"FluidBufMFCC: Invalid features buffer".throw};
source.isNil.if {"FluidBufMFCC: Invalid source buffer".throw};
features.isNil.if {"FluidBufMFCC: Invalid features buffer".throw};
^this.new(
server, nil,[features]
).processList(
[source, startFrame, numFrames, startChan, numChans, features, padding, numCoeffs, numCoeffs, numBands, startCoeff, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize,0],freeWhenDone,action
[source, startFrame, numFrames, startChan, numChans, features, padding, numCoeffs, numCoeffs, numBands, numBands, startCoeff, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize,0],freeWhenDone,action
);
}
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, startCoeff = 0, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone=true, action |
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, startCoeff = 0, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone=true, action |
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
features = features.asUGenInput;
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufMFCC: Invalid source buffer".throw};
features.isNil.if {"FluidBufMFCC: Invalid features buffer".throw};
source.isNil.if {"FluidBufMFCC: Invalid source buffer".throw};
features.isNil.if {"FluidBufMFCC: Invalid features buffer".throw};
^this.new(
server, nil,[features]
).processList(
[source, startFrame, numFrames, startChan, numChans, features, padding, numCoeffs, numCoeffs, numBands, startCoeff, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize,1],freeWhenDone,action
[source, startFrame, numFrames, startChan, numChans, features, padding, numCoeffs, numCoeffs, numBands, numBands, startCoeff, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize,1],freeWhenDone,action
);
}
}
}
FluidBufMFCCTrigger : FluidProxyUgen {}

@ -1,51 +1,51 @@
FluidBufMelBands : FluidBufProcessor {
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, scale = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 0|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, scale = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 0|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
features = features.asUGenInput;
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufMelBands: Invalid source buffer".throw};
features.isNil.if {"FluidBufMelBands: Invalid features buffer".throw};
source.isNil.if {"FluidBufMelBands: Invalid source buffer".throw};
features.isNil.if {"FluidBufMelBands: Invalid features buffer".throw};
^FluidProxyUgen.kr(\FluidBufMelBandsTrigger,-1, source, startFrame, numFrames, startChan, numChans, features, padding, numBands, numBands, minFreq, maxFreq, normalize, scale, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking);
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, scale = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, scale = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
features = features.asUGenInput;
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufMelBands: Invalid source buffer".throw};
features.isNil.if {"FluidBufMelBands: Invalid features buffer".throw};
source.isNil.if {"FluidBufMelBands: Invalid source buffer".throw};
features.isNil.if {"FluidBufMelBands: Invalid features buffer".throw};
^this.new(
server, nil, [features]
).processList(
[source, startFrame, numFrames, startChan, numChans, features, padding, numBands, numBands, minFreq, maxFreq, normalize, scale, windowSize, hopSize, fftSize, maxFFTSize, 0],freeWhenDone,action
);
);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, scale = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, scale = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
features = features.asUGenInput;
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufMelBands: Invalid source buffer".throw};
features.isNil.if {"FluidBufMelBands: Invalid features buffer".throw};
source.isNil.if {"FluidBufMelBands: Invalid source buffer".throw};
features.isNil.if {"FluidBufMelBands: Invalid features buffer".throw};
^this.new(
server, nil, [features]
).processList(
[source, startFrame, numFrames, startChan, numChans, features, padding, numBands, numBands, minFreq, maxFreq, normalize, scale, windowSize, hopSize, fftSize, maxFFTSize, 1],freeWhenDone,action
);
);
}
}
FluidBufMelBandsTrigger : FluidProxyUgen {}

@ -1,37 +1,37 @@
FluidBufNMF : FluidBufProcessor
{
*kr {|source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth, resynthMode = 0, bases, basesMode = 0, activations, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
*kr {|source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth, resynthMode = 0, bases, basesMode = 0, activations, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
source.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
resynth = resynth ? -1;
bases = bases ? -1;
activations = activations ? -1;
source.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
resynth = resynth ? -1;
bases = bases ? -1;
activations = activations ? -1;
^FluidProxyUgen.kr(\FluidBufNMFTrigger,-1,source.asUGenInput, startFrame, numFrames, startChan, numChans, resynth.asUGenInput, resynthMode, bases.asUGenInput, basesMode, activations.asUGenInput, actMode, components, iterations, windowSize, hopSize, fftSize, fftSize, trig, blocking);
}
^FluidProxyUgen.kr(\FluidBufNMFTrigger,-1,source.asUGenInput, startFrame, numFrames, startChan, numChans, resynth.asUGenInput, resynthMode, bases.asUGenInput, basesMode, activations.asUGenInput, actMode, components, iterations, windowSize, hopSize, fftSize, fftSize, trig, blocking);
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth = -1, resynthMode = 0, bases = -1, basesMode = 0, activations = -1, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1,freeWhenDone = true, action|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth = -1, resynthMode = 0, bases = -1, basesMode = 0, activations = -1, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1,freeWhenDone = true, action|
source.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
resynth = resynth ? -1;
bases = bases ? -1;
activations = activations ? -1;
source.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
resynth = resynth ? -1;
bases = bases ? -1;
activations = activations ? -1;
^this.new(
server,nil,[resynth, bases, activations].select{|x| x!= -1}
).processList([source, startFrame, numFrames, startChan, numChans, resynth, resynthMode, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize, fftSize, 0],freeWhenDone,action);
}
^this.new(
server,nil,[resynth, bases, activations].select{|x| x!= -1}
).processList([source, startFrame, numFrames, startChan, numChans, resynth, resynthMode, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize, fftSize, 0],freeWhenDone,action);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth = -1, resynthMode = 0, bases = -1, basesMode = 0, activations = -1, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1,freeWhenDone = true, action|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth = -1, resynthMode = 0, bases = -1, basesMode = 0, activations = -1, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1,freeWhenDone = true, action|
source.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
resynth = resynth ? -1;
bases = bases ? -1;
activations = activations ? -1;
source.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
resynth = resynth ? -1;
bases = bases ? -1;
activations = activations ? -1;
^this.new(
server,nil,[resynth, bases, activations].select{|x| x!= -1}
).processList([source, startFrame, numFrames, startChan, numChans, resynth, resynthMode, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize, fftSize, 1],freeWhenDone,action);
}
^this.new(
server,nil,[resynth, bases, activations].select{|x| x!= -1}
).processList([source, startFrame, numFrames, startChan, numChans, resynth, resynthMode, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize, fftSize, 1],freeWhenDone,action);
}
}
FluidBufNMFTrigger : FluidProxyUgen {}

@ -1,45 +1,45 @@
FluidBufNMFCross : FluidBufProcessor {
*kr { |source, target, output , timeSparsity = 7, polyphony = 10, continuity = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
*kr { |source, target, output , timeSparsity = 7, polyphony = 10, continuity = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
source = source.asUGenInput;
target = target.asUGenInput;
output = output.asUGenInput;
source.isNil.if {"FluidBufNMFCross: Invalid source buffer".throw};
target.isNil.if {"FluidBufNMFCross: Invalid target buffer".throw};
output.isNil.if {"FluidBufNMFCross: Invalid output buffer".throw};
source = source.asUGenInput;
target = target.asUGenInput;
output = output.asUGenInput;
source.isNil.if {"FluidBufNMFCross: Invalid source buffer".throw};
target.isNil.if {"FluidBufNMFCross: Invalid target buffer".throw};
output.isNil.if {"FluidBufNMFCross: Invalid output buffer".throw};
^FluidProxyUgen.kr(\FluidBufNMFCrossTrigger, -1, source, target, output, timeSparsity, polyphony, continuity, iterations, windowSize, hopSize, fftSize, fftSize, trig, blocking);
}
*process { |server, source, target, output , timeSparsity = 7, polyphony = 10, continuity = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
*process { |server, source, target, output , timeSparsity = 7, polyphony = 10, continuity = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
source = source.asUGenInput;
target = target.asUGenInput;
output = output.asUGenInput;
source.isNil.if {"FluidBufNMFCross: Invalid source buffer".throw};
target.isNil.if {"FluidBufNMFCross: Invalid target buffer".throw};
output.isNil.if {"FluidBufNMFCross: Invalid output buffer".throw};
source = source.asUGenInput;
target = target.asUGenInput;
output = output.asUGenInput;
source.isNil.if {"FluidBufNMFCross: Invalid source buffer".throw};
target.isNil.if {"FluidBufNMFCross: Invalid target buffer".throw};
output.isNil.if {"FluidBufNMFCross: Invalid output buffer".throw};
^this.new(
^this.new(
server, nil, [output]
).processList(
[source, target, output, timeSparsity, polyphony, continuity, iterations, windowSize, hopSize, fftSize, fftSize, 0],freeWhenDone, action
);
}
*processBlocking { |server, source, target, output , timeSparsity = 7, polyphony = 10, continuity = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
*processBlocking { |server, source, target, output , timeSparsity = 7, polyphony = 10, continuity = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
source = source.asUGenInput;
target = target.asUGenInput;
output = output.asUGenInput;
source.isNil.if {"FluidBufNMFCross: Invalid source buffer".throw};
target.isNil.if {"FluidBufNMFCross: Invalid target buffer".throw};
output.isNil.if {"FluidBufNMFCross: Invalid output buffer".throw};
source = source.asUGenInput;
target = target.asUGenInput;
output = output.asUGenInput;
source.isNil.if {"FluidBufNMFCross: Invalid source buffer".throw};
target.isNil.if {"FluidBufNMFCross: Invalid target buffer".throw};
output.isNil.if {"FluidBufNMFCross: Invalid output buffer".throw};
^this.new(
^this.new(
server, nil, [output]
).processList(
[source, target, output, timeSparsity, polyphony, continuity, iterations, windowSize, hopSize, fftSize, fftSize, 1],freeWhenDone, action

@ -2,12 +2,12 @@ FluidBufNMFSeed : FluidBufProcessor{
*kr { |source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
source.isNil.if {"FluidBufNMFSeed: Invalid source buffer".throw};
bases.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw};
activations.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw};
source = source.asUGenInput;
bases = bases.asUGenInput;
activations = activations.asUGenInput;
source.isNil.if {"FluidBufNMFSeed: Invalid source buffer".throw};
bases.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw};
activations.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw};
source = source.asUGenInput;
bases = bases.asUGenInput;
activations = activations.asUGenInput;
^FluidProxyUgen.kr1(\FluidBufNMFSeedTrigger, -1, source, bases, activations, minComponents, maxComponents, coverage, method, windowSize, hopSize, fftSize, fftSize, trig, blocking);
}
@ -15,12 +15,12 @@ FluidBufNMFSeed : FluidBufProcessor{
*process { |server, source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
source.isNil.if {"FluidBufNMFSeed: Invalid source buffer".throw};
bases.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw};
activations.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw};
source = source.asUGenInput;
bases = bases.asUGenInput;
activations = activations.asUGenInput;
source.isNil.if {"FluidBufNMFSeed: Invalid source buffer".throw};
bases.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw};
activations.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw};
source = source.asUGenInput;
bases = bases.asUGenInput;
activations = activations.asUGenInput;
^this.new(
server, nil, [bases,activations]
@ -29,14 +29,14 @@ FluidBufNMFSeed : FluidBufProcessor{
)
}
*processBlocking { |server, source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
*processBlocking { |server, source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
source.isNil.if {"FluidBufNMFSeed: Invalid source buffer".throw};
bases.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw};
activations.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw};
source = source.asUGenInput;
bases = bases.asUGenInput;
activations = activations.asUGenInput;
source.isNil.if {"FluidBufNMFSeed: Invalid source buffer".throw};
bases.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw};
activations.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw};
source = source.asUGenInput;
bases = bases.asUGenInput;
activations = activations.asUGenInput;
^this.new(
server, nil, [bases,activations]

@ -1,8 +1,8 @@
FluidBufNoveltyFeature : FluidBufProcessor {
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm = 0, kernelSize = 3, filterSize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 0|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm = 0, kernelSize = 3, filterSize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 0|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
features = features.asUGenInput;
@ -18,20 +18,20 @@ FluidBufNoveltyFeature : FluidBufProcessor {
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm= 0, kernelSize = 3, filterSize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action |
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm= 0, kernelSize = 3, filterSize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action |
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
features = features.asUGenInput;
source = source.asUGenInput;
features = features.asUGenInput;
algorithm = FluidNoveltySlice.prSelectAlgorithm(algorithm);
if (algorithm.isNil or: algorithm.isUGen) {
("FluidBufNoveltySlice: % is not a recognised algorithm")
.format(algorithm).throw;
};
source.isNil.if {"FluidBufNoveltyFeature: Invalid source buffer".throw};
features.isNil.if {"FluidBufNoveltyFeature: Invalid features buffer".throw};
source.isNil.if {"FluidBufNoveltyFeature: Invalid source buffer".throw};
features.isNil.if {"FluidBufNoveltyFeature: Invalid features buffer".throw};
^this.new(
server, nil, [features]
@ -40,15 +40,15 @@ FluidBufNoveltyFeature : FluidBufProcessor {
);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm= 0, kernelSize = 3, filterSize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action |
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm= 0, kernelSize = 3, filterSize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action |
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
features = features.asUGenInput;
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufNoveltyFeature: Invalid source buffer".throw};
features.isNil.if {"FluidBufNoveltyFeature: Invalid features buffer".throw};
source.isNil.if {"FluidBufNoveltyFeature: Invalid source buffer".throw};
features.isNil.if {"FluidBufNoveltyFeature: Invalid features buffer".throw};
algorithm = FluidNoveltySlice.prSelectAlgorithm(algorithm);
if (algorithm.isNil or: algorithm.isUGen) {
("FluidBufNoveltySlice: % is not a recognised algorithm")

@ -2,7 +2,7 @@ FluidBufNoveltySlice : FluidBufProcessor {
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, algorithm = 0, kernelSize = 3, threshold = 0.5, filterSize = 1, minSliceLength = 2, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1 , blocking = 0|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
indices = indices.asUGenInput;
@ -18,20 +18,20 @@ FluidBufNoveltySlice : FluidBufProcessor {
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, algorithm= 0, kernelSize = 3, threshold = 0.5, filterSize = 1, minSliceLength = 2, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action |
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, algorithm= 0, kernelSize = 3, threshold = 0.5, filterSize = 1, minSliceLength = 2, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action |
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
indices = indices.asUGenInput;
algorithm = FluidNoveltySlice.prSelectAlgorithm(algorithm);
if (algorithm.isNil or: algorithm.isUGen) {
("FluidBufNoveltySlice: % is not a recognised algorithm")
.format(algorithm).throw;
};
source = source.asUGenInput;
indices = indices.asUGenInput;
algorithm = FluidNoveltySlice.prSelectAlgorithm(algorithm);
if (algorithm.isNil or: algorithm.isUGen) {
("FluidBufNoveltySlice: % is not a recognised algorithm")
.format(algorithm).throw;
};
source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw};
source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw};
^this.new(
server, nil, [indices]
@ -40,20 +40,20 @@ FluidBufNoveltySlice : FluidBufProcessor {
);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, algorithm= 0, kernelSize = 3, threshold = 0.5, filterSize = 1, minSliceLength = 2, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action |
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, algorithm= 0, kernelSize = 3, threshold = 0.5, filterSize = 1, minSliceLength = 2, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action |
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
indices = indices.asUGenInput;
algorithm = FluidNoveltySlice.prSelectAlgorithm(algorithm);
if (algorithm.isNil or: algorithm.isUGen) {
("FluidBufNoveltySlice: % is not a recognised algorithm")
.format(algorithm).throw;
};
source = source.asUGenInput;
indices = indices.asUGenInput;
algorithm = FluidNoveltySlice.prSelectAlgorithm(algorithm);
if (algorithm.isNil or: algorithm.isUGen) {
("FluidBufNoveltySlice: % is not a recognised algorithm")
.format(algorithm).throw;
};
source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw};
source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw};
^this.new(
server, nil, [indices]

@ -1,35 +1,35 @@
FluidBufOnsetFeature : FluidBufProcessor {
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, metric = 0, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 0|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, metric = 0, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 0|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
features = features.asUGenInput;
metric = FluidOnsetSlice.prSelectMetric(metric) ?? {
("FluidBufOnsetSlice: % is not a recognised metric")
.format(metric).throw;
};
source = source.asUGenInput;
features = features.asUGenInput;
metric = FluidOnsetSlice.prSelectMetric(metric) ?? {
("FluidBufOnsetSlice: % is not a recognised metric")
.format(metric).throw;
};
source.isNil.if {"FluidBufOnsetFeature: Invalid source buffer".throw};
features.isNil.if {"FluidBufOnsetFeature: Invalid features buffer".throw};
source.isNil.if {"FluidBufOnsetFeature: Invalid source buffer".throw};
features.isNil.if {"FluidBufOnsetFeature: Invalid features buffer".throw};
^FluidProxyUgen.kr(\FluidBufOnsetFeatureTrigger, -1, source, startFrame, numFrames, startChan, numChans, features, padding, metric, filterSize, frameDelta, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking);
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, metric = 0, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, metric = 0, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
features = features.asUGenInput;
metric = FluidOnsetSlice.prSelectMetric(metric);
if (metric.isNil or: metric.isUGen) {
("FluidBufOnsetSlice: % is not a recognised metric")
.format(metric).throw;
};
source = source.asUGenInput;
features = features.asUGenInput;
metric = FluidOnsetSlice.prSelectMetric(metric);
if (metric.isNil or: metric.isUGen) {
("FluidBufOnsetSlice: % is not a recognised metric")
.format(metric).throw;
};
source.isNil.if {"FluidBufOnsetFeature: Invalid source buffer".throw};
features.isNil.if {"FluidBufOnsetFeature: Invalid features buffer".throw};
source.isNil.if {"FluidBufOnsetFeature: Invalid source buffer".throw};
features.isNil.if {"FluidBufOnsetFeature: Invalid features buffer".throw};
^this.new(
server, nil, [features]
@ -38,20 +38,20 @@ FluidBufOnsetFeature : FluidBufProcessor {
);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, metric = 0, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1,padding = 1, freeWhenDone = true, action|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, metric = 0, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1,padding = 1, freeWhenDone = true, action|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
features = features.asUGenInput;
metric = FluidOnsetSlice.prSelectMetric(metric);
if (metric.isNil or: metric.isUGen) {
("FluidBufOnsetSlice: % is not a recognised metric")
.format(metric).throw;
};
source = source.asUGenInput;
features = features.asUGenInput;
metric = FluidOnsetSlice.prSelectMetric(metric);
if (metric.isNil or: metric.isUGen) {
("FluidBufOnsetSlice: % is not a recognised metric")
.format(metric).throw;
};
source.isNil.if {"FluidBufOnsetFeature: Invalid source buffer".throw};
features.isNil.if {"FluidBufOnsetFeature: Invalid features buffer".throw};
source.isNil.if {"FluidBufOnsetFeature: Invalid source buffer".throw};
features.isNil.if {"FluidBufOnsetFeature: Invalid features buffer".throw};
^this.new(
server, nil, [features]

@ -1,36 +1,36 @@
FluidBufOnsetSlice : FluidBufProcessor {
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, metric = 0, threshold = 0.5, minSliceLength = 2, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, metric = 0, threshold = 0.5, minSliceLength = 2, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
indices = indices.asUGenInput;
metric = FluidOnsetSlice.prSelectMetric(metric) ?? {
("FluidBufOnsetSlice: % is not a recognised metric")
.format(metric).throw;
};
source = source.asUGenInput;
indices = indices.asUGenInput;
metric = FluidOnsetSlice.prSelectMetric(metric) ?? {
("FluidBufOnsetSlice: % is not a recognised metric")
.format(metric).throw;
};
source.isNil.if {"FluidBufOnsetSlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufOnsetSlice: Invalid features buffer".throw};
source.isNil.if {"FluidBufOnsetSlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufOnsetSlice: Invalid features buffer".throw};
^FluidProxyUgen.kr(\FluidBufOnsetSliceTrigger, -1, source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking);
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, metric = 0, threshold = 0.5, minSliceLength = 2, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, metric = 0, threshold = 0.5, minSliceLength = 2, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
indices = indices.asUGenInput;
metric = FluidOnsetSlice.prSelectMetric(metric);
if (metric.isNil or: metric.isUGen) {
("FluidBufOnsetSlice: % is not a recognised metric")
.format(metric).throw;
};
source = source.asUGenInput;
indices = indices.asUGenInput;
metric = FluidOnsetSlice.prSelectMetric(metric);
if (metric.isNil or: metric.isUGen) {
("FluidBufOnsetSlice: % is not a recognised metric")
.format(metric).throw;
};
source.isNil.if {"FluidBufOnsetSlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufOnsetSlice: Invalid features buffer".throw};
source.isNil.if {"FluidBufOnsetSlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufOnsetSlice: Invalid features buffer".throw};
^this.new(
server, nil, [indices]
@ -39,20 +39,20 @@ FluidBufOnsetSlice : FluidBufProcessor {
);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, metric = 0, threshold = 0.5, minSliceLength = 2, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, metric = 0, threshold = 0.5, minSliceLength = 2, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
indices = indices.asUGenInput;
metric = FluidOnsetSlice.prSelectMetric(metric);
if (metric.isNil or: metric.isUGen) {
("FluidBufOnsetSlice: % is not a recognised metric")
.format(metric).throw;
};
source = source.asUGenInput;
indices = indices.asUGenInput;
metric = FluidOnsetSlice.prSelectMetric(metric);
if (metric.isNil or: metric.isUGen) {
("FluidBufOnsetSlice: % is not a recognised metric")
.format(metric).throw;
};
source.isNil.if {"FluidBufOnsetSlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufOnsetSlice: Invalid features buffer".throw};
source.isNil.if {"FluidBufOnsetSlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufOnsetSlice: Invalid features buffer".throw};
^this.new(
server, nil, [indices]

@ -1,52 +1,52 @@
FluidBufPitch : FluidBufProcessor{
const <features=#[\pitch, \confidence];
classvar featuresLookup;
*initClass {
featuresLookup = Dictionary.with(*this.features.collect{|x,i| x->(1<<i)});
}
*prWarnUnrecognised {|sym| ("WARNING: FluidBufPitch -" + sym + "is not a recognised option").postln}
*prProcessSelect {|a|
var bits;
a.asBag.countsDo{|item,count,i|
if(count > 1) { ("Option '" ++ item ++ "' is repeated").warn};
};
bits = a.collect{ |sym|
(featuresLookup[sym.asSymbol] !? {|x| x} ?? {this.prWarnUnrecognised(sym); 0})
}.reduce{|x,y| x | y};
^bits
}
const <features=#[\pitch, \confidence];
classvar featuresLookup;
*initClass {
featuresLookup = Dictionary.with(*this.features.collect{|x,i| x->(1<<i)});
}
*prWarnUnrecognised {|sym| ("WARNING: FluidBufPitch -" + sym + "is not a recognised option").postln}
*prProcessSelect {|a|
var bits;
a.asBag.countsDo{|item,count,i|
if(count > 1) { ("Option '" ++ item ++ "' is repeated").warn};
};
bits = a.collect{ |sym|
(featuresLookup[sym.asSymbol] !? {|x| x} ?? {this.prWarnUnrecognised(sym); 0})
}.reduce{|x,y| x | y};
^bits
}
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 0|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)};
var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)};
source = source.asUGenInput;
features = features.asUGenInput;
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufPitch: Invalid source buffer".throw};
features.isNil.if {"FluidBufPitch: Invalid features buffer".throw};
source.isNil.if {"FluidBufPitch: Invalid source buffer".throw};
features.isNil.if {"FluidBufPitch: Invalid features buffer".throw};
^FluidProxyUgen.kr(\FluidBufPitchTrigger, -1, source, startFrame, numFrames, startChan, numChans, features, padding, selectbits, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking);
^FluidProxyUgen.kr(\FluidBufPitchTrigger, -1, source, startFrame, numFrames, startChan, numChans, features, padding, selectbits, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking);
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)};
var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)};
source = source.asUGenInput;
features = features.asUGenInput;
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufPitch: Invalid source buffer".throw};
features.isNil.if {"FluidBufPitch: Invalid features buffer".throw};
source.isNil.if {"FluidBufPitch: Invalid source buffer".throw};
features.isNil.if {"FluidBufPitch: Invalid features buffer".throw};
^this.new(
server, nil, [features]
@ -55,17 +55,17 @@ FluidBufPitch : FluidBufProcessor{
);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)};
var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)};
source = source.asUGenInput;
features = features.asUGenInput;
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufPitch: Invalid source buffer".throw};
features.isNil.if {"FluidBufPitch: Invalid features buffer".throw};
source.isNil.if {"FluidBufPitch: Invalid source buffer".throw};
features.isNil.if {"FluidBufPitch: Invalid features buffer".throw};
^this.new(
server, nil, [features]

@ -1,45 +1,38 @@
FluidBufSTFT : FluidBufProcessor {
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, magnitude, phase, resynth, inverse = 0,windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 1|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, magnitude, phase, resynth, inverse = 0,windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 1|
// source = source.asUGenInput;
source = source ? -1;
magnitude = magnitude ? -1;
phase = phase ? -1;
resynth = resynth ? - 1;
// source.isNil.if {"FluidBufScale: Invalid source buffer".throw};
source = source ? -1;
magnitude = magnitude ? -1;
phase = phase ? -1;
resynth = resynth ? - 1;
^FluidProxyUgen.kr(\FluidBufSTFTTrigger, -1, source, startFrame, numFrames, startChan, magnitude, phase, resynth, inverse, padding, windowSize, hopSize, fftSize, -1, trig, blocking);
^FluidProxyUgen.kr(\FluidBufSTFTTrigger, -1, source, startFrame, numFrames, startChan, magnitude, phase, resynth, inverse, padding, windowSize, hopSize, fftSize, -1, trig, blocking);
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, magnitude, phase, resynth, inverse = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action|
// source = source.asUGenInput;
// source.isNil.if {"FluidBufSTFT: Invalid source buffer".throw};
source = source ? -1;
magnitude = magnitude ? -1;
phase = phase ? -1;
resynth = resynth ? - 1;
source = source ? -1;
magnitude = magnitude ? -1;
phase = phase ? -1;
resynth = resynth ? - 1;
^this.new(
server, nil, [magnitude,phase,resynth].select{|b| b != -1}
^this.new(
server, nil, [magnitude,phase,resynth].select{|b| b != -1}
).processList(
[source, startFrame, numFrames, startChan, magnitude, phase, resynth, inverse, padding, windowSize, hopSize, fftSize, -1, 0], freeWhenDone, action
);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, magnitude, phase, resynth, inverse = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1,freeWhenDone = true, action|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, magnitude, phase, resynth, inverse = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1,freeWhenDone = true, action|
// source = source.asUGenInput;
source = source ? -1;
magnitude = magnitude ? -1;
phase = phase ? -1;
resynth = resynth ? - 1;
source = source ? -1;
magnitude = magnitude ? -1;
phase = phase ? -1;
resynth = resynth ? - 1;
^this.new(
server, nil, [magnitude,phase,resynth].select{|b| b != -1}
^this.new(
server, nil, [magnitude,phase,resynth].select{|b| b != -1}
).processList(
[source, startFrame, numFrames, startChan, magnitude, phase, resynth, inverse, padding, windowSize, hopSize, fftSize, -1, 1], freeWhenDone, action
);

@ -1,41 +1,41 @@
FluidBufScale : FluidBufProcessor {
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, inputLow = 0, inputHigh = 1, outputLow = 0, outputHigh = 1, clipping = 0, trig = 1, blocking = 1|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, inputLow = 0, inputHigh = 1, outputLow = 0, outputHigh = 1, clipping = 0, trig = 1, blocking = 1|
source = source.asUGenInput;
destination = destination.asUGenInput;
source = source.asUGenInput;
destination = destination.asUGenInput;
source.isNil.if {"FluidBufScale: Invalid source buffer".throw};
destination.isNil.if {"FluidBufScale: Invalid destination buffer".throw};
source.isNil.if {"FluidBufScale: Invalid source buffer".throw};
destination.isNil.if {"FluidBufScale: Invalid destination buffer".throw};
^FluidProxyUgen.kr(\FluidBufScaleTrigger, -1, source, startFrame, numFrames, startChan, numChans, destination, inputLow, inputHigh, outputLow, outputHigh, clipping, trig, blocking);
^FluidProxyUgen.kr(\FluidBufScaleTrigger, -1, source, startFrame, numFrames, startChan, numChans, destination, inputLow, inputHigh, outputLow, outputHigh, clipping, trig, blocking);
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, inputLow = 0, inputHigh = 1, outputLow = 0, outputHigh = 1, clipping = 0, freeWhenDone = true, action|
source = source.asUGenInput;
destination = destination.asUGenInput;
source = source.asUGenInput;
destination = destination.asUGenInput;
source.isNil.if {"FluidBufScale: Invalid source buffer".throw};
destination.isNil.if {"FluidBufScale: Invalid destination buffer".throw};
source.isNil.if {"FluidBufScale: Invalid source buffer".throw};
destination.isNil.if {"FluidBufScale: Invalid destination buffer".throw};
^this.new(
server, nil, [destination]
^this.new(
server, nil, [destination]
).processList(
[source, startFrame, numFrames, startChan, numChans, destination, inputLow, inputHigh, outputLow, outputHigh, clipping, 0], freeWhenDone, action
);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, inputLow = 0, inputHigh = 1, outputLow = 0, outputHigh = 1, clipping = 0, freeWhenDone = true, action|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, inputLow = 0, inputHigh = 1, outputLow = 0, outputHigh = 1, clipping = 0, freeWhenDone = true, action|
source = source.asUGenInput;
destination = destination.asUGenInput;
source = source.asUGenInput;
destination = destination.asUGenInput;
source.isNil.if {"FluidBufScale: Invalid source buffer".throw};
destination.isNil.if {"FluidBufScale: Invalid destination buffer".throw};
source.isNil.if {"FluidBufScale: Invalid source buffer".throw};
destination.isNil.if {"FluidBufScale: Invalid destination buffer".throw};
^this.new(
server, nil, [destination]
^this.new(
server, nil, [destination]
).processList(
[source, startFrame, numFrames, startChan, numChans, destination, inputLow, inputHigh, outputLow, outputHigh, clipping, 1], freeWhenDone, action
);

@ -1,62 +1,62 @@
FluidBufSelect : FluidBufProcessor {
*kr { |source, destination, indices=#[-1], channels=#[-1], trig = 1, blocking = 1|
*kr { |source, destination, indices=#[-1], channels=#[-1], trig = 1, blocking = 1|
var params;
var params;
source = source.asUGenInput;
destination = destination.asUGenInput;
source = source.asUGenInput;
destination = destination.asUGenInput;
indices = indices.asArray;
channels = channels.asArray;
indices = indices.asArray;
channels = channels.asArray;
indices = [indices.size] ++ indices;
channels = [channels.size] ++ channels;
indices = [indices.size] ++ indices;
channels = [channels.size] ++ channels;
source.isNil.if {"FluidBufSelect: Invalid source buffer".throw};
destination.isNil.if {"FluidBufSelect: Invalid destination buffer".throw};
source.isNil.if {"FluidBufSelect: Invalid source buffer".throw};
destination.isNil.if {"FluidBufSelect: Invalid destination buffer".throw};
params = indices ++ channels ++ [trig, blocking]
params = indices ++ channels ++ [trig, blocking]
^FluidProxyUgen.kr(\FluidBufSelectTrigger,-1, source, destination, *params);
^FluidProxyUgen.kr(\FluidBufSelectTrigger,-1, source, destination, *params);
}
*process { |server, source, destination, indices=#[-1], channels=#[-1], freeWhenDone = true, action|
source = source.asUGenInput;
destination = destination.asUGenInput;
source = source.asUGenInput;
destination = destination.asUGenInput;
source.isNil.if {"FluidBufSelect: Invalid source buffer".throw};
destination.isNil.if {"FluidBufSelect: Invalid destination buffer".throw};
source.isNil.if {"FluidBufSelect: Invalid source buffer".throw};
destination.isNil.if {"FluidBufSelect: Invalid destination buffer".throw};
indices = indices.asArray;
channels = channels.asArray;
indices = indices.asArray;
channels = channels.asArray;
indices = [indices.size] ++ indices;
channels = [channels.size] ++ channels;
channels = [channels.size] ++ channels;
^this.new(server, nil, [destination]).processList([source, destination]++ indices ++ channels ++ [1], freeWhenDone, action);//NB always blocking
^this.new(server, nil, [destination]).processList([source, destination]++ indices ++ channels ++ [1], freeWhenDone, action);//NB always blocking
}
*processBlocking { |server, source, destination, indices=#[-1], channels=#[-1], freeWhenDone = true, action|
*processBlocking { |server, source, destination, indices=#[-1], channels=#[-1], freeWhenDone = true, action|
source = source.asUGenInput;
destination = destination.asUGenInput;
source = source.asUGenInput;
destination = destination.asUGenInput;
source.isNil.if {"FluidBufSelect: Invalid source buffer".throw};
destination.isNil.if {"FluidBufSelect: Invalid destination buffer".throw};
source.isNil.if {"FluidBufSelect: Invalid source buffer".throw};
destination.isNil.if {"FluidBufSelect: Invalid destination buffer".throw};
indices = indices.asArray;
channels = channels.asArray;
indices = indices.asArray;
channels = channels.asArray;
indices = [indices.size] ++ indices;
channels = [channels.size] ++ channels;
indices = [indices.size] ++ indices;
channels = [channels.size] ++ channels;
^this.new(
server, nil, [destination]
).processList([source, destination]++ indices ++ channels ++ [1], freeWhenDone, action);//NB always blocking
^this.new(
server, nil, [destination]
).processList([source, destination]++ indices ++ channels ++ [1], freeWhenDone, action);//NB always blocking
}
}
FluidBufSelectTrigger : FluidProxyUgen {}

@ -1,41 +1,41 @@
FluidBufSelectEvery : FluidBufProcessor {
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, frameHop = 1, chanHop = 1, trig = 1, blocking = 1|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, frameHop = 1, chanHop = 1, trig = 1, blocking = 1|
source = source.asUGenInput;
destination = destination.asUGenInput;
source = source.asUGenInput;
destination = destination.asUGenInput;
source.isNil.if {"FluidBufSelectEvery: Invalid source buffer".throw};
destination.isNil.if {"FluidBufSelectEvery: Invalid destination buffer".throw};
source.isNil.if {"FluidBufSelectEvery: Invalid source buffer".throw};
destination.isNil.if {"FluidBufSelectEvery: Invalid destination buffer".throw};
^FluidProxyUgen.kr(\FluidBufSelectEveryTrigger, -1, source, startFrame, numFrames, startChan, numChans, destination, frameHop, chanHop, trig, blocking);
^FluidProxyUgen.kr(\FluidBufSelectEveryTrigger, -1, source, startFrame, numFrames, startChan, numChans, destination, frameHop, chanHop, trig, blocking);
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, frameHop = 1, chanHop = 1, freeWhenDone = true, action|
source = source.asUGenInput;
destination = destination.asUGenInput;
source = source.asUGenInput;
destination = destination.asUGenInput;
source.isNil.if {"FluidBufSelectEvery: Invalid source buffer".throw};
destination.isNil.if {"FluidBufSelectEvery: Invalid destination buffer".throw};
source.isNil.if {"FluidBufSelectEvery: Invalid source buffer".throw};
destination.isNil.if {"FluidBufSelectEvery: Invalid destination buffer".throw};
^this.new(
server, nil, [destination]
^this.new(
server, nil, [destination]
).processList(
[source, startFrame, numFrames, startChan, numChans, destination, frameHop, chanHop, 0], freeWhenDone, action
);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, frameHop = 1, chanHop = 1, freeWhenDone = true, action|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, frameHop = 1, chanHop = 1, freeWhenDone = true, action|
source = source.asUGenInput;
destination = destination.asUGenInput;
source = source.asUGenInput;
destination = destination.asUGenInput;
source.isNil.if {"FluidBufSelectEvery: Invalid source buffer".throw};
destination.isNil.if {"FluidBufSelectEvery: Invalid destination buffer".throw};
source.isNil.if {"FluidBufSelectEvery: Invalid source buffer".throw};
destination.isNil.if {"FluidBufSelectEvery: Invalid destination buffer".throw};
^this.new(
server, nil, [destination]
^this.new(
server, nil, [destination]
).processList(
[source, startFrame, numFrames, startChan, numChans, destination, frameHop, chanHop, 1], freeWhenDone, action
);

@ -1,27 +1,27 @@
FluidBufSines : FluidBufProcessor {
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
sines = sines !? {sines.asUGenInput} ?? {-1};
residual = residual !? {residual.asUGenInput} ?? {-1};
source = source.asUGenInput;
sines = sines !? {sines.asUGenInput} ?? {-1};
residual = residual !? {residual.asUGenInput} ?? {-1};
source.isNil.if {"FluidBufSines: Invalid source buffer".throw};
source.isNil.if {"FluidBufSines: Invalid source buffer".throw};
^FluidProxyUgen.multiNew(\FluidBufSinesTrigger, -1, source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, detectionThreshold,birthLowThreshold, birthHighThreshold, minTrackLen, trackingMethod, trackMagRange, trackFreqRange, trackProb, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking);
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
sines = sines !? {sines.asUGenInput} ?? {-1};
residual = residual !? {residual.asUGenInput} ?? {-1};
source = source.asUGenInput;
sines = sines !? {sines.asUGenInput} ?? {-1};
residual = residual !? {residual.asUGenInput} ?? {-1};
source.isNil.if {"FluidBufSines: Invalid source buffer".throw};
source.isNil.if {"FluidBufSines: Invalid source buffer".throw};
^this.new(
server, nil, [sines, residual].select{|x| x!= -1}
@ -30,15 +30,15 @@ FluidBufSines : FluidBufProcessor {
);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
sines = sines !? {sines.asUGenInput} ?? {-1};
residual = residual !? {residual.asUGenInput} ?? {-1};
source = source.asUGenInput;
sines = sines !? {sines.asUGenInput} ?? {-1};
residual = residual !? {residual.asUGenInput} ?? {-1};
source.isNil.if {"FluidBufSines: Invalid source buffer".throw};
source.isNil.if {"FluidBufSines: Invalid source buffer".throw};
^this.new(
server, nil, [sines, residual].select{|x| x!= -1}

@ -1,9 +1,9 @@
FluidBufSpectralShape : FluidBufProcessor {
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, minFreq = 0, maxFreq = -1, rolloffPercent = 95, unit = 0, power = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 0|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, minFreq = 0, maxFreq = -1, rolloffPercent = 95, unit = 0, power = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 0|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var selectbits = select !? {FluidSpectralShape.prProcessSelect(select)} ?? {FluidSpectralShape.prProcessSelect(FluidSpectralShape.features)};
var selectbits = select !? {FluidSpectralShape.prProcessSelect(select)} ?? {FluidSpectralShape.prProcessSelect(FluidSpectralShape.features)};
source = source.asUGenInput;
features = features.asUGenInput;
@ -16,10 +16,10 @@ FluidBufSpectralShape : FluidBufProcessor {
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, minFreq = 0, maxFreq = -1, rolloffPercent = 95, unit = 0, power = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, minFreq = 0, maxFreq = -1, rolloffPercent = 95, unit = 0, power = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var selectbits = select !? {FluidSpectralShape.prProcessSelect(select)} ?? {FluidSpectralShape.prProcessSelect(FluidSpectralShape.features)};
var selectbits = select !? {FluidSpectralShape.prProcessSelect(select)} ?? {FluidSpectralShape.prProcessSelect(FluidSpectralShape.features)};
source = source.asUGenInput;
features = features.asUGenInput;
@ -30,14 +30,14 @@ FluidBufSpectralShape : FluidBufProcessor {
^this.new(
server, nil, [features]
).processList(
[source, startFrame, numFrames, startChan, numChans, features, padding, selectbits, minFreq, maxFreq, rolloffPercent, unit, power, windowSize, hopSize, fftSize, maxFFTSize, 0], freeWhenDone, action
[source, startFrame, numFrames, startChan, numChans, features, padding, selectbits, minFreq, maxFreq, rolloffPercent, unit, power, windowSize, hopSize, fftSize, maxFFTSize, 0], freeWhenDone, action
);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, minFreq = 0, maxFreq = -1, rolloffPercent = 95, unit = 0, power = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, select, minFreq = 0, maxFreq = -1, rolloffPercent = 95, unit = 0, power = 0, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
var selectbits = select !? {FluidSpectralShape.prProcessSelect(select)} ?? {FluidSpectralShape.prProcessSelect(FluidSpectralShape.features)};
var selectbits = select !? {FluidSpectralShape.prProcessSelect(select)} ?? {FluidSpectralShape.prProcessSelect(FluidSpectralShape.features)};
source = source.asUGenInput;
features = features.asUGenInput;
@ -48,7 +48,7 @@ FluidBufSpectralShape : FluidBufProcessor {
^this.new(
server, nil, [features]
).processList(
[source, startFrame, numFrames, startChan, numChans, features, padding, selectbits, minFreq, maxFreq, rolloffPercent, unit, power, windowSize, hopSize, fftSize, maxFFTSize, 1], freeWhenDone, action
[source, startFrame, numFrames, startChan, numChans, features, padding, selectbits, minFreq, maxFreq, rolloffPercent, unit, power, windowSize, hopSize, fftSize, maxFFTSize, 1], freeWhenDone, action
);
}
}

@ -1,49 +1,49 @@
FluidBufStats : FluidBufProcessor {
const <stats=#[\mean,\std,\skewness,\kurtosis,\low,\mid,\high];
classvar statslookup;
*prWarnUnrecognised {|sym| ("WARNING: FluidBufStats -" + sym + "is not a recognised option").postln}
*prProcessSelect {|a|
var bits;
a.asBag.countsDo{|item,count,i|
if(count > 1) { ("Option '" ++ item ++ "' is repeated").warn};
};
bits = a.collect{ |sym|
(statslookup[sym.asSymbol] !? {|x| x} ?? {this.prWarnUnrecognised(sym); 0})
}.reduce{|x,y| x | y};
^bits
}
*initClass {
statslookup = Dictionary.with(*this.stats.collect{|x,i| x->(1<<i)});
}
const <stats=#[\mean,\std,\skewness,\kurtosis,\low,\mid,\high];
classvar statslookup;
*prWarnUnrecognised {|sym| ("WARNING: FluidBufStats -" + sym + "is not a recognised option").postln}
*prProcessSelect {|a|
var bits;
a.asBag.countsDo{|item,count,i|
if(count > 1) { ("Option '" ++ item ++ "' is repeated").warn};
};
bits = a.collect{ |sym|
(statslookup[sym.asSymbol] !? {|x| x} ?? {this.prWarnUnrecognised(sym); 0})
}.reduce{|x,y| x | y};
^bits
}
*initClass {
statslookup = Dictionary.with(*this.stats.collect{|x,i| x->(1<<i)});
}
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, select, numDerivs = 0, low = 0, middle = 50, high = 100, outliersCutoff = -1, weights, trig = 1, blocking = 0|
var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.stats)};
source = source.asUGenInput;
stats = stats.asUGenInput;
weights = weights.asUGenInput;
var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.stats)};
source = source.asUGenInput;
stats = stats.asUGenInput;
weights = weights.asUGenInput;
source.isNil.if {"FluidBufStats: Invalid source buffer".throw};
stats.isNil.if {"FluidBufStats: Invalid stats buffer".throw};
weights = weights ? -1;
source.isNil.if {"FluidBufStats: Invalid source buffer".throw};
stats.isNil.if {"FluidBufStats: Invalid stats buffer".throw};
weights = weights ? -1;
^FluidProxyUgen.kr(\FluidBufStatsTrigger, -1, source, startFrame, numFrames, startChan, numChans, stats, selectbits, numDerivs, low, middle, high, outliersCutoff, weights, trig, blocking);
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, select, numDerivs = 0, low = 0, middle = 50, high = 100, outliersCutoff = -1, weights, freeWhenDone = true, action|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, select, numDerivs = 0, low = 0, middle = 50, high = 100, outliersCutoff = -1, weights, freeWhenDone = true, action|
var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.stats)};
source = source.asUGenInput;
stats = stats.asUGenInput;
weights = weights.asUGenInput;
var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.stats)};
source = source.asUGenInput;
stats = stats.asUGenInput;
weights = weights.asUGenInput;
source.isNil.if {"FluidBufStats: Invalid source buffer".throw};
stats.isNil.if {"FluidBufStats: Invalid stats buffer".throw};
weights = weights ? -1;
source.isNil.if {"FluidBufStats: Invalid source buffer".throw};
stats.isNil.if {"FluidBufStats: Invalid stats buffer".throw};
weights = weights ? -1;
^this.new(
server, nil, [stats]
@ -52,15 +52,15 @@ FluidBufStats : FluidBufProcessor {
);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, select numDerivs = 0, low = 0, middle = 50, high = 100, outliersCutoff = -1, weights, freeWhenDone = true, action|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, select numDerivs = 0, low = 0, middle = 50, high = 100, outliersCutoff = -1, weights, freeWhenDone = true, action|
var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.stats)};
source = source.asUGenInput;
stats = stats.asUGenInput;
weights = weights.asUGenInput;
source.isNil.if {"FluidBufStats: Invalid source buffer".throw};
stats.isNil.if {"FluidBufStats: Invalid stats buffer".throw};
weights = weights ? -1;
var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.stats)};
source = source.asUGenInput;
stats = stats.asUGenInput;
weights = weights.asUGenInput;
source.isNil.if {"FluidBufStats: Invalid source buffer".throw};
stats.isNil.if {"FluidBufStats: Invalid stats buffer".throw};
weights = weights ? -1;
^this.new(
server, nil, [stats]

@ -2,33 +2,33 @@ FluidBufThreadDemo : FluidBufProcessor{
*kr {|result, time, trig = 1, blocking = 0|
result = result.asUGenInput;
result.isNil.if {this.class.name+": Invalid output buffer".throw};
result = result.asUGenInput;
result.isNil.if {this.class.name+": Invalid output buffer".throw};
^FluidProxyUgen.kr(\FluidBufThreadDemoTrigger, -1, result, time, trig, blocking);
}
^FluidProxyUgen.kr(\FluidBufThreadDemoTrigger, -1, result, time, trig, blocking);
}
*process { |server, result, time = 1000, freeWhenDone = true, action|
*process { |server, result, time = 1000, freeWhenDone = true, action|
result ?? {this.class.name+": Invalid output buffer".throw};
result ?? {this.class.name+": Invalid output buffer".throw};
^this.new(
server, nil, [result]
).processList(
[result.asUGenInput, time, 0], freeWhenDone, action
);
}
}
*processBlocking { |server, result, time = 1000, freeWhenDone = true, action|
*processBlocking { |server, result, time = 1000, freeWhenDone = true, action|
result ?? {this.class.name+": Invalid output buffer".throw};
result ?? {this.class.name+": Invalid output buffer".throw};
^this.new(
server, nil, [result]
).processList(
[result.asUGenInput, time, 1], freeWhenDone, action
);
}
}
}
FluidBufThreadDemoTrigger : FluidProxyUgen {}

@ -1,42 +1,42 @@
FluidBufThresh : FluidBufProcessor {
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, threshold = 0, trig = 1, blocking = 1|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, threshold = 0, trig = 1, blocking = 1|
source = source.asUGenInput;
destination = destination.asUGenInput;
source = source.asUGenInput;
destination = destination.asUGenInput;
source.isNil.if {"FluidBufThresh: Invalid source buffer".throw};
destination.isNil.if {"FluidBufThresh: Invalid destination buffer".throw};
source.isNil.if {"FluidBufThresh: Invalid source buffer".throw};
destination.isNil.if {"FluidBufThresh: Invalid destination buffer".throw};
^FluidProxyUgen.kr(\FluidBufThreshTrigger, -1, source, startFrame, numFrames, startChan, numChans, destination, threshold, trig, blocking);
^FluidProxyUgen.kr(\FluidBufThreshTrigger, -1, source, startFrame, numFrames, startChan, numChans, destination, threshold, trig, blocking);
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, threshold = 0, freeWhenDone = true, action|
source = source.asUGenInput;
destination = destination.asUGenInput;
source = source.asUGenInput;
destination = destination.asUGenInput;
source.isNil.if {"FluidBufThresh: Invalid source buffer".throw};
destination.isNil.if {"FluidBufThresh: Invalid destination buffer".throw};
source.isNil.if {"FluidBufThresh: Invalid source buffer".throw};
destination.isNil.if {"FluidBufThresh: Invalid destination buffer".throw};
^this.new(
server, nil, [destination],
^this.new(
server, nil, [destination],
).processList(
[source, startFrame, numFrames, startChan, numChans, destination, threshold, 0], freeWhenDone, action
);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, threshold = 0, freeWhenDone = true, action|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, threshold = 0, freeWhenDone = true, action|
source = source.asUGenInput;
destination = destination.asUGenInput;
source = source.asUGenInput;
destination = destination.asUGenInput;
source.isNil.if {"FluidBufThresh: Invalid source buffer".throw};
destination.isNil.if {"FluidBufThresh: Invalid destination buffer".throw};
source.isNil.if {"FluidBufThresh: Invalid source buffer".throw};
destination.isNil.if {"FluidBufThresh: Invalid destination buffer".throw};
^this.new(
server, nil, [destination],
^this.new(
server, nil, [destination],
).processList(
[source, startFrame, numFrames, startChan, numChans, destination, threshold, 1], freeWhenDone, action
);

@ -2,41 +2,41 @@ FluidBufTransientSlice : FluidBufProcessor {
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, minSliceLength = 1000, trig = 1, blocking = 0|
source = source.asUGenInput;
indices = indices.asUGenInput;
source = source.asUGenInput;
indices = indices.asUGenInput;
source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw};
source.isNil.if {"%: Invalid source buffer".format(this.class.name).throw};
indices.isNil.if {"%: Invalid features buffer".format(this.class.name).throw};
^FluidProxyUgen.kr(this.objectClassName++\Trigger, -1, source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength, trig, blocking);
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, minSliceLength = 1000, freeWhenDone = true, action|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, minSliceLength = 1000, freeWhenDone = true, action|
source = source.asUGenInput;
indices = indices.asUGenInput;
source = source.asUGenInput;
indices = indices.asUGenInput;
source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw};
source.isNil.if {"%: Invalid source buffer".format(this.class.name).throw};
indices.isNil.if {"%: Invalid features buffer".format(this.class.name).throw};
^this.new(
^this.new(
server, nil,[indices]
).processList([source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength,0], freeWhenDone, action
);
}
).processList([source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength,0], freeWhenDone, action
);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, minSliceLength = 1000, freeWhenDone = true, action|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, minSliceLength = 1000, freeWhenDone = true, action|
source = source.asUGenInput;
indices = indices.asUGenInput;
source = source.asUGenInput;
indices = indices.asUGenInput;
source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw};
source.isNil.if {"%: Invalid source buffer".format(this.class.name).throw};
indices.isNil.if {"%: Invalid features buffer".format(this.class.name).throw};
^this.new(
^this.new(
server, nil,[indices]
).processList([source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength,1], freeWhenDone, action
);
}
).processList([source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength,1], freeWhenDone, action
);
}
}
FluidBufTransientSliceTrigger : FluidProxyUgen {}

@ -1,23 +1,23 @@
FluidBufTransients : FluidBufProcessor {
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, transients = -1, residual = -1, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, trig = 1, blocking = 0|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, transients = -1, residual = -1, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, trig = 1, blocking = 0|
source = source.asUGenInput;
transients = transients ? -1;
residual = residual ? -1;
source = source.asUGenInput;
transients = transients ? -1;
residual = residual ? -1;
source.isNil.if {"FluidBufTransients: Invalid source buffer".throw};
source.isNil.if {"FluidBufTransients: Invalid source buffer".throw};
^FluidProxyUgen.kr(\FluidBufTransientsTrigger, -1, source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, trig, blocking);
^FluidProxyUgen.kr(\FluidBufTransientsTrigger, -1, source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, trig, blocking);
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, transients = -1, residual = -1, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, freeWhenDone = true, action|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, transients = -1, residual = -1, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, freeWhenDone = true, action|
source = source.asUGenInput;
transients = transients ? -1;
residual = residual ? -1;
source = source.asUGenInput;
transients = transients ? -1;
residual = residual ? -1;
source.isNil.if {"FluidBufTransients: Invalid source buffer".throw};
source.isNil.if {"FluidBufTransients: Invalid source buffer".throw};
^this.new(
server, nil,[transients, residual].select{|x| x!= -1}
@ -26,19 +26,19 @@ FluidBufTransients : FluidBufProcessor {
);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, transients = -1, residual = -1, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, freeWhenDone = true, action|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, transients = -1, residual = -1, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, freeWhenDone = true, action|
source = source.asUGenInput;
transients = transients ? -1;
residual = residual ? -1;
source = source.asUGenInput;
transients = transients ? -1;
residual = residual ? -1;
source.isNil.if {"FluidBufTransients: Invalid source buffer".throw};
source.isNil.if {"FluidBufTransients: Invalid source buffer".throw};
^this.new(
server, nil,[transients, residual].select{|x| x!= -1}
).processList(
[source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength,1],freeWhenDone = true,action
);
}
^this.new(
server, nil,[transients, residual].select{|x| x!= -1}
).processList(
[source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength,1],freeWhenDone = true,action
);
}
}
FluidBufTransientsTrigger : FluidProxyUgen {}

@ -2,7 +2,7 @@ FluidChroma : FluidRTMultiOutUGen {
*kr { arg in = 0, numChroma = 12, ref = 440, normalize = 0, minFreq = 0, maxFreq = -1, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = -1, maxNumChroma;
maxNumChroma = maxNumChroma ? numChroma;
maxNumChroma = maxNumChroma ? numChroma;
^this.multiNew('control', in.asAudioRateInput(this), numChroma, maxNumChroma, ref, normalize, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize);
}

@ -1,72 +1,72 @@
FluidDataSet : FluidDataObject
{
*new{|server| ^super.new(server) }
*new{|server| ^super.new(server) }
addPointMsg{|identifier,buffer|
buffer = this.prEncodeBuffer(buffer);
^this.prMakeMsg(\addPoint,id,identifier.asSymbol,buffer);
}
addPointMsg{|identifier,buffer|
buffer = this.prEncodeBuffer(buffer);
^this.prMakeMsg(\addPoint,id,identifier.asSymbol,buffer);
}
addPoint{|identifier, buffer, action|
actions[\addPoint] = [nil,action];
this.prSendMsg(this.addPointMsg(identifier,buffer));
actions[\addPoint] = [nil,action];
this.prSendMsg(this.addPointMsg(identifier,buffer));
}
getPointMsg{|identifier,buffer|
buffer = this.prEncodeBuffer(buffer);
^this.prMakeMsg(\getPoint,id,identifier.asSymbol,buffer,["/b_query",buffer.asUGenInput]);
}
getPointMsg{|identifier,buffer|
buffer = this.prEncodeBuffer(buffer);
^this.prMakeMsg(\getPoint,id,identifier.asSymbol,buffer,["/b_query",buffer.asUGenInput]);
}
getPoint{|identifier, buffer, action|
actions[\getPoint] = [nil,action];
this.prSendMsg(this.getPointMsg(identifier,buffer));
}
getPoint{|identifier, buffer, action|
actions[\getPoint] = [nil,action];
this.prSendMsg(this.getPointMsg(identifier,buffer));
}
updatePointMsg{|identifier,buffer|
buffer = this.prEncodeBuffer(buffer);
^this.prMakeMsg(\updatePoint,id,identifier.asSymbol,buffer,["/b_query",buffer.asUGenInput]);
}
updatePointMsg{|identifier,buffer|
buffer = this.prEncodeBuffer(buffer);
^this.prMakeMsg(\updatePoint,id,identifier.asSymbol,buffer,["/b_query",buffer.asUGenInput]);
}
updatePoint{|identifier, buffer, action|
actions[\updatePoint] = [nil,action];
this.prSendMsg(this.updatePointMsg(identifier,buffer));
}
updatePoint{|identifier, buffer, action|
actions[\updatePoint] = [nil,action];
this.prSendMsg(this.updatePointMsg(identifier,buffer));
}
deletePointMsg{|identifier| ^this.prMakeMsg(\deletePoint,id,identifier.asSymbol);}
deletePointMsg{|identifier| ^this.prMakeMsg(\deletePoint,id,identifier.asSymbol);}
deletePoint{|identifier, action|
actions[\deletePoint] = [nil,action];
this.prSendMsg(this.deletePointMsg(identifier));
}
deletePoint{|identifier, action|
actions[\deletePoint] = [nil,action];
this.prSendMsg(this.deletePointMsg(identifier));
}
setPointMsg{|identifier,buffer|
buffer = this.prEncodeBuffer(buffer);
^this.prMakeMsg(\setPoint,id,identifier.asSymbol,buffer,["/b_query",buffer.asUGenInput]);
}
setPointMsg{|identifier,buffer|
buffer = this.prEncodeBuffer(buffer);
^this.prMakeMsg(\setPoint,id,identifier.asSymbol,buffer,["/b_query",buffer.asUGenInput]);
}
setPoint{|identifier, buffer, action|
actions[\setPoint] = [nil,action];
this.prSendMsg(this.setPointMsg(identifier,buffer));
actions[\setPoint] = [nil,action];
this.prSendMsg(this.setPointMsg(identifier,buffer));
}
clearMsg { ^this.prMakeMsg(\clear,id); }
clear { |action|
actions[\clear] = [nil,action];
this.prSendMsg(this.clearMsg);
clear { |action|
actions[\clear] = [nil,action];
this.prSendMsg(this.clearMsg);
}
mergeMsg{|sourceDataSet, overwrite = 0|
^this.prMakeMsg(\merge,id,sourceDataSet.asUGenInput,overwrite);
}
mergeMsg{|sourceDataSet, overwrite = 0|
^this.prMakeMsg(\merge,id,sourceDataSet.asUGenInput,overwrite);
}
merge{|sourceDataSet, overwrite = 0, action|
actions[\merge] = [nil,action];
actions[\merge] = [nil,action];
this.prSendMsg(this.mergeMsg(sourceDataSet,overwrite));
}
printMsg { ^this.prMakeMsg(\print,id); }
printMsg { ^this.prMakeMsg(\print,id); }
print { |action=(postResponse)|
actions[\print] = [string(FluidMessageResponse,_,_),action];
@ -74,31 +74,31 @@ FluidDataSet : FluidDataObject
}
toBufferMsg{|buffer, transpose = 0, labelSet|
buffer = this.prEncodeBuffer(buffer);
^this.prMakeMsg(\toBuffer, id, buffer, transpose, labelSet.asUGenInput,["/b_query",buffer.asUGenInput]);
}
buffer = this.prEncodeBuffer(buffer);
^this.prMakeMsg(\toBuffer, id, buffer, transpose, labelSet.asUGenInput,["/b_query",buffer.asUGenInput]);
}
toBuffer{|buffer, transpose = 0, labelSet, action|
actions[\toBuffer] = [nil,action];
actions[\toBuffer] = [nil,action];
this.prSendMsg(this.toBufferMsg(buffer, transpose, labelSet));
}
fromBufferMsg{|buffer, transpose = 0, labelSet|
buffer = this.prEncodeBuffer(buffer);
^this.prMakeMsg(\fromBuffer, id, buffer, transpose, labelSet.asUGenInput,["/b_query",buffer.asUGenInput]);
}
buffer = this.prEncodeBuffer(buffer);
^this.prMakeMsg(\fromBuffer, id, buffer, transpose, labelSet.asUGenInput,["/b_query",buffer.asUGenInput]);
}
fromBuffer{|buffer, transpose = 0, labelSet, action|
actions[\fromBuffer] = [nil,action];
actions[\fromBuffer] = [nil,action];
this.prSendMsg(this.fromBufferMsg(buffer, transpose, labelSet));
}
getIdsMsg{|labelSet|
^this.prMakeMsg(\getIds, id, labelSet.asUGenInput);
}
^this.prMakeMsg(\getIds, id, labelSet.asUGenInput);
}
getIds{|labelSet, action|
actions[\getIds] = [nil,action];
this.prSendMsg(this.getIdsMsg(labelSet));
actions[\getIds] = [nil,action];
this.prSendMsg(this.getIdsMsg(labelSet));
}
}

@ -1,85 +1,84 @@
FluidDataSetQuery : FluidDataObject {
*new{|server| ^super.new(server) }
*new{|server| ^super.new(server) }
addColumnMsg { |column|
^this.prMakeMsg(\addColumn,id,column);
}
addColumnMsg { |column|
^this.prMakeMsg(\addColumn,id,column);
}
addColumn{|column, action|
actions[\addColumn] = [nil,action];
this.prSendMsg(this.addColumnMsg(column));
actions[\addColumn] = [nil,action];
this.prSendMsg(this.addColumnMsg(column));
}
addRangeMsg{|start,count|
^this.prMakeMsg(\addRange,id,start,count);
}
addRangeMsg{|start,count|
^this.prMakeMsg(\addRange,id,start,count);
}
addRange{|start, count, action|
actions[\addRange] = [nil, action];
actions[\addRange] = [nil, action];
this.prSendMsg(this.addRangeMsg(start, count));
}
filterMsg{|column, condition, value, action|
^this.prMakeMsg(\filter,id,column,condition.asSymbol,value);
}
filterMsg{|column, condition, value, action|
^this.prMakeMsg(\filter,id,column,condition.asSymbol,value);
}
filter{|column, condition, value, action|
actions[\filter] = [nil, action];
actions[\filter] = [nil, action];
this.prSendMsg(this.filterMsg(column, condition, value));
}
andMsg{ |column, condition, value|
^this.prMakeMsg(\and,id,column, condition.asSymbol, value);
}
andMsg{ |column, condition, value|
^this.prMakeMsg(\and,id,column, condition.asSymbol, value);
}
and{|column, condition, value, action|
actions[\and] = [nil, action];
actions[\and] = [nil, action];
this.prSendMsg(this.andMsg(column,condition,value));
}
orMsg{|column, condition, value|
^this.prMakeMsg(\or,id,column, condition.asSymbol, value)
}
orMsg{|column, condition, value|
^this.prMakeMsg(\or,id,column, condition.asSymbol, value)
}
or{|column, condition, value, action|
actions[\or] = [nil,action];
actions[\or] = [nil,action];
this.prSendMsg(this.orMsg(column, condition, value));
}
clearMsg{
^this.prMakeMsg(\clear,id);
}
clearMsg{
^this.prMakeMsg(\clear,id);
}
clear{|action|
actions[\clear] = [nil, action];
actions[\clear] = [nil, action];
this.prSendMsg(this.clearMsg);
}
limitMsg{|rows|
^this.prMakeMsg(\limit,id,rows);
}
limitMsg{|rows|
^this.prMakeMsg(\limit,id,rows);
}
limit{|rows, action|
actions[\limit] = [nil,action];
actions[\limit] = [nil,action];
this.prSendMsg(this.limitMsg(rows));
}
transformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\transform,id,sourceDataSet.id,destDataSet.id);
}
transformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\transform,id,sourceDataSet.id,destDataSet.id);
}
transform{|sourceDataSet, destDataSet, action|
actions[\transform] = [nil,action];
this.prSendMsg(this.transformMsg(sourceDataSet,destDataSet));
}
transform{|sourceDataSet, destDataSet, action|
actions[\transform] = [nil,action];
this.prSendMsg(this.transformMsg(sourceDataSet,destDataSet));
}
transformJoinMsg{|source1DataSet, source2DataSet, destDataSet|
^this.prMakeMsg(\transformJoin,id,source1DataSet.id, source2DataSet.id, destDataSet.id);
}
transformJoinMsg{|source1DataSet, source2DataSet, destDataSet|
^this.prMakeMsg(\transformJoin,id,source1DataSet.id, source2DataSet.id, destDataSet.id);
}
transformJoin{|source1DataSet, source2DataSet, destDataSet, action|
actions[\transformJoin] = [nil,action];
this.prSendMsg(this.transformJoinMsg(source1DataSet, source2DataSet, destDataSet));
}
actions[\transformJoin] = [nil,action];
this.prSendMsg(this.transformJoinMsg(source1DataSet, source2DataSet, destDataSet));
}
}

@ -1,14 +1,14 @@
FluidDataSetWr : FluidBufProcessor {
*kr { |dataset,idPrefix = "", idNumber = 0,buf, trig=1, blocking = 1|
var args;
buf ?? {(this.class.name ++ ": No input buffer provided").error};
var args;
buf ?? {(this.class.name ++ ": No input buffer provided").error};
idNumber = idNumber !? {[2,1,idNumber.asInteger.asUGenInput]} ?? {[2,0,0]};
idPrefix = idPrefix !? {[idPrefix.asString.size] ++ idPrefix.asString.ascii} ?? {0};
idNumber = idNumber !? {[2,1,idNumber.asInteger.asUGenInput]} ?? {[2,0,0]};
idPrefix = idPrefix !? {[idPrefix.asString.size] ++ idPrefix.asString.ascii} ?? {0};
args = [-1] ++ dataset.asUGenInput ++idPrefix ++ idNumber ++ buf.asUGenInput ++ trig ++ blocking;
args = [-1] ++ dataset.asUGenInput ++idPrefix ++ idNumber ++ buf.asUGenInput ++ trig ++ blocking;
^FluidProxyUgen.kr(\FluidDataSetWrTrigger,*args);
^FluidProxyUgen.kr(\FluidDataSetWrTrigger,*args);
}
}

@ -2,20 +2,20 @@ FluidGrid : FluidModelObject {
var <>oversample, <>extent, <>axis;
*new {|server, oversample = 1, extent = 0, axis = 0|
^super.new(server,[oversample, extent, axis])
.oversample_(oversample).extent_(extent).axis_(axis);
.oversample_(oversample).extent_(extent).axis_(axis);
}
prGetParams{
^[this.oversample, this.extent, this.axis];
}
prGetParams{
^[this.oversample, this.extent, this.axis];
}
fitTransformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\fitTransform,id, sourceDataSet.id, destDataSet.id);
}
^this.prMakeMsg(\fitTransform,id, sourceDataSet.id, destDataSet.id);
}
fitTransform{|sourceDataSet, destDataSet, action|
actions[\fitTransform] = [nil,action];
this.fitTransformMsg(sourceDataSet,destDataSet);
actions[\fitTransform] = [nil,action];
this.fitTransformMsg(sourceDataSet,destDataSet);
this.prSendMsg(this.fitTransformMsg(sourceDataSet,destDataSet));
}

@ -1,8 +1,8 @@
FluidHPSS : FluidRTMultiOutUGen {
*ar { arg in = 0, harmFilterSize=17, percFilterSize = 31, maskingMode=0, harmThreshFreq1 = 0.1, harmThreshAmp1 = 0, harmThreshFreq2 = 0.5, harmThreshAmp2 = 0, percThreshFreq1 = 0.1, percThreshAmp1 = 0, percThreshFreq2 = 0.5, percThreshAmp2 = 0, windowSize= 1024, hopSize= -1, fftSize= -1, maxFFTSize = -1, maxHarmFilterSize, maxPercFilterSize;
maxHarmFilterSize = maxHarmFilterSize ? harmFilterSize;
maxPercFilterSize = maxPercFilterSize ? percFilterSize;
maxHarmFilterSize = maxHarmFilterSize ? harmFilterSize;
maxPercFilterSize = maxPercFilterSize ? percFilterSize;
^this.multiNew('audio', in.asAudioRateInput(this), harmFilterSize, maxHarmFilterSize, percFilterSize, maxPercFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize, maxFFTSize)
}
@ -15,16 +15,16 @@ FluidHPSS : FluidRTMultiOutUGen {
];
^channels
}
checkInputs {
checkInputs {
if(inputs.at(17).rate != 'scalar') {
^(": maxFFTSize cannot be modulated.");
};
};
if(inputs.at(2).rate != 'scalar') {
^(": maxHarmFilterSize cannot be modulated.");
};
};
if(inputs.at(4).rate != 'scalar') {
^(": maxPercFilterSize cannot be modulated.");
};
};
^this.checkValidInputs;
}
}

@ -1,69 +1,69 @@
FluidKDTree : FluidModelObject
{
{
var neighbours,radius;
var neighbours,radius;
*new{ |server, numNeighbours = 1, radius = 0|
^super.new(server,[numNeighbours,radius ? -1])
.numNeighbours_(numNeighbours)
.radius_(radius);
}
*new{ |server, numNeighbours = 1, radius = 0|
^super.new(server,[numNeighbours,radius ? -1])
.numNeighbours_(numNeighbours)
.radius_(radius);
}
numNeighbours_{|k|neighbours = k.asInteger; }
numNeighbours{ ^neighbours; }
numNeighbours_{|k|neighbours = k.asInteger; }
numNeighbours{ ^neighbours; }
radius_{|r| radius = r.asUGenInput;}
radius{ ^radius; }
radius_{|r| radius = r.asUGenInput;}
radius{ ^radius; }
prGetParams{^[this.id, this.numNeighbours,this.radius];}
prGetParams{^[this.id, this.numNeighbours,this.radius];}
fitMsg{ |dataSet| ^this.prMakeMsg(\fit,this.id,dataSet.id);}
fitMsg{ |dataSet| ^this.prMakeMsg(\fit,this.id,dataSet.id);}
fit{|dataSet,action|
actions[\fit] = [nil,action];
actions[\fit] = [nil,action];
this.prSendMsg(this.fitMsg(dataSet));
}
kNearestMsg{|buffer,k|
k !?
{^this.prMakeMsg(\kNearest,id,this.prEncodeBuffer(buffer),k);}
??
{^this.prMakeMsg(\kNearest,id,this.prEncodeBuffer(buffer));}
}
kNearestMsg{|buffer,k|
k !?
{^this.prMakeMsg(\kNearest,id,this.prEncodeBuffer(buffer),k);}
??
{^this.prMakeMsg(\kNearest,id,this.prEncodeBuffer(buffer));}
}
kNearest{ |buffer, k, action|
actions[\kNearest] = [strings(FluidMessageResponse,_,_),action];
actions[\kNearest] = [strings(FluidMessageResponse,_,_),action];
this.prSendMsg(this.kNearestMsg(buffer,k));
}
kNearestDistMsg {|buffer|
^this.prMakeMsg(\kNearestDist,id,this.prEncodeBuffer(buffer));
}
kNearestDistMsg {|buffer|
^this.prMakeMsg(\kNearestDist,id,this.prEncodeBuffer(buffer));
}
kNearestDist { |buffer, action|
actions[\kNearestDist] = [numbers(FluidMessageResponse,_,nil,_),action];
actions[\kNearestDist] = [numbers(FluidMessageResponse,_,nil,_),action];
this.prSendMsg(this.kNearestDistMsg(buffer));
}
kr{|trig, inputBuffer,outputBuffer, numNeighbours = 1, lookupDataSet|
/* this.numNeighbours_(numNeighbours);
lookupDataSet = lookupDataSet ? -1;
this.lookupDataSet_(lookupDataSet);*/
kr{|trig, inputBuffer,outputBuffer, numNeighbours = 1, lookupDataSet|
/* this.numNeighbours_(numNeighbours);
lookupDataSet = lookupDataSet ? -1;
this.lookupDataSet_(lookupDataSet);*/
^FluidKDTreeQuery.kr(trig,
this, numNeighbours, this.radius,lookupDataSet.asUGenInput,
inputBuffer,outputBuffer);
}
^FluidKDTreeQuery.kr(trig,
this, numNeighbours, this.radius,lookupDataSet.asUGenInput,
inputBuffer,outputBuffer);
}
}
FluidKDTreeQuery : FluidRTMultiOutUGen
{
*kr{ |trig, tree, numNeighbours, radius,lookupDataSet, inputBuffer, outputBuffer |
^this.multiNew('control',trig, tree.asUGenInput, numNeighbours, radius,lookupDataSet!?(_.asUGenInput)??{-1}, inputBuffer.asUGenInput, outputBuffer.asUGenInput)
}
*kr{ |trig, tree, numNeighbours, radius,lookupDataSet, inputBuffer, outputBuffer |
^this.multiNew('control',trig, tree.asUGenInput, numNeighbours, radius,lookupDataSet!?(_.asUGenInput)??{-1}, inputBuffer.asUGenInput, outputBuffer.asUGenInput)
}
init { arg ... theInputs;
init { arg ... theInputs;
inputs = theInputs;
^this.initOutputs(1, rate);
}

@ -80,9 +80,9 @@ FluidKMeans : FluidModelObject {
transformPointMsg{ |sourceBuffer, targetBuffer|
^this.prMakeMsg(\transformPoint, id,
this.prEncodeBuffer(sourceBuffer),
this.prEncodeBuffer(targetBuffer),
["/b_query", targetBuffer.asUGenInput]);
}
this.prEncodeBuffer(targetBuffer),
["/b_query", targetBuffer.asUGenInput]);
}
transformPoint { |sourceBuffer, targetBuffer, action|
actions[\transformPoint] = [nil,{action.value(targetBuffer)}];
@ -91,40 +91,40 @@ FluidKMeans : FluidModelObject {
getMeansMsg{|dataSet| ^this.prMakeMsg(\getMeans, id, dataSet.asUGenInput) }
getMeans{ |dataSet, action|
actions[\getMeans] = [nil, action];
getMeans{ |dataSet, action|
actions[\getMeans] = [nil, action];
this.prSendMsg(this.getMeansMsg(dataSet));
}
setMeansMsg{|dataSet| ^this.prMakeMsg(\setMeans, id, dataSet.asUGenInput) }
setMeans{ |dataSet, action|
actions[\setMeans] = [nil, action];
setMeans{ |dataSet, action|
actions[\setMeans] = [nil, action];
this.prSendMsg(this.setMeansMsg(dataSet));
}
clearMsg{ ^this.prMakeMsg(\clear, id) }
clear{ |action|
actions[\clear] = [nil, action];
clear{ |action|
actions[\clear] = [nil, action];
this.prSendMsg(this.clearMsg);
}
kr{|trig, inputBuffer,outputBuffer|
^FluidKMeansQuery.kr(trig,
this,
this.prEncodeBuffer(inputBuffer),
this.prEncodeBuffer(outputBuffer));
^FluidKMeansQuery.kr(trig,
this,
this.prEncodeBuffer(inputBuffer),
this.prEncodeBuffer(outputBuffer));
}
}
FluidKMeansQuery : FluidRTMultiOutUGen {
*kr{ |trig, model,inputBuffer, outputBuffer |
^this.multiNew('control',trig, model.asUGenInput,inputBuffer.asUGenInput, outputBuffer.asUGenInput)
}
*kr{ |trig, model,inputBuffer, outputBuffer |
^this.multiNew('control',trig, model.asUGenInput,inputBuffer.asUGenInput, outputBuffer.asUGenInput)
}
init { arg ... theInputs;
init { arg ... theInputs;
inputs = theInputs;
^this.initOutputs(1, rate);
}

@ -1,60 +1,60 @@
FluidKNNClassifier : FluidModelObject {
var <>numNeighbours, <>weight;
var <>numNeighbours, <>weight;
*new {|server, numNeighbours = 3, weight = 1|
^super.new(server,[numNeighbours,weight])
.numNeighbours_(numNeighbours)
.weight_(weight);
.numNeighbours_(numNeighbours)
.weight_(weight);
}
prGetParams{^[this.id,this.numNeighbours,this.weight];}
prGetParams{^[this.id,this.numNeighbours,this.weight];}
fitMsg{|dataSet, labelSet|
^this.prMakeMsg(\fit, id, dataSet.id, labelSet.id)
}
fitMsg{|dataSet, labelSet|
^this.prMakeMsg(\fit, id, dataSet.id, labelSet.id)
}
fit{|dataSet, labelSet, action|
actions[\fit] = [nil,action];
this.prSendMsg(this.fitMsg(dataSet, labelSet));
actions[\fit] = [nil,action];
this.prSendMsg(this.fitMsg(dataSet, labelSet));
}
predictMsg{|dataSet, labelSet|
^this.prMakeMsg(\predict, id, dataSet.id, labelSet.id)
}
predictMsg{|dataSet, labelSet|
^this.prMakeMsg(\predict, id, dataSet.id, labelSet.id)
}
predict{|dataSet, labelSet, action|
actions[\predict] = [nil, action];
predict{|dataSet, labelSet, action|
actions[\predict] = [nil, action];
this.prSendMsg(this.predictMsg(dataSet, labelSet));
}
predictPointMsg{|buffer|
^this.prMakeMsg(\predictPoint, id, this.prEncodeBuffer(buffer))
}
predictPointMsg{|buffer|
^this.prMakeMsg(\predictPoint, id, this.prEncodeBuffer(buffer))
}
predictPoint {|buffer, action|
actions[\predictPoint] = [string(FluidMessageResponse,_,_),action];
this.prSendMsg(this.predictPointMsg(buffer));
}
kr{|trig, inputBuffer,outputBuffer|
^FluidKNNClassifierQuery.kr(trig,
this, this.numNeighbours, this.weight,
this.prEncodeBuffer(inputBuffer),
this.prEncodeBuffer(outputBuffer));
}
kr{|trig, inputBuffer,outputBuffer|
^FluidKNNClassifierQuery.kr(trig,
this, this.numNeighbours, this.weight,
this.prEncodeBuffer(inputBuffer),
this.prEncodeBuffer(outputBuffer));
}
}
FluidKNNClassifierQuery : FluidRTMultiOutUGen {
*kr{ |trig, model,numNeighbours = 3, weight = 1,inputBuffer, outputBuffer |
^this.multiNew('control',trig, model.asUGenInput,
numNeighbours,weight,
inputBuffer.asUGenInput, outputBuffer.asUGenInput)
}
*kr{ |trig, model,numNeighbours = 3, weight = 1,inputBuffer, outputBuffer |
^this.multiNew('control',trig, model.asUGenInput,
numNeighbours,weight,
inputBuffer.asUGenInput, outputBuffer.asUGenInput)
}
init { arg ... theInputs;
init { arg ... theInputs;
inputs = theInputs;
^this.initOutputs(1, rate);
}

@ -1,59 +1,59 @@
FluidKNNRegressor : FluidModelObject {
var <>numNeighbours, <>weight;
var <>numNeighbours, <>weight;
*new {|server, numNeighbours = 3, weight = 1|
^super.new(server,[numNeighbours,weight])
.numNeighbours_(numNeighbours)
.weight_(weight);
.numNeighbours_(numNeighbours)
.weight_(weight);
}
prGetParams{^[this.id,this.numNeighbours,this.weight,-1,-1];}
prGetParams{^[this.id,this.numNeighbours,this.weight,-1,-1];}
fitMsg{|sourceDataSet, targetDataSet|
^this.prMakeMsg(\fit,this.id,sourceDataSet.id,targetDataSet.id)
}
fitMsg{|sourceDataSet, targetDataSet|
^this.prMakeMsg(\fit,this.id,sourceDataSet.id,targetDataSet.id)
}
fit{|sourceDataSet, targetDataSet, action|
actions[\fit] = [nil,action];
this.prSendMsg(this.fitMsg(sourceDataSet, targetDataSet));
actions[\fit] = [nil,action];
this.prSendMsg(this.fitMsg(sourceDataSet, targetDataSet));
}
predictMsg{ |sourceDataSet, targetDataSet|
^this.prMakeMsg(\predict,this.id,sourceDataSet.id,targetDataSet.id)
}
predictMsg{ |sourceDataSet, targetDataSet|
^this.prMakeMsg(\predict,this.id,sourceDataSet.id,targetDataSet.id)
}
predict{ |sourceDataSet, targetDataSet,action|
actions[\predict] = [nil, action];
actions[\predict] = [nil, action];
this.prSendMsg(this.predictMsg(sourceDataSet, targetDataSet));
}
predictPointMsg { |buffer|
^this.prMakeMsg(\predictPoint,id, this.prEncodeBuffer(buffer));
}
predictPointMsg { |buffer|
^this.prMakeMsg(\predictPoint,id, this.prEncodeBuffer(buffer));
}
predictPoint { |buffer, action|
actions[\predictPoint] = [number(FluidMessageResponse,_,_),action];
this.prSendMsg(this.predictPointMsg(buffer));
}
kr{|trig, inputBuffer,outputBuffer|
^FluidKNNRegressorQuery.kr(K2A.ar(trig),
this, this.numNeighbours, this.weight,
this.prEncodeBuffer(inputBuffer),
this.prEncodeBuffer(outputBuffer));
}
kr{|trig, inputBuffer,outputBuffer|
^FluidKNNRegressorQuery.kr(K2A.ar(trig),
this, this.numNeighbours, this.weight,
this.prEncodeBuffer(inputBuffer),
this.prEncodeBuffer(outputBuffer));
}
}
FluidKNNRegressorQuery : FluidRTMultiOutUGen {
*kr{ |trig, model,numNeighbours = 3, weight = 1,inputBuffer, outputBuffer |
^this.multiNew('control',trig, model.asUGenInput,
numNeighbours,weight,
inputBuffer.asUGenInput, outputBuffer.asUGenInput)
}
*kr{ |trig, model,numNeighbours = 3, weight = 1,inputBuffer, outputBuffer |
^this.multiNew('control',trig, model.asUGenInput,
numNeighbours,weight,
inputBuffer.asUGenInput, outputBuffer.asUGenInput)
}
init { arg ... theInputs;
init { arg ... theInputs;
inputs = theInputs;
^this.initOutputs(1, rate);
}

@ -1,19 +1,19 @@
FluidLabelSet : FluidDataObject {
*new{|server| ^super.new(server) }
*new{|server| ^super.new(server) }
addLabelMsg{|identifier,label|
^this.prMakeMsg(\addLabel,id,identifier.asSymbol,label.asSymbol);
}
addLabelMsg{|identifier,label|
^this.prMakeMsg(\addLabel,id,identifier.asSymbol,label.asSymbol);
}
addLabel{|identifier, label, action|
actions[\addLabel] = [nil, action];
actions[\addLabel] = [nil, action];
this.prSendMsg(this.addLabelMsg(identifier,label));
}
updateLabelMsg{|identifier, label|
^this.prMakeMsg(\updateLabel, id, identifier.asSymbol, label.asSymbol);
}
updateLabelMsg{|identifier, label|
^this.prMakeMsg(\updateLabel, id, identifier.asSymbol, label.asSymbol);
}
setLabelMsg{|identifier,label|
^this.prMakeMsg(\setLabel,id,identifier.asSymbol,label.asSymbol);
@ -25,36 +25,36 @@ FluidLabelSet : FluidDataObject {
}
updateLabel{|identifier, label, action|
actions[\updateLabel] = [nil,action];
actions[\updateLabel] = [nil,action];
this.prSendMsg(this.updateLabelMsg(identifier,label));
}
getLabelMsg{|identifier|
^this.prMakeMsg(\getLabel, id, identifier.asSymbol);
}
getLabelMsg{|identifier|
^this.prMakeMsg(\getLabel, id, identifier.asSymbol);
}
getLabel{|identifier, action|
actions[\getLabel] = [string(FluidMessageResponse,_,_),action];
actions[\getLabel] = [string(FluidMessageResponse,_,_),action];
this.prSendMsg(this.getLabelMsg(identifier));
}
deleteLabelMsg{|identifier, action|
^this.prMakeMsg(\deleteLabel, id, identifier.asSymbol);
}
deleteLabelMsg{|identifier, action|
^this.prMakeMsg(\deleteLabel, id, identifier.asSymbol);
}
deleteLabel{|identifier, action|
actions[\deleteLabel] = [nil, action];
actions[\deleteLabel] = [nil, action];
this.prSendMsg(this.deleteLabelMsg(identifier));
}
clearMsg { ^this.prMakeMsg(\clear,id); }
clearMsg { ^this.prMakeMsg(\clear,id); }
clear { |action|
actions[\clear] = [nil,action];
this.prSendMsg(this.clearMsg);
}
clear { |action|
actions[\clear] = [nil,action];
this.prSendMsg(this.clearMsg);
}
printMsg { ^this.prMakeMsg(\print,id); }
printMsg { ^this.prMakeMsg(\print,id); }
print { |action=(postResponse)|
actions[\print] = [string(FluidMessageResponse,_,_),action];
@ -62,11 +62,11 @@ FluidLabelSet : FluidDataObject {
}
getIdsMsg{|labelSet|
^this.prMakeMsg(\getIds, id, labelSet.asUGenInput);
}
^this.prMakeMsg(\getIds, id, labelSet.asUGenInput);
}
getIds{|labelSet, action|
actions[\getIds] = [nil,action];
this.prSendMsg(this.getIdsMsg(labelSet));
actions[\getIds] = [nil,action];
this.prSendMsg(this.getIdsMsg(labelSet));
}
}

@ -1,43 +1,43 @@
FluidLoudness : FluidRTMultiOutUGen {
const <features=#[\loudness, \peak];
classvar featuresLookup;
*initClass {
featuresLookup = Dictionary.with(*this.features.collect{|x,i| x->(1<<i)});
}
*prWarnUnrecognised {|sym| ("WARNING: FluidLoudness -" + sym + "is not a recognised option").postln}
*prProcessSelect {|a|
var bits;
a.asBag.countsDo{|item,count,i|
if(count > 1) { ("Option '" ++ item ++ "' is repeated").warn};
};
bits = a.collect{ |sym|
(featuresLookup[sym.asSymbol] !? {|x| x} ?? {this.prWarnUnrecognised(sym); 0})
}.reduce{|x,y| x | y};
^bits
}
const <features=#[\loudness, \peak];
classvar featuresLookup;
*initClass {
featuresLookup = Dictionary.with(*this.features.collect{|x,i| x->(1<<i)});
}
*prWarnUnrecognised {|sym| ("WARNING: FluidLoudness -" + sym + "is not a recognised option").postln}
*prProcessSelect {|a|
var bits;
a.asBag.countsDo{|item,count,i|
if(count > 1) { ("Option '" ++ item ++ "' is repeated").warn};
};
bits = a.collect{ |sym|
(featuresLookup[sym.asSymbol] !? {|x| x} ?? {this.prWarnUnrecognised(sym); 0})
}.reduce{|x,y| x | y};
^bits
}
*kr { arg in = 0, select, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, maxWindowSize = 16384;
var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)};
var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)};
^this.multiNew('control', in.asAudioRateInput(this), selectbits, kWeighting, truePeak, windowSize, hopSize, maxWindowSize);
}
init {arg ...theInputs;
var numChannels;
init {arg ...theInputs;
var numChannels;
inputs = theInputs;
numChannels = inputs.at(1).asBinaryDigits.sum;
numChannels = inputs.at(1).asBinaryDigits.sum;
^this.initOutputs(numChannels,rate);
}
checkInputs {
if(inputs.at(6).rate != 'scalar') {
^(": maxwindowSize cannot be modulated.");
};
};
^this.checkValidInputs;
}
}

@ -7,25 +7,25 @@ FluidMDS : FluidModelObject {
classvar < kl = 5;
classvar < cosine = 5;
var <>numDimensions, <>distanceMetric;
var <>numDimensions, <>distanceMetric;
*new {|server,numDimensions = 2, distanceMetric = 1|
^super.new(server,[numDimensions, distanceMetric])
.numDimensions_(numDimensions)
.distanceMetric_(distanceMetric);
.numDimensions_(numDimensions)
.distanceMetric_(distanceMetric);
}
prGetParams{
^[this.numDimensions, this.distanceMetric];
}
prGetParams{
^[this.numDimensions, this.distanceMetric];
}
fitTransformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\fitTransform,id, sourceDataSet.id, destDataSet.id);
}
^this.prMakeMsg(\fitTransform,id, sourceDataSet.id, destDataSet.id);
}
fitTransform{|sourceDataSet, destDataSet, action|
actions[\fitTransform] = [nil,action];
this.fitTransformMsg(sourceDataSet,destDataSet);
actions[\fitTransform] = [nil,action];
this.fitTransformMsg(sourceDataSet,destDataSet);
this.prSendMsg(this.fitTransformMsg(sourceDataSet,destDataSet));
}

@ -1,10 +1,11 @@
FluidMFCC : FluidRTMultiOutUGen {
*kr { arg in = 0, numCoeffs = 13, numBands = 40, startCoeff = 0, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = -1, maxNumCoeffs = nil;
*kr { arg in = 0, numCoeffs = 13, numBands = 40, startCoeff = 0, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = -1, maxNumCoeffs = nil, maxNumBands = nil;
maxNumCoeffs = maxNumCoeffs ? numCoeffs;
maxNumCoeffs = maxNumCoeffs ? numCoeffs;
maxNumBands = maxNumBands ? numBands;
^this.multiNew('control', in.asAudioRateInput(this), numCoeffs, maxNumCoeffs, numBands, startCoeff, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize);
^this.multiNew('control', in.asAudioRateInput(this), numCoeffs, maxNumCoeffs, numBands, maxNumBands, startCoeff, minFreq, maxFreq, windowSize, hopSize, fftSize, maxFFTSize);
}
@ -18,6 +19,9 @@ FluidMFCC : FluidRTMultiOutUGen {
if(inputs.at(2).rate != 'scalar') {
^(": maxNumCoeffs cannot be modulated.");
};
if(inputs.at(4).rate != 'scalar') {
^(": maxNumBands cannot be modulated.");
};
if(inputs.at(10).rate != 'scalar') {
^(": maxFFTSize cannot be modulated.");
};

@ -5,109 +5,109 @@ FluidMLPRegressor : FluidModelObject {
const <relu = 2;
const <tanh = 3;
var <>hiddenLayers, <>activation, <>outputActivation, <>tapIn, <>tapOut, <>maxIter, <>learnRate, <>momentum, <>batchSize, <>validation;
var <>hiddenLayers, <>activation, <>outputActivation, <>tapIn, <>tapOut, <>maxIter, <>learnRate, <>momentum, <>batchSize, <>validation;
*new {|server, hiddenLayers = #[3,3] , activation = 2, outputActivation = 0, tapIn = 0, tapOut = -1,maxIter = 1000, learnRate = 0.0001, momentum = 0.9, batchSize = 50, validation = 0.2|
^super.new(server, [hiddenLayers.size] ++ hiddenLayers ++ [activation, outputActivation, tapIn, tapOut, maxIter, learnRate, momentum, batchSize, validation])
.hiddenLayers_(hiddenLayers)
.activation_(activation)
.outputActivation_(outputActivation)
.tapIn_(tapIn)
.tapOut_(tapOut)
.maxIter_(maxIter)
.learnRate_(learnRate)
.momentum_(momentum)
.batchSize_(batchSize)
.validation_(validation);
}
prGetParams{
^[this.id, this.hiddenLayers.size] ++ this.hiddenLayers ++ [this.activation, this.outputActivation, this.tapIn, this.tapOut, this.maxIter, this.learnRate, this.momentum, this.batchSize, this.validation]
}
clearMsg{ ^this.prMakeMsg(\clear, id) }
clear{ |action|
actions[\clear] = [nil, action];
.hiddenLayers_(hiddenLayers)
.activation_(activation)
.outputActivation_(outputActivation)
.tapIn_(tapIn)
.tapOut_(tapOut)
.maxIter_(maxIter)
.learnRate_(learnRate)
.momentum_(momentum)
.batchSize_(batchSize)
.validation_(validation);
}
prGetParams{
^[this.id, this.hiddenLayers.size] ++ this.hiddenLayers ++ [this.activation, this.outputActivation, this.tapIn, this.tapOut, this.maxIter, this.learnRate, this.momentum, this.batchSize, this.validation]
}
clearMsg{ ^this.prMakeMsg(\clear, id) }
clear{ |action|
actions[\clear] = [nil, action];
this.prSendMsg(this.clearMsg);
}
fitMsg{|sourceDataSet, targetDataSet|
^this.prMakeMsg(\fit,id,sourceDataSet.id, targetDataSet.id);
}
fitMsg{|sourceDataSet, targetDataSet|
^this.prMakeMsg(\fit,id,sourceDataSet.id, targetDataSet.id);
}
fit{|sourceDataSet, targetDataSet, action|
actions[\fit] = [numbers(FluidMessageResponse,_,1,_),action];
this.prSendMsg(this.fitMsg(sourceDataSet,targetDataSet));
actions[\fit] = [numbers(FluidMessageResponse,_,1,_),action];
this.prSendMsg(this.fitMsg(sourceDataSet,targetDataSet));
}
predictMsg{|sourceDataSet, targetDataSet|
^this.prMakeMsg(\predict,id,sourceDataSet.id, targetDataSet.id);
}
predictMsg{|sourceDataSet, targetDataSet|
^this.prMakeMsg(\predict,id,sourceDataSet.id, targetDataSet.id);
}
predict{|sourceDataSet, targetDataSet, action|
actions[\predict] = [nil,action];
this.prSendMsg(this.predictMsg(sourceDataSet,targetDataSet));
actions[\predict] = [nil,action];
this.prSendMsg(this.predictMsg(sourceDataSet,targetDataSet));
}
predictPointMsg { |sourceBuffer, targetBuffer|
^this.prMakeMsg(\predictPoint,id,
this.prEncodeBuffer(sourceBuffer),
this.prEncodeBuffer(targetBuffer),
["/b_query", targetBuffer.asUGenInput]);
}
^this.prMakeMsg(\predictPoint,id,
this.prEncodeBuffer(sourceBuffer),
this.prEncodeBuffer(targetBuffer),
["/b_query", targetBuffer.asUGenInput]);
}
predictPoint { |sourceBuffer, targetBuffer, action|
actions[\predictPoint] = [nil,{action.value(targetBuffer)}];
this.predictPointMsg(sourceBuffer, targetBuffer);
actions[\predictPoint] = [nil,{action.value(targetBuffer)}];
this.predictPointMsg(sourceBuffer, targetBuffer);
this.prSendMsg(this.predictPointMsg(sourceBuffer, targetBuffer));
}
read { |filename, action|
actions[\read] = [numbers(FluidMessageResponse,_,nil,_), {
|data|
this.prUpdateParams(data);
action.value;
}];
this.prSendMsg(this.readMsg(filename));
}
kr{|trig, inputBuffer,outputBuffer, tapIn = 0, tapOut = -1|
var params;
tapIn = tapIn ? this.tapIn;
tapOut = tapOut ? this.tapOut;
this.tapIn_(tapIn).tapOut_(tapOut);
params = [this.prEncodeBuffer(inputBuffer),
this.prEncodeBuffer(outputBuffer),this.tapIn,this.tapOut];
^FluidMLPRegressorQuery.kr(trig,this, *params);
}
prUpdateParams{|data|
var rest = data.keep(-9);
this.hiddenLayers_(data.drop(1).drop(-9).copy);
[\activation_, \outputActivation_,
\tapIn_, \tapOut_, \maxIter_,
\learnRate_, \momentum_,
\batchSize_, \validation_]
.do{|prop,i|
this.performList(prop,rest[i]);
};
}
read { |filename, action|
actions[\read] = [numbers(FluidMessageResponse,_,nil,_), {
|data|
this.prUpdateParams(data);
action.value;
}];
this.prSendMsg(this.readMsg(filename));
}
kr{|trig, inputBuffer,outputBuffer, tapIn = 0, tapOut = -1|
var params;
tapIn = tapIn ? this.tapIn;
tapOut = tapOut ? this.tapOut;
this.tapIn_(tapIn).tapOut_(tapOut);
params = [this.prEncodeBuffer(inputBuffer),
this.prEncodeBuffer(outputBuffer),this.tapIn,this.tapOut];
^FluidMLPRegressorQuery.kr(trig,this, *params);
}
prUpdateParams{|data|
var rest = data.keep(-9);
this.hiddenLayers_(data.drop(1).drop(-9).copy);
[\activation_, \outputActivation_,
\tapIn_, \tapOut_, \maxIter_,
\learnRate_, \momentum_,
\batchSize_, \validation_]
.do{|prop,i|
this.performList(prop,rest[i]);
};
}
}
FluidMLPRegressorQuery : FluidRTMultiOutUGen {
*kr{ |trig, model, inputBuffer,outputBuffer, tapIn = 0, tapOut = -1|
^this.multiNew('control',trig, model.asUGenInput,
tapIn, tapOut,
inputBuffer.asUGenInput, outputBuffer.asUGenInput)
}
*kr{ |trig, model, inputBuffer,outputBuffer, tapIn = 0, tapOut = -1|
^this.multiNew('control',trig, model.asUGenInput,
tapIn, tapOut,
inputBuffer.asUGenInput, outputBuffer.asUGenInput)
}
init { arg ... theInputs;
init { arg ... theInputs;
inputs = theInputs;
^this.initOutputs(1, rate);
}
@ -120,94 +120,94 @@ FluidMLPClassifier : FluidModelObject {
const <relu = 2;
const <tanh = 3;
var <>hiddenLayers, <>activation, <> maxIter, <>learnRate, <> momentum, <>batchSize, <>validation;
var <>hiddenLayers, <>activation, <> maxIter, <>learnRate, <> momentum, <>batchSize, <>validation;
*new {|server, hiddenLayers = #[3,3] , activation = 2, maxIter = 1000, learnRate = 0.0001, momentum = 0.9, batchSize = 50, validation = 0.2|
^super.new(server,[hiddenLayers.size] ++ hiddenLayers ++ [activation, maxIter, learnRate, momentum, batchSize, validation])
.hiddenLayers_(hiddenLayers)
.activation_(activation)
.maxIter_(maxIter)
.learnRate_(learnRate)
.momentum_(momentum)
.batchSize_(batchSize)
.validation_(validation);
.hiddenLayers_(hiddenLayers)
.activation_(activation)
.maxIter_(maxIter)
.learnRate_(learnRate)
.momentum_(momentum)
.batchSize_(batchSize)
.validation_(validation);
}
prGetParams{
^[this.id, this.hiddenLayers.size] ++ this.hiddenLayers ++ [this.activation, this.maxIter, this.learnRate, this.momentum, this.batchSize, this.validation];
}
prGetParams{
^[this.id, this.hiddenLayers.size] ++ this.hiddenLayers ++ [this.activation, this.maxIter, this.learnRate, this.momentum, this.batchSize, this.validation];
}
clearMsg{ ^this.prMakeMsg(\clear,id) }
clearMsg{ ^this.prMakeMsg(\clear,id) }
clear{ |action|
actions[\clear] = [nil,action];
actions[\clear] = [nil,action];
this.prSendMsg(this.clearMsg);
}
fitMsg{|sourceDataSet, targetLabelSet|
^this.prMakeMsg(\fit,id,sourceDataSet.id, targetLabelSet.id);
}
fitMsg{|sourceDataSet, targetLabelSet|
^this.prMakeMsg(\fit,id,sourceDataSet.id, targetLabelSet.id);
}
fit{|sourceDataSet, targetLabelSet, action|
actions[\fit] = [numbers(FluidMessageResponse,_,1,_),action];
this.prSendMsg(this.fitMsg(sourceDataSet,targetLabelSet));
actions[\fit] = [numbers(FluidMessageResponse,_,1,_),action];
this.prSendMsg(this.fitMsg(sourceDataSet,targetLabelSet));
}
predictMsg{|sourceDataSet, targetLabelSet|
^this.prMakeMsg(\predict,id,sourceDataSet.id, targetLabelSet.id);
}
predictMsg{|sourceDataSet, targetLabelSet|
^this.prMakeMsg(\predict,id,sourceDataSet.id, targetLabelSet.id);
}
predict{ |sourceDataSet, targetLabelSet, action|
actions[\predict]=[nil,action];
actions[\predict]=[nil,action];
this.prSendMsg(this.predictMsg(sourceDataSet,targetLabelSet));
}
predictPointMsg { |sourceBuffer|
^this.prMakeMsg(\predictPoint,id,this.prEncodeBuffer(sourceBuffer))
}
predictPointMsg { |sourceBuffer|
^this.prMakeMsg(\predictPoint,id,this.prEncodeBuffer(sourceBuffer))
}
predictPoint { |sourceBuffer, action|
actions[\predictPoint] = [string(FluidMessageResponse,_,_),action];
actions[\predictPoint] = [string(FluidMessageResponse,_,_),action];
this.prSendMsg(this.predictPointMsg(sourceBuffer));
}
read { |filename, action|
actions[\read] = [numbers(FluidMessageResponse,_,nil,_), {
|data|
this.prUpdateParams(data);
action.value;
}];
this.prSendMsg(this.readMsg(filename));
}
read { |filename, action|
actions[\read] = [numbers(FluidMessageResponse,_,nil,_), {
|data|
this.prUpdateParams(data);
action.value;
}];
this.prSendMsg(this.readMsg(filename));
}
prUpdateParams{|data|
var rest = data.keep(-6);
this.hiddenLayers_(data.drop(1).drop(-6).copy);
[\activation_, \maxIter_,
\learnRate_, \momentum_,
\batchSize_, \validation_]
.do{|prop,i|
this.performList(prop,rest[i]);
};
}
prUpdateParams{|data|
var rest = data.keep(-6);
this.hiddenLayers_(data.drop(1).drop(-6).copy);
[\activation_, \maxIter_,
\learnRate_, \momentum_,
\batchSize_, \validation_]
.do{|prop,i|
this.performList(prop,rest[i]);
};
}
kr{|trig, inputBuffer,outputBuffer|
kr{|trig, inputBuffer,outputBuffer|
var params = [this.prEncodeBuffer(inputBuffer),
this.prEncodeBuffer(outputBuffer)];
var params = [this.prEncodeBuffer(inputBuffer),
this.prEncodeBuffer(outputBuffer)];
^FluidMLPClassifierQuery.kr(trig,this, *params);
}
^FluidMLPClassifierQuery.kr(trig,this, *params);
}
}
FluidMLPClassifierQuery : FluidRTMultiOutUGen {
*kr{ |trig, model, inputBuffer,outputBuffer|
^this.multiNew('control',trig, model.asUGenInput,
inputBuffer.asUGenInput, outputBuffer.asUGenInput)
}
*kr{ |trig, model, inputBuffer,outputBuffer|
^this.multiNew('control',trig, model.asUGenInput,
inputBuffer.asUGenInput, outputBuffer.asUGenInput)
}
init { arg ... theInputs;
init { arg ... theInputs;
inputs = theInputs;
^this.initOutputs(1, rate);
}

@ -8,17 +8,17 @@
var filename = this.tmpJSONFilename;
action ?? {action = postResponse};
this.write(filename, {
action.value(this.parseJSON(File.readAllString(filename)));
File.delete(filename);
});
action.value(this.parseJSON(File.readAllString(filename)));
File.delete(filename);
});
}
load{|dict, action|
var filename = this.tmpJSONFilename;
File.use(filename, "wt", { |f| f.write(this.asJSON(dict));});
this.read(filename, {
action.notNil.if{ action.value; };
File.delete(filename);
action.notNil.if{ action.value; };
File.delete(filename);
});
}
@ -55,9 +55,9 @@
if(d.isKindOf(Symbol)){^this.asJSON(d.asString)};
if(d.isKindOf(Dictionary))
{
^"{" ++ (
d.keys.asList.collect{|k|
k.asString.asCompileString ++ ":" + this.asJSON(d[k])
^"{" ++ (
d.keys.asList.collect{|k|
k.asString.asCompileString ++ ":" + this.asJSON(d[k])
}).join(", ") ++ "}"
};
if(d.isKindOf(SequenceableCollection))

@ -2,7 +2,7 @@ FluidMelBands : FluidRTMultiOutUGen {
*kr { arg in = 0, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, scale = 0, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = -1, maxNumBands;
maxNumBands = maxNumBands ? numBands;
maxNumBands = maxNumBands ? numBands;
^this.multiNew('control', in.asAudioRateInput(this), numBands, maxNumBands, minFreq, maxFreq, normalize, scale, windowSize, hopSize, fftSize, maxFFTSize);
}

@ -1,41 +1,41 @@
FluidMessageResponse : Object
{
//selectors is an array of functions
//my cunning thought is that those that need extra data (e..g numbers()) can
//use partial applicaiton
*collectArgs{ |selectors,a|
var response = [];
var idx = 0;
selectors.do{ |selector|
var newThings;
# newThings,idx = selector.value(a, idx);
response = response ++ newThings;
};
//selectors is an array of functions
//my cunning thought is that those that need extra data (e..g numbers()) can
//use partial applicaiton
*collectArgs{ |selectors,a|
var response = [];
var idx = 0;
selectors.do{ |selector|
var newThings;
# newThings,idx = selector.value(a, idx);
response = response ++ newThings;
};
if(response.size == 1,
{^response[0]},{^response})
}
if(response.size == 1,
{^response[0]},{^response})
}
*string{ |a, offset|
^[a]
}
*string{ |a, offset|
^[a]
}
*strings {|a,offset|
//TODO add an n argument as with numbers() to make this less omnivorous
^[a.drop(offset)];
}
*strings {|a,offset|
//TODO add an n argument as with numbers() to make this less omnivorous
^[a.drop(offset)];
}
*numbers{ |a, n, offset|
n = n ? a.size - offset; //send n = nil to consume everything
^[a.copyRange(offset, offset + n),offset + n]
}
*numbers{ |a, n, offset|
n = n ? a.size - offset; //send n = nil to consume everything
^[a.copyRange(offset, offset + n),offset + n]
}
*number{ |a,offset|
^[a[offset]];
}
*number{ |a,offset|
^[a[offset]];
}
*buffer{ |a,server,offset|
server = server ? Server.default ;
^[Buffer.cachedBufferAt(server, a[offset]), offset + 1]
}
*buffer{ |a,server,offset|
server = server ? Server.default ;
^[Buffer.cachedBufferAt(server, a[offset]), offset + 1]
}
}

@ -10,11 +10,11 @@ FluidNMFFilter : FluidRTMultiOutUGen {
}
checkInputs {
if(inputs.at(3).rate != 'scalar') {
^(": maxComponents cannot be modulated.");
if(inputs.at(3).rate != 'scalar') {
^(": maxComponents cannot be modulated.");
};
if(inputs.at(8).rate != 'scalar') {
^(": maxFFTSize cannot be modulated.");
if(inputs.at(8).rate != 'scalar') {
^(": maxFFTSize cannot be modulated.");
};
^this.checkValidInputs;
}

@ -10,11 +10,11 @@ FluidNMFMatch : FluidRTMultiOutUGen {
}
checkInputs {
if(inputs.at(3).rate != 'scalar') {
^(": maxComponents cannot be modulated.");
if(inputs.at(3).rate != 'scalar') {
^(": maxComponents cannot be modulated.");
};
if(inputs.at(8).rate != 'scalar') {
^(": maxFFTSize cannot be modulated.");
if(inputs.at(8).rate != 'scalar') {
^(": maxFFTSize cannot be modulated.");
};
^this.checkValidInputs;
}

@ -1,5 +1,5 @@
FluidNRTProcess : Object{
var <server, <ugen, <action, <outputBuffers, <blocking, <synth;
var <server, <ugen, <action, <outputBuffers, <blocking, <synth;
*new {|server, ugen, action, outputBuffers, blocking = 0|
^super.newCopyArgs(server, ugen, action, outputBuffers, blocking).init;
@ -8,8 +8,8 @@ FluidNRTProcess : Object{
init{
server = server ? Server.default;
server.ifNotRunning({
"FluidNRTProcess: Server not running".throw;
});
"FluidNRTProcess: Server not running".throw;
});
if (ugen.isNil){
"FluidNRTProcess : FluidRTUGen is nil".throw;
};
@ -24,28 +24,28 @@ FluidNRTProcess : Object{
process{|...ugenArgs|
var c = Condition.new(false);
var c = Condition.new(false);
synth = {
FreeSelfWhenDone.kr(ugen.performList(\new1,\control, ugenArgs.collect{|a| a.asUGenInput} ++ 1 ++ blocking));
FreeSelfWhenDone.kr(ugen.performList(\new1,\control, ugenArgs.collect{|a| a.asUGenInput} ++ 1 ++ blocking));
}.play(server);
synth.postln;
OSCFunc({ |m|
forkIfNeeded{
outputBuffers.do{|buf|
buf = server.cachedBufferAt(buf.asUGenInput);
buf.updateInfo;
};
server.sync;
if(action.notNil && m[2]==0){action.valueArray(outputBuffers)};
c.test = true;
c.signal;
}
},'/done', srcID:server.addr, argTemplate:[synth.nodeID]).oneShot;
OSCFunc({ |m|
forkIfNeeded{
outputBuffers.do{|buf|
buf = server.cachedBufferAt(buf.asUGenInput);
buf.updateInfo;
};
server.sync;
if(action.notNil && m[2]==0){action.valueArray(outputBuffers)};
c.test = true;
c.signal;
}
},'/done', srcID:server.addr, argTemplate:[synth.nodeID]).oneShot;
forkIfNeeded{
c.wait;
c.wait;
};
^this;
}

@ -1,103 +1,103 @@
FluidNormalize : FluidModelObject {
var <>min, <>max;
var <>min, <>max;
*new {|server, min = 0, max = 1|
^super.new(server,[min,max])
.min_(min).max_(max);
.min_(min).max_(max);
}
prGetParams{
^[this.id, this.min,this.max,-1,-1];
}
prGetParams{
^[this.id, this.min,this.max,-1,-1];
}
fitMsg{|dataSet|
^this.prMakeMsg(\fit,id,dataSet.id)
}
fitMsg{|dataSet|
^this.prMakeMsg(\fit,id,dataSet.id)
}
fit{|dataSet, action|
actions[\fit] = [nil,action];
actions[\fit] = [nil,action];
this.prSendMsg(this.fitMsg(dataSet));
}
transformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\transform,id,sourceDataSet.id,destDataSet.id);
}
transformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\transform,id,sourceDataSet.id,destDataSet.id);
}
transform{|sourceDataSet, destDataSet, action|
actions[\transform] = [nil,action];
this.prSendMsg(this.transformMsg(sourceDataSet, destDataSet));
this.prSendMsg(this.transformMsg(sourceDataSet, destDataSet));
}
fitTransformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\fitTransform,id,sourceDataSet.id,destDataSet.id)
}
fitTransformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\fitTransform,id,sourceDataSet.id,destDataSet.id)
}
fitTransform{|sourceDataSet, destDataSet, action|
actions[\fitTransform] = [nil,action];
actions[\fitTransform] = [nil,action];
this.prSendMsg(this.fitTransformMsg(sourceDataSet, destDataSet));
}
transformPointMsg{|sourceBuffer, destBuffer|
^this.prMakeMsg(\transformPoint,id,
this.prEncodeBuffer(sourceBuffer),
this.prEncodeBuffer(destBuffer),
["/b_query",destBuffer.asUGenInput]
);
}
transformPointMsg{|sourceBuffer, destBuffer|
^this.prMakeMsg(\transformPoint,id,
this.prEncodeBuffer(sourceBuffer),
this.prEncodeBuffer(destBuffer),
["/b_query",destBuffer.asUGenInput]
);
}
transformPoint{|sourceBuffer, destBuffer, action|
actions[\transformPoint] = [nil,{action.value(destBuffer)}];
this.prSendMsg(this.transformPointMsg(sourceBuffer, destBuffer));
actions[\transformPoint] = [nil,{action.value(destBuffer)}];
this.prSendMsg(this.transformPointMsg(sourceBuffer, destBuffer));
}
inverseTransformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\inverseTransform,id,sourceDataSet.id,destDataSet.id);
}
inverseTransformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\inverseTransform,id,sourceDataSet.id,destDataSet.id);
}
inverseTransform{|sourceDataSet, destDataSet, action|
actions[\inverseTransform] = [nil,action];
this.prSendMsg(this.inverseTransformMsg(sourceDataSet, destDataSet));
this.prSendMsg(this.inverseTransformMsg(sourceDataSet, destDataSet));
}
inverseTransformPointMsg{|sourceBuffer, destBuffer|
^this.prMakeMsg(\inverseTransformPoint,id,
this.prEncodeBuffer(sourceBuffer),
this.prEncodeBuffer(destBuffer),
["/b_query",destBuffer.asUGenInput]
);
}
inverseTransformPointMsg{|sourceBuffer, destBuffer|
^this.prMakeMsg(\inverseTransformPoint,id,
this.prEncodeBuffer(sourceBuffer),
this.prEncodeBuffer(destBuffer),
["/b_query",destBuffer.asUGenInput]
);
}
inverseTransformPoint{|sourceBuffer, destBuffer, action|
actions[\inverseTransformPoint] = [nil,{action.value(destBuffer)}];
this.prSendMsg(this.inverseTransformPointMsg(sourceBuffer, destBuffer));
actions[\inverseTransformPoint] = [nil,{action.value(destBuffer)}];
this.prSendMsg(this.inverseTransformPointMsg(sourceBuffer, destBuffer));
}
kr{|trig, inputBuffer,outputBuffer,min = 0 ,max = 1,invert = 0|
kr{|trig, inputBuffer,outputBuffer,min = 0 ,max = 1,invert = 0|
min = min ? this.min;
max = max ? this.max;
min = min ? this.min;
max = max ? this.max;
this.min_(min).max_(max);
this.min_(min).max_(max);
^FluidNormalizeQuery.kr(trig,
this, this.prEncodeBuffer(inputBuffer), this.prEncodeBuffer(outputBuffer), this.min, this.max, invert);
}
^FluidNormalizeQuery.kr(trig,
this, this.prEncodeBuffer(inputBuffer), this.prEncodeBuffer(outputBuffer), this.min, this.max, invert);
}
}
FluidNormalizeQuery : FluidRTMultiOutUGen {
*kr{ |trig, model,inputBuffer,outputBuffer,min = 0 ,max = 1,invert = 0|
*kr{ |trig, model,inputBuffer,outputBuffer,min = 0 ,max = 1,invert = 0|
// inputBuffer.asUGenInput.postln;
^this.multiNew('control',trig, model.asUGenInput,
min,max,invert,
inputBuffer.asUGenInput, outputBuffer.asUGenInput)
}
^this.multiNew('control',trig, model.asUGenInput,
min,max,invert,
inputBuffer.asUGenInput, outputBuffer.asUGenInput)
}
init { arg ... theInputs;
init { arg ... theInputs;
inputs = theInputs;
^this.initOutputs(1, rate);
}

@ -1,11 +1,11 @@
FluidNoveltyFeature : FluidRTUGen {
*kr { arg in = 0, algorithm = 0, kernelSize = 3, filterSize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = -1, maxKernelSize, maxFilterSize;
maxKernelSize = maxKernelSize ? kernelSize;
maxFilterSize = maxFilterSize ? filterSize;
algorithm = FluidNoveltySlice.prSelectAlgorithm(algorithm) ?? {
("FluidNoveltySlice: % is not a recognised algorithm").format(algorithm);
};
maxKernelSize = maxKernelSize ? kernelSize;
maxFilterSize = maxFilterSize ? filterSize;
algorithm = FluidNoveltySlice.prSelectAlgorithm(algorithm) ?? {
("FluidNoveltySlice: % is not a recognised algorithm").format(algorithm);
};
^this.multiNew('control', in.asAudioRateInput(this), algorithm, kernelSize, maxKernelSize, filterSize, maxFilterSize, windowSize, hopSize, fftSize, maxFFTSize)
}
@ -13,16 +13,16 @@ FluidNoveltyFeature : FluidRTUGen {
checkInputs {
if([\scalar, \control].includes(inputs.at(1).rate).not) {
^(": invalid algorithm");
};
};
if(inputs.at(9).rate != 'scalar') {
^(": maxFFTSize cannot be modulated.");
};
};
if(inputs.at(3).rate != 'scalar') {
^(": maxKernelSize cannot be modulated.");
};
};
if(inputs.at(5).rate != 'scalar') {
^(": maxFilterSize cannot be modulated.");
};
};
^this.checkValidInputs;
}
}

@ -16,12 +16,12 @@ FluidNoveltySlice : FluidRTUGen {
*ar { arg in = 0, algorithm = 0, kernelSize = 3, threshold = 0.8, filterSize = 1, minSliceLength = 2, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = -1, maxKernelSize, maxFilterSize;
maxKernelSize = maxKernelSize ? kernelSize;
maxFilterSize = maxFilterSize ? filterSize;
maxKernelSize = maxKernelSize ? kernelSize;
maxFilterSize = maxFilterSize ? filterSize;
algorithm = this.prSelectAlgorithm(algorithm) ?? {
("FluidNoveltySlice: % is not a recognised algorithm").format(algorithm);
};
algorithm = this.prSelectAlgorithm(algorithm) ?? {
("FluidNoveltySlice: % is not a recognised algorithm").format(algorithm);
};
^this.multiNew('audio', in.asAudioRateInput(this), algorithm, kernelSize, maxKernelSize, threshold, filterSize, maxFilterSize, minSliceLength, windowSize, hopSize, fftSize, maxFFTSize)
}
@ -29,16 +29,16 @@ FluidNoveltySlice : FluidRTUGen {
checkInputs {
if([\scalar, \control].includes(inputs.at(1).rate).not) {
^(": invalid algorithm");
};
};
if(inputs.at(11).rate != 'scalar') {
^(": maxFFTSize cannot be modulated.");
};
};
if(inputs.at(3).rate != 'scalar') {
^(": maxKernelSize cannot be modulated.");
};
};
if(inputs.at(6).rate != 'scalar') {
^(": maxFilterSize cannot be modulated.");
};
};
^this.checkValidInputs;
}
}

@ -10,10 +10,10 @@ FluidOnsetFeature : FluidRTUGen {
checkInputs {
if([\scalar, \control].includes(inputs.at(1).rate).not) {
^(": invalid metric");
};
};
if(inputs.at(7).rate != 'scalar') {
^(": maxFFTSize cannot be modulated.");
};
};
^this.checkValidInputs;
}
}

@ -36,10 +36,10 @@ FluidOnsetSlice : FluidRTUGen {
checkInputs {
if([\scalar, \control].includes(inputs.at(1).rate).not) {
^(": invalid metric");
};
};
if(inputs.at(9).rate != 'scalar') {
^(": maxFFTSize cannot be modulated.");
};
};
^this.checkValidInputs;
}
}

@ -1,62 +1,62 @@
FluidPCA : FluidModelObject{
var <>numDimensions, <>whiten;
var <>numDimensions, <>whiten;
*new {|server, numDimensions = 2, whiten = 0|
*new {|server, numDimensions = 2, whiten = 0|
^super.new(server,[numDimensions, whiten]).numDimensions_(numDimensions).whiten_(whiten);
}
}
prGetParams{
^[this.id, numDimensions, whiten];
}
prGetParams{
^[this.id, numDimensions, whiten];
}
fitMsg{|dataSet|
^this.prMakeMsg(\fit,id, dataSet.id);
}
fitMsg{|dataSet|
^this.prMakeMsg(\fit,id, dataSet.id);
}
fit{|dataSet, action|
actions[\fit] = [nil, action];
this.prSendMsg(this.fitMsg(dataSet));
}
fit{|dataSet, action|
actions[\fit] = [nil, action];
this.prSendMsg(this.fitMsg(dataSet));
}
transformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\transform, id, sourceDataSet.id, destDataSet.id);
}
transformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\transform, id, sourceDataSet.id, destDataSet.id);
}
transform{|sourceDataSet, destDataSet, action|
actions[\transform] = [numbers(FluidMessageResponse,_,1,_),action];
this.prSendMsg(this.transformMsg(sourceDataSet,destDataSet));
}
transform{|sourceDataSet, destDataSet, action|
actions[\transform] = [numbers(FluidMessageResponse,_,1,_),action];
this.prSendMsg(this.transformMsg(sourceDataSet,destDataSet));
}
fitTransformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\fitTransform,id, sourceDataSet.id, destDataSet.id);
}
fitTransformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\fitTransform,id, sourceDataSet.id, destDataSet.id);
}
fitTransform{|sourceDataSet, destDataSet, action|
actions[\fitTransform] = [numbers(FluidMessageResponse,_,1,_),action];
this.prSendMsg(this.fitTransformMsg(sourceDataSet,destDataSet));
}
fitTransform{|sourceDataSet, destDataSet, action|
actions[\fitTransform] = [numbers(FluidMessageResponse,_,1,_),action];
this.prSendMsg(this.fitTransformMsg(sourceDataSet,destDataSet));
}
transformPointMsg{|sourceBuffer, destBuffer|
^this.prMakeMsg(\transformPoint,id,
this.prEncodeBuffer(sourceBuffer),
this.prEncodeBuffer(destBuffer),
["/b_query",destBuffer.asUGenInput]
);
}
transformPointMsg{|sourceBuffer, destBuffer|
^this.prMakeMsg(\transformPoint,id,
this.prEncodeBuffer(sourceBuffer),
this.prEncodeBuffer(destBuffer),
["/b_query",destBuffer.asUGenInput]
);
}
transformPoint{|sourceBuffer, destBuffer, action|
actions[\transformPoint] = [nil,{action.value(destBuffer)}];
this.prSendMsg(this.transformPointMsg(sourceBuffer,destBuffer));
}
transformPoint{|sourceBuffer, destBuffer, action|
actions[\transformPoint] = [nil,{action.value(destBuffer)}];
this.prSendMsg(this.transformPointMsg(sourceBuffer,destBuffer));
}
kr{|trig, inputBuffer,outputBuffer,numDimensions|
kr{|trig, inputBuffer,outputBuffer,numDimensions|
numDimensions = numDimensions ? this.numDimensions;
this.numDimensions_(numDimensions);
numDimensions = numDimensions ? this.numDimensions;
this.numDimensions_(numDimensions);
^FluidPCAQuery.kr(trig ,this, this.prEncodeBuffer(inputBuffer), this.prEncodeBuffer(outputBuffer), this.numDimensions, this.whiten);
}
^FluidPCAQuery.kr(trig ,this, this.prEncodeBuffer(inputBuffer), this.prEncodeBuffer(outputBuffer), this.numDimensions, this.whiten);
}
inverseTransformPointMsg{|sourceBuffer, destBuffer|
^this.prMakeMsg(\inverseTransformPoint,id,
@ -71,26 +71,26 @@ FluidPCA : FluidModelObject{
this.prSendMsg(this.inverseTransformPointMsg(sourceBuffer,destBuffer));
}
inverseTransformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\inverseTransform,id,sourceDataSet.id, destDataSet.id);
}
inverseTransformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\inverseTransform,id,sourceDataSet.id, destDataSet.id);
}
inverseTransform{|sourceDataSet, destDataSet,action|
actions[\inverseTransform] = [nil,action];
this.prSendMsg(this.inverseTransformMsg(sourceDataSet, destDataSet));
}
inverseTransform{|sourceDataSet, destDataSet,action|
actions[\inverseTransform] = [nil,action];
this.prSendMsg(this.inverseTransformMsg(sourceDataSet, destDataSet));
}
}
FluidPCAQuery : FluidRTMultiOutUGen {
*kr{ |trig, model, inputBuffer,outputBuffer,numDimensions, whiten|
^this.multiNew('control',trig, model.asUGenInput,
numDimensions, whiten,
inputBuffer.asUGenInput, outputBuffer.asUGenInput)
}
*kr{ |trig, model, inputBuffer,outputBuffer,numDimensions, whiten|
^this.multiNew('control',trig, model.asUGenInput,
numDimensions, whiten,
inputBuffer.asUGenInput, outputBuffer.asUGenInput)
}
init { arg ... theInputs;
init { arg ... theInputs;
inputs = theInputs;
^this.initOutputs(1, rate);
}

@ -1,44 +1,44 @@
FluidPitch : FluidRTMultiOutUGen {
const <features=#[\pitch, \confidence];
classvar featuresLookup;
const <features=#[\pitch, \confidence];
classvar featuresLookup;
*initClass {
featuresLookup = Dictionary.with(*this.features.collect{|x,i| x->(1<<i)});
}
*prWarnUnrecognised {|sym| ("WARNING: FluidPitch -" + sym + "is not a recognised option").postln}
*initClass {
featuresLookup = Dictionary.with(*this.features.collect{|x,i| x->(1<<i)});
}
*prProcessSelect {|a|
var bits;
a.asBag.countsDo{|item,count,i|
if(count > 1) { ("Option '" ++ item ++ "' is repeated").warn};
};
bits = a.collect{ |sym|
(featuresLookup[sym.asSymbol] !? {|x| x} ?? {this.prWarnUnrecognised(sym); 0})
}.reduce{|x,y| x | y};
^bits
}
*prWarnUnrecognised {|sym| ("WARNING: FluidPitch -" + sym + "is not a recognised option").postln}
*prProcessSelect {|a|
var bits;
a.asBag.countsDo{|item,count,i|
if(count > 1) { ("Option '" ++ item ++ "' is repeated").warn};
};
bits = a.collect{ |sym|
(featuresLookup[sym.asSymbol] !? {|x| x} ?? {this.prWarnUnrecognised(sym); 0})
}.reduce{|x,y| x | y};
^bits
}
*kr { arg in = 0, select, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = -1;
var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)};
var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)};
^this.multiNew('control', in.asAudioRateInput(this), selectbits, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, maxFFTSize);
}
init {arg ...theInputs;
var numChannels;
var numChannels;
inputs = theInputs;
numChannels = inputs.at(1).asBinaryDigits.sum;
numChannels = inputs.at(1).asBinaryDigits.sum;
^this.initOutputs(numChannels,rate);
}
checkInputs {
if(inputs.at(9).rate != 'scalar') {
^(": maxFFTSize cannot be modulated.");
};
};
^this.checkValidInputs;
}
}

@ -1,95 +1,95 @@
FluidRobustScale : FluidModelObject {
var <>low, <>high;
var <>low, <>high;
*new {|server, low = 25, high = 75|
^super.new(server,[low,high])
.low_(low).high_(high);
}
prGetParams{
^[this.id,this.low,this.high];
}
prGetParams{
^[this.id,this.low,this.high];
}
fitMsg{|dataSet|
^this.prMakeMsg(\fit,id,dataSet.id)
}
fitMsg{|dataSet|
^this.prMakeMsg(\fit,id,dataSet.id)
}
fit{|dataSet, action|
actions[\fit] = [nil,action];
actions[\fit] = [nil,action];
this.prSendMsg(this.fitMsg(dataSet));
}
transformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\transform,id,sourceDataSet.id,destDataSet.id);
}
transformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\transform,id,sourceDataSet.id,destDataSet.id);
}
transform{|sourceDataSet, destDataSet, action|
actions[\transform] = [nil,action];
this.prSendMsg(this.transformMsg(sourceDataSet, destDataSet));
this.prSendMsg(this.transformMsg(sourceDataSet, destDataSet));
}
fitTransformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\fitTransform,id,sourceDataSet.id,destDataSet.id)
}
fitTransformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\fitTransform,id,sourceDataSet.id,destDataSet.id)
}
fitTransform{|sourceDataSet, destDataSet, action|
actions[\fitTransform] = [nil,action];
actions[\fitTransform] = [nil,action];
this.prSendMsg(this.fitTransformMsg(sourceDataSet, destDataSet));
}
transformPointMsg{|sourceBuffer, destBuffer|
^this.prMakeMsg(\transformPoint,id,
this.prEncodeBuffer(sourceBuffer),
this.prEncodeBuffer(destBuffer),
["/b_query",destBuffer.asUGenInput]
);
}
transformPointMsg{|sourceBuffer, destBuffer|
^this.prMakeMsg(\transformPoint,id,
this.prEncodeBuffer(sourceBuffer),
this.prEncodeBuffer(destBuffer),
["/b_query",destBuffer.asUGenInput]
);
}
transformPoint{|sourceBuffer, destBuffer, action|
actions[\transformPoint] = [nil,{action.value(destBuffer)}];
this.prSendMsg(this.transformPointMsg(sourceBuffer, destBuffer));
actions[\transformPoint] = [nil,{action.value(destBuffer)}];
this.prSendMsg(this.transformPointMsg(sourceBuffer, destBuffer));
}
inverseTransformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\inverseTransform,id,sourceDataSet.id,destDataSet.id);
}
inverseTransformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\inverseTransform,id,sourceDataSet.id,destDataSet.id);
}
inverseTransform{|sourceDataSet, destDataSet, action|
actions[\inverseTransform] = [nil,action];
this.prSendMsg(this.inverseTransformMsg(sourceDataSet, destDataSet));
this.prSendMsg(this.inverseTransformMsg(sourceDataSet, destDataSet));
}
inverseTransformPointMsg{|sourceBuffer, destBuffer|
^this.prMakeMsg(\inverseTransformPoint,id,
this.prEncodeBuffer(sourceBuffer),
this.prEncodeBuffer(destBuffer),
["/b_query",destBuffer.asUGenInput]
);
}
inverseTransformPointMsg{|sourceBuffer, destBuffer|
^this.prMakeMsg(\inverseTransformPoint,id,
this.prEncodeBuffer(sourceBuffer),
this.prEncodeBuffer(destBuffer),
["/b_query",destBuffer.asUGenInput]
);
}
inverseTransformPoint{|sourceBuffer, destBuffer, action|
actions[\inverseRransformPoint] = [nil,{action.value(destBuffer)}];
this.prSendMsg(this.inverseTransformPointMsg(sourceBuffer, destBuffer));
actions[\inverseRransformPoint] = [nil,{action.value(destBuffer)}];
this.prSendMsg(this.inverseTransformPointMsg(sourceBuffer, destBuffer));
}
kr{|trig, inputBuffer,outputBuffer,invert = 0|
kr{|trig, inputBuffer,outputBuffer,invert = 0|
^FluidRobustScaleQuery.kr(trig,this, this.prEncodeBuffer(inputBuffer), this.prEncodeBuffer(outputBuffer), invert);
}
^FluidRobustScaleQuery.kr(trig,this, this.prEncodeBuffer(inputBuffer), this.prEncodeBuffer(outputBuffer), invert);
}
}
FluidRobustScaleQuery : FluidRTMultiOutUGen {
*kr{ |trig, model, inputBuffer,outputBuffer,invert|
^this.multiNew('control',trig, model.asUGenInput,
invert,
inputBuffer.asUGenInput, outputBuffer.asUGenInput)
}
*kr{ |trig, model, inputBuffer,outputBuffer,invert|
^this.multiNew('control',trig, model.asUGenInput,
invert,
inputBuffer.asUGenInput, outputBuffer.asUGenInput)
}
init { arg ... theInputs;
init { arg ... theInputs;
inputs = theInputs;
^this.initOutputs(1, rate);
}

@ -85,9 +85,9 @@ FluidSKMeans : FluidModelObject {
encodePointMsg{ |sourceBuffer, targetBuffer|
^this.prMakeMsg(\encodePoint, id,
this.prEncodeBuffer(sourceBuffer),
this.prEncodeBuffer(targetBuffer),
["/b_query", targetBuffer.asUGenInput]);
}
this.prEncodeBuffer(targetBuffer),
["/b_query", targetBuffer.asUGenInput]);
}
encodePoint { |sourceBuffer, targetBuffer, action|
actions[\encodePoint] = [nil,{action.value(targetBuffer)}];
@ -96,40 +96,40 @@ FluidSKMeans : FluidModelObject {
getMeansMsg{|dataSet| ^this.prMakeMsg(\getMeans, id, dataSet.asUGenInput) }
getMeans{ |dataSet, action|
actions[\getMeans] = [nil, action];
getMeans{ |dataSet, action|
actions[\getMeans] = [nil, action];
this.prSendMsg(this.getMeansMsg(dataSet));
}
setMeansMsg{|dataSet| ^this.prMakeMsg(\setMeans, id, dataSet.asUGenInput) }
setMeans{ |dataSet, action|
actions[\setMeans] = [nil, action];
setMeans{ |dataSet, action|
actions[\setMeans] = [nil, action];
this.prSendMsg(this.setMeansMsg(dataSet));
}
clearMsg{ ^this.prMakeMsg(\clear, id) }
clear{ |action|
actions[\clear] = [nil, action];
clear{ |action|
actions[\clear] = [nil, action];
this.prSendMsg(this.clearMsg);
}
kr{|trig, inputBuffer,outputBuffer|
^FluidSKMeansQuery.kr(trig,
this,
this.prEncodeBuffer(inputBuffer),
this.prEncodeBuffer(outputBuffer));
^FluidSKMeansQuery.kr(trig,
this,
this.prEncodeBuffer(inputBuffer),
this.prEncodeBuffer(outputBuffer));
}
}
FluidSKMeansQuery : FluidRTMultiOutUGen {
*kr{ |trig, model,inputBuffer, outputBuffer |
^this.multiNew('control',trig, model.asUGenInput,inputBuffer.asUGenInput, outputBuffer.asUGenInput)
}
*kr{ |trig, model,inputBuffer, outputBuffer |
^this.multiNew('control',trig, model.asUGenInput,inputBuffer.asUGenInput, outputBuffer.asUGenInput)
}
init { arg ... theInputs;
init { arg ... theInputs;
inputs = theInputs;
^this.initOutputs(1, rate);
}

@ -5,7 +5,7 @@ FluidSTFTPass : FluidRTUGen {
checkInputs {
if(inputs.at(4).rate != 'scalar') {
^": maxFFTSize cannot be modulated.";
};
};
^this.checkValidInputs
}
}

@ -3,7 +3,7 @@ FluidProxyUgen : UGen {
var <>pluginname;
*kr { |pluginname...args|
args = args.collect{|x| x.asUGenInput}
args = args.collect{|x| x.asUGenInput}
^this.new1('control', pluginname,*args)
}
@ -17,9 +17,9 @@ FluidProxyUgen : UGen {
^pluginname.asString;
}
poll{ |trig = 10, label, trigid = -1|
^super.poll(trig, label ? this.name, trigid)
}
poll{ |trig = 10, label, trigid = -1|
^super.poll(trig, label ? this.name, trigid)
}
}
FluidServerCache {
@ -36,17 +36,17 @@ FluidServerCache {
cache[server]!?{cache[server].do{|x|func.value(x)}}
}
doAll {|func|
cache.do{|subCache|
subCache.do{|item|
func.value(item)
}
}
}
doAll {|func|
cache.do{|subCache|
subCache.do{|item|
func.value(item)
}
}
}
postln{
cache.postln;
}
postln{
cache.postln;
}
at { |server,id|
^cache[server].tryPerform(\at,id)
@ -68,7 +68,7 @@ FluidServerCache {
cache[server] ?? {
cache[server] = IdentityDictionary.new;
NotificationCenter.register(server,\newAllocators,this,
{
{
this.clearCache(server);
});
}
@ -76,18 +76,18 @@ FluidServerCache {
clearCache { |server|
cache[server] !?
{
var bundle = [];
cache[server].values.do{|i|
if(i.respondsTo(\freeMsg)){
bundle = bundle.add(i.freeMsg); //server objects
}{
i.free; //OSCFunc
}
};
server.listSendBundle(nil,bundle);
cache.removeAt(server);
};
{
var bundle = [];
cache[server].values.do{|i|
if(i.respondsTo(\freeMsg)){
bundle = bundle.add(i.freeMsg); //server objects
}{
i.free; //OSCFunc
}
};
server.listSendBundle(nil,bundle);
cache.removeAt(server);
};
}
}

@ -1,346 +1,346 @@
FluidServerObject
{
classvar serverCaches;
classvar count;
classvar persistent = true;
var <server,<id;
*version{|server|
server ?? {server = Server.default};
server.sendMsg("/cmd","/"++this.objectClassName++'/version');
}
*initClass {
serverCaches = IdentityDictionary.new;
count = 0;
ServerBoot.add({serverCaches[this]!?{serverCaches[this].cache.put(Server.internal,nil);}},Server.internal);
}
*initCache {|server|
serverCaches[this] ?? { serverCaches[this] = FluidServerCache.new};
if(server === Server.internal and: serverCaches[this].cache[Server.internal].isNil)
{
this.flush(Server.internal)
};
serverCaches[this].initCache(server);
NotificationCenter.register(server,\newAllocators,this,{ count = 0; });
}
*newMsg{|id, params|
params = params !? {params.collect(_.asUGenInput)};
^this.prMakeMsg(\new,id,*params);
}
*new{ |server, id, params, action, callNew = true|
var newObj;
server ?? {server = Server.default};
if(server.serverRunning.not){"Server not running".warn};
id !? { id = id.asInteger }
?? { id = count; count = count + 1; };
newObj = super.newCopyArgs(server,id,action);
// params.postln;
if(callNew) {server.listSendMsg(this.newMsg(id,params))};
^newObj.cache
}
cache {
this.class.initCache(server);
serverCaches[this.class].put(server,this.id,this);
}
uncache{
serverCaches[this.class].remove(server,id);
}
*prMakeMsg{|msg,id...args|
var commandName = "%/%".format(this.objectClassName,msg);
^['/cmd', this.objectClassName,commandName,id].addAll(args);
}
prMakeMsg{|msg,id...args| ^this.class.prMakeMsg(msg,id,*args) }
freeMsg {
var msg;
id ?? {" % already freed".format(this.class.name).warn; ^nil};
this.uncache;
msg = this.prMakeMsg(\free,id);
id = nil;
^msg;
}
free{
var msg = this.freeMsg;
msg !? {server.listSendMsg(msg)} ?? {^nil};
}
*freeAll{|server|
serverCaches[this] !? {|cache|
cache.clearCache(server ? Server.default);
};
count = 0;
}
asUGenInput{ ^id }
asString {
^"%(%)".format(this.class.name,id).asString;
}
asSymbol {
^id.asSymbol
}
*objectClassName { ^this.name.asSymbol }
*flushMsg { ^['/cmd',this.objectClassName ++ '/flush'] }
*flush {|server| server.listSendMsg(this.flushMsg)}
classvar serverCaches;
classvar count;
classvar persistent = true;
var <server,<id;
*version{|server|
server ?? {server = Server.default};
server.sendMsg("/cmd","/"++this.objectClassName++'/version');
}
*initClass {
serverCaches = IdentityDictionary.new;
count = 0;
ServerBoot.add({serverCaches[this]!?{serverCaches[this].cache.put(Server.internal,nil);}},Server.internal);
}
*initCache {|server|
serverCaches[this] ?? { serverCaches[this] = FluidServerCache.new};
if(server === Server.internal and: serverCaches[this].cache[Server.internal].isNil)
{
this.flush(Server.internal)
};
serverCaches[this].initCache(server);
NotificationCenter.register(server,\newAllocators,this,{ count = 0; });
}
*newMsg{|id, params|
params = params !? {params.collect(_.asUGenInput)};
^this.prMakeMsg(\new,id,*params);
}
*new{ |server, id, params, action, callNew = true|
var newObj;
server ?? {server = Server.default};
if(server.serverRunning.not){"Server not running".warn};
id !? { id = id.asInteger }
?? { id = count; count = count + 1; };
newObj = super.newCopyArgs(server,id,action);
// params.postln;
if(callNew) {server.listSendMsg(this.newMsg(id,params))};
^newObj.cache
}
cache {
this.class.initCache(server);
serverCaches[this.class].put(server,this.id,this);
}
uncache{
serverCaches[this.class].remove(server,id);
}
*prMakeMsg{|msg,id...args|
var commandName = "%/%".format(this.objectClassName,msg);
^['/cmd', this.objectClassName,commandName,id].addAll(args);
}
prMakeMsg{|msg,id...args| ^this.class.prMakeMsg(msg,id,*args) }
freeMsg {
var msg;
id ?? {" % already freed".format(this.class.name).warn; ^nil};
this.uncache;
msg = this.prMakeMsg(\free,id);
id = nil;
^msg;
}
free{
var msg = this.freeMsg;
msg !? {server.listSendMsg(msg)} ?? {^nil};
}
*freeAll{|server|
serverCaches[this] !? {|cache|
cache.clearCache(server ? Server.default);
};
count = 0;
}
asUGenInput{ ^id }
asString {
^"%(%)".format(this.class.name,id).asString;
}
asSymbol {
^id.asSymbol
}
*objectClassName { ^this.name.asSymbol }
*flushMsg { ^['/cmd',this.objectClassName ++ '/flush'] }
*flush {|server| server.listSendMsg(this.flushMsg)}
}
FluidBufProcessor : FluidServerObject
{
var <processAction;
var <outputBuffers;
var <freeWhenDone;
classvar responder;
classvar count;
*cmdPeriod {
serverCaches[this] !? {|cache|
cache.doAll{|processor| processor !? { processor.free;} };
serverCaches[this] = nil;
};
count = 0;
}
*initCache {|server|
// "initcache".postln;
// this.done.postln;
super.initCache(server);
CmdPeriod.add(this);
if(serverCaches[this].includesKey(server,\processResponder).not)
{
serverCaches[this].put(server,\processResponder,OSCFunc({|m|
var id = m.last.asInteger;
// "I'm in the pizza hut".postln;
serverCaches[this].at(server,id) !? {|p|
// "I'm in the taco bell".postln ;
p!?{
p.processAction!?{|a|
var bufs = p.outputBuffers;
bufs = bufs.collect{|b|
if(b.isKindOf(Buffer))
{b}
{Buffer.cachedBufferAt(server,b)};
};
a.valueArray(valueArray(bufs));
};
if(p.freeWhenDone){p.free};
}
}
},this.done ,server.addr).fix)
}
}
*new {|server,id,outputBuffers|
^super.new(server,id, nil, nil,false).init(outputBuffers);
}
init{ |ob|
outputBuffers = ob;
}
*done {
^"/%/process".format(this.objectClassName);
}
wait {
var condition = Condition.new;
id ?? {Error("% already freed".format(this.class.name)).throw};
OSCFunc({
condition.unhang;
},this.class.done,server.addr,argTemplate:[nil,id]).oneShot;
condition.hang;
}
processMsg {|params|
var msg;
var completionMsg = outputBuffers !? {
[["/sync"]] ++ outputBuffers.collect{|b| ["/b_query", b.asUGenInput]}
} ?? {[]};
// completionMsg.postln;
id ?? {Error("% already freed".format(this.class.name)).throw};
msg = this.prMakeMsg(\processNew,id).addAll(params).add(completionMsg);
// msg.postln;
^msg;
}
processList { |params,shouldFree,action|
freeWhenDone = shouldFree;
processAction = action;
params = params.collect(_.asUGenInput);
server.listSendMsg(this.processMsg(params));
}
cancelMsg{
id ?? {Error("% already freed".format(this.class.name)).throw};
^this.prMakeMsg(\cancel, id);
}
cancel{
server.listSendMsg(this.cancelMsg);
}
kr{ ^FluidProxyUgen.kr(this.class.objectClassName ++ "Monitor",id) }
var <processAction;
var <outputBuffers;
var <freeWhenDone;
classvar responder;
classvar count;
*cmdPeriod {
serverCaches[this] !? {|cache|
cache.doAll{|processor| processor !? { processor.free;} };
serverCaches[this] = nil;
};
count = 0;
}
*initCache {|server|
// "initcache".postln;
// this.done.postln;
super.initCache(server);
CmdPeriod.add(this);
if(serverCaches[this].includesKey(server,\processResponder).not)
{
serverCaches[this].put(server,\processResponder,OSCFunc({|m|
var id = m.last.asInteger;
// "I'm in the pizza hut".postln;
serverCaches[this].at(server,id) !? {|p|
// "I'm in the taco bell".postln ;
p!?{
p.processAction!?{|a|
var bufs = p.outputBuffers;
bufs = bufs.collect{|b|
if(b.isKindOf(Buffer))
{b}
{Buffer.cachedBufferAt(server,b)};
};
a.valueArray(valueArray(bufs));
};
if(p.freeWhenDone){p.free};
}
}
},this.done ,server.addr).fix)
}
}
*new {|server,id,outputBuffers|
^super.new(server,id, nil, nil,false).init(outputBuffers);
}
init{ |ob|
outputBuffers = ob;
}
*done {
^"/%/process".format(this.objectClassName);
}
wait {
var condition = Condition.new;
id ?? {Error("% already freed".format(this.class.name)).throw};
OSCFunc({
condition.unhang;
},this.class.done,server.addr,argTemplate:[nil,id]).oneShot;
condition.hang;
}
processMsg {|params|
var msg;
var completionMsg = outputBuffers !? {
[["/sync"]] ++ outputBuffers.collect{|b| ["/b_query", b.asUGenInput]}
} ?? {[]};
// completionMsg.postln;
id ?? {Error("% already freed".format(this.class.name)).throw};
msg = this.prMakeMsg(\processNew,id).addAll(params).add(completionMsg);
// msg.postln;
^msg;
}
processList { |params,shouldFree,action|
freeWhenDone = shouldFree;
processAction = action;
params = params.collect(_.asUGenInput);
server.listSendMsg(this.processMsg(params));
}
cancelMsg{
id ?? {Error("% already freed".format(this.class.name)).throw};
^this.prMakeMsg(\cancel, id);
}
cancel{
server.listSendMsg(this.cancelMsg);
}
kr{ ^FluidProxyUgen.kr(this.class.objectClassName ++ "Monitor",id) }
}
FluidOSCPatternInversion : OSCMessageDispatcher
{
value {|msg, time, addr, recvPort|
var msgpath = msg[0].asSymbol;
active.keysValuesDo({|key, func|
if(msgpath.matchOSCAddressPattern(key), {func.value(msg, time, addr, recvPort);});
})
}
value {|msg, time, addr, recvPort|
var msgpath = msg[0].asSymbol;
active.keysValuesDo({|key, func|
if(msgpath.matchOSCAddressPattern(key), {func.value(msg, time, addr, recvPort);});
})
}
}
FluidDataObject : FluidServerObject
{
classvar postResponse;
var <actions;
*initClass{
postResponse = _.postln;
}
*initCache{ |server|
super.initCache(server);
if(serverCaches[this].includesKey(server,\messageResponder).not)
{
serverCaches[this].put(server,\messageResponder,OSCFunc.new({|m|
var id = m[1].asInteger;
var method;
serverCaches[this].at(server,id) !? { |p|
method = m[0].asString.findRegexp("/"++this.name++"/(.*)")[1][1].asSymbol;
p.actions[method] !? {|a|
//two items: parser and action
var parser = a[0];
var action = a[1];
var result = FluidMessageResponse.collectArgs(parser,m[2..]);
action.value(result);
}
}
},'/' ++ this.objectClassName ++ '/*',server.addr, dispatcher:FluidOSCPatternInversion.new).fix)
}
}
*new{|server...args|
// args.flatten.postln;
^super.new(server,params:args.flatten).init;
}
*cachedInstanceAt{|server,id|
this.initCache(server);
^serverCaches[this].at(server,id);
}
init {
actions = IdentityDictionary.new;
}
prEncodeBuffer { |buf| buf !? {^buf.asUGenInput} ?? {^-1} }
prSendMsg {|msg| server !? {server.listSendMsg(msg)};}
colsMsg { ^this.prMakeMsg(\cols,id);}
cols{ |action=(postResponse)|
actions[\cols] = [numbers(FluidMessageResponse,_,1,_),action];
this.prSendMsg(this.colsMsg)
}
readMsg { |filename| ^this.prMakeMsg(\read,id,filename.asString);}
read{|filename, action|
actions[\read] = [nil,action];
this.prSendMsg(this.readMsg(filename));
}
writeMsg {|filename|
// ^['/cmd',this.class.name ++ '/write',id,filename.asString]
^this.prMakeMsg(\write,id,filename.asString);
}
write{|filename, action|
actions[\write] = [nil,action];
this.prSendMsg(this.writeMsg(filename));
}
sizeMsg{
// ^['/cmd',this.class.name ++ '/size',id]
^this.prMakeMsg(\size,id);
}
size {|action=(postResponse)|
actions[\size] = [numbers(FluidMessageResponse,_,1,_),action];
this.prSendMsg(this.sizeMsg);
}
classvar postResponse;
var <actions;
*initClass{
postResponse = _.postln;
}
*initCache{ |server|
super.initCache(server);
if(serverCaches[this].includesKey(server,\messageResponder).not)
{
serverCaches[this].put(server,\messageResponder,OSCFunc.new({|m|
var id = m[1].asInteger;
var method;
serverCaches[this].at(server,id) !? { |p|
method = m[0].asString.findRegexp("/"++this.name++"/(.*)")[1][1].asSymbol;
p.actions[method] !? {|a|
//two items: parser and action
var parser = a[0];
var action = a[1];
var result = FluidMessageResponse.collectArgs(parser,m[2..]);
action.value(result);
}
}
},'/' ++ this.objectClassName ++ '/*',server.addr, dispatcher:FluidOSCPatternInversion.new).fix)
}
}
*new{|server...args|
// args.flatten.postln;
^super.new(server,params:args.flatten).init;
}
*cachedInstanceAt{|server,id|
this.initCache(server);
^serverCaches[this].at(server,id);
}
init {
actions = IdentityDictionary.new;
}
prEncodeBuffer { |buf| buf !? {^buf.asUGenInput} ?? {^-1} }
prSendMsg {|msg| server !? {server.listSendMsg(msg)};}
colsMsg { ^this.prMakeMsg(\cols,id);}
cols{ |action=(postResponse)|
actions[\cols] = [numbers(FluidMessageResponse,_,1,_),action];
this.prSendMsg(this.colsMsg)
}
readMsg { |filename| ^this.prMakeMsg(\read,id,filename.asString);}
read{|filename, action|
actions[\read] = [nil,action];
this.prSendMsg(this.readMsg(filename));
}
writeMsg {|filename|
// ^['/cmd',this.class.name ++ '/write',id,filename.asString]
^this.prMakeMsg(\write,id,filename.asString);
}
write{|filename, action|
actions[\write] = [nil,action];
this.prSendMsg(this.writeMsg(filename));
}
sizeMsg{
// ^['/cmd',this.class.name ++ '/size',id]
^this.prMakeMsg(\size,id);
}
size {|action=(postResponse)|
actions[\size] = [numbers(FluidMessageResponse,_,1,_),action];
this.prSendMsg(this.sizeMsg);
}
}
FluidModelObject : FluidDataObject
{
prGetParams{
"Subclass should provide this".throw;
}
prUpdateStateMsg{
var params = this.prGetParams.value.collect(_.asUGenInput);
^this.prMakeMsg(\setParams,id) ++ params;
}
prSendMsg {|msg|
//These need to happen sequentially, but not simultaneously
//sending as a bundle makes reasoning about timing w/r/t other
//commands more awkward, unless we set the offet to 0 (in which case,
//noisy 'late' messages)
super.prSendMsg(this.prUpdateStateMsg);
super.prSendMsg(msg);
}
prGetParams{
"Subclass should provide this".throw;
}
prUpdateStateMsg{
var params = this.prGetParams.value.collect(_.asUGenInput);
^this.prMakeMsg(\setParams,id) ++ params;
}
prSendMsg {|msg|
//These need to happen sequentially, but not simultaneously
//sending as a bundle makes reasoning about timing w/r/t other
//commands more awkward, unless we set the offet to 0 (in which case,
//noisy 'late' messages)
super.prSendMsg(this.prUpdateStateMsg);
super.prSendMsg(msg);
}
}
FluidRealTimeModel : FluidModelObject
{
*new{ |server, params|
^super.new(server,params++[-1,-1]);
}
*new{ |server, params|
^super.new(server,params++[-1,-1]);
}
}
FluidRTQuery : FluidProxyUgen
{
*kr{ |trig,obj...args|
^super.kr(this.name,trig,obj.asUGenInput, *args)
}
*kr{ |trig,obj...args|
^super.kr(this.name,trig,obj.asUGenInput, *args)
}
}
FluidRTUGen : UGen
{
*version{|server|
server ?? {server = Server.default};
server.sendMsg("/cmd","/"++this.name++'/version');
}
*version{|server|
server ?? {server = Server.default};
server.sendMsg("/cmd","/"++this.name++'/version');
}
}
FluidRTMultiOutUGen : MultiOutUGen
{
*version{|server|
server ?? {server = Server.default};
server.sendMsg("/cmd","/"++this.name++'/version');
}
*version{|server|
server ?? {server = Server.default};
server.sendMsg("/cmd","/"++this.name++'/version');
}
}

@ -13,7 +13,7 @@ FluidSines : FluidRTMultiOutUGen {
checkInputs {
if(inputs.at(13).rate != 'scalar') {
^(": maxFFTSize cannot be modulated.");
};
};
^this.checkNInputs(1)
}
}

@ -1,44 +1,44 @@
FluidSpectralShape : FluidRTMultiOutUGen {
const <features=#[\centroid,\spread,\skewness,\kurtosis,\rolloff,\flatness,\crest];
classvar featuresLookup;
const <features=#[\centroid,\spread,\skewness,\kurtosis,\rolloff,\flatness,\crest];
classvar featuresLookup;
*initClass {
featuresLookup = Dictionary.with(*this.features.collect{|x,i| x->(1<<i)});
}
*prWarnUnrecognised {|sym| ("WARNING: FluidSpectralShape -" + sym + "is not a recognised option").postln}
*initClass {
featuresLookup = Dictionary.with(*this.features.collect{|x,i| x->(1<<i)});
}
*prProcessSelect {|a|
var bits;
a.asBag.countsDo{|item,count,i|
if(count > 1) { ("Option '" ++ item ++ "' is repeated").warn};
};
bits = a.collect{ |sym|
(featuresLookup[sym.asSymbol] !? {|x| x} ?? {this.prWarnUnrecognised(sym); 0})
}.reduce{|x,y| x | y};
^bits
}
*prWarnUnrecognised {|sym| ("WARNING: FluidSpectralShape -" + sym + "is not a recognised option").postln}
*prProcessSelect {|a|
var bits;
a.asBag.countsDo{|item,count,i|
if(count > 1) { ("Option '" ++ item ++ "' is repeated").warn};
};
bits = a.collect{ |sym|
(featuresLookup[sym.asSymbol] !? {|x| x} ?? {this.prWarnUnrecognised(sym); 0})
}.reduce{|x,y| x | y};
^bits
}
*kr { arg in = 0, select, minFreq = 0, maxFreq = -1, rolloffPercent = 95, unit = 0, power = 0, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = -1;
*kr { arg in = 0, select, minFreq = 0, maxFreq = -1, rolloffPercent = 95, unit = 0, power = 0, windowSize = 1024, hopSize = -1, fftSize = -1, maxFFTSize = -1;
var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)};
var selectbits = select !? {this.prProcessSelect(select)} ?? {this.prProcessSelect(this.features)};
^this.multiNew('control', in.asAudioRateInput(this), selectbits, minFreq, maxFreq, rolloffPercent, unit, power, windowSize, hopSize, fftSize, maxFFTSize);
^this.multiNew('control', in.asAudioRateInput(this), selectbits, minFreq, maxFreq, rolloffPercent, unit, power, windowSize, hopSize, fftSize, maxFFTSize);
}
init {arg ...theInputs;
var numChannels;
var numChannels;
inputs = theInputs;
numChannels = inputs.at(1).asBinaryDigits.sum;
numChannels = inputs.at(1).asBinaryDigits.sum;
^this.initOutputs(numChannels,rate);
}
checkInputs {
if(inputs.at(10).rate != 'scalar') {
^(": maxFFTSize cannot be modulated.");
};
};
^this.checkValidInputs;
}
}

@ -1,86 +1,86 @@
FluidStandardize : FluidModelObject {
*new {|server|
*new {|server|
^super.new(server,[]);
}
prGetParams{
^[this.id];
}
prGetParams{
^[this.id];
}
fitMsg{|dataSet|
^this.prMakeMsg(\fit,id,dataSet.id);
}
^this.prMakeMsg(\fit,id,dataSet.id);
}
fit{|dataSet, action|
actions[\fit] = [nil, action];
fit{|dataSet, action|
actions[\fit] = [nil, action];
this.prSendMsg(this.fitMsg(dataSet));
}
transformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\transform,id,sourceDataSet.id,destDataSet.id);
}
^this.prMakeMsg(\transform,id,sourceDataSet.id,destDataSet.id);
}
transform{|sourceDataSet, destDataSet, action|
actions[\transform] = [nil,action];
actions[\transform] = [nil,action];
this.prSendMsg(this.transformMsg(sourceDataSet,destDataSet));
}
fitTransformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\fitTransform,id,sourceDataSet.id,destDataSet.id)
}
fitTransformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\fitTransform,id,sourceDataSet.id,destDataSet.id)
}
fitTransform{|sourceDataSet, destDataSet, action|
actions[\fitTransform] = [nil,action];
actions[\fitTransform] = [nil,action];
this.prSendMsg(this.fitTransformMsg(sourceDataSet, destDataSet));
}
transformPointMsg{|sourceBuffer, destBuffer|
^this.prMakeMsg(\transformPoint, id, this.prEncodeBuffer(sourceBuffer), this.prEncodeBuffer(destBuffer),["/b_query",destBuffer.asUGenInput]);
}
transformPointMsg{|sourceBuffer, destBuffer|
^this.prMakeMsg(\transformPoint, id, this.prEncodeBuffer(sourceBuffer), this.prEncodeBuffer(destBuffer),["/b_query",destBuffer.asUGenInput]);
}
transformPoint{|sourceBuffer, destBuffer, action|
actions[\transformPoint] = [nil, {action.value(destBuffer)}];
this.prSendMsg(this.transformPointMsg(sourceBuffer,destBuffer));
actions[\transformPoint] = [nil, {action.value(destBuffer)}];
this.prSendMsg(this.transformPointMsg(sourceBuffer,destBuffer));
}
inverseTransformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\inverseTransform,id,sourceDataSet.id,destDataSet.id);
}
inverseTransformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\inverseTransform,id,sourceDataSet.id,destDataSet.id);
}
inverseTransform{|sourceDataSet, destDataSet, action|
actions[\inverseTransform] = [nil,action];
this.prSendMsg(this.inverseTransformMsg(sourceDataSet, destDataSet));
this.prSendMsg(this.inverseTransformMsg(sourceDataSet, destDataSet));
}
inverseTransformPointMsg{|sourceBuffer, destBuffer|
^this.prMakeMsg(\inverseTransformPoint,id,
this.prEncodeBuffer(sourceBuffer),
this.prEncodeBuffer(destBuffer),
["/b_query",destBuffer.asUGenInput]
);
}
inverseTransformPointMsg{|sourceBuffer, destBuffer|
^this.prMakeMsg(\inverseTransformPoint,id,
this.prEncodeBuffer(sourceBuffer),
this.prEncodeBuffer(destBuffer),
["/b_query",destBuffer.asUGenInput]
);
}
inverseTransformPoint{|sourceBuffer, destBuffer, action|
actions[\inverseRransformPoint] = [nil,{action.value(destBuffer)}];
this.prSendMsg(this.inverseTransformPointMsg(sourceBuffer, destBuffer));
actions[\inverseRransformPoint] = [nil,{action.value(destBuffer)}];
this.prSendMsg(this.inverseTransformPointMsg(sourceBuffer, destBuffer));
}
kr{|trig, inputBuffer,outputBuffer,invert = 0|
kr{|trig, inputBuffer,outputBuffer,invert = 0|
^FluidStandardizeQuery.kr(trig,this, this.prEncodeBuffer(inputBuffer), this.prEncodeBuffer(outputBuffer), invert);
}
^FluidStandardizeQuery.kr(trig,this, this.prEncodeBuffer(inputBuffer), this.prEncodeBuffer(outputBuffer), invert);
}
}
FluidStandardizeQuery : FluidRTMultiOutUGen {
*kr{ |trig, model,inputBuffer,outputBuffer,invert = 0|
^this.multiNew('control',trig, model.asUGenInput,
invert,
inputBuffer.asUGenInput, outputBuffer.asUGenInput)
}
*kr{ |trig, model,inputBuffer,outputBuffer,invert = 0|
^this.multiNew('control',trig, model.asUGenInput,
invert,
inputBuffer.asUGenInput, outputBuffer.asUGenInput)
}
init { arg ... theInputs;
init { arg ... theInputs;
inputs = theInputs;
^this.initOutputs(1, rate);
}

@ -1,12 +1,12 @@
FluidStats : MultiOutUGen {
*kr { arg in, history;
^this.multiNew('control',*(in.asArray++history)).reshape(2,in.asArray.size);
^this.multiNew('control',*(in.asArray++history)).reshape(2,in.asArray.size);
}
init {arg ...theInputs;
inputs = theInputs;
this.specialIndex = (inputs.size - 2).max(0);
this.specialIndex = (inputs.size - 2).max(0);
^this.initOutputs(inputs.size - 1,rate)
}
@ -14,17 +14,17 @@ FluidStats : MultiOutUGen {
^this.checkValidInputs;
}
initOutputs{|numChans,rate|
if(numChans.isNil or: {numChans < 1})
{
Error("No input channels").throw
};
initOutputs{|numChans,rate|
if(numChans.isNil or: {numChans < 1})
{
Error("No input channels").throw
};
channels = Array.fill(numChans * 2, { |i|
OutputProxy('control',this,i);
});
^channels
}
channels = Array.fill(numChans * 2, { |i|
OutputProxy('control',this,i);
});
^channels
}
numOutputs { ^(channels.size); }
numOutputs { ^(channels.size); }
}

@ -1,75 +1,75 @@
FluidUMAP : FluidModelObject {
var <>numDimensions, <>numNeighbours, <>minDist, <>iterations, <>learnRate;
var <>numDimensions, <>numNeighbours, <>minDist, <>iterations, <>learnRate;
*new {|server,numDimensions = 2, numNeighbours = 15, minDist = 0.1, iterations = 200, learnRate = 0.1|
^super.new(server,[numDimensions, numNeighbours, minDist, iterations, learnRate])
.numDimensions_(numDimensions)
.numNeighbours_(numNeighbours)
.minDist_(minDist)
.iterations_(iterations)
.learnRate_(learnRate);
.numDimensions_(numDimensions)
.numNeighbours_(numNeighbours)
.minDist_(minDist)
.iterations_(iterations)
.learnRate_(learnRate);
}
prGetParams{
^[
this.id,
this.numDimensions,
this.numNeighbours,
this.minDist,
this.iterations,
this.learnRate,
]
}
fitTransformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\fitTransform, id, sourceDataSet.id, destDataSet.id)
}
prGetParams{
^[
this.id,
this.numDimensions,
this.numNeighbours,
this.minDist,
this.iterations,
this.learnRate,
]
}
fitTransformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\fitTransform, id, sourceDataSet.id, destDataSet.id)
}
fitTransform{|sourceDataSet, destDataSet, action|
actions[\fitTransform] = [nil, action];
actions[\fitTransform] = [nil, action];
this.prSendMsg(this.fitTransformMsg(sourceDataSet,destDataSet));
}
fitMsg{|dataSet|
^this.prMakeMsg(\fit,id, dataSet.id);
}
^this.prMakeMsg(\fit,id, dataSet.id);
}
fit{|dataSet, action|
actions[\fit] = [nil, action];
this.prSendMsg(this.fitMsg(dataSet));
}
fit{|dataSet, action|
actions[\fit] = [nil, action];
this.prSendMsg(this.fitMsg(dataSet));
}
transformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\transform, id, sourceDataSet.id, destDataSet.id);
}
transformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\transform, id, sourceDataSet.id, destDataSet.id);
}
transform{|sourceDataSet, destDataSet, action|
actions[\transform] = [nil, action];
this.prSendMsg(this.transformMsg(sourceDataSet,destDataSet));
}
transform{|sourceDataSet, destDataSet, action|
actions[\transform] = [nil, action];
this.prSendMsg(this.transformMsg(sourceDataSet,destDataSet));
}
transformPointMsg{|sourceBuffer, destBuffer|
^this.prMakeMsg(\transformPoint,id,
this.prEncodeBuffer(sourceBuffer),
this.prEncodeBuffer(destBuffer),
["/b_query",destBuffer.asUGenInput]
);
}
transformPointMsg{|sourceBuffer, destBuffer|
^this.prMakeMsg(\transformPoint,id,
this.prEncodeBuffer(sourceBuffer),
this.prEncodeBuffer(destBuffer),
["/b_query",destBuffer.asUGenInput]
);
}
transformPoint{|sourceBuffer, destBuffer, action|
actions[\transformPoint] = [nil,{action.value(destBuffer)}];
this.prSendMsg(this.transformPointMsg(sourceBuffer,destBuffer));
}
transformPoint{|sourceBuffer, destBuffer, action|
actions[\transformPoint] = [nil,{action.value(destBuffer)}];
this.prSendMsg(this.transformPointMsg(sourceBuffer,destBuffer));
}
kr{|trig, inputBuffer,outputBuffer|
^FluidUMAPQuery.kr(trig,
this,
^FluidUMAPQuery.kr(trig,
this,
this.prEncodeBuffer(inputBuffer),
this.prEncodeBuffer(outputBuffer));
}
}
// not implemented
cols {|action|}
@ -77,12 +77,12 @@ FluidUMAP : FluidModelObject {
}
FluidUMAPQuery : FluidRTMultiOutUGen {
*kr{ |trig, model, inputBuffer,outputBuffer|
^this.multiNew('control',trig, model.asUGenInput,
inputBuffer.asUGenInput, outputBuffer.asUGenInput)
}
*kr{ |trig, model, inputBuffer,outputBuffer|
^this.multiNew('control',trig, model.asUGenInput,
inputBuffer.asUGenInput, outputBuffer.asUGenInput)
}
init { arg ... theInputs;
init { arg ... theInputs;
inputs = theInputs;
^this.initOutputs(1, rate);
}

@ -58,7 +58,7 @@ FluidWaveformIndicesLayer : FluidViewer {
draw {
var userView;
var condition = CondVar();
var condition = Condition();
var slices_fa = nil;
var numChannels = indicesBuffer.numChannels;
@ -81,9 +81,10 @@ FluidWaveformIndicesLayer : FluidViewer {
indicesBuffer.loadToFloatArray(action: {
arg v;
slices_fa = v;
condition.signalOne;
condition.test = true;
condition.signal;
});
condition.wait { slices_fa.notNil };
condition.wait;
userView.drawFunc = numChannels.switch(
1, {{
@ -132,7 +133,7 @@ FluidWaveformFeaturesLayer : FluidViewer {
draw {
var userView = UserView();
var condition = CondVar();
var condition = Condition();
var fa = nil;
forkIfNeeded({
@ -141,9 +142,10 @@ FluidWaveformFeaturesLayer : FluidViewer {
featuresBuffer.loadToFloatArray(action:{
arg v;
fa = v;
condition.signalOne
condition.test = true;
condition.signal;
});
condition.wait { fa.notNil };
condition.wait;
if(normalizeFeaturesIndependently.not,{
minVal = fa.minItem;
@ -209,7 +211,7 @@ FluidWaveformImageLayer {
draw {
var colors = this.prGetColorsFromScheme(imageColorScheme);
var condition = CondVar();
var condition = Condition();
var vals = nil;
var userView = UserView();
@ -218,9 +220,10 @@ FluidWaveformImageLayer {
imageBuffer.loadToFloatArray(action: {
arg v;
vals = v;
condition.signalOne;
condition.test = true;
condition.signal;
});
condition.wait { vals.notNil };
condition.wait;
imageColorScaling.switch(
FluidWaveform.lin,{
@ -430,5 +433,5 @@ FluidWaveform : FluidViewer {
};
}
asView { ^view }
asView { ^view }
}

@ -1,136 +0,0 @@
TITLE:: FluidAmpGate
SUMMARY:: Amplitude-based Gating Slicer
CATEGORIES:: Libraries>FluidCorpusManipulation
RELATED:: Guides/FluidCorpusManipulation
DESCRIPTION::
This class implements an amplitude-based slicer, with various customisable options and conditions to detect absolute amplitude changes as onsets and offsets. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
FluidAmpSlice is based on an envelop follower on a highpassed version of the signal, which is then going through a Schmidt trigger and state-aware time contraints. The example code below is unfolding the various possibilites in order of complexity.
The process will return an audio steam with square envelopes around detected slices the different slices, where 1s means in slice and 0s mean in silence.
CLASSMETHODS::
METHOD:: ar
The audio rate version of the object.
ARGUMENT:: in
The audio to be processed.
ARGUMENT:: rampUp
The number of samples the envelope follower will take to reach the next value when raising.
ARGUMENT:: rampDown
The number of samples the envelope follower will take to reach the next value when falling.
ARGUMENT:: onThreshold
The threshold in dB of the envelope follower to trigger an onset, aka to go ON when in OFF state.
ARGUMENT:: offThreshold
The threshold in dB of the envelope follower to trigger an offset, , aka to go ON when in OFF state.
ARGUMENT:: minSliceLength
The length in samples that the Slice will stay ON. Changes of states during that period will be ignored.
ARGUMENT:: minSilenceLength
The length in samples that the Slice will stay OFF. Changes of states during that period will be ignored.
ARGUMENT:: minLengthAbove
The length in samples that the envelope have to be above the threshold to consider it a valid transition to ON. The Slice will start at the first sample when the condition is met. Therefore, this affects the latency.
ARGUMENT:: minLengthBelow
The length in samples that the envelope have to be below the threshold to consider it a valid transition to OFF. The Slice will end at the first sample when the condition is met. Therefore, this affects the latency.
ARGUMENT:: lookBack
The length of the buffer kept before an onset to allow the algorithm, once a new Slice is detected, to go back in time (up to that many samples) to find the minimum amplitude as the Slice onset point. This affects the latency of the algorithm.
ARGUMENT:: lookAhead
The length of the buffer kept after an offset to allow the algorithm, once the Slice is considered finished, to wait further in time (up to that many samples) to find a minimum amplitude as the Slice offset point. This affects the latency of the algorithm.
ARGUMENT:: highPassFreq
The frequency of the fourth-order LinkwitzRiley 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. A frequency of 0 bypasses the filter.
ARGUMENT:: maxSize
How large can the buffer be for time-critical conditions, by allocating memory at instantiation time. This cannot be modulated.
RETURNS::
An audio stream with square envelopes around the slices. The latency between the input and the output is STRONG::max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead))::.
EXAMPLES::
code::
//basic tests: threshold sanity
(
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
env = FluidAmpGate.ar(source, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12);
[source, env]
}.plot(0.1);
)
//basic tests: threshold hysteresis
(
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
env = FluidAmpGate.ar(source, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -16);
[source, env]
}.plot(0.1);
)
//basic tests: threshold min slice
(
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
env = FluidAmpGate.ar(source, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, minSliceLength:441);
[source, env]
}.plot(0.1);
)
//basic tests: threshold min silence
(
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
env = FluidAmpGate.ar(source, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, minSilenceLength:441);
[source, env]
}.plot(0.1);
)
//mid tests: threshold time hysteresis on
(
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
env = FluidAmpGate.ar(source, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, minLengthAbove:441);
[DelayN.ar(source,0.1,441/44100), env]
}.plot(0.1);
)
//mid tests: threshold time hysteresis off
(
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
env = FluidAmpGate.ar(source, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, minLengthBelow:441);
[DelayN.ar(source,0.1,441/44100), env]
}.plot(0.1);
)
//mid tests: threshold with lookBack
(
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
env = FluidAmpGate.ar(source, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, lookBack:441);
[DelayN.ar(source,0.1,441/44100), env]
}.plot(0.1);
)
//mid tests: threshold with lookAhead
(
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
env = FluidAmpGate.ar(source, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, lookAhead:441);
[DelayN.ar(source,0.1,441/44100), env]
}.plot(0.1);
)
//mid tests: threshold with asymetrical lookBack and lookAhead
(
{var env, source = SinOsc.ar(320,0,LFTri.ar(10).abs);
env = FluidAmpGate.ar(source, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, lookBack:221, lookAhead:441);
[DelayN.ar(source,0.1,441/44100), env]
}.plot(0.1);
)
//drum slicing, many ways
//load a buffer
b = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav"));
//have fun with a gate (explore lookahead and lookback, but correct for latency, which will be the greatest of the lookahead and lookback)
(
{var env, source = PlayBuf.ar(1,b);
env = FluidAmpGate.ar(source, rampUp:441, rampDown:2205, onThreshold:-27, offThreshold: -31, minSilenceLength:4410, lookBack:441, highPassFreq:20);
[DelayN.ar(source,delaytime:441/44100), env]
}.plot(2, separately:true);
)
::

@ -1,86 +0,0 @@
TITLE:: FluidAmpSlice
SUMMARY:: Amplitude-based Detrending Slicer
CATEGORIES:: Libraries>FluidCorpusManipulation
RELATED:: Guides/FluidCorpusManipulation
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 LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
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:: minSliceLength
The length in samples that the Slice will stay ON. Changes of states during that period will be ignored.
ARGUMENT:: highPassFreq
The frequency of the fourth-order link::https://en.wikipedia.org/wiki/Linkwitz%E2%80%93Riley_filter##LinkwitzRiley high-pass filter::. This is done first on the signal to minimise low frequency intermodulation with very fast ramp lengths. A frequency of 0 bypasses the filter.
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::
// detrending explained
// Our source here is a sinewave that does not go to silence and has sharp-ish amplitude bumps as onsets we try to track
(
{
var env, source = SinOsc.ar(320,0,LFSaw.ar(20, 0, -0.4, 0.6));
env = FluidAmpSlice.ar(source,fastRampUp: 5,fastRampDown: 50,slowRampUp: 220,slowRampDown: 220, onThreshold: 10, offThreshold: 10,floor: -60);
[source, env]
}.plot(0.08);
)
//beware of double trigger at the begining of the 2nd cycle above). A solution: Schmidth triggers
(
{var env, source = SinOsc.ar(320,0,LFSaw.ar(20, 0, -0.4, 0.6));
env = FluidAmpSlice.ar(source,fastRampUp: 5,fastRampDown: 50,slowRampUp: 220,slowRampDown: 220, onThreshold: 10, offThreshold: 7,floor: -60);
[source, env]
}.plot(0.08);
)
// another solution: minslicelength
(
{var env, source = SinOsc.ar(320,0,LFSaw.ar(20, 0, -0.4, 0.6));
env = FluidAmpSlice.ar(source,fastRampUp: 5,fastRampDown: 50,slowRampUp: 220,slowRampDown: 220, onThreshold: 10, offThreshold: 7,floor: -60, minSliceLength: 441);
[source, env]
}.plot(0.08);
)
//quick drum onsets
//load a buffer
b = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav"));
(
{var env, source = PlayBuf.ar(1,b);
env = FluidAmpSlice.ar(source,fastRampUp: 10,fastRampDown: 2205,slowRampUp: 4410,slowRampDown: 4410,onThreshold: 10,offThreshold: 5,floor: -40,minSliceLength: 4410,highPassFreq: 20);
[source, env]
}.play;
)
::

@ -1,68 +0,0 @@
TITLE:: FluidAudioTransport
summary:: Interpolate between sounds
categories:: Libraries>FluidCorpusManipulation
related:: Classes/FluidBufAudioTransport
DESCRIPTION::
Interpolates between the spectra of two sounds using the Optimal Transport algorithm
See
Henderson and Solomonm (2019) AUDIO TRANSPORT: A GENERALIZED PORTAMENTO VIA OPTIMAL TRANSPORT, DaFx
CLASSMETHODS::
METHOD:: ar
Process incoming audio signals
ARGUMENT:: in
Source A
ARGUMENT:: in2
Source B
ARGUMENT:: interpolation
The amount to interpolate between A and B (0-1, 0 = A, 1 = B)
ARGUMENT:: windowSize
The window size in samples. As AudioTransport relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty
ARGUMENT:: hopSize
The window hop size in samples. As AudioTransport relies on spectral frames, we need to move the window forward. It can be any size but low overlap may create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2).
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long; at least the size of the window; and a power of 2. Making it larger than the window size provides interpolation in frequency. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: maxFFTSize
How large can the FFT be, by allocating memory at instantiation time. This cannot be modulated.
RETURNS::
An audio stream with the interpolated spectrum of the inputs.
EXAMPLES::
code::
//didactic - the mouse X axis interpolates between the two sinewaves
{FluidAudioTransport.ar(SinOsc.ar(220,mul: 0.1),SinOsc.ar(440,mul: 0.02),MouseX.kr())}.play;
//notice how the interpolation quantizes to the FFT bins. Like most spectral processes, it benefits from oversampling the fft... at the cost of CPU power, obviously.
{FluidAudioTransport.ar(SinOsc.ar(220,mul: 0.1),SinOsc.ar(440,mul: 0.02),MouseX.kr(),fftSize: 8192)}.play;
// when the signal is steady, larger hopSize can be accommodated to save back on the CPU
{FluidAudioTransport.ar(SinOsc.ar(220,mul: 0.1),SinOsc.ar(440,mul: 0.02),MouseX.kr(),windowSize: 8192)}.play; // here we get a default hop of half the window so 8 times less than above.
//if you CPU can cope, try this setting, almost smooth, but attacks would smear (the Y axis mixes some in to hear the effect)
{var attacks = Impulse.ar(1,mul: MouseY.kr(-40,10).dbamp); FluidAudioTransport.ar(SinOsc.ar(220,mul: 0.1,add: attacks),SinOsc.ar(440,mul: 0.02,add: attacks),MouseX.kr(),windowSize: 16000)}.play;
//richer with complex spectra
//load 2 files
(
b = Buffer.read(s,FluidFilesPath("Tremblay-CEL-GlitchyMusicBoxMelo.wav"));
c = Buffer.read(s,FluidFilesPath("Tremblay-CF-ChurchBells.wav"));
)
//listen to them
b.play
c.play
//stereo cross!
{FluidAudioTransport.ar(PlayBuf.ar(2,b,loop: 1),PlayBuf.ar(2,c,loop: 1),MouseX.kr())}.play;
::

@ -1,200 +0,0 @@
TITLE:: FluidBufAmpGate
SUMMARY:: Amplitude-based Gating Slicer for Buffers
CATEGORIES:: Libraries>FluidCorpusManipulation
RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading
DESCRIPTION::
This class implements an amplitude-based slicer, with various customisable options and conditions to detect absolute amplitude changes as onsets and offsets. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
FluidBufAmpGate is based on an envelop follower on a highpassed version of the signal, which is then going through a Schmidt trigger and state-aware time contraints. The example code below is unfolding the various possibilites in order of complexity.
The process will return a two-channel buffer with the addresses of the onset on the first channel, and the address of the offset on the second channel.
STRONG::Threading::
By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::.
CLASSMETHODS::
METHOD:: process, processBlocking
This is the method that calls for the slicing to be calculated on a given source buffer.
ARGUMENT:: server
The server on which the buffers to be processed are allocated.
ARGUMENT:: source
The index of the buffer to use as the source material to be sliced through novelty identification. The different channels of multichannel buffers will be summed.
ARGUMENT:: startFrame
Where in the srcBuf should the slicing process start, in sample.
ARGUMENT:: numFrames
How many frames should be processed.
ARGUMENT:: startChan
For multichannel sources, which channel should be processed.
ARGUMENT:: numChans
For multichannel sources, how many channel should be summed.
ARGUMENT:: indices
The index of the buffer where the indices (in sample) of the estimated starting points of slices will be written. The first and last points are always the boundary points of the analysis.
ARGUMENT:: rampUp
The number of samples the envelope follower will take to reach the next value when raising.
ARGUMENT:: rampDown
The number of samples the envelope follower will take to reach the next value when falling.
ARGUMENT:: onThreshold
The threshold in dB of the envelope follower to trigger an onset, aka to go ON when in OFF state.
ARGUMENT:: offThreshold
The threshold in dB of the envelope follower to trigger an offset, , aka to go ON when in OFF state.
ARGUMENT:: minSliceLength
The length in samples that the Slice will stay ON. Changes of states during that period will be ignored.
ARGUMENT:: minSilenceLength
The length in samples that the Slice will stay OFF. Changes of states during that period will be ignored.
ARGUMENT:: minLengthAbove
The length in samples that the envelope have to be above the threshold to consider it a valid transition to ON. The Slice will start at the first sample when the condition is met. Therefore, this affects the latency.
ARGUMENT:: minLengthBelow
The length in samples that the envelope have to be below the threshold to consider it a valid transition to OFF. The Slice will end at the first sample when the condition is met. Therefore, this affects the latency.
ARGUMENT:: lookBack
The length of the buffer kept before an onset to allow the algorithm, once a new Slice is detected, to go back in time (up to that many samples) to find the minimum amplitude as the Slice onset point. This affects the latency of the algorithm.
ARGUMENT:: lookAhead
The length of the buffer kept after an offset to allow the algorithm, once the Slice is considered finished, to wait further in time (up to that many samples) to find a minimum amplitude as the Slice offset point. This affects the latency of the algorithm.
ARGUMENT:: highPassFreq
The frequency of the fourth-order link::https://en.wikipedia.org/wiki/Linkwitz%E2%80%93Riley_filter##LinkwitzRiley high-pass filter::. This is done first on the signal to minimise low frequency intermodulation with very fast ramp lengths. A frequency of 0 bypasses the filter.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The metric will be passed indices as an argument.
returns:: an instance of the processor
EXAMPLES::
code::
// define a test signal and a destination buffer
(
b = Buffer.sendCollection(s, Array.fill(44100,{|i| sin(i*pi/ (44100/640)) * (sin(i*pi/ 22050)).abs}));
c = Buffer.new(s);
)
b.play
b.plot
//basic tests: threshold sanity
FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12)
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
//basic tests: threshold hysteresis
FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -16)
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
//basic tests: threshold min slice
FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, minSliceLength:441)
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
//basic tests: threshold min silence
FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, minSilenceLength:441)
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
//mid tests: threshold time hysteresis on
FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, minLengthAbove:441)
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
//mid tests: threshold time hysteresis off
FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, minLengthBelow:441)
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
//mid tests: threshold with lookBack
FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, lookBack:441)
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
//mid tests: threshold with lookAhead
FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, lookAhead:441)
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
//mid tests: threshold with asymetrical lookBack and lookAhead
FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12, lookBack:221, lookAhead:441)
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
::
STRONG::A musical example.::
CODE::
//load a buffer
(
b = Buffer.read(s, FluidFilesPath("Nicol-LoopE-M.wav"));
c = Buffer.new(s);
)
// slice the samples
(
Routine{
FluidBufAmpGate.process(s, b, indices:c, rampUp:110, rampDown:2205, onThreshold:-27, offThreshold: -31, minSilenceLength:1100, lookBack:441, highPassFreq:40).wait;
c.query;
c.getn(0,c.numFrames*2,{|item|item.postln;});
//reformatting to read the onsets and offsets as pairs
c.getn(0,c.numFrames*2,{|items|items.reshape(c.numFrames,2).do({|x| x.postln});});
}.play
)
//loops over a splice with the MouseX, taking the respective onset and offset of a given slice
(
{
BufRd.ar(1, b,
Phasor.ar(0,1,
BufRd.kr(2, c,
MouseX.kr(0, BufFrames.kr(c)), 0, 1)[0],
BufRd.kr(2, c,
MouseX.kr(1, BufFrames.kr(c)), 0, 1)[1],
BufRd.kr(2,c,
MouseX.kr(0, BufFrames.kr(c)), 0, 1)[0]
), 0, 1);
}.play;
)
::
STRONG::A stereo buffer example.::
CODE::
// make a stereo buffer
b = Buffer.alloc(s,88200,2);
// add some stereo clicks and listen to them
((0..3)*22050+11025).do({|item,index| b.set(item+(index%2), 1.0)})
b.play
// create a new buffer as destinations
c = Buffer.new(s);
//run the process on them
(
// with basic params
Routine{
var t = Main.elapsedTime;
var proc= FluidBufAmpGate.process(s, b, indices: c, rampUp:1, rampDown:10, onThreshold: -30);
proc.wait;
(Main.elapsedTime - t).postln;
}.play
)
// list the indicies of detected attacks - the two input channels have been summed. The two channels of the output, respectively onset and offset indices, are interleaved as this is the SuperCollider buffer data formatting
c.getn(0,c.numFrames*2,{|item|(item*2).postln;})
// a more readable version: deinterleave onsetand offset
c.getn(0,c.numFrames*2,{|items|items.reshape(c.numFrames,2).do({|x| (x*2).postln});})
::

@ -1,161 +0,0 @@
TITLE:: FluidBufAmpSlice
SUMMARY:: Amplitude-based Detrending Slicer for Buffers
CATEGORIES:: Libraries>FluidCorpusManipulation
RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading
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 LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
FluidBufAmpSlice 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 a buffer which contains indices (in sample) of estimated starting points of different slices.
STRONG::Threading::
By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::.
CLASSMETHODS::
METHOD:: process, processBlocking
This is the method that calls for the slicing to be calculated on a given source buffer.
ARGUMENT:: server
The server on which the buffers to be processed are allocated.
ARGUMENT:: source
The index of the buffer to use as the source material to be sliced through novelty identification. The different channels of multichannel buffers will be summed.
ARGUMENT:: startFrame
Where in the srcBuf should the slicing process start, in sample.
ARGUMENT:: numFrames
How many frames should be processed.
ARGUMENT:: startChan
For multichannel sources, which channel should be processed.
ARGUMENT:: numChans
For multichannel sources, how many channel should be summed.
ARGUMENT:: indices
The index of the buffer where the indices (in sample) of the estimated starting points of slices will be written. The first and last points are always the boundary points of the analysis.
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:: minSliceLength
The length in samples that the Slice will stay ON. Changes of states during that period will be ignored.
ARGUMENT:: highPassFreq
The frequency of the fourth-order LinkwitzRiley 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. A frequency of 0 bypasses the filter.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The metric will be passed indices as an argument.
returns:: an instance of the processor
EXAMPLES::
code::
// detrending explained
// define a test signal and a destination buffer
(
b = Buffer.sendCollection(s, Array.fill(44100,{|i| sin(i*pi/ (44100/640)) * ((((79000-i) % 22050).abs / 28000.0) + 0.2)}));
c = Buffer.new(s);
)
// the source is a sinewave that does not go to silence and has sharp-ish amplitude bumps as onsets we try to track
b.play
b.plot
//process with symmetrical thresholds
FluidBufAmpSlice.process(s, b,indices: c,fastRampUp: 5,fastRampDown: 50,slowRampUp: 220,slowRampDown: 220, onThreshold: 10, offThreshold: 10,floor: -60);
c.query
c.getn(0,c.numFrames,{|item|item.postln;})
//beware of multiple triggers at the begining of the 2nd cycle above). A solution: Schmidth triggers
FluidBufAmpSlice.process(s, b,indices: c,fastRampUp: 5,fastRampDown: 50,slowRampUp: 220,slowRampDown: 220, onThreshold: 10, offThreshold: 7,floor: -60);
c.query
c.getn(0,c.numFrames,{|item|item.postln;})
// we got most of them sorted, but there is another solution: minslicelength
FluidBufAmpSlice.process(s, b,indices: c,fastRampUp: 5,fastRampDown: 50,slowRampUp: 220,slowRampDown: 220, onThreshold: 10, offThreshold: 7,floor: -60, minSliceLength: 500);
c.query
c.getn(0,c.numFrames,{|item|item.postln;})
::
STRONG::A musical example.::
CODE::
//load a buffer
(
b = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav"));
c = Buffer.new(s);
)
// slice the samples
FluidBufAmpSlice.process(s,b,indices:c,fastRampUp: 10,fastRampDown: 2205,slowRampUp: 4410,slowRampDown: 4410,onThreshold: 10,offThreshold: 5,floor: -40,minSliceLength: 4410,highPassFreq: 20);
c.query
c.getn(0,c.numFrames,{|item|item.postln;})
//loops over a splice with the MouseX
(
{
BufRd.ar(1, b,
Phasor.ar(0,1,
BufRd.kr(1, c,
MouseX.kr(0, BufFrames.kr(c) - 1), 0, 1),
BufRd.kr(1, c,
MouseX.kr(1, BufFrames.kr(c)), 0, 1),
BufRd.kr(1,c,
MouseX.kr(0, BufFrames.kr(c) - 1), 0, 1)), 0, 1);
}.play;
)
::
STRONG::A stereo buffer example.::
CODE::
// make a stereo buffer
b = Buffer.alloc(s,88200,2);
// add some stereo clicks and listen to them
((0..3)*22050+11025).do({|item,index| b.set(item+(index%2), 1.0)})
b.play
// create a new buffer as destinations
c = Buffer.new(s);
//run the process on them
(
// with basic params
Routine{
var t = Main.elapsedTime;
var proc = FluidBufAmpSlice.process(s,b, indices: c, fastRampUp: 10,fastRampDown: 2205,slowRampUp: 4410,slowRampDown: 4410, onThreshold: 10,offThreshold: 5);
proc.wait;
c.query;
(Main.elapsedTime - t).postln;
}.play
)
// list the indicies of detected attacks - the two input channels have been summed.
c.getn(0,c.numFrames,{|item|(item * 2).postln;})
::

@ -1,344 +0,0 @@
TITLE:: FluidBufAudioTransport
SUMMARY:: Interpolate between buffers
CATEGORIES:: Libraries>FluidCorpusManipulation
RELATED:: Classes/FluidAudioTransport
DESCRIPTION::
Interpolates between the spectra of two sounds using the Optimal Transport algorithm
See Henderson and Solomonm (2019) AUDIO TRANSPORT: A GENERALIZED PORTAMENTO VIA OPTIMAL TRANSPORT, DaFx
CLASSMETHODS::
METHOD:: process, processBlocking
Processs the source LINK::Classes/Buffer:: on the LINK::Classes/Server::. CODE::processBlocking:: will execute directly in the server command FIFO, whereas CODE::process:: will delegate to a separate worker thread. The latter is generally only worthwhile for longer-running jobs where you don't wish to tie up the server.
ARGUMENT:: server
The LINK::Classes/Server:: on which the buffers to be processed are allocated.
ARGUMENT:: sourceA
The first source buffer
ARGUMENT:: startFrameA
offset into the first source buffer (samples)
STRONG::Constraints::
LIST::
##
Minimum: 0
::
ARGUMENT:: numFramesA
number of samples to use from first source buffer
ARGUMENT:: startChanA
starting channel of first source buffer
STRONG::Constraints::
LIST::
##
Minimum: 0
::
ARGUMENT:: numChansA
number of channels to process in first source buffer
ARGUMENT:: sourceB
the second source buffer
ARGUMENT:: startFrameB
offset into the second source buffer (samples)
STRONG::Constraints::
LIST::
##
Minimum: 0
::
ARGUMENT:: numFramesB
number of samples to process from second buffer
ARGUMENT:: startChanB
starting channel for second buffer
STRONG::Constraints::
LIST::
##
Minimum: 0
::
ARGUMENT:: numChansB
number of channels to process in second buffer
ARGUMENT:: destination
buffer for interpolated audio
ARGUMENT:: interpolation
The amount to interpolate between A and B (0-1, 0 = A, 1 = B)
STRONG::Constraints::
LIST::
##
Minimum: 0.0
##
Maximum: 1.0
::
ARGUMENT:: windowSize
The window size. As spectral differencing relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. LINK::http://www.subsurfwiki.org/wiki/Gabor_uncertainty::
ARGUMENT:: hopSize
The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2).
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the highest of windowSize and (bandwidth - 1) * 2.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default CODE::true::
ARGUMENT:: action
A function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed CODE::[features]:: as an argument.
RETURNS:: An instance of the processor
METHOD:: kr
Trigger the equivalent behaviour to CODE::processBlocking / process:: from a LINK::Classes/Synth::. Can be useful for expressing a sequence of buffer and data processing jobs to execute. Note that the work still executes on the server command FIFO (not the audio thread), and it is the caller's responsibility to manage the sequencing, using the CODE::done:: status of the various UGens.
ARGUMENT:: sourceA
The first source buffer
ARGUMENT:: startFrameA
offset into the first source buffer (samples)
STRONG::Constraints::
LIST::
##
Minimum: 0
::
ARGUMENT:: numFramesA
number of samples to use from first source buffer
ARGUMENT:: startChanA
starting channel of first source buffer
STRONG::Constraints::
LIST::
##
Minimum: 0
::
ARGUMENT:: numChansA
number of channels to process in first source buffer
ARGUMENT:: sourceB
the second source buffer
ARGUMENT:: startFrameB
offset into the second source buffer (samples)
STRONG::Constraints::
LIST::
##
Minimum: 0
::
ARGUMENT:: numFramesB
number of samples to process from second buffer
ARGUMENT:: startChanB
starting channel for second buffer
STRONG::Constraints::
LIST::
##
Minimum: 0
::
ARGUMENT:: numChansB
number of channels to process in second buffer
ARGUMENT:: destination
buffer for interpolated audio
ARGUMENT:: interpolation
The amount to interpolate between A and B (0-1, 0 = A, 1 = B)
STRONG::Constraints::
LIST::
##
Minimum: 0.0
##
Maximum: 1.0
::
ARGUMENT:: windowSize
The window size. As spectral differencing relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. LINK::http://www.subsurfwiki.org/wiki/Gabor_uncertainty::
ARGUMENT:: hopSize
The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2).
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the highest of windowSize and (bandwidth - 1) * 2.
ARGUMENT:: trig
A CODE::kr:: signal that will trigger execution
ARGUMENT:: blocking
Whether to execute this process directly on the server command FIFO or delegate to a worker thread. See CODE::processBlocking/process:: for caveats.
INSTANCEMETHODS::
METHOD:: kr
Returns a UGen that reports the progress of the running task when executing in a worker thread. Calling code::scope:: with this can be used for a convinient progress monitor
METHOD:: cancel
Cancels non-blocking processing
METHOD:: wait
When called in the context of a LINK::Classes/Routine:: (it won't work otherwise), will block execution until the processor has finished. This can be convinient for writing sequences of processes more linearly than using lots of nested actions.
EXAMPLES::
code::
//Didactic:
//Make 2 sinewave sources to be interpolated
(
b = Buffer.loadCollection(s, FloatArray.fill(44100, {|a|(a / pi).sin * 0.1}));
c = Buffer.loadCollection(s, FloatArray.fill(44100, {|a|(a / pi / 2).sin * 0.02}));
d = Buffer.new
)
//make an sound interpolating their spectrum
FluidBufAudioTransport.process(s,b,source2:c,destination:d,interpolation:0.5,action:{"Ding".postln})
// listen to the source and the result
b.play
c.play
d.play
// note that the process is quantized by the spectral bins. For an example of the pros and cons of these settings on this given process, please see the real-time FluidAudioTransport helpfile.
// more interesting sources: two cardboard bowing gestures
(
b = Buffer.read(s,FluidFilesPath("Green-Box641.wav"));
c = Buffer.read(s,FluidFilesPath("Green-Box639.wav"));
d = Buffer.new
)
// listen to the source
b.play
c.play
// process and listen
FluidBufAudioTransport.process(s,b,source2:c,destination:d,interpolation:0.5,action:{"Ding".postln})
d.play
// try various interpolation factors (0.1 and 0.9 are quite good
::

@ -1,376 +0,0 @@
TITLE:: FluidBufChroma
SUMMARY:: A histogram of pitch classes on a Buffer
CATEGORIES:: Libraries>FluidCorpusManipulation
RELATED:: Classes/FluidChroma,Classes/FluidBufPitch,Classes/FluidBufLoudness,Classes/FluidBufMFCC,Classes/FluidBufSpectralShape,Classes/FluidBufStats,Guides/FluidCorpusManipulationToolkit,Classes/FluidBufMFCC
DESCRIPTION::
This class computes a histogram of the energy contained for each pitch class across the analysis frequency range.
Also known as a chromagram, this typically allows you to get a contour of how much each semitone is represented in the spectrum over time. The number of chroma bins (and, thus, pitch classes) and the central reference frequency can be adjusted.
The process will return a single multichannel buffer of STRONG::numChroma:: per input channel. Each frame represents a value, which is every hopSize.
CLASSMETHODS::
METHOD:: process, processBlocking
Processs the source LINK::Classes/Buffer:: on the LINK::Classes/Server::. CODE::processBlocking:: will execute directly in the server command FIFO, whereas CODE::process:: will delegate to a separate worker thread. The latter is generally only worthwhile for longer-running jobs where you don't wish to tie up the server.
ARGUMENT:: server
The LINK::Classes/Server:: on which the buffers to be processed are allocated.
ARGUMENT:: source
The index of the buffer to use as the source material to be analysed. The different channels of multichannel buffers will be processing sequentially.
ARGUMENT:: startFrame
Where in the srcBuf should the process start, in sample.
STRONG::Constraints::
LIST::
##
Minimum: 0
::
ARGUMENT:: numFrames
How many frames should be processed.
ARGUMENT:: startChan
For multichannel srcBuf, which channel should be processed first.
STRONG::Constraints::
LIST::
##
Minimum: 0
::
ARGUMENT:: numChans
For multichannel srcBuf, how many channel should be processed.
ARGUMENT:: features
The destination buffer for the STRONG::numChroma:: to be written to.
ARGUMENT:: numChroma
The number of chroma bins per octave. It will determine how many channels are output per input channel.
STRONG::Constraints::
LIST::
##
Minimum: 2
##
Maximum: CODE::maxNumChroma::
::
ARGUMENT:: ref
STRONG::Constraints::
LIST::
##
Minimum: 0
##
Maximum: 22000
::
ARGUMENT:: normalize
This flag enables the scaling of the output. It is off (0) by default. (1) will normalise each frame to sum to 1. (2) normalises each frame relative to the loudest chroma bin being 1.
ARGUMENT:: minFreq
The lower frequency included in the analysis, in Hz.
STRONG::Constraints::
LIST::
##
Minimum: 0
::
ARGUMENT:: maxFreq
The highest frequency included in the analysis, in Hz.
STRONG::Constraints::
LIST::
##
Minimum: -1
::
ARGUMENT:: windowSize
The window size. As chroma description relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. LINK::http://www.subsurfwiki.org/wiki/Gabor_uncertainty::
ARGUMENT:: hopSize
The window hop size. As chroma description relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts.
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision.
ARGUMENT:: padding
Controls the zero-padding added to either end of the source buffer or segment. Possible values are 0 (no padding), 1 (default, half the window size), or 2 (window size - hop size). Padding ensures that all input samples are completely analysed: with no padding, the first analysis window starts at time 0, and the samples at either end will be tapered by the STFT windowing function. Mode 1 has the effect of centering the first sample in the analysis window and ensuring that the very start and end of the segment are accounted for in the analysis. Mode 2 can be useful when the overlap factor (window size / hop size) is greater than 2, to ensure that the input samples at either end of the segment are covered by the same number of analysis frames as the rest of the analysed material.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default CODE::true::
ARGUMENT:: action
A function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed CODE::[features]:: as an argument.
RETURNS:: An instance of the processor
METHOD:: kr
Trigger the equivalent behaviour to CODE::processBlocking / process:: from a LINK::Classes/Synth::. Can be useful for expressing a sequence of buffer and data processing jobs to execute. Note that the work still executes on the server command FIFO (not the audio thread), and it is the caller's responsibility to manage the sequencing, using the CODE::done:: status of the various UGens.
ARGUMENT:: source
The index of the buffer to use as the source material to be analysed. The different channels of multichannel buffers will be processing sequentially.
ARGUMENT:: startFrame
Where in the srcBuf should the process start, in sample.
STRONG::Constraints::
LIST::
##
Minimum: 0
::
ARGUMENT:: numFrames
How many frames should be processed.
ARGUMENT:: startChan
For multichannel srcBuf, which channel should be processed first.
STRONG::Constraints::
LIST::
##
Minimum: 0
::
ARGUMENT:: numChans
For multichannel srcBuf, how many channel should be processed.
ARGUMENT:: features
The destination buffer for the STRONG::numChroma:: to be written to.
ARGUMENT:: numChroma
The number of chroma bins per octave. It will determine how many channels are output per input channel.
STRONG::Constraints::
LIST::
##
Minimum: 2
##
Maximum: CODE::maxNumChroma::
::
ARGUMENT:: ref
STRONG::Constraints::
LIST::
##
Minimum: 0
##
Maximum: 22000
::
ARGUMENT:: normalize
This flag enables the scaling of the output. It is off (0) by default. (1) will normalise each frame to sum to 1. (2) normalises each frame relative to the loudest chroma bin being 1.
ARGUMENT:: minFreq
The lower frequency included in the analysis, in Hz.
STRONG::Constraints::
LIST::
##
Minimum: 0
::
ARGUMENT:: maxFreq
The highest frequency included in the analysis, in Hz.
STRONG::Constraints::
LIST::
##
Minimum: -1
::
ARGUMENT:: windowSize
The window size. As chroma description relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. LINK::http://www.subsurfwiki.org/wiki/Gabor_uncertainty::
ARGUMENT:: hopSize
The window hop size. As chroma description relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts.
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision.
ARGUMENT:: padding
Controls the zero-padding added to either end of the source buffer or segment. Possible values are 0 (no padding), 1 (default, half the window size), or 2 (window size - hop size). Padding ensures that all input samples are completely analysed: with no padding, the first analysis window starts at time 0, and the samples at either end will be tapered by the STFT windowing function. Mode 1 has the effect of centering the first sample in the analysis window and ensuring that the very start and end of the segment are accounted for in the analysis. Mode 2 can be useful when the overlap factor (window size / hop size) is greater than 2, to ensure that the input samples at either end of the segment are covered by the same number of analysis frames as the rest of the analysed material.
ARGUMENT:: trig
A CODE::kr:: signal that will trigger execution
ARGUMENT:: blocking
Whether to execute this process directly on the server command FIFO or delegate to a worker thread. See CODE::processBlocking/process:: for caveats.
INSTANCEMETHODS::
METHOD:: kr
Returns a UGen that reports the progress of the running task when executing in a worker thread. Calling code::scope:: with this can be used for a convinient progress monitor
METHOD:: cancel
Cancels non-blocking processing
METHOD:: wait
When called in the context of a LINK::Classes/Routine:: (it won't work otherwise), will block execution until the processor has finished. This can be convinient for writing sequences of processes more linearly than using lots of nested actions.
EXAMPLES::
code::
// create some buffers
(
b = Buffer.read(s,FluidFilesPath("Tremblay-SlideChoirAdd-M.wav"));
c = Buffer.new(s);
)
// run the process with basic parameters
(
Routine{
t = Main.elapsedTime;
FluidBufChroma.process(s, b, features: c, windowSize: 4096).wait;
(Main.elapsedTime - t).postln;
}.play
)
// listen to the source and look at the buffer
b.play;
c.plot
::
STRONG::A stereo buffer example.::
CODE::
// load two very different files
(
b = Buffer.read(s,FluidFilesPath("Tremblay-SA-UprightPianoPedalWide.wav"));
c = Buffer.read(s,FluidFilesPath("Tremblay-AaS-AcousticStrums-M.wav"));
)
// composite one on left one on right as test signals
FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000,destStartChan:1, destination:b)
b.play
// create a buffer as destinations
c = Buffer.new(s);
//run the process on them
(
Routine{
t = Main.elapsedTime;
FluidBufChroma.process(s, b, features: c, windowSize: 4096).wait;
(Main.elapsedTime - t).postln;
}.play
)
// look at the buffer: 12 chroma bins for left, then 12 chroma bins for right
c.plot(separately:true)
::

@ -1,112 +0,0 @@
TITLE:: FluidBufCompose
SUMMARY:: Buffer Compositing Utility
CATEGORIES:: Libraries>FluidCorpusManipulation, UGens>Buffer
RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading, Classes/Buffer
DESCRIPTION::
A FluidBufCompose object provides a flexible utility for combining the contents of buffers on the server. It can be used for thing like mixing down multichannel buffers, or converting from left-right stereo to mid-side. It is used extensively in all the example code of LINK::Guides/FluidCorpusManipulation:: as part of the FluCoMa project. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
At its most simple, the object copies the content of a source buffer into a destination buffer. The flexibility comes from the various flags controlling which portions and channels of the source to use, and by applying gains (which can be positive or negative) to the source data and the portion of the destination that would be overwritten.
The algorithm takes a srcBuf, and writes the information at the provided dstBuf. These buffer arguments can all point to the same buffer, which gives great flexibility in transforming and reshaping.
CLASSMETHODS::
METHOD:: process, processBlocking
This method triggers the compositing.
ARGUMENT:: server
The server on which the buffers to be processed are allocated.
ARGUMENT:: source
The bufNum of the source buffer.
ARGUMENT:: startFrame
The starting point (in samples) from which to copy in the source buffer.
ARGUMENT:: numFrames
The duration (in samples) to copy from the source buffer. The default (-1) copies the full lenght of the buffer.
ARGUMENT:: startChan
The first channel from which to copy in the source buffer.
ARGUMENT:: numChans
The number of channels from which to copy in the source buffer. This parameter will wrap around the number of channels in the source buffer. The default (-1) copies all of the buffer's channel.
ARGUMENT:: gain
The gain applied to the samples to be copied from the source buffer.
ARGUMENT:: destination
The bufNum of the destination buffer.
ARGUMENT:: destStartFrame
The time offset (in samples) in the destination buffer to start writing the source at. The destination buffer will be resized if the portion to copy is overflowing.
ARGUMENT:: destStartChan
The channel offest in the destination buffer to start writing the source at. The destination buffer will be resized if the number of channels to copy is overflowing.
ARGUMENT:: destGain
The gain applied to the samples in the region of the destination buffer over which the source is to be copied. The default value (0) will overwrite that section of the destination buffer, and a value of 1.0 would sum the source to the material that was present.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and destination instance variables have been updated on the client side. The function will be passed destination as an argument.
returns:: an instance of the processor
DISCUSSION::
It is important to understand the rules used for determining the final desintinaiton buffer dimensions to get the most out of this object. If needs be, the destination buffer will be resized to the maxima of the requsted source numFrames and numChannels. Frames will be written up to the limit of actually available samples (meaning you can create zero padding); channels will be written modulo the available channels, taking into account the channel offsets, meaning you can have channels repeat or loop into the source buffer's channels. See the examples below.
EXAMPLES::
code::
// load some buffers
(
b = Buffer.read(s,FluidFilesPath("Tremblay-AaS-SynthTwoVoices-M.wav"));
c = Buffer.read(s,FluidFilesPath("Tremblay-SA-UprightPianoPedalWide.wav"));
d = Buffer.new(s);
)
// with basic params (basic summing of each full buffer in all dimensions)
(
Routine{
FluidBufCompose.process(s, source: b, destination: d);
FluidBufCompose.process(s, source: c, destination: d, destGain: 1.0);
s.sync;
d.query;
d.play;
}.play;
)
//constructing a mono buffer, with a quiet punch from the synth, with a choked piano resonance from the left channel
(
Routine{
d.free; d = Buffer.new(s);
FluidBufCompose.process(s, source: b, numFrames: 9000, gain: 0.5, destination: d);
FluidBufCompose.process(s, source: c, startFrame:30000, numFrames:44100, numChans:1, gain:0.9, destination: d, destGain: 1.0).wait;
d.query;
d.play;
}.play
)
//constructing a stereo buffer, with the end of the mono synth in both channels, with a piano resonance in swapped stereo
(
Routine{
d.free; d = Buffer.new(s);
FluidBufCompose.process(s, source: b, startFrame: 441000, numChans: 2, gain: 0.6, destination: d);
FluidBufCompose.process(s, source: c, numFrames: 78000, startChan: 1, numChans: 2, gain: 0.5, destStartFrame: 22050, destination: d, destGain: 1.0).wait;
d.query;
d.play;
}.play
)
//constructing a one second buffer: the first second of each buffer, the mono synth on the right, the piano on the left
(
Routine{
d.free; d = Buffer.new(s);
FluidBufCompose.process(s, source: b, numFrames: 44100, numChans: 1, destStartChan: 1, destination: d);
FluidBufCompose.process(s, source: c, numFrames:44100, numChans:1, destination: d, destGain: 1.0).wait;
d.query;
d.play;
}.play
)
::

@ -1,120 +0,0 @@
TITLE:: FluidBufFlatten
summary:: Flatten a multichannel buffer on the server
categories:: Libraries>FluidCorpusManipulation
related:: Classes/Buffer, Classes/FluidBufCompose, Classes/FluidBufSelect, Classes/FluidBufSelectEvery
DESCRIPTION::
Flatten a multichannel link::Classes/Buffer:: to a single channel. This can be useful for constructing n-dimensional data points for use with link::Classes/FluidDataSet::
The code::axis:: determines how the flattening is arranged. The default value, 1, flattens channel-wise, such that (if we imagine channels are rows, time positions are columns):
table::
## a 1 || a 2 || a 3
## b 1 || b 2 || b 3
## c 1 || c 2 || c 3
::
becomes
table::
## a 1 || b 1 || c 1 || a 2 || b 2 || c 2 || a 3 || b 3 || c 3
::
whereas with code::axis = 0:: we get
table::
## a 1 || a 2 || a 3 || b 1 || b 2 || b 3 || c 1 || c 2 || c 3
::
CLASSMETHODS::
private::new1
METHOD:: process, processBlocking
Run the process on the given sever, and perfrom code::action:: when done
ARGUMENT:: server
The link::Classes/Server:: on which to run
ARGUMENT:: source
The link::Classes/Buffer:: to flatten
ARGUMENT:: startFrame
Where in the source should the flattening process start, in samples.
ARGUMENT:: numFrames
How many frames should be processed.
ARGUMENT:: startChan
For multichannel source buffers, which channel to start processing at.
ARGUMENT:: numChans
For multichannel source buffers, how many channels should be processed.
ARGUMENT:: destination
The link::Classes/Buffer:: to write the flattened data to
ARGUMENT:: axis
Whether to group points channel-wise or frame-wise
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
Runs when processing is complete
EXAMPLES::
code::
//FluidBufPitch is useful to illustrate the effect of this, because the pitch and confidence values are easily distinguishable
(
~path = FluidFilesPath();
~randomsoundfile = SoundFile.collect(~path +/+ '*').choose;
b = Buffer.read(s,~randomsoundfile.path,action:{"Sound Loaded".postln});
~pitchdata = Buffer.new;
~flatdata = Buffer.new;
)
//Pitch analysis, writes pitches as frequencies to chan 0, confidences [0-1] to chan 1
FluidBufPitch.process(s,b,numFrames:512 * 10,numChans:1,features:~pitchdata,action:{"Pitch Analysis Done".postln});
// Flatten and print the flat buffer. We expect to see larger numbers (20-2000) interleaved with smaller (0-1)
(
FluidBufFlatten.process(s,~pitchdata, destination: ~flatdata, axis:1, action:{
~flatdata.loadToFloatArray(action:{ |a|
a.postln;
})
})
)
//changing the axis, we see all large numbers first
(
FluidBufFlatten.process(s,~pitchdata, destination:~flatdata, axis:0, action:{
~flatdata.loadToFloatArray(action:{ |a|
a.postln;
})
})
)
//adding the source range make this processor very powerful, but can be quite confusing
//here we take only one frame starting at the second one (0-counting)
(
FluidBufFlatten.process(s,~pitchdata,startFrame: 1,numFrames: 1, destination:~flatdata, action:{
~flatdata.loadToFloatArray(action:{ |a|
a.postln;
})
})
)
//and here we take only the confidences
(
FluidBufFlatten.process(s,~pitchdata, startChan: 1, destination:~flatdata, action:{
~flatdata.loadToFloatArray(action:{ |a|
a.postln;
})
})
)
::

@ -1,199 +0,0 @@
TITLE:: FluidBufHPSS
SUMMARY:: Buffer-Based Harmonic-Percussive Source Separation Using Median Filtering
CATEGORIES:: Libraries>FluidCorpusManipulation, UGens>Buffer
RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading
DESCRIPTION::
A FluidBufHPSS object performs Harmonic-Percussive Source Separation (HPSS) on the contents of a link::Classes/Buffer::. The class performs HPSS as described in its original form footnote::
Fitzgerald, Derry. 2010. Harmonic/Percussive Separation Using Median Filtering. In Proceedings DaFx 10. https://arrow.dit.ie/argcon/67.
:: as well as a variation on the extension propsoed by Driedger et al. footnote::
Driedger, Jonathan, Meinard Uller, and Sascha Disch. 2014. Extending Harmonic-Percussive Separation of Audio Signals. In Proc. ISMIR. http://www.terasoft.com.tw/conf/ismir2014/proceedings/T110_127_Paper.pdf.
::
The algorithm takes a buffer in, and divides it into two or three outputs, depending on the mode: LIST::
## an harmonic component;
## a percussive component;
## a residual of the previous two if the flag is set to inter-dependant thresholds. See the maskingMode below.::
It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
STRONG::Threading::
By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::.
CLASSMETHODS::
METHOD:: process, processBlocking
This is the method that calls for the HPSS to be calculated on a given source buffer.
ARGUMENT:: server
The server on which the buffers to be processed are allocated.
ARGUMENT:: source
The index of the buffer to use as the source material. The channels of multichannel buffers will be processed sequentially.
ARGUMENT:: startFrame
Where in the srcBuf should the HPSS process start, in samples.
ARGUMENT:: numFrames
How many frames should be processed.
ARGUMENT:: startChan
For multichannel srcBuf, which channel to start processing at.
ARGUMENT:: numChans
For multichannel srcBuf, how many channels should be processed.
ARGUMENT:: harmonic
The index of the buffer where the extracted harmonic component will be reconstructed.
ARGUMENT:: percussive
The index of the buffer where the extracted percussive component will be reconstructed.
ARGUMENT:: residual
The index of the buffer where the residual component will be reconstructed in mode 2.
ARGUMENT:: harmFilterSize
The size, in spectral frames, of the median filter for the harmonic component. Must be an odd number, >= 3.
ARGUMENT:: percFilterSize
The size, in spectral bins, of the median filter for the percussive component. Must be an odd number, >=3
ARGUMENT:: maskingMode
The way the masking is applied to the original spectrogram. (0,1,2)
table::
## 0 || The traditional soft mask used in Fitzgerald's original method of 'Wiener-inspired' filtering. Complimentary, soft masks are made for the harmonic and percussive parts by allocating some fraction of a point in time-frequency to each. This provides the fewest artefacts, but the weakest separation. The two resulting buffers will sum to exactly the original material.
## 1 || Relative mode - Better separation, with more artefacts. The harmonic mask is constructed using a binary decision, based on whether a threshold is exceeded at a given time-frequency point (these are set using harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, see below). The percussive mask is then formed as the inverse of the harmonic one, meaning that as above, the two components will sum to the original sound.
## 2 || Inter-dependent mode - Thresholds can be varied independently, but are coupled in effect. Binary masks are made for each of the harmonic and percussive components, and the masks are converted to soft at the end so that everything null sums even if the params are independent, that is what makes it harder to control. These aren't guranteed to cover the whole sound; in this case the 'leftovers' will placed into a third buffer.
::
ARGUMENT:: harmThreshFreq1
In modes 1 and 2, the frequency of the low part of the threshold for the harmonic filter (0-1)
ARGUMENT:: harmThreshAmp1
In modes 1 and 2, the threshold of the low part for the harmonic filter. That threshold applies to all frequencies up to harmThreshFreq1: how much more powerful (in dB) the harmonic median filter needs to be than the percussive median filter for this bin to be counted as harmonic.
ARGUMENT:: harmThreshFreq2
In modes 1 and 2, the frequency of the hight part of the threshold for the harmonic filter. (0-1)
ARGUMENT:: harmThreshAmp2
In modes 1 and 2, the threshold of the high part for the harmonic filter. That threshold applies to all frequencies above harmThreshFreq2. The threshold between harmThreshFreq1 and harmThreshFreq2 is interpolated between harmThreshAmp1 and harmThreshAmp2. How much more powerful (in dB) the harmonic median filter needs to be than the percussive median filter for this bin to be counted as harmonic.
ARGUMENT:: percThreshFreq1
In mode 2, the frequency of the low part of the threshold for the percussive filter. (0-1)
ARGUMENT:: percThreshAmp1
In mode 2, the threshold of the low part for the percussive filter. That threshold applies to all frequencies up to percThreshFreq1. How much more powerful (in dB) the percussive median filter needs to be than the harmonic median filter for this bin to be counted as percussive.
ARGUMENT:: percThreshFreq2
In mode 2, the frequency of the hight part of the threshold for the percussive filter. (0-1)
ARGUMENT:: percThreshAmp2
In mode 2, the threshold of the high part for the percussive filter. That threshold applies to all frequencies above percThreshFreq2. The threshold between percThreshFreq1 and percThreshFreq2 is interpolated between percThreshAmp1 and percThreshAmp2. How much more powerful (in dB) the percussive median filter needs to be than the harmonic median filter for this bin to be counted as percussive.
ARGUMENT:: windowSize
The window size in samples. As HPSS relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty
ARGUMENT:: hopSize
The window hop size in samples. As HPSS relies on spectral frames, we need to move the window forward. It can be any size but low overlap may create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2).
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long; at least the size of the window; and a power of 2. Making it larger than the window size provides interpolation in frequency. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [harmonic, percussive, residual] as an argument.
returns:: an instance of the processor
Discussion::
HPSS works by using median filters on the spectral magnitudes of a sound. It hinges on a simple modelling assumption that tonal components will tend to yield concentrations of energy across time, spread out in frequency, and percussive components will manifest as concentrations of energy across frequency, spread out in time. By using median filters across time and frequency respectively, we get initial esitmates of the tonal-ness / transient-ness of a point in time and frequency. These are then combined into 'masks' that are applied to the orginal spectral data in order to produce a separation.
The maskingMode parameter provides different approaches to combinging estimates and producing masks. Some settings (especially in modes 1 & 2) will provide better separation but with more artefacts. These can, in principle, be ameliorated by applying smoothing filters to the masks before transforming back to the time-domain (not yet implemented).
EXAMPLES::
code::
//load buffers
(
b = Buffer.read(s,FluidFilesPath("Tremblay-AaS-SynthTwoVoices-M.wav"));
c = Buffer.new(s);
d = Buffer.new(s);
e = Buffer.new(s);
)
// run with basic parameters
(
Routine{
t = Main.elapsedTime;
FluidBufHPSS.process(s, b, harmonic: c, percussive: d).wait;
(Main.elapsedTime - t).postln;
}.play
)
c.query
d.query
//play the harmonic
c.play;
//play the percussive
d.play;
//nullsumming tests
{(PlayBuf.ar(1,c))+(PlayBuf.ar(1,d))+(-1*PlayBuf.ar(1,b,doneAction:2))}.play
//more daring parameters, in mode 2
(
Routine{
t = Main.elapsedTime;
FluidBufHPSS.process(s, b, harmonic: c, percussive: d, residual:e, harmFilterSize:31, maskingMode:2, harmThreshFreq1: 0.005, harmThreshAmp1: 7.5, harmThreshFreq2: 0.168, harmThreshAmp2: 7.5, percThreshFreq1: 0.004, percThreshAmp1: 26.5, percThreshFreq2: 0.152, percThreshAmp2: 26.5,windowSize:4096,hopSize:512)
.wait;
(Main.elapsedTime - t).postln;
}.play
)
//play the harmonic
c.play;
//play the percussive
d.play;
//play the residual
e.play;
//still nullsumming
{PlayBuf.ar(1,c) + PlayBuf.ar(1,d) + PlayBuf.ar(1,e) - PlayBuf.ar(1,b,doneAction:2)}.play;
::
STRONG::A stereo buffer example.::
CODE::
// load two very different files
(
b = Buffer.read(s,FluidFilesPath("Tremblay-SA-UprightPianoPedalWide.wav"));
c = Buffer.read(s,FluidFilesPath("Tremblay-AaS-AcousticStrums-M.wav"));
)
// composite one on left one on right as test signals
(
Routine{
FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000,destStartChan:1, destination:b).wait;
b.play
}.play
)
// create 2 new buffers as destinations
d = Buffer.new(s); e = Buffer.new(s);
//run the process on them
(
Routine{
t = Main.elapsedTime;
FluidBufHPSS.process(s, b, harmonic: d, percussive:e).wait;
(Main.elapsedTime - t).postln;
}.play
)
//listen: stereo preserved!
d.play
e.play
::

@ -1,124 +0,0 @@
TITLE:: FluidBufLoudness
SUMMARY:: A Loudness and True-Peak Descriptor on a Buffer
CATEGORIES:: Libraries>FluidCorpusManipulation
RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading
DESCRIPTION::
This class implements two loudness descriptors, computing the true peak of the signal as well as applying the filters proposed by broadcasting standards to emulate the perception of amplitude. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The process will return a multichannel buffer with two channels per input channel, one for loudness and one for the true peak value of the frame, both in dBfs. More information on broadcasting standardisation of loudness measurement is available at the reference page FOOTNOTE::https://tech.ebu.ch/docs/tech/tech3341.pdf:: and in more musician-friendly explantions here FOOTNOTE::http://designingsound.org/2013/02/06/loudness-and-metering-part-1/::. Each sample represents a value, which is every hopSize. Its sampling rate is STRONG::sourceSR / hopSize::.
STRONG::Threading::
By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::.
CLASSMETHODS::
METHOD:: process, processBlocking
This is the method that calls for the loudness descriptor to be calculated on a given source buffer.
ARGUMENT:: server
The server on which the buffers to be processed are allocated.
ARGUMENT:: source
The index of the buffer to use as the source material to be described. The different channels of multichannel buffers will be processing sequentially.
ARGUMENT:: startFrame
Where in the srcBuf should the process start, in sample.
ARGUMENT:: numFrames
How many frames should be processed.
ARGUMENT:: startChan
For multichannel srcBuf, which channel should be processed first.
ARGUMENT:: numChans
For multichannel srcBuf, how many channel should be processed.
ARGUMENT:: features
The destination buffer for the loudness descriptors.
ARGUMENT:: kWeighting
A flag to switch the perceptual model of loudness. On by default, removing it makes the algorithm more CPU efficient by reverting to a simple RMS of the frame.
ARGUMENT:: truePeak
A flag to switch the computation of TruePeak. On by default, removing it makes the algorithm more CPU efficient by reverting to a simple absolute peak of the frame.
ARGUMENT:: windowSize
The size of the window on which the computation is done. By default 1024 to be similar with all other FluCoMa objects, the EBU specifies other values as per the examples below.
ARGUMENT:: hopSize
How much the buffered window moves forward, in samples. By default 512 to be similar with all other FluCoMa objects, the EBU specifies other values as per the examples below.
ARGUMENT:: padding
Controls the zero-padding added to either end of the source buffer or segment. Possible values are 0 (no padding), 1 (default, half the window size), or 2 (window size - hop size). Padding ensures that all input samples are completely analysed: with no padding, the first analysis window starts at time 0, and the samples at either end will be tapered by the STFT windowing function. Mode 1 has the effect of centering the first sample in the analysis window and ensuring that the very start and end of the segment are accounted for in the analysis. Mode 2 can be useful when the overlap factor (window size / hop size) is greater than 2, to ensure that the input samples at either end of the segment are covered by the same number of analysis frames as the rest of the analysed material.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [features] as an argument.
returns:: an instance of the processor
EXAMPLES::
code::
// create a buffer with a short clicking sinusoidal burst (220Hz) starting at frame 8192 for 1024 frames
(
b = Buffer.sendCollection(s, (Array.fill(8192,{0}) ++ (Signal.sineFill(1203,[0,0,0,0,0,1],[0,0,0,0,0,0.5pi]).takeThese({|x,i|i>1023})) ++ Array.fill(8192,{0})));
c = Buffer.new(s);
)
// listen to the source and look at the buffer
b.play; b.plot;
// run the process with basic parameters
(
Routine{
t = Main.elapsedTime;
FluidBufLoudness.process(s, source:b, features: c).wait;
(Main.elapsedTime - t).postln;
}.play
)
// look at the analysis
c.plot(minval:-130, maxval:6)
// The values are interleaved [loudness,truepeak] in the buffer as they are on 2 channels: to get to the right frame, divide the SR of the input by the hopSize, then multiply by 2 because of the channel interleaving
// here we are querying from one frame before (the signal starts at 8192, which is frame 16 (8192/512), therefore starting the query at frame 15, which is index 30.
c.getn(30,10,{|x|x.postln})
// observe that the first frame is silent, as expected. We can appreciate the overshoot of TruePeak of a full range sinewave starting on the second sample (fourth item in the list).
::
STRONG::A stereo buffer example.::
CODE::
// load two very different files
(
b = Buffer.read(s,FluidFilesPath("Tremblay-SA-UprightPianoPedalWide.wav"));
c = Buffer.read(s,FluidFilesPath("Tremblay-AaS-AcousticStrums-M.wav"));
)
// composite one on left one on right as test signals
FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000,destStartChan:1, destination:b)
b.play
// create a buffer as destinations
c = Buffer.new(s);
//run the process on them with EBU standard Instant Loudness of
(
Routine{
t = Main.elapsedTime;
FluidBufLoudness.process(s, b, features: c, windowSize: 17640, hopSize:4410).wait;
(Main.elapsedTime - t).postln;
}.play
)
// look at the buffer: [loudness,truepeak] for left then [loudness,truepeak] for right
c.plot(minval:-40, maxval:0)
::

@ -1,126 +0,0 @@
TITLE:: FluidBufMFCC
SUMMARY:: Mel-Frequency Cepstral Coefficients as Spectral Descriptors on a Buffer
CATEGORIES:: Libraries>FluidCorpusManipulation
RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading, Classes/FluidBufMelBands
DESCRIPTION::
This class implements a classic spectral descriptor, the Mel-Frequency Cepstral Coefficients (https://en.wikipedia.org/wiki/Mel-frequency_cepstrum). The input is first filtered in to STRONG::numBands:: perceptually-spaced bands, as in LINK::Classes/FluidMelBands::. It is then analysed into STRONG::numCoeffs:: number of cepstral coefficients. It has the advantage of being amplitude invariant, except for the first coefficient. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The process will return a single multichannel buffer of STRONG::numCoeffs:: per input channel. Each frame represents a value, which is every hopSize.
STRONG::Threading::
By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::.
CLASSMETHODS::
METHOD:: process, processBlocking
This is the method that calls for the spectral shape descriptors to be calculated on a given source buffer.
ARGUMENT:: server
The server on which the buffers to be processed are allocated.
ARGUMENT:: source
The index of the buffer to use as the source material to be described through the various descriptors. The different channels of multichannel buffers will be processing sequentially.
ARGUMENT:: startFrame
Where in the srcBuf should the process start, in sample.
ARGUMENT:: numFrames
How many frames should be processed.
ARGUMENT:: startChan
For multichannel srcBuf, which channel should be processed first.
ARGUMENT:: numChans
For multichannel srcBuf, how many channel should be processed.
ARGUMENT:: features
The destination buffer for the numCoeffs coefficients describing the spectral shape.
ARGUMENT:: numCoeffs
The number of cepstral coefficients to be outputed. It will decide how many channels are produce per channel of the source.
ARGUMENT:: numBands
The number of bands that will be perceptually equally distributed between STRONG::minFreq:: and STRONG::maxFreq::.
ARGUMENT:: startCoeff
The lowest index of the output cepstral coefficient, zero-counting.
ARGUMENT:: minFreq
The lower boundary of the lowest band of the model, in Hz.
ARGUMENT:: maxFreq
The highest boundary of the highest band of the model, in Hz.
ARGUMENT:: windowSize
The window size. As MFCC computation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty
ARGUMENT:: hopSize
The window hop size. As MFCC computation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2).
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: padding
Controls the zero-padding added to either end of the source buffer or segment. Possible values are 0 (no padding), 1 (default, half the window size), or 2 (window size - hop size). Padding ensures that all input samples are completely analysed: with no padding, the first analysis window starts at time 0, and the samples at either end will be tapered by the STFT windowing function. Mode 1 has the effect of centering the first sample in the analysis window and ensuring that the very start and end of the segment are accounted for in the analysis. Mode 2 can be useful when the overlap factor (window size / hop size) is greater than 2, to ensure that the input samples at either end of the segment are covered by the same number of analysis frames as the rest of the analysed material.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [features] as an argument.
returns:: an instance of the processor
EXAMPLES::
code::
// create some buffers
(
b = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav"));
c = Buffer.new(s);
)
// run the process with basic parameters
(
Routine{
t = Main.elapsedTime;
FluidBufMFCC.process(s, b, features: c).wait;
(Main.elapsedTime - t).postln;
}.play
)
// listen to the source and look at the buffer
b.play;
c.plot(separately:true)
::
STRONG::A stereo buffer example.::
CODE::
// load two very different files
(
b = Buffer.read(s,FluidFilesPath("Tremblay-SA-UprightPianoPedalWide.wav"));
c = Buffer.read(s,FluidFilesPath("Tremblay-AaS-AcousticStrums-M.wav"));
)
// composite one on left one on right as test signals
FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000,destStartChan:1, destination:b)
b.play
// create a buffer as destinations
c = Buffer.new(s);
//run the process on them
(
Routine{
t = Main.elapsedTime;
FluidBufMFCC.process(s, b, numCoeffs:5, features: c).wait;
(Main.elapsedTime - t).postln;
}.play
)
// look at the buffer: 5 coefs for left, then 5 coefs for right (the first of each is linked to the loudness)
c.plot(separately:true)
::

@ -1,127 +0,0 @@
TITLE:: FluidBufMelBands
SUMMARY:: A Perceptually Spread Spectral Contour Descriptor on a Buffer
CATEGORIES:: Libraries>FluidCorpusManipulation
RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading, Classes/FluidBufMFCC
DESCRIPTION::
This class implements a spectral shape descriptor where the amplitude is given for a number of equally spread perceptual bands. The spread is based on the Mel scale (https://en.wikipedia.org/wiki/Mel_scale) which is one of the first attempt to mimic pitch perception scientifically. This implementation allows to select the range and number of bands dynamically. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The process will return a single multichannel buffer of STRONG::numBands:: per input channel. Each frame represents a value, which is every hopSize.
STRONG::Threading::
By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::.
CLASSMETHODS::
METHOD:: process, processBlocking
This is the method that calls for the spectral shape descriptors to be calculated on a given source buffer.
ARGUMENT:: server
The server on which the buffers to be processed are allocated.
ARGUMENT:: source
The index of the buffer to use as the source material to be described through the various descriptors. The different channels of multichannel buffers will be processing sequentially.
ARGUMENT:: startFrame
Where in the srcBuf should the process start, in sample.
ARGUMENT:: numFrames
How many frames should be processed.
ARGUMENT:: startChan
For multichannel srcBuf, which channel should be processed first.
ARGUMENT:: numChans
For multichannel srcBuf, how many channel should be processed.
ARGUMENT:: features
The destination buffer for the STRONG::numBands:: amplitudes describing the spectral shape.
ARGUMENT:: numBands
The number of bands that will be perceptually equally distributed between STRONG::minFreq:: and STRONG::maxFreq::. It will decide how many channels are produce per channel of the source.
ARGUMENT:: minFreq
The lower boundary of the lowest band of the model, in Hz.
ARGUMENT:: maxFreq
The highest boundary of the highest band of the model, in Hz.
ARGUMENT:: normalize
This flag enables the scaling of the output to preserve the energy of the window. It is on (1) by default.
ARGUMENT:: scale
This flag sets the scaling of the output value. It is either linear (0, by default) or in dB (1).
ARGUMENT:: windowSize
The window size. As spectral description relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty
ARGUMENT:: hopSize
The window hop size. As spectral description relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2).
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: padding
Controls the zero-padding added to either end of the source buffer or segment. Possible values are 0 (no padding), 1 (default, half the window size), or 2 (window size - hop size). Padding ensures that all input samples are completely analysed: with no padding, the first analysis window starts at time 0, and the samples at either end will be tapered by the STFT windowing function. Mode 1 has the effect of centering the first sample in the analysis window and ensuring that the very start and end of the segment are accounted for in the analysis. Mode 2 can be useful when the overlap factor (window size / hop size) is greater than 2, to ensure that the input samples at either end of the segment are covered by the same number of analysis frames as the rest of the analysed material.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [features] as an argument.
returns:: an instance of the processor
EXAMPLES::
code::
// create some buffers
(
b = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav"));
c = Buffer.new(s);
)
// run the process with basic parameters
(
Routine{
t = Main.elapsedTime;
FluidBufMelBands.process(s, b, features: c, numBands:10).wait;
(Main.elapsedTime - t).postln;
}.play
)
// listen to the source and look at the buffer
b.play;
c.plot
::
STRONG::A stereo buffer example.::
CODE::
// load two very different files
(
b = Buffer.read(s,FluidFilesPath("Tremblay-SA-UprightPianoPedalWide.wav"));
c = Buffer.read(s,FluidFilesPath("Tremblay-AaS-AcousticStrums-M.wav"));
)
// composite one on left one on right as test signals
FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000,destStartChan:1, destination:b)
b.play
// create a buffer as destinations
c = Buffer.new(s);
//run the process on them
(
Routine{
t = Main.elapsedTime;
FluidBufMelBands.process(s, b, features: c, numBands:10).wait;
(Main.elapsedTime - t).postln;
}.play
)
// look at the buffer: 10 bands for left, then 10 bands for right
c.plot(separately:true)
::

@ -1,629 +0,0 @@
TITLE:: FluidBufNMF
SUMMARY:: Buffer-Based Non-Negative Matrix Factorisation on Spectral Frames
CATEGORIES:: Libraries>FluidCorpusManipulation
RELATED:: Classes/FluidNMFMatch,Classes/FluidNMFFilter,Guides/FluidCorpusManipulationToolkit,Classes/FluidNMFMatch,Classes/FluidNMFFilter
DESCRIPTION::
Decomposes the spectrum of a sound into a number of components using Non-Negative Matrix Factorisation (NMF)
NMF has been a popular technique in signal processing research for things like source separation and transcription (see Smaragdis and Brown, Non-Negative Matrix Factorization for Polyphonic Music Transcription.), although its creative potential is so far relatively unexplored.
The algorithm takes a buffer in and divides it into a number of components, determined by the components argument. It works iteratively, by trying to find a combination of spectral templates ('bases') and envelopes ('activations') that yield the original magnitude spectrogram when added together. By and large, there is no unique answer to this question (i.e. there are different ways of accounting for an evolving spectrum in terms of some set of templates and envelopes). In its basic form, NMF is a form of unsupervised learning: it starts with some random data and then converges towards something that minimizes the distance between its generated data and the original:it tends to converge very quickly at first and then level out. Fewer iterations mean less processing, but also less predictable results.
DEFINITIONLIST::
## The object can return either or all of the following:
||
LIST::
##
a spectral contour of each component in the form of a magnitude spectrogram (called a basis in NMF lingo);
##
an amplitude envelope of each component in the form of gains for each consecutive frame of the underlying spectrogram (called an activation in NMF lingo);
##
an audio reconstruction of each components in the time domain.
::
::
The bases and activations can be used to make a kind of vocoder based on what NMF has 'learned' from the original data. Alternatively, taking the matrix product of a basis and an activation will yield a synthetic magnitude spectrogram of a component (which could be reconsructed, given some phase informaiton from somewhere).
Some additional options and flexibility can be found through combinations of the basesMode and actMode arguments. If these flags are set to 1, the object expects to be supplied with pre-formed spectra (or envelopes) that will be used as seeds for the decomposition, providing more guided results. When set to 2, the supplied buffers won't be updated, so become templates to match against instead. Note that having both bases and activations set to 2 doesn't make sense, so the object will complain.
DEFINITIONLIST::
## If supplying pre-formed data, it's up to the user to make sure that the supplied buffers are the right size:
||
LIST::
##
bases must be frames and channels
##
activations must be frames and channels
::
::
In this implementation, the components are reconstructed by masking the original spectrum, such that they will sum to yield the original sound.
The whole process can be related to a channel vocoder where, instead of fixed bandpass filters, we get more complex filter shapes that are learned from the data, and the activations correspond to channel envelopes.
CLASSMETHODS::
METHOD:: process, processBlocking
Processs the source LINK::Classes/Buffer:: on the LINK::Classes/Server::. CODE::processBlocking:: will execute directly in the server command FIFO, whereas CODE::process:: will delegate to a separate worker thread. The latter is generally only worthwhile for longer-running jobs where you don't wish to tie up the server.
ARGUMENT:: server
The LINK::Classes/Server:: on which the buffers to be processed are allocated.
ARGUMENT:: source
The index of the buffer to use as the source material to be decomposed through the NMF process. The different channels of multichannel buffers will be processing sequentially.
ARGUMENT:: startFrame
Where in the srcBuf should the NMF process start, in sample.
STRONG::Constraints::
LIST::
##
Minimum: 0
::
ARGUMENT:: numFrames
How many frames should be processed.
ARGUMENT:: startChan
For multichannel srcBuf, which channel should be processed first.
STRONG::Constraints::
LIST::
##
Minimum: 0
::
ARGUMENT:: numChans
For multichannel srcBuf, how many channel should be processed.
ARGUMENT:: resynth
The index of the buffer where the different reconstructed components will be reconstructed. The buffer will be resized to channels and lenght. If is provided, the reconstruction will not happen.
ARGUMENT:: bases
The index of the buffer where the different bases will be written to and/or read from: the behaviour is set in the following argument. If is provided, no bases will be returned.
ARGUMENT:: basesMode
This flag decides of how the basis buffer passed as the previous argument is treated.
ARGUMENT:: activations
The index of the buffer where the different activations will be written to and/or read from: the behaviour is set in the following argument. If is provided, no activation will be returned.
ARGUMENT:: actMode
This flag decides of how the activation buffer passed as the previous argument is treated.
ARGUMENT:: components
The number of elements the NMF algorithm will try to divide the spectrogram of the source in.
STRONG::Constraints::
LIST::
##
Minimum: 1
::
ARGUMENT:: iterations
The NMF process is iterative, trying to converge to the smallest error in its factorisation. The number of iterations will decide how many times it tries to adjust its estimates. Higher numbers here will be more CPU expensive, lower numbers will be more unpredictable in quality.
STRONG::Constraints::
LIST::
##
Minimum: 1
::
ARGUMENT:: windowSize
The window size. As NMF relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. LINK::http://www.subsurfwiki.org/wiki/Gabor_uncertainty::
ARGUMENT:: hopSize
The window hop size. As NMF relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts.
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default CODE::true::
ARGUMENT:: action
A function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed CODE::[features]:: as an argument.
RETURNS:: An instance of the processor
METHOD:: kr
Trigger the equivalent behaviour to CODE::processBlocking / process:: from a LINK::Classes/Synth::. Can be useful for expressing a sequence of buffer and data processing jobs to execute. Note that the work still executes on the server command FIFO (not the audio thread), and it is the caller's responsibility to manage the sequencing, using the CODE::done:: status of the various UGens.
ARGUMENT:: source
The index of the buffer to use as the source material to be decomposed through the NMF process. The different channels of multichannel buffers will be processing sequentially.
ARGUMENT:: startFrame
Where in the srcBuf should the NMF process start, in sample.
STRONG::Constraints::
LIST::
##
Minimum: 0
::
ARGUMENT:: numFrames
How many frames should be processed.
ARGUMENT:: startChan
For multichannel srcBuf, which channel should be processed first.
STRONG::Constraints::
LIST::
##
Minimum: 0
::
ARGUMENT:: numChans
For multichannel srcBuf, how many channel should be processed.
ARGUMENT:: resynth
The index of the buffer where the different reconstructed components will be reconstructed. The buffer will be resized to channels and lenght. If is provided, the reconstruction will not happen.
ARGUMENT:: bases
The index of the buffer where the different bases will be written to and/or read from: the behaviour is set in the following argument. If is provided, no bases will be returned.
ARGUMENT:: basesMode
This flag decides of how the basis buffer passed as the previous argument is treated.
ARGUMENT:: activations
The index of the buffer where the different activations will be written to and/or read from: the behaviour is set in the following argument. If is provided, no activation will be returned.
ARGUMENT:: actMode
This flag decides of how the activation buffer passed as the previous argument is treated.
ARGUMENT:: components
The number of elements the NMF algorithm will try to divide the spectrogram of the source in.
STRONG::Constraints::
LIST::
##
Minimum: 1
::
ARGUMENT:: iterations
The NMF process is iterative, trying to converge to the smallest error in its factorisation. The number of iterations will decide how many times it tries to adjust its estimates. Higher numbers here will be more CPU expensive, lower numbers will be more unpredictable in quality.
STRONG::Constraints::
LIST::
##
Minimum: 1
::
ARGUMENT:: windowSize
The window size. As NMF relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. LINK::http://www.subsurfwiki.org/wiki/Gabor_uncertainty::
ARGUMENT:: hopSize
The window hop size. As NMF relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts.
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision.
ARGUMENT:: trig
A CODE::kr:: signal that will trigger execution
ARGUMENT:: blocking
Whether to execute this process directly on the server command FIFO or delegate to a worker thread. See CODE::processBlocking/process:: for caveats.
INSTANCEMETHODS::
METHOD:: kr
Returns a UGen that reports the progress of the running task when executing in a worker thread. Calling code::scope:: with this can be used for a convinient progress monitor
METHOD:: cancel
Cancels non-blocking processing
METHOD:: wait
When called in the context of a LINK::Classes/Routine:: (it won't work otherwise), will block execution until the processor has finished. This can be convinient for writing sequences of processes more linearly than using lots of nested actions.
EXAMPLES::
STRONG::A didactic example::
CODE::
// =============== decompose some sounds ===============
// let's decompose the drum loop that comes with the FluCoMa extension:
~drums = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav"));
// hear the original mono sound file to know what we're working with
~drums.play;
// an empty buffer for the decomposed components to be written into:
~resynth = Buffer(s);
// how many components we want FluidBufNMF to try to decompose the buffer into:
~n_components = 2;
// process it:
FluidBufNMF.processBlocking(s,~drums,resynth:~resynth,components:~n_components,action:{"done".postln;});
// once it is done, play the separated components one by one (with a second of silence in between)
(
fork{
~n_components.do{
arg i;
"decomposed part #%".format(i+1).postln;
{
PlayBuf.ar(~n_components,~resynth,BufRateScale.ir(~resynth),doneAction:2)[i].dup;
}.play;
(~drums.duration + 1).wait;
}
};
)
// ======== now let's try it with three components. =========
// make a guess as to what you think you'll hear
~n_components = 3;
// process it:
FluidBufNMF.processBlocking(s,~drums,resynth:~resynth,components:~n_components,action:{"done".postln;});
(
fork{
~n_components.do{
arg i;
"decomposed part #%".format(i+1).postln;
{
PlayBuf.ar(~n_components,~resynth,BufRateScale.ir(~resynth),doneAction:2)[i].dup;
}.play;
(~drums.duration + 1).wait;
}
};
)
// you may have guessed that it would separate out the three components into: (1) snare, (2) hihat, and (3) kick
// and it might have worked! but it may not have, and it won't provide the same result every time because it
// starts each process from a stochastic state (you can seed this state if you want...see below).
// ====== bases and activations ========
// first, let's make two new buffers called...
~bases = Buffer(s);
~activations = Buffer(s);
~n_components = 2; // return to 2 components for this example
// and we'll explicitly pass these into the process
FluidBufNMF.processBlocking(s,~drums,bases:~bases,activations:~activations,resynth:~resynth,components:~n_components,action:{"done".postln;});
// now we can plot them (because this process starts from a stochastic state, your results may vary!):
FluidWaveform(~drums,featureBuffer:~activations,bounds:Rect(0,0,1200,300));
// the bases are a like a spectral template that FluidBufNMF has found in the source buffer
// in one you should see one spectrum that resembles a snare spectrum (the resonant tone of the snare
// in the mid range) and another that resembles the kick + hihat we heard earlier (a large peak in the very
// low register and some shimmery higher stuff)
FluidWaveform(featureBuffer:~bases,bounds:Rect(0,0,1200,300));
// the activations are the corresponding loudness envelope of each base above. It should like an amplitude
// envelope follower of the drum hits in the corresponding bases.
// FluidBufNMF then uses the individual bases with their corresponding activations to resynthesize the sound of just
// component.
// the buffer passed to `resynth` will have one channel for each component you've requested
~resynth.numChannels
~resynth.play;
// ======== to further understand NMF's bases and activations, consider one more object: FluidNMFFilter ==========
// FluidNMFFilter will use the bases (spectral templates) of a FluidBufNMF analysis to filter (i.e., decompose) real-time audio
// for example, if we use the bases from the ~drums analysis above, it will separate the snare from the kick & hi hat like before
// this time you'll hear one in each stereo channel (again, results may vary)
(
{
var src = PlayBuf.ar(1,~drums,BufRateScale.ir(~drums),doneAction:2);
var sig = FluidNMFFilter.ar(src,~bases,2);
sig;
}.play;
)
// if we play a different source through FluidNMFFilter, it will try to decompose that real-time signal according to the bases
// it is given (in our case the bases from the drum loop)
~song = Buffer.readChannel(s,FluidFilesPath("Tremblay-BeatRemember.wav"),channels:[0]);
(
{
var src = PlayBuf.ar(1,~song,BufRateScale.ir(~song),doneAction:2);
var sig = FluidNMFFilter.ar(src,~bases,2);
sig;
}.play;
)
// ========= the activations could also be used as an envelope through time ===========
(
{
var activation = PlayBuf.ar(2,~activations,BufRateScale.ir(~activations),doneAction:2);
var sig = WhiteNoise.ar(0.dbamp) * activation;
sig;
}.play;
)
// note that the samplerate of the ~activations buffer is not a usual one...
~activations.sampleRate;
// this is because each frame in this buffer doesn't correspond to one audio sample, but instead to one
// hopSize, since these values are derived from an FFT analysis
// so it is important to use BufRateScale (as seen above) in order to make sure they play back at the
// correct rate
// if we control the amplitude of the white noise *and* send it through FluidNMFFilter, we'll get something
// somewhat resembles both the spectral template and loudness envelope of the bases of the original
// (of course it's also good to note that the combination of the *actual* bases and activations is how
// FluidBufNMF creates the channels in the resynth buffer which will sound much better than this
// filtered WhiteNoise version)
(
{
var activation = PlayBuf.ar(2,~activations,BufRateScale.ir(~activations),doneAction:2);
var sig = WhiteNoise.ar(0.dbamp);
sig = FluidNMFFilter.ar(sig,~bases,2) * activation;
sig;
}.play;
)
::
STRONG::Fixed Bases: The process can be trained, and the learnt bases or activations can be used as templates.::
CODE::
//set some buffers
(
b = Buffer.read(s,FluidFilesPath("Tremblay-AaS-AcousticStrums-M.wav"));
~originalNMF = Buffer.new(s);
~bases = Buffer.new(s);
~trainedBases = Buffer.new(s);
~activations = Buffer.new(s);
~final = Buffer.new(s);
~spectralshapes = Buffer.new(s);
~stats = Buffer.new(s);
~sortedNMF = Buffer.new(s);
)
b.play
// train using the first 2 seconds of the sound file
(
Routine {
FluidBufNMF.process(s,b,0,44100*5,0,1, ~originalNMF, ~bases, components:10).wait;
~originalNMF.query;
}.play;
)
// listen to the 10 components across the stereo image
{Splay.ar(PlayBuf.ar(10, ~originalNMF))}.play
// plot the bases
~bases.plot
// find the component that has the picking sound by checking the median spectral centroid
(
FluidBufSpectralShape.process(s, ~originalNMF, features: ~spectralshapes, action:{
|shapes|FluidBufStats.process(s,shapes,stats:~stats, action:{
|stats|stats.getn(0, (stats.numChannels * stats.numFrames) ,{
|x| ~centroids = x.select({
|item, index| (index.mod(7) == 0) && (index.div(70) == 5);
})
})
})
});
)
//what is happening there? We run the spectralshapes on the buffer of 10 components from the nmf. See the structure of that buffer:
~originalNMF.query
//10 channel are therefore giving 70 channels: the 7 shapes of component0, then 7 shapes of compoenent1, etc
~spectralshapes.query
// we then run the bufstats on them. Each channel, which had a time series (an envelope) of each descriptor, is reduced to 7 frames
~stats.query
// we then need to retrieve the values that are where we want: the first of every 7 for the centroid, and the 6th frame of them as we want the median. Because we retrieve the values in an interleave format, the select function gets a bit tricky but we get the following values:
~centroids.postln
// we then copy the basis with the highest median centroid to a channel, and all the other bases to the other channel, of a 2-channel bases for decomposition
(
z = (0..9);
[z.removeAt(~centroids.maxIndex)].do{|chan|FluidBufCompose.process(s, ~bases, startChan: chan, numChans: 1, destination: ~trainedBases, destGain:1)};
z.postln;
z.do({|chan| FluidBufCompose.process(s, ~bases, startChan:chan, numChans: 1, destStartChan: 1, destination: ~trainedBases, destGain:1)});
)
~trainedBases.plot
//process the whole file, splitting it with the 2 trained bases
(
Routine{
FluidBufNMF.process(s, b, resynth: ~sortedNMF, bases: ~trainedBases, basesMode: 2, components:2).wait;
~originalNMF.query;
}.play;
)
// play the result: pick on the left, sustain on the right!
{PlayBuf.ar(2,~sortedNMF)}.play
// it even null-sums
{(PlayBuf.ar(2,~sortedNMF,doneAction:2).sum)-(PlayBuf.ar(1,b,doneAction:2))}.play
::
STRONG::Updating Bases: The process can update bases provided as seed.::
CODE::
(
// create buffers
b = Buffer.alloc(s,44100);
c = Buffer.alloc(s, 44100);
d = Buffer.new(s);
e = Buffer.alloc(s,513,3);
f = Buffer.new(s);
g = Buffer.new(s);
)
(
// fill them with 2 clearly segregated sine waves and composite a buffer where they are consecutive
Routine {
b.sine2([500],[1], false, false);
c.sine2([5000],[1],false, false);
s.sync;
FluidBufCompose.process(s,b, destination:d);
FluidBufCompose.process(s,c, destStartFrame:44100, destination:d, destGain:1);
s.sync;
d.query;
}.play;
)
// check
d.plot
d.play //////(beware !!!! loud!!!)
(
//make a seeding basis of 3 components:
var highpass, lowpass, direct;
highpass = Array.fill(513,{|i| (i < 50).asInteger});
lowpass = 1 - highpass;
direct = Array.fill(513,0.1);
e.setn(0,[highpass, lowpass, direct].flop.flat);
)
//check the basis: a steep lowpass, a steep highpass, and a small DC
e.plot
e.query
(
// use the seeding basis, without updating
Routine {
FluidBufNMF.process(s, d, resynth:f, bases: e, basesMode: 2, activations:g, components:3).wait;
e.query;
f.query;
g.query;
}.play
)
// look at the resynthesised separated signal
f.plot;
// look at the bases that have not changed
e.plot;
// look at the activations
g.plot;
(
// use the seeding bases, with updating this time
Routine {
FluidBufNMF.process(s, d, resynth:f, bases: e, basesMode: 1, activations:g, components:3).wait;
e.query;
f.query;
g.query;
}.play
)
// look at the resynthesised separated signal
f.plot;
// look at the bases that have now updated in place (with the 3rd channel being more focused
e.plot;
// look at the activations (sharper 3rd component at transitions)
g.plot;
::

@ -1,93 +0,0 @@
TITLE:: FluidBufNMFCross
summary:: Reconstructs the sound in the target buffer using components learned from the source buffer using an NMF decomposition
categories:: Libraries>FluidCorpusManipulation
related:: Classes/FluidBufNMF, Classes/FluidNMFMatch, Classes/FluidNMFFilter
DESCRIPTION::
The process works by attempting to reconstruct compoentns of the code::target:: sound using the timbre of the code::source:: sound, learned through a Nonnegative Matrix Factorisation. The result is a hybrid whose character depends on how well the target can be represnted by the source's spectral frames.
In contrast to link::Classes/FluidBufNMF::, the size and content of the bases dictionary are fixed in this application to be the spectrogram of the `source`. Each spectral frame of `source` is a template: be aware that NMF is O(N^2) in the number of templates, so longer `source` link::Classes/Buffer::s will take dramatically longer to process.
See Driedger, J., Prätzlich, T., & Müller, M. (2015). Let it Bee-Towards NMF-Inspired Audio Mosaicing. ISMIR, 350356. http://ismir2015.uma.es/articles/13_Paper.pdf
CLASSMETHODS::
private:: kr, new1
METHOD:: process, processBlocking
Process two buffers. code::process:: will use its own worker thread on the server, and so avoid blocking the command FIFO queue. For very small jobs, it may be quicker to use code::processBlocking::, which runs directly in the server's queue.
ARGUMENT:: server
The link::Classes/Server:: on which to process
ARGUMENT:: source
A link::Classes/Buffer:: whose content will supply the spectral bases used in the hybrid
ARGUMENT:: target
A link::Classes/Buffer:: whose content will supply the temporal activations used in the hybrid
ARGUMENT:: output
A link::Classes/Buffer:: to contain the new sound
ARGUMENT:: timeSparsity
Control the repetition of source templates in the reconstruction by specifying a number of frames within which a template should not be re-used. Units are spectral frames.
ARGUMENT:: polyphony
Control the spectral density of the output sound by restricting the number of simultaneous templates that can be used. Units are spectral bins.
ARGUMENT:: continuity
Promote the use of N successive source frames, giving greater continuity in the result. This can not be bigger than the sizes of the input buffers, but useful values tend to be much lower (in the tens).
ARGUMENT:: iterations
How many iterations of NMF to run
ARGUMENT:: windowSize
The analysis window size in samples
ARGUMENT:: hopSize
The analysus hop size in samples (default winSize / 2)
ARGUMENT:: fftSize
The analsyis FFT size in samples (default = winSize)
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A function to run when processing is complete, taking the output buffer as its argument
returns:: an instance of the processor
INSTANCEMETHODS::
EXAMPLES::
code::
~path = FluidFilesPath();
b = Buffer.read(s,~path+/+"Nicol-LoopE-M.wav")
t = Buffer.read(s,~path+/+"Tremblay-SA-UprightPianoPedalWide.wav")
o = Buffer.new
FluidBufNMFCross.process(s,t,b,o,windowSize: 2048, action:{"Ding".postln})
//wait for it to be done. It can take a while, depending on the length of your source.
o.play
//The result of the cross synthesis is a hybrid of the source and target sounds. The algorithm tries to match the target spectrum over time using components learned from the source. These parameters affect the reconstruction:
~sparsity = 4; //Avoid reusing a component from the source for this number of time frames
~polyphony = 3; //Avoid overlapping more than this number of source components at the same time
~continuity = 20; //Encourage the reconstruction to use this many temporally consecutive frames from the source
//Using the UGen to run the process can be useful to monitor its progress
(
Routine{
~cross = FluidBufNMFCross.process(s,t,b,o,timeSparsity: ~sparsity, polyphony: ~polyphony, continuity: ~continuity, windowSize: 2048);
defer{{FreeSelfWhenDone.kr(~cross.kr).poll}.play;};
~cross.wait;
\Done.postln;
}.play;
)
o.play
::

@ -1,100 +0,0 @@
TITLE:: FluidBufNMFSeed
summary:: Non-Negative Double Singular Value Decomposition on a Buffer
categories:: Libraries>FluidCorpusManipulation
related:: Classes/FluidBufNMF
DESCRIPTION::
Find Initial Bases and Activations for FluidBufNMF via Non-Negative Double Singular Value Decomposition .
See http://nimfa.biolab.si/nimfa.methods.seeding.nndsvd.html
CLASSMETHODS::
METHOD:: process, processBlocking
This is the method that calls for the decomposition to be calculated on a given source buffer.
ARGUMENT:: server
The server on which the buffers to be processed are allocated.
ARGUMENT:: source
The index of the buffer to use as the source material to be decomposed through the NMF process. The different channels of multichannel buffers will be processing sequentially.
ARGUMENT:: bases
The index of the buffer where the different bases will be written to and/or read from: the behaviour is set in the following argument.
ARGUMENT:: activations
The index of the buffer where the different activations will be written to and/or read from: the behaviour is set in the following argument.
ARGUMENT:: minComponents
Minimum number of estimated components
ARGUMENT:: maxComponents
Maximum number of estimated components
ARGUMENT:: coverage
Fraction (0 to 1) of information preserved in the decomposition
ARGUMENT:: method
The method used for the decomposition. Options are:
table::
## 0 || NMF-SVD || faster
## 1 || NNDSVDar || more accurate, fill in the zero elements with random values
## 2 || NNDSVDa || fill in the zero elements with the average
## 3 || NNDSVD || leave zero elements as zero, works better for sparse spectrograms
::
ARGUMENT:: windowSize
The window size. As spectral differencing relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty
ARGUMENT:: hopSize
The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2).
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the highest of windowSize and (bandwidth - 1) * 2.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [destination] as an argument.
returns:: an instance of the processor
INSTANCEMETHODS::
private:: synth, server
EXAMPLES::
code::
(
b = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav"));
~bases = Buffer.new(s);
~activations = Buffer.new(s);
~resynth = Buffer.new(s);
)
//how many bases do I need to decompose the buffer with 90% accuracy
(
Routine{
FluidBufNMFSeed.process(s, b, ~bases, ~activations, coverage: 0.9, method: 1).wait;
"% bases".format(~bases.numChannels).postln;
}.play;
)
//check how many bases we are returned:
//try the same process with less accuracy
(
Routine{
FluidBufNMFSeed.process(s, b, ~bases, ~activations, coverage: 0.5).wait;
"% bases".format(~bases.numChannels).postln;
}.play
)
//use the bases to run NMF on
FluidBufNMF.process(s, b, resynth: ~resynth, bases: ~bases, activations: ~activations,actMode: 2, components: ~bases.numChannels, action: {\done.postln;})
{PlayBuf.ar(~resynth.numChannels, ~resynth)[2]}.play
::

@ -1,179 +0,0 @@
TITLE:: FluidBufNoveltySlice
SUMMARY:: Buffer-Based Novelty-Based Slicer
CATEGORIES:: Libraries>FluidCorpusManipulation, UGens>Buffer
RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading
DESCRIPTION::
This class implements a non-real-time slicer using an algorithm assessing novelty in the signal to estimate the slicing points. A novelty curve is being derived from running a kernel across the diagonal of the similarity matrix, and looking for peak of changes. It implements the seminal results published in 'Automatic Audio Segmentation Using a Measure of Audio Novelty' by J Foote. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The process will return a buffer which contains indices (in sample) of estimated starting points of different slices.
STRONG::Threading::
By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::.
CLASSMETHODS::
METHOD:: process, processBlocking
This is the method that calls for the slicing to be calculated on a given source buffer.
ARGUMENT:: server
The server on which the buffers to be processed are allocated.
ARGUMENT:: source
The index of the buffer to use as the source material to be sliced through novelty identification. The different channels of multichannel buffers will be summed.
ARGUMENT:: startFrame
Where in the srcBuf should the slicing process start, in sample.
ARGUMENT:: numFrames
How many frames should be processed.
ARGUMENT:: startChan
For multichannel srcBuf, which channel should be processed.
ARGUMENT:: numChans
For multichannel srcBuf, how many channel should be summed.
ARGUMENT:: indices
The index of the buffer where the indices (in sample) of the estimated starting points of slices will be written. The first and last points are always the boundary points of the analysis.
ARGUMENT:: feature
The feature on which novelty is computed.
table::
##0 || Spectrum || The magnitude of the full spectrum.
##1 || MFCC || 13 Mel-Frequency Cepstrum Coefficients.
##2 || Chroma || The contour of a 12 band Chromagram.
##3 || Pitch || The pitch and its confidence.
##4 || Loudness || The TruePeak and Loudness.
::
ARGUMENT:: kernelSize
The granularity of the window in which the algorithm looks for change, in samples. A small number will be sensitive to short term changes, and a large number should look for long term changes.
ARGUMENT:: threshold
The normalised threshold, between 0 an 1, on the novelty curve to consider it a segmentation point.
ARGUMENT:: filterSize
The size of a smoothing filter that is applied on the novelty curve. A larger filter filter size allows for cleaner cuts on very sharp changes.
ARGUMENT:: minSliceLength
The minimum duration of a slice in number of hopSize.
ARGUMENT:: windowSize
The window size. As novelty estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty
ARGUMENT:: hopSize
The window hop size. As novelty estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2).
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The function will be passed indices as an argument.
returns:: an instance of the processor
EXAMPLES::
code::
// load some buffers
(
b = Buffer.read(s,FluidFilesPath("Tremblay-AaS-AcousticStrums-M.wav"));
c = Buffer.new(s);
)
(
// with basic params, with a minimum slight length to avoid over
Routine{
t = Main.elapsedTime;
FluidBufNoveltySlice.process(s,b, indices: c, threshold:0.4,filterSize: 4, minSliceLength: 8).wait;
(Main.elapsedTime - t).postln;
}.play
)
//check the number of slices: it is the number of frames in the transBuf minus the boundary index.
c.query;
//loops over a splice with the MouseX
(
{
BufRd.ar(1, b,
Phasor.ar(0,1,
BufRd.kr(1, c,
MouseX.kr(0, BufFrames.kr(c) - 1), 0, 1),
BufRd.kr(1, c,
MouseX.kr(1, BufFrames.kr(c)), 0, 1),
BufRd.kr(1,c,
MouseX.kr(0, BufFrames.kr(c) - 1), 0, 1)), 0, 1);
}.play;
)
::
STRONG::Examples of the impact of the filterSize::
CODE::
// load some buffers
(
b = Buffer.read(s,FluidFilesPath("Tremblay-AaS-AcousticStrums-M.wav"));
c = Buffer.new(s);
)
// process with a given filterSize
(
Routine{
FluidBufNoveltySlice.process(s,b, indices: c, kernelSize:31, threshold:0.1, filterSize:1).wait;
//check the number of slices: it is the number of frames in the transBuf minus the boundary index.
c.query;
}.play;
)
//play slice number 3
(
{
BufRd.ar(1, b,
Line.ar(
BufRd.kr(1, c, DC.kr(3), 0, 1),
BufRd.kr(1, c, DC.kr(4), 0, 1),
(BufRd.kr(1, c, DC.kr(4)) - BufRd.kr(1, c, DC.kr(3), 0, 1) + 1) / s.sampleRate),
0,1);
}.play;
)
// change the filterSize in the code above to 4. Then to 12. Listen in between to the differences.
// What's happening? In the first instance (filterSize = 1), the novelty line is jittery and therefore overtriggers on the arpegiated guitar. We also can hear attacks at the end of the segment. Setting the threshold higher (like in the 'Basic Example' pane) misses some more subtle variations.
// So in the second settings (filterSize = 4), we smooth the novelty line a little, which allows us to catch small differences that are not jittery. It also corrects the ending cutting by the same trick: the averaging of the sharp pick is sliding up, crossing the threshold slightly earlier.
// If we smooth too much, like the third settings (filterSize = 12), we start to loose precision and miss attacks. Have fun with different values of theshold then will allow you to find the perfect segment for your signal.
::
STRONG::A stereo buffer example.::
CODE::
// make a stereo buffer
b = Buffer.alloc(s,88200,2);
// add some stereo clicks and listen to them
((0..3)*22050+11025).do({|item,index| b.set(item+(index%2), 1.0)});
b.play
// create a new buffer as destinations
c = Buffer.new(s);
//run the process on them
(
// with basic params
Routine{
t = Main.elapsedTime;
FluidBufNoveltySlice.process(s,b, indices: c, threshold:0.3).wait;
(Main.elapsedTime - t).postln;
}.play
)
// list the indicies of detected attacks - the two input channels have been summed
c.getn(0,c.numFrames,{|item|(item * 2).postln;})
::

@ -1,146 +0,0 @@
TITLE:: FluidBufOnsetSlice
SUMMARY:: Spectral Difference-Based Audio Buffer Slicer
CATEGORIES:: Libraries>FluidCorpusManipulation
RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading
DESCRIPTION::
This class implements many spectral-based onset detection metrics, most of them taken from the literature. (http://www.dafx.ca/proceedings/papers/p_133.pdf) Some are already available in SuperCollider's LINK::Classes/Onsets:: object yet not as offline processes. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The process will return a buffer which contains indices (in samples) of estimated starting points of different slices. If a startFrame argument is supplied, the starting point indices are still specified in terms of their location in the original buffer; they are not offset by the number startFrames.
STRONG::Threading::
By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::.
CLASSMETHODS::
METHOD:: process, processBlocking
This is the method that calls for the slicing to be calculated on a given source buffer.
ARGUMENT:: server
The server on which the buffers to be processed are allocated.
ARGUMENT:: source
The index of the buffer to use as the source material to be sliced through novelty identification. The different channels of multichannel buffers will be summed.
ARGUMENT:: startFrame
Where in the srcBuf should the slicing process start, in sample.
ARGUMENT:: numFrames
How many frames should be processed.
ARGUMENT:: startChan
For multichannel sources, which channel should be processed.
ARGUMENT:: numChans
For multichannel sources, how many channel should be summed.
ARGUMENT:: indices
The index of the buffer where the indices (in sample) of the estimated starting points of slices will be written. The first and last points are always the boundary points of the analysis.
ARGUMENT:: metric
The metric used to derive a difference curve between spectral frames. It can be any of the following:
TABLE::
##0 || Energy || thresholds on (sum of squares of magnitudes / nBins) (like Onsets \power)
##1 || HFC || thresholds on (sum of (squared magnitudes * binNum) / nBins)
##2 || SpectralFlux || thresholds on (diffence in magnitude between consecutive frames, half rectified)
##3 || MKL || thresholds on (sum of log of magnitude ratio per bin) (or equivalent: sum of difference of the log magnitude per bin) (like Onsets \mkl)
##4 || IS || (WILL PROBABLY BE REMOVED) Itakura - Saito divergence (see literature)
##5 || Cosine || thresholds on (cosine distance between comparison frames)
##6 || PhaseDev || takes the past 2 frames, projects to the current, as anticipated if it was a steady state, then compute the sum of the differences, on which it thresholds (like Onsets \phase)
##7 || WPhaseDev || same as PhaseDev, but weighted by the magnitude in order to remove chaos noise floor (like Onsets \wphase)
##8 || ComplexDev || same as PhaseDev, but in the complex domain - the anticipated amp is considered steady, and the phase is projected, then a complex subtraction is done with the actual present frame. The sum of magnitudes is used to threshold (like Onsets \complex)
##9 || RComplexDev || same as above, but rectified (like Onsets \rcomplex)
::
ARGUMENT:: threshold
The thresholding of a new slice. Value ranges are different for each metric, from 0 upwards.
ARGUMENT:: minSliceLength
The minimum duration of a slice in number of hopSize.
ARGUMENT:: filterSize
The size of a smoothing filter that is applied on the novelty curve. A larger filter filter size allows for cleaner cuts on very sharp changes.
ARGUMENT:: frameDelta
For certain metrics (HFC, SpectralFlux, MKL, Cosine), the distance does not have to be computed between consecutive frames. By default (0) it is, otherwise this sets the distance between the comparison window in samples.
ARGUMENT:: windowSize
The window size. As spectral differencing relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty
ARGUMENT:: hopSize
The window hop size. As spectral differencing relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2).
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The function will be passed indices as an argument.
returns:: an instance of the processor
EXAMPLES::
CODE::
(
//prep some buffers
b = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav"));
c = Buffer.new(s);
)
(
// with basic params
Routine{
t = Main.elapsedTime;
FluidBufOnsetSlice.process(s,b, indices: c, threshold:0.5).wait;
(Main.elapsedTime - t).postln;
}.play
)
//check the number of slices: it is the number of frames in the transBuf minus the boundary index.
c.query;
//loops over a splice with the MouseX
(
{
BufRd.ar(1, b,
Phasor.ar(0,1,
BufRd.kr(1, c,
MouseX.kr(0, BufFrames.kr(c) - 1), 0, 1),
BufRd.kr(1, c,
MouseX.kr(1, BufFrames.kr(c)), 0, 1),
BufRd.kr(1,c,
MouseX.kr(0, BufFrames.kr(c) - 1), 0, 1)), 0, 1);
}.play;
)
::
STRONG::A stereo buffer example.::
CODE::
// make a stereo buffer
b = Buffer.alloc(s,88200,2);
// add some stereo clicks and listen to them
((0..3)*22050+11025).do({|item,index| b.set(item+(index%2), 1.0)})
b.play
// create a new buffer as destinations
c = Buffer.new(s);
//run the process on them
(
// with basic params
Routine{
t = Main.elapsedTime;
FluidBufOnsetSlice.process(s,b, indices: c, threshold:0.00001).wait;
(Main.elapsedTime - t).postln;
}.play
)
// list the indicies of detected attacks - the two input channels have been summed
c.getn(0,c.numFrames,{|item|(item * 2).postln;})
::

@ -1,193 +0,0 @@
TITLE:: FluidBufPitch
SUMMARY:: A Selection of Pitch Descriptors on a Buffer
CATEGORIES:: Libraries>FluidCorpusManipulation
RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading, Classes/SpecCentroid, Classes/SpecFlatness, Classes/SpecCentroid, Classes/SpecPcile
DESCRIPTION::
This class implements three popular pitch descriptors, computed as frequency and the confidence in its value. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The process will return a multichannel buffer with two channels per input channel, one for pitch and one for the pitch tracking confidence. A pitch of 0 Hz is yield (or -999.0 when the unit is in MIDI note) when the algorithm cannot find a fundamental at all. Each sample represents a value, which is every hopSize. Its sampling rate is sourceSR / hopSize.
STRONG::Threading::
By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::.
CLASSMETHODS::
METHOD:: process, processBlocking
This is the method that calls for the pitch descriptor to be calculated on a given source buffer.
ARGUMENT:: server
The server on which the buffers to be processed are allocated.
ARGUMENT:: source
The index of the buffer to use as the source material to be pitch-tracked. The different channels of multichannel buffers will be processing sequentially.
ARGUMENT:: startFrame
Where in the srcBuf should the process start, in sample.
ARGUMENT:: numFrames
How many frames should be processed.
ARGUMENT:: startChan
For multichannel srcBuf, which channel should be processed first.
ARGUMENT:: numChans
For multichannel srcBuf, how many channel should be processed.
ARGUMENT:: features
The destination buffer for the pitch descriptors.
ARGUMENT:: algorithm
The algorithm to estimate the pitch. The options are:
TABLE::
## 0 || Cepstrum: Returns a pitch estimate as the location of the second highest peak in the Cepstrum of the signal (after DC).
## 1 || Harmonic Product Spectrum: Implements the Harmonic Product Spectrum algorithm for pitch detection . See e.g. FOOTNOTE:: A. Lerch, "An Introduction to Audio Content Analysis: Applications in Signal Processing and Music Informatics." John Wiley & Sons, 2012.https://onlinelibrary.wiley.com/doi/book/10.1002/9781118393550 ::
## 2 || YinFFT: Implements the frequency domain version of the YIN algorithm, as described in FOOTNOTE::P. M. Brossier, "Automatic Annotation of Musical Audio for Interactive Applications." QMUL, London, UK, 2007. :: See also https://essentia.upf.edu/documentation/reference/streaming_PitchYinFFT.html
::
ARGUMENT:: minFreq
The minimum frequency that the algorithm will search for an estimated fundamental. This sets the lowest value that will be generated.
ARGUMENT:: maxFreq
The maximum frequency that the algorithm will search for an estimated fundamental. This sets the highest value that will be generated.
ARGUMENT:: unit
The unit of the estimated value. The default of 0 is in Hz. A value of 1 will convert to MIDI note values.
ARGUMENT:: windowSize
The window size. As sinusoidal estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty
ARGUMENT:: hopSize
The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2).
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: padding
Controls the zero-padding added to either end of the source buffer or segment. Possible values are 0 (no padding), 1 (default, half the window size), or 2 (window size - hop size). Padding ensures that all input samples are completely analysed: with no padding, the first analysis window starts at time 0, and the samples at either end will be tapered by the STFT windowing function. Mode 1 has the effect of centering the first sample in the analysis window and ensuring that the very start and end of the segment are accounted for in the analysis. Mode 2 can be useful when the overlap factor (window size / hop size) is greater than 2, to ensure that the input samples at either end of the segment are covered by the same number of analysis frames as the rest of the analysed material.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [features] as an argument.
returns:: an instance of the processor
EXAMPLES::
code::
// create a buffer with a short clicking sinusoidal burst (220Hz) starting at frame 8192 for 1024 frames
(
b = Buffer.sendCollection(s, (Array.fill(8192,{0}) ++ (Signal.sineFill(1203,[0,0,0,0,0,1],[0,0,0,0,0,0.5pi]).takeThese({|x,i|i>1023})) ++ Array.fill(8192,{0})));
c = Buffer.new(s);
)
// listen to the source and look at the buffer
b.play; b.plot;
// run the process with basic parameters
(
Routine{
t = Main.elapsedTime;
FluidBufPitch.process(s, b, features: c).wait;
(Main.elapsedTime - t).postln;
}.play
)
// look at the analysis
c.plot(separately:true)
// The values are interleaved [pitch,confidence] in the buffer as they are on 2 channels: to get to the right frame, divide the SR of the input by the hopSize, then multiply by 2 because of the channel interleaving
// here we are querying from one frame before (the signal starts at 8192, which is frame 16 (8192/512), therefore starting the query at frame 15, which is index 30.
c.getn(30,10,{|x|x.postln})
// observe that the first frame is silent, as expected. The next frame's confidence is low-ish, because the window is half full (window of 1024, overlap of 512). Then a full window is analysed, with strong confidence. Then another half full window, then silence, as expected.
::
STRONG::A stereo buffer example.::
CODE::
// load two very different files
(
b = Buffer.read(s,FluidFilesPath("Tremblay-SA-UprightPianoPedalWide.wav"));
c = Buffer.read(s,FluidFilesPath("Tremblay-AaS-AcousticStrums-M.wav"));
)
// composite one on left one on right as test signals
FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000,destStartChan:1, destination:b)
b.play
// create a buffer as destinations
c = Buffer.new(s);
//run the process on them, with limited bandwidth and units in MIDI notes
(
Routine{
t = Main.elapsedTime;
FluidBufPitch.process(s, b, features: c, minFreq:200, maxFreq:2000, unit:1).wait;
(Main.elapsedTime - t).postln;
}.play
)
// look at the buffer: [pitch,confidence] for left then [pitch,confidence] for right
c.plot(separately:true)
::
STRONG::A musical example.::
code::
// create some buffers
(
b = Buffer.read(s,FluidFilesPath("Tremblay-ASWINE-ScratchySynth-M.wav"));
c = Buffer.new(s);
)
// run the process with basic parameters and retrieve the array in the langage side
(
Routine{
t = Main.elapsedTime;
FluidBufPitch.process(s, b, features: c,action:{c.loadToFloatArray(action: {|x| d = x.reshape((x.size()/2).asInteger, 2)})}).wait;
(Main.elapsedTime - t).postln;
}.play
)
//look at the retrieved formatted array of [pitch,confidence] values
d.postln
//iterate and make an array of the indices which are fitting the conditions
(
e = Array.new;
d.do({
arg val, i;
if ((val[0] > 500) && (val[1] > 0.98)) {e = e.add(i)}; // if pitch is greater than 500Hz and confidence higher than 0.98, keep the index
});
)
e.postln;
//granulate only the frames that are in our buffer
// We need to convert our indices to frame start. Their position was (index * hopSize) - (hopSize) in FluidBufPitch
f = e.collect({arg i; (i * 512) - 512});
// define a basic grain synth
(
SynthDef(\grain,
{ arg out=0, buf =0 , ind = 0, pan = 0;
var env;
env = EnvGen.kr(Env.new([0,1,0],[512/s.sampleRate].dup,\sine), doneAction: Done.freeSelf);
Out.ar(out, Pan2.ar(PlayBuf.ar(1,buf,startPos:ind),pan));
}).add;
)
// start the sequence
(
a = Pxrand(f, inf).asStream;
Routine({
loop({
Synth(\grain, [\buf, b, \ind, a.next, \pan, (2.0.rand - 1)]);
(256/s.sampleRate).wait;
})
}).play;
)
::

@ -1,109 +0,0 @@
TITLE:: FluidBufSTFT
summary:: Perform a Short-Time Fourier Transform on one channel of a buffer
categories:: Libraries>FluidCorpusManipulation
related:: Classes/Buffer
DESCRIPTION::
Performs either a forward or inverse Short-Time Fourier Transform (STFT) on a single channel source buffer~. In the forward case, resulting magnitudes and phases can be written to output buffers. In the inverse case, these buffers can be used to reconstruct the original source into a new buffer.
The magntude and phase buffers are laid out as (number of hops, number of bins). The number of hops is a function of the source length and the hop size. The number of bins is (1 + (fft size / 2)).
The object is restricted to analysing a single source channel, because the channel counts of the magntude and phase buffers would quickly get out of hand otherwise.
CLASSMETHODS::
private::new1
METHOD:: process, processBlocking
Run the process on the given sever, and perfrom code::action:: when done
ARGUMENT:: server
The link::Classes/Server:: on which to run
ARGUMENT:: source
The link::Classes/Buffer:: to use for the forward STFT
ARGUMENT:: startFrame
The starting point for analysis in the source (in samples)
ARGUMENT:: numFrames
The duration (in samples) to analyse
ARGUMENT:: startChan
The channel to analyse
ARGUMENT:: magnitude
The link::Classes/Buffer:: to write magnitudes to in the forward case, or read from in the inverse case. This is optional for the forward transform, mandatory for the inverse.
ARGUMENT:: phase
The link::Classes/Buffer:: to write phases to in the forward case, or read from in the inverse case. This is optional for the forward transform, mandatory for the inverse.
ARGUMENT:: resynth
The link::Classes/Buffer:: to write re-synthesised data to in the inverse case. Ignored for the forward transform. Mandatory in the inverse case.
ARGUMENT:: inverse
When set to 1, an inverse STFT is performed, and the resynthesised data is written to the resynthesis buffer using overlap-add.
ARGUMENT:: windowSize
The number of source samples that are analysed at once.
ARGUMENT:: hopSize
How many samples there are in-between analysis windows. The -1 default value will default to half of windowSize (overlap of 2).
ARGUMENT:: fftSize
The FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize. For this object it is effectively capped at 65536.
ARGUMENT:: padding
Controls the zero-padding added to either end of the source buffer or segment. Possible values are 0 (no padding), 1 (default, half the window size), or 2 (window size - hop size). Padding ensures that all input samples are completely analysed: with no padding, the first analysis window starts at time 0, and the samples at either end will be tapered by the STFT windowing function. Mode 1 has the effect of centering the first sample in the analysis window and ensuring that the very start and end of the segment are accounted for in the analysis. Mode 2 can be useful when the overlap factor (window size / hop size) is greater than 2, to ensure that the input samples at either end of the segment are covered by the same number of analysis frames as the rest of the analysed material.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
Runs when processing is complete
INSTANCEMETHODS::
EXAMPLES::
code::
s.reboot
(
b = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav"));
m = Buffer.new;
p = Buffer.new;
r = Buffer.new;
)
(
fork{
FluidBufSTFT.process(s,source:b,magnitude:m,phase:p).wait;
FluidBufSTFT.process(s,magnitude:m,phase:p,resynth:r,inverse:1).wait;
"Done".postln;
}
)
{ PlayBuf.ar(1,r); }.play
//nullsum
{ PlayBuf.ar(1,r) - PlayBuf(1,b); }.play
//draw the magnitudes as a greyscale spectrogram
// make the image
i = Image.new(m.numFrames, m.numChannels)
//retreive the image and assign to pixels
(
m.loadToFloatArray(action: {|x|
var mod = m.numChannels;
{
x.do{
|val, index|
i.setColor(Color.gray(val), index.div(mod), mod - 1 - index.mod(mod));
};
i.plot("spectrogram", showInfo: false);
}.fork(AppClock)
});
)
::

@ -1,112 +0,0 @@
TITLE:: FluidBufScale
SUMMARY:: A Scaling Processor for Buffers
CATEGORIES:: Libraries>FluidCorpusManipulation
RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading
This class implements a simple Buffer preprocessor, by scaling its values. It draws a simple translation from inputLow to outputLow, and from inputHigh to outputHigh. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The process will return a buffer with the same size and shape than the requested range.
STRONG::Threading::
By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::.
CLASSMETHODS::
METHOD:: process, processBlocking
This is the method that calls for the scaling to be calculated on a given source buffer.
ARGUMENT:: server
The server on which the buffer to be processed is allocated.
ARGUMENT:: source
The index of the buffer to use as the source material to be processed.
ARGUMENT:: startFrame
The starting point (in samples) from which to copy in the source buffer.
ARGUMENT:: numFrames
The duration (in samples) to copy from the source buffer. The default (-1) copies the full lenght of the buffer.
ARGUMENT:: startChan
The first channel from which to copy in the source buffer.
ARGUMENT:: numChans
The number of channels from which to copy in the source buffer. This parameter will wrap around the number of channels in the source buffer. The default (-1) copies all of the buffer's channel.
ARGUMENT:: destination
The index of the buffer to use as the destination for the processed material.
ARGUMENT:: inputLow
The low reference point of the input. it will be scaled to yield outputLow at the output
ARGUMENT:: inputHigh
The high reference point of the input. it will be scaled to yield outputHigh at the output
ARGUMENT:: outputLow
The output value when the input is inputLow
ARGUMENT:: outputHigh
The output value when the input is inputHigh
ARGUMENT:: clipping
Optional clipping of the input (and therefore of the output). 0 is none. 1 caps the lowest input at inputLow. 2 caps the highest input at inputHigh, 3 caps both input low and high value within the described range.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The metric will be passed indices as an argument.
returns:: an instance of the processor
EXAMPLES::
code::
(
Routine{
// make a buffer of known qualities
b = Buffer.sendCollection(s,1.0.series(1.1,2.0));
// and a destination buffer
c = Buffer(s);
// play with the scaling
FluidBufScale.process(s, b, destination: c, inputLow: 0, inputHigh: 1, outputLow: 20, outputHigh:10).wait;
// retrieve the buffer and enjoy the results.
c.getn(0,10,{|x|x.round(0.000001).postln;})
}.play
)
// also works in multichannel - explore the following buffer
//process
(
Routine{
b = Buffer.sendCollection(s,-10.0.series(-9,10.0).scramble,2);
c = Buffer(s);
s.sync;
defer{b.plot(bounds:Rect(400,400,400,400)).plotMode_(\points).bounds};
FluidBufScale.process(s, b, destination: c, inputLow: -20, inputHigh: 20, outputLow: 0, outputHigh:1).wait;
//enjoy - same shape, different range
defer{c.plot(bounds:Rect(800,400,400,400)).plotMode_(\points)};
}.play;
)
//also works with a subset of the input, resizing the output
(
Routine{
b = Buffer.sendCollection(s,0.0.series(0.1,3.0).reshape(3,10).flop.flat,3);
c = Buffer(s);
s.sync;
defer{b.plot(separately: true,bounds:Rect(400,400,400,400)).plotMode_(\points)};
//process
FluidBufScale.process(s, b, startFrame: 3,numFrames: 4,startChan: 1,numChans: 1, destination: c, inputLow: 0, inputHigh: 3, outputLow: 0, outputHigh:1).wait;
//enjoy
c.query;
c.getn(0,4,{|x|x.postln;});
defer{c.plot(separately: true,bounds:Rect(800,400,400,400)).plotMode_(\points)};
}.play
)
::

@ -1,62 +0,0 @@
TITLE:: FluidBufSelect
summary:: Cherry pick values from a buffer
categories:: Libraries>FluidCorpusManipulation
related:: Classes/FluidBufSelectEvery
DESCRIPTION::
Pick sets of values from a buffer, described in terms of a list of frame indices and channel numbers.
CLASSMETHODS::
private::new1
METHOD:: process, processBlocking
Run the process on the given sever, and perfrom code::action:: when done
ARGUMENT:: server
The link::Classes/Server:: on which to run
ARGUMENT:: source
The link::Classes/Buffer:: to select values from
ARGUMENT:: destination
The link::Classes/Buffer:: to write the selected data to
ARGUMENT:: indices
A 0-based list of frame indices to recover. Default is [-1], meaning all frames
ARGUMENT:: channels
A 0-based list of channel numbers to recover. Default is [-1], meaning all frames
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
Runs when processing is complete
EXAMPLES::
code::
//send a known collection where the value of each frame in each channel is encoded
//chan
b = Buffer.sendCollection(s,30.collect{|x| x.mod(6) + (x.div(6) * 0.1)},6)
//check the ranges (thus showing a plotter error...)
b.plot(separately: true).plotMode_(\points)
//you can also check the collection itself if in doubt
b.getToFloatArray(action: {|x|x.round(0.1).postln;});
//let's make a destination buffer
c = Buffer(s);
//using default values, we copy everything:
FluidBufSelect.process(s,b,c,action: {c.query});
c.getToFloatArray(action: {|x|x.round(0.1).postln;});
//more powerful copying, resizing the destination accordingly
FluidBufSelect.process(s,b,c, indices: [1,3], channels: [2,4], action: {c.query});
c.getToFloatArray(action: {|x|x.round(0.1).postln;});
//observe the order can be anything, and -1 (default) passes everything in that dimension
FluidBufSelect.process(s,b,c, indices: [ -1 ] , channels: [3, 1, 4], action: {c.query});
c.getToFloatArray(action: {|x|x.round(0.1).postln;});
::

@ -1,74 +0,0 @@
TITLE:: FluidBufSelectEvery
summary:: Extract every N samples / channels from a buffer
categories:: Libraries>FluidCorpusManipulation
related:: Classes/FluidBufSelect
DESCRIPTION::
Pick every N frames and / or channels from a buffer, described in terms of independent hop sizes for frames and channels
CLASSMETHODS::
private::new1
METHOD:: process, processBlocking
Run the process on the given sever, and perfrom code::action:: when done
ARGUMENT:: server
The link::Classes/Server:: on which to run
ARGUMENT:: source
The link::Classes/Buffer:: to select values from
ARGUMENT:: startFrame
The starting point (in samples) from which to copy in the source buffer.
ARGUMENT:: numFrames
The duration (in samples) to copy from the source buffer. The default (-1) copies the full lenght of the buffer.
ARGUMENT:: startChan
The first channel from which to copy in the source buffer.
ARGUMENT:: numChans
The number of channels from which to copy in the source buffer. This parameter will wrap around the number of channels in the source buffer. The default (-1) copies all of the buffer's channel.
ARGUMENT:: destination
The link::Classes/Buffer:: to write the selected data to
ARGUMENT:: frameHop
Take every `frameHop` frames. Default = 1 = all frames (where 2 would be every other frame, etc.)
ARGUMENT:: chanHop
Take every `chanHop` channels. Default = 1 = all channels (where 2 would be every other channel, etc.)
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
Runs when processing is complete
EXAMPLES::
Didactic
code::
//send a known collection where the value of each frame in each channel is encoded
//chan
b = Buffer.sendCollection(s,30.collect{|x| x.mod(6) + (x.div(6) * 0.1)},6)
//check the ranges (thus showing a plotter error...)
b.plot(separately: true).plotMode_(\points)
//you can also check the collection itself if in doubt
b.getToFloatArray(action: {|x|x.round(0.1).postln;});
//let's make a destination buffer
c = Buffer(s);
//using default values, we copy everything:
FluidBufSelectEvery.process(s,b, destination: c, action: {c.query});
c.getToFloatArray(action: {|x|x.round(0.1).postln;});
//more powerful copying, resizing the destination accordingly
FluidBufSelectEvery.process(s,b, destination: c, frameHop: 2, chanHop: 3, action: {c.query});
c.getToFloatArray(action: {|x|x.round(0.1).postln;});
//source buffer boundaries still apply before the hopping selection
FluidBufSelectEvery.process(s,b, startFrame: 1, numFrames: 3, startChan: 2, numChans: 3, destination: c, frameHop: 1, chanHop: 2, action: {c.query});
c.getToFloatArray(action: {|x|x.round(0.1).postln;});
::

@ -1,148 +0,0 @@
TITLE:: FluidBufSines
SUMMARY:: Buffer-Based Sinusoidal Modelling and Resynthesis
CATEGORIES:: Libraries>FluidCorpusManipulation, UGens>Buffer
RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading, Guides/FluidBufMultiThreading
DESCRIPTION::
This class triggers a Sinusoidal Modelling process on buffers on the non-real-time thread of the server. It implements a mix of algorithms taken from classic papers. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The algorithm will take a buffer in, and will divide it in two parts: LIST::
## a reconstruction of what it detects as sinusoidal;
## a residual derived from the previous buffer to allow null-summing::
The whole process is based on the assumption that signal is made of pitched steady components that have a long-enough duration and are periodic enough to be perceived as such, that can be tracked, resynthesised and removed from the original, leaving behind what is considered as non-pitched, noisy, and/or transient. It first tracks the peaks, then checks if they are the continuation of a peak in previous spectral frames, by assigning them a track.
STRONG::Threading::
By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::.
CLASSMETHODS::
METHOD:: process, processBlocking
This is the method that calls for the sinusoidal estimation to be calculated on a given source buffer and to be resynthesised.
ARGUMENT:: server
The server on which the buffers to be processed are allocated.
ARGUMENT:: source
The index of the buffer to use as the source material to be decomposed through the sinusoidal modelling process. The different channels of multichannel buffers will be processing sequentially.
ARGUMENT:: startFrame
Where in the srcBuf should the process start, in sample.
ARGUMENT:: numFrames
How many frames should be processed.
ARGUMENT:: startChan
For multichannel srcBuf, which channel should be processed first.
ARGUMENT:: numChans
For multichannel srcBuf, how many channel should be processed.
ARGUMENT:: sines
The index of the buffer where the extracted sinusoidal component will be reconstructed.
ARGUMENT:: residual
The index of the buffer where the residual of the sinusoidal component will be reconstructed.
ARGUMENT:: bandwidth
The number of bins used to resynthesises a peak. It has an effect on CPU cost: the widest is more accurate but more computationally expensive. It is capped at (fftSize / 2) + 1.
ARGUMENT:: detectionThreshold
The threshold in dB above which a magnitude peak is considered to be a sinusoidal component.
ARGUMENT:: birthLowThreshold
The threshold in dB above which to consider a peak to start a sinusoidal component tracking, for the low end of the spectrum. It is interpolated across the spectrum until birthHighThreshold at half-Nyquist.
ARGUMENT:: birthHighThreshold
The threshold in dB above which to consider a peak to start a sinusoidal component tracking, for the high end of the spectrum. It is interpolated across the spectrum until birthLowThreshold at DC.
ARGUMENT:: minTrackLen
The minimum duration, in spectral frames, for a sinusoidal track to be accepted as a partial. It allows to remove bubbly pitchy artefacts, but is more CPU intensive and might reject quick pitch material.
ARGUMENT:: trackingMethod
The algorithm used to track the sinusoidal continuity between spectral frames. 0 is the default, "Greedy", and 1 is a more expensive "Hungarian" one. footnote::Neri, J., and Depalle, P., "Fast Partial Tracking of Audio with Real-Time Capability through Linear Programming". Proceedings of DAFx-2018.::
ARGUMENT:: trackMagRange
The amplitude difference allowed for a track to diverge between frames, in dB.
ARGUMENT:: trackFreqRange
The frequency difference allowed for a track to diverge between frames, in Hertz.
ARGUMENT:: trackProb
The probability of the tracking algorithm to find a track.
ARGUMENT:: windowSize
The window size. As spectral differencing relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty
ARGUMENT:: hopSize
The window hop size. As sinusoidal estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2).
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the highest of windowSize and (bandwidth - 1) * 2.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [sines, residual] as an argument.
returns:: an instance of the processor
EXAMPLES::
code::
// create some buffers
(
b = Buffer.read(s,FluidFilesPath("Tremblay-AaS-SynthTwoVoices-M.wav"));
c = Buffer.new(s);
d = Buffer.new(s);
)
// run the process with basic parameters
(
Routine{
t = Main.elapsedTime;
FluidBufSines.process(s, b, sines: c, residual:d).wait;
(Main.elapsedTime - t).postln;
}.play
)
// listen to each component
c.play;
d.play;
//nullsumming tests
{(PlayBuf.ar(1, c)) + (PlayBuf.ar(1,d)) - (PlayBuf.ar(1,b,doneAction:2))}.play
::
STRONG::A stereo buffer example.::
CODE::
// load two very different files
(
b = Buffer.read(s,FluidFilesPath("Tremblay-SA-UprightPianoPedalWide.wav"));
c = Buffer.read(s,FluidFilesPath("Tremblay-AaS-AcousticStrums-M.wav"));
)
// composite one on left one on right as test signals
FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000,destStartChan:1, destination:b)
b.play
// create 2 new buffers as destinations
d = Buffer.new(s); e = Buffer.new(s);
//run the process on them
(
Routine{
t = Main.elapsedTime;
FluidBufSines.process(s, b, sines: d, residual:e, windowSize: 2048, hopSize: 256, fftSize: 16384).wait;
(Main.elapsedTime - t).postln;
}.play
)
//listen: stereo preserved!
d.play
e.play
::

@ -1,142 +0,0 @@
TITLE:: FluidBufSpectralShape
SUMMARY:: Seven Spectral Shape Descriptors on a Buffer
CATEGORIES:: Libraries>FluidCorpusManipulation
RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading, Classes/FluidSpectralShape, Classes/SpecCentroid, Classes/SpecFlatness, Classes/SpecCentroid, Classes/SpecPcile
DESCRIPTION::
This class implements seven of the most popular spectral shape descriptors, computed on a linear scale for both amplitude and frequency. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The descriptors are:
LIST::
##the four first statistical moments (https://en.wikipedia.org/wiki/Moment_(mathematics) ), more commonly known as:
LIST::
## the spectral centroid (1) in Hz. This is the point that splits the spectrum in 2 halves of equal energy. It is the weighted average of the magnitude spectrum.
## the spectral spread (2) in Hz. This is the standard deviation of the spectrum envelop, or the average of the distance to the centroid.
## the normalised skewness (3) as ratio. This indicates how tilted is the spectral curve in relation to the middle of the spectral frame, i.e. half of the Nyquist frequency. If it is below that frequency, i.e. the central bin of the magnitude spectrum, it is positive.
## the normalised kurtosis (4) as ratio. This indicates how focused is the spectral curve. If it is peaky, it is high.
::
## the rolloff (5) in Hz. This indicates the frequency under which 95% of the energy is included.
## the flatness (6) in dB. This is the ratio of geometric mean of the magnitude, over the arithmetic mean of the magnitudes. It yields a very approximate measure on how noisy a signal is.
## the crest (7) in dB. This is the ratio of the loudest magnitude over the RMS of the whole frame. A high number is an indication of a loud peak poking out from the overal spectral curve.::
The drawings in Peeters 2003 (http://recherche.ircam.fr/anasyn/peeters/ARTICLES/Peeters_2003_cuidadoaudiofeatures.pdf) are useful, as are the commented examples below. For the mathematically-inclined reader, the tutorials and code offered here (https://www.audiocontentanalysis.org/) are interesting to further the understanding. For examples of the impact of computing the moments in power magnitudes, and/or in exponential frequency scale, please refer to the LINK::Classes/FluidSpectralShape:: helpfile.
The process will return a multichannel buffer with the seven channels per input channel, each containing the 7 shapes. Each sample represents a value, which is every hopSize.
STRONG::Threading::
By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::.
CLASSMETHODS::
METHOD:: process, processBlocking
This is the method that calls for the spectral shape descriptors to be calculated on a given source buffer.
ARGUMENT:: server
The server on which the buffers to be processed are allocated.
ARGUMENT:: source
The index of the buffer to use as the source material to be described through the various descriptors. The different channels of multichannel buffers will be processing sequentially.
ARGUMENT:: startFrame
Where in the srcBuf should the process start, in sample.
ARGUMENT:: numFrames
How many frames should be processed.
ARGUMENT:: startChan
For multichannel srcBuf, which channel should be processed first.
ARGUMENT:: numChans
For multichannel srcBuf, how many channel should be processed.
ARGUMENT:: features
The destination buffer for the 7 spectral features describing the spectral shape.
ARGUMENT:: minFreq
The minimum frequency that the algorithm will consider for computing the spectral shape. Frequencies below will be ignored. The default of 0 goes down to DC when possible.
ARGUMENT:: maxFreq
The maximum frequency that the algorithm will consider for computing the spectral shape. Frequencies above will be ignored. The default of -1 goes up to Nyquist.
ARGUMENT:: rolloffPercent
This sets the percentage of the frame's energy that will be reported as the rolloff frequency. The default is 95%.
ARGUMENT:: unit
The frequency unit for the spectral shapes to be computed upon, and outputted at. The default (0) is in Hertz and computes the moments on a linear spectrum. The alternative is in MIDI note numbers(1), which compute the moments on an exponential spectrum.
ARGUMENT:: power
This flag sets the scaling of the magnitudes in the moment calculation. It uses either its amplitude (0, by default) or its power (1).
ARGUMENT:: windowSize
The window size. As spectral shape estimation relies on spectral frames, we need to decide what precision we give it spectrally and temporally, in line with Gabor Uncertainty principles. http://www.subsurfwiki.org/wiki/Gabor_uncertainty
ARGUMENT:: hopSize
The window hop size. As spectral shape estimation relies on spectral frames, we need to move the window forward. It can be any size but low overlap will create audible artefacts. The -1 default value will default to half of windowSize (overlap of 2).
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: padding
Controls the zero-padding added to either end of the source buffer or segment. Possible values are 0 (no padding), 1 (default, half the window size), or 2 (window size - hop size). Padding ensures that all input samples are completely analysed: with no padding, the first analysis window starts at time 0, and the samples at either end will be tapered by the STFT windowing function. Mode 1 has the effect of centering the first sample in the analysis window and ensuring that the very start and end of the segment are accounted for in the analysis. Mode 2 can be useful when the overlap factor (window size / hop size) is greater than 2, to ensure that the input samples at either end of the segment are covered by the same number of analysis frames as the rest of the analysed material.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [features] as an argument.
returns:: an instance of the processor
EXAMPLES::
code::
// create some buffers
(
b = Buffer.read(s,FluidFilesPath("Nicol-LoopE-M.wav"));
c = Buffer.new(s);
)
// run the process with basic parameters
(
Routine{
t = Main.elapsedTime;
FluidBufSpectralShape.process(s, b, features: c).wait;
(Main.elapsedTime - t).postln;
}.play
)
// listen to the source and look at the buffer
b.play;
c.plot(separately:true)
::
STRONG::A stereo buffer example.::
CODE::
// load two very different files
(
b = Buffer.read(s,FluidFilesPath("Tremblay-SA-UprightPianoPedalWide.wav"));
c = Buffer.read(s,FluidFilesPath("Tremblay-AaS-AcousticStrums-M.wav"));
)
// composite one on left one on right as test signals
FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000,destStartChan:1, destination:b)
b.play
// create a buffer as destinations
c = Buffer.new(s);
//run the process on them
(
Routine{
t = Main.elapsedTime;
FluidBufSpectralShape.process(s, b, features: c).wait;
(Main.elapsedTime - t).postln;
}.play
)
// look at the buffer: 7shapes for left, then 7 shapes for right
c.plot(separately:true)
::

@ -1,288 +0,0 @@
TITLE:: FluidBufStats
SUMMARY:: Computing Statistics on Buffers as Series.
CATEGORIES:: Libraries>FluidCorpusManipulation, UGens>Buffer
RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading
DESCRIPTION::
This class implements non-real-time statistical analysis on buffer channels. Typically, a buffer would hold various time series (i.e. descriptors over time), and link::Classes/FluidBufStats:: allows this series to be described statistically. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The process returns a buffer where each channel of the STRONG::source:: buffer has been reduced to 7 statistics: mean, standard deviation, skewness, kurtosis, followed by 3 percentiles, by default the minimum value, the median, and the maximum value. Moreover, it is possible to request the same 7 stats to be applied to derivative of the input. These are useful to describe statistically the rate of change of the time series. The STRONG::stats:: buffer will grow accordingly, yielding the seven same statistical description of the n requested derivatives. Therefore, the STRONG::stats:: buffer will have as many channel as the input buffer, and as many frames as 7 times the requested STRONG::numDerivs::.
STRONG::Threading::
By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::.
CLASSMETHODS::
METHOD:: process, processBlocking
This is the method that calls for the slicing to be calculated on a given source buffer.
ARGUMENT:: server
The server on which the buffers to be processed are allocated.
ARGUMENT:: source
The index of the buffer to use as the source material to be processed. The different channels of multichannel buffers will be considered independently as time series.
ARGUMENT:: startFrame
The starting point (in samples) from which to copy in the source buffer.
ARGUMENT:: numFrames
The duration (in samples) to copy from the source buffer. The default (-1) copies the full lenght of the buffer.
ARGUMENT:: startChan
The first channel from which to copy in the source buffer.
ARGUMENT:: numChans
The number of channels from which to copy in the source buffer. This parameter will wrap around the number of channels in the source buffer. The default (-1) copies all of the buffer's channel.
ARGUMENT:: stats
The index of the buffer to write the statistics to. Each channel is the fruit of the statistical computations on the same channel number of the source buffer.
ARGUMENT:: numDerivs
The number of derivatives of the original time series for the statistic to be computed on. By default, none are computed. This will influence the number of frames the stats buffer will have.
ARGUMENT:: low
The rank requested for the first percentile value. By default, it is percentile 0.0, which is the minimum of the given channel of the source buffer.
ARGUMENT:: middle
The rank requested for the second percentile value. By default, it is percentile 50.0, which is the median of the given channel of the source buffer.
ARGUMENT:: high
The rank requested for the third percentile value. By default, it is percentile 100.0, which is the maximum of the given channel of the source buffer.
ARGUMENT:: outliersCutoff
A ratio of the inter quantile range (IQR) that defines a range outside of which data will be rejected. It is run on each channel independently and a single channel being flagged as outlier removes the whole frame (on all channels). The default (-1) bypasses this function, keeping all frames in the statistical measurements. For more information on this statistical process, please refer to the concept of IQR and how the whiskers of a box plot are computed here (https://en.wikipedia.org/wiki/Box_plot)
ARGUMENT:: weights
A buffer to provide relative weighting of the source material. Not providing one will not apply weighting and consider all frames equally. The provided buffer has to satisfy all of the following conditions: LIST::
## a single-channel, that will be applied to all channels of source
## exactly the same amount of frames as source
## weights must be positive (anything lower than 0 will be rejected)
::
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The function will be passed stats as an argument.
returns:: an instance of the processor
EXAMPLES::
STRONG::A didactic example::
CODE::
// make a buffer of known length
b = Buffer.alloc(s,101);
// add known values - here, a ramp up
b.setn(0, Array.fill(101,{|i|i / 100}));
// create a new buffer as destinations
c = Buffer.new(s);
//run the process on them
(
Routine{
t = Main.elapsedTime;
FluidBufStats.process(s, b, stats:c, numDerivs:1).wait;
(Main.elapsedTime - t).postln;
}.play
)
// list the statistics. The first seven are for the source buffer values themselves, the last seven for the first derivative of the source buffer.
c.getn(0,c.numFrames,{|item|item.postln;})
// replace the source values by a ramp down
b.setn(0, Array.fill(101,{|i| 1 - (i / 100)}));
// run the process and read the values
FluidBufStats.process(s, b, stats:c, numDerivs:1, action:{c.getn(0,c.numFrames,{|item|item.postln;})});
// replace the source values by halfsine
b.setn(0, Array.fill(101,{|i| (i * pi/ 100).sin}));
b.plot
// run the process and read the values
FluidBufStats.process(s, b, stats:c, numDerivs:1, action:{c.getn(0,c.numFrames,{|item|item.postln;})});
// replace the source values by partial halfsine
b.setn(0, Array.fill(101,{|i| (i * pi/ 50).sin.max(0)}));
b.plot
// run the process and read the values
FluidBufStats.process(s, b, stats:c, numDerivs:1, action:{c.getn(0,c.numFrames,{|item|item.postln;})});
// replace the source values by positive white noise
b.setn(0, Array.fill(101,{1.0.rand}));
b.plot
// run the process and read the values
FluidBufStats.process(s, b, stats:c, numDerivs:1, action:{c.getn(0,c.numFrames,{|item|item.postln;})});
::
STRONG::A musical example::
CODE::
// create some buffers
(
// a simple random sliding bell synth
b = {
var trig = Impulse.ar(1.5);
SinOsc.ar(
Lag.ar(TRand.ar(trig: trig),
TRand.ar(0.5, trig: trig)).exprange(333,666),
mul: Decay.ar(
trig * TRand.ar(0.1,10,trig),
TRand.ar(0.5,1.1,trig)
)
).atan * 0.1;
}.asBuffer(20);
c = Buffer.new(s);
d = Buffer.new(s);
i = Buffer.new(s);
)
//play the source
b.play;
//split in various chunks, collecting the indices in an array
FluidBufOnsetSlice.process(s,b, threshold: 0.01, indices: c, action:{c.loadToFloatArray(action: {|array| e = array.add(b.numFrames);e.size.postln;e.postln;})});
//describe the whole input too, here using pitch, and collecting the values in an array, dismissing the (interleaved) confidence.
FluidBufPitch.process(s,b,features:d, windowSize: 4096, hopSize: 512, padding:2, action:{d.loadToFloatArray(action: {|array| f = array.unlace(2)[0]; f.postln;})});
// iterate through each slice, taking the median of the first derivative of the pitch of each
(
g= Array.new;
Routine({
var nb = e.size;
e.doAdjacentPairs({
arg start,end;
FluidBufStats.processBlocking(s,d,(start/512).asInteger,((end-start)/512).asInteger + 3,0,1,i,1, action: {
i.loadToFloatArray( action: {
arg array;
g = g.add(array[12]);
"% % %\n".postf((start/512).asInteger,((end-start)/512).asInteger + 3, array[12]);//adding some of the overlap but not more to not capture too much of the next attack
nb = nb - 1;
if (nb == 1, {"Done".postln;});//check if we've done all the pairs
})
}).wait;
});
}).play;
)
//play in loop the slice in order of pitch direction (the median of the slice's pitch variation) - mouse on the left should be descending, in the middle should be more stable, and it should be ascending on the right.
(
Buffer.sendCollection(s,g.order,action: {|x| {
var which = BufRd.kr(1, x, MouseX.kr(0, BufFrames.kr(x) - 1), 0, 1);
BufRd.ar(1, b,
Phasor.ar(0,1,
BufRd.kr(1,c,which,0,1),
BufRd.kr(1,c,which + 1,0,1),
BufRd.kr(1,c,which,0,1)));
}.play;
};)
)
::
STRONG::Stereo Input Behaviour::
CODE::
// make a buffer of known lenght
b = Buffer.alloc(s,101,2);
// add known values - here, a ramp up on the left and negative random values on the right
b.setn(0, Array.fill(101,{|i|[i / 100,-1.0.rand]}).flat);
// plot to confirm
b.plot.plotMode_(\points)
// create a new buffer as destinations
c = Buffer.new(s);
// run the stats and send back the values
FluidBufStats.process(s, b, stats:c, numDerivs:1, action:{c.getn(0,c.numFrames * c.numChannels,{|item|d = item; d.postln})});
//looking at the result is not easy to grasp, since it is interleaved: first number is mean of L, second is mean of R, third is stddev of L, fourth is stddev or R
//this will make it tidier - the first value of each line is Left, the second is Right
d.reshape(14,2).do({|x,i|["mean\t\t","stddev\t\t","skew\t\t", "kurtosis\t\t", "min\t\t\t", "median\t\t", "max\t\t\t","d-mean\t","d-stddev\t","d-skew\t\t", "d-kurtosis", "d-min\t\t", "d-median\t", "d-max\t\t"].at(i).post;x.round(0.01).postln});"".postln;
::
STRONG::Outliers and Weights::
CODE::
// example 1a
// make a buffer of known qualities
b = Buffer.loadCollection(s,[1, 8, 9, 10, 11, 12, 99]);
// plot to confirm
b.plot.plotMode = \points;
// create a new buffer as destinations
c = Buffer.new(s);
// run the stats and send back the values
FluidBufStats.process(s, b, stats:c, numDerivs:1, action:{c.getn(0,c.numFrames,{|item|item.postln})});
// run the same array with outliers rejected if outside of 1.5 times the IQR - observe the new minimum and maximum to see
FluidBufStats.process(s, b, stats:c, numDerivs:1, outliersCutoff: 1.5, action:{c.getn(0,c.numFrames,{|item| item.postln})});
// example 1b (run the stats above, and change the value of some elements in the array too)
b = Buffer.loadCollection(s,[1, 8, 9, 10, 11, 12, 16, 99].scramble);
// example 1c (multichannel in behaviour is greedy)
// This mean that an outlier in any channel will dismiss the whole frame.
// For instance here the outlier is 99 (frame 8) in channel 0, and 1001 in channel 1 (frame 0)
// The final stats therefore has minima of [2,10002] and maxima of [8,10008]
e = [(1..8)++99, [1001] ++ 10002.series(10003,10009)].flop.scramble.flat
b = Buffer.loadCollection(s,e,2);
FluidBufStats.process(s, b, stats:c, numDerivs:1, outliersCutoff: 1.5, action:{c.getn(0,c.numFrames * c.numChannels,{|item| f =item.postln})});
//More readable format
f.reshape(14,2).do({|x,i|["mean\t\t","stddev\t\t","skew\t\t\t", "kurtosis\t", "min\t\t\t", "median\t\t", "max\t\t\t","d-mean\t","d-stddev\t","d-skew\t\t", "d-kurtosis", "d-min\t\t", "d-median\t", "d-max\t\t"].at(i).post;x.round(0.01).postln});"".postln;
//////////////
// example 2a
// make an array of 9 values, with known weigths. Scramble them pairwise for good measure (that should not change any stats)
e = [(1..9), 1.0.series(0.9,0.2)].flop.scramble.flop;
b = Buffer.loadCollection(s,e[0]);
c = Buffer.loadCollection(s,e[1]);
d = Buffer.new(s);
// run the stats and send back the values
FluidBufStats.process(s, b, stats:d, numDerivs:1, action:{d.getn(0,d.numFrames,{|item|item.postln})});
// run the same array with the weights
FluidBufStats.process(s, b, stats:d, numDerivs:1, weights: c, action:{d.getn(0,d.numFrames * d.numChannels,{|item|item.postln})});
// example 2b
e = [(1..9), 0.series(-10,-80)].flop.scramble.flop;
b = Buffer.loadCollection(s,e[0]);
c = Buffer.loadCollection(s,e[1]);
FluidBufStats.process(s, b, stats:d, numDerivs:1, weights: c, action:{d.getn(0,d.numFrames * d.numChannels,{|item|item.postln})});
// this has only negative weights, so it bails out, outputing all 0s. It also publishes a warning if the server options on verbosity are on.
// but if we scale them up
g = Buffer(s)
FluidBufScale.process(s,c,destination: g,inputLow: -100,inputHigh: 0)
// look at the new values - because 0->1 and -100->0 we get the same weights as example 2a
g.getn(0,9,{|x|x.postln})
// run the stats - same results as example 2a
FluidBufStats.process(s, b, stats:d, numDerivs:1, weights: g, action:{d.getn(0,d.numFrames * d.numChannels,{|item|item.postln})});
//example 2c (stereo input but mono weigths - works like a charm)
e = [(1..9), (101..109), 1.0.series(0.9,0.2)].flop.scramble.flop;
b = Buffer.loadCollection(s,e[0..1].flop.flat,2);
b.plot(separately: true).plotMode = \points;
c = Buffer.loadCollection(s,e[2]);
FluidBufStats.process(s, b, stats:d, numDerivs:1, weights: c, action:{d.getn(0,d.numFrames * d.numChannels,{|item|f = item.postln})});
//More readable format
f.reshape(14,2).do({|x,i|["mean\t\t","stddev\t\t","skew\t\t\t", "kurtosis\t", "min\t\t\t", "median\t\t", "max\t\t\t","d-mean\t","d-stddev\t","d-skew\t\t", "d-kurtosis", "d-min\t\t", "d-median\t", "d-max\t\t"].at(i).post;x.round(0.01).postln});"".postln;
//see the example folder for 2 musical comparisons: 1) weighted MFCCs providing different nearest neighbours, and 2) pitch manipulations
::

@ -1,71 +0,0 @@
TITLE:: FluidBufThreadDemo
SUMMARY:: A Tutorial Object to Experiment with Multithreading in FluidBuf* Objects
CATEGORIES:: Libraries>FluidCorpusManipulation
RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading
DESCRIPTION::
This class implements a simple tutorial object to illustrate and experiment with the behaviour of the FluidBuf* objects. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
It simply starts a process that will, upon completion of its task, write a single value to the destination buffer. This is the general behaviour of much of the CPU consuming FluidBuf* objects. In this case, as a toy example, the task is simply just wait and do nothing, to simulate a task that would actually take that long in the background.
The process will, after waiting for STRONG::time:: millisecond, return its delay lenght as a Float writen at index [0] of the specified destination buffer.
CLASSMETHODS::
METHOD:: process, processBlocking
This is the method that calls for the job to be done. In this case, simply waiting STRONG::time:: millisecond before writing a value in the destination buffer.
ARGUMENT:: server
The server on which the destination buffer is declared.
ARGUMENT:: result
The destination buffer, where the value should be written at the end of the process.
ARGUMENT:: time
The duration in milliseconds of the delay that the background thread will wait for before yielding the value to the destination buffer.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A function that will be executed upon completion. It is passed the destination buffer as argument.
returns::The instance of FluidNRTProcess which can be used to cancel the job.
METHOD:: kr
This is the UGen that is holding the link with the background thread. It is called by the 'process' method and can be used directly to monitor the progress of the job. For examples of such usage, please refer to the tutorial on link::Guides/FluidBufMultiThreading::.
ARGUMENT:: result
The destination buffer, where the value should be written at the end of the process.
ARGUMENT:: time
The duration of the delay that the background thread will wait for before yielding the value to the destination buffer.
ARGUMENT:: trig
The trigger to start the job.
ARGUMENT:: blocking
The thread on which the job is processed.
returns::It report the approximate job progress, from 0 to 1.
EXAMPLES::
For a thorough explanation, please refer to the tutorial on link::Guides/FluidBufMultiThreading::.
CODE::
// define a destination buffer
b=Buffer.alloc(s,1);
// a simple call, where we query the destination buffer upon completion with the action message.
FluidBufThreadDemo.process(s, b, 1000, action:{|x|x.get(0,{|y|y.postln});});
// as the 'process' returns its instance, we can cancel the process easily
c = FluidBufThreadDemo.process(s, b, 100000, action: {|x|x.get(0,{|y|y.postln});});
c.cancel;
// if a simple call to the UGen is used, the progress can be monitored. The usual cmd-period will cancel the job by freeing the synth.
{c = FluidBufThreadDemo.kr(b,10000).poll; FreeSelfWhenDone.kr(c)}.scope;
::

@ -1,81 +0,0 @@
TITLE:: FluidBufThresh
SUMMARY:: A Gate Processor for Buffers
CATEGORIES:: Libraries>FluidCorpusManipulation
RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading
DESCRIPTION::
This class implements a simple Buffer preprocessor, by replacing values under a threshold by 0s. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The process will return a buffer with the same size and shape than the requested range.
STRONG::Threading::
By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::.
CLASSMETHODS::
METHOD:: process, processBlocking
This is the method that calls for the thresholding to be calculated on a given source buffer.
ARGUMENT:: server
The server on which the buffer to be processed is allocated.
ARGUMENT:: source
The index of the buffer to use as the source material to be processed.
ARGUMENT:: startFrame
The starting point (in samples) from which to copy in the source buffer.
ARGUMENT:: numFrames
The duration (in samples) to copy from the source buffer. The default (-1) copies the full lenght of the buffer.
ARGUMENT:: startChan
The first channel from which to copy in the source buffer.
ARGUMENT:: numChans
The number of channels from which to copy in the source buffer. This parameter will wrap around the number of channels in the source buffer. The default (-1) copies all of the buffer's channel.
ARGUMENT:: destination
The index of the buffer to use as the destination for the processed material.
ARGUMENT:: threshold
The threshold under which values will be zeroed
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The metric will be passed indices as an argument.
returns:: an instance of the processor
EXAMPLES::
code::
// make a buffer of know qualities
b = Buffer.sendCollection(s,0.0.series(0.1,1.0))
// and a destination buffer
c = Buffer(s)
// play with the threshold
FluidBufThresh.process(s, b, destination: c, threshold: 0.5)
// retrieve the buffer and enjoy the results.
c.getn(0,11,{|x|x.round(0.000001).postln;})
// also works in multichannel - explore the following buffer
b = Buffer.sendCollection(s,0.0.series(0.1,2.0).scramble,2)
b.plot.plotMode_(\points)
//process and keep just the top values
FluidBufThresh.process(s, b, destination: c, threshold: 1.6)
//enjoy
c.plot.plotMode_(\points)
//also works with a subset of the input, resizing the output
b = Buffer.sendCollection(s,0.0.series(0.1,3.0).reshape(3,10).flop.flat,3)
b.plot(separately: true).plotMode_(\points)
//process and keep just the top values
FluidBufThresh.process(s, b,startFrame: 3,numFrames: 4,startChan: 1,numChans: 1,destination: c, threshold: 1.6)
//enjoy
c.plot(separately: true).plotMode_(\points)
c.query
c.getn(0,4,{|x|x.round(0.000001).postln;})
::

@ -1,150 +0,0 @@
TITLE:: FluidBufTransientSlice
SUMMARY:: Buffer-Based Transient-Based Slicer
CATEGORIES:: Libraries>FluidCorpusManipulation, UGens>Buffer
RELATED:: Guides/FluidCorpusManipulation, Guides/FluidBufMultiThreading, Classes/FluidBufTransients
DESCRIPTION::
This class implements a non-real-time transient-based slice extractor relying on the same algorithm than Classes/FluidBufTransients using clicks/transients/derivation/anomaly in the signal to estimate the slicing points. It is part of the LINK:: Guides/FluidCorpusManipulation##Fluid Corpus Manipulation Toolkit::. For more explanations, learning material, and discussions on its musicianly uses, visit http://www.flucoma.org/
The process will return a buffer which contains indices (in sample) of estimated starting points of the different slices.
STRONG::Threading::
By default, this UGen spawns a new thread to avoid blocking the server command queue, so it is free to go about with its business. For a more detailed discussion of the available threading and monitoring options, including the two undocumented Class Methods below (.processBlocking and .kr) please read the guide LINK::Guides/FluidBufMultiThreading::.
CLASSMETHODS::
METHOD:: process, processBlocking
This is the method that calls for the slicing to be calculated on a given source buffer.
ARGUMENT:: server
The server on which the buffers to be processed are allocated.
ARGUMENT:: source
The index of the buffer to use as the source material to be sliced through transient identification. The different channels of multichannel buffers will be summed.
ARGUMENT:: startFrame
Where in the srcBuf should the slicing process start, in sample.
ARGUMENT:: numFrames
How many frames should be processed.
ARGUMENT:: startChan
For multichannel srcBuf, which channel should be processed.
ARGUMENT:: numChans
For multichannel srcBuf, how many channel should be summed.
ARGUMENT:: indices
The index of the buffer where the indices (in sample) of the estimated starting points of slices will be written. The first and last points are always the boundary points of the analysis.
ARGUMENT:: order
The order in samples of the impulse response filter used to model the estimated continuous signal. It is how many previous samples are used by the algorithm to predict the next one as reference for the model. The higher the order, the more accurate is its spectral definition, not unlike fft, improving low frequency resolution, but it differs in that it is not conected to its temporal resolution.
ARGUMENT:: blockSize
The size in samples of frame on which it the algorithm is operating. High values are more cpu intensive, and also determines the maximum transient size, which will not be allowed to be more than half that lenght in size.
ARGUMENT:: padSize
The size of the handles on each sides of the block simply used for analysis purpose and avoid boundary issues.
ARGUMENT:: skew
The nervousness of the bespoke detection function with values from -10 to 10. It allows to decide how peaks are amplified or smoothed before the thresholding. High values increase the sensitivity to small variations.
ARGUMENT:: threshFwd
The threshold of the onset of the smoothed error function. It allows tight start of the identification of the anomaly as it proceeds forward.
ARGUMENT:: threshBack
The threshold of the offset of the smoothed error function. As it proceeds backwards in time, it allows tight ending of the identification of the anomaly.
ARGUMENT:: windowSize
The averaging window of the error detection function. It needs smoothing as it is very jittery. The longer the window, the less precise, but the less false positives.
ARGUMENT:: clumpLength
The window size in sample within which positive detections will be clumped together to avoid overdetecting in time.
ARGUMENT:: minSliceLength
The minimum duration of a slice in samples.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The function will be passed indices as an argument.
returns:: an instance of the processor
EXAMPLES::
code::
// load some buffers
(
b = Buffer.read(s,FluidFilesPath("Tremblay-AaS-SynthTwoVoices-M.wav"));
c = Buffer.new(s);
)
// with basic parameters (wait for the computation time to appear)
(
Routine{
t = Main.elapsedTime;
FluidBufTransientSlice.process(s,b, indices:c).wait;
(Main.elapsedTime - t).postln;
}.play
)
//check the number of slices
c.query;
//loops over a splice
(
{
BufRd.ar(1, b,
Phasor.ar(0,1,
BufRd.kr(1, c,
MouseX.kr(0, BufFrames.kr(c) - 1), 0, 1),
BufRd.kr(1, c,
MouseX.kr(1, BufFrames.kr(c)), 0, 1),
BufRd.kr(1,c,
MouseX.kr(0, BufFrames.kr(c) - 1), 0, 1)), 0, 1);
}.play;
)
// with everything changed to make it much better, at the cost of computation time (only 5 seconds are processed here, again wait for the (longer) computation time to appear)
(
Routine{
t = Main.elapsedTime;
FluidBufTransientSlice.process(s,b, 0, 220500, 0, 1, c, 200, 2048, 1024, 1, 3, 1, 15, 30, 4410).wait;
(Main.elapsedTime - t).postln;
}.play
)
// play with the same player above to hear the segmentation difference
::
STRONG::A stereo buffer example.::
CODE::
// make a stereo buffer
b = Buffer.alloc(s,88200,2);
// add some stereo clicks and listen to them
((0..3)*22050+11025).do({|item,index| b.set(item+(index%2), 1.0)})
b.play
// create a new buffer as destinations
c = Buffer.new(s);
//run the process on them
(
// with basic params
Routine{
t = Main.elapsedTime;
FluidBufTransientSlice.process(s,b, indices: c, threshFwd: 1.2).wait;
(Main.elapsedTime - t).postln;
}.play
)
// list the indicies of detected attacks - the two input channels have been summed
c.getn(0,c.numFrames,{|item|(item*2).postln;})
::

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save