Merge commit '7188049266b11db5cf7791c6e84c2ff88f005272' into clients/inter_client_comms

# Conflicts:
#	include/FluidSCWrapper.hpp
nix
Owen Green 6 years ago
commit b1f42c943e

@ -183,24 +183,55 @@ public:
{ {
registerUnit<Wrapper>(ft, name); registerUnit<Wrapper>(ft, name);
ft->fDefineUnitCmd(name, "cancel", doCancel); ft->fDefineUnitCmd(name, "cancel", doCancel);
ft->fDefineUnitCmd(name, "queue_enabled", [](struct Unit* unit, struct sc_msg_iter* args)
{
auto w = static_cast<Wrapper *>(unit);
w->mQueueEnabled = args->geti(0);
w->mFifoMsg.Set(w->mWorld,[](FifoMsg* f)
{
auto w = static_cast<Wrapper*>(f->mData);
w->mClient.setQueueEnabled(w->mQueueEnabled);
},nullptr,w);
Wrapper::getInterfaceTable()->fSendMsgFromRT(w->mWorld, w->mFifoMsg);
});
ft->fDefineUnitCmd(name, "synchronous", [](struct Unit* unit, struct sc_msg_iter* args)
{
auto w = static_cast<Wrapper *>(unit);
w->mSynchronous = args->geti(0);
w->mFifoMsg.Set(w->mWorld,[](FifoMsg* f)
{
auto w = static_cast<Wrapper*>(f->mData);
w->mClient.setSynchronous(w->mSynchronous);
},nullptr,w);
Wrapper::getInterfaceTable()->fSendMsgFromRT(w->mWorld, w->mFifoMsg);
});
} }
/// Final input is the doneAction, not a param, so we skip it in the controlsIterator /// Penultimate input is the doneAction, final is blocking mode. Neither are params, so we skip them in the controlsIterator
NonRealTime() : NonRealTime() :
mControlsIterator{mInBuf,static_cast<size_t>(mNumInputs == 0 ? 0 : static_cast<ptrdiff_t>(mNumInputs) - mSpecialIndex - 1)} mControlsIterator{mInBuf,static_cast<size_t>(static_cast<ptrdiff_t>(mNumInputs) - mSpecialIndex - 2)}
, mParams{Wrapper::Client::getParameterDescriptors()} , mParams{Wrapper::Client::getParameterDescriptors()}
, mClient{Wrapper::setParams(mParams,mWorld->mVerbosity > 0, mWorld, mControlsIterator,true)} , mClient{Wrapper::setParams(mParams,mWorld->mVerbosity > 0, mWorld, mControlsIterator,true)}
, mSynchronous{mNumInputs > 2 ? (in0(static_cast<int>(mNumInputs - 1)) > 0) : false}
{} {}
~NonRealTime()
{
if(mClient.state() == ProcessState::kProcessing)
{
std::cout << Wrapper::getName() << ": Processing cancelled \n";
Wrapper::getInterfaceTable()->fSendNodeReply(&mParent->mNode,1,"/done",0,nullptr);
}
//processing will be cancelled in ~NRTThreadAdaptor()
}
/// No option of not using a worker thread for now /// No option of not using a worker thread for now
/// init() sets up the NRT process via the SC NRT thread, and then sets our UGen calc function going /// init() sets up the NRT process via the SC NRT thread, and then sets our UGen calc function going
void init() void init()
{ {
mClient.setSynchronous(false);
mFifoMsg.Set(mWorld, initNRTJob, nullptr, this); mFifoMsg.Set(mWorld, initNRTJob, nullptr, this);
mWorld->ft->fSendMsgFromRT(mWorld,mFifoMsg); mWorld->ft->fSendMsgFromRT(mWorld,mFifoMsg);
//we want to poll thread roughly every 20ms //we want to poll thread roughly every 20ms
checkThreadInterval = static_cast<size_t>(0.02 / controlDur()); checkThreadInterval = static_cast<size_t>(0.02 / controlDur());
set_calc_function<NonRealTime, &NonRealTime::poll>(); set_calc_function<NonRealTime, &NonRealTime::poll>();
@ -209,44 +240,35 @@ public:
/// The calc function. Checks to see if we've cancelled, spits out progress, launches tidy up when complete /// The calc function. Checks to see if we've cancelled, spits out progress, launches tidy up when complete
void poll(int) void poll(int)
{ {
// if(!mClient.done()) out0(0) = mDone ? 1.0 : static_cast<float>(mClient.progress());
// {
out0(0) = static_cast<float>(mClient.progress());
// }
// else {
if(0 == pollCounter++)
{
mWorld->ft->fDoAsynchronousCommand(mWorld, nullptr, Wrapper::getName(), this,
postProcess, exchangeBuffers, tidyUp, destroy,
0, nullptr);
}
pollCounter %= checkThreadInterval;
// if(mClient.state() == kDone)
// mDone = true;
// mCalcFunc = mWorld->ft->fClearUnitOutputs;
// if(!mDone)
// } if(0 == pollCounter++ && !mCheckingForDone)
{
mCheckingForDone = true;
mWorld->ft->fDoAsynchronousCommand(mWorld, nullptr, Wrapper::getName(), this,
postProcess, exchangeBuffers, tidyUp, destroy,
0, nullptr);
}
pollCounter %= checkThreadInterval;
} }
static void nop(Unit*, int) {}
/// To be called on NRT thread. Validate parameters and commence processing in new thread /// To be called on NRT thread. Validate parameters and commence processing in new thread
static void initNRTJob(FifoMsg* f) static void initNRTJob(FifoMsg* f)
{ {
auto w = static_cast<Wrapper*>(f->mData); auto w = static_cast<Wrapper*>(f->mData);
w->mDone = false; w->mDone = false;
w->mCancelled = false;
Result result = validateParameters(w); Result result = validateParameters(w);
if (!result.ok()) if (!result.ok())
{ {
std::cout << "ERROR: " << Wrapper::getName() << ": " << result.message().c_str() << std::endl; std::cout << "ERROR: " << Wrapper::getName() << ": " << result.message().c_str() << std::endl;
// w->mDone = true;
return; return;
} }
w->mClient.setSynchronous(w->mSynchronous);
w->mClient.enqueue(w->mParams);
w->mClient.process(); w->mClient.process();
} }
@ -257,13 +279,15 @@ public:
Result r; Result r;
ProcessState s = w->mClient.checkProgress(r); ProcessState s = w->mClient.checkProgress(r);
if(s==ProcessState::kDone || s==ProcessState::kDoneStillProcessing) if((s==ProcessState::kDone || s==ProcessState::kDoneStillProcessing)
|| (w->mSynchronous && s==ProcessState::kNoProcess) ) //I think this hinges on the fact that when mSynchrous = true, this call will always be behind process() on the command FIFO, so we can assume that if the state is kNoProcess, it has run (vs never having run)
{ {
w->mDone = true; //Given that cancellation from the language now always happens by freeing the
//synth, this block isn't reached normally. HOwever, if someone cancels using u_cmd, this is what will fire
if(r.status() == Result::Status::kCancelled) if(r.status() == Result::Status::kCancelled)
{ {
std::cout << Wrapper::getName() << ": Processing cancelled \n"; std::cout << Wrapper::getName() << ": Processing cancelled \n";
w->mCancelled = true;
return false; return false;
} }
@ -273,6 +297,7 @@ public:
return false; return false;
} }
w->mDone = true;
return true; return true;
} }
return false; return false;
@ -287,20 +312,19 @@ public:
static void destroy(World* world, void* data) static void destroy(World* world, void* data)
{ {
auto w = static_cast<Wrapper*>(data); auto w = static_cast<Wrapper*>(data);
if(w->mDone && w->mNumInputs > 0) //don't check for doneAction if UGen has no ins if(w->mDone && w->mNumInputs > 2) //don't check for doneAction if UGen has no ins (there should be 3 minimum -> sig, doneAction, blocking mode)
{ {
int doneAction = static_cast<int>(w->in0(static_cast<int>(w->mNumInputs - 1))); int doneAction = static_cast<int>(w->in0(static_cast<int>(w->mNumInputs - 2))); //doneAction is penultimate input; THIS IS THE LAW
if(doneAction >= 2) w->mCalcFunc = nop;
world->ft->fDoneAction(doneAction,w); world->ft->fDoneAction(doneAction,w);
return;
} }
w->mCheckingForDone = false;
} }
static void doCancel(Unit *unit, sc_msg_iter*) static void doCancel(Unit *unit, sc_msg_iter*)
{ {
static_cast<Wrapper *>(unit)->mClient.cancel(); static_cast<Wrapper *>(unit)->mClient.cancel();
} }
private: private:
static Result validateParameters(NonRealTime *w) static Result validateParameters(NonRealTime *w)
@ -313,13 +337,17 @@ private:
return {}; return {};
} }
bool exchangeBuffers(World *world) bool exchangeBuffers(World *world) //RT thread
{ {
mParams.template forEachParamType<BufferT, impl::AssignBuffer>(world); mParams.template forEachParamType<BufferT, impl::AssignBuffer>(world);
//At this point, we can see if we're finished and let the language know (or it can wait for the doneAction, but that takes extra time)
//use replyID to convey status (0 = normal completion, 1 = cancelled)
if(mDone) world->ft->fSendNodeReply(&mParent->mNode,0,"/done",0,nullptr);
if(mCancelled) world->ft->fSendNodeReply(&mParent->mNode,1,"/done",0,nullptr);
return true; return true;
} }
bool tidyUp(World *) bool tidyUp(World *) //NRT thread
{ {
mParams.template forEachParamType<BufferT, impl::CleanUpBuffer>(); mParams.template forEachParamType<BufferT, impl::CleanUpBuffer>();
return true; return true;
@ -329,15 +357,18 @@ private:
FloatControlsIter mControlsIterator; FloatControlsIter mControlsIterator;
FifoMsg mFifoMsg; FifoMsg mFifoMsg;
char * mCompletionMessage = nullptr; char* mCompletionMessage = nullptr;
void * mReplyAddr = nullptr; void* mReplyAddr = nullptr;
const char *mName = nullptr; const char *mName = nullptr;
size_t checkThreadInterval; size_t checkThreadInterval;
size_t pollCounter{0}; size_t pollCounter{0};
protected: protected:
ParamSetType mParams; ParamSetType mParams;
Client mClient; Client mClient;
bool mSynchronous{true};
bool mQueueEnabled{false};
bool mCheckingForDone{false}; //only write to this from RT thread kthx
bool mCancelled{false};
}; };
//////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

