diff --git a/include/clients/rt/FluidDataSetWr.hpp b/include/clients/rt/FluidDataSetWr.hpp index ebdc0a4..028fcd4 100644 --- a/include/clients/rt/FluidDataSetWr.hpp +++ b/include/clients/rt/FluidDataSetWr.hpp @@ -19,43 +19,60 @@ namespace fluid { namespace client { +namespace datasetwr { + +enum { kDataSet, kIDPrefix, kIDNumber, kBuffer }; + +constexpr std::initializer_list 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 - 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; + std::reference_wrapper mParams; + + void setParams(ParamSetViewType &p) { mParams = p; } + + template auto &get() const { + return mParams.get().template get(); + } + + static constexpr auto &getParameterDescriptors() { return DataSetWrParams; } + + DataSetWriterClient(ParamSetViewType &p) : mParams(p) {} + + template Result process(FluidContext &) { + auto dataset = get().get(); + if (auto datasetPtr = dataset.lock()) { + std::string &idPrefix = get(); + auto &idNumberArr = get(); + 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(); + return datasetPtr->setPoint(id, buf); + } else + return {Result::Status::kError, "No DataSet"}; } - - private: - index mCounter{0}; }; +} // namespace datasetwr using NRTThreadedDataSetWriter = - NRTThreadingAdaptor>; + NRTThreadingAdaptor>; } // namespace client } // namespace fluid diff --git a/include/wrapper/NonRealtime.hpp b/include/wrapper/NonRealtime.hpp index 2e19d7a..9983c5a 100644 --- a/include/wrapper/NonRealtime.hpp +++ b/include/wrapper/NonRealtime.hpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include namespace fluid { namespace client { @@ -46,7 +46,7 @@ namespace impl { using WeakCacheEntryPointer = std::weak_ptr; //could use weak_type in 17 public: - using Cache = std::map; + using Cache = std::unordered_map; 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(nullptr, world, ar); + mParams.template setParameterValuesRT(nullptr, world, ar); mSynchronous = static_cast(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(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(mInBuf[0][0]); if(mID == -1) mID = count(); auto cmd = NonRealTime::rtalloc(mWorld,mID,mWorld, mControlsIterator, this); runAsyncCommand(mWorld, cmd, nullptr, 0, nullptr); + mInst = get(mID); set_calc_function(); Wrapper::getInterfaceTable()->fClearUnitOutputs(this, 1); } ~NRTTriggerUnit() { - if(auto ptr = get(mID).lock()) + if(auto ptr = mInst.lock()) { auto cmd = NonRealTime::rtalloc(mWorld,mID); runAsyncCommand(mWorld, cmd, nullptr, 0, nullptr); @@ -762,32 +776,39 @@ namespace impl { void next(int) { + + index triggerInput = static_cast(mInBuf[static_cast(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(mWorld,mID,blocking); + CommandProcess* cmd = rtalloc(mWorld,mID,blocking,&mParams); runAsyncCommand(mWorld,cmd, nullptr,0, nullptr); mRunCount++; } else { - mDone = ptr->mDone; - out0(0) = mDone ? 1 : static_cast(client.progress()); + if(auto ptr = get(mID).lock()) + { + auto& client = ptr->mClient; + mDone = ptr->mDone; + out0(0) = mDone ? 1 : static_cast(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(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(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; }; diff --git a/include/wrapper/RealTimeBase.hpp b/include/wrapper/RealTimeBase.hpp index 88bcf3a..ab4e324 100644 --- a/include/wrapper/RealTimeBase.hpp +++ b/include/wrapper/RealTimeBase.hpp @@ -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) { diff --git a/include/wrapper/Realtime.hpp b/include/wrapper/Realtime.hpp index e6e183a..64bff83 100644 --- a/include/wrapper/Realtime.hpp +++ b/include/wrapper/Realtime.hpp @@ -34,8 +34,9 @@ public: static void setup(InterfaceTable* ft, const char* name) { - ft->fDefineUnitCmd(name, "latency", doLatency); + registerUnit(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); } diff --git a/release-packaging/Classes/FluidBufLoudness.sc b/release-packaging/Classes/FluidBufLoudness.sc index 0d3644e..8b15d68 100644 --- a/release-packaging/Classes/FluidBufLoudness.sc +++ b/release-packaging/Classes/FluidBufLoudness.sc @@ -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 ); } } diff --git a/release-packaging/Classes/FluidBufMFCC.sc b/release-packaging/Classes/FluidBufMFCC.sc index 0d6360e..8e371e9 100644 --- a/release-packaging/Classes/FluidBufMFCC.sc +++ b/release-packaging/Classes/FluidBufMFCC.sc @@ -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 ); } } diff --git a/release-packaging/Classes/FluidBufMelBands.sc b/release-packaging/Classes/FluidBufMelBands.sc index cb5eb12..b11ffb7 100644 --- a/release-packaging/Classes/FluidBufMelBands.sc +++ b/release-packaging/Classes/FluidBufMelBands.sc @@ -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 ); } } diff --git a/release-packaging/Classes/FluidBufPitch.sc b/release-packaging/Classes/FluidBufPitch.sc index 608a404..d62190b 100644 --- a/release-packaging/Classes/FluidBufPitch.sc +++ b/release-packaging/Classes/FluidBufPitch.sc @@ -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 ); } } diff --git a/release-packaging/Classes/FluidBufSTFT.sc b/release-packaging/Classes/FluidBufSTFT.sc index 382cff6..e93d206 100644 --- a/release-packaging/Classes/FluidBufSTFT.sc +++ b/release-packaging/Classes/FluidBufSTFT.sc @@ -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 ); } } diff --git a/release-packaging/Classes/FluidBufSelect.sc b/release-packaging/Classes/FluidBufSelect.sc index a9cf200..ff0a9d7 100644 --- a/release-packaging/Classes/FluidBufSelect.sc +++ b/release-packaging/Classes/FluidBufSelect.sc @@ -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 } diff --git a/release-packaging/Classes/FluidBufSpectralShape.sc b/release-packaging/Classes/FluidBufSpectralShape.sc index 4a28417..b4bb4ab 100644 --- a/release-packaging/Classes/FluidBufSpectralShape.sc +++ b/release-packaging/Classes/FluidBufSpectralShape.sc @@ -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 ); } } diff --git a/release-packaging/Classes/FluidDataSet.sc b/release-packaging/Classes/FluidDataSet.sc index 9df17cc..12c7edb 100644 --- a/release-packaging/Classes/FluidDataSet.sc +++ b/release-packaging/Classes/FluidDataSet.sc @@ -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)); + } } + diff --git a/release-packaging/Classes/FluidDataSetWr.sc b/release-packaging/Classes/FluidDataSetWr.sc index db4f79d..c72c12d 100644 --- a/release-packaging/Classes/FluidDataSetWr.sc +++ b/release-packaging/Classes/FluidDataSetWr.sc @@ -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); } diff --git a/release-packaging/Classes/FluidKMeans.sc b/release-packaging/Classes/FluidKMeans.sc index a38d613..31fda02 100644 --- a/release-packaging/Classes/FluidKMeans.sc +++ b/release-packaging/Classes/FluidKMeans.sc @@ -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 {} diff --git a/release-packaging/Examples/dataset/0-demo-dataset-maker-utilities.scd b/release-packaging/Examples/dataset/0-demo-dataset-maker-utilities.scd index 9ceb017..d07a730 100644 --- a/release-packaging/Examples/dataset/0-demo-dataset-maker-utilities.scd +++ b/release-packaging/Examples/dataset/0-demo-dataset-maker-utilities.scd @@ -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; -) \ No newline at end of file +) diff --git a/release-packaging/Examples/dataset/1-learning examples/10a-weighted-MFCCs-comparison.scd b/release-packaging/Examples/dataset/1-learning examples/10a-weighted-MFCCs-comparison.scd index 8ed2aa0..1dc091b 100644 --- a/release-packaging/Examples/dataset/1-learning examples/10a-weighted-MFCCs-comparison.scd +++ b/release-packaging/Examples/dataset/1-learning examples/10a-weighted-MFCCs-comparison.scd @@ -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)); diff --git a/release-packaging/Examples/dataset/1-learning examples/11-compositing-datasets.scd b/release-packaging/Examples/dataset/1-learning examples/11-compositing-datasets.scd index 9b55fcf..5b3aee6 100644 --- a/release-packaging/Examples/dataset/1-learning examples/11-compositing-datasets.scd +++ b/release-packaging/Examples/dataset/1-learning examples/11-compositing-datasets.scd @@ -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); }); ) diff --git a/release-packaging/Examples/dataset/1-learning examples/12-windowed-clustered-segmentation.scd b/release-packaging/Examples/dataset/1-learning examples/12-windowed-clustered-segmentation.scd index 5167a5d..69d17d5 100644 --- a/release-packaging/Examples/dataset/1-learning examples/12-windowed-clustered-segmentation.scd +++ b/release-packaging/Examples/dataset/1-learning examples/12-windowed-clustered-segmentation.scd @@ -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); }); ) diff --git a/release-packaging/Examples/dataset/1-learning examples/8c-mlp-regressor-as-dim-redux.scd b/release-packaging/Examples/dataset/1-learning examples/8c-mlp-regressor-as-dim-redux.scd index 6bf9101..6c59b6e 100644 --- a/release-packaging/Examples/dataset/1-learning examples/8c-mlp-regressor-as-dim-redux.scd +++ b/release-packaging/Examples/dataset/1-learning examples/8c-mlp-regressor-as-dim-redux.scd @@ -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)); diff --git a/release-packaging/HelpSource/Classes/FluidAudioTransport.schelp b/release-packaging/HelpSource/Classes/FluidAudioTransport.schelp index 8f629ea..d56c1c5 100644 --- a/release-packaging/HelpSource/Classes/FluidAudioTransport.schelp +++ b/release-packaging/HelpSource/Classes/FluidAudioTransport.schelp @@ -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 ( diff --git a/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp b/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp index a202579..a960987 100644 --- a/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp @@ -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"); diff --git a/release-packaging/HelpSource/Classes/FluidBufLoudness.schelp b/release-packaging/HelpSource/Classes/FluidBufLoudness.schelp index 74e0cae..86651fd 100644 --- a/release-packaging/HelpSource/Classes/FluidBufLoudness.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufLoudness.schelp @@ -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 diff --git a/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp b/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp index 8ad8c8a..9bc9764 100644 --- a/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp @@ -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. diff --git a/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp b/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp index 8b76900..9d15d33 100644 --- a/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp @@ -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 diff --git a/release-packaging/HelpSource/Classes/FluidBufPitch.schelp b/release-packaging/HelpSource/Classes/FluidBufPitch.schelp index 99cb9d8..f2352ea 100644 --- a/release-packaging/HelpSource/Classes/FluidBufPitch.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufPitch.schelp @@ -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 ( diff --git a/release-packaging/HelpSource/Classes/FluidBufSTFT.schelp b/release-packaging/HelpSource/Classes/FluidBufSTFT.schelp index e9fe662..aa7ee28 100644 --- a/release-packaging/HelpSource/Classes/FluidBufSTFT.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufSTFT.schelp @@ -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) +}); +) :: diff --git a/release-packaging/HelpSource/Classes/FluidBufSelect.schelp b/release-packaging/HelpSource/Classes/FluidBufSelect.schelp new file mode 100644 index 0000000..e20a544 --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidBufSelect.schelp @@ -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;}); +:: + diff --git a/release-packaging/HelpSource/Classes/FluidBufSelectEvery.schelp b/release-packaging/HelpSource/Classes/FluidBufSelectEvery.schelp new file mode 100644 index 0000000..cb83c0e --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidBufSelectEvery.schelp @@ -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;}); +:::: diff --git a/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp b/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp index 4f49ccd..9561cc6 100644 --- a/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp @@ -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. diff --git a/release-packaging/HelpSource/Classes/FluidBufStats.schelp b/release-packaging/HelpSource/Classes/FluidBufStats.schelp index 0a6a0e3..39aae16 100644 --- a/release-packaging/HelpSource/Classes/FluidBufStats.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufStats.schelp @@ -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; + };) ) + :: diff --git a/release-packaging/HelpSource/Classes/FluidDataSet.schelp b/release-packaging/HelpSource/Classes/FluidDataSet.schelp index 8816b16..6643181 100644 --- a/release-packaging/HelpSource/Classes/FluidDataSet.schelp +++ b/release-packaging/HelpSource/Classes/FluidDataSet.schelp @@ -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. diff --git a/release-packaging/HelpSource/Classes/FluidDataSetWr.schelp b/release-packaging/HelpSource/Classes/FluidDataSetWr.schelp index e797c52..f92de08 100644 --- a/release-packaging/HelpSource/Classes/FluidDataSetWr.schelp +++ b/release-packaging/HelpSource/Classes/FluidDataSetWr.schelp @@ -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 :: diff --git a/release-packaging/HelpSource/Classes/FluidKMeans.schelp b/release-packaging/HelpSource/Classes/FluidKMeans.schelp index 28f0ef3..1dcf71f 100644 --- a/release-packaging/HelpSource/Classes/FluidKMeans.schelp +++ b/release-packaging/HelpSource/Classes/FluidKMeans.schelp @@ -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 diff --git a/release-packaging/HelpSource/Classes/FluidMDS.schelp b/release-packaging/HelpSource/Classes/FluidMDS.schelp index c5444f2..b1f7722 100644 --- a/release-packaging/HelpSource/Classes/FluidMDS.schelp +++ b/release-packaging/HelpSource/Classes/FluidMDS.schelp @@ -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)); diff --git a/release-packaging/HelpSource/Classes/FluidNormalize.schelp b/release-packaging/HelpSource/Classes/FluidNormalize.schelp index d0b4043..5718cd6 100644 --- a/release-packaging/HelpSource/Classes/FluidNormalize.schelp +++ b/release-packaging/HelpSource/Classes/FluidNormalize.schelp @@ -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); diff --git a/release-packaging/HelpSource/Classes/FluidPCA.schelp b/release-packaging/HelpSource/Classes/FluidPCA.schelp index aaf6a00..d271202 100644 --- a/release-packaging/HelpSource/Classes/FluidPCA.schelp +++ b/release-packaging/HelpSource/Classes/FluidPCA.schelp @@ -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)); diff --git a/release-packaging/HelpSource/Classes/FluidProcessSlices.schelp b/release-packaging/HelpSource/Classes/FluidProcessSlices.schelp index 9e27f2e..5de64ec 100644 --- a/release-packaging/HelpSource/Classes/FluidProcessSlices.schelp +++ b/release-packaging/HelpSource/Classes/FluidProcessSlices.schelp @@ -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)) }); ) diff --git a/release-packaging/HelpSource/Classes/FluidRobustScale.schelp b/release-packaging/HelpSource/Classes/FluidRobustScale.schelp index 85848a0..5237e55 100644 --- a/release-packaging/HelpSource/Classes/FluidRobustScale.schelp +++ b/release-packaging/HelpSource/Classes/FluidRobustScale.schelp @@ -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); diff --git a/release-packaging/HelpSource/Classes/FluidStandardize.schelp b/release-packaging/HelpSource/Classes/FluidStandardize.schelp index c4d3b2b..a10ceab 100644 --- a/release-packaging/HelpSource/Classes/FluidStandardize.schelp +++ b/release-packaging/HelpSource/Classes/FluidStandardize.schelp @@ -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);