Merge branch 'refactor/macro-purge' into log-scaling

nix
Pierre Alexandre Tremblay 5 years ago
commit 0b61aed2b9

@ -19,43 +19,60 @@
namespace fluid {
namespace client {
namespace datasetwr {
enum { kDataSet, kIDPrefix, kIDNumber, kBuffer };
constexpr std::initializer_list<index> idNumberDefaults{0, 0};
constexpr auto DataSetWrParams = defineParameters(
DataSetClientRef::makeParam("dataSet", "DataSet ID"),
StringParam("idPrefix", "ID Prefix"),
LongArrayParam("idNumber", "ID Counter Offset", idNumberDefaults),
BufferParam("buf", "Data Buffer"));
class DataSetWriterClient : public FluidBaseClient, OfflineIn, OfflineOut {
class DataSetWriterClient : public FluidBaseClient, OfflineIn, OfflineOut
{
public:
FLUID_DECLARE_PARAMS(
DataSetClientRef::makeParam("dataSet", "DataSet ID"),
StringParam("labelPrefix","Label Prefix"),
LongParam("labelOffset", "Label Counter Offset", 0),
BufferParam("buf", "Data Buffer")
);
DataSetWriterClient(ParamSetViewType& p) : mParams(p) {}
template <typename T>
Result process(FluidContext&)
{
auto dataset = get<0>().get();
if (auto datasetPtr = dataset.lock())
{
std::stringstream ss;
ss << get<1>();
index labelOffset = get<2>();
if(labelOffset >= 0) ss << labelOffset + (mCounter++);
auto buf = get<3>();
return datasetPtr->addPoint(ss.str(), buf);
}
else
return {Result::Status::kError, "No dataset"};
using ParamDescType = decltype(DataSetWrParams);
using ParamSetViewType = ParameterSetView<ParamDescType>;
std::reference_wrapper<ParamSetViewType> mParams;
void setParams(ParamSetViewType &p) { mParams = p; }
template <size_t N> auto &get() const {
return mParams.get().template get<N>();
}
static constexpr auto &getParameterDescriptors() { return DataSetWrParams; }
DataSetWriterClient(ParamSetViewType &p) : mParams(p) {}
template <typename T> Result process(FluidContext &) {
auto dataset = get<kDataSet>().get();
if (auto datasetPtr = dataset.lock()) {
std::string &idPrefix = get<kIDPrefix>();
auto &idNumberArr = get<kIDNumber>();
if (idNumberArr.size() != 2)
return {Result::Status::kError, "ID number malformed"};
if (idPrefix.size() == 0 && idNumberArr[0] == 0)
return {Result::Status::kError, "No ID supplied"};
std::string id = idPrefix;
if (idNumberArr[0] > 0)
id += std::to_string(idNumberArr[1]);
auto buf = get<kBuffer>();
return datasetPtr->setPoint(id, buf);
} else
return {Result::Status::kError, "No DataSet"};
}
private:
index mCounter{0};
};
} // namespace datasetwr
using NRTThreadedDataSetWriter =
NRTThreadingAdaptor<ClientWrapper<DataSetWriterClient>>;
NRTThreadingAdaptor<ClientWrapper<datasetwr::DataSetWriterClient>>;
} // namespace client
} // namespace fluid

@ -10,7 +10,7 @@
#include <data/FluidMeta.hpp>
#include <SC_PlugIn.hpp>
#include <scsynthsend.h>
#include <map>
#include <unordered_map>
namespace fluid {
namespace client {
@ -46,7 +46,7 @@ namespace impl {
using WeakCacheEntryPointer = std::weak_ptr<CacheEntry>; //could use weak_type in 17
public:
using Cache = std::map<index,CacheEntryPointer>;
using Cache = std::unordered_map<index,CacheEntryPointer>;
static Cache mCache;
private:
static bool isNull(WeakCacheEntryPointer const& weak) {
@ -370,20 +370,27 @@ namespace impl {
struct CommandProcess: public NRTCommand
{
CommandProcess(World* world, sc_msg_iter* args, void* replyAddr): NRTCommand{world, args, replyAddr}
CommandProcess(World* world, sc_msg_iter* args, void* replyAddr): NRTCommand{world, args, replyAddr},mParams{Client::getParameterDescriptors()}
{
auto& ar = *args;
if(auto ptr = get(NRTCommand::mID).lock())
{
ptr->mDone = false;
ptr->mParams.template setParameterValuesRT<ParamsFromOSC>(nullptr, world, ar);
mParams.template setParameterValuesRT<ParamsFromOSC>(nullptr, world, ar);
mSynchronous = static_cast<bool>(ar.geti());
} //if this fails, we'll hear about it in stage2 anyway
}
explicit CommandProcess(index id,bool synchronous):NRTCommand{id},mSynchronous(synchronous)
{}
explicit CommandProcess(index id,bool synchronous,Params* params):NRTCommand{id},mSynchronous(synchronous),
mParams{Client::getParameterDescriptors()}
{
if(params)
{
mParams = *params;
mOverwriteParams = true;
}
}
static const char* name()
@ -396,9 +403,13 @@ namespace impl {
{
if(auto ptr = get(NRTCommand::mID).lock())
{
auto& params = ptr->mParams;
if(mOverwriteParams) params = mParams;
auto& client = ptr->mClient;
// if(mOSCData)
// {
// params.template setParameterValuesRT<ParamsFromOSC>(nullptr, world, *mOSCData);
@ -489,13 +500,15 @@ namespace impl {
bool mSynchronous;
size_t mCompletionMsgSize{0};
char* mCompletionMessage{nullptr};
Params mParams;
bool mOverwriteParams{false};
};
struct CommandProcessNew: public NRTCommand
{
CommandProcessNew(World* world, sc_msg_iter* args,void* replyAddr)
: mNew{world, args, replyAddr},
mProcess{mNew.mID,false}
mProcess{mNew.mID,false,nullptr}
{
mProcess.mSynchronous = args->geti();
mProcess.mReplyAddress = mNew.mReplyAddress;
@ -741,19 +754,20 @@ namespace impl {
}
NRTTriggerUnit()
: mControlsIterator{mInBuf + ControlOffset(),ControlSize()}
: mControlsIterator{mInBuf + ControlOffset(),ControlSize()},mParams{Client::getParameterDescriptors()}
{
mID = static_cast<index>(mInBuf[0][0]);
if(mID == -1) mID = count();
auto cmd = NonRealTime::rtalloc<CommandNew>(mWorld,mID,mWorld, mControlsIterator, this);
runAsyncCommand(mWorld, cmd, nullptr, 0, nullptr);
mInst = get(mID);
set_calc_function<NRTTriggerUnit, &NRTTriggerUnit::next>();
Wrapper::getInterfaceTable()->fClearUnitOutputs(this, 1);
}
~NRTTriggerUnit()
{
if(auto ptr = get(mID).lock())
if(auto ptr = mInst.lock())
{
auto cmd = NonRealTime::rtalloc<CommandFree>(mWorld,mID);
runAsyncCommand(mWorld, cmd, nullptr, 0, nullptr);
@ -762,32 +776,39 @@ namespace impl {
void next(int)
{
index triggerInput = static_cast<index>(mInBuf[static_cast<index>(mNumInputs) - 2][0]);
mTrigger = mTrigger || triggerInput;
if(auto ptr = get(mID).lock())
{
bool trigger = (!mPreviousTrigger) && mTrigger;
mPreviousTrigger = mTrigger;
// if(auto ptr = mInst->lock())
// if(auto ptr = get(mID).lock())
// {
bool trigger = (!mPreviousTrigger) && triggerInput;//mTrigger;
mPreviousTrigger = triggerInput;
mTrigger = 0;
auto& client = ptr->mClient;
// auto& client = ptr->mClient;
if(trigger)
{
mControlsIterator.reset(1 + mInBuf); //add one for ID
auto& params = ptr->mParams;
Wrapper::setParams(this,params,mControlsIterator,true,false);
// auto& params = ptr->mParams;
Wrapper::setParams(this,mParams,mControlsIterator,true,false);
bool blocking = mInBuf[mNumInputs - 1][0] > 0;
CommandProcess* cmd = rtalloc<CommandProcess>(mWorld,mID,blocking);
CommandProcess* cmd = rtalloc<CommandProcess>(mWorld,mID,blocking,&mParams);
runAsyncCommand(mWorld,cmd, nullptr,0, nullptr);
mRunCount++;
}
else
{
mDone = ptr->mDone;
out0(0) = mDone ? 1 : static_cast<float>(client.progress());
if(auto ptr = get(mID).lock())
{
auto& client = ptr->mClient;
mDone = ptr->mDone;
out0(0) = mDone ? 1 : static_cast<float>(client.progress());
}
}
}
// }
// else printNotFound(id);
}
@ -798,6 +819,8 @@ namespace impl {
impl::FloatControlsIter mControlsIterator;
index mID;
index mRunCount{0};
WeakCacheEntryPointer mInst;
Params mParams;
};
struct NRTModelQueryUnit: SCUnit
@ -823,7 +846,8 @@ namespace impl {
: mControls{mInBuf + ControlOffset(),ControlSize()}
{
index id = static_cast<index>(in0(1));
if(auto ptr = get(id).lock())
mInst = get(id);
if(auto ptr = mInst.lock())
{
auto& client = ptr->mClient;
mDelegate.init(*this,client,mControls);
@ -835,7 +859,7 @@ namespace impl {
void next(int)
{
index id = static_cast<index>(in0(1));
if(auto ptr = get(id).lock())
if(auto ptr = mInst.lock())
{
auto& client = ptr->mClient;
auto& params = ptr->mParams;
@ -848,6 +872,7 @@ namespace impl {
Delegate mDelegate;
FloatControlsIter mControls;
index mID;
WeakCacheEntryPointer mInst;
};

@ -129,7 +129,7 @@ namespace impl{
mPrevTrig = trig;
Wrapper::setParams(&unit, params, controls);
params.constrainParameterValues();
params.constrainParameterValuesRT(nullptr);
for (index i = 0; i < client.audioChannelsIn(); ++i)
{

@ -34,8 +34,9 @@ public:
static void setup(InterfaceTable* ft, const char* name)
{
ft->fDefineUnitCmd(name, "latency", doLatency);
registerUnit<RealTime>(ft,name);
ft->fDefineUnitCmd(name, "latency", doLatency);
}
static void doLatency(Unit* unit, sc_msg_iter*)
@ -47,7 +48,7 @@ public:
std::stringstream ss;
ss << '/' << Wrapper::getName() << "_latency";
std::cout << ss.str() << std::endl;
// std::cout << ss.str() << ": " << l[0] << std::endl;
ft->fSendNodeReply(&unit->mParent->mNode, -1, ss.str().c_str(), 1, l);
}

@ -1,6 +1,6 @@
FluidBufLoudness : FluidBufProcessor{
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, trig = 1, blocking = 0|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, padding = 1, trig = 1, blocking = 0|
var maxwindowSize = windowSize.nextPowerOfTwo;
source = source.asUGenInput;
@ -8,12 +8,12 @@ FluidBufLoudness : FluidBufProcessor{
source.isNil.if {"FluidBufPitch: Invalid source buffer".throw};
features.isNil.if {"FluidBufPitch: Invalid features buffer".throw};
^FluidProxyUgen.kr(\FluidBufLoudnessTrigger, -1, source, startFrame, numFrames, startChan, numChans, features, kWeighting, truePeak, windowSize, hopSize, maxwindowSize, trig, blocking);
^FluidProxyUgen.kr(\FluidBufLoudnessTrigger, -1, source, startFrame, numFrames, startChan, numChans, features,padding, kWeighting, truePeak, windowSize, hopSize, maxwindowSize, trig, blocking);
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, freeWhenDone = true, action|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, padding = 1, freeWhenDone = true, action|
var maxwindowSize = windowSize.nextPowerOfTwo;
source = source.asUGenInput;
@ -21,16 +21,16 @@ FluidBufLoudness : FluidBufProcessor{
source.isNil.if {"FluidBufPitch: Invalid source buffer".throw};
features.isNil.if {"FluidBufPitch: Invalid features buffer".throw};
^this.new(
server, nil, [features]
).processList(
[source, startFrame, numFrames, startChan, numChans, features, kWeighting, truePeak, windowSize, hopSize, maxwindowSize,0],freeWhenDone,action
[source, startFrame, numFrames, startChan, numChans, features, padding, kWeighting, truePeak, windowSize, hopSize, maxwindowSize,0],freeWhenDone,action
);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, freeWhenDone = true, action|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, padding = 1, freeWhenDone = true, action|
var maxwindowSize = windowSize.nextPowerOfTwo;
source = source.asUGenInput;
@ -38,11 +38,11 @@ FluidBufLoudness : FluidBufProcessor{
source.isNil.if {"FluidBufPitch: Invalid source buffer".throw};
features.isNil.if {"FluidBufPitch: Invalid features buffer".throw};
^this.new(
server, nil, [features]
).processList(
[source, startFrame, numFrames, startChan, numChans, features, kWeighting, truePeak, windowSize, hopSize, maxwindowSize,1],freeWhenDone,action
[source, startFrame, numFrames, startChan, numChans, features,padding, kWeighting, truePeak, windowSize, hopSize, maxwindowSize,1],freeWhenDone,action
);
}
}

@ -1,45 +1,45 @@
FluidBufMFCC : FluidBufProcessor{
*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, trig = 1, 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, padding = 1, trig = 1, blocking = 0|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufMFCC: Invalid source buffer".throw};
features.isNil.if {"FluidBufMFCC: Invalid features buffer".throw};
^FluidProxyUgen.kr(\FluidBufMFCCTrigger, -1, source, startFrame, numFrames, startChan, numChans, features, numCoeffs, numBands, minFreq, maxFreq,numCoeffs, windowSize, hopSize, fftSize, maxFFTSize,trig, blocking);
^FluidProxyUgen.kr(\FluidBufMFCCTrigger, -1, source, startFrame, numFrames, startChan, numChans, features, padding, numCoeffs, numBands, minFreq, maxFreq,numCoeffs, windowSize, hopSize, fftSize, maxFFTSize,trig, 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,freeWhenDone=true, action |
*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, padding = 1, freeWhenDone=true, action |
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufMFCC: Invalid source buffer".throw};
features.isNil.if {"FluidBufMFCC: Invalid features buffer".throw};
^this.new(
server, nil,[features]
).processList(
[source, startFrame, numFrames, startChan, numChans, features, numCoeffs, numBands, minFreq, maxFreq, numCoeffs,windowSize, hopSize, fftSize,maxFFTSize,0],freeWhenDone,action
[source, startFrame, numFrames, startChan, numChans, features, padding, numCoeffs, numBands, minFreq, maxFreq, numCoeffs,windowSize, hopSize, fftSize, maxFFTSize,0],freeWhenDone,action
);
}
*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,freeWhenDone=true, action |
*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, padding = 1, freeWhenDone=true, action |
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufMFCC: Invalid source buffer".throw};
features.isNil.if {"FluidBufMFCC: Invalid features buffer".throw};
^this.new(
server, nil,[features]
).processList(
[source, startFrame, numFrames, startChan, numChans, features, numCoeffs, numBands, minFreq, maxFreq,numCoeffs, windowSize, hopSize, fftSize,maxFFTSize,1],freeWhenDone,action
[source, startFrame, numFrames, startChan, numChans, features, padding, numCoeffs, numBands, minFreq, maxFreq,numCoeffs, windowSize, hopSize, fftSize, maxFFTSize,1],freeWhenDone,action
);
}
}

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

@ -1,21 +1,21 @@
FluidBufPitch : FluidBufProcessor{
*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, trig = 1, blocking = 0|
*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, padding = 1, trig = 1, blocking = 0|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufPitch: Invalid source buffer".throw};
features.isNil.if {"FluidBufPitch: Invalid features buffer".throw};
^FluidProxyUgen.kr(\FluidBufPitchTrigger, -1, source, startFrame, numFrames, startChan, numChans, features, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking);
features.isNil.if {"FluidBufPitch: Invalid features buffer".throw};
^FluidProxyUgen.kr(\FluidBufPitchTrigger, -1, source, startFrame, numFrames, startChan, numChans, features, padding, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, maxFFTSize, trig, 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, freeWhenDone = true, action|
*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, padding = 1, freeWhenDone = true, action|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
@ -23,16 +23,16 @@ FluidBufPitch : FluidBufProcessor{
source.isNil.if {"FluidBufPitch: Invalid source buffer".throw};
features.isNil.if {"FluidBufPitch: Invalid features buffer".throw};
^this.new(
server, nil, [features]
).processList(
[source, startFrame, numFrames, startChan, numChans, features, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, maxFFTSize, 0], freeWhenDone, action
[source, startFrame, numFrames, startChan, numChans, features, padding, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, maxFFTSize, 0], freeWhenDone, action
);
}
*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, freeWhenDone = true, action|
*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, padding = 1, freeWhenDone = true, action|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
@ -40,11 +40,11 @@ FluidBufPitch : FluidBufProcessor{
source.isNil.if {"FluidBufPitch: Invalid source buffer".throw};
features.isNil.if {"FluidBufPitch: Invalid features buffer".throw};
^this.new(
server, nil, [features]
).processList(
[source, startFrame, numFrames, startChan, numChans, features, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, maxFFTSize, 1], freeWhenDone, action
[source, startFrame, numFrames, startChan, numChans, features, padding, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, maxFFTSize, 1], freeWhenDone, action
);
}
}

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

@ -30,17 +30,12 @@ FluidBufSelect : FluidBufProcessor {
source.isNil.if {"FluidBufSelect: Invalid source buffer".throw};
destination.isNil.if {"FluidBufSelect: Invalid destination buffer".throw};
indices = indices.asArray;
channels = channels.asArray;
indices = [indices.size] ++ indices;
indices = [indices.size] ++ indices;
channels = [channels.size] ++ channels;
indices.postln;
^this.new(server, nil, [destination]).processList([source, destination]++ indices ++ channels ++ [1], freeWhenDone, action);//NB always blocking
}

@ -4,7 +4,7 @@ FluidBufSpectralShape : FluidBufProcessor {
^\FluidBufSpecShp
}
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, trig = 1, blocking = 0|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
@ -14,11 +14,11 @@ FluidBufSpectralShape : FluidBufProcessor {
source.isNil.if {"FluidBufSpectralShape: Invalid source buffer".throw};
features.isNil.if {"FluidBufSpectralShape: Invalid features buffer".throw};
^FluidProxyUgen.kr(this.objectClassName++\Trigger, -1, source, startFrame, numFrames, startChan, numChans, features, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking);
^FluidProxyUgen.kr(this.objectClassName++\Trigger, -1, source, startFrame, numFrames, startChan, numChans, features, padding, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking);
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
@ -31,11 +31,11 @@ FluidBufSpectralShape : FluidBufProcessor {
^this.new(
server, nil, [features]
).processList(
[source, startFrame, numFrames, startChan, numChans, features, windowSize, hopSize, fftSize, maxFFTSize, 0], freeWhenDone, action
[source, startFrame, numFrames, startChan, numChans, features, padding, windowSize, hopSize, fftSize, maxFFTSize, 0], freeWhenDone, action
);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, padding = 1, freeWhenDone = true, action|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
@ -48,7 +48,7 @@ FluidBufSpectralShape : FluidBufProcessor {
^this.new(
server, nil, [features]
).processList(
[source, startFrame, numFrames, startChan, numChans, features, windowSize, hopSize, fftSize, maxFFTSize, 1], freeWhenDone, action
[source, startFrame, numFrames, startChan, numChans, features, padding, windowSize, hopSize, fftSize, maxFFTSize, 1], freeWhenDone, action
);
}
}

@ -40,7 +40,17 @@ FluidDataSet : FluidDataObject
this.prSendMsg(this.deletePointMsg(label));
}
clearMsg { ^this.prMakeMsg(\clear,id); }
setPointMsg{|label,buffer|
buffer = this.prEncodeBuffer(buffer);
^this.prMakeMsg(\setPoint,id,label.asSymbol,buffer,["/b_query",buffer.asUGenInput]);
}
setPoint{|label, buffer, action|
actions[\setPoint] = [nil,action];
this.prSendMsg(this.setPointMsg(label,buffer));
}
clearMsg { ^this.prMakeMsg(\clear,id); }
clear { |action|
actions[\clear] = [nil,action];
@ -62,4 +72,34 @@ FluidDataSet : FluidDataObject
actions[\print] = [string(FluidMessageResponse,_,_),action];
this.prSendMsg(this.printMsg);
}
toBufferMsg{|buffer, transpose = 0, labelSet|
buffer = this.prEncodeBuffer(buffer);
^this.prMakeMsg(\toBuffer, id, buffer, transpose, labelSet.asUGenInput,["/b_query",buffer.asUGenInput]);
}
toBuffer{|buffer, transpose = 0, labelSet, action|
actions[\toBuffer] = [nil,action];
this.prSendMsg(this.toBufferMsg(buffer, transpose, labelSet));
}
fromBufferMsg{|buffer, transpose = 0, labelSet|
buffer = this.prEncodeBuffer(buffer);
^this.prMakeMsg(\fromBuffer, id, buffer, transpose, labelSet.asUGenInput,["/b_query",buffer.asUGenInput]);
}
fromBuffer{|buffer, transpose = 0, labelSet, action|
actions[\fromBuffer] = [nil,action];
this.prSendMsg(this.fromBufferMsg(buffer, transpose, labelSet));
}
getIdsMsg{|labelSet|
^this.prMakeMsg(\getIds, id, labelSet.asUGenInput);
}
getIds{|labelSet, action|
actions[\getIds] = [nil,action];
this.prSendMsg(this.getIdsMsg(labelSet));
}
}

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

@ -1,70 +1,121 @@
FluidKMeans : FluidRealTimeModel {
var clusters, maxiter;
var clusters, maxiter;
*new {|server, numClusters = 4, maxIter = 100|
^super.new(server,[numClusters,maxIter])
.numClusters_(numClusters)
.maxIter_(maxIter);
.numClusters_(numClusters)
.maxIter_(maxIter);
}
numClusters_{|n| clusters = n.asInteger}
numClusters{ ^clusters }
numClusters_{|n| clusters = n.asInteger}
numClusters{ ^clusters }
maxIter_{|i| maxiter = i.asInteger}
maxIter{ ^maxiter }
maxIter_{|i| maxiter = i.asInteger}
maxIter{ ^maxiter }
prGetParams{^[this.numClusters,this.maxIter,-1,-1];}
prGetParams{^[this.numClusters,this.maxIter,-1,-1];}
fitMsg{ |dataSet| ^this.prMakeMsg(\fit,id,dataSet.id);}
fitMsg{ |dataSet| ^this.prMakeMsg(\fit,id,dataSet.id);}
fit{|dataSet, action|
actions[\fit] = [
numbers( FluidMessageResponse, _, this.numClusters ,_),
action
];
this.prSendMsg(this.fitMsg(dataSet));
}
fit{|dataSet, action|
actions[\fit] = [
numbers( FluidMessageResponse, _, this.numClusters ,_),
action
];
this.prSendMsg(this.fitMsg(dataSet));
}
fitPredictMsg{|dataSet, labelSet|
^this.prMakeMsg(\fitPredict, id, dataSet.id, labelSet.id)
}
fitPredictMsg{|dataSet, labelSet|
^this.prMakeMsg(\fitPredict, id, dataSet.id, labelSet.id)
}
fitPredict{|dataSet, labelSet,action|
actions[\fitPredict] = [
numbers(FluidMessageResponse, _, this.numClusters, _),
action
];
actions[\fitPredict] = [
numbers(FluidMessageResponse, _, this.numClusters, _),
action
];
this.prSendMsg(this.fitPredictMsg(dataSet,labelSet));
}
predictMsg{|dataSet, labelSet|
^this.prMakeMsg(\predict, id, dataSet.id, labelSet.id)
}
predictMsg{|dataSet, labelSet|
^this.prMakeMsg(\predict, id, dataSet.id, labelSet.id)
}
predict{ |dataSet, labelSet, action|
actions[\predict] = [
numbers(FluidMessageResponse, _, this.numClusters, _),
action
];
actions[\predict] = [
numbers(FluidMessageResponse, _, this.numClusters, _),
action
];
this.prSendMsg(this.predictMsg(dataSet,labelSet));
}
predictPointMsg{|buffer|
^this.prMakeMsg(\predictPoint, id, this.prEncodeBuffer(buffer))
}
predictPointMsg{|buffer|
^this.prMakeMsg(\predictPoint, id, this.prEncodeBuffer(buffer))
}
predictPoint { |buffer, action|
actions[\predictPoint] = [number(FluidMessageResponse,_,_),action];
this.prSendMsg(this.predictPointMsg(buffer))
}
kr{|trig, inputBuffer,outputBuffer|
^FluidKMeansQuery.kr(K2A.ar(trig),
this, clusters, maxiter,
this.prEncodeBuffer(inputBuffer),
this.prEncodeBuffer(outputBuffer));
fitTransformMsg{|srcDataSet, dstDataSet|
^this.prMakeMsg(\fitTransform, id, srcDataSet.id, dstDataSet.id)
}
fitTransform{|srcDataSet, dstDataSet,action|
actions[\fitTransform] = [nil,action];
this.prSendMsg(this.fitTransformMsg(srcDataSet,dstDataSet));
}
transformMsg{|srcDataSet, dstDataSet|
^this.prMakeMsg(\transform, id, srcDataSet.id, dstDataSet.id)
}
transform{ |srcDataSet, dstDataSet, action|
actions[\transform] = [nil,action];
this.prSendMsg(this.transformMsg(srcDataSet,dstDataSet));
}
transformPointMsg{ |sourceBuffer, targetBuffer|
^this.prMakeMsg(\transformPoint, id,
this.prEncodeBuffer(sourceBuffer),
this.prEncodeBuffer(targetBuffer),
["/b_query", targetBuffer.asUGenInput]);
}
transformPoint { |sourceBuffer, targetBuffer, action|
actions[\transformPoint] = [nil,{action.value(targetBuffer)}];
this.prSendMsg(this.transformPointMsg(sourceBuffer, targetBuffer));
}
getMeansMsg{|dataSet| ^this.prMakeMsg(\getMeans, id, dataSet.asUGenInput) }
getMeans{ |dataSet, action|
actions[\getMeans] = [nil, action];
this.prSendMsg(this.getMeansMsg(dataSet));
}
setMeansMsg{|dataSet| ^this.prMakeMsg(\setMeans, id, dataSet.asUGenInput) }
setMeans{ |dataSet, action|
actions[\setMeans] = [nil, action];
this.prSendMsg(this.setMeansMsg(dataSet));
}
clearMsg{ ^this.prMakeMsg(\clear, id) }
clear{ |action|
actions[\clear] = [nil, action];
this.prSendMsg(this.clearMsg);
}
kr{|trig, inputBuffer,outputBuffer|
^FluidKMeansQuery.kr(K2A.ar(trig),
this, clusters, maxiter,
this.prEncodeBuffer(inputBuffer),
this.prEncodeBuffer(outputBuffer));
}
}
FluidKMeansQuery : FluidRTQuery {}

@ -23,7 +23,7 @@
mfcc = FluidBufMFCC.kr(src, startFrame:start, numFrames:num, numChans:1, features:~mfccbuf[voice], trig:1, blocking: 1);
stats = FluidBufStats.kr(~mfccbuf[voice], stats:~statsbuf[voice], trig:Done.kr(mfcc), blocking: 1);
flatten = FluidBufFlatten.kr(~statsbuf[voice], ~flatbuf[voice], trig:Done.kr(stats), blocking: 1);
writer = FluidDataSetWr.kr(~ds, label, -1, ~flatbuf[voice], trig: Done.kr(flatten), blocking: 1)
writer = FluidDataSetWr.kr(~ds, label, nil, ~flatbuf[voice], trig: Done.kr(flatten), blocking: 1)
});
)
@ -166,4 +166,4 @@ Routine{
dur.wait;
};
}.play;
)
)

@ -23,10 +23,10 @@
var label, voice, mfcc, stats, flatten;
label = data.key;
voice = data.value[\voice];
mfcc = FluidBufMFCC.kr(src,startFrame:start,numFrames:num,numChans:1,features:~mfccbuf[voice],trig:1,blocking: 1);
mfcc = FluidBufMFCC.kr(src, startFrame:start, numFrames:num, numChans:1, features:~mfccbuf[voice], padding: 2, trig:1, blocking: 1);
stats = FluidBufStats.kr(~mfccbuf[voice], stats:~statsbuf[voice], numDerivs: 1, trig:Done.kr(mfcc), blocking: 1);
flatten = FluidBufFlatten.kr(~statsbuf[voice],~flatbuf[voice],trig:Done.kr(stats),blocking: 1);
FluidDataSetWr.kr(~ds,label, -1, ~flatbuf[voice], Done.kr(flatten),blocking: 1);
flatten = FluidBufFlatten.kr(~statsbuf[voice], ~flatbuf[voice], trig:Done.kr(stats), blocking: 1);
FluidDataSetWr.kr(~ds, label, nil, ~flatbuf[voice], Done.kr(flatten), blocking: 1);
});
// here we make another processor, this time with doing an amplitude weighing
@ -34,12 +34,12 @@
var label, voice, loud, weights, mfcc, stats, flatten;
label = data.key;
voice = data.value[\voice];
mfcc = FluidBufMFCC.kr(src,startFrame:start,numFrames:num,numChans:1,features:~mfccbuf[voice],trig:1,blocking: 1);
loud = FluidBufLoudness.kr(src,startFrame:start,numFrames:num,numChans:1,features:~loudbuf[voice],trig:Done.kr(mfcc),blocking: 1);
weights = FluidBufScale.kr(~loudbuf[voice],numChans: 1,destination: ~weightbuf[voice],inputLow: -70,inputHigh: 0, trig: Done.kr(loud),blocking: 1);
mfcc = FluidBufMFCC.kr(src, startFrame:start, numFrames:num, numChans:1, features:~mfccbuf[voice], padding: 2, trig:1, blocking: 1);
loud = FluidBufLoudness.kr(src, startFrame:start, numFrames:num, numChans:1, features:~loudbuf[voice], padding: 2, trig:Done.kr(mfcc), blocking: 1);
weights = FluidBufScale.kr(~loudbuf[voice], numChans: 1, destination: ~weightbuf[voice], inputLow: -70, inputHigh: 0, trig: Done.kr(loud), blocking: 1);
stats = FluidBufStats.kr(~mfccbuf[voice], stats:~statsbuf[voice], numDerivs: 1, weights: ~weightbuf[voice], trig:Done.kr(weights), blocking: 1);
flatten = FluidBufFlatten.kr(~statsbuf[voice],~flatbuf[voice],trig:Done.kr(stats),blocking: 1);
FluidDataSetWr.kr(~dsW,label, -1, ~flatbuf[voice], Done.kr(flatten),blocking: 1);
flatten = FluidBufFlatten.kr(~statsbuf[voice], ~flatbuf[voice], trig:Done.kr(stats), blocking: 1);
FluidDataSetWr.kr(~dsW, label, nil, ~flatbuf[voice], Done.kr(flatten), blocking: 1);
});
// and here we make a little processor for loudness if we want to poke at it
@ -47,10 +47,10 @@
var label, voice, loud, stats, flatten;
label = data.key;
voice = data.value[\voice];
loud = FluidBufLoudness.kr(src,startFrame:start,numFrames:num,numChans:1,features:~mfccbuf[voice],trig:1,blocking: 1);
loud = FluidBufLoudness.kr(src, startFrame:start, numFrames:num, numChans:1, features:~mfccbuf[voice], trig:1, padding: 2, blocking: 1);
stats = FluidBufStats.kr(~mfccbuf[voice], stats:~statsbuf[voice], numDerivs: 1, trig:Done.kr(loud), blocking: 1);
flatten = FluidBufFlatten.kr(~statsbuf[voice],~flatbuf[voice],trig:Done.kr(stats),blocking: 1);
FluidDataSetWr.kr(~dsL,label, -1, ~flatbuf[voice], Done.kr(flatten),blocking: 1);
flatten = FluidBufFlatten.kr(~statsbuf[voice], ~flatbuf[voice], trig:Done.kr(stats), blocking: 1);
FluidDataSetWr.kr(~dsL, label, nil, ~flatbuf[voice], Done.kr(flatten), blocking: 1);
});
)
@ -150,10 +150,10 @@ FluidBufCompose.process(s,~loader.buffer,a,(b-a),numChans: 1, destination: ~targ
(
{
var loud, weights, mfcc, stats, flatten, stats2, written;
mfcc = FluidBufMFCC.kr(~targetsound,features:~mfccbuf[0],trig:1);
mfcc = FluidBufMFCC.kr(~targetsound,features:~mfccbuf[0],padding: 2, trig:1);
stats = FluidBufStats.kr(~mfccbuf[0],stats:~statsbuf[0], numDerivs: 1,trig:Done.kr(mfcc));
flatten = FluidBufFlatten.kr(~statsbuf[0],~flatbuf[0],trig:Done.kr(stats));
loud = FluidBufLoudness.kr(~targetsound,features:~loudbuf[0],trig:Done.kr(flatten),blocking: 1);
loud = FluidBufLoudness.kr(~targetsound,features:~loudbuf[0],padding: 2,trig:Done.kr(flatten),blocking: 1);
weights = FluidBufScale.kr(~loudbuf[0],numChans: 1,destination: ~weightbuf[0],inputLow: -70,inputHigh: 0,trig: Done.kr(loud),blocking: 1);
stats2 = FluidBufStats.kr(~mfccbuf[0],stats:~statsbuf[0], numDerivs: 1, weights: ~weightbuf[0], trig:Done.kr(weights),blocking: 1);
written = FluidBufFlatten.kr(~statsbuf[0],~flatbuf[1],trig:Done.kr(stats2));

@ -38,19 +38,19 @@
pitchweights = FluidBufThresh.kr(~pitchbuf[voice], numChans: 1, startChan: 1, destination: ~weightPitchbuf[voice], threshold: 0.7, trig:Done.kr(pitch), blocking: 1);//pull down low conf
pitchstats = FluidBufStats.kr(~pitchbuf[voice], stats:~statsPitchbuf[voice], numDerivs: 1, weights: ~weightPitchbuf[voice], outliersCutoff: 1.5, trig:Done.kr(pitchweights), blocking: 1);
pitchflat = FluidBufFlatten.kr(~statsPitchbuf[voice],~flatPitchbuf[voice],trig:Done.kr(pitchstats),blocking: 1);
writePitch = FluidDataSetWr.kr(~pitchDS,label, -1, ~flatPitchbuf[voice], Done.kr(pitchflat),blocking: 1);
writePitch = FluidDataSetWr.kr(~pitchDS,label, nil, ~flatPitchbuf[voice], Done.kr(pitchflat),blocking: 1);
// the mfcc need loudness to weigh, so let's start with that
loud = FluidBufLoudness.kr(src,startFrame:start, numFrames:num, numChans:1, features:~loudbuf[voice], trig:Done.kr(writePitch), blocking: 1);//here trig was 1
//we can now flatten and write Loudness in its own trigger tree
statsLoud = FluidBufStats.kr(~loudbuf[voice], stats:~statsLoudbuf[voice], numDerivs: 1, trig:Done.kr(loud), blocking: 1);
flattenLoud = FluidBufFlatten.kr(~statsLoudbuf[voice],~flatLoudbuf[voice],trig:Done.kr(statsLoud),blocking: 1);
writeLoud = FluidDataSetWr.kr(~loudDS,label, -1, ~flatLoudbuf[voice], Done.kr(flattenLoud),blocking: 1);
writeLoud = FluidDataSetWr.kr(~loudDS,label, nil, ~flatLoudbuf[voice], Done.kr(flattenLoud),blocking: 1);
//we can resume from the loud computation trigger
mfcc = FluidBufMFCC.kr(src,startFrame:start,numFrames:num,numChans:1,features:~mfccbuf[voice],trig:Done.kr(writeLoud),blocking: 1);//here trig was loud
mfccweights = FluidBufScale.kr(~loudbuf[voice],numChans: 1,destination: ~weightMFCCbuf[voice],inputLow: -70,inputHigh: 0, trig: Done.kr(mfcc), blocking: 1);
mfccstats = FluidBufStats.kr(~mfccbuf[voice], stats:~statsMFCCbuf[voice], startChan: 1, numDerivs: 1, weights: ~weightMFCCbuf[voice], trig:Done.kr(mfccweights), blocking: 1);//remove mfcc0 and weigh by loudness instead
mfccflat = FluidBufFlatten.kr(~statsMFCCbuf[voice],~flatMFCCbuf[voice],trig:Done.kr(mfccstats),blocking: 1);
FluidDataSetWr.kr(~mfccDS,label, -1, ~flatMFCCbuf[voice], Done.kr(mfccflat),blocking: 1);
FluidDataSetWr.kr(~mfccDS,label, nil, ~flatMFCCbuf[voice], Done.kr(mfccflat),blocking: 1);
});
)

@ -38,11 +38,11 @@ a = Slider(w, Rect(10, 20, 330, 20))
features = FluidBufMFCC.kr(src,startFrame:start,numFrames:num,numChans:1, numCoeffs: 20, features:~featuresbuf[voice],trig:1,blocking: 1);
stats = FluidBufStats.kr(~featuresbuf[voice],stats:~statsbuf[voice],trig:Done.kr(features),blocking: 1);
flatten = FluidBufFlatten.kr(~statsbuf[voice],~flatbuf[voice],trig:Done.kr(stats),blocking: 1);
writer = FluidDataSetWr.kr(~slicesMFCC,label, -1, ~flatbuf[voice], Done.kr(flatten),blocking: 1);
writer = FluidDataSetWr.kr(~slicesMFCC,label, nil, ~flatbuf[voice], Done.kr(flatten),blocking: 1);
features = FluidBufSpectralShape.kr(src,startFrame:start,numFrames:num,numChans:1, features:~featuresbuf[voice],trig:Done.kr(writer),blocking: 1);
stats = FluidBufStats.kr(~featuresbuf[voice],stats:~statsbuf[voice],trig:Done.kr(features),blocking: 1);
flatten = FluidBufFlatten.kr(~statsbuf[voice],~flatbuf[voice],trig:Done.kr(stats),blocking: 1);
writer = FluidDataSetWr.kr(~slicesShapes,label, -1, ~flatbuf[voice], Done.kr(flatten),blocking: 1);
writer = FluidDataSetWr.kr(~slicesShapes,label, nil, ~flatbuf[voice], Done.kr(flatten),blocking: 1);
});
)

@ -31,7 +31,7 @@ FluidBufMelBands.process(s,~audio, features: ~melfeatures,action: {\done.postln;
40.do{|i|
bufWr = BufWr.kr(rd[i], buf, DC.kr(i));
};
dsWr = FluidDataSetWr.kr(~raw, buf: buf, trig: Done.kr(stats));
dsWr = FluidDataSetWr.kr(~raw, buf: buf, idNumber: count, trig: Done.kr(stats));
LocalOut.kr( Done.kr(dsWr));
FreeSelf.kr(count - 99);
Poll.kr(trig,(100-count));

@ -47,6 +47,15 @@ code::
//didactic - the mouse X axis interpolates between the two sinewaves
{FluidAudioTransport.ar(SinOsc.ar(220,mul: 0.1),SinOsc.ar(440,mul: 0.02),MouseX.kr())}.play;
//notice how the interpolation quantizes to the FFT bins. Like most spectral processes, it benefits from oversampling the fft... at the cost of CPU power, obviously.
{FluidAudioTransport.ar(SinOsc.ar(220,mul: 0.1),SinOsc.ar(440,mul: 0.02),MouseX.kr(),fftSize: 8192)}.play;
// when the signal is steady, larger hopSize can be accommodated to save back on the CPU
{FluidAudioTransport.ar(SinOsc.ar(220,mul: 0.1),SinOsc.ar(440,mul: 0.02),MouseX.kr(),windowSize: 8192)}.play; // here we get a default hop of half the window so 8 times less than above.
//if you CPU can cope, try this setting, almost smooth, but attacks would smear (the Y axis mixes some in to hear the effect)
{var attacks = Impulse.ar(1,mul: MouseY.kr(-40,10).dbamp); FluidAudioTransport.ar(SinOsc.ar(220,mul: 0.1,add: attacks),SinOsc.ar(440,mul: 0.02,add: attacks),MouseX.kr(),windowSize: 16000)}.play;
//richer with complex spectra
//load 2 files
(

@ -94,6 +94,8 @@ b.play
c.play
d.play
// note that the process is quantized by the spectral bins. For an example of the pros and cons of these settings on this given process, please see the real-time FluidAudioTransport helpfile.
// more interesting sources: two cardboard bowing gestures
(
b = Buffer.read(s,File.realpath(FluidBufAudioTransport.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Green-Box641.wav");

@ -51,6 +51,9 @@ ARGUMENT:: windowSize
ARGUMENT:: hopSize
How much the buffered window moves forward, in samples. By default 512 to be similar with all other FluCoMa objects, the EBU specifies other values as per the examples below.
ARGUMENT:: padding
Controls the zero-padding added to either end of the source buffer or segment. Possible values are 0 (no padding), 1 (default, half the window size), or 2 (window size - hop size). Padding ensures that all input samples are completely analysed: with no padding, the first analysis window starts at time 0, and the samples at either end will be tapered by the STFT windowing function. Mode 1 has the effect of centering the first sample in the analysis window and ensuring that the very start and end of the segment are accounted for in the analysis. Mode 2 can be useful when the overlap factor (window size / hop size) is greater than 2, to ensure that the input samples at either end of the segment are covered by the same number of analysis frames as the rest of the analysed material.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true

@ -59,8 +59,11 @@ ARGUMENT:: hopSize
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: padding
Controls the zero-padding added to either end of the source buffer or segment. Possible values are 0 (no padding), 1 (default, half the window size), or 2 (window size - hop size). Padding ensures that all input samples are completely analysed: with no padding, the first analysis window starts at time 0, and the samples at either end will be tapered by the STFT windowing function. Mode 1 has the effect of centering the first sample in the analysis window and ensuring that the very start and end of the segment are accounted for in the analysis. Mode 2 can be useful when the overlap factor (window size / hop size) is greater than 2, to ensure that the input samples at either end of the segment are covered by the same number of analysis frames as the rest of the analysed material.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [features] as an argument.

@ -60,6 +60,9 @@ ARGUMENT:: hopSize
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: padding
Controls the zero-padding added to either end of the source buffer or segment. Possible values are 0 (no padding), 1 (default, half the window size), or 2 (window size - hop size). Padding ensures that all input samples are completely analysed: with no padding, the first analysis window starts at time 0, and the samples at either end will be tapered by the STFT windowing function. Mode 1 has the effect of centering the first sample in the analysis window and ensuring that the very start and end of the segment are accounted for in the analysis. Mode 2 can be useful when the overlap factor (window size / hop size) is greater than 2, to ensure that the input samples at either end of the segment are covered by the same number of analysis frames as the rest of the analysed material.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true

@ -65,8 +65,11 @@ ARGUMENT:: hopSize
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: padding
Controls the zero-padding added to either end of the source buffer or segment. Possible values are 0 (no padding), 1 (default, half the window size), or 2 (window size - hop size). Padding ensures that all input samples are completely analysed: with no padding, the first analysis window starts at time 0, and the samples at either end will be tapered by the STFT windowing function. Mode 1 has the effect of centering the first sample in the analysis window and ensuring that the very start and end of the segment are accounted for in the analysis. Mode 2 can be useful when the overlap factor (window size / hop size) is greater than 2, to ensure that the input samples at either end of the segment are covered by the same number of analysis frames as the rest of the analysed material.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [features] as an argument.
@ -164,8 +167,8 @@ d.do({
e.postln;
//granulate only the frames that are in our buffer
// We need to convert our indices to frame start. Their position was (index * hopSize) - (windowSize) in FluidBufPitch
f = e.collect({arg i; (i * 512) - 1024});
// We need to convert our indices to frame start. Their position was (index * hopSize) - (hopSize) in FluidBufPitch
f = e.collect({arg i; (i * 512) - 512});
// define a basic grain synth
(

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

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

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

@ -64,8 +64,11 @@ ARGUMENT:: hopSize
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: padding
Controls the zero-padding added to either end of the source buffer or segment. Possible values are 0 (no padding), 1 (default, half the window size), or 2 (window size - hop size). Padding ensures that all input samples are completely analysed: with no padding, the first analysis window starts at time 0, and the samples at either end will be tapered by the STFT windowing function. Mode 1 has the effect of centering the first sample in the analysis window and ensuring that the very start and end of the segment are accounted for in the analysis. Mode 2 can be useful when the overlap factor (window size / hop size) is greater than 2, to ensure that the input samples at either end of the segment are covered by the same number of analysis frames as the rest of the analysed material.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [features] as an argument.

@ -129,43 +129,66 @@ STRONG::A musical example::
CODE::
// create some buffers
(
b = Buffer.read(s,File.realpath(FluidBufStats.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-ASWINE-ScratchySynth-M.wav");
// a simple random sliding bell synth
b = {
var trig = Impulse.ar(1.5);
SinOsc.ar(
Lag.ar(TRand.ar(trig: trig),
TRand.ar(0.5, trig: trig)).exprange(333,666),
mul: Decay.ar(
trig * TRand.ar(0.1,10,trig),
TRand.ar(0.5,1.1,trig)
)
).atan * 0.1;
}.asBuffer(20);
c = Buffer.new(s);
d = Buffer.new(s);
i = Buffer.new(s);
)
//play the source
b.play;
//split in various chunks, collecting the indices in an array
FluidBufOnsetSlice.process(s,b, 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;})});
FluidBufOnsetSlice.process(s,b, threshold: 0.01, indices: c, action:{c.loadToFloatArray(action: {|array| e = array.add(b.numFrames);e.size.postln;e.postln;})});
//describe the whole input too, here using pitch, and collecting the values in an array, dismissing the (interleaved) confidence.
FluidBufPitch.process(s,b,features:c, action:{c.loadToFloatArray(action: {|array| f = array.unlace(2)[0]; f.postln;})});
FluidBufPitch.process(s,b,features:d, windowSize: 4096, hopSize: 512, padding:2, action:{d.loadToFloatArray(action: {|array| f = array.unlace(2)[0]; f.postln;})});
// iterate through each slice, taking the median of the first derivative of the pitch of each
(
g= Array.new;
Routine({
var nb = e.size;
e.doAdjacentPairs({
arg start,end;
FluidBufStats.processBlocking(s,c,(start/512).asInteger,((end-start)/512).max(2).asInteger,0,1,d,1,
action: {d.loadToFloatArray(action: {
arg array;
g = g.add(array[12]);
"% % %\n".postf((start/512).asInteger,((end-start)/512 ).max(2).asInteger, array[12]);})
}
).wait;
});
"Done".postln;
FluidBufStats.processBlocking(s,d,(start/512).asInteger,((end-start)/512).asInteger + 3,0,1,i,1, action: {
i.loadToFloatArray( action: {
arg array;
g = g.add(array[12]);
"% % %\n".postf((start/512).asInteger,((end-start)/512).asInteger + 3, array[12]);//adding some of the overlap but not more to not capture too much of the next attack
nb = nb - 1;
if (nb == 1, {"Done".postln;});//check if we've done all the pairs
})
}).wait;
});
}).play;
)
//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) - mouse on the left should be descending, in the middle should be more stable, and it should be ascending on the right.
//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;
Buffer.sendCollection(s,g.order,action: {|x| {
var which = BufRd.kr(1, x, MouseX.kr(0, BufFrames.kr(x) - 1), 0, 1);
BufRd.ar(1, b,
Phasor.ar(0,1,
BufRd.kr(1,c,which,0,1),
BufRd.kr(1,c,which + 1,0,1),
BufRd.kr(1,c,which,0,1)));
}.play;
};)
)
::

@ -41,10 +41,52 @@ Retrieve a point from the data set into a link::Classes/Buffer::. Will report an
METHOD:: deletePoint
Remove a point from the data set. Will report an error if the label doesn't exist.
METHOD:: setPoint
Set the point: if the label exists, this method behaves like updatePoint; if the label doesn't exist, behaves like addPoint.
METHOD:: clear
Empty the data set.
METHOD:: toBuffer
Dump the content of the dataset to a link::Classes/Buffer::, with optional transposition, and a map of frames/channels to the original IDs as a link::Classes/FluidLabelSet::.
ARGUMENT:: buffer
The buffer to write to. It will be resized.
ARGUMENT:: transpose
If 0, each dataset point becomes a buffer frame, and each dataset dimension becomes a buffer channel. If 1, points become channels, and dimensions become frames.
ARGUMENT:: labelSet
The link::Classes/FluidLabelSet:: in which to dump the point's IDs associated with their reference frame number (or channel number if transposed).
ARGUMENT:: action
A function to run when the dump is done.
METHOD:: fromBuffer
Import to the dataset the content of a link::Classes/Buffer::, with optional transposition, and a map of frames/channels to the original IDs as a link::Classes/FluidLabelSet::.
ARGUMENT:: buffer
The buffer to read from. The dataset will be resized.
ARGUMENT:: transpose
If 0, each buffer frame becomes a dataset point, and each buffer channel becomes a dataset dimension. If 1, channels become points, and frames become dimensions.
ARGUMENT:: labelSet
The link::Classes/FluidLabelSet:: from which to retrieve the point's IDs associated with their reference frame number (or channel number if transposed).
ARGUMENT:: action
A function to run when the import is done.
METHOD:: getIds
Export to the dataset IDs to a link::Classes/FluidLabelSet::.
ARGUMENT:: labelSet
The link::Classes/FluidLabelSet:: to export to. Its content will be replaced.
ARGUMENT:: action
A function to run when the export is done.
METHOD:: merge
Merge sourceDataSet in the current DataSet. It will update the value of points with the same label if overwrite is set to 1. To add columns instead, see the 'transformJoin' method of link::Classes/FluidDataSetQuery::
@ -73,13 +115,13 @@ fork{
~point = Buffer.alloc(s,1,1);
s.sync;
10.do{|i|
~point.set(0,i);
~ds.addPoint(i.asString,~point,{("addPoint"+i).postln});
s.sync;
};
~ds.dump;
s.sync;
~ds.free;
~point.set(0,i);
~ds.addPoint(i.asString,~point,{("addPoint"+i).postln});
s.sync;
};
~ds.dump;
s.sync;
~ds.free;
};
)
@ -89,9 +131,9 @@ d = Dictionary.new;
d.add(\cols -> 1);
d.add(\data -> Dictionary.newFrom(10.collect{|i|[i.asString, [i.asFloat]]}.flatten));
fork{
~ds = FluidDataSet.new(s);
~ds.load(d); s.sync;
~ds.dump; s.sync; ~ds.free;
~ds = FluidDataSet.new(s);
~ds.load(d); s.sync;
~ds.dump; s.sync; ~ds.free;
}
)
@ -99,16 +141,60 @@ fork{
(
~ds = FluidDataSet.new(s);
{
var trig = Impulse.kr(20);
var count = PulseCount.kr(trig) - 1;
var buf = LocalBuf(1);
BufWr.kr(count, buf);
FluidDataSetWr.kr(~ds.asUGenInput, buf: buf, trig: trig);
FreeSelf.kr(count - 8);
var trig = Impulse.kr(20);
var count = PulseCount.kr(trig) - 1;
var buf = LocalBuf(1);
BufWr.kr(count, buf);
FluidDataSetWr.kr(~ds.asUGenInput, idNumber: count, buf: buf, trig: trig);
FreeSelf.kr(count - 8);
}.play.onFree{~ds.dump{|o| o.postln;~ds.free}}
)
::
STRONG:: Buffer Interface::
As the content of the dataset has a similar structure to buffers, namely arrays of floats in parallel, it is possible to transfer the content between the two. Careful consideration of the rotation of the buffer, as well as the relation of points to channel numbers, are needed.
code::
(
//Make a dummy data set
d = FluidDataSet(s);
~data = Dictionary.with(*Array.iota(20).reshape(4,5).collect{|a,i| ("row"++i)->a});
~dsdata = Dictionary.newFrom([\cols,5,\data,~data]);
d.load(~dsdata);
d.print;
)
//convert to separate buffer and labelset
b = Buffer(s);
l = FluidLabelSet(s);
d.toBuffer(b,0,l);
//check the result: by default, dataset points become frames, with their associated data columns as channels
b.query
b.getn(0,20,{|x|x.postln})
l.print
//you can also transpose your query, where dataset points are each a buffer channel, and each data column becomes a buffer frame
d.toBuffer(b,1,l);
b.query
b.getn(0,20,{|x|x.postln})
//note that the IDs are still one per item, as columns are unamed in datasets
l.print
//Convert back to DS again
e = FluidDataSet(s);
//Let's use the transposed data we just got
e.print;
e.fromBuffer(b,1,l);
e.print;
//if we didn't transpose, we would get an error as the labelset is mismatched with the number of items
e.clear
e.print
e.fromBuffer(b,0,l)
::
STRONG:: Merging Datasets::
code::
//this is how to add items between 2 datasets.

@ -4,17 +4,17 @@ categories:: FluidManipulation
related:: Classes/FLuidDataSet
DESCRIPTION::
A UGen that adds labelled points to a link::Classes/FluidDataSet::
A UGen that adds labelled points to a link::Classes/FluidDataSet:: Internally, this calls code::setPoint::, so IDs that already exist will be overwritten, and new IDs will be added. The actual work is done on the server's command queue, rather than the real-thread.
By default the object generates a numerical index that gets used for the point labels. This index starts counting from link::#offset#labelOffset:: and increments each time the Ugen is retriggered with a zero to non-zero transition. The label is then concatenated with the code::labelPrefix:: symbol, which is fixed at instantiation. In this way, one can make custom, incrementing labels, e.g.
By default the object takes a control input (code::idNumber::) as a numerical index that gets used for the point labels. This index is used to write each time the Ugen is re-triggered with a zero to non-zero transition. The label is then concatenated with the code::idPrefix:: symbol, which is fixed at instantiation. In this way, one can make custom, incrementing labels, e.g.
code::
FluidDataSetWr.kr(~somedataset,"my_data",0,~somebuffer,trig)
FluidDataSetWr.kr(~somedataset,"my_data",PulseCount.kr(trig),~somebuffer,trig)
::
would add points like code::my_data0, mydata1, mydata2...:: if successively retriggered.
Alternatively, for one shot use you may not want a numerical suffix at all. Setting code::indexLabel:: < 0 will bypass this and use only the labelPrefix. As such, any retriggering will result in a complaint from the link::Classes/FluidDataSet:: that the requested label is already present.
Alternatively, for one shot use you may not want a numerical suffix at all. Setting code::idNumber:: to code:: nil:: will bypass this and use only the code::idPrefix:: string.
CLASSMETHODS::
@ -26,12 +26,12 @@ The equivalent of calling link::Classes/FluidDataSet#-addPoint::, but within a l
ARGUMENT:: dataset
An instance of link::Classes/FluidDataSet:: or an instance's name.
ARGUMENT:: labelPrefix
A string or symbol with a prefix for generated labels
ARGUMENT:: idPrefix
A string or symbol with a prefix for generated labels.
ARGUMENT:: labelOffset
ARGUMENT:: idNumber
ANCHOR::offset::
An integer with the offset to start labeling from. If the UGen is run in a server-side loop (i.e. repeatedly retriggered), the generated labels will count upwards from this offset. If < 0, then no numerical index will be applied to the generated label (i.e. only the labelPrefix is used).
An integer with the offset to start labeling from. If the UGen is run in a server-side loop (i.e. repeatedly re-triggered), the generated labels will count upwards from this offset. If nil, then no numerical index will be applied to the generated label (i.e. only the labelPrefix is used).
ARGUMENT:: buf
The link::Classes/Buffer:: containing the data point.
@ -40,21 +40,110 @@ ARGUMENT:: trig
A kr trigger signal
ARGUMENT:: blocking
If 0 then the job will run in its own thread (not reccomended for this object)
EXAMPLES::
code::
s.reboot;
(
~ds = FluidDataSet(s);
)
// write a single point, no counting
(
{
var b = LocalBuf.newFrom([0,1,2,3]);
FreeSelfWhenDone.kr(FluidDataSetWr.kr(~ds,"help_data_point",buf:b));
FreeSelfWhenDone.kr(FluidDataSetWr.kr(~ds,"help_data_point", idNumber: nil, buf:b));
}.play(s);
)
~ds.print;
~ds.clear
//Write a 100 points quite fast with server-side triggering
(
~ds.clear;
OSCFunc({
"FluidDataSetWr help: all points written".postln;
~ds.print
},'/datasetwrdone').oneShot;
{ |n|
var b = LocalBuf.newFrom([0,1,2,3]);
var trig = Impulse.kr(ControlRate.ir / 8);
var idx = Stepper.kr(trig,min:-1, max:n); //we need to start at -1 to catch the first increment
4.collect{|i| BufWr.kr([(4 * idx) + i],b,i)};
FluidDataSetWr.kr(~ds,idNumber:idx,buf:b,trig:trig);
SendReply.kr(idx >= (n-1), '/datasetwrdone');
FreeSelf.kr(idx >= (n-1));
}.play(s,args:[n:100]);
)
~ds.print;
~ds.clear
//Again, but as fast as possible using a feedback of the trigger we are given when the writing is done
(
~ds.clear;
OSCFunc({
"FluidDataSetWr help: all points written".postln;
~ds.print
},'/datasetwrdone').oneShot;
{ |n|
var b = LocalBuf.newFrom([0,1,2,3]);
var trig = LocalIn.kr(1,1);
var idx = Stepper.kr(trig,min:-1, max:n);
var wr = FluidDataSetWr.kr(~ds,idNumber:idx,buf:b,trig:trig);
4.collect{|i| BufWr.kr([(4 * idx) + i],b,i)};
LocalOut.kr(Done.kr(wr));
SendReply.kr(idx >= (n-1), '/datasetwrdone');
FreeSelf.kr(idx >= (n-1));
}.play(s,args:[n:100]);
)
~ds.print;
~ds.clear
// incremental buffer writing - sky is the limit
// start the entry maker, trigging twice a second
(
{
var buf = LocalBuf.newFrom([0,1,2,3]);
var noise = 4.collect{WhiteNoise.kr()};
var trig = Impulse.kr(2);
var count = PulseCount.kr(trig);
4.do{|i|
BufWr.kr(noise[i], buf, DC.kr(i));
};
FluidDataSetWr.kr(~ds, idNumber: count, trig: trig, buf:buf);
}.play(s);
)
//print a few times
~ds.print;
//clear before flushing the writing synth
~ds.clear
~ds.print;
// circular writing
(
{
var buf = LocalBuf.newFrom([0,1,2,3]);
var noise = 4.collect{WhiteNoise.kr()};
var trig = Impulse.kr(2);
var count = Stepper.kr(trig, min: 0, max: 9, resetval: -1); //0 to 9, starting at -1 to catch the first entry
4.do{|i|
BufWr.kr(noise[i], buf, DC.kr(i));
};
FluidDataSetWr.kr(~ds, idNumber: count, trig: trig, buf:buf);
}.play(s);
)
~ds.print;
~ds.clear
::

@ -25,18 +25,18 @@ INSTANCEMETHODS::
PRIVATE::k
METHOD:: fit
Identify code::k:: clusters in a link::Classes/FluidDataSet::
Identify code::numClusters:: clusters in a link::Classes/FluidDataSet::. It will optimise until no improvement is possible, or up to code::maxIter::, whichever comes first. Subsequent calls will continue training from the stopping point with the same conditions.
ARGUMENT:: dataSet
A link::Classes/FluidDataSet:: of data points.
ARGUMENT:: action
A function to run when fitting is complete, taking as its argument an array with the number of data points for each cluster.
METHOD:: predict
Given a trained object, return the cluster ID for each data point in a DataSet to a label set.
Given a trained object, return the cluster ID for each data point in a link::Classes/FluidDataSet:: to a link::Classes/FluidLabelSet::.
ARGUMENT:: dataSet
a link::Classes/FluidDataSet:: containing the data to predict.
A link::Classes/FluidDataSet:: containing the data to predict.
ARGUMENT:: labelSet
a link::Classes/FluidLabelSet:: to retrieve the predicted clusters.
A link::Classes/FluidLabelSet:: to retrieve the predicted clusters.
ARGUMENT:: action
A function to run when the server responds.
@ -52,19 +52,55 @@ A function to run when the server responds
METHOD:: predictPoint
Given a trained object, return the cluster ID for a data point in a link::Classes/Buffer::
ARGUMENT:: buffer
a link::Classes/Buffer:: containing a data point.
A link::Classes/Buffer:: containing a data point.
ARGUMENT:: action
A function to run when the server responds, taking the ID of the cluster as its argument.
METHOD:: predict
Report cluster assignments for previously unseen data.
METHOD:: transform
Given a trained object, return for each item of a provided link::Classes/FluidDataSet:: its distance to each cluster as an array, often reffered to as the cluster-distance space.
ARGUMENT:: srcDataSet
A link::Classes/FluidDataSet:: of data points to transform.
ARGUMENT:: dstDataSet
A link::Classes/FluidDataSet:: to contain the new cluster-distance space.
ARGUMENT:: action
A function to run when complete.
METHOD:: fitTransform
Run link::Classes/FluidKMeans#*fit:: and link::Classes/FluidKMeans#*transform:: in a single pass: i.e. train the model on the incoming link::Classes/FluidDataSet:: and then return its cluster-distance space in the destination link::Classes/FluidDataSet::
ARGUMENT:: srcDataSet
A link::Classes/FluidDataSet:: containing the data to fit and transform.
ARGUMENT:: dstDataSet
A link::Classes/FluidDataSet:: to contain the new cluster-distance space.
ARGUMENT:: action
A function to run when complete.
METHOD:: transformPoint
Given a trained object, return the distance of the provided point to each cluster. Both points are handled as link::Classes/Buffer::
ARGUMENT:: sourceBuffer
A link::Classes/Buffer:: containing a data point to query.
ARGUMENT:: targetBuffer
A link::Classes/Buffer:: containing a the distance of the source point to each cluster.
ARGUMENT:: action
A function to run when complete.
METHOD:: getMeans
Given a trained object, retrieve the means (centroids) of each cluster as a link::Classes/FluidDataSet::
ARGUMENT:: dataSet
A link::Classes/FluidDataSet:: of data points.
ARGUMENT:: labelSet
A link::Classes/FluidLabelSet:: to contain assignments.
A link::Classes/FluidDataSet:: of clusers with a mean per column.
ARGUMENT:: action
A function to run when complete, taking an array of the counts for each category as its argument.
A function to run when complete.
METHOD:: setMeans
Overwrites the means (centroids) of each cluster, and declare the object trained.
ARGUMENT:: dataSet
A link::Classes/FluidDataSet:: of clusers with a mean per column.
ARGUMENT:: action
A function to run when complete.
METHOD:: clear
Reset the object status to not fitted and untrained.
ARGUMENT:: action
A function to run when complete.
EXAMPLES::
code::
@ -147,6 +183,47 @@ w.front;
~kmeans.predictPoint(~inbuf,{|x|x.postln;});
::
subsection:: Accessing the means
We can get and set the means for each cluster, their centroid.
code::
// with the dataset and kmeans generated and trained in the code above
~centroids = FluidDataSet(s);
~kmeans.getMeans(~centroids, {~centroids.print});
// We can also set them to arbitrary values to seed the process
~centroids.load(Dictionary.newFrom([\cols, 2, \data, Dictionary.newFrom([\0, [0.5,0.5], \1, [-0.5,0.5], \2, [0.5,-0.5], \3, [-0.5,-0.5]])]));
~centroids.print
~kmeans.setMeans(~centroids, {~kmeans.predict(~dataSet,~clusters,{~clusters.dump{|x|var count = 0.dup(4); x["data"].keysValuesDo{|k,v|count[v[0].asInteger] = count[v[0].asInteger] + 1;};count.postln}})});
// We can further fit from the seeded means
~kmeans.fit(~dataSet)
// then retreive the improved means
~kmeans.getMeans(~centroids, {~centroids.print});
//subtle in this case but still.. each quadrant is where we seeded it.
::
subsection:: Cluster-distance Space
We can get the euclidian distance of a given point to each cluster. This is often referred to as the cluster-distance space as it creates new dimensions for each given point, one distance per cluster.
code::
// with the dataset and kmeans generated and trained in the code above
b = Buffer.sendCollection(s,[0.5,0.5])
c = Buffer(s)
// get the distance of our given point (b) to each cluster, thus giving us 4 dimensions in our cluster-distance space
~kmeans.transformPoint(b,c,{|x|x.query;x.getn(0,x.numFrames,{|y|y.postln})})
// we can also transform a full dataset
~srcDS = FluidDataSet(s)
~cdspace = FluidDataSet(s)
// make a new dataset with 4 points
~srcDS.load(Dictionary.newFrom([\cols, 2, \data, Dictionary.newFrom([\pp, [0.5,0.5], \np, [-0.5,0.5], \pn, [0.5,-0.5], \nn, [-0.5,-0.5]])]));
~kmeans.transform(~srcDS, ~cdspace, {~cdspace.print})
::
subsection:: Queries in a Synth
This is the equivalent of predictPoint, but wholly on the server

@ -89,14 +89,14 @@ FluidBufMFCC.process(s,~audio, features: ~mfcc_feature);
var chunkLen = (~mfcc_feature.numFrames / 100).asInteger;
var stats = FluidBufStats.kr(
source: ~mfcc_feature, startFrame: count * chunkLen,
startChan:1, numFrames: chunkLen, stats: ~stats, trig: trig,blocking:1
startChan:1, numFrames: chunkLen, stats: ~stats, trig: trig, blocking:1
);
var rd = BufRd.kr(12, ~stats, DC.kr(0), 0, 1);
var bufWr, dsWr;
12.do{|i|
bufWr = BufWr.kr(rd[i], buf, DC.kr(i));
};
dsWr = FluidDataSetWr.kr(~raw, buf: buf, trig: Done.kr(stats),blocking:1);
dsWr = FluidDataSetWr.kr(~raw, buf: buf, idNumber: count, trig: Done.kr(stats),blocking:1);
LocalOut.kr(Done.kr(dsWr));
FreeSelf.kr(count - 99);
Poll.kr(trig,(100-count));

@ -97,7 +97,7 @@ FluidBufPitch.process(s,~audio, features: ~pitch_feature);
var rd = BufRd.kr(2, ~stats, DC.kr(0), 0, 1);// pick only mean pitch and confidence
var wr1 = BufWr.kr(rd[0], buf, DC.kr(0));
var wr2 = BufWr.kr(rd[1], buf, DC.kr(1));
var dsWr = FluidDataSetWr.kr(~raw, buf: buf, trig: Done.kr(stats));
var dsWr = FluidDataSetWr.kr(~raw, buf: buf, idNumber: count, trig: Done.kr(stats));
LocalOut.kr( Done.kr(dsWr));
Poll.kr(trig,count,\count);
FreeSelf.kr(count - 9);

@ -59,7 +59,7 @@ Run when done. The function is passed code::destBuffer:: as argument.
EXAMPLES::
code::
s.boot;
s.reboot;
//Preliminaries: we want some audio, a couple of FluidDataSets, some Buffers, a FluidStandardize and a FluidPCA
(
~audiofile = File.realpath(FluidBufPitch.class.filenameSymbol).dirname +/+ "../AudioFiles/Tremblay-ASWINE-ScratchySynth-M.wav";
@ -99,7 +99,7 @@ FluidBufMFCC.process(s,~audio, features: ~mfcc_feature,action:{"Done MFCCs".post
12.do{|i|
bufWr = BufWr.kr(rd[i], buf, DC.kr(i));
};
dsWr = FluidDataSetWr.kr(~raw, buf: buf, trig: Done.kr(stats));
dsWr = FluidDataSetWr.kr(~raw, buf: buf, idNumber: count, trig: Done.kr(stats));
LocalOut.kr( Done.kr(dsWr));
FreeSelf.kr(count - 99);
Poll.kr(trig,(100 - count));

@ -52,7 +52,7 @@ code::
statsbuf = LocalBuf(7);
pitch = FluidBufPitch.kr(src,start,num,numChans:1,features:~featurebuffers[voice]);
stats = FluidBufStats.kr(~featurebuffers[voice],numChans:1, stats:statsbuf,trig:Done.kr(pitch));
FluidDataSetWr.kr(label,statsbuf,~mydataset,Done.kr(stats))
FluidDataSetWr.kr(~mydataset, label, nil, statsbuf,Done.kr(stats))
};
::
@ -112,7 +112,7 @@ s.reboot;
label = data.key;
pitch = FluidBufPitch.kr(src,start,num,features:~pitchbufs[i]);
stats = FluidBufStats.kr(~pitchbufs[i],stats:~statsbufs[i],trig:Done.kr(pitch));
FluidDataSetWr.kr(~pitchdata,label,-1,buf:~statsbufs[i],trig:Done.kr(stats))
FluidDataSetWr.kr(~pitchdata,label,nil,buf:~statsbufs[i],trig:Done.kr(stats))
});
)

@ -96,7 +96,7 @@ FluidBufPitch.process(s,~audio, features: ~pitch_feature);
var rd = BufRd.kr(2, ~stats, DC.kr(0), 0, 1);// pick only mean pitch and confidence
var wr1 = BufWr.kr(rd[0], buf, DC.kr(0));
var wr2 = BufWr.kr(rd[1], buf, DC.kr(1));
var dsWr = FluidDataSetWr.kr(~raw, buf: buf, trig: Done.kr(stats));
var dsWr = FluidDataSetWr.kr(~raw, buf: buf, idNumber: count, trig: Done.kr(stats));
LocalOut.kr( Done.kr(dsWr));
Poll.kr(trig,count,\count);
FreeSelf.kr(count - 9);

@ -90,7 +90,7 @@ FluidBufPitch.process(s,~audio, features: ~pitch_feature,action:{"Analysed Pitch
var rd = BufRd.kr(2, ~stats, DC.kr(0), 0, 1);// pick only mean pitch and confidence
var wr1 = BufWr.kr(rd[0], buf, DC.kr(0));
var wr2 = BufWr.kr(rd[1], buf, DC.kr(1));
var dsWr = FluidDataSetWr.kr(~raw, buf: buf, trig: Done.kr(stats));
var dsWr = FluidDataSetWr.kr(~raw, buf: buf, idNumber: count, trig: Done.kr(stats));
LocalOut.kr( Done.kr(dsWr));
FreeSelf.kr(count - 9);
Poll.kr(trig,count, \count);

Loading…
Cancel
Save