@ -1,9 +1,6 @@
FluidBufAmpSlice : UGen { FluidBufAmpSlice : UGen {
var <>server, <>synth; *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, absRampUp = 10, absRampDown = 10, absThreshOn = -90, absThreshOff = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, relRampUp = 1, relRampDown = 1, relThreshOn = 144, relThreshOff = -144, highPassFreq = 85, doneAction = 0, blocking|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, absRampUp = 10, absRampDown = 10, absThreshOn = -90, absThreshOff = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, relRampUp = 1, relRampDown = 1, relThreshOn = 144, relThreshOff = -144, highPassFreq = 85, doneAction = 0|
var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead)); var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead));
source = source.asUGenInput; source = source.asUGenInput;
@ -12,42 +9,29 @@ FluidBufAmpSlice : UGen {
source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw}; source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw}; indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw};
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, absRampUp, absRampDown, absThreshOn, absThreshOff, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, relRampUp, relRampDown, relThreshOn, relThreshOff, highPassFreq, maxSize, 0, doneAction); ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, absRampUp, absRampDown, absThreshOn, absThreshOff, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, relRampUp, relRampDown, relThreshOn, relThreshOff, highPassFreq, maxSize, 0, doneAction, blocking);
} }
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, absRampUp = 10, absRampDown = 10, absThreshOn = -90, absThreshOff = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, relRampUp = 1, relRampDown = 1, relThreshOn = 144, relThreshOff = -144, highPassFreq = 85, action| *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, absRampUp = 10, absRampDown = 10, absThreshOn = -90, absThreshOff = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, relRampUp = 1, relRampDown = 1, relThreshOn = 144, relThreshOff = -144, highPassFreq = 85, doneAction = 0|
var instance,synth;
source = source.asUGenInput; ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, absRampUp, absRampDown, absThreshOn, absThreshOff, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, relRampUp, relRampDown, relThreshOn, relThreshOff, highPassFreq, 0, doneAction,blocking:0);
indices = indices.asUGenInput; }
source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw}; *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, absRampUp = 10, absRampDown = 10, absThreshOn = -90, absThreshOff = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, relRampUp = 1, relRampDown = 1, relThreshOn = 144, relThreshOff = -144, highPassFreq = 85, action |
indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw};
server = server ? Server.default; ^FluidNRTProcess.new(
server.ifNotRunning({ server, this, action, [indices]
"WARNING: Server not running".postln; ).process(
^nil; source, startFrame, numFrames, startChan, numChans, indices, absRampUp, absRampDown, absThreshOn, absThreshOff, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, relRampUp, relRampDown, relThreshOn, relThreshOff, highPassFreq
}); );
synth = {instance = FluidBufAmpSlice.kr(source, startFrame, numFrames, startChan, numChans, indices, absRampUp, absRampDown, absThreshOn, absThreshOff, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, relRampUp, relRampDown, relThreshOn, relThreshOff, highPassFreq, doneAction: Done.freeSelf)}.play(server);
forkIfNeeded{
synth.waitForFree;
server.sync;
indices = server.cachedBufferAt(indices);
indices.updateInfo;
server.sync;
action.value(indices);
};
instance.synth = synth;
instance.server = server;
^instance;
} }
cancel{ *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, absRampUp = 10, absRampDown = 10, absThreshOn = -90, absThreshOff = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, relRampUp = 1, relRampDown = 1, relThreshOn = 144, relThreshOff = -144, highPassFreq = 85, action|
if(this.server.notNil)
{this.server.sendMsg("/u_cmd", this.synth.nodeID, this.synthIndex, "cancel")};
}
^FluidNRTProcess.new(
server, this, action, [indices], blocking: 1
).process(
source, startFrame, numFrames, startChan, numChans, indices, absRampUp, absRampDown, absThreshOn, absThreshOff, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, relRampUp, relRampDown, relThreshOn, relThreshOff, highPassFreq
);
}
} }

@ -1,8 +1,6 @@
FluidBufCompose : UGen { FluidBufCompose : UGen {
var <>server, <>synth; *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, doneAction = 0, blocking|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, doneAction = 0|
source = source.asUGenInput; source = source.asUGenInput;
destination = destination.asUGenInput; destination = destination.asUGenInput;
@ -10,46 +8,27 @@ FluidBufCompose : UGen {
source.isNil.if {"FluidBufCompose: Invalid source buffer".throw}; source.isNil.if {"FluidBufCompose: Invalid source buffer".throw};
destination.isNil.if {"FluidBufCompose: Invalid destination buffer".throw}; destination.isNil.if {"FluidBufCompose: Invalid destination buffer".throw};
^super.new1(rate, source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, doneAction, blocking);
^this.multiNew('control', source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, doneAction);
} }
/* *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, doneAction = 0|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, action| ^this.multiNew('control', source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, doneAction, blocking:1);
}*/
var synth, instance;
source = source.asUGenInput;
destination = destination.asUGenInput;
source.isNil.if {"FluidBufCompose: Invalid source buffer".throw}; *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, action|
destination.isNil.if {"FluidBufCompose: Invalid destination buffer".throw}; ^FluidNRTProcess.new(
server, this, action, [destination], blocking:1
).process(
source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain
);
server = server ? Server.default;
server.ifNotRunning({
"WARNING: Server not running".postln;
^nil;
});
synth = {instance = FluidBufCompose.kr(source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, doneAction: Done.freeSelf)}.play(server);
forkIfNeeded{
synth.waitForFree;
server.sync;
destination = server.cachedBufferAt(destination);
destination.updateInfo;
server.sync;
action.value(destination);
};
instance.server = server;
instance.synth = synth;
^instance;
} }
cancel{ *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, action|
if(this.server.notNil) ^process(
{this.server.sendMsg("/u_cmd", this.synth.nodeID, this.synthIndex, "cancel")}; source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain
} );
}
} }

@ -1,8 +1,6 @@
FluidBufHPSS : UGen { FluidBufHPSS : UGen {
var <>server, <>synth; *new1 {|rate, 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, doneAction = 0, blocking|
*kr {|source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, harmonic, percussive, residual, 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, doneAction = 0|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
@ -10,56 +8,38 @@ FluidBufHPSS : UGen {
harmonic = harmonic.asUGenInput; harmonic = harmonic.asUGenInput;
percussive = percussive.asUGenInput; percussive = percussive.asUGenInput;
residual = residual.asUGenInput; residual = residual.asUGenInput;
source.isNil.if {"FluidBufHPSS: Invalid source buffer".throw};
harmonic = harmonic ? -1;
percussive = percussive ? -1;
residual = residual ? -1;
//NB For wrapped versions of NRT classes, we set the params for maxima to //NB For wrapped versions of NRT classes, we set the params for maxima to
//whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value) //whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value)
^this.multiNew('control', source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize, maxFFTSize, harmFilterSize, percFilterSize, doneAction); ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize, maxFFTSize, harmFilterSize, percFilterSize, doneAction, blocking);
} }
*process {|server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, harmonic, percussive, residual, 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, action| *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, doneAction = 0|
var synth, instance; ^this.multiNew(
'control', source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize, doneAction, blocking:0
);
}
source = source.asUGenInput; *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, action|
harmonic = harmonic.asUGenInput;
percussive = percussive.asUGenInput;
residual = residual.asUGenInput;
source.isNil.if {"FluidBufHPSS: Invalid source buffer".throw}; ^FluidNRTProcess.new(
server, this, action, [harmonic, percussive, residual].select{|x| x!= -1}
).process(
source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize
);
server = server ? Server.default; }
server.ifNotRunning({
"WARNING: Server not running".postln;
^nil;
});
harmonic = harmonic ? -1;
percussive = percussive ? -1;
residual = residual ? -1;
synth = { instance = FluidBufHPSS.kr(source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize, doneAction:Done.freeSelf)}.play(server); *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, action|
forkIfNeeded{ ^FluidNRTProcess.new(
synth.waitForFree; server, this, action, [harmonic, percussive, residual].select{|x| x!= -1}, blocking:1
server.sync; ).process(
if (harmonic != -1) {harmonic = server.cachedBufferAt(harmonic); harmonic.updateInfo; server.sync;} {harmonic = nil}; source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize
if (percussive != -1) {percussive = server.cachedBufferAt(percussive); percussive.updateInfo; server.sync;} {percussive = nil}; );
if (residual != -1) {residual = server.cachedBufferAt(residual); residual.updateInfo; server.sync;} {residual = nil};
action.value(harmonic, percussive, residual);
};
instance.server = server;
instance.synth = synth;
^instance;
} }
cancel{
if(this.server.notNil)
{this.server.sendMsg("/u_cmd", this.synth.nodeID, this.synthIndex, "cancel")};
}
} }

@ -1,8 +1,5 @@
FluidBufLoudness : UGen{ FluidBufLoudness : UGen{
*new1 { |rate,source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, doneAction = 0, blocking = 0|
var <>server, <>synth;
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, doneAction = 0|
var maxwindowSize = windowSize.nextPowerOfTwo; var maxwindowSize = windowSize.nextPowerOfTwo;
@ -12,40 +9,26 @@ FluidBufLoudness : UGen{
source.isNil.if {"FluidBufPitch: Invalid source buffer".throw}; source.isNil.if {"FluidBufPitch: Invalid source buffer".throw};
features.isNil.if {"FluidBufPitch: Invalid features buffer".throw}; features.isNil.if {"FluidBufPitch: Invalid features buffer".throw};
^this.multiNew('control', source, startFrame, numFrames, startChan, numChans, features, kWeighting, truePeak, windowSize, hopSize, maxwindowSize, doneAction); ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, features, kWeighting, truePeak, windowSize, hopSize, maxwindowSize, doneAction, blocking);
} }
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, action| *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, doneAction = 0|
^this.multiNew('control', source, startFrame, numFrames, startChan, numChans, features, kWeighting, truePeak, windowSize, hopSize, doneAction, blocking:0 );
var synth, instance;
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufPitch: Invalid source buffer".throw};
features.isNil.if {"FluidBufPitch: Invalid features buffer".throw};
server = server ? Server.default;
server.ifNotRunning({
"WARNING: Server not running".postln;
^nil;
});
synth = {instance = FluidBufLoudness.kr(source, startFrame, numFrames, startChan, numChans, features, kWeighting, truePeak, windowSize, hopSize, doneAction:Done.freeSelf)}.play(server);
forkIfNeeded{
synth.waitForFree;
server.sync;
features = server.cachedBufferAt(features); features.updateInfo; server.sync;
action.value(features);
};
instance.server = server;
instance.synth = synth;
^instance;
} }
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, action|
^FluidNRTProcess.new(
server, this, action, [features]
).process(
source, startFrame, numFrames, startChan, numChans, features, kWeighting, truePeak, windowSize, hopSize
);
}
cancel{ *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, action|
if(this.server.notNil) ^FluidNRTProcess.new(
{this.server.sendMsg("/u_cmd", this.synth.nodeID, this.synthIndex, "cancel")}; server, this, action, [features], blocking: 1
).process(
source, startFrame, numFrames, startChan, numChans, features, kWeighting, truePeak, windowSize, hopSize
);
} }
} }

@ -1,6 +1,5 @@
FluidBufMFCC : UGen{ FluidBufMFCC : UGen{
var <>server, <>synth; *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0, blocking = 0|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput; source = source.asUGenInput;
@ -13,39 +12,26 @@ FluidBufMFCC : UGen{
//whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value) //whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value)
// same goes to maxNumCoeffs, which is passed numCoeffs in this case // same goes to maxNumCoeffs, which is passed numCoeffs in this case
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, numCoeffs, numBands, minFreq, maxFreq, numCoeffs, windowSize, hopSize, fftSize, maxFFTSize, doneAction); ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, features, numCoeffs, numBands, minFreq, maxFreq, numCoeffs, windowSize, hopSize, fftSize, maxFFTSize, doneAction, blocking);
} }
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, action| *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0|
var synth, instance; ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, numCoeffs, numBands, minFreq, maxFreq, numCoeffs, windowSize, hopSize, fftSize, doneAction);
}
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufMFCC: Invalid source buffer".throw};
features.isNil.if {"FluidBufMFCC: Invalid features buffer".throw};
server = server ? Server.default;
server.ifNotRunning({
"WARNING: Server not running".postln;
^nil;
});
synth = { instance = FluidBufMFCC.kr(source, startFrame, numFrames, startChan, numChans, features, numCoeffs, numBands, minFreq, maxFreq, windowSize, hopSize, fftSize, doneAction:Done.freeSelf)}.play(server);
forkIfNeeded{
synth.waitForFree;
server.sync;
features = server.cachedBufferAt(features); features.updateInfo; server.sync;
action.value(features);
};
instance.synth = synth; *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, action |
instance.server = server; ^FluidNRTProcess.new(
^instance; server, this, action, [features]
).process(
source, startFrame, numFrames, startChan, numChans, features, numCoeffs, numBands, minFreq, maxFreq, windowSize, hopSize, fftSize
);
} }
cancel{ *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, action |
if(this.server.notNil) ^FluidNRTProcess.new(
{this.server.sendMsg("/u_cmd", this.synth.nodeID, this.synthIndex, "cancel")}; server, this, action, [features],blocking: 1
).process(
source, startFrame, numFrames, startChan, numChans, features, numCoeffs, numBands, minFreq, maxFreq, windowSize, hopSize, fftSize
);
} }
} }

@ -1,7 +1,5 @@
FluidBufMelBands : UGen { FluidBufMelBands : UGen {
var <>server, <>synth; *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0, blocking = 0|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0 |
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
@ -15,40 +13,26 @@ FluidBufMelBands : UGen {
//whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value) //whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value)
// same for maxNumBands which is passed numBands // same for maxNumBands which is passed numBands
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, numBands, windowSize, hopSize, fftSize, maxFFTSize, doneAction); ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, numBands, windowSize, hopSize, fftSize, maxFFTSize, doneAction, blocking);
} }
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, action| *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0|
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, numBands, windowSize, hopSize, fftSize, doneAction);
var synth, instance;
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufMelBands: Invalid source buffer".throw};
features.isNil.if {"FluidBufMelBands: Invalid features buffer".throw};
server = server ? Server.default;
server.ifNotRunning({
"WARNING: Server not running".postln;
^nil;
});
synth = { instance = FluidBufMelBands.kr(source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, windowSize, hopSize, fftSize, doneAction: Done.freeSelf)}.play(server);
forkIfNeeded{
synth.waitForFree;
server.sync;
features = server.cachedBufferAt(features); features.updateInfo; server.sync;
action.value(features);
};
instance.synth = synth;
instance.server = server;
^instance;
} }
cancel{ *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, action|
if(this.server.notNil) ^FluidNRTProcess.new(
{this.server.sendMsg("/u_cmd", this.synth.nodeID, this.synthIndex, "cancel")}; server, this, action, [features]
} ).process(
source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, windowSize, hopSize, fftSize
);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, action|
^FluidNRTProcess.new(
server, this, action, [features], blocking:1
).process(
source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, windowSize, hopSize, fftSize
);
} }
}

@ -1,9 +1,6 @@
FluidBufNMF : UGen { FluidBufNMF : UGen {
var <>server, <>synth; *new1 {|rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, bases, basesMode = 0, activations, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, doneAction = 0, blocking = 0|
*kr {|source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, bases, basesMode = 0, activations, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, doneAction = 0|
source = source.asUGenInput; source = source.asUGenInput;
destination = destination.asUGenInput; destination = destination.asUGenInput;
@ -16,61 +13,35 @@ FluidBufNMF : UGen {
bases = bases ? -1; bases = bases ? -1;
activations = activations ? -1; activations = activations ? -1;
^this.multiNew('control',source, startFrame, numFrames, startChan, numChans, destination, bases, basesMode, activations, actMode, components, iterations, windowSize, hopSize, fftSize, doneAction); ^super.new1(rate,source, startFrame, numFrames, startChan, numChans, destination, bases, basesMode, activations, actMode, components, iterations, windowSize, hopSize, fftSize, doneAction, blocking);
} }
*kr {|source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, bases, basesMode = 0, activations, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, doneAction = 0|
^this.multiNew(\control,source, startFrame, numFrames, startChan, numChans, destination, bases, basesMode, activations, actMode, components, iterations, windowSize, hopSize, fftSize, doneAction);
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, bases, basesMode = 0, activations, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, action| }
var instance,synth;
source = source.asUGenInput; *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination = -1, bases = -1, basesMode = 0, activations = -1, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, action|
destination = destination.asUGenInput;
bases = bases.asUGenInput;
activations = activations.asUGenInput;
source.isNil.if {"FluidBufNMF: Invalid source buffer".throw}; source.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
destination = destination ? -1; ^FluidNRTProcess.new(
bases = bases ? -1; server, this, action, [destination, bases, activations].select{|x| x!= -1}
activations = activations ? -1; ).process(
source, startFrame, numFrames, startChan, numChans, destination, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize, windowType, randomSeed
server = server ? Server.default; );
server.ifNotRunning({ }
"WARNING: Server not running".postln;
^nil;
});
synth = {instance = FluidBufNMF.kr(source, startFrame, numFrames, startChan, numChans, destination, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize, doneAction: Done.freeSelf)}.play(server);
forkIfNeeded{ *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination = -1, bases = -1, basesMode = 0, activations = -1, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, action|
synth.waitForFree;
server.sync;
if (destination != -1) {
destination = server.cachedBufferAt(destination);
destination.updateInfo;
server.sync;
} {destination = nil};
if (bases != -1) {
bases = server.cachedBufferAt(bases);
bases.updateInfo;
server.sync;
} {bases = nil};
if (activations != -1) {
activations = server.cachedBufferAt(activations);
activations.updateInfo;
server.sync;
} {activations = nil};
action.value(destination, bases, activations);
};
instance.synth = synth; source.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
instance.server = server;
^instance;
}
cancel{ ^FluidNRTProcess.new(
if(this.server.notNil) server, this, action, [destination, bases, activations].select{|x| x!= -1},blocking: 1
{this.server.sendMsg("/u_cmd", this.synth.nodeID, this.synthIndex, "cancel")}; ).process(
source, startFrame, numFrames, startChan, numChans, destination, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize, windowType, randomSeed
);
} }
} }

@ -1,8 +1,5 @@
FluidBufNoveltySlice : UGen { FluidBufNoveltySlice : UGen {
*new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, feature = 0, kernelSize = 3, threshold = 0.5, filterSize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0, blocking = 0 |
var <>synth, <>server;
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, feature = 0, kernelSize = 3, threshold = 0.5, filterSize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0 |
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
@ -12,43 +9,29 @@ FluidBufNoveltySlice : UGen {
source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw}; source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw}; indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw};
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, windowSize, hopSize, fftSize, maxFFTSize, kernelSize, filterSize, doneAction); ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, windowSize, hopSize, fftSize, maxFFTSize, kernelSize, filterSize, doneAction, blocking);
} }
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, feature = 0, kernelSize = 3, threshold = 0.5, filterSize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, action| *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, feature = 0, kernelSize = 3, threshold = 0.5, filterSize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0 |
var synth, instance; ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, windowSize, hopSize, fftSize, doneAction);
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput; }
indices = indices.asUGenInput;
source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw};
server = server ? Server.default;
server.ifNotRunning({
"WARNING: Server not running".postln;
^nil;
});
synth = { instance = FluidBufNoveltySlice.kr(source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, windowSize, hopSize, fftSize, maxFFTSize, kernelSize, filterSize, doneAction: Done.freeSelf)}.play(server);
forkIfNeeded{
synth.waitForFree;
server.sync;
indices = server.cachedBufferAt(indices); indices.updateInfo; server.sync;
action.value(indices);
};
instance.synth = synth; *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, feature = 0, kernelSize = 3, threshold = 0.5, filterSize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, action |
instance.server = server; ^FluidNRTProcess.new(
^instance; server, this, action, [indices]
).process(
source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, windowSize, hopSize, fftSize
);
} }
cancel{ *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, feature = 0, kernelSize = 3, threshold = 0.5, filterSize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, action |
if(this.server.notNil) ^FluidNRTProcess.new(
{this.server.sendMsg("/u_cmd", this.synth.nodeID, this.synthIndex, "cancel")}; server, this, action, [indices], blocking:1
} ).process(
source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, windowSize, hopSize, fftSize
);
}
} }

@ -1,8 +1,5 @@
FluidBufOnsetSlice : UGen { FluidBufOnsetSlice : UGen {
*new1 { |rate, 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, doneAction = 0, blocking = 0|
var <>synth, <>server;
*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, doneAction = 0|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
@ -15,41 +12,26 @@ FluidBufOnsetSlice : UGen {
//NB For wrapped versions of NRT classes, we set the params for maxima to //NB For wrapped versions of NRT classes, we set the params for maxima to
//whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value) //whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value)
^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize, maxFFTSize, doneAction, blocking);
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize, maxFFTSize, doneAction);
} }
*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, doneAction = 0|
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize, doneAction);
}
*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, 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, action|
var synth, instance; ^FluidNRTProcess.new(
source = source.asUGenInput; server, this, action, [indices]
indices = indices.asUGenInput; ).process(
source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize
source.isNil.if {"FluidBufOnsetSlice: Invalid source buffer".throw}; );
indices.isNil.if {"FluidBufOnsetSlice: Invalid features buffer".throw};
server = server ? Server.default;
server.ifNotRunning({
"WARNING: Server not running".postln;
^nil;
});
//NB For wrapped versions of NRT classes, we set the params for maxima to
//whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value)
synth = { instance = FluidBufOnsetSlice.kr(source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize, doneAction: Done.freeSelf)}.play(server);
forkIfNeeded{
synth.waitForFree;
server.sync;
indices = server.cachedBufferAt(indices); indices.updateInfo; server.sync;
action.value(indices);
};
instance.server = server;
instance.synth = synth;
^instance;
} }
cancel{ *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, action|
if(this.server.notNil) ^FluidNRTProcess.new(
{this.server.sendMsg("/u_cmd", this.synth.nodeID, this.synthIndex, "cancel")}; server, this, action, [indices], blocking: 1
} ).process(
source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize
);
}
} }

@ -1,7 +1,6 @@
FluidBufPitch : UGen{ FluidBufPitch : UGen{
var <>synth, <>server;
*kr {|source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0| *new1 {|rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0, blocking = 0|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
@ -13,41 +12,28 @@ FluidBufPitch : UGen{
//NB For wrapped versions of NRT classes, we set the params for maxima to //NB For wrapped versions of NRT classes, we set the params for maxima to
//whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value) //whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value)
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, maxFFTSize, doneAction); ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, features, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, maxFFTSize, doneAction, blocking);
} }
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, action| *kr {|source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0|
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, doneAction);
var synth,instance;
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufPitch: Invalid source buffer".throw}; }
features.isNil.if {"FluidBufPitch: Invalid features buffer".throw};
server = server ? Server.default; *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, action|
server.ifNotRunning({ ^FluidNRTProcess.new(
"WARNING: Server not running".postln; server, this, action, [features]
^nil; ).process(
}); source, startFrame, numFrames, startChan, numChans, features, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize
);
synth = { instance = FluidBufPitch.kr(source, startFrame, numFrames, startChan, numChans, features, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, doneAction:Done.freeSelf)}.play(server);
forkIfNeeded{
synth.waitForFree;
server.sync;
features = server.cachedBufferAt(features); features.updateInfo; server.sync;
action.value(features);
};
instance.synth = synth;
instance.server = server;
^instance;
} }
cancel{ *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, action|
if(this.server.notNil) ^FluidNRTProcess.new(
{this.server.sendMsg("/u_cmd", this.synth.nodeID, this.synthIndex, "cancel")}; server, this, action, [features], blocking: 1
} ).process(
source, startFrame, numFrames, startChan, numChans, features, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize
);
}
} }

@ -1,8 +1,6 @@
FluidBufSines : UGen{ FluidBufSines : UGen{
var <>synth, <>server; *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, threshold = 0.7, minTrackLen = 15, magWeight = 0.1, freqWeight = 1, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0, blocking = 0|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines, residual, bandwidth = 76, threshold = 0.7, minTrackLen = 15, magWeight = 0.1, freqWeight = 1, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
@ -12,46 +10,30 @@ FluidBufSines : UGen{
source.isNil.if {"FluidBufSines: Invalid source buffer".throw}; source.isNil.if {"FluidBufSines: Invalid source buffer".throw};
sines = sines ? -1;
residual = residual ? -1;
//NB For wrapped versions of NRT classes, we set the params for maxima to //NB For wrapped versions of NRT classes, we set the params for maxima to
//whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value) //whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value)
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, threshold, minTrackLen, magWeight, freqWeight, windowSize, hopSize, fftSize, maxFFTSize, doneAction); ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, threshold, minTrackLen, magWeight, freqWeight, windowSize, hopSize, fftSize, maxFFTSize, doneAction, blocking);
} }
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, threshold = 0.7, minTrackLen = 15, magWeight = 0.1, freqWeight = 1, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0|
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, threshold, minTrackLen, magWeight, freqWeight, windowSize, hopSize, fftSize, doneAction);
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines, residual, bandwidth = 76, threshold = 0.7, minTrackLen = 15, magWeight = 0.1, freqWeight = 1, windowSize = 1024, hopSize = -1, fftSize = -1, action| *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, threshold = 0.7, minTrackLen = 15, magWeight = 0.1, freqWeight = 1, windowSize = 1024, hopSize = -1, fftSize = -1, action|
var synth, instance; ^FluidNRTProcess.new(
source = source.asUGenInput; server, this, action, [sines, residual].select{|x| x!= -1}
sines = sines.asUGenInput; ).process(
residual = residual.asUGenInput; source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, threshold, minTrackLen, magWeight, freqWeight, windowSize, hopSize, fftSize
);
source.isNil.if {"FluidBufSines: Invalid source buffer".throw}; }
server = server ? Server.default; *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, threshold = 0.7, minTrackLen = 15, magWeight = 0.1, freqWeight = 1, windowSize = 1024, hopSize = -1, fftSize = -1, action|
server.ifNotRunning({ ^FluidNRTProcess.new(
"WARNING: Server not running".postln; server, this, action, [sines, residual].select{|x| x!= -1}, blocking: 1
^nil; ).process(
}); source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, threshold, minTrackLen, magWeight, freqWeight, windowSize, hopSize, fftSize
sines = sines ? -1; );
residual = residual ? -1;
synth = { instance = FluidBufSines.kr(source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, threshold, minTrackLen, magWeight, freqWeight, windowSize, hopSize, fftSize, doneAction:Done.freeSelf)}.play(server);
forkIfNeeded{
synth.waitForFree;
server.sync;
if (sines != -1) {sines = server.cachedBufferAt(sines); sines.updateInfo; server.sync;} {sines = nil};
if (residual != -1) {residual = server.cachedBufferAt(residual); residual.updateInfo; server.sync;} {residual = nil};
action.value(sines, residual);
};
instance.synth = synth;
instance.server = server;
^instance;
} }
cancel{
if(this.server.notNil)
{this.server.sendMsg("/u_cmd", this.synth.nodeID, this.synthIndex, "cancel")};
}
} }

@ -1,8 +1,6 @@
FluidBufSpectralShape : UGen { FluidBufSpectralShape : UGen {
var <>synth, <>server; *new1{ |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0, blocking = 0|
*kr{ |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
@ -15,41 +13,30 @@ FluidBufSpectralShape : UGen {
//NB For wrapped versions of NRT classes, we set the params for maxima to //NB For wrapped versions of NRT classes, we set the params for maxima to
//whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value) //whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value)
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, windowSize, hopSize, fftSize, maxFFTSize, doneAction); ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, features, windowSize, hopSize, fftSize, maxFFTSize, doneAction, blocking);
} }
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, action|
var synth, instance; *kr{ |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0|
source = source.asUGenInput; ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, windowSize, hopSize, fftSize, doneAction);
features = features.asUGenInput;
source.isNil.if {"FluidBufSpectralShape: Invalid source buffer".throw}; }
features.isNil.if {"FluidBufSpectralShape: Invalid features buffer".throw};
server = server ? Server.default; *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, action|
server.ifNotRunning({ ^FluidNRTProcess.new(
"WARNING: Server not running".postln; server, this, action, [features]
^nil; ).process(
}); source, startFrame, numFrames, startChan, numChans, features, windowSize, hopSize, fftSize
synth = { instance = FluidBufSpectralShape.kr(source, startFrame, numFrames, startChan, numChans, features, windowSize, hopSize, fftSize, doneAction:Done.freeSelf)}.play(server); );
}
//NB For wrapped versions of NRT classes, we set the params for maxima to *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, action|
//whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value) ^FluidNRTProcess.new(
forkIfNeeded{ server, this, action, [features], blocking: 1
synth.waitForFree; ).process(
server.sync; source, startFrame, numFrames, startChan, numChans, features, windowSize, hopSize, fftSize
features = server.cachedBufferAt(features); features.updateInfo; server.sync; );
action.value(features);
};
instance.synth = synth;
instance.server = server;
^instance;
} }
cancel{
if(this.server.notNil)
{this.server.sendMsg("/u_cmd", this.synth.nodeID, this.synthIndex, "cancel")};
}
} }

@ -1,7 +1,6 @@
FluidBufStats : UGen{ FluidBufStats : UGen{
var <>synth, <>server;
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, numDerivs = 0, low = 0, middle = 50, high = 100, doneAction=0| *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, numDerivs = 0, low = 0, middle = 50, high = 100, doneAction=0, blocking = 0|
source = source.asUGenInput; source = source.asUGenInput;
stats = stats.asUGenInput; stats = stats.asUGenInput;
@ -9,38 +8,27 @@ FluidBufStats : UGen{
source.isNil.if {"FluidBufStats: Invalid source buffer".throw}; source.isNil.if {"FluidBufStats: Invalid source buffer".throw};
stats.isNil.if {"FluidBufStats: Invalid stats buffer".throw}; stats.isNil.if {"FluidBufStats: Invalid stats buffer".throw};
^super.new1(rate, source, startFrame, numFrames, startChan, numChans, stats, numDerivs, low, middle, high,doneAction, blocking);
}
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, numDerivs = 0, low = 0, middle = 50, high = 100, doneAction=0|
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, stats, numDerivs, low, middle, high,doneAction); ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, stats, numDerivs, low, middle, high,doneAction);
} }
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, numDerivs = 0, low = 0, middle = 50, high = 100, action| *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, numDerivs = 0, low = 0, middle = 50, high = 100, action|
^FluidNRTProcess.new(
server, this, action, [stats]
).process(
source, startFrame, numFrames, startChan, numChans, stats,numDerivs, low, middle, high
);
}
var synth, instance; *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, numDerivs = 0, low = 0, middle = 50, high = 100, action|
source = source.asUGenInput; ^FluidNRTProcess.new(
stats = stats.asUGenInput; server, this, action, [stats], blocking: 1
).process(
source.isNil.if {"FluidBufStats: Invalid source buffer".throw}; source, startFrame, numFrames, startChan, numChans, stats,numDerivs, low, middle, high
stats.isNil.if {"FluidBufStats: Invalid stats buffer".throw}; );
server = server ? Server.default;
server.ifNotRunning({
"WARNING: Server not running".postln;
^nil;
});
synth = { instance = FluidBufStats.kr(source, startFrame, numFrames, startChan, numChans, stats, numDerivs, low, middle, high, doneAction:Done.freeSelf)}.play(server);
forkIfNeeded{
synth.waitForFree;
server.sync;
stats = server.cachedBufferAt(stats); stats.updateInfo; server.sync;
action.value(stats);
};
instance.synth = synth;
instance.server = server;
^instance;
} }
cancel{
if(this.server.notNil)
{this.server.sendMsg("/u_cmd", this.synth.nodeID, this.synthIndex, "cancel")};
}
} }

@ -0,0 +1,29 @@
FluidBufThreadDemo : UGen{
*new1 {|rate, result, time, doneAction = 0, blocking = 0 |
result = result.asUGenInput;
result.isNil.if {this.class.name+": Invalid output buffer".throw};
^super.new1(rate, result, time, doneAction, blocking);
}
*kr {|result, time, doneAction = 0|
^this.new1(\control, result, time, doneAction);
}
*process { |server, result, time = 1000, action|
^FluidNRTProcess.new(
server, this, action, [result]
).process(
result, time
);
}
*processBlocking { |server, result, time = 1000, action|
^FluidNRTProcess.new(
server, this, action, [result], blocking: 1
).process(
result, time
);
}
}

@ -1,7 +1,5 @@
FluidBufTransientSlice : UGen{ FluidBufTransientSlice : UGen{
var <>synth, <>server; *new1 { |rate, 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, doneAction = 0, blocking = 0|
*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, doneAction = 0|
source = source.asUGenInput; source = source.asUGenInput;
indices = indices.asUGenInput; indices = indices.asUGenInput;
@ -9,39 +7,24 @@ FluidBufTransientSlice : UGen{
source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw}; source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw}; indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw};
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength, doneAction); ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength, doneAction, blocking);
}
*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, doneAction = 0|
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength, doneAction);
} }
*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, 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, action|
^FluidNRTProcess.new(
server, this, action,[indices]
).process(source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength
);
}
var synth, instance; *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, action|
source = source.asUGenInput; ^FluidNRTProcess.new(
indices = indices.asUGenInput; server, this, action,[indices], blocking: 1
).process(source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength
source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw}; );
indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw};
server = server ? Server.default;
server.ifNotRunning({
"WARNING: Server not running".postln;
^nil;
});
synth = {instance = FluidBufTransientSlice.kr(source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength, doneAction: Done.freeSelf)}.play(server);
forkIfNeeded{
synth.waitForFree;
server.sync;
indices = server.cachedBufferAt(indices); indices.updateInfo; server.sync;
action.value(indices);
};
instance.synth = synth;
instance.server = server;
^instance;
}
cancel{
if(this.server.notNil)
{this.server.sendMsg("/u_cmd", this.synth.nodeID, this.synthIndex, "cancel")};
} }
} }

@ -1,8 +1,7 @@
FluidBufTransients : UGen { FluidBufTransients : UGen {
var <>synth, <>server;
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, transients, residual, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, doneAction = 0 | *new1 { |rate, 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, doneAction = 0, blocking = 0 |
source = source.asUGenInput; source = source.asUGenInput;
transients = transients.asUGenInput; transients = transients.asUGenInput;
@ -10,46 +9,28 @@ FluidBufTransients : UGen {
source.isNil.if {"FluidBufTransients: Invalid source buffer".throw}; source.isNil.if {"FluidBufTransients: Invalid source buffer".throw};
transients = transients ? -1; ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, doneAction, blocking);
residual = residual ? -1;
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, doneAction);
} }
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, transients, residual, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, action|
var synth, instance; *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, doneAction = 0|
source = source.asUGenInput; ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, doneAction);
transients = transients.asUGenInput;
residual = residual.asUGenInput;
source.isNil.if {"FluidBufTransients: Invalid source buffer".throw}; }
server = server ? Server.default; *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, action|
server.ifNotRunning({ ^FluidNRTProcess.new(
"WARNING: Server not running".postln; server, this, action,[transients, residual].select{|x| x!= -1}
^nil; ).process(
}); source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength
transients = transients ? -1; );
residual = residual ? -1;
synth = {instance = FluidBufTransients.kr(source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, doneAction:Done.freeSelf)}.play(server);
forkIfNeeded{
synth.waitForFree;
server.sync;
if (transients != -1) {transients = server.cachedBufferAt(transients); transients.updateInfo; server.sync;} {transients = nil};
if (residual != -1) {residual = server.cachedBufferAt(residual); residual.updateInfo; server.sync;} {residual = nil};
action.value(transients, residual);
};
instance.synth = synth;
instance.server = server;
^instance;
} }
cancel{ *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, action|
if(this.server.notNil) ^FluidNRTProcess.new(
{this.server.sendMsg("/u_cmd", this.synth.nodeID, this.synthIndex, "cancel")}; server, this, action,[transients, residual].select{|x| x!= -1}, blocking: 1
} ).process(
source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength
);
}
} }

@ -1,7 +1,7 @@
TITLE:: FluidBufAmpSlice TITLE:: FluidBufAmpSlice
SUMMARY:: Amplitude-based Slicer for Buffers SUMMARY:: Amplitude-based Slicer for Buffers
CATEGORIES:: Libraries>FluidDecomposition CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading
DESCRIPTION:: DESCRIPTION::
This class implements an amplitude-based slicer, with various customisable options and conditions to detect absolute and relative amplitude changes as onsets and offsets. It is part of the Fluid Decomposition Toolkit of the FluCoMa project.footnote::This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899).:: This class implements an amplitude-based slicer, with various customisable options and conditions to detect absolute and relative amplitude changes as onsets and offsets. It is part of the Fluid Decomposition Toolkit of the FluCoMa project.footnote::This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899).::
@ -10,6 +10,10 @@ FluidAmpSlice is based on two envelop followers on a highpassed version of the s
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. 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:: CLASSMETHODS::
METHOD:: process METHOD:: process
@ -171,19 +175,22 @@ c = Buffer.new(s);
FluidBufAmpSlice.process(s,b,indices:c, absRampUp:2205, absRampDown:2205, absThreshOn:-70, absThreshOff: -80, relRampUp:10, relRampDown:441, relThreshOn:14, relThreshOff:12, minSliceLength:4410) FluidBufAmpSlice.process(s,b,indices:c, absRampUp:2205, absRampDown:2205, absThreshOn:-70, absThreshOff: -80, relRampUp:10, relRampDown:441, relThreshOn:14, relThreshOff:12, minSliceLength:4410)
c.query c.query
c.getn(0,c.numFrames*2,{|item|item.postln;}) 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});})
//loops over a splice with the MouseX //loops over a splice with the MouseX, taking the respective onset and offset of a given slice
( (
{ {
BufRd.ar(1, b, BufRd.ar(1, b,
Phasor.ar(0,1, Phasor.ar(0,1,
BufRd.kr(1, c, BufRd.kr(2, c,
MouseX.kr(0, BufFrames.kr(c) - 1), 0, 1), MouseX.kr(0, BufFrames.kr(c)), 0, 1)[0],
BufRd.kr(1, c, BufRd.kr(2, c,
MouseX.kr(1, BufFrames.kr(c)), 0, 1), MouseX.kr(1, BufFrames.kr(c)), 0, 1)[1],
BufRd.kr(1,c, BufRd.kr(2,c,
MouseX.kr(0, BufFrames.kr(c) - 1), 0, 1)), 0, 1); MouseX.kr(0, BufFrames.kr(c)), 0, 1)[0]
}.play; ), 0, 1);
}.play;
) )
:: ::
@ -209,6 +216,6 @@ Routine{
}.play }.play
) )
// list the indicies of detected attacks - the two input channels have been summed // 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,{|item|item.postln;}) c.getn(0,c.numFrames*2,{|item|item.postln;})
:: ::

@ -1,8 +1,7 @@
TITLE:: FluidBufCompose TITLE:: FluidBufCompose
SUMMARY:: Buffer Compositing Utility SUMMARY:: Buffer Compositing Utility
CATEGORIES:: Libraries>FluidDecomposition, UGens>Buffer CATEGORIES:: Libraries>FluidDecomposition, UGens>Buffer
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/Buffer RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading, Classes/Buffer
DESCRIPTION:: 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/FluidDecomposition:: as part of the FluCoMa project. footnote:: 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/FluidDecomposition:: as part of the FluCoMa project. footnote::
@ -12,7 +11,6 @@ At its most simple, the object copies the content of a source buffer into a dest
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. 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:: CLASSMETHODS::
METHOD:: process METHOD:: process

@ -1,7 +1,7 @@
TITLE:: FluidBufHPSS TITLE:: FluidBufHPSS
SUMMARY:: Buffer-Based Harmonic-Percussive Source Separation Using Median Filtering SUMMARY:: Buffer-Based Harmonic-Percussive Source Separation Using Median Filtering
CATEGORIES:: Libraries>FluidDecomposition, UGens>Buffer CATEGORIES:: Libraries>FluidDecomposition, UGens>Buffer
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading
DESCRIPTION:: DESCRIPTION::
@ -22,6 +22,9 @@ This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/
More information on median filtering, and on HPSS for musicianly usage, are availabe in LINK::Guides/FluCoMa:: overview file. More information on median filtering, and on HPSS for musicianly usage, are availabe in LINK::Guides/FluCoMa:: overview file.
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:: CLASSMETHODS::

@ -1,7 +1,7 @@
TITLE:: FluidBufLoudness TITLE:: FluidBufLoudness
SUMMARY:: A Loudness and True-Peak Descriptor on a Buffer SUMMARY:: A Loudness and True-Peak Descriptor on a Buffer
CATEGORIES:: Libraries>FluidDecomposition CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading
DESCRIPTION:: DESCRIPTION::
@ -9,6 +9,10 @@ This class implements two loudness descriptors, computing the true peak of the s
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::. 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:: CLASSMETHODS::
METHOD:: process METHOD:: process

@ -1,13 +1,17 @@
TITLE:: FluidBufMFCC TITLE:: FluidBufMFCC
SUMMARY:: Mel-Frequency Cepstral Coefficients as Spectral Descriptors on a Buffer SUMMARY:: Mel-Frequency Cepstral Coefficients as Spectral Descriptors on a Buffer
CATEGORIES:: Libraries>FluidDecomposition CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/FluidBufMelBands RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading, Classes/FluidBufMelBands
DESCRIPTION:: 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 avantage of being amplitude invarient, except for the first coefficient. It is part of the Fluid Decomposition Toolkit of the FluCoMa project.FOOTNOTE:: This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899).:: 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 avantage of being amplitude invarient, except for the first coefficient. It is part of the Fluid Decomposition Toolkit of the FluCoMa project.FOOTNOTE:: This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899).::
The process will return a single multichannel buffer of STRONG::numCoeffs:: per input channel. Each frame represents a value, which is every hopSize. 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:: CLASSMETHODS::
METHOD:: process METHOD:: process
@ -111,4 +115,4 @@ Routine{
// look at the buffer: 5 coefs for left, then 5 coefs for right (the first of each is linked to the loudness) // 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) c.plot(separately:true)
:: ::

@ -1,7 +1,7 @@
TITLE:: FluidBufMelBands TITLE:: FluidBufMelBands
SUMMARY:: A Perceptually Spread Spectral Contour Descriptor on a Buffer SUMMARY:: A Perceptually Spread Spectral Contour Descriptor on a Buffer
CATEGORIES:: Libraries>FluidDecomposition CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/FluidBufMFCC RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading, Classes/FluidBufMFCC
DESCRIPTION:: DESCRIPTION::
@ -9,6 +9,10 @@ This class implements a spectral shape descriptor where the amplitude is given f
The process will return a single multichannel buffer of STRONG::numBands:: per input channel. Each frame represents a value, which is every hopSize. 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:: CLASSMETHODS::
METHOD:: process METHOD:: process
@ -109,4 +113,4 @@ Routine{
// look at the buffer: 10 bands for left, then 10 bands for right // look at the buffer: 10 bands for left, then 10 bands for right
c.plot(separately:true) c.plot(separately:true)
:: ::

@ -1,8 +1,7 @@
TITLE:: FluidBufNMF TITLE:: FluidBufNMF
SUMMARY:: Buffer-Based Non-Negative Matrix Factorisation on Spectral Frames SUMMARY:: Buffer-Based Non-Negative Matrix Factorisation on Spectral Frames
CATEGORIES:: Libraries>FluidDecomposition, UGens>Buffer CATEGORIES:: Libraries>FluidDecomposition, UGens>Buffer
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/FluidNMFMatch, Classes/FluidNMFFilter RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading, Classes/FluidNMFMatch, Classes/FluidNMFFilter
DESCRIPTION:: DESCRIPTION::
The FluidBufNMF object decomposes the spectrum of a sound into a number of components using Non-Negative Matrix Factorisation (NMF) footnote:: Lee, Daniel D., and H. Sebastian Seung. 1999. Learning the Parts of Objects by Non-Negative Matrix Factorization. Nature 401 (6755): 78891. https://doi.org/10.1038/44565. The FluidBufNMF object decomposes the spectrum of a sound into a number of components using Non-Negative Matrix Factorisation (NMF) footnote:: Lee, Daniel D., and H. Sebastian Seung. 1999. Learning the Parts of Objects by Non-Negative Matrix Factorization. Nature 401 (6755): 78891. https://doi.org/10.1038/44565.
@ -33,6 +32,10 @@ More information on possible musicianly uses of NMF are availabe in LINK::Guides
FluidBufNMF is part of the Fluid Decomposition Toolkit of the FluCoMa project. footnote:: FluidBufNMF is part of the Fluid Decomposition Toolkit of the FluCoMa project. footnote::
This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899). :: This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899). ::
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:: CLASSMETHODS::
METHOD:: process METHOD:: process
@ -389,28 +392,3 @@ e.plot;
// look at the activations (sharper 3rd component at transitions) // look at the activations (sharper 3rd component at transitions)
g.plot; g.plot;
:: ::
STRONG:: Placeholder: Run process as .kr ugen and use Poll to get progress updates, cancel with server message::
CODE::
//load some buffers
(
b = Buffer.read(s,File.realpath(FluidBufNMF.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-AaS-SynthTwoVoices-M.wav");
c = Buffer.new(s);
x = Buffer.new(s);
y = Buffer.new(s);
~fft_size = 1024;
~frame_size = 512;
~hop_size = 256;
~which_component = 3;
)
//Run this to see progress in the scope
(
~synthID;
a = {
var nmf = FluidBufNMF.kr(b, 0,-1,0,-1,c,x,0,y,0,5,100,~frame_size,~hop_size,~fft_size, doneAction: Done.freeSelf);
~synthID = nmf.synthIndex;
Poll.kr(Impulse.kr(3),nmf)
}.scope
)
//Run this to cancel processing
Server.default.sendMsg("/u_cmd", a.nodeID, ~synthID, "cancel")
::

@ -1,14 +1,16 @@
TITLE:: FluidBufNoveltySlice TITLE:: FluidBufNoveltySlice
SUMMARY:: Buffer-Based Novelty-Based Slicer SUMMARY:: Buffer-Based Novelty-Based Slicer
CATEGORIES:: Libraries>FluidDecomposition, UGens>Buffer CATEGORIES:: Libraries>FluidDecomposition, UGens>Buffer
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading
DESCRIPTION:: 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 Fluid Decomposition Toolkit of the FluCoMa project. footnote::This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899).:: 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 Fluid Decomposition Toolkit of the FluCoMa project. footnote::This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899).::
The process will return a buffer which contains indices (in sample) of estimated starting points of different slices. 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:: CLASSMETHODS::

@ -1,13 +1,17 @@
TITLE:: FluidBufOnsetSlice TITLE:: FluidBufOnsetSlice
SUMMARY:: Spectral Difference-Based Audio Buffer Slicer SUMMARY:: Spectral Difference-Based Audio Buffer Slicer
CATEGORIES:: Libraries>FluidDecomposition CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading
DESCRIPTION:: 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 Fluid Decomposition Toolkit of the FluCoMa project.footnote::This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899).:: 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 Fluid Decomposition Toolkit of the FluCoMa project.footnote::This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899).::
The process will return a buffer which contains indices (in sample) of estimated starting points of different slices. 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:: CLASSMETHODS::
METHOD:: process METHOD:: process
@ -71,7 +75,7 @@ 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 default to windowSize. 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 default to windowSize.
ARGUMENT:: action 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. 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:: RETURNS::
Nothing, as the destination buffer is declared in the function call. Nothing, as the destination buffer is declared in the function call.

@ -1,7 +1,7 @@
TITLE:: FluidBufPitch TITLE:: FluidBufPitch
SUMMARY:: A Selection of Pitch Descriptors on a Buffer SUMMARY:: A Selection of Pitch Descriptors on a Buffer
CATEGORIES:: Libraries>FluidDecomposition CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/SpecCentroid, Classes/SpecFlatness, Classes/SpecCentroid, Classes/SpecPcile RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading, Classes/SpecCentroid, Classes/SpecFlatness, Classes/SpecCentroid, Classes/SpecPcile
DESCRIPTION:: DESCRIPTION::
@ -9,6 +9,10 @@ This class implements three popular pitch descriptors, computed as frequency and
The process will return a multichannel buffer with two channels per input channel, one for pitch and one for the pitch tracking confidence. Each sample represents a value, which is every hopSize. Its sampling rate is sourceSR / hopSize. The process will return a multichannel buffer with two channels per input channel, one for pitch and one for the pitch tracking confidence. 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:: CLASSMETHODS::
METHOD:: process METHOD:: process

@ -1,7 +1,7 @@
TITLE:: FluidBufSines TITLE:: FluidBufSines
SUMMARY:: Buffer-Based Sinusoidal Modelling and Resynthesis SUMMARY:: Buffer-Based Sinusoidal Modelling and Resynthesis
CATEGORIES:: Libraries>FluidDecomposition, UGens>Buffer CATEGORIES:: Libraries>FluidDecomposition, UGens>Buffer
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading, Guides/FluidBufMultiThreading
DESCRIPTION:: DESCRIPTION::
@ -13,6 +13,10 @@ This class triggers a Sinusoidal Modelling process on buffers on the non-real-ti
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. More information on this model, and on how it links to musicianly thinking, are availabe in LINK::Guides/FluCoMa:: overview file. 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. More information on this model, and on how it links to musicianly thinking, are availabe in LINK::Guides/FluCoMa:: overview file.
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:: CLASSMETHODS::
METHOD:: process METHOD:: process

@ -1,7 +1,7 @@
TITLE:: FluidBufSpectralShape TITLE:: FluidBufSpectralShape
SUMMARY:: Seven Spectral Shape Descriptors on a Buffer SUMMARY:: Seven Spectral Shape Descriptors on a Buffer
CATEGORIES:: Libraries>FluidDecomposition CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/SpecCentroid, Classes/SpecFlatness, Classes/SpecCentroid, Classes/SpecPcile RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading, Classes/SpecCentroid, Classes/SpecFlatness, Classes/SpecCentroid, Classes/SpecPcile
DESCRIPTION:: DESCRIPTION::
@ -25,6 +25,10 @@ LIST::
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. 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:: CLASSMETHODS::
METHOD:: process METHOD:: process
@ -116,4 +120,4 @@ Routine{
// look at the buffer: 7shapes for left, then 7 shapes for right // look at the buffer: 7shapes for left, then 7 shapes for right
c.plot(separately:true) c.plot(separately:true)
:: ::

@ -1,7 +1,7 @@
TITLE:: FluidBufStats TITLE:: FluidBufStats
SUMMARY:: Computing Statistics on Buffers as Series. SUMMARY:: Computing Statistics on Buffers as Series.
CATEGORIES:: Libraries>FluidDecomposition, UGens>Buffer CATEGORIES:: Libraries>FluidDecomposition, UGens>Buffer
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading
DESCRIPTION:: DESCRIPTION::
@ -9,6 +9,10 @@ This class implements non-real-time statistical analysis on buffer channels. Typ
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::. 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:: CLASSMETHODS::
METHOD:: process METHOD:: process
@ -111,7 +115,43 @@ FluidBufStats.process(s, b, stats:c, numDerivs:1, action:{c.getn(0,c.numFrames,{
STRONG::A musical example:: STRONG::A musical example::
CODE:: CODE::
// todo: port the Max one // create some buffers
(
b = Buffer.read(s,File.realpath(FluidBufStats.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-ASWINE-ScratchySynth-M.wav");
c = Buffer.new(s);
d = Buffer.new(s);
)
//split in various chunks, collecting the indices in an array
FluidBufOnsetSlice.process(s,b, minSliceLength: 10, metric: 9, threshold: 0.4, filterSize: 7, indices: c, action:{c.loadToFloatArray(action: {|array| e = array.add(b.numFrames).addFirst(0);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:c, action:{c.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({
e.doAdjacentPairs({
arg start,end;
FluidBufStats.processBlocking(s,c,(start/512).asInt,((end-start)/512).max(2).asInt,0,1,d,1, action: {d.loadToFloatArray(action: {
arg array;
g = g.add(array[12]);
"% % %\n".postf((start/512).asInt,((end-start)/512).max(2).asInt, array[12]);
})});
});
"Done".postln;
}).play;
)
//obtain the order of indices that would sort the stats
h = g.order;
//play in loop the slice in order of pitch direction (the median of the slice's pitch variation)
(
var which = h[5];
{BufRd.ar(1, b, Phasor.ar(0,1,e[which],e[which+1],e[which]))}.play;
)
:: ::

@ -0,0 +1,65 @@
TITLE:: FluidBufThreadDemo
SUMMARY:: A Tutorial Object to Experiment with Multithreading in FluidBuf* Objects
CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, 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 Fluid Decomposition Toolkit of the FluCoMa project.footnote::This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899).:: 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
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:: 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:: doneAction
An integer representing an action to be executed when the process is finished. This can be used to free the enclosing synth, etc. See link::Classes/Done:: for more detail.
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, {|x|x.get(0,{|y|y.postln});});
// as the 'process' returns its parent UGen, we can cancel the process easily
c = FluidBufThreadDemo.process(s, b, 100000, {|x|x.get(0,{|y|y.postln});});
c.cancel
// if a simple call to the UGen is used, the progress can be monitored
{c = FluidBufThreadDemo.kr(b,10000, Done.freeSelf); Poll.kr(Impulse.kr(2),c);}.scope;
::

@ -1,14 +1,16 @@
TITLE:: FluidBufTransientSlice TITLE:: FluidBufTransientSlice
SUMMARY:: Buffer-Based Transient-Based Slicer SUMMARY:: Buffer-Based Transient-Based Slicer
CATEGORIES:: Libraries>FluidDecomposition, UGens>Buffer CATEGORIES:: Libraries>FluidDecomposition, UGens>Buffer
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Classes/FluidBufTransients RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading, Classes/FluidBufTransients
DESCRIPTION:: 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 Fluid Decomposition Toolkit of the FluCoMa project.footnote::This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899).:: 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 Fluid Decomposition Toolkit of the FluCoMa project.footnote::This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899).::
The process will return a buffer which contains indices (in sample) of estimated starting points of the different slices. 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:: CLASSMETHODS::

@ -1,8 +1,7 @@
TITLE:: FluidBufTransients TITLE:: FluidBufTransients
SUMMARY:: Buffer-Based Transient Extractor SUMMARY:: Buffer-Based Transient Extractor
CATEGORIES:: Libraries>FluidDecomposition, UGens>Buffer CATEGORIES:: Libraries>FluidDecomposition, UGens>Buffer
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition RELATED:: Guides/FluCoMa, Guides/FluidDecomposition, Guides/FluidBufMultiThreading
DESCRIPTION:: DESCRIPTION::
This class triggers a transient extractor on buffers on the non-real-time thread of the server. It implements declicking algorithm from chapter 5 of the classic Digital Audio Restoration by Godsill, Simon J., Rayner, Peter J.W. with some bespoke improvements on the detection function tracking. It is part of the Fluid Decomposition Toolkit of the FluCoMa project. footnote:: This class triggers a transient extractor on buffers on the non-real-time thread of the server. It implements declicking algorithm from chapter 5 of the classic Digital Audio Restoration by Godsill, Simon J., Rayner, Peter J.W. with some bespoke improvements on the detection function tracking. It is part of the Fluid Decomposition Toolkit of the FluCoMa project. footnote::
@ -14,6 +13,9 @@ This was made possible thanks to the FluCoMa project ( http://www.flucoma.org/
The whole process is based on the assumption that a transient is an element that is deviating from the surrounding material, as sort of click or anomaly. The algorithm then estimates what should have happened if the signal had followed its normal path, and resynthesises this estimate, removing the anomaly which is considered as the transient. More information on signal estimation, and on its musicianly usage, are availabe in LINK::Guides/FluCoMa:: overview file. The whole process is based on the assumption that a transient is an element that is deviating from the surrounding material, as sort of click or anomaly. The algorithm then estimates what should have happened if the signal had followed its normal path, and resynthesises this estimate, removing the anomaly which is considered as the transient. More information on signal estimation, and on its musicianly usage, are availabe in LINK::Guides/FluCoMa:: overview file.
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:: CLASSMETHODS::

@ -131,7 +131,7 @@ STRONG::A musical example::
CODE:: CODE::
//program that freezes mfcc spectra, then looks for matches between two frozen spectra //program that freezes mfcc spectra, then looks for matches between two frozen spectra
( (
SynthDef("MFCCJamz", {arg freq=220, source = 0, buffer, mfccBus, distBus, t_freeze0=0, t_freeze1=0, onsetsOn0=0, onsetsOn1=0; SynthDef("MFCCJamz", {arg freq=220, source = 0, buffer, mfccBus, distBus, t_freeze0=0, t_freeze1=0, onsetsOn0=0, onsetsOn1=0, restart = 1;
var sound, mfcc, mfccFreeze0, mfccFreeze1, dist0, dist1, closest, slice; var sound, mfcc, mfccFreeze0, mfccFreeze1, dist0, dist1, closest, slice;
sound = SelectX.ar(source, [ sound = SelectX.ar(source, [
@ -141,7 +141,7 @@ SynthDef("MFCCJamz", {arg freq=220, source = 0, buffer, mfccBus, distBus, t_free
Pulse.ar(freq, 0.5, 0.1), Pulse.ar(freq, 0.5, 0.1),
WhiteNoise.ar(0.1), WhiteNoise.ar(0.1),
PinkNoise.ar(0.1), PinkNoise.ar(0.1),
PlayBuf.ar(1, buffer, 1, loop:1) PlayBuf.ar(1, buffer, 1, loop:1, trigger:restart)
]); ]);
slice = FluidOnsetSlice.ar(sound); //onset detection for mfcc freeze on onset slice = FluidOnsetSlice.ar(sound); //onset detection for mfcc freeze on onset
@ -168,7 +168,7 @@ SynthDef("MFCCJamz", {arg freq=220, source = 0, buffer, mfccBus, distBus, t_free
( (
var buffers, buffer, paths, mfccBus, freezeBus, distBus, win, sliders, updateRout, winRange, currentMFCC, synth, movingGUI, trainButtons, oscFunc, closestBus; var buffers, buffer, paths, mfccBus, freezeBus, distBus, win, sliders, updateRout, winRange, currentMFCC, synth, movingGUI, trainButtons, playbackButton, oscFunc, closestBus;
winRange = 100; winRange = 100;
@ -214,7 +214,7 @@ s.waitForBoot({
.maxWidth_(150), .maxWidth_(150),
PopUpMenu().items_(paths) PopUpMenu().items_(paths)
.action_{|menu| synth.set(\buffer, buffers[menu.value])} .action_{|menu| synth.set(\buffer, buffers[menu.value])}
.maxWidth_(150); .maxWidth_(150)
]; ];
//the buttons under the two frozen mfcc displays //the buttons under the two frozen mfcc displays
@ -237,10 +237,18 @@ s.waitForBoot({
trainButtons.add(StaticText()); trainButtons.add(StaticText());
}; };
playbackButton = Button().states_([["restart", Color.black, Color.green]])
.mouseDownAction_({arg butt;
synth.set(\restart, 0.5);
})
.action_{arg butt;
synth.set(\restart, -0.5);
};
win.layout_(HLayout( win.layout_(HLayout(
VLayout(sliders[0], movingGUI[0], movingGUI[1], movingGUI[2]), VLayout(sliders[0], movingGUI[0], movingGUI[1], movingGUI[2]),
VLayout(sliders[1],HLayout(trainButtons[0],trainButtons[1]), HLayout(trainButtons[2],trainButtons[3])), VLayout(sliders[1],HLayout(trainButtons[0],trainButtons[1]), HLayout(trainButtons[2],trainButtons[3]), movingGUI[3]),
VLayout(sliders[2],HLayout(trainButtons[4],trainButtons[5]), HLayout(trainButtons[6],trainButtons[7])) VLayout(sliders[2],HLayout(trainButtons[4],trainButtons[5]), HLayout(trainButtons[6],trainButtons[7]), playbackButton)
)); ));
win.front; win.front;
win.onClose_{synth.free; oscFunc.free; updateRout.stop}; win.onClose_{synth.free; oscFunc.free; updateRout.stop};

@ -120,8 +120,84 @@ x.set(\low,20, \high, 20000)
x.free;b.free;c.free;r.stop; x.free;b.free;c.free;r.stop;
:: ::
STRONG::A musical example:: STRONG::A musical example: a perceptually spread vocoder::
CODE:: CODE::
// todo: port the Max one //load a source and define control bus for the resynthesis cluster
(
b = Bus.new(\control,0,40);
c = Buffer.read(s,File.realpath(FluidMelBands.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav");
d = Group.new;
)
//play the source and s
(
x = {
arg dry = 0.2;
var source = PlayBuf.ar(1,c,loop:1);
Out.kr(b,FluidMelBands.kr(source,maxNumBands:40));
Out.ar(0, DelayN.ar(source,delaytime:1024*SampleDur.ir,mul:dry));
}.play;
)
// set the dry playback volume
x.set(\dry, 0.5)
// create a cluster of sines tuned on each MelBand center frequency, as a sort of vocoder.
(
var lowMel = 1127.010498 * ((20/700) + 1).log;
var highMel = 1127.010498 * ((20000/700) + 1).log;
var rangeMel = highMel - lowMel;
var stepMel = rangeMel / 41;
40.do({
arg i;
var freqMel = (stepMel * (i +1)) + lowMel;
var freq = ((freqMel/ 1127.01048).exp - 1 ) * 700;
{SinOsc.ar(freq,mul:Lag.kr(In.kr(b,40)[i],512*SampleDur.ir,0.0001))}.play(d,1,addAction:\addToTail);
});
)
// free all
d.free; x.free; b.free; c.free;
/////////////////////////////////////
// instantiate a more dynamic vocoder:
// MouseX defines the bottom frequency and MouseY define the top frequency, between which the 40 bands of analysis and synthesis are perceptually equally spred
// the bus, source and group
(
b = Bus.new(\control,0,40);
c = Buffer.read(s,File.realpath(FluidMelBands.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav");
d = Group.new;
)
// the modified source
(
x = {
arg dry = 0.2;
var source = PlayBuf.ar(1,c,loop:1);
Out.kr(b,FluidMelBands.kr(source,maxNumBands:40,minFreq:MouseX.kr().exprange(20,600),maxFreq:MouseY.kr().exprange(650,20000)));
Out.ar(0, DelayN.ar(source,delaytime:1024*SampleDur.ir,mul:dry));
}.play;
)
// the modified vocoder
(
40.do({
arg i;
{
var lowMel = 1127.010498 * ((MouseX.kr().exprange(20,600)/700) + 1).log;
var highMel = 1127.010498 * ((MouseY.kr().exprange(650,20000)/700) + 1).log;
var rangeMel = highMel - lowMel;
var stepMel = rangeMel / 41;
var freqMel = (stepMel * (i +1)) + lowMel;
var freq = ((freqMel/ 1127.01048).exp - 1 ) * 700;
SinOsc.ar(freq,mul:Lag.kr(In.kr(b,40)[i],512*SampleDur.ir,0.0001))}.play(d,1,addAction:\addToTail);
});
)
// free all
d.free; x.free; b.free; c.free;
:: ::

@ -107,7 +107,7 @@ c = Buffer.new(s);
~bases = Buffer.new(s); ~bases = Buffer.new(s);
~spectralshapes = Buffer.new(s); ~spectralshapes = Buffer.new(s);
~stats = Buffer.new(s); ~stats = Buffer.new(s);
~centroids = Buffer.new(s); ~centroids = Array.new();
~trainedBases = Buffer.new(s); ~trainedBases = Buffer.new(s);
) )
@ -312,5 +312,66 @@ c.query
:: ::
STRONG::Strange Resonators:: STRONG::Strange Resonators::
CODE:: CODE::
//to be completed //load the source and declare buffers/arrays
(
b = Buffer.read(s,File.realpath(FluidNMFMatch.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-AaS-AcousticStrums-M.wav");
c = Buffer.new(s);
~bases = Buffer.new(s);
~spectralshapes = Buffer.new(s);
~stats = Buffer.new(s);
~centroids = Array.new();
)
// train only 2 seconds
(
Routine {
FluidBufNMF.process(s,b,0,88200,0,1, c, ~bases, components:8, hopSize:256, fftSize:2048);
c.query;
}.play;
)
// wait for the query to print
// find the component that has the picking sound checking the median spectral centroid
(
FluidBufSpectralShape.process(s, c, 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(56) == 5);
})
})
})
});
)
//7 shapes (track) x 8 components (tracks) x 7 stats(frames)
~stats.query
~centroids.size()
// make a player with harmonic on the out0, percussive on the out1, and 8 ctlout of nmfmatch
~splitaudio = Bus.audio(s,1);
~nmfenvs = Bus.control(s,8);
// start the player and you should hear only the pick a little
(
x = {
var sound = PlayBuf.ar(1,b,loop:1);
var harm, perc;
# harm, perc = FluidHPSS.ar(sound, maskingMode:1, harmThreshFreq1: 0.005869, harmThreshAmp1: -9.6875, harmThreshFreq2: 0.006609, harmThreshAmp2: -4.375, hopSize:256);
Out.ar(~splitaudio, harm);
Out.kr(~nmfenvs, FluidNMFMatch.kr(sound, ~bases, maxComponents:8, hopSize:256, fftSize:2048));
Out.ar(0,perc.dup)
}.play;
)
// make an array of resonators tuned on the median of the centroids
(
8.do({
arg i;
{
var audio = BPF.ar(In.ar(~splitaudio,1), ~centroids[i],0.01,LagUD.kr(In.kr(~nmfenvs,8)[i],0.001,0.01,0.1));
Out.ar(0,Pan2.ar(audio, (i / 14) - 0.25));
}.play(x,addAction: \addAfter);
});
)
:: ::

@ -0,0 +1,22 @@
# Instructions for the PureData version of the fluid.* toolbox
## How to start:
1) move the following items to their respective relevant pd search path
- externals are in /pd_objects
- help files are in /help
- audio used in the help files are in /media, which is expected to be parallel to /help
- detailed help is provided as a website in /docs
2) The PureData document 'Fluid_Decomposition_Overview.pd' shows the toolbox at a glance.
5) Parameters can be set by message (as in max with the same names) OR by option in the object box which work similarly to the [sigmund~] options.
6) Most objects working on arrays/buffers are multichannel. The toolbox uses the following convention: a named array is expected to have a name, followed by -x where x is the 'channel' number, 0-indexed. For instance, a stereo source buffer defined as 'mybuf' will expect an array named 'mybuf-0' for the left channel, and an array named 'mybuf-1' for the right channel. A utility [multiarray.pd] is used throughout the helpfiles in conjonction with the [clone] to programmatically generate such 'multichannel' buffers.
#### Enjoy!
## Known issues:
- pd is single threaded so doing buffer ops will do bad things to realtime audio.
- providing 'multichannel' arrays not enough in numbers (aka channels) will crash Pd.

@ -0,0 +1,164 @@
TITLE:: FluidBuf* Multithreading Behaviour
SUMMARY:: A tutorial on the multithreading behaviour of offline processes of the Fluid Decomposition toolbox for signal decomposition
CATEGORIES:: Libraries>FluidDecomposition
RELATED:: Guides/FluCoMa, Guides/FluidDecomposition
DESCRIPTION::
The Fluid Decomposition toolbox footnote::This toolbox was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899).:: provides an open-ended, loosely coupled set of objects to break up and analyse sound in terms of slices (segments in time), layers (superpositions in time and frequency) and objects (configurable or discoverable patterns in sound). Almost all objects have audio-rate and buffer-based versions.
These latter buffer-based processes can be very CPU intensive, and so require a some consideration of SuperCollider's underlying architecture. The FluidBuf* objects have different entry points, from transparent usage to more advanced control, to allow the creative coder to care as much as they need to. The overarching principle is to send the CPU intensive tasks to their own background thread, to avoid blocking the Server and its Non-Real Time thread, whilst providing ways to cancel the tasks and monitor their progress.
In SuperCollider, the server will delegate tasks to a non-real-time thread that are potentially too long for the real-time server. For instance, loading a soundfile to a buffer. This process is explained in LINK::Classes/Buffer:: and LINK::Guides/ClientVsServer::. For comprehensive detail see Ross Bencina's 'SuperCollider Internals' in Chapter XX of the SuperCollider book.
section:: Basic Usage
Some FluidBuf* tasks are much longer than these native tasks, so we run them in their own worker thread to avoid clogging the server's command queue, which would interfere with you being able to fill buffers whilst these processes are running.
There are two basic approaches to interacting with these objects. The first is simply to use the 'process' method. This method will block if run in a LINK::Classes/Routine::, but not otherwise. The alternative interaction is to use a FluidBuf* object as a UGen, as part of a synth. This latter approach enables you to get progress feedback for long running jobs.
For this tutorial, we will use a demonstrative class, LINK::Classes/FluidBufThreadDemo::, which does nothing but wait on its thread of execution before sending back one value the amount of time it waited via a Buffer.
This code will wait for 1000ms, and then print 1000 to the console:
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, {|x|x.get(0,{|y|y.postln});});
::
What is happening:
NUMBEREDLIST::
## The class will check the arguments' validity
## The job runs on a new thread (in this case, doing nothing but waiting for 1000 ms, then writing that number to index [0] of a destination buffer)
## It receives an acknowledgment of the job being done
## It calls the user-defined function with the destination buffer as its argument. In this case, we send it to a function get which prints the value of index 0.
::
There are more details, but this should be enough for common use cases.
subsection:: Cancelling
The 'process' method returns an instance of LINK::Classes/FluidNRTProcess::, which wraps a LINK::Classes/Synth:: running on the server. This gives us a simple interface to cancel a job:
CODE::
// define a destination buffer
b=Buffer.alloc(s,1);
//start a long process, capturing the instance of the process
c = FluidBufThreadDemo.process(s, b, 100000, {|x|x.get(0,{|y|y.postln});});
//cancel the job. Look at the Post Window
c.cancel;
::
section:: .kr Usage
The 'process' method actually wraps a temporary LINK::Classes/Synth::, which enqueues our job on the server's command FIFO, which in turn launches a worker thread to do the actual work. We can instead interact with the class as a LINK::Classes/UGen::, running in our own custom synth. This allows us to poll the object for progress reports:
CODE::
// if a simple call to the UGen is used, the progress can be monitored
{FluidBufThreadDemo.kr(b,10000, Done.freeSelf);}.scope;
//or polled within a synth
a = {FluidBufThreadDemo.kr(b,3000).poll}.play;
a.free
//or its value used to control other processes, here changing the pitch, whilst being polled to the window twice per second
{SinOsc.ar(Poll.kr(Impulse.kr(2),FluidBufThreadDemo.kr(b,3000)).exprange(110,220),0,0.1)}.play;
::
To cancel the job in this setup way, we just free the synth and the background thread will be killed.
CODE::
// load a buffer, declare a destination, and make a control bus to monitor the work
(
b = Buffer.read(s,File.realpath(FluidBufNMF.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-AaS-SynthTwoVoices-M.wav");
c = Buffer.new(s);
d = Bus.control(s,1);
)
// start a very long job
e = {Out.kr(d,FluidBufNMF.kr(b, destination:c, components:50, iterations:1000, windowSize:8192, hopSize:256))}.play
// make a dummy synth to look at the progress
f = {In.kr(d).poll}.play
// stop the monitoring
f.free
//make a slighly more useful use of the progress
f = {SinOsc.ar(In.kr(d).poll.exprange(110,880),0,0.1)}.play
//kill the process
e.free
//kill the synth
f.free
//to appreciate the multithreading, use your favourite CPU monitoring application. scsynth will be very, very high, despite the peakCPU and avgCPU being very low.
::
subsection:: Monitoring Task Completion
There are a couple of options for dealing with the end of a job. First, the FluidBuf* objects support done actions, so you can use things like LINK::Classes/FreeSelfWhenDone:: or LINK::Classes/Done::, or set a doneAction in the call to .kr (see LINK::Classes/Done:: for details). Note that the UGen's done status only becomes true in the event of successful completion, so it will not catch cancellation. However, the doneAction will run whatever, so you can rely on the synth freeing itself.
Alternatively, the synth will send a /done reply to the node, which also carries information about whether it completed normally or was cancelled. You can use LINK::Classes/OSCFunc:: to listen for this message, targetted to your nodeID.
CODE::
// define a destination buffer
b=Buffer.alloc(s,1);
//start a long job
a = {FluidBufThreadDemo.kr(b,10000,doneAction: 2).poll}.play;
// set a OSC receiver function
(
OSCFunc({|msg| //args are message symbol (/done), nodeID and replyID (which gives status)
if(msg[2]==0){"Completed Normally".postln}{"Cancelled".postln};
},"/done",argTemplate:[a.nodeID]).oneShot; //only listen to things sent for this node
)
a.free; //optionally cancel - run the job twice to see both behaviour monitored by the OSCFunc above
::
section:: Opting Out
Whilst using a worker thread makes sense for long running jobs, the overhead of creating the thread may outweigh any advantages for very small tasks. This is because a certain amount of pre- and post-task work needs to be done before doing a job, particularly copying the buffers involved to temporary memory to avoid working on scsynth's memory outside of scsynth's official threads.
For these small jobs, you can opt out of using a worker thread by calling 'processBlocking' on a Fluid Decomposition Buf* object, instead of 'process'. This will run a job directly in the server's command FIFO. If your SCIDE status bar turns yellow, then be aware that this means you are clogging the queue and should consider using a thread instead.
It is worth mentioning that there is one exception to the behaviour of the FluidBuf* objects: LINK::Classes/FluidBufCompose:: will always run directly in the command FIFO, because the overhead of setting up a job will always be greater than the amount of work this object would have to do.
We don't offer an interface to run tasks directly in the command FIFO via .kr, because under these circumstances you would get no progress updates whilst the task runs, obviating the usefulness of using a custom synth in the first place. Similarly, jobs run with processBlocking can not be cancelled.
You can compare these behaviours here. The blocking will run slightly faster than the default non-blocking,
CODE::
//Default mode worker thread:
(
Routine{
var startTime = Main.elapsedTime;
100.do{|x,i|
FluidBufThreadDemo.process(s,b,10);
};
"Threaded Processes 100 iterations in % seconds.\n".postf((Main.elapsedTime - startTime).round(0.01));
}.play;
)
//Danger zone running directly in command FIFO:
(
Routine{
var startTime = Main.elapsedTime;
100.do{|x,i|
FluidBufThreadDemo.processBlocking(s,b,10);
};
"Blocking Processes 100 iterations in % seconds.\n".postf((Main.elapsedTime - startTime).round(0.01));
}.play;
)
::

@ -3,7 +3,7 @@ SUMMARY:: An overview of the FluCoMa toolbox for signal decomposition
CATEGORIES:: Libraries>FluidDecomposition CATEGORIES:: Libraries>FluidDecomposition
DESCRIPTION:: DESCRIPTION::
The Fluid Decomposition toolbox provides an open-ended, loosely coupled set of objects to break up and analyse sound in terms of slices (segments in time), layers (superositions in time and frequency) and objects (configurable or discoverable patterns in sound). Almost all objects have audio-rate and buffer-based versions. footnote:: The Fluid Decomposition toolbox provides an open-ended, loosely coupled set of objects to break up and analyse sound in terms of slices (segments in time), layers (superpositions in time and frequency) and objects (configurable or discoverable patterns in sound). Almost all objects have audio-rate and buffer-based versions. footnote::
This toolbox was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899).:: This toolbox was made possible thanks to the FluCoMa project ( http://www.flucoma.org/ ) funded by the European Research Council ( https://erc.europa.eu/ ) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899).::
subsection:: Slices subsection:: Slices
@ -83,4 +83,8 @@ Statistics of buffers
subsection:: Utility subsection:: Utility
LINK:: Classes/FluidBufCompose:: LINK:: Classes/FluidBufCompose::
Copy, slice, stack, mix concatenate. All the things you've wanted to do with buffers... Copy, slice, stack, mix concatenate. All the things you've wanted to do with buffers...
LINK:: Classes/FluidBufThreadDemo::
A tutorial object to experiment with multithreading in FluidBuf* objects

@ -28,8 +28,8 @@ FileDialog.new({|selection|
f.postln; f.postln;
("Loading"+(i+1)+"of"+total).postln; ("Loading"+(i+1)+"of"+total).postln;
Buffer.read(s, f.asAbsolutePath,action:{arg tempbuf; FluidBufCompose.process(s,tempbuf,destination:b,destStartFrame:c[i],action:{tempbuf.free});}); Buffer.read(s, f.asAbsolutePath,action:{arg tempbuf; FluidBufCompose.process(s,tempbuf,destination:b,destStartFrame:c[i],action:{tempbuf.free});});
s.sync;
}; };
s.sync;
("loading buffers done in" + (Main.elapsedTime - t).round(0.1) + "seconds.").postln; ("loading buffers done in" + (Main.elapsedTime - t).round(0.1) + "seconds.").postln;
}.play; }.play;
}, fileMode:2); }, fileMode:2);

@ -0,0 +1,59 @@
(
b = Buffer.read(s,File.realpath(FluidBufNoveltySlice.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav");
c = Buffer.new(s);
)
(
// with basic params
Routine{
var startTime, target, tolerance, startThresh, prevThresh, curThresh, curVal, prevVal, iters, maxIters, dVal, dThresh;
startTime = Main.elapsedTime;
prevThresh = 0.1; //initial threshold (between 0.00001 and 0.99999
target = 10; //number of slices desired
tolerance = 0; // the acceptable error in the number of slices yield
maxIters = 100; //max number of iterations acceptable
//makes a first iteration
FluidBufNoveltySlice.process(s,b, indices: c, threshold:prevThresh,action:{|x|prevVal = x.numFrames});
//makes a second iteration
if ( (prevVal < target), {
curThresh = (prevThresh * 0.5).max(0.000001);
}, {
curThresh = (prevThresh * 2).min(0.999999);
});
FluidBufNoveltySlice.process(s,b, indices: c, threshold:curThresh,action:{|x|curVal = x.numFrames});
//makes further iterations until the result is achieved, or the maximum of acceptable iterations is reached
iters = 2;
while ( {
(iters < maxIters) && ((curVal - target).abs > tolerance)
}, {
iters = iters + 1;
dVal = curVal - prevVal;
dThresh = curThresh - prevThresh;
prevThresh = curThresh;
prevVal = curVal;
if ( (dVal == 0), {
//if we have not change results between the last 2 passes, make the variation of threshold bigger
curThresh = (dThresh + curThresh).min(0.999999).max(0.000001);
},{
//if we have
curThresh = (((dThresh / dVal) * (target - curVal)) + curThresh).min(0.999999).max(0.000001);
});
FluidBufNoveltySlice.process(s,b, indices: c, threshold:curThresh,action:{|x|curVal = x.numFrames});
}
);
//depending on the outcome, gives the right info back
if ( iters >= maxIters, {
// failed
"Failed to find a suitable threshold in % seconds.\n".postf((Main.elapsedTime - startTime).round(0.01));
}, {
// succeeded
"Found % as a suitable threshold for % slices in % seconds and % iterations.\n".postf(curThresh, curVal, (Main.elapsedTime - startTime).round(0.01), iters);
}
);
}.play
)

@ -0,0 +1,20 @@
cmake_minimum_required(VERSION 3.3)
get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE)
message("Configuring ${PLUGIN}")
set(FILENAME ${PLUGIN}.cpp)
add_library(
${PLUGIN}
MODULE
${FILENAME}
)
target_include_directories(
${PLUGIN} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../../include
)
target_link_libraries(
${PLUGIN} PRIVATE FLUID_DECOMPOSITION
)
include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake)

@ -0,0 +1,13 @@
// A tool from the FluCoMa project, funded by the European Research Council (ERC) under the European Unions Horizon 2020 research and innovation programme (grant agreement No 725899)
#include <clients/nrt/FluidThreadTest.hpp>
#include <FluidSCWrapper.hpp>
static InterfaceTable *ft;
PluginLoad(OfflineFluidDecompositionUGens) {
ft = inTable;
using namespace fluid::client;
makeSCWrapper<NRTThreadedThreadTest>("FluidBufThreadDemo", ft);
}
Loading…
Cancel
Save